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 21namespace panda::ecmascript::builtins { 22namespace { 23using TransformType = base::JsonHelper::TransformType; 24using ParseOptions = base::JsonHelper::ParseOptions; 25using ParseReturnType = base::JsonHelper::ParseReturnType; 26using BigIntMode = base::JsonHelper::BigIntMode; 27 28void 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 41using Internalize = base::Internalize; 42 43JSTaggedValue BuiltinsJson::Parse(EcmaRuntimeCallInfo *argv) 44{ 45 return ParseWithTransformType(argv, TransformType::NORMAL); 46} 47 48JSTaggedValue 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 61JSTaggedValue BuiltinsBigIntJson::Parse(EcmaRuntimeCallInfo *argv) 62{ 63 return BuiltinsJson::ParseWithTransformType(argv, TransformType::BIGINT); 64} 65 66// 24.5.1 67JSTaggedValue 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 113JSTaggedValue 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 163JSTaggedValue BuiltinsJson::Stringify(EcmaRuntimeCallInfo *argv) 164{ 165 return BuiltinsJson::StringifyWithTransformType(argv, TransformType::NORMAL); 166} 167 168JSTaggedValue BuiltinsSendableJson::Stringify(EcmaRuntimeCallInfo *argv) 169{ 170 return BuiltinsJson::StringifyWithTransformType(argv, TransformType::SENDABLE); 171} 172 173JSTaggedValue BuiltinsBigIntJson::Stringify(EcmaRuntimeCallInfo *argv) 174{ 175 return BuiltinsJson::StringifyWithTransformType(argv, TransformType::BIGINT); 176} 177 178JSTaggedValue 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