1/* 2 * Copyright (c) 2021 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 { Compiler } from "src/compiler"; 18import * as jshelpers from "../jshelpers"; 19import { getParamLengthOfFunc } from "../base/util"; 20import { CacheList, getVregisterCache } from "../base/vregisterCache"; 21import { isInteger } from "./numericLiteral"; 22import { findInnerExprOfParenthesis } from "./parenthesizedExpression"; 23import { PandaGen } from "../pandagen"; 24import { VReg } from "../irnodes"; 25import { PropertyKind, Property, generatePropertyFromExpr } from "../base/properties"; 26import { LiteralTag, Literal, LiteralBuffer } from "../base/literal"; 27 28export function compileObjectLiteralExpression(compiler: Compiler, expr: ts.ObjectLiteralExpression): void { 29 let pandaGen = compiler.getPandaGen(); 30 31 // traverse the properties entries and store the useful information 32 let properties: Property[] = generatePropertyFromExpr(expr); 33 34 let objReg = pandaGen.getTemp(); 35 let hasMethod: boolean = false; 36 37 // empty ObjectLiteral expression 38 if (properties.length === 0) { 39 pandaGen.createEmptyObject(expr); 40 pandaGen.storeAccumulator(expr, objReg); 41 pandaGen.freeTemps(objReg); 42 return; 43 } 44 45 let literalBuffer = new LiteralBuffer(); 46 47 hasMethod = compileProperties(compiler, properties, literalBuffer); 48 49 createObject(expr, pandaGen, objReg, literalBuffer, hasMethod, compiler); 50 51 // for now there may left some Variable/Accessor to set the true value 52 setUncompiledProperties(compiler, pandaGen, properties, objReg); 53 54 pandaGen.loadAccumulator(expr, objReg); 55 pandaGen.freeTemps(objReg); 56} 57 58function compileProperties(compiler: Compiler, properties: Property[], literalBuffer: LiteralBuffer): boolean { 59 let hasMethod: boolean = false; 60 61 for (let prop of properties) { 62 if (prop.getKind() === PropertyKind.SPREAD || prop.getKind() === PropertyKind.COMPUTED) { 63 break; 64 } 65 66 if (prop.getKind() === PropertyKind.PROTOTYPE || prop.isRedeclared()) { 67 continue; 68 } 69 70 let nameLiteral = new Literal(LiteralTag.STRING, String(prop.getName())); 71 72 if (prop.getKind() === PropertyKind.CONSTANT) { 73 let valLiteral: Literal = createConstantLiteral(prop); 74 75 literalBuffer.addLiterals(nameLiteral, valLiteral!); 76 prop.setCompiled(); // need to be careful 77 } 78 79 if (prop.getKind() === PropertyKind.VARIABLE) { 80 let compilerDriver = compiler.getCompilerDriver(); 81 let valueNode = prop.getValue(); 82 let valLiteral: Literal; 83 84 if (ts.isMethodDeclaration(valueNode)) { 85 if (valueNode.asteriskToken) { 86 valLiteral = new Literal(LiteralTag.GENERATOR, compilerDriver.getFuncInternalName(valueNode, compiler.getRecorder())); 87 } else { 88 valLiteral = new Literal(LiteralTag.METHOD, compilerDriver.getFuncInternalName(valueNode, compiler.getRecorder())); 89 } 90 let affiliateLiteral = new Literal(LiteralTag.METHODAFFILIATE, getParamLengthOfFunc(valueNode)); 91 literalBuffer.addLiterals(nameLiteral, valLiteral, affiliateLiteral); 92 93 prop.setCompiled(); 94 hasMethod = true; 95 } else { 96 valLiteral = new Literal(LiteralTag.NULLVALUE, null); 97 literalBuffer.addLiterals(nameLiteral, valLiteral); 98 } 99 } 100 101 if (prop.getKind() === PropertyKind.ACCESSOR) { 102 let valLiteral = new Literal(LiteralTag.ACCESSOR, null); 103 literalBuffer.addLiterals(nameLiteral, valLiteral); 104 } 105 } 106 107 return hasMethod; 108} 109 110function createObject(expr: ts.ObjectLiteralExpression, pandaGen: PandaGen, objReg: VReg, 111 literalBuffer: LiteralBuffer, hasMethod: boolean, compiler: Compiler): void { 112 if (literalBuffer.isEmpty()) { 113 pandaGen.createEmptyObject(expr); 114 } else { 115 let bufferId = PandaGen.appendLiteralArrayBuffer(literalBuffer); 116 pandaGen.createObjectWithBuffer(expr, bufferId); 117 } 118 pandaGen.storeAccumulator(expr, objReg); 119} 120 121function createConstantLiteral(prop: Property): Literal { 122 let valLiteral: Literal; 123 if (prop.getValue().kind === ts.SyntaxKind.StringLiteral) { 124 valLiteral = new Literal(LiteralTag.STRING, jshelpers.getTextOfIdentifierOrLiteral(prop.getValue())); 125 } else if (prop.getValue().kind === ts.SyntaxKind.NumericLiteral) { 126 let value = Number.parseFloat(jshelpers.getTextOfIdentifierOrLiteral(prop.getValue())); 127 if (isInteger(value)) { 128 valLiteral = new Literal(LiteralTag.INTEGER, value); 129 } else { 130 valLiteral = new Literal(LiteralTag.DOUBLE, value); 131 } 132 } else if (prop.getValue().kind === ts.SyntaxKind.TrueKeyword || prop.getValue().kind === ts.SyntaxKind.FalseKeyword) { 133 if (prop.getValue().kind === ts.SyntaxKind.TrueKeyword) { 134 valLiteral = new Literal(LiteralTag.BOOLEAN, true); 135 } else { 136 valLiteral = new Literal(LiteralTag.BOOLEAN, false); 137 } 138 } else if (prop.getValue().kind === ts.SyntaxKind.NullKeyword) { 139 valLiteral = new Literal(LiteralTag.NULLVALUE, null); 140 } else { 141 throw new Error("Unreachable Kind of Literal"); 142 } 143 144 return valLiteral; 145} 146 147function compileAccessorProperty(pandaGen: PandaGen, compiler: Compiler, objReg: VReg, prop: Property): void { 148 let getterReg = pandaGen.getTemp(); 149 let setterReg = pandaGen.getTemp(); 150 let propReg = pandaGen.getTemp(); 151 let propName = String(prop.getName()); 152 let accessor: ts.GetAccessorDeclaration | ts.SetAccessorDeclaration; 153 154 if (prop.getGetter() !== undefined) { 155 let getter = <ts.GetAccessorDeclaration>prop.getGetter(); 156 createMethodOrAccessor(pandaGen, compiler, objReg, getter); 157 pandaGen.storeAccumulator(getter, getterReg); 158 accessor = getter; 159 } 160 if (prop.getSetter() !== undefined) { 161 let setter = <ts.SetAccessorDeclaration>prop.getSetter(); 162 createMethodOrAccessor(pandaGen, compiler, objReg, setter); 163 pandaGen.storeAccumulator(setter, setterReg); 164 accessor = setter; 165 } 166 167 pandaGen.loadAccumulatorString(accessor!, propName); 168 pandaGen.storeAccumulator(accessor!, propReg); 169 170 if (prop.getGetter() !== undefined && prop.getSetter() !== undefined) { 171 pandaGen.defineGetterSetterByValue(accessor!, objReg, propReg, getterReg, setterReg, false); 172 } else if (ts.isGetAccessorDeclaration(accessor!)) { 173 pandaGen.defineGetterSetterByValue(accessor, objReg, propReg, getterReg, getVregisterCache(pandaGen, CacheList.UNDEFINED), false); 174 } else { 175 pandaGen.defineGetterSetterByValue(accessor!, objReg, propReg, getVregisterCache(pandaGen, CacheList.UNDEFINED), setterReg, false); 176 } 177 178 pandaGen.freeTemps(getterReg, setterReg, propReg); 179} 180 181function compileSpreadProperty(compiler: Compiler, prop: Property, objReg: VReg): void { 182 let pandaGen = compiler.getPandaGen(); 183 184 compiler.compileExpression(<ts.Expression>prop.getValue()); 185 // srcObj is in acc now 186 pandaGen.copyDataProperties(<ts.Expression>prop.getValue().parent, objReg); 187} 188 189function compileComputedProperty(compiler: Compiler, prop: Property, objReg: VReg): void { 190 // Computed can't know its key in compile time, create Object now. 191 let pandaGen = compiler.getPandaGen(); 192 193 let keyReg = pandaGen.getTemp(); 194 compiler.compileExpression((<ts.ComputedPropertyName>prop.getName()).expression); 195 pandaGen.storeAccumulator(prop.getValue(), keyReg); 196 197 switch (prop.getValue().kind) { 198 case ts.SyntaxKind.PropertyAssignment: { 199 compiler.compileExpression((<ts.PropertyAssignment>prop.getValue()).initializer); 200 let nameSetting: boolean = needSettingName((<ts.PropertyAssignment>prop.getValue()).initializer); 201 pandaGen.storeOwnProperty(prop.getValue(), objReg, keyReg, nameSetting); 202 break; 203 } 204 case ts.SyntaxKind.MethodDeclaration: { 205 createMethodOrAccessor(pandaGen, compiler, objReg, <ts.MethodDeclaration>prop.getValue()); 206 pandaGen.storeOwnProperty(prop.getValue(), objReg, keyReg, true); 207 break; 208 } 209 case ts.SyntaxKind.GetAccessor: { 210 let accessorReg = pandaGen.getTemp(); 211 let getter = <ts.GetAccessorDeclaration>prop.getValue(); 212 createMethodOrAccessor(pandaGen, compiler, objReg, getter); 213 pandaGen.storeAccumulator(getter, accessorReg); 214 pandaGen.defineGetterSetterByValue(getter, objReg, keyReg, accessorReg, getVregisterCache(pandaGen, CacheList.UNDEFINED), true); 215 pandaGen.freeTemps(accessorReg); 216 break; 217 } 218 case ts.SyntaxKind.SetAccessor: { 219 let accessorReg = pandaGen.getTemp(); 220 let setter = <ts.SetAccessorDeclaration>prop.getValue(); 221 createMethodOrAccessor(pandaGen, compiler, objReg, setter); 222 pandaGen.storeAccumulator(setter, accessorReg); 223 pandaGen.defineGetterSetterByValue(setter, objReg, keyReg, getVregisterCache(pandaGen, CacheList.UNDEFINED), accessorReg, true); 224 pandaGen.freeTemps(accessorReg); 225 break; 226 } 227 // no default 228 } 229 230 pandaGen.freeTemps(keyReg); 231} 232 233function compileProtoProperty(compiler: Compiler, prop: Property, objReg: VReg): void { 234 let pandaGen = compiler.getPandaGen(); 235 let protoReg = pandaGen.getTemp(); 236 237 compiler.compileExpression(<ts.Expression>prop.getValue()); 238 pandaGen.storeAccumulator(<ts.Expression>prop.getValue().parent, protoReg); 239 pandaGen.setObjectWithProto(<ts.Expression>prop.getValue().parent, protoReg, objReg); 240 pandaGen.freeTemps(protoReg); 241} 242 243function setUncompiledProperties(compiler: Compiler, pandaGen: PandaGen, properties: Property[], objReg: VReg): void { 244 for (let prop of properties) { 245 if (!prop.isCompiled()) { 246 switch (prop.getKind()) { 247 case PropertyKind.ACCESSOR: { 248 compileAccessorProperty(pandaGen, compiler, objReg, prop); 249 break; 250 } 251 case PropertyKind.SPREAD: { 252 compileSpreadProperty(compiler, prop, objReg); 253 break; 254 } 255 case PropertyKind.COMPUTED: { 256 compileComputedProperty(compiler, prop, objReg); 257 break; 258 } 259 case PropertyKind.CONSTANT: 260 case PropertyKind.VARIABLE: { 261 let nameSetting: boolean = false; 262 if (ts.isMethodDeclaration(prop.getValue())) { 263 createMethodOrAccessor(pandaGen, compiler, objReg, <ts.MethodDeclaration>prop.getValue()); 264 } else { 265 compiler.compileExpression(<ts.Expression | ts.Identifier>prop.getValue()); 266 nameSetting = needSettingName(<ts.Expression | ts.Identifier>prop.getValue()) && 267 (<string | number>(prop.getName())).toString().lastIndexOf('.') != -1; 268 } 269 pandaGen.storeOwnProperty(prop.getValue().parent, objReg, <string | number>(prop.getName()), nameSetting); 270 break; 271 } 272 case PropertyKind.PROTOTYPE: { 273 compileProtoProperty(compiler, prop, objReg); 274 break; 275 } 276 default: { 277 throw new Error("Unreachable PropertyKind for NullValue setting"); 278 } 279 } 280 } 281 } 282} 283 284export function createMethodOrAccessor(pandaGen: PandaGen, compiler: Compiler, objReg: VReg, 285 func: ts.MethodDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration | ts.ConstructorDeclaration) { 286 let internalName = compiler.getCompilerDriver().getFuncInternalName(func, compiler.getRecorder()); 287 if (ts.isMethodDeclaration(func) && func.asteriskToken) { 288 pandaGen.defineFunction(func, func, internalName); 289 } else { 290 pandaGen.defineMethod(func, internalName, objReg); 291 } 292} 293 294function needSettingName(node: ts.Node): boolean { 295 let tempNode: ts.Node = node; 296 if (ts.isParenthesizedExpression(node)) { 297 tempNode = findInnerExprOfParenthesis(node); 298 } 299 300 if (ts.isFunctionLike(tempNode) || ts.isClassLike(tempNode)) { 301 let funcOrClassNode = <ts.FunctionLikeDeclaration | ts.ClassLikeDeclaration>tempNode; 302 if (!funcOrClassNode.name) { 303 return true; 304 } 305 } 306 return false; 307}