From 126d4520d1ea4ceccca13671cfd3e23b280e2352 Mon Sep 17 00:00:00 2001 From: shawn_hu_ls Date: Thu, 1 Sep 2022 01:13:01 +0800 Subject: [PATCH] Support type extractor for es2abc Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/I5QADU?from=project-issue Signed-off-by: shawn_hu_ls --- es2panda/BUILD.gn | 4 + es2panda/aot/options.cpp | 19 + es2panda/binder/binder.cpp | 7 +- es2panda/binder/binder.h | 9 +- es2panda/compiler/base/lexenv.cpp | 26 + es2panda/compiler/core/compilerContext.cpp | 15 +- es2panda/compiler/core/compilerContext.h | 24 +- es2panda/compiler/core/compilerImpl.cpp | 21 +- es2panda/compiler/core/compilerImpl.h | 2 + es2panda/compiler/core/emitter/emitter.cpp | 188 +-- es2panda/compiler/core/emitter/emitter.h | 11 +- .../core/emitter/typeExtractorEmitter.cpp | 162 +++ .../core/emitter/typeExtractorEmitter.h | 61 + es2panda/compiler/core/pandagen.cpp | 30 +- es2panda/compiler/core/pandagen.h | 19 + es2panda/compiler/core/regAllocator.cpp | 12 + es2panda/compiler/core/regAllocator.h | 18 + es2panda/es2panda.h | 2 + es2panda/ir/astDump.cpp | 47 +- es2panda/ir/astDump.h | 4 + es2panda/ir/base/classDefinition.h | 10 + es2panda/ir/expressions/literal.cpp | 18 + es2panda/ir/expressions/literal.h | 1 + es2panda/ir/ts/tsParameterProperty.h | 5 + es2panda/ir/ts/tsTypeParameter.cpp | 4 +- .../parser/module/sourceTextModuleRecord.cpp | 1 + .../parser/module/sourceTextModuleRecord.h | 44 +- es2panda/parser/parserImpl.cpp | 13 +- es2panda/parser/program/program.cpp | 4 +- es2panda/parser/program/program.h | 9 +- es2panda/parser/statementParser.cpp | 43 +- es2panda/parser/transformer/transformer.cpp | 8 +- es2panda/parser/transformer/transformer.h | 2 +- .../typescript/extractor/typeExtractor.cpp | 740 +++++++++++ es2panda/typescript/extractor/typeExtractor.h | 96 ++ .../typescript/extractor/typeRecorder.cpp | 345 ++++++ es2panda/typescript/extractor/typeRecorder.h | 137 ++ es2panda/typescript/extractor/typeSystem.h | 1098 +++++++++++++++++ es2panda/util/dumper.cpp | 9 +- 39 files changed, 3136 insertions(+), 132 deletions(-) create mode 100644 es2panda/compiler/core/emitter/typeExtractorEmitter.cpp create mode 100644 es2panda/compiler/core/emitter/typeExtractorEmitter.h create mode 100644 es2panda/typescript/extractor/typeExtractor.cpp create mode 100644 es2panda/typescript/extractor/typeExtractor.h create mode 100644 es2panda/typescript/extractor/typeRecorder.cpp create mode 100644 es2panda/typescript/extractor/typeRecorder.h create mode 100644 es2panda/typescript/extractor/typeSystem.h diff --git a/es2panda/BUILD.gn b/es2panda/BUILD.gn index 18dc68c07c..119844163f 100644 --- a/es2panda/BUILD.gn +++ b/es2panda/BUILD.gn @@ -37,6 +37,7 @@ es2panda_src = [ "compiler/core/emitter/commonjs.cpp", "compiler/core/emitter/emitter.cpp", "compiler/core/emitter/moduleRecordEmitter.cpp", + "compiler/core/emitter/typeExtractorEmitter.cpp", "compiler/core/envScope.cpp", "compiler/core/function.cpp", "compiler/core/inlineCache.cpp", @@ -208,6 +209,8 @@ es2panda_src = [ "typescript/core/typeElaborationContext.cpp", "typescript/core/typeRelation.cpp", "typescript/core/util.cpp", + "typescript/extractor/typeExtractor.cpp", + "typescript/extractor/typeRecorder.cpp", "typescript/types/anyType.cpp", "typescript/types/arrayType.cpp", "typescript/types/bigintLiteralType.cpp", @@ -274,6 +277,7 @@ config("es2abc_config_src") { "./lexer/regexp", "./typescript", "./typescript/types", + "./typescript/types/extractor", ] cflags = [ "-fexceptions" ] diff --git a/es2panda/aot/options.cpp b/es2panda/aot/options.cpp index e905e7850b..d581dab53a 100644 --- a/es2panda/aot/options.cpp +++ b/es2panda/aot/options.cpp @@ -128,6 +128,10 @@ bool Options::Parse(int argc, const char **argv) panda::PandArg opEnableTypeCheck("enable-type-check", false, "Check the type in ts after parse"); panda::PandArg opDumpAst("dump-ast", false, "Dump the parsed AST"); + // type extractor + panda::PandArg opTypeExtractor("type-extractor", false, "Enable type extractor for typescript"); + panda::PandArg opTypeDtsBuiltin("type-dts-builtin", false, "Enable builtin type extractor for .d.ts file"); + // compiler panda::PandArg opDumpAssembly("dump-assembly", false, "Dump pandasm"); panda::PandArg opDebugInfo("debug-info", false, "Compile with debug info"); @@ -168,6 +172,8 @@ bool Options::Parse(int argc, const char **argv) argparser_->Add(&opDumpAst); argparser_->Add(&opParseOnly); argparser_->Add(&opEnableTypeCheck); + argparser_->Add(&opTypeExtractor); + argparser_->Add(&opTypeDtsBuiltin); argparser_->Add(&opDumpAssembly); argparser_->Add(&opDebugInfo); argparser_->Add(&opDumpDebugInfo); @@ -251,12 +257,25 @@ bool Options::Parse(int argc, const char **argv) scriptKind_ = es2panda::parser::ScriptKind::SCRIPT; } + auto parseTypeExtractor = [&opTypeExtractor, &opTypeDtsBuiltin, this]() { + compilerOptions_.typeExtractor = opTypeExtractor.GetValue(); + if (compilerOptions_.typeExtractor) { + compilerOptions_.typeDtsBuiltin = opTypeDtsBuiltin.GetValue(); +#ifndef NDEBUG + std::cout << "[LOG]TypeExtractor is enabled, type-dts-builtin: " << + compilerOptions_.typeDtsBuiltin << std::endl; +#endif + } + }; + std::string extension = inputExtension.GetValue(); if (!extension.empty()) { if (extension == "js") { extension_ = es2panda::ScriptExtension::JS; } else if (extension == "ts") { extension_ = es2panda::ScriptExtension::TS; + // Type Extractor is only enabled for TypeScript + parseTypeExtractor(); } else if (extension == "as") { extension_ = es2panda::ScriptExtension::AS; } else { diff --git a/es2panda/binder/binder.cpp b/es2panda/binder/binder.cpp index 8aeac2307e..47b85b9967 100644 --- a/es2panda/binder/binder.cpp +++ b/es2panda/binder/binder.cpp @@ -210,7 +210,7 @@ void Binder::LookupIdentReference(ir::Identifier *ident) ident->SetVariable(res.variable); } -void Binder::BuildFunction(FunctionScope *funcScope, util::StringView name) +void Binder::BuildFunction(FunctionScope *funcScope, util::StringView name, const ir::ScriptFunction *func) { functionScopes_.push_back(funcScope); @@ -226,6 +226,9 @@ void Binder::BuildFunction(FunctionScope *funcScope, util::StringView name) ss << std::string(program_->FormatedRecordName()); uint32_t idx = functionNameIndex_++; ss << "#" << std::to_string(idx) << "#"; + if (name == ANONYMOUS_FUNC_NAME && func != nullptr) { + anonymousFunctionNames_[func] = util::UString(ss.str(), Allocator()).View(); + } if (funcNameWithoutDot && funcNameWithoutBackslash) { ss << name; } @@ -245,7 +248,7 @@ void Binder::BuildScriptFunction(Scope *outerScope, const ir::ScriptFunction *sc } ASSERT(scope_->IsFunctionScope() || scope_->IsTSModuleScope()); - BuildFunction(scope_->AsFunctionVariableScope(), util::Helpers::FunctionName(scriptFunc)); + BuildFunction(scope_->AsFunctionVariableScope(), util::Helpers::FunctionName(scriptFunc), scriptFunc); } void Binder::BuildVarDeclaratorId(const ir::AstNode *parent, ir::AstNode *childNode) diff --git a/es2panda/binder/binder.h b/es2panda/binder/binder.h index ebbbf6cb92..414ff63344 100644 --- a/es2panda/binder/binder.h +++ b/es2panda/binder/binder.h @@ -47,6 +47,7 @@ public: : program_(program), functionScopes_(Allocator()->Adapter()), functionNames_(Allocator()->Adapter()), + anonymousFunctionNames_(Allocator()->Adapter()), variableNames_(Allocator()->Adapter()), extension_(extension) { @@ -117,6 +118,11 @@ public: program_ = program; } + const ArenaUnorderedMap &AnonymousFunctionNames() const + { + return anonymousFunctionNames_; + } + void AddDeclarationName(const util::StringView &name); bool HasVariableName(const util::StringView &name) const; @@ -173,7 +179,7 @@ private: void AddMandatoryParams(); void AssignIndexToModuleVariable(); - void BuildFunction(FunctionScope *funcScope, util::StringView name); + void BuildFunction(FunctionScope *funcScope, util::StringView name, const ir::ScriptFunction *func = nullptr); void BuildScriptFunction(Scope *outerScope, const ir::ScriptFunction *scriptFunc); void BuildClassDefinition(ir::ClassDefinition *classDef); void LookupReference(const util::StringView &name); @@ -198,6 +204,7 @@ private: ArenaVector functionScopes_; ResolveBindingOptions bindingOptions_; ArenaSet functionNames_; + ArenaUnorderedMap anonymousFunctionNames_; ArenaSet variableNames_; size_t functionNameIndex_ {1}; ResolveBindingFlags bindingFlags_ {ResolveBindingFlags::ALL}; diff --git a/es2panda/compiler/base/lexenv.cpp b/es2panda/compiler/base/lexenv.cpp index f2142e3e45..a4f2764e40 100644 --- a/es2panda/compiler/base/lexenv.cpp +++ b/es2panda/compiler/base/lexenv.cpp @@ -16,9 +16,11 @@ #include "lexenv.h" #include +#include #include #include #include +#include namespace panda::es2panda::compiler { @@ -111,6 +113,30 @@ static void ExpandStoreNormalVar(PandaGen *pg, const ir::AstNode *node, const bi CheckConstAssignment(pg, node, local); } + auto context = pg->Context(); + if (context->IsTypeExtractorEnabled()) { + auto typeIndex = context->TypeRecorder()->GetVariableTypeIndex(local); + if (typeIndex != extractor::TypeRecorder::PRIMITIVETYPE_ANY) { + pg->StoreAccumulatorWithType(node, typeIndex, localReg); +#ifndef NDEBUG + std::cout << "[LOG]Local vreg in variable has type index: " << local->Name() << "@" << + local << " | " << typeIndex << std::endl; +#endif + return; + } + typeIndex = context->TypeRecorder()->GetNodeTypeIndex(node); + if (typeIndex != extractor::TypeRecorder::PRIMITIVETYPE_ANY) { + pg->StoreAccumulatorWithType(node, typeIndex, localReg); +#ifndef NDEBUG + std::cout << "[LOG]Local vreg in declnode has type index: " << local->Name() << "@" << + local << " | " << typeIndex << std::endl; +#endif + return; + } +#ifndef NDEBUG + std::cout << "[WARNING]Local vreg lose type index: " << local->Name() << "@" << local << std::endl; +#endif + } pg->StoreAccumulator(node, localReg); } diff --git a/es2panda/compiler/core/compilerContext.cpp b/es2panda/compiler/core/compilerContext.cpp index f3549110db..96ea7c675c 100644 --- a/es2panda/compiler/core/compilerContext.cpp +++ b/es2panda/compiler/core/compilerContext.cpp @@ -16,15 +16,24 @@ #include "compilerContext.h" #include +#include namespace panda::es2panda::compiler { CompilerContext::CompilerContext(binder::Binder *binder, bool isDebug, bool isDebuggerEvaluateExpressionMode, - bool isMergeAbc, std::string sourceFile, util::StringView recordName) + bool isMergeAbc, bool isTypeExtractorEnabled, std::string sourceFile, + util::StringView recordName) : binder_(binder), isDebug_(isDebug), isDebuggerEvaluateExpressionMode_(isDebuggerEvaluateExpressionMode), - isMergeAbc_(isMergeAbc), sourceFile_(sourceFile), emitter_(std::make_unique(this)), - recordName_(recordName) + isMergeAbc_(isMergeAbc), isTypeExtractorEnabled_(isTypeExtractorEnabled), sourceFile_(sourceFile), + emitter_(std::make_unique(this)), recordName_(recordName) { } +void CompilerContext::SetTypeRecorder(extractor::TypeRecorder *recorder) +{ + ASSERT(emitter_ != nullptr); + emitter_->FillTypeLiteralBuffers(recorder); + recorder_ = recorder; +} + } // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/compilerContext.h b/es2panda/compiler/core/compilerContext.h index 64fa445db1..b43cbcadcd 100644 --- a/es2panda/compiler/core/compilerContext.h +++ b/es2panda/compiler/core/compilerContext.h @@ -18,6 +18,9 @@ #include #include + +#include +#include #include #include @@ -28,6 +31,10 @@ namespace panda::es2panda::binder { class Binder; } // namespace panda::es2panda::binder +namespace panda::es2panda::extractor { +class TypeRecorder; +} // namespace panda::es2panda::extractor + namespace panda::es2panda::compiler { class DebugInfo; @@ -36,7 +43,7 @@ class Emitter; class CompilerContext { public: CompilerContext(binder::Binder *binder, bool isDebug, bool isDebuggerEvaluateExpressionMode, - bool isMergeAbc, std::string sourceFile, util::StringView recordName); + bool isMergeAbc, bool isTypeExtractorEnabled, std::string sourceFile, util::StringView recordName); NO_COPY_SEMANTIC(CompilerContext); NO_MOVE_SEMANTIC(CompilerContext); ~CompilerContext() = default; @@ -102,6 +109,18 @@ public: return recordName_; } + bool IsTypeExtractorEnabled() const + { + return isTypeExtractorEnabled_; + } + + extractor::TypeRecorder *TypeRecorder() const + { + return recorder_; + } + + void SetTypeRecorder(extractor::TypeRecorder *recorder); + private: binder::Binder *binder_; int32_t literalBufferIdx_ {0}; @@ -109,6 +128,9 @@ private: bool isDebug_; bool isDebuggerEvaluateExpressionMode_; bool isMergeAbc_; + // For Type Extractor + bool isTypeExtractorEnabled_; + extractor::TypeRecorder *recorder_ {}; std::string sourceFile_; std::unique_ptr emitter_; util::Hotfix *hotfixHelper_ {nullptr}; diff --git a/es2panda/compiler/core/compilerImpl.cpp b/es2panda/compiler/core/compilerImpl.cpp index f0e0448c2e..3225df90d8 100644 --- a/es2panda/compiler/core/compilerImpl.cpp +++ b/es2panda/compiler/core/compilerImpl.cpp @@ -15,6 +15,7 @@ #include "compilerImpl.h" +#include #include #include #include @@ -39,12 +40,30 @@ panda::pandasm::Program *CompilerImpl::Compile(parser::Program *program, const e const std::string &debugInfoSourceFile) { CompilerContext context(program->Binder(), options.isDebug, options.isDebuggerEvaluateExpressionMode, - options.mergeAbc, debugInfoSourceFile, program->RecordName()); + options.mergeAbc, options.typeExtractor, debugInfoSourceFile, program->RecordName()); if (hotfixHelper_ != nullptr) { context.AddHotfixHelper(hotfixHelper_); } + ArenaAllocator localAllocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); + + if (options.typeExtractor) { + ASSERT(program->Extension() == ScriptExtension::TS); + + auto rootNode = context.Binder()->TopScope()->Node()->AsBlockStatement(); + extractor_ = std::make_unique(rootNode, program->IsDtsFile(), + options.typeDtsBuiltin, &localAllocator, &context); + extractor_->StartTypeExtractor(program); + + context.SetTypeRecorder(extractor_->Recorder()); + } + + if (program->Extension() == ScriptExtension::TS) { + context.GetEmitter()->FillTypeInfoRecord(options.typeExtractor, + options.typeExtractor ? extractor_->Recorder()->GetTypeSummaryIndex() : 0); + } + queue_ = new CompileFuncQueue(threadCount_, &context); queue_->Schedule(); diff --git a/es2panda/compiler/core/compilerImpl.h b/es2panda/compiler/core/compilerImpl.h index 0b551ba58c..fbf7e6253a 100644 --- a/es2panda/compiler/core/compilerImpl.h +++ b/es2panda/compiler/core/compilerImpl.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -55,6 +56,7 @@ private: size_t threadCount_ {0}; CompileFuncQueue *queue_ {nullptr}; util::Hotfix *hotfixHelper_ {nullptr}; + std::unique_ptr extractor_ {}; }; } // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/emitter/emitter.cpp b/es2panda/compiler/core/emitter/emitter.cpp index 50487465e6..114d43fdce 100644 --- a/es2panda/compiler/core/emitter/emitter.cpp +++ b/es2panda/compiler/core/emitter/emitter.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -86,79 +87,7 @@ void FunctionEmitter::GenIcSize() void FunctionEmitter::GenBufferLiterals(const LiteralBuffer *buff) { - auto &[idx, array] = literalBuffers_.emplace_back(); - idx = buff->Index(); - constexpr size_t ARRAY_EXPANSION = 2; - array.reserve(buff->Literals().size() * ARRAY_EXPANSION); - - for (const auto *literal : buff->Literals()) { - panda::pandasm::LiteralArray::Literal valueLit; - panda::pandasm::LiteralArray::Literal tagLit; - - ir::LiteralTag tag = literal->Tag(); - - switch (tag) { - case ir::LiteralTag::BOOLEAN: { - valueLit.tag_ = panda::panda_file::LiteralTag::BOOL; - valueLit.value_ = literal->GetBoolean(); - break; - } - case ir::LiteralTag::INTEGER: { - valueLit.tag_ = panda::panda_file::LiteralTag::INTEGER; - valueLit.value_ = literal->GetInt(); - break; - } - case ir::LiteralTag::DOUBLE: { - valueLit.tag_ = panda::panda_file::LiteralTag::DOUBLE; - valueLit.value_ = literal->GetDouble(); - break; - } - case ir::LiteralTag::STRING: { - valueLit.tag_ = panda::panda_file::LiteralTag::STRING; - valueLit.value_ = literal->GetString().Mutf8(); - break; - } - case ir::LiteralTag::ACCESSOR: { - valueLit.tag_ = panda::panda_file::LiteralTag::ACCESSOR; - valueLit.value_ = static_cast(0); - break; - } - case ir::LiteralTag::METHOD: { - valueLit.tag_ = panda::panda_file::LiteralTag::METHOD; - valueLit.value_ = literal->GetMethod().Mutf8(); - break; - } - case ir::LiteralTag::METHODAFFILIATE: { - valueLit.tag_ = panda::panda_file::LiteralTag::METHODAFFILIATE; - valueLit.value_ = literal->GetMethodAffiliate(); - break; - } - case ir::LiteralTag::GENERATOR_METHOD: { - valueLit.tag_ = panda::panda_file::LiteralTag::GENERATORMETHOD; - valueLit.value_ = literal->GetMethod().Mutf8(); - break; - } - case ir::LiteralTag::LITERALBUFFERINDEX: { - valueLit.tag_ = panda::panda_file::LiteralTag::LITERALBUFFERINDEX; - valueLit.value_ = literal->GetInt(); - break; - } - // TODO: support ir::LiteralTag::ASYNC_GENERATOR_METHOD - case ir::LiteralTag::NULL_VALUE: { - valueLit.tag_ = panda::panda_file::LiteralTag::NULLVALUE; - valueLit.value_ = static_cast(0); - break; - } - default: - break; - } - - tagLit.tag_ = panda::panda_file::LiteralTag::TAGVALUE; - tagLit.value_ = static_cast(valueLit.tag_); - - array.emplace_back(tagLit); - array.emplace_back(valueLit); - } + Emitter::GenBufferLiterals(literalBuffers_, buff); } util::StringView FunctionEmitter::SourceCode() const @@ -260,6 +189,10 @@ void FunctionEmitter::GenFunctionInstructions() ins->Transform(&pandaIns); GenInstructionDebugInfo(ins, &pandaIns); } + + if (pg_->Context()->IsTypeExtractorEnabled()) { + TypeExtractorEmitter(pg_, func_); + } } void FunctionEmitter::GenFunctionCatchTables() @@ -354,6 +287,11 @@ Emitter::Emitter(const CompilerContext *context) prog_ = new panda::pandasm::Program(); prog_->lang = LANG_EXT; + // For Type Extractor + // Global record to show type extractor is enabled or not + GenTypeInfoRecord(); + GenESTypeAnnotationRecord(); + if (context->IsMergeAbc()) { auto recordName = context->Binder()->Program()->FormatedRecordName().Mutf8(); rec_ = new panda::pandasm::Record(recordName.substr(0, recordName.find_last_of('.')), LANG_EXT); @@ -371,6 +309,21 @@ Emitter::~Emitter() delete prog_; } +void Emitter::GenTypeInfoRecord() const +{ + auto typeInfoRecord = panda::pandasm::Record(TypeExtractorEmitter::TYPE_INFO_RECORD, LANG_EXT); + typeInfoRecord.metadata->SetAccessFlags(panda::ACC_PUBLIC); + prog_->record_table.emplace(typeInfoRecord.name, std::move(typeInfoRecord)); +} + +void Emitter::GenESTypeAnnotationRecord() const +{ + auto typeAnnotationRecord = panda::pandasm::Record(TypeExtractorEmitter::TYPE_ANNOTATION, LANG_EXT); + typeAnnotationRecord.metadata->SetAttribute("external"); + typeAnnotationRecord.metadata->SetAccessFlags(panda::ACC_ANNOTATION); + prog_->record_table.emplace(typeAnnotationRecord.name, std::move(typeAnnotationRecord)); +} + void Emitter::AddFunction(FunctionEmitter *func, CompilerContext *context) { std::lock_guard lock(m_); @@ -429,6 +382,95 @@ void Emitter::AddSourceTextModuleRecord(ModuleRecordEmitter *module, CompilerCon prog_->literalarray_table.emplace(static_cast(moduleLiteral), std::move(literalArrayInstance)); } +void Emitter::FillTypeInfoRecord(bool typeFlag, int64_t typeSummaryIndex) const +{ + TypeExtractorEmitter::GenTypeInfoRecord(prog_, typeFlag, typeSummaryIndex); +} + +void Emitter::FillTypeLiteralBuffers(const extractor::TypeRecorder *recorder) const +{ + TypeExtractorEmitter::GenTypeLiteralBuffers(prog_, recorder); +} + +// static +void Emitter::GenBufferLiterals(ArenaVector>> &literalBuffers, + const LiteralBuffer *buff) +{ + auto &[idx, array] = literalBuffers.emplace_back(); + idx = buff->Index(); + constexpr size_t ARRAY_EXPANSION = 2; + array.reserve(buff->Literals().size() * ARRAY_EXPANSION); + + for (const auto *literal : buff->Literals()) { + panda::pandasm::LiteralArray::Literal valueLit; + panda::pandasm::LiteralArray::Literal tagLit; + + ir::LiteralTag tag = literal->Tag(); + + switch (tag) { + case ir::LiteralTag::BOOLEAN: { + valueLit.tag_ = panda::panda_file::LiteralTag::BOOL; + valueLit.value_ = literal->GetBoolean(); + break; + } + case ir::LiteralTag::INTEGER: { + valueLit.tag_ = panda::panda_file::LiteralTag::INTEGER; + valueLit.value_ = literal->GetInt(); + break; + } + case ir::LiteralTag::DOUBLE: { + valueLit.tag_ = panda::panda_file::LiteralTag::DOUBLE; + valueLit.value_ = literal->GetDouble(); + break; + } + case ir::LiteralTag::STRING: { + valueLit.tag_ = panda::panda_file::LiteralTag::STRING; + valueLit.value_ = literal->GetString().Mutf8(); + break; + } + case ir::LiteralTag::ACCESSOR: { + valueLit.tag_ = panda::panda_file::LiteralTag::ACCESSOR; + valueLit.value_ = static_cast(0); + break; + } + case ir::LiteralTag::METHOD: { + valueLit.tag_ = panda::panda_file::LiteralTag::METHOD; + valueLit.value_ = literal->GetMethod().Mutf8(); + break; + } + case ir::LiteralTag::METHODAFFILIATE: { + valueLit.tag_ = panda::panda_file::LiteralTag::METHODAFFILIATE; + valueLit.value_ = literal->GetMethodAffiliate(); + break; + } + case ir::LiteralTag::GENERATOR_METHOD: { + valueLit.tag_ = panda::panda_file::LiteralTag::GENERATORMETHOD; + valueLit.value_ = literal->GetMethod().Mutf8(); + break; + } + case ir::LiteralTag::LITERALBUFFERINDEX: { + valueLit.tag_ = panda::panda_file::LiteralTag::LITERALBUFFERINDEX; + valueLit.value_ = literal->GetInt(); + break; + } + // TODO: support ir::LiteralTag::ASYNC_GENERATOR_METHOD + case ir::LiteralTag::NULL_VALUE: { + valueLit.tag_ = panda::panda_file::LiteralTag::NULLVALUE; + valueLit.value_ = static_cast(0); + break; + } + default: + break; + } + + tagLit.tag_ = panda::panda_file::LiteralTag::TAGVALUE; + tagLit.value_ = static_cast(valueLit.tag_); + + array.emplace_back(tagLit); + array.emplace_back(valueLit); + } +} + void Emitter::DumpAsm(const panda::pandasm::Program *prog) { auto &ss = std::cout; diff --git a/es2panda/compiler/core/emitter/emitter.h b/es2panda/compiler/core/emitter/emitter.h index f461ece869..d766694ff7 100644 --- a/es2panda/compiler/core/emitter/emitter.h +++ b/es2panda/compiler/core/emitter/emitter.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,8 @@ class Label; class IRNode; class CompilerContext; +using Literal = panda::pandasm::LiteralArray::Literal; + class FunctionEmitter { public: explicit FunctionEmitter(ArenaAllocator *allocator, const PandaGen *pg); @@ -91,7 +94,7 @@ private: const PandaGen *pg_; panda::pandasm::Function *func_ {}; - ArenaVector>> literalBuffers_; + ArenaVector>> literalBuffers_; size_t offset_ {0}; }; @@ -104,12 +107,18 @@ public: void AddFunction(FunctionEmitter *func, CompilerContext *context); void AddSourceTextModuleRecord(ModuleRecordEmitter *module, CompilerContext *context); + void FillTypeInfoRecord(bool typeFlag, int64_t typeSummaryIndex) const; + void FillTypeLiteralBuffers(const extractor::TypeRecorder *recorder) const; + static void GenBufferLiterals(ArenaVector>> &literalBuffers, + const LiteralBuffer *buff); static void DumpAsm(const panda::pandasm::Program *prog); panda::pandasm::Program *Finalize(bool dumpDebugInfo, util::Hotfix *hotfixHelper); private: void SetCommonjsField(bool isCommonjs); void GenCommonjsRecord() const; + void GenTypeInfoRecord() const; + void GenESTypeAnnotationRecord() const; std::mutex m_; panda::pandasm::Program *prog_; diff --git a/es2panda/compiler/core/emitter/typeExtractorEmitter.cpp b/es2panda/compiler/core/emitter/typeExtractorEmitter.cpp new file mode 100644 index 0000000000..3ac375b8ab --- /dev/null +++ b/es2panda/compiler/core/emitter/typeExtractorEmitter.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "typeExtractorEmitter.h" + +#include +#include +#include +#include + +namespace panda::es2panda::compiler { + +using AnnotationData = panda::pandasm::AnnotationData; +using AnnotationElement = panda::pandasm::AnnotationElement; +using ArrayValue = panda::pandasm::ArrayValue; +using Field = panda::pandasm::Field; +using ScalarValue = panda::pandasm::ScalarValue; +using ValueType = panda::pandasm::Value::Type; +using Type = panda::pandasm::Type; + +TypeExtractorEmitter::TypeExtractorEmitter(const PandaGen *pg, panda::pandasm::Function *func) : pg_(pg), func_(func) +{ + GenFunctionTypeInfo(); + if (func->name == binder::Binder::MAIN_FUNC_NAME) { + if (pg_->Context()->TypeRecorder()->ExportType().size() > 0U) { + GenExportTypeInfo(); + } + if (pg_->Context()->TypeRecorder()->DeclareType().size() > 0U) { + GenDeclareTypeInfo(); + } + } +} + +void TypeExtractorEmitter::GenFunctionTypeInfo() const +{ + std::vector typedInsns; + typedInsns.reserve(pg_->TypedInsns().size()); + size_t index = 0U; + for (const auto *ins : pg_->Insns()) { + auto t = pg_->TypedInsns().find(ins); + if (t != pg_->TypedInsns().end()) { + const auto &insn = func_->ins[index]; + int64_t typeIndex = t->second; + int32_t orderIndex = index; + if (typeIndex < extractor::TypeRecorder::PRIMITIVETYPE_ANY) { + // Decode type and order index for params + typeIndex = -(typeIndex + 1); + orderIndex = static_cast(func_->regs_num) - static_cast(insn.regs[1]) - 1; + } + if (typeIndex > extractor::TypeRecorder::PRIMITIVETYPE_ANY) { + ScalarValue insnOrder(ScalarValue::Create(orderIndex)); + typedInsns.emplace_back(std::move(insnOrder)); + ScalarValue insnType(ScalarValue::Create(typeIndex)); + typedInsns.emplace_back(std::move(insnType)); +#ifndef NDEBUG + std::cout << "[LOG]" << func_->name << ": " << insn.ToString("", true, func_->regs_num) << " | " + << orderIndex << " | " << typeIndex << std::endl; +#endif + } + } + index++; + } + + AnnotationData funcTypeAnnotation(TypeExtractorEmitter::TYPE_ANNOTATION); + AnnotationElement funcTypeAnnotationElement(TypeExtractorEmitter::TYPE_INSTRUCTION, + std::make_unique(ArrayValue(ValueType::U32, typedInsns))); + funcTypeAnnotation.AddElement(std::move(funcTypeAnnotationElement)); + func_->metadata->AddAnnotations({ funcTypeAnnotation }); +} + +template +static void GenTypeInfo(const M &map, F &funcTypeAnnotation) +{ + std::string symbolStr; + std::string symbolTypeStr; + if constexpr (isExport) { + symbolStr = TypeExtractorEmitter::EXPORTED_SYMBOLS; + symbolTypeStr = TypeExtractorEmitter::EXPORTED_SYMBOL_TYPES; + } else { + symbolStr = TypeExtractorEmitter::DECLARED_SYMBOLS; + symbolTypeStr = TypeExtractorEmitter::DECLARED_SYMBOL_TYPES; + } + + std::vector symbolElements; + std::vector symbolTypeElements; + for (const auto &t : map) { + ScalarValue symbol(ScalarValue::Create(t.first)); + symbolElements.emplace_back(std::move(symbol)); + ScalarValue symbolType(ScalarValue::Create(t.second)); + symbolTypeElements.emplace_back(std::move(symbolType)); + } + + AnnotationElement funcSymbolsElements(symbolStr, + std::make_unique(ArrayValue(ValueType::STRING, symbolElements))); + AnnotationElement funcSymbolTypeElement(symbolTypeStr, + std::make_unique(ArrayValue(ValueType::U32, symbolTypeElements))); + + funcTypeAnnotation.AddElement(std::move(funcSymbolsElements)); + funcTypeAnnotation.AddElement(std::move(funcSymbolTypeElement)); +} + +void TypeExtractorEmitter::GenExportTypeInfo() const +{ + AnnotationData funcTypeAnnotation(TypeExtractorEmitter::TYPE_ANNOTATION); + GenTypeInfo(pg_->Context()->TypeRecorder()->ExportType(), funcTypeAnnotation); + func_->metadata->AddAnnotations({ funcTypeAnnotation }); +} + +void TypeExtractorEmitter::GenDeclareTypeInfo() const +{ + AnnotationData funcTypeAnnotation(TypeExtractorEmitter::TYPE_ANNOTATION); + GenTypeInfo(pg_->Context()->TypeRecorder()->DeclareType(), funcTypeAnnotation); + func_->metadata->AddAnnotations({ funcTypeAnnotation }); +} + +// static +void TypeExtractorEmitter::GenTypeInfoRecord(panda::pandasm::Program *prog, bool typeFlag, int64_t typeSummaryIndex) +{ + constexpr const auto LANG_EXT = panda::pandasm::extensions::Language::ECMASCRIPT; + auto &typeInfoRecord = prog->record_table.find(TypeExtractorEmitter::TYPE_INFO_RECORD)->second; + + auto typeFlagField = Field(LANG_EXT); + typeFlagField.name = TypeExtractorEmitter::TYPE_FLAG; + typeFlagField.type = Type("u8", 0); + typeFlagField.metadata->SetValue(ScalarValue::Create(static_cast(typeFlag))); + typeInfoRecord.field_list.emplace_back(std::move(typeFlagField)); + + auto typeSummaryIndexField = Field(LANG_EXT); + typeSummaryIndexField.name = TypeExtractorEmitter::TYPE_SUMMARY; + typeSummaryIndexField.type = Type("u32", 0); + typeSummaryIndexField.metadata->SetValue(ScalarValue::Create( + static_cast(typeSummaryIndex))); + typeInfoRecord.field_list.emplace_back(std::move(typeSummaryIndexField)); +} + +void TypeExtractorEmitter::GenTypeLiteralBuffers(panda::pandasm::Program *prog, + const extractor::TypeRecorder *recorder) +{ + ArenaVector>> literalBuffers(recorder->Allocator()->Adapter()); + for (const auto *buff : recorder->BuffStorage()) { + Emitter::GenBufferLiterals(literalBuffers, buff); + } + + for (auto &[idx, buf] : literalBuffers) { + auto literalArrayInstance = panda::pandasm::LiteralArray(std::move(buf)); + prog->literalarray_table.emplace(std::to_string(idx), std::move(literalArrayInstance)); + } +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/emitter/typeExtractorEmitter.h b/es2panda/compiler/core/emitter/typeExtractorEmitter.h new file mode 100644 index 0000000000..e82b585cf0 --- /dev/null +++ b/es2panda/compiler/core/emitter/typeExtractorEmitter.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ES2PANDA_COMPILER_CORE_EMITTER_TYPEEXTRACTOR_EMITTER_H +#define ES2PANDA_COMPILER_CORE_EMITTER_TYPEEXTRACTOR_EMITTER_H + +#include + +#include +#include + +#include +#include + +namespace panda::es2panda::compiler { + +class TypeExtractorEmitter { +public: + explicit TypeExtractorEmitter(const PandaGen *pg, panda::pandasm::Function *func); + ~TypeExtractorEmitter() = default; + NO_COPY_SEMANTIC(TypeExtractorEmitter); + NO_MOVE_SEMANTIC(TypeExtractorEmitter); + + static void GenTypeInfoRecord(panda::pandasm::Program *prog, bool typeFlag, int64_t typeSummaryIndex); + static void GenTypeLiteralBuffers(panda::pandasm::Program *prog, const extractor::TypeRecorder *recorder); + + static constexpr const char *TYPE_INFO_RECORD = "_ESTypeInfoRecord"; + static constexpr const char *TYPE_ANNOTATION = "_ESTypeAnnotation"; + static constexpr const char *TYPE_INSTRUCTION = "_TypeOfInstruction"; + + static constexpr const char *TYPE_FLAG = "typeFlag"; + static constexpr const char *TYPE_SUMMARY = "typeSummaryIndex"; + static constexpr const char *EXPORTED_SYMBOLS = "exportedSymbols"; + static constexpr const char *EXPORTED_SYMBOL_TYPES = "exportedSymbolTypes"; + static constexpr const char *DECLARED_SYMBOLS = "declaredSymbols"; + static constexpr const char *DECLARED_SYMBOL_TYPES = "declaredSymbolTypes"; + +private: + const PandaGen *pg_; + panda::pandasm::Function *func_; + + void GenFunctionTypeInfo() const; + void GenExportTypeInfo() const; + void GenDeclareTypeInfo() const; +}; + +} // namespace panda::es2panda::compiler + +#endif // ES2PANDA_COMPILER_CORE_EMITTER_TYPEEXTRACTOR_EMITTER_H diff --git a/es2panda/compiler/core/pandagen.cpp b/es2panda/compiler/core/pandagen.cpp index 8c040db141..ac08480cd9 100644 --- a/es2panda/compiler/core/pandagen.cpp +++ b/es2panda/compiler/core/pandagen.cpp @@ -42,6 +42,7 @@ #include #include #include +#include namespace panda::es2panda::compiler { @@ -214,9 +215,17 @@ void PandaGen::CopyFunctionArguments(const ir::AstNode *node) for (const auto *param : topScope_->ParamScope()->Params()) { if (param->LexicalBound()) { StoreLexicalVar(node, 0, param->LexIdx(), targetReg++); - } else { - MoveVreg(node, param->Vreg(), targetReg++); + continue; + } + if (context_->IsTypeExtractorEnabled()) { + auto typeIndex = context_->TypeRecorder()->GetVariableTypeIndex(param); + if (typeIndex != extractor::TypeRecorder::PRIMITIVETYPE_ANY) { + // Simply encode type index for params + MoveVregWithType(node, -(typeIndex + 1), param->Vreg(), targetReg++); + continue; + } } + MoveVreg(node, param->Vreg(), targetReg++); } } @@ -352,6 +361,11 @@ void PandaGen::StoreAccumulator(const ir::AstNode *node, VReg vreg) ra_.Emit(node, vreg); } +void PandaGen::StoreAccumulatorWithType(const ir::AstNode *node, int64_t typeIndex, VReg vreg) +{ + ra_.EmitWithType(node, typeIndex, vreg); +} + void PandaGen::LoadAccFromArgs(const ir::AstNode *node) { const auto *varScope = scope_->AsVariableScope(); @@ -446,7 +460,12 @@ void PandaGen::TryLoadGlobalByName(const ir::AstNode *node, const util::StringVi if (isDebuggerEvaluateExpressionMode()) { LoadObjByNameViaDebugger(node, name, true); } else { - sa_.Emit(node, 0, name); + int64_t typeIndex = extractor::TypeExtractor::GetBuiltinTypeIndex(name); + if (context_->IsTypeExtractorEnabled() && typeIndex != extractor::TypeRecorder::PRIMITIVETYPE_ANY) { + sa_.EmitWithType(node, typeIndex, 0, name); + } else { + sa_.Emit(node, 0, name); + } } strings_.insert(name); } @@ -670,6 +689,11 @@ void PandaGen::MoveVreg(const ir::AstNode *node, VReg vd, VReg vs) ra_.Emit(node, vd, vs); } +void PandaGen::MoveVregWithType(const ir::AstNode *node, int64_t typeIndex, VReg vd, VReg vs) +{ + ra_.EmitWithType(node, typeIndex, vd, vs); +} + void PandaGen::SetLabel([[maybe_unused]] const ir::AstNode *node, Label *label) { sa_.AddLabel(label); diff --git a/es2panda/compiler/core/pandagen.h b/es2panda/compiler/core/pandagen.h index 1334ff54f6..e1b5ea4dfd 100644 --- a/es2panda/compiler/core/pandagen.h +++ b/es2panda/compiler/core/pandagen.h @@ -85,6 +85,7 @@ public: scope_(topScope_), rootNode_(scope->Node()), insns_(allocator_->Adapter()), + typedInsns_(allocator_->Adapter()), catchList_(allocator_->Adapter()), strings_(allocator_->Adapter()), buffStorage_(allocator_->Adapter()), @@ -102,6 +103,11 @@ public: return allocator_; } + inline CompilerContext *Context() const + { + return context_; + } + const ArenaSet &Strings() const { return strings_; @@ -137,6 +143,16 @@ public: return insns_; } + ArenaMap &TypedInsns() + { + return typedInsns_; + } + + const ArenaMap &TypedInsns() const + { + return typedInsns_; + } + VReg AllocReg() { return usedRegs_++; @@ -235,6 +251,7 @@ public: void StConstToGlobalRecord(const ir::AstNode *node, const util::StringView &name); void StoreAccumulator(const ir::AstNode *node, VReg vreg); + void StoreAccumulatorWithType(const ir::AstNode *node, int64_t typeIndex, VReg vreg); void LoadAccFromArgs(const ir::AstNode *node); void LoadObjProperty(const ir::AstNode *node, VReg obj, const Operand &prop); @@ -266,6 +283,7 @@ public: void LoadConst(const ir::AstNode *node, Constant id); void StoreConst(const ir::AstNode *node, VReg reg, Constant id); void MoveVreg(const ir::AstNode *node, VReg vd, VReg vs); + void MoveVregWithType(const ir::AstNode *node, int64_t typeIndex, VReg vd, VReg vs); void SetLabel(const ir::AstNode *node, Label *label); void Branch(const ir::AstNode *node, class Label *label); @@ -452,6 +470,7 @@ private: binder::Scope *scope_; const ir::AstNode *rootNode_; ArenaList insns_; + ArenaMap typedInsns_; ArenaVector catchList_; ArenaSet strings_; ArenaVector buffStorage_; diff --git a/es2panda/compiler/core/regAllocator.cpp b/es2panda/compiler/core/regAllocator.cpp index 01f3f81eba..7e9ce2ca72 100644 --- a/es2panda/compiler/core/regAllocator.cpp +++ b/es2panda/compiler/core/regAllocator.cpp @@ -39,6 +39,12 @@ void AllocatorBase::UpdateIcSlot(IRNode *node) pg_->IncreaseCurrentSlot(inc); } +void SimpleAllocator::Run(IRNode *ins, int64_t typeIndex) +{ + PushBack(ins); + pg_->TypedInsns()[ins] = typeIndex; +} + // FrontAllocator FrontAllocator::FrontAllocator(PandaGen *pg) @@ -119,6 +125,12 @@ void RegAllocator::Run(IRNode *ins) spillMap_.clear(); } +void RegAllocator::Run(IRNode *ins, int64_t typeIndex) +{ + Run(ins); + pg_->TypedInsns()[ins] = typeIndex; +} + // RangeRegAllocator void RangeRegAllocator::Run(IRNode *ins, VReg rangeStart, size_t argCount) diff --git a/es2panda/compiler/core/regAllocator.h b/es2panda/compiler/core/regAllocator.h index 9edf1d8cf3..dd00e365b7 100644 --- a/es2panda/compiler/core/regAllocator.h +++ b/es2panda/compiler/core/regAllocator.h @@ -92,6 +92,16 @@ public: { Add(node, std::forward(args)...); } + + template + void EmitWithType(const ir::AstNode *node, int64_t typeIndex, Args &&... args) + { + auto *ins = Alloc(node, std::forward(args)...); + Run(ins, typeIndex); + } + +private: + void Run(IRNode *ins, int64_t typeIndex); }; class FrontAllocator : public AllocatorBase { @@ -162,8 +172,16 @@ public: Run(ins); } + template + void EmitWithType(const ir::AstNode *node, int64_t typeIndex, Args &&... args) + { + auto *ins = Alloc(node, std::forward(args)...); + Run(ins, typeIndex); + } + private: void Run(IRNode *ins); + void Run(IRNode *ins, int64_t typeIndex); }; class RangeRegAllocator : public RegAllocatorBase { diff --git a/es2panda/es2panda.h b/es2panda/es2panda.h index 1c78c18e41..766d15c093 100644 --- a/es2panda/es2panda.h +++ b/es2panda/es2panda.h @@ -75,6 +75,8 @@ struct CompilerOptions { bool dumpLiteralBuffer {false}; bool isDebuggerEvaluateExpressionMode {false}; bool mergeAbc {false}; + bool typeExtractor {false}; + bool typeDtsBuiltin {false}; ScriptExtension extension {}; int fileThreadCount {0}; int functionThreadCount {0}; diff --git a/es2panda/ir/astDump.cpp b/es2panda/ir/astDump.cpp index acf37c7a81..3898260ff2 100644 --- a/es2panda/ir/astDump.cpp +++ b/es2panda/ir/astDump.cpp @@ -27,6 +27,18 @@ AstDumper::AstDumper(const BlockStatement *program, util::StringView sourceCode) SerializeObject(reinterpret_cast(program)); } +AstDumper::AstDumper(const ir::AstNode *node) : indent_(0), dumpNodeOnly_(true) +{ + SerializeNode(node); +} + +void AstDumper::SerializeNode(const ir::AstNode *node) +{ + Wrap([this, node]() -> void { + node->Dump(this); + }); +} + void AstDumper::Add(std::initializer_list props) { AddList>(props); @@ -85,7 +97,11 @@ void AstDumper::Serialize(const AstDumper::Property &prop) } else if (std::holds_alternative(value)) { SerializeNumber(std::get(value)); } else if (std::holds_alternative(value)) { - SerializeObject(std::get(value)); + if (dumpNodeOnly_) { + SerializeNode(std::get(value)); + } else { + SerializeObject(std::get(value)); + } } else if (std::holds_alternative>(value)) { SerializeArray(std::get>(value)); } else if (std::holds_alternative(value)) { @@ -104,6 +120,9 @@ void AstDumper::SerializeToken(lexer::TokenType token) void AstDumper::SerializePropKey(const char *str) { + if (dumpNodeOnly_) { + return; + } ss_ << std::endl; Indent(); SerializeString(str); @@ -173,10 +192,13 @@ void AstDumper::SerializeArray(std::vector array) Wrap( [this, &array]() -> void { for (auto it = array.begin(); it != array.end(); ++it) { - ss_ << std::endl; - Indent(); - - SerializeObject(*it); + if (dumpNodeOnly_) { + SerializeNode(*it); + } else { + ss_ << std::endl; + Indent(); + SerializeObject(*it); + } if (std::next(it) != array.end()) { ss_ << ','; @@ -197,12 +219,17 @@ void AstDumper::SerializeObject(const ir::AstNode *object) void AstDumper::Wrap(const WrapperCb &cb, char delimStart, char delimEnd) { ss_ << delimStart; - indent_++; - cb(); - ss_ << std::endl; - indent_--; - Indent(); + if (dumpNodeOnly_) { + cb(); + } else { + indent_++; + cb(); + ss_ << std::endl; + indent_--; + Indent(); + } + ss_ << delimEnd; } diff --git a/es2panda/ir/astDump.h b/es2panda/ir/astDump.h index ece0397bab..8981a2967d 100644 --- a/es2panda/ir/astDump.h +++ b/es2panda/ir/astDump.h @@ -146,6 +146,9 @@ public: }; explicit AstDumper(const BlockStatement *program, util::StringView sourceCode); + explicit AstDumper(const ir::AstNode *node); + + void SerializeNode(const ir::AstNode *node); void Add(std::initializer_list props); void Add(const AstDumper::Property &prop); @@ -200,6 +203,7 @@ private: lexer::LineIndex index_; std::stringstream ss_; int32_t indent_; + bool dumpNodeOnly_ = false; }; } // namespace panda::es2panda::ir diff --git a/es2panda/ir/base/classDefinition.h b/es2panda/ir/base/classDefinition.h index a5210c2545..43ae00c6db 100644 --- a/es2panda/ir/base/classDefinition.h +++ b/es2panda/ir/base/classDefinition.h @@ -111,6 +111,16 @@ public: return body_; } + ArenaVector &Implements() + { + return implements_; + } + + const ArenaVector &Implements() const + { + return implements_; + } + MethodDefinition *Ctor() { ASSERT(ctor_ != nullptr); diff --git a/es2panda/ir/expressions/literal.cpp b/es2panda/ir/expressions/literal.cpp index 4a021f470f..26a1920a5a 100644 --- a/es2panda/ir/expressions/literal.cpp +++ b/es2panda/ir/expressions/literal.cpp @@ -15,6 +15,7 @@ #include "literal.h" +#include #include #include #include @@ -58,4 +59,21 @@ uint16_t Literal::GetMethodAffiliate() const return AsTaggedLiteral()->MethodAffiliate(); } +std::optional Literal::GetName() const +{ + if (IsStringLiteral()) { + return GetString(); + } + if (IsNumberLiteral()) { + return AsNumberLiteral()->Str(); + } + if (IsBigIntLiteral()) { + return AsBigIntLiteral()->Str(); + } + if (IsTaggedLiteral()) { + return AsTaggedLiteral()->Str(); + } + return std::nullopt; +} + } // namespace panda::es2panda::ir diff --git a/es2panda/ir/expressions/literal.h b/es2panda/ir/expressions/literal.h index 57c76603b3..bb3d5e5b8b 100644 --- a/es2panda/ir/expressions/literal.h +++ b/es2panda/ir/expressions/literal.h @@ -62,6 +62,7 @@ public: const util::StringView &GetString() const; const util::StringView &GetMethod() const; uint16_t GetMethodAffiliate() const; + std::optional GetName() const; protected: explicit Literal(AstNodeType type) : Expression(type) {} diff --git a/es2panda/ir/ts/tsParameterProperty.h b/es2panda/ir/ts/tsParameterProperty.h index 1addbb278f..4e655d76af 100644 --- a/es2panda/ir/ts/tsParameterProperty.h +++ b/es2panda/ir/ts/tsParameterProperty.h @@ -69,6 +69,11 @@ public: return parameter_; } + Expression *Parameter() + { + return parameter_; + } + void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; diff --git a/es2panda/ir/ts/tsTypeParameter.cpp b/es2panda/ir/ts/tsTypeParameter.cpp index fc60a331fc..ac4464df87 100644 --- a/es2panda/ir/ts/tsTypeParameter.cpp +++ b/es2panda/ir/ts/tsTypeParameter.cpp @@ -55,11 +55,11 @@ void TSTypeParameter::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder: name_ = std::get(cb(name_))->AsIdentifier(); if (constraint_) { - constraint_ = std::get(cb(constraint_))->AsIdentifier(); + constraint_ = std::get(cb(constraint_))->AsExpression(); } if (defaultType_) { - defaultType_ = std::get(cb(defaultType_))->AsIdentifier(); + defaultType_ = std::get(cb(defaultType_))->AsExpression(); } } diff --git a/es2panda/parser/module/sourceTextModuleRecord.cpp b/es2panda/parser/module/sourceTextModuleRecord.cpp index a00b38ee75..1883403bc2 100644 --- a/es2panda/parser/module/sourceTextModuleRecord.cpp +++ b/es2panda/parser/module/sourceTextModuleRecord.cpp @@ -25,6 +25,7 @@ namespace panda::es2panda::parser { moduleRequests_.emplace_back(source); } auto insertedRes = moduleRequestsMap_.insert(std::make_pair(source, moduleRequestsSize)); + moduleRequestsIdxMap_.insert(std::make_pair(insertedRes.first->second, source)); return insertedRes.first->second; } diff --git a/es2panda/parser/module/sourceTextModuleRecord.h b/es2panda/parser/module/sourceTextModuleRecord.h index c16e438161..cea4266aba 100644 --- a/es2panda/parser/module/sourceTextModuleRecord.h +++ b/es2panda/parser/module/sourceTextModuleRecord.h @@ -22,12 +22,17 @@ namespace panda::es2panda::binder { class ModuleScope; } +namespace panda::es2panda::ir { +class Identifier; +} // namespace panda::es2panda::ir + namespace panda::es2panda::parser { class SourceTextModuleRecord { public: explicit SourceTextModuleRecord(ArenaAllocator *allocator) : allocator_(allocator), moduleRequestsMap_(allocator_->Adapter()), + moduleRequestsIdxMap_(allocator_->Adapter()), moduleRequests_(allocator_->Adapter()), localExportEntries_(allocator_->Adapter()), regularImportEntries_(allocator_->Adapter()), @@ -45,11 +50,15 @@ public: 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) {} + const ir::Identifier *localId_; + const ir::Identifier *importId_; + + ImportEntry(const util::StringView localName, const util::StringView importName, int moduleRequestIdx, + const ir::Identifier *localId, const ir::Identifier *importId) + : moduleRequestIdx_(moduleRequestIdx), localName_(localName), importName_(importName), + localId_(localId), importId_(importId) {} + ImportEntry(const util::StringView localName, int moduleRequestIdx, const ir::Identifier *localId) + : moduleRequestIdx_(moduleRequestIdx), localName_(localName), localId_(localId) {} }; struct ExportEntry { @@ -57,15 +66,19 @@ public: util::StringView exportName_; util::StringView localName_; util::StringView importName_; + const ir::Identifier *exportId_; + const ir::Identifier *localId_; + const ir::Identifier *importId_; 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; - } + ExportEntry(const util::StringView exportName, const util::StringView localName, + const ir::Identifier *exportId, const ir::Identifier *localId) + : moduleRequestIdx_(-1), exportName_(exportName), localName_(localName), + exportId_(exportId), localId_(localId) {} + ExportEntry(const util::StringView exportName, const util::StringView importName, int moduleRequest, + const ir::Identifier *exportId, const ir::Identifier *importId) + : moduleRequestIdx_(moduleRequest), exportName_(exportName), importName_(importName), + exportId_(exportId), importId_(importId) {} }; template @@ -88,6 +101,7 @@ public: using ModuleRequestList = ArenaVector; using ModuleRequestMap = ArenaMap; + using ModuleRequestIdxMap = ArenaMap; using LocalExportEntryMap = ArenaMultiMap; using RegularImportEntryMap = ArenaMap; using NamespaceImportEntryList = ArenaVector; @@ -98,6 +112,11 @@ public: return moduleRequests_; } + const ModuleRequestIdxMap &GetModuleRequestIdxMap() const + { + return moduleRequestsIdxMap_; + } + const LocalExportEntryMap &GetLocalExportEntries() const { return localExportEntries_; @@ -133,6 +152,7 @@ private: ArenaAllocator *allocator_; ModuleRequestMap moduleRequestsMap_; + ModuleRequestIdxMap moduleRequestsIdxMap_; ModuleRequestList moduleRequests_; LocalExportEntryMap localExportEntries_; RegularImportEntryMap regularImportEntries_; diff --git a/es2panda/parser/parserImpl.cpp b/es2panda/parser/parserImpl.cpp index dd1b69eb33..2f076b4883 100644 --- a/es2panda/parser/parserImpl.cpp +++ b/es2panda/parser/parserImpl.cpp @@ -103,9 +103,20 @@ namespace panda::es2panda::parser { ParserImpl::ParserImpl(ScriptExtension extension) : program_(extension), context_(&program_) {} +template +bool IsSuffix(T const &filename, T const &suffix) +{ + return (filename.length() > suffix.length()) && + (filename.rfind(suffix) == (filename.length() - suffix.length())); +} + std::unique_ptr ParserImpl::InitLexer(const std::string &fileName, const std::string &source) { - program_.SetSource(source, fileName); + bool isDtsFile = false; + if (Extension() == ScriptExtension::TS) { + isDtsFile = IsSuffix(fileName, std::string(".d.ts")); + } + program_.SetSource(source, fileName, isDtsFile); auto lexer = std::make_unique(&context_); lexer_ = lexer.get(); diff --git a/es2panda/parser/program/program.cpp b/es2panda/parser/program/program.cpp index 8cd8d97df3..33c6b908a9 100644 --- a/es2panda/parser/program/program.cpp +++ b/es2panda/parser/program/program.cpp @@ -41,7 +41,8 @@ Program::Program(Program &&other) extension_(other.extension_), lineIndex_(other.lineIndex_), moduleRecord_(other.moduleRecord_), - hotfixHelper_(other.hotfixHelper_) + hotfixHelper_(other.hotfixHelper_), + isDtsFile_(other.isDtsFile_) { other.binder_ = nullptr; other.ast_ = nullptr; @@ -57,6 +58,7 @@ Program &Program::operator=(Program &&other) kind_ = other.kind_; extension_ = other.extension_; lineIndex_ = other.lineIndex_; + isDtsFile_ = other.isDtsFile_; other.ast_ = nullptr; diff --git a/es2panda/parser/program/program.h b/es2panda/parser/program/program.h index 650ce1978f..0bc4ec8590 100644 --- a/es2panda/parser/program/program.h +++ b/es2panda/parser/program/program.h @@ -115,11 +115,12 @@ public: ast_ = ast; } - void SetSource(const std::string &sourceCode, const std::string &sourceFile) + void SetSource(const std::string &sourceCode, const std::string &sourceFile, bool isDtsFile) { sourceCode_ = util::UString(sourceCode, Allocator()); sourceFile_ = util::UString(sourceFile, Allocator()); lineIndex_ = lexer::LineIndex(SourceCode()); + isDtsFile_ = isDtsFile; } void SetRecordName(const std::string &recordName) @@ -139,6 +140,11 @@ public: return hotfixHelper_; } + bool IsDtsFile() const + { + return isDtsFile_; + } + std::string Dump() const; void SetKind(ScriptKind kind); @@ -155,6 +161,7 @@ private: lexer::LineIndex lineIndex_ {}; SourceTextModuleRecord *moduleRecord_ {nullptr}; util::Hotfix *hotfixHelper_ {nullptr}; + bool isDtsFile_ {false}; }; } // namespace panda::es2panda::parser diff --git a/es2panda/parser/statementParser.cpp b/es2panda/parser/statementParser.cpp index ca9a82a784..aeb224a2e6 100644 --- a/es2panda/parser/statementParser.cpp +++ b/es2panda/parser/statementParser.cpp @@ -2029,23 +2029,27 @@ void ParserImpl::AddImportEntryItem(const ir::StringLiteral *source, const Arena case ir::AstNodeType::IMPORT_DEFAULT_SPECIFIER: { auto localName = it->AsImportDefaultSpecifier()->Local()->Name(); auto importName = parser::SourceTextModuleRecord::DEFAULT_EXTERNAL_NAME; + auto localId = it->AsImportDefaultSpecifier()->Local(); auto *entry = moduleRecord->NewEntry( - localName, importName, moduleRequestIdx); + localName, importName, moduleRequestIdx, localId, nullptr); moduleRecord->AddImportEntry(entry); break; } case ir::AstNodeType::IMPORT_NAMESPACE_SPECIFIER: { auto localName = it->AsImportNamespaceSpecifier()->Local()->Name(); + auto localId = it->AsImportNamespaceSpecifier()->Local(); auto *entry = moduleRecord->NewEntry( - localName, moduleRequestIdx); + localName, moduleRequestIdx, localId); moduleRecord->AddStarImportEntry(entry); break; } case ir::AstNodeType::IMPORT_SPECIFIER: { auto localName = it->AsImportSpecifier()->Local()->Name(); auto importName = it->AsImportSpecifier()->Imported()->Name(); + auto localId = it->AsImportSpecifier()->Local(); + auto importId = it->AsImportSpecifier()->Imported(); auto *entry = moduleRecord->NewEntry( - localName, importName, moduleRequestIdx); + localName, importName, moduleRequestIdx, localId, importId); moduleRecord->AddImportEntry(entry); break; } @@ -2068,8 +2072,10 @@ void ParserImpl::AddExportNamedEntryItem(const ArenaVectorAsExportSpecifier(); auto importName = exportSpecifier->Local()->Name(); auto exportName = exportSpecifier->Exported()->Name(); + auto importId = exportSpecifier->Local(); + auto exportId = exportSpecifier->Exported(); auto *entry = moduleRecord->NewEntry( - exportName, importName, moduleRequestIdx); + exportName, importName, moduleRequestIdx, exportId, importId); if (!moduleRecord->AddIndirectExportEntry(entry)) { ThrowSyntaxError("Duplicate export name of '" + exportName.Mutf8() + "'", exportSpecifier->Start()); @@ -2080,7 +2086,10 @@ void ParserImpl::AddExportNamedEntryItem(const ArenaVectorAsExportSpecifier(); auto exportName = exportSpecifier->Exported()->Name(); auto localName = exportSpecifier->Local()->Name(); - auto *entry = moduleRecord->NewEntry(exportName, localName); + auto exportId = exportSpecifier->Exported(); + auto localId = exportSpecifier->Local(); + auto *entry = moduleRecord->NewEntry( + exportName, localName, exportId, localId); if (!moduleRecord->AddLocalExportEntry(entry)) { ThrowSyntaxError("Duplicate export name of '" + exportName.Mutf8() + "'", exportSpecifier->Start()); @@ -2110,9 +2119,9 @@ void ParserImpl::AddExportStarEntryItem(const lexer::SourcePosition &startLoc, c decl->BindNode(exported); auto *importEntry = moduleRecord->NewEntry( - namespaceExportInternalName, moduleRequestIdx); + namespaceExportInternalName, moduleRequestIdx, nullptr); auto *exportEntry = moduleRecord->NewEntry( - exported->Name(), namespaceExportInternalName); + exported->Name(), namespaceExportInternalName, exported, nullptr); moduleRecord->AddStarImportEntry(importEntry); if (!moduleRecord->AddLocalExportEntry(exportEntry)) { ThrowSyntaxError("Duplicate export name of '" + exported->Name().Mutf8() + "'", exported->Start()); @@ -2141,7 +2150,8 @@ void ParserImpl::AddExportDefaultEntryItem(const ir::AstNode *declNode) } ASSERT(!localName.Empty()); - auto *entry = moduleRecord->NewEntry(exportName, localName); + auto *entry = moduleRecord->NewEntry( + exportName, localName, nullptr, nullptr); if (!moduleRecord->AddLocalExportEntry(entry)) { ThrowSyntaxError("Duplicate export name of '" + exportName.Mutf8() + "'", declNode->Start()); } @@ -2166,7 +2176,7 @@ void ParserImpl::AddExportLocalEntryItem(const ir::Statement *declNode, bool isT tsModuleScope->AddExportVariable(binding->Name()); } else { auto *entry = moduleRecord->NewEntry( - binding->Name(), binding->Name()); + binding->Name(), binding->Name(), binding, binding); if (!moduleRecord->AddLocalExportEntry(entry)) { ThrowSyntaxError("Duplicate export name of '" + binding->Name().Mutf8() + "'", binding->Start()); @@ -2186,8 +2196,8 @@ void ParserImpl::AddExportLocalEntryItem(const ir::Statement *declNode, bool isT if (isTsModule) { tsModuleScope->AddExportVariable(name->Name()); } else { - auto *entry = moduleRecord->NewEntry(name->Name(), - name->Name()); + auto *entry = moduleRecord->NewEntry( + name->Name(), name->Name(), name, name); if (!moduleRecord->AddLocalExportEntry(entry)) { ThrowSyntaxError("Duplicate export name of '" + name->Name().Mutf8() + "'", name->Start()); } @@ -2297,6 +2307,10 @@ ir::ExportNamedDeclaration *ParserImpl::ParseExportNamedSpecifiers(const lexer:: auto *local = AllocNode(lexer_->GetToken().Ident(), Allocator()); local->SetRange(lexer_->GetToken().Loc()); + if (Extension() == ScriptExtension::TS) { + local->SetReference(); + } + lexer_->NextToken(); // eat local name ir::Identifier *exported = nullptr; @@ -2571,7 +2585,9 @@ void ParserImpl::ParseNamedImportSpecifiers(ArenaVector *specifie specifier->SetRange({imported->Start(), local->End()}); specifiers->push_back(specifier); - Binder()->AddDecl(local->Start(), binder::DeclarationFlags::IMPORT, local->Name()); + auto *decl = Binder()->AddDecl(local->Start(), binder::DeclarationFlags::IMPORT, + local->Name()); + decl->BindNode(specifier); if (lexer_->GetToken().Type() == lexer::TokenType::PUNCTUATOR_COMMA) { lexer_->NextToken(lexer::LexerNextTokenFlags::KEYWORD_TO_IDENT); // eat comma @@ -2651,7 +2667,8 @@ ir::AstNode *ParserImpl::ParseImportDefaultSpecifier(ArenaVector specifier->SetRange(specifier->Local()->Range()); specifiers->push_back(specifier); - Binder()->AddDecl(local->Start(), binder::DeclarationFlags::IMPORT, local->Name()); + auto *decl = Binder()->AddDecl(local->Start(), binder::DeclarationFlags::IMPORT, local->Name()); + decl->BindNode(specifier); if (lexer_->GetToken().Type() == lexer::TokenType::PUNCTUATOR_COMMA) { lexer_->NextToken(); // eat comma diff --git a/es2panda/parser/transformer/transformer.cpp b/es2panda/parser/transformer/transformer.cpp index 19afc1c9fd..f15b88c083 100644 --- a/es2panda/parser/transformer/transformer.cpp +++ b/es2panda/parser/transformer/transformer.cpp @@ -196,7 +196,7 @@ ir::AstNode *Transformer::VisitTsImportEqualsDeclaration(ir::TSImportEqualsDecla if (node->IsExport()) { ArenaVector specifiers(Allocator()->Adapter()); res = AllocNode(res, std::move(specifiers)); - AddExportLocalEntryItem(name); + AddExportLocalEntryItem(name, node->Id()); } return res; } @@ -486,10 +486,10 @@ ir::Expression *Transformer::CreateTsModuleParam(util::StringView paramName, boo return id; } -void Transformer::AddExportLocalEntryItem(util::StringView name) +void Transformer::AddExportLocalEntryItem(util::StringView name, const ir::Identifier *identifier) { auto moduleRecord = GetSourceTextModuleRecord(); - auto *entry = moduleRecord->NewEntry(name, name); + auto *entry = moduleRecord->NewEntry(name, name, identifier, identifier); [[maybe_unused]] bool res = moduleRecord->AddLocalExportEntry(entry); ASSERT(res); } @@ -511,7 +511,7 @@ ir::UpdateNodes Transformer::VisitTsModuleDeclaration(ir::TSModuleDeclaration *n if (doExport) { ArenaVector specifiers(Allocator()->Adapter()); res.push_back(AllocNode(var, std::move(specifiers))); - AddExportLocalEntryItem(name); + AddExportLocalEntryItem(name, node->Name()->AsIdentifier()); } else { res.push_back(var); } diff --git a/es2panda/parser/transformer/transformer.h b/es2panda/parser/transformer/transformer.h index 4a444d71a1..a69ec50c46 100644 --- a/es2panda/parser/transformer/transformer.h +++ b/es2panda/parser/transformer/transformer.h @@ -66,7 +66,7 @@ private: util::StringView GetParamName(ir::TSModuleDeclaration *node, util::StringView name) const; binder::Scope *FindExportVariableInTsModuleScope(util::StringView name) const; binder::Variable *FindTSModuleVariable(const ir::Expression *node, binder::Scope *scope) const; - void AddExportLocalEntryItem(util::StringView name); + void AddExportLocalEntryItem(util::StringView name, const ir::Identifier *identifier); bool IsInstantiatedTSModule(const ir::Expression *node) const; void SetOriginalNode(ir::UpdateNodes res, ir::AstNode *originalNode) const; diff --git a/es2panda/typescript/extractor/typeExtractor.cpp b/es2panda/typescript/extractor/typeExtractor.cpp new file mode 100644 index 0000000000..32b987255f --- /dev/null +++ b/es2panda/typescript/extractor/typeExtractor.cpp @@ -0,0 +1,740 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "typeExtractor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "typeSystem.h" + +namespace panda::es2panda::extractor { + +#ifndef NDEBUG +#define TLOG(type, res) \ + do { \ + std::cout << "[LOG]" << __func__ << ": " << static_cast(type) << " | " << (res) << std::endl; \ + } while (0) +#else +#define TLOG(type, res) static_cast(0) +#endif + +const std::set PRUNING_SET = { + ir::AstNodeType::IDENTIFIER, + ir::AstNodeType::TS_INTERFACE_DECLARATION, + ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION, + ir::AstNodeType::IMPORT_DECLARATION, + ir::AstNodeType::EXPORT_ALL_DECLARATION +}; + +TypeExtractor::TypeExtractor(const ir::BlockStatement *rootNode, bool typeDtsExtractor, bool typeDtsBuiltin, + ArenaAllocator *allocator, compiler::CompilerContext *context) + : rootNode_(rootNode), typeDtsExtractor_(typeDtsExtractor), typeDtsBuiltin_(typeDtsBuiltin) +{ + recorder_ = std::make_unique(allocator, context); + + getterMap_[ir::AstNodeType::IDENTIFIER] = + std::bind(&TypeExtractor::GetTypeIndexFromIdentifierNode, this, std::placeholders::_1, std::placeholders::_2); + getterMap_[ir::AstNodeType::CLASS_EXPRESSION] = + std::bind(&TypeExtractor::GetTypeIndexFromClassExpression, this, std::placeholders::_1, std::placeholders::_2); + getterMap_[ir::AstNodeType::CLASS_DEFINITION] = + std::bind(&TypeExtractor::GetTypeIndexFromClassDefinition, this, std::placeholders::_1, std::placeholders::_2); + getterMap_[ir::AstNodeType::TS_INTERFACE_DECLARATION] = + std::bind(&TypeExtractor::GetTypeIndexFromInterfaceNode, this, std::placeholders::_1, std::placeholders::_2); + getterMap_[ir::AstNodeType::IMPORT_NAMESPACE_SPECIFIER] = + std::bind(&TypeExtractor::GetTypeIndexFromImportNode, this, std::placeholders::_1, std::placeholders::_2); + getterMap_[ir::AstNodeType::IMPORT_SPECIFIER] = + std::bind(&TypeExtractor::GetTypeIndexFromImportNode, this, std::placeholders::_1, std::placeholders::_2); + getterMap_[ir::AstNodeType::IMPORT_DEFAULT_SPECIFIER] = + std::bind(&TypeExtractor::GetTypeIndexFromImportNode, this, std::placeholders::_1, std::placeholders::_2); + getterMap_[ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION] = + std::bind(&TypeExtractor::GetTypeIndexFromTypeAliasNode, this, std::placeholders::_1, std::placeholders::_2); + getterMap_[ir::AstNodeType::MEMBER_EXPRESSION] = + std::bind(&TypeExtractor::GetTypeIndexFromMemberNode, this, std::placeholders::_1, std::placeholders::_2); + + handlerMap_[ir::AstNodeType::VARIABLE_DECLARATION] = + std::bind(&TypeExtractor::HandleVariableDeclaration, this, std::placeholders::_1); + handlerMap_[ir::AstNodeType::FUNCTION_DECLARATION] = + std::bind(&TypeExtractor::HandleFunctionDeclaration, this, std::placeholders::_1); + handlerMap_[ir::AstNodeType::CLASS_DECLARATION] = + std::bind(&TypeExtractor::HandleClassDeclaration, this, std::placeholders::_1); + handlerMap_[ir::AstNodeType::TS_INTERFACE_DECLARATION] = + std::bind(&TypeExtractor::HandleInterfaceDeclaration, this, std::placeholders::_1); + handlerMap_[ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION] = + std::bind(&TypeExtractor::HandleTypeAliasDeclaration, this, std::placeholders::_1); +} + +void TypeExtractor::StartTypeExtractor(const parser::Program *program) +{ + ASSERT(rootNode_->IsProgram()); + + TypeCounter counter(this); + + ExtractImport(program); + ExtractNodesType(rootNode_); + ExtractExport(program); + + recorder_->Dump(program); + recorder_->SetTypeSummaryIndex(counter.GetTypeIndexPlaceHolder()); + counter.FillLiteralBuffer(); +} + +bool TypeExtractor::GetTypeDtsExtractor() const +{ + return typeDtsExtractor_; +} + +bool TypeExtractor::GetTypeDtsBuiltin() const +{ + return typeDtsBuiltin_; +} + +TypeRecorder *TypeExtractor::Recorder() const +{ + return recorder_.get(); +} + +void TypeExtractor::ExtractNodesType(const ir::AstNode *parent) +{ + parent->Iterate([this, parent](const auto *childNode) { + ExtractNodeType(parent, childNode); + }); +} + +void TypeExtractor::ExtractNodeType(const ir::AstNode *parent, const ir::AstNode *childNode) +{ + auto iter = handlerMap_.find(childNode->Type()); + if (iter != handlerMap_.end()) { + iter->second(childNode); + } + + // Traversal pruning + if (PRUNING_SET.find(childNode->Type()) != PRUNING_SET.end()) { + return; + } + + ExtractNodesType(childNode); +} + +void TypeExtractor::ExtractImport(const parser::Program *program) +{ + auto moduleRecord = program->Binder()->Program()->ModuleRecord(); + if (moduleRecord == nullptr) { + return; + } + + const auto ®ularImportEntries = moduleRecord->GetRegularImportEntries(); + for (const auto &t : regularImportEntries) { + const auto &redirectPath = moduleRecord->GetModuleRequestIdxMap().at(t.second->moduleRequestIdx_); + ExternalType externalType(this, t.first, redirectPath); + recorder_->SetNodeTypeIndex(t.second->localId_->Parent(), externalType.GetTypeIndexShift()); + } + + const auto &namespaceImportEntries = moduleRecord->GetNamespaceImportEntries(); + for (const auto &t : namespaceImportEntries) { + const auto &redirectPath = moduleRecord->GetModuleRequestIdxMap().at(t->moduleRequestIdx_); + ExternalType externalType(this, "*", redirectPath); + recorder_->SetNamespaceType(std::string(t->localName_), externalType.GetTypeIndexShift()); + recorder_->SetNamespacePath(std::string(t->localName_), std::string(redirectPath)); + } +} + +void TypeExtractor::ExtractExport(const parser::Program *program) +{ + auto moduleRecord = program->Binder()->Program()->ModuleRecord(); + if (moduleRecord == nullptr) { + return; + } + + const auto &localExportEntries = moduleRecord->GetLocalExportEntries(); + for (const auto &t : localExportEntries) { + auto identifier = t.second->localId_; + if (identifier == nullptr) { + if (t.second->exportId_ != nullptr) { + // Special case for NamespaceExport transform + // Refer to parser/statementParser.cpp `AddExportStarEntryItem` + recorder_->SetExportType(std::string(t.second->exportName_), + recorder_->GetNamespaceType(std::string(t.second->localName_))); + } + // Other export extries without local identifier is handled during traversal + continue; + } + auto typeIndex = recorder_->GetVariableTypeIndex(identifier->Variable()); + if (typeIndex != PrimitiveType::ANY) { + recorder_->SetExportType(std::string(t.second->exportName_), typeIndex); + continue; + } + if (identifier->Variable() != nullptr && identifier->Variable()->Declaration() != nullptr) { + auto declNode = identifier->Variable()->Declaration()->Node(); + typeIndex = recorder_->GetNodeTypeIndex(declNode); + if (typeIndex == PrimitiveType::ANY && declNode != nullptr) { + typeIndex = GetTypeIndexFromDeclNode(declNode, true); + recorder_->SetIdentifierTypeIndex(identifier, typeIndex); + } + if (typeIndex != PrimitiveType::ANY) { + recorder_->SetExportType(std::string(t.second->exportName_), typeIndex); + } + } + } + + const auto &starExportEntries = moduleRecord->GetStarExportEntries(); + for (const auto &t : starExportEntries) { + recorder_->AddAnonymousReExport(moduleRecord->GetModuleRequestIdxMap().at(t->moduleRequestIdx_)); + } + + const auto &indirectExportEntries = moduleRecord->GetIndirectExportEntries(); + for (const auto &t : indirectExportEntries) { + const auto &redirectPath = moduleRecord->GetModuleRequestIdxMap().at(t->moduleRequestIdx_); + ExternalType externalType(this, t->importName_, redirectPath); + recorder_->SetExportType(std::string(t->exportName_), externalType.GetTypeIndexShift()); + } +} + +const ir::Identifier *TypeExtractor::GetIdentifierFromExpression(const ir::Expression *expression) +{ + switch (expression->Type()) { + case ir::AstNodeType::IDENTIFIER: + return expression->AsIdentifier(); + case ir::AstNodeType::TS_QUALIFIED_NAME: // : A.B + // TODO(extractor): consider property type suppport + return expression->AsTSQualifiedName()->Right(); + case ir::AstNodeType::TS_CLASS_IMPLEMENTS: { + auto expr = expression->AsTSClassImplements()->Expr(); + if (expr->IsIdentifier()) { + return expr->AsIdentifier(); + } else if (expr->IsTSQualifiedName()) { + // TODO(extractor): consider property type suppport + return expr->AsTSQualifiedName()->Right(); + } + return nullptr; + } + case ir::AstNodeType::REST_ELEMENT: { + auto argument = expression->AsRestElement()->Argument(); + if (argument->IsIdentifier()) { + return argument->AsIdentifier(); + } + return nullptr; + } + case ir::AstNodeType::SPREAD_ELEMENT: { + auto argument = expression->AsSpreadElement()->Argument(); + if (argument->IsIdentifier()) { + return argument->AsIdentifier(); + } + return nullptr; + } + case ir::AstNodeType::TS_INTERFACE_HERITAGE: { + auto expr = expression->AsTSInterfaceHeritage()->Expr(); + if (expr->IsIdentifier()) { + return expr->AsIdentifier(); + } else if (expr->IsTSQualifiedName()) { + // TODO(extractor): consider property type suppport + return expr->AsTSQualifiedName()->Right(); + } + return nullptr; + } + default: + return nullptr; + } +} + +const ir::AstNode *TypeExtractor::GetDeclNodeFromIdentifier(const ir::Identifier *identifier, + const ir::Identifier **variable) +{ + if (identifier == nullptr || identifier->Variable() == nullptr || + identifier->Variable()->Declaration() == nullptr) { + return nullptr; + } + + auto res = identifier->Variable()->Declaration()->Node(); + if (res != nullptr) { + // Return reference identifier if it contains variable binding to decl node + *variable = identifier; + TLOG(res->Type(), identifier); + } + return res; +} + +const ir::AstNode *TypeExtractor::GetDeclNodeFromInitializer(const ir::Expression *initializer, + const ir::Identifier **variable) +{ + switch (initializer->Type()) { + case ir::AstNodeType::IDENTIFIER: // let a = b / let a : A + return GetDeclNodeFromIdentifier(initializer->AsIdentifier(), variable); + case ir::AstNodeType::NEW_EXPRESSION: { + auto callee = initializer->AsNewExpression()->Callee(); + if (callee->IsClassExpression()) { // let a = new class {} + return callee; + } else if (callee->IsIdentifier()) { // let a = new A() + return GetDeclNodeFromIdentifier(callee->AsIdentifier(), variable); + } + break; + } + case ir::AstNodeType::CLASS_EXPRESSION: // let a = class A {} + case ir::AstNodeType::MEMBER_EXPRESSION: // let a = ns.A / let a : ns.A + return initializer; + default: + break; + } + + auto identifier = GetIdentifierFromExpression(initializer); + if (identifier != nullptr) { + return GetDeclNodeFromIdentifier(identifier, variable); + } + return nullptr; +} + +int64_t TypeExtractor::GetTypeIndexFromDeclNode(const ir::AstNode *node, bool isNewInstance) +{ + auto iter = getterMap_.find(node->Type()); + if (iter != getterMap_.end()) { + return iter->second(node, isNewInstance); + } + return PrimitiveType::ANY; +} + +int64_t TypeExtractor::GetTypeIndexFromIdentifierNode(const ir::AstNode *node, bool isNewInstance) +{ + auto typeIndex = recorder_->GetNodeTypeIndex(node); + if (isNewInstance && typeIndex != PrimitiveType::ANY) { + typeIndex = GetTypeIndexFromClassInst(typeIndex); + } + TLOG(node->Type(), typeIndex); + return typeIndex; +} + +int64_t TypeExtractor::GetTypeIndexFromClassExpression(const ir::AstNode *node, bool isNewInstance) +{ + auto classDef = node->AsClassExpression()->Definition(); + auto typeIndex = GetTypeIndexFromClassDefinition(classDef, isNewInstance); + TLOG(node->Type(), typeIndex); + return typeIndex; +} + +int64_t TypeExtractor::GetTypeIndexFromClassDefinition(const ir::AstNode *node, bool isNewInstance) +{ + auto typeIndex = recorder_->GetNodeTypeIndex(node); + if (typeIndex == PrimitiveType::ANY) { + auto fn = [&node, &typeIndex, this](const util::StringView &name) { + ClassType classType(this, node->AsClassDefinition(), name); + typeIndex = classType.GetTypeIndexShift(); + }; + + auto identifier = node->AsClassDefinition()->Ident(); + if (identifier != nullptr) { + fn(identifier->Name()); + recorder_->SetIdentifierTypeIndex(identifier, typeIndex); + } else { + fn(std::move(DEFAULT_NAME)); + } + } + + if (isNewInstance) { + typeIndex = GetTypeIndexFromClassInst(typeIndex); + } + + TLOG(node->Type(), typeIndex); + return typeIndex; +} + +int64_t TypeExtractor::GetTypeIndexFromInterfaceNode(const ir::AstNode *node, bool isNewInstance) +{ + auto typeIndex = recorder_->GetNodeTypeIndex(node); + if (typeIndex == PrimitiveType::ANY) { + auto fn = [&node, &typeIndex, this](const util::StringView &name) { + InterfaceType interfaceType(this, node->AsTSInterfaceDeclaration(), name); + typeIndex = interfaceType.GetTypeIndexShift(); + }; + + auto identifier = node->AsTSInterfaceDeclaration()->Id(); + if (identifier != nullptr) { + fn(identifier->Name()); + recorder_->SetIdentifierTypeIndex(identifier, typeIndex); + } else { + fn(std::move(DEFAULT_NAME)); + } + } + + if (isNewInstance) { + typeIndex = GetTypeIndexFromClassInst(typeIndex); + } + + TLOG(node->Type(), typeIndex); + return typeIndex; +} + +int64_t TypeExtractor::GetTypeIndexFromImportNode(const ir::AstNode *node, [[maybe_unused]] bool isNewInstance) +{ + auto typeIndex = recorder_->GetNodeTypeIndex(node); + TLOG(node->Type(), typeIndex); + return typeIndex; +} + +int64_t TypeExtractor::GetTypeIndexFromTypeAliasNode(const ir::AstNode *node, [[maybe_unused]] bool isNewInstance) +{ + auto typeIndex = GetTypeIndexFromAnnotation(node->AsTSTypeAliasDeclaration()->TypeAnnotation()); + TLOG(node->Type(), typeIndex); + return typeIndex; +} + +int64_t TypeExtractor::GetTypeIndexFromMemberNode(const ir::AstNode *node, [[maybe_unused]] bool isNewInstance) +{ + int64_t typeIndex = PrimitiveType::ANY; + auto object = node->AsMemberExpression()->Object(); + auto property = node->AsMemberExpression()->Property(); + if (object->IsIdentifier() && property->IsIdentifier()) { + auto redirectPath = recorder_->GetNamespacePath(std::string(object->AsIdentifier()->Name())); + if (redirectPath != "") { + ExternalType externalType(this, property->AsIdentifier()->Name(), util::StringView(redirectPath)); + typeIndex = externalType.GetTypeIndexShift(); + } + } + TLOG(node->Type(), typeIndex); + return typeIndex; +} + +int64_t TypeExtractor::GetTypeIndexFromAnnotation(const ir::Expression *typeAnnotation) +{ + if (typeAnnotation == nullptr) { + return PrimitiveType::ANY; + } + + switch (typeAnnotation->AsTypeNode()->Type()) { + case ir::AstNodeType::TS_ANY_KEYWORD: + case ir::AstNodeType::TS_NUMBER_KEYWORD: + case ir::AstNodeType::TS_BOOLEAN_KEYWORD: + case ir::AstNodeType::TS_VOID_KEYWORD: + case ir::AstNodeType::TS_STRING_KEYWORD: + case ir::AstNodeType::TS_SYMBOL_KEYWORD: + case ir::AstNodeType::TS_NULL_KEYWORD: + case ir::AstNodeType::TS_UNDEFINED_KEYWORD: + return PRIMITIVE_TYPE_MAP.at(typeAnnotation->AsTypeNode()->Type()); + case ir::AstNodeType::TS_NEVER_KEYWORD: + case ir::AstNodeType::TS_UNKNOWN_KEYWORD: + return PrimitiveType::ANY; + case ir::AstNodeType::TS_ARRAY_TYPE: { // ArrayType + ArrayType arrayType(this, typeAnnotation->AsTSArrayType()); + return arrayType.GetTypeIndexShift(); + } + case ir::AstNodeType::TS_UNION_TYPE: { // UnionType + UnionType unionType(this, typeAnnotation->AsTSUnionType()); + return unionType.GetTypeIndexShift(); + } + case ir::AstNodeType::TS_PARENT_TYPE: { // (UnionType) + auto type = typeAnnotation->AsTSParenthesizedType()->Type(); + ASSERT(type != nullptr); + if (type->IsTSUnionType()) { + UnionType unionType(this, type->AsTSUnionType()); + return unionType.GetTypeIndexShift(); + } + return PrimitiveType::ANY; + } + case ir::AstNodeType::TS_TYPE_LITERAL: { // ObjectType + ObjectType objectType(this, typeAnnotation->AsTSTypeLiteral()); + return objectType.GetTypeIndexShift(); + } + case ir::AstNodeType::TS_OBJECT_KEYWORD: { // ObjectType + ObjectType objectType(this, nullptr); // let a : object + return objectType.GetTypeIndexShift(); + } + case ir::AstNodeType::TS_BIGINT_KEYWORD: + case ir::AstNodeType::TS_CONDITIONAL_TYPE: + case ir::AstNodeType::TS_CONSTRUCTOR_TYPE: + case ir::AstNodeType::TS_FUNCTION_TYPE: + case ir::AstNodeType::TS_IMPORT_TYPE: + case ir::AstNodeType::TS_INDEXED_ACCESS_TYPE: + case ir::AstNodeType::TS_INTERSECTION_TYPE: + case ir::AstNodeType::TS_INFER_TYPE: + case ir::AstNodeType::TS_LITERAL_TYPE: + case ir::AstNodeType::TS_MAPPED_TYPE: + case ir::AstNodeType::TS_OPTIONAL_TYPE: + case ir::AstNodeType::TS_REST_TYPE: + case ir::AstNodeType::TS_TEMPLATE_LITERAL_TYPE: + case ir::AstNodeType::TS_THIS_TYPE: + case ir::AstNodeType::TS_TUPLE_TYPE: + case ir::AstNodeType::TS_TYPE_OPERATOR: + case ir::AstNodeType::TS_TYPE_PREDICATE: + case ir::AstNodeType::TS_TYPE_QUERY: + return PrimitiveType::ANY; + case ir::AstNodeType::TS_TYPE_REFERENCE: { // let a : A + return GetTypeIndexFromTypeReference(typeAnnotation->AsTSTypeReference()); + } + default: + UNREACHABLE(); + } +} + +int64_t TypeExtractor::GetTypeIndexFromIdentifier(const ir::Identifier *identifier) +{ + auto typeAnnotation = identifier->TypeAnnotation(); + auto typeIndex = GetTypeIndexFromAnnotation(typeAnnotation); + recorder_->SetIdentifierTypeIndex(identifier, typeIndex); + TLOG(identifier->Type(), typeIndex); + return typeIndex; +} + +int64_t TypeExtractor::GetTypeIndexFromInitializer(const ir::Expression *initializer) +{ + int64_t typeIndex = PrimitiveType::ANY; + // Special case for Builtin + if (initializer->IsNewExpression()) { + auto callee = initializer->AsNewExpression()->Callee(); + if (callee->IsIdentifier()) { + typeIndex = GetTypeIndexFromBuiltin(callee->AsIdentifier()->Name(), + initializer->AsNewExpression()->TypeParams()); + if (typeIndex != PrimitiveType::ANY) { + return typeIndex; + } + } + } + + const ir::Identifier *identifier = nullptr; + // Identifier here is a reference identifier binding to decl node which also contains variable + auto declNode = GetDeclNodeFromInitializer(initializer, &identifier); + if (declNode != nullptr) { + typeIndex = GetTypeIndexFromDeclNode(declNode, initializer->IsNewExpression()); + recorder_->SetIdentifierTypeIndex(identifier, recorder_->GetClassType(typeIndex)); + } + TLOG(initializer->Type(), typeIndex); + return typeIndex; +} + +void TypeExtractor::HandleVariableDeclaration(const ir::AstNode *node) +{ + auto isExported = IsExportNode(node); + for (const auto *it : node->AsVariableDeclaration()->Declarators()) { + if (!it->Id()->IsIdentifier()) { + // BindingElement needs type inference, like: + // ArrayExpression: let [a, b] = [1, 2] + // ObjectExpression: let {a, b} = c + continue; + } + auto identifier = it->Id()->AsIdentifier(); + ASSERT(identifier != nullptr); + auto typeIndex = GetTypeIndexFromIdentifier(identifier); + if (typeIndex == PrimitiveType::ANY && it->Init() != nullptr) { + typeIndex = GetTypeIndexFromInitializer(it->Init()); + } + recorder_->SetIdentifierTypeIndex(identifier, typeIndex); + if (isExported && typeIndex != PrimitiveType::ANY) { + recorder_->SetExportType(std::string(identifier->Name()), typeIndex); + } + } +} + +void TypeExtractor::HandleFunctionDeclaration(const ir::AstNode *node) +{ + int64_t typeIndex = PrimitiveType::ANY; + auto fn = [&node, &typeIndex, this](const util::StringView &name) { + FunctionType functionType(this, node, name); + typeIndex = functionType.GetTypeIndexShift(); + if (IsExportNode(node)) { + recorder_->SetExportType(std::string(name), typeIndex); + } + if (IsDeclareNode(node)) { + recorder_->SetDeclareType(std::string(name), typeIndex); + } + }; + + auto identifier = node->AsFunctionDeclaration()->Function()->Id(); + if (identifier != nullptr) { + fn(identifier->Name()); + recorder_->SetIdentifierTypeIndex(identifier, typeIndex); + } else { + fn(""); + } +} + +void TypeExtractor::HandleClassDeclaration(const ir::AstNode *node) +{ + int64_t typeIndex = PrimitiveType::ANY; + auto classDef = node->AsClassDeclaration()->Definition(); + auto fn = [&node, &typeIndex, &classDef, this](const util::StringView &name) { + ClassType classType(this, classDef, name); + typeIndex = classType.GetTypeIndexShift(); + if (IsExportNode(node)) { + recorder_->SetExportType(std::string(name), typeIndex); + } + if (IsDeclareNode(node)) { + recorder_->SetDeclareType(std::string(name), typeIndex); + } + }; + + auto identifier = classDef->Ident(); + if (identifier != nullptr) { + fn(identifier->Name()); + recorder_->SetIdentifierTypeIndex(identifier, typeIndex); + } else { + fn(std::move(DEFAULT_NAME)); + } +} + +void TypeExtractor::HandleInterfaceDeclaration(const ir::AstNode *node) +{ + int64_t typeIndex = PrimitiveType::ANY; + auto interfaceDef = node->AsTSInterfaceDeclaration(); + auto fn = [&node, &typeIndex, &interfaceDef, this](const util::StringView &name) { + InterfaceType interfaceType(this, interfaceDef, name); + typeIndex = interfaceType.GetTypeIndexShift(); + if (IsExportNode(node)) { + recorder_->SetExportType(std::string(name), typeIndex); + } + if (IsDeclareNode(node)) { + recorder_->SetDeclareType(std::string(name), typeIndex); + } + }; + + auto identifier = interfaceDef->Id(); + if (identifier != nullptr) { + fn(identifier->Name()); + recorder_->SetIdentifierTypeIndex(identifier, typeIndex); + } else { + fn(std::move(DEFAULT_NAME)); + } +} + +void TypeExtractor::HandleTypeAliasDeclaration(const ir::AstNode *node) +{ + // Create the type if it is exported or declared + auto typeAliasDef = node->AsTSTypeAliasDeclaration(); + auto identifier = typeAliasDef->Id(); + if (IsExportNode(node)) { + recorder_->SetExportType(std::string(identifier->Name()), + GetTypeIndexFromAnnotation(typeAliasDef->TypeAnnotation())); + } + if (IsDeclareNode(node)) { + recorder_->SetDeclareType(std::string(identifier->Name()), + GetTypeIndexFromAnnotation(typeAliasDef->TypeAnnotation())); + } +} + +int64_t TypeExtractor::GetTypeIndexFromClassInst(int64_t typeIndex) +{ + auto typeIndexTmp = recorder_->GetClassInst(typeIndex); + if (typeIndexTmp == PrimitiveType::ANY) { + ClassInstType classInstType(this, typeIndex); + return classInstType.GetTypeIndexShift(); + } + return typeIndexTmp; +} + +int64_t TypeExtractor::GetTypeIndexFromTypeReference(const ir::TSTypeReference *typeReference) +{ + auto typeName = typeReference->TypeName(); + ASSERT(typeName != nullptr); + if (typeName->IsIdentifier()) { + // Special case for Builtin + auto typeIndexBuiltin = GetTypeIndexFromBuiltin(typeName->AsIdentifier()->Name(), typeReference->TypeParams()); + if (typeIndexBuiltin != PrimitiveType::ANY) { + return typeIndexBuiltin; + } + } + + const ir::Identifier *identifier = nullptr; + // TypeName can be Identifier or TSQualifiedName + // Identifier here is a reference identifier binding to decl node which also contains variable + auto declNode = GetDeclNodeFromInitializer(typeName, &identifier); + if (declNode != nullptr) { + auto typeIndex = GetTypeIndexFromDeclNode(declNode, true); + recorder_->SetIdentifierTypeIndex(identifier, recorder_->GetClassType(typeIndex)); + return typeIndex; + } + return PrimitiveType::ANY; +} + +int64_t TypeExtractor::GetTypeIndexFromBuiltin(const util::StringView &name, + const ir::TSTypeParameterInstantiation *node) +{ + auto typeIndexBuiltin = GetBuiltinTypeIndex(name); + if (typeIndexBuiltin != PrimitiveType::ANY) { + if (node == nullptr) { + return GetTypeIndexFromClassInst(typeIndexBuiltin); + } + return GetTypeIndexFromBuiltinInst(typeIndexBuiltin, node); + } + return PrimitiveType::ANY; +} + +int64_t TypeExtractor::GetTypeIndexFromBuiltinInst(int64_t typeIndexBuiltin, + const ir::TSTypeParameterInstantiation *node) +{ + std::vector allTypes = { typeIndexBuiltin }; + for (const auto &t : node->Params()) { + allTypes.emplace_back(GetTypeIndexFromAnnotation(t)); + } + auto typeIndex = recorder_->GetBuiltinInst(allTypes); + if (typeIndex != PrimitiveType::ANY) { + return typeIndex; + } + + // New instance for builtin generic type + BuiltinInstType builtinInstType(this, allTypes); + return GetTypeIndexFromClassInst(builtinInstType.GetTypeIndexShift()); +} + +bool TypeExtractor::IsExportNode(const ir::AstNode *node) const +{ + auto parent = node->Parent(); + if (parent->Parent() != rootNode_) { + return false; + } + if (parent->IsExportNamedDeclaration() || parent->IsExportDefaultDeclaration()) { + return true; + } + return false; +} + +bool TypeExtractor::IsDeclareNode(const ir::AstNode *node) const +{ + if (!typeDtsExtractor_) { + return false; + } + switch (node->Type()) { + case ir::AstNodeType::FUNCTION_DECLARATION: + return node->AsFunctionDeclaration()->Function()->Declare(); + case ir::AstNodeType::CLASS_DEFINITION: + return node->AsClassDefinition()->Declare(); + case ir::AstNodeType::TS_INTERFACE_DECLARATION: + return true; + case ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION: + return node->AsTSTypeAliasDeclaration()->Declare(); + default: + break; + } + return false; +} + +// static +int64_t TypeExtractor::GetBuiltinTypeIndex(util::StringView name) +{ + auto t = BUILTIN_TYPE_MAP.find(std::string(name)); + if (t != BUILTIN_TYPE_MAP.end()) { + return t->second; + } + return PrimitiveType::ANY; +} + +} // namespace panda::es2panda::extractor diff --git a/es2panda/typescript/extractor/typeExtractor.h b/es2panda/typescript/extractor/typeExtractor.h new file mode 100644 index 0000000000..202afb937e --- /dev/null +++ b/es2panda/typescript/extractor/typeExtractor.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ES2PANDA_TYPESCRIPT_EXACTOR_TYPEEXTRACTOR_H +#define ES2PANDA_TYPESCRIPT_EXACTOR_TYPEEXTRACTOR_H + +#include +#include + +#include "typeRecorder.h" + +namespace panda::es2panda::extractor { + +using Getter = std::function; +using Handler = std::function; + +class TypeExtractor { +public: + explicit TypeExtractor(const ir::BlockStatement *rootNode, bool typeDtsExtractor, bool typeDtsBuiltin, + ArenaAllocator *allocator, compiler::CompilerContext *context); + ~TypeExtractor() = default; + NO_COPY_SEMANTIC(TypeExtractor); + NO_MOVE_SEMANTIC(TypeExtractor); + + void StartTypeExtractor(const parser::Program *program); + + bool GetTypeDtsExtractor() const; + bool GetTypeDtsBuiltin() const; + TypeRecorder *Recorder() const; + + const ir::Identifier *GetIdentifierFromExpression(const ir::Expression *expression); + int64_t GetTypeIndexFromAnnotation(const ir::Expression *typeAnnotation); + int64_t GetTypeIndexFromIdentifier(const ir::Identifier *identifier); + int64_t GetTypeIndexFromInitializer(const ir::Expression *initializer); + + static int64_t GetBuiltinTypeIndex(util::StringView name); + +private: + const ir::BlockStatement *rootNode_; + const bool typeDtsExtractor_; + const bool typeDtsBuiltin_; + std::unique_ptr recorder_; + std::unordered_map getterMap_; + std::unordered_map handlerMap_; + + void ExtractNodesType(const ir::AstNode *parent); + void ExtractNodeType(const ir::AstNode *parent, const ir::AstNode *childNode); + void ExtractImport(const parser::Program *program); + void ExtractExport(const parser::Program *program); + + const ir::AstNode *GetDeclNodeFromIdentifier(const ir::Identifier *identifier, const ir::Identifier **variable); + const ir::AstNode *GetDeclNodeFromInitializer(const ir::Expression *initializer, const ir::Identifier **variable); + + int64_t GetTypeIndexFromDeclNode(const ir::AstNode *node, bool isNewInstance); + int64_t GetTypeIndexFromIdentifierNode(const ir::AstNode *node, bool isNewInstance); + int64_t GetTypeIndexFromClassExpression(const ir::AstNode *node, bool isNewInstance); + int64_t GetTypeIndexFromClassDefinition(const ir::AstNode *node, bool isNewInstance); + int64_t GetTypeIndexFromInterfaceNode(const ir::AstNode *node, bool isNewInstance); + int64_t GetTypeIndexFromImportNode(const ir::AstNode *node, [[maybe_unused]] bool isNewInstance); + int64_t GetTypeIndexFromTypeAliasNode(const ir::AstNode *node, [[maybe_unused]] bool isNewInstance); + int64_t GetTypeIndexFromMemberNode(const ir::AstNode *node, [[maybe_unused]] bool isNewInstance); + + void HandleVariableDeclaration(const ir::AstNode *node); + void HandleFunctionDeclaration(const ir::AstNode *node); + void HandleClassDeclaration(const ir::AstNode *node); + void HandleInterfaceDeclaration(const ir::AstNode *node); + void HandleTypeAliasDeclaration(const ir::AstNode *node); + + // Helpers + int64_t GetTypeIndexFromClassInst(int64_t typeIndex); + int64_t GetTypeIndexFromTypeReference(const ir::TSTypeReference *typeReference); + + // Builtin Helpers + int64_t GetTypeIndexFromBuiltin(const util::StringView &name, const ir::TSTypeParameterInstantiation *node); + int64_t GetTypeIndexFromBuiltinInst(int64_t typeIndexBuiltin, const ir::TSTypeParameterInstantiation *node); + + // Other Helpers + bool IsExportNode(const ir::AstNode *node) const; + bool IsDeclareNode(const ir::AstNode *node) const; +}; + +} // namespace panda::es2panda::extractor + +#endif // ES2PANDA_TYPESCRIPT_EXACTOR_TYPEEXTRACTOR_H diff --git a/es2panda/typescript/extractor/typeRecorder.cpp b/es2panda/typescript/extractor/typeRecorder.cpp new file mode 100644 index 0000000000..a09cfa91fa --- /dev/null +++ b/es2panda/typescript/extractor/typeRecorder.cpp @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "typeRecorder.h" + +#include + +namespace panda::es2panda::extractor { + +template +static V FindValue(const M &map, const K &key, const V &value) +{ + auto t = map.find(key); + if (t != map.end()) { + return t->second; + } + return value; +} + +TypeRecorder::TypeRecorder(ArenaAllocator *allocator, compiler::CompilerContext *context) + : allocator_(allocator), context_(context), buffStorage_(allocator_->Adapter()) +{ +} + +ArenaAllocator *TypeRecorder::Allocator() const +{ + return allocator_; +} + +const ArenaVector &TypeRecorder::BuffStorage() const +{ + return buffStorage_; +} + +const std::unordered_map &TypeRecorder::NodeTypeIndex() const +{ + return nodeTypeIndex_; +} + +const std::unordered_map &TypeRecorder::VariableTypeIndex() const +{ + return variableTypeIndex_; +} + +const std::unordered_map &TypeRecorder::ExportType() const +{ + return exportType_; +} + +const std::unordered_map &TypeRecorder::DeclareType() const +{ + return declareType_; +} + +compiler::LiteralBuffer *TypeRecorder::NewLiteralBuffer() +{ + return allocator_->New(allocator_); +} + +int64_t TypeRecorder::AddLiteralBuffer(compiler::LiteralBuffer *buffer) +{ + buffStorage_.push_back(buffer); + buffer->SetIndex(context_->NewLiteralIndex()); + return buffer->Index(); +} + +compiler::LiteralBuffer *TypeRecorder::GetLiteralBuffer(int64_t index) const +{ + auto res = std::find_if(buffStorage_.begin(), buffStorage_.end(), + [&index](const auto &t) { return t->Index() == index; }); + return (res != buffStorage_.end()) ? *res : nullptr; +} + +void TypeRecorder::SetLiteralBuffer(int64_t index, compiler::LiteralBuffer *buffer) +{ + std::replace_if(buffStorage_.begin(), buffStorage_.end(), + [&index](const auto &t) { return t->Index() == index; }, buffer); + buffer->SetIndex(index); +} + +util::StringView TypeRecorder::GetAnonymousFunctionNames(const ir::ScriptFunction *func) const +{ + const auto &m = context_->Binder()->AnonymousFunctionNames(); + auto res = m.find(func); + return (res != m.end()) ? std::move(res->second) : std::move(DEFAULT_NAME); +} + +int64_t TypeRecorder::CalculateUserType() const +{ + return userType_.size(); +} + +void TypeRecorder::AddUserType(int64_t index) +{ + userType_.insert(index); +} + +int64_t TypeRecorder::GetTypeSummaryIndex() const +{ + return typeSummaryIndex_; +} + +void TypeRecorder::SetTypeSummaryIndex(int64_t index) +{ + typeSummaryIndex_ = index; +} + +int64_t TypeRecorder::GetUserTypeIndexShift() const +{ + return userTypeIndexShift_; +} + +void TypeRecorder::SetUserTypeIndexShift(int64_t index) +{ + userTypeIndexShift_ = index; +} + +int64_t TypeRecorder::GetNodeTypeIndex(const ir::AstNode *node) const +{ + return FindValue(nodeTypeIndex_, node, PRIMITIVETYPE_ANY); +} + +void TypeRecorder::SetNodeTypeIndex(const ir::AstNode *node, int64_t index) +{ + if (node == nullptr || GetNodeTypeIndex(node) != PRIMITIVETYPE_ANY) { + return; + } + + nodeTypeIndex_[node] = index; +} + +int64_t TypeRecorder::GetVariableTypeIndex(const binder::Variable *variable) const +{ + return FindValue(variableTypeIndex_, variable, PRIMITIVETYPE_ANY); +} + +void TypeRecorder::SetVariableTypeIndex(const binder::Variable *variable, int64_t index) +{ + if (variable == nullptr || GetVariableTypeIndex(variable) != PRIMITIVETYPE_ANY) { + return; + } + + variableTypeIndex_[variable] = index; +} + +void TypeRecorder::SetIdentifierTypeIndex(const ir::Identifier *identifier, int64_t index) +{ + if (identifier == nullptr) { + return; + } + SetNodeTypeIndex(identifier, index); + SetVariableTypeIndex(identifier->Variable(), index); +} + +int64_t TypeRecorder::GetBuiltinInst(const std::vector &allTypes) const +{ + return FindValue(builtinInst_, allTypes, PRIMITIVETYPE_ANY); +} + +void TypeRecorder::SetBuiltinInst(const std::vector &allTypes, int64_t instIndex) +{ + builtinInst_[allTypes] = instIndex; +} + +int64_t TypeRecorder::GetClassInst(int64_t classIndex) const +{ + return FindValue(classInst_, classIndex, PRIMITIVETYPE_ANY); +} + +void TypeRecorder::SetClassInst(int64_t classIndex, int64_t instIndex) +{ + if (classIndex == PRIMITIVETYPE_ANY) { + return; + } + classInst_[classIndex] = instIndex; +} + +int64_t TypeRecorder::GetClassType(int64_t instIndex) const +{ + // Here we always return the original type rather than instance type + // If `instIndex` is not in `classType_`, it means `instIndex` does not come from new instance + return FindValue(classType_, instIndex, instIndex); +} + +void TypeRecorder::SetClassType(int64_t instIndex, int64_t classIndex) +{ + if (instIndex == PRIMITIVETYPE_ANY) { + return; + } + classType_[instIndex] = classIndex; +} + +int64_t TypeRecorder::GetArrayType(int64_t contentIndex) const +{ + return FindValue(arrayType_, contentIndex, PRIMITIVETYPE_ANY); +} + +void TypeRecorder::SetArrayType(int64_t contentIndex, int64_t arrayIndex) +{ + arrayType_[contentIndex] = arrayIndex; +} + +int64_t TypeRecorder::GetUnionType(const std::string &unionStr) const +{ + return FindValue(unionType_, unionStr, PRIMITIVETYPE_ANY); +} + +void TypeRecorder::SetUnionType(const std::string &unionStr, int64_t unionIndex) +{ + unionType_[unionStr] = unionIndex; +} + +int64_t TypeRecorder::GetObjectType(const std::string &objectStr) const +{ + return FindValue(objectType_, objectStr, PRIMITIVETYPE_ANY); +} + +void TypeRecorder::SetObjectType(const std::string &objectStr, int64_t objectIndex) +{ + objectType_[objectStr] = objectIndex; +} + +int64_t TypeRecorder::GetExportType(const std::string &exportStr) const +{ + return FindValue(exportType_, exportStr, PRIMITIVETYPE_ANY); +} + +void TypeRecorder::SetExportType(const std::string &exportStr, int64_t exportIndex) +{ + exportType_[exportStr] = exportIndex; +} + +int64_t TypeRecorder::GetDeclareType(const std::string &declareStr) const +{ + return FindValue(declareType_, declareStr, PRIMITIVETYPE_ANY); +} + +void TypeRecorder::SetDeclareType(const std::string &declareStr, int64_t declareIndex) +{ + declareType_[declareStr] = declareIndex; +} + +int64_t TypeRecorder::GetNamespaceType(const std::string &namespaceStr) const +{ + return FindValue(namespaceType_, namespaceStr, PRIMITIVETYPE_ANY); +} + +void TypeRecorder::SetNamespaceType(const std::string &namespaceStr, int64_t namespaceIndex) +{ + namespaceType_[namespaceStr] = namespaceIndex; +} + +std::string TypeRecorder::GetNamespacePath(const std::string &namespaceStr) const +{ + return FindValue(namespacePath_, namespaceStr, std::string()); +} + +void TypeRecorder::SetNamespacePath(const std::string &namespaceStr, const std::string &filePath) +{ + namespacePath_[namespaceStr] = filePath; +} + +const std::set &TypeRecorder::GetAnonymousReExport() const +{ + return anonymousReExport_; +} + +void TypeRecorder::AddAnonymousReExport(const util::StringView &reExportStr) +{ + anonymousReExport_.insert(reExportStr); +} + +ALWAYS_INLINE void TypeRecorder::Dump(const parser::Program *program) const +{ +#ifndef NDEBUG + std::cout << "========== TypeExtractor ==========" << std::endl; + std::cout << "---------- userType_ ----------" << std::endl; + std::stringstream ss; + for (const auto &t : userType_) { + ss << t << " | "; + } + std::cout << ss.str() << std::endl; + std::cout << "---------- nodeTypeIndex_ ----------" << std::endl; + for (const auto &t : nodeTypeIndex_) { + ir::AstDumper dumper(t.first); + std::cout << dumper.Str() << " : " << t.second << std::endl; + } + std::cout << "---------- variableTypeIndex_ ----------" << std::endl; + for (const auto &t : variableTypeIndex_) { + std::cout << t.first->Name() << " : " << t.second << std::endl; + } + std::cout << "---------- builtinInst_ ----------" << std::endl; + for (const auto &t : builtinInst_) { + for (const auto &p : t.first) { + std::cout << p << " "; + } + std::cout << t.second << std::endl; + } + + auto fn = [](const auto &map) { + for (const auto &t : map) { + std::cout << t.first << " : " << t.second << std::endl; + } + }; + std::cout << "---------- classInst_ ----------" << std::endl; + std::cout << "---- class ---- | ---- inst ----" << std::endl; + fn(classInst_); + std::cout << "---------- classType_ ----------" << std::endl; + std::cout << "---- inst ---- | ---- class ----" << std::endl; + fn(classType_); + std::cout << "---------- arrayType_ ----------" << std::endl; + fn(arrayType_); + std::cout << "---------- unionType_ ----------" << std::endl; + fn(unionType_); + std::cout << "---------- objectType_ ----------" << std::endl; + fn(objectType_); + std::cout << "---------- exportType_ ----------" << std::endl; + fn(exportType_); + std::cout << "---------- declareType_ ----------" << std::endl; + fn(declareType_); + std::cout << "---------- namespaceType_ ----------" << std::endl; + fn(namespaceType_); + std::cout << "---------- namespacePath_ ----------" << std::endl; + fn(namespacePath_); + std::cout << "---------- anonymousReExport_ ----------" << std::endl; + for (const auto &t : anonymousReExport_) { + std::cout << std::string(t) << std::endl; + } + std::cout << "========== TypeExtractor ==========" << std::endl; +#endif +} + +} // namespace panda::es2panda::extractor diff --git a/es2panda/typescript/extractor/typeRecorder.h b/es2panda/typescript/extractor/typeRecorder.h new file mode 100644 index 0000000000..31ac9350ae --- /dev/null +++ b/es2panda/typescript/extractor/typeRecorder.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ES2PANDA_TYPESCRIPT_EXACTOR_TYPERECORDER_H +#define ES2PANDA_TYPESCRIPT_EXACTOR_TYPERECORDER_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace panda::es2panda::extractor { + +const util::StringView DEFAULT_NAME = "default"; + +class TypeRecorder { +public: + explicit TypeRecorder(ArenaAllocator *allocator, compiler::CompilerContext *context); + ~TypeRecorder() = default; + NO_COPY_SEMANTIC(TypeRecorder); + NO_MOVE_SEMANTIC(TypeRecorder); + + ArenaAllocator *Allocator() const; + const ArenaVector &BuffStorage() const; + const std::unordered_map &NodeTypeIndex() const; + const std::unordered_map &VariableTypeIndex() const; + const std::unordered_map &ExportType() const; + const std::unordered_map &DeclareType() const; + + compiler::LiteralBuffer *NewLiteralBuffer(); + int64_t AddLiteralBuffer(compiler::LiteralBuffer *buffer); + compiler::LiteralBuffer *GetLiteralBuffer(int64_t index) const; + void SetLiteralBuffer(int64_t index, compiler::LiteralBuffer *buffer); + + util::StringView GetAnonymousFunctionNames(const ir::ScriptFunction *func) const; + + int64_t CalculateUserType() const; + void AddUserType(int64_t index); + + int64_t GetTypeSummaryIndex() const; + void SetTypeSummaryIndex(int64_t index); + + int64_t GetUserTypeIndexShift() const; + void SetUserTypeIndexShift(int64_t index); + + int64_t GetNodeTypeIndex(const ir::AstNode *node) const; + void SetNodeTypeIndex(const ir::AstNode *node, int64_t index); + + int64_t GetVariableTypeIndex(const binder::Variable *variable) const; + void SetVariableTypeIndex(const binder::Variable *variable, int64_t index); + + void SetIdentifierTypeIndex(const ir::Identifier *identifier, int64_t index); + + int64_t GetBuiltinInst(const std::vector &allTypes) const; + void SetBuiltinInst(const std::vector &allTypes, int64_t instIndex); + + int64_t GetClassInst(int64_t classIndex) const; + void SetClassInst(int64_t classIndex, int64_t instIndex); + + int64_t GetClassType(int64_t instIndex) const; + void SetClassType(int64_t instIndex, int64_t classIndex); + + int64_t GetArrayType(int64_t contentIndex) const; + void SetArrayType(int64_t contentIndex, int64_t arrayIndex); + + int64_t GetUnionType(const std::string &unionStr) const; + void SetUnionType(const std::string &unionStr, int64_t unionIndex); + + int64_t GetObjectType(const std::string &objectStr) const; + void SetObjectType(const std::string &objectStr, int64_t objectIndex); + + int64_t GetExportType(const std::string &exportStr) const; + void SetExportType(const std::string &exportStr, int64_t exportIndex); + + int64_t GetDeclareType(const std::string &declareStr) const; + void SetDeclareType(const std::string &declareStr, int64_t declareIndex); + + int64_t GetNamespaceType(const std::string &namespaceStr) const; + void SetNamespaceType(const std::string &namespaceStr, int64_t namespaceIndex); + + std::string GetNamespacePath(const std::string &namespaceStr) const; + void SetNamespacePath(const std::string &namespaceStr, const std::string &filePath); + + const std::set &GetAnonymousReExport() const; + void AddAnonymousReExport(const util::StringView &reExportStr); + + ALWAYS_INLINE void Dump(const parser::Program *program) const; + + static constexpr uint8_t PRIMITIVETYPE_ANY = 0U; + static constexpr int64_t USERTYPEINDEXHEAD = 100; + +private: + ArenaAllocator *allocator_; + compiler::CompilerContext *context_; + ArenaVector buffStorage_; + int64_t typeSummaryIndex_ = 0; + int64_t userTypeIndexShift_ = USERTYPEINDEXHEAD; + std::set userType_ {}; + std::unordered_map nodeTypeIndex_ {}; + std::unordered_map variableTypeIndex_ {}; + std::map, int64_t> builtinInst_ {}; + std::unordered_map classInst_ {}; + std::unordered_map classType_ {}; + std::unordered_map arrayType_ {}; + std::unordered_map unionType_ {}; + std::unordered_map objectType_ {}; + // Export symbols + std::unordered_map exportType_ {}; + std::unordered_map declareType_ {}; + // Namespace in import / export declaration + std::unordered_map namespaceType_ {}; + std::unordered_map namespacePath_ {}; + std::set anonymousReExport_ {}; +}; + +} // namespace panda::es2panda::extractor + +#endif // ES2PANDA_TYPESCRIPT_EXACTOR_TYPERECORDER_H diff --git a/es2panda/typescript/extractor/typeSystem.h b/es2panda/typescript/extractor/typeSystem.h new file mode 100644 index 0000000000..c37d41029f --- /dev/null +++ b/es2panda/typescript/extractor/typeSystem.h @@ -0,0 +1,1098 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ES2PANDA_TYPESCRIPT_EXACTOR_TYPESYSTEM_H +#define ES2PANDA_TYPESCRIPT_EXACTOR_TYPESYSTEM_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace panda::es2panda::extractor { + +enum PrimitiveType : uint8_t { + ANY = 0, + NUMBER, + BOOLEAN, + VOID, + STRING, + SYMBOL, + NUL, + UNDEFINED, + INT, +}; + +const std::unordered_map PRIMITIVE_TYPE_MAP = { + {ir::AstNodeType::TS_ANY_KEYWORD, PrimitiveType::ANY}, + {ir::AstNodeType::TS_NUMBER_KEYWORD, PrimitiveType::NUMBER}, + {ir::AstNodeType::TS_BOOLEAN_KEYWORD, PrimitiveType::BOOLEAN}, + {ir::AstNodeType::TS_VOID_KEYWORD, PrimitiveType::VOID}, + {ir::AstNodeType::TS_STRING_KEYWORD, PrimitiveType::STRING}, + {ir::AstNodeType::TS_SYMBOL_KEYWORD, PrimitiveType::SYMBOL}, + {ir::AstNodeType::TS_NULL_KEYWORD, PrimitiveType::NUL}, + {ir::AstNodeType::TS_UNDEFINED_KEYWORD, PrimitiveType::UNDEFINED}, + // Placeholder for Type Inference INT +}; + +enum BuiltinType : uint8_t { + BT_HEAD = 20, + BT_FUNCTION, + BT_RANGEERROR, + BT_ERROR, + BT_OBJECT, + BT_SYNTAXERROR, + BT_TYPEERROR, + BT_REFERENCEERROR, + BT_URIERROR, + BT_SYMBOL, + BT_EVALERROR, + BT_NUMBER, + BT_PARSEFLOAT, + BT_DATE, + BT_BOOLEAN, + BT_BIGINT, + BT_PARSEINT, + BT_WEAKMAP, + BT_REGEXP, + BT_SET, + BT_MAP, + BT_WEAKREF, + BT_WEAKSET, + BT_FINALIZATIONREGISTRY, + BT_ARRAY, + BT_UINT8CLAMPEDARRAY, + BT_UINT8ARRAY, + BT_TYPEDARRAY, + BT_INT8ARRAY, + BT_UINT16ARRAY, + BT_UINT32ARRAY, + BT_INT16ARRAY, + BT_INT32ARRAY, + BT_FLOAT32ARRAY, + BT_FLOAT64ARRAY, + BT_BIGINT64ARRAY, + BT_BIGUINT64ARRAY, + BT_SHAREDARRAYBUFFER, + BT_DATAVIEW, + BT_STRING, + BT_ARRAYBUFFER, + BT_EVAL, + BT_ISFINITE, + BT_ARKPRIVATE, + BT_PRINT, + BT_DECODEURI, + BT_DECODEURICOMPONENT, + BT_ISNAN, + BT_ENCODEURI, + BT_NAN, + BT_GLOBALTHIS, + BT_ENCODEURICOMPONENT, + BT_INFINITY, + BT_MATH, + BT_JSON, + BT_ATOMICS, + BT_UNDEFINED, + BT_REFLECT, + BT_PROMISE, + BT_PROXY, + BT_GENERATORFUNCTION, + BT_INTL, +}; + +const std::unordered_map BUILTIN_TYPE_MAP = { + {"Function", BuiltinType::BT_FUNCTION}, + {"RangeError", BuiltinType::BT_RANGEERROR}, + {"Error", BuiltinType::BT_ERROR}, + {"Object", BuiltinType::BT_OBJECT}, + {"SyntaxError", BuiltinType::BT_SYNTAXERROR}, + {"TypeError", BuiltinType::BT_TYPEERROR}, + {"ReferenceError", BuiltinType::BT_REFERENCEERROR}, + {"URIError", BuiltinType::BT_URIERROR}, + {"Symbol", BuiltinType::BT_SYMBOL}, + {"EvalError", BuiltinType::BT_EVALERROR}, + {"Number", BuiltinType::BT_NUMBER}, + {"parseFloat", BuiltinType::BT_PARSEFLOAT}, + {"Date", BuiltinType::BT_DATE}, + {"Boolean", BuiltinType::BT_BOOLEAN}, + {"BigInt", BuiltinType::BT_BIGINT}, + {"parseInt", BuiltinType::BT_PARSEINT}, + {"WeakMap", BuiltinType::BT_WEAKMAP}, + {"RegExp", BuiltinType::BT_REGEXP}, + {"Set", BuiltinType::BT_SET}, + {"Map", BuiltinType::BT_MAP}, + {"WeakRef", BuiltinType::BT_WEAKREF}, + {"WeakSet", BuiltinType::BT_WEAKSET}, + {"FinalizationRegistry", BuiltinType::BT_FINALIZATIONREGISTRY}, + {"Array", BuiltinType::BT_ARRAY}, + {"Uint8ClampedArray", BuiltinType::BT_UINT8CLAMPEDARRAY}, + {"Uint8Array", BuiltinType::BT_UINT8ARRAY}, + {"TypedArray", BuiltinType::BT_TYPEDARRAY}, + {"Int8Array", BuiltinType::BT_INT8ARRAY}, + {"Uint16Array", BuiltinType::BT_UINT16ARRAY}, + {"Uint32Array", BuiltinType::BT_UINT32ARRAY}, + {"Int16Array", BuiltinType::BT_INT16ARRAY}, + {"Int32Array", BuiltinType::BT_INT32ARRAY}, + {"Float32Array", BuiltinType::BT_FLOAT32ARRAY}, + {"Float64Array", BuiltinType::BT_FLOAT64ARRAY}, + {"BigInt64Array", BuiltinType::BT_BIGINT64ARRAY}, + {"BigUint64Array", BuiltinType::BT_BIGUINT64ARRAY}, + {"SharedArrayBuffer", BuiltinType::BT_SHAREDARRAYBUFFER}, + {"DataView", BuiltinType::BT_DATAVIEW}, + {"String", BuiltinType::BT_STRING}, + {"ArrayBuffer", BuiltinType::BT_ARRAYBUFFER}, + {"eval", BuiltinType::BT_EVAL}, + {"isFinite", BuiltinType::BT_ISFINITE}, + {"ArkPrivate", BuiltinType::BT_ARKPRIVATE}, + {"print", BuiltinType::BT_PRINT}, + {"decodeURI", BuiltinType::BT_DECODEURI}, + {"decodeURIComponent", BuiltinType::BT_DECODEURICOMPONENT}, + {"isNaN", BuiltinType::BT_ISNAN}, + {"encodeURI", BuiltinType::BT_ENCODEURI}, + {"NaN", BuiltinType::BT_NAN}, + {"globalThis", BuiltinType::BT_GLOBALTHIS}, + {"encodeURIComponent", BuiltinType::BT_ENCODEURICOMPONENT}, + {"Infinity", BuiltinType::BT_INFINITY}, + {"Math", BuiltinType::BT_MATH}, + {"JSON", BuiltinType::BT_JSON}, + {"Atomics", BuiltinType::BT_ATOMICS}, + {"undefined", BuiltinType::BT_UNDEFINED}, + {"Reflect", BuiltinType::BT_REFLECT}, + {"Promise", BuiltinType::BT_PROMISE}, + {"Proxy", BuiltinType::BT_PROXY}, + {"GeneratorFunction", BuiltinType::BT_GENERATORFUNCTION}, + {"Intl", BuiltinType::BT_INTL}, +}; + +enum UserType : uint8_t { + COUNTER, + CLASS, + CLASSINST, + FUNCTION, + UNION, + ARRAY, + OBJECT, + EXTERNAL, + INTERFACE, + BUILTININST +}; + +enum Modifier : uint8_t { + NONSTATIC = 0, + STATIC = 1 << 2, + ASYNC = 1 << 3, + GENERATOR = 1 << 4 +}; + +enum ModifierAB : uint8_t { + NONABSTRACT, + ABSTRACT +}; + +enum ModifierRO : uint8_t { + NONREADONLY, + READONLY +}; + +enum AccessFlag : uint8_t { + PUBLIC, + PRIVATE, + PROTECTED +}; + +class BaseType { +public: + explicit BaseType(TypeExtractor *extractor) + : extractor_(extractor), recorder_(extractor_->Recorder()), buffer_(recorder_->NewLiteralBuffer()) + { + } + ~BaseType() = default; + NO_COPY_SEMANTIC(BaseType); + NO_MOVE_SEMANTIC(BaseType); + +protected: + TypeExtractor *extractor_; + TypeRecorder *recorder_; + compiler::LiteralBuffer *buffer_; + + void CalculateIndex(const util::StringView &name, int64_t &typeIndex, int64_t &typeIndexShift, bool forBuiltin) + { + if (forBuiltin && extractor_->GetTypeDtsBuiltin()) { + auto t = BUILTIN_TYPE_MAP.find(std::string(name)); + if (t != BUILTIN_TYPE_MAP.end()) { + typeIndexShift = t->second; + typeIndex = typeIndexShift - BuiltinType::BT_HEAD; + } + } + + if (typeIndex == PrimitiveType::ANY) { + typeIndex = recorder_->AddLiteralBuffer(buffer_); + typeIndexShift = typeIndex + recorder_->GetUserTypeIndexShift(); + } else { + recorder_->SetLiteralBuffer(typeIndex, buffer_); + } + } + + std::variant CalculateName(const ir::Expression *expression) + { + if (expression->IsLiteral()) { + auto name = expression->AsLiteral()->GetName(); + if (name.has_value()) { + return name.value(); + } + return nullptr; + } else { + return extractor_->GetIdentifierFromExpression(expression); + } + } + + std::string CalculateStr(const ArenaVector &expressions) + { + std::vector tmp; + for (const auto &t : expressions) { + ir::AstDumper dumper(t); + tmp.emplace_back(dumper.Str()); + } + std::sort(tmp.begin(), tmp.end()); + + std::stringstream ss; + for (const auto &t : tmp) { + ss << t; + } + return ss.str(); + } +}; + +class TypeCounter : public BaseType { +public: + explicit TypeCounter(TypeExtractor *extractor) : BaseType(extractor) + { + typeIndexPlaceHolder_ = recorder_->AddLiteralBuffer(buffer_); + if (extractor_->GetTypeDtsExtractor()) { + // Make builtin type slots + for (int64_t i = 0; i < TypeRecorder::USERTYPEINDEXHEAD - BuiltinType::BT_HEAD; i++) { + auto buffer = recorder_->NewLiteralBuffer(); + (void)recorder_->AddLiteralBuffer(buffer); + } + recorder_->SetUserTypeIndexShift(BuiltinType::BT_HEAD); + } + } + ~TypeCounter() = default; + NO_COPY_SEMANTIC(TypeCounter); + NO_MOVE_SEMANTIC(TypeCounter); + + int64_t GetTypeIndexPlaceHolder() const + { + return typeIndexPlaceHolder_; + } + + void FillLiteralBuffer() + { + int64_t userTypeNumber = recorder_->CalculateUserType(); + buffer_->Add(recorder_->Allocator()->New(UserType::COUNTER)); + if (extractor_->GetTypeDtsExtractor()) { + buffer_->Add(recorder_->Allocator()->New(userTypeNumber + + TypeRecorder::USERTYPEINDEXHEAD - BuiltinType::BT_HEAD)); + } else { + buffer_->Add(recorder_->Allocator()->New(userTypeNumber)); + } + const auto &anonymousReExport = recorder_->GetAnonymousReExport(); + buffer_->Add(recorder_->Allocator()->New(anonymousReExport.size())); + std::for_each(anonymousReExport.begin(), anonymousReExport.end(), [this](const auto &t) { + buffer_->Add(recorder_->Allocator()->New(t)); + }); + recorder_->SetLiteralBuffer(typeIndexPlaceHolder_, buffer_); + } + +private: + int64_t typeIndexPlaceHolder_ = PrimitiveType::ANY; +}; + +// For { MethodDefinition / FunctionDeclaration / FunctionExpression } -> ScriptFunction / TSMethodSignature +class FunctionType : public BaseType { +public: + explicit FunctionType(TypeExtractor *extractor, const ir::AstNode *node, const util::StringView &name) + : BaseType(extractor), paramsTypeIndex_(recorder_->Allocator()->Adapter()) + { + CalculateIndex(name, typeIndex_, typeIndexShift_, !(node->IsTSMethodSignature())); + name_ = name; + + auto fn = [this](const auto *func) { + recorder_->SetNodeTypeIndex(func, typeIndexShift_); + recorder_->AddUserType(typeIndexShift_); + if constexpr (std::is_same_v) { + if (name_.Is("")) { + name_ = recorder_->GetAnonymousFunctionNames(func); + } + FillModifier(func); + } + FillParameters(func); + FillReturn(func); + FillLiteralBuffer(); + }; + if (node->IsMethodDefinition()) { + auto methodDef = node->AsMethodDefinition(); + auto modifiers = methodDef->Modifiers(); + if (modifiers & ir::ModifierFlags::PRIVATE) { + accessFlag_ = AccessFlag::PRIVATE; + } + if (modifiers & ir::ModifierFlags::PROTECTED) { + accessFlag_ = AccessFlag::PROTECTED; + } + if (methodDef->IsStatic()) { + modifier_ += Modifier::STATIC; + } + fn(node->AsMethodDefinition()->Function()); + } else if (node->IsFunctionDeclaration()) { + fn(node->AsFunctionDeclaration()->Function()); + } else if (node->IsFunctionExpression()) { + fn(node->AsFunctionExpression()->Function()); + } else if (node->IsTSMethodSignature()) { + fn(node->AsTSMethodSignature()); + } + } + ~FunctionType() = default; + NO_COPY_SEMANTIC(FunctionType); + NO_MOVE_SEMANTIC(FunctionType); + + uint8_t GetModifier() const + { + return modifier_; + } + + int64_t GetTypeIndexShift() const + { + return typeIndexShift_; + } + +private: + util::StringView name_ {}; + AccessFlag accessFlag_ = AccessFlag::PUBLIC; + uint8_t modifier_ = Modifier::NONSTATIC; + bool containThis_ = false; + ArenaVector paramsTypeIndex_; + int64_t typeIndexReturn_ = PrimitiveType::ANY; + int64_t typeIndex_ = PrimitiveType::ANY; + int64_t typeIndexShift_ = PrimitiveType::ANY; + + void FillModifier(const ir::ScriptFunction *scriptFunc) + { + if (scriptFunc->IsAsync()) { + modifier_ += Modifier::ASYNC; + } + if (scriptFunc->IsGenerator()) { + modifier_ += Modifier::GENERATOR; + } + } + + template + void FillParameters(const T *func) + { + for (const auto &t : func->Params()) { + auto param = t; + if (t->IsTSParameterProperty()) { + param = t->AsTSParameterProperty()->Parameter(); + } + if (param->IsAssignmentExpression()) { + param = param->AsAssignmentExpression()->Left(); + } + + // Identifier / SpreadElement / RestElement / ArrayExpression / ObjectExpression + auto identifier = extractor_->GetIdentifierFromExpression(param); + if (identifier != nullptr) { + if (identifier->Name().Is("this") && paramsTypeIndex_.size() == 0) { + containThis_ = true; + } + paramsTypeIndex_.emplace_back(extractor_->GetTypeIndexFromIdentifier(identifier)); + } else { + paramsTypeIndex_.emplace_back(PrimitiveType::ANY); + } + } + } + + template + void FillReturn(const T *func) + { + typeIndexReturn_ = extractor_->GetTypeIndexFromAnnotation(func->ReturnTypeAnnotation()); + } + + void FillLiteralBuffer() + { + buffer_->Add(recorder_->Allocator()->New(UserType::FUNCTION)); + buffer_->Add(recorder_->Allocator()->New(accessFlag_ + modifier_)); + buffer_->Add(recorder_->Allocator()->New(name_)); + if (containThis_) { + buffer_->Add(recorder_->Allocator()->New(1)); + buffer_->Add(recorder_->Allocator()->New(paramsTypeIndex_.at(0))); + buffer_->Add(recorder_->Allocator()->New(paramsTypeIndex_.size() - 1)); + for (size_t i = 1; i < paramsTypeIndex_.size(); i++) { + buffer_->Add(recorder_->Allocator()->New(paramsTypeIndex_.at(i))); + } + } else { + buffer_->Add(recorder_->Allocator()->New(0)); + buffer_->Add(recorder_->Allocator()->New(paramsTypeIndex_.size())); + for (size_t i = 0; i < paramsTypeIndex_.size(); i++) { + buffer_->Add(recorder_->Allocator()->New(paramsTypeIndex_.at(i))); + } + } + buffer_->Add(recorder_->Allocator()->New(typeIndexReturn_)); + } +}; + +// For { ClassDeclaration / ClassExpression } -> ClassDefinition +class ClassType : public BaseType { +public: + explicit ClassType(TypeExtractor *extractor, const ir::ClassDefinition *classDef, const util::StringView &name) + : BaseType(extractor), + implementsHeritages_(recorder_->Allocator()->Adapter()), + staticFields_(recorder_->Allocator()->Adapter()), + staticMethods_(recorder_->Allocator()->Adapter()), + fields_(recorder_->Allocator()->Adapter()), + methods_(recorder_->Allocator()->Adapter()) + { + CalculateIndex(name, typeIndex_, typeIndexShift_, true); + recorder_->SetNodeTypeIndex(classDef, typeIndexShift_); + recorder_->AddUserType(typeIndexShift_); + + FillModifier(classDef); + FillHeritages(classDef); + FillFieldsandMethods(classDef); + FillLiteralBuffer(); + } + ~ClassType() = default; + NO_COPY_SEMANTIC(ClassType); + NO_MOVE_SEMANTIC(ClassType); + + int64_t GetTypeIndexShift() + { + return typeIndexShift_; + } + +private: + ModifierAB modifierAB_ = ModifierAB::NONABSTRACT; + int64_t extendsHeritage_ = PrimitiveType::ANY; + ArenaVector implementsHeritages_; + // 3 field infos, typeIndex / accessFlag / modifier + ArenaMap> staticFields_; + ArenaMap staticMethods_; + // 3 field infos, typeIndex / accessFlag / modifier + ArenaMap> fields_; + ArenaMap methods_; + int64_t typeIndex_ = PrimitiveType::ANY; + int64_t typeIndexShift_ = PrimitiveType::ANY; + + void FillModifier(const ir::ClassDefinition *classDef) + { + if (classDef->Abstract()) { + modifierAB_ = ModifierAB::ABSTRACT; + } + } + + void FillHeritages(const ir::ClassDefinition *classDef) + { + auto super = classDef->Super(); + if (super != nullptr) { + extendsHeritage_ = extractor_->GetTypeIndexFromInitializer(super); + } + + for (const auto &t : classDef->Implements()) { + if (t != nullptr) { + implementsHeritages_.emplace_back(extractor_->GetTypeIndexFromInitializer(t)); + } + } + } + + void FillField(const ir::ClassProperty *field) + { + auto modifiers = field->Modifiers(); + bool isStatic = (modifiers & ir::ModifierFlags::STATIC); + AccessFlag flag = AccessFlag::PUBLIC; + if (modifiers & ir::ModifierFlags::PRIVATE) { + flag = AccessFlag::PRIVATE; + } + if (modifiers & ir::ModifierFlags::PROTECTED) { + flag = AccessFlag::PROTECTED; + } + ModifierRO modifier = ModifierRO::NONREADONLY; + if (modifiers & ir::ModifierFlags::READONLY) { + modifier = ModifierRO::READONLY; + } + + int64_t typeIndex = extractor_->GetTypeIndexFromAnnotation(field->TypeAnnotation()); + // 3 field infos, typeIndex / accessFlag / modifier + std::array fieldInfo = {typeIndex, flag, modifier}; + auto fn = [&fieldInfo, &isStatic, this](const util::StringView &name) { + if (isStatic) { + staticFields_[name] = fieldInfo; + } else { + fields_[name] = fieldInfo; + } + }; + + const ir::Expression *expression; + if (field->Key()->IsTSPrivateIdentifier()) { + expression = field->Key()->AsTSPrivateIdentifier()->Key(); + } else { + expression = field->Key(); + } + auto res = CalculateName(expression); + if (std::holds_alternative(res)) { + fn(std::get(res)); + } else { + auto identifier = std::get(res); + if (identifier != nullptr) { + recorder_->SetIdentifierTypeIndex(identifier, typeIndex); + fn(identifier->Name()); + } + } + } + + void FillMethod(const ir::MethodDefinition *method) + { + auto fn = [&method, this](const FunctionType &functionType, const util::StringView &name) { + if (functionType.GetModifier() == Modifier::NONSTATIC) { + methods_[name] = recorder_->GetNodeTypeIndex(method->Function()); + } else { + staticMethods_[name] = recorder_->GetNodeTypeIndex(method->Function()); + } + }; + + auto res = CalculateName(method->Key()); + if (std::holds_alternative(res)) { + auto name = std::get(res); + FunctionType functionType(extractor_, method, name); + fn(functionType, name); + } else { + auto identifier = std::get(res); + if (identifier != nullptr) { + auto name = identifier->Name(); + FunctionType functionType(extractor_, method, name); + recorder_->SetIdentifierTypeIndex(identifier, functionType.GetTypeIndexShift()); + fn(functionType, name); + } + } + } + + void FillFieldsandMethods(const ir::ClassDefinition *classDef) + { + auto methodDef = const_cast(classDef)->Ctor(); + ASSERT(methodDef->IsMethodDefinition()); + // Filter Implicit Constructor + const auto &range = methodDef->Key()->Range(); + if (!(range.start.index == range.end.index && range.start.line == range.end.line && + range.start.index == range.start.line && range.end.index == range.end.line)) { + FillMethod(methodDef); + } + + for (const auto &t : classDef->Body()) { + if (t->IsClassProperty()) { + FillField(t->AsClassProperty()); + } else if (t->IsMethodDefinition()) { + FillMethod(t->AsMethodDefinition()); + } + } + } + + void FillFieldsLiteralBuffer(bool isStatic) + { + // 3 field infos, typeIndex / accessFlag / modifier + auto fn = [this](const ArenaMap> &map) { + buffer_->Add(recorder_->Allocator()->New(map.size())); + std::for_each(map.begin(), map.end(), [this](const auto &t) { + buffer_->Add(recorder_->Allocator()->New(t.first)); + buffer_->Add(recorder_->Allocator()->New(t.second[0])); // 0. typeIndex + buffer_->Add(recorder_->Allocator()->New(t.second[1])); // 1. accessFlag + buffer_->Add(recorder_->Allocator()->New(t.second[2])); // 2. modifier + }); + }; + + if (isStatic) { + fn(staticFields_); + } else { + fn(fields_); + } + } + + void FillMethodsLiteralBuffer(bool isStatic) + { + auto fn = [this](const ArenaMap &map) { + buffer_->Add(recorder_->Allocator()->New(map.size())); + std::for_each(map.begin(), map.end(), [this](const auto &t) { + buffer_->Add(recorder_->Allocator()->New(t.first)); + buffer_->Add(recorder_->Allocator()->New(t.second)); + }); + }; + + if (isStatic) { + fn(staticMethods_); + } else { + fn(methods_); + } + } + + void FillLiteralBuffer() + { + buffer_->Add(recorder_->Allocator()->New(UserType::CLASS)); + buffer_->Add(recorder_->Allocator()->New(modifierAB_)); + buffer_->Add(recorder_->Allocator()->New(extendsHeritage_)); + buffer_->Add(recorder_->Allocator()->New(implementsHeritages_.size())); + std::for_each(implementsHeritages_.begin(), implementsHeritages_.end(), [this](const auto &t) { + buffer_->Add(recorder_->Allocator()->New(t)); + }); + + FillFieldsLiteralBuffer(false); + FillMethodsLiteralBuffer(false); + FillFieldsLiteralBuffer(true); + FillMethodsLiteralBuffer(true); + } +}; + +class ClassInstType : public BaseType { +public: + explicit ClassInstType(TypeExtractor *extractor, int64_t typeIndexRefShift) + : BaseType(extractor), typeIndexRefShift_(typeIndexRefShift) + { + typeIndex_ = recorder_->AddLiteralBuffer(buffer_); + typeIndexShift_ = typeIndex_ + recorder_->GetUserTypeIndexShift(); + recorder_->SetClassInst(typeIndexRefShift_, typeIndexShift_); + recorder_->SetClassType(typeIndexShift_, typeIndexRefShift_); + recorder_->AddUserType(typeIndexShift_); + + FillLiteralBuffer(); + } + ~ClassInstType() = default; + NO_COPY_SEMANTIC(ClassInstType); + NO_MOVE_SEMANTIC(ClassInstType); + + int64_t GetTypeIndexShift() const + { + return typeIndexShift_; + } + +private: + int64_t typeIndexRefShift_ = PrimitiveType::ANY; + int64_t typeIndex_ = PrimitiveType::ANY; + int64_t typeIndexShift_ = PrimitiveType::ANY; + + void FillLiteralBuffer() + { + buffer_->Add(recorder_->Allocator()->New(UserType::CLASSINST)); + buffer_->Add(recorder_->Allocator()->New(typeIndexRefShift_)); + } +}; + +class InterfaceType : public BaseType { +public: + explicit InterfaceType(TypeExtractor *extractor, const ir::TSInterfaceDeclaration *interfaceDef, + const util::StringView &name) + : BaseType(extractor), + heritages_(recorder_->Allocator()->Adapter()), + fields_(recorder_->Allocator()->Adapter()), + methods_(recorder_->Allocator()->Adapter()) + { + CalculateIndex(name, typeIndex_, typeIndexShift_, true); + recorder_->SetNodeTypeIndex(interfaceDef, typeIndexShift_); + recorder_->AddUserType(typeIndexShift_); + + FillHeritages(interfaceDef); + FillFieldsandMethods(interfaceDef); + FillLiteralBuffer(); + } + ~InterfaceType() = default; + NO_COPY_SEMANTIC(InterfaceType); + NO_MOVE_SEMANTIC(InterfaceType); + + int64_t GetTypeIndexShift() + { + return typeIndexShift_; + } + +private: + ArenaVector heritages_; + // 3 field infos, typeIndex / accessFlag / modifier + ArenaMap> fields_; + ArenaMap methods_; + int64_t typeIndex_ = PrimitiveType::ANY; + int64_t typeIndexShift_ = PrimitiveType::ANY; + + void FillHeritages(const ir::TSInterfaceDeclaration *interfaceDef) + { + for (const auto &t : interfaceDef->Extends()) { + if (t != nullptr) { + heritages_.emplace_back(extractor_->GetTypeIndexFromInitializer(t)); + } + } + } + + void FillField(const ir::TSPropertySignature *field) + { + AccessFlag flag = AccessFlag::PUBLIC; + ModifierRO modifier = ModifierRO::NONREADONLY; + if (field->Readonly()) { + modifier = ModifierRO::READONLY; + } + + int64_t typeIndex = extractor_->GetTypeIndexFromAnnotation(field->TypeAnnotation()); + // 3 field infos, typeIndex / accessFlag / modifier + std::array fieldInfo = {typeIndex, flag, modifier}; + + auto res = CalculateName(field->Key()); + if (std::holds_alternative(res)) { + fields_[std::get(res)] = fieldInfo; + } else { + auto identifier = std::get(res); + if (identifier != nullptr) { + recorder_->SetIdentifierTypeIndex(identifier, typeIndex); + fields_[identifier->Name()] = fieldInfo; + } + } + } + + void FillMethod(const ir::TSMethodSignature *method) + { + auto res = CalculateName(method->Key()); + if (std::holds_alternative(res)) { + auto name = std::get(res); + FunctionType functionType(extractor_, method, name); + methods_[name] = recorder_->GetNodeTypeIndex(method); + } else { + auto identifier = std::get(res); + if (identifier != nullptr) { + auto name = identifier->Name(); + FunctionType functionType(extractor_, method, name); + recorder_->SetIdentifierTypeIndex(identifier, functionType.GetTypeIndexShift()); + methods_[name] = recorder_->GetNodeTypeIndex(method); + } + } + } + + void FillFieldsandMethods(const ir::TSInterfaceDeclaration *interfaceDef) + { + for (const auto &t : interfaceDef->Body()->Body()) { + if (t->IsTSPropertySignature()) { + FillField(t->AsTSPropertySignature()); + } else if (t->IsTSMethodSignature()) { + FillMethod(t->AsTSMethodSignature()); + } + } + } + + void FillFieldsLiteralBuffer() + { + buffer_->Add(recorder_->Allocator()->New(fields_.size())); + std::for_each(fields_.begin(), fields_.end(), [this](const auto &t) { + buffer_->Add(recorder_->Allocator()->New(t.first)); + buffer_->Add(recorder_->Allocator()->New(t.second[0])); // 0. typeIndex + buffer_->Add(recorder_->Allocator()->New(t.second[1])); // 1. accessFlag + buffer_->Add(recorder_->Allocator()->New(t.second[2])); // 2. modifier + }); + } + + void FillMethodsLiteralBuffer() + { + buffer_->Add(recorder_->Allocator()->New(methods_.size())); + std::for_each(methods_.begin(), methods_.end(), [this](const auto &t) { + buffer_->Add(recorder_->Allocator()->New(t.first)); + buffer_->Add(recorder_->Allocator()->New(t.second)); + }); + } + + void FillLiteralBuffer() + { + buffer_->Add(recorder_->Allocator()->New(UserType::INTERFACE)); + buffer_->Add(recorder_->Allocator()->New(heritages_.size())); + std::for_each(heritages_.begin(), heritages_.end(), [this](const auto &t) { + buffer_->Add(recorder_->Allocator()->New(t)); + }); + + FillFieldsLiteralBuffer(); + FillMethodsLiteralBuffer(); + } +}; + +class ExternalType : public BaseType { +public: + explicit ExternalType(TypeExtractor *extractor, const util::StringView &importName, + const util::StringView &redirectPath) : BaseType(extractor) + { + std::stringstream ss; + ss << "#" + std::string(importName) + "#" + std::string(redirectPath); + redirectPath_ = util::UString(ss.str(), recorder_->Allocator()).View(); + typeIndex_ = recorder_->AddLiteralBuffer(buffer_); + typeIndexShift_ = typeIndex_ + recorder_->GetUserTypeIndexShift(); + recorder_->AddUserType(typeIndexShift_); + + FillLiteralBuffer(); + } + ~ExternalType() = default; + NO_COPY_SEMANTIC(ExternalType); + NO_MOVE_SEMANTIC(ExternalType); + + int64_t GetTypeIndexShift() + { + return typeIndexShift_; + } + +private: + util::StringView redirectPath_; + int64_t typeIndex_ = PrimitiveType::ANY; + int64_t typeIndexShift_ = PrimitiveType::ANY; + + void FillLiteralBuffer() + { + buffer_->Add(recorder_->Allocator()->New(UserType::EXTERNAL)); + buffer_->Add(recorder_->Allocator()->New(redirectPath_)); + } +}; + +class UnionType : public BaseType { +public: + explicit UnionType(TypeExtractor *extractor, const ir::TSUnionType *unionDef) + : BaseType(extractor), unionTypes_(recorder_->Allocator()->Adapter()) + { + auto unionStr = CalculateStr(unionDef->Types()); + typeIndexShift_ = recorder_->GetUnionType(unionStr); + if (typeIndexShift_ != PrimitiveType::ANY) { + return; + } + + typeIndex_ = recorder_->AddLiteralBuffer(buffer_); + typeIndexShift_ = typeIndex_ + recorder_->GetUserTypeIndexShift(); + recorder_->SetUnionType(unionStr, typeIndexShift_); + recorder_->AddUserType(typeIndexShift_); + + FillTypes(unionDef); + FillLiteralBuffer(); + } + ~UnionType() = default; + NO_COPY_SEMANTIC(UnionType); + NO_MOVE_SEMANTIC(UnionType); + + int64_t GetTypeIndexShift() + { + return typeIndexShift_; + } + +private: + ArenaVector unionTypes_; + int64_t typeIndex_ = PrimitiveType::ANY; + int64_t typeIndexShift_ = PrimitiveType::ANY; + + void FillTypes(const ir::TSUnionType *unionDef) + { + for (const auto &t : unionDef->Types()) { + unionTypes_.emplace_back(extractor_->GetTypeIndexFromAnnotation(t)); + } + } + + void FillLiteralBuffer() + { + buffer_->Add(recorder_->Allocator()->New(UserType::UNION)); + buffer_->Add(recorder_->Allocator()->New(unionTypes_.size())); + std::for_each(unionTypes_.begin(), unionTypes_.end(), [this](const auto &t) { + buffer_->Add(recorder_->Allocator()->New(t)); + }); + } +}; + +class ArrayType : public BaseType { +public: + explicit ArrayType(TypeExtractor *extractor, const ir::TSArrayType *arrayDef) : BaseType(extractor) + { + typeIndexRefShift_ = extractor_->GetTypeIndexFromAnnotation(arrayDef->ElementType()); + typeIndexShift_ = recorder_->GetArrayType(typeIndexRefShift_); + if (typeIndexShift_ != PrimitiveType::ANY) { + return; + } + + typeIndex_ = recorder_->AddLiteralBuffer(buffer_); + typeIndexShift_ = typeIndex_ + recorder_->GetUserTypeIndexShift(); + recorder_->SetArrayType(typeIndexRefShift_, typeIndexShift_); + recorder_->AddUserType(typeIndexShift_); + + FillLiteralBuffer(); + } + ~ArrayType() = default; + NO_COPY_SEMANTIC(ArrayType); + NO_MOVE_SEMANTIC(ArrayType); + + int64_t GetTypeIndexShift() + { + return typeIndexShift_; + } + +private: + int64_t typeIndexRefShift_ = PrimitiveType::ANY; + int64_t typeIndex_ = PrimitiveType::ANY; + int64_t typeIndexShift_ = PrimitiveType::ANY; + + void FillLiteralBuffer() + { + buffer_->Add(recorder_->Allocator()->New(UserType::ARRAY)); + buffer_->Add(recorder_->Allocator()->New(typeIndexRefShift_)); + } +}; + +class ObjectType : public BaseType { +public: + explicit ObjectType(TypeExtractor *extractor, const ir::TSTypeLiteral *literalDef) + : BaseType(extractor), properties_(recorder_->Allocator()->Adapter()) + { + std::string objectStr = "object"; + if (literalDef != nullptr) { + objectStr = CalculateStr(literalDef->Members()); + } + typeIndexShift_ = recorder_->GetObjectType(objectStr); + if (typeIndexShift_ != PrimitiveType::ANY) { + return; + } + + typeIndex_ = recorder_->AddLiteralBuffer(buffer_); + typeIndexShift_ = typeIndex_ + recorder_->GetUserTypeIndexShift(); + recorder_->SetObjectType(objectStr, typeIndexShift_); + recorder_->AddUserType(typeIndexShift_); + + if (literalDef != nullptr) { + FillMembers(literalDef); + } + FillLiteralBuffer(); + } + ~ObjectType() = default; + NO_COPY_SEMANTIC(ObjectType); + NO_MOVE_SEMANTIC(ObjectType); + + int64_t GetTypeIndexShift() + { + return typeIndexShift_; + } + +private: + ArenaMap properties_; + int64_t typeIndex_ = PrimitiveType::ANY; + int64_t typeIndexShift_ = PrimitiveType::ANY; + + void FillMembers(const ir::TSTypeLiteral *literalDef) + { + auto fn = [this](const auto *property, int64_t typeIndex) { + auto res = CalculateName(property->Key()); + if (std::holds_alternative(res)) { + properties_[std::get(res)] = typeIndex; + } else { + auto identifier = std::get(res); + if (identifier != nullptr) { + recorder_->SetIdentifierTypeIndex(identifier, typeIndex); + properties_[identifier->Name()] = typeIndex; + } + } + }; + + for (const auto &t : literalDef->Members()) { + if (t->IsTSPropertySignature()) { + auto property = t->AsTSPropertySignature(); + int64_t typeIndex = extractor_->GetTypeIndexFromAnnotation(property->TypeAnnotation()); + fn(property, typeIndex); + } else if (t->IsTSMethodSignature()) { + auto property = t->AsTSMethodSignature(); + int64_t typeIndex = extractor_->GetTypeIndexFromAnnotation(property->ReturnTypeAnnotation()); + fn(property, typeIndex); + } + } + } + + void FillLiteralBuffer() + { + buffer_->Add(recorder_->Allocator()->New(UserType::OBJECT)); + buffer_->Add(recorder_->Allocator()->New(properties_.size())); + std::for_each(properties_.begin(), properties_.end(), [this](const auto &t) { + buffer_->Add(recorder_->Allocator()->New(t.first)); + buffer_->Add(recorder_->Allocator()->New(t.second)); + }); + } +}; + +class BuiltinInstType : public BaseType { +public: + explicit BuiltinInstType(TypeExtractor *extractor, const std::vector &allTypes) + : BaseType(extractor), paramTypes_(recorder_->Allocator()->Adapter()) + { + typeIndexBuiltin_ = allTypes[0]; + typeIndex_ = recorder_->AddLiteralBuffer(buffer_); + typeIndexShift_ = typeIndex_ + recorder_->GetUserTypeIndexShift(); + recorder_->SetBuiltinInst(allTypes, typeIndexShift_); + recorder_->AddUserType(typeIndexShift_); + + FillTypes(allTypes); + FillLiteralBuffer(); + } + ~BuiltinInstType() = default; + NO_COPY_SEMANTIC(BuiltinInstType); + NO_MOVE_SEMANTIC(BuiltinInstType); + + int64_t GetTypeIndexShift() + { + return typeIndexShift_; + } + +private: + ArenaVector paramTypes_; + int64_t typeIndexBuiltin_ = PrimitiveType::ANY; + int64_t typeIndex_ = PrimitiveType::ANY; + int64_t typeIndexShift_ = PrimitiveType::ANY; + + void FillTypes(const std::vector &allTypes) + { + for (size_t t = 1; t < allTypes.size(); t++) { + paramTypes_.emplace_back(allTypes[t]); + } + } + + void FillLiteralBuffer() + { + buffer_->Add(recorder_->Allocator()->New(UserType::BUILTININST)); + buffer_->Add(recorder_->Allocator()->New(typeIndexBuiltin_)); + buffer_->Add(recorder_->Allocator()->New(paramTypes_.size())); + std::for_each(paramTypes_.begin(), paramTypes_.end(), [this](const auto &t) { + buffer_->Add(recorder_->Allocator()->New(t)); + }); + } +}; + +} // namespace panda::es2panda::extractor + +#endif // ES2PANDA_TYPESCRIPT_EXACTOR_TYPESYSTEM_H diff --git a/es2panda/util/dumper.cpp b/es2panda/util/dumper.cpp index 0b5e38fe88..1a4851a2d4 100644 --- a/es2panda/util/dumper.cpp +++ b/es2panda/util/dumper.cpp @@ -27,9 +27,14 @@ void Dumper::DumpLiterals(std::map co std::cout << "{" << std::endl; std::cout << " index: " << count++ << std::endl; std::cout << " tag: " << - static_cast::type>(literal.tag_) << std::endl; + unsigned(static_cast::type>(literal.tag_)) << + std::endl; std::visit([](auto&& element) { - std::cout << " val: " << element << std::endl; + if constexpr (std::is_same_v) { + std::cout << " val: " << unsigned(element) << std::endl; + } else { + std::cout << " val: " << element << std::endl; + } }, literal.value_); std::cout << "}," << std::endl; } -- Gitee