1/** 2 * Copyright (c) 2021-2024 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 16#include "destructuring.h" 17 18#include "util/helpers.h" 19#include "compiler/base/iterators.h" 20#include "compiler/base/lreference.h" 21#include "compiler/base/catchTable.h" 22#include "compiler/core/pandagen.h" 23#include "ir/base/property.h" 24#include "ir/base/spreadElement.h" 25#include "ir/expressions/arrayExpression.h" 26#include "ir/expressions/assignmentExpression.h" 27#include "ir/expressions/identifier.h" 28#include "ir/expressions/objectExpression.h" 29 30namespace ark::es2panda::compiler { 31static void GenRestElement(PandaGen *pg, const ir::SpreadElement *restElement, 32 const DestructuringIterator &destIterator, bool isDeclaration) 33{ 34 VReg array = pg->AllocReg(); 35 VReg index = pg->AllocReg(); 36 37 auto *next = pg->AllocLabel(); 38 auto *done = pg->AllocLabel(); 39 40 DestructuringRestIterator iterator(destIterator); 41 42 // create left reference for rest element 43 auto lref = JSLReference::Create(pg, restElement, isDeclaration); 44 45 // create an empty array first 46 pg->CreateEmptyArray(restElement); 47 pg->StoreAccumulator(restElement, array); 48 49 // index = 0 50 pg->LoadAccumulatorInt(restElement, 0); 51 pg->StoreAccumulator(restElement, index); 52 53 pg->SetLabel(restElement, next); 54 55 iterator.Step(done); 56 pg->StoreObjByValue(restElement, array, index); 57 58 // index++ 59 pg->LoadAccumulatorInt(restElement, 1); 60 pg->Binary(restElement, lexer::TokenType::PUNCTUATOR_PLUS, index); 61 pg->StoreAccumulator(restElement, index); 62 63 pg->Branch(restElement, next); 64 65 pg->SetLabel(restElement, done); 66 pg->LoadAccumulator(restElement, array); 67 68 lref.SetValue(); 69} 70 71static void GenElement(const ir::ArrayExpression *array, DestructuringIterator &iterator, PandaGen *pg) 72{ 73 for (const auto *element : array->Elements()) { 74 RegScope ers(pg); 75 76 if (element->IsRestElement()) { 77 GenRestElement(pg, element->AsRestElement(), iterator, array->IsDeclaration()); 78 break; 79 } 80 81 // if a hole exist, just let the iterator step ahead 82 if (element->IsOmittedExpression()) { 83 iterator.Step(); 84 continue; 85 } 86 87 const ir::Expression *init = nullptr; 88 const ir::Expression *target = element; 89 90 if (element->IsAssignmentPattern()) { 91 target = element->AsAssignmentPattern()->Left(); 92 init = element->AsAssignmentPattern()->Right(); 93 } 94 95 auto lref = JSLReference::Create(pg, target, array->IsDeclaration()); 96 iterator.Step(); 97 98 if (init != nullptr) { 99 auto *assignValue = pg->AllocLabel(); 100 auto *defaultInit = pg->AllocLabel(); 101 pg->BranchIfUndefined(element, defaultInit); 102 pg->LoadAccumulator(element, iterator.Result()); 103 pg->Branch(element, assignValue); 104 105 pg->SetLabel(element, defaultInit); 106 init->Compile(pg); 107 pg->SetLabel(element, assignValue); 108 } 109 110 lref.SetValue(); 111 } 112} 113 114static void GenArray(PandaGen *pg, const ir::ArrayExpression *array) 115{ 116 DestructuringIterator iterator(pg, array); 117 118 if (array->Elements().empty()) { 119 iterator.Close(false); 120 return; 121 } 122 123 TryContext tryCtx(pg); 124 const auto &labelSet = tryCtx.LabelSet(); 125 pg->SetLabel(array, labelSet.TryBegin()); 126 127 GenElement(array, iterator, pg); 128 129 pg->SetLabel(array, labelSet.TryEnd()); 130 131 // Normal completion 132 pg->LoadAccumulator(array, iterator.Done()); 133 pg->BranchIfTrue(array, labelSet.CatchEnd()); 134 iterator.Close(false); 135 136 pg->Branch(array, labelSet.CatchEnd()); 137 138 Label *end = pg->AllocLabel(); 139 pg->SetLabel(array, labelSet.CatchBegin()); 140 pg->StoreAccumulator(array, iterator.Result()); 141 pg->LoadAccumulator(array, iterator.Done()); 142 143 pg->BranchIfTrue(array, end); 144 pg->LoadAccumulator(array, iterator.Result()); 145 iterator.Close(true); 146 pg->SetLabel(array, end); 147 pg->LoadAccumulator(array, iterator.Result()); 148 pg->EmitThrow(array); 149 pg->SetLabel(array, labelSet.CatchEnd()); 150} 151 152static std::tuple<const ir::Expression *, const ir::Expression *> GetAssignmentTarget(const ir::Property *propExpr) 153{ 154 const ir::Expression *init = nullptr; 155 const ir::Expression *target = propExpr->Value(); 156 157 if (target->IsAssignmentPattern()) { 158 init = target->AsAssignmentPattern()->Right(); 159 target = target->AsAssignmentPattern()->Left(); 160 } 161 162 return {init, target}; 163} 164 165static void GenDefaultInitializer(PandaGen *pg, const ir::Expression *element, const ir::Expression *init) 166{ 167 if (init == nullptr) { 168 return; 169 } 170 171 RegScope rs(pg); 172 VReg loadedValue = pg->AllocReg(); 173 pg->StoreAccumulator(element, loadedValue); 174 175 auto *getDefault = pg->AllocLabel(); 176 auto *store = pg->AllocLabel(); 177 178 pg->BranchIfUndefined(element, getDefault); 179 pg->LoadAccumulator(element, loadedValue); 180 pg->Branch(element, store); 181 182 // load default value 183 pg->SetLabel(element, getDefault); 184 init->Compile(pg); 185 186 pg->SetLabel(element, store); 187} 188 189static void GenObjectWithRest(PandaGen *pg, const ir::ObjectExpression *object, VReg rhs) 190{ 191 const auto &properties = object->Properties(); 192 193 RegScope rs(pg); 194 VReg propStart = pg->NextReg(); 195 196 for (const auto *element : properties) { 197 if (element->IsRestElement()) { 198 RegScope restScope(pg); 199 auto lref = JSLReference::Create(pg, element, object->IsDeclaration()); 200 pg->CreateObjectWithExcludedKeys(element, rhs, propStart, properties.size() - 1); 201 lref.SetValue(); 202 break; 203 } 204 205 VReg propReg = pg->AllocReg(); 206 207 RegScope propScope(pg); 208 209 const ir::Property *propExpr = element->AsProperty(); 210 const ir::Expression *key = propExpr->Key(); 211 const auto [init, target] = GetAssignmentTarget(propExpr); 212 213 if (key->IsIdentifier()) { 214 pg->LoadAccumulatorString(key, key->AsIdentifier()->Name()); 215 } else { 216 key->Compile(pg); 217 } 218 219 pg->StoreAccumulator(key, propReg); 220 221 auto lref = JSLReference::Create(pg, target, object->IsDeclaration()); 222 223 pg->LoadAccumulator(element, propReg); 224 pg->LoadObjByValue(element, rhs); 225 226 GenDefaultInitializer(pg, element, init); 227 228 lref.SetValue(); 229 } 230} 231 232static void GenObject(PandaGen *pg, const ir::ObjectExpression *object, VReg rhs) 233{ 234 const auto &properties = object->Properties(); 235 236 if (properties.empty() || properties.back()->IsRestElement()) { 237 auto *notNullish = pg->AllocLabel(); 238 239 pg->LoadAccumulator(object, rhs); 240 pg->BranchIfCoercible(object, notNullish); 241 pg->ThrowObjectNonCoercible(object); 242 243 pg->SetLabel(object, notNullish); 244 245 if (!properties.empty()) { 246 return GenObjectWithRest(pg, object, rhs); 247 } 248 } 249 250 for (const auto *element : properties) { 251 RegScope propScope(pg); 252 253 const ir::Property *propExpr = element->AsProperty(); 254 const ir::Expression *key = propExpr->Key(); 255 const auto [init, target] = GetAssignmentTarget(propExpr); 256 257 Operand propOperand = pg->ToOwnPropertyKey(key, propExpr->IsComputed()); 258 259 auto lref = JSLReference::Create(pg, target, object->IsDeclaration()); 260 261 if (std::holds_alternative<VReg>(propOperand)) { 262 pg->LoadAccumulator(element, std::get<VReg>(propOperand)); 263 pg->LoadObjByValue(element, rhs); 264 } else { 265 pg->LoadAccumulator(element, rhs); 266 pg->LoadObjProperty(element, propOperand); 267 } 268 269 GenDefaultInitializer(pg, element, init); 270 271 lref.SetValue(); 272 } 273} 274 275void Destructuring::Compile(PandaGen *pg, const ir::Expression *pattern) 276{ 277 RegScope rs(pg); 278 279 VReg rhs = pg->AllocReg(); 280 pg->StoreAccumulator(pattern, rhs); 281 282 if (pattern->IsArrayPattern()) { 283 GenArray(pg, pattern->AsArrayPattern()); 284 } else { 285 GenObject(pg, pattern->AsObjectPattern(), rhs); 286 } 287 288 pg->LoadAccumulator(pattern, rhs); 289} 290} // namespace ark::es2panda::compiler 291