diff --git a/es2panda/BUILD.gn b/es2panda/BUILD.gn index 82a0d44c8f9595342c74c84c01e8e28a05da4276..748ea2045c031365b6b438468d154710202c7e08 100644 --- a/es2panda/BUILD.gn +++ b/es2panda/BUILD.gn @@ -33,12 +33,12 @@ es2panda_src = [ "compiler/core/compilerContext.cpp", "compiler/core/compilerImpl.cpp", "compiler/core/dynamicContext.cpp", - "compiler/core/emitter.cpp", + "compiler/core/emitter/emitter.cpp", + "compiler/core/emitter/moduleRecordEmitter.cpp", "compiler/core/envScope.cpp", "compiler/core/function.cpp", "compiler/core/inlineCache.cpp", "compiler/core/labelTarget.cpp", - "compiler/core/moduleContext.cpp", "compiler/core/pandagen.cpp", "compiler/core/regAllocator.cpp", "compiler/core/regScope.cpp", @@ -186,6 +186,7 @@ es2panda_src = [ "lexer/token/token.cpp", "parser/context/parserContext.cpp", "parser/expressionParser.cpp", + "parser/module/sourceTextModuleRecord.cpp", "parser/parserImpl.cpp", "parser/program/program.cpp", "parser/statementParser.cpp", @@ -380,7 +381,10 @@ ohos_static_library("es2panda_lib") { "//third_party/icu/icu4c:static_icuuc", ] - cflags = [ "-Wno-implicit-fallthrough" ] + cflags = [ + "-Wno-c++20-designator", + "-Wno-implicit-fallthrough", + ] } ohos_executable("es2panda") { diff --git a/es2panda/binder/binder.cpp b/es2panda/binder/binder.cpp index 09b100fa66261288b8e02b52809086eb1be1e780..905a417b917804d5cbf6ea57e6d4e7f0656b5aab 100644 --- a/es2panda/binder/binder.cpp +++ b/es2panda/binder/binder.cpp @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include #include @@ -75,6 +77,16 @@ void Binder::ThrowRedeclaration(const lexer::SourcePosition &pos, const util::St throw Error(ErrorType::SYNTAX, ss.str(), loc.line, loc.col); } +void Binder::ThrowUndeclaredExport(const lexer::SourcePosition &pos, const util::StringView &name) +{ + lexer::LineIndex index(program_->SourceCode()); + lexer::SourceLocation loc = index.GetLocation(pos); + + std::stringstream ss; + ss << "Export name '" << name << "' is not defined."; + throw Error(ErrorType::SYNTAX, ss.str(), loc.line, loc.col); +} + void Binder::IdentifierAnalysis() { ASSERT(program_->Ast()); @@ -85,6 +97,22 @@ void Binder::IdentifierAnalysis() AddMandatoryParams(); } +void Binder::ValidateExportDecl(const ir::ExportNamedDeclaration *exportDecl) +{ + if (exportDecl->Source() != nullptr || exportDecl->Decl() != nullptr) { + return; + } + + ASSERT(topScope_->IsModuleScope()); + for (auto *it : exportDecl->Specifiers()) { + auto localName = it->AsExportSpecifier()->Local()->Name(); + if (topScope_->FindLocal(localName) == nullptr) { + ThrowUndeclaredExport(it->AsExportSpecifier()->Local()->Start(), localName); + } + topScope_->AsModuleScope()->ConvertLocalVariableToModuleVariable(Allocator(), localName); + } +} + void Binder::LookupReference(const util::StringView &name) { ScopeFindResult res = scope_->Find(name); @@ -151,7 +179,9 @@ void Binder::LookupIdentReference(ir::Identifier *ident) return; } - if (res.variable->Declaration()->IsLetOrConstOrClassDecl() && !res.variable->HasFlag(VariableFlags::INITIALIZED)) { + auto decl = res.variable->Declaration(); + if (decl->IsLetOrConstOrClassDecl() && !decl->HasFlag(DeclarationFlags::NAMESPACE_IMPORT) && + !res.variable->HasFlag(VariableFlags::INITIALIZED)) { ident->SetTdz(); } @@ -252,7 +282,9 @@ void Binder::BuildVarDeclarator(ir::VariableDeclarator *varDecl) void Binder::BuildClassDefinition(ir::ClassDefinition *classDef) { if (classDef->Parent()->IsClassDeclaration()) { - ScopeFindResult res = scope_->Find(classDef->Ident()->Name()); + util::StringView className = classDef->GetName(); + ASSERT(!className.Empty()); + ScopeFindResult res = scope_->Find(className); ASSERT(res.variable && res.variable->Declaration()->IsClassDecl()); res.variable->AddFlag(VariableFlags::INITIALIZED); @@ -439,6 +471,12 @@ void Binder::ResolveReference(const ir::AstNode *parent, ir::AstNode *childNode) BuildCatchClause(childNode->AsCatchClause()); break; } + case ir::AstNodeType::EXPORT_NAMED_DECLARATION: { + ValidateExportDecl(childNode->AsExportNamedDeclaration()); + + ResolveReferences(childNode); + break; + } default: { ResolveReferences(childNode); break; diff --git a/es2panda/binder/binder.h b/es2panda/binder/binder.h index 90f06aebf7b561d4a2e79950e73bcbfce5f8c20d..7205fe7d1837a670bb231bf7312c523519573bdf 100644 --- a/es2panda/binder/binder.h +++ b/es2panda/binder/binder.h @@ -28,6 +28,7 @@ class BlockStatement; class CatchClause; class ClassDefinition; class Expression; +class ExportNamedDeclaration; class ForUpdateStatement; class Identifier; class ScriptFunction; @@ -57,6 +58,9 @@ public: template T *AddDecl(const lexer::SourcePosition &pos, Args &&... args); + template + T *AddDecl(const lexer::SourcePosition &pos, DeclarationFlags flag, Args &&... args); + template T *AddTsDecl(const lexer::SourcePosition &pos, Args &&... args); @@ -67,12 +71,13 @@ public: return scope_; } - GlobalScope *TopScope() const + FunctionScope *TopScope() const { return topScope_; } [[noreturn]] void ThrowRedeclaration(const lexer::SourcePosition &pos, const util::StringView &name); + [[noreturn]] void ThrowUndeclaredExport(const lexer::SourcePosition &pos, const util::StringView &name); template friend class LexicalScope; @@ -141,9 +146,10 @@ private: void LookupIdentReference(ir::Identifier *ident); void ResolveReference(const ir::AstNode *parent, ir::AstNode *childNode); void ResolveReferences(const ir::AstNode *parent); + void ValidateExportDecl(const ir::ExportNamedDeclaration *exportDecl); parser::Program *program_ {}; - GlobalScope *topScope_ {}; + FunctionScope *topScope_ {}; Scope *scope_ {}; ArenaVector functionScopes_; ArenaSet functionNames_; @@ -215,6 +221,19 @@ T *Binder::AddDecl(const lexer::SourcePosition &pos, Args &&... args) ThrowRedeclaration(pos, decl->Name()); } + +template +T *Binder::AddDecl(const lexer::SourcePosition &pos, DeclarationFlags flag, Args &&... args) +{ + T *decl = Allocator()->New(std::forward(args)...); + decl->AddFlag(flag); + + if (scope_->AddDecl(Allocator(), decl, program_->Extension())) { + return decl; + } + + ThrowRedeclaration(pos, decl->Name()); +} } // namespace panda::es2panda::binder #endif diff --git a/es2panda/binder/declaration.h b/es2panda/binder/declaration.h index 1e83a306e81acefd442f5954be7f244a77dd040d..372d697569b70346cc216a4d8330d7398c17ff81 100644 --- a/es2panda/binder/declaration.h +++ b/es2panda/binder/declaration.h @@ -82,10 +82,31 @@ public: return IsLetDecl() || IsConstDecl() || IsClassDecl(); } + DeclarationFlags Flags() const + { + return flags_; + } + + void AddFlag(DeclarationFlags flag) + { + flags_ |= flag; + } + + bool HasFlag(DeclarationFlags flag) const + { + return (flags_ & flag) != 0; + } + + bool IsImportOrExportDecl() const + { + return HasFlag(DeclarationFlags::IMPORT | DeclarationFlags::EXPORT); + } + protected: explicit Decl(util::StringView name) : name_(name) {} util::StringView name_; + DeclarationFlags flags_ {}; const ir::AstNode *node_ {}; }; @@ -254,70 +275,6 @@ public: } }; -class ImportDecl : public Decl { -public: - explicit ImportDecl(util::StringView importName, util::StringView localName) - : Decl(localName), importName_(importName) - { - } - - explicit ImportDecl(util::StringView importName, util::StringView localName, const ir::AstNode *node) - : Decl(localName), importName_(importName) - { - BindNode(node); - } - - const util::StringView &ImportName() const - { - return importName_; - } - - const util::StringView &LocalName() const - { - return name_; - } - - DeclType Type() const override - { - return DeclType::IMPORT; - } - -private: - util::StringView importName_; -}; - -class ExportDecl : public Decl { -public: - explicit ExportDecl(util::StringView exportName, util::StringView localName) - : Decl(localName), exportName_(exportName) - { - } - - explicit ExportDecl(util::StringView exportName, util::StringView localName, const ir::AstNode *node) - : Decl(localName), exportName_(exportName) - { - BindNode(node); - } - - const util::StringView &ExportName() const - { - return exportName_; - } - - const util::StringView &LocalName() const - { - return name_; - } - - DeclType Type() const override - { - return DeclType::EXPORT; - } - -private: - util::StringView exportName_; -}; - } // namespace panda::es2panda::binder #endif diff --git a/es2panda/binder/scope.cpp b/es2panda/binder/scope.cpp index d4a452d7649c57eb934147a3d3cf04def8841108..9d222e2cd9a1e2abe0fcf2f7c6d1a9b52b518ff3 100644 --- a/es2panda/binder/scope.cpp +++ b/es2panda/binder/scope.cpp @@ -342,15 +342,42 @@ bool GlobalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariabl // ModuleScope +void ModuleScope::ConvertLocalVariableToModuleVariable(ArenaAllocator *allocator, util::StringView localName) +{ + auto res = bindings_.find(localName); + // Since the module's exported [localName] has been validated before, + // [localName] must have a binding now. + ASSERT(res != bindings_.end()); + if (!res->second->IsModuleVariable()) { + auto *decl = res->second->Declaration(); + decl->AddFlag(DeclarationFlags::EXPORT); + VariableFlags flags = res->second->Flags(); + res->second = allocator->New(decl, flags | VariableFlags::LOCAL_EXPORT); + } +} + bool ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, [[maybe_unused]] ScriptExtension extension) { switch (newDecl->Type()) { case DeclType::VAR: { - return AddVar(allocator, currentVariable, newDecl); + auto [scope, shadowed] = IterateShadowedVariables( + newDecl->Name(), [](const Variable *v) { return !v->HasFlag(VariableFlags::VAR); }); + + if (shadowed) { + return false; + } + return newDecl->IsImportOrExportDecl() ? + AddVar(allocator, currentVariable, newDecl) : + AddVar(allocator, currentVariable, newDecl); } case DeclType::FUNC: { - return AddFunction(allocator, currentVariable, newDecl, extension); + if (currentVariable) { + return false; + } + return newDecl->IsImportOrExportDecl() ? + AddFunction(allocator, currentVariable, newDecl, extension) : + AddFunction(allocator, currentVariable, newDecl, extension); } case DeclType::ENUM: { bindings_.insert({newDecl->Name(), allocator->New(newDecl, false)}); @@ -362,109 +389,15 @@ bool ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariabl case DeclType::INTERFACE: { return AddTSBinding(allocator, currentVariable, newDecl, VariableFlags::INTERFACE); } - case DeclType::IMPORT: { - return AddImport(allocator, currentVariable, newDecl); - } - case DeclType::EXPORT: { - return true; - } default: { - return AddLexical(allocator, currentVariable, newDecl); - } - } -} - -void ModuleScope::AddImportDecl(const ir::ImportDeclaration *importDecl, ImportDeclList &&decls) -{ - auto res = imports_.emplace_back(importDecl, decls); - - for (auto &decl : res.second) { - decl->BindNode(importDecl); - } -} - -void ModuleScope::AddExportDecl(const ir::AstNode *exportDecl, ExportDecl *decl) -{ - decl->BindNode(exportDecl); - - ArenaVector decls(allocator_->Adapter()); - decls.push_back(decl); - - AddExportDecl(exportDecl, std::move(decls)); -} - -void ModuleScope::AddExportDecl(const ir::AstNode *exportDecl, ExportDeclList &&decls) -{ - auto res = exports_.emplace_back(exportDecl, decls); - - for (auto &decl : res.second) { - decl->BindNode(exportDecl); - } -} - -bool ModuleScope::AddImport(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl) -{ - if (currentVariable && currentVariable->Declaration()->Type() != DeclType::VAR) { - return false; - } - - if (newDecl->Node()->IsImportNamespaceSpecifier()) { - bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::READONLY)}); - } else { - auto *variable = allocator->New(newDecl, VariableFlags::NONE); - variable->ExoticName() = newDecl->AsImportDecl()->ImportName(); - bindings_.insert({newDecl->Name(), variable}); - } - - return true; -} - -bool ModuleScope::ExportAnalysis() -{ - std::set exportedNames; - - for (const auto &[exportDecl, decls] : exports_) { - if (exportDecl->IsExportAllDeclaration()) { - const auto *exportAllDecl = exportDecl->AsExportAllDeclaration(); - - if (exportAllDecl->Exported() != nullptr) { - auto result = exportedNames.insert(exportAllDecl->Exported()->Name()); - if (!result.second) { - return false; - } - } - - continue; - } - - if (exportDecl->IsExportNamedDeclaration()) { - const auto *exportNamedDecl = exportDecl->AsExportNamedDeclaration(); - - if (exportNamedDecl->Source()) { - continue; - } - } - - for (const auto *decl : decls) { - binder::Variable *variable = FindLocal(decl->LocalName()); - - if (!variable) { - continue; - } - - auto result = exportedNames.insert(decl->ExportName()); - if (!result.second) { + if (currentVariable) { return false; } - - if (!variable->IsModuleVariable()) { - variable->AddFlag(VariableFlags::LOCAL_EXPORT); - localExports_.insert({variable, decl->ExportName()}); - } + return newDecl->IsImportOrExportDecl() ? + AddLexical(allocator, currentVariable, newDecl) : + AddLexical(allocator, currentVariable, newDecl); } } - - return true; } // LocalScope diff --git a/es2panda/binder/scope.h b/es2panda/binder/scope.h index b8aae76aa63b976bc56018d2d90e927c3b114020..a6046c098a5dae268673a2529d7ced4935b2954e 100644 --- a/es2panda/binder/scope.h +++ b/es2panda/binder/scope.h @@ -295,6 +295,8 @@ protected: explicit VariableScope(ArenaAllocator *allocator, Scope *parent) : Scope(allocator, parent), lexicalVarNames_(allocator->Adapter()) {} + inline VariableFlags DeclFlagToVariableFlag(DeclarationFlags declFlag); + template bool AddVar(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl); @@ -573,8 +575,7 @@ class GlobalScope : public FunctionScope { public: explicit GlobalScope(ArenaAllocator *allocator) : FunctionScope(allocator, nullptr) { - auto *paramScope = allocator->New(allocator, this); - paramScope_ = paramScope; + paramScope_ = allocator->New(allocator, this); } ScopeType Type() const override @@ -586,21 +587,11 @@ public: [[maybe_unused]] ScriptExtension extension) override; }; -class ModuleScope : public GlobalScope { +class ModuleScope : public FunctionScope { public: - template - using ModuleEntry = ArenaVector>; - using ImportDeclList = ArenaVector; - using ExportDeclList = ArenaVector; - using LocalExportNameMap = ArenaMultiMap; - - explicit ModuleScope(ArenaAllocator *allocator) - : GlobalScope(allocator), - allocator_(allocator), - imports_(allocator_->Adapter()), - exports_(allocator_->Adapter()), - localExports_(allocator_->Adapter()) + explicit ModuleScope(ArenaAllocator *allocator) : FunctionScope(allocator, nullptr) { + paramScope_ = allocator->New(allocator, this); } ScopeType Type() const override @@ -608,52 +599,37 @@ public: return ScopeType::MODULE; } - const ModuleEntry &Imports() const - { - return imports_; - } - - const ModuleEntry &Exports() const - { - return exports_; - } - - const LocalExportNameMap &LocalExports() const - { - return localExports_; - } - - void AddImportDecl(const ir::ImportDeclaration *importDecl, ImportDeclList &&decls); - - void AddExportDecl(const ir::AstNode *exportDecl, ExportDecl *decl); - - void AddExportDecl(const ir::AstNode *exportDecl, ExportDeclList &&decls); + void ConvertLocalVariableToModuleVariable(ArenaAllocator *allocator, util::StringView localName); bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, [[maybe_unused]] ScriptExtension extension) override; - - bool ExportAnalysis(); - -private: - bool AddImport(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl); - - ArenaAllocator *allocator_; - ModuleEntry imports_; - ModuleEntry exports_; - LocalExportNameMap localExports_; }; +inline VariableFlags VariableScope::DeclFlagToVariableFlag(DeclarationFlags declFlag) +{ + VariableFlags varFlag = VariableFlags::NONE; + if (declFlag & DeclarationFlags::EXPORT) { + varFlag = VariableFlags::LOCAL_EXPORT; + } else if (declFlag & DeclarationFlags::IMPORT) { + varFlag = VariableFlags::IMPORT; + } + return varFlag; +} + template bool VariableScope::AddVar(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl) { + VariableFlags flags = VariableFlags::HOIST_VAR; + flags |= DeclFlagToVariableFlag(newDecl->Flags()); + if (!currentVariable) { - bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::HOIST_VAR)}); + bindings_.insert({newDecl->Name(), allocator->New(newDecl, flags)}); return true; } switch (currentVariable->Declaration()->Type()) { case DeclType::VAR: { - currentVariable->Reset(newDecl, VariableFlags::HOIST_VAR); + currentVariable->Reset(newDecl, flags); break; } case DeclType::PARAM: @@ -673,20 +649,21 @@ bool VariableScope::AddFunction(ArenaAllocator *allocator, Variable *currentVari [[maybe_unused]] ScriptExtension extension) { VariableFlags flags = (extension == ScriptExtension::JS) ? VariableFlags::HOIST_VAR : VariableFlags::HOIST; + flags |= DeclFlagToVariableFlag(newDecl->Flags()); if (!currentVariable) { bindings_.insert({newDecl->Name(), allocator->New(newDecl, flags)}); return true; } - if (extension != ScriptExtension::JS || IsModuleScope()) { + if (extension != ScriptExtension::JS) { return false; } switch (currentVariable->Declaration()->Type()) { case DeclType::VAR: case DeclType::FUNC: { - currentVariable->Reset(newDecl, VariableFlags::HOIST_VAR); + currentVariable->Reset(newDecl, flags); break; } default: { @@ -709,11 +686,13 @@ bool VariableScope::AddTSBinding(ArenaAllocator *allocator, [[maybe_unused]] Var template bool VariableScope::AddLexical(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl) { + VariableFlags flags = DeclFlagToVariableFlag(newDecl->Flags()); + if (currentVariable) { return false; } - bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::NONE)}); + bindings_.insert({newDecl->Name(), allocator->New(newDecl, flags)}); return true; } diff --git a/es2panda/binder/variable.h b/es2panda/binder/variable.h index 4302048a8bf2ccafe9887e3574da5bfd7be16f1c..36190236d6e4e182382259dfd6d5d697cd7efe9b 100644 --- a/es2panda/binder/variable.h +++ b/es2panda/binder/variable.h @@ -182,31 +182,7 @@ public: return VariableType::MODULE; } - compiler::VReg &ModuleReg() - { - return moduleReg_; - } - - compiler::VReg ModuleReg() const - { - return moduleReg_; - } - - const util::StringView &ExoticName() const - { - return exoticName_; - } - - util::StringView &ExoticName() - { - return exoticName_; - } - void SetLexical([[maybe_unused]] Scope *scope) override; - -private: - compiler::VReg moduleReg_ {}; - util::StringView exoticName_ {}; }; class EnumVariable : public Variable { diff --git a/es2panda/binder/variableFlags.h b/es2panda/binder/variableFlags.h index 9406cf2cfdaef7f9394c1955b24ee3c638111409..2127e3eadefa7f173ee567988cdb66d21892e2fb 100644 --- a/es2panda/binder/variableFlags.h +++ b/es2panda/binder/variableFlags.h @@ -27,8 +27,6 @@ namespace panda::es2panda::binder { _(CLASS, ClassDecl) \ _(FUNC, FunctionDecl) \ _(PARAM, ParameterDecl) \ - _(IMPORT, ImportDecl) \ - _(EXPORT, ExportDecl) \ /* TS */ \ _(TYPE_ALIAS, TypeAliasDecl) \ _(NAMESPACE, NameSpaceDecl) \ @@ -105,6 +103,7 @@ enum class VariableFlags { COMPUTED_IDENT = 1 << 8, COMPUTED_INDEX = 1 << 9, INDEX_NAME = 1 << 10, + IMPORT = 1 << 11, LOCAL_EXPORT = 1 << 12, INFERED_IN_PATTERN = 1 << 13, REST_ARG = 1 << 14, @@ -138,6 +137,15 @@ enum class VariableScopeFlags { DEFINE_BITOPS(VariableScopeFlags) +enum class DeclarationFlags { + NONE = 0, + IMPORT = 1 << 0, + EXPORT = 1 << 1, + NAMESPACE_IMPORT = 1 << 2, +}; + +DEFINE_BITOPS(DeclarationFlags) + } // namespace panda::es2panda::binder #endif diff --git a/es2panda/compiler/base/hoisting.cpp b/es2panda/compiler/base/hoisting.cpp index 815bf147a86f6909f90fd4781086b0773e07651c..e8b61fc97d3252b521f595b266bd3675664f6729 100644 --- a/es2panda/compiler/base/hoisting.cpp +++ b/es2panda/compiler/base/hoisting.cpp @@ -16,11 +16,23 @@ #include "hoisting.h" #include +#include #include #include +#include namespace panda::es2panda::compiler { +static void StoreModuleVarOrLocalVar(PandaGen *pg, binder::ScopeFindResult &result, const binder::Decl *decl) +{ + if (decl->IsImportOrExportDecl()) { + ASSERT(pg->Scope()->IsModuleScope()); + pg->StoreModuleVariable(decl->Node(), decl->Name()); + } else { + pg->StoreAccToLexEnv(decl->Node(), result, true); + } +} + static void HoistVar(PandaGen *pg, binder::Variable *var, const binder::VarDecl *decl) { auto *scope = pg->Scope(); @@ -39,7 +51,7 @@ static void HoistVar(PandaGen *pg, binder::Variable *var, const binder::VarDecl binder::ScopeFindResult result(decl->Name(), scope, 0, var); pg->LoadConst(decl->Node(), Constant::JS_UNDEFINED); - pg->StoreAccToLexEnv(decl->Node(), result, true); + StoreModuleVarOrLocalVar(pg, result, decl); } static void HoistFunction(PandaGen *pg, binder::Variable *var, const binder::FunctionDecl *decl) @@ -59,7 +71,23 @@ static void HoistFunction(PandaGen *pg, binder::Variable *var, const binder::Fun binder::ScopeFindResult result(decl->Name(), scope, 0, var); pg->DefineFunction(decl->Node(), scriptFunction, internalName); - pg->StoreAccToLexEnv(decl->Node(), result, true); + StoreModuleVarOrLocalVar(pg, result, decl); +} + +static void HoistNameSpaceImports(PandaGen *pg) +{ + if (pg->Scope()->IsModuleScope()) { + parser::SourceTextModuleRecord *moduleRecord = pg->Binder()->Program()->ModuleRecord(); + ASSERT(moduleRecord != nullptr); + for (auto nameSpaceEntry : moduleRecord->GetNamespaceImportEntries()) { + auto *var = pg->TopScope()->FindLocal(nameSpaceEntry->localName_); + ASSERT(var != nullptr); + auto *node = var->Declaration()->Node(); + ASSERT(node != nullptr); + pg->GetModuleNamespace(node, nameSpaceEntry->localName_); + pg->StoreVar(node, {nameSpaceEntry->localName_, pg->TopScope(), 0, var}, true); + } + } } void Hoisting::Hoist(PandaGen *pg) @@ -81,6 +109,8 @@ void Hoisting::Hoist(PandaGen *pg) HoistFunction(pg, var, decl->AsFunctionDecl()); } } + + HoistNameSpaceImports(pg); } } // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/base/lexenv.cpp b/es2panda/compiler/base/lexenv.cpp index 0ca34ca25e0c11190c76de901f020355f8c9171b..bd75ca475abe4247b840bc47ec479b3adcfb3a8f 100644 --- a/es2panda/compiler/base/lexenv.cpp +++ b/es2panda/compiler/base/lexenv.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include namespace panda::es2panda::compiler { @@ -44,7 +43,10 @@ static void CheckConstAssignment(PandaGen *pg, const ir::AstNode *node, binder:: static void ExpandLoadLexVar(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result) { pg->LoadLexicalVar(node, result.lexLevel, result.variable->AsLocalVariable()->LexIdx()); - pg->ThrowUndefinedIfHole(node, result.variable->Name()); + const auto *decl = result.variable->Declaration(); + if (decl->IsLetOrConstOrClassDecl()) { + pg->ThrowUndefinedIfHole(node, result.variable->Name()); + } } static void ExpandLoadNormalVar(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result) @@ -70,21 +72,6 @@ void VirtualLoadVar::Expand(PandaGen *pg, const ir::AstNode *node, const binder: // VirtualStoreVar -static void StoreLocalExport(PandaGen *pg, const ir::AstNode *node, binder::Variable *variable) -{ - if (!variable->HasFlag(binder::VariableFlags::LOCAL_EXPORT) || !pg->Scope()->IsModuleScope()) { - return; - } - - auto range = pg->Scope()->AsModuleScope()->LocalExports().equal_range(variable); - - for (auto it = range.first; it != range.second; ++it) { - if (it->second != "default") { - pg->StoreModuleVar(node, it->second); - } - } -} - static void ExpandStoreLexVar(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result, bool isDecl) { binder::LocalVariable *local = result.variable->AsLocalVariable(); @@ -107,8 +94,6 @@ static void ExpandStoreLexVar(PandaGen *pg, const ir::AstNode *node, const binde } pg->StoreLexicalVar(node, result.lexLevel, local->LexIdx()); - - StoreLocalExport(pg, node, local); } static void ExpandStoreNormalVar(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result, @@ -127,7 +112,6 @@ static void ExpandStoreNormalVar(PandaGen *pg, const ir::AstNode *node, const bi } pg->StoreAccumulator(node, localReg); - StoreLocalExport(pg, node, local); } void VirtualStoreVar::Expand(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result, bool isDecl) diff --git a/es2panda/compiler/base/lreference.cpp b/es2panda/compiler/base/lreference.cpp index 6fd21e14eedd2b4be322b5746b6411bd6f70a279..53587e65b47a252c5401b6862c5ad985967a41de 100644 --- a/es2panda/compiler/base/lreference.cpp +++ b/es2panda/compiler/base/lreference.cpp @@ -123,6 +123,13 @@ LReference LReference::CreateLRef(PandaGen *pg, const ir::AstNode *node, bool is case ir::AstNodeType::REST_ELEMENT: { return LReference::CreateLRef(pg, node->AsRestElement()->Argument(), true); } + case ir::AstNodeType::EXPORT_DEFAULT_DECLARATION: { + // export default [anonymous class decl] + util::StringView name = parser::SourceTextModuleRecord::DEFAULT_LOCAL_NAME; + binder::ScopeFindResult res = pg->Scope()->Find(name); + + return {node, pg, isDeclaration, ReferenceKind::VAR_OR_GLOBAL, res}; + } default: { UNREACHABLE(); } diff --git a/es2panda/compiler/core/compileQueue.cpp b/es2panda/compiler/core/compileQueue.cpp index 29460a7b3b4a59143e2c994747b7587c334251af..67dbd851a2491c201c14381b986ff9b90f571ce8 100644 --- a/es2panda/compiler/core/compileQueue.cpp +++ b/es2panda/compiler/core/compileQueue.cpp @@ -18,13 +18,29 @@ #include #include #include -#include +#include #include #include namespace panda::es2panda::compiler { -void CompileJob::Run() +void CompileJob::DependsOn(CompileJob *job) +{ + job->dependant_ = this; + dependencies_++; +} + +void CompileJob::Signal() +{ + { + std::lock_guard lock(m_); + dependencies_--; + } + + cond_.notify_one(); +} + +void CompileFunctionJob::Run() { std::unique_lock lock(m_); cond_.wait(lock, [this] { return dependencies_ == 0; }); @@ -44,20 +60,19 @@ void CompileJob::Run() } } -void CompileJob::DependsOn(CompileJob *job) +void CompileModuleRecordJob::Run() { - job->dependant_ = this; - dependencies_++; -} + std::unique_lock lock(m_); + cond_.wait(lock, [this] { return dependencies_ == 0; }); -void CompileJob::Signal() -{ - { - std::lock_guard lock(m_); - dependencies_--; - } + ModuleRecordEmitter moduleEmitter(context_->Binder()->Program()->ModuleRecord(), context_->NewLiteralIndex()); + moduleEmitter.Generate(); - cond_.notify_one(); + context_->GetEmitter()->AddSourceTextModuleRecord(&moduleEmitter, context_); + + if (dependant_) { + dependant_->Signal(); + } } CompileQueue::CompileQueue(size_t threadCount) @@ -88,10 +103,18 @@ void CompileQueue::Schedule(CompilerContext *context) ASSERT(jobsCount_ == 0); std::unique_lock lock(m_); const auto &functions = context->Binder()->Functions(); - jobs_ = new CompileJob[functions.size()](); for (auto *function : functions) { - jobs_[jobsCount_++].SetConext(context, function); + auto *funcJob = new CompileFunctionJob(context); + funcJob->SetFunctionScope(function); + jobs_.push_back(funcJob); + jobsCount_++; + } + + if (context->Binder()->Program()->Kind() == parser::ScriptKind::MODULE) { + auto *moduleRecordJob = new CompileModuleRecordJob(context); + jobs_.push_back(moduleRecordJob); + jobsCount_++; } lock.unlock(); @@ -122,7 +145,7 @@ void CompileQueue::Consume() while (jobsCount_ > 0) { --jobsCount_; - auto &job = jobs_[jobsCount_]; + auto &job = *(jobs_[jobsCount_]); lock.unlock(); @@ -144,7 +167,13 @@ void CompileQueue::Wait() { std::unique_lock lock(m_); jobsFinished_.wait(lock, [this]() { return activeWorkers_ == 0 && jobsCount_ == 0; }); - delete[] jobs_; + for (auto it = jobs_.begin(); it != jobs_.end(); it++) { + if (*it != nullptr) { + delete *it; + *it =nullptr; + } + } + jobs_.clear(); if (!errors_.empty()) { // NOLINTNEXTLINE diff --git a/es2panda/compiler/core/compileQueue.h b/es2panda/compiler/core/compileQueue.h index 5e227475ba26c314078b5d5f4108a8b5c069ee63..f53b8b4c6b03e0ac42b68ddf56c0b083ccf1daa5 100644 --- a/es2panda/compiler/core/compileQueue.h +++ b/es2panda/compiler/core/compileQueue.h @@ -33,33 +33,53 @@ class CompilerContext; class CompileJob { public: - CompileJob() = default; + explicit CompileJob(CompilerContext *context) : context_(context) {}; NO_COPY_SEMANTIC(CompileJob); NO_MOVE_SEMANTIC(CompileJob); - ~CompileJob() = default; + virtual ~CompileJob() = default; + + virtual void Run() = 0; + void DependsOn(CompileJob *job); + void Signal(); + +protected: + [[maybe_unused]] CompilerContext *context_ {}; + std::mutex m_; + std::condition_variable cond_; + CompileJob *dependant_ {}; + size_t dependencies_ {0}; +}; + +class CompileFunctionJob : public CompileJob { +public: + explicit CompileFunctionJob(CompilerContext *context) : CompileJob(context) {}; + NO_COPY_SEMANTIC(CompileFunctionJob); + NO_MOVE_SEMANTIC(CompileFunctionJob); + ~CompileFunctionJob() = default; binder::FunctionScope *Scope() const { return scope_; } - void SetConext(CompilerContext *context, binder::FunctionScope *scope) + void SetFunctionScope(binder::FunctionScope *scope) { - context_ = context; scope_ = scope; } - void Run(); - void DependsOn(CompileJob *job); - void Signal(); - + void Run() override; private: - std::mutex m_; - std::condition_variable cond_; - CompilerContext *context_ {}; binder::FunctionScope *scope_ {}; - CompileJob *dependant_ {}; - size_t dependencies_ {0}; +}; + +class CompileModuleRecordJob : public CompileJob { +public: + explicit CompileModuleRecordJob(CompilerContext *context) : CompileJob(context) {}; + NO_COPY_SEMANTIC(CompileModuleRecordJob); + NO_MOVE_SEMANTIC(CompileModuleRecordJob); + ~CompileModuleRecordJob() = default; + + void Run() override; }; class CompileQueue { @@ -81,7 +101,7 @@ private: std::mutex m_; std::condition_variable jobsAvailable_; std::condition_variable jobsFinished_; - CompileJob *jobs_ {}; + std::vector jobs_ {}; size_t jobsCount_ {0}; size_t activeWorkers_ {0}; bool terminate_ {false}; diff --git a/es2panda/compiler/core/compilerContext.cpp b/es2panda/compiler/core/compilerContext.cpp index 6a97c410fdd32e9a6b48274c4700ec069553893c..0cb6270324158372b92f52bd9c17eb86a5c38ccc 100644 --- a/es2panda/compiler/core/compilerContext.cpp +++ b/es2panda/compiler/core/compilerContext.cpp @@ -15,7 +15,7 @@ #include "compilerContext.h" -#include +#include namespace panda::es2panda::compiler { diff --git a/es2panda/compiler/core/compilerImpl.cpp b/es2panda/compiler/core/compilerImpl.cpp index c4ff569b2a7da04f7405cbb0f76ded8ea043606e..d459904a4e9ea05c0092642b569b886b2c1174ec 100644 --- a/es2panda/compiler/core/compilerImpl.cpp +++ b/es2panda/compiler/core/compilerImpl.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include #include diff --git a/es2panda/compiler/core/emitter.cpp b/es2panda/compiler/core/emitter/emitter.cpp similarity index 92% rename from es2panda/compiler/core/emitter.cpp rename to es2panda/compiler/core/emitter/emitter.cpp index cb117f420ffa0a481d39d528a774356084162ecd..ecec4eead8b73c44ac1e573fb0d191eb85a1ce38 100644 --- a/es2panda/compiler/core/emitter.cpp +++ b/es2panda/compiler/core/emitter/emitter.cpp @@ -37,7 +37,6 @@ #include namespace panda::es2panda::compiler { - constexpr const auto LANG_EXT = panda::pandasm::extensions::Language::ECMASCRIPT; FunctionEmitter::FunctionEmitter(ArenaAllocator *allocator, const PandaGen *pg) @@ -359,7 +358,6 @@ Emitter::Emitter(const CompilerContext *context) prog_->function_table.reserve(context->Binder()->Functions().size()); GenESAnnoatationRecord(); - GenESModuleModeRecord(context->Binder()->Program()->Kind() == parser::ScriptKind::MODULE); } Emitter::~Emitter() @@ -375,22 +373,6 @@ void Emitter::GenESAnnoatationRecord() prog_->record_table.emplace(annotationRecord.name, std::move(annotationRecord)); } -void Emitter::GenESModuleModeRecord(bool isModule) -{ - auto modeRecord = panda::pandasm::Record("_ESModuleMode", LANG_EXT); - modeRecord.metadata->SetAccessFlags(panda::ACC_PUBLIC); - - auto modeField = panda::pandasm::Field(LANG_EXT); - modeField.name = "isModule"; - modeField.type = panda::pandasm::Type("u8", 0); - modeField.metadata->SetValue( - panda::pandasm::ScalarValue::Create(static_cast(isModule))); - - modeRecord.field_list.emplace_back(std::move(modeField)); - - prog_->record_table.emplace(modeRecord.name, std::move(modeRecord)); -} - void Emitter::AddFunction(FunctionEmitter *func) { std::lock_guard lock(m_); @@ -408,6 +390,26 @@ void Emitter::AddFunction(FunctionEmitter *func) prog_->function_table.emplace(function->name, std::move(*function)); } +void Emitter::AddSourceTextModuleRecord(ModuleRecordEmitter *module, const CompilerContext *context) +{ + std::lock_guard lock(m_); + + auto ecmaModuleRecord = panda::pandasm::Record("_ESModuleRecord", LANG_EXT); + ecmaModuleRecord.metadata->SetAccessFlags(panda::ACC_PUBLIC); + + auto moduleIdxField = panda::pandasm::Field(LANG_EXT); + moduleIdxField.name = std::string {context->Binder()->Program()->SourceFile()}; + moduleIdxField.type = panda::pandasm::Type("u32", 0); + moduleIdxField.metadata->SetValue(panda::pandasm::ScalarValue::Create( + static_cast(module->Index()))); + ecmaModuleRecord.field_list.emplace_back(std::move(moduleIdxField)); + prog_->record_table.emplace(ecmaModuleRecord.name, std::move(ecmaModuleRecord)); + + auto &moduleLiteralsBuffer = module->Buffer(); + auto literalArrayInstance = panda::pandasm::LiteralArray(std::move(moduleLiteralsBuffer)); + prog_->literalarray_table.emplace(std::to_string(module->Index()), std::move(literalArrayInstance)); +} + void Emitter::DumpAsm(const panda::pandasm::Program *prog) { auto &ss = std::cout; @@ -454,5 +456,4 @@ panda::pandasm::Program *Emitter::Finalize(bool dumpDebugInfo) prog_ = nullptr; return prog; } - } // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/emitter.h b/es2panda/compiler/core/emitter/emitter.h similarity index 95% rename from es2panda/compiler/core/emitter.h rename to es2panda/compiler/core/emitter/emitter.h index 7054a6d1b1032aab829c64a5556b05057b97f451..9e3d9f934b2418d5b6c24dbcb51b73d75e22170a 100644 --- a/es2panda/compiler/core/emitter.h +++ b/es2panda/compiler/core/emitter/emitter.h @@ -17,6 +17,7 @@ #define ES2PANDA_COMPILER_IR_EMITTER_H #include +#include #include #include #include @@ -44,7 +45,6 @@ class Scope; } // namespace panda::es2panda::binder namespace panda::es2panda::compiler { - class PandaGen; class LiteralBuffer; class DebugInfo; @@ -100,17 +100,16 @@ public: NO_MOVE_SEMANTIC(Emitter); void AddFunction(FunctionEmitter *func); + void AddSourceTextModuleRecord(ModuleRecordEmitter *module, const CompilerContext *context); static void DumpAsm(const panda::pandasm::Program *prog); panda::pandasm::Program *Finalize(bool dumpDebugInfo); private: void GenESAnnoatationRecord(); - void GenESModuleModeRecord(bool isModule); std::mutex m_; panda::pandasm::Program *prog_; }; - } // namespace panda::es2panda::compiler #endif diff --git a/es2panda/compiler/core/emitter/moduleRecordEmitter.cpp b/es2panda/compiler/core/emitter/moduleRecordEmitter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd635669ffc2b3145ec14974e3429438540de9ab --- /dev/null +++ b/es2panda/compiler/core/emitter/moduleRecordEmitter.cpp @@ -0,0 +1,138 @@ +/** + * Copyright (c) 2021 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 "moduleRecordEmitter.h" + +namespace panda::es2panda::compiler { +void ModuleRecordEmitter::GenModuleRequests() +{ + ASSERT(moduleRecord_ != nullptr); + auto &moduleRequests = moduleRecord_->GetModuleRequests(); + panda::pandasm::LiteralArray::Literal moduleSize = { + .tag_ = panda::panda_file::LiteralTag::INTEGER, .value_ = static_cast(moduleRequests.size())}; + buffer_.emplace_back(moduleSize); + for (auto request : moduleRequests) { + panda::pandasm::LiteralArray::Literal moduleRequest = { + .tag_ = panda::panda_file::LiteralTag::STRING, .value_ = request.Mutf8()}; + buffer_.emplace_back(moduleRequest); + } +} + +void ModuleRecordEmitter::GenRegularImportEntries() +{ + ASSERT(moduleRecord_ != nullptr); + auto ®ularImportEntries = moduleRecord_->GetRegularImportEntries(); + panda::pandasm::LiteralArray::Literal entrySize = { + .tag_ = panda::panda_file::LiteralTag::INTEGER, + .value_ = static_cast(regularImportEntries.size())}; + buffer_.emplace_back(entrySize); + for (auto it = regularImportEntries.begin(); it != regularImportEntries.end(); ++it) { + auto *entry = it->second; + panda::pandasm::LiteralArray::Literal localName = { + .tag_ = panda::panda_file::LiteralTag::STRING, .value_ = entry->localName_.Mutf8()}; + buffer_.emplace_back(localName); + panda::pandasm::LiteralArray::Literal importName = { + .tag_ = panda::panda_file::LiteralTag::STRING, .value_ = entry->importName_.Mutf8()}; + buffer_.emplace_back(importName); + panda::pandasm::LiteralArray::Literal moduleRequest = { + .tag_ = panda::panda_file::LiteralTag::METHODAFFILIATE, + .value_ = static_cast(entry->moduleRequestIdx_)}; + buffer_.emplace_back(moduleRequest); + } +} + +void ModuleRecordEmitter::GenNamespaceImportEntries() +{ + ASSERT(moduleRecord_ != nullptr); + auto &namespaceImportEntries = moduleRecord_->GetNamespaceImportEntries(); + panda::pandasm::LiteralArray::Literal entrySize = { + .tag_ = panda::panda_file::LiteralTag::INTEGER, + .value_ = static_cast(namespaceImportEntries.size())}; + buffer_.emplace_back(entrySize); + for (const auto *entry : namespaceImportEntries) { + panda::pandasm::LiteralArray::Literal localName = { + .tag_ = panda::panda_file::LiteralTag::STRING, .value_ = entry->localName_.Mutf8()}; + buffer_.emplace_back(localName); + panda::pandasm::LiteralArray::Literal moduleRequest = { + .tag_ = panda::panda_file::LiteralTag::METHODAFFILIATE, + .value_ = static_cast(entry->moduleRequestIdx_)}; + buffer_.emplace_back(moduleRequest); + } +} + +void ModuleRecordEmitter::GenLocalExportEntries() +{ + ASSERT(moduleRecord_ != nullptr); + auto &localExportEntries = moduleRecord_->GetLocalExportEntries(); + panda::pandasm::LiteralArray::Literal entrySize = { + .tag_ = panda::panda_file::LiteralTag::INTEGER, .value_ = static_cast(localExportEntries.size())}; + buffer_.emplace_back(entrySize); + for (auto it = localExportEntries.begin(); it != localExportEntries.end(); ++it) { + auto *entry = it->second; + panda::pandasm::LiteralArray::Literal localName = { + .tag_ = panda::panda_file::LiteralTag::STRING, .value_ = entry->localName_.Mutf8()}; + buffer_.emplace_back(localName); + panda::pandasm::LiteralArray::Literal exportName = { + .tag_ = panda::panda_file::LiteralTag::STRING, .value_ = entry->exportName_.Mutf8()}; + buffer_.emplace_back(exportName); + } +} + +void ModuleRecordEmitter::GenIndirectExportEntries() +{ + ASSERT(moduleRecord_ != nullptr); + auto &indirectExportEntries = moduleRecord_->GetIndirectExportEntries(); + panda::pandasm::LiteralArray::Literal entrySize = { + .tag_ = panda::panda_file::LiteralTag::INTEGER, .value_ = static_cast(indirectExportEntries.size())}; + buffer_.emplace_back(entrySize); + for (const auto *entry : indirectExportEntries) { + panda::pandasm::LiteralArray::Literal exportName = { + .tag_ = panda::panda_file::LiteralTag::STRING, .value_ = entry->exportName_.Mutf8()}; + buffer_.emplace_back(exportName); + panda::pandasm::LiteralArray::Literal importName = { + .tag_ = panda::panda_file::LiteralTag::STRING, .value_ = entry->importName_.Mutf8()}; + buffer_.emplace_back(importName); + panda::pandasm::LiteralArray::Literal moduleRequest = { + .tag_ = panda::panda_file::LiteralTag::METHODAFFILIATE, + .value_ = static_cast(entry->moduleRequestIdx_)}; + buffer_.emplace_back(moduleRequest); + } +} + +void ModuleRecordEmitter::GenStarExportEntries() +{ + ASSERT(moduleRecord_ != nullptr); + auto &starExportEntries = moduleRecord_->GetStarExportEntries(); + panda::pandasm::LiteralArray::Literal entrySize = { + .tag_ = panda::panda_file::LiteralTag::INTEGER, .value_ = static_cast(starExportEntries.size())}; + buffer_.emplace_back(entrySize); + for (const auto *entry : starExportEntries) { + panda::pandasm::LiteralArray::Literal moduleRequest = { + .tag_ = panda::panda_file::LiteralTag::METHODAFFILIATE, + .value_ = static_cast(entry->moduleRequestIdx_)}; + buffer_.emplace_back(moduleRequest); + } +} + +void ModuleRecordEmitter::Generate() +{ + GenModuleRequests(); + GenRegularImportEntries(); + GenNamespaceImportEntries(); + GenLocalExportEntries(); + GenIndirectExportEntries(); + GenStarExportEntries(); +} +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/moduleContext.h b/es2panda/compiler/core/emitter/moduleRecordEmitter.h similarity index 42% rename from es2panda/compiler/core/moduleContext.h rename to es2panda/compiler/core/emitter/moduleRecordEmitter.h index da15b229feb4aa0fcc51b2146d6cc00bcb488010..7aadbd1ae5dff387a3565d748c9a064e52152aaa 100644 --- a/es2panda/compiler/core/moduleContext.h +++ b/es2panda/compiler/core/emitter/moduleRecordEmitter.h @@ -13,26 +13,46 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_CORE_MODULE_CONTEXT_H -#define ES2PANDA_COMPILER_CORE_MODULE_CONTEXT_H +#ifndef ES2PANDA_COMPILER_IR_MODULERECORD_EMITTER_H +#define ES2PANDA_COMPILER_IR_MODULERECORD_EMITTER_H -#include +#include +#include #include -namespace panda::es2panda::binder { -class Variable; -class ModuleScope; -} // namespace panda::es2panda::binder - namespace panda::es2panda::compiler { -class PandaGen; - -class ModuleContext { +class ModuleRecordEmitter { public: - ModuleContext() = delete; - - static void Compile(PandaGen *pg, binder::ModuleScope *scope); + explicit ModuleRecordEmitter(parser::SourceTextModuleRecord *moduleRecord, int32_t bufferIdx) + : moduleRecord_(moduleRecord), bufferIdx_(bufferIdx) {} + ~ModuleRecordEmitter() = default; + NO_COPY_SEMANTIC(ModuleRecordEmitter); + NO_MOVE_SEMANTIC(ModuleRecordEmitter); + + int32_t Index() const + { + return bufferIdx_; + } + + auto &Buffer() + { + return buffer_; + } + + void Generate(); + +private: + void GenModuleRequests(); + void GenRegularImportEntries(); + void GenNamespaceImportEntries(); + void GenLocalExportEntries(); + void GenIndirectExportEntries(); + void GenStarExportEntries(); + + parser::SourceTextModuleRecord *moduleRecord_; + int32_t bufferIdx_ {}; + std::vector buffer_; }; } // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/moduleContext.cpp b/es2panda/compiler/core/moduleContext.cpp deleted file mode 100644 index 272a319c361a78ee4436ee60cf84cc30152e5f8f..0000000000000000000000000000000000000000 --- a/es2panda/compiler/core/moduleContext.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (c) 2021 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 "moduleContext.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace panda::es2panda::compiler { -void CompileImports(PandaGen *pg, binder::ModuleScope *scope) -{ - for (const auto &[importDecl, decls] : scope->Imports()) { - pg->ImportModule(importDecl, importDecl->Source()->Str()); - - VReg moduleReg = pg->AllocReg(); - pg->StoreAccumulator(importDecl, moduleReg); - - for (const auto *decl : decls) { - binder::Variable *v = scope->FindLocal(decl->LocalName()); - - if (!v->IsModuleVariable()) { - ASSERT(decl->ImportName() == "*"); - - binder::ScopeFindResult result(decl->LocalName(), scope, 0, v); - pg->StoreAccToLexEnv(decl->Node(), result, true); - } else { - v->AsModuleVariable()->ModuleReg() = moduleReg; - } - } - } -} - -void CompileExports(PandaGen *pg, const binder::ModuleScope *scope) -{ - for (const auto &[exportDecl, decls] : scope->Exports()) { - if (exportDecl->IsExportAllDeclaration()) { - pg->ImportModule(exportDecl, exportDecl->AsExportAllDeclaration()->Source()->Str()); - } else if (exportDecl->IsExportNamedDeclaration() && exportDecl->AsExportNamedDeclaration()->Source()) { - pg->ImportModule(exportDecl, exportDecl->AsExportNamedDeclaration()->Source()->Str()); - } else { - continue; - } - - VReg moduleReg = pg->AllocReg(); - pg->StoreAccumulator(exportDecl, moduleReg); - - if (exportDecl->IsExportAllDeclaration()) { - pg->StoreModuleVar(exportDecl, decls.front()->ExportName()); - continue; - } - - pg->CopyModule(exportDecl, moduleReg); - - for (const auto *decl : decls) { - pg->LoadObjByName(decl->Node(), moduleReg, decl->LocalName()); - pg->StoreModuleVar(decl->Node(), decl->ExportName()); - } - } -} - -void ModuleContext::Compile(PandaGen *pg, binder::ModuleScope *scope) -{ - CompileImports(pg, scope); - CompileExports(pg, scope); -} -} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/pandagen.cpp b/es2panda/compiler/core/pandagen.cpp index c98ef1087ce657a369f7af8c6dab3098e05c697c..89b3f7e07ea39befe7b6b01b883d37f571c13c04 100644 --- a/es2panda/compiler/core/pandagen.cpp +++ b/es2panda/compiler/core/pandagen.cpp @@ -231,7 +231,10 @@ void PandaGen::LoadVar(const ir::Identifier *node, const binder::ScopeFindResult } if (var->IsModuleVariable()) { - LoadModuleVariable(node, var->AsModuleVariable()->ModuleReg(), var->AsModuleVariable()->ExoticName()); + LoadModuleVariable(node, var->Name(), var->HasFlag(binder::VariableFlags::LOCAL_EXPORT)); + if (var->Declaration()->IsLetOrConstOrClassDecl()) { + ThrowUndefinedIfHole(node, var->Name()); + } return; } @@ -260,7 +263,22 @@ void PandaGen::StoreVar(const ir::AstNode *node, const binder::ScopeFindResult & } if (var->IsModuleVariable()) { - ThrowConstAssignment(node, var->Name()); + 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); + LoadModuleVariable(node, var->Name(), true); + ThrowUndefinedIfHole(node, var->Name()); + LoadAccumulator(node, valueReg); + } + + StoreModuleVariable(node, var->Name()); return; } @@ -1381,15 +1399,6 @@ void PandaGen::CloseIterator(const ir::AstNode *node, VReg iter) ra_.Emit(node, iter); } -void PandaGen::ImportModule(const ir::AstNode *node, const util::StringView &name) -{ - /* - * TODO: module - * sa_.Emit(node, name); - * strings_.insert(name); - */ -} - void PandaGen::DefineClassWithBuffer(const ir::AstNode *node, const util::StringView &ctorId, int32_t litIdx, VReg lexenv, VReg base) { @@ -1398,24 +1407,22 @@ void PandaGen::DefineClassWithBuffer(const ir::AstNode *node, const util::String strings_.insert(ctorId); } -void PandaGen::LoadModuleVariable(const ir::AstNode *node, VReg module, const util::StringView &name) +void PandaGen::LoadModuleVariable(const ir::AstNode *node, const util::StringView &name, bool isLocalExport) { - /* - * TODO: module - * ra_.Emit(node, name, module); - * strings_.insert(name); - */ + ra_.Emit(node, name, isLocalExport ? static_cast(1) : static_cast(0)); + strings_.insert(name); } -void PandaGen::StoreModuleVar(const ir::AstNode *node, const util::StringView &name) +void PandaGen::StoreModuleVariable(const ir::AstNode *node, const util::StringView &name) { sa_.Emit(node, name); strings_.insert(name); } -void PandaGen::CopyModule(const ir::AstNode *node, VReg module) +void PandaGen::GetModuleNamespace(const ir::AstNode *node, const util::StringView &name) { - ra_.Emit(node, module); + sa_.Emit(node, name); + strings_.insert(name); } void PandaGen::StSuperByName(const ir::AstNode *node, VReg obj, const util::StringView &key) diff --git a/es2panda/compiler/core/pandagen.h b/es2panda/compiler/core/pandagen.h index 842deb1f871d4de3c47376434644da656c6e2ca5..10b0dafc5c1cee54c64c3b5d348ea13c325f9627 100644 --- a/es2panda/compiler/core/pandagen.h +++ b/es2panda/compiler/core/pandagen.h @@ -345,10 +345,9 @@ public: void DefineClassWithBuffer(const ir::AstNode *node, const util::StringView &ctorId, int32_t litIdx, VReg lexenv, VReg base); - void ImportModule(const ir::AstNode *node, const util::StringView &name); - void LoadModuleVariable(const ir::AstNode *node, VReg module, const util::StringView &name); - void StoreModuleVar(const ir::AstNode *node, const util::StringView &name); - void CopyModule(const ir::AstNode *node, VReg module); + void LoadModuleVariable(const ir::AstNode *node, const util::StringView &name, bool isLocalExport); + void StoreModuleVariable(const ir::AstNode *node, const util::StringView &name); + void GetModuleNamespace(const ir::AstNode *node, const util::StringView &name); void StSuperByName(const ir::AstNode *node, VReg obj, const util::StringView &key); void LdSuperByName(const ir::AstNode *node, VReg obj, const util::StringView &key); diff --git a/es2panda/compiler/core/regScope.cpp b/es2panda/compiler/core/regScope.cpp index 6426709aad4840e85003626cba7b6afda3a2d98b..196a0190158857d2145355fbb1570169161bb12f 100644 --- a/es2panda/compiler/core/regScope.cpp +++ b/es2panda/compiler/core/regScope.cpp @@ -21,7 +21,6 @@ #include #include #include -#include namespace panda::es2panda::compiler { @@ -115,10 +114,6 @@ FunctionRegScope::FunctionRegScope(PandaGen *pg) : RegScope(pg), envScope_(pg->A pg_->SetSourceLocationFlag(lexer::SourceLocationFlag::INVALID_SOURCE_LOCATION); pg_->LoadAccFromArgs(pg_->rootNode_); - if (funcScope->IsModuleScope()) { - ModuleContext::Compile(pg_, pg_->scope_->AsModuleScope()); - } - Hoisting::Hoist(pg); pg_->SetSourceLocationFlag(lexer::SourceLocationFlag::VALID_SOURCE_LOCATION); } diff --git a/es2panda/ir/base/classDefinition.cpp b/es2panda/ir/base/classDefinition.cpp index 260910e252d47833b4f7ce1bc7ccf1c74bf22fe2..03507d5551a53b359b0c0b38dd5d62dcea60d561 100644 --- a/es2panda/ir/base/classDefinition.cpp +++ b/es2panda/ir/base/classDefinition.cpp @@ -45,6 +45,19 @@ const FunctionExpression *ClassDefinition::Ctor() const return ctor_->Value(); } +util::StringView ClassDefinition::GetName() const +{ + if (ident_) { + return ident_->Name(); + } + + if (exportDefault_) { + return parser::SourceTextModuleRecord::DEFAULT_LOCAL_NAME; + } + + return ""; +} + void ClassDefinition::Iterate(const NodeTraverser &cb) const { if (ident_) { diff --git a/es2panda/ir/base/classDefinition.h b/es2panda/ir/base/classDefinition.h index 32a1e3096cc8a835adcdb871b94d4f438f8d17a3..6f02a09a70af50f9029de2b5aa9f66cb57265a8e 100644 --- a/es2panda/ir/base/classDefinition.h +++ b/es2panda/ir/base/classDefinition.h @@ -16,8 +16,8 @@ #ifndef ES2PANDA_PARSER_INCLUDE_AST_CLASS_DEFINITION_H #define ES2PANDA_PARSER_INCLUDE_AST_CLASS_DEFINITION_H -#include #include +#include #include namespace panda::es2panda::compiler { @@ -61,7 +61,8 @@ public: body_(std::move(body)), indexSignatures_(std::move(indexSignatures)), declare_(declare), - abstract_(abstract) + abstract_(abstract), + exportDefault_(false) { } @@ -95,6 +96,11 @@ public: return abstract_; } + void SetAsExportDefault() + { + exportDefault_ = true; + } + ArenaVector &Body() { return body_; @@ -113,6 +119,8 @@ public: const FunctionExpression *Ctor() const; + util::StringView GetName() const; + void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; @@ -135,6 +143,7 @@ private: ArenaVector indexSignatures_; bool declare_; bool abstract_; + bool exportDefault_; }; } // namespace panda::es2panda::ir diff --git a/es2panda/ir/base/scriptFunction.cpp b/es2panda/ir/base/scriptFunction.cpp index d4d06783480d148f97ac08d55b34c18ca0326fb6..5a5e2035b2c4d0edcec18210fb1ea71de6e8b0ad 100644 --- a/es2panda/ir/base/scriptFunction.cpp +++ b/es2panda/ir/base/scriptFunction.cpp @@ -40,6 +40,19 @@ size_t ScriptFunction::FormalParamsLength() const return length; } +util::StringView ScriptFunction::GetName() const +{ + if (id_) { + return id_->Name(); + } + + if (exportDefault_) { + return parser::SourceTextModuleRecord::DEFAULT_LOCAL_NAME; + } + + return ""; +} + void ScriptFunction::Iterate(const NodeTraverser &cb) const { if (id_) { diff --git a/es2panda/ir/base/scriptFunction.h b/es2panda/ir/base/scriptFunction.h index b0b1e9403e127baf55fd80de9eb388a02eb1a3ca..0353c259c3336f0e2463e286d0aa4431f25954cc 100644 --- a/es2panda/ir/base/scriptFunction.h +++ b/es2panda/ir/base/scriptFunction.h @@ -49,7 +49,8 @@ public: body_(body), returnTypeAnnotation_(returnTypeAnnotation), flags_(flags), - declare_(declare) + declare_(declare), + exportDefault_(false) { } @@ -133,12 +134,18 @@ public: id_ = id; } + void SetAsExportDefault() + { + exportDefault_ = true; + } + void AddFlag(ir::ScriptFunctionFlags flags) { flags_ |= flags; } size_t FormalParamsLength() const; + util::StringView GetName() const; binder::FunctionScope *Scope() const { @@ -159,6 +166,7 @@ private: Expression *returnTypeAnnotation_; ir::ScriptFunctionFlags flags_; bool declare_; + bool exportDefault_; }; } // namespace panda::es2panda::ir diff --git a/es2panda/ir/module/exportDefaultDeclaration.cpp b/es2panda/ir/module/exportDefaultDeclaration.cpp index 92dc53cd961585dca5f8dbf66d08b485ca81155f..948b0d54e16c1856d2587d3fc0eef6285476554d 100644 --- a/es2panda/ir/module/exportDefaultDeclaration.cpp +++ b/es2panda/ir/module/exportDefaultDeclaration.cpp @@ -34,7 +34,11 @@ void ExportDefaultDeclaration::Dump(ir::AstDumper *dumper) const void ExportDefaultDeclaration::Compile([[maybe_unused]] compiler::PandaGen *pg) const { decl_->Compile(pg); - pg->StoreModuleVar(this, "default"); + if (decl_->IsExpression()) { + // export default [AssignmentExpression] + // e.g. export default 42 (42 be exported as [default]) + pg->StoreModuleVariable(this, parser::SourceTextModuleRecord::DEFAULT_LOCAL_NAME); + } } checker::Type *ExportDefaultDeclaration::Check([[maybe_unused]] checker::Checker *checker) const diff --git a/es2panda/ir/statements/classDeclaration.cpp b/es2panda/ir/statements/classDeclaration.cpp index 0964fcf99a882561282f3682c1a5e4a390d6cd70..3c3893e44a16751daadba6a9c61bbabba83f9176 100644 --- a/es2panda/ir/statements/classDeclaration.cpp +++ b/es2panda/ir/statements/classDeclaration.cpp @@ -16,10 +16,12 @@ #include "classDeclaration.h" #include +#include #include #include #include #include +#include namespace panda::es2panda::ir { @@ -39,7 +41,11 @@ void ClassDeclaration::Dump(ir::AstDumper *dumper) const void ClassDeclaration::Compile([[maybe_unused]] compiler::PandaGen *pg) const { - auto lref = compiler::LReference::CreateLRef(pg, def_->Ident(), true); + // [ClassDeclaration] without [Identifier] must have parent node + // of [ExportDefaultDeclaration] during compiling phase. So we use + // the parent node to create a lreference with boundName of [*default*]. + const auto *node = def_->Ident() ? def_->Ident() : this->Parent(); + auto lref = compiler::LReference::CreateLRef(pg, node, true); def_->Compile(pg); lref.SetValue(); } diff --git a/es2panda/parser/context/parserContext.h b/es2panda/parser/context/parserContext.h index 95d9743ee52981b64a578ad6e60951b8274d852a..790e60518fa50ac8b046009787e0ba3a55490d30 100644 --- a/es2panda/parser/context/parserContext.h +++ b/es2panda/parser/context/parserContext.h @@ -46,7 +46,7 @@ enum class ParserStatus { IN_ITERATION = (1 << 14), IN_LABELED = (1 << 15), - EXPORT_DEFAULT_REACHED = (1 << 16), + EXPORT_REACHED = (1 << 16), HAS_COMPLEX_PARAM = (1 << 17), IN_SWITCH = (1 << 18), diff --git a/es2panda/parser/module/sourceTextModuleRecord.cpp b/es2panda/parser/module/sourceTextModuleRecord.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a25328bcc451ef87d5ceb337760eb5fccf653b1 --- /dev/null +++ b/es2panda/parser/module/sourceTextModuleRecord.cpp @@ -0,0 +1,163 @@ +/** + * Copyright (c) 2021 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 "sourceTextModuleRecord.h" + +namespace panda::es2panda::parser { + int SourceTextModuleRecord::AddModuleRequest(const util::StringView source) + { + ASSERT(!source.Empty()); + int moduleRequestsSize = static_cast(moduleRequestsMap_.size()); + if (moduleRequestsMap_.find(source) == moduleRequestsMap_.end()) { + moduleRequests_.emplace_back(source); + } + auto insertedRes = moduleRequestsMap_.insert(std::make_pair(source, moduleRequestsSize)); + return insertedRes.first->second; + } + + // import x from 'test.js' + // import {x} from 'test.js' + // import {x as y} from 'test.js' + // import defaultExport from 'test.js' + void SourceTextModuleRecord::AddImportEntry(SourceTextModuleRecord::ImportEntry *entry) + { + ASSERT(!entry->importName_.Empty()); + ASSERT(!entry->localName_.Empty()); + ASSERT(entry->moduleRequestIdx_ != -1); + regularImportEntries_.insert(std::make_pair(entry->localName_, entry)); + // the implicit indirect exports should be insert into indirectExportsEntries + // when add an ImportEntry. + // e.g. export { x }; import { x } from 'test.js' + CheckImplicitIndirectExport(entry); + } + + // import * as x from 'test.js' + void SourceTextModuleRecord::AddStarImportEntry(SourceTextModuleRecord::ImportEntry *entry) + { + ASSERT(!entry->localName_.Empty()); + ASSERT(entry->importName_.Empty()); + ASSERT(entry->moduleRequestIdx_ != -1); + namespaceImportEntries_.push_back(entry); + } + + // export {x} + // export {x as y} + // export VariableStatement + // export Declaration + // export default ... + bool SourceTextModuleRecord::AddLocalExportEntry(SourceTextModuleRecord::ExportEntry *entry) + { + ASSERT(entry->importName_.Empty()); + ASSERT(!entry->localName_.Empty()); + ASSERT(!entry->exportName_.Empty()); + ASSERT(entry->moduleRequestIdx_ == -1); + + // the implicit indirect exports should be insert into indirectExportsEntries + // when add an ExportEntry. + // e.g. import { x } from 'test.js'; export { x } + if (CheckImplicitIndirectExport(entry)) { + return true; + } + if (!HasDuplicateExport(entry->exportName_)) { + localExportEntries_.insert(std::make_pair(entry->localName_, entry)); + return true; + } + return false; + } + + // export {x} from 'test.js' + // export {x as y} from 'test.js' + // import { x } from 'test.js'; export { x } + bool SourceTextModuleRecord::AddIndirectExportEntry(SourceTextModuleRecord::ExportEntry *entry) + { + ASSERT(!entry->importName_.Empty()); + ASSERT(!entry->exportName_.Empty()); + ASSERT(entry->localName_.Empty()); + ASSERT(entry->moduleRequestIdx_ != -1); + if (!HasDuplicateExport(entry->exportName_)) { + indirectExportEntries_.push_back(entry); + return true; + } + return false; + } + + // export * from 'test.js' + void SourceTextModuleRecord::AddStarExportEntry(SourceTextModuleRecord::ExportEntry *entry) + { + ASSERT(entry->importName_.Empty()); + ASSERT(entry->localName_.Empty()); + ASSERT(entry->exportName_.Empty()); + ASSERT(entry->moduleRequestIdx_ != -1); + starExportEntries_.push_back(entry); + } + + bool SourceTextModuleRecord::HasDuplicateExport(util::StringView exportName) + { + for (auto const &entryUnit : localExportEntries_) { + const SourceTextModuleRecord::ExportEntry *e = entryUnit.second; + if (exportName == e->exportName_) { + return true; + } + } + + for (const auto *e : indirectExportEntries_) { + if (exportName == e->exportName_) { + return true; + } + } + + return false; + } + + bool SourceTextModuleRecord::CheckImplicitIndirectExport(SourceTextModuleRecord::ExportEntry *exportEntry) + { + ASSERT(!exportEntry->localName_.Empty()); + auto regularImport = regularImportEntries_.find(exportEntry->localName_); + if (regularImport != regularImportEntries_.end()) { + ConvertLocalExportToIndirect(regularImport->second, exportEntry); + return AddIndirectExportEntry(exportEntry); + } + return false; + } + + void SourceTextModuleRecord::CheckImplicitIndirectExport(SourceTextModuleRecord::ImportEntry *importEntry) + { + ASSERT(!importEntry->localName_.Empty()); + auto range = localExportEntries_.equal_range(importEntry->localName_); + // not found implicit indirect + if (range.first == range.second) { + return; + } + + for (auto it = range.first; it != range.second; ++it) { + SourceTextModuleRecord::ExportEntry *exportEntry = it->second; + ConvertLocalExportToIndirect(importEntry, exportEntry); + indirectExportEntries_.push_back(exportEntry); + } + localExportEntries_.erase(range.first, range.second); + } + + void SourceTextModuleRecord::ConvertLocalExportToIndirect(SourceTextModuleRecord::ImportEntry *importEntry, + SourceTextModuleRecord::ExportEntry *exportEntry) + { + ASSERT(exportEntry->importName_.Empty()); + ASSERT(exportEntry->moduleRequestIdx_ == -1); + ASSERT(!importEntry->importName_.Empty()); + ASSERT(importEntry->moduleRequestIdx_ != -1); + exportEntry->importName_ = importEntry->importName_; + exportEntry->moduleRequestIdx_ = importEntry->moduleRequestIdx_; + exportEntry->localName_ = util::StringView(""); + } +} // namespace panda::es2panda::parser diff --git a/es2panda/parser/module/sourceTextModuleRecord.h b/es2panda/parser/module/sourceTextModuleRecord.h new file mode 100644 index 0000000000000000000000000000000000000000..f4a52bf09d8216af3dde73c1d3ba1590e33ce347 --- /dev/null +++ b/es2panda/parser/module/sourceTextModuleRecord.h @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2021 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. + */ + +#ifndef ES2PANDA_PARSER_CORE_MODULERECORD_H +#define ES2PANDA_PARSER_CORE_MODULERECORD_H + +#include + +namespace panda::es2panda::parser { +class SourceTextModuleRecord { +public: + explicit SourceTextModuleRecord(ArenaAllocator *allocator) + : allocator_(allocator), + moduleRequestsMap_(allocator_->Adapter()), + moduleRequests_(allocator_->Adapter()), + localExportEntries_(allocator_->Adapter()), + regularImportEntries_(allocator_->Adapter()), + namespaceImportEntries_(allocator_->Adapter()), + starExportEntries_(allocator_->Adapter()), + indirectExportEntries_(allocator_->Adapter()) + { + } + + ~SourceTextModuleRecord() = default; + NO_COPY_SEMANTIC(SourceTextModuleRecord); + NO_MOVE_SEMANTIC(SourceTextModuleRecord); + + struct ImportEntry { + int moduleRequestIdx_; + util::StringView localName_; + util::StringView importName_; + + ImportEntry(const util::StringView localName, const util::StringView importName, int moduleRequestIdx) + : moduleRequestIdx_(moduleRequestIdx), localName_(localName), importName_(importName) {} + ImportEntry(const util::StringView localName, int moduleRequestIdx) + : moduleRequestIdx_(moduleRequestIdx), localName_(localName) {} + }; + + struct ExportEntry { + int moduleRequestIdx_; + util::StringView exportName_; + util::StringView localName_; + util::StringView importName_; + + explicit ExportEntry(int moduleRequest) : moduleRequestIdx_(moduleRequest) {} + ExportEntry(const util::StringView exportName, const util::StringView localName) + : moduleRequestIdx_(-1), exportName_(exportName), localName_(localName) {} + ExportEntry(const util::StringView exportName, const util::StringView importName, int moduleRequest) + : moduleRequestIdx_(moduleRequest), exportName_(exportName) + { + importName_ = importName; + } + }; + + template + T *NewEntry(Args &&... args) + { + return allocator_->New(std::forward(args)...); + } + + int AddModuleRequest(const util::StringView source); + void AddImportEntry(ImportEntry *entry); + void AddStarImportEntry(ImportEntry *entry); + bool AddLocalExportEntry(ExportEntry *entry); + bool AddIndirectExportEntry(ExportEntry *entry); + void AddStarExportEntry(ExportEntry *entry); + + bool CheckImplicitIndirectExport(ExportEntry *exportEntry); + void CheckImplicitIndirectExport(ImportEntry *importEntry); + + using ModuleRequestList = ArenaVector; + using ModuleRequestMap = ArenaMap; + using LocalExportEntryMap = ArenaMultiMap; + using RegularImportEntryMap = ArenaMap; + using NamespaceImportEntryList = ArenaVector; + using SpecialExportEntryList = ArenaVector; + + const ArenaVector &GetModuleRequests() const + { + return moduleRequests_; + } + + const LocalExportEntryMap &GetLocalExportEntries() const + { + return localExportEntries_; + } + + const RegularImportEntryMap &GetRegularImportEntries() const + { + return regularImportEntries_; + } + + const NamespaceImportEntryList &GetNamespaceImportEntries() const + { + return namespaceImportEntries_; + } + + const SpecialExportEntryList &GetStarExportEntries() const + { + return starExportEntries_; + } + + const SpecialExportEntryList &GetIndirectExportEntries() const + { + return indirectExportEntries_; + } + + static constexpr std::string_view DEFAULT_LOCAL_NAME = "*default*"; + static constexpr std::string_view DEFAULT_EXTERNAL_NAME = "default"; + static constexpr std::string_view ANONY_NAMESPACE_NAME = "=ens"; + +private: + bool HasDuplicateExport(util::StringView exportName); + void ConvertLocalExportToIndirect(ImportEntry *importEntry, ExportEntry *exportEntry); + + ArenaAllocator *allocator_; + ModuleRequestMap moduleRequestsMap_; + ModuleRequestList moduleRequests_; + LocalExportEntryMap localExportEntries_; + RegularImportEntryMap regularImportEntries_; + NamespaceImportEntryList namespaceImportEntries_; + SpecialExportEntryList starExportEntries_; + SpecialExportEntryList indirectExportEntries_; +}; +} // namespace panda::es2panda::parser + +#endif \ No newline at end of file diff --git a/es2panda/parser/parserFlags.h b/es2panda/parser/parserFlags.h index bb64a73ee2ac096b374ece7d2dfbfa9bbf0b3e0e..48f1020f094c750b5fa1e782b0a087be8fb265b0 100644 --- a/es2panda/parser/parserFlags.h +++ b/es2panda/parser/parserFlags.h @@ -39,6 +39,7 @@ enum class VariableParsingFlags { LET = (1 << 4), CONST = (1 << 5), STOP_AT_IN = (1 << 6), + EXPORTED = (1 << 7), }; DEFINE_BITOPS(VariableParsingFlags) diff --git a/es2panda/parser/parserImpl.cpp b/es2panda/parser/parserImpl.cpp index 6ecfc77b84dcbe26126be64ed45012ace09a8a34..83da26513a88a2c1e1fe1654576ca23f678ce232 100644 --- a/es2panda/parser/parserImpl.cpp +++ b/es2panda/parser/parserImpl.cpp @@ -122,11 +122,6 @@ Program ParserImpl::ParseModule(const std::string &fileName, const std::string & context_.Status() |= (ParserStatus::MODULE); ParseProgram(ScriptKind::MODULE); - - if (!Binder()->TopScope()->AsModuleScope()->ExportAnalysis()) { - ThrowSyntaxError("Invalid exported binding"); - } - return std::move(program_); } @@ -3250,58 +3245,9 @@ ScriptExtension ParserImpl::Extension() const return program_.Extension(); } -void ExportDeclarationContext::BindExportDecl(const ir::AstNode *exportDecl) -{ - if (!binder_) { - return; - } - - binder::ModuleScope::ExportDeclList declList(Allocator()->Adapter()); - - if (exportDecl->IsExportDefaultDeclaration()) { - const auto *decl = exportDecl->AsExportDefaultDeclaration(); - const auto *rhs = decl->Decl(); - - if (binder_->GetScope()->Bindings().size() == savedBindings_.size()) { - if (rhs->IsFunctionDeclaration()) { - binder_->AddDecl(rhs->Start(), binder_->Allocator(), - util::StringView(DEFAULT_EXPORT), - rhs->AsFunctionDeclaration()->Function()); - } else { - binder_->AddDecl(rhs->Start(), util::StringView(DEFAULT_EXPORT)); - } - } - } - - for (const auto &[name, variable] : binder_->GetScope()->Bindings()) { - if (savedBindings_.find(name) != savedBindings_.end()) { - continue; - } - - util::StringView exportName(exportDecl->IsExportDefaultDeclaration() ? "default" : name); - - variable->AddFlag(binder::VariableFlags::LOCAL_EXPORT); - auto *decl = binder_->AddDecl(variable->Declaration()->Node()->Start(), exportName, name); - declList.push_back(decl); - } - - auto *moduleScope = binder_->GetScope()->AsModuleScope(); - moduleScope->AddExportDecl(exportDecl, std::move(declList)); -} - -void ImportDeclarationContext::BindImportDecl(const ir::ImportDeclaration *importDecl) +parser::SourceTextModuleRecord *ParserImpl::GetSourceTextModuleRecord() { - binder::ModuleScope::ImportDeclList declList(Allocator()->Adapter()); - - for (const auto &[name, variable] : binder_->GetScope()->Bindings()) { - if (savedBindings_.find(name) != savedBindings_.end()) { - continue; - } - - declList.push_back(variable->Declaration()->AsImportDecl()); - } - - binder_->GetScope()->AsModuleScope()->AddImportDecl(importDecl, std::move(declList)); + return Binder()->Program()->ModuleRecord(); } } // namespace panda::es2panda::parser diff --git a/es2panda/parser/parserImpl.h b/es2panda/parser/parserImpl.h index 9a4d37a1e9452ec9ceb7e138ae9fcad92767ae2d..c07300b81b0fb4bb733236ac1f5fba8dd143f47f 100644 --- a/es2panda/parser/parserImpl.h +++ b/es2panda/parser/parserImpl.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -394,6 +395,14 @@ private: bool IsLabelFollowedByIterationStatement(); + void AddImportEntryItem(const ir::StringLiteral *source, const ArenaVector *specifiers); + void AddExportNamedEntryItem(const ArenaVector &specifiers, const ir::StringLiteral *source); + void AddExportStarEntryItem(const lexer::SourcePosition &startLoc, const ir::StringLiteral *source, + const ir::Identifier *exported); + void AddExportDefaultEntryItem(const ir::AstNode *declNode); + void AddExportLocalEntryItem(const ir::Statement *declNode); + parser::SourceTextModuleRecord *GetSourceTextModuleRecord(); + bool ParseDirective(ArenaVector *statements); void ParseDirectivePrologue(ArenaVector *statements); ArenaVector ParseStatementList(StatementParsingFlags flags = StatementParsingFlags::ALLOW_LEXICAL); @@ -439,7 +448,8 @@ private: ir::ClassDeclaration *ParseClassStatement(StatementParsingFlags flags, bool isDeclare, ArenaVector &&decorators, bool isAbstract = false); ir::ClassDeclaration *ParseClassDeclaration(bool idRequired, ArenaVector &&decorators, - bool isDeclare = false, bool isAbstract = false); + bool isDeclare = false, bool isAbstract = false, + bool isExported = false); ir::TSTypeAliasDeclaration *ParseTsTypeAliasDeclaration(bool isDeclare); ir::TSEnumDeclaration *ParseEnumMembers(ir::Identifier *key, const lexer::SourcePosition &enumStart, bool isConst); ir::TSEnumDeclaration *ParseEnumDeclaration(bool isConst = false); @@ -460,6 +470,15 @@ private: ir::VariableDeclaration *ParseContextualLet(VariableParsingFlags flags, StatementParsingFlags stmFlags = StatementParsingFlags::ALLOW_LEXICAL, bool isDeclare = false); + + util::StringView GetNamespaceExportInternalName() + { + std::string name = std::string(parser::SourceTextModuleRecord::ANONY_NAMESPACE_NAME) + + std::to_string(namespaceExportCount_++); + util::UString internalName(name, Allocator()); + return internalName.View(); + } + ArenaAllocator *Allocator() const { return program_.Allocator(); @@ -477,6 +496,7 @@ private: Program program_; ParserContext context_; lexer::Lexer *lexer_ {nullptr}; + size_t namespaceExportCount_ {0}; }; template @@ -639,53 +659,6 @@ private: } }; -class SavedBindingsContext { -public: - explicit SavedBindingsContext(binder::Binder *binder) - : binder_(binder), savedBindings_(binder_->GetScope()->Bindings()) - { - } - NO_COPY_SEMANTIC(SavedBindingsContext); - NO_MOVE_SEMANTIC(SavedBindingsContext); - ~SavedBindingsContext() = default; - -protected: - ArenaAllocator *Allocator() const - { - return binder_->Allocator(); - } - - binder::Binder *binder_; - binder::VariableMap savedBindings_; -}; - -class ExportDeclarationContext : public SavedBindingsContext { -public: - explicit ExportDeclarationContext(binder::Binder *binder) : SavedBindingsContext(binder) {} - NO_COPY_SEMANTIC(ExportDeclarationContext); - NO_MOVE_SEMANTIC(ExportDeclarationContext); - ~ExportDeclarationContext() = default; - - void BindExportDecl(const ir::AstNode *exportDecl); - -protected: - static constexpr std::string_view DEFAULT_EXPORT = "*default*"; -}; - -class ImportDeclarationContext : public SavedBindingsContext { -public: - explicit ImportDeclarationContext(binder::Binder *binder) : SavedBindingsContext(binder) {} - - NO_COPY_SEMANTIC(ImportDeclarationContext); - NO_MOVE_SEMANTIC(ImportDeclarationContext); - - ~ImportDeclarationContext() = default; - - void BindImportDecl(const ir::ImportDeclaration *importDecl); - -private: -}; - } // namespace panda::es2panda::parser #endif diff --git a/es2panda/parser/program/program.cpp b/es2panda/parser/program/program.cpp index 128c80dd137285ab605aa552fb0b91687a2e5801..717ba230d95d35a6062dbb845f5122d541c46f3d 100644 --- a/es2panda/parser/program/program.cpp +++ b/es2panda/parser/program/program.cpp @@ -63,6 +63,10 @@ void Program::SetKind(ScriptKind kind) { kind_ = kind; binder_->InitTopScope(); + + if (kind == ScriptKind::MODULE) { + moduleRecord_ = allocator_->New(Allocator()); + } } std::string Program::Dump() const diff --git a/es2panda/parser/program/program.h b/es2panda/parser/program/program.h index c32afee7a9a8df429db0eb8ce34d1d42a605ef46..33b750ed2db31dba27d0296e1b8182ccd0c6c9e7 100644 --- a/es2panda/parser/program/program.h +++ b/es2panda/parser/program/program.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "es2panda.h" @@ -68,6 +69,11 @@ public: return kind_; } + SourceTextModuleRecord *ModuleRecord() const + { + return moduleRecord_; + } + util::StringView SourceCode() const { return sourceCode_.View(); @@ -117,6 +123,7 @@ private: ScriptKind kind_ {}; ScriptExtension extension_ {}; lexer::LineIndex lineIndex_ {}; + SourceTextModuleRecord *moduleRecord_ {nullptr}; }; } // namespace panda::es2panda::parser diff --git a/es2panda/parser/statementParser.cpp b/es2panda/parser/statementParser.cpp index 7a0df02cc9e60065d9a342e28fb8ce1c6145dff4..4118364e21342315143211011000c5d29fb907a5 100644 --- a/es2panda/parser/statementParser.cpp +++ b/es2panda/parser/statementParser.cpp @@ -516,13 +516,21 @@ ir::ClassDeclaration *ParserImpl::ParseClassStatement(StatementParsingFlags flag } ir::ClassDeclaration *ParserImpl::ParseClassDeclaration(bool idRequired, ArenaVector &&decorators, - bool isDeclare, bool isAbstract) + bool isDeclare, bool isAbstract, bool isExported) { lexer::SourcePosition startLoc = lexer_->GetToken().Start(); ir::ClassDefinition *classDefinition = ParseClassDefinition(true, idRequired, isDeclare, isAbstract); + if (isExported && !idRequired) { + classDefinition->SetAsExportDefault(); + } + + auto location = classDefinition->Ident() ? classDefinition->Ident()->Start() : startLoc; + auto className = classDefinition->GetName(); + ASSERT(!className.Empty()); + + binder::DeclarationFlags flag = isExported ? binder::DeclarationFlags::EXPORT : binder::DeclarationFlags::NONE; + auto *decl = Binder()->AddDecl(location, flag, className); - auto *decl = - Binder()->AddDecl(classDefinition->Ident()->Start(), classDefinition->Ident()->Name()); decl->BindNode(classDefinition); lexer::SourcePosition endLoc = classDefinition->End(); @@ -971,14 +979,22 @@ ir::FunctionDeclaration *ParserImpl::ParseFunctionDeclaration(bool canBeAnonymou context_.Status() = savedStatus; + // e.g. export default function () {} if (lexer_->GetToken().Type() != lexer::TokenType::LITERAL_IDENT && lexer_->GetToken().Type() != lexer::TokenType::KEYW_AWAIT) { if (canBeAnonymous) { ir::ScriptFunction *func = ParseFunction(newStatus, isDeclare); func->SetStart(startLoc); + func->SetAsExportDefault(); auto *funcDecl = AllocNode(func); funcDecl->SetRange(func->Range()); + + binder::DeclarationFlags declflag = newStatus & ParserStatus::EXPORT_REACHED ? + binder::DeclarationFlags::EXPORT : binder::DeclarationFlags::NONE; + Binder()->AddDecl(startLoc, declflag, Allocator(), + parser::SourceTextModuleRecord::DEFAULT_LOCAL_NAME, func); + return funcDecl; } @@ -1004,7 +1020,9 @@ ir::FunctionDeclaration *ParserImpl::ParseFunctionDeclaration(bool canBeAnonymou funcDecl->SetRange(func->Range()); if (!func->IsOverload()) { - Binder()->AddDecl(identNode->Start(), Allocator(), ident, func); + binder::DeclarationFlags declflag = newStatus & ParserStatus::EXPORT_REACHED ? + binder::DeclarationFlags::EXPORT : binder::DeclarationFlags::NONE; + Binder()->AddDecl(identNode->Start(), declflag, Allocator(), ident, func); } else if (lexer_->GetToken().Type() == lexer::TokenType::PUNCTUATOR_SEMI_COLON) { lexer_->NextToken(); } @@ -1806,13 +1824,15 @@ ir::VariableDeclarator *ParserImpl::ParseVariableDeclarator(VariableParsingFlags for (const auto *binding : bindings) { binder::Decl *decl = nullptr; + binder::DeclarationFlags declflag = flags & VariableParsingFlags::EXPORTED ? + binder::DeclarationFlags::EXPORT : binder::DeclarationFlags::NONE; if (flags & VariableParsingFlags::VAR) { - decl = Binder()->AddDecl(startLoc, binding->Name()); + decl = Binder()->AddDecl(startLoc, declflag, binding->Name()); } else if (flags & VariableParsingFlags::LET) { - decl = Binder()->AddDecl(startLoc, binding->Name()); + decl = Binder()->AddDecl(startLoc, declflag, binding->Name()); } else { - decl = Binder()->AddDecl(startLoc, binding->Name()); + decl = Binder()->AddDecl(startLoc, declflag, binding->Name()); } decl->BindNode(init); @@ -1896,6 +1916,173 @@ ir::WhileStatement *ParserImpl::ParseWhileStatement() return whileStatement; } +void ParserImpl::AddImportEntryItem(const ir::StringLiteral *source, const ArenaVector *specifiers) +{ + ASSERT(source != nullptr); + auto *moduleRecord = GetSourceTextModuleRecord(); + ASSERT(moduleRecord != nullptr); + auto moduleRequestIdx = moduleRecord->AddModuleRequest(source->Str()); + + if (specifiers == nullptr) { + return; + } + + for (auto *it : *specifiers) { + switch (it->Type()) { + case ir::AstNodeType::IMPORT_DEFAULT_SPECIFIER: { + auto localName = it->AsImportDefaultSpecifier()->Local()->Name(); + auto importName = parser::SourceTextModuleRecord::DEFAULT_EXTERNAL_NAME; + auto *entry = moduleRecord->NewEntry( + localName, importName, moduleRequestIdx); + moduleRecord->AddImportEntry(entry); + break; + } + case ir::AstNodeType::IMPORT_NAMESPACE_SPECIFIER: { + auto localName = it->AsImportNamespaceSpecifier()->Local()->Name(); + auto *entry = moduleRecord->NewEntry( + localName, moduleRequestIdx); + moduleRecord->AddStarImportEntry(entry); + break; + } + case ir::AstNodeType::IMPORT_SPECIFIER: { + auto localName = it->AsImportSpecifier()->Local()->Name(); + auto importName = it->AsImportSpecifier()->Imported()->Name(); + auto *entry = moduleRecord->NewEntry( + localName, importName, moduleRequestIdx); + moduleRecord->AddImportEntry(entry); + break; + } + default: { + ThrowSyntaxError("Unexpected astNode type", it->Start()); + } + } + } +} + +void ParserImpl::AddExportNamedEntryItem(const ArenaVector &specifiers, + const ir::StringLiteral *source) +{ + auto moduleRecord = GetSourceTextModuleRecord(); + ASSERT(moduleRecord != nullptr); + if (source) { + auto moduleRequestIdx = moduleRecord->AddModuleRequest(source->Str()); + + for (auto *it : specifiers) { + auto exportSpecifier = it->AsExportSpecifier(); + auto importName = exportSpecifier->Local()->Name(); + auto exportName = exportSpecifier->Exported()->Name(); + auto *entry = moduleRecord->NewEntry( + exportName, importName, moduleRequestIdx); + if (!moduleRecord->AddIndirectExportEntry(entry)) { + ThrowSyntaxError("Duplicate export name of '" + exportName.Mutf8() + "'", + exportSpecifier->Start()); + } + } + } else { + for (auto *it : specifiers) { + auto exportSpecifier = it->AsExportSpecifier(); + auto exportName = exportSpecifier->Exported()->Name(); + auto localName = exportSpecifier->Local()->Name(); + auto *entry = moduleRecord->NewEntry(exportName, localName); + if (!moduleRecord->AddLocalExportEntry(entry)) { + ThrowSyntaxError("Duplicate export name of '" + exportName.Mutf8() + "'", + exportSpecifier->Start()); + } + } + } +} + +void ParserImpl::AddExportStarEntryItem(const lexer::SourcePosition &startLoc, const ir::StringLiteral *source, + const ir::Identifier *exported) +{ + auto moduleRecord = GetSourceTextModuleRecord(); + ASSERT(moduleRecord != nullptr); + ASSERT(source != nullptr); + auto moduleRequestIdx = moduleRecord->AddModuleRequest(source->Str()); + + if (exported != nullptr) { + /* Transform [NamespaceExport] into [NamespaceImport] & [LocalExport] + * e.g. export * as ns from 'test.js' + * ---> + * import * as [internalName] from 'test.js' + * export { [internalName] as ns } + */ + auto namespaceExportInternalName = GetNamespaceExportInternalName(); + auto *decl = Binder()->AddDecl(startLoc, binder::DeclarationFlags::EXPORT, + namespaceExportInternalName); + decl->BindNode(exported); + + auto *importEntry = moduleRecord->NewEntry( + namespaceExportInternalName, moduleRequestIdx); + auto *exportEntry = moduleRecord->NewEntry( + exported->Name(), namespaceExportInternalName); + moduleRecord->AddStarImportEntry(importEntry); + if (!moduleRecord->AddLocalExportEntry(exportEntry)) { + ThrowSyntaxError("Duplicate export name of '" + exported->Name().Mutf8() + "'", exported->Start()); + } + return; + } + + auto *entry = moduleRecord->NewEntry(moduleRequestIdx); + moduleRecord->AddStarExportEntry(entry); +} + +void ParserImpl::AddExportDefaultEntryItem(const ir::AstNode *declNode) +{ + ASSERT(declNode != nullptr); + if (declNode->IsTSInterfaceDeclaration()) { + return; + } + + auto moduleRecord = GetSourceTextModuleRecord(); + ASSERT(moduleRecord != nullptr); + util::StringView exportName = parser::SourceTextModuleRecord::DEFAULT_EXTERNAL_NAME; + util::StringView localName = parser::SourceTextModuleRecord::DEFAULT_LOCAL_NAME; + if (declNode->IsFunctionDeclaration() || declNode->IsClassDeclaration()) { + localName = declNode->IsFunctionDeclaration() ? declNode->AsFunctionDeclaration()->Function()->GetName() : + declNode->AsClassDeclaration()->Definition()->GetName(); + } + + ASSERT(!localName.Empty()); + auto *entry = moduleRecord->NewEntry(exportName, localName); + if (!moduleRecord->AddLocalExportEntry(entry)) { + ThrowSyntaxError("Duplicate export name of '" + exportName.Mutf8() + "'", declNode->Start()); + } +} + +void ParserImpl::AddExportLocalEntryItem(const ir::Statement *declNode) +{ + ASSERT(declNode != nullptr); + auto moduleRecord = GetSourceTextModuleRecord(); + ASSERT(moduleRecord != nullptr); + if (declNode->IsVariableDeclaration()) { + auto declarators = declNode->AsVariableDeclaration()->Declarators(); + for (auto *decl : declarators) { + std::vector bindings = util::Helpers::CollectBindingNames(decl->Id()); + for (const auto *binding : bindings) { + auto *entry = moduleRecord->NewEntry( + binding->Name(), binding->Name()); + if (!moduleRecord->AddLocalExportEntry(entry)) { + ThrowSyntaxError("Duplicate export name of '" + binding->Name().Mutf8() + "'", binding->Start()); + } + } + } + } + if (declNode->IsFunctionDeclaration() || declNode->IsClassDeclaration()) { + auto name = declNode->IsFunctionDeclaration() ? + declNode->AsFunctionDeclaration()->Function()->Id() : + declNode->AsClassDeclaration()->Definition()->Ident(); + if (name == nullptr) { + ThrowSyntaxError("A class or function declaration without the default modifier mush have a name.", + declNode->Start()); + } + auto *entry = moduleRecord->NewEntry(name->Name(), name->Name()); + if (!moduleRecord->AddLocalExportEntry(entry)) { + ThrowSyntaxError("Duplicate export name of '" + name->Name().Mutf8() + "'", name->Start()); + } + } +} + ir::ExportDefaultDeclaration *ParserImpl::ParseExportDefaultDeclaration(const lexer::SourcePosition &startLoc, ArenaVector decorators, bool isExportEquals) @@ -1910,27 +2097,31 @@ ir::ExportDefaultDeclaration *ParserImpl::ParseExportDefaultDeclaration(const le ThrowSyntaxError("Decorators are not valid here.", decorators.front()->Start()); } - ExportDeclarationContext exportDeclCtx(Binder()); - if (lexer_->GetToken().Type() == lexer::TokenType::KEYW_FUNCTION) { - declNode = ParseFunctionDeclaration(true); + declNode = ParseFunctionDeclaration(true, ParserStatus::EXPORT_REACHED); } else if (lexer_->GetToken().Type() == lexer::TokenType::KEYW_CLASS) { - declNode = ParseClassDeclaration(false, std::move(decorators)); + declNode = ParseClassDeclaration(false, std::move(decorators), false, false, true); } else if (lexer_->GetToken().IsAsyncModifier()) { lexer_->NextToken(); // eat `async` keyword - declNode = ParseFunctionDeclaration(false, ParserStatus::ASYNC_FUNCTION); + declNode = ParseFunctionDeclaration(false, ParserStatus::ASYNC_FUNCTION | ParserStatus::EXPORT_REACHED); } else if (Extension() == ScriptExtension::TS && lexer_->GetToken().KeywordType() == lexer::TokenType::KEYW_INTERFACE) { declNode = ParseTsInterfaceDeclaration(); } else { declNode = ParseExpression(); + Binder()->AddDecl(declNode->Start(), binder::DeclarationFlags::EXPORT, + parser::SourceTextModuleRecord::DEFAULT_LOCAL_NAME); eatSemicolon = true; } + // record default export entry + if (!isExportEquals) { + AddExportDefaultEntryItem(declNode); + } + lexer::SourcePosition endLoc = declNode->End(); auto *exportDeclaration = AllocNode(declNode, isExportEquals); exportDeclaration->SetRange({startLoc, endLoc}); - exportDeclCtx.BindExportDecl(exportDeclaration); if (eatSemicolon) { ConsumeSemicolon(exportDeclaration); @@ -1968,10 +2159,11 @@ ir::ExportAllDeclaration *ParserImpl::ParseExportAllDeclaration(const lexer::Sou ir::StringLiteral *source = ParseFromClause(); lexer::SourcePosition endLoc = source->End(); + // record export star entry + AddExportStarEntryItem(startLoc, source, exported); + auto *exportDeclaration = AllocNode(source, exported); exportDeclaration->SetRange({startLoc, endLoc}); - auto *decl = Binder()->AddDecl(startLoc, exported ? exported->Name() : "*", "*"); - Binder()->GetScope()->AsModuleScope()->AddExportDecl(exportDeclaration, decl); ConsumeSemicolon(exportDeclaration); @@ -1983,7 +2175,6 @@ ir::ExportNamedDeclaration *ParserImpl::ParseExportNamedSpecifiers(const lexer:: lexer_->NextToken(lexer::LexerNextTokenFlags::KEYWORD_TO_IDENT); // eat `{` character ArenaVector specifiers(Allocator()->Adapter()); - binder::ModuleScope::ExportDeclList exportDecls(Allocator()->Adapter()); while (lexer_->GetToken().Type() != lexer::TokenType::PUNCTUATOR_RIGHT_BRACE) { if (lexer_->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) { @@ -2010,8 +2201,6 @@ ir::ExportNamedDeclaration *ParserImpl::ParseExportNamedSpecifiers(const lexer:: specifier->SetRange({local->Start(), exported->End()}); specifiers.push_back(specifier); - auto *decl = Binder()->AddDecl(startLoc, exported->Name(), local->Name(), specifier); - exportDecls.push_back(decl); if (lexer_->GetToken().Type() == lexer::TokenType::PUNCTUATOR_COMMA) { lexer_->NextToken(lexer::LexerNextTokenFlags::KEYWORD_TO_IDENT); // eat comma @@ -2027,9 +2216,11 @@ ir::ExportNamedDeclaration *ParserImpl::ParseExportNamedSpecifiers(const lexer:: source = ParseFromClause(); } + // record ExportEntry + AddExportNamedEntryItem(specifiers, source); + auto *exportDeclaration = AllocNode(source, std::move(specifiers)); exportDeclaration->SetRange({startLoc, endPos}); - Binder()->GetScope()->AsModuleScope()->AddExportDecl(exportDeclaration, std::move(exportDecls)); ConsumeSemicolon(exportDeclaration); return exportDeclaration; @@ -2052,27 +2243,26 @@ ir::ExportNamedDeclaration *ParserImpl::ParseNamedExportDeclaration(const lexer: ThrowSyntaxError("Decorators are not valid here.", decorators.front()->Start()); } - ExportDeclarationContext exportDeclCtx(Binder()); - + VariableParsingFlags flag = VariableParsingFlags::EXPORTED; switch (lexer_->GetToken().Type()) { case lexer::TokenType::KEYW_VAR: { - decl = ParseVariableDeclaration(VariableParsingFlags::VAR, isDeclare); + decl = ParseVariableDeclaration(flag | VariableParsingFlags::VAR, isDeclare); break; } case lexer::TokenType::KEYW_CONST: { - decl = ParseVariableDeclaration(VariableParsingFlags::CONST, isDeclare); + decl = ParseVariableDeclaration(flag | VariableParsingFlags::CONST, isDeclare); break; } case lexer::TokenType::KEYW_LET: { - decl = ParseVariableDeclaration(VariableParsingFlags::LET, isDeclare); + decl = ParseVariableDeclaration(flag | VariableParsingFlags::LET, isDeclare); break; } case lexer::TokenType::KEYW_FUNCTION: { - decl = ParseFunctionDeclaration(false, ParserStatus::NO_OPTS, isDeclare); + decl = ParseFunctionDeclaration(false, ParserStatus::EXPORT_REACHED, isDeclare); break; } case lexer::TokenType::KEYW_CLASS: { - decl = ParseClassDeclaration(true, std::move(decorators), isDeclare); + decl = ParseClassDeclaration(true, std::move(decorators), isDeclare, false, true); break; } case lexer::TokenType::LITERAL_IDENT: { @@ -2123,7 +2313,7 @@ ir::ExportNamedDeclaration *ParserImpl::ParseNamedExportDeclaration(const lexer: } lexer_->NextToken(); // eat `async` keyword - decl = ParseFunctionDeclaration(false, ParserStatus::ASYNC_FUNCTION); + decl = ParseFunctionDeclaration(false, ParserStatus::ASYNC_FUNCTION | ParserStatus::EXPORT_REACHED); } } @@ -2131,13 +2321,13 @@ ir::ExportNamedDeclaration *ParserImpl::ParseNamedExportDeclaration(const lexer: ConsumeSemicolon(decl); } + AddExportLocalEntryItem(decl); + lexer::SourcePosition endLoc = decl->End(); ArenaVector specifiers(Allocator()->Adapter()); auto *exportDeclaration = AllocNode(decl, std::move(specifiers)); exportDeclaration->SetRange({startLoc, endLoc}); - exportDeclCtx.BindExportDecl(exportDeclaration); - return exportDeclaration; } @@ -2158,13 +2348,13 @@ ir::Statement *ParserImpl::ParseExportDeclaration(StatementParsingFlags flags, lexer_->NextToken(); // eat `export` keyword switch (lexer_->GetToken().Type()) { - case lexer::TokenType::KEYW_DEFAULT: { + case lexer::TokenType::KEYW_DEFAULT: { // export default Id return ParseExportDefaultDeclaration(startLoc, std::move(decorators)); } - case lexer::TokenType::PUNCTUATOR_MULTIPLY: { + case lexer::TokenType::PUNCTUATOR_MULTIPLY: { // export * ... return ParseExportAllDeclaration(startLoc); } - case lexer::TokenType::PUNCTUATOR_LEFT_BRACE: { + case lexer::TokenType::PUNCTUATOR_LEFT_BRACE: { // export { ... } ... return ParseExportNamedSpecifiers(startLoc); } case lexer::TokenType::KEYW_IMPORT: { @@ -2177,7 +2367,7 @@ ir::Statement *ParserImpl::ParseExportDeclaration(StatementParsingFlags flags, [[fallthrough]]; } - default: { + default: { // export [var] id ir::ExportNamedDeclaration *exportDecl = ParseNamedExportDeclaration(startLoc, std::move(decorators)); if (Extension() == ScriptExtension::TS && exportDecl->Decl()->IsVariableDeclaration() && @@ -2208,7 +2398,9 @@ void ParserImpl::ParseNameSpaceImport(ArenaVector *specifiers) specifier->SetRange({namespaceStart, lexer_->GetToken().End()}); specifiers->push_back(specifier); - Binder()->AddDecl(namespaceStart, "*", local->Name(), specifier); + auto *decl = Binder()->AddDecl(namespaceStart, binder::DeclarationFlags::NAMESPACE_IMPORT, + local->Name()); + decl->BindNode(specifier); lexer_->NextToken(); // eat local name } @@ -2265,7 +2457,7 @@ void ParserImpl::ParseNamedImportSpecifiers(ArenaVector *specifie specifier->SetRange({imported->Start(), local->End()}); specifiers->push_back(specifier); - Binder()->AddDecl(imported->Start(), imported->Name(), local->Name(), specifier); + Binder()->AddDecl(local->Start(), binder::DeclarationFlags::IMPORT, local->Name()); if (lexer_->GetToken().Type() == lexer::TokenType::PUNCTUATOR_COMMA) { lexer_->NextToken(lexer::LexerNextTokenFlags::KEYWORD_TO_IDENT); // eat comma @@ -2334,9 +2526,7 @@ ir::AstNode *ParserImpl::ParseImportDefaultSpecifier(ArenaVector specifier->SetRange(specifier->Local()->Range()); specifiers->push_back(specifier); - Binder()->AddDecl(local->Start(), "default", local->Name(), specifier); - - lexer_->NextToken(); // eat specifier name + Binder()->AddDecl(local->Start(), binder::DeclarationFlags::IMPORT, local->Name()); if (lexer_->GetToken().Type() == lexer::TokenType::PUNCTUATOR_COMMA) { lexer_->NextToken(); // eat comma @@ -2373,6 +2563,7 @@ ir::AstNode *ParserImpl::ParseImportSpecifiers(ArenaVector *speci { ASSERT(specifiers->empty()); + // import [default] from 'source' if (lexer_->GetToken().Type() == lexer::TokenType::LITERAL_IDENT) { ir::AstNode *astNode = ParseImportDefaultSpecifier(specifiers); if (astNode != nullptr) { @@ -2390,8 +2581,6 @@ ir::AstNode *ParserImpl::ParseImportSpecifiers(ArenaVector *speci ir::Statement *ParserImpl::ParseImportDeclaration(StatementParsingFlags flags) { - ImportDeclarationContext importCtx(Binder()); - if (Extension() == ScriptExtension::JS) { if (!(flags & StatementParsingFlags::GLOBAL)) { ThrowSyntaxError("'import' and 'export' may only appear at the top level"); @@ -2403,6 +2592,7 @@ ir::Statement *ParserImpl::ParseImportDeclaration(StatementParsingFlags flags) } char32_t nextChar = lexer_->Lookahead(); + // dynamic import || import.meta if (nextChar == LEX_CHAR_LEFT_PAREN || nextChar == LEX_CHAR_DOT) { return ParseExpressionStatement(); } @@ -2423,14 +2613,16 @@ ir::Statement *ParserImpl::ParseImportDeclaration(StatementParsingFlags flags) return astNode->AsTSImportEqualsDeclaration(); } source = ParseFromClause(true); + AddImportEntryItem(source, &specifiers); } else { + // import 'source' source = ParseFromClause(false); + AddImportEntryItem(source, nullptr); } lexer::SourcePosition endLoc = source->End(); auto *importDeclaration = AllocNode(source, std::move(specifiers)); importDeclaration->SetRange({startLoc, endLoc}); - importCtx.BindImportDecl(importDeclaration); ConsumeSemicolon(importDeclaration); diff --git a/es2panda/util/helpers.cpp b/es2panda/util/helpers.cpp index 5d16303d8a9dad7448281c6eb9c66120d4e75a7c..d1e0477f13d4791daffa6789c6bbe2a14d0557f9 100644 --- a/es2panda/util/helpers.cpp +++ b/es2panda/util/helpers.cpp @@ -30,6 +30,7 @@ #include #include #include +#include namespace panda::es2panda::util { @@ -300,7 +301,7 @@ util::StringView Helpers::FunctionName(const ir::ScriptFunction *func) } if (func->Parent()->IsFunctionDeclaration()) { - return "*default*"; + return parser::SourceTextModuleRecord::DEFAULT_EXTERNAL_NAME; } const ir::AstNode *parent = func->Parent()->Parent(); @@ -361,6 +362,9 @@ util::StringView Helpers::FunctionName(const ir::ScriptFunction *func) break; } + case ir::AstNodeType::EXPORT_DEFAULT_DECLARATION: { + return parser::SourceTextModuleRecord::DEFAULT_EXTERNAL_NAME; + } default: break; } diff --git a/test262/run_sunspider.py b/test262/run_sunspider.py index f2802d8e9ce496b343169d14f4a2332b014b56a7..806504521f0205c63d3c7967ebe54d2b28d982db 100755 --- a/test262/run_sunspider.py +++ b/test262/run_sunspider.py @@ -213,6 +213,15 @@ class ArkProgram(): self.arch_root = self.args.ark_arch_root + def gen_dependency_abc(self, dependency): + cmd_args = [] + output_file = os.path.splitext(os.path.join(BASE_OUT_DIR, os.path.split(dependency)[1]))[0] + output_abc = f"{output_file}.abc" + frontend_tool = ARK_FRONTEND_BINARY_LIST[1] + cmd_args = [frontend_tool, dependency, '--output', output_abc, '--module'] + proc = subprocess.Popen(cmd_args) + proc.wait() + def gen_abc(self): js_file = self.js_file file_name_pre = os.path.splitext(js_file)[0] @@ -222,6 +231,13 @@ class ArkProgram(): mod_opt_index = 0 cmd_args = [] frontend_tool = self.ark_frontend_binary + + # pre-generate the dependencies' abc when ark_frontend is [es2panda] + if file_name in self.module_list and self.ark_frontend == ARK_FRONTEND_LIST[1]: + dependencies = collect_module_dependencies(js_file, os.path.join(TEST_ES2021_DIR, "language/module-code"), []) + for dependency in list(set(dependencies)): + self.gen_dependency_abc(dependency) + if self.ark_frontend == ARK_FRONTEND_LIST[0]: mod_opt_index = 3 cmd_args = ['node', '--expose-gc', frontend_tool, diff --git a/test262/utils.py b/test262/utils.py index 3263aef7f4e666dc773e27827866e2a3f3e1df70..17501580a8f112b324f9144a1f88065f6673bc73 100755 --- a/test262/utils.py +++ b/test262/utils.py @@ -25,6 +25,7 @@ import datetime import time import shutil import platform +import re TERM_NORMAL = '\033[0m' TERM_YELLOW = '\033[1;33m' @@ -157,3 +158,25 @@ def npm_install(cwd): cmd = ['npm', 'install'] ret = run_cmd_cwd(cmd, cwd) assert not ret, f"\n error: Failed to 'npm install'" + +def search_dependency(file, directory): + for root, dirs, files in os.walk(directory, topdown=True): + for f in files: + if f == file: + return os.path.join(root, f) + +def collect_module_dependencies(file, directory, traversedDependencies): + dependencies = [] + traversedDependencies.append(file) + with open(file, 'r') as f: + content = f.read() + result_arr = re.findall(r'(import|from)(?:\s*)(\'(\.\/.*)\'|"(\.\/.*)")', content) + for result in result_arr: + specifier = result[2] if len(result[2]) != 0 else result[3] + if re.search(r'\S+_FIXTURE.js$', specifier): + dependency = search_dependency(specifier.lstrip('./'), directory) + if dependency not in traversedDependencies: + dependencies.extend(collect_module_dependencies(dependency, directory, list(set(traversedDependencies)))) + dependencies.append(dependency) + + return dependencies \ No newline at end of file