Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4428639
json.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
21 KB
Referenced Files
None
Subscribers
None
json.c
View Options
#include
<stdlib.h>
#include
<stdint.h>
#include
<string.h>
#include
<math.h>
#include
<yyjson.h>
#include
<uthash.h>
#include
"gc/roots.h"
#include
"utf8.h"
#include
"errors.h"
#include
"runtime.h"
#include
"internal.h"
#include
"silver/engine.h"
#include
"modules/json.h"
#include
"modules/symbol.h"
typedef
struct
{
const
char
*
key
;
size_t
key_len
;
ant_offset_t
prop_off
;
UT_hash_handle
hh
;
}
json_key_entry_t
;
static
inline
bool
json_value_needs_temp_root
(
ant_value_t
value
)
{
if
(
value
<=
NANBOX_PREFIX
)
return
false
;
static
const
uint32_t
mask
=
(
1u
<<
T_STR
)
|
(
1u
<<
T_OBJ
)
|
(
1u
<<
T_ARR
)
|
(
1u
<<
T_FUNC
)
|
(
1u
<<
T_PROMISE
)
|
(
1u
<<
T_GENERATOR
)
|
(
1u
<<
T_SYMBOL
)
|
(
1u
<<
T_BIGINT
);
uint8_t
t
=
vtype
(
value
);
return
t
<
32
&&
(
mask
>>
t
)
&
1
;
}
static
inline
bool
json_temp_pin
(
gc_temp_root_scope_t
*
roots
,
ant_value_t
value
)
{
if
(
!
json_value_needs_temp_root
(
value
))
return
true
;
return
gc_temp_root_handle_valid
(
gc_temp_root_add
(
roots
,
value
));
}
static
inline
ant_value_t
json_parse_oom
(
ant_t
*
js
)
{
return
js_mkerr
(
js
,
"JSON.parse() failed: out of memory"
);
}
static
inline
ant_value_t
json_stringify_oom
(
ant_t
*
js
)
{
return
js_mkerr
(
js
,
"JSON.stringify() failed: out of memory"
);
}
static
ant_value_t
yyjson_to_jsval
(
ant_t
*
js
,
yyjson_val
*
val
,
gc_temp_root_scope_t
*
roots
)
{
if
(
!
val
)
return
js_mkundef
();
switch
(
yyjson_get_type
(
val
))
{
case
YYJSON_TYPE_NULL
:
return
js_mknull
();
case
YYJSON_TYPE_BOOL
:
return
js_bool
(
yyjson_get_bool
(
val
));
case
YYJSON_TYPE_STR
:
{
ant_value_t
str
=
js_mkstr
(
js
,
yyjson_get_str
(
val
),
yyjson_get_len
(
val
));
if
(
is_err
(
str
))
return
str
;
if
(
!
json_temp_pin
(
roots
,
str
))
return
json_parse_oom
(
js
);
return
str
;
}
case
YYJSON_TYPE_NUM
:
{
if
(
yyjson_is_sint
(
val
))
return
js_mknum
((
double
)
yyjson_get_sint
(
val
));
if
(
yyjson_is_uint
(
val
))
return
js_mknum
((
double
)
yyjson_get_uint
(
val
));
return
js_mknum
(
yyjson_get_real
(
val
));
}
case
YYJSON_TYPE_ARR
:
{
ant_value_t
arr
=
js_mkarr
(
js
);
if
(
is_err
(
arr
))
return
arr
;
if
(
!
json_temp_pin
(
roots
,
arr
))
return
json_parse_oom
(
js
);
size_t
idx
,
max
;
yyjson_val
*
item
;
yyjson_arr_foreach
(
val
,
idx
,
max
,
item
)
{
ant_value_t
elem
=
yyjson_to_jsval
(
js
,
item
,
roots
);
if
(
is_err
(
elem
))
return
elem
;
js_arr_push
(
js
,
arr
,
elem
);
}
return
arr
;
}
case
YYJSON_TYPE_OBJ
:
{
ant_value_t
obj
=
js_newobj
(
js
);
if
(
is_err
(
obj
))
return
obj
;
if
(
!
json_temp_pin
(
roots
,
obj
))
return
json_parse_oom
(
js
);
size_t
idx
,
max
;
yyjson_val
*
key
,
*
item
;
json_key_entry_t
*
hash
=
NULL
,
*
entry
,
*
tmp
;
yyjson_obj_foreach
(
val
,
idx
,
max
,
key
,
item
)
{
const
char
*
k
=
yyjson_get_str
(
key
);
size_t
klen
=
yyjson_get_len
(
key
);
ant_value_t
v
=
yyjson_to_jsval
(
js
,
item
,
roots
);
if
(
is_err
(
v
))
{
HASH_ITER
(
hh
,
hash
,
entry
,
tmp
)
HASH_DEL
(
hash
,
entry
);
free
(
entry
);
return
v
;
}
HASH_FIND
(
hh
,
hash
,
k
,
klen
,
entry
);
if
(
entry
)
js_saveval
(
js
,
entry
->
prop_off
,
v
);
else
{
ant_offset_t
off
=
js_mkprop_fast_off
(
js
,
obj
,
k
,
klen
,
v
);
if
(
off
==
0
)
{
HASH_ITER
(
hh
,
hash
,
entry
,
tmp
)
HASH_DEL
(
hash
,
entry
);
free
(
entry
);
return
json_parse_oom
(
js
);
}
entry
=
malloc
(
sizeof
(
json_key_entry_t
));
if
(
!
entry
)
{
HASH_ITER
(
hh
,
hash
,
entry
,
tmp
)
HASH_DEL
(
hash
,
entry
);
free
(
entry
);
return
json_parse_oom
(
js
);
}
entry
->
key
=
k
;
entry
->
key_len
=
klen
;
entry
->
prop_off
=
off
;
HASH_ADD_KEYPTR
(
hh
,
hash
,
entry
->
key
,
entry
->
key_len
,
entry
);
}}
HASH_ITER
(
hh
,
hash
,
entry
,
tmp
)
HASH_DEL
(
hash
,
entry
);
free
(
entry
);
return
obj
;
}
default
:
return
js_mkundef
();
}
}
typedef
struct
{
ant_t
*
js
;
ant_value_t
*
stack
;
ant_value_t
replacer_func
;
ant_value_t
replacer_arr
;
ant_value_t
error
;
ant_value_t
holder
;
gc_temp_root_scope_t
temp_roots
;
gc_temp_root_handle_t
error_handle
;
gc_temp_root_handle_t
holder_handle
;
int
stack_size
;
int
stack_cap
;
int
replacer_arr_len
;
int
has_cycle
;
}
json_cycle_ctx
;
static
inline
bool
json_has_abort
(
json_cycle_ctx
*
ctx
)
{
return
ctx
->
has_cycle
||
vtype
(
ctx
->
error
)
!=
T_UNDEF
;
}
static
inline
ant_value_t
json_normalize_error
(
ant_value_t
value
)
{
if
(
is_err
(
value
)
&&
vdata
(
value
)
!=
0
)
return
js_as_obj
(
value
);
return
value
;
}
static
void
json_set_error
(
json_cycle_ctx
*
ctx
,
ant_value_t
value
)
{
ctx
->
error
=
value
;
gc_temp_root_set
(
ctx
->
error_handle
,
value
);
}
static
inline
bool
json_ctx_pin_value
(
json_cycle_ctx
*
ctx
,
ant_value_t
value
)
{
if
(
json_temp_pin
(
&
ctx
->
temp_roots
,
value
))
return
true
;
json_set_error
(
ctx
,
json_stringify_oom
(
ctx
->
js
));
return
false
;
}
static
inline
void
json_set_holder
(
json_cycle_ctx
*
ctx
,
ant_value_t
value
)
{
ctx
->
holder
=
value
;
gc_temp_root_set
(
ctx
->
holder_handle
,
value
);
}
static
void
json_capture_error
(
json_cycle_ctx
*
ctx
,
ant_value_t
value
)
{
if
(
vtype
(
ctx
->
error
)
!=
T_UNDEF
)
return
;
if
(
ctx
->
js
->
thrown_exists
)
{
json_set_error
(
ctx
,
ctx
->
js
->
thrown_value
);
ctx
->
js
->
thrown_exists
=
false
;
ctx
->
js
->
thrown_value
=
js_mkundef
();
return
;
}
json_set_error
(
ctx
,
json_normalize_error
(
value
));
}
static
yyjson_mut_val
*
json_string_to_yyjson
(
ant_t
*
js
,
yyjson_mut_doc
*
doc
,
ant_value_t
value
)
{
size_t
byte_len
=
0
;
char
*
str
=
js_getstr
(
js
,
value
,
&
byte_len
);
size_t
raw_len
=
0
;
char
*
raw
=
utf8_json_quote
(
str
,
byte_len
,
&
raw_len
);
if
(
!
raw
)
goto
oom
;
yyjson_mut_val
*
out
=
yyjson_mut_rawncpy
(
doc
,
raw
,
raw_len
);
free
(
raw
);
return
out
;
oom
:
free
(
raw
);
return
NULL
;
}
static
int
json_cycle_check
(
json_cycle_ctx
*
ctx
,
ant_value_t
val
)
{
for
(
int
i
=
0
;
i
<
ctx
->
stack_size
;
i
++
)
if
(
ctx
->
stack
[
i
]
==
val
)
{
ctx
->
has_cycle
=
1
;
return
1
;
}
return
0
;
}
static
void
json_cycle_push
(
json_cycle_ctx
*
ctx
,
ant_value_t
val
)
{
if
(
ctx
->
stack_size
>=
ctx
->
stack_cap
)
{
ctx
->
stack_cap
=
ctx
->
stack_cap
?
ctx
->
stack_cap
*
2
:
16
;
ctx
->
stack
=
realloc
(
ctx
->
stack
,
ctx
->
stack_cap
*
sizeof
(
ant_value_t
));
}
ctx
->
stack
[
ctx
->
stack_size
++
]
=
val
;
}
static
inline
void
json_cycle_pop
(
json_cycle_ctx
*
ctx
)
{
if
(
ctx
->
stack_size
>
0
)
ctx
->
stack_size
--
;
}
static
inline
int
key_matches
(
const
char
*
a
,
size_t
a_len
,
const
char
*
b
,
size_t
b_len
)
{
return
a_len
==
b_len
&&
memcmp
(
a
,
b
,
a_len
)
==
0
;
}
static
inline
bool
json_is_array
(
ant_value_t
value
)
{
return
vtype
(
value
)
==
T_ARR
;
}
static
inline
ant_value_t
json_snapshot_keys
(
ant_t
*
js
,
ant_value_t
value
)
{
if
(
!
is_special_object
(
value
))
return
js_mkarr
(
js
);
return
js_for_in_keys
(
js
,
value
);
}
static
int
is_key_in_replacer_arr
(
ant_t
*
js
,
json_cycle_ctx
*
ctx
,
const
char
*
key
,
size_t
key_len
)
{
if
(
!
is_special_object
(
ctx
->
replacer_arr
))
return
1
;
for
(
int
i
=
0
;
i
<
ctx
->
replacer_arr_len
;
i
++
)
{
char
idxstr
[
32
];
snprintf
(
idxstr
,
sizeof
(
idxstr
),
"%d"
,
i
);
ant_value_t
item
=
js_get
(
js
,
ctx
->
replacer_arr
,
idxstr
);
int
type
=
vtype
(
item
);
if
(
type
==
T_STR
)
{
size_t
item_len
;
char
*
item_str
=
js_getstr
(
js
,
item
,
&
item_len
);
if
(
key_matches
(
item_str
,
item_len
,
key
,
key_len
))
return
1
;
}
else
if
(
type
==
T_NUM
)
{
char
numstr
[
32
];
snprintf
(
numstr
,
sizeof
(
numstr
),
"%.0f"
,
js_getnum
(
item
));
if
(
key_matches
(
numstr
,
strlen
(
numstr
),
key
,
key_len
))
return
1
;
}}
return
0
;
}
static
yyjson_mut_val
*
ant_value_to_yyjson_with_key
(
ant_t
*
js
,
yyjson_mut_doc
*
doc
,
const
char
*
key
,
ant_value_t
val
,
json_cycle_ctx
*
ctx
,
int
in_array
);
static
ant_value_t
apply_reviver
(
ant_t
*
js
,
ant_value_t
holder
,
const
char
*
key
,
ant_value_t
reviver
,
gc_temp_root_scope_t
*
roots
);
static
ant_value_t
json_apply_tojson
(
ant_t
*
js
,
const
char
*
key
,
ant_value_t
val
,
json_cycle_ctx
*
ctx
)
{
if
(
!
is_special_object
(
val
))
return
val
;
ant_value_t
toJSON
=
js_get
(
js
,
val
,
"toJSON"
);
if
(
is_err
(
toJSON
))
{
json_capture_error
(
ctx
,
toJSON
);
return
js_mkundef
();
}
if
(
!
is_callable
(
toJSON
))
return
val
;
ant_value_t
key_arg
=
js_mkstr
(
js
,
key
,
strlen
(
key
));
if
(
is_err
(
key_arg
))
{
json_capture_error
(
ctx
,
key_arg
);
return
js_mkundef
();
}
if
(
!
json_ctx_pin_value
(
ctx
,
key_arg
))
return
js_mkundef
();
ant_value_t
args
[
1
]
=
{
key_arg
};
ant_value_t
transformed
=
sv_vm_call
(
js
->
vm
,
js
,
toJSON
,
val
,
args
,
1
,
NULL
,
false
);
if
(
is_err
(
transformed
))
{
json_capture_error
(
ctx
,
transformed
);
return
js_mkundef
();
}
if
(
!
json_ctx_pin_value
(
ctx
,
transformed
))
return
js_mkundef
();
return
transformed
;
}
static
ant_value_t
json_apply_replacer
(
ant_t
*
js
,
const
char
*
key
,
ant_value_t
val
,
json_cycle_ctx
*
ctx
)
{
if
(
!
is_callable
(
ctx
->
replacer_func
))
return
val
;
ant_value_t
key_arg
=
js_mkstr
(
js
,
key
,
strlen
(
key
));
if
(
is_err
(
key_arg
))
{
json_capture_error
(
ctx
,
key_arg
);
return
js_mkundef
();
}
if
(
!
json_ctx_pin_value
(
ctx
,
key_arg
))
return
js_mkundef
();
ant_value_t
args
[
2
]
=
{
key_arg
,
val
};
ant_value_t
transformed
=
sv_vm_call
(
js
->
vm
,
js
,
ctx
->
replacer_func
,
ctx
->
holder
,
args
,
2
,
NULL
,
false
);
if
(
is_err
(
transformed
))
{
json_capture_error
(
ctx
,
transformed
);
return
js_mkundef
();
}
if
(
!
json_ctx_pin_value
(
ctx
,
transformed
))
return
js_mkundef
();
return
transformed
;
}
static
inline
ant_value_t
json_create_root_holder
(
ant_t
*
js
,
ant_value_t
value
,
json_cycle_ctx
*
ctx
)
{
ant_value_t
holder
=
js_mkobj
(
js
);
if
(
is_err
(
holder
))
return
holder
;
if
(
!
json_ctx_pin_value
(
ctx
,
holder
))
return
js_mkundef
();
js_set
(
js
,
holder
,
""
,
value
);
return
holder
;
}
static
yyjson_mut_val
*
json_array_to_yyjson
(
ant_t
*
js
,
yyjson_mut_doc
*
doc
,
ant_value_t
val
,
json_cycle_ctx
*
ctx
)
{
yyjson_mut_val
*
arr
=
yyjson_mut_arr
(
doc
);
ant_offset_t
length
=
js_arr_len
(
js
,
val
);
ant_value_t
saved_holder
=
ctx
->
holder
;
json_set_holder
(
ctx
,
val
);
for
(
ant_offset_t
i
=
0
;
i
<
length
;
i
++
)
{
char
idxstr
[
32
];
uint_to_str
(
idxstr
,
sizeof
(
idxstr
),
(
uint64_t
)
i
);
ant_value_t
elem
=
js_arr_get
(
js
,
val
,
i
);
yyjson_mut_val
*
item
=
ant_value_to_yyjson_with_key
(
js
,
doc
,
idxstr
,
elem
,
ctx
,
1
);
if
(
json_has_abort
(
ctx
))
{
json_set_holder
(
ctx
,
saved_holder
);
return
NULL
;
}
yyjson_mut_arr_add_val
(
arr
,
item
);
}
json_set_holder
(
ctx
,
saved_holder
);
return
arr
;
}
static
yyjson_mut_val
*
json_object_to_yyjson
(
ant_t
*
js
,
yyjson_mut_doc
*
doc
,
ant_value_t
val
,
json_cycle_ctx
*
ctx
)
{
yyjson_mut_val
*
obj
=
yyjson_mut_obj
(
doc
);
ant_value_t
keys
=
json_snapshot_keys
(
js
,
val
);
ant_value_t
saved_holder
=
ctx
->
holder
;
if
(
is_err
(
keys
))
{
json_capture_error
(
ctx
,
keys
);
return
NULL
;
}
if
(
!
json_ctx_pin_value
(
ctx
,
keys
))
return
NULL
;
json_set_holder
(
ctx
,
val
);
ant_offset_t
key_count
=
js_arr_len
(
js
,
keys
);
for
(
ant_offset_t
i
=
0
;
i
<
key_count
;
i
++
)
{
ant_value_t
key_val
=
js_arr_get
(
js
,
keys
,
i
);
size_t
key_len
=
0
;
char
*
key
=
js_getstr
(
js
,
key_val
,
&
key_len
);
if
(
!
key
)
continue
;
if
(
!
is_key_in_replacer_arr
(
js
,
ctx
,
key
,
key_len
))
continue
;
ant_value_t
prop
=
js_get
(
js
,
val
,
key
);
if
(
is_err
(
prop
))
{
json_capture_error
(
ctx
,
prop
);
json_set_holder
(
ctx
,
saved_holder
);
return
NULL
;
}
yyjson_mut_val
*
jval
=
ant_value_to_yyjson_with_key
(
js
,
doc
,
key
,
prop
,
ctx
,
0
);
if
(
json_has_abort
(
ctx
))
{
json_set_holder
(
ctx
,
saved_holder
);
return
NULL
;
}
if
(
jval
==
YYJSON_SKIP_VALUE
)
continue
;
yyjson_mut_obj_add
(
obj
,
yyjson_mut_strncpy
(
doc
,
key
,
key_len
),
jval
);
}
json_set_holder
(
ctx
,
saved_holder
);
return
obj
;
}
static
yyjson_mut_val
*
ant_value_to_yyjson_impl
(
ant_t
*
js
,
yyjson_mut_doc
*
doc
,
ant_value_t
val
,
json_cycle_ctx
*
ctx
,
int
in_array
)
{
int
type
=
vtype
(
val
);
yyjson_mut_val
*
result
=
NULL
;
switch
(
type
)
{
case
T_NULL
:
return
yyjson_mut_null
(
doc
);
case
T_BOOL
:
return
yyjson_mut_bool
(
doc
,
val
==
js_true
);
case
T_UNDEF
:
return
in_array
?
yyjson_mut_null
(
doc
)
:
YYJSON_SKIP_VALUE
;
case
T_FUNC
:
return
in_array
?
yyjson_mut_null
(
doc
)
:
YYJSON_SKIP_VALUE
;
case
T_SYMBOL
:
return
in_array
?
yyjson_mut_null
(
doc
)
:
YYJSON_SKIP_VALUE
;
case
T_NUM
:
{
double
num
=
js_getnum
(
val
);
if
(
isnan
(
num
)
||
isinf
(
num
))
return
yyjson_mut_null
(
doc
);
if
(
num
>=
(
double
)
INT64_MIN
&&
num
<
(
double
)
INT64_MAX
&&
num
==
(
double
)(
int64_t
)
num
)
return
yyjson_mut_sint
(
doc
,
(
int64_t
)
num
);
return
yyjson_mut_real
(
doc
,
num
);
}
case
T_STR
:
{
return
json_string_to_yyjson
(
js
,
doc
,
val
);
}
case
T_OBJ
:
case
T_ARR
:
break
;
default
:
return
yyjson_mut_null
(
doc
);
}
if
(
json_cycle_check
(
ctx
,
val
))
return
NULL
;
json_cycle_push
(
ctx
,
val
);
result
=
json_is_array
(
val
)
?
json_array_to_yyjson
(
js
,
doc
,
val
,
ctx
)
:
json_object_to_yyjson
(
js
,
doc
,
val
,
ctx
);
json_cycle_pop
(
ctx
);
return
result
;
}
static
yyjson_mut_val
*
ant_value_to_yyjson_with_key
(
ant_t
*
js
,
yyjson_mut_doc
*
doc
,
const
char
*
key
,
ant_value_t
val
,
json_cycle_ctx
*
ctx
,
int
in_array
)
{
val
=
json_apply_tojson
(
js
,
key
,
val
,
ctx
);
if
(
json_has_abort
(
ctx
))
return
NULL
;
val
=
json_apply_replacer
(
js
,
key
,
val
,
ctx
);
if
(
json_has_abort
(
ctx
))
return
NULL
;
return
ant_value_to_yyjson_impl
(
js
,
doc
,
val
,
ctx
,
in_array
);
}
static
yyjson_mut_val
*
ant_value_to_yyjson
(
ant_t
*
js
,
yyjson_mut_doc
*
doc
,
ant_value_t
val
,
json_cycle_ctx
*
ctx
)
{
return
ant_value_to_yyjson_with_key
(
js
,
doc
,
""
,
val
,
ctx
,
0
);
}
static
ant_value_t
apply_reviver_call
(
ant_t
*
js
,
ant_value_t
holder
,
const
char
*
key
,
ant_value_t
reviver
,
gc_temp_root_scope_t
*
roots
)
{
ant_value_t
key_str
=
js_mkstr
(
js
,
key
,
strlen
(
key
));
if
(
is_err
(
key_str
))
return
key_str
;
if
(
!
json_temp_pin
(
roots
,
key_str
))
return
json_parse_oom
(
js
);
ant_value_t
current_value
=
js_get
(
js
,
holder
,
key
);
ant_value_t
call_args
[
2
]
=
{
key_str
,
current_value
};
ant_value_t
result
=
sv_vm_call
(
js
->
vm
,
js
,
reviver
,
holder
,
call_args
,
2
,
NULL
,
false
);
if
(
!
is_err
(
result
)
&&
!
json_temp_pin
(
roots
,
result
))
return
json_parse_oom
(
js
);
return
result
;
}
static
void
apply_reviver_to_array
(
ant_t
*
js
,
ant_value_t
value
,
ant_value_t
reviver
,
gc_temp_root_scope_t
*
roots
)
{
ant_offset_t
length
=
js_arr_len
(
js
,
value
);
for
(
ant_offset_t
i
=
0
;
i
<
length
;
i
++
)
{
char
idxstr
[
32
];
size_t
idx_len
=
uint_to_str
(
idxstr
,
sizeof
(
idxstr
),
(
uint64_t
)
i
);
ant_value_t
new_elem
=
apply_reviver
(
js
,
value
,
idxstr
,
reviver
,
roots
);
if
(
vtype
(
new_elem
)
==
T_UNDEF
)
js_delete_prop
(
js
,
value
,
idxstr
,
idx_len
);
else
{
ant_value_t
key_val
=
js_mkstr
(
js
,
idxstr
,
idx_len
);
if
(
is_err
(
key_val
))
return
;
if
(
!
json_temp_pin
(
roots
,
key_val
))
return
;
js_setprop
(
js
,
value
,
key_val
,
new_elem
);
}}
}
static
void
apply_reviver_to_object
(
ant_t
*
js
,
ant_value_t
value
,
ant_value_t
reviver
,
gc_temp_root_scope_t
*
roots
)
{
ant_value_t
keys
=
json_snapshot_keys
(
js
,
value
);
if
(
is_err
(
keys
)
||
vtype
(
keys
)
!=
T_ARR
)
return
;
if
(
!
json_temp_pin
(
roots
,
keys
))
return
;
ant_offset_t
key_count
=
js_arr_len
(
js
,
keys
);
for
(
ant_offset_t
i
=
0
;
i
<
key_count
;
i
++
)
{
ant_value_t
key_val
=
js_arr_get
(
js
,
keys
,
i
);
size_t
key_len
=
0
;
char
*
key
=
js_getstr
(
js
,
key_val
,
&
key_len
);
if
(
!
key
)
continue
;
ant_value_t
new_val
=
apply_reviver
(
js
,
value
,
key
,
reviver
,
roots
);
if
(
vtype
(
new_val
)
==
T_UNDEF
)
js_delete_prop
(
js
,
value
,
key
,
key_len
);
else
js_set
(
js
,
value
,
key
,
new_val
);
}
}
static
ant_value_t
apply_reviver
(
ant_t
*
js
,
ant_value_t
holder
,
const
char
*
key
,
ant_value_t
reviver
,
gc_temp_root_scope_t
*
roots
)
{
ant_value_t
val
=
js_get
(
js
,
holder
,
key
);
if
(
json_is_array
(
val
))
apply_reviver_to_array
(
js
,
val
,
reviver
,
roots
);
else
if
(
vtype
(
val
)
==
T_OBJ
)
apply_reviver_to_object
(
js
,
val
,
reviver
,
roots
);
return
apply_reviver_call
(
js
,
holder
,
key
,
reviver
,
roots
);
}
ant_value_t
js_json_parse
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"JSON.parse() requires at least 1 argument"
);
if
(
vtype
(
args
[
0
])
!=
T_STR
)
return
js_mkerr
(
js
,
"JSON.parse() argument must be a string"
);
gc_temp_root_scope_t
temp_roots
;
gc_temp_root_scope_begin
(
js
,
&
temp_roots
);
size_t
len
;
char
*
json_str
=
js_getstr
(
js
,
args
[
0
],
&
len
);
yyjson_doc
*
doc
=
yyjson_read
(
json_str
,
len
,
0
);
if
(
!
doc
)
{
gc_temp_root_scope_end
(
&
temp_roots
);
return
js_mkerr_typed
(
js
,
JS_ERR_SYNTAX
,
"JSON.parse: unexpected character"
);
}
ant_value_t
result
=
yyjson_to_jsval
(
js
,
yyjson_doc_get_root
(
doc
),
&
temp_roots
);
yyjson_doc_free
(
doc
);
if
(
is_err
(
result
))
{
gc_temp_root_scope_end
(
&
temp_roots
);
return
result
;
}
if
(
nargs
>=
2
&&
is_callable
(
args
[
1
]))
{
ant_value_t
reviver
=
args
[
1
];
if
(
!
json_temp_pin
(
&
temp_roots
,
reviver
))
{
gc_temp_root_scope_end
(
&
temp_roots
);
return
json_parse_oom
(
js
);
}
ant_value_t
root
=
js_mkobj
(
js
);
if
(
is_err
(
root
))
{
gc_temp_root_scope_end
(
&
temp_roots
);
return
root
;
}
if
(
!
json_temp_pin
(
&
temp_roots
,
root
))
{
gc_temp_root_scope_end
(
&
temp_roots
);
return
json_parse_oom
(
js
);
}
js_set
(
js
,
root
,
""
,
result
);
result
=
apply_reviver
(
js
,
root
,
""
,
reviver
,
&
temp_roots
);
}
gc_temp_root_scope_end
(
&
temp_roots
);
return
result
;
}
ant_value_t
json_parse_value
(
ant_t
*
js
,
ant_value_t
value
)
{
ant_value_t
args
[
1
]
=
{
value
};
return
js_json_parse
(
js
,
args
,
1
);
}
static
yyjson_write_flag
get_write_flags
(
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
3
)
return
0
;
int
type
=
vtype
(
args
[
2
]);
if
(
type
==
T_UNDEF
||
type
==
T_NULL
)
return
0
;
if
(
type
!=
T_NUM
)
return
YYJSON_WRITE_PRETTY
;
int
indent
=
(
int
)
js_getnum
(
args
[
2
]);
if
(
indent
<=
0
)
return
0
;
if
(
indent
==
2
)
return
YYJSON_WRITE_PRETTY_TWO_SPACES
;
return
YYJSON_WRITE_PRETTY
;
}
ant_value_t
js_json_stringify
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
result
;
yyjson_mut_doc
*
doc
=
NULL
;
json_cycle_ctx
ctx
=
{
.
js
=
js
,
.
replacer_func
=
js_mkundef
(),
.
replacer_arr
=
js_mkundef
(),
.
error
=
js_mkundef
(),
.
holder
=
js_mkundef
(),
};
char
*
json_str
=
NULL
;
size_t
len
;
ant_value_t
root_holder
=
js_mkundef
();
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"JSON.stringify() requires at least 1 argument"
);
gc_temp_root_scope_begin
(
js
,
&
ctx
.
temp_roots
);
ctx
.
error_handle
=
gc_temp_root_add
(
&
ctx
.
temp_roots
,
ctx
.
error
);
ctx
.
holder_handle
=
gc_temp_root_add
(
&
ctx
.
temp_roots
,
ctx
.
holder
);
if
(
!
gc_temp_root_handle_valid
(
ctx
.
error_handle
)
||
!
gc_temp_root_handle_valid
(
ctx
.
holder_handle
))
{
gc_temp_root_scope_end
(
&
ctx
.
temp_roots
);
return
json_stringify_oom
(
js
);
}
if
(
!
json_ctx_pin_value
(
&
ctx
,
args
[
0
]))
{
result
=
ctx
.
error
;
goto
cleanup
;
}
int
top_type
=
vtype
(
args
[
0
]);
if
(
nargs
<
2
&&
top_type
==
T_STR
)
{
size_t
byte_len
=
0
;
size_t
raw_len
=
0
;
char
*
str
=
js_getstr
(
js
,
args
[
0
],
&
byte_len
);
char
*
raw
=
utf8_json_quote
(
str
,
byte_len
,
&
raw_len
);
if
(
!
raw
)
{
result
=
js_mkerr
(
js
,
"JSON.stringify() failed: out of memory"
);
goto
cleanup
;
}
result
=
js_mkstr
(
js
,
raw
,
raw_len
);
free
(
raw
);
goto
cleanup
;
}
if
(
nargs
>=
2
)
{
ant_value_t
replacer
=
args
[
1
];
if
(
is_callable
(
replacer
))
{
ctx
.
replacer_func
=
replacer
;
if
(
!
json_ctx_pin_value
(
&
ctx
,
replacer
))
{
result
=
ctx
.
error
;
goto
cleanup
;
}}
else
if
(
is_special_object
(
replacer
))
{
ant_value_t
len_val
=
js_get
(
js
,
replacer
,
"length"
);
if
(
vtype
(
len_val
)
==
T_NUM
)
{
ctx
.
replacer_arr
=
replacer
;
ctx
.
replacer_arr_len
=
(
int
)
js_getnum
(
len_val
);
if
(
!
json_ctx_pin_value
(
&
ctx
,
replacer
))
{
result
=
ctx
.
error
;
goto
cleanup
;
}
}}}
doc
=
yyjson_mut_doc_new
(
NULL
);
if
(
!
doc
)
{
result
=
js_mkerr
(
js
,
"JSON.stringify() failed: out of memory"
);
goto
cleanup
;
}
root_holder
=
json_create_root_holder
(
js
,
args
[
0
],
&
ctx
);
if
(
is_err
(
root_holder
))
{
result
=
root_holder
;
goto
cleanup
;
}
if
(
vtype
(
root_holder
)
==
T_UNDEF
&&
vtype
(
ctx
.
error
)
!=
T_UNDEF
)
{
result
=
ctx
.
error
;
goto
cleanup
;
}
json_set_holder
(
&
ctx
,
root_holder
);
yyjson_mut_val
*
root
=
ant_value_to_yyjson
(
js
,
doc
,
args
[
0
],
&
ctx
);
if
(
vtype
(
ctx
.
error
)
!=
T_UNDEF
)
{
ant_value_t
error
=
json_normalize_error
(
ctx
.
error
);
result
=
is_err
(
error
)
?
error
:
js_throw
(
js
,
error
);
goto
cleanup
;
}
if
(
ctx
.
has_cycle
)
{
result
=
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Converting circular structure to JSON"
);
goto
cleanup
;
}
if
(
root
==
YYJSON_SKIP_VALUE
)
{
result
=
js_mkundef
();
goto
cleanup
;
}
yyjson_mut_doc_set_root
(
doc
,
root
);
json_str
=
yyjson_mut_write
(
doc
,
get_write_flags
(
args
,
nargs
),
&
len
);
if
(
!
json_str
)
{
result
=
js_mkerr
(
js
,
"JSON.stringify() failed: write error"
);
goto
cleanup
;
}
result
=
js_mkstr
(
js
,
json_str
,
len
);
cleanup
:
free
(
json_str
);
free
(
ctx
.
stack
);
yyjson_mut_doc_free
(
doc
);
gc_temp_root_scope_end
(
&
ctx
.
temp_roots
);
return
result
;
}
ant_value_t
json_stringify_value
(
ant_t
*
js
,
ant_value_t
value
)
{
ant_value_t
args
[
1
]
=
{
value
};
return
js_json_stringify
(
js
,
args
,
1
);
}
void
init_json_module
()
{
ant_t
*
js
=
rt
->
js
;
ant_value_t
json_obj
=
js_mkobj
(
js
);
js_set
(
js
,
json_obj
,
"parse"
,
js_mkfun
(
js_json_parse
));
js_set
(
js
,
json_obj
,
"stringify"
,
js_mkfun
(
js_json_stringify
));
js_set_sym
(
js
,
json_obj
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"JSON"
,
4
));
js_set
(
js
,
js_glob
(
js
),
"JSON"
,
json_obj
);
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, May 2, 3:38 AM (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
541778
Default Alt Text
json.c (21 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment