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 "callExpression.h" 17 18#include <util/helpers.h> 19#include <compiler/core/pandagen.h> 20#include <compiler/core/regScope.h> 21#include <typescript/checker.h> 22#include <typescript/types/objectType.h> 23#include <typescript/types/signature.h> 24#include <typescript/types/type.h> 25#include <ir/astDump.h> 26#include <ir/base/classDefinition.h> 27#include <ir/base/scriptFunction.h> 28#include <ir/base/spreadElement.h> 29#include <ir/expressions/chainExpression.h> 30#include <ir/expressions/memberExpression.h> 31#include <ir/ts/tsAsExpression.h> 32#include <ir/ts/tsNonNullExpression.h> 33#include <ir/ts/tsTypeAssertion.h> 34#include <ir/ts/tsTypeParameterInstantiation.h> 35 36namespace panda::es2panda::ir { 37 38void CallExpression::Iterate(const NodeTraverser &cb) const 39{ 40 cb(callee_); 41 42 if (typeParams_) { 43 cb(typeParams_); 44 } 45 46 for (auto *it : arguments_) { 47 cb(it); 48 } 49} 50 51void CallExpression::Dump(ir::AstDumper *dumper) const 52{ 53 dumper->Add({{"type", "CallExpression"}, 54 {"callee", callee_}, 55 {"arguments", arguments_}, 56 {"optional", optional_}, 57 {"typeParameters", AstDumper::Optional(typeParams_)}}); 58} 59 60compiler::VReg CallExpression::CreateSpreadArguments(compiler::PandaGen *pg) const 61{ 62 compiler::VReg argsObj = pg->AllocReg(); 63 pg->CreateArray(this, arguments_, argsObj); 64 65 return argsObj; 66} 67 68void CallExpression::CompileSuperCallWithSpreadArgs(compiler::PandaGen *pg) const 69{ 70 compiler::RegScope paramScope(pg); 71 const ir::ScriptFunction *constructorFunc = util::Helpers::GetContainingConstructor(this); 72 CHECK_NOT_NULL(constructorFunc); 73 // For super call in default constructor. 74 if (constructorFunc->HasFlag(ir::ScriptFunctionFlags::GENERATED_CONSTRUCTOR)) { 75 // Use callruntime.supercallforwardallargs to optimize super call in default constructor since api13. 76 if (pg->Binder()->Program()->TargetApiVersion() >= util::Helpers::SUPER_CALL_OPT_MIN_SUPPORTED_API_VERSION) { 77 compiler::VReg funcObj = pg->AllocReg(); 78 pg->GetFunctionObject(this); 79 pg->StoreAccumulator(this, funcObj); 80 pg->SuperCallForwardAllArgs(this, funcObj); 81 } else { 82 compiler::VReg argsObj = pg->AllocReg(); 83 arguments_[0]->AsSpreadElement()->Argument()->Compile(pg); 84 pg->StoreAccumulator(this, argsObj); 85 pg->GetFunctionObject(this); 86 pg->SuperCallSpread(this, argsObj); 87 } 88 } else { 89 compiler::VReg argsObj = CreateSpreadArguments(pg); 90 pg->GetFunctionObject(this); 91 pg->SuperCallSpread(this, argsObj); 92 } 93} 94 95void CallExpression::CompileSuperCall(compiler::PandaGen *pg, bool containsSpread) const 96{ 97 if (containsSpread) { 98 CompileSuperCallWithSpreadArgs(pg); 99 } else { 100 compiler::RegScope paramScope(pg); 101 compiler::VReg argStart {}; 102 103 if (arguments_.empty()) { 104 argStart = pg->AllocReg(); 105 pg->LoadConst(this, compiler::Constant::JS_UNDEFINED); 106 pg->StoreAccumulator(this, argStart); 107 } else { 108 argStart = pg->NextReg(); 109 } 110 111 for (const auto *it : arguments_) { 112 compiler::VReg arg = pg->AllocReg(); 113 it->Compile(pg); 114 pg->StoreAccumulator(it, arg); 115 } 116 117 pg->SuperCall(this, argStart, arguments_.size()); 118 } 119 120 compiler::VReg newThis = pg->AllocReg(); 121 pg->StoreAccumulator(this, newThis); 122 123 pg->GetThis(this); 124 pg->ThrowIfSuperNotCorrectCall(this, 1); 125 126 pg->LoadAccumulator(this, newThis); 127 pg->SetThis(this); 128 129 const auto *classDef = util::Helpers::GetClassDefiniton(util::Helpers::GetContainingConstructor(this)); 130 if (classDef->NeedInstanceInitializer()) { 131 auto thisReg = pg->AllocReg(); 132 pg->MoveVreg(this, thisReg, newThis); 133 134 auto [level, slot] = pg->Scope()->Find(classDef->InstanceInitializer()->Key()); 135 pg->LoadLexicalVar(this, level, slot); 136 137 pg->CallInit(this, thisReg); 138 } 139} 140 141void CallExpression::Compile(compiler::PandaGen *pg) const 142{ 143 const ir::Expression *realCallee = callee_; 144 while (realCallee->IsTSNonNullExpression() || realCallee->IsTSAsExpression() || realCallee->IsTSTypeAssertion()) { 145 if (realCallee->IsTSNonNullExpression()) { 146 realCallee = realCallee->AsTSNonNullExpression()->Expr(); 147 } else if (realCallee->IsTSAsExpression()) { 148 realCallee = realCallee->AsTSAsExpression()->Expr(); 149 } else if (realCallee->IsTSTypeAssertion()) { 150 realCallee = realCallee->AsTSTypeAssertion()->GetExpression(); 151 } 152 } 153 154 if (realCallee->IsCallExpression() || realCallee->IsNewExpression()) { 155 if (pg->TryCompileFunctionCallOrNewExpression(realCallee)) { 156 return; 157 } 158 } 159 160 compiler::RegScope rs(pg); 161 bool containsSpread = util::Helpers::ContainSpreadElement(arguments_); 162 163 if (callee_->IsSuperExpression()) { 164 CompileSuperCall(pg, containsSpread); 165 return; 166 } 167 168 compiler::VReg callee = pg->AllocReg(); 169 bool hasThis = false; 170 compiler::VReg thisReg {}; 171 172 if (realCallee->IsMemberExpression()) { 173 hasThis = true; 174 thisReg = pg->AllocReg(); 175 176 compiler::RegScope mrs(pg); 177 realCallee->AsMemberExpression()->Compile(pg, thisReg); 178 } else if (realCallee->IsChainExpression()) { 179 hasThis = realCallee->AsChainExpression()->GetExpression()->IsMemberExpression(); 180 if (hasThis) { 181 // Guaranteed by implementation in callThis, thisVReg is always the next register of callee. 182 thisVReg_ = callee + 1; 183 } 184 realCallee->AsChainExpression()->Compile(pg); 185 } else { 186 realCallee->Compile(pg); 187 } 188 189 pg->StoreAccumulator(this, callee); 190 pg->GetOptionalChain()->CheckNullish(optional_, callee); 191 192 if (containsSpread) { 193 if (!hasThis) { 194 thisReg = pg->AllocReg(); 195 pg->LoadConst(this, compiler::Constant::JS_UNDEFINED); 196 pg->StoreAccumulator(this, thisReg); 197 } 198 199 compiler::VReg argsObj = CreateSpreadArguments(pg); 200 pg->CallSpread(this, callee, thisReg, argsObj); 201 return; 202 } 203 204 for (const auto *it : arguments_) { 205 it->Compile(pg); 206 compiler::VReg arg = pg->AllocReg(); 207 pg->StoreAccumulator(it, arg); 208 } 209 210 if (hasThis) { 211 pg->CallThis(this, callee, static_cast<int64_t>(arguments_.size() + 1)); 212 return; 213 } 214 215 pg->Call(this, callee, arguments_.size()); 216} 217 218checker::Type *CallExpression::Check(checker::Checker *checker) const 219{ 220 checker::Type *calleeType = callee_->Check(checker); 221 222 // TODO(aszilagyi): handle optional chain 223 if (calleeType->IsObjectType()) { 224 checker::ObjectType *calleeObj = calleeType->AsObjectType(); 225 return checker->resolveCallOrNewExpression(calleeObj->CallSignatures(), arguments_, Start()); 226 } 227 228 checker->ThrowTypeError("This expression is not callable.", Start()); 229 return nullptr; 230} 231 232void CallExpression::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder) 233{ 234 callee_ = std::get<ir::AstNode *>(cb(callee_))->AsExpression(); 235 236 if (typeParams_) { 237 typeParams_ = std::get<ir::AstNode *>(cb(typeParams_))->AsTSTypeParameterInstantiation(); 238 } 239 240 for (auto iter = arguments_.begin(); iter != arguments_.end(); iter++) { 241 *iter = std::get<ir::AstNode *>(cb(*iter))->AsExpression(); 242 } 243} 244 245} // namespace panda::es2panda::ir 246