@lucent-lang/lucent 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.plugin.js +92 -0
- package/bin/lucent.cjs +16 -0
- package/core/index.cjs +11 -0
- package/dist/bench-CR3E7e2e.js +304 -0
- package/dist/build-CAIrRSYA.js +7 -0
- package/dist/build-check-QozuNJm5.js +92 -0
- package/dist/check-1SCmYlqA.js +7 -0
- package/dist/clean-BVwpbJ5K.js +35 -0
- package/dist/cli.js +406 -0
- package/dist/codes-CIuwXt1S.js +260 -0
- package/dist/compiler.js +4 -0
- package/dist/confirm-BYF5eY3q.js +34 -0
- package/dist/dashboard-B-NzjjaN.js +91 -0
- package/dist/dev-BYWMP7oC.js +221 -0
- package/dist/diagnostic-BM9sXmwl.js +30 -0
- package/dist/diff-BTDo3Sro.js +44 -0
- package/dist/doctor-C0PrfWT6.js +230 -0
- package/dist/doctor-DdR4JnVc.js +30 -0
- package/dist/explain--vV1oaRl.js +35 -0
- package/dist/format-D9fnIMEx.js +53 -0
- package/dist/init-gIabu1S_.js +207 -0
- package/dist/live-steps-BGcWFM9M.js +79 -0
- package/dist/new-module-C2mtb0op.js +66 -0
- package/dist/package-manager-BnxwCySi.js +30 -0
- package/dist/package.json +3 -0
- package/dist/packages-HyskKIsY.js +150 -0
- package/dist/pipeline-CH7-2cxu.js +232 -0
- package/dist/project-D8tEet0k.js +150 -0
- package/dist/sdk-coverage-BoW_3Efd.js +64 -0
- package/dist/sdk-prefetch-mo04JDZV.js +89 -0
- package/dist/sdk-search-Iv4K3-8a.js +124 -0
- package/dist/sdk-show-CkrdlqlP.js +74 -0
- package/dist/sdks-Xoyiy2E4.js +28 -0
- package/dist/src-BD7f1_aZ.js +11493 -0
- package/dist/steps--_h-FFS7.js +24 -0
- package/dist/theme-uv5bc0aK.js +134 -0
- package/dist/tsconfig-S4uyLNzf.js +59 -0
- package/dist/version-bqpIO5sP.js +26 -0
- package/gradle/lucent-classpath.gradle +38 -0
- package/gradle/lucent-classpath.init.gradle +6 -0
- package/gradle/lucent.gradle +24 -0
- package/lib/core.cjs +106 -0
- package/lib/globals.d.ts +36 -0
- package/lib/sdk/android.d.ts +8 -0
- package/lib/sdk/core.d.ts +23 -0
- package/lib/sdk/ios.d.ts +33 -0
- package/lib/sdk/platform.d.ts +8 -0
- package/lib/sdk/thread.d.ts +8 -0
- package/metro/index.cjs +72 -0
- package/metro/index.d.ts +6 -0
- package/metro/transformer.cjs +70 -0
- package/package.json +61 -0
- package/runtime/cpp/lucent/abort.cpp +61 -0
- package/runtime/cpp/lucent/abort.h +54 -0
- package/runtime/cpp/lucent/array.h +442 -0
- package/runtime/cpp/lucent/async.h +236 -0
- package/runtime/cpp/lucent/builtins.cpp +163 -0
- package/runtime/cpp/lucent/bytes.h +129 -0
- package/runtime/cpp/lucent/console.h +17 -0
- package/runtime/cpp/lucent/core.h +129 -0
- package/runtime/cpp/lucent/date.cpp +441 -0
- package/runtime/cpp/lucent/date.h +82 -0
- package/runtime/cpp/lucent/equality.h +87 -0
- package/runtime/cpp/lucent/function.h +61 -0
- package/runtime/cpp/lucent/generator.h +244 -0
- package/runtime/cpp/lucent/helpers.h +226 -0
- package/runtime/cpp/lucent/jserror.cpp +51 -0
- package/runtime/cpp/lucent/jserror.h +66 -0
- package/runtime/cpp/lucent/jsi/convert.cpp +202 -0
- package/runtime/cpp/lucent/jsi/convert.h +612 -0
- package/runtime/cpp/lucent/jsi/host.cpp +322 -0
- package/runtime/cpp/lucent/jsi/host.h +166 -0
- package/runtime/cpp/lucent/json.cpp +243 -0
- package/runtime/cpp/lucent/json.h +164 -0
- package/runtime/cpp/lucent/json_parse.h +138 -0
- package/runtime/cpp/lucent/jsstring.cpp +874 -0
- package/runtime/cpp/lucent/jsstring.h +152 -0
- package/runtime/cpp/lucent/lucent.h +24 -0
- package/runtime/cpp/lucent/map.h +344 -0
- package/runtime/cpp/lucent/native.cpp +76 -0
- package/runtime/cpp/lucent/native.h +116 -0
- package/runtime/cpp/lucent/number.cpp +545 -0
- package/runtime/cpp/lucent/number.h +190 -0
- package/runtime/cpp/lucent/ops.h +165 -0
- package/runtime/cpp/lucent/platform/android.cpp +437 -0
- package/runtime/cpp/lucent/platform/android.h +117 -0
- package/runtime/cpp/lucent/platform/ios.h +297 -0
- package/runtime/cpp/lucent/platform/ios.mm +31 -0
- package/runtime/cpp/lucent/regexp.cpp +440 -0
- package/runtime/cpp/lucent/regexp.h +107 -0
- package/runtime/cpp/lucent/scheduler.cpp +153 -0
- package/runtime/cpp/lucent/scheduler.h +160 -0
- package/runtime/cpp/lucent/unicode_data.inc +4051 -0
- package/runtime/cpp/rn/LucentModule.cpp +35 -0
- package/runtime/cpp/rn/LucentModule.h +37 -0
- package/runtime/cpp/third_party/quickjs/LICENSE +22 -0
- package/runtime/cpp/third_party/quickjs/README.md +11 -0
- package/runtime/cpp/third_party/quickjs/cutils.c +641 -0
- package/runtime/cpp/third_party/quickjs/cutils.h +457 -0
- package/runtime/cpp/third_party/quickjs/libregexp-opcode.h +73 -0
- package/runtime/cpp/third_party/quickjs/libregexp.c +3448 -0
- package/runtime/cpp/third_party/quickjs/libregexp.h +65 -0
- package/runtime/cpp/third_party/quickjs/libunicode-table.h +5206 -0
- package/runtime/cpp/third_party/quickjs/libunicode.c +2124 -0
- package/runtime/cpp/third_party/quickjs/libunicode.h +196 -0
- package/runtime/js/index.d.ts +7 -0
- package/runtime/js/index.js +60 -0
- package/runtime/native/LucentNative.podspec +28 -0
- package/runtime/native/android/CMakeLists.txt +51 -0
- package/runtime/native/android/build.gradle +22 -0
- package/runtime/native/android/include/lucentnative.h +16 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +25 -0
- package/runtime/native/android/src/main/java/dev/lucent/NativeProxy.java +132 -0
- package/runtime/native/ios/LucentRegistration.mm +23 -0
- package/runtime/native/package.json +6 -0
- package/runtime/native/react-native.config.js +19 -0
- package/runtime/test/jsi/harness.cpp +181 -0
- package/schemas/build.schema.json +152 -0
- package/schemas/check.schema.json +74 -0
- package/schemas/doctor.schema.json +40 -0
- package/schemas/lucent.schema.json +43 -0
- package/schemas/sdk-prefetch.schema.json +37 -0
- package/schemas/sdk-search.schema.json +54 -0
- package/ts-plugin/index.d.ts +4 -0
- package/ts-plugin/index.js +99 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
// Lucent runtime — Android: JNI environment, classes, errors, the main
|
|
2
|
+
// Looper. Built only into Android apps (fbjni comes with React Native).
|
|
3
|
+
#ifdef __ANDROID__
|
|
4
|
+
|
|
5
|
+
#include "android.h"
|
|
6
|
+
|
|
7
|
+
#include <fbjni/NativeRunnable.h>
|
|
8
|
+
#include <fbjni/fbjni.h>
|
|
9
|
+
#include <sys/system_properties.h>
|
|
10
|
+
|
|
11
|
+
#include <cstdlib>
|
|
12
|
+
#include <mutex>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <unordered_map>
|
|
15
|
+
|
|
16
|
+
namespace lucent::jni {
|
|
17
|
+
|
|
18
|
+
JNIEnv* env() { return facebook::jni::Environment::ensureCurrentThreadIsAttached(); }
|
|
19
|
+
|
|
20
|
+
namespace {
|
|
21
|
+
|
|
22
|
+
[[noreturn]] void failWith(const char* name, const std::string& message) {
|
|
23
|
+
throw Exception(makeError(String::fromLatin1(name), String::fromUtf8(message)));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
jclass findClassWithAppLoader(JNIEnv* e, const char* name) {
|
|
27
|
+
// Threads the app did not start see only the system class loader; the
|
|
28
|
+
// Application's loader sees AndroidX, Play services and the app's own classes.
|
|
29
|
+
jobject app = unwrap(appContext());
|
|
30
|
+
jclass contextCls = e->FindClass("android/content/Context");
|
|
31
|
+
jmethodID getLoader = e->GetMethodID(contextCls, "getClassLoader", "()Ljava/lang/ClassLoader;");
|
|
32
|
+
jobject loader = e->CallObjectMethod(app, getLoader);
|
|
33
|
+
jclass loaderCls = e->FindClass("java/lang/ClassLoader");
|
|
34
|
+
jmethodID loadClass = e->GetMethodID(loaderCls, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
|
35
|
+
std::string dotted(name);
|
|
36
|
+
for (char& c : dotted) {
|
|
37
|
+
if (c == '/') c = '.';
|
|
38
|
+
}
|
|
39
|
+
jstring jname = e->NewStringUTF(dotted.c_str());
|
|
40
|
+
auto cls = static_cast<jclass>(e->CallObjectMethod(loader, loadClass, jname));
|
|
41
|
+
check(e);
|
|
42
|
+
return cls;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
bool sameObject(void* a, void* b) { return env()->IsSameObject(static_cast<jobject>(a), static_cast<jobject>(b)); }
|
|
46
|
+
|
|
47
|
+
void releaseGlobal(void* ref) { env()->DeleteGlobalRef(static_cast<jobject>(ref)); }
|
|
48
|
+
|
|
49
|
+
} // namespace
|
|
50
|
+
|
|
51
|
+
jclass findClass(const char* name) {
|
|
52
|
+
static std::mutex m;
|
|
53
|
+
static auto* classes = new std::unordered_map<std::string, jclass>();
|
|
54
|
+
{
|
|
55
|
+
std::lock_guard<std::mutex> g(m);
|
|
56
|
+
auto it = classes->find(name);
|
|
57
|
+
if (it != classes->end()) return it->second;
|
|
58
|
+
}
|
|
59
|
+
JNIEnv* e = env();
|
|
60
|
+
jclass local = e->FindClass(name);
|
|
61
|
+
if (!local) {
|
|
62
|
+
e->ExceptionClear();
|
|
63
|
+
local = findClassWithAppLoader(e, name);
|
|
64
|
+
}
|
|
65
|
+
if (!local) failWith("TypeError", std::string("Java class not found: ") + name);
|
|
66
|
+
auto global = static_cast<jclass>(e->NewGlobalRef(local));
|
|
67
|
+
e->DeleteLocalRef(local);
|
|
68
|
+
std::lock_guard<std::mutex> g(m);
|
|
69
|
+
classes->emplace(name, global);
|
|
70
|
+
return global;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#define LUCENT_JNI_ID(fn, kind) \
|
|
74
|
+
JNIEnv* e = env(); \
|
|
75
|
+
auto id = e->fn(cls, name, sig); \
|
|
76
|
+
if (!id) { \
|
|
77
|
+
e->ExceptionClear(); \
|
|
78
|
+
failWith("TypeError", std::string("Java " kind " not found: ") + name + " " + sig); \
|
|
79
|
+
} \
|
|
80
|
+
return id;
|
|
81
|
+
|
|
82
|
+
jmethodID method(jclass cls, const char* name, const char* sig) { LUCENT_JNI_ID(GetMethodID, "method") }
|
|
83
|
+
jmethodID staticMethod(jclass cls, const char* name, const char* sig) { LUCENT_JNI_ID(GetStaticMethodID, "method") }
|
|
84
|
+
jfieldID field(jclass cls, const char* name, const char* sig) { LUCENT_JNI_ID(GetFieldID, "field") }
|
|
85
|
+
jfieldID staticField(jclass cls, const char* name, const char* sig) { LUCENT_JNI_ID(GetStaticFieldID, "field") }
|
|
86
|
+
|
|
87
|
+
void rethrowPending(JNIEnv* e) {
|
|
88
|
+
jthrowable t = e->ExceptionOccurred();
|
|
89
|
+
e->ExceptionClear();
|
|
90
|
+
jclass objectCls = e->FindClass("java/lang/Object");
|
|
91
|
+
jobject cls = e->CallObjectMethod(t, e->GetMethodID(objectCls, "getClass", "()Ljava/lang/Class;"));
|
|
92
|
+
jclass classCls = e->FindClass("java/lang/Class");
|
|
93
|
+
auto name = static_cast<jstring>(e->CallObjectMethod(cls, e->GetMethodID(classCls, "getName", "()Ljava/lang/String;")));
|
|
94
|
+
jclass throwableCls = e->FindClass("java/lang/Throwable");
|
|
95
|
+
auto message = static_cast<jstring>(e->CallObjectMethod(t, e->GetMethodID(throwableCls, "getMessage", "()Ljava/lang/String;")));
|
|
96
|
+
e->ExceptionClear();
|
|
97
|
+
String className = name ? fromJString(e, name, "") : String::fromLatin1("java.lang.Throwable");
|
|
98
|
+
Error err = makeError(String::fromLatin1("Error"), message ? fromJString(e, message, "") : className);
|
|
99
|
+
err->code = className;
|
|
100
|
+
throw Exception(err);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
NativeRef wrap(JNIEnv* e, jobject local, const char* what) {
|
|
104
|
+
if (!local) failWith("TypeError", std::string(what) + " returned null");
|
|
105
|
+
jobject global = e->NewGlobalRef(local);
|
|
106
|
+
e->DeleteLocalRef(local);
|
|
107
|
+
return NativeRef(global, releaseGlobal, sameObject);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
Opt<NativeRef> wrapOpt(JNIEnv* e, jobject local) {
|
|
111
|
+
if (!local) return Opt<NativeRef>(null);
|
|
112
|
+
return wrap(e, local, "");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
jstring toJString(JNIEnv* e, const String& s) {
|
|
116
|
+
std::u16string units = s.toUtf16();
|
|
117
|
+
return e->NewString(reinterpret_cast<const jchar*>(units.data()), static_cast<jsize>(units.size()));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
String fromJString(JNIEnv* e, jstring s, const char* what) {
|
|
121
|
+
if (!s) failWith("TypeError", std::string(what) + " returned null");
|
|
122
|
+
jsize n = e->GetStringLength(s);
|
|
123
|
+
std::u16string units(static_cast<size_t>(n), u'\0');
|
|
124
|
+
e->GetStringRegion(s, 0, n, reinterpret_cast<jchar*>(units.data()));
|
|
125
|
+
return String::fromUtf16(units);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
Opt<String> fromJStringOpt(JNIEnv* e, jstring s) {
|
|
129
|
+
if (!s) return Opt<String>(null);
|
|
130
|
+
return fromJString(e, s, "");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
String charSequenceToString(JNIEnv* e, jobject s, const char* what) {
|
|
134
|
+
if (!s) failWith("TypeError", std::string(what) + " returned null");
|
|
135
|
+
static jmethodID toString = method(findClass("java/lang/CharSequence"), "toString", "()Ljava/lang/String;");
|
|
136
|
+
auto str = static_cast<jstring>(e->CallObjectMethod(s, toString));
|
|
137
|
+
check(e);
|
|
138
|
+
return fromJString(e, str, what);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
Opt<String> charSequenceToStringOpt(JNIEnv* e, jobject s) {
|
|
142
|
+
if (!s) return Opt<String>(null);
|
|
143
|
+
return charSequenceToString(e, s, "");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
jbyteArray toByteArray(JNIEnv* e, const Bytes& b) {
|
|
147
|
+
auto n = static_cast<jsize>(b.size());
|
|
148
|
+
jbyteArray out = e->NewByteArray(n);
|
|
149
|
+
e->SetByteArrayRegion(out, 0, n, reinterpret_cast<const jbyte*>(b.data()));
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
jobjectArray toStringArray(JNIEnv* e, const Array<String>& a) {
|
|
154
|
+
auto n = static_cast<jsize>(a.size());
|
|
155
|
+
jobjectArray out = e->NewObjectArray(n, findClass("java/lang/String"), nullptr);
|
|
156
|
+
for (jsize i = 0; i < n; i++) {
|
|
157
|
+
jstring s = toJString(e, a.at(static_cast<size_t>(i)));
|
|
158
|
+
e->SetObjectArrayElement(out, i, s);
|
|
159
|
+
e->DeleteLocalRef(s);
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
Array<double> fromLongArray(JNIEnv* e, jlongArray a, const char* what) {
|
|
165
|
+
if (!a) failWith("TypeError", std::string(what) + " returned null");
|
|
166
|
+
jsize n = e->GetArrayLength(a);
|
|
167
|
+
std::vector<jlong> buf(static_cast<size_t>(n));
|
|
168
|
+
e->GetLongArrayRegion(a, 0, n, buf.data());
|
|
169
|
+
Array<double> out;
|
|
170
|
+
for (jlong v : buf) out.push(static_cast<double>(v));
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
Array<double> fromIntArray(JNIEnv* e, jintArray a, const char* what) {
|
|
175
|
+
if (!a) failWith("TypeError", std::string(what) + " returned null");
|
|
176
|
+
jsize n = e->GetArrayLength(a);
|
|
177
|
+
std::vector<jint> buf(static_cast<size_t>(n));
|
|
178
|
+
e->GetIntArrayRegion(a, 0, n, buf.data());
|
|
179
|
+
Array<double> out;
|
|
180
|
+
for (jint v : buf) out.push(static_cast<double>(v));
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
Bytes fromByteArray(JNIEnv* e, jbyteArray a, const char* what) {
|
|
185
|
+
if (!a) failWith("TypeError", std::string(what) + " returned null");
|
|
186
|
+
jsize n = e->GetArrayLength(a);
|
|
187
|
+
std::vector<uint8_t> buf(static_cast<size_t>(n));
|
|
188
|
+
e->GetByteArrayRegion(a, 0, n, reinterpret_cast<jbyte*>(buf.data()));
|
|
189
|
+
return Bytes(std::move(buf));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
Array<String> fromStringArray(JNIEnv* e, jobjectArray a, const char* what) {
|
|
193
|
+
if (!a) failWith("TypeError", std::string(what) + " returned null");
|
|
194
|
+
jsize n = e->GetArrayLength(a);
|
|
195
|
+
Array<String> out;
|
|
196
|
+
for (jsize i = 0; i < n; i++) {
|
|
197
|
+
auto s = static_cast<jstring>(e->GetObjectArrayElement(a, i));
|
|
198
|
+
// Null elements have no place in string[]: read them as empty strings.
|
|
199
|
+
out.push(s ? fromJString(e, s, what) : String());
|
|
200
|
+
if (s) e->DeleteLocalRef(s);
|
|
201
|
+
}
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
jlongArray toLongArray(JNIEnv* e, const Array<double>& a) {
|
|
206
|
+
auto n = static_cast<jsize>(a.size());
|
|
207
|
+
jlongArray out = e->NewLongArray(n);
|
|
208
|
+
std::vector<jlong> buf(static_cast<size_t>(n));
|
|
209
|
+
for (jsize i = 0; i < n; i++) buf[static_cast<size_t>(i)] = static_cast<jlong>(a.at(static_cast<size_t>(i)));
|
|
210
|
+
e->SetLongArrayRegion(out, 0, n, buf.data());
|
|
211
|
+
return out;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
jintArray toIntArray(JNIEnv* e, const Array<double>& a) {
|
|
215
|
+
auto n = static_cast<jsize>(a.size());
|
|
216
|
+
jintArray out = e->NewIntArray(n);
|
|
217
|
+
std::vector<jint> buf(static_cast<size_t>(n));
|
|
218
|
+
for (jsize i = 0; i < n; i++) buf[static_cast<size_t>(i)] = static_cast<jint>(toInt32(a.at(static_cast<size_t>(i))));
|
|
219
|
+
e->SetIntArrayRegion(out, 0, n, buf.data());
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// --- proxies ---------------------------------------------------------------------------
|
|
224
|
+
|
|
225
|
+
namespace {
|
|
226
|
+
|
|
227
|
+
struct ProxyTarget {
|
|
228
|
+
std::pair<const void*, std::string> key;
|
|
229
|
+
std::unordered_map<std::string, ProxyMethod> methods;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
struct ProxyEntry {
|
|
233
|
+
jweak ref = nullptr;
|
|
234
|
+
ProxyTarget* target = nullptr;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
struct KeyHash {
|
|
238
|
+
size_t operator()(const std::pair<const void*, std::string>& k) const { return std::hash<const void*>()(k.first) ^ std::hash<std::string>()(k.second); }
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
std::mutex proxiesMutex;
|
|
242
|
+
std::unordered_map<std::pair<const void*, std::string>, ProxyEntry, KeyHash>& proxies() {
|
|
243
|
+
static auto* m = new std::unordered_map<std::pair<const void*, std::string>, ProxyEntry, KeyHash>();
|
|
244
|
+
return *m;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
jobject JNICALL proxyCall(JNIEnv* e, jclass, jlong handle, jstring method, jobjectArray args) {
|
|
248
|
+
auto* t = reinterpret_cast<ProxyTarget*>(handle);
|
|
249
|
+
const char* chars = e->GetStringUTFChars(method, nullptr);
|
|
250
|
+
std::string name(chars);
|
|
251
|
+
e->ReleaseStringUTFChars(method, chars);
|
|
252
|
+
auto it = t->methods.find(name);
|
|
253
|
+
if (it == t->methods.end()) {
|
|
254
|
+
e->ThrowNew(e->FindClass("java/lang/AbstractMethodError"), ("Lucent does not implement " + name).c_str());
|
|
255
|
+
return nullptr;
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
return it->second(e, args);
|
|
259
|
+
} catch (...) {
|
|
260
|
+
reportUncaught(std::current_exception(), "Java callback");
|
|
261
|
+
return nullptr;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
jboolean JNICALL proxyHas(JNIEnv* e, jclass, jlong handle, jstring key) {
|
|
266
|
+
auto* t = reinterpret_cast<ProxyTarget*>(handle);
|
|
267
|
+
const char* chars = e->GetStringUTFChars(key, nullptr);
|
|
268
|
+
bool found = t->methods.count(chars) > 0;
|
|
269
|
+
e->ReleaseStringUTFChars(key, chars);
|
|
270
|
+
return found ? JNI_TRUE : JNI_FALSE;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
void JNICALL proxyRelease(JNIEnv* e, jclass, jlong handle) {
|
|
274
|
+
auto* t = reinterpret_cast<ProxyTarget*>(handle);
|
|
275
|
+
{
|
|
276
|
+
std::lock_guard<std::mutex> g(proxiesMutex);
|
|
277
|
+
auto it = proxies().find(t->key);
|
|
278
|
+
if (it != proxies().end() && it->second.target == t) {
|
|
279
|
+
e->DeleteWeakGlobalRef(it->second.ref);
|
|
280
|
+
proxies().erase(it);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// Called on Java's finalizer thread: what the methods captured (Lucent
|
|
284
|
+
// values) is released on the Lucent thread.
|
|
285
|
+
postCallback([t] { delete t; });
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
jclass nativeProxyClass(JNIEnv* e) {
|
|
289
|
+
static jclass cls = [&] {
|
|
290
|
+
jclass c = findClass("dev/lucent/NativeProxy");
|
|
291
|
+
JNINativeMethod natives[] = {
|
|
292
|
+
{const_cast<char*>("has"), const_cast<char*>("(JLjava/lang/String;)Z"), reinterpret_cast<void*>(proxyHas)},
|
|
293
|
+
{const_cast<char*>("call"), const_cast<char*>("(JLjava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;"), reinterpret_cast<void*>(proxyCall)},
|
|
294
|
+
{const_cast<char*>("release"), const_cast<char*>("(J)V"), reinterpret_cast<void*>(proxyRelease)},
|
|
295
|
+
};
|
|
296
|
+
e->RegisterNatives(c, natives, 3);
|
|
297
|
+
check(e);
|
|
298
|
+
return c;
|
|
299
|
+
}();
|
|
300
|
+
return cls;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
jobject boxWith(JNIEnv* e, const char* cls, const char* sig, jvalue v) {
|
|
304
|
+
jclass c = findClass(cls);
|
|
305
|
+
jmethodID valueOf = staticMethod(c, "valueOf", sig);
|
|
306
|
+
jobject r = e->CallStaticObjectMethodA(c, valueOf, &v);
|
|
307
|
+
check(e);
|
|
308
|
+
return r;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
} // namespace
|
|
312
|
+
|
|
313
|
+
namespace {
|
|
314
|
+
|
|
315
|
+
/** The Java object for `key`, made by `make` (given the handle) unless Java still holds one. */
|
|
316
|
+
jobject cachedJavaObject(JNIEnv* e, std::pair<const void*, std::string> key, std::initializer_list<std::pair<const char*, ProxyMethod>> methods, const std::function<jobject(jlong)>& make) {
|
|
317
|
+
nativeProxyClass(e);
|
|
318
|
+
{
|
|
319
|
+
std::lock_guard<std::mutex> g(proxiesMutex);
|
|
320
|
+
auto it = proxies().find(key);
|
|
321
|
+
if (it != proxies().end()) {
|
|
322
|
+
jobject local = e->NewLocalRef(it->second.ref);
|
|
323
|
+
if (local) return local;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
auto* t = new ProxyTarget{key, {}};
|
|
327
|
+
for (const auto& [name, fn] : methods) t->methods.emplace(name, fn);
|
|
328
|
+
jobject o = make(reinterpret_cast<jlong>(t));
|
|
329
|
+
if (e->ExceptionCheck()) {
|
|
330
|
+
delete t;
|
|
331
|
+
rethrowPending(e);
|
|
332
|
+
}
|
|
333
|
+
std::lock_guard<std::mutex> g(proxiesMutex);
|
|
334
|
+
ProxyEntry& slot = proxies()[key];
|
|
335
|
+
if (slot.ref) e->DeleteWeakGlobalRef(slot.ref);
|
|
336
|
+
slot = {e->NewWeakGlobalRef(o), t};
|
|
337
|
+
return o;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
} // namespace
|
|
341
|
+
|
|
342
|
+
jobject proxyFor(JNIEnv* e, const char* iface, const void* identity, std::initializer_list<std::pair<const char*, ProxyMethod>> methods) {
|
|
343
|
+
return cachedJavaObject(e, {identity, iface}, methods, [&](jlong handle) {
|
|
344
|
+
jclass cls = nativeProxyClass(e);
|
|
345
|
+
static jmethodID create = staticMethod(cls, "create", "(Ljava/lang/Class;J)Ljava/lang/Object;");
|
|
346
|
+
return e->CallStaticObjectMethod(cls, create, findClass(iface), handle);
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
jobject subclassFor(JNIEnv* e, const char* cls, const void* identity, std::initializer_list<std::pair<const char*, ProxyMethod>> methods) {
|
|
351
|
+
return cachedJavaObject(e, {identity, cls}, methods, [&](jlong handle) {
|
|
352
|
+
jclass c = findClass(cls);
|
|
353
|
+
return e->NewObject(c, method(c, "<init>", "(J)V"), handle);
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
jobject arg(JNIEnv* e, jobjectArray args, int i) { return e->GetObjectArrayElement(args, i); }
|
|
358
|
+
|
|
359
|
+
double unboxNumber(JNIEnv* e, jobject boxed) {
|
|
360
|
+
static jmethodID doubleValue = method(findClass("java/lang/Number"), "doubleValue", "()D");
|
|
361
|
+
double v = e->CallDoubleMethod(boxed, doubleValue);
|
|
362
|
+
check(e);
|
|
363
|
+
return v;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
bool unboxBoolean(JNIEnv* e, jobject boxed) {
|
|
367
|
+
static jmethodID booleanValue = method(findClass("java/lang/Boolean"), "booleanValue", "()Z");
|
|
368
|
+
bool v = e->CallBooleanMethod(boxed, booleanValue) == JNI_TRUE;
|
|
369
|
+
check(e);
|
|
370
|
+
return v;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
jobject boxInt(JNIEnv* e, jint v) { return boxWith(e, "java/lang/Integer", "(I)Ljava/lang/Integer;", jvalue{.i = v}); }
|
|
374
|
+
jobject boxLong(JNIEnv* e, jlong v) { return boxWith(e, "java/lang/Long", "(J)Ljava/lang/Long;", jvalue{.j = v}); }
|
|
375
|
+
jobject boxDouble(JNIEnv* e, jdouble v) { return boxWith(e, "java/lang/Double", "(D)Ljava/lang/Double;", jvalue{.d = v}); }
|
|
376
|
+
jobject boxFloat(JNIEnv* e, jfloat v) { return boxWith(e, "java/lang/Float", "(F)Ljava/lang/Float;", jvalue{.f = v}); }
|
|
377
|
+
jobject boxBoolean(JNIEnv* e, bool v) { return boxWith(e, "java/lang/Boolean", "(Z)Ljava/lang/Boolean;", jvalue{.z = static_cast<jboolean>(v ? JNI_TRUE : JNI_FALSE)}); }
|
|
378
|
+
|
|
379
|
+
NativeRef appContext() {
|
|
380
|
+
static NativeRef* app = [] {
|
|
381
|
+
JNIEnv* e = env();
|
|
382
|
+
jclass cls = e->FindClass("android/app/ActivityThread");
|
|
383
|
+
check(e);
|
|
384
|
+
jmethodID current = e->GetStaticMethodID(cls, "currentApplication", "()Landroid/app/Application;");
|
|
385
|
+
check(e);
|
|
386
|
+
return new NativeRef(wrap(e, e->CallStaticObjectMethod(cls, current), "ActivityThread.currentApplication()"));
|
|
387
|
+
}();
|
|
388
|
+
return *app;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
bool available(double api) {
|
|
392
|
+
static int level = [] {
|
|
393
|
+
char value[PROP_VALUE_MAX] = {0};
|
|
394
|
+
__system_property_get("ro.build.version.sdk", value);
|
|
395
|
+
return std::atoi(value);
|
|
396
|
+
}();
|
|
397
|
+
return level >= api;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
} // namespace lucent::jni
|
|
401
|
+
|
|
402
|
+
namespace lucent {
|
|
403
|
+
|
|
404
|
+
void postToMain(std::function<void()> job) {
|
|
405
|
+
using facebook::jni::JNativeRunnable;
|
|
406
|
+
JNIEnv* e = jni::env();
|
|
407
|
+
static jobject handler = [e] {
|
|
408
|
+
jclass looperCls = jni::findClass("android/os/Looper");
|
|
409
|
+
jobject looper = e->CallStaticObjectMethod(looperCls, jni::staticMethod(looperCls, "getMainLooper", "()Landroid/os/Looper;"));
|
|
410
|
+
jclass handlerCls = jni::findClass("android/os/Handler");
|
|
411
|
+
jobject h = e->NewObject(handlerCls, jni::method(handlerCls, "<init>", "(Landroid/os/Looper;)V"), looper);
|
|
412
|
+
jni::check(e);
|
|
413
|
+
return e->NewGlobalRef(h);
|
|
414
|
+
}();
|
|
415
|
+
static jmethodID post = jni::method(jni::findClass("android/os/Handler"), "post", "(Ljava/lang/Runnable;)Z");
|
|
416
|
+
// NativeRunnable and the HybridData behind it are app classes, which the
|
|
417
|
+
// Lucent thread's class loader cannot see: create and post the runnable
|
|
418
|
+
// with the app's loader.
|
|
419
|
+
facebook::jni::ThreadScope::WithClassLoader([&] {
|
|
420
|
+
auto runnable = JNativeRunnable::newObjectCxxArgs(std::move(job));
|
|
421
|
+
e->CallBooleanMethod(handler, post, runnable.get());
|
|
422
|
+
});
|
|
423
|
+
jni::check(e);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
bool onMainThread() {
|
|
427
|
+
JNIEnv* e = jni::env();
|
|
428
|
+
jclass looperCls = jni::findClass("android/os/Looper");
|
|
429
|
+
static jmethodID mainLooper = jni::staticMethod(looperCls, "getMainLooper", "()Landroid/os/Looper;");
|
|
430
|
+
static jmethodID myLooper = jni::staticMethod(looperCls, "myLooper", "()Landroid/os/Looper;");
|
|
431
|
+
jni::LocalFrame frame(e);
|
|
432
|
+
return e->IsSameObject(e->CallStaticObjectMethod(looperCls, mainLooper), e->CallStaticObjectMethod(looperCls, myLooper));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
} // namespace lucent
|
|
436
|
+
|
|
437
|
+
#endif // __ANDROID__
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Lucent runtime — JNI glue helpers for *.android.lucent.ts units.
|
|
2
|
+
#pragma once
|
|
3
|
+
|
|
4
|
+
#include <jni.h>
|
|
5
|
+
|
|
6
|
+
#include <functional>
|
|
7
|
+
#include <initializer_list>
|
|
8
|
+
#include <utility>
|
|
9
|
+
|
|
10
|
+
#include "../array.h"
|
|
11
|
+
#include "../bytes.h"
|
|
12
|
+
#include "../jsstring.h"
|
|
13
|
+
#include "../native.h"
|
|
14
|
+
|
|
15
|
+
namespace lucent::jni {
|
|
16
|
+
|
|
17
|
+
/// This thread's JNIEnv, attaching it to the VM if needed.
|
|
18
|
+
JNIEnv* env();
|
|
19
|
+
/// A global reference to a class, from the app's class loader when the
|
|
20
|
+
/// system one cannot see it. Throws for unknown classes.
|
|
21
|
+
jclass findClass(const char* name);
|
|
22
|
+
jmethodID method(jclass cls, const char* name, const char* sig);
|
|
23
|
+
jmethodID staticMethod(jclass cls, const char* name, const char* sig);
|
|
24
|
+
jfieldID field(jclass cls, const char* name, const char* sig);
|
|
25
|
+
jfieldID staticField(jclass cls, const char* name, const char* sig);
|
|
26
|
+
|
|
27
|
+
/// Throws a pending Java exception as a Lucent error whose code is the
|
|
28
|
+
/// exception's class name (`java.lang.IllegalArgumentException`).
|
|
29
|
+
void rethrowPending(JNIEnv* env);
|
|
30
|
+
inline void check(JNIEnv* env) {
|
|
31
|
+
if (env->ExceptionCheck()) rethrowPending(env);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// A Lucent reference to `local` (a global ref; the local ref is deleted).
|
|
35
|
+
/// Throws TypeError for null.
|
|
36
|
+
NativeRef wrap(JNIEnv* env, jobject local, const char* what);
|
|
37
|
+
Opt<NativeRef> wrapOpt(JNIEnv* env, jobject local);
|
|
38
|
+
inline jobject unwrap(const NativeRef& r) { return static_cast<jobject>(r.get()); }
|
|
39
|
+
inline jobject unwrap(const Opt<NativeRef>& r) { return r.has() ? static_cast<jobject>(r.get().get()) : nullptr; }
|
|
40
|
+
|
|
41
|
+
jstring toJString(JNIEnv* env, const String& s);
|
|
42
|
+
String fromJString(JNIEnv* env, jstring s, const char* what);
|
|
43
|
+
Opt<String> fromJStringOpt(JNIEnv* env, jstring s);
|
|
44
|
+
/// A CharSequence (String, SpannableString…) as a string, through toString().
|
|
45
|
+
String charSequenceToString(JNIEnv* env, jobject s, const char* what);
|
|
46
|
+
Opt<String> charSequenceToStringOpt(JNIEnv* env, jobject s);
|
|
47
|
+
|
|
48
|
+
// Arrays are copied in both directions; a null reference is an absent value.
|
|
49
|
+
jlongArray toLongArray(JNIEnv* env, const Array<double>& a);
|
|
50
|
+
jintArray toIntArray(JNIEnv* env, const Array<double>& a);
|
|
51
|
+
jbyteArray toByteArray(JNIEnv* env, const Bytes& b);
|
|
52
|
+
jobjectArray toStringArray(JNIEnv* env, const Array<String>& a);
|
|
53
|
+
template <class T, class F>
|
|
54
|
+
auto toArrayOpt(JNIEnv* env, const Opt<T>& v, F convert) -> decltype(convert(env, v.get())) {
|
|
55
|
+
return v.has() ? convert(env, v.get()) : nullptr;
|
|
56
|
+
}
|
|
57
|
+
Array<double> fromLongArray(JNIEnv* env, jlongArray a, const char* what);
|
|
58
|
+
Array<double> fromIntArray(JNIEnv* env, jintArray a, const char* what);
|
|
59
|
+
Bytes fromByteArray(JNIEnv* env, jbyteArray a, const char* what);
|
|
60
|
+
Array<String> fromStringArray(JNIEnv* env, jobjectArray a, const char* what);
|
|
61
|
+
template <class T, class A, class F>
|
|
62
|
+
Opt<T> fromArrayOpt(JNIEnv* env, A a, F convert) {
|
|
63
|
+
if (!a) return Opt<T>(null);
|
|
64
|
+
return convert(env, a, "");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Frees the local references a glue call creates.
|
|
68
|
+
class LocalFrame {
|
|
69
|
+
public:
|
|
70
|
+
explicit LocalFrame(JNIEnv* env) : env_(env) { env_->PushLocalFrame(16); }
|
|
71
|
+
~LocalFrame() { env_->PopLocalFrame(nullptr); }
|
|
72
|
+
LocalFrame(const LocalFrame&) = delete;
|
|
73
|
+
LocalFrame& operator=(const LocalFrame&) = delete;
|
|
74
|
+
|
|
75
|
+
private:
|
|
76
|
+
JNIEnv* env_;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// --- Java interfaces implemented by Lucent code --------------------------------------
|
|
80
|
+
|
|
81
|
+
/// Handles one method of a proxy: its arguments (boxed), its result (boxed,
|
|
82
|
+
/// or null for void).
|
|
83
|
+
using ProxyMethod = std::function<jobject(JNIEnv*, jobjectArray)>;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* A dev.lucent.NativeProxy implementing `iface` (a JNI class name) whose
|
|
87
|
+
* methods are `methods`, keyed by name and parameter descriptor
|
|
88
|
+
* (`onLocationChanged(Landroid/location/Location;)`). One per `identity` (a Lucent function or
|
|
89
|
+
* object) while Java holds it, so passing the same function twice passes
|
|
90
|
+
* the same object. A local reference.
|
|
91
|
+
*/
|
|
92
|
+
jobject proxyFor(JNIEnv* env, const char* iface, const void* identity, std::initializer_list<std::pair<const char*, ProxyMethod>> methods);
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* An instance of `cls` (a generated Java subclass of an SDK class, made with
|
|
96
|
+
* its handle) whose overridden methods are `methods`, keyed as proxyFor's.
|
|
97
|
+
* One per `identity` while Java holds it. A local reference.
|
|
98
|
+
*/
|
|
99
|
+
jobject subclassFor(JNIEnv* env, const char* cls, const void* identity, std::initializer_list<std::pair<const char*, ProxyMethod>> methods);
|
|
100
|
+
|
|
101
|
+
/// The i-th argument of a proxy call (a local reference; boxed primitives).
|
|
102
|
+
jobject arg(JNIEnv* env, jobjectArray args, int i);
|
|
103
|
+
/// Primitive values of boxed arguments, and boxed results.
|
|
104
|
+
double unboxNumber(JNIEnv* env, jobject boxed);
|
|
105
|
+
bool unboxBoolean(JNIEnv* env, jobject boxed);
|
|
106
|
+
jobject boxInt(JNIEnv* env, jint v);
|
|
107
|
+
jobject boxLong(JNIEnv* env, jlong v);
|
|
108
|
+
jobject boxDouble(JNIEnv* env, jdouble v);
|
|
109
|
+
jobject boxFloat(JNIEnv* env, jfloat v);
|
|
110
|
+
jobject boxBoolean(JNIEnv* env, bool v);
|
|
111
|
+
|
|
112
|
+
/// `appContext()` from lucent:android: the Application.
|
|
113
|
+
NativeRef appContext();
|
|
114
|
+
/// `available("android", api)`.
|
|
115
|
+
bool available(double api);
|
|
116
|
+
|
|
117
|
+
} // namespace lucent::jni
|