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