/* * Copyright (c) 2021-2024 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" namespace panda::ecmascript::base { template JSHandle JsonParser::Launch(Text begin, Text end) { // check empty if (UNLIKELY(begin == end)) { return JSHandle(thread_, [&]() -> JSTaggedValue { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); }()); } end_ = end - 1; current_ = begin; SkipEndWhiteSpace(); range_ = end_; auto vm = thread_->GetEcmaVM(); factory_ = vm->GetFactory(); env_ = *vm->GetGlobalEnv(); // For Json, we do not support ElementsKind auto index = static_cast(ConstantIndex::ELEMENT_HOLE_TAGGED_HCLASS_INDEX); auto globalConstant = const_cast(thread_->GlobalConstants()); auto hclassVal = globalConstant->GetGlobalConstantObject(index); initialJSArrayClass_ = JSHandle(thread_, hclassVal); JSHandle objectFunc(env_->GetObjectFunction()); initialJSObjectClass_ = JSHandle(thread_, JSFunction::GetOrCreateInitialJSHClass(thread_, objectFunc)); JSTaggedValue result = ParseJSONText(); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); return JSHandle(thread_, result); } template JSTaggedValue JsonParser::ParseJSONText() { JSHandle parseValue; std::vector continuationList; std::vector> elementsList; std::vector> propertyList; continuationList.reserve(16); // 16: initial capacity elementsList.reserve(16); // 16: initial capacity propertyList.reserve(16); // 16: initial capacity JsonContinuation continuation(ContType::RETURN, 0); while (true) { while (true) { SkipStartWhiteSpace(); Tokens token = ParseToken(); if (current_ > range_) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end in JSON", JSTaggedValue::Exception()); } switch (token) { case Tokens::OBJECT: if (EmptyObjectCheck()) { if (transformType_ == TransformType::SENDABLE) { JSHandle sHclass; JSHandle sJsonPrototype = GetSJsonPrototype(); JSHandle sLayout = factory_->CreateSLayoutInfo(0); sHclass = factory_->NewSEcmaHClass(JSSharedObject::SIZE, 0, JSType::JS_SHARED_OBJECT, JSHandle(sJsonPrototype), JSHandle(sLayout)); parseValue = JSHandle(factory_->NewSharedOldSpaceJSObject(sHclass)); } else { parseValue = JSHandle(factory_->NewJSObject(initialJSObjectClass_)); } GetNextNonSpaceChar(); break; } continuationList.emplace_back(std::move(continuation)); continuation = JsonContinuation(ContType::OBJECT, propertyList.size()); SkipStartWhiteSpace(); if (UNLIKELY(*current_ != '"')) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object Prop in JSON", JSTaggedValue::Exception()); } propertyList.emplace_back(ParseString(true)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); SkipStartWhiteSpace(); if (UNLIKELY(*current_ != ':')) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); } Advance(); continue; case Tokens::MAP: if (EmptyObjectCheck()) { if (transformType_ == TransformType::SENDABLE) { parseValue = JSHandle(CreateSharedMap()); } else { parseValue = JSHandle(CreateMap()); } GetNextNonSpaceChar(); break; } continuationList.emplace_back(std::move(continuation)); continuation = JsonContinuation(ContType::MAP, propertyList.size()); SkipStartWhiteSpace(); if (UNLIKELY(*current_ != '"')) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected MAP Prop in JSON", JSTaggedValue::Exception()); } propertyList.emplace_back(ParseString(true)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); SkipStartWhiteSpace(); if (UNLIKELY(*current_ != ':')) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected MAP in JSON", JSTaggedValue::Exception()); } Advance(); continue; case Tokens::ARRAY: if (EmptyArrayCheck()) { if (transformType_ == TransformType::SENDABLE) { parseValue = JSHandle(factory_->NewJSSArray()); } else { parseValue = JSHandle(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(thread_, ParseLiteralTrue()); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); break; case Tokens::LITERAL_FALSE: parseValue = JSHandle(thread_, ParseLiteralFalse()); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); break; case Tokens::LITERAL_NULL: parseValue = JSHandle(thread_, ParseLiteralNull()); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); break; case Tokens::NUMBER: parseValue = JSHandle(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_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } break; } while (true) { switch (continuation.type_) { case ContType::RETURN: ASSERT(continuationList.empty()); ASSERT(elementsList.empty()); ASSERT(propertyList.empty()); if (current_ <= range_) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } 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_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception()); } 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_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object Prop in JSON", JSTaggedValue::Exception()); } propertyList.emplace_back(ParseString(true)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); SkipStartWhiteSpace(); if (UNLIKELY(*current_ != ':')) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); } Advance(); break; } if (UNLIKELY(transformType_ == TransformType::SENDABLE)) { parseValue = CreateSJsonObject(continuation, propertyList); } else { parseValue = CreateJsonObject(continuation, propertyList); } if (UNLIKELY(*current_ != '}')) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); } 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_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected MAP Prop in JSON", JSTaggedValue::Exception()); } propertyList.emplace_back(ParseString(true)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); SkipStartWhiteSpace(); if (UNLIKELY(*current_ != ':')) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected MAP in JSON", JSTaggedValue::Exception()); } Advance(); break; } if (UNLIKELY(transformType_ == TransformType::SENDABLE)) { parseValue = CreateSJsonMap(continuation, propertyList); } else { parseValue = CreateJsonMap(continuation, propertyList); } if (UNLIKELY(*current_ != '}')) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected MAP in JSON", JSTaggedValue::Exception()); } Advance(); propertyList.resize(continuation.index_); continuation = std::move(continuationList.back()); continuationList.pop_back(); continue; } } break; } } } template JSHandle JsonParser::CreateJsonArray(JsonContinuation continuation, std::vector> &elementsList) { size_t start = continuation.index_; size_t size = elementsList.size() - start; JSHandle array = factory_->NewJSArray(size, initialJSArrayClass_); JSHandle elements = factory_->NewJsonFixedArray(start, size, elementsList); JSHandle obj(array); obj->SetElements(thread_, elements); return JSHandle(array); } template JSHandle JsonParser::CreateSJsonArray([[maybe_unused]] JsonContinuation continuation, [[maybe_unused]] std::vector> &elementsList) { size_t start = continuation.index_; size_t size = elementsList.size() - start; JSHandle array = factory_->NewJSSArray(); array->SetArrayLength(thread_, size); JSHandle elements = factory_->NewSJsonFixedArray(start, size, elementsList); JSHandle obj(array); obj->SetElements(thread_, elements); return JSHandle(array); } template JSHandle JsonParser::CreateJsonObject(JsonContinuation continuation, std::vector> &propertyList) { size_t start = continuation.index_; size_t size = propertyList.size() - start; auto obj = JSHandle(factory_->NewJSObject(initialJSObjectClass_)); for (size_t i = 0; i < size; i += 2) { // 2: prop name and value 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()) { // slow path JSTaggedValue::SetProperty(thread_, obj, keyHandle, valueHandle, true); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); } } return obj; } template JSHandle JsonParser::CreateSJsonObject(JsonContinuation continuation, std::vector> &propertyList) { size_t start = continuation.index_; size_t size = propertyList.size() - start; uint32_t fieldNum = size / 2; // 2: key-value pair JSHandle hclass; JSHandle layout; JSHandle jsonPrototype = GetSJsonPrototype(); if (fieldNum == 0) { layout = factory_->CreateSLayoutInfo(fieldNum); hclass = factory_->NewSEcmaHClass(JSSharedObject::SIZE, fieldNum, JSType::JS_SHARED_OBJECT, JSHandle(jsonPrototype), JSHandle(layout)); JSHandle obj = factory_->NewSharedOldSpaceJSObject(hclass); return JSHandle(obj); } else if (LIKELY(fieldNum <= JSSharedObject::MAX_INLINE)) { layout = factory_->CreateSLayoutInfo(fieldNum); JSHandle propertyArray = factory_->NewSTaggedArray(size); for (size_t i = 0; i < size; i += 2) { // 2: prop name and value JSHandle 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))); } hclass = factory_->NewSEcmaHClass(JSSharedObject::SIZE, fieldNum, JSType::JS_SHARED_OBJECT, JSHandle(jsonPrototype), JSHandle(layout)); JSHandle elementsDic = NumberDictionary::CreateInSharedHeap(thread_); bool hasElement = false; SendableClassDefiner::AddFieldTypeToHClass(thread_, propertyArray, size, layout, hclass, start, elementsDic, std::move(propertyList)); JSHandle obj = factory_->NewSharedOldSpaceJSObject(hclass); uint32_t index = 0; size = (hclass->GetInlinedProperties() << 1); for (size_t i = 0; i < size; i += 2) { // 2: prop name and value int64_t eleIndex = ObjectFastOperator::TryToElementsIndex(propertyList[start + i].GetTaggedValue()); if (eleIndex >= 0) { if (!hasElement) { hasElement = true; } int entry = elementsDic->FindEntry(JSTaggedValue(static_cast(eleIndex))); elementsDic->UpdateValue(thread_, entry, propertyList[start + i + 1].GetTaggedValue()); index++; continue; } obj->SetPropertyInlinedProps(thread_, index++, propertyList[start + i + 1].GetTaggedValue()); } if (hasElement) { JSHandle elementsDicHdl(elementsDic); JSHandle elements = factory_->NewAndCopySNameDictionary(elementsDicHdl, elementsDicHdl->GetLength()); obj->SetElements(thread_, elements); hclass->SetIsDictionaryElement(true); } return JSHandle(obj); } // build in dict mode JSMutableHandle dict( thread_, NameDictionary::CreateInSharedHeap(thread_, NameDictionary::ComputeHashTableSize(fieldNum))); JSMutableHandle propKey(thread_, JSTaggedValue::Undefined()); JSMutableHandle propValue(thread_, JSTaggedValue::Undefined()); // create dict and set key value for (size_t i = 0; i < size; i += 2) { // 2: prop name and value PropertyAttributes attributes = PropertyAttributes::Default(false, false, false); propKey.Update(propertyList[start + i]); propValue.Update(propertyList[start + i + 1]); JSHandle newDict = NameDictionary::PutIfAbsent(thread_, dict, propKey, propValue, attributes); dict.Update(newDict); } hclass = factory_->NewSEcmaHClassDictMode(JSSharedObject::SIZE, fieldNum, JSType::JS_SHARED_OBJECT, JSHandle(jsonPrototype)); JSHandle obj = factory_->NewSharedOldSpaceJSObject(hclass); obj->SetProperties(thread_, dict); return JSHandle(obj); } template JSHandle JsonParser::CreateSharedMap() { JSHandle proto = GetSMapPrototype(); auto emptySLayout = thread_->GlobalConstants()->GetHandledEmptySLayoutInfo(); JSHandle mapClass = factory_->NewSEcmaHClass(JSSharedMap::SIZE, 0, JSType::JS_SHARED_MAP, proto, emptySLayout); JSHandle obj = factory_->NewSharedOldSpaceJSObjectWithInit(mapClass); JSHandle jsMap = JSHandle::Cast(obj); JSHandle linkedMap( LinkedHashMap::Create(thread_, LinkedHashMap::MIN_CAPACITY, MemSpaceKind::SHARED)); jsMap->SetLinkedMap(thread_, linkedMap); jsMap->SetModRecord(0); return jsMap; } template JSHandle JsonParser::CreateMap() { JSHandle constructor = env_->GetBuiltinsMapFunction(); JSHandle map = JSHandle::Cast(factory_->NewJSObjectByConstructor(JSHandle(constructor), constructor)); JSHandle linkedMap = LinkedHashMap::Create(thread_); map->SetLinkedMap(thread_, linkedMap); return JSHandle(thread_, *map); } template JSHandle JsonParser::CreateJsonMap(JsonContinuation continuation, std::vector> &propertyList) { size_t start = continuation.index_; size_t size = propertyList.size() - start; uint32_t fieldNum = size / 2; JSHandle map = CreateMap(); if (fieldNum == 0) { return JSHandle(map); } for (size_t i = 0; i < size; i += 2) { // 2: prop name and value JSMap::Set(thread_, map, propertyList[start + i], propertyList[start + i + 1]); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); } return JSHandle(map); } template JSHandle JsonParser::CreateSJsonMap(JsonContinuation continuation, std::vector> &propertyList) { size_t start = continuation.index_; size_t size = propertyList.size() - start; uint32_t fieldNum = size / 2; // 2: key-value pair JSHandle jsMap = CreateSharedMap(); if (fieldNum == 0) { return JSHandle(jsMap); } else if (LIKELY(fieldNum <= JSSharedMap::MAX_INLINE)) { for (size_t i = 0; i < size; i += 2) { // 2: prop name and value JSSharedMap::Set(thread_, jsMap, propertyList[start + i], propertyList[start + i + 1]); } return JSHandle(jsMap); } // build in dict mode JSMutableHandle dict( thread_, NameDictionary::CreateInSharedHeap(thread_, NameDictionary::ComputeHashTableSize(fieldNum))); JSMutableHandle propKey(thread_, JSTaggedValue::Undefined()); JSMutableHandle propValue(thread_, JSTaggedValue::Undefined()); // create dict and set key value for (size_t i = 0; i < size; i += 2) { // 2: prop name and value PropertyAttributes attributes = PropertyAttributes::Default(false, false, false); propKey.Update(propertyList[start + i]); propValue.Update(propertyList[start + i + 1]); JSHandle newDict = NameDictionary::PutIfAbsent(thread_, dict, propKey, propValue, attributes); dict.Update(newDict); } jsMap->SetProperties(thread_, dict); return JSHandle(jsMap); } template JSTaggedValue JsonParser::SetPropertyByValue(const JSHandle &receiver, const JSHandle &key, const JSHandle &value) { ASSERT(key->IsString()); auto newKey = key.GetTaggedValue(); auto stringAccessor = EcmaStringAccessor(newKey); if (!stringAccessor.IsLineString() || (stringAccessor.IsUtf8() && IsNumberCharacter(*stringAccessor.GetDataUtf8()))) { uint32_t index = 0; if (stringAccessor.ToElementIndex(&index)) { return ObjectFastOperator::SetPropertyByIndex(thread_, receiver.GetTaggedValue(), index, value.GetTaggedValue()); } } if (!stringAccessor.IsInternString()) { newKey = JSTaggedValue(thread_->GetEcmaVM()->GetFactory()->InternString(key)); } return ObjectFastOperator::SetPropertyByName(thread_, receiver.GetTaggedValue(), newKey, value.GetTaggedValue()); } template JSTaggedValue JsonParser::ParseNumber(bool inObjorArr) { if (inObjorArr) { bool isFast = true; int32_t fastInteger = 0; bool isNumber = ReadNumberRange(isFast, fastInteger); if (!isNumber) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON Array Or Object", JSTaggedValue::Exception()); } if (isFast) { return parseOptions_.bigIntMode == BigIntMode::ALWAYS_PARSE_AS_BIGINT ? BigInt::Int32ToBigInt(thread_, fastInteger).GetTaggedValue() : JSTaggedValue(fastInteger); } } Text current = current_; bool negative = false; bool hasExponent = false; bool hasDecimal = false; if (*current_ == '-') { if (UNLIKELY(current_++ == end_)) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); } negative = true; } if (*current_ == '0') { if (!CheckZeroBeginNumber(hasExponent, hasDecimal)) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); } } else if (*current_ >= '1' && *current_ <= '9') { if (!CheckNonZeroBeginNumber(hasExponent, hasDecimal)) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); } } else { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); } std::string strNum(current, end_ + 1); current_ = end_ + 1; return ConvertToNumber(strNum, negative, hasExponent, hasDecimal); } template JSTaggedValue JsonParser::ConvertToNumber(const std::string &str, bool negative, bool hasExponent, bool hasDecimal) { errno = 0; // reset errno to 0 to avoid errno has been changed double v = std::strtod(str.c_str(), nullptr); if (errno == ERANGE) { errno = 0; if (v > 0) { return JSTaggedValue(base::POSITIVE_INFINITY); } else if (v < 0) { return JSTaggedValue(-base::POSITIVE_INFINITY); } } 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_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } return JSTaggedValue::TryCastDoubleToInt32(v); } else { return (hasExponent || hasDecimal) ? JSTaggedValue::TryCastDoubleToInt32(v) : NumberHelper::StringToBigInt(thread_, JSHandle::Cast(factory_->NewFromStdString(str))); } } template bool JsonParser::ParseStringLength(size_t &length, bool &isAscii, bool inObjOrArrOrMap) { Text last = inObjOrArrOrMap ? range_ : end_; for (Text current = current_; current < last; ++current) { T c = *current; if (inObjOrArrOrMap && c == '"') { end_ = current; return true; } else if (c == '\\') { if (UNLIKELY(!CheckBackslash(current, last, isAscii))) { return false; } } else if (UNLIKELY(c < CODE_SPACE)) { return false; } else if (c > ASCII_END) { ASSERT(sizeof(T) == sizeof(uint16_t)); isAscii = false; } ++length; } return !inObjOrArrOrMap; } template bool JsonParser::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') { // do nothing } else if ((ucharFirst >= '1' && ucharFirst <= '9') || (ucharFirst >= 'A' && ucharFirst <= 'F') || (ucharFirst >= 'a' && ucharFirst <= 'f')) { isAscii = false; // >= \u1000 } else { return false; } T ucharSecond = *++text; if (ucharSecond == '0') { // do nothing } else if ((ucharSecond >= '1' && ucharSecond <= '9') || (ucharSecond >= 'A' && ucharSecond <= 'F') || (ucharSecond >= 'a' && ucharSecond <= 'f')) { isAscii = false; // >= \u0100 } else { return false; } bool thirdZero = false; T ucharThird = *++text; if (ucharThird == '0') { thirdZero = true; } else if (ucharThird >= '1' && ucharThird <= '7') { // do nothing } else if ((ucharThird >= '8' && ucharThird <= '9') || (ucharThird >= 'A' && ucharThird <= 'F') || (ucharThird >= 'a' && ucharThird <= 'f')) { isAscii = false; // >= \u0080 } else { return false; } T ucharFourth = *++text; if (thirdZero && ucharFourth == '0') { isAscii = false; // \uxx00 } else if ((ucharFourth >= '0' && ucharFourth <= '9') || (ucharFourth >= 'A' && ucharFourth <= 'F') || (ucharFourth >= 'a' && ucharFourth <= 'f')) { // do nothing } else { return false; } break; } default: return false; } return true; } template template void JsonParser::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(res); break; } default: UNREACHABLE(); } } template template void JsonParser::CopyCharWithBackslash(Char *&p) { 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(); } } template JSHandle JsonParser::ParseStringWithBackslash(bool inObjOrArrOrMap) { size_t length = 0; bool isAscii = true; if (UNLIKELY(!ParseStringLength(length, isAscii, inObjOrArrOrMap))) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected string in JSON", JSHandle(thread_, JSTaggedValue::Exception())); } end_--; if (isAscii) { EcmaString *str = EcmaStringAccessor::CreateLineString(thread_->GetEcmaVM(), length, true); uint8_t *data = const_cast(EcmaStringAccessor(str).GetDataUtf8()); uint8_t *p = data; CopyCharWithBackslash(p); ASSERT(p - data == length); Advance(); return JSHandle(thread_, str); } else { EcmaString *str = EcmaStringAccessor::CreateLineString(thread_->GetEcmaVM(), length, false); uint16_t *data = const_cast(EcmaStringAccessor(str).GetDataUtf16()); uint16_t *p = data; CopyCharWithBackslash(p); ASSERT(p - data == length); Advance(); return JSHandle(thread_, str); } } template void JsonParser::SkipEndWhiteSpace() { while (current_ != end_) { if (*end_ == ' ' || *end_ == '\r' || *end_ == '\n' || *end_ == '\t') { end_--; } else { break; } } } template void JsonParser::SkipStartWhiteSpace() { while (current_ != end_) { if (*current_ == ' ' || *current_ == '\r' || *current_ == '\n' || *current_ == '\t') { Advance(); } else { break; } } } template void JsonParser::GetNextNonSpaceChar() { Advance(); SkipStartWhiteSpace(); } template Tokens JsonParser::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 JSTaggedValue JsonParser::ParseLiteralTrue() { static const char literalTrue[] = "true"; uint32_t remainingLength = range_ - current_; if (UNLIKELY(remainingLength < 3)) { // 3: literalTrue length - 1 THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } bool isMatch = MatchText(literalTrue, 4); // 4: literalTrue length if (LIKELY(isMatch)) { return JSTaggedValue::True(); } THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } template JSTaggedValue JsonParser::ParseLiteralFalse() { static const char literalFalse[] = "false"; uint32_t remainingLength = range_ - current_; if (UNLIKELY(remainingLength < 4)) { // 4: literalFalse length - 1 THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } bool isMatch = MatchText(literalFalse, 5); // 5: literalFalse length if (LIKELY(isMatch)) { return JSTaggedValue::False(); } THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } template JSTaggedValue JsonParser::ParseLiteralNull() { static const char literalNull[] = "null"; uint32_t remainingLength = range_ - current_; if (UNLIKELY(remainingLength < 3)) { // 3: literalNull length - 1 THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } bool isMatch = MatchText(literalNull, 4); // 4: literalNull length if (LIKELY(isMatch)) { return JSTaggedValue::Null(); } THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } template bool JsonParser::MatchText(const char *str, uint32_t matchLen) { // first char is already matched for (uint32_t pos = 1; pos < matchLen; ++pos) { if (current_[pos] != str[pos]) { return false; } } current_ += matchLen; return true; } template bool JsonParser::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; } end_ = range_ - 1; return true; } template typename JsonParser::Text JsonParser::AdvanceLastNumberCharacter(Text current) { return std::find_if(current, range_, [this](T c) { return !IsNumberCharacter(c); }); } template bool JsonParser::IsNumberCharacter(T ch) { if (ch >= '0' && ch <= '9') { return true; } return false; } template bool JsonParser::IsNumberSignalCharacter(T ch) { return ch == '.' || ch == 'e' || ch == 'E' || ch == '+' || ch == '-'; } template bool JsonParser::IsExponentNumber() { if (IsNumberCharacter(*current_)) { return true; } else if (*current_ == '-' || *current_ == '+') { if (current_ == end_) { return false; } Advance(); if (IsNumberCharacter(*current_)) { return true; } } return false; } template bool JsonParser::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 bool JsonParser::IsExponentLegal(bool &hasExponent) { if (hasExponent || current_ == end_) { return false; } Advance(); if (!IsExponentNumber()) { return false; } while (current_ != end_) { if (!IsNumberCharacter(*current_)) { return false; } Advance(); } return true; } template bool JsonParser::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 bool JsonParser::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 Utf8JsonParser::Parse(const JSHandle &strHandle) { ASSERT(*strHandle != nullptr); auto stringAccessor = EcmaStringAccessor(strHandle); uint32_t len = stringAccessor.GetLength(); ASSERT(len != UINT32_MAX); uint32_t slicedOffset = 0; if (LIKELY(stringAccessor.IsLineOrConstantString())) { sourceString_ = strHandle; } else if (stringAccessor.IsSlicedString()) { auto *sliced = static_cast(*strHandle); slicedOffset = sliced->GetStartIndex(); sourceString_ = JSHandle(thread_, EcmaString::Cast(sliced->GetParent())); } else { auto *flatten = EcmaStringAccessor::Flatten(thread_->GetEcmaVM(), strHandle); sourceString_ = JSHandle(thread_, flatten); } begin_ = EcmaStringAccessor(sourceString_).GetDataUtf8(); auto *heap = const_cast(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(current), nextCurrent - current); } void Utf8JsonParser::UpdatePointersListener(void *utf8Parser) { auto *parser = reinterpret_cast(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(ToUintPtr(begin) + currentOffset); parser->end_ = reinterpret_cast(ToUintPtr(begin) + endOffset); parser->range_ = reinterpret_cast(ToUintPtr(begin) + rangeOffset); parser->begin_ = begin; } } JSHandle Utf8JsonParser::ParseString(bool inObjOrArrOrMap) { bool isFastString = true; if (inObjOrArrOrMap) { if (UNLIKELY(!ReadJsonStringRange(isFastString))) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSHandle(thread_, JSTaggedValue::Exception())); } if (isFastString) { uint32_t offset = current_ - begin_; uint32_t strLength = end_ - current_; ASSERT(strLength <= static_cast(UINT32_MAX)); current_ = end_ + 1; return JSHandle::Cast(factory_->NewCompressedUtf8SubString( sourceString_, offset, strLength)); } } else { if (UNLIKELY(*end_ != '"' || current_ == end_ || !IsFastParseJsonString(isFastString))) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSHandle(thread_, JSTaggedValue::Exception())); } if (LIKELY(isFastString)) { uint32_t offset = current_ - begin_; uint32_t strLength = end_ - current_; ASSERT(strLength <= static_cast(UINT32_MAX)); current_ = end_ + 1; return JSHandle::Cast(factory_->NewFromUtf8LiteralCompressSubString( sourceString_, offset, strLength)); } } return ParseStringWithBackslash(inObjOrArrOrMap); } bool Utf8JsonParser::ReadJsonStringRange(bool &isFastString) { Advance(); // chars are within Ascii for (Text current = current_; current != range_; ++current) { uint8_t c = *current; if (c == '"') { end_ = current; return true; } else if (UNLIKELY(c == '\\')) { isFastString = false; // early return for ParseStringWithBackslash return true; } else if (UNLIKELY(c < CODE_SPACE)) { return false; } } return false; } bool Utf8JsonParser::IsFastParseJsonString(bool &isFastString) { Advance(); // chars are within Ascii for (Text current = current_; current != end_; ++current) { if (*current < CODE_SPACE) { return false; } else if (*current == '\\') { isFastString = false; // early return for ParseStringWithBackslash return true; } } return true; } JSHandle Utf16JsonParser::Parse(EcmaString *str) { ASSERT(str != nullptr); uint32_t len = EcmaStringAccessor(str).GetLength(); CVector buf(len); EcmaStringAccessor(str).WriteToFlatUtf16(buf.data(), len); Text begin = buf.data(); return Launch(begin, begin + len); } void Utf16JsonParser::ParticalParseString(std::string& str, Text current, Text nextCurrent) { str += StringHelper::U16stringToString(std::u16string(current, nextCurrent)); } JSHandle Utf16JsonParser::ParseString(bool inObjOrArrOrMap) { bool isFastString = true; bool isAscii = true; if (inObjOrArrOrMap) { if (UNLIKELY(!ReadJsonStringRange(isFastString, isAscii))) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSHandle(thread_, JSTaggedValue::Exception())); } if (isFastString) { if (isAscii) { std::string value(current_, end_); // from uint16_t* to std::string, can't use std::string_view current_ = end_ + 1; ASSERT(value.size() <= static_cast(UINT32_MAX)); return JSHandle::Cast(factory_->NewFromUtf8LiteralCompress( reinterpret_cast(value.c_str()), value.size())); } std::u16string_view value(reinterpret_cast(current_), end_ - current_); current_ = end_ + 1; ASSERT(value.size() <= static_cast(UINT32_MAX)); return JSHandle::Cast(factory_->NewFromUtf16LiteralNotCompress( reinterpret_cast(value.data()), value.size())); } } else { if (UNLIKELY(*end_ != '"' || current_ == end_ || !IsFastParseJsonString(isFastString, isAscii))) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSHandle(thread_, JSTaggedValue::Exception())); } if (LIKELY(isFastString)) { if (isAscii) { std::string value(current_, end_); // from uint16_t* to std::string, can't use std::string_view ASSERT(value.size() <= static_cast(UINT32_MAX)); current_ = end_ + 1; return JSHandle::Cast(factory_->NewFromUtf8LiteralCompress( reinterpret_cast(value.c_str()), value.size())); } std::u16string_view value(reinterpret_cast(current_), end_ - current_); ASSERT(value.size() <= static_cast(UINT32_MAX)); current_ = end_ + 1; return JSHandle::Cast(factory_->NewFromUtf16LiteralNotCompress( reinterpret_cast(value.data()), value.size())); } } return ParseStringWithBackslash(inObjOrArrOrMap); } bool Utf16JsonParser::ReadJsonStringRange(bool &isFastString, bool &isAscii) { Advance(); for (Text current = current_; current != range_; ++current) { uint16_t c = *current; if (c == '"') { end_ = current; return true; } else if (UNLIKELY(c == '\\')) { isFastString = false; // early return for ParseStringWithBackslash return true; } if (!IsLegalAsciiCharacter(c, isAscii)) { return false; } } return false; } bool Utf16JsonParser::IsFastParseJsonString(bool &isFastString, bool &isAscii) { Advance(); for (Text current = current_; current != end_; ++current) { if (!IsLegalAsciiCharacter(*current, isAscii)) { return false; } if (*current == '\\') { isFastString = false; // early return for ParseStringWithBackslash 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 Internalize::InternalizeJsonProperty(JSThread *thread, const JSHandle &holder, const JSHandle &name, const JSHandle &receiver, TransformType transformType) { JSHandle objHandle(holder); JSHandle val = JSTaggedValue::GetProperty(thread, objHandle, name).GetValue(); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); if (val->IsECMAObject()) { JSHandle obj = JSTaggedValue::ToObject(thread, val); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); bool isArray = val->IsArray(thread); if (isArray) { JSHandle 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 keyUnknow(thread, JSTaggedValue::Undefined()); JSMutableHandle keyName(thread, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < length; i++) { // Let prop be ! ToString((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 { // Let keys be ? EnumerableOwnPropertyNames(val, key). JSHandle ownerNames(JSObject::EnumerableOwnNames(thread, obj)); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); uint32_t namesLength = ownerNames->GetLength(); JSMutableHandle keyName(thread, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < namesLength; i++) { keyName.Update(ownerNames->Get(i)); RecurseAndApply(thread, obj, keyName, receiver, transformType); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); } } } // Return ? Call(receiver, holder, « name, val »). const uint32_t argsLength = 2; // 2: « name, val » JSHandle 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(thread, result); } bool Internalize::RecurseAndApply(JSThread *thread, const JSHandle &holder, const JSHandle &name, const JSHandle &receiver, TransformType transformType) { STACK_LIMIT_CHECK(thread, false); JSHandle value = InternalizeJsonProperty(thread, holder, name, receiver, transformType); bool changeResult = false; // If newElement is undefined, then Perform ? val.[[Delete]](P). if (value->IsUndefined()) { SCheckMode sCheckMode = transformType == TransformType::SENDABLE ? SCheckMode::SKIP : SCheckMode::CHECK; changeResult = JSObject::DeleteProperty(thread, holder, name, sCheckMode); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); } else { // Perform ? CreateDataProperty(val, P, newElement) changeResult = JSObject::CreateDataProperty(thread, holder, name, value); } return changeResult; } } // namespace panda::ecmascript::base