flow: refactor

This commit is contained in:
cha0s 2021-04-27 23:40:52 -05:00
parent d2dbaaa6ae
commit 8c40f81341
2 changed files with 112 additions and 159 deletions

View File

@ -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);

View File

@ -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;