• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import {
17    expect
18} from 'chai';
19import 'mocha';
20import {
21    EcmaDefinefuncdyn,
22    EcmaIstrue,
23    EcmaReturnundefined,
24    EcmaStglobalvar,
25    EcmaThrowundefinedifhole,
26    Imm,
27    Jeqz,
28    Label,
29    LdaDyn,
30    LdaiDyn,
31    LdaStr,
32    ResultType,
33    StaDyn,
34    VReg
35} from "../src/irnodes";
36import { checkInstructions, compileMainSnippet, SnippetCompiler } from "./utils/base";
37
38describe("HoistTest", function () {
39
40    // case 1: hoist var declared variable ((declared in global scope)) in global scope
41    it('case 1;', function () {
42        let insns = compileMainSnippet("var a = 1;");
43        let expected = [
44            new LdaDyn(new VReg()),
45            new EcmaStglobalvar("a"),
46            new LdaiDyn(new Imm(1)),
47            new EcmaStglobalvar("a"),
48            new EcmaReturnundefined()
49        ]
50
51        expect(checkInstructions(insns, expected)).to.be.true;
52    });
53
54    // case 2: hoist var declared variable (declared in local scope) in global scope
55    it('case 2', function () {
56        let insns = compileMainSnippet(`if (true) {
57                                  var a = 2;
58                                }`);
59        let endLabel = new Label();
60
61        let expected = [
62            new LdaDyn(new VReg()),
63            new EcmaStglobalvar("a"),
64            new LdaDyn(new VReg()),
65            new EcmaIstrue(),
66            new Jeqz(endLabel),
67            new LdaiDyn(new Imm(2)),
68            new EcmaStglobalvar("a"),
69            endLabel,
70            new EcmaReturnundefined()
71        ]
72        expect(checkInstructions(insns, expected)).to.be.true;
73    });
74
75    // case 3: hoist function declaration in global scope
76    it('case 3', function () {
77        let snippetCompiler = new SnippetCompiler();
78        snippetCompiler.compile(`function a() {};`);
79
80        let insns = snippetCompiler.getGlobalInsns();
81        let expected = [
82            new EcmaDefinefuncdyn("a", new Imm(0), new VReg()),
83            new EcmaStglobalvar("a"),
84            new EcmaReturnundefined()
85        ]
86        expect(checkInstructions(insns, expected)).to.be.true;
87    });
88
89    // case 4: In case that two function declared directly in global scope with the same name, hoist the later one.
90    it('case 4', function () {
91        let snippetCompiler = new SnippetCompiler();
92        snippetCompiler.compile(`function a() {}; function a() {}`);
93
94        let insns = snippetCompiler.getGlobalInsns();
95        let expected = [
96            new EcmaDefinefuncdyn("#2#a", new Imm(0), new VReg()),
97            new EcmaStglobalvar("a"),
98            new EcmaReturnundefined()
99        ]
100
101        expect(checkInstructions(insns, expected)).to.be.true;
102    });
103
104    // case 5: hoisting of function declaration is of higher priority than var declared variables with a same name in global scope
105    it('case 5', function () {
106        let snippetCompiler = new SnippetCompiler();
107        snippetCompiler.compile(`var a = 1; function a() {}`);
108        let insns = snippetCompiler.getGlobalInsns();
109        let expected = [
110            new EcmaDefinefuncdyn("a", new Imm(0), new VReg()),
111            new EcmaStglobalvar("a"),
112            new LdaiDyn(new Imm(1)),
113            new EcmaStglobalvar("a"),
114            new EcmaReturnundefined()
115        ]
116
117        expect(checkInstructions(insns, expected)).to.be.true;
118    });
119
120    // case 6: hoist var declared variable in function scope
121    it('case 6', function () {
122        let snippetCompiler = new SnippetCompiler();
123        snippetCompiler.compile(`function a() {var a = 1;}`);
124        let funcPg = snippetCompiler.getPandaGenByName("a");
125        let insns = funcPg!.getInsns();
126
127        let a = new VReg();
128        let expected = [
129            new LdaDyn(a),
130            new StaDyn(new VReg()),
131            new LdaiDyn(new Imm(1)),
132            new StaDyn(a),
133
134            new EcmaReturnundefined()
135        ]
136        expect(checkInstructions(insns!, expected)).to.be.true;
137    });
138
139    // case 7: hoist function declaration in function scope
140    it('case 7', function () {
141        let snippetCompiler = new SnippetCompiler();
142        snippetCompiler.compile(`function a() {function b() {}};`);
143        let funcPg = snippetCompiler.getPandaGenByName("a");
144        let insns = funcPg!.getInsns();
145        let a = new VReg();
146        let expected = [
147            new EcmaDefinefuncdyn("b", new Imm(0), new VReg()),
148            new StaDyn(a),
149
150            new EcmaReturnundefined()
151        ]
152
153        expect(checkInstructions(insns!, expected)).to.be.true;
154    });
155
156    // case 8: temporary dead zone of let in global scope
157    it('case 8', function () {
158        let snippetCompiler = new SnippetCompiler();
159        snippetCompiler.compile(`a = 1;
160                                 let a;`);
161        let funcPg = snippetCompiler.getPandaGenByName("func_main_0");
162        let insns = funcPg!.getInsns();
163        let idReg = new VReg();
164        let expected = [
165            new LdaStr("a"),
166            new StaDyn(idReg),
167            new EcmaThrowundefinedifhole(new VReg(), idReg)
168        ]
169
170        expect(checkInstructions(insns.slice(3, 5), expected));
171    });
172
173    // case 9: temporary dead zone of let in function scope
174    it('case 9', function () {
175        let snippetCompiler = new SnippetCompiler();
176        snippetCompiler.compile(`function b() {
177                                 a = 1;
178                                 let a;
179                                 }`);
180        let funcPg = snippetCompiler.getPandaGenByName("b");
181        let insns = funcPg!.getInsns();
182        let idReg = new VReg();
183
184        let expected = [
185            new LdaStr("a"),
186            new StaDyn(idReg),
187            new EcmaThrowundefinedifhole(new VReg(), idReg)
188        ]
189
190        expect(checkInstructions(insns.slice(3, 5), expected));
191    });
192
193    // case 10: temporary dead zone of let in local scope
194    it('case 10', function () {
195        let snippetCompiler = new SnippetCompiler();
196        snippetCompiler.compile(`{
197                                    a = 1;
198                                    let a;
199                                 }`);
200        let funcPg = snippetCompiler.getPandaGenByName("func_main_0");
201        let insns = funcPg!.getInsns();
202        let idReg = new VReg();
203
204        let expected = [
205            new LdaStr("a"),
206            new StaDyn(idReg),
207            new EcmaThrowundefinedifhole(new VReg(), idReg)
208        ]
209
210        expect(checkInstructions(insns.slice(3, 5), expected));
211    })
212})
213