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
|
||||
launchOptions:nil];
|
||||
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");
|
||||
(void)bridge;
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ expectErrorBlock:(BOOL(^)(NSString *error))expectErrorBlock
|
|||
// (we can only do this now, since it's been lazily initialized)
|
||||
id jsExecutor = [bridge valueForKeyPath:@"batchedBridge.javaScriptExecutor"];
|
||||
if ([jsExecutor isKindOfClass:[RCTJSCExecutor class]]) {
|
||||
weakJSContext = [jsExecutor valueForKey:@"context"];
|
||||
weakJSContext = [jsExecutor valueForKey:@"_context"];
|
||||
}
|
||||
[rootView removeFromSuperview];
|
||||
|
||||
|
|
|
@ -85,4 +85,10 @@ typedef void (^RCTJavaScriptCallback)(id json, NSError *error);
|
|||
*/
|
||||
- (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
|
||||
|
|
|
@ -215,99 +215,113 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef 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;
|
||||
[self executeBlockOnJavaScriptQueue:^{
|
||||
weakSelf.context.context[name] = block;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
__weak RCTJSCExecutor *weakSelf = self;
|
||||
[self addSynchronousHookWithName:@"noop" usingBlock:^{}];
|
||||
|
||||
[self addSynchronousHookWithName:@"nativeLoggingHook" usingBlock:^(NSString *message, NSNumber *logLevel) {
|
||||
RCTLogLevel level = RCTLogLevelInfo;
|
||||
if (logLevel) {
|
||||
level = MAX(level, logLevel.integerValue);
|
||||
}
|
||||
|
||||
_RCTLogJavaScriptInternal(level, message);
|
||||
}];
|
||||
|
||||
[self addSynchronousHookWithName:@"nativeRequireModuleConfig" usingBlock:^NSString *(NSString *moduleName) {
|
||||
RCTJSCExecutor *strongSelf = weakSelf;
|
||||
if (!strongSelf.isValid) {
|
||||
if (!strongSelf.valid) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSArray *config = [strongSelf->_bridge configForModuleName:moduleName];
|
||||
if (config) {
|
||||
return RCTJSONStringify(config, NULL);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}];
|
||||
|
||||
[self addSynchronousHookWithName:@"nativeFlushQueueImmediate" usingBlock:^(NSArray<NSArray *> *calls){
|
||||
RCTJSCExecutor *strongSelf = weakSelf;
|
||||
if (!strongSelf.valid || !calls) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strongSelf->_context) {
|
||||
JSContext *context = [JSContext new];
|
||||
strongSelf->_context = [[RCTJavaScriptContext alloc] initWithJSContext:context];
|
||||
}
|
||||
[strongSelf->_bridge handleBuffer:calls batchEnded:NO];
|
||||
}];
|
||||
|
||||
__weak RCTBridge *weakBridge = strongSelf->_bridge;
|
||||
JSContext *context = strongSelf->_context.context;
|
||||
|
||||
context[@"noop"] = ^{};
|
||||
|
||||
context[@"nativeLoggingHook"] = ^(NSString *message, NSNumber *logLevel) {
|
||||
RCTLogLevel level = RCTLogLevelInfo;
|
||||
if (logLevel) {
|
||||
level = MAX(level, logLevel.integerValue);
|
||||
}
|
||||
|
||||
_RCTLogJavaScriptInternal(level, message);
|
||||
};
|
||||
|
||||
context[@"nativeRequireModuleConfig"] = ^NSString *(NSString *moduleName) {
|
||||
if (!weakSelf.valid) {
|
||||
return nil;
|
||||
}
|
||||
NSArray *config = [weakBridge configForModuleName:moduleName];
|
||||
if (config) {
|
||||
return RCTJSONStringify(config, NULL);
|
||||
}
|
||||
return nil;
|
||||
};
|
||||
|
||||
context[@"nativeFlushQueueImmediate"] = ^(NSArray<NSArray *> *calls){
|
||||
if (!weakSelf.valid || !calls) {
|
||||
return;
|
||||
}
|
||||
[weakBridge handleBuffer:calls batchEnded:NO];
|
||||
};
|
||||
|
||||
context[@"nativePerformanceNow"] = ^{
|
||||
return @(CACurrentMediaTime() * 1000);
|
||||
};
|
||||
[self addSynchronousHookWithName:@"nativePerformanceNow" usingBlock:^{
|
||||
return @(CACurrentMediaTime() * 1000);
|
||||
}];
|
||||
|
||||
#if RCT_DEV
|
||||
if (RCTProfileIsProfiling()) {
|
||||
// Cheating, since it's not a "hook", but meh
|
||||
[self addSynchronousHookWithName:@"__RCTProfileIsProfiling" usingBlock:@YES];
|
||||
}
|
||||
|
||||
if (RCTProfileIsProfiling()) {
|
||||
context[@"__RCTProfileIsProfiling"] = @YES;
|
||||
}
|
||||
|
||||
CFMutableDictionaryRef cookieMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
|
||||
context[@"nativeTraceBeginAsyncSection"] = ^(uint64_t tag, NSString *name, NSUInteger cookie) {
|
||||
NSUInteger newCookie = RCTProfileBeginAsyncEvent(tag, name, nil);
|
||||
CFDictionarySetValue(cookieMap, (const void *)cookie, (const void *)newCookie);
|
||||
};
|
||||
|
||||
context[@"nativeTraceEndAsyncSection"] = ^(uint64_t tag, NSString *name, NSUInteger cookie) {
|
||||
NSUInteger newCookie = (NSUInteger)CFDictionaryGetValue(cookieMap, (const void *)cookie);
|
||||
RCTProfileEndAsyncEvent(tag, @"js,async", newCookie, name, nil);
|
||||
CFDictionaryRemoveValue(cookieMap, (const void *)cookie);
|
||||
};
|
||||
|
||||
context[@"nativeTraceBeginSection"] = ^(NSNumber *tag, NSString *profileName){
|
||||
static int profileCounter = 1;
|
||||
if (!profileName) {
|
||||
profileName = [NSString stringWithFormat:@"Profile %d", profileCounter++];
|
||||
}
|
||||
|
||||
RCT_PROFILE_BEGIN_EVENT(tag.longLongValue, profileName, nil);
|
||||
};
|
||||
|
||||
context[@"nativeTraceEndSection"] = ^(NSNumber *tag) {
|
||||
RCT_PROFILE_END_EVENT(tag.longLongValue, @"console", nil);
|
||||
};
|
||||
|
||||
RCTInstallJSCProfiler(_bridge, strongSelf->_context.ctx);
|
||||
|
||||
for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:strongSelf
|
||||
selector:@selector(toggleProfilingFlag:)
|
||||
name:event
|
||||
object:nil];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
CFMutableDictionaryRef cookieMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
|
||||
[self addSynchronousHookWithName:@"nativeTraceBeginAsyncSection" usingBlock:^(uint64_t tag, NSString *name, NSUInteger cookie) {
|
||||
NSUInteger newCookie = RCTProfileBeginAsyncEvent(tag, name, nil);
|
||||
CFDictionarySetValue(cookieMap, (const void *)cookie, (const void *)newCookie);
|
||||
}];
|
||||
|
||||
[self addSynchronousHookWithName:@"nativeTraceEndAsyncSection" usingBlock:^(uint64_t tag, NSString *name, NSUInteger cookie) {
|
||||
NSUInteger newCookie = (NSUInteger)CFDictionaryGetValue(cookieMap, (const void *)cookie);
|
||||
RCTProfileEndAsyncEvent(tag, @"js,async", newCookie, name, nil);
|
||||
CFDictionaryRemoveValue(cookieMap, (const void *)cookie);
|
||||
}];
|
||||
|
||||
[self addSynchronousHookWithName:@"nativeTraceBeginSection" usingBlock:^(NSNumber *tag, NSString *profileName){
|
||||
static int profileCounter = 1;
|
||||
if (!profileName) {
|
||||
profileName = [NSString stringWithFormat:@"Profile %d", profileCounter++];
|
||||
}
|
||||
|
||||
RCT_PROFILE_BEGIN_EVENT(tag.longLongValue, profileName, nil);
|
||||
}];
|
||||
|
||||
[self addSynchronousHookWithName:@"nativeTraceEndSection" usingBlock:^(NSNumber *tag) {
|
||||
RCT_PROFILE_END_EVENT(tag.longLongValue, @"console", nil);
|
||||
}];
|
||||
|
||||
[self executeBlockOnJavaScriptQueue:^{
|
||||
RCTInstallJSCProfiler(_bridge, self.context.ctx);
|
||||
}];
|
||||
|
||||
for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(toggleProfilingFlag:)
|
||||
name:event
|
||||
object:nil];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)toggleProfilingFlag:(NSNotification *)notification
|
||||
|
|
Loading…
Reference in New Issue