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