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