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
27 namespace panda::es2panda::ir {
28
Iterate(const NodeTraverser & cb) const29 void UnaryExpression::Iterate(const NodeTraverser &cb) const
30 {
31 cb(argument_);
32 }
33
Dump(ir::AstDumper * dumper) const34 void UnaryExpression::Dump(ir::AstDumper *dumper) const
35 {
36 dumper->Add({{"type", "UnaryExpression"}, {"operator", operator_}, {"prefix", true}, {"argument", argument_}});
37 }
38
Compile(compiler::PandaGen * pg) const39 void 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
Check(checker::Checker * checker) const120 checker::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()) {
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
UpdateSelf(const NodeUpdater & cb,binder::Binder * binder)212 void 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