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