From 8c40f8134164f0e541f5efccaa474c189ddbf841 Mon Sep 17 00:00:00 2001 From: cha0s Date: Tue, 27 Apr 2021 23:40:52 -0500 Subject: [PATCH] flow: refactor --- packages/sandbox/src/sandbox.js | 251 ++++++++++++++------------------ packages/sandbox/src/scope.js | 20 +-- 2 files changed, 112 insertions(+), 159 deletions(-) diff --git a/packages/sandbox/src/sandbox.js b/packages/sandbox/src/sandbox.js index d3d277e..4ff7de8 100644 --- a/packages/sandbox/src/sandbox.js +++ b/packages/sandbox/src/sandbox.js @@ -33,9 +33,7 @@ export default class Sandbox { for (let i = 0; i < properties.length; ++i) { const property = properties[i]; this.setNextScope(property, scope); - const k = property.computed - ? this.evaluate(property.key) - : {value: property.key.name}; + const k = property.computed ? this.evaluate(property.key) : {value: property.key.name}; if (k.async) { promises.push(Promise.resolve(k.value).then((k) => { if (types.isIdentifier(property.value)) { @@ -49,8 +47,7 @@ export default class Sandbox { continue; } if (types.isIdentifier(property.value)) { - const {name} = property.value; - scope.allocate(name, init[k.value]); + scope.allocate(property.value.name, init[k.value]); } else { const promiseOrVoid = this.destructure(property.value, init[k.value], scope); @@ -59,16 +56,24 @@ export default class Sandbox { } } } - return promises.length > 0 - ? Promise.all(promises) - : undefined; + return promises.length > 0 ? Promise.all(promises) : undefined; } evaluate(node) { this.setNextScope(node); - const evaluatorType = this.constructor.selectEvaluatorType(node); - return evaluatorType - ? this[`evaluate${evaluatorType}`](node) + const {type} = node; + let evaluator = `evaluate${type}`; + if (!this[evaluator]) { + const keys = types.ALIAS_KEYS[type]; + for (let i = keys.length - 1; i >= 0; --i) { + // eslint-disable-next-line no-cond-assign + if (this[evaluator = `evaluate${keys[i]}`]) { + break; + } + } + } + return this[evaluator] + ? this[evaluator](node) // eslint-disable-next-line no-console : console.error("evaluate(): Can't handle", node.type); } @@ -95,39 +100,26 @@ export default class Sandbox { if (!types.isMemberExpression(left)) { const assign = (value) => { switch (operator) { - case '=': - return scope.set(left.name, value); - case '+=': - return scope.set(left.name, scope.get(left.name) + value); - case '-=': - return scope.set(left.name, scope.get(left.name) - value); - case '*=': - return scope.set(left.name, scope.get(left.name) * value); - case '/=': - return scope.set(left.name, scope.get(left.name) / value); - case '%=': - return scope.set(left.name, scope.get(left.name) % value); - case '**=': - return scope.set(left.name, scope.get(left.name) ** value); + /* eslint-disable no-multi-spaces, switch-colon-spacing */ + case '=' : return scope.set(left.name, value); + case '+=' : return scope.set(left.name, scope.get(left.name) + value); + case '-=' : return scope.set(left.name, scope.get(left.name) - value); + case '*=' : return scope.set(left.name, scope.get(left.name) * value); + case '/=' : return scope.set(left.name, scope.get(left.name) / value); + case '%=' : return scope.set(left.name, scope.get(left.name) % value); + case '**=' : return scope.set(left.name, scope.get(left.name) ** value); /* eslint-disable no-bitwise */ - case '<<=': - return scope.set(left.name, scope.get(left.name) << value); - case '>>=': - return scope.set(left.name, scope.get(left.name) >> value); - case '>>>=': - return scope.set(left.name, scope.get(left.name) >>> value); - case '|=': - return scope.set(left.name, scope.get(left.name) | value); - case '^=': - return scope.set(left.name, scope.get(left.name) ^ value); - case '&=': - return scope.set(left.name, scope.get(left.name) & value); - /* eslint-enable no-bitwise */ - case '||=': - return scope.set(left.name, scope.get(left.name) || value); - case '&&=': - return scope.set(left.name, scope.get(left.name) && value); - case '??=': { + case '<<=' : return scope.set(left.name, scope.get(left.name) << value); + case '>>=' : return scope.set(left.name, scope.get(left.name) >> value); + case '>>>=': return scope.set(left.name, scope.get(left.name) >>> value); + case '|=' : return scope.set(left.name, scope.get(left.name) | value); + case '^=' : return scope.set(left.name, scope.get(left.name) ^ value); + case '&=' : return scope.set(left.name, scope.get(left.name) & value); + /* eslint-enable no-bitwise, no-multi-spaces */ + case '||=' : return scope.set(left.name, scope.get(left.name) || value); + case '&&=' : return scope.set(left.name, scope.get(left.name) && value); + case '??=' : { + /* eslint-enable switch-colon-spacing */ const l = scope.get(left.name); return scope.set(left.name, (l === null || l === undefined) ? value : l); } @@ -156,40 +148,26 @@ export default class Sandbox { return undefined; } switch (operator) { - /* eslint-disable no-param-reassign, no-return-assign */ - case '=': - return O[P] = value; - case '+=': - return O[P] += value; - case '-=': - return O[P] -= value; - case '*=': - return O[P] *= value; - case '/=': - return O[P] /= value; - case '%=': - return O[P] %= value; - case '**=': - return O[P] **= value; + // eslint-disable-next-line max-len + /* eslint-disable no-param-reassign, no-return-assign, no-multi-spaces, switch-colon-spacing */ + case '=' : return O[P] = value; + case '+=' : return O[P] += value; + case '-=' : return O[P] -= value; + case '*=' : return O[P] *= value; + case '/=' : return O[P] /= value; + case '%=' : return O[P] %= value; + case '**=' : return O[P] **= value; /* eslint-disable no-bitwise */ - case '<<=': - return O[P] <<= value; - case '>>=': - return O[P] >>= value; - case '>>>=': - return O[P] >>>= value; - case '|=': - return O[P] |= value; - case '^=': - return O[P] ^= value; - case '&=': - return O[P] &= value; + case '<<=' : return O[P] <<= value; + case '>>=' : return O[P] >>= value; + case '>>>=': return O[P] >>>= value; + case '|=' : return O[P] |= value; + case '^=' : return O[P] ^= value; + case '&=' : return O[P] &= value; /* eslint-enable no-bitwise */ - case '||=': - return O[P] ||= value; - case '&&=': - return O[P] &&= value; - case '??=': { + case '||=' : return O[P] ||= value; + case '&&=' : return O[P] &&= value; + case '??=' : { return O[P] = (O[P] === null || O[P] === undefined) ? value : O[P]; /* eslint-enable no-param-reassign, no-return-assign */ } @@ -282,13 +260,15 @@ export default class Sandbox { asyncArgs |= async; args.push(value); } - const invoke = (callee, args, optional) => { - if (optional) { - return callee?.(...args); - } - return callee(...args); - }; const {callee, optional: callOptional} = node; + /* eslint-disable switch-colon-spacing */ + const invoke = (fn, holder, args) => { + if (callOptional && !fn) { + return undefined; + } + return this.constructor.fastCall(fn, holder, args); + }; + /* eslint-enable switch-colon-spacing */ if (!types.isMemberExpression(callee)) { const {async, value} = this.evaluate(callee); if (asyncArgs || async) { @@ -296,30 +276,18 @@ export default class Sandbox { async: true, value: Promise .all([value, Promise.all(args)]) - .then(([callee, args]) => invoke(callee, args, callOptional)), + .then(([callee, args]) => invoke(callee, undefined, args)), }; } - return {value: invoke(value, args, callOptional)}; + return {value: invoke(value, undefined, args)}; } - const invokeMember = (O, P, args, callOptional, memberOptional) => { - if (callOptional) { - if (memberOptional) { - return O?.[P]?.(...args); - } - return O[P]?.(...args); - } - if (memberOptional) { - return O?.[P](...args); - } - return O[P](...args); - }; - this.setNextScope(callee); const { computed, object, optional: memberOptional, property, } = callee; + this.setNextScope(callee); const O = this.evaluate(object); const P = computed ? this.evaluate(property) : {value: property.name}; if (asyncArgs || O.async || P.async) { @@ -327,10 +295,10 @@ export default class Sandbox { async: true, value: Promise .all([O.value, P.value, Promise.all(args)]) - .then(([O, P, args]) => invokeMember(O, P, args, callOptional, memberOptional)), + .then(([O, P, args]) => invoke(memberOptional ? O?.[P] : O[P], O, args)), }; } - return {value: invokeMember(O.value, P.value, args, callOptional, memberOptional)}; + return {value: invoke(memberOptional ? O.value?.[P.value] : O.value[P.value], O.value, args)}; } evaluateConditionalExpression(node) { @@ -338,9 +306,8 @@ export default class Sandbox { if (test.async) { return { async: true, - value: Promise.resolve(test.value).then((test) => ( - this.evaluate(test ? node.consequent : node.alternate).value - )), + value: Promise.resolve(test.value) + .then((test) => this.evaluate(test ? node.consequent : node.alternate).value), }; } return this.evaluate(test.value ? node.consequent : node.alternate); @@ -468,19 +435,19 @@ export default class Sandbox { value: isAsync ? Promise.all(entries) .then((entries) => { - const reduced = []; + const flat = []; for (let i = 0; i < entries.length; ++i) { const entry = entries[i]; if (Array.isArray(entry[0])) { for (let j = 0; j < entry.length; j++) { - reduced.push(entry[j]); + flat.push(entry[j]); } } else { - reduced.push(entry); + flat.push(entry); } } - return Object.fromEntries(reduced); + return Object.fromEntries(flat); }) : Object.fromEntries(entries), }; @@ -524,10 +491,8 @@ export default class Sandbox { const update = (value) => { if (prefix) { switch (operator) { - case '++': - return scope.set(argument.name, value + 1); - case '--': - return scope.set(argument.name, value - 1); + case '++': return scope.set(argument.name, value + 1); + case '--': return scope.set(argument.name, value - 1); default: } } @@ -553,6 +518,42 @@ export default class Sandbox { return {value: update(value)}; } + static fastCall(fn, holder, args) { + if (holder) { + const {name} = fn; + if (name in holder && holder[name] === fn) { + switch (args.length) { + case 0 : return holder[name](); + case 1 : return holder[name](args[0]); + case 2 : return holder[name](args[0], args[1]); + case 3 : return holder[name](args[0], args[1], args[2]); + case 4 : return holder[name](args[0], args[1], args[2], args[3]); + case 5 : return holder[name](args[0], args[1], args[2], args[3], args[4]); + default: return holder[name](...args); + } + } + const bound = fn.bind(holder); + switch (args.length) { + case 0 : return bound(); + case 1 : return bound(args[0]); + case 2 : return bound(args[0], args[1]); + case 3 : return bound(args[0], args[1], args[2]); + case 4 : return bound(args[0], args[1], args[2], args[3]); + case 5 : return bound(args[0], args[1], args[2], args[3], args[4]); + default: return bound(...args); + } + } + switch (args.length) { + case 0 : return fn(); + case 1 : return fn(args[0]); + case 2 : return fn(args[0], args[1]); + case 3 : return fn(args[0], args[1], args[2]); + case 4 : return fn(args[0], args[1], args[2], args[3]); + case 5 : return fn(args[0], args[1], args[2], args[3], args[4]); + default: return fn(...args); + } + } + next() { return this.runner.next(); } @@ -621,34 +622,6 @@ export default class Sandbox { return this; } - static selectEvaluatorType(node) { - const flat = [ - 'ArrayExpression', - 'AssignmentExpression', - 'AwaitExpression', - 'BinaryExpression', - 'CallExpression', - 'ConditionalExpression', - 'DirectiveLiteral', - 'Identifier', - 'Literal', - 'LogicalExpression', - 'MemberExpression', - 'ObjectExpression', - 'UnaryExpression', - 'UpdateExpression', - ]; - for (let i = 0; i < flat.length; i++) { - const type = flat[i]; - if (types[`is${type}`](node)) { - return type; - } - } - // eslint-disable-next-line no-console - console.error("selectEvaluatorType(): Can't handle", node.type); - return undefined; - } - setNextScope(node, scope = this.nodeScope(node)) { const nodes = this.nextNodes(node, types.VISITOR_KEYS[node.type]); for (let i = 0; i < nodes.length; i++) { @@ -799,9 +772,7 @@ export default class Sandbox { // Evaluate... if (types.isReturnStatement(node)) { // eslint-disable-next-line consistent-return - return !node.argument - ? {value: undefined} - : this.evaluate(node.argument); + return !node.argument ? {value: undefined} : this.evaluate(node.argument); } if (types.isDirective(node)) { yield this.evaluate(node.value); diff --git a/packages/sandbox/src/scope.js b/packages/sandbox/src/scope.js index 2f27767..7ba8711 100644 --- a/packages/sandbox/src/scope.js +++ b/packages/sandbox/src/scope.js @@ -23,24 +23,6 @@ export default class Scope { return undefined; } - getAll() { - const all = {}; - const keys = new Set(); - let walk = this; - while (walk) { - const entries = Object.entries(walk.context); - for (let i = 0; i < entries.length; i++) { - const [key, value] = entries[i]; - if (!keys.has(key)) { - keys.add(key); - all[key] = value; - } - } - walk = walk.parent; - } - return all; - } - pop() { const {parent} = this; this.parent = null; @@ -61,7 +43,7 @@ export default class Scope { walk.context[key] = value; return value; } - // TODO: option to disallow global set + // TODO: disallow global set? if (!walk.parent) { walk.context[key] = value; return value;