* Copyright (c) 2021-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.Apache.Org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "helpers.h"
#include <common/abc_file_utils.h>
#include <ir/base/classDefinition.h>
#include <ir/base/property.h>
#include <ir/base/scriptFunction.h>
#include <ir/base/spreadElement.h>
#include <ir/expressions/arrayExpression.h>
#include <ir/expressions/assignmentExpression.h>
#include <ir/expressions/functionExpression.h>
#include <ir/expressions/literals/booleanLiteral.h>
#include <ir/expressions/literals/numberLiteral.h>
#include <ir/expressions/literals/stringLiteral.h>
#include <ir/expressions/objectExpression.h>
#include <ir/expressions/unaryExpression.h>
#include <ir/statements/blockStatement.h>
#include <ir/statements/expressionStatement.h>
#include <ir/statements/variableDeclarator.h>
#include <ir/ts/tsParameterProperty.h>
#include <util/commonUtil.h>
#include <util/concurrent.h>
#ifdef ENABLE_BYTECODE_OPT
#include <bytecode_optimizer/bytecode_analysis_results.h>
#include <bytecode_optimizer/optimize_bytecode.h>
#else
#include <assembly-type.h>
#include <assembly-program.h>
#include <assembly-emitter.h>
#endif
#ifdef PANDA_TARGET_WINDOWS
#include <windows.h>
#undef ERROR
#else
#include <unistd.h>
#endif
namespace panda::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(ArenaAllocator *allocator, 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 Helpers::ToStringView(allocator, lit->AsNumberLiteral()->Number());
}
case ir::AstNodeType::NULL_LITERAL: {
return "null";
}
case ir::AstNodeType::BOOLEAN_LITERAL: {
if (lit->AsBooleanLiteral()->Value()) {
return "true";
}
return "false";
}
default: {
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;
}
bool Helpers::FileExtensionIs(std::string_view filePath, std::string_view extension)
{
return filePath.length() > extension.length() && Helpers::EndsWith(filePath, extension);
}
bool Helpers::EndsWith(std::string_view str, std::string_view suffix)
{
if (str.length() < suffix.length()) {
return false;
}
size_t expectPos = str.length() - suffix.length();
return str.find(suffix, expectPos) == expectPos;
}
void Helpers::GetScientificNotationForDouble(double number, uint32_t significandBitCount, int32_t &numberBitCount,
char *significandArray, char *sciNotationArray, uint32_t size)
{
if (size < MAX_DOUBLE_DIGIT) {
std::cerr << "Failed to set the size of buffer, the buffer size provided (" << size
<< ") is less than the required minimum size (" << MAX_DOUBLE_DIGIT
<< ") for formatting the number in scientific notation." << std::endl;
return;
}
if (snprintf_s(sciNotationArray, size, size - 1, "%.*e", significandBitCount - 1, number) == FAIL_SNPRINTF_S) {
std::cerr << "Failed to format the number " << number
<< " into scientific notation using snprintf_s. Please check if the buffer size (" << size
<< ") and significand bit count (" << significandBitCount << ") are appropriate." << std::endl;
return;
}
int32_t exponent = atoi(sciNotationArray + significandBitCount + 1 + (significandBitCount > 1));
numberBitCount = exponent + 1;
if (significandBitCount > 1) {
for (uint32_t i = 0; i < significandBitCount + 1; i++) {
significandArray[i] = sciNotationArray[i];
}
}
significandArray[significandBitCount + 1] = '\0';
}
int32_t Helpers::GetIntegerSignificandBitCount(double number, int32_t &numberBitCount, char *significandArray)
{
uint32_t bitPos = 0;
uint32_t minBitPos = 1;
uint32_t maxBitPos = MAX_DOUBLE_PRECISION_DIGIT;
uint32_t integerAndPointBitCount = 2;
char sciNotationArray[MAX_DOUBLE_DIGIT] = {0};
while (minBitPos < maxBitPos) {
bitPos = (minBitPos + maxBitPos) / 2;
GetScientificNotationForDouble(number, bitPos, numberBitCount, significandArray,
sciNotationArray, sizeof(sciNotationArray));
if (std::strtod(sciNotationArray, nullptr) == number) {
while (bitPos >= integerAndPointBitCount && significandArray[bitPos] == '0') {
bitPos--;
}
maxBitPos = bitPos;
} else {
minBitPos = bitPos + 1;
}
}
bitPos = maxBitPos;
GetScientificNotationForDouble(number, bitPos, numberBitCount, significandArray,
sciNotationArray, sizeof(sciNotationArray));
return bitPos;
}
std::string Helpers::DoubleToString(double number)
{
std::string result;
int32_t numberBitCount = 0;
char significandArray[MAX_DOUBLE_DIGIT] = {0};
if (number < 0) {
result += "-";
number = -number;
}
int32_t integerSignificandBitCount = GetIntegerSignificandBitCount(number, numberBitCount, significandArray);
std::string significand = significandArray;
std::string integerSignificand;
if (numberBitCount > 0 && numberBitCount <= MAX_DECIMAL_EXPONENT) {
integerSignificand = significand.erase(1, 1);
if (numberBitCount >= integerSignificandBitCount) {
integerSignificand += std::string(numberBitCount - integerSignificandBitCount, '0');
} else {
integerSignificand.insert(numberBitCount, 1, '.');
}
} else if (numberBitCount <= 0 && numberBitCount > MIN_DECIMAL_EXPONENT) {
integerSignificand = significand.erase(1, 1);
integerSignificand = std::string("0.") + std::string(-numberBitCount, '0') + integerSignificand;
} else {
if (integerSignificandBitCount == 1) {
significand = significand.erase(1, 1);
}
significand += 'e' + (numberBitCount >= 1 ? std::string("+") : "") + std::to_string(numberBitCount - 1);
result += significand;
return result;
}
result += integerSignificand;
return result;
}
std::string Helpers::ToString(double number)
{
if (std::isnan(number)) {
return "NaN";
}
if (number == 0.0) {
return "0";
}
if (std::isinf(number)) {
return "Infinity";
}
std::string str;
if (Helpers::IsInteger<int32_t>(number)) {
str = std::to_string(static_cast<int32_t>(number));
} else {
str = DoubleToString(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)
{
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();
}
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::ScriptFunction *Helpers::GetContainingConstructor(const ir::ClassProperty *node)
{
for (const auto *parent = node->Parent(); parent != nullptr; parent = parent->Parent()) {
if (parent->IsClassDefinition()) {
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::GetClassDefiniton(const ir::ScriptFunction *node)
{
CHECK_NOT_NULL(node);
ASSERT(node->IsConstructor() || node->IsMethod());
ASSERT(node->Parent()->IsFunctionExpression());
ASSERT(node->Parent()->Parent()->IsMethodDefinition());
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;
}
bool Helpers::IsConstantExpr(const ir::Expression *expr)
{
switch (expr->Type()) {
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;
}
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(const ir::AstNode *node, std::vector<const ir::Identifier *> *bindings)
{
switch (node->Type()) {
case ir::AstNodeType::IDENTIFIER: {
if (!Helpers::IsGlobalIdentifier(node->AsIdentifier()->Name())) {
bindings->push_back(node->AsIdentifier());
}
break;
}
case ir::AstNodeType::OBJECT_PATTERN: {
for (const auto *prop : node->AsObjectPattern()->Properties()) {
CollectBindingName(prop, bindings);
}
break;
}
case ir::AstNodeType::ARRAY_PATTERN: {
for (const auto *element : node->AsArrayPattern()->Elements()) {
CollectBindingName(element, bindings);
}
break;
}
case ir::AstNodeType::ASSIGNMENT_PATTERN: {
CollectBindingName(node->AsAssignmentPattern()->Left(), bindings);
break;
}
case ir::AstNodeType::PROPERTY: {
CollectBindingName(node->AsProperty()->Value(), bindings);
break;
}
case ir::AstNodeType::REST_ELEMENT: {
CollectBindingName(node->AsRestElement()->Argument(), bindings);
break;
}
default:
break;
}
}
std::vector<const ir::Identifier *> Helpers::CollectBindingNames(const ir::AstNode *node)
{
std::vector<const ir::Identifier *> bindings;
CollectBindingName(node, &bindings);
return bindings;
}
util::StringView Helpers::FunctionName(ArenaAllocator *allocator, const ir::ScriptFunction *func)
{
if (func->Id()) {
return func->Id()->Name();
}
if (func->Parent()->IsFunctionDeclaration()) {
return parser::SourceTextModuleRecord::DEFAULT_EXTERNAL_NAME;
}
const ir::AstNode *parent = func->Parent()->Parent();
if (func->IsConstructor()) {
parent = parent->Parent();
if (parent->AsClassDefinition()->Ident()) {
return parent->AsClassDefinition()->Ident()->Name();
}
parent = parent->Parent()->Parent();
}
return GetName(allocator, parent);
}
util::StringView Helpers::GetName(ArenaAllocator *allocator, const ir::AstNode *node)
{
switch (node->Type()) {
case ir::AstNodeType::VARIABLE_DECLARATOR: {
const ir::VariableDeclarator *varDecl = node->AsVariableDeclarator();
if (varDecl->Id()->IsIdentifier()) {
return varDecl->Id()->AsIdentifier()->Name();
}
break;
}
case ir::AstNodeType::METHOD_DEFINITION: {
const ir::MethodDefinition *methodDef = node->AsMethodDefinition();
if (methodDef->Key()->IsIdentifier()) {
return methodDef->Key()->AsIdentifier()->Name();
}
break;
}
case ir::AstNodeType::ASSIGNMENT_EXPRESSION: {
const ir::AssignmentExpression *assignment = node->AsAssignmentExpression();
if (assignment->Left()->IsIdentifier()) {
return assignment->Left()->AsIdentifier()->Name();
}
break;
}
case ir::AstNodeType::ASSIGNMENT_PATTERN: {
const ir::AssignmentExpression *assignment = node->AsAssignmentPattern();
if (assignment->Left()->IsIdentifier()) {
return assignment->Left()->AsIdentifier()->Name();
}
break;
}
case ir::AstNodeType::PROPERTY: {
const ir::Property *prop = node->AsProperty();
if (prop->Kind() != ir::PropertyKind::PROTO &&
Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed())) {
return Helpers::LiteralToPropName(allocator, prop->Key());
}
break;
}
case ir::AstNodeType::CLASS_PROPERTY: {
const ir::ClassProperty *prop = node->AsClassProperty();
if (Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed())) {
return Helpers::LiteralToPropName(allocator, prop->Key());
}
break;
}
case ir::AstNodeType::EXPORT_DEFAULT_DECLARATION: {
return parser::SourceTextModuleRecord::DEFAULT_EXTERNAL_NAME;
}
default:
break;
}
return util::StringView();
}
std::tuple<util::StringView, bool> Helpers::ParamName(ArenaAllocator *allocator, const ir::AstNode *param,
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);
}
default:
break;
}
return {Helpers::ToStringView(allocator, index), true};
}
bool Helpers::IsChild(const ir::AstNode *parent, const ir::AstNode *child)
{
while (child) {
if (child == parent) {
return true;
}
child = child->Parent();
}
return false;
}
bool Helpers::IsChildScope(const binder::Scope *parent, const binder::Scope *child)
{
while (child) {
if (child == parent) {
return true;
}
child = child->Parent();
}
return false;
}
bool Helpers::IsObjectPropertyValue(const ArenaVector<ir::Expression *> &properties, const ir::AstNode *ident)
{
for (const auto *prop : properties) {
ASSERT(prop->IsProperty() || prop->IsSpreadElement());
if (prop->IsProperty() && (prop->AsProperty()->Value() == ident)) {
return true;
}
if (prop->IsSpreadElement() && (prop->AsSpreadElement()->Argument() == ident)) {
return true;
}
}
return false;
}
SignedNumberLiteral Helpers::GetSignedNumberLiteral(const ir::Expression *expr)
{
if (!expr->IsUnaryExpression()) {
return SignedNumberLiteral::UNRECOGNIZED;
}
auto unaryExpression = expr->AsUnaryExpression();
if (!unaryExpression->Argument()->IsNumberLiteral()) {
return SignedNumberLiteral::UNRECOGNIZED;
}
if (unaryExpression->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS) {
return SignedNumberLiteral::POSITIVE;
} else if (unaryExpression->OperatorType() == lexer::TokenType::PUNCTUATOR_MINUS) {
return SignedNumberLiteral::NEGATIVE;
}
return SignedNumberLiteral::UNRECOGNIZED;
}
void Helpers::SetConstantLocalExportSlots(const std::string &record, const std::unordered_set<uint32_t> &slots)
{
bool ignored;
auto &result = panda::bytecodeopt::BytecodeAnalysisResults::GetOrCreateBytecodeAnalysisResult(record, ignored);
result.SetConstantLocalExportSlots(slots);
}
static std::string GetTempOutputName(const std::string &inputFile)
{
std::string pid;
#ifdef PANDA_TARGET_WINDOWS
pid = std::to_string(GetCurrentProcessId());
#else
pid = std::to_string(getpid());
#endif
const std::string outputSuffix = ".unopt.abc";
return panda::os::file::File::GetExtendedFilePath(inputFile + pid + outputSuffix);
}
void Helpers::AnalysisProgram(panda::pandasm::Program *prog, const std::string &inputFile)
{
#ifdef PANDA_WITH_BYTECODE_OPTIMIZER
std::map<std::string, size_t> stat;
std::map<std::string, size_t> *statp = &stat;
auto tempOutput = GetTempOutputName(inputFile);
bool exists = false;
auto mapsp = &panda::bytecodeopt::BytecodeAnalysisResults::GetOrCreateBytecodeMaps(tempOutput, exists);
ASSERT(!exists);
const uint32_t COMPONENT_MASK = panda::Logger::Component::ASSEMBLER |
panda::Logger::Component::BYTECODE_OPTIMIZER |
panda::Logger::Component::COMPILER;
panda::Logger::InitializeStdLogging(panda::Logger::Level::ERROR, COMPONENT_MASK);
if (panda::pandasm::AsmEmitter::Emit(tempOutput, *prog, statp, mapsp, true)) {
panda::bytecodeopt::AnalysisBytecode(prog, mapsp, tempOutput, true, true);
} else {
panda::bytecodeopt::BytecodeAnalysisResults::DeleteBytecodeMaps(tempOutput);
}
#endif
}
void Helpers::OptimizeProgram(panda::pandasm::Program *prog, const std::string &inputFile)
{
auto tempOutput = GetTempOutputName(inputFile);
#ifdef PANDA_WITH_BYTECODE_OPTIMIZER
std::map<std::string, size_t> stat;
std::map<std::string, size_t> *statp = &stat;
const uint32_t COMPONENT_MASK = panda::Logger::Component::ASSEMBLER |
panda::Logger::Component::BYTECODE_OPTIMIZER |
panda::Logger::Component::COMPILER;
panda::Logger::InitializeStdLogging(panda::Logger::Level::ERROR, COMPONENT_MASK);
bool exists = false;
auto mapsp = &panda::bytecodeopt::BytecodeAnalysisResults::GetOrCreateBytecodeMaps(tempOutput, exists);
if (!exists) {
exists = panda::pandasm::AsmEmitter::Emit(tempOutput, *prog, statp, mapsp, true);
}
if (exists) {
panda::bytecodeopt::OptimizeBytecode(prog, mapsp, tempOutput, true, true);
std::remove(tempOutput.c_str());
}
panda::bytecodeopt::BytecodeAnalysisResults::DeleteBytecodeMaps(tempOutput);
#endif
}
bool Helpers::CheckAopTransformPath(const std::string &libPath)
{
std::string lowerLibPath = libPath;
std::transform(libPath.begin(), libPath.end(), lowerLibPath.begin(), ::tolower);
bool isValidSuffix = false;
#ifdef PANDA_TARGET_WINDOWS
isValidSuffix = FileExtensionIs(lowerLibPath, FileSuffix::DLL);
std::string supportSuffix = std::string(FileSuffix::DLL);
#else
isValidSuffix = FileExtensionIs(lowerLibPath, FileSuffix::SO)
|| FileExtensionIs(lowerLibPath, FileSuffix::DYLIB);
std::string supportSuffix = std::string(FileSuffix::SO) + "|" + std::string(FileSuffix::DYLIB);
#endif
if (!isValidSuffix) {
std::string msg = "aop transform file suffix support " + supportSuffix + ", error file: " + libPath;
std::cout << msg << std::endl;
return false;
}
return true;
}
AopTransformFuncDef Helpers::LoadAopTransformLibFunc(const std::string &libPath,
const std::string &funcName, os::library_loader::LibraryHandle &handler)
{
auto loadRes = os::library_loader::Load(libPath);
if (!loadRes.HasValue()) {
std::string msg = "os::library_loader::Load error: " + loadRes.Error().ToString();
std::cout << msg << std::endl;
return nullptr;
}
handler = std::move(loadRes.Value());
auto initRes = os::library_loader::ResolveSymbol(handler, funcName);
if (!initRes.HasValue()) {
std::string msg = "os::library_loader::ResolveSymbol get func Transform error: " + initRes.Error().ToString();
std::cout << msg << std::endl;
return nullptr;
}
return reinterpret_cast<AopTransformFuncDef>(initRes.Value());
}
bool Helpers::AopTransform(const std::string &inputFile, const std::string &libPath)
{
if (!CheckAopTransformPath(libPath)) {
return false;
}
os::library_loader::LibraryHandle handler(nullptr);
AopTransformFuncDef transform = LoadAopTransformLibFunc(libPath, "Transform", handler);
if (transform == nullptr) {
return false;
}
int res = transform(inputFile.c_str());
if (res != 0) {
std::string msg = "Transform exec fail: " + libPath;
std::cout << msg << std::endl;
return false;
}
return true;
}
bool Helpers::ReadFileToBuffer(const std::string &file, std::stringstream &ss)
{
std::ifstream inputStream = Helpers::FileStream<std::ifstream>(
panda::os::file::File::GetExtendedFilePath(file), std::ios::binary);
if (inputStream.fail()) {
std::cerr << "Failed to read file to buffer: " << file << std::endl <<
"Solutions: > Check whether the above file exists." <<
"> Check whether you have the correct access permissions for the file.";
return false;
}
ss << inputStream.rdbuf();
return true;
}
void Helpers::ScanDirectives(ir::ScriptFunction *func, const DirectiveScanConfig &scanConfig)
{
auto *body = func->Body();
if (!body || body->IsExpression()) {
return;
}
auto &statements = body->AsBlockStatement()->Statements();
if (statements.empty()) {
return;
}
bool keepScan = true;
auto iter = statements.begin();
while (keepScan && (iter != statements.end())) {
auto *stmt = *iter++;
if (!stmt->IsExpressionStatement()) {
return;
}
auto *expr = stmt->AsExpressionStatement()->GetExpression();
if (!expr->IsStringLiteral()) {
return;
}
keepScan = SetFuncFlagsForDirectives(expr->AsStringLiteral(), func, scanConfig);
}
return;
}
bool Helpers::SetFuncFlagsForDirectives(const ir::StringLiteral *strLit, ir::ScriptFunction *func,
const DirectiveScanConfig &scanConfig)
{
if (strLit->Str().Is(USE_CONCURRENT)) {
util::Concurrent::SetConcurrent(func, strLit, scanConfig.lineIndex);
return true;
}
if (strLit->Str().Is(USE_SENDABLE)) {
if (func->IsConstructor()) {
if (scanConfig.enableSendableClass) {
auto *classDef = const_cast<ir::ClassDefinition*>(GetClassDefiniton(func));
classDef->SetSendable();
}
} else if (scanConfig.enableSendableFunc) {
func->AddFlag(ir::ScriptFunctionFlags::SENDABLE);
}
return true;
}
if (scanConfig.enableEtsImplements && func->IsConstructor() && strLit->Str().StartsWith(IMPLEMENTS_STATIC)) {
auto *classDef = const_cast<ir::ClassDefinition*>(GetClassDefiniton(func));
classDef->SetImplementFromEts();
classDef->SetEtsImplementsMessage(strLit->Str().Substr(IMPLEMENTS_STATIC.size(), strLit->Str().Length()));
}
return false;
}
std::string Helpers::GetHashString(const std::string &str)
{
uint64_t result = FNV_OFFSET;
const uint8_t *input = reinterpret_cast<const uint8_t *>(str.c_str());
for (size_t i = 0; i < str.size(); i++) {
result ^= input[i];
result *= FNV_PRIME;
}
return std::to_string(result);
}
#ifdef PANDA_TARGET_WINDOWS
std::wstring Helpers::Utf8ToUtf16(const std::string &utf8)
{
std::wstring utf16;
if (utf8.empty()) {
return utf16;
}
if (utf8.length() > static_cast<size_t>(std::numeric_limits<int>::max())) {
std::cerr << "Length of filename: " << utf8 << " is too long" << std::endl;
return utf16;
}
const int utf8Length = static_cast<int>(utf8.length());
constexpr DWORD kFlags = MB_ERR_INVALID_CHARS;
const int utf16Length = MultiByteToWideChar(CP_UTF8, kFlags, utf8.data(), utf8Length, nullptr, 0);
if (utf16Length == 0) {
std::cerr << "The filename: " << utf8 << " is not a valid utf8 encoding string" << std::endl;
return utf16;
}
utf16.resize(utf16Length);
MultiByteToWideChar(CP_UTF8, kFlags, utf8.data(), utf8Length, &utf16[0], utf16Length);
return utf16;
}
#endif
void Helpers::ThrowError(ErrorType type, const parser::Program *program, const lexer::SourcePosition &pos,
const std::string_view &msg)
{
lexer::LineIndex index(program->SourceCode());
lexer::SourceLocation loc = index.GetLocation(pos);
throw Error {type, msg, loc.line, loc.col};
}
bool Helpers::IsUseShared(const ir::Statement *statement)
{
if (!statement->IsExpressionStatement()) {
return false;
}
if (!statement->AsExpressionStatement()->GetExpression()->IsStringLiteral()) {
return false;
}
return statement->AsExpressionStatement()->GetExpression()->AsStringLiteral()->Str().Is(USE_SHARED);
}
const ir::ClassDefinition *Helpers::GetContainingSendableClass(const ir::AstNode *node)
{
while (node != nullptr) {
if (node->IsClassDefinition() && node->AsClassDefinition()->IsSendable()) {
return node->AsClassDefinition();
}
node = node->Parent();
}
return nullptr;
}
bool Helpers::IsSpecialScopeName(const util::StringView &name)
{
return name.Find(Helpers::DOT.data()) != std::string::npos ||
name.Find(Helpers::BACKSLASH.data()) != std::string::npos;
}
bool Helpers::BelongingToRecords(const std::string &name, const std::unordered_set<std::string> &retainRecordSet,
const std::string &delimiter)
{
size_t pos = name.rfind(delimiter);
if (pos == std::string::npos) {
std::cerr << "The input name: " << name << " is illegal, it should contain the delimiter character '" <<
delimiter << "'" << std::endl;
return false;
}
auto recordName = name.substr(0, pos);
return retainRecordSet.find(recordName) != retainRecordSet.end();
}
bool Helpers::IsInnerAnnotationRecordName(const std::string_view &name)
{
return name == panda::abc2program::CONCURRENT_MODULE_REQUEST_RECORD_NAME ||
name == panda::abc2program::SLOT_NUMBER_RECORD_NAME ||
name == panda::abc2program::EXPECTED_PROPERTY_COUNT_RECORD_NAME;
}
size_t Helpers::RemoveRecordSuffixAnnotationName(std::string &name)
{
auto pos = name.rfind(abc2program::RECORD_ANNOTATION_SEPARATOR);
if (pos != std::string::npos) {
name = name.substr(0, pos);
}
return pos;
}
void Helpers::RemoveProgramsRedundantData(std::map<std::string, panda::es2panda::util::ProgramCache*> &progsInfo,
const std::map<std::string, std::unordered_set<std::string>> &resolvedDepsRelation)
{
auto progInfoIter = progsInfo.begin();
while (progInfoIter != progsInfo.end()) {
std::string name = progInfoIter->first;
auto &program = progInfoIter->second->program;
auto &recordMetaData = program.record_table.begin()->second.metadata;
* The record name of a user-defined annotation is the annotation name concatenated with the record name of the
* file where it is declared. In order to preserve the annotated record, it is necessary to remove the
* concatenated annotation name from the record name before perform dependency matching.
*/
if (!util::RecordNotGeneratedFromBytecode(name) && (recordMetaData->GetAccessFlags() & ACC_ANNOTATION) != 0 &&
!IsInnerAnnotationRecordName(program.record_table.begin()->first)) {
RemoveRecordSuffixAnnotationName(name);
}
* Imported user-defined annotations will generate external records in the abc files. Its naming convention is
* consistent with the definition of annotations. But importing annotations supports qualified names,
* such as 'ns.Anno'. so it may need to remove suffixes multiple times to ensure that record name is found.
*/
if (!util::RecordNotGeneratedFromBytecode(name) && recordMetaData->IsForeign()) {
auto versionStart = name.rfind(util::NORMALIZED_OHMURL_SEPARATOR);
size_t pos = name.length();
while (pos != std::string::npos && pos > versionStart &&
resolvedDepsRelation.find(name) == resolvedDepsRelation.end()) {
pos = RemoveRecordSuffixAnnotationName(name);
}
}
if (resolvedDepsRelation.find(name) == resolvedDepsRelation.end()) {
progInfoIter = progsInfo.erase(progInfoIter);
continue;
}
progInfoIter++;
}
}
bool Helpers::IsDefaultApiVersion(int apiVersion, std::string subApiVersion)
{
return apiVersion < DEFAULT_TARGET_API_VERSION || ((apiVersion == DEFAULT_TARGET_API_VERSION) &&
(subApiVersion == SUB_API_VERSION_1 || subApiVersion == SUB_API_VERSION_2));
}
bool Helpers::IsSupportLazyImportVersion(int apiVersion, std::string subApiVersion)
{
return !(apiVersion < LAZY_IMPORT_MIN_SUPPORTED_API_VERSION ||
((apiVersion == LAZY_IMPORT_MIN_SUPPORTED_API_VERSION) &&
(subApiVersion == SUB_API_VERSION_1 || subApiVersion == SUB_API_VERSION_2)));
}
bool Helpers::IsEnableExpectedPropertyCountApiVersion(int apiVersion)
{
return !(apiVersion < ENABLE_EXPECTED_PROPERTY_COUNT_MIN_SUPPORTED_API_VERSION);
}
bool Helpers::IsSupportAnnotationVersion(int apiVersion)
{
return !(apiVersion < ANNOTATION_SUPPORTED_API_VERSION);
}
}