Fix stringify bug for supporting proxy object

Add processing logic to handle proxy objects and avoid code logic from entering
serialize proxy.

Issue: https://gitee.com/openharmony/ark_js_runtime/issues/I4ZLYT

Signed-off-by: xujie <xujie101@huawei.com>
Change-Id: If8c7c59bb9e0d6c156b13c5badf3c3f489c2b146
This commit is contained in:
xujie 2022-03-25 17:26:05 +08:00
parent 6075ee8512
commit 38f7a9f0c7
3 changed files with 86 additions and 11 deletions

View File

@ -374,7 +374,9 @@ JSTaggedValue JsonStringifier::SerializeJSONProperty(const JSHandle<JSTaggedValu
return JSTaggedValue::Undefined();
default: {
if (!tagValue.IsCallable()) {
if (UNLIKELY(tagValue.IsJSProxy())) {
JSHClass *jsHclass = tagValue.GetTaggedObject()->GetClass();
if (UNLIKELY(jsHclass->IsJSProxy() &&
JSProxy::Cast(tagValue.GetTaggedObject())->IsArray(thread_))) {
SerializeJSProxy(valHandle, replacer);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
} else {
@ -451,15 +453,38 @@ bool JsonStringifier::SerializeJSONObject(const JSHandle<JSTaggedValue> &value,
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);
if (UNLIKELY(value->IsJSProxy())) { // serialize proxy object
JSHandle<JSProxy> proxy(value);
JSHandle<TaggedArray> propertyArray = JSObject::EnumerableOwnNames(thread_, obj);
uint32_t arrLength = propertyArray->GetLength();
for (uint32_t i = 0; i < arrLength; i++) {
handleKey_.Update(propertyArray->Get(i));
JSHandle<JSTaggedValue> proxyValue = JSProxy::GetProperty(thread_, proxy, handleKey_).GetValue();
JSTaggedValue serializeValue = GetSerializeValue(value, handleKey_, proxyValue, replacer);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
(serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
continue;
}
handleValue_.Update(serializeValue);
SerializeObjectKey(handleKey_, hasContent);
JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
if (!res.IsUndefined()) {
hasContent = true;
}
}
} else {
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();

View File

@ -20,6 +20,7 @@
#include "ecmascript/base/builtins_base.h"
#include "ecmascript/builtins/builtins_errors.h"
#include "ecmascript/builtins/builtins_json.h"
#include "ecmascript/builtins/builtins_proxy.h"
#include "ecmascript/ecma_runtime_call_info.h"
#include "ecmascript/ecma_string-inl.h"
#include "ecmascript/ecma_vm.h"
@ -461,4 +462,52 @@ HWTEST_F_L0(BuiltinsJsonTest, Stringify3)
JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get());
ASSERT_TRUE(EcmaString::StringsAreEqual(*test, EcmaString::Cast(result.GetTaggedObject())));
}
JSHandle<JSTaggedValue> CreateJSObject(JSThread *thread)
{
EcmaVM *ecmaVM = thread->GetEcmaVM();
JSHandle<GlobalEnv> globalEnv = ecmaVM->GetGlobalEnv();
JSHandle<JSTaggedValue> objFun = globalEnv->GetObjectFunction();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSTaggedValue> obj(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun));
JSHandle<JSTaggedValue> key(factory->NewFromStdString("x"));
JSHandle<JSTaggedValue> value(thread, JSTaggedValue(1));
JSObject::SetProperty(thread, obj, key, value);
return obj;
}
JSHandle<JSTaggedValue> CreateProxy(JSThread *thread)
{
JSHandle<JSTaggedValue> target = CreateJSObject(thread);
JSHandle<JSTaggedValue> handler = CreateJSObject(thread);
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Null(), 8);
ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue());
ecmaRuntimeCallInfo->SetCallArg(1, handler.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
JSTaggedValue result = BuiltinsProxy::ProxyConstructor(ecmaRuntimeCallInfo.get());
return JSHandle<JSTaggedValue>(thread, result);
}
HWTEST_F_L0(BuiltinsJsonTest, Stringify4) // Test for proxy object
{
auto ecmaVM = thread->GetEcmaVM();
ObjectFactory *factory = ecmaVM->GetFactory();
JSHandle<JSTaggedValue> proxy = CreateProxy(thread);
JSHandle<EcmaString> test = factory->NewFromStdString("{\"x\":1}");
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetCallArg(0, proxy.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get());
ASSERT_TRUE(EcmaString::StringsAreEqual(*test, EcmaString::Cast(result.GetTaggedObject())));
}
} // namespace panda::test

View File

@ -1312,7 +1312,8 @@ JSHandle<TaggedArray> JSObject::EnumerableOwnNames(JSThread *thread, const JSHan
JSTaggedValue key(keys->Get(i));
if (key.IsString()) {
PropertyDescriptor desc(thread);
bool status = GetOwnProperty(thread, obj, JSHandle<JSTaggedValue>(thread, key), desc);
bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle<JSTaggedValue>(obj),
JSHandle<JSTaggedValue>(thread, key), desc);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
if (status && desc.IsEnumerable()) {