• 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
16// singleton to parse commandLine infos
17import commandLineArgs from "command-line-args";
18import commandLineUsage from "command-line-usage";
19import * as ts from "typescript";
20import { LOGE } from "./log";
21import * as path from "path";
22import { execute } from "./base/util";
23
24export const ts2pandaOptions = [
25    { name: 'commonjs', alias: 'c', type: Boolean, defaultValue: false, description: "compile as commonJs module." },
26    { name: 'modules', alias: 'm', type: Boolean, defaultValue: false, description: "compile as module." },
27    { name: 'debug-log', alias: 'l', type: Boolean, defaultValue: false, description: "show info debug log and generate the json file." },
28    { name: 'dump-assembly', alias: 'a', type: Boolean, defaultValue: false, description: "dump assembly to file." },
29    { name: 'debug', alias: 'd', type: Boolean, defaultValue: false, description: "compile with debug info." },
30    { name: 'debug-add-watch', alias: 'w', type: String, lazyMultiple: true, defaultValue: [], description: "watch expression, abc file path and maybe watchTimeOut(in seconds) in debug mode." },
31    { name: 'keep-persistent-watch', alias: 'k', type: String, lazyMultiple: true, defaultValue: [], description: "keep persistent watch on js file with watched expression." },
32    { name: 'show-statistics', alias: 's', type: String, lazyMultiple: true, defaultValue: "", description: "show compile statistics(ast, histogram, hoisting, all)." },
33    { name: 'output', alias: 'o', type: String, defaultValue: "", description: "set output file." },
34    { name: 'timeout', alias: 't', type: Number, defaultValue: 0, description: "js to abc timeout threshold(unit: seconds)." },
35    { name: 'opt-log-level', type: String, defaultValue: "error", description: "specifie optimizer log level. Possible values: ['debug', 'info', 'error', 'fatal']" },
36    {
37        name: 'opt-level', type: Number, defaultValue: 2, description: "Optimization level. Possible values: [0, 1, 2]. Default: 0\n    0: no optimizations\n    \
38                                                                    1: basic bytecode optimizations, including valueNumber, lowering, constantResolver, regAccAllocator\n    \
39                                                                    2: other bytecode optimizations, unimplemented yet"},
40    { name: 'help', alias: 'h', type: Boolean, description: "Show usage guide." },
41    { name: 'bc-version', alias: 'v', type: Boolean, defaultValue: false, description: "Print ark bytecode version" },
42    { name: 'bc-min-version', type: Boolean, defaultValue: false, description: "Print ark bytecode minimum supported version" },
43    { name: 'included-files', alias: 'i', type: String, lazyMultiple: true, defaultValue: [], description: "The list of dependent files." },
44    { name: 'record-type', alias: 'p', type: Boolean, defaultValue: false, description: "Record type info. Default: true" },
45    { name: 'dts-type-record', alias: 'q', type: Boolean, defaultValue: false, description: "Record type info for .d.ts files. Default: false" },
46    { name: 'dts-builtin-type-record', alias: 'b', type: Boolean, defaultValue: false, description: "Recognize builtin types for .d.ts files. Default: false" },
47    { name: 'debug-type', alias: 'g', type: Boolean, defaultValue: false, description: "Print type-related log. Default: false" },
48    { name: 'output-type', type: Boolean, defaultValue: false, description: "set output type."},
49    { name: 'display-typeinfo', type: Boolean, defaultValue: false, description: "Display typeinfo of pairs of instruction orders and types when enable-typeinfo is true" },
50    { name: 'function-sourcecode', type: Boolean, defaultValue: false, description: "Record functions' sourceCode to support the feature of [function].toString()" },
51    { name: 'expression-watch-toolchain', type: String, defaultValue: "es2panda", description: "Specify the tool chain used to transform the expression" },
52    { name: 'source-file', type: String, defaultValue: "", description: "specify the file path info recorded in generated abc" },
53    { name: 'generate-tmp-file', type: Boolean, defaultValue: false, description: "whether to generate intermediate temporary files"},
54    { name: 'record-name', type: String, defaultValue: "", description: "specify the record name, this option can only be used when [merge-abc] is enabled." },
55    { name: 'package-name', type: String, defaultValue: "", description: "specify the package that the compiling file belongs to." },
56    { name: 'output-proto', type: Boolean, defaultValue: false, description: "Output protoBin file. Default: false" },
57    { name: 'merge-abc', type: Boolean, defaultValue: false, description: "Compile as merge abc" },
58    { name: 'input-file', type: String, defaultValue: "", description: "A file containing a list of source files to be compiled. Each line of this file should be constructed in such format: fileName;recordName;moduleType;sourceFile;packageName" },
59    { name: 'oh-modules', type: Boolean, defaultValue: false, description: "Set oh-modules as typescript compiler's package manager type. Default: false" }
60]
61
62
63
64export class CmdOptions {
65    private static parsedResult: ts.ParsedCommandLine;
66    private static options: commandLineArgs.CommandLineOptions;
67
68    static getDisplayTypeinfo(): boolean {
69        if (!this.options) {
70            return false;
71        }
72        return this.options["display-typeinfo"];
73    }
74
75    static isEnableDebugLog(): boolean {
76        if (!this.options) {
77            return false;
78        }
79        return this.options["debug-log"];
80    }
81
82    static isAssemblyMode(): boolean {
83        if (!this.options) {
84            return false;
85        }
86        return this.options["dump-assembly"];
87    }
88
89    static isDebugMode(): boolean {
90        if (!this.options) {
91            return false;
92        }
93        return this.options["debug"];
94    }
95
96    static setWatchEvaluateExpressionArgs(watchArgs: string[]): void {
97        this.options["debug-add-watch"] = watchArgs;
98    }
99
100    static getDeamonModeArgs(): string[] {
101        if (!this.options) {
102            return [];
103        }
104        return this.options["keep-persistent-watch"];
105    }
106
107    static isWatchEvaluateDeamonMode(): boolean {
108        return CmdOptions.getDeamonModeArgs()[0] === "start";
109    }
110
111    static isStopEvaluateDeamonMode(): boolean {
112        return CmdOptions.getDeamonModeArgs()[0] === "stop";
113    }
114
115    static getEvaluateDeamonPath(): string {
116        return CmdOptions.getDeamonModeArgs()[1];
117    }
118
119    static isWatchEvaluateExpressionMode(): boolean {
120        if (!this.options) {
121            return false;
122        }
123        return this.options["debug-add-watch"].length != 0;
124    }
125
126    static getEvaluateExpression(): string {
127        return this.options["debug-add-watch"][0];
128    }
129
130    static getWatchJsPath(): string {
131        return this.options["debug-add-watch"][1];
132    }
133
134    static getWatchTimeOutValue(): number {
135        if (this.options["debug-add-watch"].length === 2) {
136            return 0;
137        }
138        return this.options["debug-add-watch"][2];
139    }
140
141    static watchViaEs2pandaToolchain(): boolean {
142        if (!this.options) {
143            return false;
144        }
145        if (this.options["expression-watch-toolchain"] && this.options["expression-watch-toolchain"] != "es2panda") {
146            return false;
147        }
148        return true;
149    }
150
151    static isCompileFilesList(): boolean {
152        if (!this.options) {
153            return false;
154        }
155        return this.options["input-file"].length != 0;
156    }
157
158    static getCompileFilesList(): string {
159        return this.options["input-file"];
160    }
161
162    static isCommonJs(): boolean {
163        if (!this.options) {
164            return false;
165        }
166
167        if (this.options["commonjs"] && this.options["modules"]) {
168            throw new Error("Can not compile with [-c] and [-m] options at the same time");
169        }
170
171        return this.options["commonjs"];
172    }
173
174    static isModules(): boolean {
175        if (!this.options) {
176            return false;
177        }
178
179        if (this.options["modules"] && this.options["commonjs"]) {
180            throw new Error("Can not compile with [-m] and [-c] options at the same time");
181        }
182
183        return this.options["modules"];
184    }
185
186    static getOptLevel(): number {
187        return this.options["opt-level"];
188    }
189
190    static getOptLogLevel(): string {
191        return this.options["opt-log-level"];
192    }
193
194    static showASTStatistics(): boolean {
195        if (!this.options) {
196            return false;
197        }
198        return this.options["show-statistics"].includes("ast") || this.options["show-statistics"].includes("all");
199    }
200
201    static showHistogramStatistics(): boolean {
202        if (!this.options) {
203            return false;
204        }
205        return this.options["show-statistics"].includes("all") || this.options["show-statistics"].includes("histogram");
206    }
207
208    static showHoistingStatistics(): boolean {
209        if (!this.options) {
210            return false;
211        }
212        return this.options["show-statistics"].includes("all") || this.options["show-statistics"].includes("hoisting");
213    }
214
215    static getInputFileName(): string {
216        let path = this.parsedResult.fileNames[0];
217        let inputFile = path.substring(0, path.lastIndexOf('.'));
218        return inputFile;
219    }
220
221    static getOutputBinName(): string {
222        let outputFile = this.options.output;
223        if (outputFile === "") {
224            outputFile = CmdOptions.getInputFileName() + ".abc";
225        }
226        return outputFile;
227    }
228
229    static setMergeAbc(mergeAbcMode: Boolean): void {
230        if (!this.options) {
231            return;
232        }
233        this.options["merge-abc"] = mergeAbcMode;
234    }
235
236    static getRecordName(): string {
237        if (!this.options || !this.options["merge-abc"]) {
238            return "";
239        }
240
241        return this.options["record-name"];
242    }
243
244    static getTimeOut(): Number {
245        if (!this.options) {
246            return 0;
247        }
248        return this.options["timeout"];
249    }
250
251    static isOutputType(): false {
252        if (!this.options) {
253            return false;
254        }
255        return this.options["output-type"];
256    }
257
258    static showHelp(): void {
259        const usage = commandLineUsage([
260            {
261                header: "Ark JavaScript Compiler",
262                content: 'node --expose-gc index.js [options] file.js'
263            },
264            {
265                header: 'Options',
266                optionList: ts2pandaOptions
267            },
268            {
269                content: 'Project Ark'
270            }
271        ])
272        LOGE(usage);
273    }
274
275    static isBcVersion(): boolean {
276        if (!this.options) {
277            return false;
278        }
279        return this.options["bc-version"];
280    }
281
282    static getVersion(isBcVersion: boolean = true): void {
283        let js2abc = path.join(path.resolve(__dirname, '../bin'), "js2abc");
284        let versionArg = isBcVersion ? "--bc-version" : "--bc-min-version";
285        execute(`${js2abc}`, [versionArg]);
286    }
287
288    static isBcMinVersion(): boolean {
289        if (!this.options) {
290            return false;
291        }
292        return this.options["bc-min-version"];
293    }
294
295    static getIncludedFiles(): string[] {
296        if (!this.options) {
297            return [];
298        }
299
300        return this.options["included-files"];
301    }
302
303    static needRecordType(): boolean {
304        if (!this.options) {
305            return false;
306        }
307
308        return !this.options["record-type"];
309    }
310
311    static needRecordDtsType(): boolean {
312        if (!this.options) {
313            return false;
314        }
315        return this.options["dts-type-record"];
316    }
317
318    static needRecordBuiltinDtsType(): boolean {
319        if (!this.options) {
320            return false;
321        }
322        return this.options["dts-builtin-type-record"];
323    }
324
325    static enableTypeLog(): boolean {
326        if (!this.options) {
327            return false;
328        }
329        return this.options["debug-type"];
330    }
331
332    static needRecordSourceCode(): boolean {
333        if (!this.options) {
334            return false;
335        }
336        return this.options["function-sourcecode"];
337    }
338
339    static getSourceFile(): string {
340        return this.options["source-file"];
341    }
342
343    static getPackageName(): string {
344        if (!this.options || !this.options["merge-abc"]) {
345            return "";
346        }
347
348        return this.options["package-name"];
349    }
350
351    static needGenerateTmpFile(): boolean {
352        if (!this.options) {
353            return false;
354        }
355        return this.options["generate-tmp-file"];
356    }
357
358    static isOutputproto(): boolean {
359        if (!this.options) {
360            return false;
361        }
362        return this.options["output-proto"];
363    }
364
365    static isMergeAbc(): boolean {
366        if (!this.options) {
367            return false;
368        }
369        return this.options["merge-abc"]
370    }
371
372    static isOhModules(): boolean {
373        if (!this.options) {
374            return false;
375        }
376        return this.options["oh-modules"]
377    }
378
379    // @ts-ignore
380    static parseUserCmd(args: string[]): ts.ParsedCommandLine | undefined {
381        this.options = commandLineArgs(ts2pandaOptions, { partial: true });
382        if (this.options.help) {
383            this.showHelp();
384            return undefined;
385        }
386
387        if (this.isBcVersion() || this.isBcMinVersion()) {
388            this.getVersion(this.isBcVersion());
389            return undefined;
390        }
391
392        if (!this.options._unknown) {
393            LOGE("options at least one file is needed");
394            this.showHelp();
395            return undefined;
396        }
397
398        this.parsedResult = ts.parseCommandLine(this.options._unknown!);
399        return this.parsedResult;
400    }
401
402    static parseCustomLibrary(args: string[]): string[] | undefined {
403        this.options = commandLineArgs(ts2pandaOptions, { partial: true });
404        if (this.options.help || this.isBcVersion() || this.isBcMinVersion() || !this.options._unknown) {
405            return undefined;
406        }
407        this.parsedResult = ts.parseCommandLine(this.options._unknown!);
408        return this.parsedResult.options["lib"];
409    }
410}
411