blob: f0768179e6ec050b670d397f4ca20922593e5c47 [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 "WXJSCoreBridge.h"
#import "WXDefine.h"
#import "WXAssert.h"
#import "WXLog.h"
#import "WXDefine.h"
#import "WXUtility.h"
#import "WXSDKEngine.h"
#import "WXSDKError.h"
#import "WXMonitor.h"
#import "WXAppMonitorProtocol.h"
#import "WXHandlerFactory.h"
#import <sys/utsname.h>
#import <JavaScriptCore/JavaScriptCore.h>
#import "WXPolyfillSet.h"
#import "JSValue+Weex.h"
#import "WXJSExceptionProtocol.h"
#import "WXSDKManager.h"
#import "WXExtendCallNativeManager.h"
#import "WXTracingManager.h"
#import "WXExceptionUtils.h"
#import <dlfcn.h>
#import <mach/mach.h>
@interface WXJSCoreBridge ()
@property (nonatomic, strong) JSContext *jsContext;
@property (nonatomic, strong) NSMutableArray *timers;
@property (nonatomic, strong) NSMutableDictionary *intervaltimers;
@property (nonatomic) long long intervalTimerId;
@property (nonatomic, strong) NSMutableDictionary *callbacks;
@end
@implementation WXJSCoreBridge
- (instancetype)init
{
self = [super init];
if(self){
_jsContext = [[JSContext alloc] init];
if (WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
_jsContext.name = @"Weex Context";
}
_timers = [NSMutableArray new];
_callbacks = [NSMutableDictionary new];
_intervalTimerId = 0;
_intervaltimers = [NSMutableDictionary new];
__weak typeof(self) weakSelf = self;
NSDictionary *data = [WXUtility getEnvironment];
_jsContext[@"WXEnvironment"] = data;
_jsContext[@"setTimeout"] = ^(JSValue *function, JSValue *timeout) {
// this setTimeout is used by internal logic in JS framework, normal setTimeout called by users will call WXTimerModule's method;
[weakSelf performSelector: @selector(triggerTimeout:) withObject:^() {
[function callWithArguments:@[]];
} afterDelay:[timeout toDouble] / 1000];
};
_jsContext[@"setTimeoutWeex"] = ^(JSValue *appId, JSValue *ret,JSValue *arg ) {
[weakSelf triggerTimeout:[appId toString] ret:[ret toString] arg:[arg toString]];
};
_jsContext[@"setIntervalWeex"] = ^(JSValue *appId, JSValue *function,JSValue *arg) {
return [weakSelf triggerInterval:[appId toString] function:^() {
[function callWithArguments:@[]];
} arg:[arg toString]];
};
_jsContext[@"clearIntervalWeex"] = ^(JSValue *appId, JSValue *ret,JSValue *arg) {
[weakSelf triggerClearInterval:[appId toString] ret:[[ret toNumber] longLongValue]];
};
_jsContext[@"clearTimeoutWeex"] = ^(JSValue *ret) {
[weakSelf triggerClearTimeout:[ret toString]];
};
_jsContext[@"btoa"] = ^(JSValue *value ) {
NSData *nsdata = [[value toString]
dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64Encoded = [nsdata base64EncodedStringWithOptions:0];
return base64Encoded;
};
_jsContext[@"atob"] = ^(JSValue *value ) {
NSData *nsdataFromBase64String = [[NSData alloc]
initWithBase64EncodedString:[value toString] options:0];
NSString *base64Decoded = [[NSString alloc]
initWithData:nsdataFromBase64String encoding:NSUTF8StringEncoding];
return base64Decoded;
};
_jsContext[@"extendCallNative"] = ^(JSValue *value ) {
return [weakSelf extendCallNative:[value toDictionary]];
};
_jsContext[@"nativeLog"] = ^() {
static NSDictionary *levelMap;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
levelMap = @{
@"__ERROR": @(WXLogFlagError),
@"__WARN": @(WXLogFlagWarning),
@"__INFO": @(WXLogFlagInfo),
@"__DEBUG": @(WXLogFlagDebug),
@"__LOG": @(WXLogFlagLog)
};
});
NSMutableString *string = [NSMutableString string];
[string appendString:@"jsLog: "];
NSArray *args = [JSContext currentArguments];
[args enumerateObjectsUsingBlock:^(JSValue *jsVal, NSUInteger idx, BOOL *stop) {
if (idx == args.count - 1) {
NSNumber *flag = levelMap[[jsVal toString]];
if (flag) {
if ([flag isEqualToNumber:[NSNumber numberWithInteger:WXLogFlagWarning]]) {
id<WXAppMonitorProtocol> appMonitorHandler = [WXHandlerFactory handlerForProtocol:@protocol(WXAppMonitorProtocol)];
if ([appMonitorHandler respondsToSelector:@selector(commitAppMonitorAlarm:monitorPoint:success:errorCode:errorMsg:arg:)]) {
[appMonitorHandler commitAppMonitorAlarm:@"weex" monitorPoint:@"jswarning" success:FALSE errorCode:@"99999" errorMsg:string arg:[WXSDKEngine topInstance].pageName];
}
}
WX_LOG([flag unsignedIntegerValue], @"%@", string);
} else {
[string appendFormat:@"%@ ", jsVal];
WXLogInfo(@"%@", string);
}
}
[string appendFormat:@"%@ ", jsVal ];
}];
};
_jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception){
context.exception = exception;
WXSDKInstance *instance = [WXSDKEngine topInstance];
NSString *bundleUrl = [instance.scriptURL absoluteString]?:@"WX_KEY_EXCEPTION_WXBRIDGE";
NSString *errorCode = [NSString stringWithFormat:@"%d", WX_KEY_EXCEPTION_WXBRIDGE];
NSString *message = [NSString stringWithFormat:@"[WX_KEY_EXCEPTION_WXBRIDGE] [%@:%@:%@] %@\n%@", exception[@"sourceURL"], exception[@"line"], exception[@"column"], [exception toString], [exception[@"stack"] toObject]];
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
instance.userInfo[@"jsMainBundleStringContentLength"]?:@"",@"jsMainBundleStringContentLength",
instance.userInfo[@"jsMainBundleStringContentMd5"]?:@"",@"jsMainBundleStringContentMd5",nil];
WXJSExceptionInfo * jsExceptionInfo = [[WXJSExceptionInfo alloc] initWithInstanceId:instance.instanceId bundleUrl:bundleUrl errorCode:errorCode functionName:@"" exception:message userInfo:userInfo];
[WXExceptionUtils commitCriticalExceptionRT:jsExceptionInfo];
WX_MONITOR_FAIL(WXMTJSBridge, WX_ERR_JS_EXECUTE, message);
if (instance.onJSRuntimeException) {
instance.onJSRuntimeException(jsExceptionInfo);
}
};
if (WX_SYS_VERSION_LESS_THAN(@"8.0")) {
// solve iOS7 memory problem
_jsContext[@"nativeSet"] = [WXPolyfillSet class];
}
}
return self;
}
#pragma mark - WXBridgeProtocol
- (void)executeJSFramework:(NSString *)frameworkScript
{
WXAssertParam(frameworkScript);
if (WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
[_jsContext evaluateScript:frameworkScript withSourceURL:[NSURL URLWithString:@"native-bundle-main.js"]];
}else{
[_jsContext evaluateScript:frameworkScript];
}
}
- (JSValue *)callJSMethod:(NSString *)method args:(NSArray *)args
{
WXLogDebug(@"Calling JS... method:%@, args:%@", method, args);
return [[_jsContext globalObject] invokeMethod:method withArguments:args];
}
- (void)registerCallNative:(WXJSCallNative)callNative
{
JSValue* (^callNativeBlock)(JSValue *, JSValue *, JSValue *) = ^JSValue*(JSValue *instance, JSValue *tasks, JSValue *callback){
NSString *instanceId = [instance toString];
NSArray *tasksArray = [tasks toArray];
NSString *callbackId = [callback toString];
WXLogDebug(@"Calling native... instance:%@, tasks:%@, callback:%@", instanceId, tasksArray, callbackId);
return [JSValue valueWithInt32:(int32_t)callNative(instanceId, tasksArray, callbackId) inContext:[JSContext currentContext]];
};
_jsContext[@"callNative"] = callNativeBlock;
}
- (void)executeJavascript:(NSString *)script
{
WXAssertParam(script);
[_jsContext evaluateScript:script];
}
- (void)registerCallAddElement:(WXJSCallAddElement)callAddElement
{
id callAddElementBlock = ^(JSValue *instanceId, JSValue *ref, JSValue *element, JSValue *index, JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
NSDictionary *componentData = [element toDictionary];
NSString *parentRef = [ref toString];
NSInteger insertIndex = [[index toNumber] integerValue];
[WXTracingManager startTracingWithInstanceId:instanceIdString ref:componentData[@"ref"] className:nil name:WXTJSCall phase:WXTracingBegin functionName:@"addElement" options:@{@"threadName":WXTJSBridgeThread,@"componentData":componentData}];
WXLogDebug(@"callAddElement...%@, %@, %@, %ld", instanceIdString, parentRef, componentData, (long)insertIndex);
return [JSValue valueWithInt32:(int32_t)callAddElement(instanceIdString, parentRef, componentData, insertIndex) inContext:[JSContext currentContext]];
};
_jsContext[@"callAddElement"] = callAddElementBlock;
}
- (void)registerCallCreateBody:(WXJSCallCreateBody)callCreateBody
{
id WXJSCallCreateBodyBlock = ^(JSValue *instanceId, JSValue *body,JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
NSDictionary *bodyData = [body toDictionary];
WXLogDebug(@"callCreateBody...%@, %@,", instanceIdString, bodyData);
[WXTracingManager startTracingWithInstanceId:instanceIdString ref:bodyData[@"ref"] className:nil name:WXTJSCall phase:WXTracingBegin functionName:@"createBody" options:@{@"threadName":WXTJSBridgeThread}];
return [JSValue valueWithInt32:(int32_t)callCreateBody(instanceIdString, bodyData) inContext:[JSContext currentContext]];
};
_jsContext[@"callCreateBody"] = WXJSCallCreateBodyBlock;
}
- (void)registerCallRemoveElement:(WXJSCallRemoveElement)callRemoveElement
{
id WXJSCallCreateBodyBlock = ^(JSValue *instanceId, JSValue *ref,JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
NSString *refString = [ref toString];
WXLogDebug(@"callRemoveElement...%@, %@,", instanceIdString, refString);
[WXTracingManager startTracingWithInstanceId:instanceIdString ref:nil className:nil name:WXTJSCall phase:WXTracingBegin functionName:@"removeElement" options:nil];
return [JSValue valueWithInt32:(int32_t)callRemoveElement(instanceIdString, refString) inContext:[JSContext currentContext]];
};
_jsContext[@"callRemoveElement"] = WXJSCallCreateBodyBlock;
}
- (void)registerCallMoveElement:(WXJSCallMoveElement)callMoveElement
{
id WXJSCallMoveElementBlock = ^(JSValue *instanceId, JSValue *ref,JSValue *parentRef,JSValue *index, JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
NSString *refString = [ref toString];
NSString *parentRefString = [parentRef toString];
NSInteger moveIndex = [[index toNumber] integerValue];
WXLogDebug(@"callAddElement...%@, %@,", instanceIdString, refString);
[WXTracingManager startTracingWithInstanceId:instanceIdString ref:refString className:nil name:WXTJSCall phase:WXTracingBegin functionName:@"moveElement" options:nil];
return [JSValue valueWithInt32:(int32_t)callMoveElement(instanceIdString, refString,parentRefString,moveIndex) inContext:[JSContext currentContext]];
};
_jsContext[@"callMoveElement"] = WXJSCallMoveElementBlock;
}
- (void)registerCallUpdateAttrs:(WXJSCallUpdateAttrs)callUpdateAttrs
{
id WXJSCallUpdateAttrsBlock = ^(JSValue *instanceId, JSValue *ref,JSValue *attrs, JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
NSString *refString = [ref toString];
NSDictionary *attrsData = [attrs toDictionary];
WXLogDebug(@"callUpdateAttrs...%@, %@, %@", instanceIdString, refString,attrsData);
[WXTracingManager startTracingWithInstanceId:instanceIdString ref:refString className:nil name:WXTJSCall phase:WXTracingBegin functionName:@"updateAttrs" options:@{@"threadName":WXTJSBridgeThread}];
return [JSValue valueWithInt32:(int32_t)callUpdateAttrs(instanceIdString, refString,attrsData) inContext:[JSContext currentContext]];
};
_jsContext[@"callUpdateAttrs"] = WXJSCallUpdateAttrsBlock;
}
- (void)registerCallUpdateStyle:(WXJSCallUpdateStyle)callUpdateStyle
{
id WXJSCallUpdateStyleBlock = ^(JSValue *instanceId, JSValue *ref,JSValue *styles, JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
NSString *refString = [ref toString];
NSDictionary *stylessData = [styles toDictionary];
WXLogDebug(@"callUpdateStyle...%@, %@, %@", instanceIdString, refString,stylessData);
[WXTracingManager startTracingWithInstanceId:instanceIdString ref:refString className:nil name:WXTJSCall phase:WXTracingBegin functionName:@"updateStyle" options:@{@"threadName":WXTJSBridgeThread}];
return [JSValue valueWithInt32:(int32_t)callUpdateStyle(instanceIdString, refString,stylessData) inContext:[JSContext currentContext]];
};
_jsContext[@"callUpdateStyle"] = WXJSCallUpdateStyleBlock;
}
- (void)registerCallAddEvent:(WXJSCallAddEvent)callAddEvent
{
id WXJSCallAddEventBlock = ^(JSValue *instanceId, JSValue *ref,JSValue *event, JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
NSString *refString = [ref toString];
NSString *eventString = [event toString];
WXLogDebug(@"callAddEvent...%@, %@, %@", instanceIdString, refString,eventString);
[WXTracingManager startTracingWithInstanceId:instanceIdString ref:refString className:nil name:WXTJSCall phase:WXTracingBegin functionName:@"addEvent" options:nil];
return [JSValue valueWithInt32:(int32_t)callAddEvent(instanceIdString, refString,eventString) inContext:[JSContext currentContext]];
};
_jsContext[@"callAddEvent"] = WXJSCallAddEventBlock;
}
- (void)registerCallRemoveEvent:(WXJSCallRemoveEvent)callRemoveEvent
{
id WXJSCallRemoveEventBlock = ^(JSValue *instanceId, JSValue *ref,JSValue *event, JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
NSString *refString = [ref toString];
NSString *eventString = [event toString];
WXLogDebug(@"callRemoveEvent...%@, %@, %@", instanceIdString, refString,eventString);
[WXTracingManager startTracingWithInstanceId:instanceIdString ref:refString className:nil name:WXTJSCall phase:WXTracingBegin functionName:@"removeEvent" options:nil];
return [JSValue valueWithInt32:(int32_t)callRemoveEvent(instanceIdString, refString,eventString) inContext:[JSContext currentContext]];
};
_jsContext[@"callRemoveEvent"] = WXJSCallRemoveEventBlock;
}
- (void)registerCallCreateFinish:(WXJSCallCreateFinish)callCreateFinish
{
id WXJSCallCreateFinishBlock = ^(JSValue *instanceId, JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
WXLogDebug(@"callRCreateFinish...%@", instanceIdString);
[WXTracingManager startTracingWithInstanceId:instanceIdString ref:nil className:nil name:WXTJSCall phase:WXTracingBegin functionName:@"createFinish" options:@{@"threadName":WXTJSBridgeThread}];
return [JSValue valueWithInt32:(int32_t)callCreateFinish(instanceIdString) inContext:[JSContext currentContext]];
};
_jsContext[@"callCreateFinish"] = WXJSCallCreateFinishBlock;
}
- (void)registerCallNativeModule:(WXJSCallNativeModule)callNativeModuleBlock
{
_jsContext[@"callNativeModule"] = ^JSValue *(JSValue *instanceId, JSValue *moduleName, JSValue *methodName, JSValue *args, JSValue *options) {
NSString *instanceIdString = [instanceId toString];
NSString *moduleNameString = [moduleName toString];
NSString *methodNameString = [methodName toString];
NSArray *argsArray = [args toArray];
NSDictionary *optionsDic = [options toDictionary];
WXLogDebug(@"callNativeModule...%@,%@,%@,%@", instanceIdString, moduleNameString, methodNameString, argsArray);
NSInvocation *invocation = callNativeModuleBlock(instanceIdString, moduleNameString, methodNameString, argsArray, optionsDic);
JSValue *returnValue = [JSValue wx_valueWithReturnValueFromInvocation:invocation inContext:[JSContext currentContext]];
[WXTracingManager startTracingWithInstanceId:instanceIdString ref:nil className:nil name:moduleNameString phase:WXTracingInstant functionName:methodNameString options:nil];
return returnValue;
};
}
- (void)registerCallNativeComponent:(WXJSCallNativeComponent)callNativeComponentBlock
{
_jsContext[@"callNativeComponent"] = ^void(JSValue *instanceId, JSValue *componentName, JSValue *methodName, JSValue *args, JSValue *options) {
NSString *instanceIdString = [instanceId toString];
NSString *componentNameString = [componentName toString];
NSString *methodNameString = [methodName toString];
NSArray *argsArray = [args toArray];
NSDictionary *optionsDic = [options toDictionary];
WXLogDebug(@"callNativeComponent...%@,%@,%@,%@", instanceIdString, componentNameString, methodNameString, argsArray);
callNativeComponentBlock(instanceIdString, componentNameString, methodNameString, argsArray, optionsDic);
};
}
- (JSValue*)exception
{
return _jsContext.exception;
}
- (void)resetEnvironment
{
NSDictionary *data = [WXUtility getEnvironment];
_jsContext[@"WXEnvironment"] = data;
}
//typedef void (*WXJSCGarbageCollect)(JSContextRef);
- (void)garbageCollect
{
// for dev and debug only!!
// char str[80];
// strcpy(str, "JSSynchron");
// strcat(str, "ousGarbageColl");
// strcat(str, "ectForDebugging");
// WXJSCGarbageCollect garbageCollect = dlsym(RTLD_DEFAULT, str);
//
// if (garbageCollect != NULL) {
// garbageCollect(_jsContext.JSGlobalContextRef);
// }
}
#pragma mark - Public
-(void)removeTimers:(NSString *)instance
{
// remove timers
if([_callbacks objectForKey:instance]){
NSMutableArray *arr = [_callbacks objectForKey:instance];
if(arr && [arr count]>0){
for (NSString *callback in arr) {
if([_timers containsObject:callback]){
[_timers removeObject:callback];
}
}
}
}
// remove intervaltimers
if(_intervaltimers && [_intervaltimers objectForKey:instance]){
[_intervaltimers removeObjectForKey:instance];
}
}
#pragma mark - Private
-(void)addInstance:(NSString *)instance callback:(NSString *)callback
{
if(instance.length > 0){
if([_callbacks objectForKey:instance]){
NSMutableArray *arr = [_callbacks objectForKey:instance];
if (callback.length>0 && ![arr containsObject:callback]) {
[arr addObject:callback];
[_callbacks setObject:arr forKey:instance];
}
}else {
NSMutableArray *arr = [NSMutableArray new];
if (callback.length>0 && ![arr containsObject:callback]) {
[arr addObject:callback];
[_callbacks setObject:arr forKey:instance];
}
}
}
}
- (void)triggerTimeout:(void(^)())block
{
block();
}
- (void)callBack:(NSDictionary *)dic
{
if([dic objectForKey:@"ret"] && [_timers containsObject:[dic objectForKey:@"ret"]]) {
[[WXSDKManager bridgeMgr] callBack:[dic objectForKey:@"appId"] funcId:[dic objectForKey:@"ret"] params:[dic objectForKey:@"arg"] keepAlive:NO];
}
}
- (void)callBackInterval:(NSDictionary *)dic functon:(void(^)())block
{
if([dic objectForKey:@"appId"] && [_intervaltimers objectForKey:[dic objectForKey:@"appId"]]){
NSMutableArray *timers = [_intervaltimers objectForKey:[dic objectForKey:@"appId"]];
if([timers containsObject:[dic objectForKey:@"timerId"]]){
block();
[self executeInterval:[dic objectForKey:@"appId"] function:block arg:[dic objectForKey:@"arg"] timerId:[[dic objectForKey:@"timerId"] longLongValue]];
}
}
}
- (void)triggerTimeout:(NSString *)appId ret:(NSString *)ret arg:(NSString *)arg
{
double interval = [arg doubleValue]/1000.0f;
if(WXFloatEqual(interval,0)) {
return;
}
if(![_timers containsObject:ret]){
[_timers addObject:ret];
[self addInstance:appId callback:ret];
}
__weak typeof(self) weakSelf = self;
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, interval*NSEC_PER_SEC);
dispatch_after(time, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableDictionary *dic = [NSMutableDictionary new];
[dic setObject:appId forKey:@"appId"];
[dic setObject:ret forKey:@"ret"];
[dic setObject:arg forKey:@"arg"];
[weakSelf performSelector:@selector(callBack:) withObject:dic ];
});
}
- (long long)triggerInterval:(NSString *)appId function:(void(^)())block arg:(NSString *)arg
{
double interval = [arg doubleValue]/1000.0f;
long long timerId = _intervalTimerId + 1;
if(WXFloatEqual(interval,0)) {
return timerId;
}
if([_intervaltimers objectForKey:appId]){
NSMutableArray *timers = [[_intervaltimers objectForKey:appId] mutableCopy];
[timers addObject:@(timerId)];
[_intervaltimers setObject:timers forKey:appId];
}else {
NSMutableArray *timers = [NSMutableArray new];
[timers addObject:@(timerId)];
[_intervaltimers setObject:timers forKey:appId];
}
[self executeInterval:appId function:block arg:arg timerId:timerId];
return timerId;
}
-(void)executeInterval:(NSString *)appId function:(void(^)())block arg:(NSString *)arg timerId:(long long)timerId
{
double interval = [arg doubleValue]/1000.0f;
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, interval*NSEC_PER_SEC);
__weak typeof(self) weakSelf = self;
dispatch_after(time, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableDictionary *dic = [NSMutableDictionary new];
[dic setObject:appId forKey:@"appId"];
[dic setObject:arg forKey:@"arg"];
[dic setObject:@(timerId) forKey:@"timerId"];
[weakSelf performSelector:@selector(callBackInterval:functon:) withObject:dic withObject:block];
});
}
- (void)triggerClearInterval:(NSString *)appId ret:(long long)ret
{
if(_intervaltimers && [_intervaltimers objectForKey:@"appId"]){
NSMutableArray *timers = [_intervaltimers objectForKey:@"appId"];
if(timers && [timers containsObject:@(ret)]){
[timers removeObject:@(ret)];
}
}
}
- (void)triggerClearTimeout:(NSString *)ret
{
if([_timers containsObject:ret]){
[_timers removeObject:ret];
}
}
-(id)extendCallNative:(NSDictionary *)dict
{
if(dict){
return [WXExtendCallNativeManager sendExtendCallNativeEvent:dict];
}
return @(-1);
}
@end