• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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