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