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 * as path from "path"; 17import { extractCtorOfClass } from "../statement/classStatement"; 18import * as ts from "typescript"; 19import { 20 Callrange, 21 Callthisrange, 22 Createobjectwithexcludedkeys, 23 Newobjrange, 24 IRNode, 25 WideCallthisrange, 26 WideCallrange, 27 WideCreateobjectwithexcludedkeys, 28 Supercallthisrange, 29 WideSupercallthisrange, 30 Supercallarrowrange, 31 WideSupercallarrowrange, 32 WideNewobjrange 33} from "../irnodes"; 34import * as jshelpers from "../jshelpers"; 35import { LOGD } from "../log"; 36import { isFunctionLikeDeclaration } from "../syntaxCheckHelper"; 37import { CmdOptions } from "../cmdOptions"; 38import { CompilerDriver } from "../compilerDriver"; 39 40export function containSpreadElement(args?: ts.NodeArray<ts.Expression>): boolean { 41 if (!args) { 42 return false; 43 } 44 45 for (let i = 0; i < args.length; i++) { 46 if (args[i].kind === ts.SyntaxKind.SpreadElement) { 47 return true; 48 } 49 } 50 51 return false; 52} 53 54export function hasExportKeywordModifier(node: ts.Node): boolean { 55 let hasExport: boolean = false; 56 if (node.modifiers) { 57 node.modifiers.forEach((mod) => { 58 if (mod.kind === ts.SyntaxKind.ExportKeyword) { 59 hasExport = true; 60 } 61 }); 62 } 63 64 return hasExport; 65} 66 67export function hasDefaultKeywordModifier(node: ts.Node): boolean { 68 let hasDefault: boolean = false; 69 if (node.modifiers) { 70 node.modifiers.forEach((mod) => { 71 if (mod.kind === ts.SyntaxKind.DefaultKeyword) { 72 hasDefault = true; 73 } 74 }); 75 } 76 77 return hasDefault; 78} 79 80export function execute(cmd: string, args: Array<string>): number { 81 let spawn = require('child_process').spawn; 82 83 let child = spawn(cmd, [...args], { 84 stdio: ['pipe', 'inherit', 'inherit'] 85 }); 86 87 child.on('exit', (code: any) => { 88 if (code === 1) { 89 LOGD("fail to execute cmd: ", cmd); 90 return 0; 91 } 92 LOGD("execute cmd successfully: ", cmd); 93 return 1; 94 }); 95 96 return 1; 97} 98 99export function addUnicodeEscape(text: string): string { 100 let firstIdx = 0; 101 let secondIdx = 0; 102 let len = text.length; 103 let newText = ""; 104 while (secondIdx != len) { 105 if (text[secondIdx] === '\\' && secondIdx + 1 != len && text[secondIdx + 1] === 'u') { 106 if (secondIdx != 0 && text[secondIdx - 1] === '\\') { 107 newText += text.substr(firstIdx, secondIdx - firstIdx) + "\\\\" + "\\u"; 108 } else { 109 newText += text.substr(firstIdx, secondIdx - firstIdx) + "\\" + "\\u"; 110 } 111 secondIdx += 2; 112 firstIdx = secondIdx; 113 } else { 114 secondIdx++; 115 } 116 } 117 118 if (secondIdx === len && firstIdx != secondIdx) { 119 newText += text.substr(firstIdx); 120 } 121 122 return newText; 123} 124 125export function isBindingPattern(node: ts.Node): boolean { 126 return ts.isArrayBindingPattern(node) || ts.isObjectBindingPattern(node); 127} 128 129export function isObjectBindingOrAssignmentPattern(node: ts.Node): boolean { 130 return ts.isObjectLiteralExpression(node) || ts.isObjectBindingPattern(node); 131} 132 133export function isArrayBindingOrAssignmentPattern(node: ts.Node): boolean { 134 return ts.isArrayLiteralExpression(node) || ts.isArrayBindingPattern(node); 135} 136 137export function isBindingOrAssignmentPattern(node: ts.Node): boolean { 138 return isArrayBindingOrAssignmentPattern(node) || isObjectBindingOrAssignmentPattern(node); 139} 140 141export function isMemberExpression(node: ts.Node): boolean { 142 if (ts.isPropertyAccessExpression(node) || 143 ts.isElementAccessExpression(node)) { 144 return true; 145 } 146 147 return false; 148} 149 150export function isUndefinedIdentifier(node: ts.Node): boolean { 151 if (!ts.isIdentifier(node)) { 152 return false; 153 } 154 155 if (jshelpers.getTextOfIdentifierOrLiteral(node) != "undefined") { 156 return false; 157 } 158 159 return true; 160} 161 162export function isAnonymousFunctionDefinition(node: ts.Node): boolean { 163 if (!isFunctionLikeDeclaration(node)) { 164 return false; 165 } 166 167 if (node.name) { 168 return false; 169 } else { 170 return true; 171 } 172} 173 174export function escapeUnicode(data: string): string { 175 let char = '\n'; 176 let i = 0; 177 let j = 0; 178 let newData = ""; 179 while ((j = data.indexOf(char, i)) !== -1) { 180 let tmp = data.substring(i, j); 181 if (tmp.indexOf("\\u") != -1) { 182 tmp = addUnicodeEscape(tmp); 183 } 184 newData = newData.concat(tmp, "\n"); 185 i = j + 1; 186 } 187 188 newData = newData.concat("}\n"); 189 return newData; 190} 191 192export function initiateTs2abc(args: Array<string>): any { 193 let js2abc = path.join(path.resolve(__dirname, '../bin'), "js2abc"); 194 args.unshift("--compile-by-pipe"); 195 // @ts-ignore 196 let spawn = require('child_process').spawn; 197 let child = spawn(js2abc, [...args], { 198 stdio: ['pipe', 'inherit', 'inherit', 'pipe'] 199 }); 200 201 return child; 202} 203 204export function terminateWritePipe(ts2abc: any): void { 205 if (!ts2abc) { 206 LOGD("ts2abc is not a valid object"); 207 } 208 209 ts2abc.stdio[3].end(); 210} 211 212export function listenChildExit(child: any): void { 213 if (!child) { 214 LOGD("child is not a valid object"); 215 } 216 217 child.on('exit', (code: any) => { 218 if (code === 1) { 219 LOGD("fail to generate panda binary file"); 220 } 221 LOGD("success to generate panda binary file"); 222 }); 223} 224 225export function listenErrorEvent(child: any): void { 226 if (!child) { 227 LOGD("child is not a valid object"); 228 } 229 230 child.on('error', (err: any) => { 231 LOGD(err.toString()); 232 }); 233} 234 235export function isRangeInst(ins: IRNode): boolean { 236 if (ins instanceof Callthisrange || 237 ins instanceof WideCallthisrange || 238 ins instanceof Callrange || 239 ins instanceof WideCallrange || 240 ins instanceof Newobjrange || 241 ins instanceof WideNewobjrange || 242 ins instanceof Createobjectwithexcludedkeys || 243 ins instanceof WideCreateobjectwithexcludedkeys || 244 ins instanceof Supercallthisrange || 245 ins instanceof WideSupercallthisrange || 246 ins instanceof Supercallarrowrange || 247 ins instanceof WideSupercallarrowrange) { 248 return true; 249 } 250 return false; 251} 252 253export function getRangeStartVregPos(ins: IRNode): number { 254 if (!isRangeInst(ins)) { 255 return -1; 256 } 257 258 if (ins instanceof Callthisrange || 259 ins instanceof Callrange || 260 ins instanceof Newobjrange || 261 ins instanceof Supercallthisrange || 262 ins instanceof Supercallarrowrange || 263 ins instanceof Createobjectwithexcludedkeys || 264 ins instanceof WideCreateobjectwithexcludedkeys) { 265 return 2; 266 } 267 268 if (ins instanceof WideCallthisrange || 269 ins instanceof WideCallrange || 270 ins instanceof WideNewobjrange || 271 ins instanceof WideSupercallthisrange || 272 ins instanceof WideSupercallarrowrange) { 273 return 1; 274 } 275} 276 277export function getRangeExplicitVregNums(ins: IRNode): number { 278 if (!isRangeInst(ins)) { 279 return -1; 280 } 281 return ins instanceof Createobjectwithexcludedkeys ? 2 : 1; 282} 283 284export function isRestParameter(parameter: ts.ParameterDeclaration): boolean { 285 return parameter.dotDotDotToken ? true : false; 286} 287 288export function getParamLengthOfFunc(node: ts.FunctionLikeDeclaration): number { 289 let length = 0; 290 let validLengthRange = true; 291 let parameters = node.parameters; 292 if (parameters) { 293 parameters.forEach(parameter => { 294 if (parameter.initializer || isRestParameter(parameter)) { 295 validLengthRange = false; 296 } 297 298 if (validLengthRange) { 299 length++; 300 } 301 }) 302 } 303 304 return length; 305} 306 307export function getParameterLength4Ctor(node: ts.ClassLikeDeclaration): number { 308 if (!extractCtorOfClass(node)) { 309 return 0; 310 } 311 312 let members = node.members; 313 let ctorNode: ts.ConstructorDeclaration; 314 for (let index = 0; index < members.length; index++) { 315 let member = members[index]; 316 if (ts.isConstructorDeclaration(member)) { 317 ctorNode = member; 318 } 319 } 320 321 return getParamLengthOfFunc(ctorNode!); 322} 323 324export function setPos(node: ts.Node): ts.Node { 325 ts.setTextRange(node, {pos:-1, end:-1}); 326 node.forEachChild(childNode => { 327 setPos(childNode); 328 }); 329 return node; 330} 331 332export function getRecordTypeFlag(recordType: boolean): boolean { 333 return recordType && CmdOptions.needRecordType() && CompilerDriver.isTsFile; 334} 335 336export function isBase64Str(input: string): boolean { 337 if (input === '' || input.trim() === '') { 338 return false; 339 } 340 return Buffer.from(Buffer.from(input, 'base64').toString()).toString('base64') === input; 341} 342 343export function transformCommonjsModule(sourceFile: ts.SourceFile): ts.SourceFile { 344 /* 345 * Transform the commonjs module's AST by wrap the sourceCode & use Reflect.apply to invoke this wrapper with [this] 346 * pointing to [exports] object 347 * 348 * Reflect.apply(function (exports, require, module, __filename, __dirname) { 349 * [SourceCode] 350 * }, exports, [exports, require, module, __filename, __dirname]); 351 */ 352 let newStatements = [ts.factory.createExpressionStatement(ts.factory.createCallExpression( 353 ts.factory.createPropertyAccessExpression( 354 ts.factory.createIdentifier("Reflect"), ts.factory.createIdentifier("apply") 355 ), 356 undefined, 357 [ 358 ts.factory.createFunctionExpression( 359 undefined, undefined, undefined, undefined, 360 [ 361 ts.factory.createParameterDeclaration(undefined, undefined, undefined, ts.factory.createIdentifier("exports"), undefined, undefined, undefined), 362 ts.factory.createParameterDeclaration(undefined, undefined, undefined, ts.factory.createIdentifier("require"), undefined, undefined, undefined), 363 ts.factory.createParameterDeclaration(undefined, undefined, undefined, ts.factory.createIdentifier("module"), undefined, undefined, undefined), 364 ts.factory.createParameterDeclaration(undefined, undefined, undefined, ts.factory.createIdentifier("__filename"), undefined, undefined, undefined), 365 ts.factory.createParameterDeclaration(undefined, undefined, undefined, ts.factory.createIdentifier("__dirname"), undefined, undefined, undefined) 366 ], 367 undefined, 368 ts.factory.createBlock(sourceFile.statements) 369 ), 370 ts.factory.createIdentifier("exports"), 371 ts.factory.createArrayLiteralExpression( 372 [ 373 ts.factory.createIdentifier("exports"), 374 ts.factory.createIdentifier("require"), 375 ts.factory.createIdentifier("module"), 376 ts.factory.createIdentifier("__filename"), 377 ts.factory.createIdentifier("__dirname") 378 ] 379 ) 380 ] 381 ))]; 382 383 return ts.factory.updateSourceFile(sourceFile, newStatements); 384} 385 386export function hasAbstractModifier(node: ts.Node): boolean { 387 if (!node.modifiers) { 388 return false; 389 } 390 for (let modifier of node.modifiers) { 391 switch (modifier.kind) { 392 case ts.SyntaxKind.AbstractKeyword: { 393 return true; 394 } 395 default: 396 break; 397 } 398 } 399 return false; 400} 401 402export const MAX_INT8 = 127; 403export const MAX_INT16 = 32767; 404 405export function getOutputBinName(node: ts.SourceFile): string { 406 let outputBinName = CmdOptions.getOutputBinName(); 407 let fileName = node.fileName.substring(0, node.fileName.lastIndexOf('.')); 408 let inputFileName = CmdOptions.getInputFileName(); 409 if (/^win/.test(require('os').platform())) { 410 let inputFileTmps = inputFileName.split(path.sep); 411 inputFileName = path.posix.join(...inputFileTmps); 412 } 413 414 if (fileName != inputFileName) { 415 outputBinName = fileName + ".abc"; 416 } 417 return outputBinName; 418} 419 420export function getRecordName(node: ts.SourceFile): string { 421 let recordName = CmdOptions.getRecordName(); 422 423 if (recordName === "" && CmdOptions.isMergeAbc()) { 424 let outputBinName = getOutputBinName(node); 425 recordName = path.basename(outputBinName, path.extname(outputBinName)); 426 } 427 428 return recordName; 429} 430 431export function getLiteralKey(node: ts.SourceFile, idx:number): string { 432 return `${getRecordName(node)}_${idx}`; 433} 434 435/** 436 * Gets the node from which a name should be generated, from tsc logic 437 */ 438function getNodeForGeneratedName( 439 // @ts-ignore 440 name: ts.GeneratedIdentifier): ts.Node { 441 const autoGenerateId = name.autoGenerateId; 442 let node = name as ts.Node; 443 // @ts-ignore 444 let original = node.original; 445 while (original) { 446 node = original; 447 448 // if "node" is a different generated name (having a different "autoGenerateId"), use it and stop traversing. 449 if (ts.isIdentifier(node) && 450 // @ts-ignore 451 !!(node.autoGenerateFlags! & ts.GeneratedIdentifierFlags.Node) && 452 // @ts-ignore 453 node.autoGenerateId !== autoGenerateId) { 454 break; 455 } 456 // @ts-ignore 457 original = node.original; 458 } 459 460 // otherwise, return the original node for the source; 461 return node; 462} 463 464let uniqueNameIndex = 0; 465let nodeIdNameMap = new Map<number, string>(); 466let tempAndLoopVariablesNameMap = new Map<number, string>(); 467 468function generateUniqueName(): string { 469 if (uniqueNameIndex < 26) { // #a ~ #z 470 return "#" + String.fromCharCode(97 /* a */ + uniqueNameIndex++); 471 } 472 return "#" + (uniqueNameIndex++ - 26); 473} 474 475function generateNameCached(node: ts.Node): string { 476 // @ts-ignore 477 const nodeId = ts.getNodeId(node); 478 if (nodeIdNameMap.get(nodeId)) { 479 return nodeIdNameMap.get(nodeId); 480 } 481 let generatedName = generateUniqueName(); 482 nodeIdNameMap.set(nodeId, generatedName); 483 return generatedName; 484} 485 486function generateNameForTempAndLoopVariable(node: ts.Node): string { 487 // Auto, Loop and Unique names are generated and cached based on their unique autoGenerateId. 488 // @ts-ignore 489 const autoGenerateId = node.autoGenerateId!; 490 if (tempAndLoopVariablesNameMap.get(autoGenerateId)) { 491 return tempAndLoopVariablesNameMap.get(autoGenerateId); 492 } 493 let generatedName = generateUniqueName(); 494 // @ts-ignore 495 if ((node.autoGenerateFlags & ts.GeneratedIdentifierFlags.KindMask) === ts.GeneratedIdentifierFlags.Unique) { 496 // Unique names are generated and cached based on their unique autoGenerateId and original idText. 497 // @ts-ignore 498 generatedName = (<ts.Identifier>node).escapedText + generatedName; 499 } 500 tempAndLoopVariablesNameMap.set(autoGenerateId, generatedName); 501 return generatedName; 502} 503 504export function resetUniqueNameIndex(): void { 505 uniqueNameIndex = 0; 506} 507 508export function makeNameForGeneratedNode(node: ts.Node): void { 509 node.forEachChild(childNode => { 510 switch (childNode.kind) { 511 case ts.SyntaxKind.Identifier: { 512 // @ts-ignore 513 if (ts.isGeneratedIdentifier(childNode)) { 514 // Node names generate unique names based on their original node and are cached based on that node's id. 515 // @ts-ignore 516 if ((childNode.autoGenerateFlags & ts.GeneratedIdentifierFlags.KindMask) === 517 // @ts-ignore 518 ts.GeneratedIdentifierFlags.Node) { 519 let originalNode = getNodeForGeneratedName(childNode); 520 // @ts-ignore 521 (<ts.Identifier>childNode).escapedText = generateNameCached(originalNode); 522 } else { 523 // @ts-ignore 524 (<ts.Identifier>childNode).escapedText = generateNameForTempAndLoopVariable(childNode); 525 } 526 } 527 break; 528 } 529 } 530 makeNameForGeneratedNode(childNode); 531 }); 532} 533