* 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 "constantExpressionLowering.h"
#include <cmath>
#include <cstdint>
#include <limits>
#include "checker/ETSchecker.h"
#include "checker/types/typeError.h"
#include "compiler/lowering/util.h"
#include "ir/expression.h"
#include "ir/expressions/literals/undefinedLiteral.h"
#include "ir/ts/tsAsExpression.h"
#include "compiler/lowering/scopesInit/scopesInitPhase.h"
#include "util/helpers.h"
#include "libarkbase/utils/small_vector.h"
namespace ark::es2panda::compiler {
static ir::PrimitiveType TryExtractPrimitiveType(ir::TypeNode *constraint);
template <bool EXPLICIT = false>
static bool CheckCastLiteral(util::DiagnosticEngine *de, ir::TypeNode *constraint, ir::Literal *literal);
template <bool EXPLICIT>
static bool CheckCastNumber(util::DiagnosticEngine *de, ir::TypeNode *constraint, lexer::Number &number);
static ir::PrimitiveType GetPrimitiveType(const lexer::Number &number)
{
if (number.IsByte()) {
return ir::PrimitiveType::BYTE;
}
if (number.IsShort()) {
return ir::PrimitiveType::SHORT;
}
if (number.IsInt()) {
return ir::PrimitiveType::INT;
}
if (number.IsLong()) {
return ir::PrimitiveType::LONG;
}
if (number.IsFloat()) {
return ir::PrimitiveType::FLOAT;
}
if (number.IsDouble()) {
return ir::PrimitiveType::DOUBLE;
}
ES2PANDA_UNREACHABLE();
}
template <typename TargetType>
static TargetType GetVal(const ir::Literal *node)
{
if constexpr (std::is_same_v<TargetType, bool>) {
ES2PANDA_ASSERT(node->IsBooleanLiteral());
return node->AsBooleanLiteral()->Value();
}
ES2PANDA_ASSERT(node->IsNumberLiteral());
auto numNode = node->AsNumberLiteral();
if constexpr (std::is_same_v<TargetType, int8_t>) {
ES2PANDA_ASSERT(numNode->Number().IsByte());
return numNode->Number().GetByte();
}
if constexpr (std::is_same_v<TargetType, int16_t>) {
ES2PANDA_ASSERT(numNode->Number().IsShort());
return numNode->Number().GetShort();
}
if constexpr (std::is_same_v<TargetType, int32_t>) {
ES2PANDA_ASSERT(numNode->Number().IsInt());
return numNode->Number().GetInt();
}
if constexpr (std::is_same_v<TargetType, int64_t>) {
ES2PANDA_ASSERT(numNode->Number().IsLong());
return numNode->Number().GetLong();
}
if constexpr (std::is_same_v<TargetType, float>) {
ES2PANDA_ASSERT(numNode->Number().IsFloat());
return numNode->Number().GetFloat();
}
if constexpr (std::is_same_v<TargetType, double>) {
ES2PANDA_ASSERT(numNode->Number().IsDouble());
return numNode->Number().GetDouble();
}
ES2PANDA_UNREACHABLE();
}
static bool IsBitwiseLogicalExpression(const ir::BinaryExpression *expr)
{
auto opType = expr->OperatorType();
return opType == lexer::TokenType::PUNCTUATOR_BITWISE_XOR || opType == lexer::TokenType::PUNCTUATOR_BITWISE_AND ||
opType == lexer::TokenType::PUNCTUATOR_BITWISE_OR;
}
static bool IsAdditiveExpression(const ir::BinaryExpression *expr)
{
auto opType = expr->OperatorType();
return opType == lexer::TokenType::PUNCTUATOR_PLUS || opType == lexer::TokenType::PUNCTUATOR_MINUS;
}
static bool IsMultiplicativeExpression(const ir::BinaryExpression *expr)
{
auto opType = expr->OperatorType();
return opType == lexer::TokenType::PUNCTUATOR_MULTIPLY || opType == lexer::TokenType::PUNCTUATOR_DIVIDE ||
opType == lexer::TokenType::PUNCTUATOR_MOD || opType == lexer::TokenType::PUNCTUATOR_EXPONENTIATION;
}
static bool IsRelationalExpression(const ir::BinaryExpression *expr)
{
auto opType = expr->OperatorType();
return opType == lexer::TokenType::PUNCTUATOR_GREATER_THAN ||
opType == lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL ||
opType == lexer::TokenType::PUNCTUATOR_LESS_THAN || opType == lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL ||
opType == lexer::TokenType::PUNCTUATOR_EQUAL || opType == lexer::TokenType::PUNCTUATOR_NOT_EQUAL ||
opType == lexer::TokenType::PUNCTUATOR_STRICT_EQUAL ||
opType == lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL;
}
static bool IsEqualityExpression(const ir::BinaryExpression *expr)
{
auto opType = expr->OperatorType();
return opType == lexer::TokenType::PUNCTUATOR_EQUAL || opType == lexer::TokenType::PUNCTUATOR_NOT_EQUAL ||
opType == lexer::TokenType::PUNCTUATOR_STRICT_EQUAL ||
opType == lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL;
}
static bool IsShiftExpression(const ir::BinaryExpression *expr)
{
auto opType = expr->OperatorType();
return opType == lexer::TokenType::PUNCTUATOR_LEFT_SHIFT || opType == lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT ||
opType == lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT;
}
static bool IsLogicalExpression(const ir::BinaryExpression *expr)
{
auto opType = expr->OperatorType();
return opType == lexer::TokenType::PUNCTUATOR_LOGICAL_AND || opType == lexer::TokenType::PUNCTUATOR_LOGICAL_OR;
}
using ParsedBigInt = std::pair<bool, std::string>;
static std::optional<ParsedBigInt> ParseBigIntLiteral(util::StringView token);
static bool TestLiteral(const ir::Literal *lit)
{
if (lit->IsBooleanLiteral()) {
return lit->AsBooleanLiteral()->Value();
}
if (lit->IsStringLiteral()) {
return !lit->AsStringLiteral()->Str().Empty();
}
if (lit->IsCharLiteral()) {
return lit->AsCharLiteral()->Char() != 0;
}
if (lit->IsNumberLiteral()) {
return !lit->AsNumberLiteral()->Number().IsZero();
}
if (lit->IsBigIntLiteral()) {
auto parsed = ParseBigIntLiteral(lit->AsBigIntLiteral()->Str());
return parsed.has_value() && parsed->second != "0";
}
ES2PANDA_UNREACHABLE();
}
static bool IsNegativeOddInteger(const lexer::Number &number)
{
if (number.IsInteger()) {
const auto value = number.GetValue<int64_t>();
return value < 0 && (value & 1) != 0;
}
if (!number.IsReal()) {
return false;
}
const auto value = number.GetDouble();
if (!std::isfinite(value)) {
return false;
}
const auto integerPart = std::trunc(value);
if (integerPart != value || integerPart < static_cast<double>(std::numeric_limits<int64_t>::min()) ||
integerPart > static_cast<double>(std::numeric_limits<int64_t>::max())) {
return false;
}
const auto intValue = static_cast<int64_t>(integerPart);
return intValue < 0 && (intValue & 1) != 0;
}
static std::optional<ParsedBigInt> ParseBigIntLiteral(util::StringView token)
{
std::string src {token.Utf8()};
if (src.empty()) {
return std::nullopt;
}
if (src.back() == 'n' || src.back() == 'N') {
src.pop_back();
}
if (src.empty()) {
return std::nullopt;
}
ParsedBigInt parsed {false, ""};
size_t pos = 0;
if (src[0] == '+' || src[0] == '-') {
parsed.first = (src[0] == '-');
pos = 1;
}
if (pos >= src.size()) {
return std::nullopt;
}
parsed.second.reserve(src.size() - pos);
for (; pos < src.size(); pos++) {
auto c = src[pos];
if (c == '_') {
continue;
}
if (c < '0' || c > '9') {
return std::nullopt;
}
parsed.second.push_back(c);
}
if (parsed.second.empty()) {
return std::nullopt;
}
const auto firstNonZero = parsed.second.find_first_not_of('0');
if (firstNonZero == std::string::npos) {
parsed.second = "0";
parsed.first = false;
} else if (firstNonZero > 0) {
parsed.second.erase(0, firstNonZero);
}
return parsed;
}
static int CompareBigIntMagnitude(const std::string &lhs, const std::string &rhs)
{
if (lhs.size() != rhs.size()) {
return lhs.size() < rhs.size() ? -1 : 1;
}
if (lhs == rhs) {
return 0;
}
return lhs < rhs ? -1 : 1;
}
static int CompareBigInt(const ParsedBigInt &lhs, const ParsedBigInt &rhs)
{
if (lhs.first != rhs.first) {
return lhs.first ? -1 : 1;
}
const auto magCmp = CompareBigIntMagnitude(lhs.second, rhs.second);
return lhs.first ? -magCmp : magCmp;
}
static ir::NumberLiteral *TryNumberLiteralFromBigIntToken(ArenaAllocator *allocator, util::StringView token)
{
auto parsed = ParseBigIntLiteral(token);
if (!parsed.has_value()) {
return nullptr;
}
std::string compact = parsed->first ? "-" + parsed->second : parsed->second;
errno = 0;
char *endPtr = nullptr;
const auto v = std::strtoll(compact.c_str(), &endPtr, 10);
if (endPtr != compact.c_str() + compact.size() || errno == ERANGE) {
return nullptr;
}
auto num = lexer::Number(static_cast<int64_t>(v));
return util::NodeAllocator::Alloc<ir::NumberLiteral>(allocator, num);
}
static void HandleUndefinedInLogicalExpression(public_lib::Context *context, ir::Expression *node, ir::Expression *init)
{
if (init == nullptr || !init->IsUndefinedLiteral()) {
return;
}
auto parent = node->Parent();
if (parent == nullptr || !parent->IsBinaryExpression()) {
return;
}
auto *bexpr = parent->AsBinaryExpression();
if (!IsLogicalExpression(bexpr)) {
return;
}
if (bexpr->Left() == node || bexpr->Right() == node) {
auto *undef = util::NodeAllocator::Alloc<ir::UndefinedLiteral>(context->allocator);
undef->SetTsType(context->GetChecker()->AsETSChecker()->GlobalETSUndefinedType());
undef->SetParent(parent);
if (bexpr->Left() == node) {
bexpr->SetLeft(undef);
} else {
bexpr->SetRight(undef);
}
}
}
class NodeCalculator {
public:
using DAGNode = ConstantExpressionLoweringImpl::DAGNode;
using TypeRank = lexer::Number::TypeRank;
NodeCalculator(public_lib::Context *ctx, size_t sz) : context_ {ctx}
{
inputs_.resize(sz);
}
void SetInput(ir::Literal *node, size_t i)
{
ES2PANDA_ASSERT(i < inputs_.size());
inputs_[i] = node;
}
ir::Literal *Calculate(DAGNode *node);
private:
ir::Literal *SubstituteConstant()
{
ES2PANDA_ASSERT(inputs_.size() == 1);
return inputs_[0]->Clone(context_->allocator, nullptr)->AsExpression()->AsLiteral();
}
ir::Literal *SubstituteConstantConditionally()
{
ES2PANDA_ASSERT(inputs_.size() == 3U);
const auto *test = inputs_[0];
auto *conseq = inputs_[1];
auto *altern = inputs_[2U];
auto res = TestLiteral(test) ? conseq : altern;
auto resNode = res->Clone(context_->allocator, nullptr)->AsExpression()->AsLiteral();
return resNode;
}
ir::Literal *Calculate(ir::TSAsExpression *expr)
{
ES2PANDA_ASSERT(inputs_.size() == 1U);
if (!inputs_[0]->IsNumberLiteral()) {
return nullptr;
}
auto number = inputs_[0]->AsNumberLiteral()->Number();
if (!CheckCastNumber<true>(context_->diagnosticEngine, expr->TypeAnnotation(), number)) {
return nullptr;
}
auto *literal = CreateNumberLiteral(number);
auto *const targetType = expr->TypeAnnotation()->TsType();
if (targetType != nullptr) {
literal->SetTsType(targetType);
}
return literal;
}
void SetFloatLiteralStringRepresentation(ir::NumberLiteral *numberLiteral)
{
auto &number = numberLiteral->Number();
if (!number.IsFloat()) {
return;
}
std::string str;
if (std::isnan(number.GetFloat())) {
str = "Float.NaN";
} else if (!std::isfinite(number.GetFloat())) {
str = std::signbit(number.GetFloat()) ? "Float.NEGATIVE_INFINITY" : "Float.POSITIVE_INFINITY";
} else {
str = numberLiteral->ToString();
}
if (std::isfinite(number.GetFloat())) {
str += "f";
}
number.SetStr(util::UString(str, context_->allocator).View());
}
ir::Literal *Calculate(ir::TemplateLiteral *expr)
{
std::string tmpStr {};
auto quasis = expr->Quasis();
auto const num = std::max(inputs_.size(), quasis.size());
for (std::size_t i = 0U; i < num; i++) {
if (i < quasis.size()) {
tmpStr += quasis[i]->Cooked().Utf8();
}
if (i < inputs_.size()) {
if (inputs_[i]->IsCharLiteral()) {
LogError(diagnostic::CHAR_TO_STR_CONVERSION, {}, expr->Start());
} else if (inputs_[i]->IsNumberLiteral() || inputs_[i]->IsBooleanLiteral()) {
tmpStr += inputs_[i]->ToString();
} else if (inputs_[i]->IsStringLiteral()) {
tmpStr += inputs_[i]->AsStringLiteral()->Str().Utf8();
} else {
ES2PANDA_UNREACHABLE();
}
}
}
util::UString result(tmpStr, context_->allocator);
return util::NodeAllocator::Alloc<ir::StringLiteral>(context_->allocator, result.View());
}
template <typename To>
To ExtractFromLiteral(const ir::NumberLiteral *lit)
{
if (lit->Number().CanGetValue<To>()) {
return lit->Number().GetValue<To>();
}
using Limits = std::numeric_limits<To>;
if ((lit->Number().Is<double>() || lit->Number().Is<float>()) && std::is_integral_v<To>) {
auto fp = lit->Number().GetValue<double>();
if (((static_cast<double>(Limits::min()) <= fp)) && (fp <= static_cast<double>(Limits::max()))) {
return static_cast<To>(fp);
}
}
return {};
}
ir::NumberLiteral *FoldUnaryNumericConstant(const ir::UnaryExpression *unary, ir::NumberLiteral *literal)
{
auto rank = literal->Number().GetTypeRank();
switch (rank) {
case TypeRank::DOUBLE: {
return FoldUnaryNumericConstantHelper<double>(unary, literal, rank);
}
case TypeRank::FLOAT: {
return FoldUnaryNumericConstantHelper<float>(unary, literal, rank);
}
case TypeRank::INT64: {
return FoldUnaryNumericConstantHelper<int64_t>(unary, literal, rank);
}
case TypeRank::INT32: {
return FoldUnaryNumericConstantHelper<int32_t>(unary, literal, rank);
}
case TypeRank::INT16: {
return FoldUnaryNumericConstantHelper<int16_t>(unary, literal, rank);
}
case TypeRank::INT8: {
return FoldUnaryNumericConstantHelper<int8_t>(unary, literal, rank);
}
default: {
ES2PANDA_UNREACHABLE();
}
}
}
template <typename InputType>
ir::NumberLiteral *FoldUnaryNumericConstantHelper(const ir::UnaryExpression *unary, const ir::NumberLiteral *node,
TypeRank rank)
{
lexer::Number resNum {};
switch (unary->OperatorType()) {
case lexer::TokenType::PUNCTUATOR_PLUS: {
resNum = lexer::Number(ExtractFromLiteral<InputType>(node));
if (node->Number().IsNegativeZero()) {
resNum.SetNegativeZero(true);
}
break;
}
case lexer::TokenType::PUNCTUATOR_MINUS: {
resNum = lexer::Number(-ExtractFromLiteral<InputType>(node));
if (node->Number().IsZero()) {
resNum.SetNegativeZero(!node->Number().IsNegativeZero());
}
break;
}
case lexer::TokenType::PUNCTUATOR_TILDE: {
resNum = HandleBitwiseNegate(node, rank);
break;
}
default: {
ES2PANDA_UNREACHABLE();
}
}
return CreateNumberLiteral(resNum);
}
lexer::Number HandleBitwiseNegate(const ir::NumberLiteral *node, TypeRank rank)
{
switch (rank) {
case TypeRank::DOUBLE:
case TypeRank::INT64: {
return lexer::Number(~static_cast<uint64_t>(ExtractFromLiteral<int64_t>(node)));
}
case TypeRank::FLOAT:
case TypeRank::INT32:
case TypeRank::INT16:
case TypeRank::INT8: {
return lexer::Number(~static_cast<uint32_t>(ExtractFromLiteral<int32_t>(node)));
}
default: {
ES2PANDA_UNREACHABLE();
}
}
}
ir::Literal *Calculate(ir::UnaryExpression *unary)
{
ES2PANDA_ASSERT(inputs_.size() == 1);
if (unary->OperatorType() == lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK) {
return CreateBooleanLiteral(!TestLiteral(inputs_[0]));
}
auto lit = inputs_[0];
if (lit->IsNumberLiteral()) {
return FoldUnaryNumericConstant(unary, lit->AsNumberLiteral());
}
if (lit->IsBigIntLiteral()) {
return nullptr;
}
LogError(diagnostic::WRONG_OPERAND_TYPE_FOR_UNARY_EXPRESSION, {}, unary->Start());
return nullptr;
}
template <typename OperatorType, typename OperandType>
void PerformArithmeticIntegral(const ir::Expression *expr, OperandType lhs, OperandType rhs, OperandType *res)
{
using Limits = std::numeric_limits<OperandType>;
static_assert(std::is_integral_v<OperandType> && std::is_signed_v<OperandType>);
if constexpr (std::is_same_v<OperatorType, std::divides<>> || std::is_same_v<OperatorType, std::modulus<>>) {
if (rhs == 0) {
LogError(diagnostic::DIVISION_BY_ZERO, {}, expr->Start());
*res = Limits::max();
} else if ((lhs == Limits::min()) && rhs == -1) {
*res = std::is_same_v<OperatorType, std::divides<>> ? Limits::min() : 0;
} else {
*res = OperatorType {}(lhs, rhs);
}
} else {
if constexpr (sizeof(OperandType) >= sizeof(int32_t)) {
if constexpr (std::is_same_v<OperatorType, std::multiplies<>>) {
__builtin_mul_overflow(lhs, rhs, res);
} else if constexpr (std::is_same_v<OperatorType, std::plus<>>) {
__builtin_add_overflow(lhs, rhs, res);
} else if constexpr (std::is_same_v<OperatorType, std::minus<>>) {
__builtin_sub_overflow(lhs, rhs, res);
}
} else {
auto tmpRes = OperatorType {}(static_cast<int32_t>(lhs), static_cast<int32_t>(rhs));
*res = static_cast<OperandType>(tmpRes);
}
}
}
template <typename OperatorType, typename OperandType>
void PerformArithmetic(const ir::Expression *expr, OperandType lhs, OperandType rhs, OperandType *res)
{
if constexpr (std::is_integral_v<OperandType>) {
PerformArithmeticIntegral<OperatorType>(expr, lhs, rhs, res);
return;
}
ES2PANDA_ASSERT(std::is_floating_point_v<OperandType>);
if constexpr (std::is_same_v<OperatorType, std::divides<>>) {
if (rhs == 0) {
*res = lhs == 0 ? std::numeric_limits<OperandType>::quiet_NaN()
: std::copysign(std::numeric_limits<OperandType>::infinity(), lhs / rhs);
} else {
*res = OperatorType {}(lhs, rhs);
}
} else if constexpr (std::is_same_v<OperatorType, std::modulus<>>) {
if (rhs == 0) {
*res = std::numeric_limits<OperandType>::quiet_NaN();
} else {
*res = std::fmod(lhs, rhs);
}
} else {
*res = OperatorType {}(lhs, rhs);
}
}
template <typename TargetType>
ir::Literal *PerformMultiplicativeOperation(TargetType leftNum, TargetType rightNum,
const ir::BinaryExpression *expr)
{
auto opType = expr->OperatorType();
TargetType resNum {};
switch (opType) {
case lexer::TokenType::PUNCTUATOR_MULTIPLY: {
PerformArithmetic<std::multiplies<>>(expr, leftNum, rightNum, &resNum);
break;
}
case lexer::TokenType::PUNCTUATOR_DIVIDE: {
PerformArithmetic<std::divides<>>(expr, leftNum, rightNum, &resNum);
break;
}
case lexer::TokenType::PUNCTUATOR_MOD: {
PerformArithmetic<std::modulus<>>(expr, leftNum, rightNum, &resNum);
break;
}
case lexer::TokenType::PUNCTUATOR_EXPONENTIATION: {
const auto rightNumber = inputs_[1]->AsNumberLiteral()->Number();
if (inputs_[0]->AsNumberLiteral()->Number().IsNegativeZero() && IsNegativeOddInteger(rightNumber)) {
return CreateNumberLiteral(-std::numeric_limits<double>::infinity());
}
if (leftNum < 0 && !rightNumber.IsInteger()) {
resNum = std::numeric_limits<TargetType>::quiet_NaN();
break;
}
return CreateNumberLiteral(std::pow(leftNum, rightNum));
}
default:
ES2PANDA_UNREACHABLE();
}
return CreateNumberLiteral(resNum);
}
ir::Literal *HandleMultiplicativeExpression(const ir::BinaryExpression *expr, const ir::NumberLiteral *left,
const ir::NumberLiteral *right)
{
switch (std::max(left->Number().GetTypeRank(), right->Number().GetTypeRank())) {
case TypeRank::DOUBLE: {
return PerformMultiplicativeOperation(ExtractFromLiteral<double>(left),
ExtractFromLiteral<double>(right), expr);
}
case TypeRank::FLOAT: {
return PerformMultiplicativeOperation(ExtractFromLiteral<float>(left), ExtractFromLiteral<float>(right),
expr);
}
case TypeRank::INT64: {
return PerformMultiplicativeOperation(ExtractFromLiteral<int64_t>(left),
ExtractFromLiteral<int64_t>(right), expr);
}
case TypeRank::INT32: {
return PerformMultiplicativeOperation(ExtractFromLiteral<int32_t>(left),
ExtractFromLiteral<int32_t>(right), expr);
}
case TypeRank::INT16: {
return PerformMultiplicativeOperation(ExtractFromLiteral<int16_t>(left),
ExtractFromLiteral<int16_t>(right), expr);
}
case TypeRank::INT8: {
return PerformMultiplicativeOperation(ExtractFromLiteral<int8_t>(left),
ExtractFromLiteral<int8_t>(right), expr);
}
default:
ES2PANDA_UNREACHABLE();
}
}
template <typename TargetType>
ir::Literal *PerformAdditiveOperation(TargetType left, TargetType right, const ir::BinaryExpression *expr)
{
TargetType res {};
switch (expr->OperatorType()) {
case lexer::TokenType::PUNCTUATOR_PLUS:
PerformArithmetic<std::plus<>>(expr, left, right, &res);
break;
case lexer::TokenType::PUNCTUATOR_MINUS:
PerformArithmetic<std::minus<>>(expr, left, right, &res);
break;
default:
ES2PANDA_UNREACHABLE();
}
return CreateNumberLiteral(res);
}
ir::Literal *HandleNumericAdditiveExpression(const ir::BinaryExpression *expr, const ir::NumberLiteral *left,
const ir::NumberLiteral *right)
{
switch (std::max(left->Number().GetTypeRank(), right->Number().GetTypeRank())) {
case TypeRank::DOUBLE: {
return PerformAdditiveOperation(ExtractFromLiteral<double>(left), ExtractFromLiteral<double>(right),
expr);
}
case TypeRank::FLOAT: {
return PerformAdditiveOperation(ExtractFromLiteral<float>(left), ExtractFromLiteral<float>(right),
expr);
}
case TypeRank::INT64: {
return PerformAdditiveOperation(ExtractFromLiteral<int64_t>(left), ExtractFromLiteral<int64_t>(right),
expr);
}
case TypeRank::INT32: {
return PerformAdditiveOperation(ExtractFromLiteral<int32_t>(left), ExtractFromLiteral<int32_t>(right),
expr);
}
case TypeRank::INT16: {
return PerformAdditiveOperation(ExtractFromLiteral<int16_t>(left), ExtractFromLiteral<int16_t>(right),
expr);
}
case TypeRank::INT8: {
return PerformAdditiveOperation(ExtractFromLiteral<int8_t>(left), ExtractFromLiteral<int8_t>(right),
expr);
}
default:
ES2PANDA_UNREACHABLE();
}
}
ir::Literal *PerformStringAdditiveOperation(const ir::BinaryExpression *expr, const ir::Literal *left,
const ir::Literal *right)
{
if ((expr->OperatorType() != lexer::TokenType::PUNCTUATOR_PLUS) ||
(!left->IsStringLiteral() && !right->IsStringLiteral())) {
LogError(diagnostic::WRONG_OPERAND_TYPE_FOR_BINARY_EXPRESSION, {}, expr->Start());
return nullptr;
}
if (left->IsCharLiteral() || right->IsCharLiteral()) {
LogError(diagnostic::CHAR_TO_STR_CONVERSION, {}, expr->Start());
return nullptr;
}
std::string tmpStr {};
auto appendLiteral = [&tmpStr](const ir::Literal *lit) {
if (lit->IsStringLiteral()) {
tmpStr += lit->AsStringLiteral()->Str().Utf8();
} else {
tmpStr += lit->ToString();
}
};
appendLiteral(left);
appendLiteral(right);
auto resStr = util::UString(tmpStr, context_->allocator);
return util::NodeAllocator::Alloc<ir::StringLiteral>(context_->allocator, resStr.View());
}
template <typename SignedType>
ir::Literal *PerformShiftOperation(SignedType left, SignedType right, lexer::TokenType opType)
{
using UnsignedType = std::make_unsigned_t<SignedType>;
auto uLeft = bit_cast<UnsignedType, SignedType>(left);
auto uRight = bit_cast<UnsignedType, SignedType>(right);
auto mask = std::numeric_limits<UnsignedType>::digits - 1U;
UnsignedType shift = uRight & mask;
SignedType res {};
switch (opType) {
case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: {
static_assert(sizeof(UnsignedType) == 4 || sizeof(UnsignedType) == 8);
res = bit_cast<SignedType, UnsignedType>(uLeft << shift);
break;
}
case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: {
static_assert(sizeof(SignedType) == 4 || sizeof(SignedType) == 8);
res = bit_cast<SignedType, UnsignedType>(left >> shift);
break;
}
case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: {
static_assert(sizeof(UnsignedType) == 4 || sizeof(UnsignedType) == 8);
res = bit_cast<SignedType, UnsignedType>(uLeft >> shift);
break;
}
default:
ES2PANDA_UNREACHABLE();
}
return CreateNumberLiteral(res);
}
ir::Literal *HandleShiftExpression(const ir::BinaryExpression *expr, const ir::NumberLiteral *left,
const ir::NumberLiteral *right)
{
auto opType = expr->OperatorType();
switch (std::max(left->Number().GetTypeRank(), right->Number().GetTypeRank())) {
case TypeRank::DOUBLE:
case TypeRank::INT64: {
return PerformShiftOperation(ExtractFromLiteral<int64_t>(left), ExtractFromLiteral<int64_t>(right),
opType);
}
case TypeRank::FLOAT:
case TypeRank::INT32:
case TypeRank::INT16:
case TypeRank::INT8: {
return PerformShiftOperation(ExtractFromLiteral<int32_t>(left), ExtractFromLiteral<int32_t>(right),
opType);
}
default:
ES2PANDA_UNREACHABLE();
}
}
ir::Literal *PerformRelationOperation(const ir::CharLiteral *left, const ir::CharLiteral *right,
lexer::TokenType opType, const ir::BinaryExpression *expr)
{
auto leftVal = left->Char();
auto rightVal = right->Char();
bool res {};
bool reportEqualityDiagnostic = false;
switch (opType) {
case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL:
case lexer::TokenType::PUNCTUATOR_EQUAL: {
res = leftVal == rightVal;
reportEqualityDiagnostic = true;
break;
}
case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL:
case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: {
res = leftVal != rightVal;
reportEqualityDiagnostic = true;
break;
}
case lexer::TokenType::PUNCTUATOR_GREATER_THAN: {
res = leftVal > rightVal;
break;
}
case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
res = leftVal >= rightVal;
break;
}
case lexer::TokenType::PUNCTUATOR_LESS_THAN: {
res = leftVal < rightVal;
break;
}
case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: {
res = leftVal <= rightVal;
break;
}
default: {
return nullptr;
}
}
auto *result = CreateBooleanLiteral(res);
if (reportEqualityDiagnostic) {
LogError(res ? diagnostic::EQUALITY_EXPRESSION_ALWAYS_TRUE : diagnostic::EQUALITY_EXPRESSION_ALWAYS_FALSE,
{}, expr->Start());
}
return result;
}
template <typename InputType>
auto PerformRelationOperation(InputType left, InputType right, lexer::TokenType opType,
const ir::BinaryExpression *expr)
{
bool res {};
bool reportEqualityDiagnostic = false;
switch (opType) {
case lexer::TokenType::PUNCTUATOR_GREATER_THAN: {
res = left > right;
break;
}
case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
res = left >= right;
break;
}
case lexer::TokenType::PUNCTUATOR_LESS_THAN: {
res = left < right;
break;
}
case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: {
res = left <= right;
break;
}
case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL:
case lexer::TokenType::PUNCTUATOR_EQUAL: {
res = left == right;
reportEqualityDiagnostic = true;
break;
}
case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL:
case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: {
res = left != right;
reportEqualityDiagnostic = true;
break;
}
default: {
ES2PANDA_UNREACHABLE();
}
}
auto *result = CreateBooleanLiteral(res);
if (reportEqualityDiagnostic) {
LogError(res ? diagnostic::EQUALITY_EXPRESSION_ALWAYS_TRUE : diagnostic::EQUALITY_EXPRESSION_ALWAYS_FALSE,
{}, expr->Start());
}
return result;
}
ir::Literal *HandleNumericalRelationalExpression(const ir::BinaryExpression *expr, const ir::NumberLiteral *left,
const ir::NumberLiteral *right)
{
auto opType = expr->OperatorType();
switch (std::max(left->Number().GetTypeRank(), right->Number().GetTypeRank())) {
case TypeRank::DOUBLE: {
return PerformRelationOperation(ExtractFromLiteral<double>(left), ExtractFromLiteral<double>(right),
opType, expr);
}
case TypeRank::FLOAT: {
return PerformRelationOperation(ExtractFromLiteral<float>(left), ExtractFromLiteral<float>(right),
opType, expr);
}
case TypeRank::INT64: {
return PerformRelationOperation(ExtractFromLiteral<int64_t>(left), ExtractFromLiteral<int64_t>(right),
opType, expr);
}
case TypeRank::INT32: {
return PerformRelationOperation(ExtractFromLiteral<int32_t>(left), ExtractFromLiteral<int32_t>(right),
opType, expr);
}
case TypeRank::INT16: {
return PerformRelationOperation(ExtractFromLiteral<int16_t>(left), ExtractFromLiteral<int16_t>(right),
opType, expr);
}
case TypeRank::INT8: {
return PerformRelationOperation(ExtractFromLiteral<int8_t>(left), ExtractFromLiteral<int8_t>(right),
opType, expr);
}
default: {
ES2PANDA_UNREACHABLE();
}
}
}
ir::Literal *HandleNonNumericRelationalExpression(const ir::BinaryExpression *expr, const ir::Literal *left,
const ir::Literal *right)
{
auto opType = expr->OperatorType();
if (left->IsStringLiteral() && right->IsStringLiteral()) {
return PerformRelationOperation(left->AsStringLiteral()->Str(), right->AsStringLiteral()->Str(), opType,
expr);
}
if (left->IsBooleanLiteral() && right->IsBooleanLiteral()) {
return PerformRelationOperation(GetVal<bool>(left), GetVal<bool>(right), opType, expr);
}
if (left->IsCharLiteral() && right->IsCharLiteral()) {
auto res = PerformRelationOperation(left->AsCharLiteral(), right->AsCharLiteral(), opType, expr);
if (res != nullptr) {
return res;
}
}
if (left->IsCharLiteral() && right->IsNumberLiteral()) {
auto leftVal = static_cast<double>(left->AsCharLiteral()->Char());
return PerformRelationOperation(leftVal, ExtractFromLiteral<double>(right->AsNumberLiteral()), opType,
expr);
}
if (left->IsNumberLiteral() && right->IsCharLiteral()) {
auto rightVal = static_cast<double>(right->AsCharLiteral()->Char());
return PerformRelationOperation(ExtractFromLiteral<double>(left->AsNumberLiteral()), rightVal, opType,
expr);
}
LogError(diagnostic::WRONG_OPERAND_TYPE_FOR_BINARY_EXPRESSION, {}, expr->Start());
return nullptr;
}
template <typename SignedType>
ir::Literal *PerformBitwiseLogicalOperation(SignedType left, SignedType right, lexer::TokenType opType)
{
using UnsignedType = std::make_unsigned_t<SignedType>;
auto uLeft = bit_cast<UnsignedType, SignedType>(left);
auto uRight = bit_cast<UnsignedType, SignedType>(right);
SignedType res {};
switch (opType) {
case lexer::TokenType::PUNCTUATOR_BITWISE_AND: {
res = uLeft & uRight;
break;
}
case lexer::TokenType::PUNCTUATOR_BITWISE_OR: {
res = uLeft | uRight;
break;
}
case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: {
res = uLeft ^ uRight;
break;
}
default:
ES2PANDA_UNREACHABLE();
}
return CreateNumberLiteral(res);
}
ir::Literal *HandleNumericBitwiseLogicalExpression(const ir::BinaryExpression *expr, const ir::NumberLiteral *left,
const ir::NumberLiteral *right)
{
auto opType = expr->OperatorType();
switch (std::max(left->Number().GetTypeRank(), right->Number().GetTypeRank())) {
case TypeRank::DOUBLE: {
return PerformBitwiseLogicalOperation(ExtractFromLiteral<int64_t>(left),
ExtractFromLiteral<int64_t>(right), opType);
}
case TypeRank::INT64: {
return PerformBitwiseLogicalOperation(ExtractFromLiteral<int64_t>(left),
ExtractFromLiteral<int64_t>(right), opType);
}
case TypeRank::FLOAT:
case TypeRank::INT32:
case TypeRank::INT16:
case TypeRank::INT8: {
return PerformBitwiseLogicalOperation(ExtractFromLiteral<int32_t>(left),
ExtractFromLiteral<int32_t>(right), opType);
}
default:
ES2PANDA_UNREACHABLE();
}
}
ir::Literal *HandleNonNumericBitwiseLogicalExpression(const ir::BinaryExpression *expr, const ir::Literal *left,
const ir::Literal *right)
{
auto opType = expr->OperatorType();
if (!left->IsBooleanLiteral() || !right->IsBooleanLiteral()) {
LogError(diagnostic::WRONG_OPERAND_TYPE_FOR_BINARY_EXPRESSION, {}, expr->Start());
return nullptr;
}
bool res = false;
auto leftVal = left->AsBooleanLiteral()->Value();
auto rightVal = right->AsBooleanLiteral()->Value();
switch (opType) {
case lexer::TokenType::PUNCTUATOR_BITWISE_AND: {
res = leftVal && rightVal;
break;
}
case lexer::TokenType::PUNCTUATOR_BITWISE_OR: {
res = leftVal || rightVal;
break;
}
case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: {
res = leftVal ^ rightVal;
break;
}
default:
ES2PANDA_UNREACHABLE();
}
return CreateBooleanLiteral(res);
}
ir::Literal *HandleBinaryExpression(const ir::BinaryExpression *expr, ir::Literal *left, ir::Literal *right)
{
if (left->IsNumberLiteral() && right->IsNumberLiteral()) {
auto leftN = left->AsNumberLiteral();
auto rightN = right->AsNumberLiteral();
if (IsBitwiseLogicalExpression(expr)) {
return HandleNumericBitwiseLogicalExpression(expr, leftN, rightN);
}
if (IsMultiplicativeExpression(expr)) {
return HandleMultiplicativeExpression(expr, leftN, rightN);
}
if (IsAdditiveExpression(expr)) {
return HandleNumericAdditiveExpression(expr, leftN, rightN);
}
if (IsShiftExpression(expr)) {
return HandleShiftExpression(expr, leftN, rightN);
}
if (IsRelationalExpression(expr)) {
return HandleNumericalRelationalExpression(expr, leftN, rightN);
}
} else {
if (IsAdditiveExpression(expr)) {
return PerformStringAdditiveOperation(expr, left, right);
}
if (IsBitwiseLogicalExpression(expr)) {
return HandleNonNumericBitwiseLogicalExpression(expr, left, right);
}
if (IsRelationalExpression(expr)) {
return HandleNonNumericRelationalExpression(expr, left, right);
}
}
return nullptr;
}
ir::Literal *HandleLogicalExpression(const ir::BinaryExpression *expr, ir::Literal *left, ir::Literal *right)
{
auto allocator = context_->allocator;
auto parent = const_cast<ir::BinaryExpression *>(expr)->Parent();
bool lhs = TestLiteral(left);
auto opType = expr->OperatorType();
switch (opType) {
case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: {
if (lhs) {
return right->Clone(allocator, parent)->AsExpression()->AsLiteral();
}
return left->Clone(allocator, parent)->AsExpression()->AsLiteral();
}
case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
if (lhs) {
return left->Clone(allocator, parent)->AsExpression()->AsLiteral();
}
return right->Clone(allocator, parent)->AsExpression()->AsLiteral();
}
default: {
ES2PANDA_UNREACHABLE();
}
}
ES2PANDA_UNREACHABLE();
}
ir::Literal *Calculate(const ir::BinaryExpression *expr)
{
ES2PANDA_ASSERT(inputs_.size() == 2U);
auto left = inputs_[0];
auto right = inputs_[1];
if (IsLogicalExpression(expr)) {
return HandleLogicalExpression(expr, left, right);
}
if (left->IsBigIntLiteral() && right->IsBigIntLiteral() && IsEqualityExpression(expr)) {
auto *leftNum = TryNumberLiteralFromBigIntToken(context_->allocator, left->AsBigIntLiteral()->Str());
auto *rightNum = TryNumberLiteralFromBigIntToken(context_->allocator, right->AsBigIntLiteral()->Str());
if ((leftNum != nullptr) && (rightNum != nullptr)) {
return HandleNumericalRelationalExpression(expr, leftNum, rightNum);
}
auto leftBig = ParseBigIntLiteral(left->AsBigIntLiteral()->Str());
auto rightBig = ParseBigIntLiteral(right->AsBigIntLiteral()->Str());
if (leftBig.has_value() && rightBig.has_value()) {
const auto cmp = CompareBigInt(*leftBig, *rightBig);
return PerformRelationOperation(cmp, 0, expr->OperatorType(), expr);
}
}
if (left->IsBigIntLiteral() || right->IsBigIntLiteral()) {
return nullptr;
}
if (auto *res = HandleBinaryExpression(expr, left, right); res != nullptr) {
return res;
}
LogError(diagnostic::WRONG_OPERAND_TYPE_FOR_BINARY_EXPRESSION, {}, expr->Start());
return nullptr;
}
void LogError(const diagnostic::DiagnosticKind &diagnostic, const util::DiagnosticMessageParams &diagnosticParams,
const lexer::SourcePosition &pos)
{
context_->diagnosticEngine->LogDiagnostic(diagnostic, diagnosticParams, pos);
}
ir::BooleanLiteral *CreateBooleanLiteral(bool val)
{
auto resNode = util::NodeAllocator::Alloc<ir::BooleanLiteral>(context_->allocator, val);
ES2PANDA_ASSERT(resNode != nullptr);
resNode->SetFolded();
return resNode;
}
template <typename T>
ir::NumberLiteral *CreateNumberLiteral(T val)
{
auto resNum = lexer::Number(val);
return CreateNumberLiteral(resNum);
}
ir::NumberLiteral *CreateNumberLiteral(lexer::Number number)
{
auto *resNode = util::NodeAllocator::Alloc<ir::NumberLiteral>(context_->allocator, number);
ES2PANDA_ASSERT(resNode != nullptr);
if (resNode->Number().IsFloat()) {
SetFloatLiteralStringRepresentation(resNode);
} else {
resNode->Number().SetStr(util::UString(resNode->ToString(), context_->allocator).View());
}
resNode->SetFolded();
return resNode;
}
private:
public_lib::Context *context_;
SmallVector<ir::Literal *, 3U> inputs_;
};
template <typename T>
static bool TryCastInteger(lexer::Number &number)
{
if (number.Is<T>()) {
return true;
}
if (number.CanGetValue<T>()) {
const bool preserveNegativeZero = number.IsNegativeZero();
number = lexer::Number(number.GetValue<T>());
if (preserveNegativeZero && number.IsZero()) {
number.SetNegativeZero(true);
}
return true;
}
return false;
}
static ir::PrimitiveType TryExtractPrimitiveType(ir::TypeNode *constraint)
{
if (constraint->IsETSPrimitiveType()) {
return constraint->AsETSPrimitiveType()->GetPrimitiveType();
}
if (auto typeRef = Cast<ir::ETSTypeReference>(constraint); typeRef != nullptr) {
if (auto part = typeRef->Part(); part->Name()->IsIdentifier() && (part->Previous() == nullptr)) {
const static std::map<std::string_view, ir::PrimitiveType> MAP {
{"Number", ir::PrimitiveType::DOUBLE}, {"number", ir::PrimitiveType::DOUBLE},
{"Double", ir::PrimitiveType::DOUBLE}, {"Float", ir::PrimitiveType::FLOAT},
{"Long", ir::PrimitiveType::LONG}, {"Int", ir::PrimitiveType::INT},
{"Short", ir::PrimitiveType::SHORT}, {"Char", ir::PrimitiveType::CHAR},
{"Byte", ir::PrimitiveType::BYTE}, {"Boolean", ir::PrimitiveType::BOOLEAN},
};
if (auto it = MAP.find(part->Name()->AsIdentifier()->Name().Utf8()); it != MAP.end()) {
return it->second;
}
}
}
return ir::PrimitiveType::VOID;
}
static void LogErrorUnconverted(ir::PrimitiveType dst, ir::PrimitiveType src, util::DiagnosticEngine *de,
lexer::SourcePosition pos)
{
if ((dst == ir::PrimitiveType::FLOAT) && (src == ir::PrimitiveType::DOUBLE)) {
de->LogDiagnostic(diagnostic::CONSTANT_FLOATING_POINT_COVERSION, util::DiagnosticMessageParams {}, pos);
} else if (((dst != ir::PrimitiveType::FLOAT) && (dst != ir::PrimitiveType::DOUBLE)) &&
((src == ir::PrimitiveType::FLOAT) || (src == ir::PrimitiveType::DOUBLE))) {
de->LogDiagnostic(diagnostic::CONSTANT_FLOATING_POINT_COVERSION, util::DiagnosticMessageParams {}, pos);
} else {
de->LogDiagnostic(diagnostic::CONSTANT_VALUE_OUT_OF_RANGE, util::DiagnosticMessageParams {}, pos);
}
}
template <bool EXPLICIT>
static bool TryCastNumber(lexer::Number &number, ir::PrimitiveType dst)
{
bool converted = false;
switch (dst) {
case ir::PrimitiveType::DOUBLE:
converted = (!EXPLICIT && number.IsReal()) || TryCastInteger<double>(number);
break;
case ir::PrimitiveType::FLOAT:
converted = (!EXPLICIT && number.IsReal()) || TryCastInteger<float>(number);
break;
case ir::PrimitiveType::LONG:
converted = TryCastInteger<int64_t>(number);
break;
case ir::PrimitiveType::INT:
converted = TryCastInteger<int32_t>(number);
break;
case ir::PrimitiveType::SHORT:
converted = TryCastInteger<int16_t>(number);
break;
case ir::PrimitiveType::BYTE:
converted = TryCastInteger<int8_t>(number);
break;
default:
ES2PANDA_UNREACHABLE();
}
return converted;
}
template <bool EXPLICIT>
static bool CheckCastLiteral(util::DiagnosticEngine *de, ir::TypeNode *constraint, ir::Literal *literal)
{
if (literal->IsStringLiteral()) {
return true;
}
if (literal->IsBigIntLiteral()) {
if (constraint->IsTSBigintKeyword()) {
return true;
}
if (auto typeRef = Cast<ir::ETSTypeReference>(constraint); typeRef != nullptr) {
if (auto part = typeRef->Part(); part->Name()->IsIdentifier() && (part->Previous() == nullptr)) {
auto name = part->Name()->AsIdentifier()->Name().Utf8();
return name == "bigint" || name == "BigInt";
}
}
return false;
}
auto dst = TryExtractPrimitiveType(constraint);
if (dst == ir::PrimitiveType::VOID) {
return false;
}
if (literal->IsBooleanLiteral() || literal->IsCharLiteral() || (dst == ir::PrimitiveType::BOOLEAN) ||
(dst == ir::PrimitiveType::CHAR)) {
return (literal->IsBooleanLiteral() && (dst == ir::PrimitiveType::BOOLEAN)) ||
(literal->IsCharLiteral() && (dst == ir::PrimitiveType::CHAR));
}
auto &number = literal->AsNumberLiteral()->Number();
bool converted = TryCastNumber<EXPLICIT>(number, dst);
if (!converted && !EXPLICIT) {
LogErrorUnconverted(dst, GetPrimitiveType(number), de, constraint->Start());
}
return converted;
}
template <bool EXPLICIT>
static bool CheckCastNumber(util::DiagnosticEngine *de, ir::TypeNode *constraint, lexer::Number &number)
{
auto dst = TryExtractPrimitiveType(constraint);
if (dst == ir::PrimitiveType::VOID) {
return false;
}
if (dst == ir::PrimitiveType::BOOLEAN || dst == ir::PrimitiveType::CHAR) {
return false;
}
bool converted = TryCastNumber<EXPLICIT>(number, dst);
if (!converted && !EXPLICIT) {
LogErrorUnconverted(dst, GetPrimitiveType(number), de, constraint->Start());
}
return converted;
}
static ir::TypeNode *GetTypeAnnotation(ir::AstNode *initParent)
{
if (auto prop = Cast<ir::ClassProperty>(initParent); prop != nullptr) {
ES2PANDA_ASSERT(prop->Key()->AsIdentifier()->TypeAnnotation() == nullptr);
return prop->TypeAnnotation();
}
if (auto vardecl = Cast<ir::VariableDeclarator>(initParent);
(vardecl != nullptr) && vardecl->Id()->IsIdentifier()) {
return vardecl->Id()->AsIdentifier()->TypeAnnotation();
}
if (auto member = Cast<ir::TSEnumMember>(initParent); member != nullptr) {
return member->Parent()->AsTSEnumDeclaration()->TypeAnnotation();
}
return nullptr;
}
bool ConstantExpressionLoweringImpl::CalculateAndCheck(DAGNode *user)
{
auto *inputsIds = user->InputsIds();
NodeCalculator nc {context_, inputsIds->size()};
size_t i = 0;
for (auto inputId : *inputsIds) {
nc.SetInput(DNode(inputId)->Ir()->AsExpression()->AsLiteral(), i++);
}
auto *res = nc.Calculate(user);
ES2PANDA_ASSERT(!res || res->IsLiteral());
if (auto constr = GetTypeAnnotation(user->Ir()->Parent());
(res == nullptr) || ((constr != nullptr) && !CheckCastLiteral(context_->diagnosticEngine, constr, res))) {
user->UsersIds()->clear();
return false;
}
RegisterReplacement(user, res);
return true;
}
ir::Literal *NodeCalculator::Calculate(DAGNode *node)
{
switch (node->Ir()->Type()) {
case ir::AstNodeType::IDENTIFIER:
case ir::AstNodeType::MEMBER_EXPRESSION:
return SubstituteConstant();
case ir::AstNodeType::TS_AS_EXPRESSION:
return Calculate(node->Ir()->AsTSAsExpression());
case ir::AstNodeType::CONDITIONAL_EXPRESSION:
return SubstituteConstantConditionally();
case ir::AstNodeType::UNARY_EXPRESSION:
return Calculate(node->Ir()->AsUnaryExpression());
case ir::AstNodeType::BINARY_EXPRESSION:
return Calculate(node->Ir()->AsBinaryExpression());
case ir::AstNodeType::TEMPLATE_LITERAL:
return Calculate(node->Ir()->AsTemplateLiteral());
default:
ES2PANDA_UNREACHABLE();
}
}
static ir::Expression *ExtendIdentToQualifiedName(ir::Identifier *ident)
{
if (ident == nullptr) {
return nullptr;
}
ir::Expression *rvnode = ident;
if (auto mexp = Cast<ir::MemberExpression>(ident->Parent()); mexp != nullptr) {
if (mexp->Property() != ident) {
return nullptr;
}
if (mexp->Kind() == ir::MemberExpressionKind::PROPERTY_ACCESS) {
if (auto mexpPar = Cast<ir::MemberExpression>(mexp->Parent());
(mexpPar != nullptr) && (mexpPar->Object() == mexp)) {
return nullptr;
}
rvnode = mexp;
}
}
return rvnode;
}
static bool IsFoldableParent(ir::AstNode *parent, ir::Expression *rvnode)
{
if (parent->IsUnaryExpression() || parent->IsBinaryExpression() || parent->IsConditionalExpression()) {
return true;
}
if (auto vardecl = Cast<ir::VariableDeclarator>(parent); (vardecl != nullptr) && vardecl->Init() == rvnode) {
return true;
}
if (auto propdecl = Cast<ir::ClassProperty>(parent); (propdecl != nullptr) && propdecl->Value() == rvnode) {
return true;
}
if (auto enummemb = Cast<ir::TSEnumMember>(parent); (enummemb != nullptr) && enummemb->Init() == rvnode) {
return true;
}
if (auto casestmt = Cast<ir::SwitchCaseStatement>(parent); (casestmt != nullptr) && casestmt->Test() == rvnode) {
return true;
}
if (auto assignexp = Cast<ir::AssignmentExpression>(parent);
(assignexp != nullptr) && assignexp->Right() == rvnode) {
return true;
}
if (auto asExpr = Cast<ir::TSAsExpression>(parent); (asExpr != nullptr) && asExpr->Expr() == rvnode) {
return true;
}
return false;
}
static ir::Expression *AsRValue(ir::Identifier *ident)
{
ir::Expression *rvnode = ExtendIdentToQualifiedName(ident);
if (rvnode == nullptr) {
return nullptr;
}
auto parent = rvnode->Parent();
ES2PANDA_ASSERT(parent != nullptr);
if (IsFoldableParent(parent, rvnode)) {
return rvnode;
}
auto isIn = [rvnode](auto &args) { return std::find(args.begin(), args.end(), rvnode) != args.end(); };
if (auto callexp = Cast<ir::CallExpression>(parent); (callexp != nullptr) && isIn(callexp->Arguments())) {
return rvnode;
}
if (auto arrexp = Cast<ir::ArrayExpression>(parent); (arrexp != nullptr) && isIn(arrexp->Elements())) {
return rvnode;
}
if (auto newarr = Cast<ir::ETSNewArrayInstanceExpression>(parent);
(newarr != nullptr) && (newarr->Dimension() == rvnode)) {
return rvnode;
}
if (auto ar = Cast<ir::ETSNewMultiDimArrayInstanceExpression>(parent); (ar != nullptr) && isIn(ar->Dimensions())) {
return rvnode;
}
if (auto cls = Cast<ir::ETSNewClassInstanceExpression>(parent); (cls != nullptr) && isIn(cls->GetArguments())) {
return rvnode;
}
if (auto indexexp = Cast<ir::MemberExpression>(parent);
(indexexp != nullptr) && (indexexp->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) &&
indexexp->Property() == rvnode) {
return rvnode;
}
return nullptr;
}
static bool IsValidScopeVarResult(const ir::Identifier *ident, varbinder::Variable *var)
{
return ident->Parent()->IsMemberExpression() || !var->Declaration()->Node()->IsClassProperty() ||
!var->GetScope()->Node()->IsClassDefinition() || var->GetScope()->Node()->AsClassDefinition()->IsGlobal();
}
static varbinder::Variable *ResolveIdentifier(const ir::Identifier *ident)
{
if (ident->Variable() != nullptr) {
return ident->Variable();
}
static constexpr varbinder::ResolveBindingOptions const option =
varbinder::ResolveBindingOptions::ALL_DECLARATION | varbinder::ResolveBindingOptions::ALL_VARIABLES;
varbinder::Scope *scope = NearestScope(ident);
do {
varbinder::Variable *res = scope->Find(ident->Name(), option).variable;
if (res != nullptr && res->GetScope() != nullptr && res->Declaration() != nullptr &&
IsValidScopeVarResult(ident, res)) {
return res;
}
scope = scope->Parent();
} while (scope != nullptr);
return nullptr;
}
static bool AllowedEnumLiteralFoldingPosition(const ir::MemberExpression *enumLiteral)
{
auto anotherEnumLitDecl = util::Helpers::FindAncestorGivenByType(enumLiteral, ir::AstNodeType::TS_ENUM_MEMBER);
ES2PANDA_ASSERT((anotherEnumLitDecl == nullptr) || (anotherEnumLitDecl->AsTSEnumMember()->Key() != enumLiteral));
return anotherEnumLitDecl != nullptr;
}
static varbinder::Variable *ResolveMemberExpressionProperty(const ir::MemberExpression *me)
{
varbinder::Variable *var = nullptr;
auto meObject = me->Object();
if (meObject->IsMemberExpression()) {
var = ResolveMemberExpressionProperty(meObject->AsMemberExpression());
} else if (meObject->IsIdentifier()) {
var = ResolveIdentifier(meObject->AsIdentifier());
}
if (var == nullptr) {
return nullptr;
}
auto decl = var->Declaration();
varbinder::LocalScope *scope = nullptr;
if (decl->IsClassDecl()) {
auto *declNode = decl->AsClassDecl()->Node();
auto *classDef = declNode->IsClassDefinition() ? declNode->AsClassDefinition()
: declNode->IsClassDeclaration() ? declNode->AsClassDeclaration()->Definition()
: nullptr;
ES2PANDA_ASSERT(classDef != nullptr);
scope = classDef->Scope();
} else if (decl->IsEnumLiteralDecl() && AllowedEnumLiteralFoldingPosition(me)) {
scope = decl->AsEnumLiteralDecl()->Node()->AsTSEnumDeclaration()->Scope();
} else {
return nullptr;
}
if (!me->Property()->IsIdentifier()) {
return nullptr;
}
auto option =
varbinder::ResolveBindingOptions::STATIC_DECLARATION | varbinder::ResolveBindingOptions::STATIC_VARIABLES;
return scope->FindLocal(me->Property()->AsIdentifier()->Name(), option);
}
static varbinder::Variable *Resolve(const ir::Expression *identOrMexp)
{
if (identOrMexp->IsIdentifier()) {
return ResolveIdentifier(identOrMexp->AsIdentifier());
}
return ResolveMemberExpressionProperty(identOrMexp->AsMemberExpression());
}
static bool AllowConstSubstitution(const varbinder::Variable *var)
{
if (var == nullptr) {
return false;
}
auto *scope = var->GetScope();
if (scope == nullptr) {
return false;
}
auto *scopeNode = scope->Node();
if (scopeNode == nullptr) {
return false;
}
const bool isLocalScope = scopeNode->IsScriptFunction() || scopeNode->IsBlockStatement();
if (!isLocalScope) {
return false;
}
return true;
}
static bool IsInitializedBeforeUse(const ir::AstNode *declContainer, const ir::Expression *useSite)
{
ES2PANDA_ASSERT(declContainer != nullptr);
ES2PANDA_ASSERT(useSite != nullptr);
const auto declEnd = declContainer->End();
const auto useStart = useSite->Start();
if (declEnd.Program() == nullptr || useStart.Program() == nullptr || declEnd.Program() != useStart.Program()) {
return false;
}
return declEnd.index <= useStart.index;
}
static bool CheckPrivateAccess(ir::ClassProperty *propDecl, ir::Expression *rval)
{
ES2PANDA_ASSERT(propDecl->IsPrivateElement());
auto *const cls = propDecl->Parent();
auto *pred = rval->Parent();
while (pred != nullptr) {
if (pred == cls) {
return true;
}
pred = pred->Parent();
}
return false;
}
static ir::Expression *GetConstSubstitutionInitializer(varbinder::Decl *decl, const varbinder::Variable *var,
ir::Expression *useSite)
{
if ((!decl->IsConstDecl() && !decl->IsReadonlyDecl()) || !AllowConstSubstitution(var)) {
return nullptr;
}
auto *declNode = decl->Node();
if (auto *vardecl = Cast<ir::VariableDeclarator>(declNode->Parent());
vardecl != nullptr && IsInitializedBeforeUse(vardecl, useSite)) {
return vardecl->Init();
}
auto *prop = Cast<ir::ClassProperty>(declNode);
if (prop != nullptr && !prop->IsPrivateElement() && CheckPrivateAccess(prop, useSite)) {
return prop->Value();
}
return nullptr;
}
void ConstantExpressionLoweringImpl::PopulateDAGs(ir::Expression *node)
{
if (auto literal = AsSupportedLiteral(node); literal != nullptr) {
if (auto constr = GetTypeAnnotation(node->Parent());
(constr == nullptr) || CheckCastLiteral(context_->diagnosticEngine, constr, literal)) {
AddRootDNode(literal);
}
} else if (auto numLiteral = Cast<ir::NumberLiteral>(node); numLiteral != nullptr) {
ES2PANDA_ASSERT(numLiteral->Number().ConversionError());
AddDNodeToPretransform(numLiteral);
} else if (auto identOrMExp = AsRValue(Cast<ir::Identifier>(node)); identOrMExp != nullptr) {
auto *var = Resolve(identOrMExp);
auto *decl = (var != nullptr) ? var->Declaration() : nullptr;
if (decl == nullptr || decl->Node() == nullptr) {
return;
}
auto *declNode = decl->Node();
ir::Expression *init = nullptr;
if (auto *enumMember = Cast<ir::TSEnumMember>(declNode); enumMember != nullptr) {
init = enumMember->Init();
} else {
init = GetConstSubstitutionInitializer(decl, var, identOrMExp);
}
if (init != nullptr) {
HandleUndefinedInLogicalExpression(context_, node, init);
AddDNode(identOrMExp, init);
}
} else if (auto tmpl = Cast<ir::TemplateLiteral>(node); tmpl) {
if (tmpl->Expressions().empty()) {
AddDNodeToPretransform(tmpl);
} else {
AddDNode(tmpl, tmpl->Expressions());
}
} else if (auto asExpr = Cast<ir::TSAsExpression>(node); asExpr != nullptr) {
AddDNode(asExpr, asExpr->Expr());
} else if (auto unop = Cast<ir::UnaryExpression>(node); unop != nullptr) {
AddDNode(unop, unop->Argument());
} else if (auto binop = Cast<ir::BinaryExpression>(node); binop != nullptr) {
AddDNode(binop, {binop->Left(), binop->Right()});
} else if (auto condexpr = Cast<ir::ConditionalExpression>(node); condexpr != nullptr) {
AddDNode(condexpr, {condexpr->Test(), condexpr->Consequent(), condexpr->Alternate()});
}
}
static void CheckInitializerInPackage(public_lib::Context *context, ir::AstNode *node)
{
auto log = [context](const auto &kind, auto pos) {
context->diagnosticEngine->LogDiagnostic(kind, util::DiagnosticMessageParams {}, pos);
};
switch (node->Type()) {
case ir::AstNodeType::EXPRESSION_STATEMENT: {
auto assign = Cast<ir::AssignmentExpression>(node->AsExpressionStatement()->GetExpression());
if (assign == nullptr) {
return;
}
auto initTobeChecked = assign->Right();
if ((initTobeChecked != nullptr) && (AsSupportedLiteral(initTobeChecked) == nullptr)) {
log(diagnostic::INVALID_INIT_IN_PACKAGE, initTobeChecked->Start());
}
return;
}
case ir::AstNodeType::CLASS_PROPERTY: {
if (auto init = node->AsClassProperty()->Value();
(init != nullptr) && (AsSupportedLiteral(init) == nullptr)) {
log(diagnostic::INVALID_INIT_IN_PACKAGE, init->Start());
}
return;
}
default:
return;
}
}
static void PostCheckGlobalIfPackage(public_lib::Context *context, ir::ClassDefinition *globalClass)
{
for (auto element : globalClass->Body()) {
if (element->IsMethodDefinition()) {
auto const *classMethod = element->AsMethodDefinition();
if (!classMethod->Key()->IsIdentifier() ||
!classMethod->Key()->AsIdentifier()->Name().Is(compiler::Signatures::INIT_METHOD)) {
continue;
}
auto const *methodBody = classMethod->Value()->AsFunctionExpression()->Function()->Body();
if (methodBody == nullptr || !methodBody->IsBlockStatement()) {
continue;
}
auto const &initStatements = methodBody->AsBlockStatement()->Statements();
std::for_each(initStatements.begin(), initStatements.end(),
[context](ir::AstNode *node) { CheckInitializerInPackage(context, node); });
}
if (element->IsClassProperty() && element->AsClassProperty()->IsConst() &&
!element->AsClassProperty()->NeedInitInStaticBlock()) {
CheckInitializerInPackage(context, element);
}
}
}
bool ConstantExpressionLoweringImpl::PerformForProgram(parser::Program *program, std::string_view name)
{
program->Ast()->IterateRecursively([this](ir::AstNode *node) {
if (node->IsExpression()) {
PopulateDAGs(node->AsExpression());
}
});
Pretransform();
while (PerformStep()) {
}
program->Ast()->TransformChildrenRecursivelyPreorder(
[this](ir::AstNode *node) -> ir::AstNode * {
if (!node->IsExpression()) {
return node;
}
auto expr = node->AsExpression();
if (replacements_.find(expr) == replacements_.end()) {
return node;
}
auto folded = replacements_[expr];
folded->SetParent(expr->Parent());
folded->SetRange(expr->Range());
if (folded->IsNumberLiteral()) {
folded->AsNumberLiteral()->SetFolded();
}
return folded;
},
name);
if (program->Is<util::ModuleKind::PACKAGE>()) {
PostCheckGlobalIfPackage(context_, program->GlobalClass());
}
return true;
}
}