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 "binaryExpression.h"
17
18#include <binder/variable.h>
19#include <compiler/core/pandagen.h>
20#include <compiler/core/regScope.h>
21#include <typescript/checker.h>
22#include <ir/astDump.h>
23#include <ir/base/classDefinition.h>
24#include <ir/expressions/privateIdentifier.h>
25#include <lexer/token/tokenType.h>
26
27namespace panda::es2panda::ir {
28
29void BinaryExpression::Iterate(const NodeTraverser &cb) const
30{
31    cb(left_);
32    cb(right_);
33}
34
35void BinaryExpression::Dump(ir::AstDumper *dumper) const
36{
37    dumper->Add({{"type", IsLogical() ? "LogicalExpression" : "BinaryExpression"},
38                 {"operator", operator_},
39                 {"left", left_},
40                 {"right", right_}});
41}
42
43void BinaryExpression::CompileLogical(compiler::PandaGen *pg) const
44{
45    compiler::RegScope rs(pg);
46    compiler::VReg lhs = pg->AllocReg();
47
48    ASSERT(operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND ||
49           operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR ||
50           operator_ == lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING);
51    auto *skipRight = pg->AllocLabel();
52    auto *endLabel = pg->AllocLabel();
53
54    // left -> acc -> lhs -> toboolean -> acc -> bool_lhs
55    left_->Compile(pg);
56    pg->StoreAccumulator(this, lhs);
57
58    if (operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) {
59        pg->BranchIfFalse(this, skipRight);
60    } else if (operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR) {
61        pg->BranchIfTrue(this, skipRight);
62    } else {
63        ASSERT(operator_ == lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING);
64        auto *nullish = pg->AllocLabel();
65        // if lhs === null
66        pg->BranchIfStrictNull(this, nullish);
67        pg->LoadAccumulator(this, lhs);
68        // if lhs === undefined
69        pg->BranchIfStrictNotUndefined(this, skipRight);
70        pg->SetLabel(this, nullish);
71    }
72
73    // left is true/false(and/or) then right -> acc
74    right_->Compile(pg);
75    pg->Branch(this, endLabel);
76
77    // left is false/true(and/or) then lhs -> acc
78    pg->SetLabel(this, skipRight);
79    pg->LoadAccumulator(this, lhs);
80    pg->SetLabel(this, endLabel);
81}
82
83void BinaryExpression::CompilePrivateIn(compiler::PandaGen *pg) const
84{
85    ASSERT(operator_ == lexer::TokenType::KEYW_IN);
86    auto name = left_->AsPrivateIdentifier()->Name();
87    auto result = pg->Scope()->FindPrivateName(name);
88
89    right_->Compile(pg);
90    if (!result.result.isMethod) {
91        pg->TestIn(this, result.lexLevel, result.result.slot);
92        return;
93    }
94    // Instance private method check symbol("#method")
95    if (!result.result.isStatic) {
96        pg->TestIn(this, result.lexLevel, result.result.validateMethodSlot);
97        return;
98    }
99    // Static private method check whether equals the class object
100    compiler::RegScope rs(pg);
101    compiler::VReg rhs = pg->AllocReg();
102    pg->StoreAccumulator(right_, rhs);
103    pg->LoadLexicalVar(this, result.lexLevel, result.result.validateMethodSlot);
104    pg->Equal(this, rhs);
105}
106
107void BinaryExpression::Compile(compiler::PandaGen *pg) const
108{
109    if (left_->IsPrivateIdentifier()) {
110        CompilePrivateIn(pg);
111        return;
112    }
113
114    if (IsLogical()) {
115        CompileLogical(pg);
116        return;
117    }
118
119    compiler::RegScope rs(pg);
120    compiler::VReg lhs = pg->AllocReg();
121
122    left_->Compile(pg);
123    pg->StoreAccumulator(right_, lhs);
124    right_->Compile(pg);
125
126    pg->Binary(right_, operator_, lhs);
127}
128
129checker::Type *BinaryExpression::Check(checker::Checker *checker) const
130{
131    auto *leftType = left_->Check(checker);
132    auto *rightType = right_->Check(checker);
133
134    switch (operator_) {
135        case lexer::TokenType::PUNCTUATOR_MULTIPLY:
136        case lexer::TokenType::PUNCTUATOR_EXPONENTIATION:
137        case lexer::TokenType::PUNCTUATOR_DIVIDE:
138        case lexer::TokenType::PUNCTUATOR_MOD:
139        case lexer::TokenType::PUNCTUATOR_MINUS:
140        case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT:
141        case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT:
142        case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT:
143        case lexer::TokenType::PUNCTUATOR_BITWISE_AND:
144        case lexer::TokenType::PUNCTUATOR_BITWISE_XOR:
145        case lexer::TokenType::PUNCTUATOR_BITWISE_OR: {
146            return checker->CheckBinaryOperator(leftType, rightType, left_, right_, this, operator_);
147        }
148        case lexer::TokenType::PUNCTUATOR_PLUS: {
149            return checker->CheckPlusOperator(leftType, rightType, left_, right_, this, operator_);
150        }
151        case lexer::TokenType::PUNCTUATOR_LESS_THAN:
152        case lexer::TokenType::PUNCTUATOR_GREATER_THAN: {
153            return checker->CheckCompareOperator(leftType, rightType, left_, right_, this, operator_);
154        }
155        case lexer::TokenType::PUNCTUATOR_EQUAL:
156        case lexer::TokenType::PUNCTUATOR_NOT_EQUAL:
157        case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL:
158        case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: {
159            if (checker->IsTypeEqualityComparableTo(leftType, rightType) ||
160                checker->IsTypeEqualityComparableTo(rightType, leftType)) {
161                return checker->GlobalBooleanType();
162            }
163
164            checker->ThrowBinaryLikeError(operator_, leftType, rightType, Start());
165        }
166        case lexer::TokenType::KEYW_INSTANCEOF: {
167            return checker->CheckInstanceofExpression(leftType, rightType, right_, this);
168        }
169        case lexer::TokenType::KEYW_IN: {
170            return checker->CheckInExpression(leftType, rightType, left_, right_, this);
171        }
172        case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: {
173            return checker->CheckAndOperator(leftType, rightType, left_);
174        }
175        case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
176            return checker->CheckOrOperator(leftType, rightType, left_);
177        }
178        case lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING: {
179            // TODO(Csaba Repasi): Implement checker for nullish coalescing
180            return checker->GlobalAnyType();
181        }
182        case lexer::TokenType::PUNCTUATOR_SUBSTITUTION: {
183            checker->CheckAssignmentOperator(operator_, left_, leftType, rightType);
184            return rightType;
185        }
186        default: {
187            UNREACHABLE();
188            break;
189        }
190    }
191
192    return nullptr;
193}
194
195void BinaryExpression::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder)
196{
197    left_ = std::get<ir::AstNode *>(cb(left_))->AsExpression();
198    right_ = std::get<ir::AstNode *>(cb(right_))->AsExpression();
199}
200
201}  // namespace panda::es2panda::ir
202