* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "destructuring.h"
#include <compiler/base/lreference.h>
#include <compiler/core/pandagen.h>
#include <ir/base/property.h>
#include <ir/base/spreadElement.h>
#include <ir/expressions/arrayExpression.h>
#include <ir/expressions/assignmentExpression.h>
#include <ir/expressions/identifier.h>
#include <ir/expressions/objectExpression.h>
namespace panda::es2panda::compiler {
static void GenRestElement(PandaGen *pg, const ir::SpreadElement *restElement,
const DestructuringIterator &destIterator, bool isDeclaration)
{
VReg array = pg->AllocReg();
VReg index = pg->AllocReg();
auto *next = pg->AllocLabel();
auto *done = pg->AllocLabel();
DestructuringRestIterator iterator(destIterator);
LReference lref = LReference::CreateLRef(pg, restElement, isDeclaration);
pg->CreateEmptyArray(restElement);
pg->StoreAccumulator(restElement, array);
pg->LoadAccumulatorInt(restElement, 0);
pg->StoreAccumulator(restElement, index);
pg->SetLabel(restElement, next);
iterator.Step(done);
pg->StoreObjByValue(restElement, array, index);
pg->LoadAccumulatorInt(restElement, 1);
pg->Binary(restElement, lexer::TokenType::PUNCTUATOR_PLUS, index);
pg->StoreAccumulator(restElement, index);
pg->Branch(restElement, next);
pg->SetLabel(restElement, done);
pg->LoadAccumulator(restElement, array);
lref.SetValue();
}
static void GenArray(PandaGen *pg, const ir::ArrayExpression *array)
{
DestructuringIterator iterator(pg, array);
if (array->Elements().empty()) {
iterator.Close(false);
return;
}
DestructuringIteratorContext dstrCtx(pg, iterator);
for (const auto *element : array->Elements()) {
RegScope ers(pg);
if (element->IsRestElement()) {
GenRestElement(pg, element->AsRestElement(), iterator, array->IsDeclaration());
break;
}
if (element->IsOmittedExpression()) {
iterator.Step();
continue;
}
const ir::Expression *init = nullptr;
const ir::Expression *target = element;
if (element->IsAssignmentPattern() || element->IsAssignmentExpression()) {
auto *assignment = element->IsAssignmentPattern() ? element->AsAssignmentPattern() :
element->AsAssignmentExpression();
target = assignment->Left();
init = assignment->Right();
}
LReference lref = LReference::CreateLRef(pg, target, array->IsDeclaration());
iterator.Step();
if (init) {
auto *assingValue = pg->AllocLabel();
auto *defaultInit = pg->AllocLabel();
pg->BranchIfStrictUndefined(element, defaultInit);
pg->LoadAccumulator(element, iterator.Result());
pg->Branch(element, assingValue);
pg->SetLabel(element, defaultInit);
init->Compile(pg);
pg->SetLabel(element, assingValue);
}
lref.SetValue();
}
}
static void GenObjectProperty(PandaGen *pg, const ir::ObjectExpression *object,
const ir::Expression *element, VReg value)
{
RegScope propScope(pg);
const ir::Property *propExpr = element->AsProperty();
const ir::Expression *init = nullptr;
const ir::Expression *key = propExpr->Key();
const ir::Expression *target = propExpr->Value();
if (target->IsAssignmentPattern() || target->IsAssignmentExpression()) {
auto *assignment = target->IsAssignmentPattern() ? target->AsAssignmentPattern() :
target->AsAssignmentExpression();
init = assignment->Right();
target = assignment->Left();
}
LReference lref = LReference::CreateLRef(pg, target, object->IsDeclaration());
if (key->IsIdentifier() && !propExpr->IsComputed()) {
pg->LoadObjByName(element, value, key->AsIdentifier()->Name());
} else {
key->Compile(pg);
pg->LoadObjByValue(element, value);
}
if (init != nullptr) {
VReg loadedValue = pg->AllocReg();
pg->StoreAccumulator(element, loadedValue);
auto *getDefault = pg->AllocLabel();
auto *store = pg->AllocLabel();
pg->BranchIfStrictUndefined(element, getDefault);
pg->LoadAccumulator(element, loadedValue);
pg->Branch(element, store);
pg->SetLabel(element, getDefault);
init->Compile(pg);
pg->SetLabel(element, store);
}
lref.SetValue();
}
static void GenObjectWithRest(PandaGen *pg, const ir::ObjectExpression *object, VReg rhs)
{
const auto &properties = object->Properties();
RegScope rs(pg);
if (properties.size() == 1) {
auto *element = properties[0];
ASSERT(element->IsRestElement());
VReg defaultProp = pg->AllocReg();
LReference lref = LReference::CreateLRef(pg, element, object->IsDeclaration());
pg->CreateObjectWithExcludedKeys(element, rhs, defaultProp, 0);
lref.SetValue();
return;
}
VReg propStart = pg->NextReg();
for (const auto *element : properties) {
if (element->IsRestElement()) {
RegScope restScope(pg);
LReference lref = LReference::CreateLRef(pg, element, object->IsDeclaration());
pg->CreateObjectWithExcludedKeys(element, rhs, propStart, properties.size() - 1);
lref.SetValue();
break;
}
VReg propName = pg->AllocReg();
const ir::Expression *key = element->AsProperty()->Key();
if (key->IsIdentifier()) {
pg->LoadAccumulatorString(key, key->AsIdentifier()->Name());
} else {
key->Compile(pg);
}
pg->StoreAccumulator(element, propName);
GenObjectProperty(pg, object, element, rhs);
}
}
static void GenObject(PandaGen *pg, const ir::ObjectExpression *object, VReg rhs)
{
const auto &properties = object->Properties();
if (properties.empty() || properties.back()->IsRestElement()) {
auto *notNullish = pg->AllocLabel();
auto *nullish = pg->AllocLabel();
pg->LoadConst(object, Constant::JS_NULL);
pg->Condition(object, lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL, rhs, nullish);
pg->LoadConst(object, Constant::JS_UNDEFINED);
pg->Condition(object, lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL, rhs, nullish);
pg->Branch(object, notNullish);
pg->SetLabel(object, nullish);
pg->ThrowObjectNonCoercible(object);
pg->SetLabel(object, notNullish);
if (!properties.empty()) {
return GenObjectWithRest(pg, object, rhs);
}
}
for (const auto *element : properties) {
GenObjectProperty(pg, object, element, rhs);
}
}
void Destructuring::Compile(PandaGen *pg, const ir::Expression *pattern)
{
RegScope rs(pg);
VReg rhs = pg->AllocReg();
pg->StoreAccumulator(pattern, rhs);
if (pattern->IsArrayPattern() || pattern->IsArrayExpression()) {
auto *arrExpr = pattern->IsArrayPattern() ? pattern->AsArrayPattern() :
pattern->AsArrayExpression();
GenArray(pg, arrExpr);
} else {
auto *objExpr = pattern->IsObjectPattern() ? pattern->AsObjectPattern() :
pattern->AsObjectExpression();
GenObject(pg, objExpr, rhs);
}
pg->LoadAccumulator(pattern, rhs);
}
}