flow: refactor
This commit is contained in:
parent
d2dbaaa6ae
commit
8c40f81341
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user