mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-11-23 18:20:04 +00:00
Optimization of computing short-integer string's hash code
issue : https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I83SNR Signed-off-by: DaiHN <daihuina1@huawei.com> Change-Id: I81a050d7f43fb60ce185685433ca9eceafd967d2
This commit is contained in:
parent
d29a1082fb
commit
c8aa4db942
@ -484,6 +484,61 @@ int64_t NumberHelper::DoubleToInt64(double d)
|
||||
return static_cast<int64_t>(d);
|
||||
}
|
||||
|
||||
bool NumberHelper::IsDigitalString(const uint8_t *start, const uint8_t *end)
|
||||
{
|
||||
int len = end - start;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (*(start + i) < '0' || *(start + i) > '9') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int NumberHelper::StringToInt(const uint8_t *start, const uint8_t *end)
|
||||
{
|
||||
int num = *start - '0';
|
||||
for (int i = 1; i < (end - start); i++) {
|
||||
num = 10 * num + (*(start + i) - '0');
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
// only for string is ordinary string and using UTF8 encoding
|
||||
// Fast path for short integer and some special value
|
||||
std::pair<bool, JSTaggedNumber> NumberHelper::FastStringToNumber(const uint8_t *start,
|
||||
const uint8_t *end, JSTaggedValue string)
|
||||
{
|
||||
ASSERT(start < end);
|
||||
EcmaStringAccessor strAccessor(string);
|
||||
bool minus = (start[0] == '-');
|
||||
int pos = (minus ? 1 : 0);
|
||||
|
||||
if (pos == (end - start)) {
|
||||
return {true, JSTaggedNumber(NAN_VALUE)};
|
||||
} else if (*(start + pos) > '9') {
|
||||
// valid number's codes not longer than '9', except 'I' and non-breaking space.
|
||||
if (*(start + pos) != 'I' && *(start + pos) != 0xA0) {
|
||||
return {true, JSTaggedNumber(NAN_VALUE)};
|
||||
}
|
||||
} else if ((end - (start + pos)) <= MAX_ELEMENT_INDEX_LEN && IsDigitalString((start + pos), end)) {
|
||||
int num = StringToInt((start + pos), end);
|
||||
if (minus) {
|
||||
if (num == 0) {
|
||||
return {true, JSTaggedNumber(SignedZero(Sign::NEG))};
|
||||
}
|
||||
num = -num;
|
||||
} else {
|
||||
if (num != 0 || (num == 0 && (end - start == 1))) {
|
||||
strAccessor.TryToSetIntegerHash(num);
|
||||
}
|
||||
}
|
||||
return {true, JSTaggedNumber(num)};
|
||||
}
|
||||
|
||||
return {false, JSTaggedNumber(NAN_VALUE)};
|
||||
}
|
||||
|
||||
double NumberHelper::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags)
|
||||
{
|
||||
auto p = const_cast<uint8_t *>(start);
|
||||
|
@ -108,6 +108,10 @@ public:
|
||||
static JSHandle<EcmaString> NumberToString(const JSThread *thread, JSTaggedValue number);
|
||||
static double TruncateDouble(double d);
|
||||
static int64_t DoubleToInt64(double d);
|
||||
static bool IsDigitalString(const uint8_t *start, const uint8_t *end);
|
||||
static int StringToInt(const uint8_t *start, const uint8_t *end);
|
||||
static std::pair<bool, JSTaggedNumber> FastStringToNumber(const uint8_t *start,
|
||||
const uint8_t *end, JSTaggedValue string);
|
||||
static double StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags = NO_FLAGS);
|
||||
static int32_t DoubleToInt(double d, size_t bits);
|
||||
static int32_t DoubleInRangeInt32(double d);
|
||||
|
@ -44,21 +44,26 @@ JSTaggedValue BuiltinsNumber::NumberConstructor(EcmaRuntimeCallInfo *argv)
|
||||
JSThread *thread = argv->GetThread();
|
||||
[[maybe_unused]] EcmaHandleScope handleScope(thread);
|
||||
JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
|
||||
|
||||
// 1. If value is present, then a , b , c.
|
||||
// 2. Else Let n be +0𝔽.
|
||||
JSTaggedNumber numberValue(0);
|
||||
if (argv->GetArgsNumber() > 0) {
|
||||
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
|
||||
// a. Let prim be ? ToNumeric(value).
|
||||
JSHandle<JSTaggedValue> numericVal = JSTaggedValue::ToNumeric(thread, value);
|
||||
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
||||
// b. If Type(prim) is BigInt, let n be 𝔽(ℝ(prim)).
|
||||
if (numericVal->IsBigInt()) {
|
||||
JSHandle<BigInt> bigNumericVal(numericVal);
|
||||
numberValue = BigInt::BigIntToNumber(bigNumericVal);
|
||||
if (!value->IsNumber()) {
|
||||
JSHandle<JSTaggedValue> numericVal = JSTaggedValue::ToNumeric(thread, value);
|
||||
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
||||
// b. If Type(prim) is BigInt, let n be 𝔽(ℝ(prim)).
|
||||
if (numericVal->IsBigInt()) {
|
||||
JSHandle<BigInt> bigNumericVal(numericVal);
|
||||
numberValue = BigInt::BigIntToNumber(bigNumericVal);
|
||||
} else {
|
||||
// c. Otherwise, let n be prim.
|
||||
numberValue = JSTaggedNumber(numericVal.GetTaggedValue());
|
||||
}
|
||||
} else {
|
||||
// c. Otherwise, let n be prim.
|
||||
numberValue = JSTaggedNumber(numericVal.GetTaggedValue());
|
||||
numberValue = JSTaggedNumber(value.GetTaggedValue());
|
||||
}
|
||||
}
|
||||
// 3. If NewTarget is undefined, return n.
|
||||
|
@ -2735,7 +2735,7 @@ HWTEST_F_L0(BuiltinsMathTest, Max_3)
|
||||
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo);
|
||||
JSTaggedValue result = BuiltinsMath::Max(ecmaRuntimeCallInfo);
|
||||
TestHelper::TearDownFrame(thread_, prev);
|
||||
JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(100.0);
|
||||
JSTaggedValue expect = BuiltinsBase::GetTaggedInt(100);
|
||||
ASSERT_EQ(result.GetRawData(), expect.GetRawData());
|
||||
}
|
||||
|
||||
|
@ -576,6 +576,9 @@ public:
|
||||
GateRef GetLengthFromString(GateRef value);
|
||||
GateRef GetHashcodeFromString(GateRef glue, GateRef value);
|
||||
GateRef TryGetHashcodeFromString(GateRef string);
|
||||
GateRef IsIntegerString(GateRef string);
|
||||
GateRef GetRawHashFromString(GateRef value);
|
||||
void SetRawHashcode(GateRef glue, GateRef str, GateRef rawHashcode, GateRef isInteger);
|
||||
|
||||
// for in
|
||||
GateRef GetEnumCacheKind(GateRef glue, GateRef enumCache);
|
||||
|
@ -14,6 +14,9 @@
|
||||
*/
|
||||
|
||||
#include "ecmascript/compiler/mcr_circuit_builder.h"
|
||||
#include "ecmascript/message_string.h"
|
||||
#include "ecmascript/stubs/runtime_stubs-inl.h"
|
||||
#include "ecmascript/stubs/runtime_stubs.h"
|
||||
|
||||
#include "ecmascript/compiler/circuit_builder-inl.h"
|
||||
#include "ecmascript/global_env.h"
|
||||
@ -958,6 +961,47 @@ GateRef CircuitBuilder::InsertLoadArrayLength(GateRef array, bool isTypedArray)
|
||||
return Circuit::NullGate();
|
||||
}
|
||||
|
||||
GateRef CircuitBuilder::IsIntegerString(GateRef string)
|
||||
{
|
||||
// compressedStringsEnabled fixed to true constant
|
||||
GateRef hash = Load(VariableType::INT32(), string, IntPtr(EcmaString::MIX_HASHCODE_OFFSET));
|
||||
return Int32Equal(
|
||||
Int32And(hash, Int32(EcmaString::IS_INTEGER_MASK)),
|
||||
Int32(EcmaString::IS_INTEGER_MASK));
|
||||
}
|
||||
|
||||
GateRef CircuitBuilder::GetRawHashFromString(GateRef value)
|
||||
{
|
||||
GateRef hash = Load(VariableType::INT32(), value, IntPtr(EcmaString::MIX_HASHCODE_OFFSET));
|
||||
return Int32And(hash, Int32(~EcmaString::IS_INTEGER_MASK));
|
||||
}
|
||||
|
||||
void CircuitBuilder::SetRawHashcode(GateRef glue, GateRef str, GateRef rawHashcode, GateRef isInteger)
|
||||
{
|
||||
Label subentry(env_);
|
||||
SubCfgEntry(&subentry);
|
||||
Label integer(env_);
|
||||
Label notInteger(env_);
|
||||
Label exit(env_);
|
||||
|
||||
DEFVAlUE(hash, env_, VariableType::INT32(), Int32(0));
|
||||
Branch(isInteger, &integer, ¬Integer);
|
||||
Bind(&integer);
|
||||
{
|
||||
hash = Int32Or(rawHashcode, Int32(EcmaString::IS_INTEGER_MASK));
|
||||
Jump(&exit);
|
||||
}
|
||||
Bind(¬Integer);
|
||||
{
|
||||
hash = Int32And(rawHashcode, Int32(~EcmaString::IS_INTEGER_MASK));
|
||||
Jump(&exit);
|
||||
}
|
||||
Bind(&exit);
|
||||
Store(VariableType::INT32(), glue, str, IntPtr(EcmaString::MIX_HASHCODE_OFFSET), *hash);
|
||||
SubCfgExit();
|
||||
return;
|
||||
}
|
||||
|
||||
GateRef CircuitBuilder::GetLengthFromString(GateRef value)
|
||||
{
|
||||
GateRef len = Load(VariableType::INT32(), value, IntPtr(EcmaString::MIX_LENGTH_OFFSET));
|
||||
@ -972,13 +1016,13 @@ GateRef CircuitBuilder::GetHashcodeFromString(GateRef glue, GateRef value)
|
||||
Label noRawHashcode(env_);
|
||||
Label exit(env_);
|
||||
DEFVAlUE(hashcode, env_, VariableType::INT32(), Int32(0));
|
||||
hashcode = Load(VariableType::INT32(), value, IntPtr(EcmaString::HASHCODE_OFFSET));
|
||||
hashcode = Load(VariableType::INT32(), value, IntPtr(EcmaString::MIX_HASHCODE_OFFSET));
|
||||
Branch(Int32Equal(*hashcode, Int32(0)), &noRawHashcode, &exit);
|
||||
Bind(&noRawHashcode);
|
||||
{
|
||||
hashcode =
|
||||
CallNGCRuntime(glue, RTSTUB_ID(ComputeHashcode), Gate::InvalidGateRef, { value }, Circuit::NullGate());
|
||||
Store(VariableType::INT32(), glue, value, IntPtr(EcmaString::HASHCODE_OFFSET), *hashcode);
|
||||
Store(VariableType::INT32(), glue, value, IntPtr(EcmaString::MIX_HASHCODE_OFFSET), *hashcode);
|
||||
Jump(&exit);
|
||||
}
|
||||
Bind(&exit);
|
||||
@ -995,7 +1039,7 @@ GateRef CircuitBuilder::TryGetHashcodeFromString(GateRef string)
|
||||
Label storeHash(env_);
|
||||
Label exit(env_);
|
||||
DEFVAlUE(result, env_, VariableType::INT64(), Int64(-1));
|
||||
GateRef hashCode = ZExtInt32ToInt64(Load(VariableType::INT32(), string, IntPtr(EcmaString::HASHCODE_OFFSET)));
|
||||
GateRef hashCode = ZExtInt32ToInt64(Load(VariableType::INT32(), string, IntPtr(EcmaString::MIX_HASHCODE_OFFSET)));
|
||||
Branch(Int64Equal(hashCode, Int64(0)), &noRawHashcode, &storeHash);
|
||||
Bind(&noRawHashcode);
|
||||
{
|
||||
|
@ -527,7 +527,7 @@ void NewObjectStubBuilder::AllocLineStringObject(Variable *result, Label *exit,
|
||||
ConstantIndex::LINE_STRING_CLASS_INDEX);
|
||||
StoreHClass(glue_, result->ReadVariable(), stringClass);
|
||||
SetLength(glue_, result->ReadVariable(), length, compressed);
|
||||
SetRawHashcode(glue_, result->ReadVariable(), Int32(0));
|
||||
SetRawHashcode(glue_, result->ReadVariable(), Int32(0), False());
|
||||
Jump(exit);
|
||||
}
|
||||
|
||||
@ -547,7 +547,7 @@ void NewObjectStubBuilder::AllocSlicedStringObject(Variable *result, Label *exit
|
||||
GateRef mixLength = Load(VariableType::INT32(), flatString->GetFlatString(), IntPtr(EcmaString::MIX_LENGTH_OFFSET));
|
||||
GateRef isCompressed = Int32And(Int32(EcmaString::STRING_COMPRESSED_BIT), mixLength);
|
||||
SetLength(glue_, result->ReadVariable(), length, isCompressed);
|
||||
SetRawHashcode(glue_, result->ReadVariable(), Int32(0));
|
||||
SetRawHashcode(glue_, result->ReadVariable(), Int32(0), False());
|
||||
BuiltinsStringStubBuilder builtinsStringStubBuilder(this);
|
||||
builtinsStringStubBuilder.StoreParent(glue_, result->ReadVariable(), flatString->GetFlatString());
|
||||
builtinsStringStubBuilder.StoreStartIndex(glue_, result->ReadVariable(),
|
||||
@ -569,7 +569,7 @@ void NewObjectStubBuilder::AllocTreeStringObject(Variable *result, Label *exit,
|
||||
ConstantIndex::TREE_STRING_CLASS_INDEX);
|
||||
StoreHClass(glue_, result->ReadVariable(), stringClass);
|
||||
SetLength(glue_, result->ReadVariable(), length, compressed);
|
||||
SetRawHashcode(glue_, result->ReadVariable(), Int32(0));
|
||||
SetRawHashcode(glue_, result->ReadVariable(), Int32(0), False());
|
||||
Store(VariableType::JS_POINTER(), glue_, result->ReadVariable(), IntPtr(TreeEcmaString::FIRST_OFFSET), first);
|
||||
Store(VariableType::JS_POINTER(), glue_, result->ReadVariable(), IntPtr(TreeEcmaString::SECOND_OFFSET), second);
|
||||
Jump(exit);
|
||||
|
@ -2622,9 +2622,19 @@ inline void StubBuilder::SetLength(GateRef glue, GateRef str, GateRef length, Ga
|
||||
Store(VariableType::INT32(), glue, str, IntPtr(EcmaString::MIX_LENGTH_OFFSET), mixLength);
|
||||
}
|
||||
|
||||
inline void StubBuilder::SetRawHashcode(GateRef glue, GateRef str, GateRef rawHashcode)
|
||||
inline GateRef StubBuilder::IsIntegerString(GateRef string)
|
||||
{
|
||||
Store(VariableType::INT32(), glue, str, IntPtr(EcmaString::HASHCODE_OFFSET), rawHashcode);
|
||||
return env_->GetBuilder()->IsIntegerString(string);
|
||||
}
|
||||
|
||||
inline GateRef StubBuilder::GetRawHashFromString(GateRef value)
|
||||
{
|
||||
return env_->GetBuilder()->GetRawHashFromString(value);
|
||||
}
|
||||
|
||||
inline void StubBuilder::SetRawHashcode(GateRef glue, GateRef str, GateRef rawHashcode, GateRef isInteger)
|
||||
{
|
||||
env_->GetBuilder()->SetRawHashcode(glue, str, rawHashcode, isInteger);
|
||||
}
|
||||
|
||||
inline GateRef StubBuilder::TryGetHashcodeFromString(GateRef string)
|
||||
|
@ -1530,6 +1530,27 @@ GateRef StubBuilder::IsDigit(GateRef ch)
|
||||
Int32GreaterThanOrEqual(ch, Int32('0')));
|
||||
}
|
||||
|
||||
void StubBuilder::TryToGetInteger(GateRef string, Variable *num, Label *success, Label *failed)
|
||||
{
|
||||
auto env = GetEnvironment();
|
||||
Label exit(env);
|
||||
Label inRange(env);
|
||||
Label isInteger(env);
|
||||
|
||||
GateRef len = GetLengthFromString(string);
|
||||
Branch(Int32LessThan(len, Int32(MAX_ELEMENT_INDEX_LEN)), &inRange, failed);
|
||||
Bind(&inRange);
|
||||
{
|
||||
Branch(IsIntegerString(string), &isInteger, failed);
|
||||
Bind(&isInteger);
|
||||
{
|
||||
GateRef integerNum = ZExtInt32ToInt64(GetRawHashFromString(string));
|
||||
num->WriteVariable(integerNum);
|
||||
Jump(success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GateRef StubBuilder::StringToElementIndex(GateRef glue, GateRef string)
|
||||
{
|
||||
auto env = GetEnvironment();
|
||||
@ -1551,6 +1572,9 @@ GateRef StubBuilder::StringToElementIndex(GateRef glue, GateRef string)
|
||||
Branch(isUtf16String, &exit, &isUtf8);
|
||||
Bind(&isUtf8);
|
||||
{
|
||||
Label getFailed(env);
|
||||
TryToGetInteger(string, &result, &exit, &getFailed);
|
||||
Bind(&getFailed);
|
||||
DEFVARIABLE(c, VariableType::INT32(), Int32(0));
|
||||
FlatStringStubBuilder thisFlat(this);
|
||||
thisFlat.FlattenString(glue, string, &flattenFastPath);
|
||||
@ -5957,24 +5981,7 @@ void StubBuilder::ReturnExceptionIfAbruptCompletion(GateRef glue)
|
||||
|
||||
GateRef StubBuilder::GetHashcodeFromString(GateRef glue, GateRef value)
|
||||
{
|
||||
auto env = GetEnvironment();
|
||||
Label subentry(env);
|
||||
env->SubCfgEntry(&subentry);
|
||||
Label noRawHashcode(env);
|
||||
Label exit(env);
|
||||
DEFVARIABLE(hashcode, VariableType::INT32(), Int32(0));
|
||||
hashcode = Load(VariableType::INT32(), value, IntPtr(EcmaString::HASHCODE_OFFSET));
|
||||
Branch(Int32Equal(*hashcode, Int32(0)), &noRawHashcode, &exit);
|
||||
Bind(&noRawHashcode);
|
||||
{
|
||||
hashcode = CallNGCRuntime(glue, RTSTUB_ID(ComputeHashcode), { value });
|
||||
Store(VariableType::INT32(), glue, value, IntPtr(EcmaString::HASHCODE_OFFSET), *hashcode);
|
||||
Jump(&exit);
|
||||
}
|
||||
Bind(&exit);
|
||||
auto ret = *hashcode;
|
||||
env->SubCfgExit();
|
||||
return ret;
|
||||
return env_->GetBuilder()->GetHashcodeFromString(glue, value);
|
||||
}
|
||||
|
||||
GateRef StubBuilder::ConstructorCheck(GateRef glue, GateRef ctor, GateRef outPut, GateRef thisObj)
|
||||
|
@ -406,6 +406,9 @@ public:
|
||||
GateRef GetBitFieldFromHClass(GateRef hClass);
|
||||
GateRef GetLengthFromString(GateRef value);
|
||||
GateRef GetHashcodeFromString(GateRef glue, GateRef value);
|
||||
inline GateRef IsIntegerString(GateRef string);
|
||||
inline void SetRawHashcode(GateRef glue, GateRef str, GateRef rawHashcode, GateRef isInteger);
|
||||
inline GateRef GetRawHashFromString(GateRef value);
|
||||
GateRef TryGetHashcodeFromString(GateRef string);
|
||||
GateRef GetFirstFromTreeString(GateRef string);
|
||||
GateRef GetSecondFromTreeString(GateRef string);
|
||||
@ -478,6 +481,7 @@ public:
|
||||
GateRef IsUtf8String(GateRef string);
|
||||
GateRef IsInternalString(GateRef string);
|
||||
GateRef IsDigit(GateRef ch);
|
||||
void TryToGetInteger(GateRef string, Variable *num, Label *success, Label *failed);
|
||||
GateRef StringToElementIndex(GateRef glue, GateRef string);
|
||||
GateRef ComputeNonInlinedFastPropsCapacity(GateRef glue, GateRef oldLength,
|
||||
GateRef maxNonInlinedFastPropsCapacity);
|
||||
@ -701,7 +705,6 @@ public:
|
||||
std::initializer_list<GateRef> args, JSCallMode mode);
|
||||
inline void SetLength(GateRef glue, GateRef str, GateRef length, bool compressed);
|
||||
inline void SetLength(GateRef glue, GateRef str, GateRef length, GateRef isCompressed);
|
||||
inline void SetRawHashcode(GateRef glue, GateRef str, GateRef rawHashcode);
|
||||
void Assert(int messageId, int line, GateRef glue, GateRef condition, Label *nextLabel);
|
||||
|
||||
GateRef GetNormalStringData(const StringInfoGateRef &stringInfoGate);
|
||||
|
@ -281,6 +281,14 @@ inline uint16_t EcmaString::At(int32_t index) const
|
||||
}
|
||||
}
|
||||
|
||||
inline Span<const uint8_t> EcmaString::FastToUtf8Span() const
|
||||
{
|
||||
uint32_t strLen = GetLength();
|
||||
ASSERT(IsUtf8());
|
||||
const uint8_t *data = GetDataUtf8();
|
||||
return Span<const uint8_t>(data, strLen);
|
||||
}
|
||||
|
||||
inline void EcmaString::WriteData(uint32_t index, uint16_t src)
|
||||
{
|
||||
ASSERT(index < GetLength());
|
||||
@ -406,5 +414,10 @@ inline void EcmaStringAccessor::ReadData(EcmaString *dst, EcmaString *src,
|
||||
{
|
||||
dst->WriteData(src, start, destSize, length);
|
||||
}
|
||||
|
||||
inline Span<const uint8_t> EcmaStringAccessor::FastToUtf8Span()
|
||||
{
|
||||
return string_->FastToUtf8Span();
|
||||
}
|
||||
} // namespace panda::ecmascript
|
||||
#endif
|
||||
|
@ -675,43 +675,112 @@ bool EcmaString::MemCopyChars(Span<T> &dst, size_t dstMax, Span<const T> &src, s
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t EcmaString::ComputeHashcode(uint32_t hashSeed) const
|
||||
bool EcmaString::HashIntegerString(uint32_t length, uint32_t *hash, const uint32_t hashSeed) const
|
||||
{
|
||||
uint32_t hash;
|
||||
ASSERT(length >= 0);
|
||||
Span<const uint8_t> str = FastToUtf8Span();
|
||||
return HashIntegerString(str.data(), length, hash, hashSeed);
|
||||
}
|
||||
|
||||
uint32_t EcmaString::ComputeHashcode() const
|
||||
{
|
||||
auto [hash, isInteger] = ComputeRawHashcode();
|
||||
return MixHashcode(hash, isInteger);
|
||||
}
|
||||
|
||||
// hashSeed only be used when computing two separate strings merged hashcode.
|
||||
std::pair<uint32_t, bool> EcmaString::ComputeRawHashcode() const
|
||||
{
|
||||
uint32_t hash = 0;
|
||||
uint32_t length = GetLength();
|
||||
if (length == 0) {
|
||||
return {hash, false};
|
||||
}
|
||||
|
||||
if (IsUtf8()) {
|
||||
// String using UTF8 encoding, and length smaller than 10, try to compute integer hash.
|
||||
if (length < MAX_ELEMENT_INDEX_LEN && this->HashIntegerString(length, &hash, 0)) {
|
||||
return {hash, true};
|
||||
}
|
||||
CVector<uint8_t> buf;
|
||||
const uint8_t *data = EcmaString::GetUtf8DataFlat(this, buf);
|
||||
hash = ComputeHashForData(data, length, hashSeed);
|
||||
// String can not convert to integer number, using normal hashcode computing algorithm.
|
||||
hash = this->ComputeHashForData(data, length, 0);
|
||||
return {hash, false};
|
||||
} else {
|
||||
CVector<uint16_t> buf;
|
||||
const uint16_t *data = EcmaString::GetUtf16DataFlat(this, buf);
|
||||
hash = ComputeHashForData(data, length, hashSeed);
|
||||
// If rawSeed has certain value, and second string uses UTF16 encoding,
|
||||
// then merged string can not be small integer number.
|
||||
hash = this->ComputeHashForData(data, length, 0);
|
||||
return {hash, false};
|
||||
}
|
||||
}
|
||||
|
||||
// hashSeed only be used when computing two separate strings merged hashcode.
|
||||
uint32_t EcmaString::ComputeHashcode(uint32_t rawHashSeed, bool isInteger) const
|
||||
{
|
||||
uint32_t hash;
|
||||
uint32_t length = GetLength();
|
||||
if (length == 0) {
|
||||
return MixHashcode(rawHashSeed, isInteger);
|
||||
}
|
||||
|
||||
if (IsUtf8()) {
|
||||
// String using UTF8 encoding, and length smaller than 10, try to compute integer hash.
|
||||
if ((rawHashSeed == 0 || isInteger) &&
|
||||
length < MAX_ELEMENT_INDEX_LEN && this->HashIntegerString(length, &hash, rawHashSeed)) {
|
||||
return hash;
|
||||
}
|
||||
CVector<uint8_t> buf;
|
||||
const uint8_t *data = EcmaString::GetUtf8DataFlat(this, buf);
|
||||
// String can not convert to integer number, using normal hashcode computing algorithm.
|
||||
hash = this->ComputeHashForData(data, length, rawHashSeed);
|
||||
return MixHashcode(hash, NOT_INTEGER);
|
||||
} else {
|
||||
CVector<uint16_t> buf;
|
||||
const uint16_t *data = EcmaString::GetUtf16DataFlat(this, buf);
|
||||
// If rawSeed has certain value, and second string uses UTF16 encoding,
|
||||
// then merged string can not be small integer number.
|
||||
hash = this->ComputeHashForData(data, length, rawHashSeed);
|
||||
return MixHashcode(hash, NOT_INTEGER);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* static */
|
||||
uint32_t EcmaString::ComputeHashcodeUtf8(const uint8_t *utf8Data, size_t utf8Len, bool canBeCompress)
|
||||
{
|
||||
uint32_t hash = 0;
|
||||
uint32_t mixHash = 0;
|
||||
if (canBeCompress) {
|
||||
hash = ComputeHashForData(utf8Data, utf8Len, 0);
|
||||
// String using UTF8 encoding, and length smaller than 10, try to compute integer hash.
|
||||
if (utf8Len < MAX_ELEMENT_INDEX_LEN && HashIntegerString(utf8Data, utf8Len, &mixHash, 0)) {
|
||||
return mixHash;
|
||||
}
|
||||
uint32_t hash = ComputeHashForData(utf8Data, utf8Len, 0);
|
||||
return MixHashcode(hash, NOT_INTEGER);
|
||||
} else {
|
||||
auto utf16Len = base::utf_helper::Utf8ToUtf16Size(utf8Data, utf8Len);
|
||||
CVector<uint16_t> tmpBuffer(utf16Len);
|
||||
[[maybe_unused]] auto len = base::utf_helper::ConvertRegionUtf8ToUtf16(utf8Data, tmpBuffer.data(), utf8Len,
|
||||
utf16Len, 0);
|
||||
ASSERT(len == utf16Len);
|
||||
hash = ComputeHashForData(tmpBuffer.data(), utf16Len, 0);
|
||||
uint32_t hash = ComputeHashForData(tmpBuffer.data(), utf16Len, 0);
|
||||
return MixHashcode(hash, NOT_INTEGER);
|
||||
}
|
||||
return hash;
|
||||
LOG_ECMA(FATAL) << "this branch is unreachable";
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
/* static */
|
||||
uint32_t EcmaString::ComputeHashcodeUtf16(const uint16_t *utf16Data, uint32_t length)
|
||||
{
|
||||
return ComputeHashForData(utf16Data, length, 0);
|
||||
uint32_t mixHash = 0;
|
||||
// String length smaller than 10, try to compute integer hash.
|
||||
if (length < MAX_ELEMENT_INDEX_LEN && HashIntegerString(utf16Data, length, &mixHash, 0)) {
|
||||
return mixHash;
|
||||
}
|
||||
uint32_t hash = ComputeHashForData(utf16Data, length, 0);
|
||||
return MixHashcode(hash, NOT_INTEGER);
|
||||
}
|
||||
|
||||
/* static */
|
||||
@ -751,6 +820,11 @@ bool EcmaString::ToElementIndex(uint32_t *index)
|
||||
return false;
|
||||
}
|
||||
|
||||
// fast path: get integer from string's hash value
|
||||
if (TryToGetInteger(index)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CVector<uint8_t> buf;
|
||||
const uint8_t *data = EcmaString::GetUtf8DataFlat(this, buf);
|
||||
uint32_t c = data[0];
|
||||
|
@ -52,24 +52,36 @@ class FlatStringInfo;
|
||||
}
|
||||
|
||||
class EcmaString : public TaggedObject {
|
||||
/* Mix Hash Code: -- { 0 | [31 bits raw hash code] } computed through string
|
||||
\ { 1 | [31 bits integer numbers] } fastpath for string to number
|
||||
*/
|
||||
public:
|
||||
CAST_CHECK(EcmaString, IsString);
|
||||
|
||||
static constexpr uint32_t IS_INTEGER_MASK = 1U << 31;
|
||||
static constexpr uint32_t STRING_COMPRESSED_BIT = 0x1;
|
||||
static constexpr uint32_t STRING_INTERN_BIT = 0x2;
|
||||
static constexpr size_t MAX_STRING_LENGTH = 0x40000000U; // 30 bits for string length, 2 bits for special meaning
|
||||
static constexpr uint32_t STRING_LENGTH_SHIFT_COUNT = 2U;
|
||||
static constexpr uint32_t MAX_INTEGER_HASH_NUMBER = 0x3B9AC9FF;
|
||||
static constexpr uint32_t MAX_CACHED_INTEGER_SIZE = 9;
|
||||
|
||||
static constexpr size_t MIX_LENGTH_OFFSET = TaggedObjectSize();
|
||||
// In last bit of mix_length we store if this string is compressed or not.
|
||||
ACCESSORS_PRIMITIVE_FIELD(MixLength, uint32_t, MIX_LENGTH_OFFSET, HASHCODE_OFFSET)
|
||||
ACCESSORS_PRIMITIVE_FIELD(RawHashcode, uint32_t, HASHCODE_OFFSET, SIZE)
|
||||
ACCESSORS_PRIMITIVE_FIELD(MixLength, uint32_t, MIX_LENGTH_OFFSET, MIX_HASHCODE_OFFSET)
|
||||
// In last bit of mix_hash we store if this string is small-integer number or not.
|
||||
ACCESSORS_PRIMITIVE_FIELD(MixHashcode, uint32_t, MIX_HASHCODE_OFFSET, SIZE)
|
||||
|
||||
enum CompressedStatus {
|
||||
STRING_COMPRESSED,
|
||||
STRING_UNCOMPRESSED,
|
||||
};
|
||||
|
||||
enum IsIntegerStatus {
|
||||
NOT_INTEGER = 0,
|
||||
IS_INTEGER,
|
||||
};
|
||||
|
||||
enum TrimMode : uint8_t {
|
||||
TRIM,
|
||||
TRIM_START,
|
||||
@ -121,16 +133,21 @@ private:
|
||||
static inline EcmaString *FastSubUtf16String(const EcmaVM *vm,
|
||||
const JSHandle<EcmaString> &src, uint32_t start, uint32_t length);
|
||||
|
||||
bool IsUtf8() const
|
||||
inline bool IsUtf8() const
|
||||
{
|
||||
return (GetMixLength() & STRING_COMPRESSED_BIT) == STRING_COMPRESSED;
|
||||
}
|
||||
|
||||
bool IsUtf16() const
|
||||
inline bool IsUtf16() const
|
||||
{
|
||||
return (GetMixLength() & STRING_COMPRESSED_BIT) == STRING_UNCOMPRESSED;
|
||||
}
|
||||
|
||||
inline bool IsInteger() const
|
||||
{
|
||||
return (GetMixHashcode() & IS_INTEGER_MASK) == IS_INTEGER_MASK;
|
||||
}
|
||||
|
||||
// require is LineString
|
||||
inline uint16_t *GetData() const;
|
||||
inline const uint8_t *GetDataUtf8() const;
|
||||
@ -140,38 +157,54 @@ private:
|
||||
inline uint8_t *GetDataUtf8Writable();
|
||||
inline uint16_t *GetDataUtf16Writable();
|
||||
|
||||
uint32_t GetLength() const
|
||||
inline uint32_t GetLength() const
|
||||
{
|
||||
return GetMixLength() >> STRING_LENGTH_SHIFT_COUNT;
|
||||
}
|
||||
|
||||
void SetLength(uint32_t length, bool compressed = false)
|
||||
inline void SetLength(uint32_t length, bool compressed = false)
|
||||
{
|
||||
ASSERT(length < MAX_STRING_LENGTH);
|
||||
// Use 0u for compressed/utf8 expression
|
||||
SetMixLength((length << STRING_LENGTH_SHIFT_COUNT) | (compressed ? STRING_COMPRESSED : STRING_UNCOMPRESSED));
|
||||
}
|
||||
|
||||
inline uint32_t GetRawHashcode() const
|
||||
{
|
||||
return GetMixHashcode() & (~IS_INTEGER_MASK);
|
||||
}
|
||||
|
||||
static inline uint32_t MixHashcode(uint32_t hashcode, bool isInteger)
|
||||
{
|
||||
return isInteger ? (hashcode | IS_INTEGER_MASK) : (hashcode & (~IS_INTEGER_MASK));
|
||||
}
|
||||
|
||||
inline void SetRawHashcode(uint32_t hashcode, bool isInteger = false)
|
||||
{
|
||||
// Use 0u for not integer string's expression
|
||||
SetMixHashcode(MixHashcode(hashcode, isInteger));
|
||||
}
|
||||
|
||||
inline size_t GetUtf8Length(bool modify = true) const;
|
||||
|
||||
void SetIsInternString()
|
||||
inline void SetIsInternString()
|
||||
{
|
||||
SetMixLength(GetMixLength() | STRING_INTERN_BIT);
|
||||
}
|
||||
|
||||
bool IsInternString() const
|
||||
inline bool IsInternString() const
|
||||
{
|
||||
return (GetMixLength() & STRING_INTERN_BIT) != 0;
|
||||
}
|
||||
|
||||
void ClearInternStringFlag()
|
||||
inline void ClearInternStringFlag()
|
||||
{
|
||||
SetMixLength(GetMixLength() & ~STRING_INTERN_BIT);
|
||||
}
|
||||
|
||||
bool TryGetHashCode(uint32_t *hash)
|
||||
inline bool TryGetHashCode(uint32_t *hash)
|
||||
{
|
||||
uint32_t hashcode = GetRawHashcode();
|
||||
uint32_t hashcode = GetMixHashcode();
|
||||
if (hashcode == 0 && GetLength() != 0) {
|
||||
return false;
|
||||
}
|
||||
@ -179,22 +212,90 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
inline uint32_t GetIntegerCode()
|
||||
{
|
||||
ASSERT(GetMixHashcode() & IS_INTEGER_MASK);
|
||||
return GetRawHashcode();
|
||||
}
|
||||
|
||||
// not change this data structure.
|
||||
// if string is not flat, this func has low efficiency.
|
||||
uint32_t PUBLIC_API GetHashcode()
|
||||
{
|
||||
uint32_t hashcode = GetRawHashcode();
|
||||
uint32_t hashcode = GetMixHashcode();
|
||||
// GetLength() == 0 means it's an empty array.No need to computeHashCode again when hashseed is 0.
|
||||
if (hashcode == 0 && GetLength() != 0) {
|
||||
hashcode = ComputeHashcode(0);
|
||||
hashcode = ComputeHashcode();
|
||||
SetRawHashcode(hashcode);
|
||||
}
|
||||
return hashcode;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline static bool IsDecimalDigitChar(const T c)
|
||||
{
|
||||
return (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
static uint32_t ComputeIntegerHash(uint32_t *num, uint8_t c)
|
||||
{
|
||||
if (!IsDecimalDigitChar(c)) {
|
||||
return false;
|
||||
}
|
||||
int charDate = c - '0';
|
||||
*num = (*num) * 10 + charDate; // 10: decimal factor
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HashIntegerString(uint32_t length, uint32_t *hash, uint32_t hashSeed) const;
|
||||
|
||||
template<typename T>
|
||||
static bool HashIntegerString(const T *data, size_t size, uint32_t *hash, uint32_t hashSeed)
|
||||
{
|
||||
ASSERT(size >= 0);
|
||||
if (hashSeed == 0) {
|
||||
if (IsDecimalDigitChar(data[0]) && data[0] != '0') {
|
||||
uint32_t num = data[0] - '0';
|
||||
uint32_t i = 1;
|
||||
do {
|
||||
if (i == size) {
|
||||
// compute mix hash
|
||||
if (num <= MAX_INTEGER_HASH_NUMBER) {
|
||||
*hash = MixHashcode(num, IS_INTEGER);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} while (ComputeIntegerHash(&num, data[i++]));
|
||||
}
|
||||
if (size == 1 && (data[0] == '0')) {
|
||||
*hash = MixHashcode(0, IS_INTEGER);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (IsDecimalDigitChar(data[0])) {
|
||||
uint32_t num = hashSeed * 10 + (data[0] - '0'); // 10: decimal factor
|
||||
uint32_t i = 1;
|
||||
do {
|
||||
if (i == size) {
|
||||
// compute mix hash
|
||||
if (num <= MAX_INTEGER_HASH_NUMBER) {
|
||||
*hash = MixHashcode(num, IS_INTEGER);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} while (ComputeIntegerHash(&num, data[i++]));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// not change this data structure.
|
||||
// if string is not flat, this func has low efficiency.
|
||||
uint32_t PUBLIC_API ComputeHashcode(uint32_t hashSeed) const;
|
||||
uint32_t PUBLIC_API ComputeHashcode() const;
|
||||
std::pair<uint32_t, bool> PUBLIC_API ComputeRawHashcode() const;
|
||||
uint32_t PUBLIC_API ComputeHashcode(uint32_t rawHashSeed, bool isInteger) const;
|
||||
|
||||
static uint32_t ComputeHashcodeUtf8(const uint8_t *utf8Data, size_t utf8Len, bool canBeCompress);
|
||||
static uint32_t ComputeHashcodeUtf16(const uint16_t *utf16Data, uint32_t length);
|
||||
@ -460,6 +561,29 @@ private:
|
||||
return str;
|
||||
}
|
||||
|
||||
inline Span<const uint8_t> FastToUtf8Span() const;
|
||||
|
||||
bool TryToGetInteger(uint32_t *result)
|
||||
{
|
||||
if (!IsInteger()) {
|
||||
return false;
|
||||
}
|
||||
ASSERT(GetLength() <= MAX_CACHED_INTEGER_SIZE);
|
||||
*result = GetIntegerCode();
|
||||
return true;
|
||||
}
|
||||
|
||||
// using integer number set into hash
|
||||
inline bool TryToSetIntegerHash(int32_t num)
|
||||
{
|
||||
uint32_t hashcode = GetMixHashcode();
|
||||
if (hashcode == 0 && GetLength() != 0) {
|
||||
SetRawHashcode(static_cast<uint32_t>(num), IS_INTEGER);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WriteData(EcmaString *src, uint32_t start, uint32_t destSize, uint32_t length);
|
||||
|
||||
static bool CanBeCompressed(const uint8_t *utf8Data, uint32_t utf8Len);
|
||||
@ -1019,6 +1143,20 @@ public:
|
||||
return string_->ToUtf8Span(buf);
|
||||
}
|
||||
|
||||
// only for string is flat and using UTF8 encoding
|
||||
inline Span<const uint8_t> FastToUtf8Span();
|
||||
|
||||
// Using string's hash to figure out whether the string can be converted to integer
|
||||
inline bool TryToGetInteger(uint32_t *result)
|
||||
{
|
||||
return string_->TryToGetInteger(result);
|
||||
}
|
||||
|
||||
inline bool TryToSetIntegerHash(int32_t num)
|
||||
{
|
||||
return string_->TryToSetIntegerHash(num);
|
||||
}
|
||||
|
||||
// not change string data structure.
|
||||
// if string is not flat, this func has low efficiency.
|
||||
std::string ToStdString(StringConvertedUsage usage = StringConvertedUsage::PRINT);
|
||||
@ -1077,11 +1215,26 @@ public:
|
||||
return string_->GetHashcode();
|
||||
}
|
||||
|
||||
uint32_t GetRawHashcode()
|
||||
{
|
||||
return string_->GetRawHashcode();
|
||||
}
|
||||
|
||||
// not change src data structure.
|
||||
// if src is not flat, this func has low efficiency.
|
||||
uint32_t ComputeHashcode(uint32_t hashSeed)
|
||||
std::pair<uint32_t, bool> ComputeRawHashcode()
|
||||
{
|
||||
return string_->ComputeHashcode(hashSeed);
|
||||
return string_->ComputeRawHashcode();
|
||||
}
|
||||
|
||||
uint32_t ComputeHashcode()
|
||||
{
|
||||
return string_->ComputeHashcode();
|
||||
}
|
||||
|
||||
uint32_t ComputeHashcode(uint32_t rawHashSeed, bool isInteger)
|
||||
{
|
||||
return string_->ComputeHashcode(rawHashSeed, isInteger);
|
||||
}
|
||||
|
||||
static uint32_t ComputeHashcodeUtf8(const uint8_t *utf8Data, size_t utf8Len, bool canBeCompress)
|
||||
|
@ -30,8 +30,9 @@ EcmaString *EcmaStringTable::GetString(const JSHandle<EcmaString> &firstString,
|
||||
{
|
||||
ASSERT(EcmaStringAccessor(firstString).NotTreeString());
|
||||
ASSERT(EcmaStringAccessor(secondString).NotTreeString());
|
||||
uint32_t hashCode = EcmaStringAccessor(firstString).GetHashcode();
|
||||
hashCode = EcmaStringAccessor(secondString).ComputeHashcode(hashCode);
|
||||
auto [hashCode, isInteger] = EcmaStringAccessor(firstString).ComputeRawHashcode();
|
||||
hashCode = EcmaStringAccessor(secondString).ComputeHashcode(hashCode, isInteger);
|
||||
|
||||
auto range = table_.equal_range(hashCode);
|
||||
for (auto item = range.first; item != range.second; ++item) {
|
||||
auto foundString = item->second;
|
||||
|
@ -82,13 +82,14 @@ inline bool JSTaggedValue::ToBoolean() const
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
inline JSTaggedNumber JSTaggedValue::ToNumber(JSThread *thread, const JSHandle<JSTaggedValue> &tagged)
|
||||
inline JSTaggedNumber JSTaggedValue::ToNumber(JSThread *thread, JSTaggedValue tagged)
|
||||
{
|
||||
if (tagged->IsInt() || tagged->IsDouble()) {
|
||||
return JSTaggedNumber(tagged.GetTaggedValue());
|
||||
DISALLOW_GARBAGE_COLLECTION;
|
||||
if (tagged.IsInt() || tagged.IsDouble()) {
|
||||
return JSTaggedNumber(tagged);
|
||||
}
|
||||
|
||||
switch (tagged->GetRawData()) {
|
||||
switch (tagged.GetRawData()) {
|
||||
case JSTaggedValue::VALUE_UNDEFINED:
|
||||
case JSTaggedValue::VALUE_HOLE: {
|
||||
return JSTaggedNumber(base::NAN_VALUE);
|
||||
@ -105,23 +106,29 @@ inline JSTaggedNumber JSTaggedValue::ToNumber(JSThread *thread, const JSHandle<J
|
||||
}
|
||||
}
|
||||
|
||||
if (tagged->IsString()) {
|
||||
return StringToDouble(tagged.GetTaggedValue());
|
||||
if (tagged.IsString()) {
|
||||
return StringToNumber(tagged);
|
||||
}
|
||||
if (tagged->IsECMAObject()) {
|
||||
JSHandle<JSTaggedValue> primValue(thread, ToPrimitive(thread, tagged, PREFER_NUMBER));
|
||||
if (tagged.IsECMAObject()) {
|
||||
JSHandle<JSTaggedValue>taggedHandle(thread, tagged);
|
||||
JSTaggedValue primValue = ToPrimitive(thread, taggedHandle, PREFER_NUMBER);
|
||||
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedNumber::Exception());
|
||||
return ToNumber(thread, primValue);
|
||||
}
|
||||
if (tagged->IsSymbol()) {
|
||||
if (tagged.IsSymbol()) {
|
||||
THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a Symbol value to a number", JSTaggedNumber::Exception());
|
||||
}
|
||||
if (tagged->IsBigInt()) {
|
||||
if (tagged.IsBigInt()) {
|
||||
THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a BigInt value to a number", JSTaggedNumber::Exception());
|
||||
}
|
||||
THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a Unknown value to a number", JSTaggedNumber::Exception());
|
||||
}
|
||||
|
||||
inline JSTaggedNumber JSTaggedValue::ToNumber(JSThread *thread, const JSHandle<JSTaggedValue> &tagged)
|
||||
{
|
||||
return ToNumber(thread, tagged.GetTaggedValue());
|
||||
}
|
||||
|
||||
inline JSTaggedValue JSTaggedValue::ToBigInt(JSThread *thread, const JSHandle<JSTaggedValue> &tagged)
|
||||
{
|
||||
JSHandle<JSTaggedValue> primValue(thread, ToPrimitive(thread, tagged));
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "ecmascript/js_tagged_value.h"
|
||||
|
||||
#include "ecmascript/ecma_macros.h"
|
||||
#include "ecmascript/ecma_string-inl.h"
|
||||
#include "ecmascript/ecma_vm.h"
|
||||
#include "ecmascript/global_env.h"
|
||||
#include "ecmascript/interpreter/interpreter.h"
|
||||
@ -1247,14 +1248,43 @@ bool JSTaggedValue::GetContainerProperty(JSThread *thread, const JSHandle<JSTagg
|
||||
return false;
|
||||
}
|
||||
|
||||
JSTaggedNumber JSTaggedValue::StringToNumber(JSTaggedValue tagged)
|
||||
{
|
||||
EcmaStringAccessor strAccessor(tagged);
|
||||
size_t strLen = strAccessor.GetLength();
|
||||
if (strLen == 0) {
|
||||
return JSTaggedNumber(0);
|
||||
}
|
||||
if (strLen < MAX_ELEMENT_INDEX_LEN && strAccessor.IsUtf8()) {
|
||||
uint32_t index;
|
||||
// fast path: get integer from string's hash value
|
||||
if (strAccessor.TryToGetInteger(&index)) {
|
||||
return JSTaggedNumber(index);
|
||||
}
|
||||
Span<const uint8_t> str = strAccessor.FastToUtf8Span();
|
||||
if (strAccessor.GetLength() == 0) {
|
||||
return JSTaggedNumber(0);
|
||||
}
|
||||
auto [isSuccess, result] = base::NumberHelper::FastStringToNumber(str.begin(), str.end(), tagged);
|
||||
if (isSuccess) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
CVector<uint8_t> buf;
|
||||
Span<const uint8_t> str = strAccessor.ToUtf8Span(buf);
|
||||
double d = base::NumberHelper::StringToDouble(str.begin(), str.end(), 0,
|
||||
base::ALLOW_BINARY + base::ALLOW_OCTAL + base::ALLOW_HEX);
|
||||
return JSTaggedNumber(d);
|
||||
}
|
||||
|
||||
JSHandle<JSTaggedValue> JSTaggedValue::ToNumeric(JSThread *thread, JSHandle<JSTaggedValue> tagged)
|
||||
{
|
||||
// 1. Let primValue be ? ToPrimitive(value, number)
|
||||
JSHandle<JSTaggedValue> primValue(thread, ToPrimitive(thread, tagged, PREFER_NUMBER));
|
||||
JSTaggedValue primValue = ToPrimitive(thread, tagged, PREFER_NUMBER);
|
||||
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
|
||||
// 2. If Type(primValue) is BigInt, return primValue.
|
||||
if (primValue->IsBigInt()) {
|
||||
return primValue;
|
||||
if (primValue.IsBigInt()) {
|
||||
return JSHandle<JSTaggedValue>(thread, primValue);
|
||||
}
|
||||
// 3. Return ? ToNumber(primValue).
|
||||
JSTaggedNumber number = ToNumber(thread, primValue);
|
||||
|
@ -414,6 +414,7 @@ public:
|
||||
static JSTaggedValue ToPrimitive(JSThread *thread, const JSHandle<JSTaggedValue> &tagged,
|
||||
PreferredPrimitiveType type = NO_PREFERENCE);
|
||||
bool ToBoolean() const;
|
||||
static JSTaggedNumber ToNumber(JSThread *thread, JSTaggedValue tagged);
|
||||
static JSTaggedNumber ToNumber(JSThread *thread, const JSHandle<JSTaggedValue> &tagged);
|
||||
static JSTaggedValue ToBigInt(JSThread *thread, const JSHandle<JSTaggedValue> &tagged);
|
||||
static JSTaggedValue ToBigInt64(JSThread *thread, const JSHandle<JSTaggedValue> &tagged);
|
||||
@ -435,6 +436,7 @@ public:
|
||||
static JSTaggedValue CanonicalNumericIndexString(JSThread *thread, const JSHandle<JSTaggedValue> &tagged);
|
||||
static JSTaggedNumber ToIndex(JSThread *thread, const JSHandle<JSTaggedValue> &tagged);
|
||||
static JSTaggedNumber StringToDouble(JSTaggedValue tagged);
|
||||
static JSTaggedNumber StringToNumber(JSTaggedValue tagged);
|
||||
|
||||
static bool ToArrayLength(JSThread *thread, const JSHandle<JSTaggedValue> &tagged, uint32_t *output);
|
||||
static bool ToElementIndex(JSTaggedValue key, uint32_t *output);
|
||||
|
@ -184,7 +184,7 @@ CString ModulePathHelper::ParsePrefixBundle(JSThread *thread, const JSPandaFile
|
||||
if (bundleName != vm->GetBundleName()) {
|
||||
entryPoint = PREVIEW_OF_ACROSS_HAP_FLAG;
|
||||
if (vm->EnableReportModuleResolvingFailure()) {
|
||||
CString msg = "[ArkRuntime Log] Cannot preview this HSP module as" \
|
||||
CString msg = "[ArkRuntime Log] Cannot preview this HSP module as " \
|
||||
"it is imported from outside the current application.";
|
||||
LOG_NO_TAG(ERROR) << msg;
|
||||
}
|
||||
|
@ -2718,8 +2718,7 @@ void RuntimeStubs::EndCallTimer(uintptr_t argGlue, JSTaggedType func)
|
||||
uint32_t RuntimeStubs::ComputeHashcode(JSTaggedType ecmaString)
|
||||
{
|
||||
auto string = reinterpret_cast<EcmaString *>(ecmaString);
|
||||
uint32_t result = EcmaStringAccessor(string).ComputeHashcode(0);
|
||||
return result;
|
||||
return EcmaStringAccessor(string).ComputeHashcode();
|
||||
}
|
||||
|
||||
int32_t RuntimeStubs::StringGetStart(bool isUtf8, EcmaString *srcString, int32_t length)
|
||||
|
@ -38,9 +38,12 @@ int NameDictionary::Hash(const JSTaggedValue &key)
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// for ohmurl path to compute hash code
|
||||
int NameDictionary::Hash(const uint8_t* str, int strSize)
|
||||
{
|
||||
return EcmaString::ComputeHashForData(str, strSize, 0);
|
||||
uint32_t hash = EcmaString::ComputeHashForData(str, strSize, 0);
|
||||
// url path can not be small int.
|
||||
return EcmaString::MixHashcode(hash, EcmaString::NOT_INTEGER);
|
||||
}
|
||||
|
||||
bool NameDictionary::IsMatch(const JSTaggedValue &key, const JSTaggedValue &other)
|
||||
|
@ -48,18 +48,6 @@ public:
|
||||
JSThread *thread {nullptr};
|
||||
};
|
||||
|
||||
static bool CheckHole(JSHandle<TaggedHashArray> &hashArray)
|
||||
{
|
||||
uint32_t arrayLength = hashArray->GetLength();
|
||||
for (uint32_t i = 0; i < arrayLength; i++) {
|
||||
JSTaggedValue indexValue = hashArray->Get(i);
|
||||
if (indexValue.IsHole()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CreateTaggedHashArray
|
||||
* @tc.desc: Call "TaggedHashArray::Create" function Create TaggedHashArray object, check whether the object
|
||||
@ -151,8 +139,6 @@ HWTEST_F_L0(TaggedHashArrayTest, SetValAndGetLinkNode)
|
||||
keyHash = TaggedNode::Hash(listKey.GetTaggedValue());
|
||||
TaggedHashArray::SetVal(thread, taggedHashArray, keyHash, listKey, listValue);
|
||||
}
|
||||
bool result = CheckHole(taggedHashArray);
|
||||
EXPECT_TRUE(result);
|
||||
keyHash = TaggedNode::Hash(myKey4.GetTaggedValue());
|
||||
// change value and add new key
|
||||
TaggedHashArray::SetVal(thread, taggedHashArray, keyHash, myKey4, myKey4Value);
|
||||
@ -200,8 +186,6 @@ HWTEST_F_L0(TaggedHashArrayTest, SetValAndGetTreeNode)
|
||||
uint32_t hashArrayIndex = static_cast<uint32_t>(numOfElement - 1) & keyHash;
|
||||
taggedHashArray->Set(thread, hashArrayIndex, rootTreeWithValueNode.GetTaggedValue());
|
||||
}
|
||||
bool result = CheckHole(taggedHashArray);
|
||||
EXPECT_TRUE(result);
|
||||
keyHash = TaggedNode::Hash(myKey5.GetTaggedValue());
|
||||
// change value and add new key
|
||||
TaggedHashArray::SetVal(thread, taggedHashArray, keyHash, myKey5, myKey5Value);
|
||||
|
@ -54,7 +54,41 @@
|
||||
let key7 = "no-schema:" + "/src/instantiated-1af0bf5b.js";
|
||||
let key8 = head3 + tail3;
|
||||
|
||||
let keyArray = [key1, key3, key5, key7];
|
||||
// non-intern + non-intern
|
||||
let numhead1 = "12".concat("022");
|
||||
let numtail1 = "4".concat("21");
|
||||
let numkey1 = numhead1 + numtail1;
|
||||
let numkey2 = numhead1 + numtail1;
|
||||
|
||||
// intern + intern
|
||||
let numkey3 = "123420002";
|
||||
let numkey4 = "12342" + "0002";
|
||||
|
||||
// non-intern + intern
|
||||
let numhead2 = "90".concat("88");
|
||||
let numtail2 = "90";
|
||||
let numkey5 = "90" + "8890";
|
||||
let numkey6 = numhead2 + numtail2;
|
||||
|
||||
// intern + non-intern
|
||||
let numhead3 = "90880";
|
||||
let numtail3 = "0".concat("0");
|
||||
let numkey7 = "90880" + "00";
|
||||
let numkey8 = numhead3 + numtail3;
|
||||
|
||||
// intern + intern
|
||||
let numkey9 = "999999999";
|
||||
let numkey10 = "9999".concat("99999");
|
||||
|
||||
// intern + intern
|
||||
let numkey11 = "1000000000";
|
||||
let numkey12 = "10".concat("00000000");
|
||||
|
||||
// intern + intern
|
||||
let numkey13 = "0100000";
|
||||
let numkey14 = "010".concat("0000");
|
||||
|
||||
let keyArray = [key1, key3, key5, key7, numkey1, numkey3, numkey5, numkey7, numkey9, numkey11, numkey13];
|
||||
let system = new System();
|
||||
for (let i = 0; i < keyArray.length; i++) {
|
||||
getOrCreateLoad(system, keyArray[i]);
|
||||
@ -68,6 +102,19 @@
|
||||
getOrCreateLoad(system, key4);
|
||||
getOrCreateLoad(system, key6);
|
||||
getOrCreateLoad(system, key8);
|
||||
|
||||
print("numkey1 === numkey2: ", numkey1 === numkey2);
|
||||
print("numkey3 === numkey4: ", numkey3 === numkey4);
|
||||
print("numkey5 === numkey6: ", numkey5 === numkey6);
|
||||
print("numkey7 === numkey8: ", numkey7 === numkey8);
|
||||
print("numkey9 === numkey10: ", numkey9 === numkey10);
|
||||
print("numkey11 === numkey12: ", numkey11 === numkey12);
|
||||
getOrCreateLoad(system, numkey2);
|
||||
getOrCreateLoad(system, numkey4);
|
||||
getOrCreateLoad(system, numkey6);
|
||||
getOrCreateLoad(system, numkey8);
|
||||
getOrCreateLoad(system, numkey10);
|
||||
getOrCreateLoad(system, numkey12);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -15,6 +15,13 @@ id: no-schema:/src/xxx-js/instantiated-1af0bf5b.js - load: undefined
|
||||
id: no-schema:/src/xxx-js/instantiation.js - load: undefined
|
||||
id: no-schema:/src/xxx-js/cc.js - load: undefined
|
||||
id: no-schema:/src/instantiated-1af0bf5b.js - load: undefined
|
||||
id: 12022421 - load: undefined
|
||||
id: 123420002 - load: undefined
|
||||
id: 908890 - load: undefined
|
||||
id: 9088000 - load: undefined
|
||||
id: 999999999 - load: undefined
|
||||
id: 1000000000 - load: undefined
|
||||
id: 0100000 - load: undefined
|
||||
key1 === key2: true
|
||||
key3 === key4: true
|
||||
key5 === key6: true
|
||||
@ -23,6 +30,18 @@ id: no-schema:/src/xxx-js/instantiated-1af0bf5b.js - load: [object Object]
|
||||
id: no-schema:/src/xxx-js/instantiation.js - load: [object Object]
|
||||
id: no-schema:/src/xxx-js/cc.js - load: [object Object]
|
||||
id: no-schema:/src/instantiated-1af0bf5b.js - load: [object Object]
|
||||
numkey1 === numkey2: true
|
||||
numkey3 === numkey4: true
|
||||
numkey5 === numkey6: true
|
||||
numkey7 === numkey8: true
|
||||
numkey9 === numkey10: true
|
||||
numkey11 === numkey12: true
|
||||
id: 12022421 - load: [object Object]
|
||||
id: 123420002 - load: [object Object]
|
||||
id: 908890 - load: [object Object]
|
||||
id: 9088000 - load: [object Object]
|
||||
id: 999999999 - load: [object Object]
|
||||
id: 1000000000 - load: [object Object]
|
||||
res: true
|
||||
res: true
|
||||
res: true
|
||||
|
Loading…
Reference in New Issue
Block a user