• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
16/**
17 * Imlementation of bytecode generator.
18 * The PandaGen works with IR and provides an API
19 * to the compiler.
20 *
21 * This file should not contain imports of TypeScipt's AST nodes.
22 */
23import * as ts from "typescript";
24import {
25    BinaryOperator,
26    PrefixUnaryOperator,
27    SyntaxKind
28} from "typescript";
29import {
30    call,
31    closeIterator,
32    copyDataProperties,
33    copyModuleIntoCurrentModule,
34    creatDebugger,
35    createArrayWithBuffer,
36    createEmptyArray,
37    createEmptyObject,
38    createObjectHavingMethod,
39    createObjectWithBuffer,
40    createObjectWithExcludedKeys,
41    createRegExpWithLiteral,
42    defineAsyncFunc,
43    defineClassWithBuffer,
44    defineFunc,
45    defineGeneratorFunc,
46    defineGetterSetterByValue,
47    defineMethod,
48    defineNCFunc,
49    deleteObjProperty,
50    getIterator,
51    getIteratorNext,
52    getNextPropName,
53    getPropIterator,
54    importModule,
55    isFalse,
56    isTrue,
57    jumpTarget,
58    ldSuperByName,
59    ldSuperByValue,
60    loadAccumulator,
61    loadAccumulatorFloat,
62    loadAccumulatorInt,
63    loadAccumulatorString,
64    loadGlobalVar,
65    loadHomeObject,
66    loadLexicalVar,
67    loadModuleVarByName,
68    loadObjByIndex,
69    loadObjByName,
70    loadObjByValue,
71    moveVreg,
72    newLexicalEnv,
73    newObject,
74    popLexicalEnv,
75    returnUndefined,
76    setObjectWithProto,
77    stClassToGlobalRecord,
78    stConstToGlobalRecord,
79    stLetToGlobalRecord,
80    storeAccumulator,
81    storeArraySpread,
82    storeGlobalVar,
83    storeLexicalVar,
84    storeModuleVariable,
85    storeObjByIndex,
86    storeObjByName,
87    storeObjByValue,
88    storeOwnByIndex,
89    storeOwnByName,
90    storeOwnByValue,
91    stSuperByName,
92    stSuperByValue,
93    superCall,
94    superCallSpread,
95    throwDeleteSuperProperty,
96    throwException,
97    throwIfNotObject,
98    throwIfSuperNotCorrectCall,
99    throwObjectNonCoercible,
100    throwThrowNotExists,
101    throwUndefinedIfHole,
102    tryLoadGlobalByName,
103    tryStoreGlobalByName,
104    loadAccumulatorBigInt
105} from "./base/bcGenUtil";
106import {
107    Literal,
108    LiteralBuffer,
109    LiteralTag
110} from "./base/literal";
111import { BaseType } from "./base/typeSystem";
112import { getParamLengthOfFunc } from "./base/util";
113import {
114    CacheList,
115    getVregisterCache,
116    VregisterCache
117} from "./base/vregisterCache";
118import { CmdOptions } from "./cmdOptions";
119import {
120    DebugInfo,
121    NodeKind,
122    VariableDebugInfo
123} from "./debuginfo";
124import { isInteger } from "./expression/numericLiteral";
125import {
126    EcmaAdd2dyn,
127    EcmaAnd2dyn,
128    EcmaAshr2dyn,
129    EcmaAsyncfunctionawaituncaught,
130    EcmaAsyncfunctionenter,
131    EcmaAsyncfunctionreject,
132    EcmaAsyncfunctionresolve,
133    EcmaCallspreaddyn,
134    EcmaCopyrestargs,
135    EcmaCreategeneratorobj,
136    EcmaCreateiterresultobj,
137    EcmaDecdyn,
138    EcmaDiv2dyn,
139    EcmaEqdyn,
140    EcmaExpdyn,
141    EcmaGetresumemode,
142    EcmaGettemplateobject,
143    EcmaGetunmappedargs,
144    EcmaGreaterdyn,
145    EcmaGreatereqdyn,
146    EcmaIncdyn,
147    EcmaInstanceofdyn,
148    EcmaIsindyn,
149    EcmaLessdyn,
150    EcmaLesseqdyn,
151    EcmaMod2dyn,
152    EcmaMul2dyn,
153    EcmaNegdyn,
154    EcmaNewobjspreaddyn,
155    EcmaNotdyn,
156    EcmaNoteqdyn,
157    EcmaOr2dyn,
158    EcmaResumegenerator,
159    EcmaShl2dyn,
160    EcmaShr2dyn,
161    EcmaStricteqdyn,
162    EcmaStrictnoteqdyn,
163    EcmaSub2dyn,
164    EcmaSuspendgenerator,
165    EcmaTonumber,
166    EcmaTypeofdyn,
167    EcmaXor2dyn,
168    Imm,
169    IRNode,
170    Jeqz,
171    Label,
172    ReturnDyn,
173    VReg
174} from "./irnodes";
175import {
176    VariableAccessLoad,
177    VariableAcessStore
178} from "./lexenv";
179import { LOGE } from "./log";
180import {
181    FunctionScope,
182    LoopScope,
183    Scope,
184    VariableScope
185} from "./scope";
186import { CatchTable } from "./statement/tryStatement";
187import { TypeRecorder } from "./typeRecorder";
188import { Variable } from "./variable";
189
190export class PandaGen {
191    // @ts-ignore
192    private debugTag: string = "PandaGen";
193    readonly internalName: string;
194    private parametersCount: number;
195    private locals: VReg[] = [];
196    private temps: VReg[] = [];
197    private insns: IRNode[] = [];
198    private scope: Scope | undefined;
199    private vregisterCache: VregisterCache;
200    private catchMap: Map<Label, CatchTable> = new Map<Label, CatchTable>();
201    private totalRegsNum = 0;
202    // for debug info
203    private variableDebugInfoArray: VariableDebugInfo[] = [];
204    private firstStmt: ts.Statement | undefined;
205    private sourceFileDebugInfo: string = "";
206    private sourceCodeDebugInfo: string | undefined;
207    private callType: number = 0;
208
209    private static literalArrayBuffer: Array<LiteralBuffer> = new Array<LiteralBuffer>();
210
211    constructor(internalName: string, parametersCount: number, scope: Scope | undefined = undefined) {
212        this.internalName = internalName;
213        this.parametersCount = parametersCount;
214        this.scope = scope;
215        this.vregisterCache = new VregisterCache();
216    }
217
218    public appendScopeInfo(lexVarInfo: Map<string, number>): number | undefined {
219        if (lexVarInfo.size == 0) {
220            return undefined;
221        }
222
223        let scopeInfoIdx: number | undefined = undefined;
224        scopeInfoIdx = PandaGen.getLiteralArrayBuffer().length;
225        let scopeInfo = new LiteralBuffer();
226        let scopeInfoLiterals = new Array<Literal>();
227        scopeInfoLiterals.push(new Literal(LiteralTag.INTEGER, lexVarInfo.size));
228        lexVarInfo.forEach((slot: number, name: string) => {
229            scopeInfoLiterals.push(new Literal(LiteralTag.STRING, name));
230            scopeInfoLiterals.push(new Literal(LiteralTag.INTEGER, slot));
231        });
232        scopeInfo.addLiterals(...scopeInfoLiterals);
233        PandaGen.getLiteralArrayBuffer().push(scopeInfo);
234        return scopeInfoIdx;
235    }
236
237    public setCallType(callType: number) {
238        this.callType = callType;
239    }
240
241    public getCallType(): number {
242        return this.callType;
243    }
244
245    static getExportedTypes() {
246        if (TypeRecorder.getInstance()) {
247            return TypeRecorder.getInstance().getExportedType();
248        } else {
249            return new Map<string, number>();
250        }
251    }
252
253    static getDeclaredTypes() {
254        if (TypeRecorder.getInstance()) {
255            return TypeRecorder.getInstance().getDeclaredType();
256        } else {
257            return new Map<string, number>();
258        }
259    }
260
261    public getSourceCodeDebugInfo() {
262        return this.sourceCodeDebugInfo;
263    }
264
265    public setSourceCodeDebugInfo(code: string) {
266        this.sourceCodeDebugInfo = code;
267    }
268
269    public getSourceFileDebugInfo() {
270        return this.sourceFileDebugInfo;
271    }
272
273    public setSourceFileDebugInfo(sourceFile: string) {
274        this.sourceFileDebugInfo = sourceFile;
275    }
276
277    static getLiteralArrayBuffer() {
278        return PandaGen.literalArrayBuffer;
279    }
280
281    static clearLiteralArrayBuffer() {
282        PandaGen.literalArrayBuffer = [];
283    }
284
285    getParameterLength() {
286        if (this.scope instanceof FunctionScope) {
287            return this.scope.getParameterLength();
288        }
289    }
290
291    getFuncName() {
292        if (this.scope instanceof FunctionScope) {
293            return this.scope.getFuncName();
294        } else {
295            return "main";
296        }
297    }
298
299    static appendTypeArrayBuffer(type: BaseType): number {
300        let index = PandaGen.literalArrayBuffer.length;
301        PandaGen.literalArrayBuffer.push(type.transfer2LiteralBuffer());
302        return index;
303    }
304
305    static setTypeArrayBuffer(type: BaseType, index: number) {
306        PandaGen.literalArrayBuffer[index] = type.transfer2LiteralBuffer();
307    }
308
309    getFirstStmt() {
310        return this.firstStmt;
311    }
312
313    setFirstStmt(firstStmt: ts.Statement) {
314        if (this.firstStmt) {
315            return;
316        }
317        this.firstStmt = firstStmt;
318    }
319
320    getVregisterCache() {
321        return this.vregisterCache;
322    }
323
324    getCatchMap() {
325        return this.catchMap;
326    }
327
328    getScope(): Scope | undefined {
329        return this.scope;
330    }
331
332    getVariableDebugInfoArray(): VariableDebugInfo[] {
333        return this.variableDebugInfoArray;
334    }
335
336    addDebugVariableInfo(variable: VariableDebugInfo) {
337        this.variableDebugInfoArray.push(variable);
338    }
339
340    allocLocalVreg(): VReg {
341        let vreg = new VReg();
342        this.locals.push(vreg);
343        return vreg;
344    }
345
346    getVregForVariable(v: Variable): VReg {
347        if (v.hasAlreadyBinded()) {
348            return v.getVreg();
349        }
350        let vreg = this.allocLocalVreg();
351        v.bindVreg(vreg);
352        return vreg;
353    }
354
355    getTemp(): VReg {
356        let retval: VReg;
357        if (this.temps.length > 0) {
358            retval = this.temps.shift()!;
359        } else {
360            retval = new VReg();
361        }
362
363        return retval;
364    }
365
366    freeTemps(...temps: VReg[]) {
367        this.temps.unshift(...temps);
368    }
369
370    getInsns(): IRNode[] {
371        return this.insns;
372    }
373
374    setInsns(insns: IRNode[]) {
375        this.insns = insns;
376    }
377
378    printInsns() {
379        LOGE("function " + this.internalName + "() {");
380        this.getInsns().forEach(ins => {
381            LOGE(ins.toString());
382        })
383        LOGE("}");
384    }
385
386    setTotalRegsNum(num: number) {
387        this.totalRegsNum = num;
388    }
389
390    getTotalRegsNum(): number {
391        return this.totalRegsNum;
392    }
393
394    setParametersCount(count: number) {
395        this.parametersCount = count;
396    }
397
398    getParametersCount(): number {
399        return this.parametersCount;
400    }
401
402    setLocals(locals: VReg[]) {
403        this.locals = locals;
404    }
405
406    getLocals(): VReg[] {
407        return this.locals;
408    }
409
410    getTemps(): VReg[] {
411        return this.temps;
412    }
413
414    storeAccumulator(node: ts.Node | NodeKind, vreg: VReg) {
415        this.add(node, storeAccumulator(vreg));
416    }
417
418    loadAccFromArgs(node: ts.Node) {
419        if ((<VariableScope>this.scope).getUseArgs()) {
420            let v = this.scope!.findLocal("arguments");
421            if (this.scope instanceof FunctionScope) {
422                this.scope.setArgumentsOrRestargs();
423            }
424            if (v) {
425                let paramVreg = this.getVregForVariable(v);
426                this.getUnmappedArgs(node);
427                this.add(node, storeAccumulator(paramVreg));
428            } else {
429                throw new Error("fail to get arguments");
430            }
431        }
432    }
433
434    deleteObjProperty(node: ts.Node, obj: VReg, prop: VReg) {
435        this.add(node, deleteObjProperty(obj, prop));
436    }
437
438    loadAccumulator(node: ts.Node | NodeKind, vreg: VReg) {
439        this.add(node, loadAccumulator(vreg));
440    }
441
442    createLexEnv(node: ts.Node, env: VReg, scope: VariableScope | LoopScope) {
443        let numVars = scope.getNumLexEnv();
444        let scopeInfoIdx: number | undefined = undefined;
445        let lexVarInfo = scope.getLexVarInfo();
446        if (CmdOptions.isDebugMode()) {
447            scopeInfoIdx = this.appendScopeInfo(lexVarInfo);
448        }
449
450        this.add(
451            node,
452            newLexicalEnv(numVars, scopeInfoIdx),
453            storeAccumulator(env)
454        )
455    }
456
457    popLexicalEnv(node: ts.Node) {
458        this.add(
459            node,
460            popLexicalEnv()
461        )
462    }
463
464    loadAccFromLexEnv(node: ts.Node, scope: Scope, level: number, v: Variable) {
465        let expander = new VariableAccessLoad(scope, level, v);
466        let insns = expander.expand(this);
467        this.add(
468            node,
469            ...insns
470        );
471    }
472
473    storeAccToLexEnv(node: ts.Node | NodeKind, scope: Scope, level: number, v: Variable, isDeclaration: boolean) {
474        let expander = new VariableAcessStore(scope, level, v, isDeclaration, node);
475        let insns = expander.expand(this);
476        this.add(
477            node,
478            ...insns
479        )
480    }
481
482    loadObjProperty(node: ts.Node, obj: VReg, prop: VReg | string | number) {
483        switch (typeof (prop)) {
484            case "number": {
485                if (isInteger(prop)) {
486                    this.loadObjByIndex(node, obj, prop);
487                } else {
488                    let propReg = this.getTemp();
489                    this.add(
490                        node,
491                        loadAccumulatorFloat(prop),
492                        storeAccumulator(propReg),
493                    );
494                    this.loadObjByValue(node, obj, propReg);
495                    this.freeTemps(propReg);
496                }
497                break;
498            }
499            case "string":
500                this.loadObjByName(node, obj, prop);
501                break;
502            default:
503                this.loadObjByValue(node, obj, prop);
504        }
505    }
506
507    storeObjProperty(node: ts.Node | NodeKind, obj: VReg, prop: VReg | string | number) {
508        switch (typeof (prop)) {
509            case "number":
510                if (isInteger(prop)) {
511                    this.storeObjByIndex(node, obj, prop);
512                } else {
513                    let valueReg = this.getTemp();
514                    let propReg = this.getTemp();
515                    this.storeAccumulator(node, valueReg);
516                    this.add(
517                        node,
518                        loadAccumulatorFloat(prop),
519                        storeAccumulator(propReg),
520                        loadAccumulator(valueReg)
521                    );
522                    this.storeObjByValue(node, obj, propReg);
523                    this.freeTemps(valueReg, propReg);
524                }
525                break;
526            case "string":
527                this.storeObjByName(node, obj, prop);
528                break;
529            default:
530                this.storeObjByValue(node, obj, prop);
531        }
532    }
533
534    storeOwnProperty(node: ts.Node | NodeKind, obj: VReg, prop: VReg | string | number, nameSetting: boolean = false) {
535        switch (typeof prop) {
536            case "number": {
537                if (isInteger(prop)) {
538                    this.stOwnByIndex(node, obj, prop);
539                } else {
540                    let valueReg = this.getTemp();
541                    let propReg = this.getTemp();
542                    this.storeAccumulator(node, valueReg);
543                    this.add(
544                        node,
545                        loadAccumulatorFloat(prop),
546                        storeAccumulator(propReg),
547                        loadAccumulator(valueReg)
548                    );
549                    this.stOwnByValue(node, obj, propReg, nameSetting);
550                    this.freeTemps(valueReg, propReg);
551                }
552                break;
553            }
554            case "string":
555                this.stOwnByName(node, obj, prop, nameSetting);
556                break;
557            default:
558                this.stOwnByValue(node, obj, prop, nameSetting);
559        }
560    }
561
562    private loadObjByName(node: ts.Node, obj: VReg, string_id: string) {
563        this.add(
564            node,
565            loadObjByName(obj, string_id)
566        );
567    }
568
569    private storeObjByName(node: ts.Node | NodeKind, obj: VReg, string_id: string) {
570        this.add(
571            node,
572            storeObjByName(obj, string_id)
573        );
574    }
575
576    private loadObjByIndex(node: ts.Node, obj: VReg, index: number) {
577        this.add(
578            node,
579            loadObjByIndex(obj, index)
580        )
581    }
582
583    private storeObjByIndex(node: ts.Node | NodeKind, obj: VReg, index: number) {
584        this.add(
585            node,
586            storeObjByIndex(obj, index)
587        )
588    }
589
590
591    private loadObjByValue(node: ts.Node, obj: VReg, value: VReg) {
592        this.add(
593            node,
594            loadObjByValue(obj, value)
595        )
596    }
597
598    private storeObjByValue(node: ts.Node | NodeKind, obj: VReg, prop: VReg) {
599        this.add(
600            node,
601            storeObjByValue(obj, prop)
602        )
603    }
604
605    private stOwnByName(node: ts.Node | NodeKind, obj: VReg, string_id: string, nameSetting: boolean) {
606        this.add(node, storeOwnByName(obj, string_id, nameSetting));
607    }
608
609    private stOwnByIndex(node: ts.Node | NodeKind, obj: VReg, index: number) {
610        this.add(node, storeOwnByIndex(obj, index));
611    }
612
613    private stOwnByValue(node: ts.Node | NodeKind, obj: VReg, value: VReg, nameSetting: boolean) {
614        this.add(node, storeOwnByValue(obj, value, nameSetting));
615    }
616
617    loadByNameViaDebugger(node: ts.Node, string_id: string, boolVal: CacheList) {
618        this.loadObjProperty(node, getVregisterCache(this, CacheList.Global), "debuggerGetValue");
619        let getValueReg = this.getTemp();
620        this.storeAccumulator(node, getValueReg);
621        let variableReg = this.getTemp();
622        this.loadAccumulatorString(node, string_id);
623        this.storeAccumulator(node, variableReg);
624        let trueValueReg = this.getTemp();
625        this.moveVreg(node, trueValueReg, getVregisterCache(this, boolVal));
626        this.call(node, [getValueReg, variableReg, trueValueReg], false);
627        this.freeTemps(getValueReg, variableReg, trueValueReg);
628    }
629
630    // eg. print
631    tryLoadGlobalByName(node: ts.Node, string_id: string) {
632        CmdOptions.isWatchEvaluateExpressionMode() ? this.loadByNameViaDebugger(node, string_id, CacheList.True)
633                                : this.add(node, tryLoadGlobalByName(string_id));
634    }
635
636    storeByNameViaDebugger(node: ts.Node, string_id: string) {
637        let valueReg = this.getTemp();
638        this.storeAccumulator(node, valueReg);
639        this.loadObjProperty(node, getVregisterCache(this, CacheList.Global), "debuggerSetValue");
640        let setValueReg = this.getTemp();
641        this.storeAccumulator(node, setValueReg);
642        let variableReg = this.getTemp();
643        this.loadAccumulatorString(node, string_id);
644        this.storeAccumulator(node, variableReg);
645        this.call(node, [setValueReg, variableReg, valueReg], false);
646        this.freeTemps(valueReg, setValueReg, variableReg);
647    }
648
649    // eg. a = 1
650    tryStoreGlobalByName(node: ts.Node, string_id: string) {
651        CmdOptions.isWatchEvaluateExpressionMode() ? this.storeByNameViaDebugger(node, string_id)
652                                : this.add(node, tryStoreGlobalByName(string_id));
653    }
654
655    // eg. var n; n;
656    loadGlobalVar(node: ts.Node, string_id: string) {
657        this.add(
658            node,
659            loadGlobalVar(string_id));
660    }
661
662    // var n = 1;
663    storeGlobalVar(node: ts.Node | NodeKind, string_id: string) {
664        this.add(
665            node,
666            storeGlobalVar(string_id));
667    }
668
669    loadAccumulatorString(node: ts.Node | NodeKind, str: string) {
670        this.add(node, loadAccumulatorString(str));
671    }
672
673    loadAccumulatorFloat(node: ts.Node, num: number) {
674        this.add(node, loadAccumulatorFloat(num));
675    }
676
677    loadAccumulatorInt(node: ts.Node, num: number) {
678        this.add(node, loadAccumulatorInt(num));
679    }
680
681    moveVreg(node: ts.Node | NodeKind, vd: VReg, vs: VReg) {
682        this.add(node, moveVreg(vd, vs));
683    }
684
685    // @ts-ignore
686    label(node: ts.Node, label: Label) {
687        this.add(NodeKind.Invalid, label);
688    }
689
690    branch(node: ts.Node | NodeKind, target: Label) {
691        this.add(node, jumpTarget(target));
692    }
693
694    isTrue(node: ts.Node) {
695        this.add(
696            node,
697            isTrue()
698        )
699    }
700
701    jumpIfTrue(node: ts.Node, target: Label) {
702        this.isFalse(node);
703        this.add(
704            node,
705            new Jeqz(target)
706        )
707    }
708
709    isFalse(node: ts.Node) {
710        this.add(
711            node,
712            isFalse()
713        )
714    }
715
716    jumpIfFalse(node: ts.Node, target: Label) {
717        this.isTrue(node);
718        this.add(
719            node,
720            new Jeqz(target)
721        )
722    }
723
724    debugger(node: ts.Node) {
725        this.add(node, creatDebugger());
726    }
727
728    throwUndefinedIfHole(node: ts.Node, hole: VReg, name: VReg) {
729        this.add(
730            node,
731            throwUndefinedIfHole(hole, name)
732        )
733    }
734
735    /**
736     * The method generates code for ther following cases
737     *          if (lhs OP acc) {...}
738     * ifFalse: ...
739     */
740    condition(node: ts.Node, op: SyntaxKind, lhs: VReg, ifFalse: Label) {
741        // Please keep order of cases the same as in types.ts
742        switch (op) {
743            case SyntaxKind.LessThanToken: // line 57
744                this.add(node, new EcmaLessdyn(lhs));
745                this.add(node, new Jeqz(ifFalse));
746                break;
747            case SyntaxKind.GreaterThanToken: // line 59
748                this.add(node, new EcmaGreaterdyn(lhs));
749                this.add(node, new Jeqz(ifFalse));
750                break;
751            case SyntaxKind.LessThanEqualsToken: // line 60
752                this.add(node, new EcmaLesseqdyn(lhs));
753                this.add(node, new Jeqz(ifFalse));
754                break;
755            case SyntaxKind.GreaterThanEqualsToken: // line 61
756                this.add(node, new EcmaGreatereqdyn(lhs));
757                this.add(node, new Jeqz(ifFalse));
758                break;
759            case SyntaxKind.EqualsEqualsToken: // line 62
760                this.add(node, new EcmaEqdyn(lhs));
761                this.add(node, new Jeqz(ifFalse));
762                break;
763            case SyntaxKind.ExclamationEqualsToken: // line 63
764                this.add(node, new EcmaNoteqdyn(lhs));
765                this.add(node, new Jeqz(ifFalse));
766                break;
767            case SyntaxKind.EqualsEqualsEqualsToken: // line 64
768                this.add(node, new EcmaStricteqdyn(lhs));
769                this.add(node, new Jeqz(ifFalse));
770                break;
771            case SyntaxKind.ExclamationEqualsEqualsToken: // line 65
772                this.add(node, new EcmaStrictnoteqdyn(lhs));
773                this.add(node, new Jeqz(ifFalse));
774                break;
775            default:
776                break;
777        }
778    }
779
780    unary(node: ts.Node, op: PrefixUnaryOperator, operand: VReg) {
781        switch (op) {
782            case SyntaxKind.PlusToken:
783                this.add(node, new EcmaTonumber(operand));
784                break;
785            case SyntaxKind.MinusToken:
786                this.add(node, new EcmaNegdyn(operand));
787                break;
788            case SyntaxKind.PlusPlusToken:
789                this.add(node, new EcmaIncdyn(operand));
790                break;
791            case SyntaxKind.MinusMinusToken:
792                this.add(node, new EcmaDecdyn(operand));
793                break;
794            case SyntaxKind.ExclamationToken:
795                let falseLabel = new Label();
796                let endLabel = new Label();
797                this.jumpIfFalse(node, falseLabel);
798                // operand is true
799                this.add(node, loadAccumulator(getVregisterCache(this, CacheList.False)));
800                this.branch(node, endLabel);
801                // operand is false
802                this.label(node, falseLabel);
803                this.add(node, loadAccumulator(getVregisterCache(this, CacheList.True)));
804                this.label(node, endLabel);
805                break;
806            case SyntaxKind.TildeToken:
807                this.add(node, new EcmaNotdyn(operand));
808                break;
809            default:
810                throw new Error("Unimplemented");
811        }
812    }
813
814    binary(node: ts.Node, op: BinaryOperator, lhs: VReg) {
815        switch (op) {
816            case SyntaxKind.LessThanToken: // line 57
817            case SyntaxKind.GreaterThanToken: // line 59
818            case SyntaxKind.LessThanEqualsToken: // line 60
819            case SyntaxKind.GreaterThanEqualsToken: // line 61
820            case SyntaxKind.EqualsEqualsToken: // line 62
821            case SyntaxKind.ExclamationEqualsToken: // line 63
822            case SyntaxKind.EqualsEqualsEqualsToken: // line 64
823            case SyntaxKind.ExclamationEqualsEqualsToken: // line 65
824                this.binaryRelation(node, op, lhs);
825                break;
826            case SyntaxKind.PlusToken: // line 67
827            case SyntaxKind.PlusEqualsToken: // line 91
828                this.add(node, new EcmaAdd2dyn(lhs));
829                break;
830            case SyntaxKind.MinusToken: // line 68
831            case SyntaxKind.MinusEqualsToken: // line 92
832                this.add(node, new EcmaSub2dyn(lhs));
833                break;
834            case SyntaxKind.AsteriskToken: // line 69
835            case SyntaxKind.AsteriskEqualsToken: // line 93
836                this.add(node, new EcmaMul2dyn(lhs));
837                break;
838            case SyntaxKind.AsteriskAsteriskToken: // line 70
839            case SyntaxKind.AsteriskAsteriskEqualsToken: // line 94
840                this.add(node, new EcmaExpdyn(lhs));
841                break;
842            case SyntaxKind.SlashToken: // line 71
843            case SyntaxKind.SlashEqualsToken: // line 95
844                this.add(node, new EcmaDiv2dyn(lhs));
845                break;
846            case SyntaxKind.PercentToken: // line 72
847            case SyntaxKind.PercentEqualsToken: // line 96
848                this.add(node, new EcmaMod2dyn(lhs));
849                break;
850            case SyntaxKind.LessThanLessThanToken: // line 75
851            case SyntaxKind.LessThanLessThanEqualsToken: // line 97
852                this.add(node, new EcmaShl2dyn(lhs));
853                break;
854            case SyntaxKind.GreaterThanGreaterThanToken: // line 76
855            case SyntaxKind.GreaterThanGreaterThanEqualsToken: // line 98
856                this.add(node, new EcmaShr2dyn(lhs));
857                break;
858            case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: // line 77
859            case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: // line 99
860                this.add(node, new EcmaAshr2dyn(lhs));
861                break;
862            case SyntaxKind.AmpersandToken: // line 78
863            case SyntaxKind.AmpersandEqualsToken: // line 100
864                this.add(node, new EcmaAnd2dyn(lhs));
865                break;
866            case SyntaxKind.BarToken: // line 79
867            case SyntaxKind.BarEqualsToken: // line 101
868                this.add(node, new EcmaOr2dyn(lhs));
869                break;
870            case SyntaxKind.CaretToken: // line 80
871            case SyntaxKind.CaretEqualsToken: // line 102
872                this.add(node, new EcmaXor2dyn(lhs));
873                break;
874            case SyntaxKind.InKeyword: //line 125
875                // The in operator returns true if the specified property is in the specified object or its prototype chain
876                this.add(node, new EcmaIsindyn(lhs));
877                break;
878            case SyntaxKind.InstanceOfKeyword: //line 126
879                // The instanceof operator tests to see if the prototype property of
880                // a constructor appears anywhere in the prototype chain of an object.
881                // The return value is a boolean value.
882                this.add(node, new EcmaInstanceofdyn(lhs));
883                break;
884            default:
885                throw new Error("Unimplemented");
886        }
887    }
888
889    // throw needs argument of exceptionVreg
890    // to ensure rethrow the exception after finally
891    throw(node: ts.Node) {
892        this.add(
893            node,
894            throwException()
895        );
896    }
897
898    throwThrowNotExist(node: ts.Node) {
899        this.add(node, throwThrowNotExists());
900    }
901
902    throwDeleteSuperProperty(node: ts.Node) {
903        this.add(node, throwDeleteSuperProperty());
904    }
905
906    return(node: ts.Node | NodeKind) {
907        this.add(node, new ReturnDyn());
908    }
909
910    call(node: ts.Node, args: VReg[], passThis: boolean) {
911        this.add(
912            node,
913            call(args, passThis)
914        )
915    }
916
917    returnUndefined(node: ts.Node | NodeKind) {
918        this.add(
919            node,
920            returnUndefined()
921        )
922    }
923
924    newObject(node: ts.Node, args: VReg[]) {
925        this.add(
926            node,
927            newObject(args)
928        );
929    }
930
931    defineMethod(node: ts.FunctionLikeDeclaration, name: string, objReg: VReg, env: VReg) {
932        let paramLength = getParamLengthOfFunc(node);
933        this.add(
934            node,
935            loadAccumulator(objReg),
936            defineMethod(name, env, paramLength)
937        );
938    }
939
940    defineFunction(node: ts.FunctionLikeDeclaration | NodeKind, realNode: ts.FunctionLikeDeclaration, name: string, env: VReg) {
941        let paramLength = getParamLengthOfFunc(realNode);
942        if (realNode.modifiers) {
943            for (let i = 0; i < realNode.modifiers.length; i++) {
944                if (realNode.modifiers[i].kind == ts.SyntaxKind.AsyncKeyword) {
945                    if (realNode.asteriskToken) {
946                        // support async* further
947                    } else { // async
948                        this.add(
949                            node,
950                            defineAsyncFunc(name, env, paramLength)
951                        );
952                        return;
953                    }
954                }
955            }
956        }
957
958        if (realNode.asteriskToken) {
959            this.add(
960                node,
961                defineGeneratorFunc(name, env, paramLength)
962            );
963            return;
964        }
965
966        if (ts.isArrowFunction(realNode) || ts.isMethodDeclaration(realNode)) {
967            this.add(
968                node,
969                loadHomeObject(),
970                defineNCFunc(name, env, paramLength)
971            );
972            return;
973        }
974
975        this.add(
976            node,
977            defineFunc(name, env, paramLength)
978        );
979    }
980
981    typeOf(node: ts.Node) {
982        this.add(node, new EcmaTypeofdyn());
983    }
984
985    callSpread(node: ts.Node, func: VReg, thisReg: VReg, args: VReg) {
986        this.add(node, new EcmaCallspreaddyn(func, thisReg, args));
987    }
988
989    newObjSpread(node: ts.Node, obj: VReg, target: VReg) {
990        this.add(node, new EcmaNewobjspreaddyn(obj, target));
991    }
992
993    getUnmappedArgs(node: ts.Node) {
994        this.add(node, new EcmaGetunmappedargs());
995    }
996
997    toNumber(node: ts.Node, arg: VReg) {
998        this.add(node, new EcmaTonumber(arg));
999    }
1000
1001    createGeneratorObj(node: ts.Node, funcObj: VReg) {
1002        this.add(node, new EcmaCreategeneratorobj(funcObj));
1003    }
1004
1005    EcmaCreateiterresultobj(node: ts.Node, value: VReg, done: VReg) {
1006        this.add(node, new EcmaCreateiterresultobj(value, done));
1007    }
1008
1009    suspendGenerator(node: ts.Node, genObj: VReg, iterRslt: VReg) {
1010        this.add(node, new EcmaSuspendgenerator(genObj, iterRslt));
1011    }
1012
1013    resumeGenerator(node: ts.Node, genObj: VReg) {
1014        this.add(node, new EcmaResumegenerator(genObj));
1015    }
1016
1017    getResumeMode(node: ts.Node, genObj: VReg) {
1018        this.add(node, new EcmaGetresumemode(genObj));
1019    }
1020
1021    asyncFunctionEnter(node: ts.Node | NodeKind) {
1022        this.add(node, new EcmaAsyncfunctionenter());
1023    }
1024
1025    asyncFunctionAwaitUncaught(node: ts.Node, asynFuncObj: VReg, value: VReg) {
1026        this.add(node, new EcmaAsyncfunctionawaituncaught(asynFuncObj, value));
1027    }
1028
1029    asyncFunctionResolve(node: ts.Node | NodeKind, asyncObj: VReg, value: VReg, canSuspend: VReg) {
1030        this.add(node, new EcmaAsyncfunctionresolve(asyncObj, value, canSuspend));
1031    }
1032
1033    asyncFunctionReject(node: ts.Node | NodeKind, asyncObj: VReg, value: VReg, canSuspend: VReg) {
1034        this.add(node, new EcmaAsyncfunctionreject(asyncObj, value, canSuspend));
1035    }
1036
1037    getTemplateObject(node: ts.Node | NodeKind, value: VReg) {
1038        this.add(node, new EcmaGettemplateobject(value));
1039    }
1040
1041    copyRestArgs(node: ts.Node, index: number) {
1042        this.add(node, new EcmaCopyrestargs(new Imm(index)));
1043    }
1044
1045    getPropIterator(node: ts.Node) {
1046        this.add(node, getPropIterator());
1047    }
1048
1049    getNextPropName(node: ts.Node, iter: VReg) {
1050        this.add(node, getNextPropName(iter));
1051    }
1052
1053    createEmptyObject(node: ts.Node) {
1054        this.add(node, createEmptyObject());
1055    }
1056
1057    createObjectHavingMethod(node: ts.Node, idx: number, env: VReg) {
1058        this.add(
1059            node,
1060            loadAccumulator(env),
1061            createObjectHavingMethod(idx)
1062        );
1063    }
1064
1065    createObjectWithBuffer(node: ts.Node, idx: number) {
1066        this.add(node, createObjectWithBuffer(idx));
1067    }
1068
1069    setObjectWithProto(node: ts.Node, proto: VReg, object: VReg) {
1070        this.add(node, setObjectWithProto(proto, object));
1071    }
1072
1073    copyDataProperties(node: ts.Node, dstObj: VReg, srcObj: VReg) {
1074        this.add(node, copyDataProperties(dstObj, srcObj));
1075    }
1076
1077    defineGetterSetterByValue(node: ts.Node, obj: VReg, name: VReg, getter: VReg, setter: VReg, isComputedPropertyName: boolean) {
1078        if (isComputedPropertyName) {
1079            this.add(node, loadAccumulator(getVregisterCache(this, CacheList.True)));
1080        } else {
1081            this.add(node, loadAccumulator(getVregisterCache(this, CacheList.False)));
1082        }
1083        this.add(node, defineGetterSetterByValue(obj, name, getter, setter));
1084    }
1085
1086    createEmptyArray(node: ts.Node) {
1087        this.add(node, createEmptyArray());
1088    }
1089
1090    createArrayWithBuffer(node: ts.Node, idx: number) {
1091        this.add(node, createArrayWithBuffer(idx));
1092    }
1093
1094    storeArraySpreadElement(node: ts.Node, array: VReg, index: VReg) {
1095        this.add(node, storeArraySpread(array, index));
1096    }
1097
1098    storeLexicalVar(node: ts.Node, level: number, slot: number, value: VReg) {
1099        this.add(
1100            node,
1101            storeLexicalVar(level, slot, value)
1102        );
1103    }
1104
1105    loadLexicalVar(node: ts.Node, level: number, slot: number) {
1106        this.add(
1107            node,
1108            loadLexicalVar(level, slot)
1109        )
1110    }
1111
1112    importModule(node: ts.Node, moduleName: string) {
1113        this.add(node, importModule(moduleName));
1114    }
1115
1116    loadModuleVariable(node: ts.Node, module: VReg, varName: string) {
1117        this.add(node, loadModuleVarByName(varName, module));
1118    }
1119
1120    storeModuleVar(node: ts.Node, moduleVarName: string) {
1121        this.add(node, storeModuleVariable(moduleVarName));
1122    }
1123
1124    copyModule(node: ts.Node, module: VReg) {
1125        this.add(node, copyModuleIntoCurrentModule(module));
1126    }
1127
1128    defineClassWithBuffer(node: ts.Node, name: string, idx: number, parameterLength: number, base: VReg) {
1129        this.add(
1130            node,
1131            defineClassWithBuffer(name, idx, parameterLength, getVregisterCache(this, CacheList.LexEnv), base)
1132        )
1133    }
1134
1135    createObjectWithExcludedKeys(node: ts.Node, obj: VReg, args: VReg[]) {
1136        this.add(
1137            node,
1138            createObjectWithExcludedKeys(obj, args)
1139        );
1140    }
1141
1142    throwObjectNonCoercible(node: ts.Node) {
1143        this.add(
1144            node,
1145            throwObjectNonCoercible()
1146        );
1147    }
1148
1149    getIterator(node: ts.Node) {
1150        this.add(
1151            node,
1152            getIterator()
1153        );
1154    }
1155
1156    getIteratorNext(node: ts.Node, iter: VReg, nextMethod: VReg) {
1157        this.add(
1158            node,
1159            getIteratorNext(iter, nextMethod)
1160        )
1161    }
1162
1163    closeIterator(node: ts.Node, iter: VReg) {
1164        this.add(
1165            node,
1166            closeIterator(iter)
1167        )
1168    }
1169
1170    throwIfNotObject(node: ts.Node, obj: VReg) {
1171        this.add(
1172            node,
1173            throwIfNotObject(obj)
1174        );
1175    }
1176
1177    superCall(node: ts.Node, num: number, start: VReg) {
1178        this.add(
1179            node,
1180            superCall(num, start)
1181        )
1182    }
1183
1184    superCallSpread(node: ts.Node, vs: VReg) {
1185        this.add(node, superCallSpread(vs));
1186    }
1187
1188    ldSuperByName(node: ts.Node, obj: VReg, key: string) {
1189        this.add(
1190            node,
1191            ldSuperByName(obj, key)
1192        )
1193    }
1194
1195    stSuperByName(node: ts.Node, obj: VReg, key: string) {
1196        this.add(
1197            node,
1198            stSuperByName(obj, key)
1199        )
1200    }
1201
1202    ldSuperByValue(node: ts.Node, obj: VReg, prop: VReg) {
1203        this.add(
1204            node,
1205            ldSuperByValue(obj, prop)
1206        )
1207    }
1208
1209    stSuperByValue(node: ts.Node, obj: VReg, prop: VReg) {
1210        this.add(
1211            node,
1212            stSuperByValue(obj, prop)
1213        )
1214    }
1215
1216    loadSuperProperty(node: ts.Node, obj: VReg, prop: VReg | string | number) {
1217        switch (typeof (prop)) {
1218            case "string":
1219                this.ldSuperByName(node, obj, prop);
1220                break;
1221            case "number":
1222                let propReg = this.getTemp();
1223                this.loadAccumulatorInt(node, prop);
1224                this.storeAccumulator(node, propReg);
1225                this.ldSuperByValue(node, obj, propReg);
1226                this.freeTemps(propReg)
1227                break;
1228            default:
1229                this.ldSuperByValue(node, obj, prop);
1230        }
1231    }
1232
1233    throwIfSuperNotCorrectCall(node: ts.Node, num: number) {
1234        this.add(node, throwIfSuperNotCorrectCall(num));
1235    }
1236
1237    storeSuperProperty(node: ts.Node, obj: VReg, prop: VReg | string | number) {
1238        switch (typeof (prop)) {
1239            case "string":
1240                this.stSuperByName(node, obj, prop);
1241                break;
1242            case "number":
1243                let propReg = this.getTemp();
1244                this.loadAccumulatorInt(node, prop);
1245                this.storeAccumulator(node, propReg);
1246                this.stSuperByValue(node, obj, propReg);
1247                this.freeTemps(propReg)
1248                break;
1249            default:
1250                this.stSuperByValue(node, obj, prop);
1251        }
1252    }
1253
1254    loadHomeObject(node: ts.Node) {
1255        this.add(
1256            node,
1257            loadHomeObject()
1258        )
1259    }
1260
1261    createRegExpWithLiteral(node: ts.Node, pattern: string, flags: number) {
1262        this.add(
1263            node,
1264            createRegExpWithLiteral(pattern, flags)
1265        )
1266    }
1267
1268    stLetToGlobalRecord(node: ts.Node, string_id: string) {
1269        this.add(
1270            node,
1271            stLetToGlobalRecord(string_id));
1272    }
1273
1274    stConstToGlobalRecord(node: ts.Node, string_id: string) {
1275        this.add(
1276            node,
1277            stConstToGlobalRecord(string_id));
1278    }
1279
1280    stClassToGlobalRecord(node: ts.Node, string_id: string) {
1281        this.add(
1282            node,
1283            stClassToGlobalRecord(string_id));
1284    }
1285
1286    loadAccumulatorBigInt(node: ts.Node | NodeKind, str: string) {
1287        this.add(
1288            node,
1289            loadAccumulatorBigInt(str));
1290    }
1291
1292    private binaryRelation(node: ts.Node, op: BinaryOperator, lhs: VReg) {
1293        let falseLabel = new Label();
1294        let endLabel = new Label();
1295        switch (op) {
1296            case SyntaxKind.LessThanToken:
1297                this.add(node, new EcmaLessdyn(lhs));
1298                break;
1299            case SyntaxKind.GreaterThanToken:
1300                this.add(node, new EcmaGreaterdyn(lhs));
1301                break;
1302            case SyntaxKind.LessThanEqualsToken:
1303                this.add(node, new EcmaLesseqdyn(lhs));
1304                break;
1305            case SyntaxKind.GreaterThanEqualsToken:
1306                this.add(node, new EcmaGreatereqdyn(lhs));
1307                break;
1308            case SyntaxKind.EqualsEqualsToken:
1309                this.add(node, new EcmaEqdyn(lhs));
1310                break;
1311            case SyntaxKind.ExclamationEqualsToken:
1312                this.add(node, new EcmaNoteqdyn(lhs));
1313                break;
1314            case SyntaxKind.EqualsEqualsEqualsToken:
1315                this.add(node, new EcmaStricteqdyn(lhs));
1316                break;
1317            case SyntaxKind.ExclamationEqualsEqualsToken:
1318                this.add(node, new EcmaStrictnoteqdyn(lhs));
1319                break;
1320            default:
1321                break;
1322        }
1323        this.add(node, new Jeqz(falseLabel));
1324        this.add(node, loadAccumulator(getVregisterCache(this, CacheList.True)));
1325        this.branch(node, endLabel);
1326        this.label(node, falseLabel);
1327        this.add(node, loadAccumulator(getVregisterCache(this, CacheList.False)));
1328        this.label(node, endLabel);
1329    }
1330
1331    private add(node: ts.Node | NodeKind, ...insns: IRNode[]): void {
1332        // set pos debug info if debug mode
1333        DebugInfo.setDebuginfoForIns(node, ...insns);
1334
1335        this.insns.push(...insns);
1336    }
1337}
1338