remove JSC jail on native side (Android, iOS)

This commit is contained in:
Roman Volosovskyi 2018-07-29 08:32:21 +03:00
parent dc23ce2803
commit a586d177f5
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
11 changed files with 0 additions and 758 deletions

View File

@ -14,7 +14,6 @@ android {
dependencies {
implementation 'com.facebook.react:react-native:+'
implementation 'com.instabug.library:instabug:3+'
implementation 'com.github.ericwlange:AndroidJSCore:3.0.1'
implementation 'status-im:function:0.0.1'
String statusGoVersion = 'develop-ga339d7ed'

View File

@ -1,271 +0,0 @@
package im.status.ethereum.module;
import android.util.Log;
import com.github.status_im.status_go.Statusgo;
import org.json.JSONException;
import org.json.JSONObject;
import org.liquidplayer.webkit.javascriptcore.JSContext;
import org.liquidplayer.webkit.javascriptcore.JSException;
import org.liquidplayer.webkit.javascriptcore.JSFunction;
import org.liquidplayer.webkit.javascriptcore.JSValue;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
class JSCJail implements Jail {
private static final String TAG = "JSCJail";
private String initJs;
private Map<String, Cell> cells = new HashMap<>();
private StatusModule module;
@Override
public void initJail(String initJs) {
this.initJs = initJs;
}
JSCJail(StatusModule module) {
this.module = module;
}
private class Cell {
JSContext context;
Timer timer;
}
private class Timer {
private Map<String, ScheduledExecutorService> timers = new HashMap<>();
String setTimeout(JSValue callback, int interval) {
return this.scheduleTask(callback, interval, false);
}
String setInterval(JSValue callback, int interval) {
return this.scheduleTask(callback, interval, true);
}
private String scheduleTask(final JSValue callback, int interval, final boolean repeatable) {
final String id = UUID.randomUUID().toString();
final ScheduledExecutorService scheduler =
Executors.newSingleThreadScheduledExecutor();
timers.put(id, scheduler);
scheduler.scheduleAtFixedRate
(new Runnable() {
public void run() {
if (!repeatable) {
scheduler.shutdown();
timers.remove(id);
}
callback.toFunction().call();
}
}, interval, interval, TimeUnit.MILLISECONDS);
return id;
}
void clearInterval(String id) {
if (!timers.containsKey(id)) {
return;
}
ScheduledExecutorService scheduler = timers.get(id);
scheduler.shutdown();
timers.remove(id);
}
void reset() {
for (String entry : timers.keySet()) {
timers.get(entry).shutdown();
}
timers.clear();
}
}
private void addHandlers(Cell cell, final String chatId) {
JSContext context = cell.context;
final Timer timer = cell.timer;
JSFunction web3send = new JSFunction(context, "web3send") {
public String web3send(String payload) {
return Statusgo.CallRPC(payload);
}
};
context.property("web3send", web3send);
JSFunction web3sendAsync = new JSFunction(context, "web3sendAsync") {
public void web3sendAsync(final String payload, final JSValue callback) {
Runnable r = new Runnable() {
@Override
public void run() {
String result = Statusgo.CallRPC(payload);
callback.toFunction().call(null, result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
};
context.property("web3sendAsync", web3sendAsync);
JSFunction statusSendSignal = new JSFunction(context, "statusSendSignal") {
public void statusSendSignal(String data) {
JSONObject event = new JSONObject();
JSONObject signal = new JSONObject();
try {
event.put("chat_id", chatId);
event.put("data", data);
signal.put("type", "jail.signal");
signal.put("event", event);
} catch (JSONException e) {
Log.d(TAG, "Failed to construct signal JSON object: " + e.getMessage());
}
module.signalEvent(signal.toString());
}
};
context.property("statusSendSignal", statusSendSignal);
JSFunction setTimeout = new JSFunction(context, "setTimeout") {
public String setTimeout(final JSValue callback, final int ms) {
return timer.setTimeout(callback, ms);
}
};
context.property("setTimeout", setTimeout);
JSFunction setInterval = new JSFunction(context, "setInterval") {
public String setInterval(final JSValue callback, final int ms) {
return timer.setInterval(callback, ms);
}
};
context.property("setInterval", setInterval);
JSFunction clearInterval = new JSFunction(context, "clearInterval") {
public void clearInterval(String id) {
timer.clearInterval(id);
}
};
context.property("clearInterval", clearInterval);
JSFunction statusLog = new JSFunction(context, "statusLog") {
public void statusLog(String data) {
Log.d("statusJSLog", data);
}
};
context.property("statusLog", statusLog);
}
private JSException jsException;
private void checkException(String label) {
if (jsException != null) {
jsException = null;
}
}
@Override
public String parseJail(String chatId, String js) {
Cell cell = new Cell();
JSContext context = new JSContext();
cell.context = context;
cell.timer = new Timer();
context.setExceptionHandler(new JSContext.IJSExceptionHandler() {
@Override
public void handle(JSException exception) {
jsException = exception;
}
});
String web3Js = "var statusSignals = {\n" +
" sendSignal: function (s) {statusSendSignal(s);}\n" +
" };\n" +
" var Web3 = require('web3');\n" +
" var provider = {\n" +
" send: function (payload) {\n" +
" var result = web3send(JSON.stringify(payload));\n" +
" return JSON.parse(result);\n" +
" },\n" +
" sendAsync: function (payload, callback) {\n" +
" var wrappedCallback = function (result) {\n" +
" //console.log(result);\n" +
" var error = null;\n" +
" try {\n" +
" result = JSON.parse(result);\n" +
" } catch (e) {\n" +
" error = result;\n" +
" }\n" +
" callback(error, result);\n" +
" };\n" +
" web3sendAsync(JSON.stringify(payload), wrappedCallback);\n" +
" }\n" +
" };\n" +
" var web3 = new Web3(provider);\n" +
" var console = {\n" +
" log: function (data) {\n" +
" statusLog(data);\n" +
" }\n" +
" };\n" +
" var Bignumber = require(\"bignumber.js\");\n" +
" function bn(val){\n" +
" return new Bignumber(val);\n" +
" }\n";
addHandlers(cell, chatId);
context.evaluateScript(initJs);
context.evaluateScript(web3Js);
context.evaluateScript(js);
context.evaluateScript("var catalog = JSON.stringify(_status_catalog);");
JSValue catalog = context.property("catalog");
cells.put(chatId, cell);
JSONObject result = new JSONObject();
try {
result.put("result", catalog.toString());
if (jsException != null) {
result.put("error", jsException.toString());
jsException = null;
}
} catch (JSONException e) {
Log.d(TAG, "Failed to construct JSON response for parseJail: " + e.getMessage());
}
return result.toString();
}
@Override
public String callJail(String chatId, String path, String params) {
if (!cells.containsKey(chatId)) {
return null;
}
JSContext context = cells.get(chatId).context;
JSValue call = context.property("call");
JSValue callResult = call.toFunction().call(null, path, params);
JSONObject result = new JSONObject();
try {
result.put("result", callResult.toString());
if (jsException != null) {
result.put("error", jsException.toString());
}
} catch (JSONException e) {
Log.d(TAG, "Failed to construct JSON response for callJail: " + e.getMessage());
}
return result.toString();
}
@Override
public void reset() {
for (String entry : cells.keySet()) {
cells.get(entry).timer.reset();
}
cells.clear();
}
}

View File

@ -1,8 +0,0 @@
package im.status.ethereum.module;
public interface Jail {
void initJail(String initJs);
String parseJail(String chatId, String js);
String callJail(String chatId, String path, String params);
void reset();
}

View File

@ -43,7 +43,6 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
private boolean debug;
private boolean devCluster;
private String logLevel;
private Jail jail;
StatusModule(ReactApplicationContext reactContext, boolean debug, boolean devCluster, String logLevel) {
super(reactContext);
@ -54,7 +53,6 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
this.devCluster = devCluster;
this.logLevel = logLevel;
reactContext.addLifecycleEventListener(this);
jail = new JSCJail(this);
}
@Override
@ -404,8 +402,6 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
return;
}
jail.reset();
Runnable r = new Runnable() {
@Override
public void run() {
@ -558,58 +554,6 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
StatusThreadPoolExecutor.getInstance().execute(r);
}
// Jail
@ReactMethod
public void initJail(final String js, final Callback callback) {
Log.d(TAG, "initJail");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
jail.initJail(js);
callback.invoke(false);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void parseJail(final String chatId, final String js, final Callback callback) {
Log.d(TAG, "parseJail chatId:" + chatId);
//Log.d(TAG, js);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
String res = jail.parseJail(chatId, js);
Log.d(TAG, res);
Log.d(TAG, "endParseJail");
callback.invoke(res);
}
@ReactMethod
public void callJail(final String chatId, final String path, final String params, final Callback callback) {
Log.d(TAG, "callJail");
Log.d(TAG, path);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Log.d(TAG, "startCallJail");
String res = jail.callJail(chatId, path, params);
Log.d(TAG, "endCallJail");
callback.invoke(res);
}
@ReactMethod
public void setAdjustResize() {
Log.d(TAG, "setAdjustResize");

View File

@ -1,41 +0,0 @@
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
#import <Statusgo/Statusgo.h>
#import "TimerJSExport.h"
@interface Jail : NSObject
- (void)initJail:(NSString *)js;
- (NSDictionary *)parseJail:(NSString *)chatId
withCode:(NSString *)js;
- (NSDictionary *)call:(NSString *)chatId
path:(NSString *)path
params:(NSString *)params;
- (void)reset;
@property (nonatomic) NSMutableDictionary * cells;
@property (nonatomic) NSString * initialJs;
@property (nonatomic) TimerJS * timer;
@end
@interface Cell : NSObject
@property (nonatomic)JSContext * context;
@property (nonatomic)TimerJS * timer;
@end
@protocol HandlersJSExport <JSExport>
- (void)log:(JSValue *)data;
- (NSString *)send:(JSValue *)payload;
- (void)sendAsync:(JSValue *)payload;
- (BOOL)isConnected;
- (void)sendSignal:(JSValue *)data;
@end
@interface HandlersJs : NSObject <HandlersJSExport>
@property (nonatomic) NSString * chatId;
@end

View File

@ -1,211 +0,0 @@
#import "Jail.h"
#import "RCTStatus.h"
//source: http://stackoverflow.com/a/23387659/828487
#define NSStringMultiline(...) [[NSString alloc] initWithCString:#__VA_ARGS__ encoding:NSUTF8StringEncoding]
@implementation HandlersJs
- (void)log:(JSValue *)data
{
NSLog(@"jail log: %@", [data toString]);
}
- (NSString *)send:(JSValue *)payload
{
char * result = CallRPC((char *) [[payload toString] UTF8String]);
return [NSString stringWithUTF8String: result];
}
- (void)sendAsync:(JSValue *)args
{
// TODO(rasom): fix this black magic, need to figure how to pass more than one
// parameter to sendAsync
JSValue *payload = [args callWithArguments:@[@0]];
JSValue *callback = [args callWithArguments:@[@1]];
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *result = [self send:payload];
dispatch_async( dispatch_get_main_queue(), ^{
[callback callWithArguments:@[result]];
});
});
}
- (BOOL)isConnected
{
return YES;
}
- (void)sendSignal:(JSValue *)data
{
[Status jailEvent:_chatId data:[data toString]];
}
- (void)setChatId:(NSString *)chatId
{
_chatId = chatId;
}
- (void)addToContext:(JSContext *)context
{
[context setObject:self forKeyedSubscript:@"statusNativeHandlers"];
}
@end
@implementation Cell
@end
@implementation Jail
- (void)initJail:(NSString *)js {
_initialJs = js;
}
- (NSDictionary *)parseJail:(NSString *)chatId
withCode:(NSString *)js {
Cell *cell = [self createCell:chatId withCode:js];
JSContext *context = cell.context;
[_cells setValue:cell forKey:chatId];
JSValue *catalog = context[@"catalog"];;
JSValue *exception = [context exception];
NSString *error;
if(exception != nil) {
error = [exception toString];
[context setException:nil];
}
NSDictionary *result = [[NSDictionary alloc] initWithObjectsAndKeys:[catalog toString], @"result", error, @"error", nil];
return result;
}
- (NSDictionary *)call:(NSString *)chatId
path:(NSString *)path
params:(NSString *)params {
Cell *cell = [_cells valueForKey:chatId];
JSContext *context = cell.context;
if(cell == nil) {
return [[NSDictionary alloc] initWithObjectsAndKeys:nil, @"result", @"jail is not initialized", @"error", nil];
}
JSValue *callResult = [context[@"call"] callWithArguments:@[path, params]];
JSValue *exception = [context exception];
NSString *error;
if(exception != nil) {
error = [exception toString];
[context setException:nil];
}
NSDictionary *result = [[NSDictionary alloc] initWithObjectsAndKeys:[callResult toString], @"result", error, @"error", nil];
return result;
}
- (Cell *)createCell:(NSString *)chatId
withCode:(NSString *)js {
if(_cells == nil) {
_cells = [NSMutableDictionary dictionaryWithCapacity:1];
}
Cell * cell = [Cell new];
JSContext *context = [JSContext new];
[context setName:chatId];
cell.context = context;
HandlersJs *handlers = [HandlersJs new];
[handlers setChatId:chatId];
[handlers addToContext:context];
[self addTimer:cell];
[context evaluateScript:_initialJs];
JSValue *excep = [context exception];
NSLog(@"err1 %@", [excep toString]);
NSString *webJs =
NSStringMultiline
(
var statusSignals = {
sendSignal: function (s) {statusNativeHandlers.sendSignal(s);}
};
var setTimeout = function (fn, t) {
var args = [fn, t];
var getItem = function(idx) {return args[idx];};
return jsTimer.setTimeout(getItem);
};
var setInterval = function (fn, t) {
var args = [fn, t];
var getItem = function(idx) {return args[idx];};
return jsTimer.setInterval(getItem);
};
var clearInterval = function (id) {
jsTimer.clearInterval(id);
};
var Web3 = require('web3');
var provider = {
send: function (payload) {
var result = statusNativeHandlers.send(JSON.stringify(payload));
return JSON.parse(result);
},
sendAsync: function (payload, callback) {
var wrappedCallback = function (result) {
console.log(result);
var error = null;
try {
result = JSON.parse(result);
} catch (e) {
error = result;
}
callback(error, result);
};
var args = [JSON.stringify(payload), wrappedCallback];
var getItem = function(idx) {return args[idx];};
statusNativeHandlers.sendAsync(getItem);
}
};
var web3 = new Web3(provider);
var console = {
log: function (data) {
statusNativeHandlers.log(data);
}
};
var Bignumber = require("bignumber.js");
function bn(val){
return new Bignumber(val);
}
);
[context evaluateScript:webJs];
excep = [context exception];
NSLog(@"err2 %@", [excep toString]);
[context evaluateScript:js];
excep = [context exception];
NSLog(@"err3 %@", [excep toString]);
[context evaluateScript:@"var catalog = JSON.stringify(_status_catalog);"];
excep = [context exception];
NSLog(@"err4 %@", [excep toString]);
return cell;
}
- (void)addTimer:(Cell *)cell {
TimerJS *timer = [TimerJS new];
cell.timer = timer;
[timer addToContext:cell.context];
}
-(void)reset {
NSArray *keys = [_cells allKeys];
for (NSString *key in keys) {
Cell *cell = [_cells valueForKey:key];
TimerJS *timer = cell.timer;
[timer stopTimers];
[_cells removeObjectForKey:key];
}
}
@end

View File

@ -1,13 +1,10 @@
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import "RCTLog.h"
#import <JavaScriptCore/JavaScriptCore.h>
#import "Jail.h"
@interface Status : NSObject <RCTBridgeModule>
+ (void)signalEvent:(const char *)signal;
+ (void)jailEvent:(NSString *)chatId
data:(NSString *)data;
+ (BOOL)JSCEnabled;
@property (nonatomic) Jail * jail;
@end

View File

@ -62,61 +62,6 @@ static RCTBridge *bridge;
RCT_EXPORT_MODULE();
////////////////////////////////////////////////////////////////////
#pragma mark - Jails functions
//////////////////////////////////////////////////////////////////// initJail
RCT_EXPORT_METHOD(initJail: (NSString *) js
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"InitJail() method called");
#endif
if(_jail == nil) {
_jail = [Jail new];
}
[_jail initJail:js];
callback(@[[NSNull null]]);
}
//////////////////////////////////////////////////////////////////// parseJail
RCT_EXPORT_METHOD(parseJail:(NSString *)chatId
js:(NSString *)js
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"ParseJail() method called");
#endif
NSString *stringResult;
if(_jail == nil) {
_jail = [Jail new];
}
NSDictionary *result = [_jail parseJail:chatId withCode:js];
stringResult = [result bv_jsonStringWithPrettyPrint:NO];
callback(@[stringResult]);
}
//////////////////////////////////////////////////////////////////// callJail
RCT_EXPORT_METHOD(callJail:(NSString *)chatId
path:(NSString *)path
params:(NSString *)params
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"CallJail() method called");
#endif
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *stringResult;
if(_jail == nil) {
_jail = [Jail new];
}
NSDictionary *result = [_jail call:chatId path:path params:params];
stringResult = [result bv_jsonStringWithPrettyPrint:NO];
dispatch_async( dispatch_get_main_queue(), ^{
callback(@[stringResult]);
});
});
}
////////////////////////////////////////////////////////////////////
#pragma mark - startNode
//////////////////////////////////////////////////////////////////// startNode
@ -301,9 +246,6 @@ RCT_EXPORT_METHOD(login:(NSString *)address
#if DEBUG
NSLog(@"Login() method called");
#endif
if(_jail != nil) {
[_jail reset];
}
char * result = Login((char *) [address UTF8String], (char *) [password UTF8String]);
callback(@[[NSString stringWithUTF8String: result]]);
}
@ -456,23 +398,4 @@ RCT_EXPORT_METHOD(getDeviceUUID:(RCTResponseSenderBlock)callback) {
return;
}
+ (void)jailEvent:(NSString *)chatId
data:(NSString *)data
{
NSData *signalData = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:signalData options:NSJSONReadingMutableContainers error:nil];
[dict setValue:@"jail.signal" forKey:@"type"];
NSDictionary *event = [[NSDictionary alloc] initWithObjectsAndKeys:chatId, @"chat_id", data, @"data", nil];
[dict setValue:event forKey:@"event"];
NSString *signal = [dict bv_jsonStringWithPrettyPrint:NO];
#if DEBUG
NSLog(@"SignalEventData");
NSLog(signal);
#endif
[bridge.eventDispatcher sendAppEventWithName:@"gethEvent"
body:@{@"jsonEvent": signal}];
return;
}
@end

