* Copyright (c) 2021-2025 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 "varbinder/varbinder.h"
#include "checker/checker.h"
#include "checker/types/globalTypesHolder.h"
#include "util/helpers.h"
#include "util/options.h"
#include "varbinder/scope.h"
#include "varbinder/variable.h"
#include "compiler/base/catchTable.h"
#include "compiler/base/lexenv.h"
#include "compiler/base/literals.h"
#include "public/public.h"
#include "compiler/core/labelTarget.h"
#include "compiler/core/regAllocator.h"
#include "compiler/function/asyncFunctionBuilder.h"
#include "compiler/function/asyncGeneratorFunctionBuilder.h"
#include "compiler/function/functionBuilder.h"
#include "compiler/function/generatorFunctionBuilder.h"
#include "es2panda.h"
#include "generated/isa.h"
#include "ir/base/scriptFunction.h"
#include "ir/base/spreadElement.h"
#include "ir/statement.h"
#include "ir/expressions/identifier.h"
#include "ir/expressions/literals/numberLiteral.h"
#include "ir/expressions/literals/stringLiteral.h"
namespace ark::es2panda::compiler {
#ifndef PANDA_WITH_ECMASCRIPT
class EcmaDisabled : public IRNode {
public:
template <typename... Args>
explicit EcmaDisabled(const ir::AstNode *node, [[maybe_unused]] Args &&...args) : IRNode(node)
{
ES2PANDA_UNREACHABLE();
}
Formats GetFormats() const override
{
ES2PANDA_UNREACHABLE();
}
size_t Registers([[maybe_unused]] std::array<VReg *, MAX_REG_OPERAND> *regs) override
{
ES2PANDA_UNREACHABLE();
}
size_t Registers([[maybe_unused]] std::array<const VReg *, MAX_REG_OPERAND> *regs) const override
{
ES2PANDA_UNREACHABLE();
}
size_t OutRegisters([[maybe_unused]] std::array<OutVReg, MAX_REG_OPERAND> *regs) const override
{
ES2PANDA_UNREACHABLE();
}
void Transform([[maybe_unused]] pandasm::Ins *ins, [[maybe_unused]] ProgramElement *programElement,
[[maybe_unused]] uint32_t totalRegs) const override
{
ES2PANDA_UNREACHABLE();
}
OperandType GetOperandRegType([[maybe_unused]] size_t idx) const override
{
return OperandType::NONE;
}
uint32_t GetRegLimit() const noexcept override
{
return 0U;
}
OperandKind GetOperandRegKind([[maybe_unused]] size_t idx) const override
{
return OperandKind::NONE;
}
};
using EcmaLdhole = EcmaDisabled;
using EcmaLdnan = EcmaDisabled;
using EcmaLdinfinity = EcmaDisabled;
using EcmaLdglobal = EcmaDisabled;
using EcmaLdundefined = EcmaDisabled;
using EcmaLdsymbol = EcmaDisabled;
using EcmaLdnull = EcmaDisabled;
using EcmaLdtrue = EcmaDisabled;
using EcmaLdfalse = EcmaDisabled;
using EcmaTryldglobalbyname = EcmaDisabled;
using EcmaTrystglobalbyname = EcmaDisabled;
using EcmaLdobjbyname = EcmaDisabled;
using EcmaStobjbyname = EcmaDisabled;
using EcmaLdobjbyindex = EcmaDisabled;
using EcmaLdobjbyvalue = EcmaDisabled;
using EcmaStobjbyvalue = EcmaDisabled;
using EcmaStobjbyindex = EcmaDisabled;
using EcmaStownbyname = EcmaDisabled;
using EcmaStownbyvalue = EcmaDisabled;
using EcmaStownbyindex = EcmaDisabled;
using EcmaDelobjprop = EcmaDisabled;
using EcmaLdglobalvar = EcmaDisabled;
using EcmaStglobalvar = EcmaDisabled;
using EcmaLdbigint = EcmaDisabled;
using EcmaEqdyn = EcmaDisabled;
using EcmaNoteqdyn = EcmaDisabled;
using EcmaStricteqdyn = EcmaDisabled;
using EcmaStrictnoteqdyn = EcmaDisabled;
using EcmaLessdyn = EcmaDisabled;
using EcmaLesseqdyn = EcmaDisabled;
using EcmaGreaterdyn = EcmaDisabled;
using EcmaGreatereqdyn = EcmaDisabled;
using EcmaTonumber = EcmaDisabled;
using EcmaNegdyn = EcmaDisabled;
using EcmaNotdyn = EcmaDisabled;
using EcmaNegate = EcmaDisabled;
using EcmaIncdyn = EcmaDisabled;
using EcmaDecdyn = EcmaDisabled;
using EcmaEqdyn = EcmaDisabled;
using EcmaNoteqdyn = EcmaDisabled;
using EcmaStricteqdyn = EcmaDisabled;
using EcmaStrictnoteqdyn = EcmaDisabled;
using EcmaLessdyn = EcmaDisabled;
using EcmaLesseqdyn = EcmaDisabled;
using EcmaGreaterdyn = EcmaDisabled;
using EcmaGreatereqdyn = EcmaDisabled;
using EcmaAdd2dyn = EcmaDisabled;
using EcmaSub2dyn = EcmaDisabled;
using EcmaMul2dyn = EcmaDisabled;
using EcmaDiv2dyn = EcmaDisabled;
using EcmaMod2dyn = EcmaDisabled;
using EcmaExpdyn = EcmaDisabled;
using EcmaShl2dyn = EcmaDisabled;
using EcmaShr2dyn = EcmaDisabled;
using EcmaAshr2dyn = EcmaDisabled;
using EcmaAnd2dyn = EcmaDisabled;
using EcmaOr2dyn = EcmaDisabled;
using EcmaXor2dyn = EcmaDisabled;
using EcmaIsindyn = EcmaDisabled;
using EcmaInstanceofdyn = EcmaDisabled;
using EcmaIsundefined = EcmaDisabled;
using EcmaIsundefined = EcmaDisabled;
using EcmaJtrue = EcmaDisabled;
using EcmaIstrue = EcmaDisabled;
using EcmaJfalse = EcmaDisabled;
using EcmaIscoercible = EcmaDisabled;
using EcmaThrowdyn = EcmaDisabled;
using EcmaRethrowdyn = EcmaDisabled;
using EcmaReturnDyn = EcmaDisabled;
using EcmaReturnundefined = EcmaDisabled;
using EcmaCall0thisdyn = EcmaDisabled;
using EcmaCall1thisdyn = EcmaDisabled;
using EcmaCall0dyn = EcmaDisabled;
using EcmaCall1thisdyn = EcmaDisabled;
using EcmaCall1dyn = EcmaDisabled;
using EcmaCall2thisdyn = EcmaDisabled;
using EcmaCall2dyn = EcmaDisabled;
using EcmaCall3thisdyn = EcmaDisabled;
using EcmaCall3dyn = EcmaDisabled;
using EcmaCallithisrangedyn = EcmaDisabled;
using EcmaCallirangedyn = EcmaDisabled;
using EcmaCall1thisdyn = EcmaDisabled;
using EcmaCall1dyn = EcmaDisabled;
using EcmaCall2thisdyn = EcmaDisabled;
using EcmaCall2dyn = EcmaDisabled;
using EcmaCall3thisdyn = EcmaDisabled;
using EcmaCall3dyn = EcmaDisabled;
using EcmaCallithisrangedyn = EcmaDisabled;
using EcmaCallirangedyn = EcmaDisabled;
using EcmaSupercall = EcmaDisabled;
using EcmaSupercallspread = EcmaDisabled;
using EcmaNewobjdynrange = EcmaDisabled;
using EcmaLdhomeobject = EcmaDisabled;
using EcmaDefinemethod = EcmaDisabled;
using EcmaDefineasyncgeneratorfunc = EcmaDisabled;
using EcmaDefineasyncfunc = EcmaDisabled;
using EcmaDefinegeneratorfunc = EcmaDisabled;
using EcmaDefinencfuncdyn = EcmaDisabled;
using EcmaDefinefuncdyn = EcmaDisabled;
using EcmaTypeofdyn = EcmaDisabled;
using EcmaCallspreaddyn = EcmaDisabled;
using EcmaNewobjspreaddyn = EcmaDisabled;
using EcmaGetunmappedargs = EcmaDisabled;
using EcmaNegate = EcmaDisabled;
using EcmaToboolean = EcmaDisabled;
using EcmaTonumber = EcmaDisabled;
using EcmaGetmethod = EcmaDisabled;
using EcmaCreategeneratorobj = EcmaDisabled;
using EcmaCreateasyncgeneratorobj = EcmaDisabled;
using EcmaCreateiterresultobj = EcmaDisabled;
using EcmaSuspendgenerator = EcmaDisabled;
using EcmaSuspendasyncgenerator = EcmaDisabled;
using EcmaSetgeneratorstate = EcmaDisabled;
using EcmaSetgeneratorstate = EcmaDisabled;
using EcmaResumegenerator = EcmaDisabled;
using EcmaGetresumemode = EcmaDisabled;
using EcmaAsyncfunctionenter = EcmaDisabled;
using EcmaAsyncfunctionawait = EcmaDisabled;
using EcmaAsyncfunctionresolve = EcmaDisabled;
using EcmaAsyncfunctionreject = EcmaDisabled;
using EcmaAsyncgeneratorresolve = EcmaDisabled;
using EcmaAsyncgeneratorreject = EcmaDisabled;
using EcmaGettemplateobject = EcmaDisabled;
using EcmaCopyrestargs = EcmaDisabled;
using EcmaGetpropiterator = EcmaDisabled;
using EcmaGetnextpropname = EcmaDisabled;
using EcmaCreateemptyobject = EcmaDisabled;
using EcmaCreateobjectwithbuffer = EcmaDisabled;
using EcmaCreateobjecthavingmethod = EcmaDisabled;
using EcmaSetobjectwithproto = EcmaDisabled;
using EcmaCopydataproperties = EcmaDisabled;
using EcmaDefinegettersetterbyvalue = EcmaDisabled;
using EcmaCreateemptyarray = EcmaDisabled;
using EcmaCreatearraywithbuffer = EcmaDisabled;
using EcmaStarrayspread = EcmaDisabled;
using EcmaCreateregexpwithliteral = EcmaDisabled;
using EcmaThrowifnotobject = EcmaDisabled;
using EcmaThrowthrownotexists = EcmaDisabled;
using EcmaGetiterator = EcmaDisabled;
using EcmaGetasynciterator = EcmaDisabled;
using EcmaCreateobjectwithexcludedkeys = EcmaDisabled;
using EcmaThrowpatternnoncoercible = EcmaDisabled;
using EcmaCloseiterator = EcmaDisabled;
using EcmaImportmodule = EcmaDisabled;
using EcmaSetclasscomputedfields = EcmaDisabled;
using EcmaDefineclasswithbuffer = EcmaDisabled;
using EcmaLoadclasscomputedinstancefields = EcmaDisabled;
using EcmaDefineclassprivatefields = EcmaDisabled;
using EcmaClassfieldadd = EcmaDisabled;
using EcmaClassprivatefieldadd = EcmaDisabled;
using EcmaClassprivatemethodoraccessoradd = EcmaDisabled;
using EcmaClassprivatefieldget = EcmaDisabled;
using EcmaClassprivatefieldset = EcmaDisabled;
using EcmaClassprivatefieldin = EcmaDisabled;
using EcmaLdmodvarbyname = EcmaDisabled;
using EcmaStmodulevar = EcmaDisabled;
using EcmaCopymodule = EcmaDisabled;
using EcmaStsuperbyname = EcmaDisabled;
using EcmaLdsuperbyname = EcmaDisabled;
using EcmaStsuperbyvalue = EcmaDisabled;
using EcmaLdsuperbyvalue = EcmaDisabled;
using EcmaLdlexvardyn = EcmaDisabled;
using EcmaLdlexdyn = EcmaDisabled;
using EcmaStlexvardyn = EcmaDisabled;
using EcmaStlexdyn = EcmaDisabled;
using EcmaThrowifsupernotcorrectcall = EcmaDisabled;
using EcmaThrowtdz = EcmaDisabled;
using EcmaThrowconstassignment = EcmaDisabled;
using EcmaPoplexenvdyn = EcmaDisabled;
using EcmaCopylexenvdyn = EcmaDisabled;
using EcmaNewlexenvdyn = EcmaDisabled;
using EcmaLdlexenvdyn = EcmaDisabled;
using EcmaLdevalvar = EcmaDisabled;
using EcmaStevalvar = EcmaDisabled;
using EcmaLdevalbindings = EcmaDisabled;
using EcmaDirecteval = EcmaDisabled;
#endif
PandaGen::PandaGen(SArenaAllocator *const allocator, RegSpiller *const spiller, public_lib::Context *const context,
std::tuple<varbinder::FunctionScope *, ProgramElement *, AstCompiler *> toCompile)
: CodeGen(allocator, spiller, context, toCompile)
{
Function::Compile(this);
}
FunctionBuilder *PandaGen::FuncBuilder() const noexcept
{
return builder_;
}
EnvScope *PandaGen::GetEnvScope() const noexcept
{
return envScope_;
}
void PandaGen::OptionalChainCheck(const bool optional, const VReg obj) const
{
if (optional && optionalChain_ != nullptr) {
optionalChain_->Check(obj);
}
}
void PandaGen::FunctionInit(CatchTable *catchTable)
{
if (RootNode()->IsProgram()) {
builder_ = Allocator()->New<FunctionBuilder>(this, catchTable);
return;
}
const ir::ScriptFunction *func = RootNode()->AsScriptFunction();
if (func->IsAsyncFunc()) {
if (func->IsGenerator()) {
builder_ = Allocator()->New<AsyncGeneratorFunctionBuilder>(this, catchTable);
return;
}
builder_ = Allocator()->New<AsyncFunctionBuilder>(this, catchTable);
return;
}
if (func->IsGenerator()) {
builder_ = Allocator()->New<GeneratorFunctionBuilder>(this, catchTable);
return;
}
builder_ = Allocator()->New<FunctionBuilder>(this, catchTable);
}
bool PandaGen::FunctionHasFinalizer() const
{
if (RootNode()->IsProgram()) {
return false;
}
const ir::ScriptFunction *func = RootNode()->AsScriptFunction();
return func->IsAsyncFunc() || func->IsGenerator();
}
void PandaGen::FunctionEnter()
{
builder_->Prepare(RootNode()->AsScriptFunction());
}
void PandaGen::FunctionExit()
{
builder_->CleanUp(RootNode()->AsScriptFunction());
}
void PandaGen::StoreAccumulator(const ir::AstNode *node, VReg vreg)
{
Ra().Emit<StaDyn>(node, vreg);
}
void PandaGen::LoadAccumulator(const ir::AstNode *node, VReg reg)
{
Ra().Emit<LdaDyn>(node, reg);
}
IRNode *PandaGen::AllocMov(const ir::AstNode *node, const VReg vd, const VReg vs)
{
return Allocator()->New<MovDyn>(node, vd, vs);
}
IRNode *PandaGen::AllocMov(const ir::AstNode *node, OutVReg vd, const VReg vs)
{
ES2PANDA_ASSERT(vd.type == OperandType::ANY);
return Allocator()->New<MovDyn>(node, *vd.reg, vs);
}
IRNode *PandaGen::AllocSpillMov(const ir::AstNode *node, VReg vd, VReg vs, [[maybe_unused]] OperandType type)
{
return Allocator()->New<MovDyn>(node, vd, vs);
}
void PandaGen::MoveVreg(const ir::AstNode *node, VReg vd, VReg vs)
{
Ra().Emit<MovDyn>(node, vd, vs);
}
void PandaGen::LoadAccumulatorDouble(const ir::AstNode *node, double num)
{
Sa().Emit<FldaiDyn>(node, num);
}
void PandaGen::LoadAccumulatorInt(const ir::AstNode *node, size_t num)
{
Sa().Emit<LdaiDyn>(node, static_cast<int64_t>(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: {
Sa().Emit<EcmaLdhole>(node);
break;
}
case Constant::JS_NAN: {
Sa().Emit<EcmaLdnan>(node);
break;
}
case Constant::JS_INFINITY: {
Sa().Emit<EcmaLdinfinity>(node);
break;
}
case Constant::JS_GLOBAL: {
Sa().Emit<EcmaLdglobal>(node);
break;
}
case Constant::JS_UNDEFINED: {
Sa().Emit<EcmaLdundefined>(node);
break;
}
case Constant::JS_SYMBOL: {
Sa().Emit<EcmaLdsymbol>(node);
break;
}
case Constant::JS_NULL: {
Sa().Emit<EcmaLdnull>(node);
break;
}
case Constant::JS_TRUE: {
Sa().Emit<EcmaLdtrue>(node);
break;
}
case Constant::JS_FALSE: {
Sa().Emit<EcmaLdfalse>(node);
break;
}
default: {
ES2PANDA_UNREACHABLE();
}
}
}
void PandaGen::GetFunctionObject(const ir::AstNode *node)
{
LoadAccFromLexEnv(node, Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_FUNC));
}
void PandaGen::GetNewTarget(const ir::AstNode *node)
{
LoadAccFromLexEnv(node, Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_NEW_TARGET));
}
void PandaGen::GetThis(const ir::AstNode *node)
{
LoadAccFromLexEnv(node, Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS));
}
void PandaGen::SetThis(const ir::AstNode *node)
{
StoreAccToLexEnv(node, Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS), true);
}
void PandaGen::LoadVar(const ir::Identifier *node, const varbinder::ConstScopeFindResult &result)
{
auto *var = result.variable;
if (var == nullptr) {
TryLoadGlobalByName(node, result.name);
return;
}
if (var->IsGlobalVariable()) {
LoadGlobalVar(node, var->Name());
return;
}
if (var->IsModuleVariable()) {
LoadModuleVariable(node, var->AsModuleVariable()->ModuleReg(), var->AsModuleVariable()->ExoticName());
return;
}
ES2PANDA_ASSERT(var->IsLocalVariable());
LoadAccFromLexEnv(node, result);
}
void PandaGen::StoreVar(const ir::AstNode *node, const varbinder::ConstScopeFindResult &result, bool isDeclaration)
{
varbinder::Variable *var = result.variable;
if (var == nullptr) {
if (IsDirectEval()) {
StoreEvalVariable(node, result.name);
} else {
TryStoreGlobalByName(node, result.name);
}
return;
}
if (var->IsGlobalVariable()) {
StoreGlobalVar(node, var->Name());
return;
}
if (var->IsModuleVariable()) {
ThrowConstAssignment(node, var->Name());
return;
}
ES2PANDA_ASSERT(var->IsLocalVariable());
StoreAccToLexEnv(node, result, isDeclaration);
}
void PandaGen::LoadAccFromArgs(const ir::AstNode *node)
{
if (!Scope()->HasFlag(varbinder::ScopeFlags::USE_ARGS)) {
return;
}
auto res = Scope()->Find(varbinder::VarBinder::FUNCTION_ARGUMENTS);
ES2PANDA_ASSERT(res.scope);
GetUnmappedArgs(node);
StoreAccToLexEnv(node, res, true);
}
void PandaGen::LoadObjProperty(const ir::AstNode *node, const Operand &prop)
{
if (std::holds_alternative<VReg>(prop)) {
LoadObjByValue(node, std::get<VReg>(prop));
return;
}
if (std::holds_alternative<int64_t>(prop)) {
LoadObjByIndex(node, std::get<int64_t>(prop));
return;
}
ES2PANDA_ASSERT(std::holds_alternative<util::StringView>(prop));
LoadObjByName(node, std::get<util::StringView>(prop));
}
void PandaGen::StoreObjProperty(const ir::AstNode *node, VReg obj, const Operand &prop)
{
if (std::holds_alternative<VReg>(prop)) {
StoreObjByValue(node, obj, std::get<VReg>(prop));
return;
}
if (std::holds_alternative<int64_t>(prop)) {
StoreObjByIndex(node, obj, std::get<int64_t>(prop));
return;
}
ES2PANDA_ASSERT(std::holds_alternative<util::StringView>(prop));
StoreObjByName(node, obj, std::get<util::StringView>(prop));
}
void PandaGen::StoreOwnProperty(const ir::AstNode *node, VReg obj, const Operand &prop)
{
if (std::holds_alternative<VReg>(prop)) {
StOwnByValue(node, obj, std::get<VReg>(prop));
return;
}
if (std::holds_alternative<int64_t>(prop)) {
StOwnByIndex(node, obj, std::get<int64_t>(prop));
return;
}
ES2PANDA_ASSERT(std::holds_alternative<util::StringView>(prop));
StOwnByName(node, obj, std::get<util::StringView>(prop));
}
void PandaGen::TryLoadGlobalByName(const ir::AstNode *node, const util::StringView &name)
{
Sa().Emit<EcmaTryldglobalbyname>(node, name);
}
void PandaGen::TryStoreGlobalByName(const ir::AstNode *node, const util::StringView &name)
{
Sa().Emit<EcmaTrystglobalbyname>(node, name);
}
void PandaGen::LoadObjByName(const ir::AstNode *node, const util::StringView &prop)
{
Ra().Emit<EcmaLdobjbyname>(node, prop);
}
void PandaGen::StoreObjByName(const ir::AstNode *node, VReg obj, const util::StringView &prop)
{
Ra().Emit<EcmaStobjbyname>(node, prop, obj);
}
void PandaGen::LoadObjByIndex(const ir::AstNode *node, int64_t index)
{
Ra().Emit<EcmaLdobjbyindex>(node, index);
}
void PandaGen::LoadObjByValue(const ir::AstNode *node, VReg obj)
{
Ra().Emit<EcmaLdobjbyvalue>(node, obj);
}
void PandaGen::StoreObjByValue(const ir::AstNode *node, VReg obj, VReg prop)
{
Ra().Emit<EcmaStobjbyvalue>(node, obj, prop);
}
void PandaGen::StoreObjByIndex(const ir::AstNode *node, VReg obj, int64_t index)
{
Ra().Emit<EcmaStobjbyindex>(node, index, obj);
}
void PandaGen::StOwnByName(const ir::AstNode *node, VReg obj, const util::StringView &prop)
{
Ra().Emit<EcmaStownbyname>(node, prop, obj);
}
void PandaGen::StOwnByValue(const ir::AstNode *node, VReg obj, VReg prop)
{
Ra().Emit<EcmaStownbyvalue>(node, obj, prop);
}
void PandaGen::StOwnByIndex(const ir::AstNode *node, VReg obj, int64_t index)
{
Ra().Emit<EcmaStownbyindex>(node, index, obj);
}
void PandaGen::DeleteObjProperty(const ir::AstNode *node, VReg obj, VReg prop)
{
Ra().Emit<EcmaDelobjprop>(node, obj, prop);
}
void PandaGen::LoadGlobalVar(const ir::AstNode *node, const util::StringView &name)
{
Sa().Emit<EcmaLdglobalvar>(node, name);
}
void PandaGen::StoreGlobalVar(const ir::AstNode *node, const util::StringView &name)
{
Sa().Emit<EcmaStglobalvar>(node, name);
}
VReg PandaGen::LexEnv() const noexcept
{
return envScope_->LexEnv();
}
void PandaGen::LoadAccFromLexEnv(const ir::AstNode *node, const varbinder::ConstScopeFindResult &result)
{
VirtualLoadVar::Expand(this, node, result);
}
void PandaGen::StoreAccToLexEnv(const ir::AstNode *node, const varbinder::ConstScopeFindResult &result,
bool isDeclaration)
{
VirtualStoreVar::Expand(this, node, result, isDeclaration);
}
void PandaGen::LoadAccumulatorBigInt(const ir::AstNode *node, const util::StringView &bigInt)
{
Sa().Emit<EcmaLdbigint>(node, bigInt);
}
void PandaGen::Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, Label *ifFalse)
{
switch (op) {
case lexer::TokenType::PUNCTUATOR_EQUAL: {
Ra().Emit<EcmaEqdyn>(node, lhs);
break;
}
case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: {
Ra().Emit<EcmaNoteqdyn>(node, lhs);
break;
}
case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: {
Ra().Emit<EcmaStricteqdyn>(node, lhs);
break;
}
case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: {
Ra().Emit<EcmaStrictnoteqdyn>(node, lhs);
break;
}
case lexer::TokenType::PUNCTUATOR_LESS_THAN: {
Ra().Emit<EcmaLessdyn>(node, lhs);
break;
}
case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: {
Ra().Emit<EcmaLesseqdyn>(node, lhs);
break;
}
case lexer::TokenType::PUNCTUATOR_GREATER_THAN: {
Ra().Emit<EcmaGreaterdyn>(node, lhs);
break;
}
case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
Ra().Emit<EcmaGreatereqdyn>(node, lhs);
break;
}
default: {
ES2PANDA_UNREACHABLE();
}
}
BranchIfFalse(node, ifFalse);
}
void PandaGen::Unary(const ir::AstNode *node, lexer::TokenType op, VReg operand)
{
switch (op) {
case lexer::TokenType::PUNCTUATOR_PLUS: {
Ra().Emit<EcmaTonumber>(node, operand);
break;
}
case lexer::TokenType::PUNCTUATOR_MINUS: {
Ra().Emit<EcmaNegdyn>(node, operand);
break;
}
case lexer::TokenType::PUNCTUATOR_TILDE: {
Ra().Emit<EcmaNotdyn>(node, operand);
break;
}
case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: {
Sa().Emit<EcmaNegate>(node);
break;
}
case lexer::TokenType::PUNCTUATOR_PLUS_PLUS: {
Ra().Emit<EcmaIncdyn>(node, operand);
break;
}
case lexer::TokenType::PUNCTUATOR_MINUS_MINUS: {
Ra().Emit<EcmaDecdyn>(node, operand);
break;
}
case lexer::TokenType::KEYW_VOID:
case lexer::TokenType::KEYW_DELETE: {
LoadConst(node, Constant::JS_UNDEFINED);
break;
}
default: {
ES2PANDA_UNREACHABLE();
}
}
}
void PandaGen::Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs)
{
switch (op) {
case lexer::TokenType::PUNCTUATOR_EQUAL:
return Ra().Emit<EcmaEqdyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_NOT_EQUAL:
return Ra().Emit<EcmaNoteqdyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL:
return Ra().Emit<EcmaStricteqdyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL:
return Ra().Emit<EcmaStrictnoteqdyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_LESS_THAN:
return Ra().Emit<EcmaLessdyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL:
return Ra().Emit<EcmaLesseqdyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_GREATER_THAN:
return Ra().Emit<EcmaGreaterdyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL:
return Ra().Emit<EcmaGreatereqdyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_PLUS:
case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL:
return Ra().Emit<EcmaAdd2dyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_MINUS:
case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL:
return Ra().Emit<EcmaSub2dyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_MULTIPLY:
case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL:
return Ra().Emit<EcmaMul2dyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_DIVIDE:
case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL:
return Ra().Emit<EcmaDiv2dyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_MOD:
case lexer::TokenType::PUNCTUATOR_MOD_EQUAL:
return Ra().Emit<EcmaMod2dyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL:
case lexer::TokenType::PUNCTUATOR_EXPONENTIATION:
return Ra().Emit<EcmaExpdyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT:
case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL:
return Ra().Emit<EcmaShl2dyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT:
case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL:
return Ra().Emit<EcmaShr2dyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT:
case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL:
return Ra().Emit<EcmaAshr2dyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_BITWISE_AND:
case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL:
return Ra().Emit<EcmaAnd2dyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_BITWISE_OR:
case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL:
return Ra().Emit<EcmaOr2dyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_BITWISE_XOR:
case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL:
return Ra().Emit<EcmaXor2dyn>(node, lhs);
case lexer::TokenType::KEYW_IN:
return Ra().Emit<EcmaIsindyn>(node, lhs);
case lexer::TokenType::KEYW_INSTANCEOF:
return Ra().Emit<EcmaInstanceofdyn>(node, lhs);
case lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING:
case lexer::TokenType::PUNCTUATOR_LOGICAL_NULLISH_EQUAL:
return Unimplemented();
default:
ES2PANDA_UNREACHABLE();
}
}
void PandaGen::BranchIfUndefined(const ir::AstNode *node, Label *target)
{
Sa().Emit<EcmaIsundefined>(node);
BranchIfTrue(node, target);
}
void PandaGen::BranchIfNotUndefined(const ir::AstNode *node, Label *target)
{
Sa().Emit<EcmaIsundefined>(node);
BranchIfFalse(node, target);
}
void PandaGen::BranchIfTrue(const ir::AstNode *node, Label *target)
{
Sa().Emit<EcmaJtrue>(node, target);
}
void PandaGen::BranchIfNotTrue(const ir::AstNode *node, Label *target)
{
Sa().Emit<EcmaIstrue>(node);
BranchIfFalse(node, target);
}
void PandaGen::BranchIfFalse(const ir::AstNode *node, Label *target)
{
Sa().Emit<EcmaJfalse>(node, target);
}
void PandaGen::BranchIfCoercible(const ir::AstNode *node, Label *target)
{
Sa().Emit<EcmaIscoercible>(node);
BranchIfTrue(node, target);
}
void PandaGen::EmitThrow(const ir::AstNode *node)
{
Sa().Emit<EcmaThrowdyn>(node);
}
void PandaGen::EmitRethrow(const ir::AstNode *node)
{
Sa().Emit<EcmaRethrowdyn>(node);
}
void PandaGen::EmitReturn(const ir::AstNode *node)
{
Sa().Emit<EcmaReturnDyn>(node);
}
void PandaGen::EmitReturnUndefined(const ir::AstNode *node)
{
Sa().Emit<EcmaReturnundefined>(node);
}
void PandaGen::ImplicitReturn(const ir::AstNode *node)
{
builder_->ImplicitReturn(node);
}
void PandaGen::DirectReturn(const ir::AstNode *node)
{
builder_->DirectReturn(node);
}
void PandaGen::ValidateClassDirectReturn(const ir::AstNode *node)
{
const ir::ScriptFunction *func = util::Helpers::GetContainingFunction(node);
if (func == nullptr || !func->IsConstructor()) {
return;
}
RegScope rs(this);
VReg value = AllocReg();
StoreAccumulator(node, value);
auto *notUndefined = AllocLabel();
auto *condEnd = AllocLabel();
BranchIfNotUndefined(node, notUndefined);
GetThis(func);
ThrowIfSuperNotCorrectCall(func, 0);
Branch(node, condEnd);
SetLabel(node, notUndefined);
LoadAccumulator(node, value);
SetLabel(node, condEnd);
}
void PandaGen::EmitAwait(const ir::AstNode *node)
{
builder_->Await(node);
}
void PandaGen::Call0This(const ir::AstNode *node, VReg callee, VReg thisReg)
{
LoadAccumulator(node, thisReg);
Ra().Emit<EcmaCall0thisdyn>(node, callee);
}
void PandaGen::Call1This(const ir::AstNode *node, VReg callee, VReg thisReg, VReg arg0)
{
LoadAccumulator(node, arg0);
Ra().Emit<EcmaCall1thisdyn>(node, callee, thisReg);
}
void PandaGen::Call0Args(const ir::AstNode *n, VReg c, VReg thisR, bool hasThis)
{
if (hasThis) {
Call0This(n, c, thisR);
} else {
Sa().Emit<EcmaCall0dyn>(n);
}
}
void PandaGen::Call1Arg(const ir::AstNode *n, VReg c, VReg thisR, const ArenaVector<ir::Expression *> &args,
bool hasThis)
{
const auto *arg0 = args[0];
arg0->Compile(this);
if (hasThis) {
Ra().Emit<EcmaCall1thisdyn>(n, c, thisR);
} else {
Ra().Emit<EcmaCall1dyn>(n, c);
}
}
void PandaGen::Call2Args(const ir::AstNode *n, VReg c, VReg thisR, const ArenaVector<ir::Expression *> &args,
bool hasThis)
{
const auto *arg0 = args[0];
arg0->Compile(this);
compiler::VReg arg0Reg = AllocReg();
StoreAccumulator(arg0, arg0Reg);
const auto *arg1 = args[1];
arg1->Compile(this);
if (hasThis) {
Ra().Emit<EcmaCall2thisdyn>(n, c, thisR, arg0Reg);
} else {
Ra().Emit<EcmaCall2dyn>(n, c, arg0Reg);
}
}
void PandaGen::Call3Args(const ir::AstNode *n, VReg c, VReg thisR, const ArenaVector<ir::Expression *> &args,
bool hasThis)
{
const auto *arg0 = args[0];
arg0->Compile(this);
compiler::VReg arg0Reg = AllocReg();
StoreAccumulator(arg0, arg0Reg);
const auto *arg1 = args[1];
arg1->Compile(this);
compiler::VReg arg1Reg = AllocReg();
StoreAccumulator(arg1, arg1Reg);
const auto *arg2 = args[2];
arg2->Compile(this);
if (hasThis) {
Ra().Emit<EcmaCall3thisdyn>(n, c, thisR, arg0Reg, arg1Reg);
} else {
Ra().Emit<EcmaCall3dyn>(n, c, arg0Reg, arg1Reg);
}
}
void PandaGen::Call(const ir::AstNode *node, VReg callee, VReg thisReg, const ArenaVector<ir::Expression *> &arguments)
{
bool hasThis = !thisReg.IsInvalid();
switch (arguments.size()) {
case 0: {
Call0Args(node, callee, thisReg, hasThis);
return;
}
case 1: {
Call1Arg(node, callee, thisReg, arguments, hasThis);
return;
}
case 2: {
Call2Args(node, callee, thisReg, arguments, hasThis);
return;
}
case 3: {
Call3Args(node, callee, thisReg, arguments, hasThis);
return;
}
default: {
break;
}
}
for (const auto *it : arguments) {
it->Compile(this);
compiler::VReg arg = AllocReg();
StoreAccumulator(it, arg);
}
if (hasThis) {
size_t argCount = arguments.size() + 1;
auto constexpr EXTRA_ARGS = 2;
Rra().Emit<EcmaCallithisrangedyn>(node, callee, argCount + EXTRA_ARGS, static_cast<int64_t>(argCount), callee);
} else {
size_t argCount = arguments.size();
Rra().Emit<EcmaCallirangedyn>(node, callee, argCount + 1, static_cast<int64_t>(argCount), callee);
}
}
bool PandaGen::CallArgsTagged(const ir::AstNode *node, VReg callee, VReg thisReg,
const ArenaVector<ir::Expression *> &arguments, bool hasThis)
{
VReg arg0Reg = AllocReg();
StoreAccumulator(node, arg0Reg);
const auto call1 = [this, hasThis, arg0Reg](const ir::AstNode *n, VReg c, VReg thisR,
const ArenaVector<ir::Expression *> &args) {
const auto *arg = args[0];
arg->Compile(this);
if (hasThis) {
Ra().Emit<EcmaCall2thisdyn>(n, c, thisR, arg0Reg);
} else {
Ra().Emit<EcmaCall2dyn>(n, c, arg0Reg);
}
};
const auto call2 = [this, hasThis, arg0Reg](const ir::AstNode *n, VReg c, VReg thisR,
const ArenaVector<ir::Expression *> &args) {
const auto *arg1 = args[0];
arg1->Compile(this);
compiler::VReg arg1Reg = AllocReg();
StoreAccumulator(arg1, arg1Reg);
const auto *arg2 = args[1];
arg2->Compile(this);
if (hasThis) {
Ra().Emit<EcmaCall3thisdyn>(n, c, thisR, arg0Reg, arg1Reg);
} else {
Ra().Emit<EcmaCall3dyn>(n, c, arg0Reg, arg1Reg);
}
};
switch (arguments.size()) {
case 1: {
call1(node, callee, thisReg, arguments);
return true;
}
case 2: {
call2(node, callee, thisReg, arguments);
return true;
}
default: {
break;
}
}
return false;
}
void PandaGen::CallTagged(const ir::AstNode *node, VReg callee, VReg thisReg,
const ArenaVector<ir::Expression *> &arguments)
{
bool hasThis = !thisReg.IsInvalid();
StoreAccumulator(node, callee);
Literals::GetTemplateObject(this, node->AsTaggedTemplateExpression());
if (arguments.empty()) {
if (hasThis) {
Ra().Emit<EcmaCall1thisdyn>(node, callee, thisReg);
} else {
Ra().Emit<EcmaCall1dyn>(node, callee);
}
return;
}
if (CallArgsTagged(node, callee, thisReg, arguments, hasThis)) {
return;
}
for (const auto *it : arguments) {
it->Compile(this);
compiler::VReg arg = AllocReg();
StoreAccumulator(it, arg);
}
if (hasThis) {
auto constexpr EXTRA_ARGS = 2;
size_t argCount = arguments.size() + EXTRA_ARGS;
Rra().Emit<EcmaCallithisrangedyn>(node, callee, argCount + EXTRA_ARGS, static_cast<int64_t>(argCount), callee);
} else {
size_t argCount = arguments.size() + 1;
Rra().Emit<EcmaCallirangedyn>(node, callee, argCount + 1, static_cast<int64_t>(argCount), callee);
}
}
void PandaGen::SuperCall(const ir::AstNode *node, VReg startReg, size_t argCount)
{
Rra().Emit<EcmaSupercall>(node, startReg, argCount + 1, static_cast<int64_t>(argCount), startReg);
}
void PandaGen::SuperCallSpread(const ir::AstNode *node, VReg vs)
{
Ra().Emit<EcmaSupercallspread>(node, vs);
}
void PandaGen::NewObject(const ir::AstNode *node, VReg startReg, size_t argCount)
{
Rra().Emit<EcmaNewobjdynrange>(node, startReg, argCount, static_cast<int64_t>(argCount), startReg);
}
void PandaGen::LoadHomeObject(const ir::AstNode *node)
{
Sa().Emit<EcmaLdhomeobject>(node);
}
void PandaGen::DefineMethod(const ir::AstNode *node, const util::StringView &name)
{
Ra().Emit<EcmaDefinemethod>(node, name, LexEnv());
}
void PandaGen::DefineFunction(const ir::AstNode *node, const ir::ScriptFunction *realNode, const util::StringView &name)
{
if (realNode->IsAsyncFunc()) {
if (realNode->IsGenerator()) {
Ra().Emit<EcmaDefineasyncgeneratorfunc>(node, name, LexEnv());
} else {
Ra().Emit<EcmaDefineasyncfunc>(node, name, LexEnv());
}
} else if (realNode->IsGenerator()) {
Ra().Emit<EcmaDefinegeneratorfunc>(node, name, LexEnv());
} else if (realNode->IsArrow()) {
LoadHomeObject(node);
Ra().Emit<EcmaDefinencfuncdyn>(node, name, LexEnv());
} else if (realNode->IsMethod()) {
DefineMethod(node, name);
} else {
Ra().Emit<EcmaDefinefuncdyn>(node, name, LexEnv());
}
}
void PandaGen::TypeOf(const ir::AstNode *node)
{
Sa().Emit<EcmaTypeofdyn>(node);
}
void PandaGen::CallSpread(const ir::AstNode *node, VReg func, VReg thisReg, VReg args)
{
Ra().Emit<EcmaCallspreaddyn>(node, func, thisReg, args);
}
void PandaGen::NewObjSpread(const ir::AstNode *node, VReg obj, VReg target)
{
Ra().Emit<EcmaNewobjspreaddyn>(node, obj, target);
}
void PandaGen::GetUnmappedArgs(const ir::AstNode *node)
{
Sa().Emit<EcmaGetunmappedargs>(node);
}
void PandaGen::Negate(const ir::AstNode *node)
{
Sa().Emit<EcmaNegate>(node);
}
void PandaGen::ToBoolean(const ir::AstNode *node)
{
Sa().Emit<EcmaToboolean>(node);
}
void PandaGen::ToNumber(const ir::AstNode *node, VReg arg)
{
Ra().Emit<EcmaTonumber>(node, arg);
}
void PandaGen::GetMethod(const ir::AstNode *node, VReg obj, const util::StringView &name)
{
Ra().Emit<EcmaGetmethod>(node, name, obj);
}
void PandaGen::CreateGeneratorObj(const ir::AstNode *node, VReg funcObj)
{
Ra().Emit<EcmaCreategeneratorobj>(node, funcObj);
}
void PandaGen::CreateAsyncGeneratorObj(const ir::AstNode *node, VReg funcObj)
{
Ra().Emit<EcmaCreateasyncgeneratorobj>(node, funcObj);
}
void PandaGen::CreateIterResultObject(const ir::AstNode *node, bool done)
{
Ra().Emit<EcmaCreateiterresultobj>(node, static_cast<int32_t>(done));
}
void PandaGen::SuspendGenerator(const ir::AstNode *node, VReg genObj)
{
Ra().Emit<EcmaSuspendgenerator>(node, genObj);
}
void PandaGen::SuspendAsyncGenerator(const ir::AstNode *node, VReg asyncGenObj)
{
Ra().Emit<EcmaSuspendasyncgenerator>(node, asyncGenObj);
}
void PandaGen::GeneratorYield(const ir::AstNode *node, VReg genObj)
{
Ra().Emit<EcmaSetgeneratorstate>(node, genObj, static_cast<int32_t>(GeneratorState::SUSPENDED_YIELD));
}
void PandaGen::GeneratorComplete(const ir::AstNode *node, VReg genObj)
{
Ra().Emit<EcmaSetgeneratorstate>(node, genObj, static_cast<int32_t>(GeneratorState::COMPLETED));
}
void PandaGen::ResumeGenerator(const ir::AstNode *node, VReg genObj)
{
Ra().Emit<EcmaResumegenerator>(node, genObj);
}
void PandaGen::GetResumeMode(const ir::AstNode *node, VReg genObj)
{
Ra().Emit<EcmaGetresumemode>(node, genObj);
}
void PandaGen::AsyncFunctionEnter(const ir::AstNode *node)
{
Sa().Emit<EcmaAsyncfunctionenter>(node);
}
void PandaGen::AsyncFunctionAwait(const ir::AstNode *node, VReg asyncFuncObj)
{
Ra().Emit<EcmaAsyncfunctionawait>(node, asyncFuncObj);
}
void PandaGen::AsyncFunctionResolve(const ir::AstNode *node, VReg asyncFuncObj)
{
Ra().Emit<EcmaAsyncfunctionresolve>(node, asyncFuncObj);
}
void PandaGen::AsyncFunctionReject(const ir::AstNode *node, VReg asyncFuncObj)
{
Ra().Emit<EcmaAsyncfunctionreject>(node, asyncFuncObj);
}
void PandaGen::AsyncGeneratorResolve(const ir::AstNode *node, VReg asyncGenObj)
{
Ra().Emit<EcmaAsyncgeneratorresolve>(node, asyncGenObj);
}
void PandaGen::AsyncGeneratorReject(const ir::AstNode *node, VReg asyncGenObj)
{
Ra().Emit<EcmaAsyncgeneratorreject>(node, asyncGenObj);
}
void PandaGen::GetTemplateObject(const ir::AstNode *node, VReg value)
{
Ra().Emit<EcmaGettemplateobject>(node, value);
}
void PandaGen::CopyRestArgs(const ir::AstNode *node, uint32_t index)
{
Sa().Emit<EcmaCopyrestargs>(node, index);
}
void PandaGen::GetPropIterator(const ir::AstNode *node)
{
Sa().Emit<EcmaGetpropiterator>(node);
}
void PandaGen::GetNextPropName(const ir::AstNode *node, VReg iter)
{
Ra().Emit<EcmaGetnextpropname>(node, iter);
}
void PandaGen::CreateEmptyObject(const ir::AstNode *node)
{
Sa().Emit<EcmaCreateemptyobject>(node);
}
void PandaGen::CreateObjectWithBuffer(const ir::AstNode *node, uint32_t idx)
{
ES2PANDA_ASSERT(util::Helpers::IsInteger<uint32_t>(idx));
Sa().Emit<EcmaCreateobjectwithbuffer>(node, util::Helpers::ToStringView(Allocator(), idx));
}
void PandaGen::CreateObjectHavingMethod(const ir::AstNode *node, uint32_t idx)
{
ES2PANDA_ASSERT(util::Helpers::IsInteger<uint32_t>(idx));
LoadAccumulator(node, LexEnv());
Sa().Emit<EcmaCreateobjecthavingmethod>(node, util::Helpers::ToStringView(Allocator(), idx));
}
void PandaGen::SetObjectWithProto(const ir::AstNode *node, VReg proto, VReg obj)
{
Ra().Emit<EcmaSetobjectwithproto>(node, proto, obj);
}
void PandaGen::CopyDataProperties(const ir::AstNode *node, VReg dst, VReg src)
{
Ra().Emit<EcmaCopydataproperties>(node, dst, src);
}
void PandaGen::DefineGetterSetterByValue(const ir::AstNode *node, std::tuple<VReg, VReg, VReg, VReg> registers,
bool setName)
{
const auto [obj, name, getter, setter] = registers;
LoadConst(node, setName ? Constant::JS_TRUE : Constant::JS_FALSE);
Ra().Emit<EcmaDefinegettersetterbyvalue>(node, obj, name, getter, setter);
}
void PandaGen::CreateEmptyArray(const ir::AstNode *node)
{
Sa().Emit<EcmaCreateemptyarray>(node);
}
void PandaGen::CreateArrayWithBuffer(const ir::AstNode *node, uint32_t idx)
{
ES2PANDA_ASSERT(util::Helpers::IsInteger<uint32_t>(idx));
Sa().Emit<EcmaCreatearraywithbuffer>(node, util::Helpers::ToStringView(Allocator(), idx));
}
size_t PandaGen::HandleArrayLiterals(const ir::AstNode *node, const ArenaVector<ir::Expression *> &elements)
{
LiteralBuffer buf {};
size_t i = 0;
while (i < elements.size()) {
Literal lit = util::Helpers::ToConstantLiteral(elements[i]);
if (lit.IsInvalid()) {
break;
}
buf.emplace_back(std::move(lit));
i++;
}
if (buf.empty()) {
CreateEmptyArray(node);
} else {
uint32_t bufIdx = AddLiteralBuffer(std::move(buf));
CreateArrayWithBuffer(node, bufIdx);
}
return i;
}
void PandaGen::HandleArraySpread(const ir::AstNode *node, const ArenaVector<ir::Expression *> &elements, VReg obj)
{
size_t i = 0;
bool hasSpread = false;
for (; i < elements.size(); i++) {
const ir::Expression *elem = elements[i];
if (elem->IsOmittedExpression()) {
continue;
}
if (elem->IsSpreadElement()) {
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);
}
for (; i < elements.size(); i++) {
const ir::Expression *elem = elements[i];
if (elem->IsSpreadElement()) {
elem->AsSpreadElement()->Argument()->Compile(this);
StoreArraySpread(elem, obj, idxReg);
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 (elements.back()->IsOmittedExpression()) {
if (!hasSpread) {
LoadAccumulatorInt(node, i);
}
StOwnByName(node, obj, "length");
}
}
void PandaGen::CreateArray(const ir::AstNode *node, const ArenaVector<ir::Expression *> &elements, VReg obj)
{
if (elements.empty()) {
CreateEmptyArray(node);
StoreAccumulator(node, obj);
return;
}
const auto i = HandleArrayLiterals(node, elements);
StoreAccumulator(node, obj);
if (i == elements.size()) {
return;
}
HandleArraySpread(node, elements, obj);
LoadAccumulator(node, obj);
}
void PandaGen::StoreArraySpread(const ir::AstNode *node, VReg array, VReg index)
{
Ra().Emit<EcmaStarrayspread>(node, array, index);
}
void PandaGen::CreateRegExpWithLiteral(const ir::AstNode *node, const util::StringView &pattern, uint8_t flags)
{
Sa().Emit<EcmaCreateregexpwithliteral>(node, pattern, flags);
}
void PandaGen::ThrowIfNotObject(const ir::AstNode *node)
{
Ra().Emit<EcmaThrowifnotobject>(node);
}
void PandaGen::ThrowThrowNotExist(const ir::AstNode *node)
{
Sa().Emit<EcmaThrowthrownotexists>(node);
}
void PandaGen::GetIterator(const ir::AstNode *node)
{
Sa().Emit<EcmaGetiterator>(node);
}
void PandaGen::GetAsyncIterator(const ir::AstNode *node)
{
Sa().Emit<EcmaGetasynciterator>(node);
}
void PandaGen::CreateObjectWithExcludedKeys(const ir::AstNode *node, VReg obj, VReg argStart, size_t argCount)
{
ES2PANDA_ASSERT(argStart.GetIndex() == obj.GetIndex() - 1);
if (argCount == 0) {
argStart = obj;
}
Rra().Emit<EcmaCreateobjectwithexcludedkeys>(node, argStart, argCount + 1, static_cast<int64_t>(argCount), obj,
argStart);
}
void PandaGen::ThrowObjectNonCoercible(const ir::AstNode *node)
{
Sa().Emit<EcmaThrowpatternnoncoercible>(node);
}
void PandaGen::CloseIterator(const ir::AstNode *node, VReg iter)
{
Ra().Emit<EcmaCloseiterator>(node, iter);
}
void PandaGen::ImportModule(const ir::AstNode *node, const util::StringView &name)
{
Sa().Emit<EcmaImportmodule>(node, name);
}
void PandaGen::SetClassComputedFields(const ir::AstNode *node, VReg classReg, VReg computedInstanceFieldArray)
{
Ra().Emit<EcmaSetclasscomputedfields>(node, classReg, computedInstanceFieldArray);
}
void PandaGen::DefineClassWithBuffer(const ir::AstNode *node, const util::StringView &ctorId, uint32_t litIdx,
VReg lexenv, VReg base)
{
Ra().Emit<EcmaDefineclasswithbuffer>(node, ctorId, litIdx, lexenv, base);
}
void PandaGen::LoadClassComputedInstanceFields(const ir::AstNode *node, VReg ctor)
{
Ra().Emit<EcmaLoadclasscomputedinstancefields>(node, ctor);
}
void PandaGen::DefineClassPrivateFields(const ir::AstNode *node, uint32_t privateBufIdx)
{
Ra().Emit<EcmaDefineclassprivatefields>(node, util::Helpers::ToStringView(Allocator(), privateBufIdx), LexEnv());
}
void PandaGen::ClassFieldAdd(const ir::AstNode *node, VReg obj, VReg prop)
{
Ra().Emit<EcmaClassfieldadd>(node, obj, prop);
}
void PandaGen::ClassPrivateFieldAdd(const ir::AstNode *node, VReg ctor, VReg obj, const util::StringView &prop)
{
Ra().Emit<EcmaClassprivatefieldadd>(node, prop, ctor, obj);
}
void PandaGen::ClassPrivateMethodOrAccessorAdd(const ir::AstNode *node, VReg ctor, VReg obj)
{
Ra().Emit<EcmaClassprivatemethodoraccessoradd>(node, ctor, obj);
}
void PandaGen::ClassPrivateFieldGet(const ir::AstNode *node, VReg ctor, VReg obj, const util::StringView &prop)
{
Ra().Emit<EcmaClassprivatefieldget>(node, prop, ctor, obj);
}
void PandaGen::ClassPrivateFieldSet(const ir::AstNode *node, VReg ctor, VReg obj, const util::StringView &prop)
{
Ra().Emit<EcmaClassprivatefieldset>(node, prop, ctor, obj);
}
void PandaGen::ClassPrivateFieldIn(const ir::AstNode *node, VReg ctor, const util::StringView &prop)
{
Ra().Emit<EcmaClassprivatefieldin>(node, prop, ctor);
}
void PandaGen::LoadModuleVariable(const ir::AstNode *node, VReg module, const util::StringView &name)
{
Ra().Emit<EcmaLdmodvarbyname>(node, name, module);
}
void PandaGen::StoreModuleVar(const ir::AstNode *node, const util::StringView &name)
{
Sa().Emit<EcmaStmodulevar>(node, name);
}
void PandaGen::CopyModule(const ir::AstNode *node, VReg module)
{
Ra().Emit<EcmaCopymodule>(node, module);
}
void PandaGen::StSuperByName(const ir::AstNode *node, VReg obj, const util::StringView &key)
{
Ra().Emit<EcmaStsuperbyname>(node, key, obj);
}
void PandaGen::LdSuperByName(const ir::AstNode *node, const util::StringView &key)
{
Ra().Emit<EcmaLdsuperbyname>(node, key);
}
void PandaGen::StSuperByValue(const ir::AstNode *node, VReg obj, VReg prop)
{
Ra().Emit<EcmaStsuperbyvalue>(node, obj, prop);
}
void PandaGen::LdSuperByValue(const ir::AstNode *node, VReg obj)
{
Ra().Emit<EcmaLdsuperbyvalue>(node, obj);
}
void PandaGen::StoreSuperProperty(const ir::AstNode *node, VReg obj, const Operand &prop)
{
if (std::holds_alternative<util::StringView>(prop)) {
StSuperByName(node, obj, std::get<util::StringView>(prop));
return;
}
ES2PANDA_ASSERT(std::holds_alternative<VReg>(prop));
StSuperByValue(node, obj, std::get<VReg>(prop));
}
void PandaGen::LoadSuperProperty(const ir::AstNode *node, const Operand &prop)
{
if (std::holds_alternative<util::StringView>(prop)) {
LdSuperByName(node, std::get<util::StringView>(prop));
return;
}
ES2PANDA_ASSERT(std::holds_alternative<VReg>(prop));
LdSuperByValue(node, std::get<VReg>(prop));
}
void PandaGen::LoadLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot)
{
Sa().Emit<EcmaLdlexvardyn>(node, level, slot);
}
void PandaGen::LoadLexical(const ir::AstNode *node, const util::StringView &name, uint32_t level, uint32_t slot)
{
Sa().Emit<EcmaLdlexdyn>(node, name, level, slot);
}
void PandaGen::StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot)
{
Ra().Emit<EcmaStlexvardyn>(node, level, slot);
}
void PandaGen::StoreLexical(const ir::AstNode *node, const util::StringView &name, uint32_t level, uint32_t slot)
{
Ra().Emit<EcmaStlexdyn>(node, name, level, slot);
}
void PandaGen::ThrowIfSuperNotCorrectCall(const ir::AstNode *node, int64_t num)
{
Sa().Emit<EcmaThrowifsupernotcorrectcall>(node, num);
}
void PandaGen::ThrowTdz(const ir::AstNode *node, const util::StringView &name)
{
Sa().Emit<EcmaThrowtdz>(node, name);
}
void PandaGen::ThrowConstAssignment(const ir::AstNode *node, const util::StringView &name)
{
Ra().Emit<EcmaThrowconstassignment>(node, name);
}
void PandaGen::PopLexEnv(const ir::AstNode *node)
{
Sa().Emit<EcmaPoplexenvdyn>(node);
}
void PandaGen::CopyLexEnv(const ir::AstNode *node)
{
Sa().Emit<EcmaCopylexenvdyn>(node);
}
void PandaGen::NewLexEnv(const ir::AstNode *node, uint32_t num)
{
Sa().Emit<EcmaNewlexenvdyn>(node, num);
}
void PandaGen::LdLexEnv(const ir::AstNode *node)
{
Sa().Emit<EcmaLdlexenvdyn>(node);
}
Operand PandaGen::ToNamedPropertyKey(const ir::Expression *prop, bool isComputed)
{
VReg res {VReg::REG_START};
if (!isComputed) {
if (prop->IsIdentifier()) {
return prop->AsIdentifier()->Name();
}
return res;
}
if (prop->IsStringLiteral()) {
const util::StringView &str = prop->AsStringLiteral()->Str();
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().GetDouble();
if (util::Helpers::IsIndex(num)) {
return static_cast<int64_t>(num);
}
return prop->AsNumberLiteral()->Str();
}
return res;
}
Operand PandaGen::ToPropertyKey(const ir::Expression *prop, bool isComputed, bool isSuperExpression)
{
Operand op = ToNamedPropertyKey(prop, isComputed);
if (std::holds_alternative<util::StringView>(op)) {
return op;
}
if (std::holds_alternative<int64_t>(op) && !isSuperExpression) {
return op;
}
VReg objReg = AllocReg();
StoreAccumulator(prop, objReg);
prop->Compile(this);
return objReg;
}
Operand PandaGen::ToOwnPropertyKey(const ir::Expression *prop, bool isComputed)
{
Operand op = ToNamedPropertyKey(prop, isComputed);
if (std::holds_alternative<util::StringView>(op)) {
ES2PANDA_ASSERT(std::holds_alternative<util::StringView>(op) || std::holds_alternative<int64_t>(op));
return op;
}
if (std::holds_alternative<int64_t>(op)) {
return op;
}
VReg propReg = AllocReg();
prop->Compile(this);
StoreAccumulator(prop, propReg);
return propReg;
}
void PandaGen::LoadPropertyKeyAcc(const ir::Expression *prop, bool isComputed)
{
Operand op = ToNamedPropertyKey(prop, isComputed);
if (std::holds_alternative<util::StringView>(op)) {
LoadAccumulatorString(prop, std::get<util::StringView>(op));
} else if (std::holds_alternative<int64_t>(op)) {
LoadAccumulatorInt(prop, static_cast<size_t>(std::get<int64_t>(op)));
} else {
prop->Compile(this);
}
}
VReg PandaGen::LoadPropertyKey(const ir::Expression *prop, bool isComputed)
{
LoadPropertyKeyAcc(prop, isComputed);
VReg propReg = AllocReg();
StoreAccumulator(prop, propReg);
return propReg;
}
void PandaGen::LoadEvalVariable(const ir::AstNode *node, const util::StringView &name)
{
RegScope rs(this);
LoadLexicalContext(node);
Ra().Emit<EcmaLdevalvar>(node, name);
}
void PandaGen::StoreEvalVariable(const ir::AstNode *node, const util::StringView &name)
{
RegScope rs(this);
VReg value = AllocReg();
StoreAccumulator(node, value);
LoadLexicalContext(node);
Ra().Emit<EcmaStevalvar>(node, name, value);
}
void PandaGen::DirectEval(const ir::AstNode *node, uint32_t parserStatus)
{
RegScope rs(this);
uint32_t index = 0;
uint32_t evalBindingsIndex = 0;
VReg arg0 = AllocReg();
StoreAccumulator(node, arg0);
VReg bindings = AllocReg();
CreateEmptyArray(node);
StoreAccumulator(node, bindings);
GetFunctionObject(node);
StOwnByIndex(node, bindings, index++);
GetNewTarget(node);
StOwnByIndex(node, bindings, index++);
GetThis(node);
StOwnByIndex(node, bindings, index++);
VReg evalBindings = AllocReg();
CreateEmptyArray(node);
StoreAccumulator(node, evalBindings);
LoadAccumulator(node, LexEnv());
StOwnByIndex(node, evalBindings, evalBindingsIndex++);
const auto *iter = Scope()->EnclosingVariableScope();
while (true) {
ES2PANDA_ASSERT(iter != nullptr);
uint32_t scopeBindingsBuf = iter->EvalBindings();
if (scopeBindingsBuf != INVALID_LITERAL_BUFFER_ID) {
Sa().Emit<EcmaLdevalbindings>(node, util::Helpers::ToStringView(Allocator(), scopeBindingsBuf));
StOwnByIndex(node, evalBindings, evalBindingsIndex++);
}
if (iter->Parent() == nullptr) {
break;
}
iter = iter->Parent()->EnclosingVariableScope();
}
LoadAccumulator(node, evalBindings);
StOwnByIndex(node, bindings, index++);
Ra().Emit<EcmaDirecteval>(node, static_cast<int64_t>(parserStatus), arg0, bindings);
}
void PandaGen::LoadLexicalContext(const ir::AstNode *node)
{
auto result = Scope()->Find(varbinder::VarBinder::LEXICAL_CONTEXT_PARAM);
LoadLexicalVar(node, result.lexLevel, result.variable->AsLocalVariable()->LexIdx());
}
bool PandaGen::IsDirectEval() const
{
return Context()->config->options->IsDirectEval();
}
bool PandaGen::IsEval() const
{
return Context()->config->options->IsEval();
}
const checker::Type *PandaGen::GetVRegType(VReg vreg) const
{
if (vreg.GetIndex() > NextReg().GetIndex()) {
return Context()->GetChecker()->GetGlobalTypesHolder()->GlobalAnyType();
}
return nullptr;
}
}