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 Definefunc, 22 Istrue, 23 Returnundefined, 24 Stglobalvar, 25 ThrowUndefinedifhole, 26 Imm, 27 Jeqz, 28 Label, 29 Lda, 30 Ldai, 31 LdaStr, 32 Sta, 33 VReg, 34 IRNode 35} from "../src/irnodes"; 36import { checkInstructions, compileMainSnippet, SnippetCompiler } from "./utils/base"; 37import { creatAstFromSnippet } from "./utils/asthelper"; 38import { PandaGen } from '../src/pandagen'; 39 40describe("HoistTest", function () { 41 42 // case 1: hoist var declared variable ((declared in global scope)) in global scope 43 it('case 1;', function () { 44 let insns = compileMainSnippet("var a = 1;"); 45 IRNode.pg = new PandaGen("foo", creatAstFromSnippet("var a = 1;"), 0, undefined); 46 let expected = [ 47 new Lda(new VReg()), 48 new Stglobalvar(new Imm(0), "a"), 49 new Ldai(new Imm(1)), 50 new Stglobalvar(new Imm(1), "a"), 51 new Returnundefined() 52 ] 53 54 expect(checkInstructions(insns, expected)).to.be.true; 55 }); 56 57 // case 2: hoist var declared variable (declared in local scope) in global scope 58 it('case 2', function () { 59 let insns = compileMainSnippet(`if (true) { 60 var a = 2; 61 }`); 62 IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`if (true) { 63 var a = 2; 64 }`), 0, undefined); 65 let endLabel = new Label(); 66 67 let expected = [ 68 new Lda(new VReg()), 69 new Stglobalvar(new Imm(0), "a"), 70 new Lda(new VReg()), 71 new Istrue(), 72 new Jeqz(endLabel), 73 new Ldai(new Imm(2)), 74 new Stglobalvar(new Imm(1), "a"), 75 endLabel, 76 new Returnundefined() 77 ] 78 expect(checkInstructions(insns, expected)).to.be.true; 79 }); 80 81 // case 3: hoist function declaration in global scope 82 it('case 3', function () { 83 let snippetCompiler = new SnippetCompiler(); 84 snippetCompiler.compile(`function a() {};`); 85 IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`function a() {};`), 0, undefined); 86 87 let insns = snippetCompiler.getGlobalInsns(); 88 let expected = [ 89 new Definefunc(new Imm(0), "UnitTest.a", new Imm(0)), 90 new Stglobalvar(new Imm(1), "a"), 91 new Returnundefined() 92 ] 93 expect(checkInstructions(insns, expected)).to.be.true; 94 }); 95 96 // case 4: In case that two function declared directly in global scope with the same name, hoist the later one. 97 it('case 4', function () { 98 let snippetCompiler = new SnippetCompiler(); 99 snippetCompiler.compile(`function a() {}; function a() {}`); 100 IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`function a() {}; function a() {}`), 0, undefined); 101 102 let insns = snippetCompiler.getGlobalInsns(); 103 let expected = [ 104 new Definefunc(new Imm(0), "UnitTest.#2#a", new Imm(0)), 105 new Stglobalvar(new Imm(1), "a"), 106 new Returnundefined() 107 ] 108 109 expect(checkInstructions(insns, expected)).to.be.true; 110 }); 111 112 // case 5: hoisting of function declaration is of higher priority than var declared variables with a same name in global scope 113 it('case 5', function () { 114 let snippetCompiler = new SnippetCompiler(); 115 snippetCompiler.compile(`var a = 1; function a() {}`); 116 IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`var a = 1; function a() {}`), 0, undefined); 117 118 let insns = snippetCompiler.getGlobalInsns(); 119 let expected = [ 120 new Definefunc(new Imm(0), "UnitTest.a", new Imm(0)), 121 new Stglobalvar(new Imm(1), "a"), 122 new Ldai(new Imm(1)), 123 new Stglobalvar(new Imm(2), "a"), 124 new Returnundefined() 125 ] 126 127 expect(checkInstructions(insns, expected)).to.be.true; 128 }); 129 130 // case 6: hoist var declared variable in function scope 131 it('case 6', function () { 132 let snippetCompiler = new SnippetCompiler(); 133 snippetCompiler.compile(`function a() {var a = 1;}`); 134 IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`function a() {var a = 1;}`), 0, undefined); 135 let funcPg = snippetCompiler.getPandaGenByName("UnitTest.a"); 136 let insns = funcPg!.getInsns(); 137 138 let a = new VReg(); 139 let expected = [ 140 new Lda(a), 141 new Sta(new VReg()), 142 new Ldai(new Imm(1)), 143 new Sta(a), 144 145 new Returnundefined() 146 ] 147 expect(checkInstructions(insns!, expected)).to.be.true; 148 }); 149 150 // case 7: hoist function declaration in function scope 151 it('case 7', function () { 152 let snippetCompiler = new SnippetCompiler(); 153 snippetCompiler.compile(`function a() {function b() {}};`); 154 IRNode.pg = new PandaGen("foo", creatAstFromSnippet(`function a() {function b() {}};`), 0, undefined); 155 let funcPg = snippetCompiler.getPandaGenByName("UnitTest.a"); 156 let insns = funcPg!.getInsns(); 157 let a = new VReg(); 158 let expected = [ 159 new Definefunc(new Imm(0), "UnitTest.b", new Imm(0)), 160 new Sta(a), 161 162 new Returnundefined() 163 ] 164 165 expect(checkInstructions(insns!, expected)).to.be.true; 166 }); 167 168 // case 8: temporary dead zone of let in global scope 169 it('case 8', function () { 170 let snippetCompiler = new SnippetCompiler(); 171 snippetCompiler.compile(`a = 1; 172 let a;`); 173 let funcPg = snippetCompiler.getPandaGenByName("UnitTest.func_main_0"); 174 let insns = funcPg!.getInsns(); 175 let idReg = new VReg(); 176 let expected = [ 177 new LdaStr("a"), 178 new Sta(idReg), 179 new ThrowUndefinedifhole(new VReg(), idReg) 180 ] 181 182 expect(checkInstructions(insns.slice(3, 5), expected)); 183 }); 184 185 // case 9: temporary dead zone of let in function scope 186 it('case 9', function () { 187 let snippetCompiler = new SnippetCompiler(); 188 snippetCompiler.compile(`function b() { 189 a = 1; 190 let a; 191 }`); 192 let funcPg = snippetCompiler.getPandaGenByName("UnitTest.b"); 193 let insns = funcPg!.getInsns(); 194 let idReg = new VReg(); 195 196 let expected = [ 197 new LdaStr("a"), 198 new Sta(idReg), 199 new ThrowUndefinedifhole(new VReg(), idReg) 200 ] 201 202 expect(checkInstructions(insns.slice(3, 5), expected)); 203 }); 204 205 // case 10: temporary dead zone of let in local scope 206 it('case 10', function () { 207 let snippetCompiler = new SnippetCompiler(); 208 snippetCompiler.compile(`{ 209 a = 1; 210 let a; 211 }`); 212 let funcPg = snippetCompiler.getPandaGenByName("UnitTest.func_main_0"); 213 let insns = funcPg!.getInsns(); 214 let idReg = new VReg(); 215 216 let expected = [ 217 new LdaStr("a"), 218 new Sta(idReg), 219 new ThrowUndefinedifhole(new VReg(), idReg) 220 ] 221 222 expect(checkInstructions(insns.slice(3, 5), expected)); 223 }) 224}) 225