2020-06-10 11:01:00 -07:00
/*
2021-04-22 16:53:07 -07:00
* Copyright ( c ) 2020 , Matthew Olsson < mattco @ serenityos . org >
2020-06-10 11:01:00 -07:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-06-10 11:01:00 -07:00
*/
2020-09-28 09:17:33 +02:00
# include <AK/Function.h>
2020-06-10 23:30:36 -07:00
# include <AK/JsonArray.h>
2020-06-20 16:56:01 +02:00
# include <AK/JsonObject.h>
# include <AK/JsonParser.h>
2020-06-10 11:01:00 -07:00
# include <AK/StringBuilder.h>
2022-11-23 13:41:50 +01:00
# include <AK/TypeCasts.h>
2022-02-06 22:22:06 +01:00
# include <AK/Utf16View.h>
2021-07-01 03:24:04 +03:00
# include <AK/Utf8View.h>
2021-06-19 21:45:00 +01:00
# include <LibJS/Runtime/AbstractOperations.h>
2020-06-10 11:01:00 -07:00
# include <LibJS/Runtime/Array.h>
2021-01-01 17:46:39 +01:00
# include <LibJS/Runtime/BigIntObject.h>
# include <LibJS/Runtime/BooleanObject.h>
2020-06-10 11:01:00 -07:00
# include <LibJS/Runtime/Error.h>
2022-01-23 02:12:26 -07:00
# include <LibJS/Runtime/FunctionObject.h>
2020-06-10 11:01:00 -07:00
# include <LibJS/Runtime/GlobalObject.h>
# include <LibJS/Runtime/JSONObject.h>
2021-01-01 17:46:39 +01:00
# include <LibJS/Runtime/NumberObject.h>
2020-06-10 11:01:00 -07:00
# include <LibJS/Runtime/Object.h>
2021-01-01 17:46:39 +01:00
# include <LibJS/Runtime/StringObject.h>
2023-10-06 17:54:21 +02:00
# include <LibJS/Runtime/ValueInlines.h>
2020-06-10 11:01:00 -07:00
namespace JS {
2023-11-19 09:45:05 +01:00
JS_DEFINE_ALLOCATOR ( JSONObject ) ;
2022-08-16 00:20:49 +01:00
JSONObject : : JSONObject ( Realm & realm )
2023-04-13 00:47:15 +02:00
: Object ( ConstructWithPrototypeTag : : Tag , realm . intrinsics ( ) . object_prototype ( ) )
2020-06-20 17:11:11 +02:00
{
}
2023-08-07 08:41:28 +02:00
void JSONObject : : initialize ( Realm & realm )
2020-06-10 11:01:00 -07:00
{
2020-10-14 00:03:58 +02:00
auto & vm = this - > vm ( ) ;
2023-08-07 08:41:28 +02:00
Base : : initialize ( realm ) ;
2020-06-10 11:01:00 -07:00
u8 attr = Attribute : : Writable | Attribute : : Configurable ;
2022-08-22 21:47:35 +01:00
define_native_function ( realm , vm . names . stringify , stringify , 3 , attr ) ;
define_native_function ( realm , vm . names . parse , parse , 2 , attr ) ;
2021-06-13 00:22:35 +01:00
// 25.5.3 JSON [ @@toStringTag ], https://tc39.es/ecma262/#sec-json-@@tostringtag
2023-08-08 18:25:57 +02:00
define_direct_property ( vm . well_known_symbol_to_string_tag ( ) , PrimitiveString : : create ( vm , " JSON " _string ) , Attribute : : Configurable ) ;
2020-06-10 11:01:00 -07:00
}
2021-07-01 03:24:04 +03:00
// 25.5.2 JSON.stringify ( value [ , replacer [ , space ] ] ), https://tc39.es/ecma262/#sec-json.stringify
2023-12-16 17:49:34 +03:30
ThrowCompletionOr < Optional < ByteString > > JSONObject : : stringify_impl ( VM & vm , Value value , Value replacer , Value space )
2020-08-25 22:18:32 +04:30
{
2022-08-21 17:41:49 +01:00
auto & realm = * vm . current_realm ( ) ;
2022-08-16 00:20:49 +01:00
2020-06-10 11:01:00 -07:00
StringifyState state ;
if ( replacer . is_object ( ) ) {
if ( replacer . as_object ( ) . is_function ( ) ) {
state . replacer_function = & replacer . as_function ( ) ;
2021-07-01 03:24:04 +03:00
} else {
2022-08-21 14:00:56 +01:00
auto is_array = TRY ( replacer . is_array ( vm ) ) ;
2021-07-01 03:24:04 +03:00
if ( is_array ) {
auto & replacer_object = replacer . as_object ( ) ;
2022-08-21 19:24:32 +01:00
auto replacer_length = TRY ( length_of_array_like ( vm , replacer_object ) ) ;
2023-12-16 17:49:34 +03:30
Vector < ByteString > list ;
2021-07-01 03:24:04 +03:00
for ( size_t i = 0 ; i < replacer_length ; + + i ) {
2021-10-29 00:27:25 +03:00
auto replacer_value = TRY ( replacer_object . get ( i ) ) ;
2023-12-16 17:49:34 +03:30
Optional < ByteString > item ;
2021-07-01 03:24:04 +03:00
if ( replacer_value . is_string ( ) ) {
2023-12-16 17:49:34 +03:30
item = replacer_value . as_string ( ) . byte_string ( ) ;
2021-07-01 03:24:04 +03:00
} else if ( replacer_value . is_number ( ) ) {
2023-12-16 17:49:34 +03:30
item = MUST ( replacer_value . to_byte_string ( vm ) ) ;
2021-07-01 03:24:04 +03:00
} else if ( replacer_value . is_object ( ) ) {
auto & value_object = replacer_value . as_object ( ) ;
2021-10-12 17:49:01 +01:00
if ( is < StringObject > ( value_object ) | | is < NumberObject > ( value_object ) )
2023-12-16 17:49:34 +03:30
item = TRY ( replacer_value . to_byte_string ( vm ) ) ;
2021-07-01 03:24:04 +03:00
}
2023-10-10 15:00:58 +03:30
if ( item . has_value ( ) & & ! list . contains_slow ( * item ) ) {
list . append ( * item ) ;
2020-06-10 11:01:00 -07:00
}
}
2021-07-01 03:24:04 +03:00
state . property_list = list ;
2020-06-10 11:01:00 -07:00
}
}
}
if ( space . is_object ( ) ) {
2021-07-01 03:24:04 +03:00
auto & space_object = space . as_object ( ) ;
2021-10-17 23:20:05 +03:00
if ( is < NumberObject > ( space_object ) )
2022-08-21 14:00:56 +01:00
space = TRY ( space . to_number ( vm ) ) ;
2021-10-17 23:20:05 +03:00
else if ( is < StringObject > ( space_object ) )
2022-08-21 14:00:56 +01:00
space = TRY ( space . to_primitive_string ( vm ) ) ;
2020-06-10 11:01:00 -07:00
}
if ( space . is_number ( ) ) {
2022-08-21 14:00:56 +01:00
auto space_mv = MUST ( space . to_integer_or_infinity ( vm ) ) ;
2021-07-01 03:24:04 +03:00
space_mv = min ( 10 , space_mv ) ;
2023-12-16 17:49:34 +03:30
state . gap = space_mv < 1 ? ByteString : : empty ( ) : ByteString : : repeated ( ' ' , space_mv ) ;
2020-06-10 11:01:00 -07:00
} else if ( space . is_string ( ) ) {
2023-12-16 17:49:34 +03:30
auto string = space . as_string ( ) . byte_string ( ) ;
2021-07-01 03:24:04 +03:00
if ( string . length ( ) < = 10 )
2020-06-10 11:01:00 -07:00
state . gap = string ;
2021-07-01 03:24:04 +03:00
else
2020-06-10 11:01:00 -07:00
state . gap = string . substring ( 0 , 10 ) ;
} else {
2023-12-16 17:49:34 +03:30
state . gap = ByteString : : empty ( ) ;
2020-06-10 11:01:00 -07:00
}
2022-12-13 20:49:50 +00:00
auto wrapper = Object : : create ( realm , realm . intrinsics ( ) . object_prototype ( ) ) ;
2023-12-16 17:49:34 +03:30
MUST ( wrapper - > create_data_property_or_throw ( ByteString : : empty ( ) , value ) ) ;
return serialize_json_property ( vm , state , ByteString : : empty ( ) , wrapper ) ;
2020-07-04 11:37:50 -07:00
}
2021-06-13 00:22:35 +01:00
// 25.5.2 JSON.stringify ( value [ , replacer [ , space ] ] ), https://tc39.es/ecma262/#sec-json.stringify
2021-10-29 00:27:25 +03:00
JS_DEFINE_NATIVE_FUNCTION ( JSONObject : : stringify )
2020-07-04 11:37:50 -07:00
{
2020-09-27 18:36:49 +02:00
if ( ! vm . argument_count ( ) )
2020-07-04 11:37:50 -07:00
return js_undefined ( ) ;
2020-09-27 18:36:49 +02:00
auto value = vm . argument ( 0 ) ;
auto replacer = vm . argument ( 1 ) ;
auto space = vm . argument ( 2 ) ;
2020-07-04 11:37:50 -07:00
2023-10-10 15:00:58 +03:30
auto maybe_string = TRY ( stringify_impl ( vm , value , replacer , space ) ) ;
if ( ! maybe_string . has_value ( ) )
2020-06-10 11:01:00 -07:00
return js_undefined ( ) ;
2020-07-04 11:37:50 -07:00
2023-10-10 15:00:58 +03:30
return PrimitiveString : : create ( vm , maybe_string . release_value ( ) ) ;
2020-06-10 11:01:00 -07:00
}
2021-07-01 03:24:04 +03:00
// 25.5.2.1 SerializeJSONProperty ( state, key, holder ), https://tc39.es/ecma262/#sec-serializejsonproperty
2023-12-16 17:49:34 +03:30
ThrowCompletionOr < Optional < ByteString > > JSONObject : : serialize_json_property ( VM & vm , StringifyState & state , PropertyKey const & key , Object * holder )
2020-06-10 11:01:00 -07:00
{
2022-02-06 22:02:17 -05:00
// 1. Let value be ? Get(holder, key).
2021-10-29 00:27:25 +03:00
auto value = TRY ( holder - > get ( key ) ) ;
2022-02-06 22:02:17 -05:00
// 2. If Type(value) is Object or BigInt, then
2021-07-01 03:24:04 +03:00
if ( value . is_object ( ) | | value . is_bigint ( ) ) {
2022-02-06 22:02:17 -05:00
// a. Let toJSON be ? GetV(value, "toJSON").
2022-08-21 14:00:56 +01:00
auto to_json = TRY ( value . get ( vm , vm . names . toJSON ) ) ;
2022-02-06 22:02:17 -05:00
// b. If IsCallable(toJSON) is true, then
if ( to_json . is_function ( ) ) {
// i. Set value to ? Call(toJSON, value, « key »).
2022-12-06 22:17:27 +00:00
value = TRY ( call ( vm , to_json . as_function ( ) , value , PrimitiveString : : create ( vm , key . to_string ( ) ) ) ) ;
2022-02-06 22:02:17 -05:00
}
2020-06-10 11:01:00 -07:00
}
2022-02-06 22:02:17 -05:00
// 3. If state.[[ReplacerFunction]] is not undefined, then
if ( state . replacer_function ) {
// a. Set value to ? Call(state.[[ReplacerFunction]], holder, « key, value »).
2022-12-06 22:17:27 +00:00
value = TRY ( call ( vm , * state . replacer_function , holder , PrimitiveString : : create ( vm , key . to_string ( ) ) , value ) ) ;
2022-02-06 22:02:17 -05:00
}
2020-06-10 11:01:00 -07:00
2022-02-06 22:02:17 -05:00
// 4. If Type(value) is Object, then
2020-06-10 11:01:00 -07:00
if ( value . is_object ( ) ) {
auto & value_object = value . as_object ( ) ;
2022-02-06 22:02:17 -05:00
// a. If value has a [[NumberData]] internal slot, then
if ( is < NumberObject > ( value_object ) ) {
// i. Set value to ? ToNumber(value).
2022-08-21 14:00:56 +01:00
value = TRY ( value . to_number ( vm ) ) ;
2022-02-06 22:02:17 -05:00
}
// b. Else if value has a [[StringData]] internal slot, then
else if ( is < StringObject > ( value_object ) ) {
// i. Set value to ? ToString(value).
2022-08-21 14:00:56 +01:00
value = TRY ( value . to_primitive_string ( vm ) ) ;
2022-02-06 22:02:17 -05:00
}
// c. Else if value has a [[BooleanData]] internal slot, then
else if ( is < BooleanObject > ( value_object ) ) {
// i. Set value to value.[[BooleanData]].
2021-12-10 22:50:02 +00:00
value = Value ( static_cast < BooleanObject & > ( value_object ) . boolean ( ) ) ;
2022-02-06 22:02:17 -05:00
}
// d. Else if value has a [[BigIntData]] internal slot, then
else if ( is < BigIntObject > ( value_object ) ) {
// i. Set value to value.[[BigIntData]].
2021-12-10 22:50:02 +00:00
value = Value ( & static_cast < BigIntObject & > ( value_object ) . bigint ( ) ) ;
2022-02-06 22:02:17 -05:00
}
2020-06-10 11:01:00 -07:00
}
2022-02-06 22:02:17 -05:00
// 5. If value is null, return "null".
2020-06-10 11:01:00 -07:00
if ( value . is_null ( ) )
2022-02-06 22:02:17 -05:00
return " null " sv ;
// 6. If value is true, return "true".
// 7. If value is false, return "false".
2020-06-10 11:01:00 -07:00
if ( value . is_boolean ( ) )
2022-02-06 22:02:17 -05:00
return value . as_bool ( ) ? " true " sv : " false " sv ;
// 8. If Type(value) is String, return QuoteJSONString(value).
2020-06-10 11:01:00 -07:00
if ( value . is_string ( ) )
2023-12-16 17:49:34 +03:30
return quote_json_string ( value . as_string ( ) . byte_string ( ) ) ;
2022-02-06 22:02:17 -05:00
// 9. If Type(value) is Number, then
2020-06-10 11:01:00 -07:00
if ( value . is_number ( ) ) {
2022-02-06 22:02:17 -05:00
// a. If value is finite, return ! ToString(value).
2020-06-10 11:01:00 -07:00
if ( value . is_finite_number ( ) )
2023-12-16 17:49:34 +03:30
return MUST ( value . to_byte_string ( vm ) ) ;
2022-02-06 22:02:17 -05:00
// b. Return "null".
return " null " sv ;
2020-06-10 11:01:00 -07:00
}
2022-02-06 22:02:17 -05:00
// 10. If Type(value) is BigInt, throw a TypeError exception.
2021-10-29 00:27:25 +03:00
if ( value . is_bigint ( ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < TypeError > ( ErrorType : : JsonBigInt ) ;
2022-02-06 22:02:17 -05:00
// 11. If Type(value) is Object and IsCallable(value) is false, then
2020-06-10 11:01:00 -07:00
if ( value . is_object ( ) & & ! value . is_function ( ) ) {
2022-02-06 22:02:17 -05:00
// a. Let isArray be ? IsArray(value).
2022-08-21 14:00:56 +01:00
auto is_array = TRY ( value . is_array ( vm ) ) ;
2022-02-06 22:02:17 -05:00
// b. If isArray is true, return ? SerializeJSONArray(state, value).
2021-10-29 00:27:25 +03:00
if ( is_array )
2023-10-10 15:00:58 +03:30
return TRY ( serialize_json_array ( vm , state , value . as_object ( ) ) ) ;
2022-02-06 22:02:17 -05:00
// c. Return ? SerializeJSONObject(state, value).
2023-10-10 15:00:58 +03:30
return TRY ( serialize_json_object ( vm , state , value . as_object ( ) ) ) ;
2020-06-10 11:01:00 -07:00
}
2022-02-06 22:02:17 -05:00
// 12. Return undefined.
2023-12-16 17:49:34 +03:30
return Optional < ByteString > { } ;
2020-06-10 11:01:00 -07:00
}
2021-07-01 03:24:04 +03:00
// 25.5.2.4 SerializeJSONObject ( state, value ), https://tc39.es/ecma262/#sec-serializejsonobject
2023-12-16 17:49:34 +03:30
ThrowCompletionOr < ByteString > JSONObject : : serialize_json_object ( VM & vm , StringifyState & state , Object & object )
2020-06-10 11:01:00 -07:00
{
2021-10-29 00:27:25 +03:00
if ( state . seen_objects . contains ( & object ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < TypeError > ( ErrorType : : JsonCircular ) ;
2020-06-10 11:01:00 -07:00
state . seen_objects . set ( & object ) ;
2023-12-16 17:49:34 +03:30
ByteString previous_indent = state . indent ;
state . indent = ByteString : : formatted ( " {}{} " , state . indent , state . gap ) ;
Vector < ByteString > property_strings ;
2020-06-10 11:01:00 -07:00
2022-04-01 20:58:27 +03:00
auto process_property = [ & ] ( PropertyKey const & key ) - > ThrowCompletionOr < void > {
2021-04-16 13:02:51 +03:00
if ( key . is_symbol ( ) )
2021-10-29 00:27:25 +03:00
return { } ;
2022-08-21 17:41:49 +01:00
auto serialized_property_string = TRY ( serialize_json_property ( vm , state , key , & object ) ) ;
2023-10-10 15:00:58 +03:30
if ( serialized_property_string . has_value ( ) ) {
2023-12-16 17:49:34 +03:30
property_strings . append ( ByteString : : formatted (
2020-10-04 15:18:52 +01:00
" {}:{}{} " ,
quote_json_string ( key . to_string ( ) ) ,
2020-06-10 11:01:00 -07:00
state . gap . is_empty ( ) ? " " : " " ,
2020-10-04 15:18:52 +01:00
serialized_property_string ) ) ;
2020-06-10 11:01:00 -07:00
}
2021-10-29 00:27:25 +03:00
return { } ;
2020-06-10 11:01:00 -07:00
} ;
if ( state . property_list . has_value ( ) ) {
auto property_list = state . property_list . value ( ) ;
2021-10-29 00:27:25 +03:00
for ( auto & property : property_list )
TRY ( process_property ( property ) ) ;
2020-06-10 11:01:00 -07:00
} else {
2021-10-29 00:27:25 +03:00
auto property_list = TRY ( object . enumerable_own_property_names ( PropertyKind : : Key ) ) ;
for ( auto & property : property_list )
2023-12-16 17:49:34 +03:30
TRY ( process_property ( property . as_string ( ) . byte_string ( ) ) ) ;
2020-06-10 11:01:00 -07:00
}
StringBuilder builder ;
if ( property_strings . is_empty ( ) ) {
2022-07-11 17:32:29 +00:00
builder . append ( " {} " sv ) ;
2020-06-10 11:01:00 -07:00
} else {
bool first = true ;
builder . append ( ' { ' ) ;
if ( state . gap . is_empty ( ) ) {
for ( auto & property_string : property_strings ) {
if ( ! first )
builder . append ( ' , ' ) ;
first = false ;
builder . append ( property_string ) ;
}
} else {
builder . append ( ' \n ' ) ;
builder . append ( state . indent ) ;
2023-12-16 17:49:34 +03:30
auto separator = ByteString : : formatted ( " , \n {} " , state . indent ) ;
2020-06-10 11:01:00 -07:00
for ( auto & property_string : property_strings ) {
if ( ! first )
builder . append ( separator ) ;
first = false ;
builder . append ( property_string ) ;
}
builder . append ( ' \n ' ) ;
builder . append ( previous_indent ) ;
}
builder . append ( ' } ' ) ;
}
state . seen_objects . remove ( & object ) ;
state . indent = previous_indent ;
2023-12-16 17:49:34 +03:30
return builder . to_byte_string ( ) ;
2020-06-10 11:01:00 -07:00
}
2021-07-01 03:24:04 +03:00
// 25.5.2.5 SerializeJSONArray ( state, value ), https://tc39.es/ecma262/#sec-serializejsonarray
2023-12-16 17:49:34 +03:30
ThrowCompletionOr < ByteString > JSONObject : : serialize_json_array ( VM & vm , StringifyState & state , Object & object )
2020-06-10 11:01:00 -07:00
{
2021-10-29 00:27:25 +03:00
if ( state . seen_objects . contains ( & object ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < TypeError > ( ErrorType : : JsonCircular ) ;
2020-06-10 11:01:00 -07:00
state . seen_objects . set ( & object ) ;
2023-12-16 17:49:34 +03:30
ByteString previous_indent = state . indent ;
state . indent = ByteString : : formatted ( " {}{} " , state . indent , state . gap ) ;
Vector < ByteString > property_strings ;
2020-06-10 11:01:00 -07:00
2022-08-21 19:24:32 +01:00
auto length = TRY ( length_of_array_like ( vm , object ) ) ;
2021-07-01 03:24:04 +03:00
// Optimization
property_strings . ensure_capacity ( length ) ;
2020-06-10 11:01:00 -07:00
for ( size_t i = 0 ; i < length ; + + i ) {
2022-08-21 17:41:49 +01:00
auto serialized_property_string = TRY ( serialize_json_property ( vm , state , i , & object ) ) ;
2023-10-10 15:00:58 +03:30
if ( ! serialized_property_string . has_value ( ) ) {
2022-07-11 17:32:29 +00:00
property_strings . append ( " null " sv ) ;
2020-06-10 11:01:00 -07:00
} else {
2023-10-10 15:00:58 +03:30
property_strings . append ( serialized_property_string . release_value ( ) ) ;
2020-06-10 11:01:00 -07:00
}
}
StringBuilder builder ;
if ( property_strings . is_empty ( ) ) {
2022-07-11 17:32:29 +00:00
builder . append ( " [] " sv ) ;
2020-06-10 11:01:00 -07:00
} else {
if ( state . gap . is_empty ( ) ) {
builder . append ( ' [ ' ) ;
bool first = true ;
for ( auto & property_string : property_strings ) {
if ( ! first )
builder . append ( ' , ' ) ;
first = false ;
builder . append ( property_string ) ;
}
builder . append ( ' ] ' ) ;
} else {
2022-07-11 17:32:29 +00:00
builder . append ( " [ \n " sv ) ;
2020-06-10 11:01:00 -07:00
builder . append ( state . indent ) ;
2023-12-16 17:49:34 +03:30
auto separator = ByteString : : formatted ( " , \n {} " , state . indent ) ;
2020-06-10 11:01:00 -07:00
bool first = true ;
for ( auto & property_string : property_strings ) {
if ( ! first )
builder . append ( separator ) ;
first = false ;
builder . append ( property_string ) ;
}
builder . append ( ' \n ' ) ;
builder . append ( previous_indent ) ;
builder . append ( ' ] ' ) ;
}
}
state . seen_objects . remove ( & object ) ;
state . indent = previous_indent ;
2023-12-16 17:49:34 +03:30
return builder . to_byte_string ( ) ;
2020-06-10 11:01:00 -07:00
}
2021-07-01 03:24:04 +03:00
// 25.5.2.2 QuoteJSONString ( value ), https://tc39.es/ecma262/#sec-quotejsonstring
2023-12-16 17:49:34 +03:30
ByteString JSONObject : : quote_json_string ( ByteString string )
2020-06-10 11:01:00 -07:00
{
2023-01-22 14:03:01 -05:00
// 1. Let product be the String value consisting solely of the code unit 0x0022 (QUOTATION MARK).
2020-06-10 11:01:00 -07:00
StringBuilder builder ;
builder . append ( ' " ' ) ;
2023-01-22 14:03:01 -05:00
// 2. For each code point C of StringToCodePoints(value), do
2021-07-01 03:24:04 +03:00
auto utf_view = Utf8View ( string ) ;
for ( auto code_point : utf_view ) {
2023-01-22 14:03:01 -05:00
// a. If C is listed in the “Code Point” column of Table 70, then
// i. Set product to the string-concatenation of product and the escape sequence for C as specified in the “Escape Sequence” column of the corresponding row.
2021-07-01 03:24:04 +03:00
switch ( code_point ) {
2020-06-10 11:01:00 -07:00
case ' \b ' :
2022-07-11 17:32:29 +00:00
builder . append ( " \\ b " sv ) ;
2020-06-10 11:01:00 -07:00
break ;
case ' \t ' :
2022-07-11 17:32:29 +00:00
builder . append ( " \\ t " sv ) ;
2020-06-10 11:01:00 -07:00
break ;
case ' \n ' :
2022-07-11 17:32:29 +00:00
builder . append ( " \\ n " sv ) ;
2020-06-10 11:01:00 -07:00
break ;
case ' \f ' :
2022-07-11 17:32:29 +00:00
builder . append ( " \\ f " sv ) ;
2020-06-10 11:01:00 -07:00
break ;
case ' \r ' :
2022-07-11 17:32:29 +00:00
builder . append ( " \\ r " sv ) ;
2020-06-10 11:01:00 -07:00
break ;
case ' " ' :
2022-07-11 17:32:29 +00:00
builder . append ( " \\ \" " sv ) ;
2020-06-10 11:01:00 -07:00
break ;
case ' \\ ' :
2022-07-11 17:32:29 +00:00
builder . append ( " \\ \\ " sv ) ;
2020-06-10 11:01:00 -07:00
break ;
default :
2023-01-22 14:03:01 -05:00
// b. Else if C has a numeric value less than 0x0020 (SPACE), or if C has the same numeric value as a leading surrogate or trailing surrogate, then
2023-01-22 14:04:28 -05:00
if ( code_point < 0x20 | | is_unicode_surrogate ( code_point ) ) {
2023-01-22 14:03:01 -05:00
// i. Let unit be the code unit whose numeric value is that of C.
// ii. Set product to the string-concatenation of product and UnicodeEscape(unit).
2021-07-01 03:24:04 +03:00
builder . appendff ( " \\ u{:04x} " , code_point ) ;
2023-01-22 14:03:01 -05:00
}
// c. Else,
else {
// i. Set product to the string-concatenation of product and UTF16EncodeCodePoint(C).
2021-07-01 03:24:04 +03:00
builder . append_code_point ( code_point ) ;
2020-06-10 11:01:00 -07:00
}
}
}
2023-01-22 14:03:01 -05:00
// 3. Set product to the string-concatenation of product and the code unit 0x0022 (QUOTATION MARK).
2020-06-10 11:01:00 -07:00
builder . append ( ' " ' ) ;
2023-01-22 14:03:01 -05:00
// 4. Return product.
2023-12-16 17:49:34 +03:30
return builder . to_byte_string ( ) ;
2020-06-10 11:01:00 -07:00
}
2021-06-13 00:22:35 +01:00
// 25.5.1 JSON.parse ( text [ , reviver ] ), https://tc39.es/ecma262/#sec-json.parse
2021-10-29 00:27:25 +03:00
JS_DEFINE_NATIVE_FUNCTION ( JSONObject : : parse )
2020-06-10 11:01:00 -07:00
{
2022-08-22 11:48:08 +01:00
auto & realm = * vm . current_realm ( ) ;
2022-08-16 00:20:49 +01:00
2023-12-16 17:49:34 +03:30
auto string = TRY ( vm . argument ( 0 ) . to_byte_string ( vm ) ) ;
2020-09-27 18:36:49 +02:00
auto reviver = vm . argument ( 1 ) ;
2020-06-10 23:30:36 -07:00
auto json = JsonValue : : from_string ( string ) ;
2021-11-15 01:46:51 +01:00
if ( json . is_error ( ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < SyntaxError > ( ErrorType : : JsonMalformed ) ;
2022-08-21 17:41:49 +01:00
Value unfiltered = parse_json_value ( vm , json . value ( ) ) ;
2020-06-10 23:30:36 -07:00
if ( reviver . is_function ( ) ) {
2022-12-13 20:49:50 +00:00
auto root = Object : : create ( realm , realm . intrinsics ( ) . object_prototype ( ) ) ;
2023-12-16 17:49:34 +03:30
auto root_name = ByteString : : empty ( ) ;
2021-10-03 01:18:46 +01:00
MUST ( root - > create_data_property_or_throw ( root_name , unfiltered ) ) ;
2022-08-21 17:41:49 +01:00
return internalize_json_property ( vm , root , root_name , reviver . as_function ( ) ) ;
2020-06-10 23:30:36 -07:00
}
2021-07-01 03:54:24 +03:00
return unfiltered ;
2020-06-10 23:30:36 -07:00
}
2022-08-21 17:41:49 +01:00
Value JSONObject : : parse_json_value ( VM & vm , JsonValue const & value )
2020-06-10 23:30:36 -07:00
{
if ( value . is_object ( ) )
2022-08-21 17:41:49 +01:00
return Value ( parse_json_object ( vm , value . as_object ( ) ) ) ;
2020-06-10 23:30:36 -07:00
if ( value . is_array ( ) )
2022-08-21 17:41:49 +01:00
return Value ( parse_json_array ( vm , value . as_array ( ) ) ) ;
2020-06-10 23:30:36 -07:00
if ( value . is_null ( ) )
return js_null ( ) ;
2024-01-12 20:52:38 -05:00
if ( auto double_value = value . get_double_with_precision_loss ( ) ; double_value . has_value ( ) )
return Value ( double_value . value ( ) ) ;
2020-06-10 23:30:36 -07:00
if ( value . is_string ( ) )
2024-01-06 15:49:17 -05:00
return PrimitiveString : : create ( vm , value . as_string ( ) ) ;
2020-06-10 23:30:36 -07:00
if ( value . is_bool ( ) )
return Value ( static_cast < bool > ( value . as_bool ( ) ) ) ;
2021-02-23 20:42:32 +01:00
VERIFY_NOT_REACHED ( ) ;
2020-06-10 23:30:36 -07:00
}
2022-08-21 17:41:49 +01:00
Object * JSONObject : : parse_json_object ( VM & vm , JsonObject const & json_object )
2020-06-10 23:30:36 -07:00
{
2022-08-21 17:41:49 +01:00
auto & realm = * vm . current_realm ( ) ;
2022-12-13 20:49:50 +00:00
auto object = Object : : create ( realm , realm . intrinsics ( ) . object_prototype ( ) ) ;
2020-06-20 16:56:01 +02:00
json_object . for_each_member ( [ & ] ( auto & key , auto & value ) {
2022-08-21 17:41:49 +01:00
object - > define_direct_property ( key , parse_json_value ( vm , value ) , default_attributes ) ;
2020-06-10 23:30:36 -07:00
} ) ;
return object ;
}
2022-08-21 17:41:49 +01:00
Array * JSONObject : : parse_json_array ( VM & vm , JsonArray const & json_array )
2020-06-10 23:30:36 -07:00
{
2022-08-21 17:41:49 +01:00
auto & realm = * vm . current_realm ( ) ;
2022-12-13 20:49:49 +00:00
auto array = MUST ( Array : : create ( realm , 0 ) ) ;
2020-06-10 23:30:36 -07:00
size_t index = 0 ;
2020-06-20 16:56:01 +02:00
json_array . for_each ( [ & ] ( auto & value ) {
2022-08-21 17:41:49 +01:00
array - > define_direct_property ( index + + , parse_json_value ( vm , value ) , default_attributes ) ;
2020-06-10 23:30:36 -07:00
} ) ;
return array ;
}
2021-06-16 20:52:30 +01:00
// 25.5.1.1 InternalizeJSONProperty ( holder, name, reviver ), https://tc39.es/ecma262/#sec-internalizejsonproperty
2022-08-21 17:41:49 +01:00
ThrowCompletionOr < Value > JSONObject : : internalize_json_property ( VM & vm , Object * holder , PropertyKey const & name , FunctionObject & reviver )
2020-06-10 23:30:36 -07:00
{
2021-10-29 00:27:25 +03:00
auto value = TRY ( holder - > get ( name ) ) ;
2020-06-10 23:30:36 -07:00
if ( value . is_object ( ) ) {
2022-08-21 14:00:56 +01:00
auto is_array = TRY ( value . is_array ( vm ) ) ;
2020-06-10 23:30:36 -07:00
2021-07-01 03:54:24 +03:00
auto & value_object = value . as_object ( ) ;
2022-04-01 20:58:27 +03:00
auto process_property = [ & ] ( PropertyKey const & key ) - > ThrowCompletionOr < void > {
2022-08-21 17:41:49 +01:00
auto element = TRY ( internalize_json_property ( vm , & value_object , key , reviver ) ) ;
2021-10-03 00:53:06 +01:00
if ( element . is_undefined ( ) )
2021-09-29 18:45:33 +01:00
TRY ( value_object . internal_delete ( key ) ) ;
2021-10-03 00:53:06 +01:00
else
TRY ( value_object . create_data_property ( key , element ) ) ;
2021-09-29 18:45:33 +01:00
return { } ;
2020-06-10 23:30:36 -07:00
} ;
2021-07-01 03:54:24 +03:00
if ( is_array ) {
2022-08-21 19:24:32 +01:00
auto length = TRY ( length_of_array_like ( vm , value_object ) ) ;
2021-09-29 18:45:33 +01:00
for ( size_t i = 0 ; i < length ; + + i )
2021-10-29 00:27:25 +03:00
TRY ( process_property ( i ) ) ;
2020-06-10 23:30:36 -07:00
} else {
2021-10-29 00:27:25 +03:00
auto property_list = TRY ( value_object . enumerable_own_property_names ( Object : : PropertyKind : : Key ) ) ;
2022-02-06 15:59:04 +00:00
for ( auto & property_key : property_list )
2023-12-16 17:49:34 +03:30
TRY ( process_property ( property_key . as_string ( ) . byte_string ( ) ) ) ;
2020-06-10 23:30:36 -07:00
}
}
2020-08-25 22:18:32 +04:30
2022-12-06 22:17:27 +00:00
return TRY ( call ( vm , reviver , holder , PrimitiveString : : create ( vm , name . to_string ( ) ) , value ) ) ;
2020-06-10 11:01:00 -07:00
}
}