View File

@ -9,8 +9,6 @@
/* Begin PBXBuildFile section */
206C9F3E1D474E910063E3E6 /* RCTStatus.h in Copy Files */ = {isa = PBXBuildFile; fileRef = 206C9F3D1D474E910063E3E6 /* RCTStatus.h */; };
206C9F401D474E910063E3E6 /* RCTStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 206C9F3F1D474E910063E3E6 /* RCTStatus.m */; };
4C0AB2231F8A2ED700CFD175 /* Jail.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C0AB2221F8A2ED700CFD175 /* Jail.m */; };
4C16DE661F8A171B00AA10DB /* TimerJSExport.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C16DE651F8A171B00AA10DB /* TimerJSExport.m */; };
9806BE77206D044D00548CC4 /* Instabug.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9806BE76206D044D00548CC4 /* Instabug.framework */; };
CE4E31B11D86951A0033ED64 /* Statusgo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE4E31B01D86951A0033ED64 /* Statusgo.framework */; };
/* End PBXBuildFile section */
@ -33,10 +31,6 @@
206C9F3A1D474E910063E3E6 /* libRCTStatus.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTStatus.a; sourceTree = BUILT_PRODUCTS_DIR; };
206C9F3D1D474E910063E3E6 /* RCTStatus.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTStatus.h; sourceTree = "<group>"; };
206C9F3F1D474E910063E3E6 /* RCTStatus.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTStatus.m; sourceTree = "<group>"; };
4C0AB2211F8A2EC700CFD175 /* Jail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Jail.h; sourceTree = "<group>"; };
4C0AB2221F8A2ED700CFD175 /* Jail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Jail.m; sourceTree = "<group>"; };
4C16DE641F8A170500AA10DB /* TimerJSExport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimerJSExport.h; sourceTree = "<group>"; };
4C16DE651F8A171B00AA10DB /* TimerJSExport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TimerJSExport.m; sourceTree = "<group>"; };
9806BE76206D044D00548CC4 /* Instabug.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Instabug.framework; path = "../../../../node_modules/instabug-reactnative/ios/Instabug.framework"; sourceTree = "<group>"; };
CE4E31B01D86951A0033ED64 /* Statusgo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Statusgo.framework; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -77,10 +71,6 @@
children = (
206C9F3D1D474E910063E3E6 /* RCTStatus.h */,
206C9F3F1D474E910063E3E6 /* RCTStatus.m */,
4C16DE641F8A170500AA10DB /* TimerJSExport.h */,
4C16DE651F8A171B00AA10DB /* TimerJSExport.m */,
4C0AB2211F8A2EC700CFD175 /* Jail.h */,
4C0AB2221F8A2ED700CFD175 /* Jail.m */,
);
name = Status;
sourceTree = "<group>";
@ -149,8 +139,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4C16DE661F8A171B00AA10DB /* TimerJSExport.m in Sources */,
4C0AB2231F8A2ED700CFD175 /* Jail.m in Sources */,
206C9F401D474E910063E3E6 /* RCTStatus.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -1,18 +0,0 @@
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
@protocol TimerJSExport <JSExport>
- (NSString *)setTimeout:(JSValue *)args;
- (void)clearInterval:(NSString *)id;
- (NSString *)setInterval:(JSValue *)args;
@end
@interface TimerJS : NSObject <TimerJSExport>
- (void)addToContext:(JSContext *)context;
- (void)stopTimers;
@property (nonatomic) NSMutableDictionary * timers;
@end

View File

@ -1,60 +0,0 @@
#import "TimerJSExport.h"
@implementation TimerJS
- (NSString *)setTimeout:(JSValue *)args {
JSValue *callback = [args callWithArguments:@[@0]];
double ms = [[args callWithArguments:@[@1]] toDouble];
return [self createTimer:callback inteval:ms repeats:NO];
}
- (void)clearInterval:(NSString *)id {
NSTimer *timer = [_timers objectForKey:id];
if(timer != nil) {
[timer invalidate];
}
}
- (NSString *)setInterval:(JSValue *)args {
JSValue *callback = [args callWithArguments:@[@0]];
double ms = [[args callWithArguments:@[@1]] toDouble];
return [self createTimer:callback inteval:ms repeats:YES];
}
- (NSString *)createTimer:(JSValue *)callback
inteval:(double)ms
repeats:(BOOL)repeats {
if (_timers == nil) {
_timers = [NSMutableDictionary dictionaryWithCapacity:1];
}
double interval = ms/1000.0;
NSString *uuid = [[NSUUID UUID] UUIDString];
dispatch_async( dispatch_get_main_queue(), ^{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:interval
repeats:repeats
block:^(NSTimer * _Nonnull timer) {
[callback callWithArguments:nil]; }];
[_timers setObject:timer forKey:uuid];
});
return uuid;
}
- (void)addToContext:(JSContext *)context {
[context setObject:self forKeyedSubscript:@"jsTimer"];
}
- (void)stopTimers {
NSArray *keys = [_timers allKeys];
for (NSString *key in keys) {
NSTimer *timer = [_timers valueForKey:key];
[timer invalidate];
[_timers removeObjectForKey:key];
}
}
@end