* Copyright (c) 2021-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 "helpers.h"
#include <iomanip>
#include "checker/ETSchecker.h"
#include "checker/types/ets/etsTupleType.h"
#include "parser/program/program.h"
#include "util/ustring.h"
#include "varbinder/privateBinding.h"
#include "varbinder/ETSBinder.h"
#include "lexer/token/letters.h"
#include "ir/base/classDefinition.h"
#include "ir/base/scriptFunction.h"
#include "ir/base/classProperty.h"
#include "ir/base/property.h"
#include "ir/base/spreadElement.h"
#include "ir/base/methodDefinition.h"
#include "ir/expressions/identifier.h"
#include "ir/expressions/literals/numberLiteral.h"
#include "ir/expressions/literals/stringLiteral.h"
#include "ir/expressions/literals/booleanLiteral.h"
#include "ir/expressions/functionExpression.h"
#include "ir/expressions/objectExpression.h"
#include "ir/expressions/arrayExpression.h"
#include "ir/expressions/assignmentExpression.h"
#include "ir/statements/variableDeclarator.h"
#include "ir/module/importSpecifier.h"
#include "ir/module/importDefaultSpecifier.h"
#include "ir/ets/etsDestructuring.h"
#include "ir/ets/etsImportDeclaration.h"
#include "ir/ets/etsParameterExpression.h"
#include "ir/ts/tsParameterProperty.h"
#include "ir/ts/tsInterfaceDeclaration.h"
#include "ir/ts/tsEnumDeclaration.h"
#include "libarkbase/os/file.h"
#include "libarkbase/utils/utf.h"
#include "importPathManager.h"
namespace ark::es2panda::util {
bool Helpers::IsGlobalIdentifier(const util::StringView &str)
{
return (str.Is("NaN") || str.Is("undefined") || str.Is("Infinity"));
}
bool Helpers::ContainSpreadElement(const ArenaVector<ir::Expression *> &args)
{
return std::any_of(args.begin(), args.end(), [](const auto *it) { return it->IsSpreadElement(); });
}
util::StringView Helpers::LiteralToPropName(const ir::Expression *lit)
{
switch (lit->Type()) {
case ir::AstNodeType::IDENTIFIER: {
return lit->AsIdentifier()->Name();
}
case ir::AstNodeType::STRING_LITERAL: {
return lit->AsStringLiteral()->Str();
}
case ir::AstNodeType::NUMBER_LITERAL: {
return lit->AsNumberLiteral()->Str();
}
case ir::AstNodeType::NULL_LITERAL: {
return "null";
}
case ir::AstNodeType::UNDEFINED_LITERAL: {
return "undefined";
}
default: {
ES2PANDA_UNREACHABLE();
}
}
}
bool Helpers::IsIndex(double number)
{
if (number >= 0 && number < static_cast<double>(INVALID_INDEX)) {
auto intNum = static_cast<uint32_t>(number);
if (static_cast<double>(intNum) == number) {
return true;
}
}
return false;
}
static bool IsDigit(char c)
{
return (c >= '0' && c <= '9');
}
int64_t Helpers::GetIndex(const util::StringView &str)
{
const auto &s = str.Utf8();
if (s.empty() || (*s.begin() == '0' && s.length() > 1)) {
return INVALID_INDEX;
}
int64_t value = 0;
for (const auto c : s) {
if (!IsDigit(c)) {
return INVALID_INDEX;
}
constexpr auto MULTIPLIER = 10;
value *= MULTIPLIER;
value += (c - '0');
if (value >= INVALID_INDEX) {
return INVALID_INDEX;
}
}
return value;
}
std::string Helpers::ToString(double number)
{
std::string str;
if (Helpers::IsInteger<int32_t>(number)) {
str = std::to_string(static_cast<int32_t>(number));
} else {
str = std::to_string(number);
}
return str;
}
util::StringView Helpers::ToStringView(ArenaAllocator *allocator, double number)
{
util::UString str(ToString(number), allocator);
return str.View();
}
util::StringView Helpers::ToStringView(ArenaAllocator *allocator, uint32_t number)
{
ES2PANDA_ASSERT(number <= static_cast<uint32_t>(std::numeric_limits<int32_t>::max()));
return ToStringView(allocator, static_cast<int32_t>(number));
}
util::StringView Helpers::ToStringView(ArenaAllocator *allocator, int32_t number)
{
util::UString str(ToString(number), allocator);
return str.View();
}
util::StringView Helpers::ToStringView(SArenaAllocator *allocator, double number)
{
return util::StringView(std::string_view(*allocator->New<SArenaString>(ToString(number), allocator->Adapter())));
}
util::StringView Helpers::ToStringView(SArenaAllocator *allocator, uint32_t number)
{
ES2PANDA_ASSERT(number <= static_cast<uint32_t>(std::numeric_limits<int32_t>::max()));
return ToStringView(allocator, static_cast<int32_t>(number));
}
util::StringView Helpers::ToStringView(SArenaAllocator *allocator, int32_t number)
{
return util::StringView(std::string_view(*allocator->New<SArenaString>(ToString(number), allocator->Adapter())));
}
bool Helpers::StartsWith(const std::string_view str, const std::string_view prefix)
{
if (str.length() < prefix.length()) {
return false;
}
return str.compare(0, prefix.size(), prefix) == 0;
}
bool Helpers::EndsWith(const std::string_view str, const std::string_view suffix)
{
if (str.length() < suffix.length()) {
return false;
}
size_t expectPos = str.length() - suffix.length();
return str.find(suffix, expectPos) == expectPos;
}
const ir::ScriptFunction *Helpers::GetContainingConstructor(const ir::AstNode *node)
{
const ir::ScriptFunction *iter = GetContainingFunction(node);
while (iter != nullptr) {
if (iter->IsConstructor()) {
return iter;
}
if (!iter->IsArrow()) {
return nullptr;
}
iter = GetContainingFunction(iter);
}
return iter;
}
const ir::TSEnumDeclaration *Helpers::GetContainingEnumDeclaration(const ir::AstNode *node)
{
auto *iter = node;
while (iter != nullptr) {
if (iter->IsTSEnumDeclaration()) {
return iter->AsTSEnumDeclaration();
}
iter = iter->Parent();
}
return nullptr;
}
const checker::ETSObjectType *Helpers::GetContainingObjectType(const ir::AstNode *node)
{
const auto *iter = node;
while (iter != nullptr) {
if (iter->IsClassDefinition()) {
auto *ret = iter->AsClassDefinition()->TsType();
return ret != nullptr ? ret->AsETSObjectType() : nullptr;
}
if (iter->IsTSInterfaceDeclaration()) {
auto *ret = iter->AsTSInterfaceDeclaration()->TsType();
return ret != nullptr ? ret->AsETSObjectType() : nullptr;
}
if (iter->IsTSEnumDeclaration()) {
auto *ret = iter->AsTSEnumDeclaration()->TsType();
return ret != nullptr ? ret->AsETSObjectType() : nullptr;
}
iter = iter->Parent();
}
return nullptr;
}
const util::StringView Helpers::GetContainingObjectName(const ir::AstNode *node)
{
const auto *iter = node;
while (iter != nullptr) {
if (iter->IsClassDefinition()) {
return iter->AsClassDefinition()->Ident()->Name();
}
if (iter->IsTSInterfaceDeclaration()) {
return iter->AsTSInterfaceDeclaration()->Id()->Name();
}
iter = iter->Parent();
}
return nullptr;
}
const ir::ClassDefinition *Helpers::GetContainingClassDefinition(const ir::AstNode *node)
{
const auto *iter = node;
while (iter != nullptr) {
if (iter->IsClassDefinition()) {
return iter->AsClassDefinition();
}
iter = iter->Parent();
}
return nullptr;
}
const ir::TSInterfaceDeclaration *Helpers::GetContainingInterfaceDeclaration(const ir::AstNode *node)
{
const auto *iter = node;
while (iter != nullptr) {
if (iter->IsTSInterfaceDeclaration()) {
return iter->AsTSInterfaceDeclaration();
}
iter = iter->Parent();
}
return nullptr;
}
const ir::MethodDefinition *Helpers::GetContainingClassMethodDefinition(const ir::AstNode *node)
{
const auto *iter = node;
while (iter != nullptr) {
if (iter->IsMethodDefinition()) {
return iter->AsMethodDefinition();
}
if (iter->IsClassDefinition()) {
break;
}
iter = iter->Parent();
}
return nullptr;
}
const ir::ClassStaticBlock *Helpers::GetContainingClassStaticBlock(const ir::AstNode *node)
{
const auto *iter = node;
while (iter != nullptr) {
if (iter->IsClassStaticBlock()) {
return iter->AsClassStaticBlock();
}
if (iter->IsClassDefinition()) {
break;
}
iter = iter->Parent();
}
return nullptr;
}
const ir::ScriptFunction *Helpers::GetContainingConstructor(const ir::ClassProperty *node)
{
for (const auto *parent = node->Parent(); parent != nullptr; parent = parent->Parent()) {
if (parent->IsClassDefinition()) {
ES2PANDA_ASSERT(parent->AsClassDefinition()->Ctor() != nullptr);
return parent->AsClassDefinition()->Ctor()->Function();
}
}
return nullptr;
}
const ir::ScriptFunction *Helpers::GetContainingFunction(const ir::AstNode *node)
{
for (const auto *parent = node->Parent(); parent != nullptr; parent = parent->Parent()) {
if (parent->IsScriptFunction()) {
return parent->AsScriptFunction();
}
}
return nullptr;
}
const ir::ClassDefinition *Helpers::GetClassDefinition(const ir::ScriptFunction *node)
{
ES2PANDA_ASSERT(node->IsConstructor());
ES2PANDA_ASSERT(node->Parent()->IsFunctionExpression());
ES2PANDA_ASSERT(node->Parent()->Parent()->IsMethodDefinition());
ES2PANDA_ASSERT(node->Parent()->Parent()->Parent()->IsClassDefinition());
return node->Parent()->Parent()->Parent()->AsClassDefinition();
}
bool Helpers::IsSpecialPropertyKey(const ir::Expression *expr)
{
if (!expr->IsStringLiteral()) {
return false;
}
auto *lit = expr->AsStringLiteral();
return lit->Str().Is("prototype") || lit->Str().Is("constructor");
}
bool Helpers::IsConstantPropertyKey(const ir::Expression *expr, bool isComputed)
{
switch (expr->Type()) {
case ir::AstNodeType::IDENTIFIER: {
return !isComputed;
}
case ir::AstNodeType::NUMBER_LITERAL:
case ir::AstNodeType::STRING_LITERAL:
case ir::AstNodeType::BOOLEAN_LITERAL:
case ir::AstNodeType::NULL_LITERAL: {
return true;
}
default:
break;
}
return false;
}
compiler::Literal Helpers::ToConstantLiteral(const ir::Expression *expr)
{
switch (expr->Type()) {
case ir::AstNodeType::NUMBER_LITERAL: {
auto *lit = expr->AsNumberLiteral();
if (util::Helpers::IsInteger<uint32_t>(lit->Number().GetDouble())) {
return compiler::Literal(static_cast<uint32_t>(lit->Number().GetDouble()));
}
return compiler::Literal(lit->Number().GetDouble());
}
case ir::AstNodeType::STRING_LITERAL: {
auto *lit = expr->AsStringLiteral();
return compiler::Literal(lit->Str());
}
case ir::AstNodeType::BOOLEAN_LITERAL: {
auto *lit = expr->AsBooleanLiteral();
return compiler::Literal(lit->Value());
}
case ir::AstNodeType::NULL_LITERAL: {
return compiler::Literal::NullLiteral();
}
case ir::AstNodeType::UNDEFINED_LITERAL: {
return compiler::Literal::UndefinedLiteral();
}
default:
break;
}
return compiler::Literal();
}
bool Helpers::IsErrorPlaceHolder(ir::Identifier const *ident, bool const isNull) noexcept
{
return !isNull ? ident != nullptr && ident->IsErrorPlaceHolder() : ident == nullptr || ident->IsErrorPlaceHolder();
}
bool Helpers::IsGlobalClass(ir::AstNode const *node) noexcept
{
return node != nullptr && node->IsClassDefinition() && node->AsClassDefinition()->IsGlobal();
}
bool Helpers::IsETSMethodType(checker::Type const *type) noexcept
{
return type != nullptr && type->IsETSMethodType();
}
bool Helpers::IsBindingPattern(const ir::AstNode *node)
{
return node->IsArrayPattern() || node->IsObjectPattern();
}
bool Helpers::IsPattern(const ir::AstNode *node)
{
return node->IsArrayPattern() || node->IsObjectPattern() || node->IsAssignmentPattern();
}
static void CollectBindingName(varbinder::VarBinder *vb, ir::AstNode *node, std::vector<ir::Identifier *> *bindings)
{
switch (node->Type()) {
case ir::AstNodeType::IDENTIFIER: {
if (!vb->IsGlobalIdentifier(node->AsIdentifier()->Name())) {
bindings->push_back(node->AsIdentifier());
}
break;
}
case ir::AstNodeType::OBJECT_PATTERN: {
for (auto *prop : node->AsObjectPattern()->Properties()) {
CollectBindingName(vb, prop, bindings);
}
break;
}
case ir::AstNodeType::ARRAY_PATTERN: {
for (auto *element : node->AsArrayPattern()->Elements()) {
CollectBindingName(vb, element, bindings);
}
break;
}
case ir::AstNodeType::ETS_DESTRUCTURING: {
for (auto *element : node->AsETSDestructuring()->Elements()) {
CollectBindingName(vb, element, bindings);
}
break;
}
case ir::AstNodeType::ASSIGNMENT_PATTERN: {
CollectBindingName(vb, node->AsAssignmentPattern()->Left(), bindings);
break;
}
case ir::AstNodeType::PROPERTY: {
CollectBindingName(vb, node->AsProperty()->Value(), bindings);
break;
}
case ir::AstNodeType::REST_ELEMENT: {
CollectBindingName(vb, node->AsRestElement()->Argument(), bindings);
break;
}
default:
break;
}
}
std::vector<ir::Identifier *> Helpers::CollectBindingNames(varbinder::VarBinder *vb, ir::Expression *node)
{
std::vector<ir::Identifier *> bindings;
CollectBindingName(vb, node, &bindings);
return bindings;
}
void Helpers::CheckImportedName(const ArenaVector<ir::ImportSpecifier *> &specifiers,
const ir::ImportSpecifier *specifier, DiagnosticEngine &diagnosticEngine)
{
auto newIdentName = specifier->Imported()->Name();
auto newAliasName = specifier->Local()->Name();
std::stringstream message {};
for (auto *it : specifiers) {
auto savedIdentName = it->Imported()->Name();
auto savedAliasName = it->Local()->Name();
if (savedIdentName == savedAliasName && savedAliasName == newIdentName) {
diagnosticEngine.LogDiagnostic(diagnostic::DUPLICATE_IMPORT, DiagnosticMessageParams {newIdentName},
specifier->Start());
break;
}
if (savedIdentName == newIdentName && newAliasName != savedAliasName) {
diagnosticEngine.LogDiagnostic(diagnostic::DUPLICATE_ALIAS, DiagnosticMessageParams {newIdentName},
specifier->Start());
break;
}
}
}
void Helpers::CheckDefaultImportedName(const ArenaVector<ir::ImportDefaultSpecifier *> &specifiers,
const ir::ImportDefaultSpecifier *specifier, const std::string &fileName)
{
for (auto *it : specifiers) {
if (specifier->Local()->Name() != it->Local()->Name()) {
std::cerr << "Warning: default element is explicitly used with alias several times [" << fileName.c_str()
<< ":" << specifier->Start().line << ":" << specifier->Start().index << "]" << std::endl;
return;
}
}
}
void Helpers::CheckDefaultImport(const ArenaVector<ir::ETSImportDeclaration *> &statements)
{
for (auto statement : statements) {
for (auto specifier : statement->Specifiers()) {
if (specifier->Type() == ir::AstNodeType::IMPORT_DEFAULT_SPECIFIER) {
auto fileName = statement->ResolvedSource();
std::cerr << "Warning: default element has already imported [" << fileName << ":"
<< specifier->Start().line << ":" << specifier->Start().index << "]" << std::endl;
return;
}
}
}
}
static util::StringView FunctionNameFromParent(const ir::AstNode *parent, ArenaAllocator *allocator)
{
switch (parent->Type()) {
case ir::AstNodeType::VARIABLE_DECLARATOR: {
const ir::VariableDeclarator *varDecl = parent->AsVariableDeclarator();
if (varDecl->Id()->IsIdentifier()) {
return varDecl->Id()->AsIdentifier()->Name();
}
break;
}
case ir::AstNodeType::METHOD_DEFINITION: {
const ir::MethodDefinition *methodDef = parent->AsMethodDefinition();
if (methodDef->Key()->IsIdentifier()) {
auto *ident = methodDef->Id();
ES2PANDA_ASSERT(ident != nullptr);
if (!ident->IsPrivateIdent()) {
return ident->Name();
}
return util::UString(varbinder::PrivateBinding::ToPrivateBinding(ident->Name()), allocator).View();
}
break;
}
case ir::AstNodeType::ASSIGNMENT_EXPRESSION: {
const ir::AssignmentExpression *assignment = parent->AsAssignmentExpression();
if (assignment->Left()->IsIdentifier()) {
return assignment->Left()->AsIdentifier()->Name();
}
break;
}
case ir::AstNodeType::ASSIGNMENT_PATTERN: {
const ir::AssignmentExpression *assignment = parent->AsAssignmentPattern();
if (assignment->Left()->IsIdentifier()) {
return assignment->Left()->AsIdentifier()->Name();
}
break;
}
case ir::AstNodeType::PROPERTY: {
const ir::Property *prop = parent->AsProperty();
if (prop->Kind() != ir::PropertyKind::PROTO &&
Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed())) {
return Helpers::LiteralToPropName(prop->Key());
}
break;
}
default:
break;
}
return util::StringView();
}
util::StringView Helpers::FunctionName(ArenaAllocator *allocator, const ir::ScriptFunction *func)
{
if (func->Id() != nullptr) {
return func->Id()->Name();
}
if (func->Parent()->IsFunctionDeclaration()) {
return "*default*";
}
const ir::AstNode *parent = func->Parent()->Parent();
if (func->IsConstructor()) {
parent = parent->Parent();
if (parent->AsClassDefinition()->Ident() != nullptr) {
return parent->AsClassDefinition()->Ident()->Name();
}
parent = parent->Parent()->Parent();
}
return FunctionNameFromParent(parent, allocator);
}
std::tuple<util::StringView, bool> Helpers::ParamName(ArenaAllocator *allocator, const ir::Expression *param,
std::uint32_t index)
{
switch (param->Type()) {
case ir::AstNodeType::IDENTIFIER: {
return {param->AsIdentifier()->Name(), false};
}
case ir::AstNodeType::ASSIGNMENT_PATTERN: {
const auto *lhs = param->AsAssignmentPattern()->Left();
if (lhs->IsIdentifier()) {
return {param->AsAssignmentPattern()->Left()->AsIdentifier()->Name(), false};
}
break;
}
case ir::AstNodeType::REST_ELEMENT: {
if (param->AsRestElement()->Argument()->IsIdentifier()) {
return {param->AsRestElement()->Argument()->AsIdentifier()->Name(), false};
}
break;
}
case ir::AstNodeType::TS_PARAMETER_PROPERTY: {
return ParamName(allocator, param->AsTSParameterProperty()->Parameter(), index);
}
case ir::AstNodeType::ETS_PARAMETER_EXPRESSION: {
return {param->AsETSParameterExpression()->Name(), false};
}
default:
break;
}
return {UString(ToString(index), allocator).View(), true};
}
static std::string GetEscapedCharacter(const unsigned char c)
{
std::stringstream escapedStr;
escapedStr << '\\';
switch (c) {
case lexer::LEX_CHAR_DOUBLE_QUOTE: {
escapedStr << '"';
break;
}
case lexer::LEX_CHAR_BS: {
escapedStr << 'b';
break;
}
case lexer::LEX_CHAR_TAB: {
escapedStr << 't';
break;
}
case lexer::LEX_CHAR_LF: {
escapedStr << 'n';
break;
}
case lexer::LEX_CHAR_VT: {
escapedStr << 'v';
break;
}
case lexer::LEX_CHAR_FF: {
escapedStr << 'f';
break;
}
case lexer::LEX_CHAR_CR: {
escapedStr << 'r';
break;
}
case lexer::LEX_CHAR_NULL: {
escapedStr << '0';
break;
}
default: {
escapedStr << 'u' << std::hex << std::setw(4U) << std::setfill('0') << static_cast<unsigned int>(c);
break;
}
}
return escapedStr.str();
}
std::string Helpers::CreateEscapedString(std::string_view const str)
{
std::string escapedStr;
for (const unsigned char c : str) {
if (LIKELY((std::isprint(c) != 0U || c >= lexer::LEX_ASCII_MAX_BITS) && c != lexer::LEX_CHAR_DOUBLE_QUOTE)) {
escapedStr += c;
} else {
escapedStr += GetEscapedCharacter(c);
}
}
return escapedStr;
}
std::string Helpers::UTF16toUTF8(const char16_t c)
{
const utf::Utf8Char utf8Ch = utf::ConvertUtf16ToUtf8(c, 0, false);
return std::string(reinterpret_cast<const char *>(utf8Ch.ch.data()), utf8Ch.n);
}
std::pair<std::string_view, std::string_view> Helpers::SplitSignature(std::string_view signature)
{
auto idx = signature.find_last_of(':');
auto stripped = signature.substr(0, idx);
idx = stripped.find_last_of('.');
auto fullClassName = stripped.substr(0, idx);
auto methodName = stripped.substr(idx + 1);
idx = fullClassName.find_last_of('.');
auto className = fullClassName.substr(idx + 1);
return {className, methodName};
}
std::vector<std::string> const &Helpers::StdLib()
{
static std::vector<std::string> stdlib {
"std/core", "std/math", "std/containers", "std/interop/js",
"std/time", "std/debug", "std/debug/concurrency", "std/dfx",
"std/testing", "std/concurrency", "std/annotations", "std/interop",
"std/math/consts", "arkruntime"};
return stdlib;
}
varbinder::Scope *Helpers::NearestScope(const ir::AstNode *ast)
{
while (ast != nullptr && !ast->IsScopeBearer()) {
ast = ast->Parent();
}
return ast == nullptr ? nullptr : ast->Scope();
}
checker::ETSObjectType const *Helpers::ContainingClass(const ir::AstNode *ast)
{
while (ast != nullptr && !ast->IsClassDefinition()) {
ast = ast->Parent();
}
return ast == nullptr ? nullptr : ast->AsClassDefinition()->TsType()->AsETSObjectType();
}
bool CheckTypeRelation(checker::ETSChecker *checker, checker::Type *super, checker::Type *sub)
{
return checker->Relation()->IsSupertypeOf(super, sub);
}
void Helpers::CheckLoweredNode(varbinder::ETSBinder *varBinder, checker::ETSChecker *checker, ir::AstNode *node)
{
auto *scope = util::Helpers::NearestScope(node);
varBinder->ResolveReferencesForScopeWithContext(node, scope);
auto *containingClass = ContainingClass(node);
checker::CheckerStatus newStatus =
(containingClass == nullptr) ? checker::CheckerStatus::NO_OPTS : checker::CheckerStatus::IN_CLASS;
if ((checker->Context().Status() & checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) != 0) {
newStatus |= checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK;
}
auto checkerCtx = checker::SavedCheckerContext(checker, newStatus, containingClass);
auto scopeCtx = checker::ScopeContext(checker, scope);
node->Check(checker);
}
bool Helpers::IsNumericGlobalBuiltIn(checker::Type *type, checker::ETSChecker *checker)
{
return CheckTypeRelation(checker, type, checker->GetGlobalTypesHolder()->GlobalIntegerBuiltinType()) ||
CheckTypeRelation(checker, type, checker->GetGlobalTypesHolder()->GlobalShortBuiltinType()) ||
CheckTypeRelation(checker, type, checker->GetGlobalTypesHolder()->GlobalByteBuiltinType()) ||
CheckTypeRelation(checker, type, checker->GetGlobalTypesHolder()->GlobalCharBuiltinType()) ||
CheckTypeRelation(checker, type, checker->GetGlobalTypesHolder()->GlobalLongBuiltinType()) ||
CheckTypeRelation(checker, type, checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType()) ||
CheckTypeRelation(checker, type, checker->GetGlobalTypesHolder()->GlobalFloatBuiltinType());
}
bool Helpers::IsStdLib(const parser::Program *program)
{
auto filePath = program->AbsoluteName().Mutf8();
std::replace(filePath.begin(), filePath.end(), util::Path::GetPathDelimiter(), '.');
if (filePath.find("stdlib") != std::string_view::npos) {
return true;
}
auto const &stdlib = StdLib();
for (const auto &stdlibPackage : stdlib) {
auto stdlibPackageCache = stdlibPackage + std::string(ImportPathManager::CACHE_SUFFIX);
std::replace(stdlibPackageCache.begin(), stdlibPackageCache.end(), '/', '.');
if (filePath.find(stdlibPackageCache) != std::string::npos) {
return true;
}
}
if (!program->Is<util::ModuleKind::PACKAGE>()) {
return false;
}
auto fileFolder = std::string(program->ModuleName());
std::replace(fileFolder.begin(), fileFolder.end(), *compiler::Signatures::METHOD_SEPARATOR.begin(),
*compiler::Signatures::NAMESPACE_SEPARATOR.begin());
return std::count(stdlib.begin(), stdlib.end(), fileFolder) != 0;
}
bool Helpers::IsStdLib(std::string_view path)
{
auto const &stdlib = StdLib();
return std::find(stdlib.begin(), stdlib.end(), path) != stdlib.end();
}
checker::Type *Helpers::CheckReturnTypeOfCheck([[maybe_unused]] const ir::AstNode *const node,
checker::Type *const type)
{
ES2PANDA_ASSERT(type != nullptr || !node->IsExpression());
return type;
}
bool Helpers::TypeContainsParameterUnderInference(checker::Type const *type)
{
return type->TypeExpressionContains(
[](checker::Type const *tp) { return tp->IsETSTypeParameter() && tp->AsETSTypeParameter()->UnderInference(); });
}
util::UString Helpers::EscapeHTMLString(ArenaAllocator *allocator, std::string_view const str)
{
util::UString replaced(allocator);
for (const auto c : str) {
switch (c) {
case '<':
replaced.Append("<");
break;
case '>':
replaced.Append(">");
break;
case '&':
replaced.Append("&");
break;
case '"':
replaced.Append(""");
break;
case '\'':
replaced.Append("'");
break;
default:
replaced.Append(c);
break;
}
}
return replaced;
}
ir::AstNode *Helpers::DerefETSTypeReference(ir::AstNode *node)
{
ES2PANDA_ASSERT(node->IsETSTypeReference());
do {
auto *name = node->AsETSTypeReference()->Part()->GetIdent();
ES2PANDA_ASSERT(name->IsIdentifier());
if (varbinder::ETSBinder::IsSpecialName(name->Name())) {
return node;
}
auto *var = name->AsIdentifier()->Variable();
if (var == nullptr) {
return node;
}
auto *declNode = var->Declaration()->Node();
if (!declNode->IsTSTypeAliasDeclaration()) {
return declNode;
}
node = declNode->AsTSTypeAliasDeclaration()->TypeAnnotation();
} while (node->IsETSTypeReference());
return node;
}
bool Helpers::IsGlobalVar(const ark::es2panda::varbinder::Variable *var)
{
return var->Declaration()->Node()->IsClassDeclaration() &&
var->Declaration()->Node()->AsClassDeclaration()->Definition()->IsGlobal();
}
bool Helpers::IsExported(ir::AstNode const *node) noexcept
{
if (node == nullptr) {
return true;
}
bool exported;
do {
exported = node->IsExported() || node->IsDefaultExported() || node->HasExportAlias();
node = node->Parent();
} while (!exported && node != nullptr);
return exported;
}
std::vector<std::string> Helpers::Split(const std::string &str, char delimiter)
{
std::vector<std::string> items;
size_t start = 0;
size_t pos = str.find(delimiter);
while (pos != std::string::npos) {
std::string item = str.substr(start, pos - start);
items.emplace_back(item);
start = pos + 1;
pos = str.find(delimiter, start);
}
std::string tail = str.substr(start);
items.emplace_back(tail);
return items;
}
it is better to use std::filesystem::relative()
but using std::filesystem::relative() in xts_static CI pipeline is disallowed
and there is no relative() in std::experimental::filesystem
*/
std::string Helpers::CalcRelativePath(const std::string &target, const std::string &base)
{
std::string targetPath = ark::os::GetAbsolutePath(target);
std::string basePath = ark::os::GetAbsolutePath(base);
if (targetPath.empty() || basePath.empty()) {
return "";
}
auto delim = ark::os::file::File::GetPathDelim();
ES2PANDA_ASSERT(delim.length() == 1);
auto delimChar = delim[0];
std::string ret;
auto targetPathVec = Split(targetPath, delimChar);
auto basePathVec = Split(basePath, delimChar);
auto mismatched = std::mismatch(targetPathVec.begin(), targetPathVec.end(), basePathVec.begin(), basePathVec.end());
if (mismatched.first == targetPathVec.end() && mismatched.second == basePathVec.end()) {
return ".";
}
for (auto itBase = mismatched.second; itBase != basePathVec.end(); ++itBase) {
ret += "../";
}
for (auto itP = mismatched.first; itP != targetPathVec.end(); ++itP) {
ret += *itP + "/";
}
return ret;
}
std::string Helpers::RelPathByStrippingPrefix(const std::string &absPath, const std::string &prefix)
{
auto normalizeSlashes = [](std::string path) {
std::replace(path.begin(), path.end(), '\\', '/');
return path;
};
auto normAbsPath = normalizeSlashes(absPath);
auto normPrefix = normalizeSlashes(prefix);
if (normPrefix.empty() || !StartsWith(normAbsPath, normPrefix) ||
(normAbsPath.size() > normPrefix.size() && normAbsPath[normPrefix.size()] != '/')) {
return "";
}
auto relPath = normAbsPath.substr(normPrefix.size());
if (!relPath.empty() && relPath.front() == '/') {
relPath = relPath.substr(1);
}
if (!relPath.empty() && relPath.back() == '/') {
relPath.pop_back();
}
return relPath;
}
bool Helpers::IsArrayType(checker::Type *type)
{
return type->IsETSArrayType() || type->IsETSResizableArrayType() || type->IsETSReadonlyArrayType();
}
checker::Type *Helpers::CreateUnionOfTupleConstituentTypes(checker::ETSChecker *checker,
const checker::ETSTupleType *type)
{
std::vector<checker::Type *> tupleTypes(type->GetTupleTypesList().begin(), type->GetTupleTypesList().end());
return checker->CreateETSUnionType(std::move(tupleTypes));
}
}