Skip to content
This repository has been archived by the owner on Sep 10, 2023. It is now read-only.

Commit

Permalink
feat: support ComputedProperties
Browse files Browse the repository at this point in the history
  • Loading branch information
axetroy committed Mar 7, 2018
1 parent 955af02 commit 01f4170
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 35 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ It base on [https://github.com/bramblex/jsjs](https://github.com/bramblex/jsjs)
* [x] es2015-arrow-functions
* [x] es2015-block-scoping
* [x] es2015-classes
* [ ] es2015-computed-properties
* [x] es2015-computed-properties
* [x] es2015-destructuring
* [ ] es2015-for-of
* [ ] es2015-function-name
Expand Down
98 changes: 64 additions & 34 deletions src/evaluate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,43 +386,71 @@ const evaluate_map = {
},
ObjectExpression(node: types.ObjectExpression, scope: Scope, arg) {
let object = {};
const newScope = scope.$child("block");
const computedProperties: (
| types.ObjectProperty
| types.ObjectMethod)[] = [];

const newArg = {...arg, ...{object}};

for (const property of node.properties) {
if (types.isObjectProperty(property)) {
if (types.isIdentifier(property.key)) {
object[property.key.name] = evaluate(property.value, scope, arg);
} else {
object[evaluate(property.key, scope, arg)] = evaluate(
property.value,
scope,
arg
);
}
} else if (types.isObjectMethod(property)) {
switch (property.kind) {
case "get":
Object.defineProperty(object, evaluate(property.key, scope, arg), {
get: evaluate(property.value, scope, arg)
});
break;
case "set":
Object.defineProperty(object, evaluate(property.key, scope, arg), {
set: evaluate(property.value, scope, arg)
});
break;
case "method":
break;
default:
throw new Error("Invalid kind of property");
}
} else if (types.isSpreadProperty(property)) {
// @experimental Object rest spread
object = Object.assign(object, evaluate(property.argument, scope, arg));
} else {
throw node;
const _property = <types.ObjectMethod | types.ObjectProperty>property;
if (_property.computed === true) {
computedProperties.push(_property);
continue;
}
evaluate(property, newScope, newArg);
}

// eval the computed properties
for (const property of computedProperties) {
evaluate(property, newScope, newArg);
}

return object;
},
ObjectProperty(node: types.ObjectProperty, scope: Scope, arg) {
const {object} = arg;
const val = evaluate(node.value, scope, arg);
if (types.isIdentifier(node.key)) {
object[node.key.name] = val;
scope.$const(node.key.name, val);
} else {
object[evaluate(node.key, scope, arg)] = val;
}
},
ObjectMethod(node: types.ObjectMethod, scope: Scope, arg) {
const key = evaluate(node.key, scope, arg);
const val = function() {
const _arguments = [].slice.call(arguments);
const newScope = scope.$child("function");
newScope.$const("this", this);
// define argument
node.params.forEach((param, i) => {
if (types.isIdentifier(param)) {
newScope.$const(param.name, _arguments[i]);
} else {
throw node;
}
});
const result = evaluate(node.body, newScope, arg);
return result.result ? result.result : result;
};
Object.defineProperty(val, "length", {value: node.params.length});
switch (node.kind) {
case "get":
Object.defineProperty(arg.object, key, {get: val});
scope.$const(key, val);
break;
case "set":
Object.defineProperty(arg.object, key, {set: val});
break;
case "method":
break;
default:
throw new Error("Invalid kind of property");
}
},
FunctionExpression(node: types.FunctionExpression, scope: Scope, arg) {
const func = function(...args) {
const new_scope = scope.$child("function");
Expand Down Expand Up @@ -785,8 +813,10 @@ const evaluate_map = {
SpreadElement(node: types.SpreadElement, scope: Scope, arg) {
return evaluate(node.argument, scope, arg);
},
ObjectProperty(node: types.ObjectProperty, scope: Scope, arg) {
// do nothing
// @experimental Object rest spread
SpreadProperty(node: types.SpreadProperty, scope: Scope, arg) {
const {object} = arg;
Object.assign(object, evaluate(node.argument, scope, arg));
},
ImportDeclaration(node: types.ImportDeclaration, scope: Scope, arg) {
let defaultImport: string = ""; // default import object
Expand Down
71 changes: 71 additions & 0 deletions test/ComputedProperties.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import test from "ava";

import vm from "../src/vm";

test("ComputedProperties-1", t => {
const sandbox: any = vm.createContext({});

const obj: any = vm.runInContext(
`
const name = "axetroy"
const obj = {
["@" + name]: 21
}
module.exports = obj;
`,
sandbox
);

t.deepEqual(obj, {
"@axetroy": 21
});
});

test("ComputedProperties-2", t => {
const sandbox: any = vm.createContext({});

const obj: any = vm.runInContext(
`
var obj = {
["x" + foo]: "heh",
["y" + bar]: "noo",
foo: "foo",
bar: "bar"
};
module.exports = obj;
`,
sandbox
);

t.deepEqual(obj, {
xfoo: "heh",
ybar: "noo",
foo: "foo",
bar: "bar"
});
});

test("ComputedProperties-3", t => {
const sandbox: any = vm.createContext({});

const obj: any = vm.runInContext(
`
var obj = {
foo: "foo",
get ["x" + foo](){
return this.foo
}
};
module.exports = obj;
`,
sandbox
);

t.deepEqual(obj.xfoo, "foo");
t.throws(function() {
obj.xfoo = 123; // it will throw
});
});

0 comments on commit 01f4170

Please sign in to comment.