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 * as ts from "typescript"; 17import { CompilerDriver } from "../../src/compilerDriver"; 18import { 19 Imm, 20 IRNode, 21 Label, 22 OperandType, 23 VReg 24} from "../../src/irnodes"; 25import * as jshelpers from "../../src/jshelpers"; 26import { PandaGen } from "../../src/pandagen"; 27import { Pass } from "../../src/pass"; 28import { 29 Scope 30} from "../../src/scope"; 31import { setGlobalStrict } from "../../src/strictMode"; 32import { creatAstFromSnippet } from "./asthelper"; 33import { LiteralBuffer } from "../../src/base/literal"; 34import { CmdOptions } from "../../src/cmdOptions"; 35 36const compileOptions = { 37 outDir: "../tmp/build", 38 allowJs: true, 39 noEmitOnError: true, 40 noImplicitAny: true, 41 target: ts.ScriptTarget.ES5, 42 module: ts.ModuleKind.CommonJS, 43 strictNullChecks: true 44}; 45 46function isVReg(node: OperandType): node is VReg { 47 return (node instanceof VReg); 48} 49 50function isImm(node: OperandType): node is Imm { 51 return (node instanceof Imm); 52} 53 54function isId(node: OperandType): node is string { 55 return (typeof node === "string"); 56} 57 58function isLabel(node: OperandType): node is Label { 59 return (node instanceof Label); 60} 61 62function isSameOperandType(left: OperandType, right: OperandType): boolean { 63 return (left.constructor === right.constructor); 64} 65 66function object2String(obj: any): string { 67 let retval = ""; 68 retval += obj.constructor.name + '\n'; 69 retval += JSON.stringify(obj) + '\n'; 70 return retval; 71} 72 73function basicOperandsEqual(left: OperandType, right: OperandType): boolean { 74 if (!isSameOperandType(left, right)) { 75 return false; 76 } 77 78 if (isVReg(left) && isVReg(right)) { 79 return true; 80 } 81 82 if (isImm(left) && isImm(right)) { 83 return left.value === right.value; 84 } 85 86 if (isLabel(left) && isLabel(right)) { 87 return true; 88 } 89 90 if (isId(left) && isId(right)) { 91 // string compare 92 return left == right; 93 } 94 95 let str = "operandsEqual: operands are not one of this types: VReg | Imm | Label | BuiltIns | string\n"; 96 str += object2String(left); 97 str += object2String(right); 98 99 throw new Error(str); 100} 101 102export function basicChecker(left: IRNode, right: IRNode): boolean { 103 if (left.kind !== right.kind) { 104 console.log("left.kind:" + left.kind + " right.kind:" + right.kind); 105 return false; 106 } 107 108 const operandCount = left.operands.length; 109 for (let i = 0; i < operandCount; ++i) { 110 const lop = left.operands[i]; 111 const rop = right.operands[i]; 112 if (!basicOperandsEqual(lop, rop)) { 113 console.log("left.operands:"); 114 console.log(left.operands[i]); 115 console.log("right.operands:"); 116 console.log(right.operands[i]); 117 return false; 118 } 119 } 120 121 return true; 122} 123 124export function checkInstructions(actual: IRNode[], expected: IRNode[], checkFn?: Function): boolean { 125 if (!checkFn) { 126 checkFn = basicChecker; 127 } 128 129 if (actual.length !== expected.length) { 130 console.log("actual.length:" + actual.length + " expected.length:" + expected.length) 131 return false; 132 } 133 134 for (let i = 0; i < actual.length; ++i) { 135 if (!checkFn(actual[i], expected[i])) { 136 return false; 137 } 138 } 139 140 return true; 141} 142 143export function compileAllSnippet(snippet: string, passes?: Pass[], literalBufferArray?: Array<LiteralBuffer>): PandaGen[] { 144 let sourceFile = creatAstFromSnippet(snippet); 145 jshelpers.bindSourceFile(sourceFile, {}); 146 CmdOptions.isWatchEvaluateExpressionMode() ? setGlobalStrict(true) 147 : setGlobalStrict(jshelpers.isEffectiveStrictModeSourceFile(sourceFile, compileOptions)); 148 let compilerDriver = new CompilerDriver('UnitTest'); 149 150 if (!passes) { 151 passes = []; 152 } 153 compilerDriver.setCustomPasses(passes); 154 compilerDriver.compileUnitTest(sourceFile, literalBufferArray); 155 return compilerDriver.getCompilationUnits(); 156} 157 158export function compileMainSnippet(snippet: string, pandaGen?: PandaGen, scope?: Scope, passes?: Pass[], compileFunc?: boolean): IRNode[] { 159 let compileUnits = compileAllSnippet(snippet, passes); 160 161 if (compileUnits.length != 1 && !compileFunc) { 162 throw new Error("Error: please use compileMainSnippet1 for multi function compile"); 163 } 164 165 // only return main function 166 if (compileFunc) { 167 compileUnits.filter((pg) => { 168 return (pg.internalName == "func_main_0"); 169 }) 170 } 171 172 return compileUnits[0].getInsns(); 173} 174 175export function compileAfterSnippet(snippet: string, name:string) { 176 let compileUnits = null; 177 ts.transpileModule( 178 snippet, 179 { 180 compilerOptions : { 181 "target": ts.ScriptTarget.ES2015 182 }, 183 fileName : name, 184 transformers : { 185 after : [ 186 (ctx: ts.TransformationContext) => { 187 return (sourceFile: ts.SourceFile) => { 188 jshelpers.bindSourceFile(sourceFile, {}); 189 setGlobalStrict(jshelpers.isEffectiveStrictModeSourceFile(sourceFile, compileOptions)); 190 let compilerDriver = new CompilerDriver('UnitTest'); 191 compilerDriver.setCustomPasses([]); 192 compilerDriver.compileUnitTest(sourceFile, []); 193 compileUnits = compilerDriver.getCompilationUnits(); 194 return sourceFile; 195 } 196 } 197 ] 198 } 199 } 200 ); 201 202 return compileUnits; 203} 204 205export function getCompileOptions(): ts.CompilerOptions { 206 return compileOptions; 207} 208 209export class SnippetCompiler { 210 pandaGens: PandaGen[] = []; 211 compile(snippet: string, passes?: Pass[], literalBufferArray?: Array<LiteralBuffer>) { 212 this.pandaGens = compileAllSnippet(snippet, passes, literalBufferArray); 213 return this.pandaGens; 214 } 215 216 compileAfter(snippet: string, name: string, passes?: Pass[], literalBufferArray?: Array<LiteralBuffer>) { 217 this.pandaGens = compileAfterSnippet(snippet, name); 218 return this.pandaGens; 219 } 220 221 getGlobalInsns(): IRNode[] { 222 let root = this.getPandaGenByName("func_main_0"); 223 if (root) { 224 return root.getInsns(); 225 } else { 226 return []; 227 } 228 } 229 230 getGlobalScope(): Scope | undefined { 231 let globalPandaGen = this.getPandaGenByName("func_main_0"); 232 233 return globalPandaGen ? globalPandaGen.getScope()!.getNearestVariableScope() : undefined; 234 } 235 236 getPandaGenByName(name: string): PandaGen | undefined { 237 let pgs = this.pandaGens.filter(pg => { 238 return (pg.internalName == name); 239 }) 240 return pgs[0]; 241 } 242}