/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "pandagen.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace panda::es2panda::compiler { // PandaGen void PandaGen::SetFunctionKind() { int targetApiVersion = Binder()->Program()->TargetApiVersion(); if (rootNode_->IsProgram()) { funcKind_ = panda::panda_file::FunctionKind::FUNCTION; return; } auto *func = rootNode_->AsScriptFunction(); if (func->IsConcurrent()) { funcKind_ = panda::panda_file::FunctionKind::CONCURRENT_FUNCTION; return; } if (func->IsMethod()) { if (func->IsAsync()) { funcKind_ = panda::panda_file::FunctionKind::ASYNC_FUNCTION; } return; } if (func->IsAsync()) { if (func->IsGenerator()) { funcKind_ = panda::panda_file::FunctionKind::ASYNC_GENERATOR_FUNCTION; return; } if (func->IsArrow()) { funcKind_ = panda::panda_file::FunctionKind::ASYNC_NC_FUNCTION; return; } funcKind_ = panda::panda_file::FunctionKind::ASYNC_FUNCTION; if (func->IsSendable() && targetApiVersion >= util::Helpers::SENDABLE_FUNCTION_MIN_SUPPORTED_API_VERSION) { funcKind_ |= panda::panda_file::FunctionKind::SENDABLE_FUNCTION; } return; } if (func->IsGenerator()) { funcKind_ = panda::panda_file::FunctionKind::GENERATOR_FUNCTION; return; } if (func->IsArrow()) { funcKind_ = panda::panda_file::FunctionKind::NC_FUNCTION; return; } funcKind_ = panda::panda_file::FunctionKind::FUNCTION; if (func->IsSendable() && targetApiVersion >= util::Helpers::SENDABLE_FUNCTION_MIN_SUPPORTED_API_VERSION) { funcKind_ |= panda::panda_file::FunctionKind::SENDABLE_FUNCTION; } } void PandaGen::SetInSendable() { if (rootNode_->IsProgram()) { return; } auto *func = rootNode_->AsScriptFunction(); inSendable_ = func->InSendable(); } size_t PandaGen::GetExpectedPropertyCount() const { if (rootNode_->IsProgram() || !util::Helpers::IsEnableExpectedPropertyCountApiVersion(context_->Binder()->Program()->TargetApiVersion())) { return 0; } auto *func = rootNode_->AsScriptFunction(); /** * The expected property count only includes class fields and properties declared in the constructor. * Properties defined in regular methods are not counted because they are only dynamically added * to the instance after the method is called, and are not part of the instance during creation. */ if (func->IsConstructor()) { return util::Helpers::GetClassDefiniton(func)->ExpectedPropertyCount(); } if (func->IsMethod()) { return 0; } return func->ExpectedPropertyCount(); } Label *PandaGen::AllocLabel() { std::string id = std::string {Label::PREFIX} + std::to_string(labelId_++); return ra_.AllocLabel(std::move(id)); } bool PandaGen::IsDebug() const { return context_->IsDebug(); } bool PandaGen::EnableColumn() const { return context_->EnableColumn(); } bool PandaGen::isDebuggerEvaluateExpressionMode() const { return context_->isDebuggerEvaluateExpressionMode(); } std::string PandaGen::SourceFile() const { return context_->SourceFile(); } uint32_t PandaGen::ParamCount() const { if (rootNode_->IsProgram()) { return 0; } return rootNode_->AsScriptFunction()->Params().size(); } uint32_t PandaGen::FormalParametersCount() const { if (rootNode_->IsProgram()) { return 0; } ASSERT(rootNode_->IsScriptFunction()); return rootNode_->AsScriptFunction()->FormalParamsLength(); } uint32_t PandaGen::InternalParamCount() const { if (rootNode_->IsProgram() && context_->Binder()->Program()->IsCommonjs()) { return binder::Binder::CJS_MANDATORY_PARAMS_NUMBER; } return ParamCount() + binder::Binder::MANDATORY_PARAMS_NUMBER; } const util::StringView &PandaGen::InternalName() const { return topScope_->InternalName(); } const util::StringView &PandaGen::FunctionName() const { return topScope_->Name(); } binder::Binder *PandaGen::Binder() const { return context_->Binder(); } pandasm::extensions::Language PandaGen::SourceLang() const { return context_->SourceLang(); } void PandaGen::FunctionInit(CatchTable *catchTable) { if (rootNode_->IsProgram()) { if (context_->Binder()->Program()->HasTLA()) { builder_ = allocator_->New(this, catchTable); } else { builder_ = allocator_->New(this, catchTable); } return; } const ir::ScriptFunction *func = rootNode_->AsScriptFunction(); if (func->IsAsync()) { if (func->IsGenerator()) { builder_ = allocator_->New(this, catchTable); return; } builder_ = allocator_->New(this, catchTable); return; } if (func->IsGenerator()) { builder_ = allocator_->New(this, catchTable); return; } builder_ = allocator_->New(this, catchTable); } bool PandaGen::FunctionHasFinalizer() const { if (rootNode_->IsProgram()) { return context_->Binder()->Program()->HasTLA(); } const ir::ScriptFunction *func = rootNode_->AsScriptFunction(); return func->IsAsync() || func->IsGenerator(); } bool PandaGen::IsAsyncFunction() const { if (rootNode_->IsProgram() && context_->Binder()->Program()->HasTLA()) { return true; } const ir::ScriptFunction *func = rootNode_->AsScriptFunction(); return func->IsAsync() && !func->IsGenerator(); } void PandaGen::FunctionEnter() { if (rootNode_->IsProgram() && context_->Binder()->Program()->HasTLA()) { builder_->Prepare(nullptr); return; } builder_->Prepare(rootNode_->AsScriptFunction()); } void PandaGen::FunctionExit() { if (rootNode_->IsProgram() && context_->Binder()->Program()->HasTLA()) { builder_->CleanUp(nullptr); return; } builder_->CleanUp(rootNode_->AsScriptFunction()); } void PandaGen::InitializeLexEnv(const ir::AstNode *node) { FrontAllocator fa(this); if (topScope_->NeedLexEnv()) { NewLexicalEnv(node, topScope_->LexicalSlots(), topScope_); } if (topScope_->NeedSendableEnv()) { NewSendableEnv(node, topScope_->SendableSlots()); } } void PandaGen::CopyFunctionArguments(const ir::AstNode *node) { FrontAllocator fa(this); VReg targetReg = totalRegs_; for (const auto *param : topScope_->ParamScope()->Params()) { if (param->LexicalBound()) { StoreLexicalVar(node, 0, param->LexIdx(), targetReg++); continue; } MoveVreg(node, param->Vreg(), targetReg++); } } LiteralBuffer *PandaGen::NewLiteralBuffer() { LiteralBuffer *buf = allocator_->New(allocator_); CHECK_NOT_NULL(buf); return buf; } int32_t PandaGen::AddLiteralBuffer(LiteralBuffer *buf) { CHECK_NOT_NULL(buf); buffStorage_.push_back(buf); buf->SetIndex(context_->NewLiteralIndex()); return buf->Index(); } int32_t PandaGen::AddLexicalVarNamesForDebugInfo(ArenaMap> &lexicalVars) { auto *buf = NewLiteralBuffer(); buf->Add(Allocator()->New(lexicalVars.size())); for (auto &iter : lexicalVars) { // The slot is set to UINT32_MAX when the variable is a patchvar while its value is not stored in the slot // The patchvar info should not be added to the DebugInfo since its value cannot be found in slot UINT32_MAX if (iter.first != UINT32_MAX) { buf->Add(Allocator()->New(iter.second.first)); buf->Add(Allocator()->New(iter.first)); } } return AddLiteralBuffer(buf); } void PandaGen::GetFunctionObject(const ir::AstNode *node) { LoadAccFromLexEnv(node, scope_->Find(binder::Binder::MANDATORY_PARAM_FUNC)); } void PandaGen::GetNewTarget(const ir::AstNode *node) { LoadAccFromLexEnv(node, scope_->Find(binder::Binder::MANDATORY_PARAM_NEW_TARGET)); } void PandaGen::GetThis(const ir::AstNode *node) { LoadAccFromLexEnv(node, scope_->Find(binder::Binder::MANDATORY_PARAM_THIS)); } void PandaGen::SetThis(const ir::AstNode *node) { StoreAccToLexEnv(node, scope_->Find(binder::Binder::MANDATORY_PARAM_THIS), true); } void PandaGen::LoadVar(const ir::Identifier *node, const binder::ScopeFindResult &result) { auto *var = result.variable; if (!var || var->Declaration()->IsDeclare()) { TryLoadGlobalByName(node, result.name); return; } if (var->IsGlobalVariable()) { LoadGlobalVar(node, var->Name()); return; } if (var->IsModuleVariable()) { var->HasFlag(binder::VariableFlags::LOCAL_EXPORT) ? LoadLocalModuleVariable(node, var->AsModuleVariable()) : LoadExternalModuleVariable(node, var->AsModuleVariable()); if (var->Declaration()->IsLetOrConstOrClassDecl()) { ThrowUndefinedIfHole(node, var->Name()); } return; } ASSERT(var->IsLocalVariable()); if (var->Declaration()->IsLetOrConstOrClassDecl() && result.scope->IsGlobalScope()) { TryLoadGlobalByName(node, result.name); return; } LoadAccFromLexEnv(node, result); } void PandaGen::StoreVar(const ir::AstNode *node, const binder::ScopeFindResult &result, bool isDeclaration) { binder::Variable *var = result.variable; if (!var) { TryStoreGlobalByName(node, result.name); return; } if (var->IsGlobalVariable()) { StoreGlobalVar(node, var->Name()); return; } if (var->IsModuleVariable()) { if (!isDeclaration && var->Declaration()->IsConstDecl()) { ThrowConstAssignment(node, var->Name()); return; } if (!isDeclaration && (var->Declaration()->IsLetDecl() || var->Declaration()->IsClassDecl())) { RegScope rs(this); VReg valueReg = AllocReg(); StoreAccumulator(node, valueReg); LoadLocalModuleVariable(node, var->AsModuleVariable()); ThrowUndefinedIfHole(node, var->Name()); LoadAccumulator(node, valueReg); } StoreModuleVariable(node, var->AsModuleVariable()); return; } ASSERT(var->IsLocalVariable()); if (var->Declaration()->IsLetOrConstOrClassDecl() && result.scope->IsGlobalScope()) { if (!isDeclaration) { TryStoreGlobalByName(node, var->Name()); } else if (var->Declaration()->IsLetDecl() || var->Declaration()->IsClassDecl()) { StLetOrClassToGlobalRecord(node, var->Name()); } else if (var->Declaration()->IsConstDecl()) { StConstToGlobalRecord(node, var->Name()); } return; } StoreAccToLexEnv(node, result, isDeclaration); } void PandaGen::StoreAccumulator(const ir::AstNode *node, VReg vreg) { ra_.Emit(node, vreg); } void PandaGen::LoadAccFromArgs(const ir::AstNode *node) { const auto *varScope = scope_->AsVariableScope(); if (!varScope->HasFlag(binder::VariableScopeFlags::USE_ARGS)) { return; } binder::ScopeFindResult res = scope_->Find(binder::Binder::FUNCTION_ARGUMENTS); ASSERT(res.scope); GetUnmappedArgs(node); StoreAccToLexEnv(node, res, true); } void PandaGen::LoadObjProperty(const ir::AstNode *node, VReg obj, const Operand &prop) { if (std::holds_alternative(prop)) { LoadAccumulator(node, std::get(prop)); LoadObjByValue(node, obj); return; } if (std::holds_alternative(prop)) { LoadObjByIndex(node, obj, std::get(prop)); return; } ASSERT(std::holds_alternative(prop)); LoadObjByName(node, obj, std::get(prop)); } void PandaGen::StoreObjProperty(const ir::AstNode *node, VReg obj, const Operand &prop) { if (std::holds_alternative(prop)) { StoreObjByValue(node, obj, std::get(prop)); return; } if (std::holds_alternative(prop)) { StoreObjByIndex(node, obj, std::get(prop)); return; } ASSERT(std::holds_alternative(prop)); StoreObjByName(node, obj, std::get(prop)); } void PandaGen::DefineOwnProperty(const ir::AstNode *node, VReg obj, const Operand &prop) { if (std::holds_alternative(prop)) { DefineFieldByValue(node, obj, std::get(prop)); return; } if (std::holds_alternative(prop)) { DefineFieldByIndex(node, obj, std::get(prop)); return; } ASSERT(std::holds_alternative(prop)); DefineFieldByName(node, obj, std::get(prop)); } void PandaGen::DefineClassPrivateField(const ir::AstNode *node, uint32_t level, uint32_t slot, VReg obj) { ra_.Emit(node, 0, level, slot, obj); } void PandaGen::StoreOwnProperty(const ir::AstNode *node, VReg obj, const Operand &prop, bool nameSetting) { if (std::holds_alternative(prop)) { StOwnByValue(node, obj, std::get(prop), nameSetting); return; } if (std::holds_alternative(prop)) { StOwnByIndex(node, obj, std::get(prop)); return; } ASSERT(std::holds_alternative(prop)); StOwnByName(node, obj, std::get(prop), nameSetting); } constexpr size_t DEBUGGER_GET_SET_ARGS_NUM = 2; void PandaGen::LoadObjByNameViaDebugger(const ir::AstNode *node, const util::StringView &name, bool throwUndefinedIfHole) { RegScope rs(this); VReg global = AllocReg(); LoadConst(node, compiler::Constant::JS_GLOBAL); StoreAccumulator(node, global); LoadObjByName(node, global, "debuggerGetValue"); VReg debuggerGetValueReg = AllocReg(); StoreAccumulator(node, debuggerGetValueReg); VReg variableReg = AllocReg(); LoadAccumulatorString(node, name); StoreAccumulator(node, variableReg); VReg boolFlag = AllocReg(); if (throwUndefinedIfHole) { LoadConst(node, compiler::Constant::JS_TRUE); } else { LoadConst(node, compiler::Constant::JS_FALSE); } StoreAccumulator(node, boolFlag); Call(node, debuggerGetValueReg, DEBUGGER_GET_SET_ARGS_NUM); } void PandaGen::TryLoadGlobalByName(const ir::AstNode *node, const util::StringView &name) { if (isDebuggerEvaluateExpressionMode()) { LoadObjByNameViaDebugger(node, name, true); } else { ra_.Emit(node, 0, name); } strings_.insert(name); } void PandaGen::StoreObjByNameViaDebugger(const ir::AstNode *node, const util::StringView &name) { RegScope rs(this); VReg valueReg = AllocReg(); StoreAccumulator(node, valueReg); VReg global = AllocReg(); LoadConst(node, compiler::Constant::JS_GLOBAL); StoreAccumulator(node, global); LoadObjByName(node, global, "debuggerSetValue"); VReg debuggerSetValueReg = AllocReg(); StoreAccumulator(node, debuggerSetValueReg); VReg variableReg = AllocReg(); LoadAccumulatorString(node, name); StoreAccumulator(node, variableReg); MoveVreg(node, AllocReg(), valueReg); Call(node, debuggerSetValueReg, DEBUGGER_GET_SET_ARGS_NUM); } void PandaGen::TryStoreGlobalByName(const ir::AstNode *node, const util::StringView &name) { if (isDebuggerEvaluateExpressionMode()) { StoreObjByNameViaDebugger(node, name); } else { ra_.Emit(node, 0, name); } strings_.insert(name); } void PandaGen::LoadObjByName(const ir::AstNode *node, VReg obj, const util::StringView &prop) { LoadAccumulator(node, obj); // object is load to acc ra_.Emit(node, 0, prop); strings_.insert(prop); } void PandaGen::StoreObjByName(const ir::AstNode *node, VReg obj, const util::StringView &prop) { ra_.Emit(node, 0, prop, obj); strings_.insert(prop); } void PandaGen::DefineFieldByName(const ir::AstNode *node, VReg obj, const util::StringView &prop) { if (util::Helpers::IsDefaultApiVersion(Binder()->Program()->TargetApiVersion(), Binder()->Program()->GetTargetApiSubVersion())) { ra_.Emit(node, 0, prop, obj); strings_.insert(prop); return; } ra_.Emit(node, 0, prop, obj); strings_.insert(prop); } void PandaGen::LoadObjByIndex(const ir::AstNode *node, VReg obj, int64_t index) { LoadAccumulator(node, obj); // object is load to acc if (index <= util::Helpers::MAX_INT16) { ra_.Emit(node, 0, index); return; } ra_.Emit(node, index); } void PandaGen::LoadObjByValue(const ir::AstNode *node, VReg obj) { ra_.Emit(node, 0, obj); // prop is in acc } void PandaGen::StoreObjByValue(const ir::AstNode *node, VReg obj, VReg prop) { ra_.Emit(node, 0, obj, prop); } void PandaGen::StoreObjByIndex(const ir::AstNode *node, VReg obj, int64_t index) { if (index <= util::Helpers::MAX_INT16) { ra_.Emit(node, 0, obj, index); return; } ra_.Emit(node, obj, index); } void PandaGen::DefineFieldByValue(const ir::AstNode *node, VReg obj, VReg prop) { ra_.Emit(node, 0, prop, obj); } void PandaGen::DefineFieldByIndex(const ir::AstNode *node, VReg obj, int64_t index) { ra_.Emit(node, 0, index, obj); } void PandaGen::StOwnByName(const ir::AstNode *node, VReg obj, const util::StringView &prop, bool nameSetting) { nameSetting ? ra_.Emit(node, 0, prop, obj) : ra_.Emit(node, 0, prop, obj); strings_.insert(prop); } void PandaGen::StOwnByValue(const ir::AstNode *node, VReg obj, VReg prop, bool nameSetting) { nameSetting ? ra_.Emit(node, 0, obj, prop) : ra_.Emit(node, 0, obj, prop); } void PandaGen::StOwnByIndex(const ir::AstNode *node, VReg obj, int64_t index) { if (index <= util::Helpers::MAX_INT16) { ra_.Emit(node, 0, obj, index); return; } ra_.Emit(node, obj, index); } void PandaGen::DeleteObjProperty(const ir::AstNode *node, VReg obj, const Operand &prop) { if (std::holds_alternative(prop)) { LoadAccumulator(node, std::get(prop)); } else if (std::holds_alternative(prop)) { LoadAccumulatorInt(node, static_cast(std::get(prop))); } else { ASSERT(std::holds_alternative(prop)); LoadAccumulatorString(node, std::get(prop)); } ra_.Emit(node, obj); // property is load to acc } void PandaGen::LoadAccumulator(const ir::AstNode *node, VReg reg) { ra_.Emit(node, reg); } void PandaGen::LoadGlobalVar(const ir::AstNode *node, const util::StringView &name) { ra_.Emit(node, 0, name); strings_.insert(name); } void PandaGen::StoreGlobalVar(const ir::AstNode *node, const util::StringView &name) { ra_.Emit(node, 0, name); strings_.insert(name); } void PandaGen::LoadAccFromLexEnv(const ir::AstNode *node, const binder::ScopeFindResult &result) { VirtualLoadVar::Expand(this, node, result); } void PandaGen::StoreAccToLexEnv(const ir::AstNode *node, const binder::ScopeFindResult &result, bool isDeclaration) { VirtualStoreVar::Expand(this, node, result, isDeclaration); } void PandaGen::LoadAccumulatorString(const ir::AstNode *node, const util::StringView &str) { ra_.Emit(node, str); strings_.insert(str); } void PandaGen::LoadAccumulatorFloat(const ir::AstNode *node, double num) { ra_.Emit(node, num); } void PandaGen::LoadAccumulatorInt(const ir::AstNode *node, int32_t num) { ra_.Emit(node, num); } void PandaGen::LoadAccumulatorInt(const ir::AstNode *node, size_t num) { ra_.Emit(node, static_cast(num)); } void PandaGen::LoadAccumulatorBigInt(const ir::AstNode *node, const util::StringView &num) { ra_.Emit(node, num); strings_.insert(num); } void PandaGen::StoreConst(const ir::AstNode *node, VReg reg, Constant id) { LoadConst(node, id); StoreAccumulator(node, reg); } void PandaGen::LoadConst(const ir::AstNode *node, Constant id) { switch (id) { case Constant::JS_HOLE: { ra_.Emit(node); break; } case Constant::JS_NAN: { ra_.Emit(node); break; } case Constant::JS_INFINITY: { ra_.Emit(node); break; } case Constant::JS_GLOBAL: { ra_.Emit(node); break; } case Constant::JS_UNDEFINED: { ra_.Emit(node); break; } case Constant::JS_SYMBOL: { ra_.Emit(node); break; } case Constant::JS_NULL: { ra_.Emit(node); break; } case Constant::JS_TRUE: { ra_.Emit(node); break; } case Constant::JS_FALSE: { ra_.Emit(node); break; } default: { UNREACHABLE(); } } } void PandaGen::MoveVreg(const ir::AstNode *node, VReg vd, VReg vs) { ra_.Emit(node, vd, vs); } void PandaGen::SetLabel([[maybe_unused]] const ir::AstNode *node, Label *label) { ra_.AddLabel(label); } void PandaGen::Branch(const ir::AstNode *node, Label *label) { ra_.Emit(node, label); } bool PandaGen::CheckControlFlowChange() const { const auto *iter = dynamicContext_; while (iter) { if (iter->HasFinalizer()) { return true; } iter = iter->Prev(); } return false; } Label *PandaGen::ControlFlowChangeBreak(const ir::Identifier *label) { auto *iter = dynamicContext_; util::StringView labelName = label ? label->Name() : LabelTarget::BREAK_LABEL; Label *breakTarget = nullptr; while (iter) { iter->AbortContext(ControlFlowChange::BREAK, labelName); const auto &labelTargetName = iter->Target().BreakLabel(); if (iter->Target().BreakTarget()) { breakTarget = iter->Target().BreakTarget(); } if (labelTargetName == labelName) { break; } iter = iter->Prev(); } return breakTarget; } Label *PandaGen::ControlFlowChangeContinue(const ir::Identifier *label) { auto *iter = dynamicContext_; util::StringView labelName = label ? label->Name() : LabelTarget::CONTINUE_LABEL; Label *continueTarget = nullptr; while (iter) { iter->AbortContext(ControlFlowChange::CONTINUE, labelName); const auto &labelTargetName = iter->Target().ContinueLabel(); if (iter->Target().ContinueTarget()) { continueTarget = iter->Target().ContinueTarget(); } if (labelTargetName == labelName) { break; } iter = iter->Prev(); } return continueTarget; } void PandaGen::ControlFlowChangeReturn() { auto *iter = dynamicContext_; while (iter) { iter->AbortContext(ControlFlowChange::BREAK, LabelTarget::RETURN_LABEL); iter = iter->Prev(); } } void PandaGen::Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, Label *ifFalse) { switch (op) { case lexer::TokenType::PUNCTUATOR_EQUAL: { Equal(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: { NotEqual(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: { StrictEqual(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: { StrictNotEqual(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_LESS_THAN: { LessThan(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: { LessEqual(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_GREATER_THAN: { GreaterThan(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: { GreaterEqual(node, lhs); break; } default: { UNREACHABLE(); } } ra_.Emit(node, ifFalse); } void PandaGen::Unary(const ir::AstNode *node, lexer::TokenType op, VReg operand) { switch (op) { case lexer::TokenType::PUNCTUATOR_PLUS: { ToNumber(node, operand); break; } case lexer::TokenType::PUNCTUATOR_MINUS: { LoadAccumulator(node, operand); ra_.Emit(node, 0); break; } case lexer::TokenType::PUNCTUATOR_TILDE: { LoadAccumulator(node, operand); ra_.Emit(node, 0); break; } case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: { Negate(node); break; } case lexer::TokenType::PUNCTUATOR_PLUS_PLUS: { LoadAccumulator(node, operand); ra_.Emit(node, 0); break; } case lexer::TokenType::PUNCTUATOR_MINUS_MINUS: { LoadAccumulator(node, operand); ra_.Emit(node, 0); break; } case lexer::TokenType::KEYW_VOID: case lexer::TokenType::KEYW_DELETE: { LoadConst(node, Constant::JS_UNDEFINED); break; } default: { UNREACHABLE(); } } } void PandaGen::Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs) { switch (op) { case lexer::TokenType::PUNCTUATOR_EQUAL: { Equal(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: { NotEqual(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: { StrictEqual(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: { StrictNotEqual(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_LESS_THAN: { LessThan(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: { LessEqual(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_GREATER_THAN: { GreaterThan(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: { GreaterEqual(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_PLUS: case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: { ra_.Emit(node, 0, lhs); break; } case lexer::TokenType::PUNCTUATOR_MINUS: case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: { ra_.Emit(node, 0, lhs); break; } case lexer::TokenType::PUNCTUATOR_MULTIPLY: case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: { ra_.Emit(node, 0, lhs); break; } case lexer::TokenType::PUNCTUATOR_DIVIDE: case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: { ra_.Emit(node, 0, lhs); break; } case lexer::TokenType::PUNCTUATOR_MOD: case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: { ra_.Emit(node, 0, lhs); break; } case lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL: case lexer::TokenType::PUNCTUATOR_EXPONENTIATION: { ra_.Emit(node, 0, lhs); break; } case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: { ra_.Emit(node, 0, lhs); break; } case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: { ra_.Emit(node, 0, lhs); break; } case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: { ra_.Emit(node, 0, lhs); break; } case lexer::TokenType::PUNCTUATOR_BITWISE_AND: case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: { ra_.Emit(node, 0, lhs); break; } case lexer::TokenType::PUNCTUATOR_BITWISE_OR: case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: { ra_.Emit(node, 0, lhs); break; } case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: { ra_.Emit(node, 0, lhs); break; } case lexer::TokenType::KEYW_IN: { ra_.Emit(node, 0, lhs); break; } case lexer::TokenType::KEYW_INSTANCEOF: { ra_.Emit(node, 0, lhs); break; } default: { UNREACHABLE(); } } } void PandaGen::Equal(const ir::AstNode *node, VReg lhs) { ra_.Emit(node, 0, lhs); } void PandaGen::NotEqual(const ir::AstNode *node, VReg lhs) { ra_.Emit(node, 0, lhs); } void PandaGen::StrictEqual(const ir::AstNode *node, VReg lhs) { ra_.Emit(node, 0, lhs); } void PandaGen::StrictNotEqual(const ir::AstNode *node, VReg lhs) { ra_.Emit(node, 0, lhs); } void PandaGen::LessThan(const ir::AstNode *node, VReg lhs) { ra_.Emit(node, 0, lhs); } void PandaGen::LessEqual(const ir::AstNode *node, VReg lhs) { ra_.Emit(node, 0, lhs); } void PandaGen::GreaterThan(const ir::AstNode *node, VReg lhs) { ra_.Emit(node, 0, lhs); } void PandaGen::GreaterEqual(const ir::AstNode *node, VReg lhs) { ra_.Emit(node, 0, lhs); } void PandaGen::IsTrue(const ir::AstNode *node) { if (util::Helpers::IsDefaultApiVersion(Binder()->Program()->TargetApiVersion(), Binder()->Program()->GetTargetApiSubVersion())) { ra_.Emit(node); return; } ra_.Emit(node, 0); } void PandaGen::BranchIfUndefined(const ir::AstNode *node, Label *target) { RegScope rs(this); VReg tmp = AllocReg(); StoreAccumulator(node, tmp); LoadConst(node, Constant::JS_UNDEFINED); Equal(node, tmp); ra_.Emit(node, target); } void PandaGen::BranchIfStrictUndefined(const ir::AstNode *node, class Label *target) { RegScope rs(this); VReg tmp = AllocReg(); StoreAccumulator(node, tmp); LoadConst(node, Constant::JS_UNDEFINED); StrictEqual(node, tmp); ra_.Emit(node, target); } void PandaGen::BranchIfNotUndefined(const ir::AstNode *node, Label *target) { RegScope rs(this); VReg tmp = AllocReg(); StoreAccumulator(node, tmp); LoadConst(node, Constant::JS_UNDEFINED); Equal(node, tmp); ra_.Emit(node, target); } void PandaGen::BranchIfStrictNotUndefined(const ir::AstNode *node, class Label *target) { RegScope rs(this); VReg tmp = AllocReg(); StoreAccumulator(node, tmp); LoadConst(node, Constant::JS_UNDEFINED); StrictEqual(node, tmp); ra_.Emit(node, target); } void PandaGen::BranchIfTrue(const ir::AstNode *node, Label *target) { IsTrue(node); ra_.Emit(node, target); } void PandaGen::BranchIfNotTrue(const ir::AstNode *node, Label *target) { IsTrue(node); BranchIfFalse(node, target); } void PandaGen::BranchIfFalse(const ir::AstNode *node, Label *target) { if (util::Helpers::IsDefaultApiVersion(Binder()->Program()->TargetApiVersion(), Binder()->Program()->GetTargetApiSubVersion())) { ra_.Emit(node); ra_.Emit(node, target); return; } ra_.Emit(node, 0); ra_.Emit(node, target); } void PandaGen::BranchIfStrictNull(const ir::AstNode *node, class Label *target) { RegScope rs(this); VReg tmp = AllocReg(); StoreAccumulator(node, tmp); LoadConst(node, Constant::JS_NULL); ra_.Emit(node, 0, tmp); ra_.Emit(node, target); } void PandaGen::EmitThrow(const ir::AstNode *node) { ra_.Emit(node); } void PandaGen::EmitRethrow(const ir::AstNode *node) { RegScope rs(this); auto *skipThrow = AllocLabel(); auto *doThrow = AllocLabel(); VReg exception = AllocReg(); StoreAccumulator(node, exception); VReg hole = AllocReg(); StoreConst(node, hole, Constant::JS_HOLE); LoadAccumulator(node, exception); NotEqual(node, hole); ra_.Emit(node, skipThrow); SetLabel(node, doThrow); LoadAccumulator(node, exception); EmitThrow(node); SetLabel(node, skipThrow); } void PandaGen::EmitReturn(const ir::AstNode *node) { ra_.Emit(node); } void PandaGen::EmitReturnUndefined(const ir::AstNode *node) { ra_.Emit(node); } void PandaGen::ImplicitReturn(const ir::AstNode *node) { builder_->ImplicitReturn(node); } void PandaGen::DirectReturn(const ir::AstNode *node) { builder_->DirectReturn(node); } void PandaGen::ExplicitReturn(const ir::AstNode *node) { builder_->ExplicitReturn(node); } void PandaGen::ValidateClassDirectReturn(const ir::AstNode *node) { const ir::ScriptFunction *func = util::Helpers::GetContainingFunction(node); if (!func || !func->IsConstructor()) { return; } RegScope rs(this); VReg value = AllocReg(); StoreAccumulator(node, value); auto *notUndefined = AllocLabel(); auto *condEnd = AllocLabel(); BranchIfStrictNotUndefined(node, notUndefined); GetThis(func); ThrowIfSuperNotCorrectCall(func, 0); auto *iter = dynamicContext_; while (iter) { if (iter->Type() == DynamicContextType::LEX_ENV) { auto *envContext = static_cast(iter); envContext->HandleForUpdateDirectReturnContext(); } iter = iter->Prev(); } Branch(node, condEnd); SetLabel(node, notUndefined); LoadAccumulator(node, value); SetLabel(node, condEnd); } void PandaGen::EmitAwait(const ir::AstNode *node) { builder_->Await(node); } void PandaGen::CallThis(const ir::AstNode *node, VReg startReg, size_t argCount) { LoadAccumulator(node, startReg); // callee is load to acc VReg thisReg = startReg + 1; // This dependency is used in other places, do not modify. switch (argCount) { case 1: { // no args ra_.Emit(node, 0, thisReg); break; } case 2: { // 1 arg VReg arg0 = thisReg + 1; ra_.Emit(node, 0, thisReg, arg0); break; } case 3: { // 2 args VReg arg0 = thisReg + 1; VReg arg1 = arg0 + 1; ra_.Emit(node, 0, thisReg, arg0, arg1); break; } case 4: { // 3 args VReg arg0 = thisReg + 1; VReg arg1 = arg0 + 1; VReg arg2 = arg1 + 1; ra_.Emit(node, 0, thisReg, arg0, arg1, arg2); break; } default: { int64_t actualArgs = static_cast(argCount) - 1; if (actualArgs <= util::Helpers::MAX_INT8) { ra_.EmitRange(node, argCount, 0, actualArgs, thisReg); break; } ra_.EmitRange(node, argCount, actualArgs, thisReg); break; } } } void PandaGen::Call(const ir::AstNode *node, VReg startReg, size_t argCount) { LoadAccumulator(node, startReg); // callee is load to acc switch (argCount) { case 0: { // 0 args ra_.Emit(node, 0); break; } case 1: { // 1 arg VReg arg0 = startReg + 1; ra_.Emit(node, 0, arg0); break; } case 2: { // 2 args VReg arg0 = startReg + 1; VReg arg1 = arg0 + 1; ra_.Emit(node, 0, arg0, arg1); break; } case 3: { // 3 args VReg arg0 = startReg + 1; VReg arg1 = arg0 + 1; VReg arg2 = arg1 + 1; ra_.Emit(node, 0, arg0, arg1, arg2); break; } default: { VReg arg0 = startReg + 1; if (argCount <= util::Helpers::MAX_INT8) { ra_.EmitRange(node, argCount, 0, argCount, arg0); break; } ra_.EmitRange(node, argCount, argCount, arg0); break; } } } void PandaGen::SuperCall(const ir::AstNode *node, VReg startReg, size_t argCount) { if (RootNode()->AsScriptFunction()->IsArrow()) { GetFunctionObject(node); // load funcobj to acc for super call in arrow function if (argCount <= util::Helpers::MAX_INT8) { ra_.EmitRange(node, argCount, 0, static_cast(argCount), startReg); } else { ra_.EmitRange(node, argCount, static_cast(argCount), startReg); } return; } if (argCount <= util::Helpers::MAX_INT8) { // no need to load funcobj to acc for super call in other kinds of functions ra_.EmitRange(node, argCount, 0, static_cast(argCount), startReg); return; } ra_.EmitRange(node, argCount, static_cast(argCount), startReg); } void PandaGen::SuperCallSpread(const ir::AstNode *node, VReg vs) { ra_.Emit(node, 0, vs); } void PandaGen::SuperCallForwardAllArgs(const ir::AstNode *node, VReg funcObj) { ra_.Emit(node, funcObj); } void PandaGen::NotifyConcurrentResult(const ir::AstNode *node) { if (IsConcurrent()) { ra_.Emit(node); } } void PandaGen::CallInit(const ir::AstNode *node, VReg thisReg) { // callee is in acc ra_.Emit(node, 0, thisReg); } void PandaGen::NewObject(const ir::AstNode *node, VReg startReg, size_t argCount) { if (argCount <= util::Helpers::MAX_INT8) { ra_.EmitRange(node, argCount, 0, static_cast(argCount), startReg); return; } ra_.EmitRange(node, argCount, static_cast(argCount), startReg); } void PandaGen::DefineFunction(const ir::AstNode *node, const ir::ScriptFunction *realNode, const util::StringView &name) { if (realNode->IsOverload() || realNode->Declare()) { return; } auto formalParamCnt = realNode->FormalParamsLength(); if (realNode->IsMethod()) { ra_.Emit(node, 0, name, static_cast(formalParamCnt)); } else { ra_.Emit(node, 0, name, static_cast(formalParamCnt)); } strings_.insert(name); } void PandaGen::TypeOf(const ir::AstNode *node) { ra_.Emit(node, 0); } void PandaGen::CallSpread(const ir::AstNode *node, VReg func, VReg thisReg, VReg args) { LoadAccumulator(node, func); // callee is load to acc ra_.Emit(node, 0, thisReg, args); } void PandaGen::NewObjSpread(const ir::AstNode *node, VReg obj) { ra_.Emit(node, 0, obj); } void PandaGen::GetUnmappedArgs(const ir::AstNode *node) { ra_.Emit(node); } void PandaGen::Negate(const ir::AstNode *node) { auto *falseLabel = AllocLabel(); auto *endLabel = AllocLabel(); BranchIfTrue(node, falseLabel); LoadConst(node, Constant::JS_TRUE); Branch(node, endLabel); SetLabel(node, falseLabel); LoadConst(node, Constant::JS_FALSE); SetLabel(node, endLabel); } void PandaGen::ToNumber(const ir::AstNode *node, VReg arg) { LoadAccumulator(node, arg); ra_.Emit(node, 0); } void PandaGen::ToNumeric(const ir::AstNode *node, VReg arg) { LoadAccumulator(node, arg); ra_.Emit(node, 0); } void PandaGen::CreateGeneratorObj(const ir::AstNode *node, VReg funcObj) { ra_.Emit(node, funcObj); } void PandaGen::CreateAsyncGeneratorObj(const ir::AstNode *node, VReg funcObj) { ra_.Emit(node, funcObj); } void PandaGen::CreateIterResultObject(const ir::AstNode *node, VReg value, VReg done) { ra_.Emit(node, value, done); } void PandaGen::SuspendGenerator(const ir::AstNode *node, VReg genObj) { ra_.Emit(node, genObj); // iterResult is in acc } void PandaGen::GeneratorYield(const ir::AstNode *node, VReg genObj) { LoadAccumulator(node, genObj); ra_.Emit(node, static_cast(GeneratorState::SUSPENDED_YIELD)); } void PandaGen::GeneratorComplete(const ir::AstNode *node, VReg genObj) { LoadAccumulator(node, genObj); ra_.Emit(node, static_cast(GeneratorState::COMPLETED)); } void PandaGen::ResumeGenerator(const ir::AstNode *node, VReg genObj) { LoadAccumulator(node, genObj); ra_.Emit(node); } void PandaGen::GetResumeMode(const ir::AstNode *node, VReg genObj) { LoadAccumulator(node, genObj); ra_.Emit(node); } void PandaGen::AsyncFunctionEnter(const ir::AstNode *node) { ra_.Emit(node); } void PandaGen::AsyncFunctionAwait(const ir::AstNode *node, VReg asyncFuncObj) { ra_.Emit(node, asyncFuncObj); // receivedValue is in acc } void PandaGen::AsyncFunctionResolve(const ir::AstNode *node, VReg asyncFuncObj) { ra_.Emit(node, asyncFuncObj); // use retVal in acc } void PandaGen::AsyncFunctionReject(const ir::AstNode *node, VReg asyncFuncObj) { ra_.Emit(node, asyncFuncObj); // exception is in acc } void PandaGen::AsyncGeneratorResolve(const ir::AstNode *node, VReg asyncGenObj, VReg value, VReg canSuspend) { ra_.Emit(node, asyncGenObj, value, canSuspend); } void PandaGen::AsyncGeneratorReject(const ir::AstNode *node, VReg asyncGenObj) { ra_.Emit(node, asyncGenObj); // value is in acc } void PandaGen::GetTemplateObject(const ir::AstNode *node, VReg value) { LoadAccumulator(node, value); ra_.Emit(node, 0); } void PandaGen::CopyRestArgs(const ir::AstNode *node, uint32_t index) { index <= util::Helpers::MAX_INT8 ? ra_.Emit(node, index) : ra_.Emit(node, index); } void PandaGen::GetPropIterator(const ir::AstNode *node) { ra_.Emit(node); } void PandaGen::GetNextPropName(const ir::AstNode *node, VReg iter) { ra_.Emit(node, iter); } void PandaGen::CreateEmptyObject(const ir::AstNode *node) { ra_.Emit(node); } void PandaGen::CreateObjectWithBuffer(const ir::AstNode *node, uint32_t idx) { ASSERT(util::Helpers::IsInteger(idx)); std::string idxStr = std::string(context_->Binder()->Program()->RecordName()) + "_" + std::to_string(idx); util::UString litId(idxStr, allocator_); ra_.Emit(node, 0, litId.View()); } void PandaGen::SetObjectWithProto(const ir::AstNode *node, VReg proto, VReg obj) { LoadAccumulator(node, obj); ra_.Emit(node, 0, proto); } void PandaGen::CopyDataProperties(const ir::AstNode *node, VReg dst) { ra_.Emit(node, dst); // use acc as srcObj } void PandaGen::DefineGetterSetterByValue(const ir::AstNode *node, VReg obj, VReg name, VReg getter, VReg setter, bool setName) { LoadConst(node, setName ? Constant::JS_TRUE : Constant::JS_FALSE); ra_.Emit(node, obj, name, getter, setter); } void PandaGen::CreateEmptyArray(const ir::AstNode *node) { ra_.Emit(node, 0); } void PandaGen::CreateArrayWithBuffer(const ir::AstNode *node, uint32_t idx) { ASSERT(util::Helpers::IsInteger(idx)); std::string idxStr = std::string(context_->Binder()->Program()->RecordName()) + "_" + std::to_string(idx); util::UString litId(idxStr, allocator_); ra_.Emit(node, 0, litId.View()); } void PandaGen::CreateArray(const ir::AstNode *node, const ArenaVector &elements, VReg obj) { if (elements.empty()) { CreateEmptyArray(node); StoreAccumulator(node, obj); return; } auto *buf = NewLiteralBuffer(); size_t i = 0; // This loop handles constant literal data by collecting it into a literal buffer // until a non-constant element is encountered. while (i < elements.size() && util::Helpers::IsConstantExpr(elements[i])) { buf->Add(elements[i]->AsLiteral()); i++; } if (buf->IsEmpty()) { CreateEmptyArray(node); } else { uint32_t bufIdx = static_cast(AddLiteralBuffer(buf)); CreateArrayWithBuffer(node, bufIdx); } StoreAccumulator(node, obj); if (i == elements.size()) { return; } bool hasSpread = false; // This loop handles array elements until a spread element is encountered for (; i < elements.size(); i++) { const ir::Expression *elem = elements[i]; if (elem->IsOmittedExpression()) { continue; } if (elem->IsSpreadElement()) { // The next loop will handle arrays that have a spread element hasSpread = true; break; } elem->Compile(this); StOwnByIndex(elem, obj, i); } RegScope rs(this); VReg idxReg {}; if (hasSpread) { idxReg = AllocReg(); LoadAccumulatorInt(node, i); StoreAccumulator(node, idxReg); } // This loop handles arrays that contain spread elements for (; i < elements.size(); i++) { const ir::Expression *elem = elements[i]; if (elem->IsSpreadElement()) { elem->AsSpreadElement()->Argument()->Compile(this); StoreArraySpread(elem, obj, idxReg); LoadObjByName(node, obj, "length"); StoreAccumulator(elem, idxReg); continue; } if (!elem->IsOmittedExpression()) { elem->Compile(this); StOwnByValue(elem, obj, idxReg); } Unary(elem, lexer::TokenType::PUNCTUATOR_PLUS_PLUS, idxReg); StoreAccumulator(elem, idxReg); } // If the last element is omitted, we also have to update the length property if (elements.back()->IsOmittedExpression()) { // if there was a spread value then acc already contains the length if (!hasSpread) { LoadAccumulatorInt(node, i); } StOwnByName(node, obj, "length"); } LoadAccumulator(node, obj); } void PandaGen::StoreArraySpread(const ir::AstNode *node, VReg array, VReg index) { ra_.Emit(node, array, index); } void PandaGen::ThrowIfNotObject(const ir::AstNode *node, VReg obj) { ra_.Emit(node, obj); } void PandaGen::ThrowThrowNotExist(const ir::AstNode *node) { ra_.Emit(node); } void PandaGen::GetIterator(const ir::AstNode *node) { ra_.Emit(node, 0); } void PandaGen::GetAsyncIterator(const ir::AstNode *node) { ra_.Emit(node, 0); } void PandaGen::CreateObjectWithExcludedKeys(const ir::AstNode *node, VReg obj, VReg argStart, size_t argCount) { ASSERT(argStart == obj + 1); if (argCount == 0) { LoadConst(node, Constant::JS_UNDEFINED); StoreAccumulator(node, argStart); } size_t argRegCnt = (argCount == 0 ? argCount : argCount - 1); if (argRegCnt <= util::Helpers::MAX_INT8) { ra_.EmitRange(node, argCount, static_cast(argRegCnt), obj, argStart); return; } ra_.EmitRange(node, argCount, static_cast(argRegCnt), obj, argStart); } void PandaGen::ThrowObjectNonCoercible(const ir::AstNode *node) { ra_.Emit(node); } void PandaGen::CloseIterator(const ir::AstNode *node, VReg iter) { ra_.Emit(node, 0, iter); } void PandaGen::DefineClassWithBuffer(const ir::AstNode *node, const util::StringView &ctorId, int32_t litIdx, VReg base) { auto formalParamCnt = node->AsClassDefinition()->Ctor()->Function()->FormalParamsLength(); std::string idxStr = std::string(context_->Binder()->Program()->RecordName()) + "_" + std::to_string(litIdx); util::UString litId(idxStr, allocator_); ra_.Emit(node, 0, ctorId, litId.View(), static_cast(formalParamCnt), base); strings_.insert(ctorId); } void PandaGen::DefineSendableClass(const ir::AstNode *node, const util::StringView &ctorId, int32_t litIdx, VReg base) { auto formalParamCnt = node->AsClassDefinition()->Ctor()->Function()->FormalParamsLength(); std::string idxStr = std::string(context_->Binder()->Program()->RecordName()) + "_" + std::to_string(litIdx); util::UString litId(idxStr, allocator_); ra_.Emit(node, 0, ctorId, litId.View(), static_cast(formalParamCnt), base); strings_.insert(ctorId); } void PandaGen::LoadSendableClass(const ir::AstNode *node, int32_t level) { ra_.Emit(node, level); } void PandaGen::LoadLocalModuleVariable(const ir::AstNode *node, const binder::ModuleVariable *variable) { auto index = variable->Index(); if (inSendable_ && Binder()->Program()->TargetApiVersion() >= util::Helpers::SENDABLE_CLASS_USING_LOCAL_MODULE_VAR_MIN_SUPPORTED_API_VERSION) { index <= util::Helpers::MAX_INT8 ? ra_.Emit(node, index) : ra_.Emit(node, index); return; } index <= util::Helpers::MAX_INT8 ? ra_.Emit(node, index) : ra_.Emit(node, index); } void PandaGen::LoadExternalModuleVariable(const ir::AstNode *node, const binder::ModuleVariable *variable) { auto index = variable->Index(); auto targetApiVersion = Binder()->Program()->TargetApiVersion(); bool isLazy = variable->Declaration()->Node()->IsImportSpecifier() ? variable->Declaration()->Node()->AsImportSpecifier()->IsLazy() : false; if (!isLazy) { isLazy = variable->Declaration()->Node()->IsImportDefaultSpecifier() ? variable->Declaration()->Node()->AsImportDefaultSpecifier()->IsLazy() : false; } if (isLazy) { // Change the behavior of using imported object in sendable class since api12 if (inSendable_ && targetApiVersion >= util::Helpers::SENDABLE_LAZY_LOADING_MIN_SUPPORTED_API_VERSION) { index <= util::Helpers::MAX_INT8 ? ra_.Emit(node, index) : ra_.Emit(node, index); return; } index <= util::Helpers::MAX_INT8 ? ra_.Emit(node, index) : ra_.Emit(node, index); return; } // Change the behavior of using imported object in sendable class since api12 if (inSendable_ && targetApiVersion >= util::Helpers::SENDABLE_LAZY_LOADING_MIN_SUPPORTED_API_VERSION) { index <= util::Helpers::MAX_INT8 ? ra_.Emit(node, index) : ra_.Emit(node, index); return; } index <= util::Helpers::MAX_INT8 ? ra_.Emit(node, index) : ra_.Emit(node, index); } void PandaGen::StoreModuleVariable(const ir::AstNode *node, const binder::ModuleVariable *variable) { auto index = variable->Index(); index <= util::Helpers::MAX_INT8 ? ra_.Emit(node, index) : ra_.Emit(node, index); } void PandaGen::GetModuleNamespace(const ir::AstNode *node, uint32_t index) { index <= util::Helpers::MAX_INT8 ? ra_.Emit(node, index) : ra_.Emit(node, index); } void PandaGen::DynamicImportCall(const ir::AstNode *node) { ra_.Emit(node); } void PandaGen::StSuperByName(const ir::AstNode *node, VReg obj, const util::StringView &key) { ra_.Emit(node, 0, key, obj); strings_.insert(key); } void PandaGen::LdSuperByName(const ir::AstNode *node, VReg obj, const util::StringView &key) { LoadAccumulator(node, obj); // object is load to acc ra_.Emit(node, 0, key); strings_.insert(key); } void PandaGen::StSuperByValue(const ir::AstNode *node, VReg obj, VReg prop) { ra_.Emit(node, 0, obj, prop); } void PandaGen::LdSuperByValue(const ir::AstNode *node, VReg obj) { ra_.Emit(node, 0, obj); // prop is in acc } void PandaGen::StoreSuperProperty(const ir::AstNode *node, VReg obj, const Operand &prop) { if (std::holds_alternative(prop)) { StSuperByName(node, obj, std::get(prop)); return; } if (std::holds_alternative(prop)) { StSuperByValue(node, obj, std::get(prop)); return; } ASSERT(std::holds_alternative(prop)); RegScope rs(this); VReg property = AllocReg(); VReg value = AllocReg(); StoreAccumulator(node, value); LoadAccumulatorInt(node, static_cast(std::get(prop))); StoreAccumulator(node, property); LoadAccumulator(node, value); StSuperByValue(node, obj, property); } void PandaGen::LoadSuperProperty(const ir::AstNode *node, VReg obj, const Operand &prop) { if (std::holds_alternative(prop)) { LdSuperByName(node, obj, std::get(prop)); return; } if (std::holds_alternative(prop)) { LoadAccumulator(node, std::get(prop)); LdSuperByValue(node, obj); return; } ASSERT(std::holds_alternative(prop)); LoadAccumulatorInt(node, static_cast(std::get(prop))); LdSuperByValue(node, obj); } void PandaGen::LoadLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot) { if ((level > util::Helpers::MAX_INT8) || (slot > util::Helpers::MAX_INT8)) { ra_.Emit(node, level, slot); return; } ra_.Emit(node, level, slot); } void PandaGen::LoadSendableVar(const ir::AstNode *node, uint32_t level, uint32_t slot) { if ((level > util::Helpers::MAX_INT8) || (slot > util::Helpers::MAX_INT8)) { ra_.Emit(node, level, slot); return; } ra_.Emit(node, level, slot); } void PandaGen::LoadLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot, const util::StringView &name) { if (context_->PatchFixHelper() && context_->PatchFixHelper()->IsAdditionalVarInPatch(slot)) { uint32_t patchSlot = context_->PatchFixHelper()->GetPatchLexicalIdx(std::string(name)); ra_.Emit(node, patchSlot); return; } if ((level > util::Helpers::MAX_INT8) || (slot > util::Helpers::MAX_INT8)) { ra_.Emit(node, level, slot); return; } ra_.Emit(node, level, slot); } void PandaGen::StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot) { if ((level > util::Helpers::MAX_INT8) || (slot > util::Helpers::MAX_INT8)) { ra_.Emit(node, level, slot); return; } ra_.Emit(node, level, slot); } void PandaGen::StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot, VReg value) { LoadAccumulator(node, value); if ((level > util::Helpers::MAX_INT8) || (slot > util::Helpers::MAX_INT8)) { ra_.Emit(node, level, slot); return; } ra_.Emit(node, level, slot); } void PandaGen::StoreSendableVar(const ir::AstNode *node, uint32_t level, uint32_t slot) { if ((level > util::Helpers::MAX_INT8) || (slot > util::Helpers::MAX_INT8)) { ra_.Emit(node, level, slot); return; } ra_.Emit(node, level, slot); } void PandaGen::StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot, const binder::LocalVariable *local) { if (context_->PatchFixHelper() && context_->PatchFixHelper()->IsAdditionalVarInPatch(slot)) { uint32_t patchSlot = context_->PatchFixHelper()->GetPatchLexicalIdx(std::string(local->Name())); ra_.Emit(node, patchSlot); return; } RegScope rs(this); VReg value = AllocReg(); StoreAccumulator(node, value); StoreLexicalVar(node, level, slot, value); } void PandaGen::ThrowIfSuperNotCorrectCall(const ir::AstNode *node, int64_t num) { ra_.Emit(node, num); } void PandaGen::ThrowUndefinedIfHole(const ir::AstNode *node, const util::StringView &name) { ra_.Emit(node, name); strings_.insert(name); } void PandaGen::ThrowConstAssignment(const ir::AstNode *node, const util::StringView &name) { RegScope rs(this); LoadAccumulatorString(node, name); VReg nameReg = AllocReg(); StoreAccumulator(node, nameReg); ra_.Emit(node, nameReg); strings_.insert(name); } void PandaGen::PopLexEnv(const ir::AstNode *node) { ra_.Emit(node); } void PandaGen::GenDebugger(const ir::AstNode *node) { if (IsDebug()) { ra_.Emit(node); } } void PandaGen::NewLexicalEnv(const ir::AstNode *node, uint32_t num, binder::VariableScope *scope) { if (IsDebug()) { int32_t scopeInfoIdx = AddLexicalVarNamesForDebugInfo(scope->GetLexicalVarNameAndTypes()); NewLexEnvWithScopeInfo(node, num, scopeInfoIdx); return; } NewLexEnv(node, num); } void PandaGen::NewLexEnv(const ir::AstNode *node, uint32_t num) { num <= util::Helpers::MAX_INT8 ? ra_.Emit(node, num) : ra_.Emit(node, num); } void PandaGen::NewSendableEnv(const ir::AstNode * node, uint32_t num) { num <= util::Helpers::MAX_INT8 ? ra_.Emit(node, num) : ra_.Emit(node, num); } void PandaGen::NewLexEnvWithScopeInfo(const ir::AstNode *node, uint32_t num, int32_t scopeInfoIdx) { std::string idxStr = std::string(context_->Binder()->Program()->RecordName()) + "_" + std::to_string(scopeInfoIdx); util::UString litId(idxStr, allocator_); num <= util::Helpers::MAX_INT8 ? ra_.Emit(node, num, litId.View()) : ra_.Emit(node, num, litId.View()); } uint32_t PandaGen::TryDepth() const { const auto *iter = dynamicContext_; uint32_t depth = 0; while (iter) { if (iter->HasTryCatch()) { depth++; } iter = iter->Prev(); } return depth; } CatchTable *PandaGen::CreateCatchTable() { auto *catchTable = allocator_->New(this, TryDepth()); CHECK_NOT_NULL(catchTable); catchList_.push_back(catchTable); return catchTable; } void PandaGen::SortCatchTables() { std::sort(catchList_.begin(), catchList_.end(), [](const CatchTable *a, const CatchTable *b) { return b->Depth() < a->Depth(); }); } Operand PandaGen::ToNamedPropertyKey(const ir::Expression *prop, bool isComputed) { VReg res {0}; if (isComputed) { return res; } if (prop->IsIdentifier()) { return prop->AsIdentifier()->Name(); } if (prop->IsStringLiteral()) { const util::StringView &str = prop->AsStringLiteral()->Str(); // remove this when runtime handles __proto__ as property name correctly if (str.Is("__proto__")) { return res; } int64_t index = util::Helpers::GetIndex(str); if (index != util::Helpers::INVALID_INDEX) { return index; } return str; } if (prop->IsNumberLiteral()) { auto num = prop->AsNumberLiteral()->Number(); if (util::Helpers::IsIndex(num)) { return static_cast(num); } return util::Helpers::ToStringView(allocator_, num); } return res; } Operand PandaGen::ToPropertyKey(const ir::Expression *prop, bool isComputed) { Operand op = ToNamedPropertyKey(prop, isComputed); if (std::holds_alternative(op) || (std::holds_alternative(op) && (std::get(op) <= util::Helpers::MAX_INT32))) { return op; } VReg propReg = AllocReg(); /** * Store index to vreg when index > MAX_INT32 to simplify ASM interpreter If byindex-related instructions support * index > MAX_INT32, ASM interpreter will have to add a judgment whether index needs more than 32 bits which will * cause inefficiency of it since cases when index > MAX_INT32 can be quite rare **/ if (std::holds_alternative(op) && (std::get(op) > util::Helpers::MAX_INT32)) { LoadAccumulatorFloat(prop, std::get(op)); StoreAccumulator(prop, propReg); return propReg; } ASSERT(std::holds_alternative(op)); prop->Compile(this); StoreAccumulator(prop, propReg); return propReg; } VReg PandaGen::LoadPropertyKey(const ir::Expression *prop, bool isComputed) { Operand op = ToNamedPropertyKey(prop, isComputed); if (std::holds_alternative(op)) { LoadAccumulatorString(prop, std::get(op)); } else if (std::holds_alternative(op)) { LoadAccumulatorInt(prop, static_cast(std::get(op))); } else { prop->Compile(this); } VReg propReg = AllocReg(); StoreAccumulator(prop, propReg); return propReg; } void PandaGen::ToComputedPropertyKey(const ir::AstNode *node) { ra_.Emit(node); } void PandaGen::StLetOrClassToGlobalRecord(const ir::AstNode *node, const util::StringView &name) { ra_.Emit(node, 0, name); strings_.insert(name); } void PandaGen::StConstToGlobalRecord(const ir::AstNode *node, const util::StringView &name) { ra_.Emit(node, 0, name); strings_.insert(name); } bool PandaGen::TryCompileFunctionCallOrNewExpression(const ir::Expression *expr) { ASSERT(expr->IsCallExpression() || expr->IsNewExpression()); const auto *callee = expr->IsCallExpression() ? expr->AsCallExpression()->Callee() : expr->AsNewExpression()->Callee(); if (!callee->IsIdentifier()) { return false; } if (callee->AsIdentifier()->Name().Is("Function")) { auto arguments = expr->IsCallExpression() ? expr->AsCallExpression()->Arguments() : expr->AsNewExpression()->Arguments(); if (arguments.empty()) { return false; } auto *arg = arguments[arguments.size() - 1]; if (!arg->IsStringLiteral()) { return false; } if (std::regex_match(arg->AsStringLiteral()->Str().Mutf8(), std::regex(" *return +this[;]? *$"))) { LoadConst(arg, Constant::JS_GLOBAL); return true; } } return false; } void PandaGen::ReArrangeIc() { if (!IsIcOverFlow()) { return; } ResetCurrentSlot(0); for (auto *ins: Insns()) { if (!ins->InlineCacheEnabled()) { continue; } if (ins->oneByteSlotOnly()) { auto inc = ins->SetIcSlot(GetCurrentSlot()); IncreaseCurrentSlot(inc); } } for (auto *ins: Insns()) { if (!ins->InlineCacheEnabled()) { continue; } if (ins->oneByteSlotOnly()) { continue; } auto inc = ins->SetIcSlot(GetCurrentSlot()); IncreaseCurrentSlot(inc); } } void PandaGen::CreatePrivateProperty(const ir::AstNode *node, uint32_t num, int32_t bufIdx) { std::string idxStr = std::string(context_->Binder()->Program()->RecordName()) + "_" + std::to_string(bufIdx); util::UString litId(idxStr, allocator_); ra_.Emit(node, num, litId.View()); } void PandaGen::TestIn(const ir::AstNode *node, uint32_t level, uint32_t slot) { ra_.Emit(node, 0, level, slot); } void PandaGen::LoadPrivateProperty(const ir::AstNode *node, uint32_t level, uint32_t slot) { ra_.Emit(node, 0, level, slot); } void PandaGen::StorePrivateProperty(const ir::AstNode *node, uint32_t level, uint32_t slot, VReg obj) { ra_.Emit(node, 0, level, slot, obj); } void PandaGen::ThrowTypeErrorIfFalse(const ir::AstNode *node, util::StringView str) { auto *trueLabel = AllocLabel(); BranchIfTrue(node, trueLabel); ThrowTypeError(node, str); SetLabel(node, trueLabel); } void PandaGen::ThrowTypeError(const ir::AstNode *node, util::StringView str) { LoadAccumulatorString(node, str); VReg reg = AllocReg(); StoreAccumulator(node, reg); TryLoadGlobalByName(node, "TypeError"); ra_.Emit(node, 0, reg); EmitThrow(node); } } // namespace panda::es2panda::compiler