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