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