@rn-org/react-native-thread 0.1.0
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/LICENSE +20 -0
- package/README.md +440 -0
- package/ReactNativeThread.podspec +20 -0
- package/android/build.gradle +69 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/rnorg/reactnativethread/ReactNativeThreadModule.kt +162 -0
- package/android/src/main/java/com/rnorg/reactnativethread/ReactNativeThreadPackage.kt +31 -0
- package/ios/ReactNativeThread.h +6 -0
- package/ios/ReactNativeThread.mm +180 -0
- package/lib/module/NativeReactNativeThread.js +5 -0
- package/lib/module/NativeReactNativeThread.js.map +1 -0
- package/lib/module/babel-plugin.js +71 -0
- package/lib/module/babel-plugin.js.map +1 -0
- package/lib/module/globals.d.js +2 -0
- package/lib/module/globals.d.js.map +1 -0
- package/lib/module/index.js +166 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeReactNativeThread.d.ts +20 -0
- package/lib/typescript/src/NativeReactNativeThread.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +49 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +179 -0
- package/src/NativeReactNativeThread.ts +24 -0
- package/src/babel-plugin.js +77 -0
- package/src/globals.d.ts +34 -0
- package/src/index.tsx +211 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
package com.rnorg.reactnativethread
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.facebook.react.bridge.Arguments
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
7
|
+
import org.mozilla.javascript.BaseFunction
|
|
8
|
+
import org.mozilla.javascript.Context as RhinoContext
|
|
9
|
+
import org.mozilla.javascript.NativeObject
|
|
10
|
+
import org.mozilla.javascript.ScriptRuntime
|
|
11
|
+
import org.mozilla.javascript.Scriptable
|
|
12
|
+
import org.mozilla.javascript.ScriptableObject
|
|
13
|
+
import java.util.concurrent.CompletableFuture
|
|
14
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
15
|
+
import java.util.concurrent.ExecutorService
|
|
16
|
+
import java.util.concurrent.Executors
|
|
17
|
+
import java.util.concurrent.atomic.AtomicLong
|
|
18
|
+
|
|
19
|
+
class ReactNativeThreadModule(reactContext: ReactApplicationContext) :
|
|
20
|
+
NativeReactNativeThreadSpec(reactContext) {
|
|
21
|
+
|
|
22
|
+
private data class ThreadEntry(
|
|
23
|
+
val executor: ExecutorService,
|
|
24
|
+
val scope: Scriptable
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
private val threads = ConcurrentHashMap<Long, ThreadEntry>()
|
|
28
|
+
private val nextId = AtomicLong(1L)
|
|
29
|
+
|
|
30
|
+
// ─── NativeEventEmitter boilerplate ────────────────────────────────────────
|
|
31
|
+
override fun addListener(eventName: String?) { /* no-op: handled by JS */ }
|
|
32
|
+
override fun removeListeners(count: Double) { /* no-op */ }
|
|
33
|
+
|
|
34
|
+
// ─── Thread lifecycle ───────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
override fun createThread(): Double {
|
|
37
|
+
val id = nextId.getAndIncrement()
|
|
38
|
+
val executor = Executors.newSingleThreadExecutor { r ->
|
|
39
|
+
Thread(r, "RNThread-$id")
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
val scopeFuture = CompletableFuture<Scriptable>()
|
|
43
|
+
executor.execute {
|
|
44
|
+
val cx = RhinoContext.enter()
|
|
45
|
+
try {
|
|
46
|
+
cx.optimizationLevel = -1
|
|
47
|
+
val scope = cx.initStandardObjects()
|
|
48
|
+
injectConsole(scope, id)
|
|
49
|
+
injectPostMessage(scope, id)
|
|
50
|
+
scopeFuture.complete(scope)
|
|
51
|
+
} catch (e: Exception) {
|
|
52
|
+
scopeFuture.completeExceptionally(e)
|
|
53
|
+
} finally {
|
|
54
|
+
RhinoContext.exit()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
val scope = scopeFuture.get()
|
|
59
|
+
threads[id] = ThreadEntry(executor, scope)
|
|
60
|
+
return id.toDouble()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
override fun runOnThread(threadId: Double, code: String) {
|
|
64
|
+
val entry = threads[threadId.toLong()]
|
|
65
|
+
if (entry == null) {
|
|
66
|
+
Log.w(NAME, "runOnThread: unknown threadId $threadId")
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
entry.executor.execute {
|
|
70
|
+
val cx = RhinoContext.enter()
|
|
71
|
+
try {
|
|
72
|
+
cx.optimizationLevel = -1
|
|
73
|
+
cx.evaluateString(entry.scope, code, "RNThread-${threadId.toLong()}", 1, null)
|
|
74
|
+
} catch (e: Exception) {
|
|
75
|
+
Log.e(NAME, "JS exception on thread $threadId: ${e.message}")
|
|
76
|
+
} finally {
|
|
77
|
+
RhinoContext.exit()
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
override fun destroyThread(threadId: Double) {
|
|
83
|
+
val entry = threads.remove(threadId.toLong()) ?: return
|
|
84
|
+
entry.executor.shutdown()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─── Scope injection ────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
private fun injectPostMessage(scope: Scriptable, threadId: Long) {
|
|
90
|
+
val ctx = reactApplicationContext
|
|
91
|
+
ScriptableObject.putProperty(
|
|
92
|
+
scope as ScriptableObject,
|
|
93
|
+
"postMessage",
|
|
94
|
+
object : BaseFunction() {
|
|
95
|
+
override fun call(
|
|
96
|
+
cx: RhinoContext,
|
|
97
|
+
callScope: Scriptable,
|
|
98
|
+
thisObj: Scriptable?,
|
|
99
|
+
args: Array<out Any?>
|
|
100
|
+
): Any {
|
|
101
|
+
val raw = args.getOrNull(0)
|
|
102
|
+
val jsonString: String = try {
|
|
103
|
+
val jsonObj = ScriptableObject.getProperty(callScope, "JSON") as? Scriptable
|
|
104
|
+
val stringify = jsonObj?.let { ScriptableObject.getProperty(it, "stringify") }
|
|
105
|
+
if (stringify is org.mozilla.javascript.Function) {
|
|
106
|
+
ScriptRuntime.toString(stringify.call(cx, callScope, jsonObj, arrayOf(raw)))
|
|
107
|
+
} else {
|
|
108
|
+
raw?.toString() ?: "null"
|
|
109
|
+
}
|
|
110
|
+
} catch (e: Exception) {
|
|
111
|
+
raw?.toString() ?: "null"
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
val params = Arguments.createMap().apply {
|
|
115
|
+
putDouble("threadId", threadId.toDouble())
|
|
116
|
+
putString("data", jsonString)
|
|
117
|
+
}
|
|
118
|
+
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
119
|
+
.emit("RNThreadMessage", params)
|
|
120
|
+
|
|
121
|
+
return RhinoContext.getUndefinedValue()
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private fun injectConsole(scope: Scriptable, threadId: Long) {
|
|
128
|
+
val tag = "RNThread-$threadId"
|
|
129
|
+
|
|
130
|
+
fun makeLogFn(level: Int) = object : BaseFunction() {
|
|
131
|
+
override fun call(
|
|
132
|
+
cx: RhinoContext,
|
|
133
|
+
scope: Scriptable,
|
|
134
|
+
thisObj: Scriptable?,
|
|
135
|
+
args: Array<out Any?>
|
|
136
|
+
): Any {
|
|
137
|
+
val msg = args.joinToString(" ") { it?.toString() ?: "null" }
|
|
138
|
+
when (level) {
|
|
139
|
+
Log.DEBUG -> Log.d(tag, msg)
|
|
140
|
+
Log.INFO -> Log.i(tag, msg)
|
|
141
|
+
Log.WARN -> Log.w(tag, msg)
|
|
142
|
+
Log.ERROR -> Log.e(tag, msg)
|
|
143
|
+
else -> Log.v(tag, msg)
|
|
144
|
+
}
|
|
145
|
+
return RhinoContext.getUndefinedValue()
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
val console = NativeObject()
|
|
150
|
+
console.parentScope = scope
|
|
151
|
+
ScriptableObject.putProperty(console, "log", makeLogFn(Log.INFO))
|
|
152
|
+
ScriptableObject.putProperty(console, "info", makeLogFn(Log.INFO))
|
|
153
|
+
ScriptableObject.putProperty(console, "warn", makeLogFn(Log.WARN))
|
|
154
|
+
ScriptableObject.putProperty(console, "error", makeLogFn(Log.ERROR))
|
|
155
|
+
ScriptableObject.putProperty(console, "debug", makeLogFn(Log.DEBUG))
|
|
156
|
+
ScriptableObject.putProperty(scope as ScriptableObject, "console", console)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
companion object {
|
|
160
|
+
const val NAME = NativeReactNativeThreadSpec.NAME
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package com.rnorg.reactnativethread
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
|
+
import java.util.HashMap
|
|
9
|
+
|
|
10
|
+
class ReactNativeThreadPackage : BaseReactPackage() {
|
|
11
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
12
|
+
return if (name == ReactNativeThreadModule.NAME) {
|
|
13
|
+
ReactNativeThreadModule(reactContext)
|
|
14
|
+
} else {
|
|
15
|
+
null
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
|
|
20
|
+
mapOf(
|
|
21
|
+
ReactNativeThreadModule.NAME to ReactModuleInfo(
|
|
22
|
+
name = ReactNativeThreadModule.NAME,
|
|
23
|
+
className = ReactNativeThreadModule.NAME,
|
|
24
|
+
canOverrideExistingModule = false,
|
|
25
|
+
needsEagerInit = false,
|
|
26
|
+
isCxxModule = false,
|
|
27
|
+
isTurboModule = true
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#import "ReactNativeThread.h"
|
|
2
|
+
#import <JavaScriptCore/JavaScriptCore.h>
|
|
3
|
+
|
|
4
|
+
// One entry per thread: a JSContext running on a dedicated serial queue.
|
|
5
|
+
// Each thread owns its own JSVirtualMachine so execution is fully concurrent
|
|
6
|
+
// and never contends with the main thread's VM lock.
|
|
7
|
+
@interface RNThread : NSObject
|
|
8
|
+
@property (nonatomic, strong) JSVirtualMachine *vm;
|
|
9
|
+
@property (nonatomic, strong) JSContext *context;
|
|
10
|
+
@property (nonatomic, strong) dispatch_queue_t queue;
|
|
11
|
+
// Signals when VM+context initialisation is complete so that runOnThread
|
|
12
|
+
// can be enqueued immediately without blocking the JS thread during init.
|
|
13
|
+
@property (nonatomic) dispatch_semaphore_t ready;
|
|
14
|
+
@end
|
|
15
|
+
|
|
16
|
+
@implementation RNThread
|
|
17
|
+
@end
|
|
18
|
+
|
|
19
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
@implementation ReactNativeThread {
|
|
22
|
+
NSMutableDictionary<NSNumber *, RNThread *> *_threads;
|
|
23
|
+
NSLock *_lock;
|
|
24
|
+
double _nextId;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
- (instancetype)init {
|
|
28
|
+
if (self = [super init]) {
|
|
29
|
+
_threads = [NSMutableDictionary new];
|
|
30
|
+
_lock = [NSLock new];
|
|
31
|
+
_nextId = 1;
|
|
32
|
+
}
|
|
33
|
+
return self;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Required by RCTEventEmitter
|
|
37
|
+
- (NSArray<NSString *> *)supportedEvents {
|
|
38
|
+
return @[@"RNThreadMessage"];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
- (void)addListener:(NSString *)eventName { [super addListener:eventName]; }
|
|
42
|
+
- (void)removeListeners:(double)count { [super removeListeners:count]; }
|
|
43
|
+
|
|
44
|
+
// ─── createThread ─────────────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
- (NSNumber *)createThread {
|
|
47
|
+
RNThread *t = [RNThread new];
|
|
48
|
+
|
|
49
|
+
NSString *label = [NSString stringWithFormat:@"com.rnorg.reactnativethread.%lu",
|
|
50
|
+
(unsigned long)(NSUInteger)_nextId];
|
|
51
|
+
|
|
52
|
+
// QOS_CLASS_UTILITY keeps background JS work below the main/UI thread in
|
|
53
|
+
// the scheduler, preventing CPU starvation when a tight loop is running.
|
|
54
|
+
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(
|
|
55
|
+
DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0);
|
|
56
|
+
t.queue = dispatch_queue_create(label.UTF8String, attr);
|
|
57
|
+
t.ready = dispatch_semaphore_create(0);
|
|
58
|
+
|
|
59
|
+
NSUInteger tidForLog = (NSUInteger)_nextId;
|
|
60
|
+
|
|
61
|
+
// Async init: the JS thread is never blocked waiting for VM creation.
|
|
62
|
+
// runOnThread enqueues a semaphore-wait block first so it executes only
|
|
63
|
+
// after init is complete, all on the background queue.
|
|
64
|
+
dispatch_async(t.queue, ^{
|
|
65
|
+
t.vm = [[JSVirtualMachine alloc] init];
|
|
66
|
+
t.context = [[JSContext alloc] initWithVirtualMachine:t.vm];
|
|
67
|
+
t.context.exceptionHandler = ^(JSContext *ctx, JSValue *exception) {
|
|
68
|
+
NSLog(@"[RNThread-%lu] JS exception: %@", (unsigned long)tidForLog, exception);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// ── console ───────────────────────────────────────────────────────────
|
|
72
|
+
NSString *tag = [NSString stringWithFormat:@"RNThread-%lu", (unsigned long)tidForLog];
|
|
73
|
+
NSMutableDictionary *console = [NSMutableDictionary new];
|
|
74
|
+
console[@"log"] = ^{
|
|
75
|
+
NSArray *args = [JSContext currentArguments];
|
|
76
|
+
NSMutableArray *parts = [NSMutableArray new];
|
|
77
|
+
for (JSValue *v in args) [parts addObject:[v toString]];
|
|
78
|
+
NSLog(@"[%@] %@", tag, [parts componentsJoinedByString:@" "]);
|
|
79
|
+
};
|
|
80
|
+
console[@"info"] = console[@"log"];
|
|
81
|
+
console[@"debug"] = console[@"log"];
|
|
82
|
+
console[@"warn"] = ^{
|
|
83
|
+
NSArray *args = [JSContext currentArguments];
|
|
84
|
+
NSMutableArray *parts = [NSMutableArray new];
|
|
85
|
+
for (JSValue *v in args) [parts addObject:[v toString]];
|
|
86
|
+
NSLog(@"[%@] WARN: %@", tag, [parts componentsJoinedByString:@" "]);
|
|
87
|
+
};
|
|
88
|
+
console[@"error"] = ^{
|
|
89
|
+
NSArray *args = [JSContext currentArguments];
|
|
90
|
+
NSMutableArray *parts = [NSMutableArray new];
|
|
91
|
+
for (JSValue *v in args) [parts addObject:[v toString]];
|
|
92
|
+
NSLog(@"[%@] ERROR: %@", tag, [parts componentsJoinedByString:@" "]);
|
|
93
|
+
};
|
|
94
|
+
t.context[@"console"] = console;
|
|
95
|
+
|
|
96
|
+
// ── postMessage ───────────────────────────────────────────────────────
|
|
97
|
+
NSUInteger capturedTid = tidForLog;
|
|
98
|
+
__weak ReactNativeThread *weakSelf = self;
|
|
99
|
+
t.context[@"postMessage"] = ^(JSValue *data) { // evaluated on t.queue
|
|
100
|
+
JSValue *jsonStr = [data.context[@"JSON"] invokeMethod:@"stringify"
|
|
101
|
+
withArguments:@[data]];
|
|
102
|
+
NSString *serialised = [jsonStr toString];
|
|
103
|
+
if (!serialised || [serialised isEqualToString:@"undefined"]) {
|
|
104
|
+
serialised = @"null";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
ReactNativeThread *strongSelf = weakSelf;
|
|
108
|
+
if (!strongSelf) return;
|
|
109
|
+
|
|
110
|
+
[strongSelf sendEventWithName:@"RNThreadMessage"
|
|
111
|
+
body:@{
|
|
112
|
+
@"threadId": @(capturedTid),
|
|
113
|
+
@"data": serialised
|
|
114
|
+
}];
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Signal that the thread is ready to accept work.
|
|
118
|
+
dispatch_semaphore_signal(t.ready);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
double tid = _nextId;
|
|
122
|
+
[_lock lock];
|
|
123
|
+
_threads[@(tid)] = t;
|
|
124
|
+
_nextId += 1;
|
|
125
|
+
[_lock unlock];
|
|
126
|
+
|
|
127
|
+
return @(tid);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ─── runOnThread ──────────────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
- (void)runOnThread:(double)threadId code:(NSString *)code {
|
|
133
|
+
[_lock lock];
|
|
134
|
+
RNThread *t = _threads[@(threadId)];
|
|
135
|
+
[_lock unlock];
|
|
136
|
+
|
|
137
|
+
if (!t) {
|
|
138
|
+
NSLog(@"[ReactNativeThread] runOnThread: unknown threadId %.0f", threadId);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
NSString *codeCopy = [code copy];
|
|
143
|
+
dispatch_semaphore_t ready = t.ready;
|
|
144
|
+
dispatch_async(t.queue, ^{
|
|
145
|
+
// Wait for init to finish (no-op after first runOnThread call).
|
|
146
|
+
dispatch_semaphore_wait(ready, DISPATCH_TIME_FOREVER);
|
|
147
|
+
dispatch_semaphore_signal(ready); // re-signal for subsequent calls
|
|
148
|
+
[t.context evaluateScript:codeCopy];
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ─── destroyThread ────────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
- (void)destroyThread:(double)threadId {
|
|
155
|
+
[_lock lock];
|
|
156
|
+
RNThread *t = _threads[@(threadId)];
|
|
157
|
+
[_threads removeObjectForKey:@(threadId)];
|
|
158
|
+
[_lock unlock];
|
|
159
|
+
|
|
160
|
+
if (!t) return;
|
|
161
|
+
|
|
162
|
+
dispatch_async(t.queue, ^{
|
|
163
|
+
(void)t;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ─── TurboModule boilerplate ──────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
170
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
171
|
+
{
|
|
172
|
+
return std::make_shared<facebook::react::NativeReactNativeThreadSpecJSI>(params);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
+ (NSString *)moduleName
|
|
176
|
+
{
|
|
177
|
+
return @"ReactNativeThread";
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
@end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeReactNativeThread.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAuBpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,mBAAmB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Babel plugin for @rn-org/react-native-thread
|
|
3
|
+
*
|
|
4
|
+
* Hermes compiles JS to bytecode at build time, so fn.toString() at runtime
|
|
5
|
+
* returns a placeholder ("bytecode") rather than the original source.
|
|
6
|
+
*
|
|
7
|
+
* This plugin runs at compile time and replaces arrow-function / function-
|
|
8
|
+
* expression arguments passed to the thread API with string literals that
|
|
9
|
+
* contain the source wrapped as an IIFE. The background thread engine
|
|
10
|
+
* (JSC on iOS, Rhino on Android) can then simply eval() the string.
|
|
11
|
+
*
|
|
12
|
+
* Transforms:
|
|
13
|
+
* runOnJS((args) => { ... }) → runOnJS("((args) => { ... })(__params__)")
|
|
14
|
+
* runOnNewJS((args) => { ... }) → runOnNewJS("((args) => { ... })(__params__)")
|
|
15
|
+
* thread.run((args) => { ... }) → thread.run("((args) => { ... })(__params__)")
|
|
16
|
+
*
|
|
17
|
+
* Add to your app's babel.config.js:
|
|
18
|
+
* plugins: ['@rn-org/react-native-thread/babel-plugin']
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
'use strict';
|
|
22
|
+
|
|
23
|
+
// Top-level function names that take a fn-or-code as their first argument.
|
|
24
|
+
const TOP_LEVEL_API = new Set(['runOnJS', 'runOnNewJS']);
|
|
25
|
+
|
|
26
|
+
// Method names on a ThreadHandle that take a fn-or-code as their first arg.
|
|
27
|
+
const METHOD_API = new Set(['run']);
|
|
28
|
+
module.exports = function reactNativeThreadPlugin({
|
|
29
|
+
types: t
|
|
30
|
+
}) {
|
|
31
|
+
/**
|
|
32
|
+
* If `argNode` is an arrow function or function expression, replace the
|
|
33
|
+
* argument at `index` with a string literal `"(<source>)()"`.
|
|
34
|
+
*/
|
|
35
|
+
function maybeTransformArg(callPath, state, argNode, index) {
|
|
36
|
+
if (!t.isArrowFunctionExpression(argNode) && !t.isFunctionExpression(argNode)) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const originalCode = state.file.code;
|
|
40
|
+
const fnSource = originalCode.slice(argNode.start, argNode.end);
|
|
41
|
+
const iife = `(${fnSource})(__params__)`;
|
|
42
|
+
callPath.node.arguments[index] = t.stringLiteral(iife);
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
name: 'react-native-thread',
|
|
46
|
+
visitor: {
|
|
47
|
+
CallExpression(path, state) {
|
|
48
|
+
const {
|
|
49
|
+
callee,
|
|
50
|
+
arguments: args
|
|
51
|
+
} = path.node;
|
|
52
|
+
|
|
53
|
+
// runOnJS(() => {}) / runOnNewJS(() => {})
|
|
54
|
+
if (t.isIdentifier(callee) && TOP_LEVEL_API.has(callee.name)) {
|
|
55
|
+
if (args.length >= 1) {
|
|
56
|
+
maybeTransformArg(path, state, args[0], 0);
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// someThread.run(() => {})
|
|
62
|
+
if (t.isMemberExpression(callee) && !callee.computed && t.isIdentifier(callee.property) && METHOD_API.has(callee.property.name)) {
|
|
63
|
+
if (args.length >= 1) {
|
|
64
|
+
maybeTransformArg(path, state, args[0], 0);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=babel-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TOP_LEVEL_API","Set","METHOD_API","module","exports","reactNativeThreadPlugin","types","t","maybeTransformArg","callPath","state","argNode","index","isArrowFunctionExpression","isFunctionExpression","originalCode","file","code","fnSource","slice","start","end","iife","node","arguments","stringLiteral","name","visitor","CallExpression","path","callee","args","isIdentifier","has","length","isMemberExpression","computed","property"],"sourceRoot":"../../src","sources":["babel-plugin.js"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,YAAY;;AAEZ;AACA,MAAMA,aAAa,GAAG,IAAIC,GAAG,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;;AAExD;AACA,MAAMC,UAAU,GAAG,IAAID,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AAEnCE,MAAM,CAACC,OAAO,GAAG,SAASC,uBAAuBA,CAAC;EAAEC,KAAK,EAAEC;AAAE,CAAC,EAAE;EAC9D;AACF;AACA;AACA;EACE,SAASC,iBAAiBA,CAACC,QAAQ,EAAEC,KAAK,EAAEC,OAAO,EAAEC,KAAK,EAAE;IAC1D,IACE,CAACL,CAAC,CAACM,yBAAyB,CAACF,OAAO,CAAC,IACrC,CAACJ,CAAC,CAACO,oBAAoB,CAACH,OAAO,CAAC,EAChC;MACA;IACF;IAEA,MAAMI,YAAY,GAAGL,KAAK,CAACM,IAAI,CAACC,IAAI;IACpC,MAAMC,QAAQ,GAAGH,YAAY,CAACI,KAAK,CAACR,OAAO,CAACS,KAAK,EAAET,OAAO,CAACU,GAAG,CAAC;IAC/D,MAAMC,IAAI,GAAG,IAAIJ,QAAQ,eAAe;IAExCT,QAAQ,CAACc,IAAI,CAACC,SAAS,CAACZ,KAAK,CAAC,GAAGL,CAAC,CAACkB,aAAa,CAACH,IAAI,CAAC;EACxD;EAEA,OAAO;IACLI,IAAI,EAAE,qBAAqB;IAC3BC,OAAO,EAAE;MACPC,cAAcA,CAACC,IAAI,EAAEnB,KAAK,EAAE;QAC1B,MAAM;UAAEoB,MAAM;UAAEN,SAAS,EAAEO;QAAK,CAAC,GAAGF,IAAI,CAACN,IAAI;;QAE7C;QACA,IAAIhB,CAAC,CAACyB,YAAY,CAACF,MAAM,CAAC,IAAI9B,aAAa,CAACiC,GAAG,CAACH,MAAM,CAACJ,IAAI,CAAC,EAAE;UAC5D,IAAIK,IAAI,CAACG,MAAM,IAAI,CAAC,EAAE;YACpB1B,iBAAiB,CAACqB,IAAI,EAAEnB,KAAK,EAAEqB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;UAC5C;UACA;QACF;;QAEA;QACA,IACExB,CAAC,CAAC4B,kBAAkB,CAACL,MAAM,CAAC,IAC5B,CAACA,MAAM,CAACM,QAAQ,IAChB7B,CAAC,CAACyB,YAAY,CAACF,MAAM,CAACO,QAAQ,CAAC,IAC/BnC,UAAU,CAAC+B,GAAG,CAACH,MAAM,CAACO,QAAQ,CAACX,IAAI,CAAC,EACpC;UACA,IAAIK,IAAI,CAACG,MAAM,IAAI,CAAC,EAAE;YACpB1B,iBAAiB,CAACqB,IAAI,EAAEnB,KAAK,EAAEqB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;UAC5C;QACF;MACF;IACF;EACF,CAAC;AACH,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["globals.d.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { NativeEventEmitter } from 'react-native';
|
|
4
|
+
import ReactNativeThread from "./NativeReactNativeThread.js";
|
|
5
|
+
|
|
6
|
+
/** Snapshot of a single live thread. */
|
|
7
|
+
|
|
8
|
+
const _emitter = new NativeEventEmitter(ReactNativeThread);
|
|
9
|
+
const RN_THREAD_MESSAGE_EVENT = 'RNThreadMessage';
|
|
10
|
+
|
|
11
|
+
// ─── Thread registry (JS-layer; names are a JS concept) ──────────────────────
|
|
12
|
+
const _registry = new Map();
|
|
13
|
+
function _register(id, name) {
|
|
14
|
+
_registry.set(id, {
|
|
15
|
+
id,
|
|
16
|
+
name
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function _unregister(id) {
|
|
20
|
+
_registry.delete(id);
|
|
21
|
+
}
|
|
22
|
+
function toCode(task, params) {
|
|
23
|
+
if (typeof task === 'string') {
|
|
24
|
+
// Always declare __params__ so Babel-transformed strings can pass it as
|
|
25
|
+
// an argument to the function, and raw code strings that reference
|
|
26
|
+
// __params__ directly continue to work.
|
|
27
|
+
const paramsJson = params !== undefined ? JSON.stringify(params) : 'undefined';
|
|
28
|
+
return `var __params__ = ${paramsJson};\n${task}`;
|
|
29
|
+
}
|
|
30
|
+
const src = task.toString();
|
|
31
|
+
if (src.includes('[bytecode]') || /^\s*function\s*\(\)\s*\{\s*bytecode\s*\}/.test(src)) {
|
|
32
|
+
if (__DEV__) {
|
|
33
|
+
console.warn('[react-native-thread] fn.toString() returned a Hermes bytecode placeholder. ' + "Add the Babel plugin: plugins: ['@rn-org/react-native-thread/babel-plugin']");
|
|
34
|
+
}
|
|
35
|
+
return '/* bytecode: no-op */';
|
|
36
|
+
}
|
|
37
|
+
const argsStr = params !== undefined ? JSON.stringify(params) : '';
|
|
38
|
+
return `(${src})(${argsStr})`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ─── Shared thread (runOnJS) ──────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
const SHARED_THREAD_NAME = 'RNOrgThread';
|
|
44
|
+
let _sharedThreadId = null;
|
|
45
|
+
function getSharedThread() {
|
|
46
|
+
if (_sharedThreadId === null) {
|
|
47
|
+
_sharedThreadId = ReactNativeThread.createThread();
|
|
48
|
+
_register(_sharedThreadId, SHARED_THREAD_NAME);
|
|
49
|
+
}
|
|
50
|
+
return _sharedThreadId;
|
|
51
|
+
}
|
|
52
|
+
export function runOnJS(task, params) {
|
|
53
|
+
ReactNativeThread.runOnThread(getSharedThread(), toCode(task, params));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Runs a task on a brand-new isolated thread and returns a handle to it.
|
|
58
|
+
* @param name - Optional display name (default: `RNThread-<id>`).
|
|
59
|
+
*/
|
|
60
|
+
export function runOnNewJS(task, params, name) {
|
|
61
|
+
const id = ReactNativeThread.createThread();
|
|
62
|
+
const resolvedName = name ?? `RNThread-${id}`;
|
|
63
|
+
_register(id, resolvedName);
|
|
64
|
+
ReactNativeThread.runOnThread(id, toCode(task, params));
|
|
65
|
+
return createThreadHandle(id, resolvedName);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Creates a persistent thread. Optionally give it a name.
|
|
70
|
+
* @param name - Display name (default: `RNThread-<id>`).
|
|
71
|
+
*/
|
|
72
|
+
export function createThread(name) {
|
|
73
|
+
const id = ReactNativeThread.createThread();
|
|
74
|
+
const resolvedName = name ?? `RNThread-${id}`;
|
|
75
|
+
_register(id, resolvedName);
|
|
76
|
+
return createThreadHandle(id, resolvedName);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Returns info about every live thread currently managed by this library,
|
|
81
|
+
* including the shared `runOnJS` thread once it has been started.
|
|
82
|
+
*/
|
|
83
|
+
export function getThreads() {
|
|
84
|
+
return Array.from(_registry.values());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Destroy a thread by its numeric ID **or** by its name.
|
|
89
|
+
* When a name is given, the first matching thread is destroyed.
|
|
90
|
+
* This is also what `ThreadHandle.destroy()` calls internally.
|
|
91
|
+
*/
|
|
92
|
+
export function destroyThread(idOrName) {
|
|
93
|
+
let targetId;
|
|
94
|
+
if (typeof idOrName === 'number') {
|
|
95
|
+
if (_registry.has(idOrName)) targetId = idOrName;
|
|
96
|
+
} else {
|
|
97
|
+
for (const info of _registry.values()) {
|
|
98
|
+
if (info.name === idOrName) {
|
|
99
|
+
targetId = info.id;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (targetId === undefined) {
|
|
105
|
+
if (__DEV__) {
|
|
106
|
+
console.warn(`[react-native-thread] destroyThread: no thread found for "${idOrName}"`);
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
_unregister(targetId);
|
|
111
|
+
if (targetId === _sharedThreadId) _sharedThreadId = null;
|
|
112
|
+
ReactNativeThread.destroyThread(targetId);
|
|
113
|
+
}
|
|
114
|
+
export function onMessage(handler) {
|
|
115
|
+
if (handler) {
|
|
116
|
+
const sub = _emitter.addListener(RN_THREAD_MESSAGE_EVENT, event => {
|
|
117
|
+
let parsed = event.data;
|
|
118
|
+
try {
|
|
119
|
+
parsed = JSON.parse(event.data);
|
|
120
|
+
} catch {}
|
|
121
|
+
handler(parsed, event.threadId);
|
|
122
|
+
});
|
|
123
|
+
return () => sub.remove();
|
|
124
|
+
}
|
|
125
|
+
return new Promise(resolve => {
|
|
126
|
+
const sub = _emitter.addListener(RN_THREAD_MESSAGE_EVENT, event => {
|
|
127
|
+
let parsed = event.data;
|
|
128
|
+
try {
|
|
129
|
+
parsed = JSON.parse(event.data);
|
|
130
|
+
} catch {}
|
|
131
|
+
sub.remove();
|
|
132
|
+
resolve({
|
|
133
|
+
data: parsed,
|
|
134
|
+
threadId: event.threadId
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function createThreadHandle(id, name) {
|
|
140
|
+
return {
|
|
141
|
+
id,
|
|
142
|
+
name,
|
|
143
|
+
run(task, params) {
|
|
144
|
+
ReactNativeThread.runOnThread(id, toCode(task, params));
|
|
145
|
+
},
|
|
146
|
+
onMessage: function (handler) {
|
|
147
|
+
if (handler) {
|
|
148
|
+
return onMessage((data, threadId) => {
|
|
149
|
+
if (threadId === id) handler(data);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return new Promise(resolve => {
|
|
153
|
+
const unsub = onMessage((data, threadId) => {
|
|
154
|
+
if (threadId === id) {
|
|
155
|
+
unsub();
|
|
156
|
+
resolve(data);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
destroy() {
|
|
162
|
+
destroyThread(id);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NativeEventEmitter","ReactNativeThread","_emitter","RN_THREAD_MESSAGE_EVENT","_registry","Map","_register","id","name","set","_unregister","delete","toCode","task","params","paramsJson","undefined","JSON","stringify","src","toString","includes","test","__DEV__","console","warn","argsStr","SHARED_THREAD_NAME","_sharedThreadId","getSharedThread","createThread","runOnJS","runOnThread","runOnNewJS","resolvedName","createThreadHandle","getThreads","Array","from","values","destroyThread","idOrName","targetId","has","info","onMessage","handler","sub","addListener","event","parsed","data","parse","threadId","remove","Promise","resolve","run","unsub","destroy"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|