Pass sessionContext to sandbox

This commit is contained in:
Zaid Abdulla committed 2017-02-26 05:34:18 -08:00
1 parent 59d412155b
commit d92ca4c2bf
2 files changed
+77 -94

No files matched your search

+70 -85
View File
@@ -31,38 +31,29 @@ const COMMIT = Symbol();
// Used to execute extensions and restart // Used to execute extensions and restart
const EXT = Symbol(); const EXT = Symbol();
const sandbox = { const context = vm.createContext();
timestamp: null,
deviceData: null, let state;
revision: null,
maxRevision: null,
uncommitted: false,
declarations: null,
extensionsCache: null,
extensions: null,
clear: null,
rng: null,
context: vm.createContext()
}
class SandboxDate { class SandboxDate {
constructor(arg) { constructor(arg) {
if (arguments.length) if (arguments.length)
return new (Function.prototype.bind.apply(Date, arguments)); return new (Function.prototype.bind.apply(Date, arguments));
return new Date(sandbox.timestamp); return new Date(state.sessionContext.timestamp);
} }
static now() { static now() {
return sandbox.timestamp; return state.sessionContext.timestamp;
} }
}; };
function random() { function random() {
if (!sandbox.rng) if (!state.rng)
sandbox.rng = seedrandom(sandbox.timestamp); state.rng = seedrandom(sessionContext.timestamp);
return sandbox.rng(); return state.rng();
} }
@@ -70,18 +61,18 @@ class ParameterWrapper {
constructor(path, attributes, unpacked, unpackedRevision) { constructor(path, attributes, unpacked, unpackedRevision) {
for (let attrName of attributes) { for (let attrName of attributes) {
Object.defineProperty(this, attrName, {get: function() { Object.defineProperty(this, attrName, {get: function() {
if (sandbox.uncommitted) if (state.uncommitted)
commit(); commit();
if (sandbox.revision !== unpackedRevision) { if (state.revision !== unpackedRevision) {
unpackedRevision = sandbox.revision; unpackedRevision = state.revision;
unpacked = device.unpack(sandbox.deviceData, path, sandbox.revision + 1); unpacked = device.unpack(state.sessionContext.deviceData, path, state.revision + 1);
} }
if (!unpacked.length) if (!unpacked.length)
return undefined; return undefined;
let attr = sandbox.deviceData.attributes.get(unpacked[0], sandbox.revision + 1)[attrName]; let attr = state.sessionContext.deviceData.attributes.get(unpacked[0], state.revision + 1)[attrName];
if (!attr) if (!attr)
return undefined; return undefined;
@@ -91,27 +82,27 @@ class ParameterWrapper {
} }
Object.defineProperty(this, "path", {get: function() { Object.defineProperty(this, "path", {get: function() {
if (sandbox.uncommitted) if (state.uncommitted)
commit(); commit();
if (sandbox.revision !== unpackedRevision) { if (state.revision !== unpackedRevision) {
unpackedRevision = sandbox.revision; unpackedRevision = state.revision;
unpacked = device.unpack(sandbox.deviceData, path, sandbox.revision + 1); unpacked = device.unpack(state.sessionContext.deviceData, path, state.revision + 1);
} }
if (!unpacked.length) if (!unpacked.length)
return undefined; return undefined;
return unpacked[0].join('.'); return unpacked[0].join(".");
}}); }});
Object.defineProperty(this, "size", {get: function() { Object.defineProperty(this, "size", {get: function() {
if (sandbox.uncommitted) if (state.uncommitted)
commit(); commit();
if (sandbox.revision !== unpackedRevision) { if (state.revision !== unpackedRevision) {
unpackedRevision = sandbox.revision; unpackedRevision = state.revision;
unpacked = device.unpack(sandbox.deviceData, path, sandbox.revision + 1); unpacked = device.unpack(state.sessionContext.deviceData, path, state.revision + 1);
} }
if (!unpacked.length) if (!unpacked.length)
@@ -121,23 +112,23 @@ class ParameterWrapper {
}}); }});
this[Symbol.iterator] = function*() { this[Symbol.iterator] = function*() {
if (sandbox.uncommitted) if (state.uncommitted)
commit(); commit();
if (sandbox.revision !== unpackedRevision) { if (state.revision !== unpackedRevision) {
unpackedRevision = sandbox.revision; unpackedRevision = state.revision;
unpacked = device.unpack(sandbox.deviceData, path, sandbox.revision + 1); unpacked = device.unpack(state.sessionContext.deviceData, path, state.revision + 1);
} }
for (let p of unpacked) for (let p of unpacked)
yield new ParameterWrapper(p, attributes, [p], sandbox.revision); yield new ParameterWrapper(p, attributes, [p], state.revision);
} }
} }
} }
function declare(path, timestamps, values) { function declare(path, timestamps, values) {
sandbox.uncommitted = true; state.uncommitted = true;
if (!timestamps) if (!timestamps)
timestamps = {}; timestamps = {};
@@ -175,55 +166,53 @@ function declare(path, timestamps, values) {
} }
} }
sandbox.declarations.push(declaration); state.declarations.push(declaration);
return new ParameterWrapper(parsedPath, Object.keys(attrs)); return new ParameterWrapper(parsedPath, Object.keys(attrs));
} }
function clear(path, timestamp, attributes) { function clear(path, timestamp, attributes) {
sandbox.uncommitted = true; state.uncommitted = true;
if (sandbox.revision === sandbox.maxRevision) { if (state.revision === maxRevision)
sandbox.clear = sandbox.clear || []; state.clear.push([common.parsePath(path), timestamp, attributes]);
sandbox.clear.push([common.parsePath(path), timestamp, attributes]);
}
} }
function commit() { function commit() {
++ sandbox.revision; ++ state.revision;
sandbox.uncommitted = false; state.uncommitted = false;
if (sandbox.revision === sandbox.maxRevision + 1) if (state.revision === state.maxRevision + 1)
throw COMMIT; throw COMMIT;
else if (sandbox.revision > sandbox.maxRevision + 1) else if (state.revision > state.maxRevision + 1)
throw new Error("Declare function should not be called from within a try/catch block"); throw new Error("Declare function should not be called from within a try/catch block");
} }
function ext() { function ext() {
let extCall = Array.from(arguments).map(String); let extCall = Array.from(arguments).map(String);
let key = `${sandbox.revision}: ${JSON.stringify(extCall)}`; let key = `${state.revision}: ${JSON.stringify(extCall)}`;
if (key in sandbox.extensionsCache) if (key in state.sessionContext.extensionsCache)
return sandbox.extensionsCache[key]; return state.sessionContext.extensionsCache[key];
sandbox.extensions[key] = extCall; state.extensions[key] = extCall;
throw EXT; throw EXT;
} }
Object.defineProperty(sandbox.context, 'Date', {value: SandboxDate}); Object.defineProperty(context, "Date", {value: SandboxDate});
Object.defineProperty(sandbox.context, 'declare', {value: declare}); Object.defineProperty(context, "declare", {value: declare});
Object.defineProperty(sandbox.context, 'clear', {value: clear}); Object.defineProperty(context, "clear", {value: clear});
Object.defineProperty(sandbox.context, 'commit', {value: commit}); Object.defineProperty(context, "commit", {value: commit});
Object.defineProperty(sandbox.context, 'ext', {value: ext}); Object.defineProperty(context, "ext", {value: ext});
// Monkey-patch Math.random() to make it deterministic // Monkey-patch Math.random() to make it deterministic
sandbox.context.random = random; context.random = random;
vm.runInContext("Math.random = random;", sandbox.context); vm.runInContext("Math.random = random;", context);
delete sandbox.random; delete context.random;
function errorToFault(err) { function errorToFault(err) {
@@ -245,28 +234,27 @@ function errorToFault(err) {
} }
function run(script, globals, timestamp, deviceData, extensionsCache, startRevision, maxRevision, callback) { function run(script, globals, sessionContext, startRevision, maxRevision, callback) {
sandbox.timestamp = timestamp; state = {
sandbox.deviceData = deviceData; sessionContext: sessionContext,
sandbox.extensionsCache = extensionsCache; revision: startRevision,
sandbox.revision = startRevision; maxRevision: maxRevision,
sandbox.maxRevision = maxRevision; uncommitted: false,
sandbox.uncommitted = false; declarations: [],
sandbox.declarations = []; extensions: {},
sandbox.extensions = {}; clear: [],
sandbox.clear = null; rng: null
sandbox.rng = null; };
for (let n in sandbox.context) for (let n of Object.keys(context))
delete sandbox.context[n]; delete context[n];
for (let n in globals) Object.assign(context, globals);
sandbox.context[n] = globals[n];
let ret, status; let ret, status;
try { try {
ret = script.runInContext(sandbox.context, {displayErrors: false}); ret = script.runInContext(context, {displayErrors: false});
status = 0; status = 0;
} }
catch (err) { catch (err) {
@@ -278,27 +266,24 @@ function run(script, globals, timestamp, deviceData, extensionsCache, startRevis
return callback(null, errorToFault(err)); return callback(null, errorToFault(err));
} }
let ext = sandbox.extensions; let _state = state;
let decs = sandbox.declarations;
let clr = sandbox.clear;
let extCache = sandbox.extensionsCache;
let args = arguments; let args = arguments;
let counter = 3; let counter = 3;
for (let key in sandbox.extensions) { for (let key of Object.keys(_state.extensions)) {
counter += 2; counter += 2;
extensions.run(sandbox.extensions[key], function(err, fault, res) { extensions.run(_state.extensions[key], function(err, fault, res) {
if (err || fault) { if (err || fault) {
if (counter & 1) if (counter & 1)
callback(err, fault); callback(err, fault);
return counter = 0; return counter = 0;
} }
extCache[key] = res; _state.sessionContext.extensionsCache[key] = res;
if ((counter -= 2) === 1) if ((counter -= 2) === 1)
if (status === 2) if (status === 2)
return run.apply(null, args); return run.apply(null, args);
else else
return callback(null, null, clr, decs, status === 0, ret); return callback(null, null, _state.clear, _state.declarations, status === 0, ret);
}); });
} }
@@ -306,7 +291,7 @@ function run(script, globals, timestamp, deviceData, extensionsCache, startRevis
if (status === 2) if (status === 2)
return run.apply(null, args); return run.apply(null, args);
else else
return callback(null, null, clr, decs, status === 0, ret); return callback(null, null, _state.clear, _state.declarations, status === 0, ret);
} }
+7 -9
View File
@@ -348,8 +348,7 @@ runProvisions = (sessionContext, provisions, startRevision, endRevision, callbac
counter += 2 counter += 2
sandbox.run(allProvisions[provision[0]].script, sandbox.run(allProvisions[provision[0]].script,
{args: provision.slice(1)}, sessionContext.timestamp, sessionContext.deviceData, {args: provision.slice(1)}, sessionContext, startRevision, endRevision,
sessionContext.extensionsCache, startRevision, endRevision,
(err, _fault, _clear, _declarations, _done) -> (err, _fault, _clear, _declarations, _done) ->
if err or _fault if err or _fault
callback(err, _fault) if counter & 1 callback(err, _fault) if counter & 1
@@ -357,10 +356,10 @@ runProvisions = (sessionContext, provisions, startRevision, endRevision, callbac
done &&= _done done &&= _done
if _declarations if _declarations.length
allDeclarations = allDeclarations.concat(_declarations) allDeclarations = allDeclarations.concat(_declarations)
if _clear if _clear.length
allClear = allClear.concat(_clear) allClear = allClear.concat(_clear)
if (counter -= 2) == 1 if (counter -= 2) == 1
@@ -382,8 +381,7 @@ runVirtualParameters = (sessionContext, provisions, startRevision, endRevision,
counter += 2 counter += 2
globals = {TIMESTAMPS: provision[1], VALUES: provision[2]} globals = {TIMESTAMPS: provision[1], VALUES: provision[2]}
sandbox.run(allVirtualParameters[provision[0]].script, globals, sandbox.run(allVirtualParameters[provision[0]].script, globals,
sessionContext.timestamp, sessionContext.deviceData, sessionContext, startRevision, endRevision,
sessionContext.extensionsCache, startRevision, endRevision,
(err, _fault, _clear, _declarations, _done, _returnValue) -> (err, _fault, _clear, _declarations, _done, _returnValue) ->
if err or _fault if err or _fault
callback(err, _fault) if counter & 1 callback(err, _fault) if counter & 1
@@ -391,10 +389,10 @@ runVirtualParameters = (sessionContext, provisions, startRevision, endRevision,
done &&= _done done &&= _done
if _declarations if _declarations.length
allDeclarations = allDeclarations.concat(_declarations) allDeclarations = allDeclarations.concat(_declarations)
if _clear if _clear.length
allClear = allClear.concat(_clear) allClear = allClear.concat(_clear)
if _done if _done
@@ -575,7 +573,7 @@ rpcRequest = (sessionContext, _declarations, callback) ->
return callback(null, fault) return callback(null, fault)
sessionContext.declarations.push(decs) sessionContext.declarations.push(decs)
sessionContext.doneProvisions |= 1 << inception if done or not decs.length sessionContext.doneProvisions |= 1 << inception if done
for d in decs for d in decs
for ad in device.getAliasDeclarations(d[0], 1) for ad in device.getAliasDeclarations(d[0], 1)