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

Commit e9a9542

Browse files
committed
fix: fix do-while scope
1 parent 127c5c6 commit e9a9542

File tree

5 files changed

+120
-23
lines changed

5 files changed

+120
-23
lines changed

src/evaluate.ts

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ const evaluate_map: EvaluateMap = {
9898
}
9999
}
100100

101-
let new_scope = scope.invasived ? scope : scope.$child("block");
101+
let new_scope = scope.invasive ? scope : scope.$child("block");
102102
let _result;
103103
for (const node of block.body) {
104104
const result = (_result = evaluate(path.$child(node, new_scope)));
@@ -136,16 +136,15 @@ const evaluate_map: EvaluateMap = {
136136
VariableDeclaration(path) {
137137
const { node, scope } = path;
138138
const kind = node.kind;
139+
139140
for (const declartor of node.declarations) {
141+
const varKeyValueMap: string[] = [];
142+
let varName: string;
143+
let varValue: any;
140144
if (types.isIdentifier(declartor.id)) {
141-
const { name } = declartor.id;
142-
const value = declartor.init
145+
varKeyValueMap[declartor.id.name] = declartor.init
143146
? evaluate(path.$child(declartor.init))
144147
: undefined;
145-
if (!scope.$declar(kind, name, value)) {
146-
throw new ErrDuplicateDeclard(name);
147-
}
148-
return value;
149148
} else if (types.isObjectPattern(declartor.id)) {
150149
// @es2015 destrucuring
151150
const vars: { key: string; alias: string }[] = [];
@@ -161,7 +160,7 @@ const evaluate_map: EvaluateMap = {
161160

162161
for (let $var of vars) {
163162
if ($var.key in obj) {
164-
scope.$declar(kind, $var.alias, obj[$var.key]);
163+
varKeyValueMap[$var.alias] = obj[$var.key];
165164
}
166165
}
167166
} else if (types.isArrayPattern(declartor.id)) {
@@ -176,12 +175,10 @@ const evaluate_map: EvaluateMap = {
176175
if (types.isArrayExpression(declartor.init)) {
177176
const el = declartor.init.elements[i];
178177
if (!el) {
179-
scope.$declar(kind, $varName, undefined);
180-
return undefined;
178+
varKeyValueMap[$varName] = undefined;
181179
} else {
182180
const result = evaluate(path.$child(el));
183-
scope.$declar(kind, $varName, result);
184-
return result;
181+
varKeyValueMap[$varName] = result;
185182
}
186183
} else {
187184
throw node;
@@ -191,6 +188,18 @@ const evaluate_map: EvaluateMap = {
191188
} else {
192189
throw node;
193190
}
191+
192+
for (let varName in varKeyValueMap) {
193+
if (scope.invasive && kind === "var") {
194+
if (scope.parent) {
195+
scope.parent.$declar(kind, varName, varKeyValueMap[varName]);
196+
} else {
197+
scope.$declar(kind, varName, varKeyValueMap[varName]);
198+
}
199+
} else {
200+
scope.$declar(kind, varName, varKeyValueMap[varName]);
201+
}
202+
}
194203
}
195204
},
196205
VariableDeclarator: path => {
@@ -347,7 +356,7 @@ const evaluate_map: EvaluateMap = {
347356

348357
for (const value in evaluate(path.$child(node.right))) {
349358
const new_scope = scope.$child("loop");
350-
new_scope.invasived = true;
359+
new_scope.invasive = true;
351360

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

@@ -363,9 +372,10 @@ const evaluate_map: EvaluateMap = {
363372
},
364373
DoWhileStatement(path) {
365374
const { node, scope } = path;
375+
// do while don't have his own scope
366376
do {
367377
const new_scope = scope.$child("loop");
368-
new_scope.invasived = true;
378+
new_scope.invasive = true; // do while循环具有侵入性,定义var的时候,是覆盖父级变量
369379
const result = evaluate(path.$child(node.body, new_scope)); // 先把do的执行一遍
370380
if (result === BREAK_SINGAL) {
371381
break;
@@ -380,7 +390,7 @@ const evaluate_map: EvaluateMap = {
380390
const { node, scope } = path;
381391
while (evaluate(path.$child(node.test))) {
382392
const new_scope = scope.$child("loop");
383-
new_scope.invasived = true;
393+
new_scope.invasive = true;
384394
const result = evaluate(path.$child(node.body, new_scope));
385395

386396
if (result === BREAK_SINGAL) {
@@ -407,7 +417,7 @@ const evaluate_map: EvaluateMap = {
407417
if (node.handler) {
408418
const param = <types.Identifier>node.handler.param;
409419
const new_scope = scope.$child("block");
410-
new_scope.invasived = true; // 标记为侵入式Scope,不用再多构造啦
420+
new_scope.invasive = true; // 标记为侵入式Scope,不用再多构造啦
411421
new_scope.$const(param.name, err);
412422
return evaluate(path.$child(node.handler, new_scope));
413423
} else {
@@ -621,7 +631,7 @@ const evaluate_map: EvaluateMap = {
621631
const { node, scope } = path;
622632
const func = function functionDeclaration(..._arguments) {
623633
const newScope = scope.$child("function");
624-
newScope.invasived = true;
634+
newScope.invasive = true;
625635
for (let i = 0; i < node.params.length; i++) {
626636
const param = node.params[i];
627637
if (types.isIdentifier(param)) {
@@ -840,7 +850,6 @@ const evaluate_map: EvaluateMap = {
840850
const { node, scope } = path;
841851
const func = function(...args) {
842852
const new_scope = scope.$child("function");
843-
new_scope.invasived = true;
844853
for (let i = 0; i < node.params.length; i++) {
845854
const { name } = <types.Identifier>node.params[i];
846855
new_scope.$const(name, args[i]);

src/scope.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,21 @@ export class ScopeVar {
2222
}
2323

2424
export class Scope {
25+
// scope var
2526
private content: { [key: string]: ScopeVar } = {};
2627

27-
public invasived: boolean = false;
28+
// the scope have invasive property
29+
public invasive: boolean = false;
2830

31+
// is the top level scope
2932
public isTopLevel: boolean = false;
3033

34+
// scope context
3135
public context: Context;
3236

3337
constructor(
3438
public readonly type: ScopeType,
35-
public parent: Scope | null = null,
39+
public parent: Scope | null,
3640
label?: string
3741
) {
3842
this.context = new Context();
@@ -48,7 +52,7 @@ export class Scope {
4852
}
4953
}
5054

51-
$all() {
55+
$all(): { [key: string]: any } {
5256
const map = {};
5357
for (let varName in this.content) {
5458
const val = this.content[varName];
@@ -132,3 +136,9 @@ export class Scope {
132136
return new Scope(type, this, label);
133137
}
134138
}
139+
140+
let a = 1;
141+
142+
for (let i = 0; i < 2; i++) {
143+
let a = i;
144+
}

src/vm.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export class Vm {
2222
filename: "index.js"
2323
}
2424
): any | null {
25-
const scope = new Scope("block");
25+
const scope = new Scope("block", null);
2626
scope.isTopLevel = true;
2727
scope.$const("this", this);
2828
scope.$setContext(context);

test/ecma5/array/ArrayExpression.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import test from "ava";
22

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

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

88
const arr: any = vm.runInContext(
@@ -19,3 +19,21 @@ module.exports = arr;
1919
t.deepEqual(arr.length, 4);
2020
t.deepEqual(arr, [1, 2, 3, 4]);
2121
});
22+
23+
test("ArrayExpression-2", t => {
24+
const sandbox: any = vm.createContext({});
25+
26+
const arr: any = vm.runInContext(
27+
`
28+
const arr = [1, 2, 3 + 3];
29+
arr.push(4);
30+
31+
module.exports = arr;
32+
`,
33+
sandbox
34+
);
35+
36+
t.true(Array.isArray(arr));
37+
t.deepEqual(arr.length, 4);
38+
t.deepEqual(arr, [1, 2, 6, 4]);
39+
});

test/ecma5/do-while/DoWhileStatement.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,63 @@ module.exports = obj;
2121
t.true(typeof obj.i === "number");
2222
t.deepEqual(obj.i, 3);
2323
});
24+
25+
test("DoWhileStatement var in do block should cover the parent scope", t => {
26+
const sandbox: any = vm.createContext({});
27+
28+
const a: any = vm.runInContext(
29+
`
30+
var a = 1;
31+
32+
do {
33+
var a = 2; // parent scope have a = 1, here is the child scope
34+
} while (false);
35+
36+
module.exports = a;
37+
`,
38+
sandbox
39+
);
40+
t.deepEqual(a, 2);
41+
});
42+
43+
test("DoWhileStatement let in do block should define in it's scope", t => {
44+
const sandbox: any = vm.createContext({});
45+
46+
const obj: any = vm.runInContext(
47+
`
48+
var a = 1;
49+
var b;
50+
51+
do {
52+
let a = 2; // have his own scope
53+
b = a;
54+
} while (false)
55+
56+
module.exports = {a: a, b: b};
57+
`,
58+
sandbox
59+
);
60+
t.deepEqual(obj.a, 1);
61+
t.deepEqual(obj.b, 2);
62+
});
63+
64+
test("DoWhileStatement const in do block should define in it's scope", t => {
65+
const sandbox: any = vm.createContext({});
66+
67+
const obj: any = vm.runInContext(
68+
`
69+
var a = 1;
70+
var b;
71+
72+
do {
73+
const a = 2; // have his own scope
74+
b = a;
75+
} while (false)
76+
77+
module.exports = {a: a, b: b};
78+
`,
79+
sandbox
80+
);
81+
t.deepEqual(obj.a, 1);
82+
t.deepEqual(obj.b, 2);
83+
});

0 commit comments

Comments
 (0)