1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/builtins/builtins_json.h"
17
18 #include "ecmascript/base/json_parser.h"
19 #include "ecmascript/base/json_stringifier.h"
20
21 namespace panda::ecmascript::builtins {
22 namespace {
23 using TransformType = base::JsonHelper::TransformType;
24 using ParseOptions = base::JsonHelper::ParseOptions;
25 using ParseReturnType = base::JsonHelper::ParseReturnType;
26 using BigIntMode = base::JsonHelper::BigIntMode;
27
InitWithTransformType(JSHandle<GlobalEnv> &env, TransformType transformType, JSMutableHandle<JSFunction> &constructor, SCheckMode &sCheckMode)28 void InitWithTransformType(JSHandle<GlobalEnv> &env, TransformType transformType,
29 JSMutableHandle<JSFunction> &constructor, SCheckMode &sCheckMode)
30 {
31 if (transformType == TransformType::NORMAL || transformType == TransformType::BIGINT) {
32 sCheckMode = SCheckMode::CHECK;
33 constructor.Update(env->GetObjectFunction());
34 } else {
35 sCheckMode = SCheckMode::SKIP;
36 constructor.Update(env->GetSObjectFunction());
37 }
38 }
39 } // namespace
40
41 using Internalize = base::Internalize;
42
Parse(EcmaRuntimeCallInfo *argv)43 JSTaggedValue BuiltinsJson::Parse(EcmaRuntimeCallInfo *argv)
44 {
45 return ParseWithTransformType(argv, TransformType::NORMAL);
46 }
47
Parse(EcmaRuntimeCallInfo *argv)48 JSTaggedValue BuiltinsSendableJson::Parse(EcmaRuntimeCallInfo *argv)
49 {
50 uint32_t argc = argv->GetArgsNumber();
51 if (argc >= 2) { // 2: two args
52 JSHandle<JSTaggedValue> reviverVal = GetCallArg(argv, 1);
53 if (!reviverVal->IsUndefined()) {
54 THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), GET_MESSAGE_STRING(ReviverOnlySupportUndefined),
55 JSTaggedValue::Exception());
56 }
57 }
58 return BuiltinsJson::ParseWithTransformType(argv, TransformType::SENDABLE);
59 }
60
Parse(EcmaRuntimeCallInfo *argv)61 JSTaggedValue BuiltinsBigIntJson::Parse(EcmaRuntimeCallInfo *argv)
62 {
63 return BuiltinsJson::ParseWithTransformType(argv, TransformType::BIGINT);
64 }
65
66 // 24.5.1
ParseWithTransformType(EcmaRuntimeCallInfo *argv, TransformType transformType)67 JSTaggedValue BuiltinsJson::ParseWithTransformType(EcmaRuntimeCallInfo *argv, TransformType transformType)
68 {
69 BUILTINS_API_TRACE(argv->GetThread(), Json, Parse);
70 ASSERT(argv);
71 JSThread *thread = argv->GetThread();
72 [[maybe_unused]] EcmaHandleScope handleScope(thread);
73
74 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
75
76 uint32_t argc = argv->GetArgsNumber();
77 if (argc == 0) {
78 JSHandle<JSObject> syntaxError = factory->GetJSError(base::ErrorType::SYNTAX_ERROR,
79 "arg is empty", StackCheck::NO);
80 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), JSTaggedValue::Exception());
81 }
82
83 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
84 JSMutableHandle<JSTaggedValue> reviverVal(thread, JSTaggedValue::Undefined());
85 ParseOptions parseOptions;
86 if (argc == 2) { // 2: two args
87 reviverVal.Update(GetCallArg(argv, 1));
88 } else if (argc == 3 && base::JsonHelper::IsTypeSupportBigInt(transformType)) { // 3: three args
89 reviverVal.Update(GetCallArg(argv, 1));
90 JSHandle<JSTaggedValue> options = GetCallArg(argv, 2); // 2: two args
91 JSHandle<JSTaggedValue> modeKey(factory->NewFromStdString("bigIntMode"));
92 JSHandle<JSTaggedValue> typeKey(factory->NewFromStdString("parseReturnType"));
93 if (options->IsECMAObject()) {
94 JSHandle<JSTaggedValue> type = JSTaggedValue::GetProperty(thread, options, typeKey).GetValue();
95 if (transformType == TransformType::SENDABLE && type->IsInt() && type->GetInt() == 1) { // 1: map
96 parseOptions.returnType = ParseReturnType::MAP;
97 }
98 JSHandle<JSTaggedValue> modeValue = JSTaggedValue::GetProperty(thread, options, modeKey).GetValue();
99 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
100 if (modeValue->IsInt()) {
101 int val = modeValue->GetInt();
102 if (val == 2) { // 2: bigIntMode
103 parseOptions.bigIntMode = BigIntMode::ALWAYS_PARSE_AS_BIGINT;
104 } else if (val == 1) {
105 parseOptions.bigIntMode = BigIntMode::PARSE_AS_BIGINT;
106 }
107 }
108 }
109 }
110 return ParseWithTransformType(thread->GetEcmaVM(), msg, reviverVal, transformType, parseOptions);
111 }
112
ParseWithTransformType(const EcmaVM *vm, JSHandle<JSTaggedValue> &msg, JSHandle<JSTaggedValue> &reviverVal, TransformType transformType, ParseOptions options)113 JSTaggedValue BuiltinsJson::ParseWithTransformType(const EcmaVM *vm, JSHandle<JSTaggedValue> &msg,
114 JSHandle<JSTaggedValue> &reviverVal, TransformType transformType,
115 ParseOptions options)
116 {
117 JSThread *thread = vm->GetJSThread();
118 [[maybe_unused]] EcmaHandleScope handleScope(thread);
119
120 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
121 JSHandle<EcmaString> parseString = JSTaggedValue::ToString(thread, msg);
122 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
123 JSHandle<JSTaggedValue> result;
124 if (EcmaStringAccessor(parseString).IsUtf8()) {
125 panda::ecmascript::base::Utf8JsonParser parser(thread, transformType, options);
126 result = parser.Parse(parseString);
127 } else {
128 panda::ecmascript::base::Utf16JsonParser parser(thread, transformType, options);
129 result = parser.Parse(*parseString);
130 }
131 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
132 JSTaggedValue reviver = JSTaggedValue::Undefined();
133 if (reviverVal->IsJSFunction()) {
134 reviver = reviverVal.GetTaggedValue();
135 if (reviver.IsCallable()) {
136 JSHandle<JSTaggedValue> callbackfnHandle(thread, reviver);
137 // Let root be ! OrdinaryObjectCreate(%Object.prototype%).
138 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
139 JSMutableHandle<JSFunction> constructor(thread, JSTaggedValue::Undefined());
140 SCheckMode sCheckMode = SCheckMode::CHECK;
141 InitWithTransformType(env, transformType, constructor, sCheckMode);
142 JSHandle<JSObject> root = factory->NewJSObjectByConstructor(constructor);
143 // Let rootName be the empty String.
144 JSHandle<JSTaggedValue> rootName(factory->GetEmptyString());
145 // Perform ! CreateDataPropertyOrThrow(root, rootName, unfiltered).
146 bool success = JSObject::CreateDataProperty(thread, root, rootName, result, sCheckMode);
147 if (success) {
148 result = Internalize::InternalizeJsonProperty(thread, root, rootName, callbackfnHandle, transformType);
149 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
150 }
151 }
152 }
153 if (transformType == TransformType::SENDABLE) {
154 if (result->IsHeapObject() && !result->IsJSShared() && !result->IsString()) {
155 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(ClassNotDerivedFromShared),
156 JSTaggedValue::Exception());
157 }
158 }
159 return result.GetTaggedValue();
160 }
161
162 // 24.5.2
Stringify(EcmaRuntimeCallInfo *argv)163 JSTaggedValue BuiltinsJson::Stringify(EcmaRuntimeCallInfo *argv)
164 {
165 return BuiltinsJson::StringifyWithTransformType(argv, TransformType::NORMAL);
166 }
167
Stringify(EcmaRuntimeCallInfo *argv)168 JSTaggedValue BuiltinsSendableJson::Stringify(EcmaRuntimeCallInfo *argv)
169 {
170 return BuiltinsJson::StringifyWithTransformType(argv, TransformType::SENDABLE);
171 }
172
Stringify(EcmaRuntimeCallInfo *argv)173 JSTaggedValue BuiltinsBigIntJson::Stringify(EcmaRuntimeCallInfo *argv)
174 {
175 return BuiltinsJson::StringifyWithTransformType(argv, TransformType::BIGINT);
176 }
177
StringifyWithTransformType(EcmaRuntimeCallInfo *argv, TransformType transformType)178 JSTaggedValue BuiltinsJson::StringifyWithTransformType(EcmaRuntimeCallInfo *argv, TransformType transformType)
179 {
180 BUILTINS_API_TRACE(argv->GetThread(), Json, Stringify);
181 ASSERT(argv);
182 JSThread *thread = argv->GetThread();
183 [[maybe_unused]] EcmaHandleScope handleScope(thread);
184
185 uint32_t argc = argv->GetArgsNumber();
186 JSTaggedValue value = GetCallArg(argv, 0).GetTaggedValue();
187 JSTaggedValue replacer = JSTaggedValue::Undefined();
188 JSTaggedValue gap = JSTaggedValue::Undefined();
189
190 if (argc == 2) { // 2: 2 args
191 replacer = GetCallArg(argv, 1).GetTaggedValue();
192 } else if (argc == 3) { // 3: 3 args
193 replacer = GetCallArg(argv, 1).GetTaggedValue();
194 gap = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD).GetTaggedValue();
195 }
196
197 JSHandle<JSTaggedValue> handleValue(thread, value);
198 JSHandle<JSTaggedValue> handleReplacer(thread, replacer);
199 JSHandle<JSTaggedValue> handleGap(thread, gap);
200 panda::ecmascript::base::JsonStringifier stringifier(thread, transformType);
201 JSHandle<JSTaggedValue> result = stringifier.Stringify(handleValue, handleReplacer, handleGap);
202
203 return result.GetTaggedValue();
204 }
205 } // namespace panda::ecmascript::builtins
206