blob: 7f1e8f14c62e3c323e93f6fedf7bea0d967b23fd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#import "WXBridgeContext.h"
#import "WXJSCoreBridge.h"
#import "WXLog.h"
#import "WXUtility.h"
#import "WXModuleFactory.h"
#import "WXModuleProtocol.h"
#import "WXUtility.h"
#import "WXSDKError.h"
#import "WXMonitor.h"
#import "WXAssert.h"
#import "WXSDKManager.h"
#import "WXDebugTool.h"
#import "WXSDKInstance_private.h"
#import "WXAppConfiguration.h"
#import "WXInvocationConfig.h"
#import "WXComponentMethod.h"
#import "WXModuleMethod.h"
#import "WXCallJSMethod.h"
#import "WXSDKInstance_private.h"
#import "WXPrerenderManager.h"
#import "WXExceptionUtils.h"
#import "WXSDKEngine.h"
#import "WXPolyfillSet.h"
#import "WXJSExceptionProtocol.h"
#import "WXMonitor.h"
#import "WXAppMonitorProtocol.h"
#import "WXConfigCenterProtocol.h"
#import "WXSDKInstance_performance.h"
#import "JSContext+Weex.h"
#import "WXCoreBridge.h"
#import "WXJSFrameworkLoadProtocol.h"
#import "WXJSFrameworkLoadDefaultImpl.h"
#import "WXHandlerFactory.h"
#import "WXExtendCallNativeManager.h"
#import "WXDataRenderHandler.h"
#define SuppressPerformSelectorLeakWarning(Stuff) \
do { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
Stuff; \
_Pragma("clang diagnostic pop") \
} while (0)
@interface WXBridgeContext ()
@property (nonatomic, strong) id<WXBridgeProtocol> jsBridge;
@property (nonatomic, strong) id<WXBridgeProtocol> devToolSocketBridge;
@property (nonatomic, assign) BOOL debugJS;
//store the methods which will be executed from native to js
@property (nonatomic, strong) NSMutableDictionary *sendQueue;
//the instance stack
@property (nonatomic, strong) NSMutableArray *insStack;
//identify if the JSFramework has been loaded
@property (nonatomic) BOOL frameworkLoadFinished;
//store some methods temporarily before JSFramework is loaded
@property (nonatomic, strong) NSMutableArray *methodQueue;
// store service
@property (nonatomic, strong) NSMutableArray *jsServiceQueue;
@property (nonatomic, readonly) id<WXDataRenderHandler> dataRenderHandler;
@end
@implementation WXBridgeContext
@synthesize dataRenderHandler = _dataRenderHandler;
- (instancetype) init
{
self = [super init];
if (self) {
_methodQueue = [NSMutableArray new];
_frameworkLoadFinished = NO;
_jsServiceQueue = [NSMutableArray new];
_dataRenderHandler = [WXHandlerFactory handlerForProtocol:@protocol(WXDataRenderHandler)];
if (!_dataRenderHandler) {
Class handlerClass = NSClassFromString(@"WXEagleHandler");
if (handlerClass) {
_dataRenderHandler = [[handlerClass alloc] init];
[WXSDKEngine registerHandler:_dataRenderHandler withProtocol:@protocol(WXDataRenderHandler)];
}
}
}
return self;
}
- (id<WXBridgeProtocol>)jsBridge
{
WXAssertBridgeThread();
_debugJS = [WXDebugTool isDevToolDebug];
Class bridgeClass = _debugJS ? NSClassFromString(@"WXDebugger") : [WXJSCoreBridge class];
if (_jsBridge && [_jsBridge isKindOfClass:bridgeClass]) {
return _jsBridge;
}
if (_jsBridge) {
[_methodQueue removeAllObjects];
_frameworkLoadFinished = NO;
}
// WXDebugger is a singleton actually and should not call its init twice.
_jsBridge = _debugJS ? [NSClassFromString(@"WXDebugger") alloc] : [[WXJSCoreBridge alloc] init];
[self registerGlobalFunctions];
return _jsBridge;
}
- (NSInteger)checkInstance:(WXSDKInstance *)instance
{
if (!instance) {
WXLogInfo(@"instance not found, maybe already destroyed");
return FALSE;
}
return TRUE;
}
- (void)registerGlobalFunctions
{
__weak typeof(self) weakSelf = self;
[_jsBridge registerCallNative:^NSInteger(NSString *instance, NSArray *tasks, NSString *callback) {
return [weakSelf invokeNative:instance tasks:tasks callback:callback];
}];
[_jsBridge registerCallUpdateComponentData:^NSInteger(NSString *instanceId, NSString *componentId, NSString *jsonData) {
if (weakSelf.dataRenderHandler) {
WXPerformBlockOnComponentThread(^{
long start = [WXUtility getUnixFixTimeMillis];
WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId];
[instance.apmInstance addUpdateComponentDataTimestamp:start];
[weakSelf.dataRenderHandler callUpdateComponentData:instanceId componentId:componentId jsonData:jsonData];
[instance.apmInstance addUpdateComponentDataTime:[WXUtility getUnixFixTimeMillis] - start];
});
}
else {
WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId];
WXComponentManager *manager = instance.componentManager;
if (manager.isValid) {
WXSDKErrCode errorCode = WX_KEY_EXCEPTION_DEGRADE_EAGLE_RENDER_ERROR;
NSError *error = [NSError errorWithDomain:WX_ERROR_DOMAIN code:errorCode userInfo:@{@"message":@"No data render handler found!"}];
WXPerformBlockOnComponentThread(^{
[manager renderFailed:error];
});
}
}
return 0;
}];
[_jsBridge registerCallAddElement:^NSInteger(NSString *instanceId, NSString *parentRef, NSDictionary *elementData, NSInteger index) {
if ([WXCustomPageBridge isCustomPage:instanceId]) {
[[WXCustomPageBridge sharedInstance] callAddElement:instanceId parentRef:parentRef data:elementData index:(int)index];
}
else {
WXPerformBlockOnComponentThread(^{
[WXCoreBridge callAddElement:instanceId parentRef:parentRef data:elementData index:(int)index];
});
}
return 0;
}];
[_jsBridge registerCallCreateBody:^NSInteger(NSString *instanceId, NSDictionary *bodyData) {
if ([WXCustomPageBridge isCustomPage:instanceId]) {
[[WXCustomPageBridge sharedInstance] callCreateBody:instanceId data:bodyData];
}
else {
WXPerformBlockOnComponentThread(^{
[WXCoreBridge callCreateBody:instanceId data:bodyData];
});
}
return 0;
}];
[_jsBridge registerCallRemoveElement:^NSInteger(NSString *instanceId, NSString *ref) {
if ([WXCustomPageBridge isCustomPage:instanceId]) {
[[WXCustomPageBridge sharedInstance] callRemoveElement:instanceId ref:ref];
}
else {
WXPerformBlockOnComponentThread(^{
[WXCoreBridge callRemoveElement:instanceId ref:ref];
});
}
return 0;
}];
[_jsBridge registerCallMoveElement:^NSInteger(NSString *instanceId, NSString *ref, NSString *parentRef, NSInteger index) {
if ([WXCustomPageBridge isCustomPage:instanceId]) {
[[WXCustomPageBridge sharedInstance] callMoveElement:instanceId ref:ref parentRef:parentRef index:(int)index];
}
else {
WXPerformBlockOnComponentThread(^{
[WXCoreBridge callMoveElement:instanceId ref:ref parentRef:parentRef index:(int)index];
});
}
return 0;
}];
[_jsBridge registerCallUpdateAttrs:^NSInteger(NSString *instanceId, NSString *ref, NSDictionary *attrsData) {
if ([WXCustomPageBridge isCustomPage:instanceId]) {
[[WXCustomPageBridge sharedInstance] callUpdateAttrs:instanceId ref:ref data:attrsData];
}
else {
WXPerformBlockOnComponentThread(^{
[WXCoreBridge callUpdateAttrs:instanceId ref:ref data:attrsData];
});
}
return 0;
}];
[_jsBridge registerCallUpdateStyle:^NSInteger(NSString *instanceId, NSString *ref, NSDictionary *stylesData) {
if ([WXCustomPageBridge isCustomPage:instanceId]) {
[[WXCustomPageBridge sharedInstance] callUpdateStyle:instanceId ref:ref data:stylesData];
}
else {
WXPerformBlockOnComponentThread(^{
[WXCoreBridge callUpdateStyle:instanceId ref:ref data:stylesData];
});
}
return 0;
}];
[_jsBridge registerCallAddEvent:^NSInteger(NSString *instanceId, NSString *ref, NSString *event) {
if ([WXCustomPageBridge isCustomPage:instanceId]) {
[[WXCustomPageBridge sharedInstance] callAddEvent:instanceId ref:ref event:event];
}
else {
WXPerformBlockOnComponentThread(^{
[WXCoreBridge callAddEvent:instanceId ref:ref event:event];
});
}
return 0;
}];
[_jsBridge registerCallRemoveEvent:^NSInteger(NSString *instanceId, NSString *ref, NSString *event) {
if ([WXCustomPageBridge isCustomPage:instanceId]) {
[[WXCustomPageBridge sharedInstance] callRemoveEvent:instanceId ref:ref event:event];
}
else {
WXPerformBlockOnComponentThread(^{
[WXCoreBridge callRemoveEvent:instanceId ref:ref event:event];
});
}
return 0;
}];
[_jsBridge registerCallCreateFinish:^NSInteger(NSString *instanceId) {
WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId];
[instance.apmInstance onStage:KEY_PAGE_STAGES_CREATE_FINISH];
if ([WXCustomPageBridge isCustomPage:instanceId]) {
[[WXCustomPageBridge sharedInstance] callCreateFinish:instanceId];
}
else {
WXPerformBlockOnComponentThread(^{
[WXCoreBridge callCreateFinish:instanceId];
});
}
return 0;
}];
if ([_jsBridge respondsToSelector:@selector(registerCallRefreshFinish:)]) {
[_jsBridge registerCallRefreshFinish:^NSInteger(NSString *instanceId) {
if ([WXCustomPageBridge isCustomPage:instanceId]) {
[[WXCustomPageBridge sharedInstance] callRefreshFinish:instanceId];
}
else {
WXPerformBlockOnComponentThread(^{
[WXCoreBridge callRefreshFinish:instanceId];
});
}
return 0;
}];
}
if ([_jsBridge respondsToSelector:@selector(registerCallUpdateFinish:)]) {
[_jsBridge registerCallUpdateFinish:^NSInteger(NSString *instanceId) {
if ([WXCustomPageBridge isCustomPage:instanceId]) {
[[WXCustomPageBridge sharedInstance] callUpdateFinish:instanceId];
}
else {
WXPerformBlockOnComponentThread(^{
[WXCoreBridge callUpdateFinish:instanceId];
});
}
return 0;
}];
}
[_jsBridge registerCallNativeModule:^NSInvocation*(NSString *instanceId, NSString *moduleName, NSString *methodName, NSArray *arguments, NSDictionary *options) {
WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId];
if (!instance) {
WXLogInfo(@"instance not found for callNativeModule:%@.%@, maybe already destroyed", moduleName, methodName);
return nil;
}
#ifdef DEBUG
WXLogDebug(@"flexLayout -> action: callNativeModule : %@ . %@",moduleName,methodName);
#endif
NSMutableDictionary * newOptions = options ? [options mutableCopy] : [NSMutableDictionary new];
NSMutableArray * newArguments = [arguments mutableCopy];
if ([instance.bundleType.lowercaseString isEqualToString:@"rax"]) {
// we need to adjust __weex_options__ params in arguments to options compatible with rax javaScript framework.
NSDictionary * weexOptions = nil;
for(int i = 0;i < [arguments count]; i ++) {
if ([arguments[i] isKindOfClass:[NSDictionary class]]) {
NSDictionary * dict = (NSDictionary*)arguments[i];
if (dict[@"__weex_options__"] && [dict[@"__weex_options__"] isKindOfClass:[NSDictionary class]]) {
weexOptions = dict;
[newOptions addEntriesFromDictionary:(NSDictionary*)(weexOptions[@"__weex_options__"])];
}
}
}
if (weexOptions) {
[newArguments removeObject:weexOptions];
}
}
WXModuleMethod *method = [[WXModuleMethod alloc] initWithModuleName:moduleName methodName:methodName arguments:[newArguments copy] options:[newOptions copy] instance:instance];
if(![moduleName isEqualToString:@"dom"] && instance.needPrerender){
[WXPrerenderManager storePrerenderModuleTasks:method forUrl:instance.scriptURL.absoluteString];
return nil;
}
BOOL intercepted = [instance moduleInterceptWithModuleName:moduleName methodName:methodName arguments:[newArguments copy] options:[newOptions copy]];
if (intercepted) {
return nil;
}
return [method invoke];
}];
[_jsBridge registerCallNativeComponent:^void(NSString *instanceId, NSString *componentRef, NSString *methodName, NSArray *args, NSDictionary *options) {
#ifdef DEBUG
WXLogDebug(@"flexLayout -> action: callNativeComponent ref:%@",componentRef);
#endif
WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId];
WXComponentMethod *method = [[WXComponentMethod alloc] initWithComponentRef:componentRef methodName:methodName arguments:args instance:instance];
[method invoke];
}];
}
- (NSMutableArray *)insStack
{
if (_insStack) return _insStack;
_insStack = [NSMutableArray array];
return _insStack;
}
- (WXSDKInstance *)topInstance
{
WXSDKInstance *topInstance = nil;
@synchronized(self) {
if (self.insStack.count > 0) {
topInstance = [WXSDKManager instanceForID:[self.insStack firstObject]];
}
}
return topInstance;
}
- (NSMutableDictionary *)sendQueue
{
WXAssertBridgeThread();
if (_sendQueue) return _sendQueue;
_sendQueue = [NSMutableDictionary dictionary];
return _sendQueue;
}
#pragma mark JS Bridge Management
- (NSInteger)invokeNative:(NSString *)instanceId tasks:(NSArray *)tasks callback:(NSString __unused*)callback
{
WXAssertBridgeThread();
if (!instanceId || !tasks) {
WX_MONITOR_FAIL(WXMTNativeRender, WX_ERR_JSFUNC_PARAM, @"JS call Native params error!");
return 0;
}
WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId];
if (!instance) {
WXLogInfo(@"instance already destroyed, task ignored");
return -1;
}
NSTimeInterval startTime = CACurrentMediaTime()*1000;
for (NSDictionary *task in tasks) {
NSString *methodName = task[@"method"];
NSArray *arguments = task[@"args"];
if (task[@"component"]) {
NSString *ref = task[@"ref"];
WXComponentMethod *method = [[WXComponentMethod alloc] initWithComponentRef:ref methodName:methodName arguments:arguments instance:instance];
[method invoke];
} else {
NSString *moduleName = task[@"module"];
NSDictionary *options = task[@"options"];
WXModuleMethod *method = [[WXModuleMethod alloc] initWithModuleName:moduleName methodName:methodName arguments:arguments options:options instance:instance];
[method invoke];
}
}
[self performSelector:@selector(_sendQueueLoop) withObject:nil];
if (!instance.isJSCreateFinish) {
NSTimeInterval diff = CACurrentMediaTime()*1000-startTime;
instance.performance.fsCallNativeNum++;
instance.performance.fsCallNativeTime = instance.performance.fsCallNativeTime + diff;
}
if (instance && !instance.apmInstance.isFSEnd) {
NSTimeInterval diff = CACurrentMediaTime()*1000 - startTime;
[instance.apmInstance updateFSDiffStats:KEY_PAGE_STATS_FS_CALL_NATIVE_NUM withDiffValue:1];
[instance.apmInstance updateFSDiffStats:KEY_PAGE_STATS_FS_CALL_NATIVE_TIME withDiffValue:diff];
}
return 1;
}
- (void)createInstance:(NSString *)instanceIdString
template:(NSString *)jsBundleString
options:(NSDictionary *)options
data:(id)data
{
WXAssertBridgeThread();
WXAssertParam(instanceIdString);
@synchronized(self) {
if (![self.insStack containsObject:instanceIdString]) {
if ([options[@"RENDER_IN_ORDER"] boolValue]) {
[self.insStack addObject:instanceIdString];
} else {
[self.insStack insertObject:instanceIdString atIndex:0];
}
}
}
WXSDKInstance *sdkInstance = [WXSDKManager instanceForID:instanceIdString];
if (!sdkInstance) {
return;
}
[sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_LOAD_BUNDLE_START];
//create a sendQueue bind to the current instance
NSMutableArray *sendQueue = [NSMutableArray array];
[self.sendQueue setValue:sendQueue forKey:instanceIdString];
if (sdkInstance.dataRender && ![options[@"EXEC_JS"] boolValue]) {
if (_dataRenderHandler) {
WXPerformBlockOnComponentThread(^{
[_dataRenderHandler createPage:instanceIdString template:jsBundleString options:options data:data];
});
}
else {
WXComponentManager *manager = sdkInstance.componentManager;
if (manager.isValid) {
WXSDKErrCode errorCode = WX_KEY_EXCEPTION_DEGRADE_EAGLE_RENDER_ERROR;
NSError *error = [NSError errorWithDomain:WX_ERROR_DOMAIN code:errorCode userInfo:@{@"message":@"No data render handler found!"}];
WXPerformBlockOnComponentThread(^{
[manager renderFailed:error];
});
}
}
return;
}
NSArray *args = nil;
WX_MONITOR_INSTANCE_PERF_START(WXFirstScreenJSFExecuteTime, [WXSDKManager instanceForID:instanceIdString]);
WX_MONITOR_INSTANCE_PERF_START(WXPTJSCreateInstance, [WXSDKManager instanceForID:instanceIdString]);
NSString * bundleType = [self _pareJSBundleType:instanceIdString jsBundleString:jsBundleString]; // bundleType can be Vue, Rax and the new framework.
if (bundleType) {
[sdkInstance.apmInstance setProperty:KEY_PAGE_PROPERTIES_BUNDLE_TYPE withValue:bundleType];
NSMutableDictionary *newOptions = [options mutableCopy];
if (!options) {
newOptions = [NSMutableDictionary new];
}
NSDictionary* immutableEnvDict = [[WXUtility getEnvironment] copy];
if (immutableEnvDict) {
[newOptions addEntriesFromDictionary:@{@"env":immutableEnvDict}];
}
newOptions[@"bundleType"] = bundleType;
__block NSString *raxAPIScript = nil;
__block NSString *raxAPIScriptPath = nil;
id<WXJSFrameworkLoadProtocol> handler = [WXHandlerFactory handlerForProtocol:@protocol(WXJSFrameworkLoadProtocol)];
sdkInstance.bundleType = bundleType;
if ([bundleType.lowercaseString isEqualToString:@"rax"]) {
if (handler && [handler respondsToSelector:@selector(loadRaxFramework:)]) {
[handler loadRaxFramework:^(NSString *path, NSString *script) {
raxAPIScriptPath = path;
raxAPIScript = script;
}];
}
}
if ([WXDebugTool isDevToolDebug]) {
[self callJSMethod:@"createInstanceContext" args:@[instanceIdString, newOptions, data?:@[],raxAPIScript?:@""]];
if ([NSURL URLWithString:sdkInstance.pageName]) {
[sdkInstance.instanceJavaScriptContext executeJavascript:jsBundleString withSourceURL:[NSURL URLWithString:sdkInstance.pageName]];
} else {
[sdkInstance.instanceJavaScriptContext executeJavascript:jsBundleString];
}
} else {
NSDictionary* immutableOptions = [newOptions copy];
sdkInstance.callCreateInstanceContext = [NSString stringWithFormat:@"instanceId:%@\noptions:%@\ndata:%@", instanceIdString, immutableOptions, data];
//add instanceId to weexContext ,if fucn createInstanceContext failure ,then we will know which instance has problem (exceptionhandler)
self.jsBridge.javaScriptContext[@"wxExtFuncInfo"]= @{
@"func":@"createInstanceContext",
@"arg":@"start",
@"instanceId":sdkInstance.instanceId?:@"unknownId"
};
__weak typeof(self) weakSelf = self;
[sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_LOAD_BUNDLE_END];
[self callJSMethod:@"createInstanceContext" args:@[instanceIdString, immutableOptions, data?:@[]] onContext:nil completion:^(JSValue *instanceContextEnvironment) {
if (sdkInstance.pageName) {
[sdkInstance.instanceJavaScriptContext.javaScriptContext setName:sdkInstance.pageName];
}
weakSelf.jsBridge.javaScriptContext[@"wxExtFuncInfo"]= nil;
NSArray* allKeys = [WXUtility extractPropertyNamesOfJSValueObject:instanceContextEnvironment];
sdkInstance.createInstanceContextResult = [NSString stringWithFormat:@"%@", allKeys];
JSGlobalContextRef instanceContextRef = sdkInstance.instanceJavaScriptContext.javaScriptContext.JSGlobalContextRef;
JSObjectRef instanceGlobalObject = JSContextGetGlobalObject(instanceContextRef);
for (NSString * key in allKeys) {
JSStringRef propertyName = JSStringCreateWithUTF8CString([key cStringUsingEncoding:NSUTF8StringEncoding]);
if ([key isEqualToString:@"Vue"]) {
JSObjectSetPrototype(instanceContextRef, JSValueToObject(instanceContextRef, [instanceContextEnvironment valueForProperty:key].JSValueRef, NULL), JSObjectGetPrototype(instanceContextRef, instanceGlobalObject));
}
JSObjectSetProperty(instanceContextRef, instanceGlobalObject, propertyName, [instanceContextEnvironment valueForProperty:key].JSValueRef, 0, NULL);
JSStringRelease(propertyName);
}
if (WX_SYS_VERSION_LESS_THAN(@"10.2")) {
if (handler && [handler respondsToSelector:@selector(loadPolyfillFramework:)]) {
[handler loadPolyfillFramework:^(NSString *path, NSString *script) {
if (script) {
[sdkInstance.instanceJavaScriptContext executeJavascript:script withSourceURL:[NSURL URLWithString:path]];
} else {
WXLogError(@"weex-pollyfill can not found");
}
}];
}
}
if (raxAPIScript) {
[sdkInstance.instanceJavaScriptContext executeJavascript:raxAPIScript withSourceURL:[NSURL URLWithString:raxAPIScriptPath]];
NSArray* allKeys = [WXUtility extractPropertyNamesOfJSValueObject:sdkInstance.instanceJavaScriptContext.javaScriptContext.globalObject];
sdkInstance.executeRaxApiResult = [NSString stringWithFormat:@"%@", allKeys];
}
NSDictionary* funcInfo = @{
@"func":@"createInstance",
@"arg":@"start",
@"instanceId":sdkInstance.instanceId?:@"unknownId"
};
sdkInstance.instanceJavaScriptContext.javaScriptContext[@"wxExtFuncInfo"]= funcInfo;
if ([NSURL URLWithString:sdkInstance.pageName] || sdkInstance.scriptURL) {
[sdkInstance.instanceJavaScriptContext executeJavascript:jsBundleString withSourceURL:[NSURL URLWithString:sdkInstance.pageName]?:sdkInstance.scriptURL];
} else {
[sdkInstance.instanceJavaScriptContext executeJavascript:jsBundleString];
}
sdkInstance.instanceJavaScriptContext.javaScriptContext[@"wxExtFuncInfo"] = nil;
[sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_EXECUTE_BUNDLE_END];
WX_MONITOR_INSTANCE_PERF_END(WXPTJSCreateInstance, [WXSDKManager instanceForID:instanceIdString]);
}];
}
} else {
[sdkInstance.apmInstance setProperty:KEY_PAGE_PROPERTIES_BUNDLE_TYPE withValue:@"other"];
[sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_LOAD_BUNDLE_END];
if (data){
args = @[instanceIdString, jsBundleString, options ?: @{}, data];
} else {
args = @[instanceIdString, jsBundleString, options ?: @{}];
}
NSDictionary* funcInfo = @{
@"func":@"createInstance",
@"arg":@"start",
@"instanceId":sdkInstance.instanceId?:@"unknownId"
};
sdkInstance.instanceJavaScriptContext.javaScriptContext[@"wxExtFuncInfo"] = funcInfo;
[self callJSMethod:@"createInstance" args:args];
sdkInstance.instanceJavaScriptContext.javaScriptContext[@"wxExtFuncInfo"] = nil;
[sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_EXECUTE_BUNDLE_END];
WX_MONITOR_INSTANCE_PERF_END(WXPTJSCreateInstance, [WXSDKManager instanceForID:instanceIdString]);
}
}
- (BOOL) _shouldMountExtInfoToInstanceContxt
{
id configCenter = [WXSDKEngine handlerForProtocol:@protocol(WXConfigCenterProtocol)];
BOOL shouldMountInstanceContextExtInfo = YES;
if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) {
shouldMountInstanceContextExtInfo = [[configCenter configForKey:@"iOS_weex_ext_config.shouldMountInstanceContextExtInfo" defaultValue:@YES isDefault:NULL] boolValue];
}
return shouldMountInstanceContextExtInfo;
}
- (void)createInstance:(NSString *)instanceIdString
contents:(NSData *)contents
options:(NSDictionary *)options
data:(id)data
{
WXAssertBridgeThread();
WXAssertParam(instanceIdString);
@synchronized(self) {
if (![self.insStack containsObject:instanceIdString]) {
if ([options[@"RENDER_IN_ORDER"] boolValue]) {
[self.insStack addObject:instanceIdString];
} else {
[self.insStack insertObject:instanceIdString atIndex:0];
}
}
}
WXSDKInstance *sdkInstance = [WXSDKManager instanceForID:instanceIdString];
[sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_LOAD_BUNDLE_START];
//create a sendQueue bind to the current instance
NSMutableArray *sendQueue = [NSMutableArray array];
[self.sendQueue setValue:sendQueue forKey:instanceIdString];
if (sdkInstance.dataRender) {
if (_dataRenderHandler) {
WXPerformBlockOnComponentThread(^{
[_dataRenderHandler createPage:instanceIdString contents:contents options:options data:data];
});
}
else {
WXComponentManager *manager = sdkInstance.componentManager;
if (manager.isValid) {
WXSDKErrCode errorCode = WX_KEY_EXCEPTION_DEGRADE_EAGLE_RENDER_ERROR;
NSError *error = [NSError errorWithDomain:WX_ERROR_DOMAIN code:errorCode userInfo:@{@"message":@"No data render handler found!"}];
WXPerformBlockOnComponentThread(^{
[manager renderFailed:error];
});
}
}
return;
}
}
- (NSString *)_pareJSBundleType:(NSString*)instanceIdString jsBundleString:(NSString*)jsBundleString
{
NSString * bundleType = nil;
WXSDKInstance * instance = [WXSDKManager instanceForID:instanceIdString];
NSURLComponents * urlComponent = [NSURLComponents componentsWithString:instance.pageName?:@""];
for (NSURLQueryItem * queryItem in urlComponent.queryItems) {
if ([queryItem.name isEqualToString:@"bundleType"] && [@[@"Vue", @"Rax", @"vue", @"rax"] containsObject:queryItem.value]) {
bundleType = queryItem.value;
return bundleType;
}
}
// find first character that is not space or new line character
NSCharacterSet* voidCharSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSUInteger length = [jsBundleString length];
NSUInteger validCharacter = 0;
while (validCharacter < length && [voidCharSet characterIsMember:[jsBundleString characterAtIndex:validCharacter]]) {
validCharacter ++;
}
if (validCharacter >= length) {
return bundleType;
}
@try {
jsBundleString = [jsBundleString substringWithRange:NSMakeRange(validCharacter, MIN(100, length - validCharacter))];
}
@catch (NSException* e) {//!OCLint
}
if ([jsBundleString length] == 0) {
return bundleType;
}
static NSRegularExpression* headerExp = nil;
static NSRegularExpression* vueExp = nil;
static NSRegularExpression* raxExp = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
headerExp = [NSRegularExpression regularExpressionWithPattern:@"^\\s*\\/\\/ *(\\{[^}]*\\}) *\\r?\\n" options:NSRegularExpressionCaseInsensitive error:NULL];
vueExp = [NSRegularExpression regularExpressionWithPattern:@"(use)(\\s+)(weex:vue)" options:NSRegularExpressionCaseInsensitive error:NULL];
raxExp = [NSRegularExpression regularExpressionWithPattern:@"(use)(\\s+)(weex:rax)" options:NSRegularExpressionCaseInsensitive error:NULL];
});
if ( [self _isParserByRegEx]) {
NSTextCheckingResult *match = [headerExp firstMatchInString:jsBundleString options:0 range:NSMakeRange(0, jsBundleString.length)];
if (match) {
NSString* bundleTypeStr = [jsBundleString substringWithRange:match.range];
bundleTypeStr = [bundleTypeStr stringByReplacingOccurrencesOfString:@"//" withString:@""];
id vale = [WXUtility objectFromJSON:bundleTypeStr];
bundleType = [vale objectForKey:@"framework"];
}else{
NSTextCheckingResult *match = [vueExp firstMatchInString:jsBundleString options:0 range:NSMakeRange(0, jsBundleString.length)];
if (match) {
bundleType = [jsBundleString substringWithRange:match.range];
return bundleType;
}
match = [raxExp firstMatchInString:jsBundleString options:0 range:NSMakeRange(0, jsBundleString.length)];
if (match) {
bundleType = [jsBundleString substringWithRange:match.range];
}
}
}else{
if ([jsBundleString hasPrefix:@"// { \"framework\": \"Vue\""] || [jsBundleString hasPrefix:@"// { \"framework\": \"vue\""]) {
bundleType = @"Vue";
} else if ([jsBundleString hasPrefix:@"// { \"framework\": \"Rax\""] || [jsBundleString hasPrefix:@"// { \"framework\": \"rax\""] || [jsBundleString hasPrefix:@"// {\"framework\" : \"Rax\"}"] || [jsBundleString hasPrefix:@"// {\"framework\" : \"rax\"}"]) {
bundleType = @"Rax";
}else {
NSTextCheckingResult *match = [vueExp firstMatchInString:jsBundleString options:0 range:NSMakeRange(0, jsBundleString.length)];
if (match) {
bundleType = [jsBundleString substringWithRange:match.range];
return bundleType;
}
match = [raxExp firstMatchInString:jsBundleString options:0 range:NSMakeRange(0, jsBundleString.length)];
if (match) {
bundleType = [jsBundleString substringWithRange:match.range];
}
}
}
return bundleType;
}
- (bool)_isParserByRegEx
{
return false;
// bool useRegEx = true;
// id<WXConfigCenterProtocol> configCenter = [WXSDKEngine handlerForProtocol:@protocol(WXConfigCenterProtocol)];
//
// if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) {
// useRegEx = [[configCenter configForKey:@"iOS_weex_ext_config.parserTypeByRegEx" defaultValue:@(YES) isDefault:NULL] boolValue];
// }
// return useRegEx;
}
- (void)destroyInstance:(NSString *)instance
{
WXAssertBridgeThread();
WXAssertParam(instance);
//remove this instance exception history
[WXExceptionUtils removeExceptionHistory:instance];
//remove instance from stack
@synchronized(self) {
[self.insStack removeObject:instance];
}
if(_jsBridge && [_jsBridge respondsToSelector:@selector(removeTimers:)]){
[_jsBridge removeTimers:instance];
}
if(self.sendQueue[instance]){
[self.sendQueue removeObjectForKey:instance];
}
WXSDKInstance *sdkInstance = [WXSDKManager instanceForID:instance];
if (!sdkInstance.wlasmRender) {
[self callJSMethod:@"destroyInstance" args:@[instance]];
}
}
- (void)forceGarbageCollection
{
if ([self.jsBridge respondsToSelector:@selector(garbageCollect)]) {
[self.jsBridge garbageCollect];
}
}
- (void)refreshInstance:(NSString *)instance
data:(id)data
{
WXAssertBridgeThread();
WXAssertParam(instance);
if (!data) return;
WXSDKInstance *sdkInstance = [WXSDKManager instanceForID:instance];
if (sdkInstance.dataRender) {
if (!_dataRenderHandler) {
WXComponentManager *manager = sdkInstance.componentManager;
if (manager.isValid) {
WXSDKErrCode errorCode = WX_KEY_EXCEPTION_DEGRADE_EAGLE_RENDER_ERROR;
NSError *error = [NSError errorWithDomain:WX_ERROR_DOMAIN code:errorCode userInfo:@{@"message":@"No data render handler found!"}];
WXPerformBlockOnComponentThread(^{
[manager renderFailed:error];
});
}
return;
}
WXPerformBlockOnComponentThread(^{
if ([data isKindOfClass:[NSDictionary class]]) {
[_dataRenderHandler refreshDataRenderInstance:instance data:[WXUtility JSONString:data]];
} else if ([data isKindOfClass:[NSString class]]) {
[_dataRenderHandler refreshDataRenderInstance:instance data:data];
}
});
[[WXSDKManager bridgeMgr] callJSMethod:@"callJS" args:@[instance, @[@{@"method":@"fireEvent", @"args":@[@"", @"refresh", [WXUtility objectFromJSON:@"[]"], @"", @{@"params":@[@{@"data":data}]}]}]]];
} else {
[self callJSMethod:@"refreshInstance" args:@[instance, data]];
}
}
- (void)updateState:(NSString *)instance data:(id)data
{
WXAssertBridgeThread();
WXAssertParam(instance);
//[self.jsBridge callJSMethod:@"updateState" args:@[instance, data]];
}
- (void)executeJsFramework:(NSString *)script
{
WXAssertBridgeThread();
WXAssertParam(script);
WX_MONITOR_PERF_START(WXPTFrameworkExecute);
[self.jsBridge executeJSFramework:script];
WX_MONITOR_PERF_END(WXPTFrameworkExecute);
if ([self.jsBridge exception]) {
NSString *exception = [[self.jsBridge exception] toString];
NSMutableString *errMsg = [NSMutableString stringWithFormat:@"[WX_KEY_EXCEPTION_SDK_INIT_JSFM_INIT_FAILED] %@",exception];
[WXExceptionUtils commitCriticalExceptionRT:@"WX_KEY_EXCEPTION_SDK_INIT" errCode:[NSString stringWithFormat:@"%d", WX_KEY_EXCEPTION_SDK_INIT] function:@"" exception:errMsg extParams:nil];
WX_MONITOR_FAIL(WXMTJSFramework, WX_ERR_JSFRAMEWORK_EXECUTE, errMsg);
} else {
WX_MONITOR_SUCCESS(WXMTJSFramework);
//the JSFramework has been load successfully.
self.frameworkLoadFinished = YES;
[self executeAllJsService];
JSValue *frameworkVersion = [self.jsBridge callJSMethod:@"getJSFMVersion" args:nil];
if (frameworkVersion && [frameworkVersion isString]) {
[WXAppConfiguration setJSFrameworkVersion:[frameworkVersion toString]];
}
if (script) {
[WXAppConfiguration setJSFrameworkLibSize:[script lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
}
//execute methods which has been stored in methodQueue temporarily.
for (NSDictionary *method in _methodQueue) {
[self callJSMethod:method[@"method"] args:method[@"args"]];
}
[_methodQueue removeAllObjects];
WX_MONITOR_PERF_END(WXPTInitalize);
};
}
- (void)executeJsMethod:(WXCallJSMethod *)method
{
WXAssertBridgeThread();
if (!method.instance) {
WXLogError(@"Instance doesn't exist!");
return;
}
NSMutableArray *sendQueue = self.sendQueue[method.instance.instanceId];
if (!sendQueue) {
WXLogInfo(@"No send queue for instance:%@, may it has been destroyed so method:%@ is ignored", method.instance, method.methodName);
return;
}
[sendQueue addObject:method];
[self performSelector:@selector(_sendQueueLoop) withObject:nil];
}
- (void)callJSMethod:(NSString *)method args:(NSArray *)args onContext:(id<WXBridgeProtocol>)bridge completion:(void (^)(JSValue * value))completion
{
NSMutableArray *newArg = nil;
if (!bridge) {
bridge = self.jsBridge;
}
if (self.frameworkLoadFinished) {
WXLogDebug(@"Calling JS... method:%@, args:%@", method, args);
if (([bridge isKindOfClass:[WXJSCoreBridge class]]) ||
([bridge isKindOfClass:NSClassFromString(@"WXDebugger") ]) ) {
JSValue *value = [bridge callJSMethod:method args:args];
if (completion) {
completion(value);
}
} else {
[bridge callJSMethod:method args:args];
}
} else {
newArg = [args mutableCopy];
if (completion) {
[newArg addObject:completion];
}
[_methodQueue addObject:@{@"method":method, @"args":[newArg copy]}];
}
}
- (JSValue *)excuteJSMethodWithResult:(WXCallJSMethod *)method
{
WXAssertBridgeThread();
return [self.jsBridge callJSMethod:@"callJS" args:@[method.instance.instanceId, @[[method callJSTask]]]];
}
- (void)executeAllJsService
{
for(NSDictionary *service in _jsServiceQueue) {
NSString *script = [service valueForKey:@"script"];
NSString *name = [service valueForKey:@"name"];
[self executeJsService:script withName:name];
}
[_jsServiceQueue removeAllObjects];
}
- (void)executeJsService:(NSString *)script withName:(NSString *)name
{
if(self.frameworkLoadFinished) {
WXAssert(script, @"param script required!");
if ([self.jsBridge respondsToSelector:@selector(javaScriptContext)]) {
NSDictionary* funcInfo = @{
@"func":@"executeJsService",
@"arg":name?:@"unsetScriptName"
};
self.jsBridge.javaScriptContext[@"wxExtFuncInfo"] = funcInfo;
}
[self.jsBridge executeJavascript:script];
if ([self.jsBridge respondsToSelector:@selector(javaScriptContext)]) {
self.jsBridge.javaScriptContext[@"wxExtFuncInfo"] = nil;
}
if ([self.jsBridge exception]) {
NSString *exception = [[self.jsBridge exception] toString];
NSMutableString *errMsg = [NSMutableString stringWithFormat:@"[WX_KEY_EXCEPTION_INVOKE_JSSERVICE_EXECUTE] name:%@,arg:%@,exception :$@",name,exception];
[WXExceptionUtils commitCriticalExceptionRT:@"WX_KEY_EXCEPTION_INVOKE" errCode:[NSString stringWithFormat:@"%d", WX_KEY_EXCEPTION_INVOKE] function:@"executeJsService" exception:errMsg extParams:nil];
WX_MONITOR_FAIL(WXMTJSService, WX_ERR_JSFRAMEWORK_EXECUTE, errMsg);
}
}else {
[_jsServiceQueue addObject:@{
@"name": name,
@"script": script
}];
}
}
- (void)registerModules:(NSDictionary *)modules
{
WXAssertBridgeThread();
if(!modules) return;
[self callJSMethod:@"registerModules" args:@[modules]];
if (_dataRenderHandler) {
WXPerformBlockOnComponentThread(^{
[_dataRenderHandler registerModules:modules];
});
}
}
- (void)registerComponents:(NSArray *)components
{
WXAssertBridgeThread();
if(!components) return;
[self callJSMethod:@"registerComponents" args:@[components]];
if (_dataRenderHandler) {
WXPerformBlockOnComponentThread(^{
[_dataRenderHandler registerComponents:components];
});
}
}
- (void)callJSMethod:(NSString *)method args:(NSArray *)args
{
if (self.frameworkLoadFinished) {
[self.jsBridge callJSMethod:method args:args];
}
else {
[_methodQueue addObject:@{@"method":method, @"args":args}];
}
}
- (void)resetEnvironment
{
[_jsBridge resetEnvironment];
}
#pragma mark JS Debug Management
- (void)connectToDevToolWithUrl:(NSURL *)url
{
id webSocketBridge = [NSClassFromString(@"WXDebugger") alloc];
if(!webSocketBridge || ![webSocketBridge respondsToSelector:NSSelectorFromString(@"connectToURL:")]) {
return;
} else {
SuppressPerformSelectorLeakWarning(
[webSocketBridge performSelector:NSSelectorFromString(@"connectToURL:") withObject:url]
);
}
}
- (void)connectToWebSocket:(NSURL *)url
{
if (NSClassFromString(@"WXDebugLoggerBridge")) {
_devToolSocketBridge = [[NSClassFromString(@"WXDebugLoggerBridge") alloc] initWithURL:url];
}
}
- (void)logToWebSocket:(NSString *)flag message:(NSString *)message
{
[_devToolSocketBridge callJSMethod:@"__logger" args:@[flag, message]];
}
#pragma mark Private Mehtods
- (void)_sendQueueLoop
{
WXAssertBridgeThread();
BOOL hasTask = NO;
NSMutableArray *tasks = [NSMutableArray array];
NSString *execIns = nil;
@synchronized(self) {
for (NSString *instance in self.insStack) {
NSMutableArray *sendQueue = self.sendQueue[instance];
if(sendQueue.count > 0){
hasTask = YES;
for(WXCallJSMethod *method in sendQueue){
[tasks addObject:[method callJSTask]];
}
[sendQueue removeAllObjects];
execIns = instance;
break;
}
}
}
if ([tasks count] > 0 && execIns) {
WXSDKInstance * execInstance = [WXSDKManager instanceForID:execIns];
NSTimeInterval start = CACurrentMediaTime()*1000;
if (execInstance.instanceJavaScriptContext && execInstance.bundleType) {
[self callJSMethod:@"__WEEX_CALL_JAVASCRIPT__" args:@[execIns, [tasks copy]] onContext:execInstance.instanceJavaScriptContext completion:nil];
} else {
[self callJSMethod:@"callJS" args:@[execIns, [tasks copy]]];
}
if (execInstance) {
NSTimeInterval diff = CACurrentMediaTime()*1000 - start;
execInstance.performance.callJsNum++;
execInstance.performance.callJsTime = execInstance.performance.callJsTime+ diff;
}
if (execInstance && !execInstance.apmInstance.isFSEnd) {
NSTimeInterval diff = CACurrentMediaTime()*1000 - start;
[execInstance.apmInstance updateFSDiffStats:KEY_PAGE_STATS_FS_CALL_JS_NUM withDiffValue:1];
[execInstance.apmInstance updateFSDiffStats:KEY_PAGE_STATS_FS_CALL_JS_TIME withDiffValue:diff];
}
}
if (hasTask) {
[self performSelector:@selector(_sendQueueLoop) withObject:nil];
}
}
+ (void)mountContextEnvironment:(JSContext*)context
{
NSDictionary *data = [WXUtility getEnvironment];
context[@"WXEnvironment"] = data;
// use latin1 encode also named ISO/IEC_8859-1 more: https://en.wikipedia.org/wiki/ISO/IEC_8859-1
// 8-bit single-byte coded graphic character sets as webkit does
// https://github.com/WebKit/webkit/blob/master/Source/WebCore/page/Base64Utilities.cpp
context[@"btoa"] = ^(JSValue *value ) {
NSData *nsdata = [[value toString]
dataUsingEncoding:NSISOLatin1StringEncoding];
NSString *base64Encoded = [nsdata base64EncodedStringWithOptions:0];
return base64Encoded;
};
context[@"atob"] = ^(JSValue *value ) {
NSData *nsdataFromBase64String = [[NSData alloc]
initWithBase64EncodedString:[value toString] options:0];
NSString *base64Decoded = [[NSString alloc]
initWithData:nsdataFromBase64String encoding:NSISOLatin1StringEncoding];
return base64Decoded;
};
// Avoid exceptionHandler be recursively invoked and finally cause stack overflow.
static BOOL gInExceptionHandler = NO;
context.exceptionHandler = ^(JSContext *context, JSValue *exception){
if (gInExceptionHandler) {
return;
}
gInExceptionHandler = YES;
@try {
BOOL tryFindInstanceInfoInGlobalContext = NO;
context.exception = exception;
NSString *errorCode = [NSString stringWithFormat:@"%d", WX_KEY_EXCEPTION_WXBRIDGE];;
NSString *bundleUrl = nil;
NSString *message = nil;
NSDictionary *userInfo = nil;
BOOL commitException = YES;
WXSDKInstance * instance = nil;
if (context.instanceId) {
// instance page javaScript runtime exception
instance = [WXSDKManager instanceForID:context.instanceId];
if (instance) {
// instance already existed
commitException = YES;
} else {
// instance already destroyed
commitException = NO;
}
} else {
// weex-main-jsfm.js runtime exception throws
message = [NSString stringWithFormat:@"[WX_KEY_EXCEPTION_WXBRIDGE] [%@:%@:%@] %@ js stack: %@", exception[@"sourceURL"], exception[@"line"], exception[@"column"], [exception toString], [exception[@"stack"] toObject]];
if (!JSValueIsUndefined(context.JSGlobalContextRef, exception[@"sourceURL"].JSValueRef)) {
bundleUrl = exception[@"sourceURL"].toString;
} else {
bundleUrl = @"weex-main-jsfm";
}
userInfo = [NSDictionary dictionary];
}
NSDictionary* wxExtFuncInfo = [context[@"wxExtFuncInfo"] toDictionary];
NSString* recordFunc = [wxExtFuncInfo objectForKey:@"func"];
NSString* recordArg = [wxExtFuncInfo objectForKey:@"arg"];
NSString* recordInsstanceId = [wxExtFuncInfo objectForKey:@"instanceId"];
if (nil == instance) {
instance = [WXSDKManager instanceForID:recordInsstanceId];
tryFindInstanceInfoInGlobalContext = nil!= instance;
}
if(nil != instance && [recordFunc isEqualToString:@"createInstance"] && !instance.apmInstance.hasAddView){
errorCode = [NSString stringWithFormat:@"%d", WX_KEY_EXCEPTION_EMPTY_SCREEN_JS];
}
if (instance) {
bundleUrl = instance.pageName?:([instance.scriptURL absoluteString]?:@"WX_KEY_EXCEPTION_WXBRIDGE");
message = [NSString stringWithFormat:@"[WX_KEY_EXCEPTION_WXBRIDGE] exception: %@\n stack:%@",[exception toString],[exception[@"stack"] toObject]];
userInfo = @{@"jsMainBundleStringContentLength":instance.userInfo[@"jsMainBundleStringContentLength"]?:@"",
@"jsMainBundleStringContentMd5":instance.userInfo[@"jsMainBundleStringContentMd5"]?:@"",
@"sourceURL":[NSString stringWithFormat:@"%@:%@:%@",exception[@"sourceURL"],exception[@"line"],exception[@"column"]],
@"callCreateInstanceContext":instance.callCreateInstanceContext?:@"",
@"createInstanceContextResult": instance.createInstanceContextResult?:@"",
@"executeRaxApiResult":instance.executeRaxApiResult?:@""
};
}
if (commitException) {
NSMutableDictionary* reportInfo = [[NSMutableDictionary alloc] initWithDictionary:[userInfo mutableCopy]];
[reportInfo setObject:context.name?:@"unknownContextName" forKey:@"wxContextName"];
WXJSExceptionInfo * jsExceptionInfo = [[WXJSExceptionInfo alloc] initWithInstanceId:instance.instanceId bundleUrl:bundleUrl errorCode:errorCode functionName:[NSString stringWithFormat:@"func: %@ arg:%@",recordFunc,recordArg]?:@"exceptionHandler" exception:message userInfo:reportInfo];
if (nil == instance) {
[WXExceptionUtils commitCriticalExceptionRT:jsExceptionInfo];
}else{
[WXExceptionUtils commitCriticalExceptionRT:jsExceptionInfo.instanceId errCode:jsExceptionInfo.errorCode function:jsExceptionInfo.functionName exception:jsExceptionInfo.exception extParams:jsExceptionInfo.userInfo];
}
WX_MONITOR_FAIL(WXMTJSBridge, WX_ERR_JS_EXECUTE, message);
if (instance.onJSRuntimeException && !tryFindInstanceInfoInGlobalContext) {
instance.onJSRuntimeException(jsExceptionInfo);
}
}
}
@finally {
gInExceptionHandler = NO;
}
};
context[@"console"][@"error"] = ^(){
[WXBridgeContext handleConsoleOutputWithArgument:[JSContext currentArguments] logLevel:WXLogFlagError];
};
context[@"console"][@"warn"] = ^(){
[WXBridgeContext handleConsoleOutputWithArgument:[JSContext currentArguments] logLevel:WXLogFlagWarning];
};
context[@"console"][@"info"] = ^(){
[WXBridgeContext handleConsoleOutputWithArgument:[JSContext currentArguments] logLevel:WXLogFlagInfo];
};
context[@"console"][@"debug"] = ^(){
[WXBridgeContext handleConsoleOutputWithArgument:[JSContext currentArguments] logLevel:WXLogFlagDebug];
};
context[@"console"][@"log"] = ^(){
[WXBridgeContext handleConsoleOutputWithArgument:[JSContext currentArguments] logLevel:WXLogFlagLog];
};
context[@"nativeLog"] = ^() {
static NSDictionary *levelMap;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
levelMap = @{
@"__ERROR": @(WXLogFlagError),
@"__WARN": @(WXLogFlagWarning),
@"__INFO": @(WXLogFlagInfo),
@"__DEBUG": @(WXLogFlagDebug),
@"__LOG": @(WXLogFlagLog)
};
});
NSArray * args = [JSContext currentArguments];
NSString * levelStr = [[args lastObject] toString];
[WXBridgeContext handleConsoleOutputWithArgument:args logLevel:(WXLogFlag)[levelMap[levelStr] integerValue]];
};
context[@"extendCallNative"] = ^(JSValue *value ) {
return [WXBridgeContext extendCallNative:[value toDictionary]];
};
}
+ (void)handleConsoleOutputWithArgument:(NSArray *)arguments logLevel:(WXLogFlag)logLevel
{
NSMutableString *string = [NSMutableString string];
[string appendString:@"jsLog: "];
[arguments enumerateObjectsUsingBlock:^(JSValue *jsVal, NSUInteger idx, BOOL *stop) {
[string appendFormat:@"%@ ", jsVal];
if (idx == arguments.count - 1) {
if (logLevel) {
if (WXLogFlagWarning == logLevel || WXLogFlagError == logLevel) {
id<WXAppMonitorProtocol> appMonitorHandler = [WXSDKEngine handlerForProtocol:@protocol(WXAppMonitorProtocol)];
if ([appMonitorHandler respondsToSelector:@selector(commitAppMonitorAlarm:monitorPoint:success:errorCode:errorMsg:arg:)]) {
[appMonitorHandler commitAppMonitorAlarm:@"weex" monitorPoint:@"jswarning" success:NO errorCode:@"99999" errorMsg:string arg:[WXSDKEngine topInstance].pageName];
}
}
WX_LOG(logLevel, @"%@", string);
} else {
[string appendFormat:@"%@ ", jsVal];
WXLogInfo(@"%@", string);
}
}
}];
}
+(id)extendCallNative:(NSDictionary *)dict
{
if(dict){
return [WXExtendCallNativeManager sendExtendCallNativeEvent:dict];
}
return @(-1);
}
@end