• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-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 { expect } from 'chai';
17import 'mocha';
18import * as ts from "typescript";
19import {
20    CompilerDriver
21} from "../src/compilerDriver";
22import {
23    Add2,
24    Definefunc,
25    Inc,
26    Ldlexvar,
27    Newlexenv,
28    Returnundefined,
29    Stglobalvar,
30    Sttoglobalrecord,
31    Stlexvar,
32    ThrowUndefinedifholewithname,
33    Tonumeric,
34    Trystglobalbyname,
35    Imm,
36    IRNode,
37    Lda,
38    Ldai,
39    Return,
40    Sta,
41    VReg
42} from "../src/irnodes";
43import { PandaGen } from "../src/pandagen";
44import { CacheExpander } from "../src/pass/cacheExpander";
45import { Recorder } from '../src/recorder';
46import {
47    FunctionScope,
48    GlobalScope,
49    VariableScope
50} from "../src/scope";
51import {
52    GlobalVariable,
53    LocalVariable,
54    VarDeclarationKind,
55} from "../src/variable";
56import { creatAstFromSnippet } from "./utils/asthelper";
57import {
58    checkInstructions,
59    compileAllSnippet,
60    SnippetCompiler
61} from "./utils/base";
62
63describe("lexenv-compile-testcase in lexenv.test.ts", function () {
64
65    it("test CompilerDriver.scanFunctions-with-empty", function () {
66        let source: string = ``;
67        let sourceFile = creatAstFromSnippet(source);
68        let compilerDriver = new CompilerDriver('UnitTest', 'UnitTest');
69        let globalScope = new GlobalScope(sourceFile);
70        let recorder = new Recorder(sourceFile, globalScope, compilerDriver, false, false, true);
71        recorder.record();
72
73        expect(globalScope, "root is null!").to.not.equal(null);
74
75        expect(globalScope.getChildVariableScope().length, "should not have any children!").to.be.equal(0);
76        expect(globalScope.getParentVariableScope(), "should not have any children!").to.be.equal(null);
77        expect(globalScope.getBindingNode() === sourceFile, "functionblock.node should equal to sourceFile").to.be.true;
78    });
79
80    it("test CompilerDriver.scanFunctions-with-embedded-function", function () {
81        let source: string = `
82      {
83      function outer() {
84        function innerA() {
85        }
86        function innerB() {
87        }
88      }
89      }
90      {
91
92      }
93      var funcExpression = function() { }
94    `;
95        let sourceFile = creatAstFromSnippet(source);
96        let compilerDriver = new CompilerDriver('UnitTest', 'UnitTest');
97        let globalScope = new GlobalScope(sourceFile);
98        let recorder = new Recorder(sourceFile, globalScope, compilerDriver, false, false, true);
99        recorder.record();
100
101        let children = globalScope.getChildVariableScope();
102        let parent = globalScope.getParentVariableScope();
103        let bindingNode = globalScope.getBindingNode();
104        expect(globalScope != null, "root is null!");
105        expect(children.length, "should have 2 child!").to.be.equal(2);
106        expect(parent, "should not have any children!").to.be.equal(null);
107        expect(bindingNode, "functionblock.root should equal to sourceFile").to.be.deep.equal(sourceFile);
108        // check children
109        let son0 = children[0];
110        let grandchildren0 = son0.getChildVariableScope();
111        let parentOfSon0 = son0.getParentVariableScope();
112        let bindingNodeOfSon0 = <ts.Node>son0.getBindingNode();
113        expect(grandchildren0.length === 2, "son should have two children!").to.be.true;
114        expect(parentOfSon0, "son's parent should equal root!").deep.equal(globalScope);
115        expect(bindingNodeOfSon0.kind, "son's parent should equal root!").deep.equal(ts.SyntaxKind.FunctionDeclaration);
116        // check grandson
117        let grandson0 = grandchildren0[0];
118        let parentOfGrandson0 = grandson0.getParentVariableScope();
119        let grandgrandchiildren0 = grandson0.getChildVariableScope();
120        let bindingNodeOfGrandson0 = <ts.Node>grandson0.getBindingNode();
121        expect(parentOfGrandson0).to.be.equal(son0);
122        expect(grandgrandchiildren0.length).to.be.equal(0);
123        expect(bindingNodeOfGrandson0.kind, "grandson0's parent should equal son0!").deep.equal(ts.SyntaxKind.FunctionDeclaration);
124
125        let grandson1 = grandchildren0[1];
126        expect(grandson1.getParentVariableScope()).to.be.equal(son0);
127        expect(grandson1.getChildVariableScope().length).to.be.equal(0);
128        expect((<ts.Node>grandson1.getBindingNode()).kind, "grandson1's parent should equal son0!").deep.equal(ts.SyntaxKind.FunctionDeclaration);
129
130        let son1 = children[1];
131        let grandchildren1 = son1.getChildVariableScope();
132        let parentOfSon1 = son1.getParentVariableScope();
133        let bindingNodeOfSon1 = <ts.Node>son1.getBindingNode();
134        expect(grandchildren1.length === 0, "son1 should have two children!").to.be.true;
135        expect(parentOfSon1, "son1's parent should equal root!").deep.equal(globalScope);
136        expect(bindingNodeOfSon1.kind, "son1's parent should equal root!").deep.equal(ts.SyntaxKind.FunctionExpression);
137    });
138
139    it("test CompilerDriver.postorderanalysis-with-empty", function () {
140        let source: string = `
141    `;
142        let sourceFile = creatAstFromSnippet(source);
143        let compilerDriver = new CompilerDriver('UnitTest', 'UnitTest');
144        let globalScope = new GlobalScope(sourceFile);
145
146        let recorder = new Recorder(sourceFile, globalScope, compilerDriver, false, false, true);
147        recorder.record();
148        let postOrderVariableScopes = compilerDriver.postOrderAnalysis(globalScope);
149
150        expect(postOrderVariableScopes.length === 1, "postorder array length not correct");
151        expect(postOrderVariableScopes[0]).to.be.deep.equal(globalScope);
152    });
153
154    it("test CompilerDriver.postorderanalysis-with-embedded-function", function () {
155        let source: string = `
156      {
157      function outer() {
158        function innerA() {
159        }
160        function innerB() {
161        }
162      }
163      }
164      var funcExt = function() { }
165    `;
166        let sourceFile = creatAstFromSnippet(source);
167        let compilerDriver = new CompilerDriver('UnitTest', 'UnitTest');
168        let globalScope = new GlobalScope(sourceFile);
169
170        let recorder = new Recorder(sourceFile, globalScope, compilerDriver, false, false, true);
171        recorder.record();
172        let postOrderVariableScopes = compilerDriver.postOrderAnalysis(globalScope);
173
174        let children = globalScope.getChildVariableScope();
175        expect(postOrderVariableScopes.length === 5, "postorder array length not correct");
176        expect(postOrderVariableScopes[0]).to.be.deep.equal(children[0].getChildVariableScope()[0]);
177        expect(postOrderVariableScopes[1]).to.be.deep.equal(children[0].getChildVariableScope()[1]);
178        expect(postOrderVariableScopes[2]).to.be.deep.equal(children[0]);
179        expect(postOrderVariableScopes[3]).to.be.deep.equal(children[1]);
180        expect(postOrderVariableScopes[4]).to.be.deep.equal(globalScope);
181    });
182
183    /*
184     * the function inherit chart, total IIFE expression
185     *            +---------+
186     *            | global  |
187     *            +---.-----+
188     *              .`   `.
189     *            .`       `,
190     *          .`           ',
191     *  +-----`--+       +----'---+
192     *  |   1    |       |   2    |
193     *  +--------+       +----/---+
194     *                        |
195     *                        |
196     *                        |
197     *                    +----\---+
198     *                    |    3   |
199     *                    +--,.-,--+
200     *                    ,-`    `.
201     *                .'`         `.
202     *          +----'`-+        +---'--+
203     *          |   4   |        |  5   |
204     *          +-------+        +------+
205    */
206    it("test CompilerDriver.postorderanalysis-with-IIFE", function () {
207        let source: string = `
208    (function (global, factory) { // 1
209    } (this, (function () { 'use strict'; // 2
210      Array.from = (function() { // 3
211        var isCallable = function(fn) { //4
212        };
213
214        return function from(arrayLike) { //5
215        };
216      }());
217
218    })))
219    `;
220        let sourceFile = creatAstFromSnippet(source);
221        let compilerDriver = new CompilerDriver('UnitTest', 'UnitTest');
222        let globalScope = new GlobalScope(sourceFile);
223
224        let recorder = new Recorder(sourceFile, globalScope, compilerDriver, false, false, true);
225        recorder.record();
226        let postOrderVariableScopes = compilerDriver.postOrderAnalysis(globalScope);
227
228        let children = globalScope.getChildVariableScope();
229        let grandchildren1 = children[1].getChildVariableScope();
230        expect(postOrderVariableScopes.length === 6, "postorder array length not correct");
231        expect(postOrderVariableScopes[0]).to.be.deep.equal(children[0]);
232        expect(postOrderVariableScopes[1]).to.be.deep.equal(grandchildren1[0].getChildVariableScope()[0]);
233        expect(postOrderVariableScopes[2]).to.be.deep.equal(grandchildren1[0].getChildVariableScope()[1]);
234        expect(postOrderVariableScopes[3]).to.be.deep.equal(grandchildren1[0]);
235        expect(postOrderVariableScopes[4]).to.be.deep.equal(children[1]);
236        expect(postOrderVariableScopes[5]).to.be.deep.equal(globalScope);
237    });
238
239    it("test loadAccFromLexEnv with local variable", function () {
240        let globalScope = new GlobalScope();
241        let pandaGen = new PandaGen("lexVarPassPandaGen", creatAstFromSnippet(`class C {}; export {C}`), 1, globalScope);
242        let var1 = globalScope.add("var1", VarDeclarationKind.LET);
243        let funcObj = globalScope.add("4funcObj", VarDeclarationKind.LET);
244        funcObj!.bindVreg(new VReg());
245
246        let pass = new CacheExpander();
247        let varReg = pandaGen.getVregForVariable(var1!);
248
249        pandaGen.loadAccFromLexEnv(ts.createNode(0), globalScope, 0, var1!);
250        pass.run(pandaGen);
251
252        // load local register to acc
253        let outInsns = pandaGen.getInsns();
254        let expected = [
255            new Lda(varReg),
256        ];
257
258        expect(checkInstructions(outInsns, expected)).to.be.true;
259    });
260
261    it("test loadAccFromLexEnv with lex env variable", function () {
262        let globalScope = new GlobalScope();
263        let pandaGen = new PandaGen("lexVarPassPandaGen", creatAstFromSnippet(`class C {}; export {C}`), 1, globalScope);
264        let var1 = globalScope.add("var1", VarDeclarationKind.LET);
265        let funcObj = globalScope.add("4funcObj", VarDeclarationKind.LET);
266        funcObj!.bindVreg(new VReg());
267        let pass = new CacheExpander();
268
269        var1!.setLexVar(globalScope);
270        pandaGen.loadAccFromLexEnv(ts.createNode(0), globalScope, 0, var1!);
271        pass.run(pandaGen);
272
273        let outInsns = pandaGen.getInsns();
274        let expected = [
275            new Ldlexvar(new Imm(0), new Imm(0)),
276            new ThrowUndefinedifholewithname("var1"),
277        ];
278        expect(checkInstructions(outInsns, expected)).to.be.true;
279    });
280
281    it("test storeAccFromLexEnv with local variable", function () {
282        let globalScope = new GlobalScope();
283        let pandaGen = new PandaGen("lexVarPassPandaGen", creatAstFromSnippet(`class C {}; export {C}`), 1, globalScope);
284        let var1 = globalScope.add("var1", VarDeclarationKind.LET);
285        let pass = new CacheExpander();
286        let varReg = pandaGen.getVregForVariable(var1!);
287
288        pandaGen.storeAccToLexEnv(ts.createNode(0), globalScope, 0, var1!, true);
289        pass.run(pandaGen);
290
291        // load local register to acc
292        let outInsns = pandaGen.getInsns();
293        let expected = [
294            new Sta(varReg),
295        ];
296
297        expect(checkInstructions(outInsns, expected)).to.be.true;
298    });
299
300    it("test storeAccFromLexEnv with lex env let-variable", function () {
301        let globalScope = new GlobalScope();
302        let pandaGen = new PandaGen("lexVarPassPandaGen", creatAstFromSnippet(`class C {}; export {C}`), 1, globalScope);
303        let var1 = globalScope.add("var1", VarDeclarationKind.LET);
304        let funcObj = globalScope.add("4funcObj", VarDeclarationKind.LET);
305        funcObj!.bindVreg(new VReg());
306
307        let pass = new CacheExpander();
308
309        var1!.setLexVar(globalScope);
310        pandaGen.storeAccToLexEnv(ts.createNode(0), globalScope, 0, var1!, true);
311        pass.run(pandaGen);
312
313        let outInsns = pandaGen.getInsns();
314        let valueReg = new VReg();
315        let expected = [
316            new Sta(valueReg),
317            new Lda(valueReg),
318            new Stlexvar(new Imm(0), new Imm(0)),
319            new Lda(new VReg())
320        ];
321        expect(checkInstructions(outInsns, expected)).to.be.true;
322    });
323
324    it("test storeAccFromLexEnv with lex env const-variable", function () {
325        let globalScope = new GlobalScope();
326        let pandaGen = new PandaGen("lexVarPassPandaGen", creatAstFromSnippet(`class C {}; export {C}`), 1, globalScope);
327        let var1 = globalScope.add("var1", VarDeclarationKind.CONST);
328        let funcObj = globalScope.add("4funcObj", VarDeclarationKind.LET);
329        funcObj!.bindVreg(new VReg());
330        let pass = new CacheExpander();
331
332        var1!.setLexVar(globalScope);
333        pandaGen.storeAccToLexEnv(ts.createNode(0), globalScope, 0, var1!, true);
334        pass.run(pandaGen);
335
336        let outInsns = pandaGen.getInsns();
337        let valueReg = new VReg();
338        let expected = [
339            new Sta(valueReg),
340            new Lda(valueReg),
341            new Stlexvar(new Imm(0), new Imm(0)),
342            new Lda(valueReg)
343        ];
344        expect(checkInstructions(outInsns, expected)).to.be.true;
345    });
346
347    it("test lexenv variable capture in function", function () {
348        let source: string = `
349      var outer = 1;
350
351      function func() {
352        outer = 2;
353      }
354    `;
355
356        let pandaGens = compileAllSnippet(source);
357        IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`class C {}; export {C}`), 0, undefined);
358        let expectedMain = [
359            new Lda(new VReg()),
360            new Stglobalvar(new Imm(0), "outer"),
361            new Definefunc(new Imm(1), "UnitTest.func", new Imm(0)),
362            new Stglobalvar(new Imm(2), "func"),
363            new Ldai(new Imm(1)),
364            new Stglobalvar(new Imm(3), "outer"),
365            new Returnundefined()
366        ];
367        IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`class C {}; export {C}`), 0, undefined);
368        let expectedFunc = [
369            new Ldai(new Imm(2)),
370            new Stglobalvar(new Imm(0), "outer"),
371            new Returnundefined()
372        ];
373
374        pandaGens.forEach((pg) => {
375            if (pg.internalName === "UnitTest.func_main_0") {
376                expect(checkInstructions(pg.getInsns(), expectedMain)).to.be.true;
377            } else if (pg.internalName === "UnitTest.func") {
378                expect(checkInstructions(pg.getInsns(), expectedFunc)).to.be.true;
379            }
380        })
381    });
382
383    it("test lexenv let capture in function", function () {
384        let source: string = `
385      let outer = 1;
386
387      function func() {
388        outer = 2;
389      }
390    `;
391
392        let passes = [new CacheExpander()];
393        let pandaGens = compileAllSnippet(source, passes);
394        IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`class C {}; export {C}`), 0, undefined);
395        let expectedMain = [
396            new Definefunc(new Imm(0), "UnitTest.func", new Imm(0)),
397            new Stglobalvar(new Imm(1), "func"), // global.func = func_func_1
398            new Ldai(new Imm(1)), // value = 1
399            new Sttoglobalrecord(new Imm(2), "outer"),
400            new Returnundefined()
401        ];
402
403        IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`class C {}; export {C}`), 0, undefined);
404        let expectedFunc = [
405            new Ldai(new Imm(2)),
406            // ...insnsStoreLexVar_func,
407            new Trystglobalbyname(new Imm(0), "outer"),
408            new Returnundefined()
409        ];
410
411        pandaGens.forEach((pg) => {
412            let scope = <VariableScope>pg.getScope();
413            if (pg.internalName === "UnitTest.func_main_0") {
414                expect(checkInstructions(pg.getInsns(), expectedMain), "check main insns").to.be.true;
415                expect(scope.getNumLexEnv(), "main scope has 0 lexvar").to.be.equal(0);
416                // expect(scope.hasLexEnv(), "main scope has lexenv").to.be.true;
417            } else if (pg.internalName === "UnitTest.func") {
418
419                expect(checkInstructions(pg.getInsns(), expectedFunc), "check func insns").to.be.true;
420                expect(scope.getNumLexEnv(), "func scope has 1 lexvar").to.be.equal(0);
421                // expect(scope.hasLexEnv(), "func scope has lexenv").to.be.true;
422            }
423        });
424
425    });
426
427    it("test lexenv capture in function", function () {
428        let source: string = `
429            var a = 1;
430            function outer(a, b) {
431                return function () {
432                a++;
433                return a + b;
434                }
435            }
436            var fun = outer(a, 5);
437            a = 3;
438            func();
439        `;
440
441        IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`class C {}; export {C}`), 0, undefined);
442        let expectOuter: IRNode[] = [
443            new Newlexenv(new Imm(2)),
444            new Sta(new VReg()),
445            new Lda(new VReg()),
446            new Sta(new VReg()),
447            new Lda(new VReg()),
448            new Stlexvar(new Imm(0), new Imm(0)),
449            new Lda(new VReg()),
450            new Lda(new VReg()),
451            new Sta(new VReg()),
452            new Lda(new VReg()),
453            new Stlexvar(new Imm(0), new Imm(1)),
454            new Lda(new VReg()),
455            new Definefunc(new Imm(0), "UnitTest.#1#", new Imm(0)),
456            // returnStatement
457            new Sta(new VReg()),
458            new Lda(new VReg()),
459            new Return()
460        ];
461
462        IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`class C {}; export {C}`), 0, undefined);
463        let expectAnonymous = [
464            new Ldlexvar(new Imm(0), new Imm(0)),
465            new Sta(new VReg()),
466            new Lda(new VReg()),
467            new Inc(new Imm(0)),
468            new Sta(new VReg()),
469            new Lda(new VReg()),
470            new Stlexvar(new Imm(0), new Imm(0)),
471            new Lda(new VReg()),
472            new Lda(new VReg()),
473            new Tonumeric(new Imm(1)), // this is redundant load varialbe
474            new Ldlexvar(new Imm(0), new Imm(0)),
475            new Sta(new VReg),
476            new Ldlexvar(new Imm(0), new Imm(1)),
477            new Add2(new Imm(2), new VReg()),
478            // returnStatement
479            new Sta(new VReg()),
480            new Lda(new VReg()),
481            new Return()
482        ];
483
484        let passes = [new CacheExpander()];
485        let snippetCompiler = new SnippetCompiler();
486        snippetCompiler.compile(source, passes);
487
488        // check compile result!
489        let outerPg = snippetCompiler.getPandaGenByName("UnitTest.outer");
490        let outerScope = outerPg!.getScope();
491        let outerA = outerScope!.findLocal("a");
492        let outerB = outerScope!.findLocal("b");
493        expect(outerA instanceof LocalVariable, "a in outer is local variable").to.be.true;
494        expect(outerB instanceof LocalVariable, "b in outer is local variable").to.be.true;
495        expect((<FunctionScope>outerScope).getNumLexEnv(), "number of lexvar at outer scope").to.be.equal(2);
496        let anonymousPg = snippetCompiler.getPandaGenByName("UnitTest.#1#");
497        let anonymousScope = anonymousPg!.getScope();
498        let anonymousA = anonymousScope!.findLocal("a");
499        let searchRlt = anonymousScope!.find("a");
500        expect(searchRlt!.level).to.be.equal(0);
501        expect(searchRlt!.scope, "a is defined in outerscope").to.be.deep.equal(outerScope);
502        expect(anonymousA, "no a in anonymous function").to.be.undefined;
503        let globalPg = snippetCompiler.getPandaGenByName("UnitTest.func_main_0");
504        let globalScope = globalPg!.getScope();
505        let globalA = globalScope!.findLocal("a");
506        expect(globalA instanceof GlobalVariable, "globalA is GlobalVariable").to.be.true;
507
508        expect(checkInstructions(anonymousPg!.getInsns(), expectAnonymous), "check anonymous func ins").to.be.true;
509        expect(checkInstructions(outerPg!.getInsns(), expectOuter), "check outer func ins").to.be.true;
510    });
511});
512