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 "../compiler"; 18import * as jshelpers from "../jshelpers"; 19import { LiteralTag, Literal, LiteralBuffer } from "../base/literal"; 20import { isConstantExpr } from "../base/properties"; 21import { PandaGen } from "../pandagen"; 22import { isInteger } from "./numericLiteral"; 23import { VReg } from "../irnodes"; 24 25export function compileArrayLiteralExpression(compiler: Compiler, node: ts.ArrayLiteralExpression) { 26 let pandaGen = compiler.getPandaGen(); 27 let arrayObj = pandaGen.getTemp(); 28 createArrayFromElements(node, compiler, node.elements, arrayObj); 29 pandaGen.freeTemps(arrayObj); 30} 31 32export function createArrayFromElements(node: ts.Node, compiler: Compiler, elements: ts.NodeArray<ts.Expression>, arrayObj: VReg) { 33 let pandaGen = compiler.getPandaGen(); 34 // empty Array 35 if (elements.length == 0) { 36 pandaGen.createEmptyArray(node); 37 pandaGen.storeAccumulator(node, arrayObj); 38 return; 39 } 40 41 let literalBuffer = new LiteralBuffer(); 42 let indexReg = pandaGen.getTemp(); 43 let arrayCreated: boolean = false; 44 let hasSpread: boolean = false; 45 46 for (let i = 0; i < elements.length; i++) { 47 let element = elements[i]; 48 49 if (isConstantExpr(element)) { 50 let elem = parseConstantExpr(element); 51 52 if (!arrayCreated) { 53 literalBuffer.addLiterals(elem); 54 if (i == elements.length - 1) { 55 emitCreateArrayWithBuffer(pandaGen, literalBuffer, element); 56 pandaGen.storeAccumulator(element, arrayObj); 57 arrayCreated = true; 58 } 59 continue; 60 } 61 62 compiler.compileExpression(element); 63 if (hasSpread) { 64 storeElementIfSpreadExisted(pandaGen, element, arrayObj, indexReg); 65 } else { 66 pandaGen.storeOwnProperty(element, arrayObj, i); 67 } 68 continue; 69 } 70 71 if (ts.isSpreadElement(element)) { 72 if (!arrayCreated) { 73 emitCreateArrayWithBuffer(pandaGen, literalBuffer, element); 74 pandaGen.storeAccumulator(element, arrayObj); 75 arrayCreated = true; 76 } 77 78 if (hasSpread) { 79 storeSpreadElement(compiler, pandaGen, element, arrayObj, indexReg); 80 } else { 81 hasSpread = true; 82 pandaGen.loadAccumulatorInt(element, i); 83 pandaGen.storeAccumulator(element, indexReg); 84 storeSpreadElement(compiler, pandaGen, element, arrayObj, indexReg); 85 } 86 continue; 87 } 88 89 if (ts.isOmittedExpression(element)) { 90 if (!arrayCreated) { 91 emitCreateArrayWithBuffer(pandaGen, literalBuffer, element); 92 pandaGen.storeAccumulator(element, arrayObj); 93 arrayCreated = true; 94 } 95 96 if (i == elements.length - 1) { 97 // omittedExpression is the last element, we need to set the length of the array 98 if (hasSpread) { 99 pandaGen.loadAccumulator(element, indexReg); 100 pandaGen.storeObjProperty(element, arrayObj, "length"); 101 // no need to increment index since it's the last element 102 } else { 103 pandaGen.loadAccumulatorInt(element, elements.length); 104 pandaGen.storeObjProperty(element, arrayObj, "length"); 105 } 106 } 107 continue; 108 } 109 110 // non-constant elements 111 if (!arrayCreated) { 112 emitCreateArrayWithBuffer(pandaGen, literalBuffer, element); 113 pandaGen.storeAccumulator(element, arrayObj); 114 arrayCreated = true; 115 } 116 117 compiler.compileExpression(element); 118 119 if (hasSpread) { 120 storeElementIfSpreadExisted(pandaGen, element, arrayObj, indexReg); 121 } else { 122 pandaGen.storeOwnProperty(element, arrayObj, i); 123 } 124 } 125 126 pandaGen.loadAccumulator(node, arrayObj); 127 pandaGen.freeTemps(indexReg); 128} 129 130function parseConstantExpr(element: ts.Expression): Literal { 131 let elem: Literal; 132 switch (element.kind) { 133 case ts.SyntaxKind.FalseKeyword: 134 elem = new Literal(LiteralTag.BOOLEAN, false); 135 break; 136 case ts.SyntaxKind.TrueKeyword: 137 elem = new Literal(LiteralTag.BOOLEAN, true); 138 break; 139 case ts.SyntaxKind.StringLiteral: 140 elem = new Literal(LiteralTag.STRING, jshelpers.getTextOfIdentifierOrLiteral(element)); 141 break; 142 case ts.SyntaxKind.NumericLiteral: { 143 let value = Number.parseFloat(jshelpers.getTextOfIdentifierOrLiteral(element)); 144 if (isInteger(value)) { 145 elem = new Literal(LiteralTag.INTEGER, value); 146 } else { 147 elem = new Literal(LiteralTag.DOUBLE, value); 148 } 149 break; 150 } 151 case ts.SyntaxKind.NullKeyword: 152 elem = new Literal(LiteralTag.NULLVALUE, null); 153 break; 154 default: 155 throw new Error("invalid constant expression"); 156 } 157 158 return elem; 159} 160 161function emitCreateArrayWithBuffer(pandaGen: PandaGen, literalBuffer: LiteralBuffer, element: ts.Expression) { 162 if (literalBuffer.isEmpty()) { 163 pandaGen.createEmptyArray(element); 164 } else { 165 let bufferId = PandaGen.appendLiteralArrayBuffer(literalBuffer); 166 pandaGen.createArrayWithBuffer(element, bufferId); 167 } 168} 169 170function storeElementIfSpreadExisted(pandaGen: PandaGen, element: ts.Expression, arrayObj: VReg, indexReg: VReg) { 171 pandaGen.storeOwnProperty(element, arrayObj, indexReg); 172 pandaGen.unary(element, ts.SyntaxKind.PlusPlusToken, indexReg); 173 pandaGen.storeAccumulator(element, indexReg); 174} 175 176function storeSpreadElement(compiler: Compiler, pandaGen: PandaGen, element: ts.SpreadElement, arrayObj: VReg, indexReg: VReg) { 177 compiler.compileExpression(element.expression); 178 pandaGen.storeArraySpreadElement(element, arrayObj, indexReg); 179 pandaGen.storeAccumulator(element, indexReg); 180}