Add context executor API for sync hooks
Summary: public This exposes a proper API for adding synchronous callbacks to JS, as an optional feature of the executor. This is based on nicklockwood's work in D2764492, but avoids refactoring bridge/executor interactions for the time being, since we agree on this API and can move the actual callsites around later. Reviewed By: nicklockwood Differential Revision: D2799506 fb-gh-sync-id: af209d9a0be927f3404205feb16e59745cc37aec
This commit is contained in:
parent
1c1d7006c2
commit
a5d82c2823
|
@ -187,7 +187,7 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a
|
||||||
moduleProvider:nil
|
moduleProvider:nil
|
||||||
launchOptions:nil];
|
launchOptions:nil];
|
||||||
id executor = [bridge.batchedBridge valueForKey:@"javaScriptExecutor"];
|
id executor = [bridge.batchedBridge valueForKey:@"javaScriptExecutor"];
|
||||||
RUN_RUNLOOP_WHILE(!(weakContext = [executor valueForKey:@"context"]));
|
RUN_RUNLOOP_WHILE(!(weakContext = [executor valueForKey:@"_context"]));
|
||||||
XCTAssertNotNil(weakContext, @"RCTJavaScriptContext should have been created");
|
XCTAssertNotNil(weakContext, @"RCTJavaScriptContext should have been created");
|
||||||
(void)bridge;
|
(void)bridge;
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@ expectErrorBlock:(BOOL(^)(NSString *error))expectErrorBlock
|
||||||
// (we can only do this now, since it's been lazily initialized)
|
// (we can only do this now, since it's been lazily initialized)
|
||||||
id jsExecutor = [bridge valueForKeyPath:@"batchedBridge.javaScriptExecutor"];
|
id jsExecutor = [bridge valueForKeyPath:@"batchedBridge.javaScriptExecutor"];
|
||||||
if ([jsExecutor isKindOfClass:[RCTJSCExecutor class]]) {
|
if ([jsExecutor isKindOfClass:[RCTJSCExecutor class]]) {
|
||||||
weakJSContext = [jsExecutor valueForKey:@"context"];
|
weakJSContext = [jsExecutor valueForKey:@"_context"];
|
||||||
}
|
}
|
||||||
[rootView removeFromSuperview];
|
[rootView removeFromSuperview];
|
||||||
|
|
||||||
|
|
|
@ -85,4 +85,10 @@ typedef void (^RCTJavaScriptCallback)(id json, NSError *error);
|
||||||
*/
|
*/
|
||||||
- (void)executeAsyncBlockOnJavaScriptQueue:(dispatch_block_t)block;
|
- (void)executeAsyncBlockOnJavaScriptQueue:(dispatch_block_t)block;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For executors that support it, this method can be used to add a synchronous
|
||||||
|
* callback function for communicating with the javascript context.
|
||||||
|
*/
|
||||||
|
- (void)addSynchronousHookWithName:(NSString *)name usingBlock:(id)block;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -215,99 +215,113 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context)
|
||||||
return [self initWithJavaScriptThread:javaScriptThread context:context];
|
return [self initWithJavaScriptThread:javaScriptThread context:context];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setUp
|
- (RCTJavaScriptContext *)context
|
||||||
|
{
|
||||||
|
RCTAssertThread(_javaScriptThread, @"Must be called on JS thread.");
|
||||||
|
|
||||||
|
if (!self.isValid) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_context) {
|
||||||
|
JSContext *context = [JSContext new];
|
||||||
|
_context = [[RCTJavaScriptContext alloc] initWithJSContext:context];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _context;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)addSynchronousHookWithName:(NSString *)name usingBlock:(id)block
|
||||||
{
|
{
|
||||||
__weak RCTJSCExecutor *weakSelf = self;
|
__weak RCTJSCExecutor *weakSelf = self;
|
||||||
[self executeBlockOnJavaScriptQueue:^{
|
[self executeBlockOnJavaScriptQueue:^{
|
||||||
RCTJSCExecutor *strongSelf = weakSelf;
|
weakSelf.context.context[name] = block;
|
||||||
if (!strongSelf.isValid) {
|
}];
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strongSelf->_context) {
|
- (void)setUp
|
||||||
JSContext *context = [JSContext new];
|
{
|
||||||
strongSelf->_context = [[RCTJavaScriptContext alloc] initWithJSContext:context];
|
__weak RCTJSCExecutor *weakSelf = self;
|
||||||
}
|
[self addSynchronousHookWithName:@"noop" usingBlock:^{}];
|
||||||
|
|
||||||
__weak RCTBridge *weakBridge = strongSelf->_bridge;
|
[self addSynchronousHookWithName:@"nativeLoggingHook" usingBlock:^(NSString *message, NSNumber *logLevel) {
|
||||||
JSContext *context = strongSelf->_context.context;
|
|
||||||
|
|
||||||
context[@"noop"] = ^{};
|
|
||||||
|
|
||||||
context[@"nativeLoggingHook"] = ^(NSString *message, NSNumber *logLevel) {
|
|
||||||
RCTLogLevel level = RCTLogLevelInfo;
|
RCTLogLevel level = RCTLogLevelInfo;
|
||||||
if (logLevel) {
|
if (logLevel) {
|
||||||
level = MAX(level, logLevel.integerValue);
|
level = MAX(level, logLevel.integerValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
_RCTLogJavaScriptInternal(level, message);
|
_RCTLogJavaScriptInternal(level, message);
|
||||||
};
|
}];
|
||||||
|
|
||||||
context[@"nativeRequireModuleConfig"] = ^NSString *(NSString *moduleName) {
|
[self addSynchronousHookWithName:@"nativeRequireModuleConfig" usingBlock:^NSString *(NSString *moduleName) {
|
||||||
if (!weakSelf.valid) {
|
RCTJSCExecutor *strongSelf = weakSelf;
|
||||||
|
if (!strongSelf.valid) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
NSArray *config = [weakBridge configForModuleName:moduleName];
|
|
||||||
|
NSArray *config = [strongSelf->_bridge configForModuleName:moduleName];
|
||||||
if (config) {
|
if (config) {
|
||||||
return RCTJSONStringify(config, NULL);
|
return RCTJSONStringify(config, NULL);
|
||||||
}
|
}
|
||||||
return nil;
|
|
||||||
};
|
|
||||||
|
|
||||||
context[@"nativeFlushQueueImmediate"] = ^(NSArray<NSArray *> *calls){
|
return nil;
|
||||||
if (!weakSelf.valid || !calls) {
|
}];
|
||||||
|
|
||||||
|
[self addSynchronousHookWithName:@"nativeFlushQueueImmediate" usingBlock:^(NSArray<NSArray *> *calls){
|
||||||
|
RCTJSCExecutor *strongSelf = weakSelf;
|
||||||
|
if (!strongSelf.valid || !calls) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
[weakBridge handleBuffer:calls batchEnded:NO];
|
|
||||||
};
|
|
||||||
|
|
||||||
context[@"nativePerformanceNow"] = ^{
|
[strongSelf->_bridge handleBuffer:calls batchEnded:NO];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self addSynchronousHookWithName:@"nativePerformanceNow" usingBlock:^{
|
||||||
return @(CACurrentMediaTime() * 1000);
|
return @(CACurrentMediaTime() * 1000);
|
||||||
};
|
}];
|
||||||
|
|
||||||
#if RCT_DEV
|
#if RCT_DEV
|
||||||
|
|
||||||
if (RCTProfileIsProfiling()) {
|
if (RCTProfileIsProfiling()) {
|
||||||
context[@"__RCTProfileIsProfiling"] = @YES;
|
// Cheating, since it's not a "hook", but meh
|
||||||
|
[self addSynchronousHookWithName:@"__RCTProfileIsProfiling" usingBlock:@YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
CFMutableDictionaryRef cookieMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
|
CFMutableDictionaryRef cookieMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
|
||||||
context[@"nativeTraceBeginAsyncSection"] = ^(uint64_t tag, NSString *name, NSUInteger cookie) {
|
[self addSynchronousHookWithName:@"nativeTraceBeginAsyncSection" usingBlock:^(uint64_t tag, NSString *name, NSUInteger cookie) {
|
||||||
NSUInteger newCookie = RCTProfileBeginAsyncEvent(tag, name, nil);
|
NSUInteger newCookie = RCTProfileBeginAsyncEvent(tag, name, nil);
|
||||||
CFDictionarySetValue(cookieMap, (const void *)cookie, (const void *)newCookie);
|
CFDictionarySetValue(cookieMap, (const void *)cookie, (const void *)newCookie);
|
||||||
};
|
}];
|
||||||
|
|
||||||
context[@"nativeTraceEndAsyncSection"] = ^(uint64_t tag, NSString *name, NSUInteger cookie) {
|
[self addSynchronousHookWithName:@"nativeTraceEndAsyncSection" usingBlock:^(uint64_t tag, NSString *name, NSUInteger cookie) {
|
||||||
NSUInteger newCookie = (NSUInteger)CFDictionaryGetValue(cookieMap, (const void *)cookie);
|
NSUInteger newCookie = (NSUInteger)CFDictionaryGetValue(cookieMap, (const void *)cookie);
|
||||||
RCTProfileEndAsyncEvent(tag, @"js,async", newCookie, name, nil);
|
RCTProfileEndAsyncEvent(tag, @"js,async", newCookie, name, nil);
|
||||||
CFDictionaryRemoveValue(cookieMap, (const void *)cookie);
|
CFDictionaryRemoveValue(cookieMap, (const void *)cookie);
|
||||||
};
|
}];
|
||||||
|
|
||||||
context[@"nativeTraceBeginSection"] = ^(NSNumber *tag, NSString *profileName){
|
[self addSynchronousHookWithName:@"nativeTraceBeginSection" usingBlock:^(NSNumber *tag, NSString *profileName){
|
||||||
static int profileCounter = 1;
|
static int profileCounter = 1;
|
||||||
if (!profileName) {
|
if (!profileName) {
|
||||||
profileName = [NSString stringWithFormat:@"Profile %d", profileCounter++];
|
profileName = [NSString stringWithFormat:@"Profile %d", profileCounter++];
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_PROFILE_BEGIN_EVENT(tag.longLongValue, profileName, nil);
|
RCT_PROFILE_BEGIN_EVENT(tag.longLongValue, profileName, nil);
|
||||||
};
|
}];
|
||||||
|
|
||||||
context[@"nativeTraceEndSection"] = ^(NSNumber *tag) {
|
[self addSynchronousHookWithName:@"nativeTraceEndSection" usingBlock:^(NSNumber *tag) {
|
||||||
RCT_PROFILE_END_EVENT(tag.longLongValue, @"console", nil);
|
RCT_PROFILE_END_EVENT(tag.longLongValue, @"console", nil);
|
||||||
};
|
}];
|
||||||
|
|
||||||
RCTInstallJSCProfiler(_bridge, strongSelf->_context.ctx);
|
[self executeBlockOnJavaScriptQueue:^{
|
||||||
|
RCTInstallJSCProfiler(_bridge, self.context.ctx);
|
||||||
|
}];
|
||||||
|
|
||||||
for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) {
|
for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) {
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:strongSelf
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
selector:@selector(toggleProfilingFlag:)
|
selector:@selector(toggleProfilingFlag:)
|
||||||
name:event
|
name:event
|
||||||
object:nil];
|
object:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)toggleProfilingFlag:(NSNotification *)notification
|
- (void)toggleProfilingFlag:(NSNotification *)notification
|
||||||
|
|
Loading…
Reference in New Issue