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

Commit

Permalink
fix: fix do-while scope
Browse files Browse the repository at this point in the history
  • Loading branch information
axetroy committed Mar 10, 2018
1 parent 127c5c6 commit e9a9542
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 23 deletions.
45 changes: 27 additions & 18 deletions src/evaluate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const evaluate_map: EvaluateMap = {
}
}

let new_scope = scope.invasived ? scope : scope.$child("block");
let new_scope = scope.invasive ? scope : scope.$child("block");
let _result;
for (const node of block.body) {
const result = (_result = evaluate(path.$child(node, new_scope)));
Expand Down Expand Up @@ -136,16 +136,15 @@ const evaluate_map: EvaluateMap = {
VariableDeclaration(path) {
const { node, scope } = path;
const kind = node.kind;

for (const declartor of node.declarations) {
const varKeyValueMap: string[] = [];
let varName: string;
let varValue: any;
if (types.isIdentifier(declartor.id)) {
const { name } = declartor.id;
const value = declartor.init
varKeyValueMap[declartor.id.name] = declartor.init
? evaluate(path.$child(declartor.init))
: undefined;
if (!scope.$declar(kind, name, value)) {
throw new ErrDuplicateDeclard(name);
}
return value;
} else if (types.isObjectPattern(declartor.id)) {
// @es2015 destrucuring
const vars: { key: string; alias: string }[] = [];
Expand All @@ -161,7 +160,7 @@ const evaluate_map: EvaluateMap = {

for (let $var of vars) {
if ($var.key in obj) {
scope.$declar(kind, $var.alias, obj[$var.key]);
varKeyValueMap[$var.alias] = obj[$var.key];
}
}
} else if (types.isArrayPattern(declartor.id)) {
Expand All @@ -176,12 +175,10 @@ const evaluate_map: EvaluateMap = {
if (types.isArrayExpression(declartor.init)) {
const el = declartor.init.elements[i];
if (!el) {
scope.$declar(kind, $varName, undefined);
return undefined;
varKeyValueMap[$varName] = undefined;
} else {
const result = evaluate(path.$child(el));
scope.$declar(kind, $varName, result);
return result;
varKeyValueMap[$varName] = result;
}
} else {
throw node;
Expand All @@ -191,6 +188,18 @@ const evaluate_map: EvaluateMap = {
} else {
throw node;
}

for (let varName in varKeyValueMap) {
if (scope.invasive && kind === "var") {
if (scope.parent) {
scope.parent.$declar(kind, varName, varKeyValueMap[varName]);
} else {
scope.$declar(kind, varName, varKeyValueMap[varName]);
}
} else {
scope.$declar(kind, varName, varKeyValueMap[varName]);
}
}
}
},
VariableDeclarator: path => {
Expand Down Expand Up @@ -347,7 +356,7 @@ const evaluate_map: EvaluateMap = {

for (const value in evaluate(path.$child(node.right))) {
const new_scope = scope.$child("loop");
new_scope.invasived = true;
new_scope.invasive = true;

new_scope.$declar(kind, name, value);

Expand All @@ -363,9 +372,10 @@ const evaluate_map: EvaluateMap = {
},
DoWhileStatement(path) {
const { node, scope } = path;
// do while don't have his own scope
do {
const new_scope = scope.$child("loop");
new_scope.invasived = true;
new_scope.invasive = true; // do while循环具有侵入性,定义var的时候,是覆盖父级变量
const result = evaluate(path.$child(node.body, new_scope)); // 先把do的执行一遍
if (result === BREAK_SINGAL) {
break;
Expand All @@ -380,7 +390,7 @@ const evaluate_map: EvaluateMap = {
const { node, scope } = path;
while (evaluate(path.$child(node.test))) {
const new_scope = scope.$child("loop");
new_scope.invasived = true;
new_scope.invasive = true;
const result = evaluate(path.$child(node.body, new_scope));

if (result === BREAK_SINGAL) {
Expand All @@ -407,7 +417,7 @@ const evaluate_map: EvaluateMap = {
if (node.handler) {
const param = <types.Identifier>node.handler.param;
const new_scope = scope.$child("block");
new_scope.invasived = true; // 标记为侵入式Scope,不用再多构造啦
new_scope.invasive = true; // 标记为侵入式Scope,不用再多构造啦
new_scope.$const(param.name, err);
return evaluate(path.$child(node.handler, new_scope));
} else {
Expand Down Expand Up @@ -621,7 +631,7 @@ const evaluate_map: EvaluateMap = {
const { node, scope } = path;
const func = function functionDeclaration(..._arguments) {
const newScope = scope.$child("function");
newScope.invasived = true;
newScope.invasive = true;
for (let i = 0; i < node.params.length; i++) {
const param = node.params[i];
if (types.isIdentifier(param)) {
Expand Down Expand Up @@ -840,7 +850,6 @@ const evaluate_map: EvaluateMap = {
const { node, scope } = path;
const func = function(...args) {
const new_scope = scope.$child("function");
new_scope.invasived = true;
for (let i = 0; i < node.params.length; i++) {
const { name } = <types.Identifier>node.params[i];
new_scope.$const(name, args[i]);
Expand Down
16 changes: 13 additions & 3 deletions src/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,21 @@ export class ScopeVar {
}

export class Scope {
// scope var
private content: { [key: string]: ScopeVar } = {};

public invasived: boolean = false;
// the scope have invasive property
public invasive: boolean = false;

// is the top level scope
public isTopLevel: boolean = false;

// scope context
public context: Context;

constructor(
public readonly type: ScopeType,
public parent: Scope | null = null,
public parent: Scope | null,
label?: string
) {
this.context = new Context();
Expand All @@ -48,7 +52,7 @@ export class Scope {
}
}

$all() {
$all(): { [key: string]: any } {
const map = {};
for (let varName in this.content) {
const val = this.content[varName];
Expand Down Expand Up @@ -132,3 +136,9 @@ export class Scope {
return new Scope(type, this, label);
}
}

let a = 1;

for (let i = 0; i < 2; i++) {
let a = i;
}
2 changes: 1 addition & 1 deletion src/vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class Vm {
filename: "index.js"
}
): any | null {
const scope = new Scope("block");
const scope = new Scope("block", null);
scope.isTopLevel = true;
scope.$const("this", this);
scope.$setContext(context);
Expand Down
20 changes: 19 additions & 1 deletion test/ecma5/array/ArrayExpression.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import test from "ava";

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

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

const arr: any = vm.runInContext(
Expand All @@ -19,3 +19,21 @@ module.exports = arr;
t.deepEqual(arr.length, 4);
t.deepEqual(arr, [1, 2, 3, 4]);
});

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

const arr: any = vm.runInContext(
`
const arr = [1, 2, 3 + 3];
arr.push(4);
module.exports = arr;
`,
sandbox
);

t.true(Array.isArray(arr));
t.deepEqual(arr.length, 4);
t.deepEqual(arr, [1, 2, 6, 4]);
});
60 changes: 60 additions & 0 deletions test/ecma5/do-while/DoWhileStatement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,63 @@ module.exports = obj;
t.true(typeof obj.i === "number");
t.deepEqual(obj.i, 3);
});

test("DoWhileStatement var in do block should cover the parent scope", t => {
const sandbox: any = vm.createContext({});

const a: any = vm.runInContext(
`
var a = 1;
do {
var a = 2; // parent scope have a = 1, here is the child scope
} while (false);
module.exports = a;
`,
sandbox
);
t.deepEqual(a, 2);
});

test("DoWhileStatement let in do block should define in it's scope", t => {
const sandbox: any = vm.createContext({});

const obj: any = vm.runInContext(
`
var a = 1;
var b;
do {
let a = 2; // have his own scope
b = a;
} while (false)
module.exports = {a: a, b: b};
`,
sandbox
);
t.deepEqual(obj.a, 1);
t.deepEqual(obj.b, 2);
});

test("DoWhileStatement const in do block should define in it's scope", t => {
const sandbox: any = vm.createContext({});

const obj: any = vm.runInContext(
`
var a = 1;
var b;
do {
const a = 2; // have his own scope
b = a;
} while (false)
module.exports = {a: a, b: b};
`,
sandbox
);
t.deepEqual(obj.a, 1);
t.deepEqual(obj.b, 2);
});

0 comments on commit e9a9542

Please sign in to comment.