/* * 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/builtins/builtins_json.h" #include "ecmascript/base/json_parser.h" #include "ecmascript/base/json_stringifier.h" namespace panda::ecmascript::builtins { namespace { using TransformType = base::JsonHelper::TransformType; using ParseOptions = base::JsonHelper::ParseOptions; using ParseReturnType = base::JsonHelper::ParseReturnType; using BigIntMode = base::JsonHelper::BigIntMode; void InitWithTransformType(JSHandle &env, TransformType transformType, JSMutableHandle &constructor, SCheckMode &sCheckMode) { if (transformType == TransformType::NORMAL || transformType == TransformType::BIGINT) { sCheckMode = SCheckMode::CHECK; constructor.Update(env->GetObjectFunction()); } else { sCheckMode = SCheckMode::SKIP; constructor.Update(env->GetSObjectFunction()); } } } // namespace using Internalize = base::Internalize; JSTaggedValue BuiltinsJson::Parse(EcmaRuntimeCallInfo *argv) { return ParseWithTransformType(argv, TransformType::NORMAL); } JSTaggedValue BuiltinsSendableJson::Parse(EcmaRuntimeCallInfo *argv) { uint32_t argc = argv->GetArgsNumber(); if (argc >= 2) { // 2: two args JSHandle reviverVal = GetCallArg(argv, 1); if (!reviverVal->IsUndefined()) { THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), GET_MESSAGE_STRING(ReviverOnlySupportUndefined), JSTaggedValue::Exception()); } } return BuiltinsJson::ParseWithTransformType(argv, TransformType::SENDABLE); } JSTaggedValue BuiltinsBigIntJson::Parse(EcmaRuntimeCallInfo *argv) { return BuiltinsJson::ParseWithTransformType(argv, TransformType::BIGINT); } // 24.5.1 JSTaggedValue BuiltinsJson::ParseWithTransformType(EcmaRuntimeCallInfo *argv, TransformType transformType) { BUILTINS_API_TRACE(argv->GetThread(), Json, Parse); ASSERT(argv); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); uint32_t argc = argv->GetArgsNumber(); if (argc == 0) { JSHandle syntaxError = factory->GetJSError(base::ErrorType::SYNTAX_ERROR, "arg is empty", StackCheck::NO); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), JSTaggedValue::Exception()); } JSHandle msg = GetCallArg(argv, 0); JSMutableHandle reviverVal(thread, JSTaggedValue::Undefined()); ParseOptions parseOptions; if (argc == 2) { // 2: two args reviverVal.Update(GetCallArg(argv, 1)); } else if (argc == 3 && base::JsonHelper::IsTypeSupportBigInt(transformType)) { // 3: three args reviverVal.Update(GetCallArg(argv, 1)); JSHandle options = GetCallArg(argv, 2); // 2: two args JSHandle modeKey(factory->NewFromStdString("bigIntMode")); JSHandle typeKey(factory->NewFromStdString("parseReturnType")); if (options->IsECMAObject()) { JSHandle type = JSTaggedValue::GetProperty(thread, options, typeKey).GetValue(); if (transformType == TransformType::SENDABLE && type->IsInt() && type->GetInt() == 1) { // 1: map parseOptions.returnType = ParseReturnType::MAP; } JSHandle modeValue = JSTaggedValue::GetProperty(thread, options, modeKey).GetValue(); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (modeValue->IsInt()) { int val = modeValue->GetInt(); if (val == 2) { // 2: bigIntMode parseOptions.bigIntMode = BigIntMode::ALWAYS_PARSE_AS_BIGINT; } else if (val == 1) { parseOptions.bigIntMode = BigIntMode::PARSE_AS_BIGINT; } } } } return ParseWithTransformType(thread->GetEcmaVM(), msg, reviverVal, transformType, parseOptions); } JSTaggedValue BuiltinsJson::ParseWithTransformType(const EcmaVM *vm, JSHandle &msg, JSHandle &reviverVal, TransformType transformType, ParseOptions options) { JSThread *thread = vm->GetJSThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle parseString = JSTaggedValue::ToString(thread, msg); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle result; if (EcmaStringAccessor(parseString).IsUtf8()) { panda::ecmascript::base::Utf8JsonParser parser(thread, transformType, options); result = parser.Parse(parseString); } else { panda::ecmascript::base::Utf16JsonParser parser(thread, transformType, options); result = parser.Parse(*parseString); } RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSTaggedValue reviver = JSTaggedValue::Undefined(); if (reviverVal->IsJSFunction()) { reviver = reviverVal.GetTaggedValue(); if (reviver.IsCallable()) { JSHandle callbackfnHandle(thread, reviver); // Let root be ! OrdinaryObjectCreate(%Object.prototype%). JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSMutableHandle constructor(thread, JSTaggedValue::Undefined()); SCheckMode sCheckMode = SCheckMode::CHECK; InitWithTransformType(env, transformType, constructor, sCheckMode); JSHandle root = factory->NewJSObjectByConstructor(constructor); // Let rootName be the empty String. JSHandle rootName(factory->GetEmptyString()); // Perform ! CreateDataPropertyOrThrow(root, rootName, unfiltered). bool success = JSObject::CreateDataProperty(thread, root, rootName, result, sCheckMode); if (success) { result = Internalize::InternalizeJsonProperty(thread, root, rootName, callbackfnHandle, transformType); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } } } if (transformType == TransformType::SENDABLE) { if (result->IsHeapObject() && !result->IsJSShared() && !result->IsString()) { THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(ClassNotDerivedFromShared), JSTaggedValue::Exception()); } } return result.GetTaggedValue(); } // 24.5.2 JSTaggedValue BuiltinsJson::Stringify(EcmaRuntimeCallInfo *argv) { return BuiltinsJson::StringifyWithTransformType(argv, TransformType::NORMAL); } JSTaggedValue BuiltinsSendableJson::Stringify(EcmaRuntimeCallInfo *argv) { return BuiltinsJson::StringifyWithTransformType(argv, TransformType::SENDABLE); } JSTaggedValue BuiltinsBigIntJson::Stringify(EcmaRuntimeCallInfo *argv) { return BuiltinsJson::StringifyWithTransformType(argv, TransformType::BIGINT); } JSTaggedValue BuiltinsJson::StringifyWithTransformType(EcmaRuntimeCallInfo *argv, TransformType transformType) { BUILTINS_API_TRACE(argv->GetThread(), Json, Stringify); ASSERT(argv); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); uint32_t argc = argv->GetArgsNumber(); JSTaggedValue value = GetCallArg(argv, 0).GetTaggedValue(); JSTaggedValue replacer = JSTaggedValue::Undefined(); JSTaggedValue gap = JSTaggedValue::Undefined(); if (argc == 2) { // 2: 2 args replacer = GetCallArg(argv, 1).GetTaggedValue(); } else if (argc == 3) { // 3: 3 args replacer = GetCallArg(argv, 1).GetTaggedValue(); gap = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD).GetTaggedValue(); } JSHandle handleValue(thread, value); JSHandle handleReplacer(thread, replacer); JSHandle handleGap(thread, gap); panda::ecmascript::base::JsonStringifier stringifier(thread, transformType); JSHandle result = stringifier.Stringify(handleValue, handleReplacer, handleGap); return result.GetTaggedValue(); } } // namespace panda::ecmascript::builtins