xref: /aosp_15_r20/external/webrtc/sdk/android/src/jni/jni_generator_helper.h (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 // Do not include this file directly. It's intended to be used only by the JNI
11 // generation script. We are exporting types in strange namespaces in order to
12 // be compatible with the generated code targeted for Chromium.
13 
14 #ifndef SDK_ANDROID_SRC_JNI_JNI_GENERATOR_HELPER_H_
15 #define SDK_ANDROID_SRC_JNI_JNI_GENERATOR_HELPER_H_
16 
17 #include <jni.h>
18 #include <atomic>
19 
20 #include "rtc_base/checks.h"
21 #include "sdk/android/native_api/jni/jni_int_wrapper.h"
22 #include "sdk/android/native_api/jni/scoped_java_ref.h"
23 
24 #define CHECK_CLAZZ(env, jcaller, clazz, ...) RTC_DCHECK(clazz);
25 #define CHECK_NATIVE_PTR(env, jcaller, native_ptr, method_name, ...) \
26   RTC_DCHECK(native_ptr) << method_name;
27 
28 #define BASE_EXPORT
29 #define JNI_REGISTRATION_EXPORT __attribute__((visibility("default")))
30 
31 #if defined(WEBRTC_ARCH_X86)
32 // Dalvik JIT generated code doesn't guarantee 16-byte stack alignment on
33 // x86 - use force_align_arg_pointer to realign the stack at the JNI
34 // boundary. crbug.com/655248
35 #define JNI_GENERATOR_EXPORT \
36   __attribute__((force_align_arg_pointer)) extern "C" JNIEXPORT JNICALL
37 #else
38 #define JNI_GENERATOR_EXPORT extern "C" JNIEXPORT JNICALL
39 #endif
40 
41 #define CHECK_EXCEPTION(jni)        \
42   RTC_CHECK(!jni->ExceptionCheck()) \
43       << (jni->ExceptionDescribe(), jni->ExceptionClear(), "")
44 
45 namespace webrtc {
46 
47 // This function will initialize `atomic_class_id` to contain a global ref to
48 // the given class, and will return that ref on subsequent calls. The caller is
49 // responsible to zero-initialize `atomic_class_id`. It's fine to
50 // simultaneously call this on multiple threads referencing the same
51 // `atomic_method_id`.
52 jclass LazyGetClass(JNIEnv* env,
53                     const char* class_name,
54                     std::atomic<jclass>* atomic_class_id);
55 
56 // This class is a wrapper for JNIEnv Get(Static)MethodID.
57 class MethodID {
58  public:
59   enum Type {
60     TYPE_STATIC,
61     TYPE_INSTANCE,
62   };
63 
64   // This function will initialize `atomic_method_id` to contain a ref to
65   // the given method, and will return that ref on subsequent calls. The caller
66   // is responsible to zero-initialize `atomic_method_id`. It's fine to
67   // simultaneously call this on multiple threads referencing the same
68   // `atomic_method_id`.
69   template <Type type>
70   static jmethodID LazyGet(JNIEnv* env,
71                            jclass clazz,
72                            const char* method_name,
73                            const char* jni_signature,
74                            std::atomic<jmethodID>* atomic_method_id);
75 };
76 
77 }  // namespace webrtc
78 
79 // Re-export relevant classes into the namespaces the script expects.
80 namespace base {
81 namespace android {
82 
83 using webrtc::JavaParamRef;
84 using webrtc::JavaRef;
85 using webrtc::ScopedJavaLocalRef;
86 using webrtc::LazyGetClass;
87 using webrtc::MethodID;
88 
89 }  // namespace android
90 }  // namespace base
91 
92 namespace jni_generator {
CheckException(JNIEnv * env)93 inline void CheckException(JNIEnv* env) {
94   CHECK_EXCEPTION(env);
95 }
96 
97 // A 32 bit number could be an address on stack. Random 64 bit marker on the
98 // stack is much less likely to be present on stack.
99 constexpr uint64_t kJniStackMarkerValue = 0xbdbdef1bebcade1b;
100 
101 // Context about the JNI call with exception checked to be stored in stack.
102 struct BASE_EXPORT JniJavaCallContextUnchecked {
JniJavaCallContextUncheckedJniJavaCallContextUnchecked103   inline JniJavaCallContextUnchecked() {
104 // TODO(ssid): Implement for other architectures.
105 #if defined(__arm__) || defined(__aarch64__)
106     // This assumes that this method does not increment the stack pointer.
107     asm volatile("mov %0, sp" : "=r"(sp));
108 #else
109     sp = 0;
110 #endif
111   }
112 
113   // Force no inline to reduce code size.
114   template <base::android::MethodID::Type type>
InitJniJavaCallContextUnchecked115   void Init(JNIEnv* env,
116             jclass clazz,
117             const char* method_name,
118             const char* jni_signature,
119             std::atomic<jmethodID>* atomic_method_id) {
120     env1 = env;
121 
122     // Make sure compiler doesn't optimize out the assignment.
123     memcpy(&marker, &kJniStackMarkerValue, sizeof(kJniStackMarkerValue));
124     // Gets PC of the calling function.
125     pc = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
126 
127     method_id = base::android::MethodID::LazyGet<type>(
128         env, clazz, method_name, jni_signature, atomic_method_id);
129   }
130 
~JniJavaCallContextUncheckedJniJavaCallContextUnchecked131   ~JniJavaCallContextUnchecked() {
132     // Reset so that spurious marker finds are avoided.
133     memset(&marker, 0, sizeof(marker));
134   }
135 
136   uint64_t marker;
137   uintptr_t sp;
138   uintptr_t pc;
139 
140   JNIEnv* env1;
141   jmethodID method_id;
142 };
143 
144 // Context about the JNI call with exception unchecked to be stored in stack.
145 struct BASE_EXPORT JniJavaCallContextChecked {
146   // Force no inline to reduce code size.
147   template <base::android::MethodID::Type type>
InitJniJavaCallContextChecked148   void Init(JNIEnv* env,
149             jclass clazz,
150             const char* method_name,
151             const char* jni_signature,
152             std::atomic<jmethodID>* atomic_method_id) {
153     base.Init<type>(env, clazz, method_name, jni_signature, atomic_method_id);
154     // Reset `pc` to correct caller.
155     base.pc = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
156   }
157 
~JniJavaCallContextCheckedJniJavaCallContextChecked158   ~JniJavaCallContextChecked() { jni_generator::CheckException(base.env1); }
159 
160   JniJavaCallContextUnchecked base;
161 };
162 
163 static_assert(sizeof(JniJavaCallContextChecked) ==
164                   sizeof(JniJavaCallContextUnchecked),
165               "Stack unwinder cannot work with structs of different sizes.");
166 }  // namespace jni_generator
167 
168 #endif  // SDK_ANDROID_SRC_JNI_JNI_GENERATOR_HELPER_H_
169