Files
ark_js_runtime/ecmascript/base/json_stringifier.cpp
T
wengchangcheng 2bfd68c01a modify ACCESSOR for EcmaString and TaggedArray
Signed-off-by: wengchangcheng <wengchangcheng@huawei.com>
Change-Id: I2995dc75ed34e8eb2bf249f2cced62ec9d91250e
2022-01-17 11:51:50 +08:00

774 lines
31 KiB
C++

/*
* Copyright (c) 2021 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/base/json_stringifier.h"
#include <algorithm>
#include <iomanip>
#include <sstream>
#include "ecmascript/base/builtins_base.h"
#include "ecmascript/base/number_helper.h"
#include "ecmascript/builtins/builtins_errors.h"
#include "ecmascript/ecma_runtime_call_info.h"
#include "ecmascript/ecma_string-inl.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/internal_call_params.h"
#include "ecmascript/interpreter/fast_runtime_stub-inl.h"
#include "ecmascript/js_array.h"
#include "ecmascript/js_function.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_invoker.h"
#include "ecmascript/js_object-inl.h"
#include "ecmascript/js_primitive_ref.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/js_tagged_value.h"
namespace panda::ecmascript::base {
constexpr unsigned char CODE_SPACE = 0x20;
constexpr int GAP_MAX_LEN = 10;
constexpr int FOUR_HEX = 4;
bool JsonStringifier::IsFastValueToQuotedString(const char *value)
{
if (strpbrk(value, "\"\\\b\f\n\r\t") != nullptr) {
return false;
}
while (*value != '\0') {
if (*value > 0 && *value < CODE_SPACE) {
return false;
}
value++;
}
return true;
}
CString JsonStringifier::ValueToQuotedString(CString str)
{
CString product;
const char *value = str.c_str();
// fast mode
bool isFast = IsFastValueToQuotedString(value);
if (isFast) {
product += "\"";
product += str;
product += "\"";
return product;
}
// 1. Let product be code unit 0x0022 (QUOTATION MARK).
product += "\"";
// 2. For each code unit C in value
for (const char *c = value; *c != 0; ++c) {
switch (*c) {
/*
* a. If C is 0x0022 (QUOTATION MARK) or 0x005C (REVERSE SOLIDUS), then
* i. Let product be the concatenation of product and code unit 0x005C (REVERSE SOLIDUS).
* ii. Let product be the concatenation of product and C.
*/
case '\"':
product += "\\\"";
break;
case '\\':
product += "\\\\";
break;
/*
* b. Else if C is 0x0008 (BACKSPACE), 0x000C (FORM FEED), 0x000A (LINE FEED), 0x000D (CARRIAGE RETURN),
* or 0x000B (LINE TABULATION), then
* i. Let product be the concatenation of product and code unit 0x005C (REVERSE SOLIDUS).
* ii. Let abbrev be the String value corresponding to the value of C as follows:
* BACKSPACE "b"
* FORM FEED (FF) "f"
* LINE FEED (LF) "n"
* CARRIAGE RETURN (CR) "r"
* LINE TABULATION "t"
* iii. Let product be the concatenation of product and abbrev.
*/
case '\b':
product += "\\b";
break;
case '\f':
product += "\\f";
break;
case '\n':
product += "\\n";
break;
case '\r':
product += "\\r";
break;
case '\t':
product += "\\t";
break;
default:
// c. Else if C has a code unit value less than 0x0020 (SPACE), then
if (*c > 0 && *c < CODE_SPACE) {
/*
* i. Let product be the concatenation of product and code unit 0x005C (REVERSE SOLIDUS).
* ii. Let product be the concatenation of product and "u".
* iii. Let hex be the string result of converting the numeric code unit value of C to a String of
* four hexadecimal digits. Alphabetic hexadecimal digits are presented as lowercase Latin letters.
* iv. Let product be the concatenation of product and hex.
*/
std::ostringstream oss;
oss << "\\u" << std::hex << std::setfill('0') << std::setw(FOUR_HEX) << static_cast<int>(*c);
product += oss.str();
} else {
// Else,
// i. Let product be the concatenation of product and C.
product += *c;
}
}
}
// 3. Let product be the concatenation of product and code unit 0x0022 (QUOTATION MARK).
product += "\"";
// Return product.
return product;
}
JSHandle<JSTaggedValue> JsonStringifier::Stringify(const JSHandle<JSTaggedValue> &value,
const JSHandle<JSTaggedValue> &replacer,
const JSHandle<JSTaggedValue> &gap)
{
factory_ = thread_->GetEcmaVM()->GetFactory();
handleValue_ = JSMutableHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
handleKey_ = JSMutableHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
// Let isArray be IsArray(replacer).
bool isArray = replacer->IsArray(thread_);
// ReturnIfAbrupt(isArray).
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
// If isArray is true, then
if (isArray) {
uint32_t len;
if (replacer->IsJSArray()) {
// FastPath
JSHandle<JSArray> arr(replacer);
len = arr->GetArrayLength();
} else {
// Let len be ToLength(Get(replacer, "length")).
JSHandle<JSTaggedValue> lengthKey = thread_->GlobalConstants()->GetHandledLengthString();
JSHandle<JSTaggedValue> lenResult = JSTaggedValue::GetProperty(thread_, replacer, lengthKey).GetValue();
// ReturnIfAbrupt(len).
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenResult);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
len = lenNumber.ToUint32();
}
if (len > 0) {
JSMutableHandle<JSTaggedValue> propHandle(thread_, JSTaggedValue::Undefined());
// Repeat while k<len.
for (uint32_t i = 0; i < len; i++) {
// a. Let v be Get(replacer, ToString(k)).
JSTaggedValue prop = FastRuntimeStub::FastGetPropertyByIndex(thread_, replacer.GetTaggedValue(), i);
// b. ReturnIfAbrupt(v).
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
/*
* c. Let item be undefined.
* d. If Type(v) is String, let item be v.
* e. Else if Type(v) is Number, let item be ToString(v).
* f. Else if Type(v) is Object, then
* i. If v has a [[StringData]] or [[NumberData]] internal slot, let item be ToString(v).
* ii. ReturnIfAbrupt(item).
* g. If item is not undefined and item is not currently an element of PropertyList, then
* i. Append item to the end of PropertyList.
* h. Let k be k+1.
*/
propHandle.Update(prop);
if (prop.IsNumber() || prop.IsString()) {
AddDeduplicateProp(propHandle);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
} else if (prop.IsJSPrimitiveRef()) {
JSTaggedValue primitive = JSPrimitiveRef::Cast(prop.GetTaggedObject())->GetValue();
if (primitive.IsNumber() || primitive.IsString()) {
AddDeduplicateProp(propHandle);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
}
}
}
}
}
// If Type(space) is Object, then
if (gap->IsJSPrimitiveRef()) {
JSTaggedValue primitive = JSPrimitiveRef::Cast(gap->GetTaggedObject())->GetValue();
// a. If space has a [[NumberData]] internal slot, then
if (primitive.IsNumber()) {
// i. Let space be ToNumber(space).
JSTaggedNumber num = JSTaggedValue::ToNumber(thread_, gap);
// ii. ReturnIfAbrupt(space).
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
CalculateNumberGap(num);
} else if (primitive.IsString()) {
// b. Else if space has a [[StringData]] internal slot, then
// i. Let space be ToString(space).
auto str = JSTaggedValue::ToString(thread_, gap);
// ii. ReturnIfAbrupt(space).
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
CalculateStringGap(JSHandle<EcmaString>(thread_, str.GetTaggedValue()));
}
} else if (gap->IsNumber()) {
// If Type(space) is Number
CalculateNumberGap(gap.GetTaggedValue());
} else if (gap->IsString()) {
// Else if Type(space) is String
CalculateStringGap(JSHandle<EcmaString>::Cast(gap));
}
JSHandle<JSTaggedValue> key(factory_->GetEmptyString());
JSTaggedValue serializeValue = GetSerializeValue(value, key, value, replacer);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
handleValue_.Update(serializeValue);
JSTaggedValue result = SerializeJSONProperty(handleValue_, replacer);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
if (!result.IsUndefined()) {
return JSHandle<JSTaggedValue>(
factory_->NewFromUtf8Literal(reinterpret_cast<const uint8_t *>(result_.c_str()), result_.size()));
}
return thread_->GlobalConstants()->GetHandledUndefined();
}
void JsonStringifier::AddDeduplicateProp(const JSHandle<JSTaggedValue> &property)
{
uint32_t propLen = propList_.size();
for (uint32_t i = 0; i < propLen; i++) {
if (JSTaggedValue::SameValue(propList_[i], property)) {
return;
}
}
JSHandle<EcmaString> primString = JSTaggedValue::ToString(thread_, property);
RETURN_IF_ABRUPT_COMPLETION(thread_);
JSHandle<JSTaggedValue> addVal(thread_, *primString);
propList_.emplace_back(addVal);
}
bool JsonStringifier::CalculateNumberGap(JSTaggedValue gap)
{
double numValue = gap.GetNumber();
int num = static_cast<int>(numValue);
if (num > 0) {
int gapLength = std::min(num, GAP_MAX_LEN);
for (int i = 0; i < gapLength; i++) {
gap_ += " ";
}
gap_.append("\0");
}
return true;
}
bool JsonStringifier::CalculateStringGap(const JSHandle<EcmaString> &primString)
{
CString gapString = ConvertToString(*primString);
int gapLen = gapString.length();
if (gapLen > 0) {
int gapLength = std::min(gapLen, GAP_MAX_LEN);
CString str = gapString.substr(0, gapLength);
gap_ += str;
gap_.append("\0");
}
return true;
}
JSTaggedValue JsonStringifier::GetSerializeValue(const JSHandle<JSTaggedValue> &object,
const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value,
const JSHandle<JSTaggedValue> &replacer)
{
JSTaggedValue tagValue = value.GetTaggedValue();
// If Type(value) is Object, then
if (value->IsECMAObject()) {
// a. Let toJSON be Get(value, "toJSON").
JSHandle<JSTaggedValue> toJson = thread_->GlobalConstants()->GetHandledToJsonString();
JSHandle<JSTaggedValue> toJsonFun(
thread_, FastRuntimeStub::FastGetPropertyByValue(thread_, tagValue, toJson.GetTaggedValue()));
// b. ReturnIfAbrupt(toJSON).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
// c. If IsCallable(toJSON) is true
if (UNLIKELY(toJsonFun->IsCallable())) {
// Let value be Call(toJSON, value, «key»).
InternalCallParams *arguments = thread_->GetInternalCallParams();
arguments->MakeArgv(key);
tagValue = JSFunction::Call(thread_, toJsonFun, value, 1, arguments->GetArgv());
// ii. ReturnIfAbrupt(value).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
}
}
if (UNLIKELY(replacer->IsCallable())) {
handleValue_.Update(tagValue);
// a. Let value be Call(ReplacerFunction, holder, «key, value»).
InternalCallParams *arguments = thread_->GetInternalCallParams();
arguments->MakeArgv(key, handleValue_);
tagValue = JSFunction::Call(thread_, replacer, object, 2, arguments->GetArgv()); // 2: two args
// b. ReturnIfAbrupt(value).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
}
return tagValue;
}
JSTaggedValue JsonStringifier::SerializeJSONProperty(const JSHandle<JSTaggedValue> &value,
const JSHandle<JSTaggedValue> &replacer)
{
JSTaggedValue tagValue = value.GetTaggedValue();
if (!tagValue.IsHeapObject()) {
TaggedType type = tagValue.GetRawData();
switch (type) {
// If value is false, return "false".
case JSTaggedValue::VALUE_FALSE:
result_ += "false";
return tagValue;
// If value is true, return "true".
case JSTaggedValue::VALUE_TRUE:
result_ += "true";
return tagValue;
// If value is null, return "null".
case JSTaggedValue::VALUE_NULL:
result_ += "null";
return tagValue;
default:
// If Type(value) is Number, then
if (tagValue.IsNumber()) {
// a. If value is finite, return ToString(value).
if (std::isfinite(tagValue.GetNumber())) {
result_ += ConvertToString(*base::NumberHelper::NumberToString(thread_, tagValue));
} else {
// b. Else, return "null".
result_ += "null";
}
return tagValue;
}
}
} else {
JSType jsType = tagValue.GetTaggedObject()->GetClass()->GetObjectType();
JSHandle<JSTaggedValue> valHandle(thread_, tagValue);
switch (jsType) {
case JSType::JS_ARRAY: {
SerializeJSArray(valHandle, replacer);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
return tagValue;
}
// If Type(value) is String, return QuoteJSONString(value).
case JSType::STRING: {
CString str = ConvertToString(*JSHandle<EcmaString>(valHandle));
str = ValueToQuotedString(str);
result_ += str;
return tagValue;
}
case JSType::JS_PRIMITIVE_REF: {
SerilaizePrimitiveRef(valHandle);
return tagValue;
}
case JSType::SYMBOL:
return JSTaggedValue::Undefined();
default: {
if (!tagValue.IsCallable()) {
if (UNLIKELY(tagValue.IsJSProxy())) {
SerializeJSProxy(valHandle, replacer);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
} else {
SerializeJSONObject(valHandle, replacer);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
}
return tagValue;
}
}
}
}
return JSTaggedValue::Undefined();
}
void JsonStringifier::SerializeObjectKey(const JSHandle<JSTaggedValue> &key, bool hasContent)
{
CString stepBegin;
CString stepEnd;
if (hasContent) {
result_ += ",";
}
if (!gap_.empty()) {
stepBegin += "\n";
stepBegin += indent_;
stepEnd += " ";
}
CString str;
if (key->IsString()) {
str = ConvertToString(EcmaString::Cast(key->GetTaggedObject()));
} else if (key->IsInt()) {
str = NumberHelper::IntToString(static_cast<int32_t>(key->GetInt()));
} else {
str = ConvertToString(*JSTaggedValue::ToString(thread_, key));
}
result_ += stepBegin;
str = ValueToQuotedString(str);
result_ += str;
result_ += ":";
result_ += stepEnd;
}
bool JsonStringifier::PushValue(const JSHandle<JSTaggedValue> &value)
{
uint32_t thisLen = stack_.size();
for (uint32_t i = 0; i < thisLen; i++) {
bool equal = JSTaggedValue::SameValue(stack_[i].GetTaggedValue(), value.GetTaggedValue());
if (equal) {
return true;
}
}
stack_.emplace_back(value);
return false;
}
void JsonStringifier::PopValue()
{
stack_.pop_back();
}
bool JsonStringifier::SerializeJSONObject(const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &replacer)
{
bool isContain = PushValue(value);
if (isContain) {
THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
}
CString stepback = indent_;
indent_ += gap_;
result_ += "{";
bool hasContent = false;
JSHandle<JSObject> obj(value);
if (!replacer->IsArray(thread_)) {
uint32_t numOfKeys = obj->GetNumberOfKeys();
uint32_t numOfElements = obj->GetNumberOfElements();
if (numOfElements > 0) {
hasContent = JsonStringifier::SerializeElements(obj, replacer, hasContent);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
}
if (numOfKeys > 0) {
hasContent = JsonStringifier::SerializeKeys(obj, replacer, hasContent);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
}
} else {
uint32_t propLen = propList_.size();
for (uint32_t i = 0; i < propLen; i++) {
JSTaggedValue tagVal =
FastRuntimeStub::FastGetPropertyByValue(thread_, obj.GetTaggedValue(), propList_[i].GetTaggedValue());
handleValue_.Update(tagVal);
JSTaggedValue serializeValue = GetSerializeValue(value, propList_[i], handleValue_, replacer);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
(serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
continue;
}
handleValue_.Update(serializeValue);
SerializeObjectKey(propList_[i], hasContent);
JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
if (!res.IsUndefined()) {
hasContent = true;
}
}
}
if (hasContent && gap_.length() != 0) {
result_ += "\n";
result_ += stepback;
}
result_ += "}";
PopValue();
indent_ = stepback;
return true;
}
bool JsonStringifier::SerializeJSProxy(const JSHandle<JSTaggedValue> &object, const JSHandle<JSTaggedValue> &replacer)
{
bool isContain = PushValue(object);
if (isContain) {
THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
}
CString stepback = indent_;
CString stepBegin;
indent_ += gap_;
if (!gap_.empty()) {
stepBegin += "\n";
stepBegin += indent_;
}
result_ += "[";
JSHandle<JSProxy> proxy(object);
JSHandle<JSTaggedValue> lengthKey = thread_->GlobalConstants()->GetHandledLengthString();
JSHandle<JSTaggedValue> lenghHandle = JSProxy::GetProperty(thread_, proxy, lengthKey).GetValue();
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenghHandle);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
uint32_t length = lenNumber.ToUint32();
for (uint32_t i = 0; i < length; i++) {
handleKey_.Update(JSTaggedValue(i));
JSHandle<JSTaggedValue> valHandle = JSProxy::GetProperty(thread_, proxy, handleKey_).GetValue();
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
if (i > 0) {
result_ += ",";
}
result_ += stepBegin;
JSTaggedValue serializeValue = GetSerializeValue(object, handleKey_, valHandle, replacer);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
handleValue_.Update(serializeValue);
JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
if (res.IsUndefined()) {
result_ += "null";
}
}
if (length > 0 && !gap_.empty()) {
result_ += "\n";
result_ += stepback;
}
result_ += "]";
PopValue();
indent_ = stepback;
return true;
}
bool JsonStringifier::SerializeJSArray(const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &replacer)
{
// If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical.
bool isContain = PushValue(value);
if (isContain) {
THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
}
CString stepback = indent_;
CString stepBegin;
indent_ += gap_;
if (!gap_.empty()) {
stepBegin += "\n";
stepBegin += indent_;
}
result_ += "[";
JSHandle<JSArray> jsArr(value);
uint32_t len = jsArr->GetArrayLength();
if (len > 0) {
for (uint32_t i = 0; i < len; i++) {
JSTaggedValue tagVal = FastRuntimeStub::FastGetPropertyByIndex(thread_, value.GetTaggedValue(), i);
if (UNLIKELY(tagVal.IsAccessor())) {
tagVal = JSObject::CallGetter(thread_, AccessorData::Cast(tagVal.GetTaggedObject()), value);
}
handleKey_.Update(JSTaggedValue(i));
handleValue_.Update(tagVal);
if (i > 0) {
result_ += ",";
}
result_ += stepBegin;
JSTaggedValue serializeValue = GetSerializeValue(value, handleKey_, handleValue_, replacer);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
handleValue_.Update(serializeValue);
JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
if (res.IsUndefined()) {
result_ += "null";
}
}
if (!gap_.empty()) {
result_ += "\n";
result_ += stepback;
}
}
result_ += "]";
PopValue();
indent_ = stepback;
return true;
}
void JsonStringifier::SerilaizePrimitiveRef(const JSHandle<JSTaggedValue> &primitiveRef)
{
JSTaggedValue primitive = JSPrimitiveRef::Cast(primitiveRef.GetTaggedValue().GetTaggedObject())->GetValue();
if (primitive.IsString()) {
auto priStr = JSTaggedValue::ToString(thread_, primitiveRef);
RETURN_IF_ABRUPT_COMPLETION(thread_);
CString str = ConvertToString(*priStr);
str = ValueToQuotedString(str);
result_ += str;
} else if (primitive.IsNumber()) {
auto priNum = JSTaggedValue::ToNumber(thread_, primitiveRef);
RETURN_IF_ABRUPT_COMPLETION(thread_);
if (std::isfinite(priNum.GetNumber())) {
result_ += ConvertToString(*base::NumberHelper::NumberToString(thread_, priNum));
} else {
result_ += "null";
}
} else if (primitive.IsBoolean()) {
result_ += primitive.IsTrue() ? "true" : "false";
}
}
bool JsonStringifier::SerializeElements(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer,
bool hasContent)
{
JSHandle<TaggedArray> elementsArr(thread_, obj->GetElements());
if (!elementsArr->IsDictionaryMode()) {
uint32_t elementsLen = elementsArr->GetLength();
for (uint32_t i = 0; i < elementsLen; ++i) {
if (!elementsArr->Get(i).IsHole()) {
handleKey_.Update(JSTaggedValue(i));
handleValue_.Update(elementsArr->Get(i));
hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
}
}
} else {
JSHandle<NumberDictionary> numberDic(elementsArr);
CVector<JSHandle<JSTaggedValue>> sortArr;
int size = numberDic->Size();
for (int hashIndex = 0; hashIndex < size; hashIndex++) {
JSTaggedValue key = numberDic->GetKey(hashIndex);
if (!key.IsUndefined() && !key.IsHole()) {
PropertyAttributes attr = numberDic->GetAttributes(hashIndex);
if (attr.IsEnumerable()) {
JSTaggedValue numberKey = JSTaggedValue(static_cast<uint32_t>(key.GetInt()));
sortArr.emplace_back(JSHandle<JSTaggedValue>(thread_, numberKey));
}
}
}
std::sort(sortArr.begin(), sortArr.end(), CompareNumber);
for (const auto &entry : sortArr) {
JSTaggedValue entryKey = entry.GetTaggedValue();
handleKey_.Update(entryKey);
int index = numberDic->FindEntry(entryKey);
JSTaggedValue value = numberDic->GetValue(index);
handleValue_.Update(value);
hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
}
}
return hasContent;
}
bool JsonStringifier::SerializeKeys(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer,
bool hasContent)
{
JSHandle<TaggedArray> propertiesArr(thread_, obj->GetProperties());
if (!propertiesArr->IsDictionaryMode()) {
JSHandle<JSHClass> jsHclass(thread_, obj->GetJSHClass());
JSTaggedValue enumCache = jsHclass->GetEnumCache();
if (!enumCache.IsNull()) {
int propsNumber = jsHclass->NumberOfProps();
JSHandle<TaggedArray> cache(thread_, enumCache);
uint32_t length = cache->GetLength();
for (uint32_t i = 0; i < length; i++) {
JSTaggedValue key = cache->Get(i);
if (!key.IsString()) {
continue;
}
handleKey_.Update(key);
JSTaggedValue value;
LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
int index = layoutInfo->FindElementWithCache(thread_, *jsHclass, key, propsNumber);
PropertyAttributes attr(layoutInfo->GetAttr(index));
ASSERT(static_cast<int>(attr.GetOffset()) == index);
value = attr.IsInlinedProps()
? obj->GetPropertyInlinedProps(index)
: propertiesArr->Get(index - jsHclass->GetInlinedProperties());
if (UNLIKELY(value.IsAccessor())) {
value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
JSHandle<JSTaggedValue>(obj));
}
handleValue_.Update(value);
hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
}
return hasContent;
}
int end = jsHclass->NumberOfProps();
if (end <= 0) {
return hasContent;
}
int propsNumber = jsHclass->NumberOfProps();
for (int i = 0; i < end; i++) {
LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
JSTaggedValue key = layoutInfo->GetKey(i);
if (key.IsString() && layoutInfo->GetAttr(i).IsEnumerable()) {
handleKey_.Update(key);
JSTaggedValue value;
int index = layoutInfo->FindElementWithCache(thread_, *jsHclass, key, propsNumber);
PropertyAttributes attr(layoutInfo->GetAttr(index));
ASSERT(static_cast<int>(attr.GetOffset()) == index);
value = attr.IsInlinedProps()
? obj->GetPropertyInlinedProps(index)
: propertiesArr->Get(index - jsHclass->GetInlinedProperties());
if (UNLIKELY(value.IsAccessor())) {
value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
JSHandle<JSTaggedValue>(obj));
}
handleValue_.Update(value);
hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
}
}
return hasContent;
}
JSHandle<NameDictionary> nameDic(propertiesArr);
int size = nameDic->Size();
CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
for (int hashIndex = 0; hashIndex < size; hashIndex++) {
JSTaggedValue key = nameDic->GetKey(hashIndex);
if (!key.IsString()) {
continue;
}
PropertyAttributes attr = nameDic->GetAttributes(hashIndex);
if (!attr.IsEnumerable()) {
continue;
}
std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(JSHandle<JSTaggedValue>(thread_, key), attr);
sortArr.emplace_back(pair);
}
std::sort(sortArr.begin(), sortArr.end(), CompareKey);
for (const auto &entry : sortArr) {
JSTaggedValue entryKey = entry.first.GetTaggedValue();
handleKey_.Update(entryKey);
int index = nameDic->FindEntry(entryKey);
JSTaggedValue value = nameDic->GetValue(index);
if (UNLIKELY(value.IsAccessor())) {
value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
JSHandle<JSTaggedValue>(obj));
}
handleValue_.Update(value);
hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
}
return hasContent;
}
bool JsonStringifier::AppendJsonString(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer,
bool hasContent)
{
JSTaggedValue serializeValue = GetSerializeValue(JSHandle<JSTaggedValue>(obj), handleKey_, handleValue_, replacer);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
(serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
return hasContent;
}
handleValue_.Update(serializeValue);
SerializeObjectKey(handleKey_, hasContent);
JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
if (!res.IsUndefined()) {
return true;
}
return hasContent;
}
} // namespace panda::ecmascript::base