1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18 /*
19 * WARNING: Do not include and use these directly. Use jni_macros.h instead!
20 * The "detail" namespace should be a strong hint not to depend on the internals,
21 * which could change at any time.
22 *
23 * This implements the underlying mechanism for compile-time JNI signature/ctype checking
24 * and inference.
25 *
26 * This file provides the constexpr basic blocks such as strings, arrays, vectors
27 * as well as the JNI-specific parsing functionality.
28 *
29 * Everything is implemented via generic-style (templates without metaprogramming)
30 * wherever possible. Traditional template metaprogramming is used sparingly.
31 *
32 * Everything in this file except ostream<< is constexpr.
33 */
34
35 #ifndef LIBNATIVEHELPER_PLATFORM_INCLUDE_NATIVEHELPER_DETAIL_SIGNATURE_CHECKER_H_
36 #define LIBNATIVEHELPER_PLATFORM_INCLUDE_NATIVEHELPER_DETAIL_SIGNATURE_CHECKER_H_
37
38 #include <iostream> // std::ostream
39 #include <jni.h> // jni typedefs, JniNativeMethod.
40 #include <type_traits> // std::common_type, std::remove_cv
41
42 namespace nativehelper {
43 namespace detail {
44
45 // If CHECK evaluates to false then X_ASSERT will halt compilation.
46 //
47 // Asserts meant to be used only within constexpr context.
48 #if defined(JNI_SIGNATURE_CHECKER_DISABLE_ASSERTS)
49 # define X_ASSERT(CHECK) do { if ((false)) { (CHECK) ? void(0) : void(0); } } while (false)
50 #else
51 # define X_ASSERT(CHECK) \
52 ( (CHECK) ? void(0) : jni_assertion_failure(#CHECK) )
53 #endif
54
55 // The runtime 'jni_assert' will never get called from a constexpr context;
56 // instead compilation will abort with a stack trace.
57 //
58 // Inspect the frame above this one to see the exact nature of the failure.
59 inline void jni_assertion_failure(const char* /*msg*/) __attribute__((noreturn));
jni_assertion_failure(const char *)60 inline void jni_assertion_failure(const char* /*msg*/) {
61 std::terminate();
62 }
63
64 // An immutable constexpr string view, similar to std::string_view but for C++14.
65 // For a mutable string see instead ConstexprVector<char>.
66 //
67 // As it is a read-only view into a string, it is not guaranteed to be zero-terminated.
68 struct ConstexprStringView {
69 // Implicit conversion from string literal:
70 // ConstexprStringView str = "hello_world";
71 template<size_t N>
ConstexprStringViewConstexprStringView72 constexpr ConstexprStringView(const char (& lit)[N]) // NOLINT: explicit.
73 : _array(lit), _size(N - 1) {
74 // Using an array of characters is not allowed because the inferred size would be wrong.
75 // Use the other constructor instead for that.
76 X_ASSERT(lit[N - 1] == '\0');
77 }
78
ConstexprStringViewConstexprStringView79 constexpr ConstexprStringView(const char* ptr, size_t size)
80 : _array(ptr), _size(size) {
81 // See the below constructor instead.
82 X_ASSERT(ptr != nullptr);
83 }
84
85 // No-arg constructor: Create empty view.
ConstexprStringViewConstexprStringView86 constexpr ConstexprStringView() : _array(""), _size(0u) {}
87
sizeConstexprStringView88 constexpr size_t size() const {
89 return _size;
90 }
91
emptyConstexprStringView92 constexpr bool empty() const {
93 return size() == 0u;
94 }
95
96 constexpr char operator[](size_t i) const {
97 X_ASSERT(i <= size());
98 return _array[i];
99 }
100
101 // Create substring from this[start..start+len).
substrConstexprStringView102 constexpr ConstexprStringView substr(size_t start, size_t len) const {
103 X_ASSERT(start <= size());
104 X_ASSERT(len <= size() - start);
105
106 return ConstexprStringView(&_array[start], len);
107 }
108
109 // Create maximum length substring that begins at 'start'.
substrConstexprStringView110 constexpr ConstexprStringView substr(size_t start) const {
111 X_ASSERT(start <= size());
112 return substr(start, size() - start);
113 }
114
115 using const_iterator = const char*;
116
beginConstexprStringView117 constexpr const_iterator begin() const {
118 return &_array[0];
119 }
120
endConstexprStringView121 constexpr const_iterator end() const {
122 return &_array[size()];
123 }
124
125 private:
126 const char* _array; // Never-null for simplicity.
127 size_t _size;
128 };
129
130 constexpr bool
131 operator==(const ConstexprStringView& lhs, const ConstexprStringView& rhs) {
132 if (lhs.size() != rhs.size()) {
133 return false;
134 }
135 for (size_t i = 0; i < lhs.size(); ++i) {
136 if (lhs[i] != rhs[i]) {
137 return false;
138 }
139 }
140 return true;
141 }
142
143 constexpr bool
144 operator!=(const ConstexprStringView& lhs, const ConstexprStringView& rhs) {
145 return !(lhs == rhs);
146 }
147
148 inline std::ostream& operator<<(std::ostream& os, const ConstexprStringView& str) {
149 for (char c : str) {
150 os << c;
151 }
152 return os;
153 }
154
IsValidJniDescriptorStart(char shorty)155 constexpr bool IsValidJniDescriptorStart(char shorty) {
156 constexpr char kValidJniStarts[] =
157 {'V', 'Z', 'B', 'C', 'S', 'I', 'J', 'F', 'D', 'L', '[', '(', ')'};
158
159 for (char c : kValidJniStarts) {
160 if (c == shorty) {
161 return true;
162 }
163 }
164
165 return false;
166 }
167
168 // A constexpr "vector" that supports storing a variable amount of Ts
169 // in an array-like interface.
170 //
171 // An up-front kMaxSize must be given since constexpr does not support
172 // dynamic allocations.
173 template<typename T, size_t kMaxSize>
174 struct ConstexprVector {
175 public:
ConstexprVectorConstexprVector176 constexpr explicit ConstexprVector() : _size(0u), _array{} {
177 }
178
179 private:
180 // Custom iterator to support ptr-one-past-end into the union array without
181 // undefined behavior.
182 template<typename Elem>
183 struct VectorIterator {
184 Elem* ptr;
185
186 constexpr VectorIterator& operator++() {
187 ++ptr;
188 return *this;
189 }
190
191 constexpr VectorIterator operator++(int) const {
192 VectorIterator tmp(*this);
193 ++tmp;
194 return tmp;
195 }
196
197 constexpr /*T&*/ auto& operator*() {
198 // Use 'auto' here since using 'T' is incorrect with const_iterator.
199 return ptr->_value;
200 }
201
202 constexpr const /*T&*/ auto& operator*() const {
203 // Use 'auto' here for consistency with above.
204 return ptr->_value;
205 }
206
207 constexpr bool operator==(const VectorIterator& other) const {
208 return ptr == other.ptr;
209 }
210
211 constexpr bool operator!=(const VectorIterator& other) const {
212 return !(*this == other);
213 }
214 };
215
216 // Do not require that T is default-constructible by using a union.
217 struct MaybeElement {
218 union {
219 T _value;
220 };
221 };
222
223 public:
224 using iterator = VectorIterator<MaybeElement>;
225 using const_iterator = VectorIterator<const MaybeElement>;
226
beginConstexprVector227 constexpr iterator begin() {
228 return {&_array[0]};
229 }
230
endConstexprVector231 constexpr iterator end() {
232 return {&_array[size()]};
233 }
234
beginConstexprVector235 constexpr const_iterator begin() const {
236 return {&_array[0]};
237 }
238
endConstexprVector239 constexpr const_iterator end() const {
240 return {&_array[size()]};
241 }
242
push_backConstexprVector243 constexpr void push_back(const T& value) {
244 X_ASSERT(_size + 1 <= kMaxSize);
245
246 _array[_size]._value = value;
247 _size++;
248 }
249
250 // A pop operation could also be added since constexpr T's
251 // have default destructors, it would just be _size--.
252 // We do not need a pop() here though.
253
254 constexpr const T& operator[](size_t i) const {
255 return _array[i]._value;
256 }
257
258 constexpr T& operator[](size_t i) {
259 return _array[i]._value;
260 }
261
sizeConstexprVector262 constexpr size_t size() const {
263 return _size;
264 }
265 private:
266
267 size_t _size;
268 MaybeElement _array[kMaxSize];
269 };
270
271 // Parsed and validated "long" form of a single JNI descriptor.
272 // e.g. one of "J", "Ljava/lang/Object;" etc.
273 struct JniDescriptorNode {
274 ConstexprStringView longy;
275
JniDescriptorNodeJniDescriptorNode276 constexpr JniDescriptorNode(ConstexprStringView longy) : longy(longy) { // NOLINT(google-explicit-constructor)
277 X_ASSERT(!longy.empty());
278 }
JniDescriptorNodeJniDescriptorNode279 constexpr JniDescriptorNode() : longy() {}
280
shortyJniDescriptorNode281 constexpr char shorty() {
282 // Must be initialized with the non-default constructor.
283 X_ASSERT(!longy.empty());
284 return longy[0];
285 }
286 };
287
288 inline std::ostream& operator<<(std::ostream& os, const JniDescriptorNode& node) {
289 os << node.longy;
290 return os;
291 }
292
293 // Equivalent of C++17 std::optional.
294 //
295 // An optional is essentially a type safe
296 // union {
297 // void Nothing,
298 // T Some;
299 // };
300 //
301 template<typename T>
302 struct ConstexprOptional {
303 // Create a default optional with no value.
ConstexprOptionalConstexprOptional304 constexpr ConstexprOptional() : _has_value(false), _nothing() {
305 }
306
307 // Create an optional with a value.
ConstexprOptionalConstexprOptional308 constexpr ConstexprOptional(const T& value) // NOLINT(google-explicit-constructor)
309 : _has_value(true), _value(value) {
310 }
311
312 constexpr explicit operator bool() const {
313 return _has_value;
314 }
315
has_valueConstexprOptional316 constexpr bool has_value() const {
317 return _has_value;
318 }
319
valueConstexprOptional320 constexpr const T& value() const {
321 X_ASSERT(has_value());
322 return _value;
323 }
324
325 constexpr const T* operator->() const {
326 return &(value());
327 }
328
329 constexpr const T& operator*() const {
330 return value();
331 }
332
333 private:
334 bool _has_value;
335 // The "Nothing" is likely unnecessary but improves readability.
336 struct Nothing {};
337 union {
338 Nothing _nothing;
339 T _value;
340 };
341 };
342
343 template<typename T>
344 constexpr bool
345 operator==(const ConstexprOptional<T>& lhs, const ConstexprOptional<T>& rhs) {
346 if (lhs && rhs) {
347 return lhs.value() == rhs.value();
348 }
349 return lhs.has_value() == rhs.has_value();
350 }
351
352 template<typename T>
353 constexpr bool
354 operator!=(const ConstexprOptional<T>& lhs, const ConstexprOptional<T>& rhs) {
355 return !(lhs == rhs);
356 }
357
358 template<typename T>
359 inline std::ostream& operator<<(std::ostream& os, const ConstexprOptional<T>& val) {
360 if (val) {
361 os << val.value();
362 }
363 return os;
364 }
365
366 // Equivalent of std::nullopt
367 // Allows implicit conversion to any empty ConstexprOptional<T>.
368 // Mostly useful for macros that need to return an empty constexpr optional.
369 struct NullConstexprOptional {
370 template<typename T>
371 constexpr operator ConstexprOptional<T>() const { // NOLINT(google-explicit-constructor)
372 return ConstexprOptional<T>();
373 }
374 };
375
376 inline std::ostream& operator<<(std::ostream& os, NullConstexprOptional) {
377 return os;
378 }
379
380 #if !defined(PARSE_FAILURES_NONFATAL)
381 // Unfortunately we cannot have custom messages here, as it just prints a stack trace with the
382 // macros expanded. This is at least more flexible than static_assert which requires a string
383 // literal.
384 // NOTE: The message string literal must be on same line as the macro to be seen during a
385 // compilation error.
386 #define PARSE_FAILURE(msg) X_ASSERT(! #msg)
387 #define PARSE_ASSERT_MSG(cond, msg) X_ASSERT(#msg && (cond))
388 #define PARSE_ASSERT(cond) X_ASSERT(cond)
389 #else
390 #define PARSE_FAILURE(msg) return NullConstexprOptional{};
391 #define PARSE_ASSERT_MSG(cond, msg) if (!(cond)) { PARSE_FAILURE(msg); }
392 #define PARSE_ASSERT(cond) if (!(cond)) { PARSE_FAILURE(""); }
393 #endif
394
395 // This is a placeholder function and should not be called directly.
ParseFailure(const char * msg)396 constexpr void ParseFailure(const char* msg) {
397 (void) msg; // intentionally no-op.
398 }
399
400 // Temporary parse data when parsing a function descriptor.
401 struct ParseTypeDescriptorResult {
402 // A single argument descriptor, e.g. "V" or "Ljava/lang/Object;"
403 ConstexprStringView token;
404 // The remainder of the function descriptor yet to be parsed.
405 ConstexprStringView remainder;
406
has_tokenParseTypeDescriptorResult407 constexpr bool has_token() const {
408 return token.size() > 0u;
409 }
410
has_remainderParseTypeDescriptorResult411 constexpr bool has_remainder() const {
412 return remainder.size() > 0u;
413 }
414
as_nodeParseTypeDescriptorResult415 constexpr JniDescriptorNode as_node() const {
416 X_ASSERT(has_token());
417 return {token};
418 }
419 };
420
421 // Parse a single type descriptor out of a function type descriptor substring,
422 // and return the token and the remainder string.
423 //
424 // If parsing fails (i.e. illegal syntax), then:
425 // parses are fatal -> assertion is triggered (default behavior),
426 // parses are nonfatal -> returns nullopt (test behavior).
427 constexpr ConstexprOptional<ParseTypeDescriptorResult>
428 ParseSingleTypeDescriptor(ConstexprStringView single_type,
429 bool allow_void = false) {
430 constexpr NullConstexprOptional kUnreachable = {};
431
432 // Nothing else left.
433 if (single_type.size() == 0) {
434 return ParseTypeDescriptorResult{};
435 }
436
437 ConstexprStringView token;
438 ConstexprStringView remainder = single_type.substr(/*start*/1u);
439
440 char c = single_type[0];
441 PARSE_ASSERT(IsValidJniDescriptorStart(c));
442
443 enum State {
444 kSingleCharacter,
445 kArray,
446 kObject
447 };
448
449 State state = kSingleCharacter;
450
451 // Parse the first character to figure out if we should parse the rest.
452 switch (c) {
453 case '!': {
454 constexpr bool fast_jni_is_deprecated = false;
455 PARSE_ASSERT(fast_jni_is_deprecated);
456 break;
457 }
458 case 'V':
459 if (!allow_void) {
460 constexpr bool void_type_descriptor_only_allowed_in_return_type = false;
461 PARSE_ASSERT(void_type_descriptor_only_allowed_in_return_type);
462 }
463 [[clang::fallthrough]];
464 case 'Z':
465 case 'B':
466 case 'C':
467 case 'S':
468 case 'I':
469 case 'J':
470 case 'F':
471 case 'D':
472 token = single_type.substr(/*start*/0u, /*len*/1u);
473 break;
474 case 'L':
475 state = kObject;
476 break;
477 case '[':
478 state = kArray;
479 break;
480 default: {
481 // See JNI Chapter 3: Type Signatures.
482 PARSE_FAILURE("Expected a valid type descriptor character.");
483 return kUnreachable;
484 }
485 }
486
487 // Possibly parse an arbitary-long remainder substring.
488 switch (state) {
489 case kSingleCharacter:
490 return {{token, remainder}};
491 case kArray: {
492 // Recursively parse the array component, as it's just any non-void type descriptor.
493 ConstexprOptional<ParseTypeDescriptorResult>
494 maybe_res = ParseSingleTypeDescriptor(remainder, /*allow_void*/false);
495 PARSE_ASSERT(maybe_res); // Downstream parsing has asserted, bail out.
496
497 ParseTypeDescriptorResult res = maybe_res.value();
498
499 // Reject illegal array type descriptors such as "]".
500 PARSE_ASSERT_MSG(res.has_token(), "All array types must follow by their component type (e.g. ']I', ']]Z', etc. ");
501
502 token = single_type.substr(/*start*/0u, res.token.size() + 1u);
503
504 return {{token, res.remainder}};
505 }
506 case kObject: {
507 // Parse the fully qualified class, e.g. Lfoo/bar/baz;
508 // Note checking that each part of the class name is a valid class identifier
509 // is too complicated (JLS 3.8).
510 // This simple check simply scans until the next ';'.
511 bool found_semicolon = false;
512 size_t semicolon_len = 0;
513 for (size_t i = 0; i < single_type.size(); ++i) {
514 switch (single_type[i]) {
515 case ')':
516 case '(':
517 case '[':
518 PARSE_FAILURE("Object identifiers cannot have ()[ in them.");
519 break;
520 }
521 if (single_type[i] == ';') {
522 semicolon_len = i + 1;
523 found_semicolon = true;
524 break;
525 }
526 }
527
528 PARSE_ASSERT(found_semicolon);
529
530 token = single_type.substr(/*start*/0u, semicolon_len);
531 remainder = single_type.substr(/*start*/semicolon_len);
532
533 bool class_name_is_empty = token.size() <= 2u; // e.g. "L;"
534 PARSE_ASSERT(!class_name_is_empty);
535
536 return {{token, remainder}};
537 }
538 default:
539 X_ASSERT(false);
540 }
541
542 X_ASSERT(false);
543 return kUnreachable;
544 }
545
546 // Abstract data type to represent container for Ret(Args,...).
547 template<typename T, size_t kMaxSize>
548 struct FunctionSignatureDescriptor {
549 ConstexprVector<T, kMaxSize> args;
550 T ret;
551
552 static constexpr size_t max_size = kMaxSize;
553 };
554
555
556 template<typename T, size_t kMaxSize>
557 inline std::ostream& operator<<(
558 std::ostream& os,
559 const FunctionSignatureDescriptor<T, kMaxSize>& signature) {
560 size_t count = 0;
561 os << "args={";
562 for (auto& arg : signature.args) {
563 os << arg;
564
565 if (count != signature.args.size() - 1) {
566 os << ",";
567 }
568
569 ++count;
570 }
571 os << "}, ret=";
572 os << signature.ret;
573 return os;
574 }
575
576 // Ret(Args...) of JniDescriptorNode.
577 template<size_t kMaxSize>
578 using JniSignatureDescriptor = FunctionSignatureDescriptor<JniDescriptorNode,
579 kMaxSize>;
580
581 // Parse a JNI function signature descriptor into a JniSignatureDescriptor.
582 //
583 // If parsing fails (i.e. illegal syntax), then:
584 // parses are fatal -> assertion is triggered (default behavior),
585 // parses are nonfatal -> returns nullopt (test behavior).
586 template<size_t kMaxSize>
587 constexpr ConstexprOptional<JniSignatureDescriptor<kMaxSize>>
ParseSignatureAsList(ConstexprStringView signature)588 ParseSignatureAsList(ConstexprStringView signature) {
589 // The list of JNI descriptors cannot possibly exceed the number of characters
590 // in the JNI string literal. We leverage this to give an upper bound of the strlen.
591 // This is a bit wasteful but in constexpr there *must* be a fixed upper size for data structures.
592 ConstexprVector<JniDescriptorNode, kMaxSize> jni_desc_node_list;
593 JniDescriptorNode return_jni_desc;
594
595 enum State {
596 kInitial = 0,
597 kParsingParameters = 1,
598 kParsingReturnType = 2,
599 kCompleted = 3,
600 };
601
602 State state = kInitial;
603
604 while (!signature.empty()) {
605 switch (state) {
606 case kInitial: {
607 char c = signature[0];
608 PARSE_ASSERT_MSG(c == '(',
609 "First character of a JNI signature must be a '('");
610 state = kParsingParameters;
611 signature = signature.substr(/*start*/1u);
612 break;
613 }
614 case kParsingParameters: {
615 char c = signature[0];
616 if (c == ')') {
617 state = kParsingReturnType;
618 signature = signature.substr(/*start*/1u);
619 break;
620 }
621
622 ConstexprOptional<ParseTypeDescriptorResult>
623 res = ParseSingleTypeDescriptor(signature, /*allow_void*/false);
624 PARSE_ASSERT(res);
625
626 jni_desc_node_list.push_back(res->as_node());
627
628 signature = res->remainder;
629 break;
630 }
631 case kParsingReturnType: {
632 ConstexprOptional<ParseTypeDescriptorResult>
633 res = ParseSingleTypeDescriptor(signature, /*allow_void*/true);
634 PARSE_ASSERT(res);
635
636 return_jni_desc = res->as_node();
637 signature = res->remainder;
638 state = kCompleted;
639 break;
640 }
641 default: {
642 // e.g. "()VI" is illegal because the V terminates the signature.
643 PARSE_FAILURE("Signature had left over tokens after parsing return type");
644 break;
645 }
646 }
647 }
648
649 switch (state) {
650 case kCompleted:
651 // Everything is ok.
652 break;
653 case kParsingParameters:
654 PARSE_FAILURE("Signature was missing ')'");
655 break;
656 case kParsingReturnType:
657 PARSE_FAILURE("Missing return type");
658 case kInitial:
659 PARSE_FAILURE("Cannot have an empty signature");
660 default:
661 X_ASSERT(false);
662 }
663
664 return {{jni_desc_node_list, return_jni_desc}};
665 }
666
667 // What kind of JNI does this type belong to?
668 enum NativeKind {
669 kNotJni, // Illegal parameter used inside of a function type.
670 kNormalJniCallingConventionParameter,
671 kNormalNative,
672 kFastNative, // Also valid in normal.
673 kCriticalNative, // Also valid in fast/normal.
674 };
675
676 // Is this type final, i.e. it cannot be subtyped?
677 enum TypeFinal {
678 kNotFinal,
679 kFinal // e.g. any primitive or any "final" class such as String.
680 };
681
682 // What position is the JNI type allowed to be in?
683 // Ignored when in a CriticalNative context.
684 enum NativePositionAllowed {
685 kNotAnyPosition,
686 kReturnPosition,
687 kZerothPosition,
688 kFirstOrLaterPosition,
689 kSecondOrLaterPosition,
690 };
691
ConvertPositionToAllowed(size_t position)692 constexpr NativePositionAllowed ConvertPositionToAllowed(size_t position) {
693 switch (position) {
694 case 0:
695 return kZerothPosition;
696 case 1:
697 return kFirstOrLaterPosition;
698 default:
699 return kSecondOrLaterPosition;
700 }
701 }
702
703 // Type traits for a JNI parameter type. See below for specializations.
704 template<typename T>
705 struct jni_type_trait {
706 static constexpr NativeKind native_kind = kNotJni;
707 static constexpr const char type_descriptor[] = "(illegal)";
708 static constexpr NativePositionAllowed position_allowed = kNotAnyPosition;
709 static constexpr TypeFinal type_finality = kNotFinal;
710 static constexpr const char type_name[] = "(illegal)";
711 };
712
713 // Access the jni_type_trait<T> from a non-templated constexpr function.
714 // Identical non-static fields to jni_type_trait, see Reify().
715 struct ReifiedJniTypeTrait {
716 NativeKind native_kind;
717 ConstexprStringView type_descriptor;
718 NativePositionAllowed position_allowed;
719 TypeFinal type_finality;
720 ConstexprStringView type_name;
721
722 template<typename T>
ReifyReifiedJniTypeTrait723 static constexpr ReifiedJniTypeTrait Reify() {
724 // This should perhaps be called 'Type Erasure' except we don't use virtuals,
725 // so it's not quite the same idiom.
726 using TR = jni_type_trait<T>;
727 return {TR::native_kind,
728 TR::type_descriptor,
729 TR::position_allowed,
730 TR::type_finality,
731 TR::type_name};
732 }
733
734 // Find the most similar ReifiedJniTypeTrait corresponding to the type descriptor.
735 //
736 // Any type can be found by using the exact canonical type descriptor as listed
737 // in the jni type traits definitions.
738 //
739 // Non-final JNI types have limited support for inexact similarity:
740 // [[* | [L* -> jobjectArray
741 // L* -> jobject
742 //
743 // Otherwise return a nullopt.
744 static constexpr ConstexprOptional<ReifiedJniTypeTrait>
745 MostSimilarTypeDescriptor(ConstexprStringView type_descriptor);
746 };
747
748 constexpr bool
749 operator==(const ReifiedJniTypeTrait& lhs, const ReifiedJniTypeTrait& rhs) {
750 return lhs.native_kind == rhs.native_kind
751 && rhs.type_descriptor == lhs.type_descriptor &&
752 lhs.position_allowed == rhs.position_allowed
753 && rhs.type_finality == lhs.type_finality &&
754 lhs.type_name == rhs.type_name;
755 }
756
757 inline std::ostream& operator<<(std::ostream& os, const ReifiedJniTypeTrait& rjtt) {
758 // os << "ReifiedJniTypeTrait<" << rjft.type_name << ">";
759 os << rjtt.type_name;
760 return os;
761 }
762
763 // Template specialization for any JNI typedefs.
764 #define JNI_TYPE_TRAIT(jtype, the_type_descriptor, the_native_kind, the_type_finality, the_position) \
765 template <> \
766 struct jni_type_trait< jtype > { \
767 static constexpr NativeKind native_kind = the_native_kind; \
768 static constexpr const char type_descriptor[] = the_type_descriptor; \
769 static constexpr NativePositionAllowed position_allowed = the_position; \
770 static constexpr TypeFinal type_finality = the_type_finality; \
771 static constexpr const char type_name[] = #jtype; \
772 };
773
774 #define DEFINE_JNI_TYPE_TRAIT(TYPE_TRAIT_FN) \
775 TYPE_TRAIT_FN(jboolean, "Z", kCriticalNative, kFinal, kSecondOrLaterPosition) \
776 TYPE_TRAIT_FN(jbyte, "B", kCriticalNative, kFinal, kSecondOrLaterPosition) \
777 TYPE_TRAIT_FN(jchar, "C", kCriticalNative, kFinal, kSecondOrLaterPosition) \
778 TYPE_TRAIT_FN(jshort, "S", kCriticalNative, kFinal, kSecondOrLaterPosition) \
779 TYPE_TRAIT_FN(jint, "I", kCriticalNative, kFinal, kSecondOrLaterPosition) \
780 TYPE_TRAIT_FN(jlong, "J", kCriticalNative, kFinal, kSecondOrLaterPosition) \
781 TYPE_TRAIT_FN(jfloat, "F", kCriticalNative, kFinal, kSecondOrLaterPosition) \
782 TYPE_TRAIT_FN(jdouble, "D", kCriticalNative, kFinal, kSecondOrLaterPosition) \
783 TYPE_TRAIT_FN(jobject, "Ljava/lang/Object;", kFastNative, kNotFinal, kFirstOrLaterPosition) \
784 TYPE_TRAIT_FN(jclass, "Ljava/lang/Class;", kFastNative, kFinal, kFirstOrLaterPosition) \
785 TYPE_TRAIT_FN(jstring, "Ljava/lang/String;", kFastNative, kFinal, kSecondOrLaterPosition) \
786 TYPE_TRAIT_FN(jarray, "Ljava/lang/Object;", kFastNative, kNotFinal, kSecondOrLaterPosition) \
787 TYPE_TRAIT_FN(jobjectArray, "[Ljava/lang/Object;", kFastNative, kNotFinal, kSecondOrLaterPosition) \
788 TYPE_TRAIT_FN(jbooleanArray, "[Z", kFastNative, kFinal, kSecondOrLaterPosition) \
789 TYPE_TRAIT_FN(jbyteArray, "[B", kFastNative, kFinal, kSecondOrLaterPosition) \
790 TYPE_TRAIT_FN(jcharArray, "[C", kFastNative, kFinal, kSecondOrLaterPosition) \
791 TYPE_TRAIT_FN(jshortArray, "[S", kFastNative, kFinal, kSecondOrLaterPosition) \
792 TYPE_TRAIT_FN(jintArray, "[I", kFastNative, kFinal, kSecondOrLaterPosition) \
793 TYPE_TRAIT_FN(jlongArray, "[J", kFastNative, kFinal, kSecondOrLaterPosition) \
794 TYPE_TRAIT_FN(jfloatArray, "[F", kFastNative, kFinal, kSecondOrLaterPosition) \
795 TYPE_TRAIT_FN(jdoubleArray, "[D", kFastNative, kFinal, kSecondOrLaterPosition) \
796 TYPE_TRAIT_FN(jthrowable, "Ljava/lang/Throwable;", kFastNative, kNotFinal, kSecondOrLaterPosition) \
797 TYPE_TRAIT_FN(JNIEnv*, "", kNormalJniCallingConventionParameter, kFinal, kZerothPosition) \
798 TYPE_TRAIT_FN(void, "V", kCriticalNative, kFinal, kReturnPosition) \
799
DEFINE_JNI_TYPE_TRAIT(JNI_TYPE_TRAIT)800 DEFINE_JNI_TYPE_TRAIT(JNI_TYPE_TRAIT)
801
802 // See ReifiedJniTypeTrait for documentation.
803 constexpr ConstexprOptional<ReifiedJniTypeTrait>
804 ReifiedJniTypeTrait::MostSimilarTypeDescriptor(ConstexprStringView type_descriptor) {
805 #define MATCH_EXACT_TYPE_DESCRIPTOR_FN(type, type_desc, native_kind, ...) \
806 if (type_descriptor == type_desc && native_kind >= kNormalNative) { \
807 return { Reify<type>() }; \
808 }
809
810 // Attempt to look up by the precise type match first.
811 DEFINE_JNI_TYPE_TRAIT(MATCH_EXACT_TYPE_DESCRIPTOR_FN);
812
813 // Otherwise, we need to do an imprecise match:
814 char shorty = type_descriptor.size() >= 1 ? type_descriptor[0] : '\0';
815 if (shorty == 'L') {
816 // Something more specific like Ljava/lang/Throwable, string, etc
817 // is already matched by the macro-expanded conditions above.
818 return {Reify<jobject>()};
819 } else if (type_descriptor.size() >= 2) {
820 auto shorty_shorty = type_descriptor.substr(/*start*/0, /*size*/2u);
821 if (shorty_shorty == "[[" || shorty_shorty == "[L") {
822 // JNI arrays are covariant, so any type T[] (T!=primitive) is castable to Object[].
823 return {Reify<jobjectArray>()};
824 }
825 }
826
827 // To handle completely invalid values.
828 return NullConstexprOptional{};
829 }
830
831 // Is this actual JNI position consistent with the expected position?
IsValidJniParameterPosition(NativeKind native_kind,NativePositionAllowed position,NativePositionAllowed expected_position)832 constexpr bool IsValidJniParameterPosition(NativeKind native_kind,
833 NativePositionAllowed position,
834 NativePositionAllowed expected_position) {
835 X_ASSERT(expected_position != kNotAnyPosition);
836
837 if (native_kind == kCriticalNative) {
838 // CriticalNatives ignore positions since the first 2 special
839 // parameters are stripped.
840 return true;
841 }
842
843 // Is this a return-only position?
844 if (expected_position == kReturnPosition) {
845 if (position != kReturnPosition) {
846 // void can only be in the return position.
847 return false;
848 }
849 // Don't do the other non-return position checks for a return-only position.
850 return true;
851 }
852
853 // JNIEnv* can only be in the first spot.
854 if (position == kZerothPosition && expected_position != kZerothPosition) {
855 return false;
856 // jobject, jclass can be 1st or anywhere afterwards.
857 } else if (position == kFirstOrLaterPosition && expected_position != kFirstOrLaterPosition) {
858 return false;
859 // All other parameters must be in 2nd+ spot, or in the return type.
860 } else if (position == kSecondOrLaterPosition || position == kReturnPosition) {
861 if (expected_position != kFirstOrLaterPosition && expected_position != kSecondOrLaterPosition) {
862 return false;
863 }
864 }
865
866 return true;
867 }
868
869 // Check if a jni parameter type is valid given its position and native_kind.
870 template <typename T>
IsValidJniParameter(NativeKind native_kind,NativePositionAllowed position)871 constexpr bool IsValidJniParameter(NativeKind native_kind, NativePositionAllowed position) {
872 // const,volatile does not affect JNI compatibility since it does not change ABI.
873 using expected_trait = jni_type_trait<typename std::remove_cv<T>::type>;
874 NativeKind expected_native_kind = expected_trait::native_kind;
875
876 // Most types 'T' are not valid for JNI.
877 if (expected_native_kind == NativeKind::kNotJni) {
878 return false;
879 }
880
881 // The rest of the types might be valid, but it depends on the context (native_kind)
882 // and also on their position within the parameters.
883
884 // Position-check first.
885 NativePositionAllowed expected_position = expected_trait::position_allowed;
886 if (!IsValidJniParameterPosition(native_kind, position, expected_position)) {
887 return false;
888 }
889
890 // Ensure the type appropriate is for the native kind.
891 if (expected_native_kind == kNormalJniCallingConventionParameter) {
892 // It's always wrong to use a JNIEnv* anywhere but the 0th spot.
893 if (native_kind == kCriticalNative) {
894 // CriticalNative does not allow using a JNIEnv*.
895 return false;
896 }
897
898 return true; // OK: JniEnv* used in 0th position.
899 } else if (expected_native_kind == kCriticalNative) {
900 // CriticalNative arguments are always valid JNI types anywhere used.
901 return true;
902 } else if (native_kind == kCriticalNative) {
903 // The expected_native_kind was non-critical but we are in a critical context.
904 // Illegal type.
905 return false;
906 }
907
908 // Everything else is fine, e.g. fast/normal native + fast/normal native parameters.
909 return true;
910 }
911
912 // Is there sufficient number of parameters given the kind of JNI that it is?
IsJniParameterCountValid(NativeKind native_kind,size_t count)913 constexpr bool IsJniParameterCountValid(NativeKind native_kind, size_t count) {
914 if (native_kind == kNormalNative || native_kind == kFastNative) {
915 return count >= 2u;
916 } else if (native_kind == kCriticalNative) {
917 return true;
918 }
919
920 constexpr bool invalid_parameter = false;
921 X_ASSERT(invalid_parameter);
922 return false;
923 }
924
925 // Basic template interface. See below for partial specializations.
926 //
927 // Each instantiation will have a 'value' field that determines whether or not
928 // all of the Args are valid JNI arguments given their native_kind.
929 template<NativeKind native_kind, size_t position, typename ... Args>
930 struct is_valid_jni_argument_type {
931 // static constexpr bool value = ?;
932 };
933
934 template<NativeKind native_kind, size_t position>
935 struct is_valid_jni_argument_type<native_kind, position> {
936 static constexpr bool value = true;
937 };
938
939 template<NativeKind native_kind, size_t position, typename T>
940 struct is_valid_jni_argument_type<native_kind, position, T> {
941 static constexpr bool value =
942 IsValidJniParameter<T>(native_kind, ConvertPositionToAllowed(position));
943 };
944
945 template<NativeKind native_kind, size_t position, typename T, typename ... Args>
946 struct is_valid_jni_argument_type<native_kind, position, T, Args...> {
947 static constexpr bool value =
948 IsValidJniParameter<T>(native_kind, ConvertPositionToAllowed(position))
949 && is_valid_jni_argument_type<native_kind,
950 position + 1,
951 Args...>::value;
952 };
953
954 // This helper is required to decompose the function type into a list of arg types.
955 template<NativeKind native_kind, typename T, T* fn>
956 struct is_valid_jni_function_type_helper;
957
958 template<NativeKind native_kind, typename R, typename ... Args, R (*fn)(Args...)>
959 struct is_valid_jni_function_type_helper<native_kind, R(Args...), fn> {
960 static constexpr bool value =
961 IsJniParameterCountValid(native_kind, sizeof...(Args))
962 && IsValidJniParameter<R>(native_kind, kReturnPosition)
963 && is_valid_jni_argument_type<native_kind, /*position*/
964 0,
965 Args...>::value;
966 };
967
968 // Is this function type 'T' a valid C++ function type given the native_kind?
969 template<NativeKind native_kind, typename T, T* fn>
970 constexpr bool IsValidJniFunctionType() {
971 return is_valid_jni_function_type_helper<native_kind, T, fn>::value;
972 // TODO: we could replace template metaprogramming with constexpr by
973 // using FunctionTypeMetafunction.
974 }
975
976 // Many parts of std::array is not constexpr until C++17.
977 template<typename T, size_t N>
978 struct ConstexprArray {
979 // Intentionally public to conform to std::array.
980 // This means all constructors are implicit.
981 // *NOT* meant to be used directly, use the below functions instead.
982 //
983 // The reason std::array has it is to support direct-list-initialization,
984 // e.g. "ConstexprArray<T, sz>{T{...}, T{...}, T{...}, ...};"
985 //
986 // Note that otherwise this would need a very complicated variadic
987 // argument constructor to only support list of Ts.
988 T _array[N];
989
990 constexpr size_t size() const {
991 return N;
992 }
993
994 using iterator = T*;
995 using const_iterator = const T*;
996
997 constexpr iterator begin() {
998 return &_array[0];
999 }
1000
1001 constexpr iterator end() {
1002 return &_array[N];
1003 }
1004
1005 constexpr const_iterator begin() const {
1006 return &_array[0];
1007 }
1008
1009 constexpr const_iterator end() const {
1010 return &_array[N];
1011 }
1012
1013 constexpr T& operator[](size_t i) {
1014 return _array[i];
1015 }
1016
1017 constexpr const T& operator[](size_t i) const {
1018 return _array[i];
1019 }
1020 };
1021
1022 // Why do we need this?
1023 // auto x = {1,2,3} creates an initializer_list,
1024 // but they can't be returned because it contains pointers to temporaries.
1025 // auto x[] = {1,2,3} doesn't even work because auto for arrays is not supported.
1026 //
1027 // an alternative would be to pull up std::common_t directly into the call site
1028 // std::common_type_t<Args...> array[] = {1,2,3}
1029 // but that's even more cludgier.
1030 //
1031 // As the other "stdlib-wannabe" functions, it's weaker than the library
1032 // fundamentals std::make_array but good enough for our use.
1033 template<typename... Args>
1034 constexpr auto MakeArray(Args&& ... args) {
1035 return ConstexprArray<typename std::common_type<Args...>::type,
1036 sizeof...(Args)>{args...};
1037 }
1038
1039 // See below.
1040 template<typename T, T* fn>
1041 struct FunctionTypeMetafunction {
1042 };
1043
1044 // Enables the "map" operation over the function component types.
1045 template<typename R, typename ... Args, R (*fn)(Args...)>
1046 struct FunctionTypeMetafunction<R(Args...), fn> {
1047 // Count how many arguments there are, and add 1 for the return type.
1048 static constexpr size_t
1049 count = sizeof...(Args) + 1u; // args and return type.
1050
1051 // Return an array where the metafunction 'Func' has been applied
1052 // to every argument type. The metafunction must be returning a common type.
1053 template<template<typename Arg> class Func>
1054 static constexpr auto map_args() {
1055 return map_args_impl<Func>(holder < Args > {}...);
1056 }
1057
1058 // Apply the metafunction 'Func' over the return type.
1059 template<template<typename Ret> class Func>
1060 static constexpr auto map_return() {
1061 return Func<R>{}();
1062 }
1063
1064 private:
1065 template<typename T>
1066 struct holder {
1067 };
1068
1069 template<template<typename Arg> class Func, typename Arg0, typename... ArgsRest>
1070 static constexpr auto map_args_impl(holder<Arg0>, holder<ArgsRest>...) {
1071 // One does not simply call MakeArray with 0 template arguments...
1072 auto array = MakeArray(
1073 Func<Args>{}()...
1074 );
1075
1076 return array;
1077 }
1078
1079 template<template<typename Arg> class Func>
1080 static constexpr auto map_args_impl() {
1081 // This overload provides support for MakeArray() with 0 arguments.
1082 using ComponentType = decltype(Func<void>{}());
1083
1084 return ConstexprArray<ComponentType, /*size*/0u>{};
1085 }
1086 };
1087
1088 // Apply ReifiedJniTypeTrait::Reify<T> for every function component type.
1089 template<typename T>
1090 struct ReifyJniTypeMetafunction {
1091 constexpr ReifiedJniTypeTrait operator()() const {
1092 auto res = ReifiedJniTypeTrait::Reify<T>();
1093 X_ASSERT(res.native_kind != kNotJni);
1094 return res;
1095 }
1096 };
1097
1098 // Ret(Args...) where every component is a ReifiedJniTypeTrait.
1099 template<size_t kMaxSize>
1100 using ReifiedJniSignature = FunctionSignatureDescriptor<ReifiedJniTypeTrait,
1101 kMaxSize>;
1102
1103 // Attempts to convert the function type T into a list of ReifiedJniTypeTraits
1104 // that correspond to the function components.
1105 //
1106 // If conversion fails (i.e. non-jni compatible types), then:
1107 // parses are fatal -> assertion is triggered (default behavior),
1108 // parses are nonfatal -> returns nullopt (test behavior).
1109 template <NativeKind native_kind,
1110 typename T,
1111 T* fn,
1112 size_t kMaxSize = FunctionTypeMetafunction<T, fn>::count>
1113 constexpr ConstexprOptional<ReifiedJniSignature<kMaxSize>>
1114 MaybeMakeReifiedJniSignature() {
1115 if (!IsValidJniFunctionType<native_kind, T, fn>()) {
1116 PARSE_FAILURE("The function signature has one or more types incompatible with JNI.");
1117 }
1118
1119 ReifiedJniTypeTrait return_jni_trait =
1120 FunctionTypeMetafunction<T,
1121 fn>::template map_return<ReifyJniTypeMetafunction>();
1122
1123 constexpr size_t
1124 kSkipArgumentPrefix = (native_kind != kCriticalNative) ? 2u : 0u;
1125 ConstexprVector<ReifiedJniTypeTrait, kMaxSize> args;
1126 auto args_list =
1127 FunctionTypeMetafunction<T, fn>::template map_args<ReifyJniTypeMetafunction>();
1128 size_t args_index = 0;
1129 for (auto& arg : args_list) {
1130 // Ignore the 'JNIEnv*, jobject' / 'JNIEnv*, jclass' prefix,
1131 // as its not part of the function descriptor string.
1132 if (args_index >= kSkipArgumentPrefix) {
1133 args.push_back(arg);
1134 }
1135
1136 ++args_index;
1137 }
1138
1139 return {{args, return_jni_trait}};
1140 }
1141
1142 #define COMPARE_DESCRIPTOR_CHECK(expr) if (!(expr)) return false
1143 #define COMPARE_DESCRIPTOR_FAILURE_MSG(msg) if ((true)) return false
1144
1145 // Compares a user-defined JNI descriptor (of a single argument or return value)
1146 // to a reified jni type trait that was derived from the C++ function type.
1147 //
1148 // If comparison fails (i.e. non-jni compatible types), then:
1149 // parses are fatal -> assertion is triggered (default behavior),
1150 // parses are nonfatal -> returns false (test behavior).
1151 constexpr bool
1152 CompareJniDescriptorNodeErased(JniDescriptorNode user_defined_descriptor,
1153 ReifiedJniTypeTrait derived) {
1154
1155 ConstexprOptional<ReifiedJniTypeTrait> user_reified_opt =
1156 ReifiedJniTypeTrait::MostSimilarTypeDescriptor(user_defined_descriptor.longy);
1157
1158 if (!user_reified_opt.has_value()) {
1159 COMPARE_DESCRIPTOR_FAILURE_MSG(
1160 "Could not find any JNI C++ type corresponding to the type descriptor");
1161 }
1162
1163 char user_shorty = user_defined_descriptor.longy.size() > 0 ?
1164 user_defined_descriptor.longy[0] :
1165 '\0';
1166
1167 ReifiedJniTypeTrait user = user_reified_opt.value();
1168 if (user == derived) {
1169 // If we had a similar match, immediately return success.
1170 return true;
1171 } else if (derived.type_name == "jthrowable") {
1172 if (user_shorty == 'L') {
1173 // Weakly allow any objects to correspond to a jthrowable.
1174 // We do not know the managed type system so we have to be permissive here.
1175 return true;
1176 } else {
1177 COMPARE_DESCRIPTOR_FAILURE_MSG(
1178 "jthrowable must correspond to an object type descriptor");
1179 }
1180 } else if (derived.type_name == "jarray") {
1181 if (user_shorty == '[') {
1182 // a jarray is the base type for all other array types. Allow.
1183 return true;
1184 } else {
1185 // Ljava/lang/Object; is the root for all array types.
1186 // Already handled above in 'if user == derived'.
1187 COMPARE_DESCRIPTOR_FAILURE_MSG(
1188 "jarray must correspond to array type descriptor");
1189 }
1190 }
1191 // Otherwise, the comparison has failed and the rest of this is only to
1192 // pick the most appropriate error message.
1193 //
1194 // Note: A weaker form of comparison would allow matching 'Ljava/lang/String;'
1195 // against 'jobject', etc. However the policy choice here is to enforce the strictest
1196 // comparison that we can to utilize the type system to its fullest.
1197
1198 if (derived.type_finality == kFinal || user.type_finality == kFinal) {
1199 // Final types, e.g. "I", "Ljava/lang/String;" etc must match exactly
1200 // the C++ jni descriptor string ('I' -> jint, 'Ljava/lang/String;' -> jstring).
1201 COMPARE_DESCRIPTOR_FAILURE_MSG(
1202 "The JNI descriptor string must be the exact type equivalent of the "
1203 "C++ function signature.");
1204 } else if (user_shorty == '[') {
1205 COMPARE_DESCRIPTOR_FAILURE_MSG(
1206 "The array JNI descriptor must correspond to j${type}Array or jarray");
1207 } else if (user_shorty == 'L') {
1208 COMPARE_DESCRIPTOR_FAILURE_MSG(
1209 "The object JNI descriptor must correspond to jobject.");
1210 } else {
1211 X_ASSERT(false); // We should never get here, but either way this means the types did not match
1212 COMPARE_DESCRIPTOR_FAILURE_MSG(
1213 "The JNI type descriptor string does not correspond to the C++ JNI type.");
1214 }
1215 }
1216
1217 // Matches a user-defined JNI function descriptor against the C++ function type.
1218 //
1219 // If matches fails, then:
1220 // parses are fatal -> assertion is triggered (default behavior),
1221 // parses are nonfatal -> returns false (test behavior).
1222 template<NativeKind native_kind, typename T, T* fn, size_t kMaxSize>
1223 constexpr bool
1224 MatchJniDescriptorWithFunctionType(ConstexprStringView user_function_descriptor) {
1225 constexpr size_t kReifiedMaxSize = FunctionTypeMetafunction<T, fn>::count;
1226
1227 ConstexprOptional<ReifiedJniSignature<kReifiedMaxSize>>
1228 reified_signature_opt =
1229 MaybeMakeReifiedJniSignature<native_kind, T, fn>();
1230 if (!reified_signature_opt) {
1231 // Assertion handling done by MaybeMakeReifiedJniSignature.
1232 return false;
1233 }
1234
1235 ConstexprOptional<JniSignatureDescriptor<kMaxSize>> user_jni_sig_desc_opt =
1236 ParseSignatureAsList<kMaxSize>(user_function_descriptor);
1237
1238 if (!user_jni_sig_desc_opt) {
1239 // Assertion handling done by ParseSignatureAsList.
1240 return false;
1241 }
1242
1243 ReifiedJniSignature<kReifiedMaxSize>
1244 reified_signature = reified_signature_opt.value();
1245 JniSignatureDescriptor<kMaxSize>
1246 user_jni_sig_desc = user_jni_sig_desc_opt.value();
1247
1248 if (reified_signature.args.size() != user_jni_sig_desc.args.size()) {
1249 COMPARE_DESCRIPTOR_FAILURE_MSG(
1250 "Number of parameters in JNI descriptor string"
1251 "did not match number of parameters in C++ function type");
1252 } else if (!CompareJniDescriptorNodeErased(user_jni_sig_desc.ret,
1253 reified_signature.ret)) {
1254 // Assertion handling done by CompareJniDescriptorNodeErased.
1255 return false;
1256 } else {
1257 for (size_t i = 0; i < user_jni_sig_desc.args.size(); ++i) {
1258 if (!CompareJniDescriptorNodeErased(user_jni_sig_desc.args[i],
1259 reified_signature.args[i])) {
1260 // Assertion handling done by CompareJniDescriptorNodeErased.
1261 return false;
1262 }
1263 }
1264 }
1265
1266 return true;
1267 }
1268
1269 // Supports inferring the JNI function descriptor string from the C++
1270 // function type when all type components are final.
1271 template<NativeKind native_kind, typename T, T* fn>
1272 struct InferJniDescriptor {
1273 static constexpr size_t kMaxSize = FunctionTypeMetafunction<T, fn>::count;
1274
1275 // Convert the C++ function type into a JniSignatureDescriptor which holds
1276 // the canonical (according to jni_traits) descriptors for each component.
1277 // The C++ type -> JNI mapping must be nonambiguous (see jni_macros.h for exact rules).
1278 //
1279 // If conversion fails (i.e. C++ signatures is illegal for JNI, or the types are ambiguous):
1280 // if parsing is fatal -> assertion failure (default behavior)
1281 // if parsing is nonfatal -> returns nullopt (test behavior).
1282 static constexpr ConstexprOptional<JniSignatureDescriptor<kMaxSize>> FromFunctionType() {
1283 constexpr size_t kReifiedMaxSize = kMaxSize;
1284 ConstexprOptional<ReifiedJniSignature<kReifiedMaxSize>>
1285 reified_signature_opt =
1286 MaybeMakeReifiedJniSignature<native_kind, T, fn>();
1287 if (!reified_signature_opt) {
1288 // Assertion handling done by MaybeMakeReifiedJniSignature.
1289 return NullConstexprOptional{};
1290 }
1291
1292 ReifiedJniSignature<kReifiedMaxSize>
1293 reified_signature = reified_signature_opt.value();
1294
1295 JniSignatureDescriptor<kReifiedMaxSize> signature_descriptor;
1296
1297 if (reified_signature.ret.type_finality != kFinal) {
1298 // e.g. jint, jfloatArray, jstring, jclass are ok. jobject, jthrowable, jarray are not.
1299 PARSE_FAILURE("Bad return type. Only unambigous (final) types can be used to infer a signature."); // NOLINT
1300 }
1301 signature_descriptor.ret =
1302 JniDescriptorNode{reified_signature.ret.type_descriptor};
1303
1304 for (size_t i = 0; i < reified_signature.args.size(); ++i) {
1305 const ReifiedJniTypeTrait& arg_trait = reified_signature.args[i];
1306 if (arg_trait.type_finality != kFinal) {
1307 PARSE_FAILURE("Bad parameter type. Only unambigous (final) types can be used to infer a signature."); // NOLINT
1308 }
1309 signature_descriptor.args.push_back(JniDescriptorNode{
1310 arg_trait.type_descriptor});
1311 }
1312
1313 return {signature_descriptor};
1314 }
1315
1316 // Calculate the exact string size that the JNI descriptor will be
1317 // at runtime.
1318 //
1319 // Without this we cannot allocate enough space within static storage
1320 // to fit the compile-time evaluated string.
1321 static constexpr size_t CalculateStringSize() {
1322 ConstexprOptional<JniSignatureDescriptor<kMaxSize>>
1323 signature_descriptor_opt =
1324 FromFunctionType();
1325 if (!signature_descriptor_opt) {
1326 // Assertion handling done by FromFunctionType.
1327 return 0u;
1328 }
1329
1330 JniSignatureDescriptor<kMaxSize> signature_descriptor =
1331 signature_descriptor_opt.value();
1332
1333 size_t acc_size = 1u; // All sigs start with '('.
1334
1335 // Now add every parameter.
1336 for (size_t j = 0; j < signature_descriptor.args.size(); ++j) {
1337 const JniDescriptorNode& arg_descriptor = signature_descriptor.args[j];
1338 // for (const JniDescriptorNode& arg_descriptor : signature_descriptor.args) {
1339 acc_size += arg_descriptor.longy.size();
1340 }
1341
1342 acc_size += 1u; // Add space for ')'.
1343
1344 // Add space for the return value.
1345 acc_size += signature_descriptor.ret.longy.size();
1346
1347 return acc_size;
1348 }
1349
1350 static constexpr size_t kMaxStringSize = CalculateStringSize();
1351 using ConstexprStringDescriptorType = ConstexprArray<char,
1352 kMaxStringSize + 1>;
1353
1354 // Convert the JniSignatureDescriptor we get in FromFunctionType()
1355 // into a flat constexpr char array.
1356 //
1357 // This is done by repeated string concatenation at compile-time.
1358 static constexpr ConstexprStringDescriptorType GetString() {
1359 ConstexprStringDescriptorType c_str{};
1360
1361 ConstexprOptional<JniSignatureDescriptor<kMaxSize>>
1362 signature_descriptor_opt =
1363 FromFunctionType();
1364 if (!signature_descriptor_opt.has_value()) {
1365 // Assertion handling done by FromFunctionType.
1366 c_str[0] = '\0';
1367 return c_str;
1368 }
1369
1370 JniSignatureDescriptor<kMaxSize> signature_descriptor =
1371 signature_descriptor_opt.value();
1372
1373 size_t pos = 0u;
1374 c_str[pos++] = '(';
1375
1376 // Copy all parameter descriptors.
1377 for (size_t j = 0; j < signature_descriptor.args.size(); ++j) {
1378 const JniDescriptorNode& arg_descriptor = signature_descriptor.args[j];
1379 ConstexprStringView longy = arg_descriptor.longy;
1380 for (size_t i = 0; i < longy.size(); ++i) {
1381 c_str[pos++] = longy[i];
1382 }
1383 }
1384
1385 c_str[pos++] = ')';
1386
1387 // Copy return descriptor.
1388 ConstexprStringView longy = signature_descriptor.ret.longy;
1389 for (size_t i = 0; i < longy.size(); ++i) {
1390 c_str[pos++] = longy[i];
1391 }
1392
1393 X_ASSERT(pos == kMaxStringSize);
1394
1395 c_str[pos] = '\0';
1396
1397 return c_str;
1398 }
1399
1400 // Turn a pure constexpr string into one that can be accessed at non-constexpr
1401 // time. Note that the 'static constexpr' storage must be in the scope of a
1402 // function (prior to C++17) to avoid linking errors.
1403 static const char* GetStringAtRuntime() {
1404 static constexpr ConstexprStringDescriptorType str = GetString();
1405 return &str[0];
1406 }
1407 };
1408
1409 // Expression to return JNINativeMethod, performs checking on signature+fn.
1410 #define MAKE_CHECKED_JNI_NATIVE_METHOD(native_kind, name_, signature_, fn) \
1411 ([]() { \
1412 using namespace nativehelper::detail; /* NOLINT(google-build-using-namespace) */ \
1413 static_assert( \
1414 MatchJniDescriptorWithFunctionType<native_kind, \
1415 decltype(fn), \
1416 fn, \
1417 sizeof(signature_)>(signature_),\
1418 "JNI signature doesn't match C++ function type."); \
1419 /* Suppress implicit cast warnings by explicitly casting. */ \
1420 return JNINativeMethod { \
1421 const_cast<decltype(JNINativeMethod::name)>(name_), \
1422 const_cast<decltype(JNINativeMethod::signature)>(signature_), \
1423 reinterpret_cast<void*>(&(fn))}; \
1424 })()
1425
1426 // Expression to return JNINativeMethod, infers signature from fn.
1427 #define MAKE_INFERRED_JNI_NATIVE_METHOD(native_kind, name_, fn) \
1428 ([]() { \
1429 using namespace nativehelper::detail; /* NOLINT(google-build-using-namespace) */ \
1430 /* Suppress implicit cast warnings by explicitly casting. */ \
1431 return JNINativeMethod { \
1432 const_cast<decltype(JNINativeMethod::name)>(name_), \
1433 const_cast<decltype(JNINativeMethod::signature)>( \
1434 InferJniDescriptor<native_kind, \
1435 decltype(fn), \
1436 fn>::GetStringAtRuntime()), \
1437 reinterpret_cast<void*>(&(fn))}; \
1438 })()
1439
1440 } // namespace detail
1441 } // namespace nativehelper
1442
1443 #endif // LIBNATIVEHELPER_PLATFORM_INCLUDE_NATIVEHELPER_DETAIL_SIGNATURE_CHECKER_H_
1444