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 "unaryExpression.h"
17
18#include <binder/variable.h>
19#include <compiler/core/pandagen.h>
20#include <typescript/checker.h>
21#include <ir/astDump.h>
22#include <ir/expressions/identifier.h>
23#include <ir/expressions/literals/bigIntLiteral.h>
24#include <ir/expressions/literals/numberLiteral.h>
25#include <ir/expressions/memberExpression.h>
26
27namespace panda::es2panda::ir {
28
29void UnaryExpression::Iterate(const NodeTraverser &cb) const
30{
31    cb(argument_);
32}
33
34void UnaryExpression::Dump(ir::AstDumper *dumper) const
35{
36    dumper->Add({{"type", "UnaryExpression"}, {"operator", operator_}, {"prefix", true}, {"argument", argument_}});
37}
38
39void UnaryExpression::Compile(compiler::PandaGen *pg) const
40{
41    switch (operator_) {
42        case lexer::TokenType::KEYW_DELETE: {
43            if (argument_->IsIdentifier()) {
44                binder::ScopeFindResult result = pg->Scope()->Find(argument_->AsIdentifier()->Name());
45                if (!result.variable || (result.scope->IsGlobalScope() && result.variable->IsGlobalVariable())) {
46                    compiler::RegScope rs(pg);
47                    compiler::VReg variable = pg->AllocReg();
48                    compiler::VReg global = pg->AllocReg();
49
50                    pg->LoadConst(this, compiler::Constant::JS_GLOBAL);
51                    pg->StoreAccumulator(this, global);
52
53                    pg->LoadAccumulatorString(this, argument_->AsIdentifier()->Name());
54                    pg->StoreAccumulator(this, variable);
55
56                    pg->DeleteObjProperty(this, global, variable);
57                } else {
58                    // Otherwise it is a local variable which can't be deleted and we just
59                    // return false.
60                    pg->LoadConst(this, compiler::Constant::JS_FALSE);
61                }
62            } else if (argument_->IsMemberExpression()) {
63                compiler::RegScope rs(pg);
64                compiler::VReg object = pg->AllocReg();
65
66                argument_->AsMemberExpression()->CompileObject(pg, object);
67                compiler::Operand prop = argument_->AsMemberExpression()->CompileKey(pg);
68
69                pg->DeleteObjProperty(this, object, prop);
70            } else {
71                // compile the delete operand.
72                argument_->Compile(pg);
73                // Deleting any value or a result of an expression returns True.
74                pg->LoadConst(this, compiler::Constant::JS_TRUE);
75            }
76            break;
77        }
78        case lexer::TokenType::KEYW_TYPEOF: {
79            if (argument_->IsIdentifier()) {
80                const ir::Identifier *ident = argument_->AsIdentifier();
81
82                binder::ScopeFindResult res = pg->Scope()->Find(ident->Name());
83                if (!res.variable && !pg->isDebuggerEvaluateExpressionMode()) {
84                    compiler::RegScope rs(pg);
85                    compiler::VReg global = pg->AllocReg();
86
87                    pg->LoadConst(this, compiler::Constant::JS_GLOBAL);
88                    pg->StoreAccumulator(this, global);
89                    pg->LoadObjByName(this, global, ident->Name());
90                } else if (!res.variable && pg->isDebuggerEvaluateExpressionMode()) {
91                    // false: typeof an undeclared variable will return undefined
92                    pg->LoadObjByNameViaDebugger(this, ident->Name(), false);
93                } else {
94                    pg->LoadVar(ident, res);
95                }
96            } else {
97                argument_->Compile(pg);
98            }
99
100            pg->TypeOf(this);
101            break;
102        }
103        case lexer::TokenType::KEYW_VOID: {
104            argument_->Compile(pg);
105            pg->LoadConst(this, compiler::Constant::JS_UNDEFINED);
106            break;
107        }
108        default: {
109            argument_->Compile(pg);
110
111            compiler::RegScope rs(pg);
112            compiler::VReg operandReg = pg->AllocReg();
113            pg->StoreAccumulator(this, operandReg);
114            pg->Unary(this, operator_, operandReg);
115            break;
116        }
117    }
118}
119
120checker::Type *UnaryExpression::Check(checker::Checker *checker) const
121{
122    checker::Type *operandType = argument_->Check(checker);
123
124    if (operator_ == lexer::TokenType::KEYW_TYPEOF) {
125        return operandType;
126    }
127
128    if (operator_ == lexer::TokenType::KEYW_DELETE) {
129        checker::Type *propType = argument_->Check(checker);
130
131        if (!argument_->IsMemberExpression()) {
132            checker->ThrowTypeError("The operand of a delete operator must be a property reference.",
133                                    argument_->Start());
134        }
135
136        const ir::MemberExpression *memberArg = argument_->AsMemberExpression();
137
138        if (memberArg->Property()->IsTSPrivateIdentifier() || memberArg->Property()->IsPrivateIdentifier()) {
139            checker->ThrowTypeError("The operand of a delete operator cannot be a private identifier.",
140                                    argument_->Start());
141        }
142
143        ASSERT(propType->Variable());
144
145        if (propType->Variable()->HasFlag(binder::VariableFlags::READONLY)) {
146            checker->ThrowTypeError("The operand of a delete operator cannot be a readonly property.",
147                                    argument_->Start());
148        }
149
150        if (!propType->Variable()->HasFlag(binder::VariableFlags::OPTIONAL)) {
151            checker->ThrowTypeError("The operand of a delete operator must be a optional.", argument_->Start());
152        }
153
154        return checker->GlobalBooleanType();
155    }
156
157    if (argument_->IsLiteral()) {
158        const ir::Literal *lit = argument_->AsLiteral();
159
160        if (lit->IsNumberLiteral()) {
161            auto numberValue = lit->AsNumberLiteral()->Number<double>();
162            if (operator_ == lexer::TokenType::PUNCTUATOR_PLUS) {
163                return checker->CreateNumberLiteralType(numberValue);
164            }
165
166            if (operator_ == lexer::TokenType::PUNCTUATOR_MINUS) {
167                return checker->CreateNumberLiteralType(-numberValue);
168            }
169        } else if (lit->IsBigIntLiteral() && operator_ == lexer::TokenType::PUNCTUATOR_MINUS) {
170            return checker->CreateBigintLiteralType(lit->AsBigIntLiteral()->Str(), true);
171        }
172    }
173
174    switch (operator_) {
175        case lexer::TokenType::PUNCTUATOR_PLUS:
176        case lexer::TokenType::PUNCTUATOR_MINUS:
177        case lexer::TokenType::PUNCTUATOR_TILDE: {
178            checker->CheckNonNullType(operandType, Start());
179            // TODO(aszilagyi): check Symbol like types
180
181            if (operator_ == lexer::TokenType::PUNCTUATOR_PLUS) {
182                if (checker::Checker::MaybeTypeOfKind(operandType, checker::TypeFlag::BIGINT_LIKE)) {
183                    checker->ThrowTypeError({"Operator '+' cannot be applied to type '", operandType, "'"}, Start());
184                }
185
186                return checker->GlobalNumberType();
187            }
188
189            return checker->GetUnaryResultType(operandType);
190        }
191        case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: {
192            checker->CheckTruthinessOfType(operandType, Start());
193            auto facts = operandType->GetTypeFacts();
194            if (facts & checker::TypeFacts::TRUTHY) {
195                return checker->GlobalFalseType();
196            }
197
198            if (facts & checker::TypeFacts::FALSY) {
199                return checker->GlobalTrueType();
200            }
201
202            return checker->GlobalBooleanType();
203        }
204        default: {
205            UNREACHABLE();
206        }
207    }
208
209    return nullptr;
210}
211
212void UnaryExpression::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder)
213{
214    argument_ = std::get<ir::AstNode *>(cb(argument_))->AsExpression();
215}
216
217}  // namespace panda::es2panda::ir
218