• 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
16import * as ts from "typescript";
17import { Literal, LiteralBuffer, LiteralTag } from "../base/literal";
18import { LReference } from "../base/lreference";
19import {
20    getPropName,
21    isConstantExpr,
22    Property,
23    propertyKeyAsString,
24    PropertyKind
25} from "../base/properties";
26import {
27    getParameterLength4Ctor,
28    getParamLengthOfFunc,
29    hasDefaultKeywordModifier,
30    hasExportKeywordModifier,
31    isUndefinedIdentifier
32} from "../base/util";
33import { CacheList, getVregisterCache } from "../base/vregisterCache";
34import { Compiler } from "../compiler";
35import { createArrayFromElements } from "../expression/arrayLiteralExpression";
36import { createMethodOrAccessor } from "../expression/objectLiteralExpression";
37import { findOuterNodeOfParenthesis } from "../expression/parenthesizedExpression";
38import {
39    VReg
40} from "../irnodes";
41import * as jshelpers from "../jshelpers";
42import { PandaGen } from "../pandagen";
43import { Recorder } from "../recorder";
44import {
45    FunctionScope,
46    GlobalScope,
47    LocalScope,
48    Scope,
49    VariableScope
50} from "../scope";
51import {
52    LocalVariable,
53    MandatoryFuncObj,
54    MandatoryThis,
55    ModuleVariable,
56    Variable
57} from "../variable";
58
59export function compileClassDeclaration(compiler: Compiler, stmt: ts.ClassLikeDeclaration) {
60    compiler.pushScope(stmt);
61
62    let pandaGen = compiler.getPandaGen();
63    let namedPropertyMap: Map<string, Property> = new Map<string, Property>();
64    let properties: Array<Property> = [];
65    let classFields: Array<ts.PropertyDeclaration> = [];
66
67    properties = generatePropertyFromExpr(stmt, classFields, namedPropertyMap);
68    let classReg = pandaGen.getTemp();
69
70    let baseVreg = compileHeritageClause(compiler, stmt);
71    let classBuffer = new LiteralBuffer();
72    let propertyIndex = 0;
73    let staticItemsNum = 0;
74    let hasConstructor = extractCtorOfClass(stmt) == undefined ? false : true;
75
76    for (; propertyIndex < properties.length; propertyIndex++) {
77        let prop = properties[propertyIndex];
78        let tmpVreg = pandaGen.getTemp();
79        if (prop.getKind() == PropertyKind.Constant) {
80            staticItemsNum++;
81            let nameLiteral = new Literal(LiteralTag.STRING, String(prop.getName()));
82            classBuffer.addLiterals(nameLiteral);
83            compiler.compileExpression(<ts.Expression>prop.getValue());
84            pandaGen.storeAccumulator(prop.getValue(), tmpVreg);
85            prop.setCompiled();
86        }
87
88        if (prop.getKind() == PropertyKind.Variable) {
89            if (prop.getValue().kind != ts.SyntaxKind.Constructor) {
90                if (jshelpers.hasStaticModifier(prop.getValue())) {
91                    staticItemsNum++;
92                }
93                let nameLiteral = new Literal(LiteralTag.STRING, String(prop.getName()));
94                classBuffer.addLiterals(nameLiteral);
95            }
96
97            if (ts.isMethodDeclaration(prop.getValue())) {
98                let methodLiteral = new Literal(LiteralTag.METHOD, compiler.getCompilerDriver().getFuncInternalName(<ts.MethodDeclaration>prop.getValue(), compiler.getRecorder()));
99                let affiliateLiteral = new Literal(LiteralTag.METHODAFFILIATE, getParamLengthOfFunc(<ts.MethodDeclaration>prop.getValue()));
100                classBuffer.addLiterals(methodLiteral, affiliateLiteral);
101            } else {
102                if (!ts.isConstructorDeclaration(prop.getValue())) {
103                    let valLiteral = new Literal(LiteralTag.NULLVALUE, null);
104                    classBuffer.addLiterals(valLiteral);
105                    compiler.compileExpression(<ts.Expression | ts.Identifier>prop.getValue());
106                    pandaGen.storeAccumulator(prop.getValue(), tmpVreg);
107                }
108            }
109            prop.setCompiled();
110        }
111
112        pandaGen.freeTemps(tmpVreg);
113        if (prop.getKind() == PropertyKind.Computed || prop.getKind() == PropertyKind.Accessor) {
114            break;
115        }
116    }
117
118    let notStaticItemsNum = propertyIndex - staticItemsNum;
119    let nameLiteral = new Literal(LiteralTag.INTEGER, hasConstructor ? notStaticItemsNum - 1 : notStaticItemsNum);
120    classBuffer.addLiterals(nameLiteral);
121
122    createClassLiteralBuf(compiler, classBuffer, stmt, [baseVreg, classReg]);
123
124    compileUnCompiledProperty(compiler, properties, classReg);
125    pandaGen.loadAccumulator(stmt, classReg);
126
127    let classScope = <Scope>compiler.getRecorder().getScopeOfNode(stmt);
128    if (hasExportKeywordModifier(stmt)) {
129        if (stmt.name) {
130            let className = jshelpers.getTextOfIdentifierOrLiteral(stmt.name);
131            let v: ModuleVariable = <ModuleVariable>(pandaGen.getScope().findLocal(className));
132            v.initialize();
133            pandaGen.storeModuleVariable(stmt, v);
134        } else if (hasDefaultKeywordModifier(stmt)) {
135            let defaultV: ModuleVariable = <ModuleVariable>(pandaGen.getScope().findLocal("*default*"));
136            pandaGen.storeModuleVariable(stmt, defaultV);
137        } else {
138            // throw SyntaxError in Recorder
139        }
140    } else {
141        if (stmt.name) {
142            let className = jshelpers.getTextOfIdentifierOrLiteral(stmt.name);
143            if (!ts.isClassExpression(stmt) && classScope.getParent() instanceof GlobalScope) {
144                pandaGen.stLetOrClassToGlobalRecord(stmt, className);
145            } else {
146                let classInfo = classScope.find(className);
147                (<LocalVariable | ModuleVariable>classInfo.v).initialize();
148                if (classInfo.v instanceof ModuleVariable) {
149                    let v: ModuleVariable = <ModuleVariable>(pandaGen.getScope().findLocal(className));
150                    pandaGen.storeModuleVariable(stmt, v);
151                } else {
152                    pandaGen.storeAccToLexEnv(stmt, classInfo.scope!, classInfo.level, classInfo.v!, true);
153                }
154            }
155        } else {
156            // throw SyntaxError in SyntaxChecker
157        }
158    }
159
160    pandaGen.freeTemps(classReg, baseVreg);
161    compiler.popScope();
162}
163
164export function AddCtor2Class(recorder: Recorder, classNode: ts.ClassLikeDeclaration, scope: Scope) {
165    let ctorNode;
166    if (jshelpers.getClassExtendsHeritageElement(classNode)) {
167        let parameter = ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createToken(ts.SyntaxKind.DotDotDotToken), "args");
168        ctorNode = ts.factory.createConstructorDeclaration(undefined, undefined, [parameter], undefined);
169    } else {
170        ctorNode = ts.factory.createConstructorDeclaration(undefined, undefined, [], undefined);
171    }
172
173    ctorNode = jshelpers.setParent(ctorNode, classNode)!;
174    ctorNode = ts.setTextRange(ctorNode, classNode);
175
176    let body = ts.factory.createBlock([]);
177    body = jshelpers.setParent(body, ctorNode)!;
178    body = ts.setTextRange(body, classNode)!;
179
180    ctorNode = ts.factory.updateConstructorDeclaration(ctorNode, undefined, undefined, ctorNode.parameters, body);
181    ctorNode = jshelpers.setParent(ctorNode, classNode)!;
182    ctorNode = ts.setTextRange(ctorNode, classNode);
183
184    let parentScope = <LocalScope>recorder.getScopeOfNode(classNode);
185    let funcScope = recorder.buildVariableScope(scope, ctorNode);
186    funcScope.setParent(parentScope);
187
188    let ctorBodyScope = new LocalScope(funcScope);
189    ctorBodyScope.setParent(funcScope);
190
191    recorder.setScopeMap(ctorNode, funcScope);
192    recorder.setScopeMap(ctorNode.body!, ctorBodyScope);
193
194    recorder.recordFuncName(ctorNode);
195    recorder.recordFunctionParameters(ctorNode);
196
197    recorder.setCtorOfClass(classNode, ctorNode);
198}
199
200export function compileDefaultConstructor(compiler: Compiler, ctrNode: ts.ConstructorDeclaration) {
201    let callNode = ts.factory.createCallExpression(ts.factory.createSuper(), undefined,
202        [ts.factory.createSpreadElement(ts.factory.createIdentifier("args"))]);
203
204    callNode = jshelpers.setParent(callNode, ctrNode)!;
205    callNode = ts.setTextRange(callNode, ctrNode)!
206
207    compileSuperCall(compiler, callNode, [], true);
208    compileConstructor(compiler, ctrNode, false);
209}
210
211function compileUnCompiledProperty(compiler: Compiler, properties: Property[], classReg: VReg) {
212    let pandaGen = compiler.getPandaGen();
213    for (let propertyIndex = 0; propertyIndex < properties.length; propertyIndex++) {
214        let prop = properties[propertyIndex];
215        if (prop.isCompiled()) {
216            continue;
217        }
218
219        switch (prop.getKind()) {
220            case PropertyKind.Constant:
221                compiler.compileExpression(<ts.Expression>prop.getValue());
222                pandaGen.storeOwnProperty(prop.getValue().parent, classReg, <string | number>prop.getName());
223                break;
224            case PropertyKind.Variable:
225                compileUnCompiledVariable(compiler, prop, classReg);
226                break;
227            case PropertyKind.Computed:
228                let keyReg = pandaGen.getTemp();
229                compiler.compileExpression((<ts.ComputedPropertyName>prop.getName()).expression);
230                pandaGen.storeAccumulator(prop.getValue(), keyReg);
231                compileComputedProperty(compiler, prop, classReg, keyReg);
232                break;
233            case PropertyKind.Accessor:
234                setClassAccessor(pandaGen, compiler, classReg, prop);
235                break;
236            default:
237                throw new Error("Unreachable PropertyKind for NullValue setting");
238        }
239    }
240}
241
242function compileUnCompiledVariable(compiler: Compiler, prop: Property, classReg: VReg) {
243    let pandaGen = compiler.getPandaGen();
244    let proptoReg = pandaGen.getTemp();
245    let tmpReg = pandaGen.getTemp();
246    let flag = false;
247
248    if (ts.isMethodDeclaration(prop.getValue())) {
249        flag = createClassMethodOrAccessor(compiler, classReg, proptoReg, tmpReg, <ts.MethodDeclaration>prop.getValue());
250    } else {
251        compiler.compileExpression(<ts.Expression | ts.Identifier>prop.getValue());
252        flag = setPrototypeAttributes(compiler, prop.getValue().parent, classReg, proptoReg, tmpReg);
253    }
254
255    pandaGen.storeOwnProperty(prop.getValue().parent, flag ? proptoReg : classReg, <string>prop.getName());
256    pandaGen.freeTemps(proptoReg, tmpReg);
257    prop.setCompiled();
258
259}
260
261function createClassLiteralBuf(compiler: Compiler, classBuffer: LiteralBuffer,
262    stmt: ts.ClassLikeDeclaration, vregs: VReg[]) {
263    let litId: string = PandaGen.appendLiteralArrayBuffer(classBuffer);
264
265    let ctorNode = compiler.getRecorder().getCtorOfClass(stmt);
266    let internalName = compiler.getCompilerDriver().getInternalNameForCtor(stmt, <ts.ConstructorDeclaration>ctorNode);
267
268    let pandaGen = compiler.getPandaGen();
269    let parameterLength = getParameterLength4Ctor(stmt);
270    pandaGen.defineClassWithBuffer(stmt, internalName, litId, parameterLength, vregs[0]);
271    pandaGen.storeAccumulator(stmt, vregs[1]);
272}
273
274export function compileDefaultInitClassMembers(compiler: Compiler, node: ts.ConstructorDeclaration) {
275    let pandaGen = compiler.getPandaGen();
276    let members = node.parent!.members;
277    for (let index = 0; index < members.length; index++) {
278        let decl = members[index];
279        if (ts.isPropertyDeclaration(decl) && !jshelpers.hasStaticModifier(decl)) {
280            if (!decl.initializer) {
281                continue;
282            }
283
284            let prop: VReg | string = "";
285            let thisReg: VReg = pandaGen.getTemp();
286            compiler.getThis(node, thisReg);
287
288            compiler.compileExpression(decl.initializer);
289
290            switch (decl.name.kind) {
291                case ts.SyntaxKind.Identifier:
292                case ts.SyntaxKind.StringLiteral:
293                case ts.SyntaxKind.NumericLiteral: {
294                    prop = jshelpers.getTextOfIdentifierOrLiteral(decl.name);
295                    pandaGen.storeObjProperty(node, thisReg, prop);
296                    break;
297                }
298                case ts.SyntaxKind.ComputedPropertyName: {
299                    // need to store the init value first
300                    let initVal: VReg = pandaGen.getTemp();
301                    pandaGen.storeAccumulator(node, initVal);
302
303                    prop = pandaGen.getTemp();
304                    compiler.compileExpression(decl.name.expression);
305                    pandaGen.storeAccumulator(node, prop);
306
307                    pandaGen.loadAccumulator(node, initVal);
308                    pandaGen.storeObjProperty(node, thisReg, prop);
309                    pandaGen.freeTemps(initVal, prop);
310                    break;
311                }
312                default:
313                    throw new Error("Private Identifier has not been supported")
314
315            }
316
317            pandaGen.freeTemps(thisReg);
318        }
319    }
320}
321
322export function compileReturnThis4Ctor(compiler: Compiler, node: ts.ConstructorDeclaration, unreachableFlag: boolean) {
323    let pandaGen = compiler.getPandaGen();
324
325    if (unreachableFlag) {
326        return;
327    }
328
329    let thisReg = pandaGen.getTemp();
330    compiler.getThis(node, thisReg);
331    pandaGen.loadAccumulator(node, thisReg);
332
333    checkValidUseSuperBeforeSuper(compiler, node);
334
335    pandaGen.return(node);
336    pandaGen.freeTemps(thisReg);
337}
338
339export function compileConstructor(compiler: Compiler, node: ts.ConstructorDeclaration, unreachableFlag: boolean) {
340    let pandaGen = compiler.getPandaGen();
341    let members = node.parent!.members;
342
343    for (let index = 0; index < members.length; index++) {
344        let decl = members[index];
345        if (ts.isPropertyDeclaration(decl) && !jshelpers.hasStaticModifier(decl)) {
346            let lref = LReference.generateLReference(compiler, decl.name, true);
347            if (decl.initializer) {
348                compiler.compileExpression(decl.initializer);
349            }
350            lref.setValue();
351        }
352    }
353
354    if (unreachableFlag) {
355        return;
356    }
357
358    let thisReg = pandaGen.getTemp();
359
360    compiler.getThis(node, thisReg);
361    pandaGen.loadAccumulator(node, thisReg);
362    checkValidUseSuperBeforeSuper(compiler, node);
363
364    pandaGen.return(node);
365    pandaGen.freeTemps(thisReg);
366}
367
368export function compileSuperCall(compiler: Compiler, node: ts.CallExpression, args: VReg[], hasSpread: boolean) {
369    let pandaGen = compiler.getPandaGen();
370
371    if (hasSpread) {
372        let argArray = pandaGen.getTemp();
373        createArrayFromElements(node, compiler, <ts.NodeArray<ts.Expression>>node.arguments, argArray);
374        loadCtorObj(node, compiler);
375        pandaGen.superCallSpread(node, argArray);
376        pandaGen.freeTemps(argArray);
377    } else {
378        let num = args.length;
379        loadCtorObj(node, compiler);
380        pandaGen.superCall(node, num, num ? args : [getVregisterCache(pandaGen, CacheList.undefined)]);
381    }
382
383    let tmpReg = pandaGen.getTemp();
384    pandaGen.storeAccumulator(node, tmpReg);
385
386    checkValidUseSuperBeforeSuper(compiler, node);
387
388    pandaGen.loadAccumulator(node, tmpReg);
389    pandaGen.freeTemps(tmpReg);
390
391    compiler.setThis(node);
392}
393
394function loadCtorObj(node: ts.CallExpression, compiler: Compiler) {
395    let recorder = compiler.getRecorder();
396    let pandaGen = compiler.getPandaGen();
397    let nearestFunc = jshelpers.getContainingFunctionDeclaration(node);
398    if (!nearestFunc) {
399        return;
400    }
401
402    if (ts.isConstructorDeclaration(nearestFunc)) {
403        pandaGen.loadAccumulator(node, getVregisterCache(pandaGen, CacheList.FUNC));
404    } else {
405        let curFuncScope = <FunctionScope>recorder.getScopeOfNode(nearestFunc);
406        let level = curFuncScope.need2CreateLexEnv() ? 0 : -1;
407
408        while (curFuncScope) {
409            if (curFuncScope.need2CreateLexEnv()) {
410                level += 1;
411            }
412
413            if (ts.isConstructorDeclaration(curFuncScope.getBindingNode())) {
414                break;
415            }
416
417            curFuncScope = <FunctionScope>curFuncScope.getParentVariableScope();
418        }
419
420        let funcObj = <Variable>curFuncScope.findLocal(MandatoryFuncObj);
421        pandaGen.loadLexicalVar(node, level, funcObj.lexIndex());
422    }
423
424}
425
426export function extractCtorOfClass(stmt: ts.ClassLikeDeclaration) {
427    let members = stmt.members;
428    for (let index = 0; index < members.length; index++) {
429        let member = members[index];
430        if (ts.isConstructorDeclaration(member)) {
431            return member;
432        }
433    }
434
435    return undefined;
436}
437
438export function defineClassMember(
439    propName: string | number | ts.ComputedPropertyName | undefined,
440    propValue: ts.Node,
441    propKind: PropertyKind,
442    properties: Property[],
443    namedPropertyMap: Map<string, Property>) {
444    let staticFlag = false;
445    if (propKind == PropertyKind.Computed || propKind == PropertyKind.Spread) {
446        let prop = new Property(propKind, <ts.ComputedPropertyName | undefined>propName);
447        prop.setValue(propValue);
448        if (jshelpers.hasStaticModifier(propValue)) {
449            staticFlag = true;
450            properties.push(prop);
451        } else {
452            properties.unshift(prop);
453        }
454    } else {
455        let name_str = propertyKeyAsString(<string | number>propName);
456        if (!checkAndUpdateProperty(namedPropertyMap, name_str, propKind, propValue)) {
457            let prop = new Property(propKind, propName);
458            if (propKind == PropertyKind.Accessor) {
459                if (ts.isGetAccessorDeclaration(propValue)) {
460                    prop.setGetter(propValue);
461                } else if (ts.isSetAccessorDeclaration(propValue)) {
462                    prop.setSetter(propValue);
463                }
464            } else {
465                prop.setValue(propValue);
466            }
467            if (jshelpers.hasStaticModifier(propValue)) {
468                staticFlag = true;
469                properties.push(prop);
470            } else {
471                properties.unshift(prop);
472            }
473            namedPropertyMap.set(name_str, prop);
474        }
475    }
476    return staticFlag;
477}
478
479function compileHeritageClause(compiler: Compiler, node: ts.ClassLikeDeclaration) {
480    let pandaGen = compiler.getPandaGen();
481    let baseVreg = pandaGen.getTemp();
482    if (node.heritageClauses && node.heritageClauses.length) {
483        let heritageClause = node.heritageClauses[0];
484        if (heritageClause.types.length) {
485            let exp = heritageClause.types[0];
486            compiler.compileExpression(exp.expression);
487            pandaGen.storeAccumulator(exp.expression, baseVreg);
488            return baseVreg;
489        }
490    }
491
492    pandaGen.moveVreg(node, baseVreg, getVregisterCache(pandaGen, CacheList.HOLE));
493    return baseVreg;
494}
495
496export function getClassNameForConstructor(classNode: ts.ClassLikeDeclaration) {
497    let className = "";
498
499    if (!isAnonymousClass(classNode)) {
500        className = jshelpers.getTextOfIdentifierOrLiteral(classNode.name!);
501    } else {
502        if (ts.isClassDeclaration(classNode) && hasExportKeywordModifier(classNode) && hasDefaultKeywordModifier(classNode)) {
503            return 'default';
504        }
505
506        let outerNode = findOuterNodeOfParenthesis(classNode);
507
508        if (ts.isVariableDeclaration(outerNode)) {
509            let decl = outerNode.name;
510            if (ts.isIdentifier(decl)) {
511                className = jshelpers.getTextOfIdentifierOrLiteral(decl);
512            }
513        } else if (ts.isBinaryExpression(outerNode)) {
514            let leftExp = outerNode.left;
515            if (outerNode.operatorToken.kind == ts.SyntaxKind.EqualsToken && ts.isIdentifier(leftExp)) {
516                className = jshelpers.getTextOfIdentifierOrLiteral(leftExp);
517            }
518        } else if (ts.isPropertyAssignment(outerNode)) {
519            let propName = outerNode.name;
520            if (ts.isIdentifier(propName) || ts.isStringLiteral(propName) || ts.isNumericLiteral(propName)) {
521                className = jshelpers.getTextOfIdentifierOrLiteral(propName);
522            }
523        } else if (ts.isExportAssignment(outerNode)) {
524            className = 'default';
525        }
526    }
527
528    return className;
529}
530
531function isAnonymousClass(node: ts.ClassLikeDeclaration) {
532    return node.name ? false : true;
533}
534
535function generatePropertyFromExpr(node: ts.ClassLikeDeclaration, classFields: Array<ts.PropertyDeclaration>, namedPropertyMap: Map<string, Property>) {
536    let properties: Array<Property> = [];
537    let staticNum = 0;
538    let constructNode: any;
539
540    node.members.forEach(member => {
541        switch (member.kind) {
542            case ts.SyntaxKind.Constructor:
543                constructNode = member;
544                break;
545            case ts.SyntaxKind.PropertyDeclaration: {
546                if (!jshelpers.hasStaticModifier(member)) {
547                    classFields.push(<ts.PropertyDeclaration>member);
548                    break;
549                }
550
551                if (ts.isComputedPropertyName(member.name!)) {
552                    if (defineClassMember(member.name, member, PropertyKind.Computed, properties, namedPropertyMap)) {
553                        staticNum++;
554                    }
555                } else {
556                    let memberName: number | string = <number | string>getPropName(member.name!);
557                    let initializer = (<ts.PropertyDeclaration>member).initializer;
558                    if (initializer) {
559                        if (isConstantExpr(initializer)) {
560                            if (defineClassMember(memberName, initializer, PropertyKind.Constant, properties, namedPropertyMap)) {
561                                staticNum++;
562                            }
563                        } else {
564                            if (defineClassMember(memberName, initializer, PropertyKind.Variable, properties, namedPropertyMap)) {
565                                staticNum++;
566                            }
567                        }
568                    } else {
569                        initializer = ts.createIdentifier("undefined");
570                        if (defineClassMember(memberName, initializer, PropertyKind.Constant, properties, namedPropertyMap)) {
571                            staticNum++;
572                        }
573                    }
574                }
575                break;
576            }
577            case ts.SyntaxKind.MethodDeclaration: {
578                let memberName = getPropName(member.name!);
579                if (typeof (memberName) == 'string' || typeof (memberName) == 'number') {
580                    if (defineClassMember(memberName, member, PropertyKind.Variable, properties, namedPropertyMap)) {
581                        staticNum++;
582                    }
583                } else {
584                    if (defineClassMember(memberName, member, PropertyKind.Computed, properties, namedPropertyMap)) {
585                        staticNum++;
586                    }
587                }
588                break;
589            }
590            case ts.SyntaxKind.GetAccessor:
591            case ts.SyntaxKind.SetAccessor: {
592                let accessorName = getPropName(member.name!);
593                if (typeof (accessorName) == 'string' || typeof (accessorName) == 'number') {
594                    if (defineClassMember(accessorName, member, PropertyKind.Accessor, properties, namedPropertyMap)) {
595                        staticNum++;
596                    }
597                } else {
598                    if (defineClassMember(accessorName, member, PropertyKind.Computed, properties, namedPropertyMap)) {
599                        staticNum++;
600                    }
601                }
602                break;
603            }
604            case ts.SyntaxKind.SemicolonClassElement:
605            case ts.SyntaxKind.IndexSignature:
606                break;
607            default:
608                throw new Error("Unreachable Kind");
609        }
610    });
611
612    /**
613     * If it is a non-static member, `unshift`; otherwise `push`
614     * Need to reverse the order of non-static members
615     */
616
617    let staticItems = properties.slice(properties.length - staticNum)
618    properties = properties.slice(0, properties.length - staticNum);
619    properties = properties.reverse();
620    properties.push(...staticItems);
621
622    if (constructNode) {
623        defineClassMember("constructor", constructNode, PropertyKind.Variable, properties, namedPropertyMap);
624    }
625
626    return properties;
627}
628
629function compileComputedProperty(compiler: Compiler, prop: Property, classReg: VReg, keyReg: VReg) {
630    let pandaGen = compiler.getPandaGen();
631    switch (prop.getValue().kind) {
632        case ts.SyntaxKind.PropertyDeclaration: {
633            let initializer = (<ts.PropertyDeclaration>prop.getValue()).initializer;
634            if (initializer) {
635                compiler.compileExpression(initializer);
636                pandaGen.storeOwnProperty(prop.getValue(), classReg, keyReg);
637            }
638            break;
639        }
640        case ts.SyntaxKind.MethodDeclaration: {
641            let protoReg = pandaGen.getTemp();
642            let tmpReg = pandaGen.getTemp();
643            let flag = createClassMethodOrAccessor(compiler, classReg, protoReg, tmpReg, <ts.MethodDeclaration>prop.getValue());
644            pandaGen.storeOwnProperty(prop.getValue(), flag ? protoReg : classReg, keyReg, true);
645            pandaGen.freeTemps(protoReg, tmpReg);
646            break;
647        }
648        case ts.SyntaxKind.GetAccessor: {
649            let accessorReg = pandaGen.getTemp();
650            let getProtoReg = pandaGen.getTemp();
651            let getter = <ts.GetAccessorDeclaration>prop.getValue();
652            let getFlag = createClassMethodOrAccessor(compiler, classReg, getProtoReg, accessorReg, getter);
653            pandaGen.defineGetterSetterByValue(getter, getFlag ? getProtoReg : classReg, keyReg, accessorReg, getVregisterCache(pandaGen, CacheList.undefined), true);
654            pandaGen.freeTemps(accessorReg, getProtoReg);
655            break;
656        }
657        case ts.SyntaxKind.SetAccessor: {
658            let accesReg = pandaGen.getTemp();
659            let setter = <ts.SetAccessorDeclaration>prop.getValue();
660            let setProtoReg = pandaGen.getTemp();
661            let setFlag = createClassMethodOrAccessor(compiler, classReg, setProtoReg, accesReg, setter);
662            pandaGen.defineGetterSetterByValue(setter, setFlag ? setProtoReg : classReg, keyReg, getVregisterCache(pandaGen, CacheList.undefined), accesReg, true);
663            pandaGen.freeTemps(accesReg, setProtoReg);
664            break;
665        }
666        default:
667            break;
668    }
669    pandaGen.freeTemps(keyReg);
670}
671
672function setClassAccessor(pandaGen: PandaGen, compiler: Compiler, objReg: VReg, prop: Property) {
673
674    let getterReg = pandaGen.getTemp();
675    let setterReg = pandaGen.getTemp();
676    let propReg = pandaGen.getTemp();
677
678    let tmpVreg = pandaGen.getTemp();
679    let flag = false;
680    let accessor: ts.GetAccessorDeclaration | ts.SetAccessorDeclaration;
681
682    if (prop.getGetter() !== undefined) {
683        let getter = <ts.GetAccessorDeclaration>prop.getGetter();
684        accessor = getter;
685        flag = createClassMethodOrAccessor(compiler, objReg, tmpVreg, getterReg, getter);
686    }
687    if (prop.getSetter() !== undefined) {
688        let setter = <ts.SetAccessorDeclaration>prop.getSetter();
689        accessor = setter;
690        flag = createClassMethodOrAccessor(compiler, objReg, tmpVreg, setterReg, setter);
691    }
692
693    pandaGen.loadAccumulatorString(accessor!, String(prop.getName()));
694    pandaGen.storeAccumulator(accessor!, propReg);
695
696    if (prop.getGetter() !== undefined && prop.getSetter() !== undefined) {
697        pandaGen.defineGetterSetterByValue(accessor!, flag ? tmpVreg : objReg, propReg, getterReg, setterReg, false);
698    } else if (ts.isGetAccessorDeclaration(accessor!)) {
699        pandaGen.defineGetterSetterByValue(accessor, flag ? tmpVreg : objReg, propReg, getterReg, getVregisterCache(pandaGen, CacheList.undefined), false);
700    } else {
701        pandaGen.defineGetterSetterByValue(accessor!, flag ? tmpVreg : objReg, propReg, getVregisterCache(pandaGen, CacheList.undefined), setterReg, false);
702    }
703
704    pandaGen.freeTemps(getterReg, setterReg, propReg, tmpVreg);
705}
706
707function createClassMethodOrAccessor(compiler: Compiler, classReg: VReg, propReg: VReg, storeReg: VReg,
708    node: ts.MethodDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration | ts.ConstructorDeclaration) {
709    let pandaGen = compiler.getPandaGen();
710    if (jshelpers.hasStaticModifier(node)) {
711        createMethodOrAccessor(pandaGen, compiler, classReg, node);
712        pandaGen.storeAccumulator(node, storeReg);
713        return false;
714    }
715    pandaGen.storeAccumulator(node, storeReg);
716    pandaGen.loadObjProperty(node, classReg, "prototype");
717    pandaGen.storeAccumulator(node, propReg);
718    pandaGen.loadAccumulator(node, storeReg);
719    createMethodOrAccessor(pandaGen, compiler, propReg, node);
720    pandaGen.storeAccumulator(node, storeReg);
721    return true;
722}
723
724function scalarArrayEquals(node1: ts.Node | undefined, node2: ts.Node | undefined) {
725    if (node1 && node2) {
726        let val1Modifs = node1.modifiers;
727        let val2Modifs = node2.modifiers;
728        if (val1Modifs && val2Modifs) {
729            return val1Modifs.length == val2Modifs.length && val1Modifs.every(function (v, i) { return v === val2Modifs![i] });;
730        }
731
732        if (!val1Modifs && !val2Modifs) {
733            return true;
734        }
735    } else if (!node1 && !node2) {
736        return true;
737    }
738
739    return false;
740}
741
742export function setPrototypeAttributes(compiler: Compiler, node: ts.Node, classReg: VReg, propReg: VReg, storeReg: VReg) {
743    let pandaGen = compiler.getPandaGen();
744    pandaGen.storeAccumulator(node, storeReg);
745    if (jshelpers.hasStaticModifier(node)) {
746        return false;
747    }
748    pandaGen.loadObjProperty(node, classReg, "prototype");
749    pandaGen.storeAccumulator(node, propReg);
750    pandaGen.loadAccumulator(node, storeReg);
751    return true;
752}
753
754function checkAndUpdateProperty(namedPropertyMap: Map<string, Property>, name: string, propKind: PropertyKind, valueNode: ts.Node): boolean {
755    if (namedPropertyMap.has(name)) {
756        let prop = namedPropertyMap.get(name);
757        if (propKind == PropertyKind.Accessor) {
758            if (ts.isGetAccessorDeclaration(valueNode)) {
759                if (!scalarArrayEquals(prop!.getGetter(), valueNode)) {
760                    return false;
761                }
762                prop!.setGetter(valueNode);
763            } else if (ts.isSetAccessorDeclaration(valueNode)) {
764                if (!scalarArrayEquals(prop!.getSetter(), valueNode)) {
765                    return false;
766                }
767                prop!.setSetter(valueNode);
768            }
769        } else {
770            if (!scalarArrayEquals(prop!.getValue(), valueNode)) {
771                return false;
772            }
773            prop!.setValue(valueNode);
774            prop!.setKind(propKind);
775        }
776        return true;
777    }
778    return false;
779}
780
781export function shouldReturnThisForConstruct(stmt: ts.ReturnStatement): boolean {
782    let ctorNode = jshelpers.getContainingFunction(stmt);
783    let expr = stmt.expression;
784    if (!ctorNode || !ts.isConstructorDeclaration(ctorNode)) {
785        return false;
786    }
787
788    if (!expr || isUndefinedIdentifier(expr) || expr.kind == ts.SyntaxKind.ThisKeyword) {
789        return true;
790    }
791
792    return false;
793}
794
795export function compileSuperProperty(compiler: Compiler, expr: ts.Expression, thisReg: VReg, prop: VReg | string | number) {
796    checkValidUseSuperBeforeSuper(compiler, expr);
797    let pandaGen = compiler.getPandaGen();
798    compiler.getThis(expr, thisReg);
799
800    pandaGen.loadSuperProperty(expr, thisReg, prop);
801}
802
803export function checkValidUseSuperBeforeSuper(compiler: Compiler, node: ts.Node) {
804    let pandaGen = compiler.getPandaGen();
805    let ctorNode = jshelpers.findAncestor(node, ts.isConstructorDeclaration);
806
807    if (!ctorNode || !ts.isClassLike(ctorNode.parent) || !jshelpers.getClassExtendsHeritageElement(ctorNode.parent)) {
808        return;
809    }
810
811    let thisReg = pandaGen.getTemp();
812    compiler.getThis(node, thisReg);
813    pandaGen.loadAccumulator(node, thisReg);
814    pandaGen.freeTemps(thisReg);
815
816    if (jshelpers.isSuperProperty(node) ||
817        ts.isConstructorDeclaration(node) ||
818        node.kind == ts.SyntaxKind.ThisKeyword ||
819        node.kind == ts.SyntaxKind.ReturnStatement) {
820        pandaGen.throwIfSuperNotCorrectCall(ctorNode, 0);
821    }
822
823    if (jshelpers.isSuperCall(node)) {
824        pandaGen.throwIfSuperNotCorrectCall(ctorNode, 1);
825    }
826}
827