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