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 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 panda::es2panda::compiler { 31 32static void GenRestElement(PandaGen *pg, const ir::SpreadElement *restElement, 33 const DestructuringIterator &destIterator, bool isDeclaration) 34{ 35 VReg array = pg->AllocReg(); 36 VReg index = pg->AllocReg(); 37 38 auto *next = pg->AllocLabel(); 39 auto *done = pg->AllocLabel(); 40 41 DestructuringRestIterator iterator(destIterator); 42 43 // create left reference for rest element 44 LReference lref = LReference::CreateLRef(pg, restElement, isDeclaration); 45 46 // create an empty array first 47 pg->CreateEmptyArray(restElement); 48 pg->StoreAccumulator(restElement, array); 49 50 // index = 0 51 pg->LoadAccumulatorInt(restElement, 0); 52 pg->StoreAccumulator(restElement, index); 53 54 pg->SetLabel(restElement, next); 55 56 iterator.Step(done); 57 pg->StoreObjByValue(restElement, array, index); 58 59 // index++ 60 pg->LoadAccumulatorInt(restElement, 1); 61 pg->Binary(restElement, lexer::TokenType::PUNCTUATOR_PLUS, index); 62 pg->StoreAccumulator(restElement, index); 63 64 pg->Branch(restElement, next); 65 66 pg->SetLabel(restElement, done); 67 pg->LoadAccumulator(restElement, array); 68 69 lref.SetValue(); 70} 71 72static void GenArray(PandaGen *pg, const ir::ArrayExpression *array) 73{ 74 DestructuringIterator iterator(pg, array); 75 76 if (array->Elements().empty()) { 77 iterator.Close(false); 78 return; 79 } 80 81 DestructuringIteratorContext dstrCtx(pg, iterator); 82 83 for (const auto *element : array->Elements()) { 84 RegScope ers(pg); 85 86 if (element->IsRestElement()) { 87 GenRestElement(pg, element->AsRestElement(), iterator, array->IsDeclaration()); 88 break; 89 } 90 91 // if a hole exist, just let the iterator step ahead 92 if (element->IsOmittedExpression()) { 93 iterator.Step(); 94 continue; 95 } 96 97 const ir::Expression *init = nullptr; 98 const ir::Expression *target = element; 99 100 if (element->IsAssignmentPattern() || element->IsAssignmentExpression()) { 101 auto *assignment = element->IsAssignmentPattern() ? element->AsAssignmentPattern() : 102 element->AsAssignmentExpression(); 103 target = assignment->Left(); 104 init = assignment->Right(); 105 } 106 107 LReference lref = LReference::CreateLRef(pg, target, array->IsDeclaration()); 108 iterator.Step(); 109 110 if (init) { 111 auto *assingValue = pg->AllocLabel(); 112 auto *defaultInit = pg->AllocLabel(); 113 pg->BranchIfStrictUndefined(element, defaultInit); 114 pg->LoadAccumulator(element, iterator.Result()); 115 pg->Branch(element, assingValue); 116 117 pg->SetLabel(element, defaultInit); 118 init->Compile(pg); 119 pg->SetLabel(element, assingValue); 120 } 121 122 lref.SetValue(); 123 } 124} 125 126static void GenObjectProperty(PandaGen *pg, const ir::ObjectExpression *object, 127 const ir::Expression *element, VReg value) 128{ 129 RegScope propScope(pg); 130 131 const ir::Property *propExpr = element->AsProperty(); 132 133 const ir::Expression *init = nullptr; 134 const ir::Expression *key = propExpr->Key(); 135 const ir::Expression *target = propExpr->Value(); 136 137 if (target->IsAssignmentPattern() || target->IsAssignmentExpression()) { 138 auto *assignment = target->IsAssignmentPattern() ? target->AsAssignmentPattern() : 139 target->AsAssignmentExpression(); 140 init = assignment->Right(); 141 target = assignment->Left(); 142 } 143 144 LReference lref = LReference::CreateLRef(pg, target, object->IsDeclaration()); 145 146 // load obj property from rhs, return undefined if no corresponding property exists 147 if (key->IsIdentifier() && !propExpr->IsComputed()) { 148 pg->LoadObjByName(element, value, key->AsIdentifier()->Name()); 149 } else { 150 key->Compile(pg); 151 pg->LoadObjByValue(element, value); 152 } 153 154 if (init != nullptr) { 155 VReg loadedValue = pg->AllocReg(); 156 pg->StoreAccumulator(element, loadedValue); 157 auto *getDefault = pg->AllocLabel(); 158 auto *store = pg->AllocLabel(); 159 160 pg->BranchIfStrictUndefined(element, getDefault); 161 pg->LoadAccumulator(element, loadedValue); 162 pg->Branch(element, store); 163 164 // load default value 165 pg->SetLabel(element, getDefault); 166 init->Compile(pg); 167 168 pg->SetLabel(element, store); 169 } 170 171 lref.SetValue(); 172} 173 174static void GenObjectWithRest(PandaGen *pg, const ir::ObjectExpression *object, VReg rhs) 175{ 176 const auto &properties = object->Properties(); 177 178 RegScope rs(pg); 179 180 if (properties.size() == 1) { 181 auto *element = properties[0]; 182 ASSERT(element->IsRestElement()); 183 VReg defaultProp = pg->AllocReg(); 184 LReference lref = LReference::CreateLRef(pg, element, object->IsDeclaration()); 185 pg->CreateObjectWithExcludedKeys(element, rhs, defaultProp, 0); 186 lref.SetValue(); 187 return; 188 } 189 190 VReg propStart = pg->NextReg(); 191 192 for (const auto *element : properties) { 193 if (element->IsRestElement()) { 194 RegScope restScope(pg); 195 LReference lref = LReference::CreateLRef(pg, element, object->IsDeclaration()); 196 pg->CreateObjectWithExcludedKeys(element, rhs, propStart, properties.size() - 1); 197 lref.SetValue(); 198 break; 199 } 200 201 VReg propName = pg->AllocReg(); 202 const ir::Expression *key = element->AsProperty()->Key(); 203 if (key->IsIdentifier()) { 204 pg->LoadAccumulatorString(key, key->AsIdentifier()->Name()); 205 } else { 206 key->Compile(pg); 207 } 208 pg->StoreAccumulator(element, propName); 209 210 GenObjectProperty(pg, object, element, rhs); 211 } 212} 213 214static void GenObject(PandaGen *pg, const ir::ObjectExpression *object, VReg rhs) 215{ 216 const auto &properties = object->Properties(); 217 218 if (properties.empty() || properties.back()->IsRestElement()) { 219 auto *notNullish = pg->AllocLabel(); 220 auto *nullish = pg->AllocLabel(); 221 222 pg->LoadConst(object, Constant::JS_NULL); 223 pg->Condition(object, lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL, rhs, nullish); 224 pg->LoadConst(object, Constant::JS_UNDEFINED); 225 pg->Condition(object, lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL, rhs, nullish); 226 pg->Branch(object, notNullish); 227 228 pg->SetLabel(object, nullish); 229 pg->ThrowObjectNonCoercible(object); 230 231 pg->SetLabel(object, notNullish); 232 233 if (!properties.empty()) { 234 return GenObjectWithRest(pg, object, rhs); 235 } 236 } 237 238 for (const auto *element : properties) { 239 GenObjectProperty(pg, object, element, rhs); 240 } 241} 242 243void Destructuring::Compile(PandaGen *pg, const ir::Expression *pattern) 244{ 245 RegScope rs(pg); 246 247 VReg rhs = pg->AllocReg(); 248 pg->StoreAccumulator(pattern, rhs); 249 250 if (pattern->IsArrayPattern() || pattern->IsArrayExpression()) { 251 auto *arrExpr = pattern->IsArrayPattern() ? pattern->AsArrayPattern() : 252 pattern->AsArrayExpression(); 253 GenArray(pg, arrExpr); 254 } else { 255 auto *objExpr = pattern->IsObjectPattern() ? pattern->AsObjectPattern() : 256 pattern->AsObjectExpression(); 257 GenObject(pg, objExpr, rhs); 258 } 259 260 pg->LoadAccumulator(pattern, rhs); 261} 262 263} // namespace panda::es2panda::compiler 264