* Copyright (c) 2024-2026 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 "es2panda_lib.h"
#include <cstddef>
#include <cstring>
#include <cstdint>
#include "util/diagnostic.h"
#include "util/eheap.h"
#include "util/perfMetrics.h"
#include "varbinder/varbinder.h"
#include "varbinder/scope.h"
#include "public/public.h"
#include "generated/signatures.h"
#include "es2panda.h"
#include "varbinder/ETSBinder.h"
#include "checker/ETSAnalyzer.h"
#include "checker/ETSchecker.h"
#include "compiler/core/compileQueue.h"
#include "compiler/core/compilerImpl.h"
#include "compiler/core/ETSCompiler.h"
#include "compiler/core/ETSemitter.h"
#include "compiler/core/ETSGen.h"
#include "compiler/core/regSpiller.h"
#include "compiler/lowering/phase.h"
#include "compiler/lowering/ets/lambdaLowering.h"
#include "ir/astNode.h"
#include "ir/expressions/arrowFunctionExpression.h"
#include "ir/ts/tsAsExpression.h"
#include "ir/expressions/assignmentExpression.h"
#include "ir/expressions/binaryExpression.h"
#include "ir/statements/blockStatement.h"
#include "ir/expressions/callExpression.h"
#include "ir/base/classProperty.h"
#include "ir/ets/etsFunctionType.h"
#include "ir/statements/ifStatement.h"
#include "ir/base/methodDefinition.h"
#include "ir/ets/etsGenericInstantiatedNode.h"
#include "ir/ets/etsNewClassInstanceExpression.h"
#include "ir/ets/etsNewArrayInstanceExpression.h"
#include "ir/ets/etsNewMultiDimArrayInstanceExpression.h"
#include "parser/ETSparser.h"
#include "parser/context/parserContext.h"
#include "parser/program/program.h"
#include "util/generateBin.h"
#include "util/options.h"
#include "compiler/lowering/util.h"
#include "generated/es2panda_lib/es2panda_lib_include.inc"
#include "declgen_ets2ts/declgenEts2Ts.h"
namespace ark::es2panda::public_lib {
static const char *ETSCACHE_SUFFIX = ".etscache";
static const char *DECL_ETS_SUFFIX = ".d.ets";
struct TokenTypeToStr {
lexer::TokenType token;
char const *str;
};
__attribute__((unused)) lexer::TokenType StrToToken(TokenTypeToStr const *table, char const *str)
{
for (auto *tp = table; tp->str != nullptr; tp++) {
if (strcmp(str, tp->str) == 0) {
return tp->token;
}
}
ES2PANDA_UNREACHABLE();
}
__attribute__((unused)) char const *TokenToStr(TokenTypeToStr const *table, lexer::TokenType token)
{
for (auto *tp = table; tp->str != nullptr; tp++) {
if (tp->token == token) {
return tp->str;
}
}
ES2PANDA_UNREACHABLE();
}
char *StringViewToCString(ArenaAllocator *allocator, std::string_view const utf8)
{
if (!utf8.empty() && (utf8.back() == '\0')) {
return const_cast<char *>(utf8.data());
}
char *res = reinterpret_cast<char *>(allocator->Alloc(utf8.size() + 1));
if (!utf8.empty()) {
[[maybe_unused]] auto err = memmove_s(res, utf8.size() + 1, utf8.cbegin(), utf8.size());
ES2PANDA_ASSERT(err == EOK);
}
res[utf8.size()] = '\0';
return res;
}
__attribute__((unused)) char *StringViewToCString(ArenaAllocator *allocator, util::StringView const sv)
{
return StringViewToCString(allocator, sv.Utf8());
}
__attribute__((unused)) char *StdStringToCString(ArenaAllocator *allocator, std::string str)
{
char *res = reinterpret_cast<char *>(allocator->Alloc(str.length() + 1));
[[maybe_unused]] auto err = memcpy_s(res, str.length() + 1, str.c_str(), str.length() + 1);
ES2PANDA_ASSERT(err == EOK);
return res;
}
__attribute__((unused)) char *UStringToCString(ArenaAllocator *allocator, util::UString const sv)
{
return StringViewToCString(allocator, sv.View());
}
__attribute__((unused)) es2panda_variantDoubleCharArrayBool EnumMemberResultToEs2pandaVariant(
ArenaAllocator *allocator, varbinder::EnumMemberResult variant)
{
es2panda_variantDoubleCharArrayBool es2panda_variant;
es2panda_variant.index = static_cast<int>(variant.index());
switch (es2panda_variant.index) {
case es2panda_variantIndex::CAPI_DOUBLE:
es2panda_variant.variant.d = std::get<double>(variant);
break;
case es2panda_variantIndex::CAPI_CHAR:
es2panda_variant.variant.c = StringViewToCString(allocator, std::get<util::StringView>(variant));
break;
case es2panda_variantIndex::CAPI_BOOL:
es2panda_variant.variant.b = std::get<bool>(variant);
break;
default:
break;
}
return es2panda_variant;
}
__attribute__((unused)) es2panda_DynamicImportData *DynamicImportDataToE2pPtr(
ArenaAllocator *allocator, const varbinder::DynamicImportData *dynamicImportData)
{
auto import = reinterpret_cast<const es2panda_AstNode *>(dynamicImportData->import);
auto specifier = reinterpret_cast<const es2panda_AstNode *>(dynamicImportData->specifier);
auto variable = reinterpret_cast<es2panda_Variable *>(dynamicImportData->variable);
auto es2pandaDynamicImportData = allocator->New<es2panda_DynamicImportData>();
es2pandaDynamicImportData->import = import;
es2pandaDynamicImportData->specifier = specifier;
es2pandaDynamicImportData->variable = variable;
return es2pandaDynamicImportData;
}
__attribute__((unused)) es2panda_DynamicImportData DynamicImportDataToE2p(
const varbinder::DynamicImportData dynamicImportData)
{
auto import = reinterpret_cast<const es2panda_AstNode *>(dynamicImportData.import);
auto specifier = reinterpret_cast<const es2panda_AstNode *>(dynamicImportData.specifier);
auto variable = reinterpret_cast<es2panda_Variable *>(dynamicImportData.variable);
es2panda_DynamicImportData es2pandaDynamicImportData;
es2pandaDynamicImportData.import = import;
es2pandaDynamicImportData.specifier = specifier;
es2pandaDynamicImportData.variable = variable;
return es2pandaDynamicImportData;
}
__attribute__((unused)) char const *ArenaStrdup(ArenaAllocator *allocator, char const *src)
{
size_t len = strlen(src);
char *res = reinterpret_cast<char *>(allocator->Alloc(len + 1));
[[maybe_unused]] auto err = memmove_s(res, len + 1, src, len);
ES2PANDA_ASSERT(err == EOK);
res[len] = '\0';
return res;
}
extern "C" void MemInitialize()
{
if (ScopedAllocatorsManager::IsInitialized()) {
return;
}
ScopedAllocatorsManager::Initialize();
}
extern "C" void MemFinalize()
{
ScopedAllocatorsManager::Finalize();
}
extern "C" es2panda_Config *CreateConfig(int args, char const *const *argv)
{
MemInitialize();
auto diagnosticEngine = new util::DiagnosticEngine();
auto *options = new util::Options(argv[0], *diagnosticEngine);
if (!options->Parse(Span(argv, args))) {
return nullptr;
}
ark::Logger::ComponentMask mask {};
mask.set(ark::Logger::Component::ES2PANDA);
ark::Logger::InitializeStdLogging(options->LogLevel(), mask);
auto *res = new ConfigImpl;
res->options = options;
res->diagnosticEngine = diagnosticEngine;
return reinterpret_cast<es2panda_Config *>(res);
}
extern "C" __attribute__((unused)) void ResetCounters()
{
compiler::ResetCalleeCountOutside();
compiler::ResetGenSymCounter();
}
extern "C" void DestroyConfigControl(es2panda_Config *config, bool logFlag)
{
auto *cfg = reinterpret_cast<ConfigImpl *>(config);
if (cfg == nullptr) {
return;
}
if (cfg->options->IsDumpPerfMetrics()) {
util::DumpPerfMetrics();
}
delete cfg->options;
if (logFlag) {
cfg->diagnosticEngine->FlushDiagnostic();
} else {
cfg->diagnosticEngine->ClearDiagnostics();
}
delete cfg->diagnosticEngine;
delete cfg;
ark::Logger::Destroy();
ResetCounters();
}
extern "C" void DestroyConfig(es2panda_Config *config)
{
DestroyConfigControl(config, true);
}
extern "C" void DestroyConfigWithoutLog(es2panda_Config *config)
{
DestroyConfigControl(config, false);
}
extern "C" __attribute__((unused)) char const *GetAllErrorMessages(es2panda_Context *context)
{
auto *ctx = reinterpret_cast<Context *>(context);
ES2PANDA_ASSERT(ctx != nullptr);
ES2PANDA_ASSERT(ctx->config != nullptr);
ES2PANDA_ASSERT(ctx->allocator != nullptr);
auto *cfg = reinterpret_cast<ConfigImpl *>(ctx->config);
ES2PANDA_ASSERT(cfg != nullptr);
ES2PANDA_ASSERT(cfg->diagnosticEngine != nullptr);
auto allMessages = cfg->diagnosticEngine->PrintAndFlushErrorDiagnostic();
size_t bufferSize = allMessages.length() + 1;
char *cStringMessages = reinterpret_cast<char *>(ctx->allocator->Alloc(bufferSize));
[[maybe_unused]] auto err = memcpy_s(cStringMessages, bufferSize, allMessages.c_str(), bufferSize);
ES2PANDA_ASSERT(err == EOK);
return cStringMessages;
}
extern "C" const es2panda_Options *ConfigGetOptions(es2panda_Config *config)
{
auto options = reinterpret_cast<ConfigImpl *>(config)->options;
return reinterpret_cast<const es2panda_Options *>(options);
}
static void CompileJob(public_lib::Context *context, varbinder::FunctionScope *scope,
compiler::ProgramElement *programElement)
{
if (!compiler::ETSFunctionEmitter::IsEmissionRequired(scope->Node()->AsScriptFunction(), context->parserProgram)) {
return;
}
compiler::StaticRegSpiller regSpiller;
auto allocator = ScopedAllocatorsManager::CreateAllocator();
compiler::ETSCompiler astCompiler {};
compiler::ETSGen cg {&allocator, ®Spiller, context, std::make_tuple(scope, programElement, &astCompiler)};
compiler::ETSFunctionEmitter funcEmitter {&cg, programElement};
funcEmitter.Generate();
}
static void GenerateStdLibCache(es2panda_Config *config, GlobalContext *globalContext, bool LspUsage);
extern "C" __attribute__((unused)) es2panda_GlobalContext *CreateGlobalContext(es2panda_Config *config,
const char **externalFileList,
size_t fileNum, bool LspUsage)
{
auto *globalContext = new GlobalContext;
for (size_t i = 0; i < fileNum; i++) {
auto fileName = externalFileList[i];
auto globalAllocator = EHeap::NewAllocator().release();
globalContext->cachedExternalPrograms.emplace(fileName, nullptr);
globalContext->externalProgramAllocators.emplace(fileName, globalAllocator);
}
GenerateStdLibCache(config, globalContext, LspUsage);
return reinterpret_cast<es2panda_GlobalContext *>(globalContext);
}
extern "C" __attribute__((unused)) void RemoveFileCache(es2panda_GlobalContext *globalContext, const char *fileName)
{
auto globalCtx = reinterpret_cast<GlobalContext *>(globalContext);
ES2PANDA_ASSERT(globalCtx->cachedExternalPrograms.count(fileName) == 1);
globalCtx->cachedExternalPrograms.erase(fileName);
delete globalCtx->externalProgramAllocators[fileName];
globalCtx->externalProgramAllocators.erase(fileName);
}
extern "C" __attribute__((unused)) void AddFileCache(es2panda_GlobalContext *globalContext, const char *fileName)
{
auto globalCtx = reinterpret_cast<GlobalContext *>(globalContext);
ES2PANDA_ASSERT(globalCtx->cachedExternalPrograms.count(fileName) == 0);
auto globalAllocator = EHeap::NewAllocator().release();
globalCtx->cachedExternalPrograms.emplace(fileName, nullptr);
globalCtx->externalProgramAllocators.emplace(fileName, globalAllocator);
}
extern "C" __attribute__((unused)) void InvalidateFileCache(es2panda_GlobalContext *globalContext, const char *fileName)
{
RemoveFileCache(globalContext, fileName);
AddFileCache(globalContext, fileName);
}
static void ResetContextAndProgram(Context *ctx, parser::Program *program)
{
ctx->phaseManager->Reset();
ctx->diagnosticEngine->ClearDiagnostics();
ctx->state = ES2PANDA_STATE_NEW;
delete ctx->sourceFile;
ctx->sourceFile = nullptr;
program->Clear();
}
static void UpdateProgramTextForIncremental(Context *ctx, parser::Program *program, std::string &&text)
{
auto *ImportInfo = const_cast<util::ImportInfo *>(&program->GetImportInfo());
auto sourcePath = std::string {ImportInfo->TextSource()};
auto resolvedPath = ImportInfo->ResolvedSource();
ImportInfo->UpdateTextForLSPIncremental(sourcePath, std::move(text));
auto textView = ImportInfo->DataFor<parser::CacheType::SOURCES>();
SourceFile updatedSource {sourcePath, textView, resolvedPath, ctx->config->options->IsModule(),
program->IsDeclForDynamicStaticInterop()};
program->SetSource(updatedSource);
}
static void AddDirectDependenciesToExternalSourcesForIncremental(Context *ctx, parser::Program *targetProgram)
{
auto *importPathManager = ctx->parser->GetImportPathManager();
auto &externalSources = *targetProgram->GetExternalDecls();
externalSources.Direct().clear();
externalSources.Get<util::ModuleKind::MODULE>().clear();
externalSources.Get<util::ModuleKind::SOURCE_DECL>().clear();
auto depsIt = importPathManager->GetFileDependencies().find(ArenaString {targetProgram->AbsoluteName().Utf8()});
if (depsIt == importPathManager->GetFileDependencies().end()) {
return;
}
for (const auto &dep : depsIt->second) {
auto *depProgram =
importPathManager->FindOrIntroduceProgramForIncremental(std::string_view {dep.data(), dep.size()});
if (depProgram == nullptr || depProgram == targetProgram) {
continue;
}
externalSources.Add(depProgram);
}
}
extern "C" __attribute__((unused)) int IncrementalPrepareProgram(es2panda_Context *context, const char *fileName,
const char *sourceText, bool isChanged)
{
ES2PANDA_ASSERT(context != nullptr);
auto *ctx = reinterpret_cast<Context *>(context);
parser::Program *targetProgram =
ctx->parser->GetImportPathManager()->FindOrIntroduceProgramForIncremental(fileName);
if (!isChanged && targetProgram->Ast() != nullptr) {
ctx->parserProgram = targetProgram;
ctx->sourceFileName = fileName;
ctx->input = std::string(sourceText);
delete ctx->sourceFile;
ctx->sourceFile = new SourceFile(ctx->sourceFileName, ctx->input, ctx->config->options->IsModule());
AddDirectDependenciesToExternalSourcesForIncremental(ctx, targetProgram);
return 1;
}
ResetContextAndProgram(ctx, targetProgram);
ctx->parserProgram = targetProgram;
ctx->sourceFileName = fileName;
ctx->input = std::string(sourceText);
ctx->sourceFile = new SourceFile(ctx->sourceFileName, ctx->input, ctx->config->options->IsModule());
UpdateProgramTextForIncremental(ctx, targetProgram, std::string(sourceText));
ctx->parser->GetImportPathManager()->PrepareParseQueueForProgram(targetProgram);
return 0;
}
static void InitializeContext(Context *res)
{
parser::ImportCache<parser::CacheType::SOURCES>::ActivateCache();
res->phaseManager = new compiler::PhaseManager(res, ScriptExtension::ETS, res->allocator);
res->queue = new compiler::CompileQueue(res->config->options->GetThread());
res->parser = new parser::ETSParser(res, parser::ParserStatus::NO_OPTS);
res->PushChecker(res->allocator->New<checker::ETSChecker>(res->allocator, *res->diagnosticEngine));
res->PushAnalyzer(res->allocator->New<checker::ETSAnalyzer>(res->GetChecker()));
res->GetChecker()->SetAnalyzer(res->GetAnalyzer());
res->codeGenCb = CompileJob;
res->emitter = new compiler::ETSEmitter(res);
res->state = ES2PANDA_STATE_NEW;
}
static Context *InitContext(es2panda_Config *config)
{
auto *cfg = reinterpret_cast<ConfigImpl *>(config);
auto *res = new Context;
if (cfg == nullptr) {
res->errorMessage = "Config is nullptr.";
res->state = ES2PANDA_STATE_ERROR;
return res;
}
if (cfg->options->GetExtension() != ScriptExtension::ETS) {
res->errorMessage = "Invalid extension. Plugin API supports only ETS.";
res->state = ES2PANDA_STATE_ERROR;
res->diagnosticEngine = cfg->diagnosticEngine;
return res;
}
res->config = cfg;
res->diagnosticEngine = cfg->diagnosticEngine;
return res;
}
__attribute__((unused)) static es2panda_Context *CreateContext(es2panda_Config *config, std::string &&source,
const char *fileName,
es2panda_GlobalContext *globalContext, bool isExternal,
bool genStdLib, bool isLspUsage = false)
{
auto res = InitContext(config);
if (res->state == ES2PANDA_STATE_ERROR) {
return reinterpret_cast<es2panda_Context *>(res);
}
auto *cfg = reinterpret_cast<ConfigImpl *>(config);
res->isExternal = isExternal;
res->isLspUsage = isLspUsage;
res->globalContext = reinterpret_cast<GlobalContext *>(globalContext);
res->input = std::move(source);
res->sourceFileName = fileName;
res->sourceFile = new SourceFile(res->sourceFileName, res->input, cfg->options->IsModule());
if (isExternal) {
ir::EnableContextHistory();
ES2PANDA_ASSERT(res->globalContext != nullptr);
if (genStdLib) {
ES2PANDA_ASSERT(res->globalContext->stdLibAllocator != nullptr);
res->allocator = res->globalContext->stdLibAllocator;
} else {
ES2PANDA_ASSERT(res->globalContext->externalProgramAllocators.count(fileName) != 0);
res->allocator =
reinterpret_cast<ArenaAllocator *>(res->globalContext->externalProgramAllocators[fileName]);
}
} else {
ir::DisableContextHistory();
res->eheapScope = new EHeap::Scope();
res->allocator = EHeap::NewAllocator().release();
}
InitializeContext(res);
return reinterpret_cast<es2panda_Context *>(res);
}
__attribute__((unused)) static std::stringstream ReadFile(char const *sourceFileName, Context *&res)
{
std::ifstream inputStream;
inputStream.open(sourceFileName);
if (inputStream.fail()) {
res = new Context;
res->errorMessage = "Failed to open file: ";
res->errorMessage.append(sourceFileName);
}
std::stringstream ss;
ss << inputStream.rdbuf();
if (inputStream.fail()) {
res = new Context;
res->errorMessage = "Failed to read file: ";
res->errorMessage.append(sourceFileName);
}
return ss;
}
extern "C" __attribute__((unused)) es2panda_Context *CreateCacheContextFromFile(es2panda_Config *config,
char const *sourceFileName,
es2panda_GlobalContext *globalContext,
bool isExternal)
{
Context *res = nullptr;
auto ss = ReadFile(sourceFileName, res);
if (res != nullptr) {
return reinterpret_cast<es2panda_Context *>(res);
}
return CreateContext(config, ss.str(), sourceFileName, globalContext, isExternal, false);
}
extern "C" __attribute__((unused)) es2panda_Context *CreateContextFromFile(es2panda_Config *config,
char const *sourceFileName)
{
Context *res = nullptr;
auto ss = ReadFile(sourceFileName, res);
if (res != nullptr) {
return reinterpret_cast<es2panda_Context *>(res);
}
return CreateContext(config, ss.str(), sourceFileName, nullptr, false, false);
}
extern "C" __attribute__((unused)) es2panda_Context *CreateCacheContextFromString(es2panda_Config *config,
const char *source,
char const *fileName,
es2panda_GlobalContext *globalContext,
bool isExternal, bool isLspUsage)
{
return CreateContext(config, std::string(source), fileName, globalContext, isExternal, false, isLspUsage);
}
extern "C" __attribute__((unused)) es2panda_Context *CreateContextFromMultiFile(es2panda_Config *config,
char const *sourceFileNames)
{
return CreateContext(config, "", sourceFileNames, nullptr, false, false);
}
extern "C" __attribute__((unused)) es2panda_Context *CreateContextFromString(es2panda_Config *config,
const char *source, char const *fileName)
{
return CreateContext(config, std::string(source), fileName, nullptr, false, false);
}
extern __attribute__((unused)) es2panda_Context *CreateContextSimultaneousMode(es2panda_Config *config,
int fileNamesCount,
char const *const *fileNames)
{
auto res = InitContext(config);
if (res->state == ES2PANDA_STATE_ERROR) {
return reinterpret_cast<es2panda_Context *>(res);
}
auto *cfg = reinterpret_cast<ConfigImpl *>(config);
ES2PANDA_ASSERT(cfg->options->IsSimultaneous());
Span<const char *const> files(fileNames, fileNamesCount);
for (const char *file : files) {
res->sourceFileNames.emplace_back(std::string {file});
}
res->input = "";
res->sourceFileName = "";
res->sourceFile = new SourceFile(res->sourceFileName, res->input, cfg->options->IsModule());
ir::DisableContextHistory();
res->eheapScope = new EHeap::Scope();
res->allocator = EHeap::NewAllocator().release();
InitializeContext(res);
return reinterpret_cast<es2panda_Context *>(res);
}
extern __attribute__((unused)) es2panda_Context *CreateContextSimultaneousModeForLsp(es2panda_Config *config,
int fileNamesCount,
char const *const *fileNames,
bool isLspUsage)
{
auto *ctx = reinterpret_cast<Context *>(CreateContextSimultaneousMode(config, fileNamesCount, fileNames));
ctx->isLspUsage = isLspUsage;
return reinterpret_cast<es2panda_Context *>(ctx);
}
extern __attribute__((unused)) es2panda_Context *CreateContextGenerateAbcForExternalSourceFiles(
es2panda_Config *config, int fileNamesCount, char const *const *fileNames)
{
reinterpret_cast<ConfigImpl *>(config)->diagnosticEngine->LogDiagnostic(
diagnostic::DEPRECATED_PUBLIC_API, util::DiagnosticMessageParams {__FUNCTION__});
return CreateContextSimultaneousMode(config, fileNamesCount, fileNames);
}
extern "C" __attribute__((unused)) es2panda_Context *CreateContextFromStringWithHistory(es2panda_Config *config,
const char *source,
char const *fileName)
{
es2panda_Context *context = CreateContextFromString(config, source, fileName);
ir::EnableContextHistory();
return context;
}
__attribute__((unused)) static Context *Parse(Context *ctx)
{
if (ctx->state != ES2PANDA_STATE_NEW) {
ctx->state = ES2PANDA_STATE_ERROR;
ctx->errorMessage = "Bad state at entry to Parse, needed NEW";
return ctx;
}
ctx->phaseManager->Reset();
ES2PANDA_PERF_SCOPE("@Parser");
if (ctx->parserProgram != nullptr && ctx->isLspUsage) {
ctx->parser->AsETSParser()->IncrementalParse();
} else if (ctx->config->options->IsSimultaneous()) {
ES2PANDA_ASSERT(ctx->parserProgram == nullptr);
ctx->parser->AsETSParser()->ParseInSimultMode();
} else {
ES2PANDA_ASSERT(ctx->parserProgram == nullptr);
ctx->parser->ParseGlobal();
}
ES2PANDA_ASSERT(ctx->parserProgram != nullptr);
ctx->state = ES2PANDA_STATE_PARSED;
ctx->phaseManager->SetCurrentPhaseIdToAfterParse();
return ctx;
}
__attribute__((unused)) static Context *Bind(Context *ctx)
{
if (ctx->state < ES2PANDA_STATE_PARSED) {
ctx = Parse(ctx);
}
if (ctx->state == ES2PANDA_STATE_ERROR) {
return ctx;
}
ES2PANDA_ASSERT(ctx->state == ES2PANDA_STATE_PARSED);
while (auto phase = ctx->phaseManager->NextPhase()) {
if (phase->Name() == "plugins-after-bind") {
break;
}
phase->Apply(ctx);
}
ctx->state = ES2PANDA_STATE_BOUND;
return ctx;
}
__attribute__((unused)) static void SaveCache(Context *ctx)
{
if (ctx->allocator == ctx->globalContext->stdLibAllocator) {
return;
}
ES2PANDA_ASSERT(ctx->globalContext != nullptr);
auto &cacheMap = ctx->globalContext->cachedExternalPrograms;
ES2PANDA_ASSERT(cacheMap.count(ctx->sourceFileName) != 0);
cacheMap[ctx->sourceFileName] = ctx->parserProgram;
ctx->parserProgram->GetExternalDecls()->Visit([&cacheMap](auto *extProgram) {
auto absPath = std::string {extProgram->AbsoluteName()};
if (cacheMap.count(absPath) == 1 && cacheMap[absPath] == nullptr) {
cacheMap[absPath] = extProgram;
}
});
}
__attribute__((unused)) static void MarkAsLowered(Context *ctx)
{
auto markAsLowered = [](parser::Program *program) {
if (!program->IsASTLowered()) {
program->MarkASTAsLowered();
}
if (!program->IsProgramModified()) {
program->SetProgramModified(true);
}
};
markAsLowered(ctx->parserProgram);
ctx->parserProgram->SetProgramModified(true);
ctx->parserProgram->GetExternalDecls()->Visit<false>([&markAsLowered](auto *extProgram) {
markAsLowered(extProgram);
extProgram->MaybeIteratePackage(
[&markAsLowered](parser::Program *fractionOrSelf) { markAsLowered(fractionOrSelf); });
});
}
__attribute__((unused)) static Context *Check(Context *ctx)
{
if (ctx->state < ES2PANDA_STATE_PARSED) {
ctx = Parse(ctx);
}
if (ctx->state == ES2PANDA_STATE_ERROR) {
return ctx;
}
ES2PANDA_ASSERT(ctx->state >= ES2PANDA_STATE_PARSED && ctx->state < ES2PANDA_STATE_CHECKED);
while (auto phase = ctx->phaseManager->NextPhase()) {
if (phase->Name() == "plugins-after-check") {
break;
}
ES2PANDA_PERF_EVENT_SCOPE("@phases/" + std::string(phase->Name()));
phase->Apply(ctx);
}
ctx->phaseManager->SetCurrentPhaseIdToAfterCheck();
ctx->state = !ctx->diagnosticEngine->IsAnyError() ? ES2PANDA_STATE_CHECKED : ES2PANDA_STATE_ERROR;
if (ctx->isLspUsage) {
MarkAsLowered(ctx);
}
return ctx;
}
extern "C" void FreeCompilerPartMemory(es2panda_Context *context)
{
auto *ctx = reinterpret_cast<Context *>(context);
delete ctx->emitter;
ctx->emitter = nullptr;
delete ctx->parser;
ctx->parser = nullptr;
delete ctx->queue;
ctx->queue = nullptr;
delete ctx->sourceFile;
ctx->sourceFile = nullptr;
delete ctx->phaseManager;
if (!ctx->isExternal) {
delete ctx->allocator;
ctx->allocator = nullptr;
delete ctx->eheapScope;
ctx->eheapScope = nullptr;
} else {
ES2PANDA_ASSERT(ctx->globalContext != nullptr);
for (auto [_, varbinder] : ctx->parserProgram->VarBinders()) {
ctx->globalContext->allocatedVarbinders.insert(varbinder->AsETSBinder());
}
}
}
__attribute__((unused)) static Context *Lower(Context *ctx)
{
if (ctx->state < ES2PANDA_STATE_CHECKED) {
ctx = Check(ctx);
}
if (ctx->state == ES2PANDA_STATE_ERROR) {
return ctx;
}
ES2PANDA_ASSERT(ctx->state == ES2PANDA_STATE_CHECKED);
while (auto phase = ctx->phaseManager->NextPhase()) {
ES2PANDA_PERF_EVENT_SCOPE("@phases/" + std::string(phase->Name()));
phase->Apply(ctx);
if (ctx->diagnosticEngine->IsAnyError()) {
ctx->state = ES2PANDA_STATE_ERROR;
return ctx;
}
}
ctx->parserProgram->GetExternalDecls()->Visit([](auto *extProgram) {
if (!extProgram->IsASTLowered()) {
extProgram->MarkASTAsLowered();
}
});
if (ctx->isExternal) {
SaveCache(ctx);
}
ctx->state = ES2PANDA_STATE_LOWERED;
return ctx;
}
__attribute__((unused)) static Context *GenerateAsm(Context *ctx)
{
if (ctx->state < ES2PANDA_STATE_LOWERED) {
ctx = Lower(ctx);
}
if (ctx->state == ES2PANDA_STATE_ERROR) {
return ctx;
}
ES2PANDA_ASSERT(ctx->state == ES2PANDA_STATE_LOWERED);
ES2PANDA_PERF_SCOPE("@EmitProgram");
auto *emitter = ctx->emitter;
uint32_t index = 0;
for (const auto &buff : ctx->contextLiterals) {
emitter->AddLiteralBuffer(buff, index++);
}
emitter->LiteralBufferIndex() += ctx->contextLiterals.size();
ctx->queue->Schedule(ctx);
emitter->AsETSEmitter()->SetupDependenciesForTheProgram(ctx->parserProgram);
ctx->queue->Consume();
ctx->queue->Wait([emitter](compiler::CompileJob *job) { emitter->AddProgramElement(job->GetProgramElement()); });
if (ctx->config->options->GetCompilationMode() == CompilationMode::SIMULTANEOUS_INCREMENTAL) {
ctx->output = emitter->AsETSEmitter()->EmitRecordsSimultIncMode();
} else {
emitter->EmitRecords();
std::unordered_map<std::string, std::unique_ptr<pandasm::Program>> res;
auto &imd = ctx->parserProgram->GetImportInfo();
res.emplace(ctx->parser->GetImportPathManager()->FormAbcFilePath(imd),
std::unique_ptr<pandasm::Program>(emitter->DumpDebugInfo()));
ctx->output = std::move(res);
}
ctx->state = !ctx->diagnosticEngine->IsAnyError() ? ES2PANDA_STATE_ASM_GENERATED : ES2PANDA_STATE_ERROR;
return ctx;
}
__attribute__((unused)) Context *GenerateBin(Context *ctx)
{
if (ctx->state < ES2PANDA_STATE_ASM_GENERATED) {
ctx = GenerateAsm(ctx);
}
if (ctx->state == ES2PANDA_STATE_ERROR) {
return ctx;
}
ES2PANDA_ASSERT(ctx->state == ES2PANDA_STATE_ASM_GENERATED);
auto reporter = [ctx](const diagnostic::DiagnosticKind &kind, const util::DiagnosticMessageParams ¶ms) {
ctx->diagnosticEngine->LogDiagnostic(kind, params);
};
util::GenerateBinaryFiles(ctx->output, *ctx->config->options, reporter);
ctx->state = !ctx->diagnosticEngine->IsAnyError() ? ES2PANDA_STATE_BIN_GENERATED : ES2PANDA_STATE_ERROR;
return ctx;
}
extern "C" __attribute__((unused)) es2panda_Context *ProceedToState(es2panda_Context *context,
es2panda_ContextState state)
{
auto *ctx = reinterpret_cast<Context *>(context);
switch (state) {
case ES2PANDA_STATE_NEW:
break;
case ES2PANDA_STATE_PARSED:
ctx = Parse(ctx);
break;
case ES2PANDA_STATE_BOUND:
ctx = Bind(ctx);
break;
case ES2PANDA_STATE_CHECKED:
ctx = Check(ctx);
break;
case ES2PANDA_STATE_LOWERED:
ctx = Lower(ctx);
break;
case ES2PANDA_STATE_ASM_GENERATED:
ctx = GenerateAsm(ctx);
break;
case ES2PANDA_STATE_BIN_GENERATED:
ctx = GenerateBin(ctx);
break;
default:
ctx->errorMessage = "It does not make sense to request stage";
ctx->state = ES2PANDA_STATE_ERROR;
break;
}
if (ctx->config != nullptr) {
ctx->config->diagnosticEngine->EnsureLocations();
}
return reinterpret_cast<es2panda_Context *>(ctx);
}
extern "C" __attribute__((unused)) void DestroyContext(es2panda_Context *context)
{
auto *ctx = reinterpret_cast<Context *>(context);
if (ctx->config != nullptr) {
ctx->config->diagnosticEngine->EnsureLocations();
}
if (ctx->emitter != nullptr) {
FreeCompilerPartMemory(context);
}
delete ctx;
}
extern "C" __attribute__((unused)) void DestroyGlobalContext(es2panda_GlobalContext *globalContext)
{
auto *globalCtx = reinterpret_cast<GlobalContext *>(globalContext);
for (auto [_, alloctor] : globalCtx->externalProgramAllocators) {
delete alloctor;
}
delete globalCtx->stdLibAllocator;
delete globalCtx;
}
extern "C" __attribute__((unused)) es2panda_ContextState ContextState(es2panda_Context *context)
{
auto *s = reinterpret_cast<Context *>(context);
return s->state;
}
extern "C" __attribute__((unused)) char const *ContextErrorMessage(es2panda_Context *context)
{
auto *s = reinterpret_cast<Context *>(context);
return s->errorMessage.c_str();
}
extern "C" __attribute__((unused)) es2panda_Program *ContextProgram(es2panda_Context *context)
{
auto *ctx = reinterpret_cast<Context *>(context);
return reinterpret_cast<es2panda_Program *>(ctx->parserProgram);
}
using ExternalSourceEntry = std::pair<char *, parser::Program *>;
extern "C" __attribute__((unused)) es2panda_ExternalSource **ProgramExternalSources(es2panda_Context *context,
es2panda_Program *program,
size_t *lenP)
{
auto *ctx = reinterpret_cast<Context *>(context);
auto *allocator = ctx->allocator;
auto *vec = allocator->New<ArenaVector<ExternalSourceEntry *>>(allocator->Adapter());
auto programE2p = reinterpret_cast<parser::Program *>(program);
programE2p->GetExternalDecls()->Visit([vec, allocator](auto *extProgram) {
auto key = StringViewToCString(allocator, extProgram->GetImportInfo().ModuleName());
vec->emplace_back(allocator->New<ExternalSourceEntry>(key, extProgram));
});
*lenP = vec->size();
return reinterpret_cast<es2panda_ExternalSource **>(vec->data());
}
extern "C" __attribute__((unused)) es2panda_ExternalSource **ProgramDirectExternalSources(es2panda_Context *context,
es2panda_Program *program,
size_t *lenP)
{
auto *ctx = reinterpret_cast<Context *>(context);
auto *allocator = ctx->allocator;
auto *vec = allocator->New<ArenaVector<ExternalSourceEntry *>>(allocator->Adapter());
auto programE2p = reinterpret_cast<parser::Program *>(program);
for (auto &[name, extProgram] : programE2p->GetExternalDecls()->Direct()) {
auto key = StringViewToCString(allocator, name);
vec->emplace_back(allocator->New<ExternalSourceEntry>(key, extProgram));
}
*lenP = vec->size();
return reinterpret_cast<es2panda_ExternalSource **>(vec->data());
}
extern "C" __attribute__((unused)) char const *ExternalSourceName(es2panda_ExternalSource *eSource)
{
auto *entry = reinterpret_cast<ExternalSourceEntry *>(eSource);
return entry->first;
}
extern "C" __attribute__((unused)) es2panda_Program **ExternalSourcePrograms(es2panda_ExternalSource *eSource,
size_t *lenP)
{
auto *entry = reinterpret_cast<ExternalSourceEntry *>(eSource);
*lenP = 1;
return reinterpret_cast<es2panda_Program **>(&entry->second);
}
extern "C" void AstNodeForEach(es2panda_AstNode *ast, void (*func)(es2panda_AstNode *, void *), void *arg)
{
auto *node = reinterpret_cast<ir::AstNode *>(ast);
func(ast, arg);
node->IterateRecursively([=](ir::AstNode *child) { func(reinterpret_cast<es2panda_AstNode *>(child), arg); });
}
#define SET_NUMBER_LITERAL_IMPL(name, type) \
extern "C" bool NumberLiteralSet##name(es2panda_AstNode *node, type new_value) \
{ \
auto &n = reinterpret_cast<ir::NumberLiteral *>(node)->Number(); \
if (!n.Is##name()) { \
\
return false; \
} \
n.SetValue<type>(std::move(new_value)); \
\
return true; \
}
SET_NUMBER_LITERAL_IMPL(Int, int32_t)
SET_NUMBER_LITERAL_IMPL(Long, int64_t)
SET_NUMBER_LITERAL_IMPL(Double, double)
SET_NUMBER_LITERAL_IMPL(Float, float)
#undef SET_NUMBER_LITERAL_IMPL
template <typename T>
es2panda_AstNode *CreateNumberLiteral(es2panda_Context *ctx, T value)
{
auto number = ark::es2panda::lexer::Number(value);
auto allocator = reinterpret_cast<Context *>(ctx)->allocator;
auto node = allocator->New<ir::NumberLiteral>(number);
return reinterpret_cast<es2panda_AstNode *>(node);
}
template <typename T>
es2panda_AstNode *UpdateNumberLiteral(es2panda_Context *ctx, es2panda_AstNode *original, T value)
{
auto number = ark::es2panda::lexer::Number(value);
auto allocator = reinterpret_cast<Context *>(ctx)->allocator;
auto node = allocator->New<ir::NumberLiteral>(number);
auto *e2pOriginal = reinterpret_cast<ir::AstNode *>(original);
node->SetOriginalNode(e2pOriginal);
node->SetParent(e2pOriginal->Parent());
node->SetRange(e2pOriginal->Range());
return reinterpret_cast<es2panda_AstNode *>(node);
}
extern "C" const char *NumberLiteralStrConst(es2panda_Context *context, es2panda_AstNode *classInstance)
{
auto str = reinterpret_cast<const ir::NumberLiteral *>(classInstance)->Str();
return StringViewToCString(reinterpret_cast<Context *>(context)->allocator, str);
}
extern "C" void *AllocMemory(es2panda_Context *context, size_t numberOfElements, size_t sizeOfElement)
{
auto *allocator = reinterpret_cast<Context *>(context)->allocator;
void *ptr = allocator->Alloc(numberOfElements * sizeOfElement);
return ptr;
}
extern "C" es2panda_SourcePosition *CreateSourcePosition(es2panda_Context *context, size_t index, size_t line)
{
auto *allocator = reinterpret_cast<Context *>(context)->allocator;
return reinterpret_cast<es2panda_SourcePosition *>(
allocator->New<lexer::SourcePosition>(index, line, reinterpret_cast<Context *>(context)->parserProgram));
}
extern "C" es2panda_SourceRange *CreateSourceRange(es2panda_Context *context, es2panda_SourcePosition *start,
es2panda_SourcePosition *end)
{
auto *allocator = reinterpret_cast<Context *>(context)->allocator;
auto startE2p = *(reinterpret_cast<lexer::SourcePosition *>(start));
auto endE2p = *(reinterpret_cast<lexer::SourcePosition *>(end));
return reinterpret_cast<es2panda_SourceRange *>(allocator->New<lexer::SourceRange>(startE2p, endE2p));
}
extern "C" const es2panda_DiagnosticKind *CreateDiagnosticKind(es2panda_Context *context, const char *dmessage,
es2panda_PluginDiagnosticType etype)
{
auto ctx = reinterpret_cast<Context *>(context);
auto id = ctx->config->diagnosticKindStorage.size() + 1;
auto type = util::DiagnosticType::SUGGESTION;
if (etype == ES2PANDA_PLUGIN_WARNING) {
type = util::DiagnosticType::PLUGIN_WARNING;
} else if (etype == ES2PANDA_PLUGIN_ERROR) {
type = util::DiagnosticType::PLUGIN_ERROR;
}
ctx->config->diagnosticKindStorage.emplace_back(type, id, dmessage, false);
return reinterpret_cast<const es2panda_DiagnosticKind *>(&ctx->config->diagnosticKindStorage.back());
}
extern "C" es2panda_DiagnosticInfo *CreateDiagnosticInfo(es2panda_Context *context, const es2panda_DiagnosticKind *kind,
const char **args, size_t argc, es2panda_SourcePosition *pos)
{
auto *allocator = reinterpret_cast<Context *>(context)->allocator;
auto diagnosticInfo = allocator->New<es2panda_DiagnosticInfo>();
diagnosticInfo->kind = kind;
diagnosticInfo->args = args;
diagnosticInfo->argc = argc;
diagnosticInfo->pos = pos;
return diagnosticInfo;
}
extern "C" void AstNodeSetNoDebugLineFlag([[maybe_unused]] es2panda_Context *context, es2panda_AstNode *node)
{
auto *e2pNode = reinterpret_cast<ir::AstNode *>(node);
ES2PANDA_ASSERT(e2pNode != nullptr);
e2pNode->AddAstNodeFlags(ir::AstNodeFlags::NO_DEBUG_LINE_INFO);
}
extern "C" es2panda_SuggestionInfo *CreateSuggestionInfo(es2panda_Context *context, const es2panda_DiagnosticKind *kind,
const char **args, size_t argc, const char *substitutionCode,
const char *title, es2panda_SourceRange *range)
{
auto *allocator = reinterpret_cast<Context *>(context)->allocator;
auto suggestionInfo = allocator->New<es2panda_SuggestionInfo>();
suggestionInfo->kind = kind;
suggestionInfo->args = args;
suggestionInfo->argc = argc;
suggestionInfo->substitutionCode = substitutionCode;
suggestionInfo->title = title;
suggestionInfo->range = range;
return suggestionInfo;
}
extern "C" void LogDiagnosticWithSuggestions(es2panda_Context *context, const es2panda_DiagnosticInfo *diagnosticInfo,
es2panda_SuggestionInfo **suggestionInfos, size_t suggestionCount)
{
auto *ctx = reinterpret_cast<Context *>(context);
auto *diagnosticKind = reinterpret_cast<const diagnostic::DiagnosticKind *>(diagnosticInfo->kind);
util::DiagnosticMessageParams diagnosticParams;
diagnosticParams.reserve(diagnosticInfo->argc);
for (size_t i = 0; i < diagnosticInfo->argc; ++i) {
diagnosticParams.push_back(diagnosticInfo->args[i]);
}
auto *pos = reinterpret_cast<lexer::SourcePosition *>(diagnosticInfo->pos);
std::vector<util::Suggestion *> suggestions;
suggestions.reserve(suggestionCount);
for (size_t i = 0; i < suggestionCount; ++i) {
const es2panda_SuggestionInfo *info = suggestionInfos[i];
if (info == nullptr) {
continue;
}
auto *suggestionKind = reinterpret_cast<const diagnostic::DiagnosticKind *>(info->kind);
std::vector<std::string> suggestionParams;
suggestionParams.reserve(info->argc);
for (size_t j = 0; j < info->argc; ++j) {
suggestionParams.push_back(info->args[j]);
}
auto *range = reinterpret_cast<lexer::SourceRange *>(info->range);
auto suggestion = ctx->diagnosticEngine->CreateSuggestion(suggestionKind, suggestionParams,
info->substitutionCode, info->title, range);
suggestions.push_back(std::move(suggestion));
}
ctx->diagnosticEngine->LogDiagnostic(*diagnosticKind, diagnosticParams, *pos, std::move(suggestions));
}
extern "C" void LogDiagnosticWithSuggestion(es2panda_Context *context, const es2panda_DiagnosticInfo *diagnosticInfo,
const es2panda_SuggestionInfo *suggestionInfo)
{
auto ctx = reinterpret_cast<Context *>(context);
auto diagnostickind = reinterpret_cast<const diagnostic::DiagnosticKind *>(diagnosticInfo->kind);
auto suggestionkind = reinterpret_cast<const diagnostic::DiagnosticKind *>(suggestionInfo->kind);
util::DiagnosticMessageParams diagnosticParams;
for (size_t i = 0; i < diagnosticInfo->argc; ++i) {
diagnosticParams.push_back(diagnosticInfo->args[i]);
}
std::vector<std::string> suggestionParams;
for (size_t i = 0; i < suggestionInfo->argc; ++i) {
suggestionParams.push_back(suggestionInfo->args[i]);
}
auto E2pRange = reinterpret_cast<lexer::SourceRange *>(suggestionInfo->range);
auto posE2p = reinterpret_cast<lexer::SourcePosition *>(diagnosticInfo->pos);
auto suggestion = ctx->diagnosticEngine->CreateSuggestion(
suggestionkind, suggestionParams, suggestionInfo->substitutionCode, suggestionInfo->title, E2pRange);
auto *diag = ctx->diagnosticEngine->LogDiagnostic(*diagnostickind, diagnosticParams, *posE2p, suggestion);
diag->EnsureLocation();
}
extern "C" void LogDiagnostic(es2panda_Context *context, const es2panda_DiagnosticKind *ekind, const char **args,
size_t argc, es2panda_SourcePosition *pos)
{
auto ctx = reinterpret_cast<Context *>(context);
auto kind = reinterpret_cast<const diagnostic::DiagnosticKind *>(ekind);
util::DiagnosticMessageParams params;
for (size_t i = 0; i < argc; ++i) {
params.push_back(args[i]);
}
auto posE2p = reinterpret_cast<lexer::SourcePosition *>(pos);
auto *diag = ctx->diagnosticEngine->LogDiagnostic(*kind, params, *posE2p);
diag->EnsureLocation();
}
const es2panda_DiagnosticStorage *GetDiagnostics(es2panda_Context *context, size_t etype)
{
auto ctx = reinterpret_cast<Context *>(context);
auto type = static_cast<util::DiagnosticType>(etype);
return reinterpret_cast<const es2panda_DiagnosticStorage *>(&ctx->diagnosticEngine->GetDiagnosticStorage(type));
}
extern "C" const es2panda_DiagnosticStorage *GetSemanticErrors(es2panda_Context *context)
{
return GetDiagnostics(context, util::DiagnosticType::SEMANTIC);
}
extern "C" const es2panda_DiagnosticStorage *GetSyntaxErrors(es2panda_Context *context)
{
return GetDiagnostics(context, util::DiagnosticType::SYNTAX);
}
extern "C" const es2panda_DiagnosticStorage *GetPluginErrors(es2panda_Context *context)
{
return GetDiagnostics(context, util::DiagnosticType::PLUGIN_ERROR);
}
extern "C" const es2panda_DiagnosticStorage *GetPluginWarnings(es2panda_Context *context)
{
return GetDiagnostics(context, util::DiagnosticType::PLUGIN_WARNING);
}
extern "C" const es2panda_DiagnosticStorage *GetWarnings(es2panda_Context *context)
{
return GetDiagnostics(context, util::DiagnosticType::WARNING);
}
extern "C" bool IsAnyError(es2panda_Context *context)
{
return reinterpret_cast<Context *>(context)->diagnosticEngine->IsAnyError();
}
extern "C" size_t SourcePositionCol([[maybe_unused]] es2panda_Context *context, es2panda_SourcePosition *position)
{
static const size_t EMPTY = 1;
auto es2pandaPosition = reinterpret_cast<lexer::SourcePosition *>(position);
if (es2pandaPosition->Program() == nullptr) {
return EMPTY;
}
return es2pandaPosition->ToLocation().col;
}
extern "C" size_t SourcePositionIndex([[maybe_unused]] es2panda_Context *context, es2panda_SourcePosition *position)
{
return reinterpret_cast<lexer::SourcePosition *>(position)->index;
}
extern "C" size_t SourcePositionLine([[maybe_unused]] es2panda_Context *context, es2panda_SourcePosition *position)
{
return reinterpret_cast<lexer::SourcePosition *>(position)->line;
}
extern "C" es2panda_SourcePosition *SourceRangeStart([[maybe_unused]] es2panda_Context *context,
es2panda_SourceRange *range)
{
auto *allocator = reinterpret_cast<Context *>(context)->allocator;
auto E2pRange = reinterpret_cast<lexer::SourceRange *>(range);
return reinterpret_cast<es2panda_SourcePosition *>(allocator->New<lexer::SourcePosition>(E2pRange->start));
}
extern "C" es2panda_SourcePosition *SourceRangeEnd([[maybe_unused]] es2panda_Context *context,
es2panda_SourceRange *range)
{
auto *allocator = reinterpret_cast<Context *>(context)->allocator;
auto E2pRange = reinterpret_cast<lexer::SourceRange *>(range);
return reinterpret_cast<es2panda_SourcePosition *>(allocator->New<lexer::SourcePosition>(E2pRange->end));
}
extern "C" es2panda_Scope *AstNodeFindNearestScope([[maybe_unused]] es2panda_Context *ctx, es2panda_AstNode *node)
{
auto E2pNode = reinterpret_cast<ir::AstNode *>(node);
return reinterpret_cast<es2panda_Scope *>(compiler::NearestScope(E2pNode));
}
extern "C" es2panda_Scope *AstNodeRebind(es2panda_Context *ctx, es2panda_AstNode *node)
{
auto E2pNode = reinterpret_cast<ir::AstNode *>(node);
auto context = reinterpret_cast<Context *>(ctx);
auto varbinder = context->parserProgram->VarBinder()->AsETSBinder();
auto phaseManager = context->phaseManager;
if (E2pNode->IsScriptFunction() ||
E2pNode->FindChild([](ir::AstNode *n) { return n->IsScriptFunction(); }) != nullptr) {
while (!E2pNode->IsProgram()) {
E2pNode = E2pNode->Parent();
}
}
return reinterpret_cast<es2panda_Scope *>(compiler::Rebind(phaseManager, varbinder, E2pNode));
}
extern "C" void AstNodeRecheck(es2panda_Context *ctx, es2panda_AstNode *node)
{
ES2PANDA_PERF_SCOPE("@Recheck");
auto E2pNode = reinterpret_cast<ir::AstNode *>(node);
auto context = reinterpret_cast<Context *>(ctx);
auto varbinder = context->parserProgram->VarBinder()->AsETSBinder();
auto checker = context->GetChecker()->AsETSChecker();
auto phaseManager = context->phaseManager;
if (E2pNode->IsScriptFunction() ||
E2pNode->FindChild([](ir::AstNode *n) { return n->IsScriptFunction(); }) != nullptr) {
while (!E2pNode->IsProgram()) {
E2pNode = E2pNode->Parent();
}
}
compiler::Recheck(phaseManager, varbinder, checker, E2pNode);
context->state = !context->diagnosticEngine->IsAnyError() ? ES2PANDA_STATE_CHECKED : ES2PANDA_STATE_ERROR;
return;
}
#include "generated/es2panda_lib/es2panda_lib_impl.inc"
extern "C" Es2pandaEnum Es2pandaEnumFromString([[maybe_unused]] es2panda_Context *ctx, const char *str)
{
return IrToE2pEnum(es2panda::util::gen::ast_verifier::FromString(str));
}
extern "C" char *Es2pandaEnumToString(es2panda_Context *ctx, Es2pandaEnum id)
{
auto *allocator = reinterpret_cast<Context *>(ctx)->allocator;
return StringViewToCString(allocator, es2panda::util::gen::ast_verifier::ToString(E2pToIrEnum(id)));
}
extern "C" es2panda_AstNode *DeclarationFromIdentifier([[maybe_unused]] es2panda_Context *ctx, es2panda_AstNode *node)
{
auto E2pNode = reinterpret_cast<ir::Identifier *>(node);
return reinterpret_cast<es2panda_AstNode *>(compiler::DeclarationFromIdentifier(E2pNode));
}
extern "C" bool IsImportTypeKind([[maybe_unused]] es2panda_Context *context, es2panda_AstNode *node)
{
auto E2pNode = reinterpret_cast<const ir::AstNode *>(node);
if (E2pNode->IsETSImportDeclaration()) {
return E2pNode->AsETSImportDeclaration()->IsTypeKind();
}
if (E2pNode->IsImportDeclaration()) {
return E2pNode->AsETSImportDeclaration()->IsTypeKind();
}
auto ctx = reinterpret_cast<Context *>(context);
auto id = ctx->config->diagnosticKindStorage.size() + 1;
auto type = util::DiagnosticType::PLUGIN_WARNING;
util::DiagnosticMessageParams params {};
diagnostic::DiagnosticKind *kind =
&ctx->config->diagnosticKindStorage.emplace_back(type, id, "Insert wrong node!", false);
ctx->diagnosticEngine->LogDiagnostic(*kind, params, E2pNode->Start());
return false;
}
extern "C" char *GetLicenseFromRootNode(es2panda_Context *ctx, es2panda_AstNode *node)
{
auto E2pNode = reinterpret_cast<const ir::AstNode *>(node);
auto *allocator = reinterpret_cast<Context *>(ctx)->allocator;
return StringViewToCString(allocator, compiler::GetLicenseFromRootNode(E2pNode));
}
extern "C" char *JsdocStringFromDeclaration([[maybe_unused]] es2panda_Context *ctx, es2panda_AstNode *node)
{
auto E2pNode = reinterpret_cast<const ir::AstNode *>(node);
auto *allocator = reinterpret_cast<Context *>(ctx)->allocator;
return StringViewToCString(allocator, compiler::JsdocStringFromDeclaration(E2pNode));
}
extern "C" es2panda_AstNode *FirstDeclarationByNameFromNode([[maybe_unused]] es2panda_Context *ctx,
const es2panda_AstNode *node, const char *name)
{
if (node == nullptr) {
return nullptr;
}
util::StringView nameE2p {name};
ir::AstNode *res = reinterpret_cast<const ir::AstNode *>(node)->FindChild([&nameE2p](const ir::AstNode *ast) {
if (ast != nullptr && ast->IsMethodDefinition() && ast->AsMethodDefinition()->Key() != nullptr &&
ast->AsMethodDefinition()->Key()->IsIdentifier() &&
ast->AsMethodDefinition()->Key()->AsIdentifier()->Name() == nameE2p) {
return true;
}
return false;
});
return reinterpret_cast<es2panda_AstNode *>(res);
}
extern "C" es2panda_AstNode *FirstDeclarationByNameFromProgram([[maybe_unused]] es2panda_Context *ctx,
const es2panda_Program *program, const char *name)
{
if (program == nullptr) {
return nullptr;
}
auto programE2p = reinterpret_cast<const parser::Program *>(program);
es2panda_AstNode *res =
FirstDeclarationByNameFromNode(ctx, reinterpret_cast<const es2panda_AstNode *>(programE2p->Ast()), name);
if (res != nullptr) {
return res;
}
for (const auto &ext_source : programE2p->GetExternalDecls()->Direct()) {
const auto *ext_program = ext_source.second;
if (ext_program != nullptr) {
ES2PANDA_ASSERT(!ext_program->Is<util::ModuleKind::PACKAGE>());
res = FirstDeclarationByNameFromNode(ctx, reinterpret_cast<const es2panda_AstNode *>(ext_program->Ast()),
name);
}
if (res != nullptr) {
return res;
}
}
return nullptr;
}
static ArenaSet<ir::AstNode *> AllDeclarationsByNameFromNodeHelper(ArenaAllocator *const allocator,
const ir::AstNode *node,
const util::StringView &name)
{
auto result = ArenaSet<ir::AstNode *> {allocator->Adapter()};
if (node == nullptr) {
return result;
}
node->IterateRecursively([&result, &name](ir::AstNode *ast) {
if (ast != nullptr && ast->IsMethodDefinition() && ast->AsMethodDefinition()->Key() != nullptr &&
ast->AsMethodDefinition()->Key()->IsIdentifier() &&
ast->AsMethodDefinition()->Key()->AsIdentifier()->Name() == name) {
result.insert(ast);
}
});
return result;
}
extern "C" es2panda_AstNode **AllDeclarationsByNameFromNode([[maybe_unused]] es2panda_Context *ctx,
const es2panda_AstNode *node, const char *name,
size_t *declsLen)
{
util::StringView nameE2p {name};
auto nodeE2p = reinterpret_cast<const ir::AstNode *>(node);
auto allocator = reinterpret_cast<Context *>(ctx)->allocator;
auto result = AllDeclarationsByNameFromNodeHelper(allocator, nodeE2p, nameE2p);
*declsLen = result.size();
auto apiRes = allocator->New<es2panda_AstNode *[]>(*declsLen);
size_t i = 0;
for (auto elem : result) {
auto toPush = reinterpret_cast<es2panda_AstNode *>(elem);
apiRes[i++] = toPush;
};
return apiRes;
}
extern "C" es2panda_AstNode **AllDeclarationsByNameFromProgram([[maybe_unused]] es2panda_Context *ctx,
const es2panda_Program *program, const char *name,
size_t *declsLen)
{
auto allocator = reinterpret_cast<Context *>(ctx)->allocator;
if (program == nullptr) {
*declsLen = 0;
return allocator->New<es2panda_AstNode *[]>(0);
}
util::StringView nameE2p {name};
auto programE2p = reinterpret_cast<const parser::Program *>(program);
auto result = ArenaSet<ir::AstNode *> {allocator->Adapter()};
ArenaSet<ir::AstNode *> res = AllDeclarationsByNameFromNodeHelper(allocator, programE2p->Ast(), nameE2p);
result.insert(res.begin(), res.end());
for (auto &[_, ext_program] : programE2p->GetExternalDecls()->Direct()) {
if (ext_program != nullptr) {
res = AllDeclarationsByNameFromNodeHelper(allocator, ext_program->Ast(), nameE2p);
result.insert(res.begin(), res.end());
}
}
*declsLen = result.size();
auto apiRes = allocator->New<es2panda_AstNode *[]>(*declsLen);
size_t i = 0;
for (auto &elem : result) {
auto toPush = reinterpret_cast<es2panda_AstNode *>(elem);
apiRes[i++] = toPush;
};
return apiRes;
}
extern "C" __attribute__((unused)) es2panda_TsDeclgen *CreateTsDeclgen(
es2panda_Context *ctx, size_t fileNamesCount, const char *const *inputFiles, const char *const *outputDeclEts,
const char *const *outputEts, bool exportAll, bool isolated, const char *recordFile, bool genAnnotations)
{
auto *ctxImpl = reinterpret_cast<Context *>(ctx);
if (fileNamesCount == 0) {
return nullptr;
}
if (!ctxImpl->config->options->IsSimultaneous()) {
ES2PANDA_ASSERT(fileNamesCount == 1);
}
ark::es2panda::declgen_ets2ts::DeclgenOptions declgenOptions;
declgenOptions.exportAll = exportAll;
declgenOptions.isolated = isolated;
declgenOptions.genAnnotations = genAnnotations;
declgenOptions.recordFile = recordFile ? recordFile : "";
declgenOptions.outputDeclEts = outputDeclEts[0] ? outputDeclEts[0] : "";
declgenOptions.outputEts = outputEts[0] ? outputEts[0] : "";
std::unordered_map<std::string, size_t> inputFileIndexMap;
for (size_t i = 0; i < fileNamesCount; ++i) {
inputFileIndexMap.emplace(std::string(inputFiles[i]), i);
}
auto *declgen = new ark::es2panda::declgen_ets2ts::TSDeclGenerator(ctxImpl, inputFileIndexMap, outputDeclEts,
outputEts, fileNamesCount);
if (!declgen->SetDeclgenOptions(declgenOptions)) {
delete declgen;
return nullptr;
}
return reinterpret_cast<es2panda_TsDeclgen *>(declgen);
}
extern "C" __attribute__((unused)) void DestroyTsDeclgen(es2panda_TsDeclgen *declgen)
{
auto *declgenImpl = reinterpret_cast<ark::es2panda::declgen_ets2ts::TSDeclGenerator *>(declgen);
delete declgenImpl;
}
extern "C" __attribute__((unused)) int GenerateTsDeclarationsAfterParsed(es2panda_TsDeclgen *declgen)
{
auto *declgenImpl = reinterpret_cast<ark::es2panda::declgen_ets2ts::TSDeclGenerator *>(declgen);
if (declgenImpl->GenerateTsDeclarationsAfterParsedPhase()) {
return 0;
}
return 1;
}
extern "C" __attribute__((unused)) int GenerateTsDeclarationsAfterCheck(es2panda_TsDeclgen *declgen)
{
auto *declgenImpl = reinterpret_cast<ark::es2panda::declgen_ets2ts::TSDeclGenerator *>(declgen);
if (declgenImpl->GenerateTsDeclarationsAfterCheckPhase()) {
return 0;
}
return 1;
}
extern "C" __attribute__((unused)) int WriteTsDeclarations(es2panda_TsDeclgen *declgen)
{
auto *declgenImpl = reinterpret_cast<ark::es2panda::declgen_ets2ts::TSDeclGenerator *>(declgen);
if (declgenImpl->Write()) {
return 0;
}
return 1;
}
inline static parser::Program *FindProgramInContextByPath(Context *ctxImpl, const std::string &inputPathStr)
{
if (!ctxImpl->config->options->IsSimultaneous()) {
return ctxImpl->parserProgram;
}
for (const auto &[_, prog] : ctxImpl->parserProgram->GetExternalDecls()->Direct()) {
if (prog && prog->IsBuiltSimultaneously() && prog->AbsoluteName().Mutf8() == inputPathStr) {
return prog;
}
}
return nullptr;
}
extern "C" __attribute__((unused)) char *FormOutputPathForFile(es2panda_Context *ctx, const char *inputPath)
{
auto *ctxImpl = reinterpret_cast<Context *>(ctx);
if (ctxImpl->state != ES2PANDA_STATE_PARSED) {
return StdStringToCString(ctxImpl->Allocator(), "");
}
auto *prog = FindProgramInContextByPath(ctxImpl, inputPath);
if (prog == nullptr) {
return StdStringToCString(ctxImpl->Allocator(), "");
}
return StdStringToCString(ctxImpl->Allocator(),
ctxImpl->parser->GetImportPathManager()->FormAbcFilePath(prog->GetImportInfo()));
}
static bool HandleMultiFileModeTemplate(
Context *ctxImpl, const std::string &outputPath,
const std::function<std::pair<bool, const std::string>(const std::string &)> &findPath,
const std::function<bool(const std::string &, const std::string &)> &compare)
{
std::pair<bool, const std::string> pair = findPath(outputPath);
if (!pair.first) {
return false;
}
for (auto [_, prog] : ctxImpl->parserProgram->GetExternalDecls()->Direct()) {
if (prog == nullptr || !prog->IsBuiltSimultaneously()) {
continue;
}
std::string inputRelativeDir = ark::os::RemoveExtension(prog->AbsoluteName().Mutf8());
if (compare(inputRelativeDir, pair.second)) {
compiler::HandleGenerateDecl(ctxImpl, prog, outputPath);
return !ctxImpl->diagnosticEngine->IsAnyError();
}
}
return false;
}
__attribute__((unused)) static bool HandleMultiFileMode(Context *ctxImpl, const std::string &outputPath)
{
if (util::StringView(outputPath).EndsWith(ETSCACHE_SUFFIX)) {
auto findPathFunc = [](const std::string &path) -> std::pair<bool, const std::string> {
return std::pair<bool, const std::string>(true, ark::os::RemoveExtension(path));
};
auto compareFunc = [](const std::string &path1, const std::string &path2) -> bool {
return util::StringView(path1).EndsWith(path2);
};
return HandleMultiFileModeTemplate(ctxImpl, outputPath, findPathFunc, compareFunc);
}
if (util::StringView(outputPath).EndsWith(DECL_ETS_SUFFIX)) {
auto findPathFunc = [ctxImpl](const std::string &path) -> std::pair<bool, const std::string> {
* path = declgenV2OutPath + /direct1/direct2/file.d.ets
* relativePath = /direct1/direct2/file.d.ets
* relativePath = package/direct1/direct2/file.d.ets
* relativePath = package/direct1/direct2/file
*/
const util::Options &options = *ctxImpl->config->options;
const auto &arktsConfig = options.ArkTSConfig();
const std::string &declgenV2OutPath = arktsConfig.DeclgenV2OutPath();
if (!util::StringView(path).StartsWith(declgenV2OutPath)) {
return std::pair<bool, const std::string>(false, path);
}
std::string relativePath = path.substr(declgenV2OutPath.length());
relativePath = arktsConfig.Package() + relativePath;
relativePath = ark::os::RemoveExtension(relativePath);
relativePath = ark::os::RemoveExtension(relativePath);
return std::pair<bool, const std::string>(true, relativePath);
};
auto compareFunc = [](const std::string &path1, const std::string &path2) -> bool {
return util::StringView(path1).EndsWith(path2);
};
return HandleMultiFileModeTemplate(ctxImpl, outputPath, findPathFunc, compareFunc);
}
return false;
}
extern "C" __attribute__((unused)) int GenerateStaticDeclarationsFromContext(es2panda_Context *ctx,
const char *outputPath)
{
auto *ctxImpl = reinterpret_cast<Context *>(ctx);
if (ctxImpl->state != ES2PANDA_STATE_CHECKED) {
return 1;
}
if (ctxImpl->config->options->IsSimultaneous()) {
bool success = HandleMultiFileMode(ctxImpl, outputPath);
return success ? 0 : 1;
}
compiler::HandleGenerateDecl(ctxImpl, ctxImpl->parserProgram, outputPath);
return ctxImpl->diagnosticEngine->IsAnyError() ? 1 : 0;
}
extern "C" void InsertETSImportDeclarationAndParse(es2panda_Context *context, es2panda_Program *program,
es2panda_AstNode *importDeclaration)
{
auto *ctx = reinterpret_cast<Context *>(context);
auto *parserProgram = reinterpret_cast<parser::Program *>(program);
auto *importDeclE2p = reinterpret_cast<ir::ETSImportDeclaration *>(importDeclaration);
importDeclE2p->AddAstNodeFlags(ir::AstNodeFlags::NOCLEANUP);
auto &stmt = parserProgram->Ast()->StatementsForUpdates();
bool hasUseStatic = !stmt.empty() && stmt.front()->IsExpressionStatement();
if (hasUseStatic) {
auto *expansion = stmt.front()->AsExpressionStatement()->GetExpression();
hasUseStatic = hasUseStatic && expansion->IsStringLiteral() &&
expansion->AsStringLiteral()->Str() == compiler::Signatures::STATIC_PROGRAM_FLAG;
}
size_t insertIndex = hasUseStatic ? 1 : 0;
stmt.insert(stmt.begin() + insertIndex, importDeclE2p);
importDeclE2p->SetParent(parserProgram->Ast());
ctx->parser->AsETSParser()->ParseSources();
for ([[maybe_unused]] auto *specific : importDeclE2p->Specifiers()) {
ES2PANDA_ASSERT(specific->Parent() != nullptr);
}
}
__attribute__((unused)) static void GenerateStdLibCache(es2panda_Config *config, GlobalContext *globalContext,
bool LspUsage)
{
auto cfg = reinterpret_cast<ConfigImpl *>(config);
globalContext->stdLibAllocator = EHeap::NewAllocator().release();
auto ctx = CreateContext(config, std::move(""), cfg->options->SourceFileName().c_str(),
reinterpret_cast<es2panda_GlobalContext *>(globalContext), true, true, LspUsage);
ProceedToState(ctx, es2panda_ContextState::ES2PANDA_STATE_CHECKED);
if (!LspUsage) {
AstNodeRecheck(ctx,
reinterpret_cast<es2panda_AstNode *>(reinterpret_cast<Context *>(ctx)->parserProgram->Ast()));
AstNodeRecheck(ctx,
reinterpret_cast<es2panda_AstNode *>(reinterpret_cast<Context *>(ctx)->parserProgram->Ast()));
ProceedToState(ctx, es2panda_ContextState::ES2PANDA_STATE_LOWERED);
}
globalContext->stdLibAstCache = reinterpret_cast<Context *>(ctx)->parserProgram->GetExternalDecls();
DestroyContext(ctx);
}
extern "C" int ExtractDeclarationsFromAbcFile(const char *abcFile, const char *cacheDir)
{
const auto pf = panda_file::OpenPandaFile(abcFile);
if (pf == nullptr) {
return 1;
}
util::ImportPathManager::ExtractEtscacheToFile(*pf, abcFile, cacheDir);
return 0;
}
es2panda_Impl g_impl = {
ES2PANDA_LIB_VERSION,
MemInitialize,
MemFinalize,
CreateConfig,
DestroyConfig,
DestroyConfigWithoutLog,
GetAllErrorMessages,
ConfigGetOptions,
CreateContextFromFile,
CreateCacheContextFromFile,
CreateContextFromString,
CreateContextFromStringWithHistory,
CreateCacheContextFromString,
CreateContextGenerateAbcForExternalSourceFiles,
CreateContextSimultaneousMode,
CreateContextSimultaneousModeForLsp,
ProceedToState,
DestroyContext,
CreateGlobalContext,
DestroyGlobalContext,
ContextState,
ContextErrorMessage,
ContextProgram,
ProgramExternalSources,
ProgramDirectExternalSources,
ExternalSourceName,
ExternalSourcePrograms,
AstNodeForEach,
NumberLiteralSetInt,
NumberLiteralSetLong,
NumberLiteralSetDouble,
NumberLiteralSetFloat,
CreateNumberLiteral<int32_t>,
UpdateNumberLiteral<int32_t>,
CreateNumberLiteral<int64_t>,
UpdateNumberLiteral<int64_t>,
CreateNumberLiteral<double>,
UpdateNumberLiteral<double>,
CreateNumberLiteral<float>,
UpdateNumberLiteral<float>,
NumberLiteralStrConst,
AllocMemory,
CreateSourcePosition,
CreateSourceRange,
SourcePositionCol,
SourcePositionIndex,
SourcePositionLine,
SourceRangeStart,
SourceRangeEnd,
AstNodeSetNoDebugLineFlag,
CreateDiagnosticKind,
CreateDiagnosticInfo,
CreateSuggestionInfo,
LogDiagnosticWithSuggestions,
LogDiagnosticWithSuggestion,
LogDiagnostic,
GetSemanticErrors,
GetSyntaxErrors,
GetPluginErrors,
GetWarnings,
IsAnyError,
AstNodeFindNearestScope,
AstNodeRebind,
AstNodeRecheck,
Es2pandaEnumFromString,
Es2pandaEnumToString,
DeclarationFromIdentifier,
IsImportTypeKind,
JsdocStringFromDeclaration,
GetLicenseFromRootNode,
FirstDeclarationByNameFromNode,
FirstDeclarationByNameFromProgram,
AllDeclarationsByNameFromNode,
AllDeclarationsByNameFromProgram,
CreateTsDeclgen,
GenerateTsDeclarationsAfterParsed,
GenerateTsDeclarationsAfterCheck,
WriteTsDeclarations,
DestroyTsDeclgen,
FormOutputPathForFile,
InsertETSImportDeclarationAndParse,
GenerateStaticDeclarationsFromContext,
InvalidateFileCache,
RemoveFileCache,
AddFileCache,
IncrementalPrepareProgram,
FreeCompilerPartMemory,
ResetCounters,
ExtractDeclarationsFromAbcFile,
#include "generated/es2panda_lib/es2panda_lib_list.inc"
};
}
extern "C" es2panda_Impl const *es2panda_GetImpl(int version)
{
if (version != ES2PANDA_LIB_VERSION) {
return nullptr;
}
return &ark::es2panda::public_lib::g_impl;
}