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
16#include "assignmentExpression.h"
17
18#include <compiler/base/lreference.h>
19#include <compiler/core/pandagen.h>
20#include <compiler/core/regScope.h>
21#include <typescript/checker.h>
22#include <typescript/core/destructuringContext.h>
23#include <ir/astDump.h>
24#include <ir/base/spreadElement.h>
25#include <ir/expressions/arrayExpression.h>
26#include <ir/expressions/objectExpression.h>
27#include <ir/expressions/identifier.h>
28
29namespace panda::es2panda::ir {
30
31bool AssignmentExpression::ConvertibleToAssignmentPattern(bool mustBePattern)
32{
33    bool convResult = true;
34
35    switch (left_->Type()) {
36        case AstNodeType::ARRAY_EXPRESSION: {
37            convResult = left_->AsArrayExpression()->ConvertibleToArrayPattern();
38            break;
39        }
40        case AstNodeType::SPREAD_ELEMENT: {
41            convResult = mustBePattern && left_->AsSpreadElement()->ConvertibleToRest(false);
42            break;
43        }
44        case AstNodeType::OBJECT_EXPRESSION: {
45            convResult = left_->AsObjectExpression()->ConvertibleToObjectPattern();
46            break;
47        }
48        case AstNodeType::ASSIGNMENT_EXPRESSION: {
49            convResult = left_->AsAssignmentExpression()->ConvertibleToAssignmentPattern(mustBePattern);
50            break;
51        }
52        case AstNodeType::META_PROPERTY_EXPRESSION:
53        case AstNodeType::CHAIN_EXPRESSION: {
54            convResult = false;
55            break;
56        }
57        default: {
58            break;
59        }
60    }
61
62    if (mustBePattern) {
63        SetType(AstNodeType::ASSIGNMENT_PATTERN);
64    }
65
66    if (!right_->IsAssignmentExpression()) {
67        return convResult;
68    }
69
70    switch (right_->Type()) {
71        case AstNodeType::ARRAY_EXPRESSION: {
72            convResult = right_->AsArrayExpression()->ConvertibleToArrayPattern();
73            break;
74        }
75        case AstNodeType::CHAIN_EXPRESSION:
76        case AstNodeType::SPREAD_ELEMENT: {
77            convResult = false;
78            break;
79        }
80        case AstNodeType::OBJECT_EXPRESSION: {
81            convResult = right_->AsObjectExpression()->ConvertibleToObjectPattern();
82            break;
83        }
84        case AstNodeType::ASSIGNMENT_EXPRESSION: {
85            convResult = right_->AsAssignmentExpression()->ConvertibleToAssignmentPattern(false);
86            break;
87        }
88        default: {
89            break;
90        }
91    }
92
93    return convResult;
94}
95
96void AssignmentExpression::Iterate(const NodeTraverser &cb) const
97{
98    cb(left_);
99    cb(right_);
100}
101
102void AssignmentExpression::Dump(ir::AstDumper *dumper) const
103{
104    if (type_ == AstNodeType::ASSIGNMENT_EXPRESSION) {
105        dumper->Add({{"type", "AssignmentExpression"}, {"operator", operator_}, {"left", left_}, {"right", right_}});
106    } else {
107        dumper->Add({{"type", "AssignmentPattern"}, {"left", left_}, {"right", right_}});
108    }
109}
110
111void AssignmentExpression::Compile(compiler::PandaGen *pg) const
112{
113    compiler::RegScope rs(pg);
114    compiler::LReference lref = compiler::LReference::CreateLRef(pg, left_, false);
115
116    if (operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND_EQUAL ||
117        operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR_EQUAL ||
118        operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_NULLISH_EQUAL) {
119        auto *skipRight = pg->AllocLabel();
120        auto *endLabel = pg->AllocLabel();
121        compiler::VReg lhsReg = pg->AllocReg();
122
123        lref.GetValue();
124        pg->StoreAccumulator(left_, lhsReg);
125        if (operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND_EQUAL) {
126            pg->BranchIfFalse(left_, skipRight);
127        } else if (operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR_EQUAL) {
128            pg->BranchIfTrue(left_, skipRight);
129        } else {
130            ASSERT(operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_NULLISH_EQUAL);
131            auto *nullish = pg->AllocLabel();
132            pg->BranchIfStrictNull(left_, nullish);
133            pg->LoadAccumulator(left_, lhsReg);
134            pg->BranchIfStrictNotUndefined(this, skipRight);
135            pg->SetLabel(left_, nullish);
136        }
137        // left = right
138        right_->Compile(pg);
139        lref.SetValue();
140        pg->Branch(this, endLabel);
141        // skip right part
142        pg->SetLabel(this, skipRight);
143        pg->LoadAccumulator(this, lhsReg);
144        pg->SetLabel(this, endLabel);
145        return;
146    } else if (operator_ != lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
147        compiler::VReg lhsReg = pg->AllocReg();
148
149        lref.GetValue();
150        pg->StoreAccumulator(left_, lhsReg);
151        right_->Compile(pg);
152        pg->Binary(this, operator_, lhsReg);
153    } else {
154        right_->Compile(pg);
155    }
156
157    lref.SetValue();
158}
159
160void AssignmentExpression::CompilePattern(compiler::PandaGen *pg) const
161{
162    compiler::RegScope rs(pg);
163    compiler::LReference lref = compiler::LReference::CreateLRef(pg, left_, false);
164    right_->Compile(pg);
165    lref.SetValue();
166}
167
168checker::Type *AssignmentExpression::Check(checker::Checker *checker) const
169{
170    if (left_->IsArrayPattern()) {
171        auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
172        auto destructuringContext = checker::ArrayDestructuringContext(checker, left_, true, true, nullptr, right_);
173        destructuringContext.Start();
174        return destructuringContext.InferedType();
175    }
176
177    if (left_->IsObjectPattern()) {
178        auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
179        auto destructuringContext = checker::ObjectDestructuringContext(checker, left_, true, true, nullptr, right_);
180        destructuringContext.Start();
181        return destructuringContext.InferedType();
182    }
183
184    if (left_->IsIdentifier() && left_->AsIdentifier()->Variable() &&
185        left_->AsIdentifier()->Variable()->Declaration()->IsConstDecl()) {
186        checker->ThrowTypeError({"Cannot assign to ", left_->AsIdentifier()->Name(), " because it is a constant."},
187                                left_->Start());
188    }
189
190    auto *leftType = left_->Check(checker);
191
192    if (leftType->HasTypeFlag(checker::TypeFlag::READONLY)) {
193        checker->ThrowTypeError("Cannot assign to this property because it is readonly.", left_->Start());
194    }
195
196    if (operator_ == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
197        checker->ElaborateElementwise(leftType, right_, left_->Start());
198        return checker->CheckTypeCached(right_);
199    }
200
201    auto *rightType = right_->Check(checker);
202
203    switch (operator_) {
204        case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL:
205        case lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL:
206        case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL:
207        case lexer::TokenType::PUNCTUATOR_MOD_EQUAL:
208        case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL:
209        case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL:
210        case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL:
211        case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL:
212        case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL:
213        case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL:
214        case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: {
215            return checker->CheckBinaryOperator(leftType, rightType, left_, right_, this, operator_);
216        }
217        case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: {
218            return checker->CheckPlusOperator(leftType, rightType, left_, right_, this, operator_);
219        }
220        case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL:
221        case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
222            return checker->CheckCompareOperator(leftType, rightType, left_, right_, this, operator_);
223        }
224        case lexer::TokenType::PUNCTUATOR_SUBSTITUTION: {
225            checker->CheckAssignmentOperator(operator_, left_, leftType, rightType);
226            return rightType;
227        }
228        default: {
229            UNREACHABLE();
230            break;
231        }
232    }
233
234    return nullptr;
235}
236
237void AssignmentExpression::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder)
238{
239    left_ = std::get<ir::AstNode *>(cb(left_))->AsExpression();
240    right_ = std::get<ir::AstNode *>(cb(right_))->AsExpression();
241}
242
243}  // namespace panda::es2panda::ir
244