unittests/VMRuntime/OperationsTest.cpp (772 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include "hermes/VM/Operations.h" #include "hermes/VM/JSObject.h" #include "hermes/VM/PrimitiveBox.h" #include "hermes/VM/StringView.h" #include "TestHelpers.h" #include <limits> #include "gtest/gtest.h" using namespace hermes::vm; namespace { CallResult<HermesValue> res{ExecutionStatus ::EXCEPTION}; #define IsSameValueTest(result, v1, v2) \ { \ EXPECT_##result(isSameValue(v1, v2)); \ EXPECT_##result(isSameValue(v2, v1)); \ } using OperationsTest = RuntimeTestFixture; TEST_F(OperationsTest, IsSameValueTest) { PinnedHermesValue v1; PinnedHermesValue v2; PinnedHermesValue v3; v1 = HermesValue::encodeNullValue(); v2 = HermesValue::encodeNullValue(); IsSameValueTest(TRUE, v1, v2); v1 = HermesValue::encodeUndefinedValue(); v2 = HermesValue::encodeUndefinedValue(); IsSameValueTest(TRUE, v1, v2); v1 = HermesValue::encodeNullValue(); v2 = HermesValue::encodeUndefinedValue(); IsSameValueTest(FALSE, v1, v2); v1 = HermesValue::encodeBoolValue(true); v2 = HermesValue::encodeBoolValue(true); IsSameValueTest(TRUE, v1, v2); v1 = HermesValue::encodeBoolValue(true); v2 = HermesValue::encodeBoolValue(false); IsSameValueTest(FALSE, v1, v2); v1 = HermesValue::encodeUndefinedValue(); v2 = HermesValue::encodeBoolValue(false); IsSameValueTest(FALSE, v1, v2); v1 = HermesValue::encodeDoubleValue(123.45); v2 = HermesValue::encodeDoubleValue(123.45); IsSameValueTest(TRUE, v1, v2); v1 = HermesValue::encodeDoubleValue(-0.0); v2 = HermesValue::encodeDoubleValue(+0.0); IsSameValueTest(FALSE, v1, v2); v1 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::quiet_NaN()); v2 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::quiet_NaN()); IsSameValueTest(TRUE, v1, v2); v1 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity()); v2 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity()); IsSameValueTest(TRUE, v1, v2); v1 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity()); v2 = HermesValue::encodeDoubleValue(-std::numeric_limits<double>::infinity()); IsSameValueTest(FALSE, v1, v2); v1 = HermesValue::encodeNumberValue(+0); v2 = HermesValue::encodeDoubleValue(+0.0); IsSameValueTest(TRUE, v1, v2); v1 = HermesValue::encodeNumberValue(-134); v2 = HermesValue::encodeDoubleValue(-134.0); IsSameValueTest(TRUE, v1, v2); v1 = HermesValue::encodeNumberValue(182); v2 = HermesValue::encodeDoubleValue(182.54); IsSameValueTest(FALSE, v1, v2); auto nullObj = runtime.makeNullHandle<JSObject>(); auto obj1 = runtime.makeHandle(JSObject::create(runtime, nullObj)); auto obj2 = runtime.makeHandle(JSObject::create(runtime, nullObj)); v1 = HermesValue::encodeObjectValue(obj1.get()); v2 = HermesValue::encodeObjectValue(&obj2); IsSameValueTest(FALSE, v1, v2); v1 = HermesValue::encodeObjectValue(obj1.get()); v2 = HermesValue::encodeObjectValue(obj1.get()); IsSameValueTest(TRUE, v1, v2); auto s1 = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"hello world")); auto s2 = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"hello world")); auto s3 = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"hi earth")); v1 = HermesValue::encodeStringValue(s1.get()); v2 = HermesValue::encodeStringValue(s2.get()); v3 = HermesValue::encodeStringValue(s3.get()); IsSameValueTest(TRUE, v1, v2); IsSameValueTest(FALSE, v1, v3); v1 = HermesValue::encodeSymbolValue(SymbolID::unsafeCreate(1)); v2 = HermesValue::encodeSymbolValue(SymbolID::unsafeCreate(2)); IsSameValueTest(FALSE, v1, v2); IsSameValueTest(TRUE, v1, v1); v2 = HermesValue::encodeBoolValue(true); IsSameValueTest(FALSE, v1, v2); v2 = HermesValue::encodeDoubleValue(0); IsSameValueTest(FALSE, v1, v2); } #define AbstractEqualityTest(result, x, y) \ { \ auto xHandle = runtime.makeHandle(x); \ auto yHandle = runtime.makeHandle(y); \ auto res = abstractEqualityTest_RJS(runtime, xHandle, yHandle); \ EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); \ EXPECT_##result(*res); \ \ res = abstractEqualityTest_RJS(runtime, yHandle, xHandle); \ EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); \ EXPECT_##result(*res); \ } TEST_F(OperationsTest, AbstractEqualityTest) { GCScope gcScope{runtime, "OperationsTest.AbstractEqualityTest", 200}; PinnedHermesValue v1; PinnedHermesValue v2; PinnedHermesValue v3; v1 = HermesValue::encodeNullValue(); v2 = HermesValue::encodeNullValue(); AbstractEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeUndefinedValue(); v2 = HermesValue::encodeUndefinedValue(); AbstractEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeNullValue(); v2 = HermesValue::encodeUndefinedValue(); AbstractEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeBoolValue(true); v2 = HermesValue::encodeBoolValue(true); AbstractEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeBoolValue(true); v2 = HermesValue::encodeBoolValue(false); AbstractEqualityTest(FALSE, v1, v2); v1 = HermesValue::encodeUndefinedValue(); v2 = HermesValue::encodeBoolValue(false); AbstractEqualityTest(FALSE, v1, v2); v1 = HermesValue::encodeDoubleValue(123.45); v2 = HermesValue::encodeDoubleValue(123.45); AbstractEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::quiet_NaN()); v2 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::quiet_NaN()); AbstractEqualityTest(FALSE, v1, v2); v1 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::quiet_NaN()); v2 = HermesValue::encodeDoubleValue(123.563); AbstractEqualityTest(FALSE, v1, v2); v1 = HermesValue::encodeDoubleValue(-0.0); v2 = HermesValue::encodeDoubleValue(+0.0); AbstractEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity()); v2 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity()); AbstractEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity()); v2 = HermesValue::encodeDoubleValue(-std::numeric_limits<double>::infinity()); AbstractEqualityTest(FALSE, v1, v2); v1 = HermesValue::encodeNumberValue(+0); v2 = HermesValue::encodeDoubleValue(+0.0); AbstractEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeNumberValue(-134); v2 = HermesValue::encodeDoubleValue(-134.0); AbstractEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeNumberValue(182); v2 = HermesValue::encodeDoubleValue(182.54); AbstractEqualityTest(FALSE, v1, v2); auto nullObj = runtime.makeNullHandle<JSObject>(); auto obj1 = runtime.makeHandle(JSObject::create(runtime, nullObj)); auto obj2 = runtime.makeHandle(JSObject::create(runtime, nullObj)); v1 = HermesValue::encodeObjectValue(obj1.get()); v2 = HermesValue::encodeObjectValue(obj2.get()); AbstractEqualityTest(FALSE, v1, v2); v1 = HermesValue::encodeObjectValue(obj1.get()); v2 = HermesValue::encodeObjectValue(obj1.get()); AbstractEqualityTest(TRUE, v1, v2); auto s1 = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"hello world")); auto s2 = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"hello world")); auto s3 = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"hi earth")); v1 = HermesValue::encodeStringValue(s1.get()); v2 = HermesValue::encodeStringValue(s2.get()); v3 = HermesValue::encodeStringValue(s3.get()); AbstractEqualityTest(TRUE, v1, v2); AbstractEqualityTest(FALSE, v1, v3); auto s4 = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"1000")); v1 = HermesValue::encodeStringValue(s4.get()); v2 = HermesValue::encodeDoubleValue(1000); AbstractEqualityTest(TRUE, v1, v2); auto s5 = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"Infinity")); v1 = HermesValue::encodeStringValue(s5.get()); v2 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity()); AbstractEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeBoolValue(true); v2 = HermesValue::encodeDoubleValue(1); AbstractEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeBoolValue(false); v2 = HermesValue::encodeDoubleValue(0); AbstractEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeBoolValue(true); v2 = HermesValue::encodeDoubleValue(153); AbstractEqualityTest(FALSE, v1, v2); // TODO: Test Object equality once Runtime::interpretFunction() is written. } #define StrictEqualityTest(result, v1, v2) \ { \ EXPECT_##result(strictEqualityTest(v1, v2)); \ EXPECT_##result(strictEqualityTest(v2, v1)); \ } TEST_F(OperationsTest, StrictEquaityTest) { PinnedHermesValue v1; PinnedHermesValue v2; PinnedHermesValue v3; v1 = HermesValue::encodeNullValue(); v2 = HermesValue::encodeNullValue(); StrictEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeUndefinedValue(); v2 = HermesValue::encodeUndefinedValue(); StrictEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeNullValue(); v2 = HermesValue::encodeUndefinedValue(); StrictEqualityTest(FALSE, v1, v2); v1 = HermesValue::encodeBoolValue(true); v2 = HermesValue::encodeBoolValue(true); StrictEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeBoolValue(true); v2 = HermesValue::encodeBoolValue(false); StrictEqualityTest(FALSE, v1, v2); v1 = HermesValue::encodeUndefinedValue(); v2 = HermesValue::encodeBoolValue(false); StrictEqualityTest(FALSE, v1, v2); v1 = HermesValue::encodeDoubleValue(123.45); v2 = HermesValue::encodeDoubleValue(123.45); StrictEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeDoubleValue(-0.0); v2 = HermesValue::encodeDoubleValue(+0.0); StrictEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::quiet_NaN()); v2 = HermesValue::encodeDoubleValue(3); StrictEqualityTest(FALSE, v1, v2); v1 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::quiet_NaN()); v2 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::quiet_NaN()); StrictEqualityTest(FALSE, v1, v2); v1 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity()); v2 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity()); StrictEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity()); v2 = HermesValue::encodeDoubleValue(-std::numeric_limits<double>::infinity()); StrictEqualityTest(FALSE, v1, v2); v1 = HermesValue::encodeNumberValue(+0); v2 = HermesValue::encodeDoubleValue(+0.0); StrictEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeNumberValue(-134); v2 = HermesValue::encodeDoubleValue(-134.0); StrictEqualityTest(TRUE, v1, v2); v1 = HermesValue::encodeNumberValue(182); v2 = HermesValue::encodeDoubleValue(182.54); StrictEqualityTest(FALSE, v1, v2); auto nullObj = runtime.makeNullHandle<JSObject>(); auto obj1 = runtime.makeHandle(JSObject::create(runtime, nullObj)); auto obj2 = runtime.makeHandle(JSObject::create(runtime, nullObj)); v1 = HermesValue::encodeObjectValue(obj1.get()); v2 = HermesValue::encodeObjectValue(&obj2); StrictEqualityTest(FALSE, v1, v2); v1 = HermesValue::encodeObjectValue(obj1.get()); v2 = HermesValue::encodeObjectValue(obj1.get()); StrictEqualityTest(TRUE, v1, v2); auto s1 = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"hello world")); auto s2 = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"hello world")); auto s3 = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"hi earth")); v1 = HermesValue::encodeStringValue(s1.get()); v2 = HermesValue::encodeStringValue(s2.get()); v3 = HermesValue::encodeStringValue(s3.get()); StrictEqualityTest(TRUE, v1, v2); StrictEqualityTest(FALSE, v1, v3); } TEST_F(OperationsTest, IsPrimitiveTest) { PinnedHermesValue v; v = HermesValue::encodeNumberValue(1); EXPECT_TRUE(isPrimitive(v)); v = HermesValue::encodeNullValue(); EXPECT_TRUE(isPrimitive(v)); v = HermesValue::encodeUndefinedValue(); EXPECT_TRUE(isPrimitive(v)); v = HermesValue::encodeBoolValue(true); EXPECT_TRUE(isPrimitive(v)); v = HermesValue::encodeDoubleValue(1.21); EXPECT_TRUE(isPrimitive(v)); auto s = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"hello world")); v = HermesValue::encodeStringValue(s.get()); EXPECT_TRUE(isPrimitive(v)); auto nullObj = runtime.makeNullHandle<JSObject>(); auto obj = JSObject::create(runtime, nullObj); v = HermesValue::encodeObjectValue(&obj); EXPECT_FALSE(isPrimitive(v)); } TEST_F(OperationsTest, ToBooleanTest) { { EXPECT_FALSE(toBoolean(HermesValue::encodeUndefinedValue())); EXPECT_FALSE(toBoolean(HermesValue::encodeNullValue())); } { EXPECT_TRUE(toBoolean(HermesValue::encodeBoolValue(true))); EXPECT_FALSE(toBoolean(HermesValue::encodeBoolValue(false))); } { auto empty = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"")); EXPECT_FALSE(toBoolean(HermesValue::encodeStringValue(empty.get()))); auto full = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"full")); EXPECT_TRUE(toBoolean(HermesValue::encodeStringValue(full.get()))); } { EXPECT_FALSE(toBoolean(HermesValue::encodeDoubleValue(0))); EXPECT_FALSE(toBoolean(HermesValue::encodeDoubleValue(-0.0))); EXPECT_FALSE(toBoolean(HermesValue::encodeNaNValue())); EXPECT_FALSE(toBoolean(HermesValue::encodeNumberValue(0))); EXPECT_TRUE(toBoolean(HermesValue::encodeDoubleValue(123.34))); EXPECT_TRUE(toBoolean(HermesValue::encodeNumberValue(123))); } } // Use macros for these tests because they're verbose. #define ToStringTest(result, value) \ { \ Handle<> scopedValue = runtime.makeHandle(value); \ auto strRes = toString_RJS(runtime, scopedValue); \ EXPECT_EQ(ExecutionStatus::RETURNED, strRes.getStatus()); \ EXPECT_TRUE(StringPrimitive::createStringView( \ runtime, runtime.makeHandle(std::move(strRes.getValue()))) \ .equals(createUTF16Ref(result))); \ } #define DoubleToStringTest(result, value) \ ToStringTest(result, HermesValue::encodeDoubleValue(value)); #define SmallIntToStringTest(result, value) \ ToStringTest(result, HermesValue::encodeNumberValue(value)); TEST_F(OperationsTest, ToStringTest) { GCScope gcScope{runtime, "OperationsTest.ToStringTest", 128}; // Simple tests { ToStringTest(u"null", HermesValue::encodeNullValue()); ToStringTest(u"undefined", HermesValue::encodeUndefinedValue()); ToStringTest(u"true", HermesValue::encodeBoolValue(true)); ToStringTest(u"false", HermesValue::encodeBoolValue(false)); } // Strings { auto str1 = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"asdf")); ToStringTest(u"asdf", HermesValue::encodeStringValue(str1.get())); auto str2 = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"")); ToStringTest(u"", HermesValue::encodeStringValue(str2.get())); } // Numbers { DoubleToStringTest(u"NaN", std::numeric_limits<double>::quiet_NaN()); auto infinity = std::numeric_limits<double>::infinity(); DoubleToStringTest(u"Infinity", infinity); DoubleToStringTest(u"-Infinity", -infinity); DoubleToStringTest(u"0", +0.0); DoubleToStringTest(u"0", -0.0); DoubleToStringTest(u"3", 3); DoubleToStringTest(u"-3", -3); DoubleToStringTest(u"-10000", -10000); DoubleToStringTest(u"1100000", 1.1e6); DoubleToStringTest(u"10000", 10000); DoubleToStringTest(u"100000", 100000); DoubleToStringTest(u"1000000", 1000000); DoubleToStringTest(u"10000000", 10000000); DoubleToStringTest(u"100000000000000000000", 1e20); DoubleToStringTest(u"1e+21", 1e21); DoubleToStringTest(u"0.125", 0.125); DoubleToStringTest(u"0.5", 0.5); DoubleToStringTest(u"3.14", 3.14); DoubleToStringTest(u"3.14", 3.14); DoubleToStringTest(u"14583.1832", 14583.1832); DoubleToStringTest(u"-14583.1832", -14583.1832); DoubleToStringTest(u"1.23e+25", 1.23e25); DoubleToStringTest(u"1.23e-25", 1.23e-25); DoubleToStringTest(u"5e+25", 5e+25); DoubleToStringTest(u"-5e+25", -5e+25); SmallIntToStringTest(u"0", 0); SmallIntToStringTest(u"12384", 12384); SmallIntToStringTest(u"-12384", -12384); } // TODO: Test Object toString once Runtime::interpretFunction() is written. } // Use macros for these tests because they're verbose. #define ToNumberTest(result, value) \ { \ Handle<> scopedValue = runtime.makeHandle(value); \ res = toNumber_RJS(runtime, scopedValue); \ EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); \ EXPECT_EQ((double)result, res->getDouble()); \ } #define InvalidToNumberTest(value) \ { \ Handle<> scopedValue = runtime.makeHandle(value); \ res = toNumber_RJS(runtime, scopedValue); \ EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); \ EXPECT_TRUE(std::isnan(res->getDouble())); \ } #define StringToNumberTest(result, string) \ { \ auto strPrim = \ StringPrimitive::createNoThrow(runtime, createUTF16Ref(string)); \ ToNumberTest(result, HermesValue::encodeStringValue(strPrim.get())); \ } #define InvalidStringToNumberTest(string) \ { \ auto strPrim = \ StringPrimitive::createNoThrow(runtime, createUTF16Ref(string)); \ InvalidToNumberTest(HermesValue::encodeStringValue(strPrim.get())); \ } using OperationsLargeHeapTest = LargeHeapRuntimeTestFixture; TEST_F(OperationsLargeHeapTest, ToNumberTest) { GCScope gcScope{runtime, "OperationsTest.ToNumberTest", 200}; // Simple tests { ToNumberTest(+0.0, HermesValue::encodeNullValue()); InvalidToNumberTest(HermesValue::encodeUndefinedValue()); ToNumberTest(1, HermesValue::encodeBoolValue(true)); ToNumberTest(0, HermesValue::encodeBoolValue(false)); } // Strings { // Infinity StringToNumberTest(std::numeric_limits<double>::infinity(), u"Infinity"); StringToNumberTest(std::numeric_limits<double>::infinity(), u"+Infinity"); StringToNumberTest(-std::numeric_limits<double>::infinity(), u"-Infinity"); StringToNumberTest(std::numeric_limits<double>::infinity(), u" Infinity\t"); StringToNumberTest(std::numeric_limits<double>::infinity(), u" +Infinity "); StringToNumberTest(-std::numeric_limits<double>::infinity(), u" -Infinity"); // Integers StringToNumberTest(0, u""); StringToNumberTest(0, u"\t "); StringToNumberTest(0, u" 0"); StringToNumberTest(0, u"0 "); StringToNumberTest(3, u" \t 3 \t \n \u2028 \uFEFF "); StringToNumberTest(3, u"3"); StringToNumberTest(3, u"00000000000000000000000000000003"); StringToNumberTest(-3, u"-3"); StringToNumberTest(10000, u"10000"); StringToNumberTest(-10000, u"-10000"); StringToNumberTest(2147483647, u"2147483647"); StringToNumberTest(-2147483647, u"-2147483647"); // Signed zero StringToNumberTest(0, u"+0.0"); EXPECT_FALSE(std::signbit(res->getDouble())); StringToNumberTest(0, u"-0.0"); EXPECT_TRUE(std::signbit(res->getDouble())); // Non-integrals StringToNumberTest(3.14, u"3.14"); StringToNumberTest(14583.1832, u"14583.1832"); StringToNumberTest(-14583.1832, u"-14583.1832"); StringToNumberTest(1.23e25, u"1.23e+25"); StringToNumberTest(1.23e-25, u"1.23e-25"); StringToNumberTest(13e8, u"13e8"); StringToNumberTest(.11248, u".11248"); StringToNumberTest(6853294837411.219, u"6853294837411.2184921374291384"); StringToNumberTest( 1.0948273409128347e+43, u"10948273409128347210948271309487321094712389"); // Scientific notation StringToNumberTest(1.23e+25, u"1.23e+25"); StringToNumberTest(1.23e+25, u"1.23e25"); StringToNumberTest(1.23e+25, u"1.23E+25"); StringToNumberTest(1.23e+25, u"1.23E25"); StringToNumberTest(1.23e+25, u"+1.23E25"); StringToNumberTest(1.23e-25, u"+1.23E-25"); StringToNumberTest(1.23e-25, u"+1.23e-25"); StringToNumberTest(-1.23e-25, u"-1.23E-25"); StringToNumberTest(4.3214123123e+29, u"43214.123123e25"); StringToNumberTest( std::numeric_limits<double>::infinity(), u"6143812.482134891732e12904"); // Hex StringToNumberTest(0x234aL, u"0x234a"); StringToNumberTest(0x234aL, u"0X234A"); StringToNumberTest( 6.582018229284824e+63, u"0xfffffffffffffffffffffffffffffffffffffffffffffffffffff"); StringToNumberTest(0xdeadbeefL, u"0xdeAdBeeF"); // Special hex StringToNumberTest(1152921504606847200, u"0x1000000000000081"); StringToNumberTest(1152921504606847200, u"0x1000000000000084"); StringToNumberTest(1152921504606847000, u"0x1000000000000079"); // Octal StringToNumberTest(0, u"0o0"); StringToNumberTest(9, u"0o11"); // Binary StringToNumberTest(0, u"0b0"); StringToNumberTest(3, u"0b11"); StringToNumberTest(1024, u"0b10000000000"); } // Big hex number { auto str = StringPrimitive::createNoThrow( runtime, createUTF16Ref(u"0xFFFFFFFFFFFFFFFF")); auto scopedVal = runtime.makeHandle(HermesValue::encodeStringValue(str.get())); res = toNumber_RJS(runtime, scopedVal); EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); auto strRes = toString_RJS(runtime, runtime.makeHandle(res.getValue())); EXPECT_EQ(ExecutionStatus::RETURNED, strRes.getStatus()); EXPECT_TRUE(StringPrimitive::createStringView( runtime, runtime.makeHandle(std::move(*strRes))) .equals(createUTF16Ref(u"18446744073709552000"))); } // Invalid strings { InvalidStringToNumberTest(u"NaN"); InvalidStringToNumberTest(u" NaN "); InvalidStringToNumberTest(u"0x"); InvalidStringToNumberTest(u" 0x"); InvalidStringToNumberTest(u"0x123E-4"); InvalidStringToNumberTest(u"-0x234a"); InvalidStringToNumberTest(u"-0xdeAdBeeF"); InvalidStringToNumberTest(u"0xDEADBEEG"); InvalidStringToNumberTest(u"0xdeadbeeg"); InvalidStringToNumberTest(u"aawpfeioj"); InvalidStringToNumberTest(u"123asdf"); InvalidStringToNumberTest(u"123.123.123.123"); InvalidStringToNumberTest(u"4 6 7 "); InvalidStringToNumberTest(u" 1 a "); InvalidStringToNumberTest(u"!(*#&!"); InvalidStringToNumberTest(u"1֍ "); } // TODO: Test Object toNumber once Runtime::interpretFunction() is written. } #define ToIntegerTest(result, value) \ { \ auto scopedValue = runtime.makeHandle(value); \ res = toIntegerOrInfinity(runtime, scopedValue); \ EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); \ EXPECT_EQ(result, res->getNumber()); \ } TEST_F(OperationsTest, ToIntegerTest) { ToIntegerTest(0, HermesValue::encodeNullValue()); ToIntegerTest(0, HermesValue::encodeUndefinedValue()); ToIntegerTest(2, HermesValue::encodeDoubleValue(2.0)); ToIntegerTest(2, HermesValue::encodeDoubleValue(2.4)); ToIntegerTest(-2, HermesValue::encodeDoubleValue(-2.4)); ToIntegerTest( 0, HermesValue::encodeDoubleValue(std::numeric_limits<double>::quiet_NaN())); ToIntegerTest(0, HermesValue::encodeDoubleValue(-0.0)); ToIntegerTest(0, HermesValue::encodeDoubleValue(+0.0)); ToIntegerTest( std::numeric_limits<double>::infinity(), HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity())); ToIntegerTest( -std::numeric_limits<double>::infinity(), HermesValue::encodeDoubleValue(-std::numeric_limits<double>::infinity())); } #define ToInt32Test(result, value) \ { \ auto scopedValue = runtime.makeHandle(value); \ res = toInt32_RJS(runtime, scopedValue); \ EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); \ EXPECT_EQ(result, res->getNumber()); \ } TEST_F(OperationsTest, ToInt32Test) { ToInt32Test(0, HermesValue::encodeNullValue()); ToInt32Test(0, HermesValue::encodeUndefinedValue()); ToInt32Test( 0, HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity())); ToInt32Test( 0, HermesValue::encodeDoubleValue(-std::numeric_limits<double>::infinity())); ToInt32Test(2, HermesValue::encodeDoubleValue(2.0)); ToInt32Test(2, HermesValue::encodeDoubleValue(2.4)); ToInt32Test(-2, HermesValue::encodeDoubleValue(-2.4)); ToInt32Test( 0, HermesValue::encodeDoubleValue(std::numeric_limits<double>::quiet_NaN())); ToInt32Test(0, HermesValue::encodeDoubleValue(-0.0)); ToInt32Test(0, HermesValue::encodeDoubleValue(+0.0)); ToInt32Test(1, HermesValue::encodeDoubleValue(std::pow(2, 33) + 1.0)); ToInt32Test(-1, HermesValue::encodeDoubleValue(std::pow(2, 33) - 1.0)); ToInt32Test(1238, HermesValue::encodeDoubleValue(std::pow(2, 33) + 1238.0)); ToInt32Test(-1238, HermesValue::encodeDoubleValue(std::pow(2, 33) - 1238.0)); } #define ToUInt32Test(result, value) \ { \ auto scopedValue = runtime.makeHandle(value); \ res = toUInt32_RJS(runtime, scopedValue); \ EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); \ EXPECT_EQ(result, res->getNumber()); \ } TEST_F(OperationsTest, ToUInt32Test) { ToUInt32Test(0, HermesValue::encodeNullValue()); ToUInt32Test(0, HermesValue::encodeUndefinedValue()); ToUInt32Test( 0, HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity())); ToUInt32Test( 0, HermesValue::encodeDoubleValue(-std::numeric_limits<double>::infinity())); ToUInt32Test(2, HermesValue::encodeDoubleValue(2.0)); ToUInt32Test(2, HermesValue::encodeDoubleValue(2.4)); ToUInt32Test(std::pow(2, 32) - 2, HermesValue::encodeDoubleValue(-2.4)); ToUInt32Test( 0, HermesValue::encodeDoubleValue(std::numeric_limits<double>::quiet_NaN())); ToUInt32Test(0, HermesValue::encodeDoubleValue(-0.0)); ToUInt32Test(0, HermesValue::encodeDoubleValue(+0.0)); ToUInt32Test(1, HermesValue::encodeDoubleValue(std::pow(2, 33) + 1.0)); ToUInt32Test( std::pow(2, 32) - 1, HermesValue::encodeDoubleValue(std::pow(2, 33) - 1.0)); ToUInt32Test(1238, HermesValue::encodeDoubleValue(std::pow(2, 33) + 1238.0)); ToUInt32Test( std::pow(2, 32) - 1238, HermesValue::encodeDoubleValue(std::pow(2, 33) - 1238.0)); } #define ToUInt16Test(result, value) \ { \ auto scopedValue = runtime.makeHandle(value); \ res = toUInt16(runtime, scopedValue); \ EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); \ EXPECT_EQ(result, res->getNumber()); \ } TEST_F(OperationsTest, ToUInt16Test) { ToUInt16Test(0, HermesValue::encodeNullValue()); ToUInt16Test(0, HermesValue::encodeUndefinedValue()); ToUInt16Test( 0, HermesValue::encodeDoubleValue(std::numeric_limits<double>::infinity())); ToUInt16Test( 0, HermesValue::encodeDoubleValue(-std::numeric_limits<double>::infinity())); ToUInt16Test(2, HermesValue::encodeDoubleValue(2.0)); ToUInt16Test(2, HermesValue::encodeDoubleValue(2.4)); ToUInt16Test(std::pow(2, 16) - 2, HermesValue::encodeDoubleValue(-2.4)); ToUInt16Test( 0, HermesValue::encodeDoubleValue(std::numeric_limits<double>::quiet_NaN())); ToUInt16Test(0, HermesValue::encodeDoubleValue(-0.0)); ToUInt16Test(0, HermesValue::encodeDoubleValue(+0.0)); ToUInt16Test(1, HermesValue::encodeDoubleValue(std::pow(2, 18) + 1.0)); ToUInt16Test( std::pow(2, 16) - 1, HermesValue::encodeDoubleValue(std::pow(2, 18) - 1.0)); ToUInt16Test(1238, HermesValue::encodeDoubleValue(std::pow(2, 33) + 1238.0)); ToUInt16Test( std::pow(2, 16) - 1238, HermesValue::encodeDoubleValue(std::pow(2, 18) - 1238.0)); } TEST_F(OperationsTest, ToObjectTest) { { auto scopedVal = runtime.makeHandle(HermesValue::encodeNullValue()); EXPECT_EQ( ExecutionStatus::EXCEPTION, toObject(runtime, scopedVal).getStatus()); runtime.clearThrownValue(); } { auto scopedVal = runtime.makeHandle(HermesValue::encodeUndefinedValue()); EXPECT_EQ( ExecutionStatus::EXCEPTION, toObject(runtime, scopedVal).getStatus()); runtime.clearThrownValue(); } { auto scopedVal = runtime.makeHandle(HermesValue::encodeBoolValue(true)); auto res = toObject(runtime, scopedVal); EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); EXPECT_TRUE(res->isObject()); auto obj = vmcast<JSBoolean>(static_cast<GCCell *>(res->getObject())); EXPECT_TRUE(obj->getPrimitiveBoolean()); } { auto m = 1529.184; auto scopedVal = runtime.makeHandle(HermesValue::encodeDoubleValue(m)); auto res = toObject(runtime, scopedVal); EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); EXPECT_TRUE(res->isObject()); auto obj = vmcast<JSNumber>(static_cast<GCCell *>(res->getObject())); EXPECT_EQ(m, obj->getPrimitiveNumber()); } { auto strRef = createUTF16Ref(u"hello"); auto str = StringPrimitive::createNoThrow(runtime, strRef); auto scopedVal = runtime.makeHandle(HermesValue::encodeStringValue(str.get())); auto res = toObject(runtime, scopedVal); EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); EXPECT_TRUE(res->isObject()); auto obj = vmcast<JSString>(static_cast<GCCell *>(res->getObject())); auto objStrHandle = runtime.makeHandle(JSString::getPrimitiveString(obj, runtime)); EXPECT_TRUE(StringPrimitive::createStringView(runtime, objStrHandle) .equals(strRef)); } } #define NumberAdditionTest(result, x, y) \ { \ res = addOp_RJS(runtime, runtime.makeHandle(x), runtime.makeHandle(y)); \ EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); \ EXPECT_TRUE(res.getValue().isNumber()); \ EXPECT_EQ(result, res->getNumber()); \ } #define InvalidNumberAdditionTest(x, y) \ { \ res = addOp_RJS(runtime, runtime.makeHandle(x), runtime.makeHandle(y)); \ EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); \ EXPECT_TRUE(res.getValue().isNumber()); \ EXPECT_TRUE(std::isnan(res->getNumber())); \ } #define DoubleAdditionTest(result, x, y) \ { \ NumberAdditionTest( \ result, \ HermesValue::encodeDoubleValue(x), \ HermesValue::encodeDoubleValue(y)); \ } TEST_F(OperationsTest, AdditionTest) { GCScope gcScope{runtime, "OperationsTest.AdditionTest", 128}; { auto a = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"abc")); auto b = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"def")); auto aHandle = runtime.makeHandle(HermesValue::encodeStringValue(a.get())); auto bHandle = runtime.makeHandle(HermesValue::encodeStringValue(b.get())); res = addOp_RJS(runtime, aHandle, bHandle); EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); EXPECT_TRUE(res.getValue().isString()); EXPECT_TRUE(StringPrimitive::createStringView( runtime, runtime.makeHandle(vmcast<StringPrimitive>(res.getValue()))) .equals(createUTF16Ref(u"abcdef"))); } { auto a = StringPrimitive::createNoThrow(runtime, createUTF16Ref(u"number: ")); auto aHandle = runtime.makeHandle(HermesValue::encodeStringValue(a.get())); auto bHandle = runtime.makeHandle(HermesValue::encodeDoubleValue(1.4)); res = addOp_RJS(runtime, aHandle, bHandle); EXPECT_EQ(ExecutionStatus::RETURNED, res.getStatus()); EXPECT_TRUE(res.getValue().isString()); EXPECT_TRUE(StringPrimitive::createStringView( runtime, runtime.makeHandle(vmcast<StringPrimitive>(res.getValue()))) .equals(createUTF16Ref(u"number: 1.4"))); } { InvalidNumberAdditionTest( HermesValue::encodeDoubleValue( std::numeric_limits<double>::quiet_NaN()), HermesValue::encodeDoubleValue(1)); InvalidNumberAdditionTest( HermesValue::encodeDoubleValue(1), HermesValue::encodeDoubleValue( std::numeric_limits<double>::quiet_NaN())); } { auto inf = std::numeric_limits<double>::infinity(); InvalidNumberAdditionTest( HermesValue::encodeDoubleValue(inf), HermesValue::encodeDoubleValue(-inf)); DoubleAdditionTest(inf, inf, inf); DoubleAdditionTest(-inf, -inf, -inf); DoubleAdditionTest(inf, inf, 4); DoubleAdditionTest(-inf, -inf, 4); } { DoubleAdditionTest(5, 0, 5); DoubleAdditionTest(5, -0, 5); DoubleAdditionTest(+0.0, -5, 5); EXPECT_FALSE(std::signbit(res->getNumber())); DoubleAdditionTest(-0.0, -0.0, -0.0); EXPECT_TRUE(std::signbit(res->getNumber())); DoubleAdditionTest(-0.0, +0.0, +0.0); EXPECT_FALSE(std::signbit(res->getNumber())); DoubleAdditionTest(-0.0, -0.0, 0.0); EXPECT_FALSE(std::signbit(res->getNumber())); } { NumberAdditionTest( 4.0, HermesValue::encodeDoubleValue(3.0), HermesValue::encodeBoolValue(true)); NumberAdditionTest( 3.0, HermesValue::encodeDoubleValue(3.0), HermesValue::encodeBoolValue(false)); } { InvalidNumberAdditionTest( HermesValue::encodeDoubleValue(3.0), HermesValue::encodeUndefinedValue()); NumberAdditionTest( 3.0, HermesValue::encodeDoubleValue(3.0), HermesValue::encodeNullValue()); } { DoubleAdditionTest(2, 1, 1); DoubleAdditionTest(185, 189, -4); DoubleAdditionTest(3528.142, 3527, 1.142); } } } // namespace