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