diff --git a/es2panda/binder/binder.cpp b/es2panda/binder/binder.cpp index ebf25d0f8b391bdbd9b0087a298b13e2b9377a0c..3514ac496b8c9556868c29996e13268a0b8fd311 100644 --- a/es2panda/binder/binder.cpp +++ b/es2panda/binder/binder.cpp @@ -395,7 +395,7 @@ void Binder::BuildScriptFunction(Scope *outerScope, const ir::ScriptFunction *sc } ASSERT(scope_->IsFunctionScope() || scope_->IsTSModuleScope() || scope_->IsTSEnumScope()); - BuildFunction(scope_->AsFunctionVariableScope(), util::Helpers::FunctionName(scriptFunc), scriptFunc); + BuildFunction(scope_->AsFunctionVariableScope(), util::Helpers::FunctionName(Allocator(), scriptFunc), scriptFunc); } void Binder::BuildVarDeclaratorId(const ir::AstNode *parent, ir::AstNode *childNode) diff --git a/es2panda/ir/base/classDefinition.cpp b/es2panda/ir/base/classDefinition.cpp index 14896cf5f382195cd79d4f8d9393d3600463d9a5..1dcd5c4eddc1e0c95657510f5b3cab3852116725 100644 --- a/es2panda/ir/base/classDefinition.cpp +++ b/es2panda/ir/base/classDefinition.cpp @@ -166,7 +166,7 @@ int32_t ClassDefinition::CreateClassStaticProperties(compiler::PandaGen *pg, uti continue; } - util::StringView name = util::Helpers::LiteralToPropName(prop->Key()); + util::StringView name = util::Helpers::LiteralToPropName(pg->Allocator(), prop->Key()); compiler::LiteralBuffer *literalBuf = prop->IsStatic() ? &staticBuf : buf; auto &nameMap = prop->IsStatic() ? staticPropNameMap : propNameMap; diff --git a/es2panda/ir/expressions/objectExpression.cpp b/es2panda/ir/expressions/objectExpression.cpp index 2e5e0e924dc28594635ce8a394a7e5a50090116a..3c401ebb0bc6c7bbdb91bb2dd287392742c0706a 100644 --- a/es2panda/ir/expressions/objectExpression.cpp +++ b/es2panda/ir/expressions/objectExpression.cpp @@ -295,7 +295,7 @@ void ObjectExpression::CompileStaticProperties(compiler::PandaGen *pg, util::Bit } std::vector propBuf; - util::StringView name = util::Helpers::LiteralToPropName(prop->Key()); + util::StringView name = util::Helpers::LiteralToPropName(pg->Allocator(), prop->Key()); size_t propIndex = i; auto res = propNameMap.insert({name, propIndex}); if (res.second) { // name not found in map diff --git a/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-bound-value-expected.txt b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-bound-value-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..98beafaa9dfebf2e5b2ebbfdfcb994e7493032d5 --- /dev/null +++ b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-bound-value-expected.txt @@ -0,0 +1,6 @@ +0.000001 +1e-7 +1,000,000,000,000,000,000 +10,000,000,000,000,000,000 +100,000,000,000,000,000,000 +1e+21 diff --git a/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-bound-value.js b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-bound-value.js new file mode 100644 index 0000000000000000000000000000000000000000..f67266a7700dc2773a4af4f043429463968befa7 --- /dev/null +++ b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-bound-value.js @@ -0,0 +1,15 @@ +let number = { + 1e-6 : '0.000001', + 1e-7 : '1e-7', + 1e18 : '1,000,000,000,000,000,000', + 1e19 : '10,000,000,000,000,000,000', + 1e20 : '100,000,000,000,000,000,000', + 1e21 : '1e+21', +} + +print(number[1e-6]); +print(number[1e-7]); +print(number[1e18]); +print(number[1e19]); +print(number[1e20]); +print(number[1e21]); diff --git a/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-diff-type-expected.txt b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-diff-type-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..8fda00d6d889695dcb90ed6cf04bbeb07dceaf87 --- /dev/null +++ b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-diff-type-expected.txt @@ -0,0 +1,5 @@ +A +B +C +D +E diff --git a/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-diff-type.js b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-diff-type.js new file mode 100644 index 0000000000000000000000000000000000000000..4f87734766d24404ec251bfbaa1542abe3c9eeb4 --- /dev/null +++ b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-diff-type.js @@ -0,0 +1,13 @@ +let number = { + 1e3 : 'A', + 1E4 : 'B', + 100 : 'C', + 0.11 : 'D', + '1.11' : 'E' +} + +print(number[1e3]); +print(number[1E4]); +print(number[100]); +print(number[0.11]); +print(number[1.11]); diff --git a/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-float-expected.txt b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-float-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..4fc803e1274e3eb6976c134acad5f06abf865da3 --- /dev/null +++ b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-float-expected.txt @@ -0,0 +1 @@ +1000,2000 diff --git a/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-float.js b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-float.js new file mode 100644 index 0000000000000000000000000000000000000000..b9e662b20e59aad0b9c010e14c2c11e1286666ca --- /dev/null +++ b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-key-float.js @@ -0,0 +1,6 @@ +let number = { + 1e3 : '1000', + '2000' : 2e3 +} + +print(Object.keys(number)); diff --git a/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-value-float-expected.txt b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-value-float-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..4fc803e1274e3eb6976c134acad5f06abf865da3 --- /dev/null +++ b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-value-float-expected.txt @@ -0,0 +1 @@ +1000,2000 diff --git a/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-value-float.js b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-value-float.js new file mode 100644 index 0000000000000000000000000000000000000000..438cc79a07ca61d5bc2432688b9c95c445ecec9e --- /dev/null +++ b/es2panda/test/compiler/js/language/arguments-object/test-obj-expr-value-float.js @@ -0,0 +1,6 @@ +let number = { + 1e3 : '1000', + '2000' : 2e3 +} + +print(Object.values(number)); diff --git a/es2panda/util/helpers.cpp b/es2panda/util/helpers.cpp index 3eb0d69326e6422da00e3d534d5281865c30bec2..7f8ac2e827515aecf1a161172b19dd511987ecf0 100644 --- a/es2panda/util/helpers.cpp +++ b/es2panda/util/helpers.cpp @@ -55,7 +55,6 @@ #include #endif #include - namespace panda::es2panda::util { // Helpers @@ -70,7 +69,7 @@ bool Helpers::ContainSpreadElement(const ArenaVector &args) return std::any_of(args.begin(), args.end(), [](const auto *it) { return it->IsSpreadElement(); }); } -util::StringView Helpers::LiteralToPropName(const ir::Expression *lit) +util::StringView Helpers::LiteralToPropName(ArenaAllocator *allocator, const ir::Expression *lit) { switch (lit->Type()) { case ir::AstNodeType::IDENTIFIER: { @@ -80,7 +79,16 @@ util::StringView Helpers::LiteralToPropName(const ir::Expression *lit) return lit->AsStringLiteral()->Str(); } case ir::AstNodeType::NUMBER_LITERAL: { - return lit->AsNumberLiteral()->Str(); + auto str = lit->AsNumberLiteral()->Str(); + auto num = lit->AsNumberLiteral()->Number(); + + // "e" and "E" represent Scientific notation. + if ((std::fabs(num) >= MIN_SCINOTATION_TO_LITERAL && std::fabs(num) < MAX_SCINOTATION_TO_LITERAL) && + (str.Find("e") == std::string::npos && str.Find("E") == std::string::npos)) { + return str; + } else { + return util::Helpers::ToStringView(allocator, num); + } } case ir::AstNodeType::NULL_LITERAL: { return "null"; @@ -149,14 +157,131 @@ bool Helpers::EndsWith(std::string_view str, std::string_view suffix) return str.find(suffix, expectPos) == expectPos; } +void Helpers::GetBase(double d, int digits, int *decpt, char *buf, char *bufTmp, int size) +{ + int result = snprintf_s(bufTmp, size, size - 1, "%+.*e", digits - 1, d); + + if (result == -1) { + std::cerr << "Failed to write number to buffer in snprintf_s" << std::endl; + UNREACHABLE(); + } + // mantissa + buf[0] = bufTmp[1]; + if (digits > 1) { + if (memcpy_s(buf + 1, digits, bufTmp + 2, digits) != EOK) { // 2 means add the point char to buf + std::cerr << "Failed to copy bufTmp to buf in memcpy_s" << std::endl; + UNREACHABLE(); + } + } + buf[digits + 1] = '\0'; + + // exponent + *decpt = atoi(bufTmp + digits + 2 + (digits > 1)) + 1; // 2 means ignore the integer and point +} + +int Helpers::GetMinmumDigits(double d, int *decpt, char *buf) +{ + int digits = 0; + char bufTmp[JS_DTOA_BUF_SIZE] = {0}; + + // find the minimum amount of digits + int MinDigits = 1; + int MaxDigits = DOUBLE_MAX_PRECISION; + while (MinDigits < MaxDigits) { + digits = (MinDigits + MaxDigits) / 2; + GetBase(d, digits, decpt, buf, bufTmp, sizeof(bufTmp)); + if (strtod(bufTmp, NULL) == d) { + // no need to keep the trailing zeros + while (digits >= 2 && buf[digits] == '0') { // 2 means ignore the integer and point + digits--; + } + MaxDigits = digits; + } else { + MinDigits = digits + 1; + } + } + digits = MaxDigits; + GetBase(d, digits, decpt, buf, bufTmp, sizeof(bufTmp)); + + return digits; +} + +std::string Helpers::DoubleToString(double number) +{ + // Let n, k, and s be integers such that k ≥ 1, 10k−1 ≤ s < 10k, the Number value for s × 10n−k is m, + // and k is as small as possible. + // n is the number of digits before the Decimal separator. + // s is the number in the Decimal representation. + // k is the number of digits in the decimal representation of s and that s is not divisible by 10. + std::string result; + if (number < 0) { + result += "-"; + number = -number; + } + + // In this case, n==0, just need to calculate k and s. + if (0.1 <= number && number < 1) { // 0.1: 10 ** -1 + + std::string resultFast = "0."; + int64_t sFast = 0; + int kFast = 1; + int64_t power = 1; + while (kFast <= DOUBLE_MAX_PRECISION) { + power *= 10; // 10: base 10 + int digitFast = static_cast(number * power) % 10; // 10: base 10 + ASSERT(0 <= digitFast && digitFast <= 9); // 9: single digit max + sFast = sFast * 10 + digitFast; // 10: base 10 + resultFast += (digitFast + '0'); + if (sFast / static_cast(power) == number) { // s * (10 ** -k) + result += resultFast; + } + kFast++; + } + } + + char buffer[JS_DTOA_BUF_SIZE] = {0}; + int n = 0; + double d = number; + int k = GetMinmumDigits(d, &n, buffer); + std::string base = buffer; + if (n > 0 && n <= 21) { // NOLINT(readability-magic-numbers) + base.erase(1, 1); + if (k <= n) { + base += std::string(n - k, '0'); + } else { + base.insert(n, 1, '.'); + } + } else if (-6 < n && n <= 0) { // NOLINT(readability-magic-numbers) + base.erase(1, 1); + base = std::string("0.") + std::string(-n, '0') + base; + } else { + if (k == 1) { + base.erase(1, 1); + } + base += "e" + (n >= 1 ? std::string("+") : "") + std::to_string(n - 1); + } + result += base; + + return result; +} + std::string Helpers::ToString(double number) { - std::string str; + if (std::isnan(number)) { + return "NaN"; + } + if (number == 0.0) { + return "0"; + } + if (std::isinf(number)) { + return (number > 0) ? "Infinity" : "-Infinity"; + } + std::string str; if (Helpers::IsInteger(number)) { str = std::to_string(static_cast(number)); } else { - str = std::to_string(number); + str = DoubleToString(number); } return str; @@ -332,7 +457,7 @@ std::vector Helpers::CollectBindingNames(const ir::AstNo return bindings; } -util::StringView Helpers::FunctionName(const ir::ScriptFunction *func) +util::StringView Helpers::FunctionName(ArenaAllocator *allocator, const ir::ScriptFunction *func) { if (func->Id()) { return func->Id()->Name(); @@ -395,7 +520,7 @@ util::StringView Helpers::FunctionName(const ir::ScriptFunction *func) if (prop->Kind() != ir::PropertyKind::PROTO && Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed())) { - return Helpers::LiteralToPropName(prop->Key()); + return Helpers::LiteralToPropName(allocator, prop->Key()); } break; diff --git a/es2panda/util/helpers.h b/es2panda/util/helpers.h index 5d06cac59fca657bc0079a26db12910269bc13b3..a6f8607d23942d3f1f3c67ed00bad5245be9bb9e 100644 --- a/es2panda/util/helpers.h +++ b/es2panda/util/helpers.h @@ -60,7 +60,7 @@ public: static bool IsGlobalIdentifier(const util::StringView &str); static bool ContainSpreadElement(const ArenaVector &args); - static util::StringView LiteralToPropName(const ir::Expression *lit); + static util::StringView LiteralToPropName(ArenaAllocator *allocator, const ir::Expression *lit); template static bool IsInteger(double number); @@ -69,6 +69,9 @@ public: static bool FileExtensionIs(std::string_view filePath, std::string_view extension); static bool EndsWith(std::string_view str, std::string_view suffix); + static std::string DoubleToString(double number); + static int GetMinmumDigits(double d, int *decpt, char *buf); + static void GetBase(double d, int digits, int *decpt, char *buf, char *bufTmp, int size); static std::string ToString(double number); static util::StringView ToStringView(ArenaAllocator *allocator, double number); static util::StringView ToStringView(ArenaAllocator *allocator, int32_t number); @@ -84,7 +87,7 @@ public: static bool IsBindingPattern(const ir::AstNode *node); static bool IsPattern(const ir::AstNode *node); static std::vector CollectBindingNames(const ir::AstNode *node); - static util::StringView FunctionName(const ir::ScriptFunction *func); + static util::StringView FunctionName(ArenaAllocator *allocator, const ir::ScriptFunction *func); static std::tuple ParamName(ArenaAllocator *allocator, const ir::AstNode *param, uint32_t index); static bool IsChild(const ir::AstNode *parent, const ir::AstNode *child); @@ -98,6 +101,10 @@ public: static void ScanDirectives(ir::ScriptFunction *func, const lexer::LineIndex &lineIndex); static std::string GetHashString(std::string str); + static constexpr size_t JS_DTOA_BUF_SIZE = 128; + static constexpr int DOUBLE_MAX_PRECISION = 17; + static constexpr double MAX_SCINOTATION_TO_LITERAL = 1e21; + static constexpr double MIN_SCINOTATION_TO_LITERAL = 1e-6; static const uint32_t INVALID_INDEX = 4294967295L; static const uint32_t MAX_INT32 = 2147483647; static const uint32_t MAX_INT16 = std::numeric_limits::max(); diff --git a/es2panda/util/ustring.h b/es2panda/util/ustring.h index dc73aaf4e1f07af186483a84adea0bfb3665096a..52976ee8acabd2c235cb2bafb73229d6c880a2ed 100644 --- a/es2panda/util/ustring.h +++ b/es2panda/util/ustring.h @@ -109,7 +109,7 @@ public: return StringView(std::string_view(sv_.data() + begin, end - begin)); } - constexpr size_t Find(const char *str) + constexpr size_t Find(const char *str) const { return sv_.find(str); }