• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}