* Copyright (c) 2021-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/interpreter/interpreter.h"
#include "ecmascript/base/json_parser.h"
#include "ecmascript/linked_hash_table.h"
#include "ecmascript/ecma_string-inl.h"
#include "ecmascript/ecma_string_table.h"
#include "ecmascript/platform/json_platform_helper.h"
#if !ENABLE_V70_OPTIMIZATION
#undef ENABLE_LINXKIT
#endif
#ifdef ENABLE_LINXKIT
#include "ecmascript/platform/arm64/linxkit_parse.h"
#endif
namespace panda::ecmascript::base {
namespace {
#if ENABLE_V70_OPTIMIZATION
constexpr uint32_t K_UTF8_OBJECT_KEY_CACHE_MAX_LEN = 16;
constexpr uint32_t K_UTF16_OBJECT_KEY_CACHE_MAX_LEN = 8;
static_assert(K_UTF8_OBJECT_KEY_CACHE_MAX_LEN <= sizeof(uint64_t) * 2,
"UTF-8 object key cache length must fit PackedKey128");
static_assert(K_UTF16_OBJECT_KEY_CACHE_MAX_LEN <= sizeof(uint64_t) * 2 / sizeof(uint16_t),
"UTF-16 object key cache length must fit PackedKey128");
template<typename T>
bool TryAccumulateFastInteger(const T *current, const T *advance, uint64_t &value)
{
constexpr uint64_t K_MAX_FAST_INTEGER = static_cast<uint64_t>(base::MAX_SAFE_INTEGER);
value = 0;
for (const T *digit = current; digit != advance; digit++) {
uint32_t nextDigit = static_cast<uint32_t>(*digit - '0');
if (value > ((K_MAX_FAST_INTEGER - nextDigit) / 10U)) {
return false;
}
value = value * 10U + nextDigit;
}
return true;
}
JSTaggedValue CreateFastNumberValue(JSThread *thread, const JsonHelper::ParseOptions &parseOptions,
bool negative, uint64_t value)
{
if (parseOptions.bigIntMode == JsonHelper::BigIntMode::ALWAYS_PARSE_AS_BIGINT) {
return negative ? BigInt::Int64ToBigInt(thread, -static_cast<int64_t>(value)).GetTaggedValue() :
BigInt::Uint64ToBigInt(thread, value).GetTaggedValue();
}
double number = negative ? -static_cast<double>(value) : static_cast<double>(value);
return JSTaggedValue::TryCastDoubleToInt32(number);
}
#endif
}
template<typename T>
JSHandle<JSTaggedValue> JsonParser<T>::Launch(Text begin, Text end)
{
if (UNLIKELY(begin == end)) {
return JSHandle<JSTaggedValue>(thread_, [&]() -> JSTaggedValue {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Text in JSON: Empty Text",
JSTaggedValue::Exception(),
rawString_,
0);
}());
}
end_ = end - 1;
current_ = begin;
SkipEndWhiteSpace();
range_ = end_;
auto vm = thread_->GetEcmaVM();
factory_ = vm->GetFactory();
env_ = *vm->GetGlobalEnv();
auto index = static_cast<size_t>(GlobalEnvField::ELEMENT_HOLE_TAGGED_HCLASS_INDEX);
auto hclassVal = env_->GetGlobalEnvObjectByIndex(index).GetTaggedValue();
initialJSArrayClass_ = JSHandle<JSHClass>(thread_, hclassVal);
JSHandle<JSFunction> objectFunc(env_->GetObjectFunction());
initialJSObjectClass_ =
JSHandle<JSHClass>(thread_, JSFunction::GetOrCreateInitialJSHClass(thread_, objectFunc));
JSTaggedValue result = g_isEnableCMCGC ? ParseJSONText<true>() : ParseJSONText<false>();
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
return JSHandle<JSTaggedValue>(thread_, result);
}
template<typename T>
template<bool isEnableCMCGC>
JSTaggedValue JsonParser<T>::ParseJSONText()
{
JSHandle<JSTaggedValue> parseValue;
std::vector<JsonContinuation> continuationList;
std::vector<JSHandle<JSTaggedValue>> elementsList;
std::vector<JSHandle<JSTaggedValue>> propertyList;
continuationList.reserve(16);
elementsList.reserve(16);
propertyList.reserve(16);
JsonContinuation continuation(ContType::RETURN, 0);
while (true) {
while (true) {
SkipStartWhiteSpace();
Tokens token = ParseToken();
if (current_ > range_) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected end in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
switch (token) {
case Tokens::OBJECT:
if (EmptyObjectCheck()) {
if (transformType_ == TransformType::SENDABLE) {
JSHandle<JSHClass> sHclass;
JSHandle<JSTaggedValue> sJsonPrototype = GetSJsonPrototype();
JSHandle<LayoutInfo> sLayout = factory_->CreateSLayoutInfo(0);
sHclass = factory_->NewSEcmaHClass(JSSharedObject::SIZE, 0, JSType::JS_SHARED_OBJECT,
JSHandle<JSTaggedValue>(sJsonPrototype),
JSHandle<JSTaggedValue>(sLayout));
parseValue = JSHandle<JSTaggedValue>(factory_->NewSharedOldSpaceJSObject(sHclass));
} else {
parseValue = JSHandle<JSTaggedValue>(factory_->NewJSObject(initialJSObjectClass_));
}
GetNextNonSpaceChar();
break;
}
continuationList.emplace_back(std::move(continuation));
continuation = JsonContinuation(ContType::OBJECT, propertyList.size());
SkipStartWhiteSpace();
if (UNLIKELY(*current_ != '"')) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Object Prop in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
#if ENABLE_V70_OPTIMIZATION
propertyList.emplace_back(this->ParseObjectKey());
#else
propertyList.emplace_back(ParseString(true));
#endif
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
SkipStartWhiteSpace();
if (UNLIKELY(*current_ != ':')) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
Advance();
continue;
case Tokens::MAP:
if (EmptyObjectCheck()) {
if (transformType_ == TransformType::SENDABLE) {
parseValue = JSHandle<JSTaggedValue>(CreateSharedMap());
} else {
parseValue = JSHandle<JSTaggedValue>(CreateMap());
}
GetNextNonSpaceChar();
break;
}
continuationList.emplace_back(std::move(continuation));
continuation = JsonContinuation(ContType::MAP, propertyList.size());
SkipStartWhiteSpace();
if (UNLIKELY(*current_ != '"')) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected MAP Prop in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
#if ENABLE_V70_OPTIMIZATION
propertyList.emplace_back(this->ParseObjectKey());
#else
propertyList.emplace_back(ParseString(true));
#endif
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
SkipStartWhiteSpace();
if (UNLIKELY(*current_ != ':')) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected MAP in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
Advance();
continue;
case Tokens::ARRAY:
if (EmptyArrayCheck()) {
if (transformType_ == TransformType::SENDABLE) {
parseValue = JSHandle<JSTaggedValue>(factory_->NewJSSArray());
} else {
parseValue = JSHandle<JSTaggedValue>(factory_->NewJSArray(0, initialJSArrayClass_));
}
GetNextNonSpaceChar();
break;
}
continuationList.emplace_back(std::move(continuation));
continuation = JsonContinuation(ContType::ARRAY, elementsList.size());
continue;
case Tokens::LITERAL_TRUE:
parseValue = JSHandle<JSTaggedValue>(thread_, ParseLiteralTrue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
break;
case Tokens::LITERAL_FALSE:
parseValue = JSHandle<JSTaggedValue>(thread_, ParseLiteralFalse());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
break;
case Tokens::LITERAL_NULL:
parseValue = JSHandle<JSTaggedValue>(thread_, ParseLiteralNull());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
break;
case Tokens::NUMBER:
parseValue = JSHandle<JSTaggedValue>(thread_,
ParseNumber(IsInObjOrArrayOrMap(continuation.type_)));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
break;
case Tokens::STRING:
parseValue = ParseString(IsInObjOrArrayOrMap(continuation.type_));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
break;
default:
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Text in JSON: Invalid Token",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
break;
}
while (true) {
switch (continuation.type_) {
case ContType::RETURN:
ASSERT(continuationList.empty());
ASSERT(elementsList.empty());
ASSERT(propertyList.empty());
if (current_ <= range_) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Text in JSON: Remaining Text Before Return",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
return parseValue.GetTaggedValue();
case ContType::ARRAY: {
elementsList.emplace_back(parseValue);
SkipStartWhiteSpace();
if (*current_ == ',') {
Advance();
break;
}
if (transformType_ == TransformType::SENDABLE) {
parseValue = CreateSJsonArray(continuation, elementsList);
} else {
parseValue = CreateJsonArray(continuation, elementsList);
}
if (*current_ != ']') {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Array in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
Advance();
elementsList.resize(continuation.index_);
continuation = std::move(continuationList.back());
continuationList.pop_back();
continue;
}
case ContType::OBJECT: {
propertyList.emplace_back(parseValue);
SkipStartWhiteSpace();
if (*current_ == ',') {
GetNextNonSpaceChar();
if (UNLIKELY(*current_ != '"')) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Object Prop in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
#if ENABLE_V70_OPTIMIZATION
propertyList.emplace_back(this->ParseObjectKey());
#else
propertyList.emplace_back(ParseString(true));
#endif
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
SkipStartWhiteSpace();
if (UNLIKELY(*current_ != ':')) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Object in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
Advance();
break;
}
if (UNLIKELY(transformType_ == TransformType::SENDABLE)) {
parseValue = CreateSJsonObject<isEnableCMCGC>(continuation, propertyList);
} else {
parseValue = CreateJsonObject<isEnableCMCGC>(continuation, propertyList);
}
if (UNLIKELY(*current_ != '}')) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Object in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
Advance();
propertyList.resize(continuation.index_);
continuation = std::move(continuationList.back());
continuationList.pop_back();
continue;
}
case ContType::MAP: {
propertyList.emplace_back(parseValue);
SkipStartWhiteSpace();
if (*current_ == ',') {
GetNextNonSpaceChar();
if (UNLIKELY(*current_ != '"')) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected MAP Prop in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
#if ENABLE_V70_OPTIMIZATION
propertyList.emplace_back(this->ParseObjectKey());
#else
propertyList.emplace_back(ParseString(true));
#endif
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
SkipStartWhiteSpace();
if (UNLIKELY(*current_ != ':')) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected MAP in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
Advance();
break;
}
if (UNLIKELY(transformType_ == TransformType::SENDABLE)) {
parseValue = CreateSJsonMap(continuation, propertyList);
} else {
parseValue = CreateJsonMap(continuation, propertyList);
}
if (UNLIKELY(*current_ != '}')) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected MAP in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
Advance();
propertyList.resize(continuation.index_);
continuation = std::move(continuationList.back());
continuationList.pop_back();
continue;
}
}
break;
}
if constexpr (isEnableCMCGC) {
thread_->CheckSafepointIfSuspended();
}
}
}
template<typename T>
JSHandle<JSTaggedValue> JsonParser<T>::CreateJsonArray(JsonContinuation continuation,
std::vector<JSHandle<JSTaggedValue>> &elementsList)
{
size_t start = continuation.index_;
size_t size = elementsList.size() - start;
JSHandle<JSArray> array = factory_->NewJSArray(size, initialJSArrayClass_);
JSHandle<TaggedArray> elements = factory_->NewJsonFixedArray(start, size, elementsList);
JSHandle<JSObject> obj(array);
obj->SetElements(thread_, elements);
return JSHandle<JSTaggedValue>(array);
}
template<typename T>
JSHandle<JSTaggedValue> JsonParser<T>::CreateSJsonArray([[maybe_unused]] JsonContinuation continuation,
[[maybe_unused]] std::vector<JSHandle<JSTaggedValue>> &elementsList)
{
size_t start = continuation.index_;
size_t size = elementsList.size() - start;
JSHandle<JSSharedArray> array = factory_->NewJSSArray();
array->SetArrayLength(thread_, size);
JSHandle<TaggedArray> elements = factory_->NewSJsonFixedArray(start, size, elementsList);
JSHandle<JSObject> obj(array);
obj->SetElements(thread_, elements);
return JSHandle<JSTaggedValue>(array);
}
template<typename T>
template<bool isEnableCMCGC>
JSHandle<JSTaggedValue> JsonParser<T>::CreateJsonObject(JsonContinuation continuation,
std::vector<JSHandle<JSTaggedValue>> &propertyList)
{
size_t start = continuation.index_;
size_t size = propertyList.size() - start;
auto obj = JSHandle<JSTaggedValue>(factory_->NewJSObject(initialJSObjectClass_));
for (size_t i = 0; i < size; i += 2) {
auto &keyHandle = propertyList[start + i];
auto &valueHandle = propertyList[start + i + 1];
auto res = SetPropertyByValue(obj, keyHandle, valueHandle);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
if (res.IsHole()) {
JSTaggedValue::SetProperty(thread_, obj, keyHandle, valueHandle, true);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
}
if constexpr (isEnableCMCGC) {
thread_->CheckSafepointIfSuspended();
}
}
return obj;
}
template<typename T>
template<bool isEnableCMCGC>
JSHandle<JSTaggedValue> JsonParser<T>::CreateSJsonObject(JsonContinuation continuation,
std::vector<JSHandle<JSTaggedValue>> &propertyList)
{
size_t start = continuation.index_;
size_t size = propertyList.size() - start;
uint32_t fieldNum = size / 2;
JSHandle<JSHClass> hclass;
JSHandle<LayoutInfo> layout;
JSHandle<JSTaggedValue> jsonPrototype = GetSJsonPrototype();
if (fieldNum == 0) {
layout = factory_->CreateSLayoutInfo(fieldNum);
hclass = factory_->NewSEcmaHClass(JSSharedObject::SIZE, fieldNum, JSType::JS_SHARED_OBJECT,
JSHandle<JSTaggedValue>(jsonPrototype), JSHandle<JSTaggedValue>(layout));
JSHandle<JSObject> obj = factory_->NewSharedOldSpaceJSObject(hclass);
return JSHandle<JSTaggedValue>(obj);
} else if (LIKELY(fieldNum <= JSSharedObject::MAX_INLINE)) {
layout = factory_->CreateSLayoutInfo(fieldNum);
JSHandle<TaggedArray> propertyArray = factory_->NewSTaggedArray(size);
for (size_t i = 0; i < size; i += 2) {
JSHandle<JSTaggedValue> keyHandle = propertyList[start + i];
auto newKey = keyHandle.GetTaggedValue();
auto stringAccessor = EcmaStringAccessor(newKey);
if (!stringAccessor.IsInternString()) {
newKey = JSTaggedValue(thread_->GetEcmaVM()->GetFactory()->InternString(keyHandle));
}
propertyArray->Set(thread_, i, newKey);
propertyArray->Set(thread_, i + 1, JSTaggedValue(int(FieldType::NONE)));
if constexpr (isEnableCMCGC) {
thread_->CheckSafepointIfSuspended();
}
}
hclass = factory_->NewSEcmaHClass(JSSharedObject::SIZE, fieldNum, JSType::JS_SHARED_OBJECT,
JSHandle<JSTaggedValue>(jsonPrototype), JSHandle<JSTaggedValue>(layout));
JSHandle<NumberDictionary> elementsDic = NumberDictionary::CreateInSharedHeap(thread_);
bool hasElement = false;
SendableClassDefiner::AddFieldTypeToHClass(thread_, propertyArray, size, layout, hclass, elementsDic,
hasElement, start, std::move(propertyList));
JSHandle<JSObject> obj = factory_->NewSharedOldSpaceJSObject(hclass);
uint32_t index = 0;
size = (hclass->GetInlinedProperties() << 1);
for (size_t i = 0; i < size; i += 2) {
obj->SetPropertyInlinedProps(thread_, index++, propertyList[start + i + 1].GetTaggedValue());
}
if (hasElement) {
JSHandle<TaggedArray> elementsDicHdl(elementsDic);
JSHandle<TaggedArray> elements =
factory_->NewAndCopySNameDictionary(elementsDicHdl, elementsDicHdl->GetLength());
obj->SetElements(thread_, elements);
hclass->SetIsDictionaryElement(true);
}
return JSHandle<JSTaggedValue>(obj);
}
JSMutableHandle<NameDictionary> dict(
thread_, NameDictionary::CreateInSharedHeap(thread_, NameDictionary::ComputeHashTableSize(fieldNum)));
JSMutableHandle<JSTaggedValue> propKey(thread_, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> propValue(thread_, JSTaggedValue::Undefined());
for (size_t i = 0; i < size; i += 2) {
PropertyAttributes attributes = PropertyAttributes::Default(false, false, false);
propKey.Update(propertyList[start + i]);
propValue.Update(propertyList[start + i + 1]);
JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread_, dict, propKey,
propValue, attributes);
dict.Update(newDict);
}
hclass = factory_->NewSEcmaHClassDictMode(JSSharedObject::SIZE, fieldNum, JSType::JS_SHARED_OBJECT,
JSHandle<JSTaggedValue>(jsonPrototype));
JSHandle<JSObject> obj = factory_->NewSharedOldSpaceJSObject(hclass);
obj->SetProperties(thread_, dict);
return JSHandle<JSTaggedValue>(obj);
}
template<typename T>
JSHandle<JSSharedMap> JsonParser<T>::CreateSharedMap()
{
JSHandle<JSTaggedValue> proto = GetSMapPrototype();
auto emptySLayout = thread_->GlobalConstants()->GetHandledEmptySLayoutInfo();
JSHandle<JSHClass> mapClass = factory_->NewSEcmaHClass(JSSharedMap::SIZE, 0,
JSType::JS_SHARED_MAP, proto,
emptySLayout);
JSHandle<JSObject> obj = factory_->NewSharedOldSpaceJSObjectWithInit(mapClass);
JSHandle<JSSharedMap> jsMap = JSHandle<JSSharedMap>::Cast(obj);
JSHandle<LinkedHashMap> linkedMap(
LinkedHashMap::Create(thread_, LinkedHashMap::MIN_CAPACITY, MemSpaceKind::SHARED));
jsMap->SetLinkedMap(thread_, linkedMap);
jsMap->SetModRecord(0);
return jsMap;
}
template<typename T>
JSHandle<JSMap> JsonParser<T>::CreateMap()
{
JSHandle<JSTaggedValue> constructor = env_->GetBuiltinsMapFunction();
JSHandle<JSMap> map =
JSHandle<JSMap>::Cast(factory_->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), constructor));
JSHandle<LinkedHashMap> linkedMap = LinkedHashMap::Create(thread_);
map->SetLinkedMap(thread_, linkedMap);
return JSHandle<JSMap>(thread_, *map);
}
template<typename T>
JSHandle<JSTaggedValue> JsonParser<T>::CreateJsonMap(JsonContinuation continuation,
std::vector<JSHandle<JSTaggedValue>> &propertyList)
{
size_t start = continuation.index_;
size_t size = propertyList.size() - start;
uint32_t fieldNum = size / 2;
JSHandle<JSMap> map = CreateMap();
if (fieldNum == 0) {
return JSHandle<JSTaggedValue>(map);
}
for (size_t i = 0; i < size; i += 2) {
JSMap::Set(thread_, map, propertyList[start + i], propertyList[start + i + 1]);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
}
return JSHandle<JSTaggedValue>(map);
}
template<typename T>
JSHandle<JSTaggedValue> JsonParser<T>::CreateSJsonMap(JsonContinuation continuation,
std::vector<JSHandle<JSTaggedValue>> &propertyList)
{
size_t start = continuation.index_;
size_t size = propertyList.size() - start;
uint32_t fieldNum = size / 2;
JSHandle<JSSharedMap> jsMap = CreateSharedMap();
if (fieldNum == 0) {
return JSHandle<JSTaggedValue>(jsMap);
} else if (LIKELY(fieldNum <= JSSharedMap::MAX_INLINE)) {
for (size_t i = 0; i < size; i += 2) {
JSSharedMap::Set(thread_, jsMap, propertyList[start + i], propertyList[start + i + 1]);
}
return JSHandle<JSTaggedValue>(jsMap);
}
JSMutableHandle<NameDictionary> dict(
thread_, NameDictionary::CreateInSharedHeap(thread_, NameDictionary::ComputeHashTableSize(fieldNum)));
JSMutableHandle<JSTaggedValue> propKey(thread_, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> propValue(thread_, JSTaggedValue::Undefined());
for (size_t i = 0; i < size; i += 2) {
PropertyAttributes attributes = PropertyAttributes::Default(false, false, false);
propKey.Update(propertyList[start + i]);
propValue.Update(propertyList[start + i + 1]);
JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread_, dict, propKey,
propValue, attributes);
dict.Update(newDict);
}
jsMap->SetProperties(thread_, dict);
return JSHandle<JSTaggedValue>(jsMap);
}
template<typename T>
JSTaggedValue JsonParser<T>::SetPropertyByValue(const JSHandle<JSTaggedValue> &receiver,
const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
{
ASSERT(key->IsString());
auto newKey = key.GetTaggedValue();
auto stringAccessor = EcmaStringAccessor(newKey);
if (!stringAccessor.IsLineString() || (stringAccessor.GetLength() > 0 && stringAccessor.IsUtf8() &&
IsNumberCharacter(*stringAccessor.GetDataUtf8()))) {
uint32_t index = 0;
if (stringAccessor.ToElementIndex(thread_, &index)) {
return ObjectFastOperator::SetPropertyByIndex<ObjectFastOperator::Status::UseOwn>(thread_,
receiver.GetTaggedValue(), index, value.GetTaggedValue());
}
}
if (!stringAccessor.IsInternString()) {
newKey = JSTaggedValue(thread_->GetEcmaVM()->GetFactory()->InternString(key));
}
return ObjectFastOperator::SetJsonPropertyByName(thread_, receiver.GetTaggedValue(),
newKey, value.GetTaggedValue());
}
template<typename T>
JSTaggedValue JsonParser<T>::ParseNumber(bool inObjorArr)
{
if (inObjorArr) {
bool isFast = true;
#if ENABLE_V70_OPTIMIZATION
JSTaggedValue fastNumber = JSTaggedValue::Undefined();
bool isNumber = ReadNumberRange(isFast, fastNumber);
#else
int32_t fastInteger = 0;
bool isNumber = ReadNumberRange(isFast, fastInteger);
#endif
if (!isNumber) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Number in JSON Array Or Object",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
if (isFast) {
#if ENABLE_V70_OPTIMIZATION
return fastNumber;
#else
return parseOptions_.bigIntMode == BigIntMode::ALWAYS_PARSE_AS_BIGINT ?
BigInt::Int32ToBigInt(thread_, fastInteger).GetTaggedValue() : JSTaggedValue(fastInteger);
#endif
}
}
Text current = current_;
bool negative = false;
bool hasExponent = false;
bool hasDecimal = false;
if (*current_ == '-') {
if (UNLIKELY(current_++ == end_)) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Number in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
negative = true;
}
if (*current_ == '0') {
if (!CheckZeroBeginNumber(hasExponent, hasDecimal)) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Number in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
} else if (*current_ >= '1' && *current_ <= '9') {
if (!CheckNonZeroBeginNumber(hasExponent, hasDecimal)) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Number in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
} else {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Number in JSON",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
std::string strNum(current, end_ + 1);
current_ = end_ + 1;
return ConvertToNumber(strNum, negative, hasExponent, hasDecimal);
}
template<typename T>
JSTaggedValue JsonParser<T>::ConvertToNumber(const std::string &str, bool negative, bool hasExponent, bool hasDecimal)
{
errno = 0;
double v = std::strtod(str.c_str(), nullptr);
if (errno == ERANGE) {
errno = 0;
return JSTaggedValue(v);
}
errno = 0;
if (negative && v == 0) {
return JSTaggedValue(-0.0);
}
if (parseOptions_.bigIntMode == BigIntMode::DEFAULT) {
return JSTaggedValue::TryCastDoubleToInt32(v);
}
if (NumberHelper::IsSafeIntegerNumber(v)) {
if (parseOptions_.bigIntMode == BigIntMode::ALWAYS_PARSE_AS_BIGINT) {
if (v == 0.0) {
return BigInt::Int32ToBigInt(thread_, 0).GetTaggedValue();
}
JSTaggedValue value = BigInt::DoubleToBigInt(thread_, v);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
if (value.IsBigInt()) {
return value;
}
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Text in JSON: ConvertToNumber Fail",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
return JSTaggedValue::TryCastDoubleToInt32(v);
} else {
return (hasExponent || hasDecimal) ? JSTaggedValue::TryCastDoubleToInt32(v) :
NumberHelper::StringToBigInt(thread_, JSHandle<JSTaggedValue>::Cast(factory_->NewFromStdString(str)));
}
}
template<typename T>
bool JsonParser<T>::ParseStringLength(size_t &length, bool &isAscii, bool inObjOrArrOrMap)
{
#ifndef ENABLE_LINXKIT
#if ENABLE_V70_OPTIMIZATION
auto checkBackslash = [this](Text &text, Text last, bool &asciiFlag) {
return CheckBackslash(text, last, asciiFlag);
};
Text current = current_;
if constexpr (sizeof(T) == sizeof(uint8_t)) {
bool result = JsonPlatformHelper::ParseStringLengthForPlatformForUtf8(
length, isAscii, inObjOrArrOrMap, current, range_, end_, checkBackslash);
if (!result) {
current_ = current;
}
return result;
} else {
bool result = JsonPlatformHelper::ParseStringLengthForPlatformForUtf16(
length, isAscii, inObjOrArrOrMap, current, range_, end_, checkBackslash);
if (!result) {
current_ = current;
}
return result;
}
#else
Text last = inObjOrArrOrMap ? range_ : end_;
for (Text current = current_; current < last; ++current) {
T c = *current;
if (c == '"') {
if (inObjOrArrOrMap) {
end_ = current;
return true;
}
current_ = current;
return false;
} else if (c == '\\') {
if (UNLIKELY(!CheckBackslash(current, last, isAscii))) {
current_ = current;
return false;
}
} else if (UNLIKELY(c < CODE_SPACE)) {
current_ = current;
return false;
} else if (c > ASCII_END) {
ASSERT(sizeof(T) == sizeof(uint16_t));
isAscii = false;
}
++length;
}
return !inObjOrArrOrMap;
#endif
#else
return LinxkitParseStringLength(length, isAscii, inObjOrArrOrMap, end_, current_, range_);
#endif
}
template<typename T>
bool JsonParser<T>::CheckBackslash(Text &text, Text last, bool &isAscii)
{
ASSERT(*text == '\\');
++text;
if (text >= last) {
return false;
}
switch (*text) {
case '\"':
case '\\':
case '/':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
break;
case 'u': {
if (text + UNICODE_DIGIT_LENGTH >= last) {
return false;
};
T ucharFirst = *++text;
if (ucharFirst == '0') {
} else if ((ucharFirst >= '1' && ucharFirst <= '9') ||
(ucharFirst >= 'A' && ucharFirst <= 'F') || (ucharFirst >= 'a' && ucharFirst <= 'f')) {
isAscii = false;
} else {
return false;
}
T ucharSecond = *++text;
if (ucharSecond == '0') {
} else if ((ucharSecond >= '1' && ucharSecond <= '9') ||
(ucharSecond >= 'A' && ucharSecond <= 'F') || (ucharSecond >= 'a' && ucharSecond <= 'f')) {
isAscii = false;
} else {
return false;
}
bool thirdZero = false;
T ucharThird = *++text;
if (ucharThird == '0') {
thirdZero = true;
} else if (ucharThird >= '1' && ucharThird <= '7') {
} else if ((ucharThird >= '8' && ucharThird <= '9') ||
(ucharThird >= 'A' && ucharThird <= 'F') || (ucharThird >= 'a' && ucharThird <= 'f')) {
isAscii = false;
} else {
return false;
}
T ucharFourth = *++text;
if (thirdZero && ucharFourth == '0') {
isAscii = false;
} else if ((ucharFourth >= '0' && ucharFourth <= '9') ||
(ucharFourth >= 'A' && ucharFourth <= 'F') || (ucharFourth >= 'a' && ucharFourth <= 'f')) {
} else {
return false;
}
break;
}
default:
return false;
}
return true;
}
template<typename T>
template<typename Char>
void JsonParser<T>::ParseBackslash(Char *&p)
{
ASSERT(current_ < end_);
Advance();
switch (*current_) {
case '\"':
*p++ = '\"';
break;
case '\\':
*p++ = '\\';
break;
case '/':
*p++ = '/';
break;
case 'b':
*p++ = '\b';
break;
case 'f':
*p++ = '\f';
break;
case 'n':
*p++ = '\n';
break;
case 'r':
*p++ = '\r';
break;
case 't':
*p++ = '\t';
break;
case 'u': {
ASSERT(end_ - current_ >= UNICODE_DIGIT_LENGTH);
uint16_t res = 0;
for (size_t pos = 0; pos < UNICODE_DIGIT_LENGTH; pos++) {
Advance();
T uchar = *current_;
if (uchar >= '0' && uchar <= '9') {
res *= NUMBER_SIXTEEN;
res += (uchar - '0');
} else if (uchar >= 'a' && uchar <= 'f') {
res *= NUMBER_SIXTEEN;
res += (uchar - 'a' + NUMBER_TEN);
} else if (uchar >= 'A' && uchar <= 'F') {
res *= NUMBER_SIXTEEN;
res += (uchar - 'A' + NUMBER_TEN);
} else {
UNREACHABLE();
}
}
ASSERT(sizeof(Char) == sizeof(uint16_t) || res <= ASCII_END);
*p++ = static_cast<Char>(res);
break;
}
default:
UNREACHABLE();
}
}
template<typename T>
template<typename Char>
void JsonParser<T>::CopyCharWithBackslash(Char *&p)
{
#if ENABLE_V70_OPTIMIZATION
auto parseBackslash = [this](Char *&output) {
ParseBackslash(output);
};
if constexpr (sizeof(T) == sizeof(uint8_t)) {
JsonPlatformHelper::CopyCharWithBackslashForPlatformForUtf8(p, current_, end_, parseBackslash);
} else {
JsonPlatformHelper::CopyCharWithBackslashForPlatformForUtf16(p, current_, end_, parseBackslash);
}
#else
while (current_ <= end_) {
T c = *current_;
ASSERT(c >= CODE_SPACE);
ASSERT(sizeof(Char) == sizeof(uint16_t) || c <= ASCII_END);
if (c == '\\') {
ParseBackslash(p);
} else {
*p++ = c;
}
Advance();
}
#endif
}
template<typename T>
JSHandle<JSTaggedValue> JsonParser<T>::ParseStringWithBackslash(bool inObjOrArrOrMap)
{
size_t length = 0;
bool isAscii = true;
if (UNLIKELY(!ParseStringLength(length, isAscii, inObjOrArrOrMap))) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected string in JSON",
JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Exception()),
rawString_,
current_ - begin_ - slicedOffset_);
}
end_--;
if (isAscii) {
EcmaString *str = EcmaStringAccessor::CreateLineString(thread_->GetEcmaVM(), length, true);
uint8_t *data = const_cast<uint8_t *>(EcmaStringAccessor(str).GetDataUtf8());
uint8_t *p = data;
#ifndef ENABLE_LINXKIT
CopyCharWithBackslash(p);
#else
static_assert(sizeof(T) == sizeof(uint16_t) || sizeof(T) == sizeof(uint8_t),
"T must be 1 or 2 bytes in size");
LinxkitCopyCharWithBackslashTo8(p, end_, current_);
#endif
ASSERT(static_cast<size_t>(p - data) == length);
Advance();
return JSHandle<JSTaggedValue>(thread_, str);
} else {
EcmaString *str = EcmaStringAccessor::CreateLineString(thread_->GetEcmaVM(), length, false);
uint16_t *data = const_cast<uint16_t *>(EcmaStringAccessor(str).GetDataUtf16());
uint16_t *p = data;
#ifndef ENABLE_LINXKIT
CopyCharWithBackslash(p);
#else
LinxkitCopyCharWithBackslashTo16(p, end_, current_);
#endif
ASSERT(static_cast<size_t>(p - data) == length);
Advance();
return JSHandle<JSTaggedValue>(thread_, str);
}
}
template<typename T>
void JsonParser<T>::SkipEndWhiteSpace()
{
while (current_ != range_) {
if (*end_ == ' ' || *end_ == '\r' || *end_ == '\n' || *end_ == '\t') {
end_--;
} else {
break;
}
}
}
template<typename T>
void JsonParser<T>::SkipStartWhiteSpace()
{
while (current_ != range_) {
if (*current_ == ' ' || *current_ == '\r' || *current_ == '\n' || *current_ == '\t') {
Advance();
} else {
break;
}
}
}
template<typename T>
void JsonParser<T>::GetNextNonSpaceChar()
{
Advance();
SkipStartWhiteSpace();
}
template<typename T>
Tokens JsonParser<T>::ParseToken()
{
switch (*current_) {
case '{':
return parseOptions_.returnType == ParseReturnType::MAP ? Tokens::MAP : Tokens::OBJECT;
case '[':
return Tokens::ARRAY;
case '"':
return Tokens::STRING;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
return Tokens::NUMBER;
case 't':
return Tokens::LITERAL_TRUE;
case 'f':
return Tokens::LITERAL_FALSE;
case 'n':
return Tokens::LITERAL_NULL;
default:
return Tokens::TOKEN_ILLEGAL;
}
}
template<typename T>
JSTaggedValue JsonParser<T>::ParseLiteralTrue()
{
static const char literalTrue[] = "true";
uint32_t remainingLength = range_ - current_;
if (UNLIKELY(remainingLength < 3)) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Text in JSON: ParseLiteralTrue Fail",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
bool isMatch = MatchText(literalTrue, 4);
if (LIKELY(isMatch)) {
return JSTaggedValue::True();
}
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Text in JSON: ParseLiteralTrue Fail",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
template<typename T>
JSTaggedValue JsonParser<T>::ParseLiteralFalse()
{
static const char literalFalse[] = "false";
uint32_t remainingLength = range_ - current_;
if (UNLIKELY(remainingLength < 4)) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Text in JSON: ParseLiteralFalse Fail",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
bool isMatch = MatchText(literalFalse, 5);
if (LIKELY(isMatch)) {
return JSTaggedValue::False();
}
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Text in JSON: ParseLiteralFalse Fail",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
template<typename T>
JSTaggedValue JsonParser<T>::ParseLiteralNull()
{
static const char literalNull[] = "null";
uint32_t remainingLength = range_ - current_;
if (UNLIKELY(remainingLength < 3)) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Text in JSON: ParseLiteralNull Fail",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
bool isMatch = MatchText(literalNull, 4);
if (LIKELY(isMatch)) {
return JSTaggedValue::Null();
}
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected Text in JSON: ParseLiteralNull Fail",
JSTaggedValue::Exception(),
rawString_,
current_ - begin_ - slicedOffset_);
}
template<typename T>
bool JsonParser<T>::MatchText(const char *str, uint32_t matchLen)
{
for (uint32_t pos = 1; pos < matchLen; ++pos) {
if (current_[pos] != str[pos]) {
return false;
}
}
current_ += matchLen;
return true;
}
#if ENABLE_V70_OPTIMIZATION
template<typename T>
bool JsonParser<T>::ReadNumberRange(bool &isFast, JSTaggedValue &fastNumber)
{
Text current = current_;
bool negative = false;
if (*current == '-') {
current++;
negative = true;
}
if (UNLIKELY(current > range_)) {
return false;
}
if (*current == '0') {
if (TryReadFastZeroNumber(current, negative, fastNumber)) {
return true;
}
} else if (*current >= '1' && *current <= '9') {
if (TryReadFastNonZeroNumber(current, negative, fastNumber)) {
return true;
}
} else {
return false;
}
if (UNLIKELY(current > range_)) {
current_ = range_;
return false;
}
isFast = false;
return FinishReadNumberRange(current, isFast);
}
template<typename T>
bool JsonParser<T>::HasValidNumberTerminator(Text numberEnd)
{
Text cursor = numberEnd;
while (cursor != range_) {
if (*cursor == ' ' || *cursor == '\r' || *cursor == '\n' || *cursor == '\t') {
cursor++;
continue;
}
return *cursor == ',' || *cursor == ']' || *cursor == '}';
}
return *cursor == ',' || *cursor == ']' || *cursor == '}';
}
template<typename T>
bool JsonParser<T>::TryReadFastZeroNumber(Text ¤t, bool negative, JSTaggedValue &fastNumber)
{
Text numberEnd = current + 1;
if (numberEnd > range_) {
return false;
}
if (numberEnd <= range_ && !IsNumberCharacter(*numberEnd) && !IsNumberSignalCharacter(*numberEnd) &&
HasValidNumberTerminator(numberEnd)) {
current_ = numberEnd;
if (negative) {
fastNumber = JSTaggedValue(-0.0);
} else if (parseOptions_.bigIntMode == BigIntMode::ALWAYS_PARSE_AS_BIGINT) {
fastNumber = BigInt::Int32ToBigInt(thread_, 0).GetTaggedValue();
} else {
fastNumber = JSTaggedValue(0);
}
return true;
}
current = numberEnd;
return false;
}
template<typename T>
bool JsonParser<T>::TryReadFastNonZeroNumber(Text current, bool negative, JSTaggedValue &fastNumber)
{
Text advance = AdvanceLastNumberCharacter(current);
if (UNLIKELY(current == advance)) {
return false;
}
if (advance > range_ || IsNumberSignalCharacter(*advance) || !HasValidNumberTerminator(advance)) {
return false;
}
uint64_t value = 0;
if (!TryAccumulateFastInteger(current, advance, value)) {
return false;
}
current_ = advance;
fastNumber = CreateFastNumberValue(thread_, parseOptions_, negative, value);
return true;
}
template<typename T>
bool JsonParser<T>::FinishReadNumberRange(Text current, bool &isFast)
{
while (current != range_) {
if (IsNumberCharacter(*current)) {
current++;
continue;
}
if (IsNumberSignalCharacter(*current)) {
isFast = false;
current++;
continue;
}
Text end = current;
while (current != range_) {
if (*current == ' ' || *current == '\r' || *current == '\n' || *current == '\t') {
current++;
} else if (*current == ',' || *current == ']' || *current == '}') {
end_ = end - 1;
return true;
} else {
return false;
}
}
if (*current == ']' || *current == '}') {
end_ = end - 1;
return true;
}
return false;
}
if (*current == ',' || *current == ']' || *current == '}') {
end_ = range_ - 1;
return true;
}
current_ = current;
return false;
}
#else
template<typename T>
bool JsonParser<T>::ReadNumberRange(bool &isFast, int32_t &fastInteger)
{
Text current = current_;
int32_t sign = 1;
if (*current == '-') {
current++;
sign = -1;
}
if (*current == '0') {
isFast = false;
current++;
} else {
Text advance = AdvanceLastNumberCharacter(current);
if (UNLIKELY(current == advance)) {
return false;
}
size_t numberLength = advance - current;
int32_t i = 0;
if (numberLength <= INTEGER_MAX_LEN && (*advance == ',' || *advance == ']' || *advance == '}')) {
for (; current != advance; current++) {
i = (i * 10U) + ((*current) - '0');
}
fastInteger = i * sign;
current_ = advance;
return true;
}
isFast = false;
}
while (current != range_) {
if (IsNumberCharacter(*current)) {
current++;
continue;
} else if (IsNumberSignalCharacter(*current)) {
isFast = false;
current++;
continue;
}
Text end = current;
while (current != range_) {
if (*current == ' ' || *current == '\r' || *current == '\n' || *current == '\t') {
current++;
} else if (*current == ',' || *current == ']' || *current == '}') {
end_ = end - 1;
return true;
} else {
return false;
}
}
if (*current == ']' || *current == '}') {
end_ = end - 1;
return true;
}
return false;
}
if (*current == ',' || *current == ']' || *current == '}') {
end_ = range_ - 1;
return true;
}
current_ = current;
return false;
}
#endif
template<typename T>
typename JsonParser<T>::Text JsonParser<T>::AdvanceLastNumberCharacter(Text current)
{
return std::find_if(current, range_, [this](T c) { return !IsNumberCharacter(c); });
}
template<typename T>
bool JsonParser<T>::IsNumberCharacter(T ch)
{
if (ch >= '0' && ch <= '9') {
return true;
}
return false;
}
template<typename T>
bool JsonParser<T>::IsNumberSignalCharacter(T ch)
{
return ch == '.' || ch == 'e' || ch == 'E' || ch == '+' || ch == '-';
}
template<typename T>
bool JsonParser<T>::IsExponentNumber()
{
if (IsNumberCharacter(*current_)) {
return true;
} else if (*current_ == '-' || *current_ == '+') {
if (current_ == end_) {
return false;
}
Advance();
if (IsNumberCharacter(*current_)) {
return true;
}
}
return false;
}
template<typename T>
bool JsonParser<T>::IsDecimalsLegal(bool &hasExponent)
{
if (current_ == end_ && !IsNumberCharacter(*++current_)) {
return false;
}
while (current_ != end_) {
Advance();
if (IsNumberCharacter(*current_)) {
continue;
} else if (*current_ == 'e' || *current_ == 'E') {
if (hasExponent || current_ == end_) {
return false;
}
Advance();
if (!IsExponentNumber()) {
return false;
}
hasExponent = true;
} else {
return false;
}
}
return true;
}
template<typename T>
bool JsonParser<T>::IsExponentLegal(bool &hasExponent)
{
if (hasExponent || current_ == end_) {
return false;
}
Advance();
if (!IsExponentNumber()) {
return false;
}
while (current_ != end_) {
Advance();
if (!IsNumberCharacter(*current_)) {
return false;
}
}
return true;
}
template<typename T>
bool JsonParser<T>::CheckZeroBeginNumber(bool &hasExponent, bool &hasDecimal)
{
if (current_++ != end_) {
if (*current_ == '.') {
hasDecimal = true;
if (!IsDecimalsLegal(hasExponent)) {
return false;
}
} else if (*current_ == 'e' || *current_ == 'E') {
if (!IsExponentLegal(hasExponent)) {
return false;
}
} else {
return false;
}
}
return true;
}
template<typename T>
bool JsonParser<T>::CheckNonZeroBeginNumber(bool &hasExponent, bool &hasDecimal)
{
while (current_ != end_) {
Advance();
if (IsNumberCharacter(*current_)) {
continue;
} else if (*current_ == '.') {
hasDecimal = true;
if (!IsDecimalsLegal(hasExponent)) {
return false;
}
} else if (*current_ == 'e' || *current_ == 'E') {
if (!IsExponentLegal(hasExponent)) {
return false;
}
} else {
return false;
}
}
return true;
}
JSHandle<JSTaggedValue> Utf8JsonParser::Parse(const JSHandle<EcmaString> &strHandle)
{
ASSERT(*strHandle != nullptr);
#if ENABLE_V70_OPTIMIZATION
ResetObjectKeyCache();
#endif
auto stringAccessor = EcmaStringAccessor(strHandle);
uint32_t len = stringAccessor.GetLength();
ASSERT(len != UINT32_MAX);
rawString_ = strHandle;
if (LIKELY(stringAccessor.IsLineString())) {
sourceString_ = strHandle;
} else if (stringAccessor.IsCachedExternalString()) {
sourceString_ = thread_->GetEcmaVM()->GetFactory()->NewFromUtf8LiteralCompress(
stringAccessor.GetDataUtf8(), len);
} else if (stringAccessor.IsSlicedString()) {
auto *sliced = static_cast<SlicedEcmaString *>(*strHandle);
slicedOffset_ = sliced->GetStartIndex();
sourceString_ = JSHandle<EcmaString>(thread_, EcmaString::Cast(sliced->GetParent(thread_)));
} else {
auto *flatten = EcmaStringAccessor::Flatten(thread_->GetEcmaVM(), strHandle);
sourceString_ = JSHandle<EcmaString>(thread_, flatten);
}
begin_ = EcmaStringAccessor(sourceString_).GetDataUtf8();
auto *heap = const_cast<Heap *>(thread_->GetEcmaVM()->GetHeap());
auto listenerId = heap->AddGCListener(UpdatePointersListener, this);
auto res = Launch(begin_ + slicedOffset_, begin_ + slicedOffset_ + len);
heap->RemoveGCListener(listenerId);
return res;
}
void Utf8JsonParser::ParticalParseString(std::string& str, Text current, Text nextCurrent)
{
str += std::string_view(reinterpret_cast<const char *>(current), nextCurrent - current);
}
void Utf8JsonParser::UpdatePointersListener(void *utf8Parser)
{
auto *parser = reinterpret_cast<Utf8JsonParser *>(utf8Parser);
auto *begin = EcmaStringAccessor(parser->sourceString_).GetDataUtf8();
if (parser->begin_ != begin) {
uint32_t currentOffset = parser->current_ - parser->begin_;
uint32_t endOffset = parser->end_ - parser->begin_;
uint32_t rangeOffset = parser->range_ - parser->begin_;
parser->current_ = reinterpret_cast<uint8_t *>(ToUintPtr(begin) + currentOffset);
parser->end_ = reinterpret_cast<uint8_t *>(ToUintPtr(begin) + endOffset);
parser->range_ = reinterpret_cast<uint8_t *>(ToUintPtr(begin) + rangeOffset);
parser->begin_ = begin;
}
}
#if ENABLE_V70_OPTIMIZATION
JSHandle<JSTaggedValue> Utf8JsonParser::ParseObjectKey()
{
if (UNLIKELY(!EcmaStringAccessor(sourceString_).IsLineOrCachedExternalString())) {
return ParseString(true);
}
bool isFastString = true;
Text stringStart = current_ + 1;
if (UNLIKELY(!ReadJsonStringRange(isFastString))) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected end Text in JSON",
JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Exception()),
rawString_,
current_ - begin_ - slicedOffset_);
}
if (LIKELY(isFastString)) {
uint32_t offset = stringStart - begin_;
uint32_t strLength = end_ - stringStart;
ASSERT(strLength <= static_cast<size_t>(UINT32_MAX));
return ParseCachedObjectKey(offset, strLength);
}
return ParseEscapedObjectKey();
}
JSHandle<JSTaggedValue> Utf8JsonParser::ParseCachedObjectKey(uint32_t offset, uint32_t strLength)
{
auto *vm = thread_->GetEcmaVM();
auto *stringTable = vm->GetEcmaStringTable();
EcmaStringAccessor sourceAccessor(sourceString_);
const uint8_t *utf8Data = sourceAccessor.GetDataUtf8() + offset;
current_ = end_ + 1;
if (strLength == 0) {
return JSHandle<JSTaggedValue>::Cast(factory_->GetEmptyString());
}
if (strLength == 1 && EcmaStringAccessor::IsASCIICharacter(utf8Data[0])) {
int32_t ch = static_cast<int32_t>(utf8Data[0]);
JSHandle<SingleCharTable> singleCharTable(thread_, thread_->GetSingleCharTable());
return JSHandle<JSTaggedValue>(thread_, singleCharTable->GetStringFromSingleCharTable(thread_, ch));
}
bool shouldCache = strLength <= K_UTF8_OBJECT_KEY_CACHE_MAX_LEN;
PackedKey128 packed;
size_t cacheIndex = 0;
if (shouldCache) {
packed = PackUtf8ObjectKeyBytes(utf8Data, strLength);
cacheIndex = Utf8ObjectKeyCacheIndex(packed, strLength);
auto cachedKey = LookupObjectKeyCacheEntry(objectKeyCache_, cacheIndex, packed, strLength);
if (!cachedKey.IsEmpty()) {
return JSHandle<JSTaggedValue>::Cast(cachedKey);
}
}
JSHandle<EcmaString> key(thread_, sourceAccessor.IsLineString() ?
stringTable->GetOrInternStringFromCompressedSubString(vm, sourceString_, offset, strLength) :
stringTable->GetOrInternString(vm, utf8Data, strLength, true));
if (shouldCache) {
UpdateObjectKeyCacheEntry(objectKeyCache_, cacheIndex, packed, strLength, key);
}
return JSHandle<JSTaggedValue>::Cast(key);
}
JSHandle<JSTaggedValue> Utf8JsonParser::ParseEscapedObjectKey()
{
auto *stringTable = thread_->GetEcmaVM()->GetEcmaStringTable();
auto keyValue = ParseStringWithBackslash(true);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
auto decodedKey = JSHandle<EcmaString>::Cast(keyValue);
auto key = decodedKey;
EcmaStringAccessor keyAccessor(decodedKey);
if (!keyAccessor.IsInternString()) {
EcmaString *internKey = stringTable->TryGetInternString(thread_, decodedKey);
if (internKey == nullptr) {
internKey = factory_->InternString(JSHandle<JSTaggedValue>::Cast(decodedKey));
}
key = JSHandle<EcmaString>(thread_, internKey);
}
UpdateObjectKeyCacheFromDecodedKey(decodedKey, key);
return JSHandle<JSTaggedValue>::Cast(key);
}
void Utf8JsonParser::ResetObjectKeyCache()
{
ResetObjectKeyCacheEntries(objectKeyCache_);
}
void Utf8JsonParser::UpdateObjectKeyCache(const uint8_t *utf8Data, uint32_t utf8Len,
const JSHandle<EcmaString> &key)
{
if (utf8Len <= 1 || utf8Len > K_UTF8_OBJECT_KEY_CACHE_MAX_LEN) {
return;
}
PackedKey128 packed = PackUtf8ObjectKeyBytes(utf8Data, utf8Len);
size_t index = Utf8ObjectKeyCacheIndex(packed, utf8Len);
UpdateObjectKeyCacheEntry(objectKeyCache_, index, packed, utf8Len, key);
}
void Utf8JsonParser::UpdateObjectKeyCacheFromDecodedKey(const JSHandle<EcmaString> &decodedKey,
const JSHandle<EcmaString> &internKey)
{
EcmaStringAccessor decodedAccessor(decodedKey);
if (!decodedAccessor.IsUtf8()) {
return;
}
uint32_t utf8Len = decodedAccessor.GetLength();
if (utf8Len <= 1 || utf8Len > K_UTF8_OBJECT_KEY_CACHE_MAX_LEN) {
return;
}
if (UNLIKELY(!decodedAccessor.IsLineOrCachedExternalString())) {
return;
}
UpdateObjectKeyCache(decodedAccessor.GetDataUtf8(), utf8Len, internKey);
}
#endif
#if ENABLE_V70_OPTIMIZATION
JSHandle<JSTaggedValue> Utf8JsonParser::ParseString(bool inObjOrArrOrMap)
{
bool isFastString = true;
Text stringStart = current_ + 1;
if (inObjOrArrOrMap) {
if (UNLIKELY(!ReadJsonStringRange(isFastString))) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected end Text in JSON",
JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Exception()),
rawString_,
current_ - begin_ - slicedOffset_);
}
} else if (UNLIKELY(*end_ != '"' || current_ == end_)) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected end Text in JSON",
JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Exception()),
rawString_,
end_ - begin_ - slicedOffset_);
} else if (UNLIKELY(!IsFastParseJsonString(isFastString))) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected end Text in JSON",
JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Exception()),
rawString_,
current_ - begin_ - slicedOffset_);
}
if (LIKELY(isFastString && EcmaStringAccessor(sourceString_).IsLineString())) {
uint32_t offset = stringStart - begin_;
uint32_t strLength = end_ - stringStart;
ASSERT(strLength <= static_cast<size_t>(UINT32_MAX));
return ParseFastStringValue(offset, strLength, inObjOrArrOrMap);
}
return ParseStringWithBackslash(inObjOrArrOrMap);
}
JSHandle<JSTaggedValue> Utf8JsonParser::ParseFastStringValue(uint32_t offset, uint32_t strLength,
bool inObjOrArrOrMap)
{
current_ = end_ + 1;
auto *utf8Data = EcmaStringAccessor(sourceString_).GetDataUtf8() + offset;
if (strLength == 1 && EcmaStringAccessor::IsASCIICharacter(utf8Data[0])) {
int32_t ch = static_cast<int32_t>(utf8Data[0]);
JSHandle<SingleCharTable> singleCharTable(thread_, thread_->GetSingleCharTable());
return JSHandle<JSTaggedValue>(thread_, singleCharTable->GetStringFromSingleCharTable(thread_, ch));
}
if (inObjOrArrOrMap) {
return JSHandle<JSTaggedValue>::Cast(factory_->NewCompressedUtf8SubString(
sourceString_, offset, strLength));
}
return JSHandle<JSTaggedValue>::Cast(factory_->NewFromUtf8LiteralCompressSubString(
sourceString_, offset, strLength));
}
#else
JSHandle<JSTaggedValue> Utf8JsonParser::ParseString(bool inObjOrArrOrMap)
{
bool isFastString = true;
if (inObjOrArrOrMap) {
if (UNLIKELY(!ReadJsonStringRange(isFastString))) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected end Text in JSON",
JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Exception()),
rawString_,
current_ - begin_ - slicedOffset_);
}
if (isFastString) {
uint32_t offset = current_ - begin_;
uint32_t strLength = end_ - current_;
ASSERT(strLength <= static_cast<size_t>(UINT32_MAX));
current_ = end_ + 1;
return JSHandle<JSTaggedValue>::Cast(factory_->NewCompressedUtf8SubString(
sourceString_, offset, strLength));
}
} else {
if (UNLIKELY(*end_ != '"' || current_ == end_)) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected end Text in JSON",
JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Exception()),
rawString_,
end_ - begin_ - slicedOffset_);
} else if (UNLIKELY(!IsFastParseJsonString(isFastString))) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected end Text in JSON",
JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Exception()),
rawString_,
current_ - begin_ - slicedOffset_);
}
if (LIKELY(isFastString)) {
uint32_t offset = current_ - begin_;
uint32_t strLength = end_ - current_;
ASSERT(strLength <= static_cast<size_t>(UINT32_MAX));
current_ = end_ + 1;
auto *utf8Data = EcmaStringAccessor(sourceString_).GetDataUtf8() + offset;
if (strLength == 1 && EcmaStringAccessor::IsASCIICharacter(utf8Data[0])) {
int32_t ch = static_cast<int32_t>(utf8Data[0]);
JSHandle<SingleCharTable> singleCharTable(thread_, thread_->GetSingleCharTable());
return JSHandle<JSTaggedValue>(thread_, singleCharTable->GetStringFromSingleCharTable(thread_, ch));
}
return JSHandle<JSTaggedValue>::Cast(factory_->NewFromUtf8LiteralCompressSubString(
sourceString_, offset, strLength));
}
}
return ParseStringWithBackslash(inObjOrArrOrMap);
}
#endif
bool Utf8JsonParser::ReadJsonStringRange(bool &isFastString)
{
Advance();
Text current = current_;
bool result = JsonPlatformHelper::ReadJsonStringRangeForPlatformForUtf8(isFastString, current, range_, end_);
if (!result) {
current_ = current;
}
return result;
}
bool Utf8JsonParser::IsFastParseJsonString(bool &isFastString)
{
Advance();
for (Text current = current_; current != end_; ++current) {
if (*current < CODE_SPACE || *current == '"') {
current_ = current;
return false;
} else if (*current == '\\') {
isFastString = false;
return true;
}
}
return true;
}
JSHandle<JSTaggedValue> Utf16JsonParser::Parse(EcmaString *str)
{
ASSERT(str != nullptr);
#if ENABLE_V70_OPTIMIZATION
ResetObjectKeyCache();
#else
#endif
uint32_t len = EcmaStringAccessor(str).GetLength();
CVector<uint16_t> buf(len + 1, 0);
EcmaStringAccessor(str).WriteToFlatUtf16(thread_, buf.data(), len);
Text begin = buf.data();
begin_ = begin;
rawString_ = JSHandle<EcmaString>(thread_, str);
slicedOffset_ = 0;
return Launch(begin, begin + len);
}
void Utf16JsonParser::ParticalParseString(std::string& str, Text current, Text nextCurrent)
{
str += StringHelper::U16stringToString(std::u16string(current, nextCurrent));
}
#if ENABLE_V70_OPTIMIZATION
JSHandle<JSTaggedValue> Utf16JsonParser::ParseObjectKey()
{
bool isFastString = true;
bool isAscii = true;
Text stringStart = current_ + 1;
if (UNLIKELY(!ReadJsonStringRange(isFastString, isAscii))) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected end Text in JSON",
JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Exception()),
rawString_,
current_ - begin_ - slicedOffset_);
}
if (LIKELY(isFastString)) {
const uint16_t *utf16Data = stringStart;
uint32_t strLength = end_ - stringStart;
ASSERT(strLength <= static_cast<size_t>(UINT32_MAX));
return ParseCachedObjectKey(utf16Data, strLength, isAscii);
}
return ParseEscapedObjectKey();
}
JSHandle<JSTaggedValue> Utf16JsonParser::ParseCachedObjectKey(const uint16_t *utf16Data, uint32_t strLength,
bool isAscii)
{
auto *vm = thread_->GetEcmaVM();
auto *stringTable = vm->GetEcmaStringTable();
current_ = end_ + 1;
if (strLength == 0) {
return JSHandle<JSTaggedValue>::Cast(factory_->GetEmptyString());
}
if (strLength == 1 && isAscii && utf16Data[0] <= ASCII_END) {
int32_t ch = static_cast<int32_t>(utf16Data[0]);
JSHandle<SingleCharTable> singleCharTable(thread_, thread_->GetSingleCharTable());
return JSHandle<JSTaggedValue>(thread_, singleCharTable->GetStringFromSingleCharTable(thread_, ch));
}
bool shouldCache = strLength <= K_UTF16_OBJECT_KEY_CACHE_MAX_LEN;
PackedKey128 packed;
size_t cacheIndex = 0;
if (shouldCache) {
packed = PackUtf16ObjectKeyCodeUnits(utf16Data, strLength);
cacheIndex = Utf16ObjectKeyCacheIndex(packed, strLength);
auto cachedKey = LookupObjectKeyCacheEntry(objectKeyCache_, cacheIndex, packed, strLength);
if (!cachedKey.IsEmpty()) {
return JSHandle<JSTaggedValue>::Cast(cachedKey);
}
}
JSHandle<EcmaString> key(thread_, stringTable->GetOrInternString(vm, utf16Data, strLength, isAscii));
if (shouldCache) {
UpdateObjectKeyCacheEntry(objectKeyCache_, cacheIndex, packed, strLength, key);
}
return JSHandle<JSTaggedValue>::Cast(key);
}
JSHandle<JSTaggedValue> Utf16JsonParser::ParseEscapedObjectKey()
{
auto *stringTable = thread_->GetEcmaVM()->GetEcmaStringTable();
auto keyValue = ParseStringWithBackslash(true);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
auto decodedKey = JSHandle<EcmaString>::Cast(keyValue);
auto key = decodedKey;
EcmaStringAccessor keyAccessor(decodedKey);
if (!keyAccessor.IsInternString()) {
EcmaString *internKey = stringTable->TryGetInternString(thread_, decodedKey);
if (internKey == nullptr) {
internKey = factory_->InternString(JSHandle<JSTaggedValue>::Cast(decodedKey));
}
key = JSHandle<EcmaString>(thread_, internKey);
}
UpdateObjectKeyCacheFromDecodedKey(decodedKey, key);
return JSHandle<JSTaggedValue>::Cast(key);
}
void Utf16JsonParser::ResetObjectKeyCache()
{
ResetObjectKeyCacheEntries(objectKeyCache_);
}
void Utf16JsonParser::UpdateObjectKeyCache(const uint16_t *utf16Data, uint32_t utf16Len,
const JSHandle<EcmaString> &key)
{
if (utf16Len <= 1 || utf16Len > K_UTF16_OBJECT_KEY_CACHE_MAX_LEN) {
return;
}
PackedKey128 packed = PackUtf16ObjectKeyCodeUnits(utf16Data, utf16Len);
size_t index = Utf16ObjectKeyCacheIndex(packed, utf16Len);
UpdateObjectKeyCacheEntry(objectKeyCache_, index, packed, utf16Len, key);
}
void Utf16JsonParser::UpdateObjectKeyCacheFromDecodedKey(const JSHandle<EcmaString> &decodedKey,
const JSHandle<EcmaString> &internKey)
{
EcmaStringAccessor decodedAccessor(decodedKey);
uint32_t utf16Len = decodedAccessor.GetLength();
if (utf16Len <= 1 || utf16Len > K_UTF16_OBJECT_KEY_CACHE_MAX_LEN) {
return;
}
if (UNLIKELY(!decodedAccessor.IsLineOrCachedExternalString())) {
return;
}
PackedKey128 packed = PackUtf16ObjectKeyFromAccessor(decodedAccessor, utf16Len);
size_t index = Utf16ObjectKeyCacheIndex(packed, utf16Len);
UpdateObjectKeyCacheEntry(objectKeyCache_, index, packed, utf16Len, internKey);
}
#endif
JSHandle<JSTaggedValue> Utf16JsonParser::ParseString(bool inObjOrArrOrMap)
{
bool isFastString = true;
bool isAscii = true;
if (inObjOrArrOrMap) {
Text stringStart = current_ + 1;
if (UNLIKELY(!ReadJsonStringRange(isFastString, isAscii))) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected end Text in JSON",
JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Exception()),
rawString_,
current_ - begin_ - slicedOffset_);
}
if (isFastString) {
if (isAscii) {
std::string value(stringStart, end_);
current_ = end_ + 1;
ASSERT(value.size() <= static_cast<size_t>(UINT32_MAX));
return JSHandle<JSTaggedValue>::Cast(factory_->NewFromUtf8LiteralCompress(
reinterpret_cast<const uint8_t *>(value.c_str()), value.size()));
}
std::u16string_view value(reinterpret_cast<const char16_t *>(stringStart), end_ - stringStart);
current_ = end_ + 1;
ASSERT(value.size() <= static_cast<size_t>(UINT32_MAX));
return JSHandle<JSTaggedValue>::Cast(factory_->NewFromUtf16LiteralNotCompress(
reinterpret_cast<const uint16_t *>(value.data()), value.size()));
}
} else {
if (UNLIKELY(*end_ != '"' || current_ == end_)) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected end Text in JSON",
JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Exception()),
rawString_,
end_ - begin_ - slicedOffset_);
} else if (!IsFastParseJsonString(isFastString, isAscii)) {
THROW_JSON_SYNTAX_ERROR_AND_RETURN(thread_,
"Unexpected end Text in JSON",
JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Exception()),
rawString_,
current_ - begin_ - slicedOffset_);
}
if (LIKELY(isFastString)) {
if (isAscii) {
std::string value(current_, end_);
ASSERT(value.size() <= static_cast<size_t>(UINT32_MAX));
current_ = end_ + 1;
return JSHandle<JSTaggedValue>::Cast(factory_->NewFromUtf8LiteralCompress(
reinterpret_cast<const uint8_t *>(value.c_str()), value.size()));
}
std::u16string_view value(reinterpret_cast<const char16_t *>(current_), end_ - current_);
ASSERT(value.size() <= static_cast<size_t>(UINT32_MAX));
current_ = end_ + 1;
return JSHandle<JSTaggedValue>::Cast(factory_->NewFromUtf16LiteralNotCompress(
reinterpret_cast<const uint16_t *>(value.data()), value.size()));
}
}
return ParseStringWithBackslash(inObjOrArrOrMap);
}
bool Utf16JsonParser::ReadJsonStringRange(bool &isFastString, bool &isAscii)
{
Advance();
#if ENABLE_V70_OPTIMIZATION
Text current = current_;
bool result = JsonPlatformHelper::ReadJsonStringRangeForPlatformForUtf16(
isFastString, isAscii, current, range_, end_);
if (!result) {
current_ = current;
}
return result;
#else
for (Text current = current_; current != range_; ++current) {
uint16_t c = *current;
if (c == '"') {
end_ = current;
return true;
} else if (UNLIKELY(c == '\\')) {
isFastString = false;
return true;
}
if (!IsLegalAsciiCharacter(c, isAscii)) {
current_ = current;
return false;
}
}
current_ = range_;
return false;
#endif
}
bool Utf16JsonParser::IsFastParseJsonString(bool &isFastString, bool &isAscii)
{
Advance();
for (Text current = current_; current != end_; ++current) {
if (!IsLegalAsciiCharacter(*current, isAscii) || *current == '"') {
current_ = current;
return false;
}
if (*current == '\\') {
isFastString = false;
return true;
}
}
return true;
}
bool Utf16JsonParser::IsLegalAsciiCharacter(uint16_t c, bool &isAscii)
{
if (c <= ASCII_END) {
return c >= CODE_SPACE ? true : false;
}
isAscii = false;
return true;
}
JSHandle<JSTaggedValue> Internalize::InternalizeJsonProperty(JSThread *thread, const JSHandle<JSObject> &holder,
const JSHandle<JSTaggedValue> &name,
const JSHandle<JSTaggedValue> &receiver,
TransformType transformType)
{
JSHandle<JSTaggedValue> objHandle(holder);
JSHandle<JSTaggedValue> val = JSTaggedValue::GetProperty(thread, objHandle, name).GetValue();
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
if (val->IsECMAObject()) {
JSHandle<JSObject> obj = JSTaggedValue::ToObject(thread, val);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
bool isArray = val->IsArray(thread);
if (isArray) {
JSHandle<JSTaggedValue> lenResult = JSTaggedValue::GetProperty(thread, val, lengthKey).GetValue();
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread, lenResult);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
uint32_t length = lenNumber.ToUint32();
JSMutableHandle<JSTaggedValue> keyUnknow(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> keyName(thread, JSTaggedValue::Undefined());
for (uint32_t i = 0; i < length; i++) {
keyUnknow.Update(JSTaggedValue(i));
keyName.Update(JSTaggedValue::ToString(thread, keyUnknow).GetTaggedValue());
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
RecurseAndApply(thread, obj, keyName, receiver, transformType);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
}
} else {
JSHandle<TaggedArray> ownerNames(JSObject::EnumerableOwnNames(thread, obj));
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
uint32_t namesLength = ownerNames->GetLength();
JSMutableHandle<JSTaggedValue> keyName(thread, JSTaggedValue::Undefined());
for (uint32_t i = 0; i < namesLength; i++) {
keyName.Update(ownerNames->Get(thread, i));
RecurseAndApply(thread, obj, keyName, receiver, transformType);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
}
}
}
const uint32_t argsLength = 2;
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, receiver, objHandle, undefined, argsLength);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
info->SetCallArg(name.GetTaggedValue(), val.GetTaggedValue());
JSTaggedValue result = JSFunction::Call(info);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
return JSHandle<JSTaggedValue>(thread, result);
}
bool Internalize::RecurseAndApply(JSThread *thread, const JSHandle<JSObject> &holder,
const JSHandle<JSTaggedValue> &name, const JSHandle<JSTaggedValue> &receiver,
TransformType transformType)
{
STACK_LIMIT_CHECK(thread, false);
JSHandle<JSTaggedValue> value = InternalizeJsonProperty(thread, holder, name, receiver, transformType);
bool changeResult = false;
if (value->IsUndefined()) {
SCheckMode sCheckMode = transformType == TransformType::SENDABLE ? SCheckMode::SKIP : SCheckMode::CHECK;
changeResult = JSTaggedValue::DeleteProperty(thread, JSHandle<JSTaggedValue>(holder), name, sCheckMode);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
} else {
changeResult = JSObject::CreateDataProperty(thread, holder, name, value);
}
return changeResult;
}
}