mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Reduce build latency and fix old cpu bugs
This commit is contained in:
parent
df8ab0aa0c
commit
533f3d1ef1
69 changed files with 43069 additions and 43683 deletions
|
@ -1,21 +1,3 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
|
|
|
@ -172,13 +172,12 @@ void *MemCpy(void *, const void *, size_t);
|
|||
free(s); \
|
||||
} while (0)
|
||||
|
||||
#define BB(N) \
|
||||
do { \
|
||||
B(memmove_pure, N); \
|
||||
B(memcpy, N); \
|
||||
B(MemCpy, N); \
|
||||
(fprintf)(stderr, "\n"); \
|
||||
} while (0)
|
||||
void BB(size_t N) {
|
||||
B(memmove_pure, N);
|
||||
B(memcpy, N);
|
||||
B(MemCpy, N);
|
||||
(fprintf)(stderr, "\n");
|
||||
}
|
||||
|
||||
BENCH(memcpy, bench) {
|
||||
BB(0);
|
||||
|
|
|
@ -20,10 +20,6 @@
|
|||
#include "test/libc/xed/lib.h"
|
||||
#include "third_party/xed/x86.h"
|
||||
|
||||
/**
|
||||
* @fileoverview GCC's popular i186+ instruction w/ NexGen32e encoding.
|
||||
*/
|
||||
|
||||
TEST(x86ild, test_C0E800) {
|
||||
/*
|
||||
ICLASS: SHR
|
||||
|
|
3741
test/libc/xed/x86ild_popular_i86_2_test.c
Normal file
3741
test/libc/xed/x86ild_popular_i86_2_test.c
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -678,6 +678,7 @@ BENCH(inv_mod, bench3) {
|
|||
}
|
||||
|
||||
TEST(ShiftRightAvx, test1) {
|
||||
if (!X86_HAVE(AVX)) return;
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
uint64_t mem[1] = {rand64()};
|
||||
|
@ -692,6 +693,7 @@ TEST(ShiftRightAvx, test1) {
|
|||
}
|
||||
|
||||
TEST(ShiftRightAvx, test2) {
|
||||
if (!X86_HAVE(AVX)) return;
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
uint64_t mem[2] = {rand64(), rand64()};
|
||||
|
@ -707,6 +709,7 @@ TEST(ShiftRightAvx, test2) {
|
|||
}
|
||||
|
||||
TEST(ShiftRightAvx, test3) {
|
||||
if (!X86_HAVE(AVX)) return;
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
uint64_t mem[3] = {rand64(), rand64(), rand64()};
|
||||
|
@ -723,6 +726,7 @@ TEST(ShiftRightAvx, test3) {
|
|||
}
|
||||
|
||||
TEST(ShiftRightAvx, test4) {
|
||||
if (!X86_HAVE(AVX)) return;
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
uint64_t mem[4] = {rand64(), rand64(), rand64(), rand64()};
|
||||
|
@ -740,6 +744,7 @@ TEST(ShiftRightAvx, test4) {
|
|||
}
|
||||
|
||||
TEST(ShiftRightAvx, test8) {
|
||||
if (!X86_HAVE(AVX)) return;
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
uint64_t mem[8] = {rand64(), rand64(), rand64(), rand64(),
|
||||
|
@ -762,6 +767,7 @@ TEST(ShiftRightAvx, test8) {
|
|||
}
|
||||
|
||||
TEST(ShiftRightAvx, test9) {
|
||||
if (!X86_HAVE(AVX)) return;
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
uint64_t mem[9] = {rand64(), rand64(), rand64(), rand64(), rand64(),
|
||||
|
@ -780,6 +786,7 @@ TEST(ShiftRightAvx, test9) {
|
|||
}
|
||||
|
||||
BENCH(ShiftRight, bench) {
|
||||
if (!X86_HAVE(AVX)) return;
|
||||
uint64_t x[64];
|
||||
rngset(x, sizeof(x), rand64, -1);
|
||||
EZBENCH2("ShiftRight", donothing, ShiftRight(x, 64, 1));
|
||||
|
|
16
third_party/lz4cli/lz4.c
vendored
16
third_party/lz4cli/lz4.c
vendored
|
@ -107,21 +107,7 @@
|
|||
# pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#ifndef LZ4_FORCE_INLINE
|
||||
# ifdef _MSC_VER /* Visual Studio */
|
||||
# define LZ4_FORCE_INLINE static __forceinline
|
||||
# else
|
||||
# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
|
||||
# ifdef __GNUC__
|
||||
# define LZ4_FORCE_INLINE static inline __attribute__((always_inline))
|
||||
# else
|
||||
# define LZ4_FORCE_INLINE static inline
|
||||
# endif
|
||||
# else
|
||||
# define LZ4_FORCE_INLINE static
|
||||
# endif /* __STDC_VERSION__ */
|
||||
# endif /* _MSC_VER */
|
||||
#endif /* LZ4_FORCE_INLINE */
|
||||
#define LZ4_FORCE_INLINE static inline
|
||||
|
||||
/* LZ4_FORCE_O2_GCC_PPC64LE and LZ4_FORCE_O2_INLINE_GCC_PPC64LE
|
||||
* Gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy,
|
||||
|
|
4
third_party/mbedtls/bigmul.c
vendored
4
third_party/mbedtls/bigmul.c
vendored
|
@ -45,7 +45,7 @@ void mbedtls_mpi_mul_hlp1(size_t n, const uint64_t *s, uint64_t *d, uint64_t b)
|
|||
uint128_t x;
|
||||
uint64_t c, t, t1, t2;
|
||||
i = c = 0;
|
||||
#ifdef __x86_64__
|
||||
#if defined(__x86_64__) && !defined(__STRICT_ANSI__)
|
||||
if( X86_HAVE(BMI2) )
|
||||
{
|
||||
for( ; i + 8 <= n; i += 8 )
|
||||
|
@ -120,7 +120,7 @@ void mbedtls_mpi_mul_hlp(size_t n, uint64_t *s, uint64_t *d, uint64_t b)
|
|||
uint128_t x;
|
||||
uint64_t c, l, h, t;
|
||||
i = c = 0;
|
||||
#ifdef __x86_64__
|
||||
#if defined(__x86_64__) && !defined(__STRICT_ANSI__)
|
||||
if (X86_HAVE(BMI2) && X86_HAVE(ADX))
|
||||
{
|
||||
for( ; i + 8 <= n; i += 8 )
|
||||
|
|
4
third_party/mbedtls/bignum.c
vendored
4
third_party/mbedtls/bignum.c
vendored
|
@ -1284,7 +1284,7 @@ forceinline mbedtls_mpi_uint mpi_sub_hlp(mbedtls_mpi_uint *d,
|
|||
unsigned char cf;
|
||||
mbedtls_mpi_uint c, x;
|
||||
cf = c = i = 0;
|
||||
#ifdef __x86_64__
|
||||
#if defined(__x86_64__) && !defined(__STRICT_ANSI__)
|
||||
if (!n) return 0;
|
||||
asm volatile("xor\t%1,%1\n\t"
|
||||
".align\t16\n1:\t"
|
||||
|
@ -1553,7 +1553,7 @@ static mbedtls_mpi_uint mbedtls_int_div_int( mbedtls_mpi_uint u1,
|
|||
mbedtls_mpi_uint d,
|
||||
mbedtls_mpi_uint *r )
|
||||
{
|
||||
#ifdef __x86_64__
|
||||
#if defined(__x86_64__) && !defined(__STRICT_ANSI__)
|
||||
if (d && u1 < d)
|
||||
{
|
||||
mbedtls_mpi_uint quo, rem;
|
||||
|
|
4
third_party/mbedtls/bigshift.c
vendored
4
third_party/mbedtls/bigshift.c
vendored
|
@ -94,6 +94,10 @@ int mbedtls_mpi_shift_l(mbedtls_mpi *X, size_t k)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void ShiftRightPure(mbedtls_mpi_uint *p, size_t n, unsigned char k) {
|
||||
shrd(p, n, 0, n, k);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs right arithmetic shift on big number: X >>= k
|
||||
*/
|
||||
|
|
5
third_party/mbedtls/config.h
vendored
5
third_party/mbedtls/config.h
vendored
|
@ -2,6 +2,11 @@
|
|||
#define MBEDTLS_CONFIG_H_
|
||||
#include "libc/dce.h"
|
||||
|
||||
/* #include "libc/nexgen32e/x86feature.h" */
|
||||
/* #undef X86_HAVE */
|
||||
/* #define X86_HAVE(x) 0 */
|
||||
/* #undef __x86_64__ */
|
||||
|
||||
/* protocols */
|
||||
#define MBEDTLS_SSL_PROTO_TLS1_2
|
||||
#ifndef TINY
|
||||
|
|
20
third_party/mbedtls/ecp256.c
vendored
20
third_party/mbedtls/ecp256.c
vendored
|
@ -185,11 +185,11 @@ mbedtls_p256_plu( uint64_t A[5],
|
|||
: "rax", "memory", "cc");
|
||||
#else
|
||||
uint64_t c;
|
||||
ADC( X[0], A[0], B[0], 0, c );
|
||||
ADC( X[1], A[1], B[1], c, c );
|
||||
ADC( X[2], A[2], B[2], c, c );
|
||||
ADC( X[3], A[3], B[3], c, c );
|
||||
ADC( X[4], A[4], B[4], c, c );
|
||||
ADC( A[0], A[0], B[0], 0, c );
|
||||
ADC( A[1], A[1], B[1], c, c );
|
||||
ADC( A[2], A[2], B[2], c, c );
|
||||
ADC( A[3], A[3], B[3], c, c );
|
||||
ADC( A[4], A[4], B[4], c, c );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -213,11 +213,11 @@ mbedtls_p256_slu( uint64_t A[5],
|
|||
: "rax", "memory", "cc");
|
||||
#else
|
||||
uint64_t c;
|
||||
SBB( X[0], A[0], B[0], 0, c );
|
||||
SBB( X[1], A[1], B[1], c, c );
|
||||
SBB( X[2], A[2], B[2], c, c );
|
||||
SBB( X[3], A[3], B[3], c, c );
|
||||
SBB( X[4], A[4], B[4], c, c );
|
||||
SBB( A[0], A[0], B[0], 0, c );
|
||||
SBB( A[1], A[1], B[1], c, c );
|
||||
SBB( A[2], A[2], B[2], c, c );
|
||||
SBB( A[3], A[3], B[3], c, c );
|
||||
SBB( A[4], A[4], B[4], c, c );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
37
third_party/mbedtls/ecp384.c
vendored
37
third_party/mbedtls/ecp384.c
vendored
|
@ -35,8 +35,7 @@ mbedtls_p384_isz( uint64_t p[6] )
|
|||
return( !p[0] & !p[1] & !p[2] & !p[3] & !p[4] & !p[5] );
|
||||
}
|
||||
|
||||
static bool
|
||||
mbedtls_p384_gte( uint64_t p[7] )
|
||||
bool mbedtls_p384_gte( uint64_t p[7] )
|
||||
{
|
||||
return( (((int64_t)p[6] > 0) |
|
||||
(!p[6] &
|
||||
|
@ -129,15 +128,13 @@ mbedtls_p384_gro( uint64_t p[7] )
|
|||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
mbedtls_p384_rum( uint64_t p[7] )
|
||||
void mbedtls_p384_rum( uint64_t p[7] )
|
||||
{
|
||||
while( mbedtls_p384_gte( p ) )
|
||||
mbedtls_p384_red( p );
|
||||
}
|
||||
|
||||
static inline void
|
||||
mbedtls_p384_mod( uint64_t X[12] )
|
||||
void mbedtls_p384_mod( uint64_t X[12] )
|
||||
{
|
||||
secp384r1(X);
|
||||
if( (int64_t)X[6] < 0 ){
|
||||
|
@ -217,13 +214,13 @@ mbedtls_p384_plu( uint64_t A[7],
|
|||
: "rax", "memory", "cc");
|
||||
#else
|
||||
uint64_t c;
|
||||
ADC( X[0], A[0], B[0], 0, c );
|
||||
ADC( X[1], A[1], B[1], c, c );
|
||||
ADC( X[2], A[2], B[2], c, c );
|
||||
ADC( X[3], A[3], B[3], c, c );
|
||||
ADC( X[4], A[4], B[4], c, c );
|
||||
ADC( X[5], A[5], B[5], c, c );
|
||||
ADC( X[6], A[6], B[6], c, c );
|
||||
ADC( A[0], A[0], B[0], 0, c );
|
||||
ADC( A[1], A[1], B[1], c, c );
|
||||
ADC( A[2], A[2], B[2], c, c );
|
||||
ADC( A[3], A[3], B[3], c, c );
|
||||
ADC( A[4], A[4], B[4], c, c );
|
||||
ADC( A[5], A[5], B[5], c, c );
|
||||
ADC( A[6], A[6], B[6], c, c );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -251,13 +248,13 @@ mbedtls_p384_slu( uint64_t A[7],
|
|||
: "rax", "memory", "cc");
|
||||
#else
|
||||
uint64_t c;
|
||||
SBB( X[0], A[0], B[0], 0, c );
|
||||
SBB( X[1], A[1], B[1], c, c );
|
||||
SBB( X[2], A[2], B[2], c, c );
|
||||
SBB( X[3], A[3], B[3], c, c );
|
||||
SBB( X[4], A[4], B[4], c, c );
|
||||
SBB( X[5], A[5], B[5], c, c );
|
||||
SBB( X[6], A[6], B[6], c, c );
|
||||
SBB( A[0], A[0], B[0], 0, c );
|
||||
SBB( A[1], A[1], B[1], c, c );
|
||||
SBB( A[2], A[2], B[2], c, c );
|
||||
SBB( A[3], A[3], B[3], c, c );
|
||||
SBB( A[4], A[4], B[4], c, c );
|
||||
SBB( A[5], A[5], B[5], c, c );
|
||||
SBB( A[6], A[6], B[6], c, c );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
3
third_party/mbedtls/ecp_internal.h
vendored
3
third_party/mbedtls/ecp_internal.h
vendored
|
@ -261,4 +261,7 @@ int mbedtls_p384_normalize_jac( const mbedtls_ecp_group *,
|
|||
int mbedtls_p384_normalize_jac_many( const mbedtls_ecp_group *,
|
||||
mbedtls_ecp_point *[], size_t );
|
||||
|
||||
void mbedtls_p384_rum( uint64_t p[7] );
|
||||
void mbedtls_p384_mod( uint64_t X[12] );
|
||||
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_MBEDTLS_ECP_INTERNAL_H_ */
|
||||
|
|
4
third_party/mbedtls/secp384r1.c
vendored
4
third_party/mbedtls/secp384r1.c
vendored
|
@ -42,7 +42,7 @@
|
|||
* @see FIPS 186-3 §D.2.4
|
||||
*/
|
||||
void secp384r1(uint64_t p[12]) {
|
||||
uint64_t A, B, C, D, E, F, G, a, b;
|
||||
uint64_t A, B, C, D, E, F, G, a, b, o;
|
||||
A = Q(0);
|
||||
B = Q(2);
|
||||
C = Q(4);
|
||||
|
@ -56,7 +56,7 @@ void secp384r1(uint64_t p[12]) {
|
|||
ADC(C, C, a << 1, 0, o);
|
||||
ADC(D, D, b << 1 | a >> 63, o, o);
|
||||
ADC(E, E, b >> 63, o, o);
|
||||
ADC(F, F, o, o, o);
|
||||
ADC(F, F, 0, o, o);
|
||||
G += o;
|
||||
ADC(A, A, Q(12), 0, o);
|
||||
ADC(B, B, Q(14), o, o);
|
||||
|
|
2
third_party/mbedtls/shiftright.c
vendored
2
third_party/mbedtls/shiftright.c
vendored
|
@ -22,7 +22,7 @@
|
|||
void (*ShiftRight)(uint64_t *, size_t, unsigned char);
|
||||
|
||||
static textstartup void ShiftRightInit(void) {
|
||||
ShiftRight = X86_HAVE(AVX) ? ShiftRightAvx : ShiftRightPure;
|
||||
ShiftRight = 0 && X86_HAVE(AVX) ? ShiftRightAvx : ShiftRightPure;
|
||||
}
|
||||
|
||||
const void *const ShiftRightCtor[] initarray = {ShiftRightInit};
|
||||
|
|
151
third_party/mbedtls/test/secp384r1_test.c
vendored
151
third_party/mbedtls/test/secp384r1_test.c
vendored
|
@ -48,96 +48,13 @@ TEST(secp384r1, testIsTheSame) {
|
|||
for (i = 0; i < 12; ++i) {
|
||||
printf("0x%016lx vs. 0x%016lx %d\n", A.p[i], B.p[i], A.p[i] == B.p[i]);
|
||||
}
|
||||
exit(1);
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
}
|
||||
mbedtls_mpi_free(&B);
|
||||
mbedtls_mpi_free(&A);
|
||||
}
|
||||
|
||||
static inline bool mbedtls_p384_gte(uint64_t p[7]) {
|
||||
return (((int64_t)p[6] > 0 ||
|
||||
(p[5] > 0xffffffffffffffff ||
|
||||
(p[5] == 0xffffffffffffffff &&
|
||||
(p[4] > 0xffffffffffffffff ||
|
||||
(p[4] == 0xffffffffffffffff &&
|
||||
(p[3] > 0xffffffffffffffff ||
|
||||
(p[3] == 0xffffffffffffffff &&
|
||||
(p[2] > 0xfffffffffffffffe ||
|
||||
(p[2] == 0xfffffffffffffffe &&
|
||||
(p[1] > 0xffffffff00000000 ||
|
||||
(p[1] == 0xffffffff00000000 &&
|
||||
(p[0] > 0x00000000ffffffff ||
|
||||
(p[0] == 0x00000000ffffffff))))))))))))));
|
||||
}
|
||||
|
||||
static inline void mbedtls_p384_gro(uint64_t p[7]) {
|
||||
#if defined(__x86_64__) && !defined(__STRICT_ANSI__)
|
||||
asm("addq\t%1,%0\n\t"
|
||||
"adcq\t%2,8+%0\n\t"
|
||||
"adcq\t%3,16+%0\n\t"
|
||||
"adcq\t%4,24+%0\n\t"
|
||||
"adcq\t%4,32+%0\n\t"
|
||||
"adcq\t%4,40+%0\n\t"
|
||||
"adcq\t$0,48+%0"
|
||||
: "+o"(*p)
|
||||
: "r"(0x00000000ffffffffl), "r"(0xffffffff00000000),
|
||||
"i"(0xfffffffffffffffel), "i"(0xffffffffffffffff)
|
||||
: "memory", "cc");
|
||||
#else
|
||||
uint64_t c;
|
||||
ADC(p[0], p[0], 0x00000000ffffffff, 0, c);
|
||||
ADC(p[1], p[1], 0xffffffff00000000, c, c);
|
||||
ADC(p[2], p[2], 0xfffffffffffffffe, c, c);
|
||||
ADC(p[3], p[3], 0xffffffffffffffff, c, c);
|
||||
ADC(p[4], p[4], 0xffffffffffffffff, c, c);
|
||||
ADC(p[5], p[5], 0xffffffffffffffff, c, c);
|
||||
ADC(p[6], p[6], 0, c, c);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void mbedtls_p384_red(uint64_t p[7]) {
|
||||
#if defined(__x86_64__) && !defined(__STRICT_ANSI__)
|
||||
asm("subq\t%1,%0\n\t"
|
||||
"sbbq\t%2,8+%0\n\t"
|
||||
"sbbq\t%3,16+%0\n\t"
|
||||
"sbbq\t%4,24+%0\n\t"
|
||||
"sbbq\t%4,32+%0\n\t"
|
||||
"sbbq\t%4,40+%0\n\t"
|
||||
"sbbq\t$0,48+%0"
|
||||
: "+o"(*p)
|
||||
: "r"(0x00000000ffffffffl), "r"(0xffffffff00000000),
|
||||
"i"(0xfffffffffffffffel), "i"(0xffffffffffffffff)
|
||||
: "memory", "cc");
|
||||
#else
|
||||
uint64_t c;
|
||||
SBB(p[0], p[0], 0x00000000ffffffff, 0, c);
|
||||
SBB(p[1], p[1], 0xffffffff00000000, c, c);
|
||||
SBB(p[2], p[2], 0xfffffffffffffffe, c, c);
|
||||
SBB(p[3], p[3], 0xffffffffffffffff, c, c);
|
||||
SBB(p[4], p[4], 0xffffffffffffffff, c, c);
|
||||
SBB(p[5], p[5], 0xffffffffffffffff, c, c);
|
||||
SBB(p[6], p[6], 0, c, c);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void mbedtls_p384_rum(uint64_t p[7]) {
|
||||
while (mbedtls_p384_gte(p)) mbedtls_p384_red(p);
|
||||
}
|
||||
|
||||
static inline void mbedtls_p384_mod(uint64_t X[12]) {
|
||||
secp384r1(X);
|
||||
if ((int64_t)X[6] < 0) {
|
||||
do {
|
||||
mbedtls_p384_gro(X);
|
||||
} while ((int64_t)X[6] < 0);
|
||||
} else {
|
||||
while (mbedtls_p384_gte(X)) {
|
||||
mbedtls_p384_red(X);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(secp384r1, needsDownwardCorrection) {
|
||||
int i;
|
||||
uint64_t P[6] = {
|
||||
|
@ -174,7 +91,7 @@ TEST(secp384r1, needsDownwardCorrection) {
|
|||
for (i = 0; i < 12; ++i) {
|
||||
printf("0x%016lx vs. 0x%016lx %d\n", W[i], X[i], W[i] == X[i]);
|
||||
}
|
||||
exit(1);
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +132,7 @@ TEST(secp384r1, needsUpwardCorrection) {
|
|||
for (i = 0; i < 12; ++i) {
|
||||
printf("0x%016lx vs. 0x%016lx %d\n", W[i], X[i], W[i] == X[i]);
|
||||
}
|
||||
exit(1);
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,66 +146,4 @@ BENCH(secp384r1, bench) {
|
|||
mbedtls_mpi_free(&A);
|
||||
}
|
||||
|
||||
void mbedtls_p384_shl_a(uint64_t p[7]) {
|
||||
asm("shlq\t%0\n\t"
|
||||
"rclq\t8+%0\n\t"
|
||||
"rclq\t16+%0\n\t"
|
||||
"rclq\t24+%0\n\t"
|
||||
"rclq\t32+%0\n\t"
|
||||
"rclq\t40+%0\n\t"
|
||||
"rclq\t48+%0\n\t"
|
||||
: "+o"(*p)
|
||||
: /* no inputs */
|
||||
: "memory", "cc");
|
||||
mbedtls_p384_rum(p);
|
||||
}
|
||||
|
||||
void mbedtls_p384_shl_b(uint64_t p[7]) {
|
||||
p[6] = p[5] >> 63;
|
||||
p[5] = p[5] << 1 | p[4] >> 63;
|
||||
p[4] = p[4] << 1 | p[3] >> 63;
|
||||
p[3] = p[3] << 1 | p[2] >> 63;
|
||||
p[2] = p[2] << 1 | p[1] >> 63;
|
||||
p[1] = p[1] << 1 | p[0] >> 63;
|
||||
p[0] = p[0] << 1;
|
||||
mbedtls_p384_rum(p);
|
||||
}
|
||||
|
||||
BENCH(shl, bench) {
|
||||
uint64_t A[7] = {0};
|
||||
EZBENCH2("mbedtls_p384_shl_a", donothing, mbedtls_p384_shl_a(A));
|
||||
EZBENCH2("mbedtls_p384_shl_b", donothing, mbedtls_p384_shl_b(A));
|
||||
}
|
||||
|
||||
void mbedtls_p384_red_a(uint64_t p[7]) {
|
||||
asm("subq\t%1,%0\n\t"
|
||||
"sbbq\t%2,8+%0\n\t"
|
||||
"sbbq\t%3,16+%0\n\t"
|
||||
"sbbq\t%4,24+%0\n\t"
|
||||
"sbbq\t%4,32+%0\n\t"
|
||||
"sbbq\t%4,40+%0\n\t"
|
||||
"sbbq\t$0,48+%0"
|
||||
: "+o"(*p)
|
||||
: "r"(0x00000000ffffffffl), "r"(0xffffffff00000000),
|
||||
"i"(0xfffffffffffffffel), "i"(0xffffffffffffffff)
|
||||
: "memory", "cc");
|
||||
}
|
||||
|
||||
void mbedtls_p384_red_b(uint64_t p[7]) {
|
||||
uint64_t c;
|
||||
SBB(p[0], p[0], 0x00000000ffffffff, 0, c);
|
||||
SBB(p[1], p[1], 0xffffffff00000000, c, c);
|
||||
SBB(p[2], p[2], 0xfffffffffffffffe, c, c);
|
||||
SBB(p[3], p[3], 0xffffffffffffffff, c, c);
|
||||
SBB(p[4], p[4], 0xffffffffffffffff, c, c);
|
||||
SBB(p[5], p[5], 0xffffffffffffffff, c, c);
|
||||
SBB(p[6], p[6], 0, c, c);
|
||||
}
|
||||
|
||||
BENCH(red, bench) {
|
||||
uint64_t A[7] = {0};
|
||||
EZBENCH2("mbedtls_p384_red_a", donothing, mbedtls_p384_red_a(A));
|
||||
EZBENCH2("mbedtls_p384_red_b", donothing, mbedtls_p384_red_b(A));
|
||||
}
|
||||
|
||||
#endif /* MBEDTLS_ECP_C */
|
||||
|
|
1952
third_party/quickjs/array.c
vendored
Normal file
1952
third_party/quickjs/array.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
321
third_party/quickjs/atof.c
vendored
Normal file
321
third_party/quickjs/atof.c
vendored
Normal file
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "third_party/gdtoa/gdtoa.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
/* XXX: remove */
|
||||
static double js_strtod(const char *p, int radix, BOOL is_float)
|
||||
{
|
||||
double d;
|
||||
int c;
|
||||
if (!is_float || radix != 10) {
|
||||
uint64_t n_max, n;
|
||||
int int_exp, is_neg;
|
||||
is_neg = 0;
|
||||
if (*p == '-') {
|
||||
is_neg = 1;
|
||||
p++;
|
||||
}
|
||||
/* skip leading zeros */
|
||||
while (*p == '0')
|
||||
p++;
|
||||
n = 0;
|
||||
if (radix == 10)
|
||||
n_max = ((uint64_t)-1 - 9) / 10; /* most common case */
|
||||
else
|
||||
n_max = ((uint64_t)-1 - (radix - 1)) / radix;
|
||||
/* XXX: could be more precise */
|
||||
int_exp = 0;
|
||||
while (*p != '\0') {
|
||||
c = to_digit((uint8_t)*p);
|
||||
if (c >= radix)
|
||||
break;
|
||||
if (n <= n_max) {
|
||||
n = n * radix + c;
|
||||
} else {
|
||||
int_exp++;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
d = n;
|
||||
if (int_exp != 0) {
|
||||
d *= pow(radix, int_exp);
|
||||
}
|
||||
if (is_neg)
|
||||
d = -d;
|
||||
} else {
|
||||
d = strtod(p, NULL);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/* return an exception in case of memory error. Return JS_NAN if
|
||||
invalid syntax */
|
||||
#ifdef CONFIG_BIGNUM
|
||||
JSValue js_atof2(JSContext *ctx, const char *str, const char **pp, int radix, int flags, slimb_t *pexponent)
|
||||
#else
|
||||
JSValue js_atof(JSContext *ctx, const char *str, const char **pp, int radix, int flags)
|
||||
#endif
|
||||
{
|
||||
const char *p, *p_start;
|
||||
int sep, is_neg;
|
||||
BOOL is_float, has_legacy_octal;
|
||||
int atod_type = flags & ATOD_TYPE_MASK;
|
||||
char buf1[64], *buf;
|
||||
int i, j, len;
|
||||
BOOL buf_allocated = FALSE;
|
||||
JSValue val;
|
||||
/* optional separator between digits */
|
||||
sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
|
||||
has_legacy_octal = FALSE;
|
||||
p = str;
|
||||
p_start = p;
|
||||
is_neg = 0;
|
||||
if (p[0] == '+') {
|
||||
p++;
|
||||
p_start++;
|
||||
if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
|
||||
goto no_radix_prefix;
|
||||
} else if (p[0] == '-') {
|
||||
p++;
|
||||
p_start++;
|
||||
is_neg = 1;
|
||||
if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
|
||||
goto no_radix_prefix;
|
||||
}
|
||||
if (p[0] == '0') {
|
||||
if ((p[1] == 'x' || p[1] == 'X') &&
|
||||
(radix == 0 || radix == 16)) {
|
||||
p += 2;
|
||||
radix = 16;
|
||||
} else if ((p[1] == 'o' || p[1] == 'O') &&
|
||||
radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
|
||||
p += 2;
|
||||
radix = 8;
|
||||
} else if ((p[1] == 'b' || p[1] == 'B') &&
|
||||
radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
|
||||
p += 2;
|
||||
radix = 2;
|
||||
} else if ((p[1] >= '0' && p[1] <= '9') &&
|
||||
radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {
|
||||
int i;
|
||||
has_legacy_octal = TRUE;
|
||||
sep = 256;
|
||||
for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
|
||||
continue;
|
||||
if (p[i] == '8' || p[i] == '9')
|
||||
goto no_prefix;
|
||||
p += 1;
|
||||
radix = 8;
|
||||
} else {
|
||||
goto no_prefix;
|
||||
}
|
||||
/* there must be a digit after the prefix */
|
||||
if (to_digit((uint8_t)*p) >= radix)
|
||||
goto fail;
|
||||
no_prefix: ;
|
||||
} else {
|
||||
no_radix_prefix:
|
||||
if (!(flags & ATOD_INT_ONLY) &&
|
||||
(atod_type == ATOD_TYPE_FLOAT64 ||
|
||||
atod_type == ATOD_TYPE_BIG_FLOAT) &&
|
||||
strstart(p, "Infinity", &p)) {
|
||||
#ifdef CONFIG_BIGNUM
|
||||
if (atod_type == ATOD_TYPE_BIG_FLOAT) {
|
||||
bf_t *a;
|
||||
val = JS_NewBigFloat(ctx);
|
||||
if (JS_IsException(val))
|
||||
goto done;
|
||||
a = JS_GetBigFloat(val);
|
||||
bf_set_inf(a, is_neg);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
double d = 1.0 / 0.0;
|
||||
if (is_neg)
|
||||
d = -d;
|
||||
val = JS_NewFloat64(ctx, d);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (radix == 0)
|
||||
radix = 10;
|
||||
is_float = FALSE;
|
||||
p_start = p;
|
||||
while (to_digit((uint8_t)*p) < radix
|
||||
|| (*p == sep && (radix != 10 ||
|
||||
p != p_start + 1 || p[-1] != '0') &&
|
||||
to_digit((uint8_t)p[1]) < radix)) {
|
||||
p++;
|
||||
}
|
||||
if (!(flags & ATOD_INT_ONLY)) {
|
||||
if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {
|
||||
is_float = TRUE;
|
||||
p++;
|
||||
if (*p == sep)
|
||||
goto fail;
|
||||
while (to_digit((uint8_t)*p) < radix ||
|
||||
(*p == sep && to_digit((uint8_t)p[1]) < radix))
|
||||
p++;
|
||||
}
|
||||
if (p > p_start &&
|
||||
(((*p == 'e' || *p == 'E') && radix == 10) ||
|
||||
((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
|
||||
const char *p1 = p + 1;
|
||||
is_float = TRUE;
|
||||
if (*p1 == '+') {
|
||||
p1++;
|
||||
} else if (*p1 == '-') {
|
||||
p1++;
|
||||
}
|
||||
if (isdigit((uint8_t)*p1)) {
|
||||
p = p1 + 1;
|
||||
while (isdigit((uint8_t)*p) || (*p == sep && isdigit((uint8_t)p[1])))
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p == p_start)
|
||||
goto fail;
|
||||
buf = buf1;
|
||||
buf_allocated = FALSE;
|
||||
len = p - p_start;
|
||||
if (UNLIKELY((len + 2) > sizeof(buf1))) {
|
||||
buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */
|
||||
if (!buf)
|
||||
goto mem_error;
|
||||
buf_allocated = TRUE;
|
||||
}
|
||||
/* remove the separators and the radix prefixes */
|
||||
j = 0;
|
||||
if (is_neg)
|
||||
buf[j++] = '-';
|
||||
for (i = 0; i < len; i++) {
|
||||
if (p_start[i] != '_')
|
||||
buf[j++] = p_start[i];
|
||||
}
|
||||
buf[j] = '\0';
|
||||
#ifdef CONFIG_BIGNUM
|
||||
if (flags & ATOD_ACCEPT_SUFFIX) {
|
||||
if (*p == 'n') {
|
||||
p++;
|
||||
atod_type = ATOD_TYPE_BIG_INT;
|
||||
} else if (*p == 'l') {
|
||||
p++;
|
||||
atod_type = ATOD_TYPE_BIG_FLOAT;
|
||||
} else if (*p == 'm') {
|
||||
p++;
|
||||
atod_type = ATOD_TYPE_BIG_DECIMAL;
|
||||
} else {
|
||||
if (flags & ATOD_MODE_BIGINT) {
|
||||
if (!is_float)
|
||||
atod_type = ATOD_TYPE_BIG_INT;
|
||||
if (has_legacy_octal)
|
||||
goto fail;
|
||||
} else {
|
||||
if (is_float && radix != 10)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (atod_type == ATOD_TYPE_FLOAT64) {
|
||||
if (flags & ATOD_MODE_BIGINT) {
|
||||
if (!is_float)
|
||||
atod_type = ATOD_TYPE_BIG_INT;
|
||||
if (has_legacy_octal)
|
||||
goto fail;
|
||||
} else {
|
||||
if (is_float && radix != 10)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch(atod_type) {
|
||||
case ATOD_TYPE_FLOAT64:
|
||||
{
|
||||
double d;
|
||||
d = js_strtod(buf, radix, is_float);
|
||||
/* return int or float64 */
|
||||
val = JS_NewFloat64(ctx, d);
|
||||
}
|
||||
break;
|
||||
case ATOD_TYPE_BIG_INT:
|
||||
if (has_legacy_octal || is_float)
|
||||
goto fail;
|
||||
val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL);
|
||||
break;
|
||||
case ATOD_TYPE_BIG_FLOAT:
|
||||
if (has_legacy_octal)
|
||||
goto fail;
|
||||
val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags,
|
||||
pexponent);
|
||||
break;
|
||||
case ATOD_TYPE_BIG_DECIMAL:
|
||||
if (radix != 10)
|
||||
goto fail;
|
||||
val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
#else
|
||||
{
|
||||
double d;
|
||||
(void)has_legacy_octal;
|
||||
if (is_float && radix != 10)
|
||||
goto fail;
|
||||
d = js_strtod(buf, radix, is_float);
|
||||
val = JS_NewFloat64(ctx, d);
|
||||
}
|
||||
#endif
|
||||
done:
|
||||
if (buf_allocated)
|
||||
js_free_rt(ctx->rt, buf);
|
||||
if (pp)
|
||||
*pp = p;
|
||||
return val;
|
||||
fail:
|
||||
val = JS_NAN;
|
||||
goto done;
|
||||
mem_error:
|
||||
val = JS_ThrowOutOfMemory(ctx);
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BIGNUM
|
||||
JSValue js_atof(JSContext *ctx, const char *str, const char **pp, int radix, int flags)
|
||||
{
|
||||
return js_atof2(ctx, str, pp, radix, flags, NULL);
|
||||
}
|
||||
#endif
|
244
third_party/quickjs/atom.c
vendored
Normal file
244
third_party/quickjs/atom.c
vendored
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
#include "third_party/quickjs/libregexp.h"
|
||||
#include "third_party/quickjs/quickjs.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
/* Should only be used for debug. */
|
||||
const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, JSAtom atom)
|
||||
{
|
||||
if (__JS_AtomIsTaggedInt(atom)) {
|
||||
snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom));
|
||||
} else {
|
||||
JSAtomStruct *p;
|
||||
assert(atom < rt->atom_size);
|
||||
if (atom == JS_ATOM_NULL) {
|
||||
snprintf(buf, buf_size, "<null>");
|
||||
} else {
|
||||
int i, c;
|
||||
char *q;
|
||||
JSString *str;
|
||||
q = buf;
|
||||
p = rt->atom_array[atom];
|
||||
assert(!atom_is_free(p));
|
||||
str = p;
|
||||
if (str) {
|
||||
if (!str->is_wide_char) {
|
||||
/* special case ASCII strings */
|
||||
c = 0;
|
||||
for(i = 0; i < str->len; i++) {
|
||||
c |= str->u.str8[i];
|
||||
}
|
||||
if (c < 0x80)
|
||||
return (const char *)str->u.str8;
|
||||
}
|
||||
for(i = 0; i < str->len; i++) {
|
||||
if (str->is_wide_char)
|
||||
c = str->u.str16[i];
|
||||
else
|
||||
c = str->u.str8[i];
|
||||
if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX)
|
||||
break;
|
||||
if (c < 128) {
|
||||
*q++ = c;
|
||||
} else {
|
||||
q += unicode_to_utf8((uint8_t *)q, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
*q = '\0';
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom)
|
||||
{
|
||||
return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);
|
||||
}
|
||||
|
||||
static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)
|
||||
{
|
||||
char buf[ATOM_GET_STR_BUF_SIZE];
|
||||
if (__JS_AtomIsTaggedInt(atom)) {
|
||||
snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom));
|
||||
return JS_NewString(ctx, buf);
|
||||
} else {
|
||||
JSRuntime *rt = ctx->rt;
|
||||
JSAtomStruct *p;
|
||||
assert(atom < rt->atom_size);
|
||||
p = rt->atom_array[atom];
|
||||
if (p->atom_type == JS_ATOM_TYPE_STRING) {
|
||||
goto ret_string;
|
||||
} else if (force_string) {
|
||||
if (p->len == 0 && p->is_wide_char != 0) {
|
||||
/* no description string */
|
||||
p = rt->atom_array[JS_ATOM_empty_string];
|
||||
}
|
||||
ret_string:
|
||||
return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
|
||||
} else {
|
||||
return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)
|
||||
{
|
||||
return __JS_AtomToValue(ctx, atom, FALSE);
|
||||
}
|
||||
|
||||
JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)
|
||||
{
|
||||
return __JS_AtomToValue(ctx, atom, TRUE);
|
||||
}
|
||||
|
||||
/* return TRUE if the atom is an array index (i.e. 0 <= index <=
|
||||
2^32-2 and return its value */
|
||||
BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)
|
||||
{
|
||||
if (__JS_AtomIsTaggedInt(atom)) {
|
||||
*pval = __JS_AtomToUInt32(atom);
|
||||
return TRUE;
|
||||
} else {
|
||||
JSRuntime *rt = ctx->rt;
|
||||
JSAtomStruct *p;
|
||||
uint32_t val;
|
||||
assert(atom < rt->atom_size);
|
||||
p = rt->atom_array[atom];
|
||||
if (p->atom_type == JS_ATOM_TYPE_STRING &&
|
||||
is_num_string(&val, p) && val != -1) {
|
||||
*pval = val;
|
||||
return TRUE;
|
||||
} else {
|
||||
*pval = 0;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */
|
||||
BOOL is_num_string(uint32_t *pval, const JSString *p)
|
||||
{
|
||||
uint32_t n;
|
||||
uint64_t n64;
|
||||
int c, i, len;
|
||||
len = p->len;
|
||||
if (len == 0 || len > 10)
|
||||
return FALSE;
|
||||
if (p->is_wide_char)
|
||||
c = p->u.str16[0];
|
||||
else
|
||||
c = p->u.str8[0];
|
||||
if (isdigit(c)) {
|
||||
if (c == '0') {
|
||||
if (len != 1)
|
||||
return FALSE;
|
||||
n = 0;
|
||||
} else {
|
||||
n = c - '0';
|
||||
for(i = 1; i < len; i++) {
|
||||
if (p->is_wide_char)
|
||||
c = p->u.str16[i];
|
||||
else
|
||||
c = p->u.str8[i];
|
||||
if (!isdigit(c))
|
||||
return FALSE;
|
||||
n64 = (uint64_t)n * 10 + (c - '0');
|
||||
if ((n64 >> 32) != 0)
|
||||
return FALSE;
|
||||
n = n64;
|
||||
}
|
||||
}
|
||||
*pval = n;
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)
|
||||
{
|
||||
uint32_t i = p->hash_next; /* atom_index */
|
||||
if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
|
||||
JSAtomStruct *p1;
|
||||
i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)];
|
||||
p1 = rt->atom_array[i];
|
||||
while (p1 != p) {
|
||||
assert(i != 0);
|
||||
i = p1->hash_next;
|
||||
p1 = rt->atom_array[i];
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/* val must be a symbol */
|
||||
JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val)
|
||||
{
|
||||
JSAtomStruct *p = JS_VALUE_GET_PTR(val);
|
||||
return js_get_atom_index(ctx->rt, p);
|
||||
}
|
||||
|
||||
/* return a string atom containing name concatenated with str1 */
|
||||
JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1)
|
||||
{
|
||||
JSValue str;
|
||||
JSAtom atom;
|
||||
const char *cstr;
|
||||
char *cstr2;
|
||||
size_t len, len1;
|
||||
str = JS_AtomToString(ctx, name);
|
||||
if (JS_IsException(str))
|
||||
return JS_ATOM_NULL;
|
||||
cstr = JS_ToCStringLen(ctx, &len, str);
|
||||
if (!cstr)
|
||||
goto fail;
|
||||
len1 = strlen(str1);
|
||||
cstr2 = js_malloc(ctx, len + len1 + 1);
|
||||
if (!cstr2)
|
||||
goto fail;
|
||||
memcpy(cstr2, cstr, len);
|
||||
memcpy(cstr2 + len, str1, len1);
|
||||
cstr2[len + len1] = '\0';
|
||||
atom = JS_NewAtomLen(ctx, cstr2, len + len1);
|
||||
js_free(ctx, cstr2);
|
||||
JS_FreeCString(ctx, cstr);
|
||||
JS_FreeValue(ctx, str);
|
||||
return atom;
|
||||
fail:
|
||||
JS_FreeCString(ctx, cstr);
|
||||
JS_FreeValue(ctx, str);
|
||||
return JS_ATOM_NULL;
|
||||
}
|
521
third_party/quickjs/atomics.c
vendored
Normal file
521
third_party/quickjs/atomics.c
vendored
Normal file
|
@ -0,0 +1,521 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
#ifdef CONFIG_ATOMICS
|
||||
|
||||
typedef enum AtomicsOpEnum {
|
||||
ATOMICS_OP_ADD,
|
||||
ATOMICS_OP_AND,
|
||||
ATOMICS_OP_OR,
|
||||
ATOMICS_OP_SUB,
|
||||
ATOMICS_OP_XOR,
|
||||
ATOMICS_OP_EXCHANGE,
|
||||
ATOMICS_OP_COMPARE_EXCHANGE,
|
||||
ATOMICS_OP_LOAD,
|
||||
} AtomicsOpEnum;
|
||||
|
||||
static void *js_atomics_get_ptr(JSContext *ctx,
|
||||
JSArrayBuffer **pabuf,
|
||||
int *psize_log2, JSClassID *pclass_id,
|
||||
JSValueConst obj, JSValueConst idx_val,
|
||||
int is_waitable)
|
||||
{
|
||||
JSObject *p;
|
||||
JSTypedArray *ta;
|
||||
JSArrayBuffer *abuf;
|
||||
void *ptr;
|
||||
uint64_t idx;
|
||||
BOOL err;
|
||||
int size_log2;
|
||||
if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
|
||||
goto fail;
|
||||
p = JS_VALUE_GET_OBJ(obj);
|
||||
#ifdef CONFIG_BIGNUM
|
||||
if (is_waitable)
|
||||
err = (p->class_id != JS_CLASS_INT32_ARRAY &&
|
||||
p->class_id != JS_CLASS_BIG_INT64_ARRAY);
|
||||
else
|
||||
err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&
|
||||
p->class_id <= JS_CLASS_BIG_UINT64_ARRAY);
|
||||
#else
|
||||
if (is_waitable)
|
||||
err = (p->class_id != JS_CLASS_INT32_ARRAY);
|
||||
else
|
||||
err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&
|
||||
p->class_id <= JS_CLASS_UINT32_ARRAY);
|
||||
#endif
|
||||
if (err) {
|
||||
fail:
|
||||
JS_ThrowTypeError(ctx, "integer TypedArray expected");
|
||||
return NULL;
|
||||
}
|
||||
ta = p->u.typed_array;
|
||||
abuf = ta->buffer->u.array_buffer;
|
||||
if (!abuf->shared) {
|
||||
if (is_waitable == 2) {
|
||||
JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray");
|
||||
return NULL;
|
||||
}
|
||||
if (abuf->detached) {
|
||||
JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (JS_ToIndex(ctx, &idx, idx_val)) {
|
||||
return NULL;
|
||||
}
|
||||
/* if the array buffer is detached, p->u.array.count = 0 */
|
||||
if (idx >= p->u.array.count) {
|
||||
JS_ThrowRangeError(ctx, "out-of-bound access");
|
||||
return NULL;
|
||||
}
|
||||
size_log2 = typed_array_size_log2(p->class_id);
|
||||
ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
|
||||
if (pabuf)
|
||||
*pabuf = abuf;
|
||||
if (psize_log2)
|
||||
*psize_log2 = size_log2;
|
||||
if (pclass_id)
|
||||
*pclass_id = p->class_id;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static JSValue js_atomics_op(JSContext *ctx,
|
||||
JSValueConst this_obj,
|
||||
int argc, JSValueConst *argv, int op)
|
||||
{
|
||||
int size_log2;
|
||||
#ifdef CONFIG_BIGNUM
|
||||
uint64_t v, a, rep_val;
|
||||
#else
|
||||
uint32_t v, a, rep_val;
|
||||
#endif
|
||||
void *ptr;
|
||||
JSValue ret;
|
||||
JSClassID class_id;
|
||||
JSArrayBuffer *abuf;
|
||||
|
||||
ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id,
|
||||
argv[0], argv[1], 0);
|
||||
if (!ptr)
|
||||
return JS_EXCEPTION;
|
||||
rep_val = 0;
|
||||
if (op == ATOMICS_OP_LOAD) {
|
||||
v = 0;
|
||||
} else {
|
||||
#ifdef CONFIG_BIGNUM
|
||||
if (size_log2 == 3) {
|
||||
int64_t v64;
|
||||
if (JS_ToBigInt64(ctx, &v64, argv[2]))
|
||||
return JS_EXCEPTION;
|
||||
v = v64;
|
||||
if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
|
||||
if (JS_ToBigInt64(ctx, &v64, argv[3]))
|
||||
return JS_EXCEPTION;
|
||||
rep_val = v64;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
uint32_t v32;
|
||||
if (JS_ToUint32(ctx, &v32, argv[2]))
|
||||
return JS_EXCEPTION;
|
||||
v = v32;
|
||||
if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
|
||||
if (JS_ToUint32(ctx, &v32, argv[3]))
|
||||
return JS_EXCEPTION;
|
||||
rep_val = v32;
|
||||
}
|
||||
}
|
||||
if (abuf->detached)
|
||||
return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
|
||||
}
|
||||
|
||||
switch(op | (size_log2 << 3)) {
|
||||
|
||||
#ifdef CONFIG_BIGNUM
|
||||
#define OP(op_name, func_name) \
|
||||
case ATOMICS_OP_ ## op_name | (0 << 3): \
|
||||
a = func_name((_Atomic(uint8_t) *)ptr, v); \
|
||||
break; \
|
||||
case ATOMICS_OP_ ## op_name | (1 << 3): \
|
||||
a = func_name((_Atomic(uint16_t) *)ptr, v); \
|
||||
break; \
|
||||
case ATOMICS_OP_ ## op_name | (2 << 3): \
|
||||
a = func_name((_Atomic(uint32_t) *)ptr, v); \
|
||||
break; \
|
||||
case ATOMICS_OP_ ## op_name | (3 << 3): \
|
||||
a = func_name((_Atomic(uint64_t) *)ptr, v); \
|
||||
break;
|
||||
#else
|
||||
#define OP(op_name, func_name) \
|
||||
case ATOMICS_OP_ ## op_name | (0 << 3): \
|
||||
a = func_name((_Atomic(uint8_t) *)ptr, v); \
|
||||
break; \
|
||||
case ATOMICS_OP_ ## op_name | (1 << 3): \
|
||||
a = func_name((_Atomic(uint16_t) *)ptr, v); \
|
||||
break; \
|
||||
case ATOMICS_OP_ ## op_name | (2 << 3): \
|
||||
a = func_name((_Atomic(uint32_t) *)ptr, v); \
|
||||
break;
|
||||
#endif
|
||||
OP(ADD, atomic_fetch_add)
|
||||
OP(AND, atomic_fetch_and)
|
||||
OP(OR, atomic_fetch_or)
|
||||
OP(SUB, atomic_fetch_sub)
|
||||
OP(XOR, atomic_fetch_xor)
|
||||
OP(EXCHANGE, atomic_exchange)
|
||||
#undef OP
|
||||
|
||||
case ATOMICS_OP_LOAD | (0 << 3):
|
||||
a = atomic_load((_Atomic(uint8_t) *)ptr);
|
||||
break;
|
||||
case ATOMICS_OP_LOAD | (1 << 3):
|
||||
a = atomic_load((_Atomic(uint16_t) *)ptr);
|
||||
break;
|
||||
case ATOMICS_OP_LOAD | (2 << 3):
|
||||
a = atomic_load((_Atomic(uint32_t) *)ptr);
|
||||
break;
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case ATOMICS_OP_LOAD | (3 << 3):
|
||||
a = atomic_load((_Atomic(uint64_t) *)ptr);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3):
|
||||
{
|
||||
uint8_t v1 = v;
|
||||
atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val);
|
||||
a = v1;
|
||||
}
|
||||
break;
|
||||
case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3):
|
||||
{
|
||||
uint16_t v1 = v;
|
||||
atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val);
|
||||
a = v1;
|
||||
}
|
||||
break;
|
||||
case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3):
|
||||
{
|
||||
uint32_t v1 = v;
|
||||
atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val);
|
||||
a = v1;
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3):
|
||||
{
|
||||
uint64_t v1 = v;
|
||||
atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val);
|
||||
a = v1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
switch(class_id) {
|
||||
case JS_CLASS_INT8_ARRAY:
|
||||
a = (int8_t)a;
|
||||
goto done;
|
||||
case JS_CLASS_UINT8_ARRAY:
|
||||
a = (uint8_t)a;
|
||||
goto done;
|
||||
case JS_CLASS_INT16_ARRAY:
|
||||
a = (int16_t)a;
|
||||
goto done;
|
||||
case JS_CLASS_UINT16_ARRAY:
|
||||
a = (uint16_t)a;
|
||||
goto done;
|
||||
case JS_CLASS_INT32_ARRAY:
|
||||
done:
|
||||
ret = JS_NewInt32(ctx, a);
|
||||
break;
|
||||
case JS_CLASS_UINT32_ARRAY:
|
||||
ret = JS_NewUint32(ctx, a);
|
||||
break;
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_CLASS_BIG_INT64_ARRAY:
|
||||
ret = JS_NewBigInt64(ctx, a);
|
||||
break;
|
||||
case JS_CLASS_BIG_UINT64_ARRAY:
|
||||
ret = JS_NewBigUint64(ctx, a);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_atomics_store(JSContext *ctx,
|
||||
JSValueConst this_obj,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
int size_log2;
|
||||
void *ptr;
|
||||
JSValue ret;
|
||||
JSArrayBuffer *abuf;
|
||||
|
||||
ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL,
|
||||
argv[0], argv[1], 0);
|
||||
if (!ptr)
|
||||
return JS_EXCEPTION;
|
||||
#ifdef CONFIG_BIGNUM
|
||||
if (size_log2 == 3) {
|
||||
int64_t v64;
|
||||
ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2]));
|
||||
if (JS_IsException(ret))
|
||||
return ret;
|
||||
if (JS_ToBigInt64(ctx, &v64, ret)) {
|
||||
JS_FreeValue(ctx, ret);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
if (abuf->detached)
|
||||
return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
|
||||
atomic_store((_Atomic(uint64_t) *)ptr, v64);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
uint32_t v;
|
||||
/* XXX: spec, would be simpler to return the written value */
|
||||
ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2]));
|
||||
if (JS_IsException(ret))
|
||||
return ret;
|
||||
if (JS_ToUint32(ctx, &v, ret)) {
|
||||
JS_FreeValue(ctx, ret);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
if (abuf->detached)
|
||||
return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
|
||||
switch(size_log2) {
|
||||
case 0:
|
||||
atomic_store((_Atomic(uint8_t) *)ptr, v);
|
||||
break;
|
||||
case 1:
|
||||
atomic_store((_Atomic(uint16_t) *)ptr, v);
|
||||
break;
|
||||
case 2:
|
||||
atomic_store((_Atomic(uint32_t) *)ptr, v);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_atomics_isLockFree(JSContext *ctx,
|
||||
JSValueConst this_obj,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
int v, ret;
|
||||
if (JS_ToInt32Sat(ctx, &v, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
ret = (v == 1 || v == 2 || v == 4
|
||||
#ifdef CONFIG_BIGNUM
|
||||
|| v == 8
|
||||
#endif
|
||||
);
|
||||
return JS_NewBool(ctx, ret);
|
||||
}
|
||||
|
||||
typedef struct JSAtomicsWaiter {
|
||||
struct list_head link;
|
||||
BOOL linked;
|
||||
pthread_cond_t cond;
|
||||
int32_t *ptr;
|
||||
} JSAtomicsWaiter;
|
||||
|
||||
static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static struct list_head js_atomics_waiter_list =
|
||||
LIST_HEAD_INIT(js_atomics_waiter_list);
|
||||
|
||||
static JSValue js_atomics_wait(JSContext *ctx,
|
||||
JSValueConst this_obj,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
int64_t v;
|
||||
int32_t v32;
|
||||
void *ptr;
|
||||
int64_t timeout;
|
||||
struct timespec ts;
|
||||
JSAtomicsWaiter waiter_s, *waiter;
|
||||
int ret, size_log2, res;
|
||||
double d;
|
||||
ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL,
|
||||
argv[0], argv[1], 2);
|
||||
if (!ptr)
|
||||
return JS_EXCEPTION;
|
||||
#ifdef CONFIG_BIGNUM
|
||||
if (size_log2 == 3) {
|
||||
if (JS_ToBigInt64(ctx, &v, argv[2]))
|
||||
return JS_EXCEPTION;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (JS_ToInt32(ctx, &v32, argv[2]))
|
||||
return JS_EXCEPTION;
|
||||
v = v32;
|
||||
}
|
||||
if (JS_ToFloat64(ctx, &d, argv[3]))
|
||||
return JS_EXCEPTION;
|
||||
if (isnan(d) || d > INT64_MAX)
|
||||
timeout = INT64_MAX;
|
||||
else if (d < 0)
|
||||
timeout = 0;
|
||||
else
|
||||
timeout = (int64_t)d;
|
||||
if (!ctx->rt->can_block)
|
||||
return JS_ThrowTypeError(ctx, "cannot block in this thread");
|
||||
/* XXX: inefficient if large number of waiters, should hash on
|
||||
'ptr' value */
|
||||
/* XXX: use Linux futexes when available ? */
|
||||
pthread_mutex_lock(&js_atomics_mutex);
|
||||
if (size_log2 == 3) {
|
||||
res = *(int64_t *)ptr != v;
|
||||
} else {
|
||||
res = *(int32_t *)ptr != v;
|
||||
}
|
||||
if (res) {
|
||||
pthread_mutex_unlock(&js_atomics_mutex);
|
||||
return JS_AtomToString(ctx, JS_ATOM_not_equal);
|
||||
}
|
||||
waiter = &waiter_s;
|
||||
waiter->ptr = ptr;
|
||||
pthread_cond_init(&waiter->cond, NULL);
|
||||
waiter->linked = TRUE;
|
||||
list_add_tail(&waiter->link, &js_atomics_waiter_list);
|
||||
if (timeout == INT64_MAX) {
|
||||
pthread_cond_wait(&waiter->cond, &js_atomics_mutex);
|
||||
ret = 0;
|
||||
} else {
|
||||
/* XXX: use clock monotonic */
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
ts.tv_sec += timeout / 1000;
|
||||
ts.tv_nsec += (timeout % 1000) * 1000000;
|
||||
if (ts.tv_nsec >= 1000000000) {
|
||||
ts.tv_nsec -= 1000000000;
|
||||
ts.tv_sec++;
|
||||
}
|
||||
ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex,
|
||||
&ts);
|
||||
}
|
||||
if (waiter->linked)
|
||||
list_del(&waiter->link);
|
||||
pthread_mutex_unlock(&js_atomics_mutex);
|
||||
pthread_cond_destroy(&waiter->cond);
|
||||
if (ret == ETIMEDOUT) {
|
||||
return JS_AtomToString(ctx, JS_ATOM_timed_out);
|
||||
} else {
|
||||
return JS_AtomToString(ctx, JS_ATOM_ok);
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue js_atomics_notify(JSContext *ctx,
|
||||
JSValueConst this_obj,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
struct list_head *el, *el1, waiter_list;
|
||||
int32_t count, n;
|
||||
void *ptr;
|
||||
JSAtomicsWaiter *waiter;
|
||||
JSArrayBuffer *abuf;
|
||||
|
||||
ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1);
|
||||
if (!ptr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
if (JS_IsUndefined(argv[2])) {
|
||||
count = INT32_MAX;
|
||||
} else {
|
||||
if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0))
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
if (abuf->detached)
|
||||
return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
|
||||
|
||||
n = 0;
|
||||
if (abuf->shared && count > 0) {
|
||||
pthread_mutex_lock(&js_atomics_mutex);
|
||||
init_list_head(&waiter_list);
|
||||
list_for_each_safe(el, el1, &js_atomics_waiter_list) {
|
||||
waiter = list_entry(el, JSAtomicsWaiter, link);
|
||||
if (waiter->ptr == ptr) {
|
||||
list_del(&waiter->link);
|
||||
waiter->linked = FALSE;
|
||||
list_add_tail(&waiter->link, &waiter_list);
|
||||
n++;
|
||||
if (n >= count)
|
||||
break;
|
||||
}
|
||||
}
|
||||
list_for_each(el, &waiter_list) {
|
||||
waiter = list_entry(el, JSAtomicsWaiter, link);
|
||||
pthread_cond_signal(&waiter->cond);
|
||||
}
|
||||
pthread_mutex_unlock(&js_atomics_mutex);
|
||||
}
|
||||
return JS_NewInt32(ctx, n);
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_atomics_funcs[] = {
|
||||
JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ),
|
||||
JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ),
|
||||
JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ),
|
||||
JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ),
|
||||
JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ),
|
||||
JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ),
|
||||
JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ),
|
||||
JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ),
|
||||
JS_CFUNC_DEF("store", 3, js_atomics_store ),
|
||||
JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ),
|
||||
JS_CFUNC_DEF("wait", 4, js_atomics_wait ),
|
||||
JS_CFUNC_DEF("notify", 3, js_atomics_notify ),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_atomics_obj[] = {
|
||||
JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
void JS_AddIntrinsicAtomics(JSContext *ctx)
|
||||
{
|
||||
/* add Atomics as autoinit object */
|
||||
JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj));
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ATOMICS */
|
723
third_party/quickjs/bigdecimal.c
vendored
Normal file
723
third_party/quickjs/bigdecimal.c
vendored
Normal file
|
@ -0,0 +1,723 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
JSValue JS_NewBigDecimal(JSContext *ctx)
|
||||
{
|
||||
JSBigDecimal *p;
|
||||
p = js_malloc(ctx, sizeof(*p));
|
||||
if (!p)
|
||||
return JS_EXCEPTION;
|
||||
p->header.ref_count = 1;
|
||||
bfdec_init(ctx->bf_ctx, &p->num);
|
||||
return JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
|
||||
}
|
||||
|
||||
/* return NULL if invalid type */
|
||||
bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val)
|
||||
{
|
||||
uint32_t tag;
|
||||
JSBigDecimal *p;
|
||||
bfdec_t *r;
|
||||
tag = JS_VALUE_GET_NORM_TAG(val);
|
||||
switch(tag) {
|
||||
case JS_TAG_BIG_DECIMAL:
|
||||
p = JS_VALUE_GET_PTR(val);
|
||||
r = &p->num;
|
||||
break;
|
||||
default:
|
||||
JS_ThrowTypeError(ctx, "bigdecimal expected");
|
||||
r = NULL;
|
||||
break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
|
||||
BOOL allow_null_or_undefined)
|
||||
{
|
||||
redo:
|
||||
switch(JS_VALUE_GET_NORM_TAG(val)) {
|
||||
case JS_TAG_BIG_DECIMAL:
|
||||
break;
|
||||
case JS_TAG_NULL:
|
||||
if (!allow_null_or_undefined)
|
||||
goto fail;
|
||||
/* fall thru */
|
||||
case JS_TAG_BOOL:
|
||||
case JS_TAG_INT:
|
||||
{
|
||||
bfdec_t *r;
|
||||
int32_t v = JS_VALUE_GET_INT(val);
|
||||
val = JS_NewBigDecimal(ctx);
|
||||
if (JS_IsException(val))
|
||||
break;
|
||||
r = JS_GetBigDecimal(val);
|
||||
if (bfdec_set_si(r, v)) {
|
||||
JS_FreeValue(ctx, val);
|
||||
val = JS_EXCEPTION;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_TAG_FLOAT64:
|
||||
case JS_TAG_BIG_INT:
|
||||
case JS_TAG_BIG_FLOAT:
|
||||
val = JS_ToStringFree(ctx, val);
|
||||
if (JS_IsException(val))
|
||||
break;
|
||||
goto redo;
|
||||
case JS_TAG_STRING:
|
||||
{
|
||||
const char *str, *p;
|
||||
size_t len;
|
||||
int err;
|
||||
str = JS_ToCStringLen(ctx, &len, val);
|
||||
JS_FreeValue(ctx, val);
|
||||
if (!str)
|
||||
return JS_EXCEPTION;
|
||||
p = str;
|
||||
p += skip_spaces(p);
|
||||
if ((p - str) == len) {
|
||||
bfdec_t *r;
|
||||
val = JS_NewBigDecimal(ctx);
|
||||
if (JS_IsException(val))
|
||||
break;
|
||||
r = JS_GetBigDecimal(val);
|
||||
bfdec_set_zero(r, 0);
|
||||
err = 0;
|
||||
} else {
|
||||
val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL);
|
||||
if (JS_IsException(val)) {
|
||||
JS_FreeCString(ctx, str);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
p += skip_spaces(p);
|
||||
err = ((p - str) != len);
|
||||
}
|
||||
JS_FreeCString(ctx, str);
|
||||
if (err) {
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_TAG_OBJECT:
|
||||
val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
|
||||
if (JS_IsException(val))
|
||||
break;
|
||||
goto redo;
|
||||
case JS_TAG_UNDEFINED:
|
||||
{
|
||||
bfdec_t *r;
|
||||
if (!allow_null_or_undefined)
|
||||
goto fail;
|
||||
val = JS_NewBigDecimal(ctx);
|
||||
if (JS_IsException(val))
|
||||
break;
|
||||
r = JS_GetBigDecimal(val);
|
||||
bfdec_set_nan(r);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fail:
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal");
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static JSValue js_bigdecimal_constructor(JSContext *ctx,
|
||||
JSValueConst new_target,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValue val;
|
||||
if (!JS_IsUndefined(new_target))
|
||||
return JS_ThrowTypeError(ctx, "not a constructor");
|
||||
if (argc == 0) {
|
||||
bfdec_t *r;
|
||||
val = JS_NewBigDecimal(ctx);
|
||||
if (JS_IsException(val))
|
||||
return val;
|
||||
r = JS_GetBigDecimal(val);
|
||||
bfdec_set_zero(r, 0);
|
||||
} else {
|
||||
val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val)
|
||||
{
|
||||
if (JS_IsBigDecimal(this_val))
|
||||
return JS_DupValue(ctx, this_val);
|
||||
|
||||
if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(this_val);
|
||||
if (p->class_id == JS_CLASS_BIG_DECIMAL) {
|
||||
if (JS_IsBigDecimal(p->u.object_data))
|
||||
return JS_DupValue(ctx, p->u.object_data);
|
||||
}
|
||||
}
|
||||
return JS_ThrowTypeError(ctx, "not a bigdecimal");
|
||||
}
|
||||
|
||||
static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValue val;
|
||||
|
||||
val = js_thisBigDecimalValue(ctx, this_val);
|
||||
if (JS_IsException(val))
|
||||
return val;
|
||||
return JS_ToStringFree(ctx, val);
|
||||
}
|
||||
|
||||
static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
return js_thisBigDecimalValue(ctx, this_val);
|
||||
}
|
||||
|
||||
static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj)
|
||||
{
|
||||
const char *str;
|
||||
size_t size;
|
||||
int rnd_mode;
|
||||
|
||||
str = JS_ToCStringLen(ctx, &size, obj);
|
||||
if (!str)
|
||||
return -1;
|
||||
if (strlen(str) != size)
|
||||
goto invalid_rounding_mode;
|
||||
if (!strcmp(str, "floor")) {
|
||||
rnd_mode = BF_RNDD;
|
||||
} else if (!strcmp(str, "ceiling")) {
|
||||
rnd_mode = BF_RNDU;
|
||||
} else if (!strcmp(str, "down")) {
|
||||
rnd_mode = BF_RNDZ;
|
||||
} else if (!strcmp(str, "up")) {
|
||||
rnd_mode = BF_RNDA;
|
||||
} else if (!strcmp(str, "half-even")) {
|
||||
rnd_mode = BF_RNDN;
|
||||
} else if (!strcmp(str, "half-up")) {
|
||||
rnd_mode = BF_RNDNA;
|
||||
} else {
|
||||
invalid_rounding_mode:
|
||||
JS_FreeCString(ctx, str);
|
||||
JS_ThrowTypeError(ctx, "invalid rounding mode");
|
||||
return -1;
|
||||
}
|
||||
JS_FreeCString(ctx, str);
|
||||
return rnd_mode;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int64_t prec;
|
||||
bf_flags_t flags;
|
||||
} BigDecimalEnv;
|
||||
|
||||
static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,
|
||||
JSValueConst obj)
|
||||
{
|
||||
JSValue prop;
|
||||
int64_t val;
|
||||
BOOL has_prec;
|
||||
int rnd_mode;
|
||||
if (!JS_IsObject(obj)) {
|
||||
JS_ThrowTypeErrorNotAnObject(ctx);
|
||||
return -1;
|
||||
}
|
||||
prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode);
|
||||
if (JS_IsException(prop))
|
||||
return -1;
|
||||
rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop);
|
||||
JS_FreeValue(ctx, prop);
|
||||
if (rnd_mode < 0)
|
||||
return -1;
|
||||
fe->flags = rnd_mode;
|
||||
prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits);
|
||||
if (JS_IsException(prop))
|
||||
return -1;
|
||||
has_prec = FALSE;
|
||||
if (!JS_IsUndefined(prop)) {
|
||||
if (JS_ToInt64SatFree(ctx, &val, prop))
|
||||
return -1;
|
||||
if (val < 1 || val > BF_PREC_MAX)
|
||||
goto invalid_precision;
|
||||
fe->prec = val;
|
||||
has_prec = TRUE;
|
||||
}
|
||||
prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits);
|
||||
if (JS_IsException(prop))
|
||||
return -1;
|
||||
if (!JS_IsUndefined(prop)) {
|
||||
if (has_prec) {
|
||||
JS_FreeValue(ctx, prop);
|
||||
JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits");
|
||||
return -1;
|
||||
}
|
||||
if (JS_ToInt64SatFree(ctx, &val, prop))
|
||||
return -1;
|
||||
if (val < 0 || val > BF_PREC_MAX) {
|
||||
invalid_precision:
|
||||
JS_ThrowTypeError(ctx, "invalid precision");
|
||||
return -1;
|
||||
}
|
||||
fe->prec = val;
|
||||
fe->flags |= BF_FLAG_RADPNT_PREC;
|
||||
has_prec = TRUE;
|
||||
}
|
||||
if (!has_prec) {
|
||||
JS_ThrowTypeError(ctx, "precision must be present");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
bfdec_t *a, *b, r_s, *r = &r_s;
|
||||
JSValue op1, op2, res;
|
||||
BigDecimalEnv fe_s, *fe = &fe_s;
|
||||
int op_count, ret;
|
||||
if (magic == MATH_OP_SQRT ||
|
||||
magic == MATH_OP_ROUND)
|
||||
op_count = 1;
|
||||
else
|
||||
op_count = 2;
|
||||
op1 = JS_ToNumeric(ctx, argv[0]);
|
||||
if (JS_IsException(op1))
|
||||
return op1;
|
||||
a = JS_ToBigDecimal(ctx, op1);
|
||||
if (!a) {
|
||||
JS_FreeValue(ctx, op1);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
if (op_count >= 2) {
|
||||
op2 = JS_ToNumeric(ctx, argv[1]);
|
||||
if (JS_IsException(op2)) {
|
||||
JS_FreeValue(ctx, op1);
|
||||
return op2;
|
||||
}
|
||||
b = JS_ToBigDecimal(ctx, op2);
|
||||
if (!b)
|
||||
goto fail;
|
||||
} else {
|
||||
op2 = JS_UNDEFINED;
|
||||
b = NULL;
|
||||
}
|
||||
fe->flags = BF_RNDZ;
|
||||
fe->prec = BF_PREC_INF;
|
||||
if (op_count < argc) {
|
||||
if (js_bigdecimal_get_env(ctx, fe, argv[op_count]))
|
||||
goto fail;
|
||||
}
|
||||
res = JS_NewBigDecimal(ctx);
|
||||
if (JS_IsException(res)) {
|
||||
fail:
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
r = JS_GetBigDecimal(res);
|
||||
switch (magic) {
|
||||
case MATH_OP_ADD:
|
||||
ret = bfdec_add(r, a, b, fe->prec, fe->flags);
|
||||
break;
|
||||
case MATH_OP_SUB:
|
||||
ret = bfdec_sub(r, a, b, fe->prec, fe->flags);
|
||||
break;
|
||||
case MATH_OP_MUL:
|
||||
ret = bfdec_mul(r, a, b, fe->prec, fe->flags);
|
||||
break;
|
||||
case MATH_OP_DIV:
|
||||
ret = bfdec_div(r, a, b, fe->prec, fe->flags);
|
||||
break;
|
||||
case MATH_OP_FMOD:
|
||||
ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
|
||||
break;
|
||||
case MATH_OP_SQRT:
|
||||
ret = bfdec_sqrt(r, a, fe->prec, fe->flags);
|
||||
break;
|
||||
case MATH_OP_ROUND:
|
||||
ret = bfdec_set(r, a);
|
||||
if (!(ret & BF_ST_MEM_ERROR))
|
||||
ret = bfdec_round(r, fe->prec, fe->flags);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP |
|
||||
BF_ST_OVERFLOW;
|
||||
if (ret != 0) {
|
||||
JS_FreeValue(ctx, res);
|
||||
return throw_bf_exception(ctx, ret);
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValue val, ret;
|
||||
int64_t f;
|
||||
int rnd_mode;
|
||||
val = js_thisBigDecimalValue(ctx, this_val);
|
||||
if (JS_IsException(val))
|
||||
return val;
|
||||
if (JS_ToInt64Sat(ctx, &f, argv[0]))
|
||||
goto fail;
|
||||
if (f < 0 || f > BF_PREC_MAX) {
|
||||
JS_ThrowRangeError(ctx, "invalid number of digits");
|
||||
goto fail;
|
||||
}
|
||||
rnd_mode = BF_RNDNA;
|
||||
if (argc > 1) {
|
||||
rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
|
||||
if (rnd_mode < 0)
|
||||
goto fail;
|
||||
}
|
||||
ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
|
||||
JS_FreeValue(ctx, val);
|
||||
return ret;
|
||||
fail:
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValue val, ret;
|
||||
int64_t f;
|
||||
int rnd_mode;
|
||||
val = js_thisBigDecimalValue(ctx, this_val);
|
||||
if (JS_IsException(val))
|
||||
return val;
|
||||
if (JS_ToInt64Sat(ctx, &f, argv[0]))
|
||||
goto fail;
|
||||
if (JS_IsUndefined(argv[0])) {
|
||||
ret = js_bigdecimal_to_string1(ctx, val, 0,
|
||||
BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
|
||||
} else {
|
||||
if (f < 0 || f > BF_PREC_MAX) {
|
||||
JS_ThrowRangeError(ctx, "invalid number of digits");
|
||||
goto fail;
|
||||
}
|
||||
rnd_mode = BF_RNDNA;
|
||||
if (argc > 1) {
|
||||
rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
|
||||
if (rnd_mode < 0)
|
||||
goto fail;
|
||||
}
|
||||
ret = js_bigdecimal_to_string1(ctx, val, f + 1,
|
||||
rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return ret;
|
||||
fail:
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValue val, ret;
|
||||
int64_t p;
|
||||
int rnd_mode;
|
||||
val = js_thisBigDecimalValue(ctx, this_val);
|
||||
if (JS_IsException(val))
|
||||
return val;
|
||||
if (JS_IsUndefined(argv[0])) {
|
||||
return JS_ToStringFree(ctx, val);
|
||||
}
|
||||
if (JS_ToInt64Sat(ctx, &p, argv[0]))
|
||||
goto fail;
|
||||
if (p < 1 || p > BF_PREC_MAX) {
|
||||
JS_ThrowRangeError(ctx, "invalid number of digits");
|
||||
goto fail;
|
||||
}
|
||||
rnd_mode = BF_RNDNA;
|
||||
if (argc > 1) {
|
||||
rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
|
||||
if (rnd_mode < 0)
|
||||
goto fail;
|
||||
}
|
||||
ret = js_bigdecimal_to_string1(ctx, val, p,
|
||||
rnd_mode | BF_FTOA_FORMAT_FIXED);
|
||||
JS_FreeValue(ctx, val);
|
||||
return ret;
|
||||
fail:
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = {
|
||||
JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ),
|
||||
JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ),
|
||||
JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ),
|
||||
JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ),
|
||||
JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_bigdecimal_funcs[] = {
|
||||
JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ),
|
||||
JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ),
|
||||
JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ),
|
||||
JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ),
|
||||
JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ),
|
||||
JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ),
|
||||
JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ),
|
||||
};
|
||||
|
||||
static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf,
|
||||
int radix, int flags, slimb_t *pexponent)
|
||||
{
|
||||
bfdec_t *a;
|
||||
int ret;
|
||||
JSValue val;
|
||||
val = JS_NewBigDecimal(ctx);
|
||||
if (JS_IsException(val))
|
||||
return val;
|
||||
a = JS_GetBigDecimal(val);
|
||||
ret = bfdec_atof(a, buf, NULL, BF_PREC_INF,
|
||||
BF_RNDZ | BF_ATOF_NO_NAN_INF);
|
||||
if (ret & BF_ST_MEM_ERROR) {
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_ThrowOutOfMemory(ctx);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static int js_unary_arith_bigdecimal(JSContext *ctx,
|
||||
JSValue *pres, OPCodeEnum op, JSValue op1)
|
||||
{
|
||||
bfdec_t *r, *a;
|
||||
int ret, v;
|
||||
JSValue res;
|
||||
if (op == OP_plus && !is_math_mode(ctx)) {
|
||||
JS_ThrowTypeError(ctx, "bigdecimal argument with unary +");
|
||||
JS_FreeValue(ctx, op1);
|
||||
return -1;
|
||||
}
|
||||
res = JS_NewBigDecimal(ctx);
|
||||
if (JS_IsException(res)) {
|
||||
JS_FreeValue(ctx, op1);
|
||||
return -1;
|
||||
}
|
||||
r = JS_GetBigDecimal(res);
|
||||
a = JS_ToBigDecimal(ctx, op1);
|
||||
ret = 0;
|
||||
switch(op) {
|
||||
case OP_inc:
|
||||
case OP_dec:
|
||||
v = 2 * (op - OP_dec) - 1;
|
||||
ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
|
||||
break;
|
||||
case OP_plus:
|
||||
ret = bfdec_set(r, a);
|
||||
break;
|
||||
case OP_neg:
|
||||
ret = bfdec_set(r, a);
|
||||
bfdec_neg(r);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
JS_FreeValue(ctx, op1);
|
||||
if (UNLIKELY(ret)) {
|
||||
JS_FreeValue(ctx, res);
|
||||
throw_bf_exception(ctx, ret);
|
||||
return -1;
|
||||
}
|
||||
*pres = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* b must be a positive integer */
|
||||
static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b)
|
||||
{
|
||||
bfdec_t b1;
|
||||
int32_t b2;
|
||||
int ret;
|
||||
bfdec_init(b->ctx, &b1);
|
||||
ret = bfdec_set(&b1, b);
|
||||
if (ret) {
|
||||
bfdec_delete(&b1);
|
||||
return ret;
|
||||
}
|
||||
ret = bfdec_rint(&b1, BF_RNDZ);
|
||||
if (ret) {
|
||||
bfdec_delete(&b1);
|
||||
return BF_ST_INVALID_OP; /* must be an integer */
|
||||
}
|
||||
ret = bfdec_get_int32(&b2, &b1);
|
||||
bfdec_delete(&b1);
|
||||
if (ret)
|
||||
return ret; /* overflow */
|
||||
if (b2 < 0)
|
||||
return BF_ST_INVALID_OP; /* must be positive */
|
||||
return bfdec_pow_ui(r, a, b2);
|
||||
}
|
||||
|
||||
static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op,
|
||||
JSValue op1, JSValue op2)
|
||||
{
|
||||
bfdec_t *a, *b;
|
||||
int res;
|
||||
/* Note: binary floats are converted to bigdecimal with
|
||||
toString(). It is not mathematically correct but is consistent
|
||||
with the BigDecimal() constructor behavior */
|
||||
op1 = JS_ToBigDecimalFree(ctx, op1, TRUE);
|
||||
if (JS_IsException(op1)) {
|
||||
JS_FreeValue(ctx, op2);
|
||||
return -1;
|
||||
}
|
||||
op2 = JS_ToBigDecimalFree(ctx, op2, TRUE);
|
||||
if (JS_IsException(op2)) {
|
||||
JS_FreeValue(ctx, op1);
|
||||
return -1;
|
||||
}
|
||||
a = JS_ToBigDecimal(ctx, op1);
|
||||
b = JS_ToBigDecimal(ctx, op2);
|
||||
switch(op) {
|
||||
case OP_lt:
|
||||
res = bfdec_cmp_lt(a, b); /* if NaN return false */
|
||||
break;
|
||||
case OP_lte:
|
||||
res = bfdec_cmp_le(a, b); /* if NaN return false */
|
||||
break;
|
||||
case OP_gt:
|
||||
res = bfdec_cmp_lt(b, a); /* if NaN return false */
|
||||
break;
|
||||
case OP_gte:
|
||||
res = bfdec_cmp_le(b, a); /* if NaN return false */
|
||||
break;
|
||||
case OP_eq:
|
||||
res = bfdec_cmp_eq(a, b); /* if NaN return false */
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
|
||||
JSValue *pres, JSValue op1, JSValue op2)
|
||||
{
|
||||
bfdec_t *r, *a, *b;
|
||||
int ret;
|
||||
JSValue res;
|
||||
res = JS_NewBigDecimal(ctx);
|
||||
if (JS_IsException(res))
|
||||
goto fail;
|
||||
r = JS_GetBigDecimal(res);
|
||||
a = JS_ToBigDecimal(ctx, op1);
|
||||
if (!a)
|
||||
goto fail;
|
||||
b = JS_ToBigDecimal(ctx, op2);
|
||||
if (!b)
|
||||
goto fail;
|
||||
switch(op) {
|
||||
case OP_add:
|
||||
ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ);
|
||||
break;
|
||||
case OP_sub:
|
||||
ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
|
||||
break;
|
||||
case OP_mul:
|
||||
ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
|
||||
break;
|
||||
case OP_div:
|
||||
ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ);
|
||||
break;
|
||||
case OP_math_mod:
|
||||
/* Euclidian remainder */
|
||||
ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN);
|
||||
break;
|
||||
case OP_mod:
|
||||
ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ);
|
||||
break;
|
||||
case OP_pow:
|
||||
ret = js_bfdec_pow(r, a, b);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
if (UNLIKELY(ret)) {
|
||||
JS_FreeValue(ctx, res);
|
||||
throw_bf_exception(ctx, ret);
|
||||
return -1;
|
||||
}
|
||||
*pres = res;
|
||||
return 0;
|
||||
fail:
|
||||
JS_FreeValue(ctx, res);
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void JS_AddIntrinsicBigDecimal(JSContext *ctx)
|
||||
{
|
||||
JSRuntime *rt = ctx->rt;
|
||||
JSValueConst obj1;
|
||||
rt->bigdecimal_ops.to_string = js_bigdecimal_to_string;
|
||||
rt->bigdecimal_ops.from_string = js_string_to_bigdecimal;
|
||||
rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal;
|
||||
rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal;
|
||||
rt->bigdecimal_ops.compare = js_compare_bigdecimal;
|
||||
ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL],
|
||||
js_bigdecimal_proto_funcs,
|
||||
countof(js_bigdecimal_proto_funcs));
|
||||
obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal",
|
||||
js_bigdecimal_constructor, 1,
|
||||
ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
|
||||
JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs,
|
||||
countof(js_bigdecimal_funcs));
|
||||
}
|
||||
|
||||
void JS_EnableBignumExt(JSContext *ctx, BOOL enable)
|
||||
{
|
||||
ctx->bignum_ext = enable;
|
||||
}
|
850
third_party/quickjs/bigint.c
vendored
Normal file
850
third_party/quickjs/bigint.c
vendored
Normal file
|
@ -0,0 +1,850 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
JSValue JS_NewBigInt(JSContext *ctx)
|
||||
{
|
||||
JSBigFloat *p;
|
||||
p = js_malloc(ctx, sizeof(*p));
|
||||
if (!p)
|
||||
return JS_EXCEPTION;
|
||||
p->header.ref_count = 1;
|
||||
bf_init(ctx->bf_ctx, &p->num);
|
||||
return JS_MKPTR(JS_TAG_BIG_INT, p);
|
||||
}
|
||||
|
||||
static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
|
||||
BOOL convert_to_safe_integer)
|
||||
{
|
||||
int64_t v;
|
||||
bf_t *a;
|
||||
if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT)
|
||||
return val; /* fail safe */
|
||||
a = JS_GetBigInt(val);
|
||||
if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 &&
|
||||
v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_NewInt64(ctx, v);
|
||||
} else if (a->expn == BF_EXP_ZERO && a->sign) {
|
||||
JSBigFloat *p = JS_VALUE_GET_PTR(val);
|
||||
assert(p->header.ref_count == 1);
|
||||
a->sign = 0;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Convert the big int to a safe integer if in math mode. normalize
|
||||
the zero representation. Could also be used to convert the bigint
|
||||
to a short bigint value. The reference count of the value must be
|
||||
1. Cannot fail */
|
||||
JSValue JS_CompactBigInt(JSContext *ctx, JSValue val)
|
||||
{
|
||||
return JS_CompactBigInt1(ctx, val, is_math_mode(ctx));
|
||||
}
|
||||
|
||||
/* return NaN if bad bigint literal */
|
||||
JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
|
||||
{
|
||||
const char *str, *p;
|
||||
size_t len;
|
||||
int flags;
|
||||
str = JS_ToCStringLen(ctx, &len, val);
|
||||
JS_FreeValue(ctx, val);
|
||||
if (!str)
|
||||
return JS_EXCEPTION;
|
||||
p = str;
|
||||
p += skip_spaces(p);
|
||||
if ((p - str) == len) {
|
||||
val = JS_NewBigInt64(ctx, 0);
|
||||
} else {
|
||||
flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
|
||||
if (is_math_mode(ctx))
|
||||
flags |= ATOD_MODE_BIGINT;
|
||||
val = js_atof(ctx, p, &p, 0, flags);
|
||||
p += skip_spaces(p);
|
||||
if (!JS_IsException(val)) {
|
||||
if ((p - str) != len) {
|
||||
JS_FreeValue(ctx, val);
|
||||
val = JS_NAN;
|
||||
}
|
||||
}
|
||||
}
|
||||
JS_FreeCString(ctx, str);
|
||||
return val;
|
||||
}
|
||||
|
||||
static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)
|
||||
{
|
||||
val = JS_StringToBigInt(ctx, val);
|
||||
if (JS_VALUE_IS_NAN(val))
|
||||
return JS_ThrowSyntaxError(ctx, "invalid bigint literal");
|
||||
return val;
|
||||
}
|
||||
|
||||
static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
|
||||
{
|
||||
uint32_t tag;
|
||||
redo:
|
||||
tag = JS_VALUE_GET_NORM_TAG(val);
|
||||
switch(tag) {
|
||||
case JS_TAG_INT:
|
||||
case JS_TAG_BOOL:
|
||||
val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
|
||||
break;
|
||||
case JS_TAG_BIG_INT:
|
||||
break;
|
||||
case JS_TAG_FLOAT64:
|
||||
case JS_TAG_BIG_FLOAT:
|
||||
{
|
||||
bf_t *a, a_s;
|
||||
a = JS_ToBigFloat(ctx, &a_s, val);
|
||||
if (!bf_is_finite(a)) {
|
||||
JS_FreeValue(ctx, val);
|
||||
val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint");
|
||||
} else {
|
||||
JSValue val1 = JS_NewBigInt(ctx);
|
||||
bf_t *r;
|
||||
int ret;
|
||||
if (JS_IsException(val1)) {
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
r = JS_GetBigInt(val1);
|
||||
ret = bf_set(r, a);
|
||||
ret |= bf_rint(r, BF_RNDZ);
|
||||
JS_FreeValue(ctx, val);
|
||||
if (ret & BF_ST_MEM_ERROR) {
|
||||
JS_FreeValue(ctx, val1);
|
||||
val = JS_ThrowOutOfMemory(ctx);
|
||||
} else if (ret & BF_ST_INEXACT) {
|
||||
JS_FreeValue(ctx, val1);
|
||||
val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer");
|
||||
} else {
|
||||
val = JS_CompactBigInt(ctx, val1);
|
||||
}
|
||||
}
|
||||
if (a == &a_s)
|
||||
bf_delete(a);
|
||||
}
|
||||
break;
|
||||
case JS_TAG_BIG_DECIMAL:
|
||||
val = JS_ToStringFree(ctx, val);
|
||||
if (JS_IsException(val))
|
||||
break;
|
||||
goto redo;
|
||||
case JS_TAG_STRING:
|
||||
val = JS_StringToBigIntErr(ctx, val);
|
||||
break;
|
||||
case JS_TAG_OBJECT:
|
||||
val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
|
||||
if (JS_IsException(val))
|
||||
break;
|
||||
goto redo;
|
||||
case JS_TAG_NULL:
|
||||
case JS_TAG_UNDEFINED:
|
||||
default:
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_ThrowTypeError(ctx, "cannot convert to bigint");
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static JSValue js_bigint_constructor(JSContext *ctx,
|
||||
JSValueConst new_target,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
if (!JS_IsUndefined(new_target))
|
||||
return JS_ThrowTypeError(ctx, "not a constructor");
|
||||
return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
|
||||
}
|
||||
|
||||
static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
|
||||
{
|
||||
if (JS_IsBigInt(ctx, this_val))
|
||||
return JS_DupValue(ctx, this_val);
|
||||
if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(this_val);
|
||||
if (p->class_id == JS_CLASS_BIG_INT) {
|
||||
if (JS_IsBigInt(ctx, p->u.object_data))
|
||||
return JS_DupValue(ctx, p->u.object_data);
|
||||
}
|
||||
}
|
||||
return JS_ThrowTypeError(ctx, "not a bigint");
|
||||
}
|
||||
|
||||
static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValue val;
|
||||
int base;
|
||||
JSValue ret;
|
||||
val = js_thisBigIntValue(ctx, this_val);
|
||||
if (JS_IsException(val))
|
||||
return val;
|
||||
if (argc == 0 || JS_IsUndefined(argv[0])) {
|
||||
base = 10;
|
||||
} else {
|
||||
base = js_get_radix(ctx, argv[0]);
|
||||
if (base < 0)
|
||||
goto fail;
|
||||
}
|
||||
ret = js_bigint_to_string1(ctx, val, base);
|
||||
JS_FreeValue(ctx, val);
|
||||
return ret;
|
||||
fail:
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
return js_thisBigIntValue(ctx, this_val);
|
||||
}
|
||||
|
||||
static JSValue js_bigint_div(JSContext *ctx,
|
||||
JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
bf_t a_s, b_s, *a, *b, *r, *q;
|
||||
int status;
|
||||
JSValue q_val, r_val;
|
||||
q_val = JS_NewBigInt(ctx);
|
||||
if (JS_IsException(q_val))
|
||||
return JS_EXCEPTION;
|
||||
r_val = JS_NewBigInt(ctx);
|
||||
if (JS_IsException(r_val))
|
||||
goto fail;
|
||||
b = NULL;
|
||||
a = JS_ToBigInt(ctx, &a_s, argv[0]);
|
||||
if (!a)
|
||||
goto fail;
|
||||
b = JS_ToBigInt(ctx, &b_s, argv[1]);
|
||||
if (!b) {
|
||||
JS_FreeBigInt(ctx, a, &a_s);
|
||||
goto fail;
|
||||
}
|
||||
q = JS_GetBigInt(q_val);
|
||||
r = JS_GetBigInt(r_val);
|
||||
status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf);
|
||||
JS_FreeBigInt(ctx, a, &a_s);
|
||||
JS_FreeBigInt(ctx, b, &b_s);
|
||||
if (UNLIKELY(status)) {
|
||||
throw_bf_exception(ctx, status);
|
||||
goto fail;
|
||||
}
|
||||
q_val = JS_CompactBigInt(ctx, q_val);
|
||||
if (magic & 0x10) {
|
||||
JSValue ret;
|
||||
ret = JS_NewArray(ctx);
|
||||
if (JS_IsException(ret))
|
||||
goto fail;
|
||||
JS_SetPropertyUint32(ctx, ret, 0, q_val);
|
||||
JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val));
|
||||
return ret;
|
||||
} else {
|
||||
JS_FreeValue(ctx, r_val);
|
||||
return q_val;
|
||||
}
|
||||
fail:
|
||||
JS_FreeValue(ctx, q_val);
|
||||
JS_FreeValue(ctx, r_val);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
static JSValue js_bigint_sqrt(JSContext *ctx,
|
||||
JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
bf_t a_s, *a, *r, *rem;
|
||||
int status;
|
||||
JSValue r_val, rem_val;
|
||||
r_val = JS_NewBigInt(ctx);
|
||||
if (JS_IsException(r_val))
|
||||
return JS_EXCEPTION;
|
||||
rem_val = JS_NewBigInt(ctx);
|
||||
if (JS_IsException(rem_val))
|
||||
return JS_EXCEPTION;
|
||||
r = JS_GetBigInt(r_val);
|
||||
rem = JS_GetBigInt(rem_val);
|
||||
a = JS_ToBigInt(ctx, &a_s, argv[0]);
|
||||
if (!a)
|
||||
goto fail;
|
||||
status = bf_sqrtrem(r, rem, a);
|
||||
JS_FreeBigInt(ctx, a, &a_s);
|
||||
if (UNLIKELY(status & ~BF_ST_INEXACT)) {
|
||||
throw_bf_exception(ctx, status);
|
||||
goto fail;
|
||||
}
|
||||
r_val = JS_CompactBigInt(ctx, r_val);
|
||||
if (magic) {
|
||||
JSValue ret;
|
||||
ret = JS_NewArray(ctx);
|
||||
if (JS_IsException(ret))
|
||||
goto fail;
|
||||
JS_SetPropertyUint32(ctx, ret, 0, r_val);
|
||||
JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val));
|
||||
return ret;
|
||||
} else {
|
||||
JS_FreeValue(ctx, rem_val);
|
||||
return r_val;
|
||||
}
|
||||
fail:
|
||||
JS_FreeValue(ctx, r_val);
|
||||
JS_FreeValue(ctx, rem_val);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
static JSValue js_bigint_op1(JSContext *ctx,
|
||||
JSValueConst this_val,
|
||||
int argc, JSValueConst *argv,
|
||||
int magic)
|
||||
{
|
||||
bf_t a_s, *a;
|
||||
int64_t res;
|
||||
a = JS_ToBigInt(ctx, &a_s, argv[0]);
|
||||
if (!a)
|
||||
return JS_EXCEPTION;
|
||||
switch(magic) {
|
||||
case 0: /* floorLog2 */
|
||||
if (a->sign || a->expn <= 0) {
|
||||
res = -1;
|
||||
} else {
|
||||
res = a->expn - 1;
|
||||
}
|
||||
break;
|
||||
case 1: /* ctz */
|
||||
if (bf_is_zero(a)) {
|
||||
res = -1;
|
||||
} else {
|
||||
res = bf_get_exp_min(a);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
JS_FreeBigInt(ctx, a, &a_s);
|
||||
return JS_NewBigInt64(ctx, res);
|
||||
}
|
||||
|
||||
static JSValue js_bigint_asUintN(JSContext *ctx,
|
||||
JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int asIntN)
|
||||
{
|
||||
uint64_t bits;
|
||||
bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s;
|
||||
JSValue res;
|
||||
if (JS_ToIndex(ctx, &bits, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
res = JS_NewBigInt(ctx);
|
||||
if (JS_IsException(res))
|
||||
return JS_EXCEPTION;
|
||||
r = JS_GetBigInt(res);
|
||||
a = JS_ToBigInt(ctx, &a_s, argv[1]);
|
||||
if (!a) {
|
||||
JS_FreeValue(ctx, res);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
/* XXX: optimize */
|
||||
r = JS_GetBigInt(res);
|
||||
bf_init(ctx->bf_ctx, mask);
|
||||
bf_set_ui(mask, 1);
|
||||
bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
|
||||
bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ);
|
||||
bf_logic_and(r, a, mask);
|
||||
if (asIntN && bits != 0) {
|
||||
bf_set_ui(mask, 1);
|
||||
bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ);
|
||||
if (bf_cmpu(r, mask) >= 0) {
|
||||
bf_set_ui(mask, 1);
|
||||
bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
|
||||
bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ);
|
||||
}
|
||||
}
|
||||
bf_delete(mask);
|
||||
JS_FreeBigInt(ctx, a, &a_s);
|
||||
return JS_CompactBigInt(ctx, res);
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_bigint_funcs[] = {
|
||||
JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),
|
||||
JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),
|
||||
/* QuickJS extensions */
|
||||
JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ),
|
||||
JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ),
|
||||
JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ),
|
||||
JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ),
|
||||
JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ),
|
||||
JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ),
|
||||
JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ),
|
||||
JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ),
|
||||
JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ),
|
||||
JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ),
|
||||
JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ),
|
||||
JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_bigint_proto_funcs[] = {
|
||||
JS_CFUNC_DEF("toString", 0, js_bigint_toString ),
|
||||
JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,
|
||||
int radix, int flags, slimb_t *pexponent)
|
||||
{
|
||||
bf_t a_s, *a = &a_s;
|
||||
int ret;
|
||||
JSValue val;
|
||||
val = JS_NewBigInt(ctx);
|
||||
if (JS_IsException(val))
|
||||
return val;
|
||||
a = JS_GetBigInt(val);
|
||||
ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ);
|
||||
if (ret & BF_ST_MEM_ERROR) {
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_ThrowOutOfMemory(ctx);
|
||||
}
|
||||
val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
static int js_unary_arith_bigint(JSContext *ctx,
|
||||
JSValue *pres, OPCodeEnum op, JSValue op1)
|
||||
{
|
||||
bf_t a_s, *r, *a;
|
||||
int ret, v;
|
||||
JSValue res;
|
||||
if (op == OP_plus && !is_math_mode(ctx)) {
|
||||
JS_ThrowTypeError(ctx, "bigint argument with unary +");
|
||||
JS_FreeValue(ctx, op1);
|
||||
return -1;
|
||||
}
|
||||
res = JS_NewBigInt(ctx);
|
||||
if (JS_IsException(res)) {
|
||||
JS_FreeValue(ctx, op1);
|
||||
return -1;
|
||||
}
|
||||
r = JS_GetBigInt(res);
|
||||
a = JS_ToBigInt(ctx, &a_s, op1);
|
||||
ret = 0;
|
||||
switch(op) {
|
||||
case OP_inc:
|
||||
case OP_dec:
|
||||
v = 2 * (op - OP_dec) - 1;
|
||||
ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
|
||||
break;
|
||||
case OP_plus:
|
||||
ret = bf_set(r, a);
|
||||
break;
|
||||
case OP_neg:
|
||||
ret = bf_set(r, a);
|
||||
bf_neg(r);
|
||||
break;
|
||||
case OP_not:
|
||||
ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ);
|
||||
bf_neg(r);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
JS_FreeBigInt(ctx, a, &a_s);
|
||||
JS_FreeValue(ctx, op1);
|
||||
if (UNLIKELY(ret)) {
|
||||
JS_FreeValue(ctx, res);
|
||||
throw_bf_exception(ctx, ret);
|
||||
return -1;
|
||||
}
|
||||
res = JS_CompactBigInt(ctx, res);
|
||||
*pres = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* try to call the operation on the operatorSet field of 'obj'. Only
|
||||
used for "/" and "**" on the BigInt prototype in math mode */
|
||||
static __exception int js_call_binary_op_simple(JSContext *ctx,
|
||||
JSValue *pret,
|
||||
JSValueConst obj,
|
||||
JSValueConst op1,
|
||||
JSValueConst op2,
|
||||
OPCodeEnum op)
|
||||
{
|
||||
JSValue opset1_obj, method, ret, new_op1, new_op2;
|
||||
JSOperatorSetData *opset1;
|
||||
JSOverloadableOperatorEnum ovop;
|
||||
JSObject *p;
|
||||
JSValueConst args[2];
|
||||
opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
|
||||
if (JS_IsException(opset1_obj))
|
||||
goto exception;
|
||||
if (JS_IsUndefined(opset1_obj))
|
||||
return 0;
|
||||
opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
|
||||
if (!opset1)
|
||||
goto exception;
|
||||
ovop = get_ovop_from_opcode(op);
|
||||
p = opset1->self_ops[ovop];
|
||||
if (!p) {
|
||||
JS_FreeValue(ctx, opset1_obj);
|
||||
return 0;
|
||||
}
|
||||
new_op1 = JS_ToNumeric(ctx, op1);
|
||||
if (JS_IsException(new_op1))
|
||||
goto exception;
|
||||
new_op2 = JS_ToNumeric(ctx, op2);
|
||||
if (JS_IsException(new_op2)) {
|
||||
JS_FreeValue(ctx, new_op1);
|
||||
goto exception;
|
||||
}
|
||||
method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
|
||||
args[0] = new_op1;
|
||||
args[1] = new_op2;
|
||||
ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
|
||||
JS_FreeValue(ctx, new_op1);
|
||||
JS_FreeValue(ctx, new_op2);
|
||||
if (JS_IsException(ret))
|
||||
goto exception;
|
||||
JS_FreeValue(ctx, opset1_obj);
|
||||
*pret = ret;
|
||||
return 1;
|
||||
exception:
|
||||
JS_FreeValue(ctx, opset1_obj);
|
||||
*pret = JS_UNDEFINED;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,
|
||||
JSValue *pres, JSValue op1, JSValue op2)
|
||||
{
|
||||
bf_t a_s, b_s, *r, *a, *b;
|
||||
int ret;
|
||||
JSValue res;
|
||||
res = JS_NewBigInt(ctx);
|
||||
if (JS_IsException(res))
|
||||
goto fail;
|
||||
a = JS_ToBigInt(ctx, &a_s, op1);
|
||||
if (!a)
|
||||
goto fail;
|
||||
b = JS_ToBigInt(ctx, &b_s, op2);
|
||||
if (!b) {
|
||||
JS_FreeBigInt(ctx, a, &a_s);
|
||||
goto fail;
|
||||
}
|
||||
r = JS_GetBigInt(res);
|
||||
ret = 0;
|
||||
switch(op) {
|
||||
case OP_add:
|
||||
ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ);
|
||||
break;
|
||||
case OP_sub:
|
||||
ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
|
||||
break;
|
||||
case OP_mul:
|
||||
ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
|
||||
break;
|
||||
case OP_div:
|
||||
if (!is_math_mode(ctx)) {
|
||||
bf_t rem_s, *rem = &rem_s;
|
||||
bf_init(ctx->bf_ctx, rem);
|
||||
ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ,
|
||||
BF_RNDZ);
|
||||
bf_delete(rem);
|
||||
} else {
|
||||
goto math_mode_div_pow;
|
||||
}
|
||||
break;
|
||||
case OP_math_mod:
|
||||
/* Euclidian remainder */
|
||||
ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
|
||||
BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP;
|
||||
break;
|
||||
case OP_mod:
|
||||
ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
|
||||
BF_RNDZ) & BF_ST_INVALID_OP;
|
||||
break;
|
||||
case OP_pow:
|
||||
if (b->sign) {
|
||||
if (!is_math_mode(ctx)) {
|
||||
ret = BF_ST_INVALID_OP;
|
||||
} else {
|
||||
math_mode_div_pow:
|
||||
JS_FreeValue(ctx, res);
|
||||
ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op);
|
||||
if (ret != 0) {
|
||||
JS_FreeBigInt(ctx, a, &a_s);
|
||||
JS_FreeBigInt(ctx, b, &b_s);
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
*pres = res;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* if no BigInt power operator defined, return a
|
||||
bigfloat */
|
||||
res = JS_NewBigFloat(ctx);
|
||||
if (JS_IsException(res)) {
|
||||
JS_FreeBigInt(ctx, a, &a_s);
|
||||
JS_FreeBigInt(ctx, b, &b_s);
|
||||
goto fail;
|
||||
}
|
||||
r = JS_GetBigFloat(res);
|
||||
if (op == OP_div) {
|
||||
ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR;
|
||||
} else {
|
||||
ret = bf_pow(r, a, b, ctx->fp_env.prec,
|
||||
ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR;
|
||||
}
|
||||
JS_FreeBigInt(ctx, a, &a_s);
|
||||
JS_FreeBigInt(ctx, b, &b_s);
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
if (UNLIKELY(ret)) {
|
||||
JS_FreeValue(ctx, res);
|
||||
throw_bf_exception(ctx, ret);
|
||||
return -1;
|
||||
}
|
||||
*pres = res;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS);
|
||||
}
|
||||
break;
|
||||
/* logical operations */
|
||||
case OP_shl:
|
||||
case OP_sar:
|
||||
{
|
||||
slimb_t v2;
|
||||
#if LIMB_BITS == 32
|
||||
bf_get_int32(&v2, b, 0);
|
||||
if (v2 == INT32_MIN)
|
||||
v2 = INT32_MIN + 1;
|
||||
#else
|
||||
bf_get_int64(&v2, b, 0);
|
||||
if (v2 == INT64_MIN)
|
||||
v2 = INT64_MIN + 1;
|
||||
#endif
|
||||
if (op == OP_sar)
|
||||
v2 = -v2;
|
||||
ret = bf_set(r, a);
|
||||
ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ);
|
||||
if (v2 < 0) {
|
||||
ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_and:
|
||||
ret = bf_logic_and(r, a, b);
|
||||
break;
|
||||
case OP_or:
|
||||
ret = bf_logic_or(r, a, b);
|
||||
break;
|
||||
case OP_xor:
|
||||
ret = bf_logic_xor(r, a, b);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
JS_FreeBigInt(ctx, a, &a_s);
|
||||
JS_FreeBigInt(ctx, b, &b_s);
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
if (UNLIKELY(ret)) {
|
||||
JS_FreeValue(ctx, res);
|
||||
throw_bf_exception(ctx, ret);
|
||||
return -1;
|
||||
}
|
||||
*pres = JS_CompactBigInt(ctx, res);
|
||||
return 0;
|
||||
fail:
|
||||
JS_FreeValue(ctx, res);
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void JS_AddIntrinsicBigInt(JSContext *ctx)
|
||||
{
|
||||
JSRuntime *rt = ctx->rt;
|
||||
JSValueConst obj1;
|
||||
rt->bigint_ops.to_string = js_bigint_to_string;
|
||||
rt->bigint_ops.from_string = js_string_to_bigint;
|
||||
rt->bigint_ops.unary_arith = js_unary_arith_bigint;
|
||||
rt->bigint_ops.binary_arith = js_binary_arith_bigint;
|
||||
rt->bigint_ops.compare = js_compare_bigfloat;
|
||||
ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
|
||||
js_bigint_proto_funcs,
|
||||
countof(js_bigint_proto_funcs));
|
||||
obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1,
|
||||
ctx->class_proto[JS_CLASS_BIG_INT]);
|
||||
JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,
|
||||
countof(js_bigint_funcs));
|
||||
}
|
||||
|
||||
/* if the returned bigfloat is allocated it is equal to
|
||||
'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */
|
||||
static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
|
||||
{
|
||||
uint32_t tag;
|
||||
bf_t *r;
|
||||
JSBigFloat *p;
|
||||
|
||||
redo:
|
||||
tag = JS_VALUE_GET_NORM_TAG(val);
|
||||
switch(tag) {
|
||||
case JS_TAG_INT:
|
||||
case JS_TAG_NULL:
|
||||
case JS_TAG_UNDEFINED:
|
||||
if (!is_math_mode(ctx))
|
||||
goto fail;
|
||||
/* fall tru */
|
||||
case JS_TAG_BOOL:
|
||||
r = buf;
|
||||
bf_init(ctx->bf_ctx, r);
|
||||
bf_set_si(r, JS_VALUE_GET_INT(val));
|
||||
break;
|
||||
case JS_TAG_FLOAT64:
|
||||
{
|
||||
double d = JS_VALUE_GET_FLOAT64(val);
|
||||
if (!is_math_mode(ctx))
|
||||
goto fail;
|
||||
if (!isfinite(d))
|
||||
goto fail;
|
||||
r = buf;
|
||||
bf_init(ctx->bf_ctx, r);
|
||||
d = trunc(d);
|
||||
bf_set_float64(r, d);
|
||||
}
|
||||
break;
|
||||
case JS_TAG_BIG_INT:
|
||||
p = JS_VALUE_GET_PTR(val);
|
||||
r = &p->num;
|
||||
break;
|
||||
case JS_TAG_BIG_FLOAT:
|
||||
if (!is_math_mode(ctx))
|
||||
goto fail;
|
||||
p = JS_VALUE_GET_PTR(val);
|
||||
if (!bf_is_finite(&p->num))
|
||||
goto fail;
|
||||
r = buf;
|
||||
bf_init(ctx->bf_ctx, r);
|
||||
bf_set(r, &p->num);
|
||||
bf_rint(r, BF_RNDZ);
|
||||
JS_FreeValue(ctx, val);
|
||||
break;
|
||||
case JS_TAG_STRING:
|
||||
val = JS_StringToBigIntErr(ctx, val);
|
||||
if (JS_IsException(val))
|
||||
return NULL;
|
||||
goto redo;
|
||||
case JS_TAG_OBJECT:
|
||||
val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
|
||||
if (JS_IsException(val))
|
||||
return NULL;
|
||||
goto redo;
|
||||
default:
|
||||
fail:
|
||||
JS_FreeValue(ctx, val);
|
||||
JS_ThrowTypeError(ctx, "cannot convert to bigint");
|
||||
return NULL;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val)
|
||||
{
|
||||
return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val));
|
||||
}
|
||||
|
||||
static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val)
|
||||
{
|
||||
if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) {
|
||||
return val;
|
||||
} else {
|
||||
bf_t a_s, *a, *r;
|
||||
int ret;
|
||||
JSValue res;
|
||||
|
||||
res = JS_NewBigInt(ctx);
|
||||
if (JS_IsException(res))
|
||||
return JS_EXCEPTION;
|
||||
a = JS_ToBigIntFree(ctx, &a_s, val);
|
||||
if (!a) {
|
||||
JS_FreeValue(ctx, res);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
r = JS_GetBigInt(res);
|
||||
ret = bf_set(r, a);
|
||||
JS_FreeBigInt(ctx, a, &a_s);
|
||||
if (ret) {
|
||||
JS_FreeValue(ctx, res);
|
||||
return JS_ThrowOutOfMemory(ctx);
|
||||
}
|
||||
return JS_CompactBigInt(ctx, res);
|
||||
}
|
||||
}
|
||||
|
||||
/* free the bf_t allocated by JS_ToBigInt */
|
||||
void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf)
|
||||
{
|
||||
if (a == buf) {
|
||||
bf_delete(a);
|
||||
} else {
|
||||
JSBigFloat *p = (JSBigFloat *)((uint8_t *)a -
|
||||
offsetof(JSBigFloat, num));
|
||||
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_FLOAT, p));
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: merge with JS_ToInt64Free with a specific flag */
|
||||
int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
|
||||
{
|
||||
bf_t a_s, *a;
|
||||
a = JS_ToBigIntFree(ctx, &a_s, val);
|
||||
if (!a) {
|
||||
*pres = 0;
|
||||
return -1;
|
||||
}
|
||||
bf_get_int64(pres, a, BF_GET_INT_MOD);
|
||||
JS_FreeBigInt(ctx, a, &a_s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
|
||||
{
|
||||
return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));
|
||||
}
|
2238
third_party/quickjs/byte.c
vendored
Normal file
2238
third_party/quickjs/byte.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
4107
third_party/quickjs/call.c
vendored
Normal file
4107
third_party/quickjs/call.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
120
third_party/quickjs/cutils.c
vendored
120
third_party/quickjs/cutils.c
vendored
|
@ -87,126 +87,6 @@ int has_suffix(const char *str, const char *suffix)
|
|||
|
||||
/* Dynamic buffer package */
|
||||
|
||||
static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size)
|
||||
{
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
|
||||
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func)
|
||||
{
|
||||
memset(s, 0, sizeof(*s));
|
||||
if (!realloc_func)
|
||||
realloc_func = dbuf_default_realloc;
|
||||
s->opaque = opaque;
|
||||
s->realloc_func = realloc_func;
|
||||
}
|
||||
|
||||
void dbuf_init(DynBuf *s)
|
||||
{
|
||||
dbuf_init2(s, NULL, NULL);
|
||||
}
|
||||
|
||||
/* return < 0 if error */
|
||||
int dbuf_realloc(DynBuf *s, size_t new_size)
|
||||
{
|
||||
size_t size;
|
||||
uint8_t *new_buf;
|
||||
if (new_size > s->allocated_size) {
|
||||
if (s->error)
|
||||
return -1;
|
||||
size = s->allocated_size * 3 / 2;
|
||||
if (size > new_size)
|
||||
new_size = size;
|
||||
new_buf = s->realloc_func(s->opaque, s->buf, new_size);
|
||||
if (!new_buf) {
|
||||
s->error = TRUE;
|
||||
return -1;
|
||||
}
|
||||
s->buf = new_buf;
|
||||
s->allocated_size = new_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len)
|
||||
{
|
||||
size_t end;
|
||||
end = offset + len;
|
||||
if (dbuf_realloc(s, end))
|
||||
return -1;
|
||||
memcpy(s->buf + offset, data, len);
|
||||
if (end > s->size)
|
||||
s->size = end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len)
|
||||
{
|
||||
if (unlikely((s->size + len) > s->allocated_size)) {
|
||||
if (dbuf_realloc(s, s->size + len))
|
||||
return -1;
|
||||
}
|
||||
memcpy(s->buf + s->size, data, len);
|
||||
s->size += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbuf_put_self(DynBuf *s, size_t offset, size_t len)
|
||||
{
|
||||
if (unlikely((s->size + len) > s->allocated_size)) {
|
||||
if (dbuf_realloc(s, s->size + len))
|
||||
return -1;
|
||||
}
|
||||
memcpy(s->buf + s->size, s->buf + offset, len);
|
||||
s->size += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbuf_putc(DynBuf *s, uint8_t c)
|
||||
{
|
||||
return dbuf_put(s, &c, 1);
|
||||
}
|
||||
|
||||
int dbuf_putstr(DynBuf *s, const char *str)
|
||||
{
|
||||
return dbuf_put(s, (const uint8_t *)str, strlen(str));
|
||||
}
|
||||
|
||||
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[128];
|
||||
int len;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
if (len < sizeof(buf)) {
|
||||
/* fast case */
|
||||
return dbuf_put(s, (uint8_t *)buf, len);
|
||||
} else {
|
||||
if (dbuf_realloc(s, s->size + len + 1))
|
||||
return -1;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size,
|
||||
fmt, ap);
|
||||
va_end(ap);
|
||||
s->size += len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbuf_free(DynBuf *s)
|
||||
{
|
||||
/* we test s->buf as a fail safe to avoid crashing if dbuf_free()
|
||||
is called twice */
|
||||
if (s->buf) {
|
||||
s->realloc_func(s->opaque, s->buf, 0);
|
||||
}
|
||||
memset(s, 0, sizeof(*s));
|
||||
}
|
||||
|
||||
/* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes
|
||||
are output. */
|
||||
int unicode_to_utf8(uint8_t *buf, unsigned int c)
|
||||
|
|
70
third_party/quickjs/cutils.h
vendored
70
third_party/quickjs/cutils.h
vendored
|
@ -3,14 +3,12 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
/* clang-format off */
|
||||
#include "libc/bits/bswap.h"
|
||||
#include "libc/bits/bswap.h"
|
||||
|
||||
/* set if CPU is big endian */
|
||||
#undef WORDS_BIGENDIAN
|
||||
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#define force_inline forceinline
|
||||
#define no_inline noinline
|
||||
#define __maybe_unused __attribute__((__unused__))
|
||||
|
||||
#define xglue(x, y) x ## y
|
||||
|
@ -111,15 +109,15 @@ forceinline int ctz64(uint64_t a)
|
|||
return __builtin_ctzll(a);
|
||||
}
|
||||
|
||||
struct __attribute__((packed)) packed_u64 {
|
||||
struct thatispacked packed_u64 {
|
||||
uint64_t v;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) packed_u32 {
|
||||
struct thatispacked packed_u32 {
|
||||
uint32_t v;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) packed_u16 {
|
||||
struct thatispacked packed_u16 {
|
||||
uint16_t v;
|
||||
};
|
||||
|
||||
|
@ -185,25 +183,17 @@ static inline void put_u8(uint8_t *tab, uint8_t val)
|
|||
|
||||
forceinline uint16_t bswap16(uint16_t x)
|
||||
{
|
||||
return (x >> 8) | (x << 8);
|
||||
return bswap_16(x);
|
||||
}
|
||||
|
||||
forceinline uint32_t bswap32(uint32_t v)
|
||||
{
|
||||
return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |
|
||||
((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);
|
||||
return bswap_32(v);
|
||||
}
|
||||
|
||||
forceinline uint64_t bswap64(uint64_t v)
|
||||
{
|
||||
return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) |
|
||||
((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) |
|
||||
((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) |
|
||||
((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) |
|
||||
((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) |
|
||||
((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) |
|
||||
((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) |
|
||||
((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8));
|
||||
return bswap_64(v);
|
||||
}
|
||||
|
||||
/* XXX: should take an extra argument to pass slack information to the caller */
|
||||
|
@ -218,42 +208,42 @@ typedef struct DynBuf {
|
|||
void *opaque; /* for realloc_func */
|
||||
} DynBuf;
|
||||
|
||||
void dbuf_init(DynBuf *s);
|
||||
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func);
|
||||
int dbuf_realloc(DynBuf *s, size_t new_size);
|
||||
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len);
|
||||
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len);
|
||||
int dbuf_put_self(DynBuf *s, size_t offset, size_t len);
|
||||
int dbuf_putc(DynBuf *s, uint8_t c);
|
||||
int dbuf_putstr(DynBuf *s, const char *str);
|
||||
static inline int dbuf_put_u16(DynBuf *s, uint16_t val)
|
||||
{
|
||||
void dbuf_init(DynBuf *);
|
||||
void dbuf_init2(DynBuf *, void *, DynBufReallocFunc *);
|
||||
int dbuf_realloc(DynBuf *, size_t);
|
||||
int dbuf_write(DynBuf *, size_t, const uint8_t *, size_t);
|
||||
int dbuf_put(DynBuf *, const uint8_t *, size_t);
|
||||
int dbuf_put_self(DynBuf *, size_t, size_t);
|
||||
int dbuf_putc(DynBuf *, uint8_t);
|
||||
int dbuf_putstr(DynBuf *, const char *);
|
||||
int dbuf_printf(DynBuf *, const char *, ...) printfesque(2);
|
||||
void dbuf_free(DynBuf *);
|
||||
|
||||
int unicode_to_utf8(uint8_t *buf, unsigned int c);
|
||||
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp);
|
||||
|
||||
static inline int dbuf_put_u16(DynBuf *s, uint16_t val) {
|
||||
return dbuf_put(s, (uint8_t *)&val, 2);
|
||||
}
|
||||
static inline int dbuf_put_u32(DynBuf *s, uint32_t val)
|
||||
{
|
||||
|
||||
static inline int dbuf_put_u32(DynBuf *s, uint32_t val) {
|
||||
return dbuf_put(s, (uint8_t *)&val, 4);
|
||||
}
|
||||
static inline int dbuf_put_u64(DynBuf *s, uint64_t val)
|
||||
{
|
||||
|
||||
static inline int dbuf_put_u64(DynBuf *s, uint64_t val) {
|
||||
return dbuf_put(s, (uint8_t *)&val, 8);
|
||||
}
|
||||
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
|
||||
const char *fmt, ...);
|
||||
void dbuf_free(DynBuf *s);
|
||||
|
||||
static inline BOOL dbuf_error(DynBuf *s) {
|
||||
return s->error;
|
||||
}
|
||||
static inline void dbuf_set_error(DynBuf *s)
|
||||
{
|
||||
|
||||
static inline void dbuf_set_error(DynBuf *s) {
|
||||
s->error = TRUE;
|
||||
}
|
||||
|
||||
#define UTF8_CHAR_LEN_MAX 6
|
||||
|
||||
int unicode_to_utf8(uint8_t *buf, unsigned int c);
|
||||
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp);
|
||||
|
||||
static inline int from_hex(int c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
|
|
1034
third_party/quickjs/date.c
vendored
Normal file
1034
third_party/quickjs/date.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
175
third_party/quickjs/dbuf.c
vendored
Normal file
175
third_party/quickjs/dbuf.c
vendored
Normal file
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
#include "third_party/quickjs/libregexp.h"
|
||||
#include "third_party/quickjs/quickjs.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size)
|
||||
{
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
|
||||
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func)
|
||||
{
|
||||
memset(s, 0, sizeof(*s));
|
||||
if (!realloc_func)
|
||||
realloc_func = dbuf_default_realloc;
|
||||
s->opaque = opaque;
|
||||
s->realloc_func = realloc_func;
|
||||
}
|
||||
|
||||
void dbuf_init(DynBuf *s)
|
||||
{
|
||||
dbuf_init2(s, NULL, NULL);
|
||||
}
|
||||
|
||||
/* return < 0 if error */
|
||||
int dbuf_realloc(DynBuf *s, size_t new_size)
|
||||
{
|
||||
size_t size;
|
||||
uint8_t *new_buf;
|
||||
if (new_size > s->allocated_size) {
|
||||
if (s->error)
|
||||
return -1;
|
||||
size = s->allocated_size * 3 / 2;
|
||||
if (size > new_size)
|
||||
new_size = size;
|
||||
new_buf = s->realloc_func(s->opaque, s->buf, new_size);
|
||||
if (!new_buf) {
|
||||
s->error = TRUE;
|
||||
return -1;
|
||||
}
|
||||
s->buf = new_buf;
|
||||
s->allocated_size = new_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len)
|
||||
{
|
||||
size_t end;
|
||||
end = offset + len;
|
||||
if (dbuf_realloc(s, end))
|
||||
return -1;
|
||||
memcpy(s->buf + offset, data, len);
|
||||
if (end > s->size)
|
||||
s->size = end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len)
|
||||
{
|
||||
if (UNLIKELY((s->size + len) > s->allocated_size)) {
|
||||
if (dbuf_realloc(s, s->size + len))
|
||||
return -1;
|
||||
}
|
||||
memcpy(s->buf + s->size, data, len);
|
||||
s->size += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbuf_put_self(DynBuf *s, size_t offset, size_t len)
|
||||
{
|
||||
if (UNLIKELY((s->size + len) > s->allocated_size)) {
|
||||
if (dbuf_realloc(s, s->size + len))
|
||||
return -1;
|
||||
}
|
||||
memcpy(s->buf + s->size, s->buf + offset, len);
|
||||
s->size += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbuf_putc(DynBuf *s, uint8_t c)
|
||||
{
|
||||
return dbuf_put(s, &c, 1);
|
||||
}
|
||||
|
||||
int dbuf_putstr(DynBuf *s, const char *str)
|
||||
{
|
||||
return dbuf_put(s, (const uint8_t *)str, strlen(str));
|
||||
}
|
||||
|
||||
int dbuf_printf(DynBuf *s, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[128];
|
||||
int len;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
if (len < sizeof(buf)) {
|
||||
/* fast case */
|
||||
return dbuf_put(s, (uint8_t *)buf, len);
|
||||
} else {
|
||||
if (dbuf_realloc(s, s->size + len + 1))
|
||||
return -1;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size,
|
||||
fmt, ap);
|
||||
va_end(ap);
|
||||
s->size += len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbuf_free(DynBuf *s)
|
||||
{
|
||||
/* we test s->buf as a fail safe to avoid crashing if dbuf_free()
|
||||
is called twice */
|
||||
if (s->buf) {
|
||||
s->realloc_func(s->opaque, s->buf, 0);
|
||||
}
|
||||
memset(s, 0, sizeof(*s));
|
||||
}
|
||||
|
||||
void dbuf_put_leb128(DynBuf *s, uint32_t v)
|
||||
{
|
||||
uint32_t a;
|
||||
for(;;) {
|
||||
a = v & 0x7f;
|
||||
v >>= 7;
|
||||
if (v != 0) {
|
||||
dbuf_putc(s, a | 0x80);
|
||||
} else {
|
||||
dbuf_putc(s, a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dbuf_put_sleb128(DynBuf *s, int32_t v1)
|
||||
{
|
||||
uint32_t v = v1;
|
||||
dbuf_put_leb128(s, (2 * v) ^ -(v >> 31));
|
||||
}
|
|
@ -16,24 +16,15 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "third_party/mbedtls/bignum_internal.h"
|
||||
#include "third_party/mbedtls/platform.h"
|
||||
#include "third_party/quickjs/diglet.h"
|
||||
|
||||
void ShiftRightPure(mbedtls_mpi_uint *p, size_t n, unsigned char k) {
|
||||
mbedtls_mpi_uint x, y, *e, *f;
|
||||
MBEDTLS_ASSERT(!(k & ~63));
|
||||
f = p;
|
||||
if (n) {
|
||||
y = 0;
|
||||
x = p[0];
|
||||
e = p + n;
|
||||
for (; ++p < e; x = y) {
|
||||
y = p[0];
|
||||
p[-1] = x >> 1 | y << (64 - 1);
|
||||
}
|
||||
p[-1] = x >> 1;
|
||||
}
|
||||
while (p < f) {
|
||||
*p++ = 0;
|
||||
}
|
||||
int to_digit(int c) {
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
else if (c >= 'A' && c <= 'Z')
|
||||
return c - 'A' + 10;
|
||||
else if (c >= 'a' && c <= 'z')
|
||||
return c - 'a' + 10;
|
||||
else
|
||||
return 36;
|
||||
}
|
10
third_party/quickjs/diglet.h
vendored
Normal file
10
third_party/quickjs/diglet.h
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
#ifndef COSMOPOLITAN_THIRD_PARTY_QUICKJS_DIGLET_H_
|
||||
#define COSMOPOLITAN_THIRD_PARTY_QUICKJS_DIGLET_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
int to_digit(int);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_QUICKJS_DIGLET_H_ */
|
459
third_party/quickjs/eq.c
vendored
Normal file
459
third_party/quickjs/eq.c
vendored
Normal file
|
@ -0,0 +1,459 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "third_party/quickjs/internal.h"
|
||||
#include "third_party/quickjs/libregexp.h"
|
||||
#include "third_party/quickjs/quickjs.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
/* XXX: Should take JSValueConst arguments */
|
||||
BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
|
||||
JSStrictEqModeEnum eq_mode)
|
||||
{
|
||||
BOOL res;
|
||||
int tag1, tag2;
|
||||
double d1, d2;
|
||||
tag1 = JS_VALUE_GET_NORM_TAG(op1);
|
||||
tag2 = JS_VALUE_GET_NORM_TAG(op2);
|
||||
switch(tag1) {
|
||||
case JS_TAG_BOOL:
|
||||
if (tag1 != tag2) {
|
||||
res = FALSE;
|
||||
} else {
|
||||
res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
|
||||
goto done_no_free;
|
||||
}
|
||||
break;
|
||||
case JS_TAG_NULL:
|
||||
case JS_TAG_UNDEFINED:
|
||||
res = (tag1 == tag2);
|
||||
break;
|
||||
case JS_TAG_STRING:
|
||||
{
|
||||
JSString *p1, *p2;
|
||||
if (tag1 != tag2) {
|
||||
res = FALSE;
|
||||
} else {
|
||||
p1 = JS_VALUE_GET_STRING(op1);
|
||||
p2 = JS_VALUE_GET_STRING(op2);
|
||||
res = (js_string_compare(ctx, p1, p2) == 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_TAG_SYMBOL:
|
||||
{
|
||||
JSAtomStruct *p1, *p2;
|
||||
if (tag1 != tag2) {
|
||||
res = FALSE;
|
||||
} else {
|
||||
p1 = JS_VALUE_GET_PTR(op1);
|
||||
p2 = JS_VALUE_GET_PTR(op2);
|
||||
res = (p1 == p2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_TAG_OBJECT:
|
||||
if (tag1 != tag2)
|
||||
res = FALSE;
|
||||
else
|
||||
res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2);
|
||||
break;
|
||||
case JS_TAG_INT:
|
||||
d1 = JS_VALUE_GET_INT(op1);
|
||||
if (tag2 == JS_TAG_INT) {
|
||||
d2 = JS_VALUE_GET_INT(op2);
|
||||
goto number_test;
|
||||
} else if (tag2 == JS_TAG_FLOAT64) {
|
||||
d2 = JS_VALUE_GET_FLOAT64(op2);
|
||||
goto number_test;
|
||||
} else {
|
||||
res = FALSE;
|
||||
}
|
||||
break;
|
||||
case JS_TAG_FLOAT64:
|
||||
d1 = JS_VALUE_GET_FLOAT64(op1);
|
||||
if (tag2 == JS_TAG_FLOAT64) {
|
||||
d2 = JS_VALUE_GET_FLOAT64(op2);
|
||||
} else if (tag2 == JS_TAG_INT) {
|
||||
d2 = JS_VALUE_GET_INT(op2);
|
||||
} else {
|
||||
res = FALSE;
|
||||
break;
|
||||
}
|
||||
number_test:
|
||||
if (UNLIKELY(eq_mode >= JS_EQ_SAME_VALUE)) {
|
||||
JSFloat64Union u1, u2;
|
||||
/* NaN is not always normalized, so this test is necessary */
|
||||
if (isnan(d1) || isnan(d2)) {
|
||||
res = isnan(d1) == isnan(d2);
|
||||
} else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) {
|
||||
res = (d1 == d2); /* +0 == -0 */
|
||||
} else {
|
||||
u1.d = d1;
|
||||
u2.d = d2;
|
||||
res = (u1.u64 == u2.u64); /* +0 != -0 */
|
||||
}
|
||||
} else {
|
||||
res = (d1 == d2); /* if NaN return false and +0 == -0 */
|
||||
}
|
||||
goto done_no_free;
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_TAG_BIG_INT:
|
||||
{
|
||||
bf_t a_s, *a, b_s, *b;
|
||||
if (tag1 != tag2) {
|
||||
res = FALSE;
|
||||
break;
|
||||
}
|
||||
a = JS_ToBigFloat(ctx, &a_s, op1);
|
||||
b = JS_ToBigFloat(ctx, &b_s, op2);
|
||||
res = bf_cmp_eq(a, b);
|
||||
if (a == &a_s)
|
||||
bf_delete(a);
|
||||
if (b == &b_s)
|
||||
bf_delete(b);
|
||||
}
|
||||
break;
|
||||
case JS_TAG_BIG_FLOAT:
|
||||
{
|
||||
JSBigFloat *p1, *p2;
|
||||
const bf_t *a, *b;
|
||||
if (tag1 != tag2) {
|
||||
res = FALSE;
|
||||
break;
|
||||
}
|
||||
p1 = JS_VALUE_GET_PTR(op1);
|
||||
p2 = JS_VALUE_GET_PTR(op2);
|
||||
a = &p1->num;
|
||||
b = &p2->num;
|
||||
if (UNLIKELY(eq_mode >= JS_EQ_SAME_VALUE)) {
|
||||
if (eq_mode == JS_EQ_SAME_VALUE_ZERO &&
|
||||
a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) {
|
||||
res = TRUE;
|
||||
} else {
|
||||
res = (bf_cmp_full(a, b) == 0);
|
||||
}
|
||||
} else {
|
||||
res = bf_cmp_eq(a, b);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_TAG_BIG_DECIMAL:
|
||||
{
|
||||
JSBigDecimal *p1, *p2;
|
||||
const bfdec_t *a, *b;
|
||||
if (tag1 != tag2) {
|
||||
res = FALSE;
|
||||
break;
|
||||
}
|
||||
p1 = JS_VALUE_GET_PTR(op1);
|
||||
p2 = JS_VALUE_GET_PTR(op2);
|
||||
a = &p1->num;
|
||||
b = &p2->num;
|
||||
res = bfdec_cmp_eq(a, b);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
res = FALSE;
|
||||
break;
|
||||
}
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
done_no_free:
|
||||
return res;
|
||||
}
|
||||
|
||||
BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2)
|
||||
{
|
||||
return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
|
||||
}
|
||||
|
||||
BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2)
|
||||
{
|
||||
return js_strict_eq2(ctx,
|
||||
JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
|
||||
JS_EQ_SAME_VALUE);
|
||||
}
|
||||
|
||||
BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2)
|
||||
{
|
||||
return js_strict_eq2(ctx,
|
||||
JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
|
||||
JS_EQ_SAME_VALUE_ZERO);
|
||||
}
|
||||
|
||||
int js_strict_eq_slow(JSContext *ctx, JSValue *sp, BOOL is_neq)
|
||||
{
|
||||
BOOL res;
|
||||
res = js_strict_eq(ctx, sp[-2], sp[-1]);
|
||||
sp[-2] = JS_NewBool(ctx, res ^ is_neq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL tag_is_number(uint32_t tag)
|
||||
{
|
||||
return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT ||
|
||||
tag == JS_TAG_FLOAT64 || tag == JS_TAG_BIG_FLOAT ||
|
||||
tag == JS_TAG_BIG_DECIMAL);
|
||||
}
|
||||
|
||||
static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj)
|
||||
{
|
||||
JSObject *p;
|
||||
if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
|
||||
return FALSE;
|
||||
p = JS_VALUE_GET_OBJ(obj);
|
||||
return p->is_HTMLDDA;
|
||||
}
|
||||
|
||||
int js_eq_slow(JSContext *ctx, JSValue *sp, BOOL is_neq)
|
||||
{
|
||||
#ifdef CONFIG_BIGNUM
|
||||
JSValue op1, op2, ret;
|
||||
int res;
|
||||
uint32_t tag1, tag2;
|
||||
op1 = sp[-2];
|
||||
op2 = sp[-1];
|
||||
redo:
|
||||
tag1 = JS_VALUE_GET_NORM_TAG(op1);
|
||||
tag2 = JS_VALUE_GET_NORM_TAG(op2);
|
||||
if (tag_is_number(tag1) && tag_is_number(tag2)) {
|
||||
if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
|
||||
res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
|
||||
} else if ((tag1 == JS_TAG_FLOAT64 &&
|
||||
(tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) ||
|
||||
(tag2 == JS_TAG_FLOAT64 &&
|
||||
(tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) {
|
||||
double d1, d2;
|
||||
if (tag1 == JS_TAG_FLOAT64) {
|
||||
d1 = JS_VALUE_GET_FLOAT64(op1);
|
||||
} else {
|
||||
d1 = JS_VALUE_GET_INT(op1);
|
||||
}
|
||||
if (tag2 == JS_TAG_FLOAT64) {
|
||||
d2 = JS_VALUE_GET_FLOAT64(op2);
|
||||
} else {
|
||||
d2 = JS_VALUE_GET_INT(op2);
|
||||
}
|
||||
res = (d1 == d2);
|
||||
} else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
|
||||
res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2);
|
||||
if (res < 0)
|
||||
goto exception;
|
||||
} else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
|
||||
res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2);
|
||||
if (res < 0)
|
||||
goto exception;
|
||||
} else {
|
||||
res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2);
|
||||
if (res < 0)
|
||||
goto exception;
|
||||
}
|
||||
} else if (tag1 == tag2) {
|
||||
if (tag1 == JS_TAG_OBJECT) {
|
||||
/* try the fallback operator */
|
||||
res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
|
||||
is_neq ? OP_neq : OP_eq,
|
||||
FALSE, HINT_NONE);
|
||||
if (res != 0) {
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
if (res < 0) {
|
||||
goto exception;
|
||||
} else {
|
||||
sp[-2] = ret;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
|
||||
} else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
|
||||
(tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
|
||||
res = TRUE;
|
||||
} else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) ||
|
||||
(tag2 == JS_TAG_STRING && tag_is_number(tag1))) {
|
||||
if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) &&
|
||||
!is_math_mode(ctx)) {
|
||||
if (tag1 == JS_TAG_STRING) {
|
||||
op1 = JS_StringToBigInt(ctx, op1);
|
||||
if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
|
||||
goto invalid_bigint_string;
|
||||
}
|
||||
if (tag2 == JS_TAG_STRING) {
|
||||
op2 = JS_StringToBigInt(ctx, op2);
|
||||
if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
|
||||
invalid_bigint_string:
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
res = FALSE;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
op1 = JS_ToNumericFree(ctx, op1);
|
||||
if (JS_IsException(op1)) {
|
||||
JS_FreeValue(ctx, op2);
|
||||
goto exception;
|
||||
}
|
||||
op2 = JS_ToNumericFree(ctx, op2);
|
||||
if (JS_IsException(op2)) {
|
||||
JS_FreeValue(ctx, op1);
|
||||
goto exception;
|
||||
}
|
||||
}
|
||||
res = js_strict_eq(ctx, op1, op2);
|
||||
} else if (tag1 == JS_TAG_BOOL) {
|
||||
op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
|
||||
goto redo;
|
||||
} else if (tag2 == JS_TAG_BOOL) {
|
||||
op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
|
||||
goto redo;
|
||||
} else if ((tag1 == JS_TAG_OBJECT &&
|
||||
(tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) ||
|
||||
(tag2 == JS_TAG_OBJECT &&
|
||||
(tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) {
|
||||
/* try the fallback operator */
|
||||
res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
|
||||
is_neq ? OP_neq : OP_eq,
|
||||
FALSE, HINT_NONE);
|
||||
if (res != 0) {
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
if (res < 0) {
|
||||
goto exception;
|
||||
} else {
|
||||
sp[-2] = ret;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
|
||||
if (JS_IsException(op1)) {
|
||||
JS_FreeValue(ctx, op2);
|
||||
goto exception;
|
||||
}
|
||||
op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
|
||||
if (JS_IsException(op2)) {
|
||||
JS_FreeValue(ctx, op1);
|
||||
goto exception;
|
||||
}
|
||||
goto redo;
|
||||
} else {
|
||||
/* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
|
||||
if ((JS_IsHTMLDDA(ctx, op1) &&
|
||||
(tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
|
||||
(JS_IsHTMLDDA(ctx, op2) &&
|
||||
(tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
|
||||
res = TRUE;
|
||||
} else {
|
||||
res = FALSE;
|
||||
}
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
}
|
||||
done:
|
||||
sp[-2] = JS_NewBool(ctx, res ^ is_neq);
|
||||
return 0;
|
||||
exception:
|
||||
sp[-2] = JS_UNDEFINED;
|
||||
sp[-1] = JS_UNDEFINED;
|
||||
return -1;
|
||||
#else /* CONFIG_BIGNUM */
|
||||
JSValue op1, op2;
|
||||
int tag1, tag2;
|
||||
BOOL res;
|
||||
op1 = sp[-2];
|
||||
op2 = sp[-1];
|
||||
redo:
|
||||
tag1 = JS_VALUE_GET_NORM_TAG(op1);
|
||||
tag2 = JS_VALUE_GET_NORM_TAG(op2);
|
||||
if (tag1 == tag2 ||
|
||||
(tag1 == JS_TAG_INT && tag2 == JS_TAG_FLOAT64) ||
|
||||
(tag2 == JS_TAG_INT && tag1 == JS_TAG_FLOAT64)) {
|
||||
res = js_strict_eq(ctx, op1, op2);
|
||||
} else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
|
||||
(tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
|
||||
res = TRUE;
|
||||
} else if ((tag1 == JS_TAG_STRING && (tag2 == JS_TAG_INT ||
|
||||
tag2 == JS_TAG_FLOAT64)) ||
|
||||
(tag2 == JS_TAG_STRING && (tag1 == JS_TAG_INT ||
|
||||
tag1 == JS_TAG_FLOAT64))) {
|
||||
double d1;
|
||||
double d2;
|
||||
if (JS_ToFloat64Free(ctx, &d1, op1)) {
|
||||
JS_FreeValue(ctx, op2);
|
||||
goto exception;
|
||||
}
|
||||
if (JS_ToFloat64Free(ctx, &d2, op2))
|
||||
goto exception;
|
||||
res = (d1 == d2);
|
||||
} else if (tag1 == JS_TAG_BOOL) {
|
||||
op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
|
||||
goto redo;
|
||||
} else if (tag2 == JS_TAG_BOOL) {
|
||||
op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
|
||||
goto redo;
|
||||
} else if (tag1 == JS_TAG_OBJECT &&
|
||||
(tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64 || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) {
|
||||
op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
|
||||
if (JS_IsException(op1)) {
|
||||
JS_FreeValue(ctx, op2);
|
||||
goto exception;
|
||||
}
|
||||
goto redo;
|
||||
} else if (tag2 == JS_TAG_OBJECT &&
|
||||
(tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64 || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL)) {
|
||||
op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
|
||||
if (JS_IsException(op2)) {
|
||||
JS_FreeValue(ctx, op1);
|
||||
goto exception;
|
||||
}
|
||||
goto redo;
|
||||
} else {
|
||||
/* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
|
||||
if ((JS_IsHTMLDDA(ctx, op1) &&
|
||||
(tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
|
||||
(JS_IsHTMLDDA(ctx, op2) &&
|
||||
(tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
|
||||
res = TRUE;
|
||||
} else {
|
||||
res = FALSE;
|
||||
}
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
}
|
||||
sp[-2] = JS_NewBool(ctx, res ^ is_neq);
|
||||
return 0;
|
||||
exception:
|
||||
sp[-2] = JS_UNDEFINED;
|
||||
sp[-1] = JS_UNDEFINED;
|
||||
return -1;
|
||||
#endif /* CONFIG_BIGNUM */
|
||||
}
|
418
third_party/quickjs/err.c
vendored
Normal file
418
third_party/quickjs/err.c
vendored
Normal file
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
#include "third_party/quickjs/leb128.h"
|
||||
#include "third_party/quickjs/libregexp.h"
|
||||
#include "third_party/quickjs/quickjs.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
int js_parse_error(JSParseState *s, const char *fmt, ...)
|
||||
{
|
||||
JSContext *ctx = s->ctx;
|
||||
va_list ap;
|
||||
int backtrace_flags;
|
||||
va_start(ap, fmt);
|
||||
JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE);
|
||||
va_end(ap);
|
||||
backtrace_flags = 0;
|
||||
if (s->cur_func && s->cur_func->backtrace_barrier)
|
||||
backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
|
||||
build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num,
|
||||
backtrace_flags);
|
||||
return -1;
|
||||
}
|
||||
|
||||
JSValue JS_NewError(JSContext *ctx)
|
||||
{
|
||||
return JS_NewObjectClass(ctx, JS_CLASS_ERROR);
|
||||
}
|
||||
|
||||
JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, const char *fmt, va_list ap, BOOL add_backtrace)
|
||||
{
|
||||
char buf[256];
|
||||
JSValue obj, ret;
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num],
|
||||
JS_CLASS_ERROR);
|
||||
if (UNLIKELY(JS_IsException(obj))) {
|
||||
/* out of memory: throw JS_NULL to avoid recursing */
|
||||
obj = JS_NULL;
|
||||
} else {
|
||||
JS_DefinePropertyValue(ctx, obj, JS_ATOM_message,
|
||||
JS_NewString(ctx, buf),
|
||||
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
|
||||
}
|
||||
if (add_backtrace) {
|
||||
build_backtrace(ctx, obj, NULL, 0, 0);
|
||||
}
|
||||
ret = JS_Throw(ctx, obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, const char *fmt, va_list ap)
|
||||
{
|
||||
JSRuntime *rt = ctx->rt;
|
||||
JSStackFrame *sf;
|
||||
BOOL add_backtrace;
|
||||
/* the backtrace is added later if called from a bytecode function */
|
||||
sf = rt->current_stack_frame;
|
||||
add_backtrace = !rt->in_out_of_memory &&
|
||||
(!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL));
|
||||
return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace);
|
||||
}
|
||||
|
||||
JSValue JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)
|
||||
{
|
||||
JSValue val;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap);
|
||||
va_end(ap);
|
||||
return val;
|
||||
}
|
||||
|
||||
JSValue JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)
|
||||
{
|
||||
JSValue val;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
|
||||
va_end(ap);
|
||||
return val;
|
||||
}
|
||||
|
||||
int JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
if ((flags & JS_PROP_THROW) ||
|
||||
((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
|
||||
va_start(ap, fmt);
|
||||
JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
|
||||
va_end(ap);
|
||||
return -1;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* never use it directly */
|
||||
JSValue __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
|
||||
{
|
||||
char buf[ATOM_GET_STR_BUF_SIZE];
|
||||
return JS_ThrowTypeError(ctx, fmt,
|
||||
JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
|
||||
}
|
||||
|
||||
/* never use it directly */
|
||||
JSValue __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
|
||||
{
|
||||
char buf[ATOM_GET_STR_BUF_SIZE];
|
||||
return JS_ThrowSyntaxError(ctx, fmt,
|
||||
JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
|
||||
}
|
||||
|
||||
int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)
|
||||
{
|
||||
if ((flags & JS_PROP_THROW) ||
|
||||
((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
|
||||
JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom);
|
||||
return -1;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
JSValue JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)
|
||||
{
|
||||
JSValue val;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap);
|
||||
va_end(ap);
|
||||
return val;
|
||||
}
|
||||
|
||||
JSValue JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)
|
||||
{
|
||||
JSValue val;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap);
|
||||
va_end(ap);
|
||||
return val;
|
||||
}
|
||||
|
||||
JSValue JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)
|
||||
{
|
||||
JSValue val;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap);
|
||||
va_end(ap);
|
||||
return val;
|
||||
}
|
||||
|
||||
JSValue JS_ThrowOutOfMemory(JSContext *ctx)
|
||||
{
|
||||
JSRuntime *rt = ctx->rt;
|
||||
if (!rt->in_out_of_memory) {
|
||||
rt->in_out_of_memory = TRUE;
|
||||
JS_ThrowInternalError(ctx, "out of memory");
|
||||
rt->in_out_of_memory = FALSE;
|
||||
}
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
JSValue JS_ThrowStackOverflow(JSContext *ctx)
|
||||
{
|
||||
return JS_ThrowInternalError(ctx, "stack overflow");
|
||||
}
|
||||
|
||||
JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx)
|
||||
{
|
||||
return JS_ThrowTypeError(ctx, "not an object");
|
||||
}
|
||||
|
||||
JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx)
|
||||
{
|
||||
return JS_ThrowTypeError(ctx, "not a symbol");
|
||||
}
|
||||
|
||||
int js_throw_URIError(JSContext *ctx, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap);
|
||||
va_end(ap);
|
||||
return -1;
|
||||
}
|
||||
|
||||
JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name)
|
||||
{
|
||||
char buf[ATOM_GET_STR_BUF_SIZE];
|
||||
return JS_ThrowReferenceError(ctx, "'%s' is not defined",
|
||||
JS_AtomGetStr(ctx, buf, sizeof(buf), name));
|
||||
}
|
||||
|
||||
JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name)
|
||||
{
|
||||
char buf[ATOM_GET_STR_BUF_SIZE];
|
||||
return JS_ThrowReferenceError(ctx, "%s is not initialized",
|
||||
name == JS_ATOM_NULL ? "lexical variable" :
|
||||
JS_AtomGetStr(ctx, buf, sizeof(buf), name));
|
||||
}
|
||||
|
||||
JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx,
|
||||
JSFunctionBytecode *b,
|
||||
int idx, BOOL is_ref)
|
||||
{
|
||||
JSAtom atom = JS_ATOM_NULL;
|
||||
if (is_ref) {
|
||||
atom = b->closure_var[idx].var_name;
|
||||
} else {
|
||||
/* not present if the function is stripped and contains no eval() */
|
||||
if (b->vardefs)
|
||||
atom = b->vardefs[b->arg_count + idx].var_name;
|
||||
}
|
||||
return JS_ThrowReferenceErrorUninitialized(ctx, atom);
|
||||
}
|
||||
|
||||
JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop)
|
||||
{
|
||||
return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop);
|
||||
}
|
||||
|
||||
JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom)
|
||||
{
|
||||
return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist",
|
||||
atom);
|
||||
}
|
||||
|
||||
JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx)
|
||||
{
|
||||
return JS_ThrowTypeError(ctx, "ArrayBuffer is detached");
|
||||
}
|
||||
|
||||
/* in order to avoid executing arbitrary code during the stack trace
|
||||
generation, we only look at simple 'name' properties containing a
|
||||
string. */
|
||||
static const char *get_func_name(JSContext *ctx, JSValueConst func)
|
||||
{
|
||||
JSProperty *pr;
|
||||
JSShapeProperty *prs;
|
||||
JSValueConst val;
|
||||
if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)
|
||||
return NULL;
|
||||
prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name);
|
||||
if (!prs)
|
||||
return NULL;
|
||||
if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
|
||||
return NULL;
|
||||
val = pr->u.value;
|
||||
if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
|
||||
return NULL;
|
||||
return JS_ToCString(ctx, val);
|
||||
}
|
||||
|
||||
int find_line_num(JSContext *ctx, JSFunctionBytecode *b, uint32_t pc_value)
|
||||
{
|
||||
const uint8_t *p_end, *p;
|
||||
int new_line_num, line_num, pc, v, ret;
|
||||
unsigned int op;
|
||||
if (!b->has_debug || !b->debug.pc2line_buf) {
|
||||
/* function was stripped */
|
||||
return -1;
|
||||
}
|
||||
p = b->debug.pc2line_buf;
|
||||
p_end = p + b->debug.pc2line_len;
|
||||
pc = 0;
|
||||
line_num = b->debug.line_num;
|
||||
while (p < p_end) {
|
||||
op = *p++;
|
||||
if (op == 0) {
|
||||
uint32_t val;
|
||||
ret = get_leb128(&val, p, p_end);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
pc += val;
|
||||
p += ret;
|
||||
ret = get_sleb128(&v, p, p_end);
|
||||
if (ret < 0) {
|
||||
fail:
|
||||
/* should never happen */
|
||||
return b->debug.line_num;
|
||||
}
|
||||
p += ret;
|
||||
new_line_num = line_num + v;
|
||||
} else {
|
||||
op -= PC2LINE_OP_FIRST;
|
||||
pc += (op / PC2LINE_RANGE);
|
||||
new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE;
|
||||
}
|
||||
if (pc_value < pc)
|
||||
return line_num;
|
||||
line_num = new_line_num;
|
||||
}
|
||||
return line_num;
|
||||
}
|
||||
|
||||
/* if filename != NULL, an additional level is added with the filename
|
||||
and line number information (used for parse error). */
|
||||
void build_backtrace(JSContext *ctx, JSValueConst error_obj,
|
||||
const char *filename, int line_num,
|
||||
int backtrace_flags)
|
||||
{
|
||||
JSStackFrame *sf;
|
||||
JSValue str;
|
||||
DynBuf dbuf;
|
||||
const char *func_name_str;
|
||||
const char *str1;
|
||||
JSObject *p;
|
||||
BOOL backtrace_barrier;
|
||||
js_dbuf_init(ctx, &dbuf);
|
||||
if (filename) {
|
||||
dbuf_printf(&dbuf, " at %s", filename);
|
||||
if (line_num != -1)
|
||||
dbuf_printf(&dbuf, ":%d", line_num);
|
||||
dbuf_putc(&dbuf, '\n');
|
||||
str = JS_NewString(ctx, filename);
|
||||
JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str,
|
||||
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
|
||||
JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num),
|
||||
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
|
||||
if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)
|
||||
goto done;
|
||||
}
|
||||
for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {
|
||||
if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {
|
||||
backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
|
||||
continue;
|
||||
}
|
||||
func_name_str = get_func_name(ctx, sf->cur_func);
|
||||
if (!func_name_str || func_name_str[0] == '\0')
|
||||
str1 = "<anonymous>";
|
||||
else
|
||||
str1 = func_name_str;
|
||||
dbuf_printf(&dbuf, " at %s", str1);
|
||||
JS_FreeCString(ctx, func_name_str);
|
||||
p = JS_VALUE_GET_OBJ(sf->cur_func);
|
||||
backtrace_barrier = FALSE;
|
||||
if (js_class_has_bytecode(p->class_id)) {
|
||||
JSFunctionBytecode *b;
|
||||
const char *atom_str;
|
||||
int line_num1;
|
||||
b = p->u.func.function_bytecode;
|
||||
backtrace_barrier = b->backtrace_barrier;
|
||||
if (b->has_debug) {
|
||||
line_num1 = find_line_num(ctx, b,
|
||||
sf->cur_pc - b->byte_code_buf - 1);
|
||||
atom_str = JS_AtomToCString(ctx, b->debug.filename);
|
||||
dbuf_printf(&dbuf, " (%s",
|
||||
atom_str ? atom_str : "<null>");
|
||||
JS_FreeCString(ctx, atom_str);
|
||||
if (line_num1 != -1)
|
||||
dbuf_printf(&dbuf, ":%d", line_num1);
|
||||
dbuf_putc(&dbuf, ')');
|
||||
}
|
||||
} else {
|
||||
dbuf_printf(&dbuf, " (native)");
|
||||
}
|
||||
dbuf_putc(&dbuf, '\n');
|
||||
/* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */
|
||||
if (backtrace_barrier)
|
||||
break;
|
||||
}
|
||||
done:
|
||||
dbuf_putc(&dbuf, '\0');
|
||||
if (dbuf_error(&dbuf))
|
||||
str = JS_NULL;
|
||||
else
|
||||
str = JS_NewString(ctx, (char *)dbuf.buf);
|
||||
dbuf_free(&dbuf);
|
||||
JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str,
|
||||
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
|
||||
}
|
||||
|
||||
JSValue throw_bf_exception(JSContext *ctx, int status)
|
||||
{
|
||||
const char *str;
|
||||
if (status & BF_ST_MEM_ERROR)
|
||||
return JS_ThrowOutOfMemory(ctx);
|
||||
if (status & BF_ST_DIVIDE_ZERO) {
|
||||
str = "division by zero";
|
||||
} else if (status & BF_ST_INVALID_OP) {
|
||||
str = "invalid operation";
|
||||
} else {
|
||||
str = "integer overflow";
|
||||
}
|
||||
return JS_ThrowRangeError(ctx, "%s", str);
|
||||
}
|
1160
third_party/quickjs/float.c
vendored
Normal file
1160
third_party/quickjs/float.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
495
third_party/quickjs/gc.c
vendored
Normal file
495
third_party/quickjs/gc.c
vendored
Normal file
|
@ -0,0 +1,495 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
void js_trigger_gc(JSRuntime *rt, size_t size)
|
||||
{
|
||||
BOOL force_gc;
|
||||
#ifdef FORCE_GC_AT_MALLOC
|
||||
force_gc = TRUE;
|
||||
#else
|
||||
force_gc = ((rt->malloc_state.malloc_size + size) >
|
||||
rt->malloc_gc_threshold);
|
||||
#endif
|
||||
if (force_gc) {
|
||||
#ifdef DUMP_GC
|
||||
printf("GC: size=%" PRIu64 "\n",
|
||||
(uint64_t)rt->malloc_state.malloc_size);
|
||||
#endif
|
||||
JS_RunGC(rt);
|
||||
rt->malloc_gc_threshold = rt->malloc_state.malloc_size +
|
||||
(rt->malloc_state.malloc_size >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
|
||||
{
|
||||
if (JS_VALUE_HAS_REF_COUNT(val)) {
|
||||
switch(JS_VALUE_GET_TAG(val)) {
|
||||
case JS_TAG_OBJECT:
|
||||
case JS_TAG_FUNCTION_BYTECODE:
|
||||
mark_func(rt, JS_VALUE_GET_PTR(val));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
|
||||
JS_MarkFunc *mark_func)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < m->export_entries_count; i++) {
|
||||
JSExportEntry *me = &m->export_entries[i];
|
||||
if (me->export_type == JS_EXPORT_TYPE_LOCAL &&
|
||||
me->u.local.var_ref) {
|
||||
mark_func(rt, &me->u.local.var_ref->header);
|
||||
}
|
||||
}
|
||||
JS_MarkValue(rt, m->module_ns, mark_func);
|
||||
JS_MarkValue(rt, m->func_obj, mark_func);
|
||||
JS_MarkValue(rt, m->eval_exception, mark_func);
|
||||
JS_MarkValue(rt, m->meta_obj, mark_func);
|
||||
}
|
||||
|
||||
static void JS_MarkContext(JSRuntime *rt, JSContext *ctx, JS_MarkFunc *mark_func)
|
||||
{
|
||||
int i;
|
||||
struct list_head *el;
|
||||
/* modules are not seen by the GC, so we directly mark the objects
|
||||
referenced by each module */
|
||||
list_for_each(el, &ctx->loaded_modules) {
|
||||
JSModuleDef *m = list_entry(el, JSModuleDef, link);
|
||||
js_mark_module_def(rt, m, mark_func);
|
||||
}
|
||||
JS_MarkValue(rt, ctx->global_obj, mark_func);
|
||||
JS_MarkValue(rt, ctx->global_var_obj, mark_func);
|
||||
JS_MarkValue(rt, ctx->throw_type_error, mark_func);
|
||||
JS_MarkValue(rt, ctx->eval_obj, mark_func);
|
||||
JS_MarkValue(rt, ctx->array_proto_values, mark_func);
|
||||
for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
|
||||
JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);
|
||||
}
|
||||
for(i = 0; i < rt->class_count; i++) {
|
||||
JS_MarkValue(rt, ctx->class_proto[i], mark_func);
|
||||
}
|
||||
JS_MarkValue(rt, ctx->iterator_proto, mark_func);
|
||||
JS_MarkValue(rt, ctx->async_iterator_proto, mark_func);
|
||||
JS_MarkValue(rt, ctx->promise_ctor, mark_func);
|
||||
JS_MarkValue(rt, ctx->array_ctor, mark_func);
|
||||
JS_MarkValue(rt, ctx->regexp_ctor, mark_func);
|
||||
JS_MarkValue(rt, ctx->function_ctor, mark_func);
|
||||
JS_MarkValue(rt, ctx->function_proto, mark_func);
|
||||
if (ctx->array_shape)
|
||||
mark_func(rt, &ctx->array_shape->header);
|
||||
}
|
||||
|
||||
static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
|
||||
JS_MarkFunc *mark_func)
|
||||
{
|
||||
switch(gp->gc_obj_type) {
|
||||
case JS_GC_OBJ_TYPE_JS_OBJECT:
|
||||
{
|
||||
JSObject *p = (JSObject *)gp;
|
||||
JSShapeProperty *prs;
|
||||
JSShape *sh;
|
||||
int i;
|
||||
sh = p->shape;
|
||||
mark_func(rt, &sh->header);
|
||||
/* mark all the fields */
|
||||
prs = get_shape_prop(sh);
|
||||
for(i = 0; i < sh->prop_count; i++) {
|
||||
JSProperty *pr = &p->prop[i];
|
||||
if (prs->atom != JS_ATOM_NULL) {
|
||||
if (prs->flags & JS_PROP_TMASK) {
|
||||
if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
|
||||
if (pr->u.getset.getter)
|
||||
mark_func(rt, &pr->u.getset.getter->header);
|
||||
if (pr->u.getset.setter)
|
||||
mark_func(rt, &pr->u.getset.setter->header);
|
||||
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
|
||||
if (pr->u.var_ref->is_detached) {
|
||||
/* Note: the tag does not matter
|
||||
provided it is a GC object */
|
||||
mark_func(rt, &pr->u.var_ref->header);
|
||||
}
|
||||
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
|
||||
js_autoinit_mark(rt, pr, mark_func);
|
||||
}
|
||||
} else {
|
||||
JS_MarkValue(rt, pr->u.value, mark_func);
|
||||
}
|
||||
}
|
||||
prs++;
|
||||
}
|
||||
if (p->class_id != JS_CLASS_OBJECT) {
|
||||
JSClassGCMark *gc_mark;
|
||||
gc_mark = rt->class_array[p->class_id].gc_mark;
|
||||
if (gc_mark)
|
||||
gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
|
||||
/* the template objects can be part of a cycle */
|
||||
{
|
||||
JSFunctionBytecode *b = (JSFunctionBytecode *)gp;
|
||||
int i;
|
||||
for(i = 0; i < b->cpool_count; i++) {
|
||||
JS_MarkValue(rt, b->cpool[i], mark_func);
|
||||
}
|
||||
if (b->realm)
|
||||
mark_func(rt, &b->realm->header);
|
||||
}
|
||||
break;
|
||||
case JS_GC_OBJ_TYPE_VAR_REF:
|
||||
{
|
||||
JSVarRef *var_ref = (JSVarRef *)gp;
|
||||
/* only detached variable referenced are taken into account */
|
||||
assert(var_ref->is_detached);
|
||||
JS_MarkValue(rt, *var_ref->pvalue, mark_func);
|
||||
}
|
||||
break;
|
||||
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
|
||||
{
|
||||
JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp;
|
||||
if (s->is_active)
|
||||
async_func_mark(rt, &s->func_state, mark_func);
|
||||
JS_MarkValue(rt, s->resolving_funcs[0], mark_func);
|
||||
JS_MarkValue(rt, s->resolving_funcs[1], mark_func);
|
||||
}
|
||||
break;
|
||||
case JS_GC_OBJ_TYPE_SHAPE:
|
||||
{
|
||||
JSShape *sh = (JSShape *)gp;
|
||||
if (sh->proto != NULL) {
|
||||
mark_func(rt, &sh->proto->header);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_GC_OBJ_TYPE_JS_CONTEXT:
|
||||
{
|
||||
JSContext *ctx = (JSContext *)gp;
|
||||
JS_MarkContext(rt, ctx, mark_func);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p)
|
||||
{
|
||||
assert(p->ref_count > 0);
|
||||
p->ref_count--;
|
||||
if (p->ref_count == 0 && p->mark == 1) {
|
||||
list_del(&p->link);
|
||||
list_add_tail(&p->link, &rt->tmp_obj_list);
|
||||
}
|
||||
}
|
||||
|
||||
static void gc_decref(JSRuntime *rt)
|
||||
{
|
||||
struct list_head *el, *el1;
|
||||
JSGCObjectHeader *p;
|
||||
init_list_head(&rt->tmp_obj_list);
|
||||
/* decrement the refcount of all the children of all the GC
|
||||
objects and move the GC objects with zero refcount to
|
||||
tmp_obj_list */
|
||||
list_for_each_safe(el, el1, &rt->gc_obj_list) {
|
||||
p = list_entry(el, JSGCObjectHeader, link);
|
||||
assert(p->mark == 0);
|
||||
mark_children(rt, p, gc_decref_child);
|
||||
p->mark = 1;
|
||||
if (p->ref_count == 0) {
|
||||
list_del(&p->link);
|
||||
list_add_tail(&p->link, &rt->tmp_obj_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p)
|
||||
{
|
||||
p->ref_count++;
|
||||
if (p->ref_count == 1) {
|
||||
/* ref_count was 0: remove from tmp_obj_list and add at the
|
||||
end of gc_obj_list */
|
||||
list_del(&p->link);
|
||||
list_add_tail(&p->link, &rt->gc_obj_list);
|
||||
p->mark = 0; /* reset the mark for the next GC call */
|
||||
}
|
||||
}
|
||||
|
||||
static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p)
|
||||
{
|
||||
p->ref_count++;
|
||||
}
|
||||
|
||||
static void gc_scan(JSRuntime *rt)
|
||||
{
|
||||
struct list_head *el;
|
||||
JSGCObjectHeader *p;
|
||||
|
||||
/* keep the objects with a refcount > 0 and their children. */
|
||||
list_for_each(el, &rt->gc_obj_list) {
|
||||
p = list_entry(el, JSGCObjectHeader, link);
|
||||
assert(p->ref_count > 0);
|
||||
p->mark = 0; /* reset the mark for the next GC call */
|
||||
mark_children(rt, p, gc_scan_incref_child);
|
||||
}
|
||||
|
||||
/* restore the refcount of the objects to be deleted. */
|
||||
list_for_each(el, &rt->tmp_obj_list) {
|
||||
p = list_entry(el, JSGCObjectHeader, link);
|
||||
mark_children(rt, p, gc_scan_incref_child2);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_object(JSRuntime *rt, JSObject *p)
|
||||
{
|
||||
int i;
|
||||
JSClassFinalizer *finalizer;
|
||||
JSShape *sh;
|
||||
JSShapeProperty *pr;
|
||||
p->free_mark = 1; /* used to tell the object is invalid when
|
||||
freeing cycles */
|
||||
/* free all the fields */
|
||||
sh = p->shape;
|
||||
pr = get_shape_prop(sh);
|
||||
for(i = 0; i < sh->prop_count; i++) {
|
||||
free_property(rt, &p->prop[i], pr->flags);
|
||||
pr++;
|
||||
}
|
||||
js_free_rt(rt, p->prop);
|
||||
/* as an optimization we destroy the shape immediately without
|
||||
putting it in gc_zero_ref_count_list */
|
||||
js_free_shape(rt, sh);
|
||||
/* fail safe */
|
||||
p->shape = NULL;
|
||||
p->prop = NULL;
|
||||
if (UNLIKELY(p->first_weak_ref)) {
|
||||
reset_weak_ref(rt, p);
|
||||
}
|
||||
finalizer = rt->class_array[p->class_id].finalizer;
|
||||
if (finalizer)
|
||||
(*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p));
|
||||
/* fail safe */
|
||||
p->class_id = 0;
|
||||
p->u.opaque = NULL;
|
||||
p->u.func.var_refs = NULL;
|
||||
p->u.func.home_object = NULL;
|
||||
remove_gc_object(&p->header);
|
||||
if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) {
|
||||
list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);
|
||||
} else {
|
||||
js_free_rt(rt, p);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)
|
||||
{
|
||||
switch(gp->gc_obj_type) {
|
||||
case JS_GC_OBJ_TYPE_JS_OBJECT:
|
||||
free_object(rt, (JSObject *)gp);
|
||||
break;
|
||||
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
|
||||
free_function_bytecode(rt, (JSFunctionBytecode *)gp);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void free_zero_refcount(JSRuntime *rt)
|
||||
{
|
||||
struct list_head *el;
|
||||
JSGCObjectHeader *p;
|
||||
rt->gc_phase = JS_GC_PHASE_DECREF;
|
||||
for(;;) {
|
||||
el = rt->gc_zero_ref_count_list.next;
|
||||
if (el == &rt->gc_zero_ref_count_list)
|
||||
break;
|
||||
p = list_entry(el, JSGCObjectHeader, link);
|
||||
assert(p->ref_count == 0);
|
||||
free_gc_object(rt, p);
|
||||
}
|
||||
rt->gc_phase = JS_GC_PHASE_NONE;
|
||||
}
|
||||
|
||||
static void gc_free_cycles(JSRuntime *rt)
|
||||
{
|
||||
struct list_head *el, *el1;
|
||||
JSGCObjectHeader *p;
|
||||
#ifdef DUMP_GC_FREE
|
||||
BOOL header_done = FALSE;
|
||||
#endif
|
||||
rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES;
|
||||
for(;;) {
|
||||
el = rt->tmp_obj_list.next;
|
||||
if (el == &rt->tmp_obj_list)
|
||||
break;
|
||||
p = list_entry(el, JSGCObjectHeader, link);
|
||||
/* Only need to free the GC object associated with JS
|
||||
values. The rest will be automatically removed because they
|
||||
must be referenced by them. */
|
||||
switch(p->gc_obj_type) {
|
||||
case JS_GC_OBJ_TYPE_JS_OBJECT:
|
||||
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
|
||||
#ifdef DUMP_GC_FREE
|
||||
if (!header_done) {
|
||||
printf("Freeing cycles:\n");
|
||||
JS_DumpObjectHeader(rt);
|
||||
header_done = TRUE;
|
||||
}
|
||||
JS_DumpGCObject(rt, p);
|
||||
#endif
|
||||
free_gc_object(rt, p);
|
||||
break;
|
||||
default:
|
||||
list_del(&p->link);
|
||||
list_add_tail(&p->link, &rt->gc_zero_ref_count_list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
rt->gc_phase = JS_GC_PHASE_NONE;
|
||||
list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {
|
||||
p = list_entry(el, JSGCObjectHeader, link);
|
||||
assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
|
||||
p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
|
||||
js_free_rt(rt, p);
|
||||
}
|
||||
init_list_head(&rt->gc_zero_ref_count_list);
|
||||
}
|
||||
|
||||
void JS_RunGC(JSRuntime *rt)
|
||||
{
|
||||
/* decrement the reference of the children of each object. mark =
|
||||
1 after this pass. */
|
||||
gc_decref(rt);
|
||||
/* keep the GC objects with a non zero refcount and their childs */
|
||||
gc_scan(rt);
|
||||
/* free the GC objects in a cycle */
|
||||
gc_free_cycles(rt);
|
||||
}
|
||||
|
||||
/* Return false if not an object or if the object has already been
|
||||
freed (zombie objects are visible in finalizers when freeing
|
||||
cycles). */
|
||||
BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj)
|
||||
{
|
||||
JSObject *p;
|
||||
if (!JS_IsObject(obj))
|
||||
return FALSE;
|
||||
p = JS_VALUE_GET_OBJ(obj);
|
||||
return !p->free_mark;
|
||||
}
|
||||
|
||||
/* called with the ref_count of 'v' reaches zero. */
|
||||
void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
|
||||
{
|
||||
uint32_t tag = JS_VALUE_GET_TAG(v);
|
||||
#ifdef DUMP_FREE
|
||||
{
|
||||
printf("Freeing ");
|
||||
if (tag == JS_TAG_OBJECT) {
|
||||
JS_DumpObject(rt, JS_VALUE_GET_OBJ(v));
|
||||
} else {
|
||||
JS_DumpValueShort(rt, v);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
switch(tag) {
|
||||
case JS_TAG_STRING:
|
||||
{
|
||||
JSString *p = JS_VALUE_GET_STRING(v);
|
||||
if (p->atom_type) {
|
||||
JS_FreeAtomStruct(rt, p);
|
||||
} else {
|
||||
#ifdef DUMP_LEAKS
|
||||
list_del(&p->link);
|
||||
#endif
|
||||
js_free_rt(rt, p);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_TAG_OBJECT:
|
||||
case JS_TAG_FUNCTION_BYTECODE:
|
||||
{
|
||||
JSGCObjectHeader *p = JS_VALUE_GET_PTR(v);
|
||||
if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
|
||||
list_del(&p->link);
|
||||
list_add(&p->link, &rt->gc_zero_ref_count_list);
|
||||
if (rt->gc_phase == JS_GC_PHASE_NONE) {
|
||||
free_zero_refcount(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_TAG_MODULE:
|
||||
abort(); /* never freed here */
|
||||
break;
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_TAG_BIG_INT:
|
||||
case JS_TAG_BIG_FLOAT:
|
||||
{
|
||||
JSBigFloat *bf = JS_VALUE_GET_PTR(v);
|
||||
bf_delete(&bf->num);
|
||||
js_free_rt(rt, bf);
|
||||
}
|
||||
break;
|
||||
case JS_TAG_BIG_DECIMAL:
|
||||
{
|
||||
JSBigDecimal *bf = JS_VALUE_GET_PTR(v);
|
||||
bfdec_delete(&bf->num);
|
||||
js_free_rt(rt, bf);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case JS_TAG_SYMBOL:
|
||||
{
|
||||
JSAtomStruct *p = JS_VALUE_GET_PTR(v);
|
||||
JS_FreeAtomStruct(rt, p);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("__JS_FreeValue: unknown tag=%d\n", tag);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void __JS_FreeValue(JSContext *ctx, JSValue v)
|
||||
{
|
||||
__JS_FreeValueRT(ctx->rt, v);
|
||||
}
|
509
third_party/quickjs/gen.c
vendored
Normal file
509
third_party/quickjs/gen.c
vendored
Normal file
|
@ -0,0 +1,509 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
typedef enum JSGeneratorStateEnum {
|
||||
JS_GENERATOR_STATE_SUSPENDED_START,
|
||||
JS_GENERATOR_STATE_SUSPENDED_YIELD,
|
||||
JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
|
||||
JS_GENERATOR_STATE_EXECUTING,
|
||||
JS_GENERATOR_STATE_COMPLETED,
|
||||
} JSGeneratorStateEnum;
|
||||
|
||||
typedef struct JSGeneratorData {
|
||||
JSGeneratorStateEnum state;
|
||||
JSAsyncFunctionState func_state;
|
||||
} JSGeneratorData;
|
||||
|
||||
static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)
|
||||
{
|
||||
if (s->state == JS_GENERATOR_STATE_COMPLETED)
|
||||
return;
|
||||
async_func_free(rt, &s->func_state);
|
||||
s->state = JS_GENERATOR_STATE_COMPLETED;
|
||||
}
|
||||
|
||||
void js_generator_finalizer(JSRuntime *rt, JSValue obj)
|
||||
{
|
||||
JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR);
|
||||
if (s) {
|
||||
free_generator_stack_rt(rt, s);
|
||||
js_free_rt(rt, s);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_generator_stack(JSContext *ctx, JSGeneratorData *s)
|
||||
{
|
||||
free_generator_stack_rt(ctx->rt, s);
|
||||
}
|
||||
|
||||
void js_generator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
|
||||
{
|
||||
JSObject *p = JS_VALUE_GET_OBJ(val);
|
||||
JSGeneratorData *s = p->u.generator_data;
|
||||
if (!s || s->state == JS_GENERATOR_STATE_COMPLETED)
|
||||
return;
|
||||
async_func_mark(rt, &s->func_state, mark_func);
|
||||
}
|
||||
|
||||
static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv,
|
||||
BOOL *pdone, int magic)
|
||||
{
|
||||
JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR);
|
||||
JSStackFrame *sf;
|
||||
JSValue ret, func_ret;
|
||||
*pdone = TRUE;
|
||||
if (!s)
|
||||
return JS_ThrowTypeError(ctx, "not a generator");
|
||||
sf = &s->func_state.frame;
|
||||
switch(s->state) {
|
||||
default:
|
||||
case JS_GENERATOR_STATE_SUSPENDED_START:
|
||||
if (magic == GEN_MAGIC_NEXT) {
|
||||
goto exec_no_arg;
|
||||
} else {
|
||||
free_generator_stack(ctx, s);
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
|
||||
case JS_GENERATOR_STATE_SUSPENDED_YIELD:
|
||||
/* cur_sp[-1] was set to JS_UNDEFINED in the previous call */
|
||||
ret = JS_DupValue(ctx, argv[0]);
|
||||
if (magic == GEN_MAGIC_THROW &&
|
||||
s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {
|
||||
JS_Throw(ctx, ret);
|
||||
s->func_state.throw_flag = TRUE;
|
||||
} else {
|
||||
sf->cur_sp[-1] = ret;
|
||||
sf->cur_sp[0] = JS_NewInt32(ctx, magic);
|
||||
sf->cur_sp++;
|
||||
exec_no_arg:
|
||||
s->func_state.throw_flag = FALSE;
|
||||
}
|
||||
s->state = JS_GENERATOR_STATE_EXECUTING;
|
||||
func_ret = async_func_resume(ctx, &s->func_state);
|
||||
s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;
|
||||
if (JS_IsException(func_ret)) {
|
||||
/* finalize the execution in case of exception */
|
||||
free_generator_stack(ctx, s);
|
||||
return func_ret;
|
||||
}
|
||||
if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
|
||||
/* get the returned yield value at the top of the stack */
|
||||
ret = sf->cur_sp[-1];
|
||||
sf->cur_sp[-1] = JS_UNDEFINED;
|
||||
if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) {
|
||||
s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
|
||||
/* return (value, done) object */
|
||||
*pdone = 2;
|
||||
} else {
|
||||
*pdone = FALSE;
|
||||
}
|
||||
} else {
|
||||
/* end of iterator */
|
||||
ret = sf->cur_sp[-1];
|
||||
sf->cur_sp[-1] = JS_UNDEFINED;
|
||||
JS_FreeValue(ctx, func_ret);
|
||||
free_generator_stack(ctx, s);
|
||||
}
|
||||
break;
|
||||
case JS_GENERATOR_STATE_COMPLETED:
|
||||
done:
|
||||
/* execution is finished */
|
||||
switch(magic) {
|
||||
default:
|
||||
case GEN_MAGIC_NEXT:
|
||||
ret = JS_UNDEFINED;
|
||||
break;
|
||||
case GEN_MAGIC_RETURN:
|
||||
ret = JS_DupValue(ctx, argv[0]);
|
||||
break;
|
||||
case GEN_MAGIC_THROW:
|
||||
ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0]));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case JS_GENERATOR_STATE_EXECUTING:
|
||||
ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator");
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
|
||||
JSValueConst this_obj, int argc,
|
||||
JSValueConst *argv, int flags)
|
||||
{
|
||||
JSValue obj, func_ret;
|
||||
JSGeneratorData *s;
|
||||
s = js_mallocz(ctx, sizeof(*s));
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
s->state = JS_GENERATOR_STATE_SUSPENDED_START;
|
||||
if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
|
||||
s->state = JS_GENERATOR_STATE_COMPLETED;
|
||||
goto fail;
|
||||
}
|
||||
/* execute the function up to 'OP_initial_yield' */
|
||||
func_ret = async_func_resume(ctx, &s->func_state);
|
||||
if (JS_IsException(func_ret))
|
||||
goto fail;
|
||||
JS_FreeValue(ctx, func_ret);
|
||||
obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR);
|
||||
if (JS_IsException(obj))
|
||||
goto fail;
|
||||
JS_SetOpaque(obj, s);
|
||||
return obj;
|
||||
fail:
|
||||
free_generator_stack_rt(ctx->rt, s);
|
||||
js_free(ctx, s);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
static JSValue js_async_generator_resolve_function(JSContext *ctx,
|
||||
JSValueConst this_obj,
|
||||
int argc, JSValueConst *argv,
|
||||
int magic, JSValue *func_data);
|
||||
|
||||
static int js_async_generator_resolve_function_create(JSContext *ctx,
|
||||
JSValueConst generator,
|
||||
JSValue *resolving_funcs,
|
||||
BOOL is_resume_next)
|
||||
{
|
||||
int i;
|
||||
JSValue func;
|
||||
for(i = 0; i < 2; i++) {
|
||||
func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1,
|
||||
i + is_resume_next * 2, 1, &generator);
|
||||
if (JS_IsException(func)) {
|
||||
if (i == 1)
|
||||
JS_FreeValue(ctx, resolving_funcs[0]);
|
||||
return -1;
|
||||
}
|
||||
resolving_funcs[i] = func;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int js_async_generator_await(JSContext *ctx,
|
||||
JSAsyncGeneratorData *s,
|
||||
JSValueConst value)
|
||||
{
|
||||
JSValue promise, resolving_funcs[2], resolving_funcs1[2];
|
||||
int i, res;
|
||||
promise = js_promise_resolve(ctx, ctx->promise_ctor,
|
||||
1, &value, 0);
|
||||
if (JS_IsException(promise))
|
||||
goto fail;
|
||||
if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator),
|
||||
resolving_funcs, FALSE)) {
|
||||
JS_FreeValue(ctx, promise);
|
||||
goto fail;
|
||||
}
|
||||
/* Note: no need to create 'thrownawayCapability' as in
|
||||
the spec */
|
||||
for(i = 0; i < 2; i++)
|
||||
resolving_funcs1[i] = JS_UNDEFINED;
|
||||
res = perform_promise_then(ctx, promise,
|
||||
(JSValueConst *)resolving_funcs,
|
||||
(JSValueConst *)resolving_funcs1);
|
||||
JS_FreeValue(ctx, promise);
|
||||
for(i = 0; i < 2; i++)
|
||||
JS_FreeValue(ctx, resolving_funcs[i]);
|
||||
if (res)
|
||||
goto fail;
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void js_async_generator_resolve_or_reject(JSContext *ctx,
|
||||
JSAsyncGeneratorData *s,
|
||||
JSValueConst result,
|
||||
int is_reject)
|
||||
{
|
||||
JSAsyncGeneratorRequest *next;
|
||||
JSValue ret;
|
||||
next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
|
||||
list_del(&next->link);
|
||||
ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1,
|
||||
&result);
|
||||
JS_FreeValue(ctx, ret);
|
||||
JS_FreeValue(ctx, next->result);
|
||||
JS_FreeValue(ctx, next->promise);
|
||||
JS_FreeValue(ctx, next->resolving_funcs[0]);
|
||||
JS_FreeValue(ctx, next->resolving_funcs[1]);
|
||||
js_free(ctx, next);
|
||||
}
|
||||
|
||||
static void js_async_generator_resolve(JSContext *ctx,
|
||||
JSAsyncGeneratorData *s,
|
||||
JSValueConst value,
|
||||
BOOL done)
|
||||
{
|
||||
JSValue result;
|
||||
result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done);
|
||||
/* XXX: better exception handling ? */
|
||||
js_async_generator_resolve_or_reject(ctx, s, result, 0);
|
||||
JS_FreeValue(ctx, result);
|
||||
}
|
||||
|
||||
static void js_async_generator_reject(JSContext *ctx,
|
||||
JSAsyncGeneratorData *s,
|
||||
JSValueConst exception)
|
||||
{
|
||||
js_async_generator_resolve_or_reject(ctx, s, exception, 1);
|
||||
}
|
||||
|
||||
static void js_async_generator_complete(JSContext *ctx,
|
||||
JSAsyncGeneratorData *s)
|
||||
{
|
||||
if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {
|
||||
s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
|
||||
async_func_free(ctx->rt, &s->func_state);
|
||||
}
|
||||
}
|
||||
|
||||
static int js_async_generator_completed_return(JSContext *ctx,
|
||||
JSAsyncGeneratorData *s,
|
||||
JSValueConst value)
|
||||
{
|
||||
JSValue promise, resolving_funcs[2], resolving_funcs1[2];
|
||||
int res;
|
||||
|
||||
promise = js_promise_resolve(ctx, ctx->promise_ctor,
|
||||
1, (JSValueConst *)&value, 0);
|
||||
if (JS_IsException(promise))
|
||||
return -1;
|
||||
if (js_async_generator_resolve_function_create(ctx,
|
||||
JS_MKPTR(JS_TAG_OBJECT, s->generator),
|
||||
resolving_funcs1,
|
||||
TRUE)) {
|
||||
JS_FreeValue(ctx, promise);
|
||||
return -1;
|
||||
}
|
||||
resolving_funcs[0] = JS_UNDEFINED;
|
||||
resolving_funcs[1] = JS_UNDEFINED;
|
||||
res = perform_promise_then(ctx, promise,
|
||||
(JSValueConst *)resolving_funcs1,
|
||||
(JSValueConst *)resolving_funcs);
|
||||
JS_FreeValue(ctx, resolving_funcs1[0]);
|
||||
JS_FreeValue(ctx, resolving_funcs1[1]);
|
||||
JS_FreeValue(ctx, promise);
|
||||
return res;
|
||||
}
|
||||
|
||||
void js_async_generator_resume_next(JSContext *ctx, JSAsyncGeneratorData *s)
|
||||
{
|
||||
JSAsyncGeneratorRequest *next;
|
||||
JSValue func_ret, value;
|
||||
for(;;) {
|
||||
if (list_empty(&s->queue))
|
||||
break;
|
||||
next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
|
||||
switch(s->state) {
|
||||
case JS_ASYNC_GENERATOR_STATE_EXECUTING:
|
||||
/* only happens when restarting execution after await() */
|
||||
goto resume_exec;
|
||||
case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN:
|
||||
goto done;
|
||||
case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START:
|
||||
if (next->completion_type == GEN_MAGIC_NEXT) {
|
||||
goto exec_no_arg;
|
||||
} else {
|
||||
js_async_generator_complete(ctx, s);
|
||||
}
|
||||
break;
|
||||
case JS_ASYNC_GENERATOR_STATE_COMPLETED:
|
||||
if (next->completion_type == GEN_MAGIC_NEXT) {
|
||||
js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE);
|
||||
} else if (next->completion_type == GEN_MAGIC_RETURN) {
|
||||
s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;
|
||||
js_async_generator_completed_return(ctx, s, next->result);
|
||||
goto done;
|
||||
} else {
|
||||
js_async_generator_reject(ctx, s, next->result);
|
||||
}
|
||||
goto done;
|
||||
case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD:
|
||||
case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
|
||||
value = JS_DupValue(ctx, next->result);
|
||||
if (next->completion_type == GEN_MAGIC_THROW &&
|
||||
s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {
|
||||
JS_Throw(ctx, value);
|
||||
s->func_state.throw_flag = TRUE;
|
||||
} else {
|
||||
/* 'yield' returns a value. 'yield *' also returns a value
|
||||
in case the 'throw' method is called */
|
||||
s->func_state.frame.cur_sp[-1] = value;
|
||||
s->func_state.frame.cur_sp[0] =
|
||||
JS_NewInt32(ctx, next->completion_type);
|
||||
s->func_state.frame.cur_sp++;
|
||||
exec_no_arg:
|
||||
s->func_state.throw_flag = FALSE;
|
||||
}
|
||||
s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;
|
||||
resume_exec:
|
||||
func_ret = async_func_resume(ctx, &s->func_state);
|
||||
if (JS_IsException(func_ret)) {
|
||||
value = JS_GetException(ctx);
|
||||
js_async_generator_complete(ctx, s);
|
||||
js_async_generator_reject(ctx, s, value);
|
||||
JS_FreeValue(ctx, value);
|
||||
} else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
|
||||
int func_ret_code;
|
||||
value = s->func_state.frame.cur_sp[-1];
|
||||
s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
|
||||
func_ret_code = JS_VALUE_GET_INT(func_ret);
|
||||
switch(func_ret_code) {
|
||||
case FUNC_RET_YIELD:
|
||||
case FUNC_RET_YIELD_STAR:
|
||||
if (func_ret_code == FUNC_RET_YIELD_STAR)
|
||||
s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
|
||||
else
|
||||
s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD;
|
||||
js_async_generator_resolve(ctx, s, value, FALSE);
|
||||
JS_FreeValue(ctx, value);
|
||||
break;
|
||||
case FUNC_RET_AWAIT:
|
||||
js_async_generator_await(ctx, s, value);
|
||||
JS_FreeValue(ctx, value);
|
||||
goto done;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
assert(JS_IsUndefined(func_ret));
|
||||
/* end of function */
|
||||
value = s->func_state.frame.cur_sp[-1];
|
||||
s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
|
||||
js_async_generator_complete(ctx, s);
|
||||
js_async_generator_resolve(ctx, s, value, TRUE);
|
||||
JS_FreeValue(ctx, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
done: ;
|
||||
}
|
||||
|
||||
static JSValue js_async_generator_resolve_function(JSContext *ctx,
|
||||
JSValueConst this_obj,
|
||||
int argc, JSValueConst *argv,
|
||||
int magic, JSValue *func_data)
|
||||
{
|
||||
BOOL is_reject = magic & 1;
|
||||
JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR);
|
||||
JSValueConst arg = argv[0];
|
||||
/* XXX: what if s == NULL */
|
||||
if (magic >= 2) {
|
||||
/* resume next case in AWAITING_RETURN state */
|
||||
assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN ||
|
||||
s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED);
|
||||
s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
|
||||
if (is_reject) {
|
||||
js_async_generator_reject(ctx, s, arg);
|
||||
} else {
|
||||
js_async_generator_resolve(ctx, s, arg, TRUE);
|
||||
}
|
||||
} else {
|
||||
/* restart function execution after await() */
|
||||
assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);
|
||||
s->func_state.throw_flag = is_reject;
|
||||
if (is_reject) {
|
||||
JS_Throw(ctx, JS_DupValue(ctx, arg));
|
||||
} else {
|
||||
/* return value of await */
|
||||
s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);
|
||||
}
|
||||
js_async_generator_resume_next(ctx, s);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void js_async_generator_free(JSRuntime *rt, JSAsyncGeneratorData *s)
|
||||
{
|
||||
struct list_head *el, *el1;
|
||||
JSAsyncGeneratorRequest *req;
|
||||
list_for_each_safe(el, el1, &s->queue) {
|
||||
req = list_entry(el, JSAsyncGeneratorRequest, link);
|
||||
JS_FreeValueRT(rt, req->result);
|
||||
JS_FreeValueRT(rt, req->promise);
|
||||
JS_FreeValueRT(rt, req->resolving_funcs[0]);
|
||||
JS_FreeValueRT(rt, req->resolving_funcs[1]);
|
||||
js_free_rt(rt, req);
|
||||
}
|
||||
if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
|
||||
s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
|
||||
async_func_free(rt, &s->func_state);
|
||||
}
|
||||
js_free_rt(rt, s);
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_generator_function_proto_funcs[] = {
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_generator_proto_funcs[] = {
|
||||
JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ),
|
||||
JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ),
|
||||
JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE),
|
||||
};
|
||||
|
||||
void JS_AddIntrinsicGenerator(JSContext *ctx)
|
||||
{
|
||||
JSValue obj1;
|
||||
ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
|
||||
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR],
|
||||
js_generator_proto_funcs,
|
||||
countof(js_generator_proto_funcs));
|
||||
ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
|
||||
obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor,
|
||||
"GeneratorFunction", 1,
|
||||
JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR);
|
||||
JS_SetPropertyFunctionList(ctx,
|
||||
ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
|
||||
js_generator_function_proto_funcs,
|
||||
countof(js_generator_function_proto_funcs));
|
||||
JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
|
||||
ctx->class_proto[JS_CLASS_GENERATOR],
|
||||
JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
|
||||
JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
|
||||
0, JS_PROP_CONFIGURABLE);
|
||||
JS_FreeValue(ctx, obj1);
|
||||
}
|
2214
third_party/quickjs/internal.h
vendored
Normal file
2214
third_party/quickjs/internal.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
48
third_party/quickjs/iter.c
vendored
Normal file
48
third_party/quickjs/iter.c
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv) {
|
||||
return JS_DupValue(ctx, this_val);
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_iterator_proto_funcs[] = {
|
||||
JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator),
|
||||
};
|
||||
|
||||
void JS_AddIteratorProto(JSContext *ctx) {
|
||||
ctx->iterator_proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, ctx->iterator_proto,
|
||||
js_iterator_proto_funcs,
|
||||
countof(js_iterator_proto_funcs));
|
||||
}
|
992
third_party/quickjs/json.c
vendored
Normal file
992
third_party/quickjs/json.c
vendored
Normal file
|
@ -0,0 +1,992 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
#include "third_party/quickjs/libregexp.h"
|
||||
#include "third_party/quickjs/quickjs.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
/* 'c' is the first character. Return JS_ATOM_NULL in case of error */
|
||||
static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)
|
||||
{
|
||||
const uint8_t *p;
|
||||
char ident_buf[128], *buf;
|
||||
size_t ident_size, ident_pos;
|
||||
JSAtom atom;
|
||||
p = *pp;
|
||||
buf = ident_buf;
|
||||
ident_size = sizeof(ident_buf);
|
||||
ident_pos = 0;
|
||||
for(;;) {
|
||||
buf[ident_pos++] = c;
|
||||
c = *p;
|
||||
if (c >= 128 ||
|
||||
!((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1))
|
||||
break;
|
||||
p++;
|
||||
if (UNLIKELY(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
|
||||
if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
|
||||
atom = JS_ATOM_NULL;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
|
||||
done:
|
||||
if (UNLIKELY(buf != ident_buf))
|
||||
js_free(s->ctx, buf);
|
||||
*pp = p;
|
||||
return atom;
|
||||
}
|
||||
|
||||
static __exception int json_next_token(JSParseState *s)
|
||||
{
|
||||
const uint8_t *p;
|
||||
int c;
|
||||
JSAtom atom;
|
||||
if (js_check_stack_overflow(s->ctx->rt, 0)) {
|
||||
return js_parse_error(s, "stack overflow");
|
||||
}
|
||||
free_token(s, &s->token);
|
||||
p = s->last_ptr = s->buf_ptr;
|
||||
s->last_line_num = s->token.line_num;
|
||||
redo:
|
||||
s->token.line_num = s->line_num;
|
||||
s->token.ptr = p;
|
||||
c = *p;
|
||||
switch(c) {
|
||||
case 0:
|
||||
if (p >= s->buf_end) {
|
||||
s->token.val = TOK_EOF;
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
case '\'':
|
||||
if (!s->ext_json) {
|
||||
/* JSON does not accept single quoted strings */
|
||||
goto def_token;
|
||||
}
|
||||
/* fall through */
|
||||
case '\"':
|
||||
if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
|
||||
goto fail;
|
||||
break;
|
||||
case '\r': /* accept DOS and MAC newline sequences */
|
||||
if (p[1] == '\n') {
|
||||
p++;
|
||||
}
|
||||
/* fall thru */
|
||||
case '\n':
|
||||
p++;
|
||||
s->line_num++;
|
||||
goto redo;
|
||||
case '\f':
|
||||
case '\v':
|
||||
if (!s->ext_json) {
|
||||
/* JSONWhitespace does not match <VT>, nor <FF> */
|
||||
goto def_token;
|
||||
}
|
||||
/* fall through */
|
||||
case ' ':
|
||||
case '\t':
|
||||
p++;
|
||||
goto redo;
|
||||
case '/':
|
||||
if (!s->ext_json) {
|
||||
/* JSON does not accept comments */
|
||||
goto def_token;
|
||||
}
|
||||
if (p[1] == '*') {
|
||||
/* comment */
|
||||
p += 2;
|
||||
for(;;) {
|
||||
if (*p == '\0' && p >= s->buf_end) {
|
||||
js_parse_error(s, "unexpected end of comment");
|
||||
goto fail;
|
||||
}
|
||||
if (p[0] == '*' && p[1] == '/') {
|
||||
p += 2;
|
||||
break;
|
||||
}
|
||||
if (*p == '\n') {
|
||||
s->line_num++;
|
||||
p++;
|
||||
} else if (*p == '\r') {
|
||||
p++;
|
||||
} else if (*p >= 0x80) {
|
||||
c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
|
||||
if (c == -1) {
|
||||
p++; /* skip invalid UTF-8 */
|
||||
}
|
||||
} else {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
goto redo;
|
||||
} else if (p[1] == '/') {
|
||||
/* line comment */
|
||||
p += 2;
|
||||
for(;;) {
|
||||
if (*p == '\0' && p >= s->buf_end)
|
||||
break;
|
||||
if (*p == '\r' || *p == '\n')
|
||||
break;
|
||||
if (*p >= 0x80) {
|
||||
c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
|
||||
/* LS or PS are considered as line terminator */
|
||||
if (c == CP_LS || c == CP_PS) {
|
||||
break;
|
||||
} else if (c == -1) {
|
||||
p++; /* skip invalid UTF-8 */
|
||||
}
|
||||
} else {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
goto redo;
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
case 'a': case 'b': case 'c': case 'd':
|
||||
case 'e': case 'f': case 'g': case 'h':
|
||||
case 'i': case 'j': case 'k': case 'l':
|
||||
case 'm': case 'n': case 'o': case 'p':
|
||||
case 'q': case 'r': case 's': case 't':
|
||||
case 'u': case 'v': case 'w': case 'x':
|
||||
case 'y': case 'z':
|
||||
case 'A': case 'B': case 'C': case 'D':
|
||||
case 'E': case 'F': case 'G': case 'H':
|
||||
case 'I': case 'J': case 'K': case 'L':
|
||||
case 'M': case 'N': case 'O': case 'P':
|
||||
case 'Q': case 'R': case 'S': case 'T':
|
||||
case 'U': case 'V': case 'W': case 'X':
|
||||
case 'Y': case 'Z':
|
||||
case '_':
|
||||
case '$':
|
||||
/* identifier : only pure ascii characters are accepted */
|
||||
p++;
|
||||
atom = json_parse_ident(s, &p, c);
|
||||
if (atom == JS_ATOM_NULL)
|
||||
goto fail;
|
||||
s->token.u.ident.atom = atom;
|
||||
s->token.u.ident.has_escape = FALSE;
|
||||
s->token.u.ident.is_reserved = FALSE;
|
||||
s->token.val = TOK_IDENT;
|
||||
break;
|
||||
case '+':
|
||||
if (!s->ext_json || !isdigit(p[1]))
|
||||
goto def_token;
|
||||
goto parse_number;
|
||||
case '0':
|
||||
if (isdigit(p[1]))
|
||||
goto def_token;
|
||||
goto parse_number;
|
||||
case '-':
|
||||
if (!isdigit(p[1]))
|
||||
goto def_token;
|
||||
goto parse_number;
|
||||
case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8':
|
||||
case '9':
|
||||
/* number */
|
||||
parse_number:
|
||||
{
|
||||
JSValue ret;
|
||||
int flags, radix;
|
||||
if (!s->ext_json) {
|
||||
flags = 0;
|
||||
radix = 10;
|
||||
} else {
|
||||
flags = ATOD_ACCEPT_BIN_OCT;
|
||||
radix = 0;
|
||||
}
|
||||
ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
|
||||
flags);
|
||||
if (JS_IsException(ret))
|
||||
goto fail;
|
||||
s->token.val = TOK_NUMBER;
|
||||
s->token.u.num.val = ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (c >= 128) {
|
||||
js_parse_error(s, "unexpected character");
|
||||
goto fail;
|
||||
}
|
||||
def_token:
|
||||
s->token.val = c;
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
s->buf_ptr = p;
|
||||
// dump_token(s, &s->token);
|
||||
return 0;
|
||||
fail:
|
||||
s->token.val = TOK_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int json_parse_expect(JSParseState *s, int tok)
|
||||
{
|
||||
if (s->token.val != tok) {
|
||||
/* XXX: dump token correctly in all cases */
|
||||
return js_parse_error(s, "expecting '%c'", tok);
|
||||
}
|
||||
return json_next_token(s);
|
||||
}
|
||||
|
||||
static JSValue json_parse_value(JSParseState *s)
|
||||
{
|
||||
JSContext *ctx = s->ctx;
|
||||
JSValue val = JS_NULL;
|
||||
int ret;
|
||||
switch(s->token.val) {
|
||||
case '{':
|
||||
{
|
||||
JSValue prop_val;
|
||||
JSAtom prop_name;
|
||||
if (json_next_token(s))
|
||||
goto fail;
|
||||
val = JS_NewObject(ctx);
|
||||
if (JS_IsException(val))
|
||||
goto fail;
|
||||
if (s->token.val != '}') {
|
||||
for(;;) {
|
||||
if (s->token.val == TOK_STRING) {
|
||||
prop_name = JS_ValueToAtom(ctx, s->token.u.str.str);
|
||||
if (prop_name == JS_ATOM_NULL)
|
||||
goto fail;
|
||||
} else if (s->ext_json && s->token.val == TOK_IDENT) {
|
||||
prop_name = JS_DupAtom(ctx, s->token.u.ident.atom);
|
||||
} else {
|
||||
js_parse_error(s, "expecting property name");
|
||||
goto fail;
|
||||
}
|
||||
if (json_next_token(s))
|
||||
goto fail1;
|
||||
if (json_parse_expect(s, ':'))
|
||||
goto fail1;
|
||||
prop_val = json_parse_value(s);
|
||||
if (JS_IsException(prop_val)) {
|
||||
fail1:
|
||||
JS_FreeAtom(ctx, prop_name);
|
||||
goto fail;
|
||||
}
|
||||
ret = JS_DefinePropertyValue(ctx, val, prop_name,
|
||||
prop_val, JS_PROP_C_W_E);
|
||||
JS_FreeAtom(ctx, prop_name);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
if (s->token.val != ',')
|
||||
break;
|
||||
if (json_next_token(s))
|
||||
goto fail;
|
||||
if (s->ext_json && s->token.val == '}')
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (json_parse_expect(s, '}'))
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
{
|
||||
JSValue el;
|
||||
uint32_t idx;
|
||||
if (json_next_token(s))
|
||||
goto fail;
|
||||
val = JS_NewArray(ctx);
|
||||
if (JS_IsException(val))
|
||||
goto fail;
|
||||
if (s->token.val != ']') {
|
||||
idx = 0;
|
||||
for(;;) {
|
||||
el = json_parse_value(s);
|
||||
if (JS_IsException(el))
|
||||
goto fail;
|
||||
ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
if (s->token.val != ',')
|
||||
break;
|
||||
if (json_next_token(s))
|
||||
goto fail;
|
||||
idx++;
|
||||
if (s->ext_json && s->token.val == ']')
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (json_parse_expect(s, ']'))
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case TOK_STRING:
|
||||
val = JS_DupValue(ctx, s->token.u.str.str);
|
||||
if (json_next_token(s))
|
||||
goto fail;
|
||||
break;
|
||||
case TOK_NUMBER:
|
||||
val = s->token.u.num.val;
|
||||
if (json_next_token(s))
|
||||
goto fail;
|
||||
break;
|
||||
case TOK_IDENT:
|
||||
if (s->token.u.ident.atom == JS_ATOM_false ||
|
||||
s->token.u.ident.atom == JS_ATOM_true) {
|
||||
val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true));
|
||||
} else if (s->token.u.ident.atom == JS_ATOM_null) {
|
||||
val = JS_NULL;
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
if (json_next_token(s))
|
||||
goto fail;
|
||||
break;
|
||||
default:
|
||||
def_token:
|
||||
if (s->token.val == TOK_EOF) {
|
||||
js_parse_error(s, "unexpected end of input");
|
||||
} else {
|
||||
js_parse_error(s, "unexpected token: '%.*s'",
|
||||
(int)(s->buf_ptr - s->token.ptr), s->token.ptr);
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
return val;
|
||||
fail:
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
|
||||
const char *filename, int flags)
|
||||
{
|
||||
JSParseState s1, *s = &s1;
|
||||
JSValue val = JS_UNDEFINED;
|
||||
js_parse_init(ctx, s, buf, buf_len, filename);
|
||||
s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);
|
||||
if (json_next_token(s))
|
||||
goto fail;
|
||||
val = json_parse_value(s);
|
||||
if (JS_IsException(val))
|
||||
goto fail;
|
||||
if (s->token.val != TOK_EOF) {
|
||||
if (js_parse_error(s, "unexpected data at the end"))
|
||||
goto fail;
|
||||
}
|
||||
return val;
|
||||
fail:
|
||||
JS_FreeValue(ctx, val);
|
||||
free_token(s, &s->token);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
|
||||
const char *filename)
|
||||
{
|
||||
return JS_ParseJSON2(ctx, buf, buf_len, filename, 0);
|
||||
}
|
||||
|
||||
static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
|
||||
JSAtom name, JSValueConst reviver)
|
||||
{
|
||||
JSValue val, new_el, name_val, res;
|
||||
JSValueConst args[2];
|
||||
int ret, is_array;
|
||||
uint32_t i, len = 0;
|
||||
JSAtom prop;
|
||||
JSPropertyEnum *atoms = NULL;
|
||||
if (js_check_stack_overflow(ctx->rt, 0)) {
|
||||
return JS_ThrowStackOverflow(ctx);
|
||||
}
|
||||
val = JS_GetProperty(ctx, holder, name);
|
||||
if (JS_IsException(val))
|
||||
return val;
|
||||
if (JS_IsObject(val)) {
|
||||
is_array = JS_IsArray(ctx, val);
|
||||
if (is_array < 0)
|
||||
goto fail;
|
||||
if (is_array) {
|
||||
if (js_get_length32(ctx, &len, val))
|
||||
goto fail;
|
||||
} else {
|
||||
ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
for(i = 0; i < len; i++) {
|
||||
if (is_array) {
|
||||
prop = JS_NewAtomUInt32(ctx, i);
|
||||
if (prop == JS_ATOM_NULL)
|
||||
goto fail;
|
||||
} else {
|
||||
prop = JS_DupAtom(ctx, atoms[i].atom);
|
||||
}
|
||||
new_el = internalize_json_property(ctx, val, prop, reviver);
|
||||
if (JS_IsException(new_el)) {
|
||||
JS_FreeAtom(ctx, prop);
|
||||
goto fail;
|
||||
}
|
||||
if (JS_IsUndefined(new_el)) {
|
||||
ret = JS_DeleteProperty(ctx, val, prop, 0);
|
||||
} else {
|
||||
ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E);
|
||||
}
|
||||
JS_FreeAtom(ctx, prop);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
js_free_prop_enum(ctx, atoms, len);
|
||||
atoms = NULL;
|
||||
name_val = JS_AtomToValue(ctx, name);
|
||||
if (JS_IsException(name_val))
|
||||
goto fail;
|
||||
args[0] = name_val;
|
||||
args[1] = val;
|
||||
res = JS_Call(ctx, reviver, holder, 2, args);
|
||||
JS_FreeValue(ctx, name_val);
|
||||
JS_FreeValue(ctx, val);
|
||||
return res;
|
||||
fail:
|
||||
js_free_prop_enum(ctx, atoms, len);
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValue obj, root;
|
||||
JSValueConst reviver;
|
||||
const char *str;
|
||||
size_t len;
|
||||
str = JS_ToCStringLen(ctx, &len, argv[0]);
|
||||
if (!str)
|
||||
return JS_EXCEPTION;
|
||||
obj = JS_ParseJSON(ctx, str, len, "<input>");
|
||||
JS_FreeCString(ctx, str);
|
||||
if (JS_IsException(obj))
|
||||
return obj;
|
||||
if (argc > 1 && JS_IsFunction(ctx, argv[1])) {
|
||||
reviver = argv[1];
|
||||
root = JS_NewObject(ctx);
|
||||
if (JS_IsException(root)) {
|
||||
JS_FreeValue(ctx, obj);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj,
|
||||
JS_PROP_C_W_E) < 0) {
|
||||
JS_FreeValue(ctx, root);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
obj = internalize_json_property(ctx, root, JS_ATOM_empty_string,
|
||||
reviver);
|
||||
JS_FreeValue(ctx, root);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
|
||||
{
|
||||
JSValue val;
|
||||
JSString *p;
|
||||
int i;
|
||||
uint32_t c;
|
||||
StringBuffer b_s, *b = &b_s;
|
||||
char buf[16];
|
||||
val = JS_ToStringCheckObject(ctx, val1);
|
||||
if (JS_IsException(val))
|
||||
return val;
|
||||
p = JS_VALUE_GET_STRING(val);
|
||||
if (string_buffer_init(ctx, b, p->len + 2))
|
||||
goto fail;
|
||||
if (string_buffer_putc8(b, '\"'))
|
||||
goto fail;
|
||||
for(i = 0; i < p->len; ) {
|
||||
c = string_getc(p, &i);
|
||||
switch(c) {
|
||||
case '\t':
|
||||
c = 't';
|
||||
goto quote;
|
||||
case '\r':
|
||||
c = 'r';
|
||||
goto quote;
|
||||
case '\n':
|
||||
c = 'n';
|
||||
goto quote;
|
||||
case '\b':
|
||||
c = 'b';
|
||||
goto quote;
|
||||
case '\f':
|
||||
c = 'f';
|
||||
goto quote;
|
||||
case '\"':
|
||||
case '\\':
|
||||
quote:
|
||||
if (string_buffer_putc8(b, '\\'))
|
||||
goto fail;
|
||||
if (string_buffer_putc8(b, c))
|
||||
goto fail;
|
||||
break;
|
||||
default:
|
||||
if (c < 32 || (c >= 0xd800 && c < 0xe000)) {
|
||||
snprintf(buf, sizeof(buf), "\\u%04x", c);
|
||||
if (string_buffer_puts8(b, buf))
|
||||
goto fail;
|
||||
} else {
|
||||
if (string_buffer_putc(b, c))
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (string_buffer_putc8(b, '\"'))
|
||||
goto fail;
|
||||
JS_FreeValue(ctx, val);
|
||||
return string_buffer_end(b);
|
||||
fail:
|
||||
JS_FreeValue(ctx, val);
|
||||
string_buffer_free(b);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) {
|
||||
JSValue r = JS_ToQuotedString(ctx, val);
|
||||
JS_FreeValue(ctx, val);
|
||||
return r;
|
||||
}
|
||||
|
||||
static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
|
||||
JSValueConst holder, JSValue val, JSValueConst key)
|
||||
{
|
||||
JSValue v;
|
||||
JSValueConst args[2];
|
||||
if (JS_IsObject(val)
|
||||
#ifdef CONFIG_BIGNUM
|
||||
|| JS_IsBigInt(ctx, val) /* XXX: probably useless */
|
||||
#endif
|
||||
) {
|
||||
JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);
|
||||
if (JS_IsException(f))
|
||||
goto exception;
|
||||
if (JS_IsFunction(ctx, f)) {
|
||||
v = JS_CallFree(ctx, f, val, 1, &key);
|
||||
JS_FreeValue(ctx, val);
|
||||
val = v;
|
||||
if (JS_IsException(val))
|
||||
goto exception;
|
||||
} else {
|
||||
JS_FreeValue(ctx, f);
|
||||
}
|
||||
}
|
||||
if (!JS_IsUndefined(jsc->replacer_func)) {
|
||||
args[0] = key;
|
||||
args[1] = val;
|
||||
v = JS_Call(ctx, jsc->replacer_func, holder, 2, args);
|
||||
JS_FreeValue(ctx, val);
|
||||
val = v;
|
||||
if (JS_IsException(val))
|
||||
goto exception;
|
||||
}
|
||||
switch (JS_VALUE_GET_NORM_TAG(val)) {
|
||||
case JS_TAG_OBJECT:
|
||||
if (JS_IsFunction(ctx, val))
|
||||
break;
|
||||
case JS_TAG_STRING:
|
||||
case JS_TAG_INT:
|
||||
case JS_TAG_FLOAT64:
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_TAG_BIG_FLOAT:
|
||||
#endif
|
||||
case JS_TAG_BOOL:
|
||||
case JS_TAG_NULL:
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_TAG_BIG_INT:
|
||||
#endif
|
||||
case JS_TAG_EXCEPTION:
|
||||
return val;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_UNDEFINED;
|
||||
exception:
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
|
||||
JSValueConst holder, JSValue val,
|
||||
JSValueConst indent)
|
||||
{
|
||||
JSValue indent1, sep, sep1, tab, v, prop;
|
||||
JSObject *p;
|
||||
int64_t i, len;
|
||||
int cl, ret;
|
||||
BOOL has_content;
|
||||
indent1 = JS_UNDEFINED;
|
||||
sep = JS_UNDEFINED;
|
||||
sep1 = JS_UNDEFINED;
|
||||
tab = JS_UNDEFINED;
|
||||
prop = JS_UNDEFINED;
|
||||
switch (JS_VALUE_GET_NORM_TAG(val)) {
|
||||
case JS_TAG_OBJECT:
|
||||
p = JS_VALUE_GET_OBJ(val);
|
||||
cl = p->class_id;
|
||||
if (cl == JS_CLASS_STRING) {
|
||||
val = JS_ToStringFree(ctx, val);
|
||||
if (JS_IsException(val))
|
||||
goto exception;
|
||||
val = JS_ToQuotedStringFree(ctx, val);
|
||||
if (JS_IsException(val))
|
||||
goto exception;
|
||||
return string_buffer_concat_value_free(jsc->b, val);
|
||||
} else if (cl == JS_CLASS_NUMBER) {
|
||||
val = JS_ToNumberFree(ctx, val);
|
||||
if (JS_IsException(val))
|
||||
goto exception;
|
||||
return string_buffer_concat_value_free(jsc->b, val);
|
||||
} else if (cl == JS_CLASS_BOOLEAN) {
|
||||
ret = string_buffer_concat_value(jsc->b, p->u.object_data);
|
||||
JS_FreeValue(ctx, val);
|
||||
return ret;
|
||||
}
|
||||
#ifdef CONFIG_BIGNUM
|
||||
else if (cl == JS_CLASS_BIG_FLOAT) {
|
||||
return string_buffer_concat_value_free(jsc->b, val);
|
||||
} else if (cl == JS_CLASS_BIG_INT) {
|
||||
JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");
|
||||
goto exception;
|
||||
}
|
||||
#endif
|
||||
v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);
|
||||
if (JS_IsException(v))
|
||||
goto exception;
|
||||
if (JS_ToBoolFree(ctx, v)) {
|
||||
JS_ThrowTypeError(ctx, "circular reference");
|
||||
goto exception;
|
||||
}
|
||||
indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap));
|
||||
if (JS_IsException(indent1))
|
||||
goto exception;
|
||||
if (!JS_IsEmptyString(jsc->gap)) {
|
||||
sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), "");
|
||||
if (JS_IsException(sep))
|
||||
goto exception;
|
||||
sep1 = JS_NewString(ctx, " ");
|
||||
if (JS_IsException(sep1))
|
||||
goto exception;
|
||||
} else {
|
||||
sep = JS_DupValue(ctx, jsc->empty);
|
||||
sep1 = JS_DupValue(ctx, jsc->empty);
|
||||
}
|
||||
v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0);
|
||||
if (check_exception_free(ctx, v))
|
||||
goto exception;
|
||||
ret = JS_IsArray(ctx, val);
|
||||
if (ret < 0)
|
||||
goto exception;
|
||||
if (ret) {
|
||||
if (js_get_length64(ctx, &len, val))
|
||||
goto exception;
|
||||
string_buffer_putc8(jsc->b, '[');
|
||||
for(i = 0; i < len; i++) {
|
||||
if (i > 0)
|
||||
string_buffer_putc8(jsc->b, ',');
|
||||
string_buffer_concat_value(jsc->b, sep);
|
||||
v = JS_GetPropertyInt64(ctx, val, i);
|
||||
if (JS_IsException(v))
|
||||
goto exception;
|
||||
/* XXX: could do this string conversion only when needed */
|
||||
prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i));
|
||||
if (JS_IsException(prop))
|
||||
goto exception;
|
||||
v = js_json_check(ctx, jsc, val, v, prop);
|
||||
JS_FreeValue(ctx, prop);
|
||||
prop = JS_UNDEFINED;
|
||||
if (JS_IsException(v))
|
||||
goto exception;
|
||||
if (JS_IsUndefined(v))
|
||||
v = JS_NULL;
|
||||
if (js_json_to_str(ctx, jsc, val, v, indent1))
|
||||
goto exception;
|
||||
}
|
||||
if (len > 0 && !JS_IsEmptyString(jsc->gap)) {
|
||||
string_buffer_putc8(jsc->b, '\n');
|
||||
string_buffer_concat_value(jsc->b, indent);
|
||||
}
|
||||
string_buffer_putc8(jsc->b, ']');
|
||||
} else {
|
||||
if (!JS_IsUndefined(jsc->property_list))
|
||||
tab = JS_DupValue(ctx, jsc->property_list);
|
||||
else
|
||||
tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY);
|
||||
if (JS_IsException(tab))
|
||||
goto exception;
|
||||
if (js_get_length64(ctx, &len, tab))
|
||||
goto exception;
|
||||
string_buffer_putc8(jsc->b, '{');
|
||||
has_content = FALSE;
|
||||
for(i = 0; i < len; i++) {
|
||||
JS_FreeValue(ctx, prop);
|
||||
prop = JS_GetPropertyInt64(ctx, tab, i);
|
||||
if (JS_IsException(prop))
|
||||
goto exception;
|
||||
v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop));
|
||||
if (JS_IsException(v))
|
||||
goto exception;
|
||||
v = js_json_check(ctx, jsc, val, v, prop);
|
||||
if (JS_IsException(v))
|
||||
goto exception;
|
||||
if (!JS_IsUndefined(v)) {
|
||||
if (has_content)
|
||||
string_buffer_putc8(jsc->b, ',');
|
||||
prop = JS_ToQuotedStringFree(ctx, prop);
|
||||
if (JS_IsException(prop)) {
|
||||
JS_FreeValue(ctx, v);
|
||||
goto exception;
|
||||
}
|
||||
string_buffer_concat_value(jsc->b, sep);
|
||||
string_buffer_concat_value(jsc->b, prop);
|
||||
string_buffer_putc8(jsc->b, ':');
|
||||
string_buffer_concat_value(jsc->b, sep1);
|
||||
if (js_json_to_str(ctx, jsc, val, v, indent1))
|
||||
goto exception;
|
||||
has_content = TRUE;
|
||||
}
|
||||
}
|
||||
if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) {
|
||||
string_buffer_putc8(jsc->b, '\n');
|
||||
string_buffer_concat_value(jsc->b, indent);
|
||||
}
|
||||
string_buffer_putc8(jsc->b, '}');
|
||||
}
|
||||
if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0)))
|
||||
goto exception;
|
||||
JS_FreeValue(ctx, val);
|
||||
JS_FreeValue(ctx, tab);
|
||||
JS_FreeValue(ctx, sep);
|
||||
JS_FreeValue(ctx, sep1);
|
||||
JS_FreeValue(ctx, indent1);
|
||||
JS_FreeValue(ctx, prop);
|
||||
return 0;
|
||||
case JS_TAG_STRING:
|
||||
val = JS_ToQuotedStringFree(ctx, val);
|
||||
if (JS_IsException(val))
|
||||
goto exception;
|
||||
goto concat_value;
|
||||
case JS_TAG_FLOAT64:
|
||||
if (!isfinite(JS_VALUE_GET_FLOAT64(val))) {
|
||||
val = JS_NULL;
|
||||
}
|
||||
goto concat_value;
|
||||
case JS_TAG_INT:
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_TAG_BIG_FLOAT:
|
||||
#endif
|
||||
case JS_TAG_BOOL:
|
||||
case JS_TAG_NULL:
|
||||
concat_value:
|
||||
return string_buffer_concat_value_free(jsc->b, val);
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_TAG_BIG_INT:
|
||||
JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");
|
||||
goto exception;
|
||||
#endif
|
||||
default:
|
||||
JS_FreeValue(ctx, val);
|
||||
return 0;
|
||||
}
|
||||
exception:
|
||||
JS_FreeValue(ctx, val);
|
||||
JS_FreeValue(ctx, tab);
|
||||
JS_FreeValue(ctx, sep);
|
||||
JS_FreeValue(ctx, sep1);
|
||||
JS_FreeValue(ctx, indent1);
|
||||
JS_FreeValue(ctx, prop);
|
||||
return -1;
|
||||
}
|
||||
|
||||
JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
|
||||
JSValueConst replacer, JSValueConst space0)
|
||||
{
|
||||
StringBuffer b_s;
|
||||
JSONStringifyContext jsc_s, *jsc = &jsc_s;
|
||||
JSValue val, v, space, ret, wrapper;
|
||||
int res;
|
||||
int64_t i, j, n;
|
||||
jsc->replacer_func = JS_UNDEFINED;
|
||||
jsc->stack = JS_UNDEFINED;
|
||||
jsc->property_list = JS_UNDEFINED;
|
||||
jsc->gap = JS_UNDEFINED;
|
||||
jsc->b = &b_s;
|
||||
jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string);
|
||||
ret = JS_UNDEFINED;
|
||||
wrapper = JS_UNDEFINED;
|
||||
string_buffer_init(ctx, jsc->b, 0);
|
||||
jsc->stack = JS_NewArray(ctx);
|
||||
if (JS_IsException(jsc->stack))
|
||||
goto exception;
|
||||
if (JS_IsFunction(ctx, replacer)) {
|
||||
jsc->replacer_func = replacer;
|
||||
} else {
|
||||
res = JS_IsArray(ctx, replacer);
|
||||
if (res < 0)
|
||||
goto exception;
|
||||
if (res) {
|
||||
/* XXX: enumeration is not fully correct */
|
||||
jsc->property_list = JS_NewArray(ctx);
|
||||
if (JS_IsException(jsc->property_list))
|
||||
goto exception;
|
||||
if (js_get_length64(ctx, &n, replacer))
|
||||
goto exception;
|
||||
for (i = j = 0; i < n; i++) {
|
||||
JSValue present;
|
||||
v = JS_GetPropertyInt64(ctx, replacer, i);
|
||||
if (JS_IsException(v))
|
||||
goto exception;
|
||||
if (JS_IsObject(v)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(v);
|
||||
if (p->class_id == JS_CLASS_STRING ||
|
||||
p->class_id == JS_CLASS_NUMBER) {
|
||||
v = JS_ToStringFree(ctx, v);
|
||||
if (JS_IsException(v))
|
||||
goto exception;
|
||||
} else {
|
||||
JS_FreeValue(ctx, v);
|
||||
continue;
|
||||
}
|
||||
} else if (JS_IsNumber(v)) {
|
||||
v = JS_ToStringFree(ctx, v);
|
||||
if (JS_IsException(v))
|
||||
goto exception;
|
||||
} else if (!JS_IsString(v)) {
|
||||
JS_FreeValue(ctx, v);
|
||||
continue;
|
||||
}
|
||||
present = js_array_includes(ctx, jsc->property_list,
|
||||
1, (JSValueConst *)&v);
|
||||
if (JS_IsException(present)) {
|
||||
JS_FreeValue(ctx, v);
|
||||
goto exception;
|
||||
}
|
||||
if (!JS_ToBoolFree(ctx, present)) {
|
||||
JS_SetPropertyInt64(ctx, jsc->property_list, j++, v);
|
||||
} else {
|
||||
JS_FreeValue(ctx, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
space = JS_DupValue(ctx, space0);
|
||||
if (JS_IsObject(space)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(space);
|
||||
if (p->class_id == JS_CLASS_NUMBER) {
|
||||
space = JS_ToNumberFree(ctx, space);
|
||||
} else if (p->class_id == JS_CLASS_STRING) {
|
||||
space = JS_ToStringFree(ctx, space);
|
||||
}
|
||||
if (JS_IsException(space)) {
|
||||
JS_FreeValue(ctx, space);
|
||||
goto exception;
|
||||
}
|
||||
}
|
||||
if (JS_IsNumber(space)) {
|
||||
int n;
|
||||
if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0))
|
||||
goto exception;
|
||||
jsc->gap = JS_NewStringLen(ctx, " ", n);
|
||||
} else if (JS_IsString(space)) {
|
||||
JSString *p = JS_VALUE_GET_STRING(space);
|
||||
jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10));
|
||||
} else {
|
||||
jsc->gap = JS_DupValue(ctx, jsc->empty);
|
||||
}
|
||||
JS_FreeValue(ctx, space);
|
||||
if (JS_IsException(jsc->gap))
|
||||
goto exception;
|
||||
wrapper = JS_NewObject(ctx);
|
||||
if (JS_IsException(wrapper))
|
||||
goto exception;
|
||||
if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string,
|
||||
JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0)
|
||||
goto exception;
|
||||
val = JS_DupValue(ctx, obj);
|
||||
val = js_json_check(ctx, jsc, wrapper, val, jsc->empty);
|
||||
if (JS_IsException(val))
|
||||
goto exception;
|
||||
if (JS_IsUndefined(val)) {
|
||||
ret = JS_UNDEFINED;
|
||||
goto done1;
|
||||
}
|
||||
if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty))
|
||||
goto exception;
|
||||
ret = string_buffer_end(jsc->b);
|
||||
goto done;
|
||||
exception:
|
||||
ret = JS_EXCEPTION;
|
||||
done1:
|
||||
string_buffer_free(jsc->b);
|
||||
done:
|
||||
JS_FreeValue(ctx, wrapper);
|
||||
JS_FreeValue(ctx, jsc->empty);
|
||||
JS_FreeValue(ctx, jsc->gap);
|
||||
JS_FreeValue(ctx, jsc->property_list);
|
||||
JS_FreeValue(ctx, jsc->stack);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
// stringify(val, replacer, space)
|
||||
return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]);
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_json_funcs[] = {
|
||||
JS_CFUNC_DEF("parse", 2, js_json_parse ),
|
||||
JS_CFUNC_DEF("stringify", 3, js_json_stringify ),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_json_obj[] = {
|
||||
JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
void JS_AddIntrinsicJSON(JSContext *ctx)
|
||||
{
|
||||
/* add JSON as autoinit object */
|
||||
JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj));
|
||||
}
|
65
third_party/quickjs/leb128.c
vendored
Normal file
65
third_party/quickjs/leb128.c
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/bits/likely.h"
|
||||
#include "third_party/quickjs/leb128.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
int get_leb128(uint32_t *pval, const uint8_t *buf, const uint8_t *buf_end)
|
||||
{
|
||||
const uint8_t *ptr = buf;
|
||||
uint32_t v, a, i;
|
||||
v = 0;
|
||||
for(i = 0; i < 5; i++) {
|
||||
if (UNLIKELY(ptr >= buf_end))
|
||||
break;
|
||||
a = *ptr++;
|
||||
v |= (a & 0x7f) << (i * 7);
|
||||
if (!(a & 0x80)) {
|
||||
*pval = v;
|
||||
return ptr - buf;
|
||||
}
|
||||
}
|
||||
*pval = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int get_sleb128(int32_t *pval, const uint8_t *buf, const uint8_t *buf_end)
|
||||
{
|
||||
int ret;
|
||||
uint32_t val;
|
||||
ret = get_leb128(&val, buf, buf_end);
|
||||
if (ret < 0) {
|
||||
*pval = 0;
|
||||
return -1;
|
||||
}
|
||||
*pval = (val >> 1) ^ -(val & 1);
|
||||
return ret;
|
||||
}
|
11
third_party/quickjs/leb128.h
vendored
Normal file
11
third_party/quickjs/leb128.h
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef COSMOPOLITAN_THIRD_PARTY_QUICKJS_LEB128_H_
|
||||
#define COSMOPOLITAN_THIRD_PARTY_QUICKJS_LEB128_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
int get_leb128(uint32_t *, const uint8_t *, const uint8_t *);
|
||||
int get_sleb128(int32_t *, const uint8_t *, const uint8_t *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_QUICKJS_LEB128_H_ */
|
76
third_party/quickjs/libbf.c
vendored
76
third_party/quickjs/libbf.c
vendored
|
@ -23,10 +23,12 @@
|
|||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/avxintrin.internal.h"
|
||||
#include "libc/bits/likely.h"
|
||||
#include "libc/inttypes.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/quickjs/cutils.h"
|
||||
#include "third_party/quickjs/diglet.h"
|
||||
#include "third_party/quickjs/libbf.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
|
@ -83,9 +85,9 @@ typedef int bf_op2_func_t(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
|
|||
#define FFT_MUL_R_OVERLAP_B (1 << 1)
|
||||
#define FFT_MUL_R_NORESIZE (1 << 2)
|
||||
|
||||
static no_inline int fft_mul(bf_context_t *s,
|
||||
bf_t *res, limb_t *a_tab, limb_t a_len,
|
||||
limb_t *b_tab, limb_t b_len, int mul_flags);
|
||||
static noinline int fft_mul(bf_context_t *s,
|
||||
bf_t *res, limb_t *a_tab, limb_t a_len,
|
||||
limb_t *b_tab, limb_t b_len, int mul_flags);
|
||||
static void fft_clear_cache(bf_context_t *s);
|
||||
#endif
|
||||
#ifdef USE_BF_DEC
|
||||
|
@ -502,7 +504,7 @@ static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l,
|
|||
prec = r->expn + prec1;
|
||||
else
|
||||
prec = prec1;
|
||||
} else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) {
|
||||
} else if (UNLIKELY(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) {
|
||||
/* restrict the precision in case of potentially subnormal
|
||||
result */
|
||||
assert(prec1 != BF_PREC_INF);
|
||||
|
@ -553,7 +555,7 @@ static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l,
|
|||
}
|
||||
|
||||
/* check underflow */
|
||||
if (unlikely(r->expn < e_min)) {
|
||||
if (UNLIKELY(r->expn < e_min)) {
|
||||
if (flags & BF_FLAG_SUBNORMAL) {
|
||||
/* if inexact, also set the underflow flag */
|
||||
if (ret & BF_ST_INEXACT)
|
||||
|
@ -567,7 +569,7 @@ static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l,
|
|||
}
|
||||
|
||||
/* check overflow */
|
||||
if (unlikely(r->expn > e_max))
|
||||
if (UNLIKELY(r->expn > e_max))
|
||||
return bf_set_overflow(r, r->sign, prec1, flags);
|
||||
|
||||
/* keep the bits starting at 'prec - 1' */
|
||||
|
@ -1181,7 +1183,7 @@ int mp_mul(bf_context_t *s, limb_t *result,
|
|||
const limb_t *op2, limb_t op2_size)
|
||||
{
|
||||
#ifdef USE_FFT_MUL
|
||||
if (unlikely(bf_min(op1_size, op2_size) >= FFT_MUL_THRESHOLD)) {
|
||||
if (UNLIKELY(bf_min(op1_size, op2_size) >= FFT_MUL_THRESHOLD)) {
|
||||
bf_t r_s, *r = &r_s;
|
||||
r->tab = result;
|
||||
/* XXX: optimize memory usage in API */
|
||||
|
@ -1311,7 +1313,7 @@ static int mp_divnorm(bf_context_t *s, limb_t *tabq, limb_t *taba, limb_t na,
|
|||
}
|
||||
|
||||
for(i = n - 1; i >= 0; i--) {
|
||||
if (unlikely(taba[i + nb] >= b1)) {
|
||||
if (UNLIKELY(taba[i + nb] >= b1)) {
|
||||
q = -1;
|
||||
} else if (b1_inv) {
|
||||
q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv);
|
||||
|
@ -1959,7 +1961,7 @@ static limb_t mp_sqrtrem2(limb_t *tabs, limb_t *taba)
|
|||
u = num % (2 * s1);
|
||||
s = (s1 << l) + q;
|
||||
r = ((dlimb_t)u << l) | (a0 & (((limb_t)1 << l) - 1));
|
||||
if (unlikely((q >> l) != 0))
|
||||
if (UNLIKELY((q >> l) != 0))
|
||||
r -= (dlimb_t)1 << LIMB_BITS; /* special case when q=2^l */
|
||||
else
|
||||
r -= q * q;
|
||||
|
@ -2190,8 +2192,8 @@ int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
|
|||
return BF_ST_MEM_ERROR;
|
||||
}
|
||||
|
||||
static no_inline int bf_op2(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
|
||||
bf_flags_t flags, bf_op2_func_t *func)
|
||||
static noinline int bf_op2(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
|
||||
bf_flags_t flags, bf_op2_func_t *func)
|
||||
{
|
||||
bf_t tmp;
|
||||
int ret;
|
||||
|
@ -2820,25 +2822,13 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline int to_digit(int c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
else if (c >= 'A' && c <= 'Z')
|
||||
return c - 'A' + 10;
|
||||
else if (c >= 'a' && c <= 'z')
|
||||
return c - 'a' + 10;
|
||||
else
|
||||
return 36;
|
||||
}
|
||||
|
||||
/* add a limb at 'pos' and decrement pos. new space is created if
|
||||
needed. Return 0 if OK, -1 if memory error */
|
||||
static int bf_add_limb(bf_t *a, slimb_t *ppos, limb_t v)
|
||||
{
|
||||
slimb_t pos;
|
||||
pos = *ppos;
|
||||
if (unlikely(pos < 0)) {
|
||||
if (UNLIKELY(pos < 0)) {
|
||||
limb_t new_size, d, *new_tab;
|
||||
new_size = bf_max(a->len + 1, a->len * 3 / 2);
|
||||
new_tab = bf_realloc(a->ctx, a->tab, sizeof(limb_t) * new_size);
|
||||
|
@ -3066,7 +3056,7 @@ static int bf_atof_internal(bf_t *r, slimb_t *pexponent,
|
|||
c = to_digit(*p);
|
||||
if (c >= 10)
|
||||
break;
|
||||
if (unlikely(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) {
|
||||
if (UNLIKELY(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) {
|
||||
/* exponent overflow */
|
||||
if (exp_is_neg) {
|
||||
bf_set_zero(r, is_neg);
|
||||
|
@ -3390,7 +3380,7 @@ static int bf_integer_to_radix_rec(bf_t *pow_tab,
|
|||
pos = a->len * LIMB_BITS - a->expn;
|
||||
t = ((dlimb_t)get_bits(a->tab, a->len, pos + LIMB_BITS) << LIMB_BITS) |
|
||||
get_bits(a->tab, a->len, pos);
|
||||
if (likely(radixl == RADIXL_10)) {
|
||||
if (LIKELY(radixl == RADIXL_10)) {
|
||||
/* use division by a constant when possible */
|
||||
out[0] = t % RADIXL_10;
|
||||
out[1] = t / RADIXL_10;
|
||||
|
@ -5867,7 +5857,7 @@ static int mp_div_dec(bf_context_t *s, limb_t *tabq,
|
|||
i--;
|
||||
} else {
|
||||
mult = base / (r + 1);
|
||||
if (likely(nb <= DIV_STATIC_ALLOC_LEN)) {
|
||||
if (LIKELY(nb <= DIV_STATIC_ALLOC_LEN)) {
|
||||
tabb = static_tabb;
|
||||
} else {
|
||||
tabb = bf_malloc(s, sizeof(limb_t) * nb);
|
||||
|
@ -5885,7 +5875,7 @@ static int mp_div_dec(bf_context_t *s, limb_t *tabq,
|
|||
#endif
|
||||
|
||||
for(; i >= 0; i--) {
|
||||
if (unlikely(taba[i + nb] >= tabb[nb - 1])) {
|
||||
if (UNLIKELY(taba[i + nb] >= tabb[nb - 1])) {
|
||||
/* XXX: check if it is really possible */
|
||||
q = base - 1;
|
||||
} else {
|
||||
|
@ -5930,7 +5920,7 @@ static int mp_div_dec(bf_context_t *s, limb_t *tabq,
|
|||
/* remove the normalization */
|
||||
if (mult != 1) {
|
||||
mp_div1_dec(taba, taba, nb, mult, 0);
|
||||
if (unlikely(tabb != static_tabb))
|
||||
if (UNLIKELY(tabb != static_tabb))
|
||||
bf_free(s, tabb);
|
||||
}
|
||||
return 0;
|
||||
|
@ -6435,7 +6425,7 @@ static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l)
|
|||
prec = r->expn + prec1;
|
||||
else
|
||||
prec = prec1;
|
||||
} else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) {
|
||||
} else if (UNLIKELY(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) {
|
||||
/* restrict the precision in case of potentially subnormal
|
||||
result */
|
||||
assert(prec1 != BF_PREC_INF);
|
||||
|
@ -6475,7 +6465,7 @@ static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l)
|
|||
}
|
||||
|
||||
/* check underflow */
|
||||
if (unlikely(r->expn < e_min)) {
|
||||
if (UNLIKELY(r->expn < e_min)) {
|
||||
if (flags & BF_FLAG_SUBNORMAL) {
|
||||
/* if inexact, also set the underflow flag */
|
||||
if (ret & BF_ST_INEXACT)
|
||||
|
@ -6489,7 +6479,7 @@ static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l)
|
|||
}
|
||||
|
||||
/* check overflow */
|
||||
if (unlikely(r->expn > e_max)) {
|
||||
if (UNLIKELY(r->expn > e_max)) {
|
||||
bfdec_set_inf(r, r->sign);
|
||||
ret |= BF_ST_OVERFLOW | BF_ST_INEXACT;
|
||||
return ret;
|
||||
|
@ -7580,7 +7570,7 @@ static void ntt_free(BFNTTState *s, void *ptr)
|
|||
bf_aligned_free(s->ctx, ptr);
|
||||
}
|
||||
|
||||
static no_inline int ntt_fft(BFNTTState *s,
|
||||
static noinline int ntt_fft(BFNTTState *s,
|
||||
NTTLimb *out_buf, NTTLimb *in_buf,
|
||||
NTTLimb *tmp_buf, int fft_len_log2,
|
||||
int inverse, int m_idx)
|
||||
|
@ -7713,7 +7703,7 @@ static void ntt_vec_mul(BFNTTState *s,
|
|||
}
|
||||
}
|
||||
|
||||
static no_inline void mul_trig(NTTLimb *buf,
|
||||
static noinline void mul_trig(NTTLimb *buf,
|
||||
limb_t n, limb_t c1, limb_t m, limb_t m_inv1)
|
||||
{
|
||||
limb_t i, c2, c3, c4;
|
||||
|
@ -7761,7 +7751,7 @@ static inline NTTLimb int_to_ntt_limb(slimb_t a, limb_t m)
|
|||
return a;
|
||||
}
|
||||
|
||||
static no_inline int ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf,
|
||||
static noinline int ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf,
|
||||
NTTLimb *tmp_buf, int fft_len_log2,
|
||||
int inverse, int m_idx)
|
||||
{
|
||||
|
@ -7843,7 +7833,7 @@ static void ntt_vec_mul(BFNTTState *s,
|
|||
}
|
||||
}
|
||||
|
||||
static no_inline void mul_trig(NTTLimb *buf,
|
||||
static noinline void mul_trig(NTTLimb *buf,
|
||||
limb_t n, limb_t c_mul, limb_t m, limb_t m_inv)
|
||||
{
|
||||
limb_t i, c0, c_mul_inv;
|
||||
|
@ -7858,7 +7848,7 @@ static no_inline void mul_trig(NTTLimb *buf,
|
|||
|
||||
#endif /* !AVX2 */
|
||||
|
||||
static no_inline NTTLimb *get_trig(BFNTTState *s,
|
||||
static noinline NTTLimb *get_trig(BFNTTState *s,
|
||||
int k, int inverse, int m_idx)
|
||||
{
|
||||
NTTLimb *tab;
|
||||
|
@ -8013,7 +8003,7 @@ static int ntt_conv(BFNTTState *s, NTTLimb *buf1, NTTLimb *buf2,
|
|||
}
|
||||
|
||||
|
||||
static no_inline void limb_to_ntt(BFNTTState *s,
|
||||
static noinline void limb_to_ntt(BFNTTState *s,
|
||||
NTTLimb *tabr, limb_t fft_len,
|
||||
const limb_t *taba, limb_t a_len, int dpl,
|
||||
int first_m_idx, int nb_mods)
|
||||
|
@ -8082,7 +8072,7 @@ typedef union {
|
|||
double d[4];
|
||||
} VecUnion;
|
||||
|
||||
static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len,
|
||||
static noinline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len,
|
||||
const NTTLimb *buf, int fft_len_log2, int dpl,
|
||||
int nb_mods)
|
||||
{
|
||||
|
@ -8186,7 +8176,7 @@ static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len,
|
|||
}
|
||||
}
|
||||
#else
|
||||
static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len,
|
||||
static noinline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len,
|
||||
const NTTLimb *buf, int fft_len_log2, int dpl,
|
||||
int nb_mods)
|
||||
{
|
||||
|
@ -8399,9 +8389,9 @@ int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len)
|
|||
}
|
||||
|
||||
/* return 0 if OK, -1 if memory error */
|
||||
static no_inline int fft_mul(bf_context_t *s1,
|
||||
bf_t *res, limb_t *a_tab, limb_t a_len,
|
||||
limb_t *b_tab, limb_t b_len, int mul_flags)
|
||||
static noinline int fft_mul(bf_context_t *s1,
|
||||
bf_t *res, limb_t *a_tab, limb_t a_len,
|
||||
limb_t *b_tab, limb_t b_len, int mul_flags)
|
||||
{
|
||||
BFNTTState *s;
|
||||
int dpl, fft_len_log2, j, nb_mods, reduced_mem;
|
||||
|
|
109
third_party/quickjs/libregexp.c
vendored
109
third_party/quickjs/libregexp.c
vendored
|
@ -22,6 +22,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/likely.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
|
@ -133,7 +134,7 @@ static uint32_t lre_canonicalize(uint32_t c, BOOL is_utf16)
|
|||
uint32_t res[LRE_CC_RES_LEN_MAX];
|
||||
int len;
|
||||
if (is_utf16) {
|
||||
if (likely(c < 128)) {
|
||||
if (LIKELY(c < 128)) {
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
c = c - 'A' + 'a';
|
||||
} else {
|
||||
|
@ -141,7 +142,7 @@ static uint32_t lre_canonicalize(uint32_t c, BOOL is_utf16)
|
|||
c = res[0];
|
||||
}
|
||||
} else {
|
||||
if (likely(c < 128)) {
|
||||
if (LIKELY(c < 128)) {
|
||||
if (c >= 'a' && c <= 'z')
|
||||
c = c - 'a' + 'A';
|
||||
} else {
|
||||
|
@ -233,7 +234,7 @@ static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c)
|
|||
BOOL invert;
|
||||
const uint16_t *c_pt;
|
||||
int len, i;
|
||||
|
||||
|
||||
invert = c & 1;
|
||||
c_pt = char_range_table[c >> 1];
|
||||
len = *c_pt++;
|
||||
|
@ -283,7 +284,7 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t *buf,
|
|||
{
|
||||
int pos, len, opcode, bc_len, re_flags, i;
|
||||
uint32_t val;
|
||||
|
||||
|
||||
assert(buf_len >= RE_HEADER_LEN);
|
||||
|
||||
re_flags= buf[0];
|
||||
|
@ -455,7 +456,7 @@ static int parse_digits(const uint8_t **pp, BOOL allow_overflow)
|
|||
const uint8_t *p;
|
||||
uint64_t v;
|
||||
int c;
|
||||
|
||||
|
||||
p = *pp;
|
||||
v = 0;
|
||||
for(;;) {
|
||||
|
@ -500,7 +501,6 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16)
|
|||
{
|
||||
const uint8_t *p;
|
||||
uint32_t c;
|
||||
|
||||
p = *pp;
|
||||
c = *p++;
|
||||
switch(c) {
|
||||
|
@ -527,7 +527,7 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16)
|
|||
{
|
||||
int h, n, i;
|
||||
uint32_t c1;
|
||||
|
||||
|
||||
if (*p == '{' && allow_utf16) {
|
||||
p++;
|
||||
c = 0;
|
||||
|
@ -548,7 +548,6 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16)
|
|||
} else {
|
||||
n = 4;
|
||||
}
|
||||
|
||||
c = 0;
|
||||
for(i = 0; i < n; i++) {
|
||||
h = from_hex(*p++);
|
||||
|
@ -721,7 +720,7 @@ static int get_class_atom(REParseState *s, CharRange *cr,
|
|||
const uint8_t *p;
|
||||
uint32_t c;
|
||||
int ret;
|
||||
|
||||
|
||||
p = *pp;
|
||||
|
||||
c = *p;
|
||||
|
@ -829,7 +828,7 @@ static int re_emit_range(REParseState *s, const CharRange *cr)
|
|||
{
|
||||
int len, i;
|
||||
uint32_t high;
|
||||
|
||||
|
||||
len = (unsigned)cr->len / 2;
|
||||
if (len >= 65535)
|
||||
return re_parse_error(s, "too many ranges");
|
||||
|
@ -870,7 +869,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
|
|||
CharRange cr_s, *cr = &cr_s;
|
||||
CharRange cr1_s, *cr1 = &cr1_s;
|
||||
BOOL invert;
|
||||
|
||||
|
||||
cr_init(cr, s->opaque, lre_realloc);
|
||||
p = *pp;
|
||||
p++; /* skip '[' */
|
||||
|
@ -960,12 +959,12 @@ static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len)
|
|||
uint32_t val, last;
|
||||
BOOL has_back_reference;
|
||||
uint8_t capture_bitmap[CAPTURE_COUNT_MAX];
|
||||
|
||||
|
||||
ret = -2; /* not known yet */
|
||||
pos = 0;
|
||||
has_back_reference = FALSE;
|
||||
memset(capture_bitmap, 0, sizeof(capture_bitmap));
|
||||
|
||||
|
||||
while (pos < bc_buf_len) {
|
||||
opcode = bc_buf[pos];
|
||||
len = reopcode_info[opcode].size;
|
||||
|
@ -1042,7 +1041,7 @@ static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len)
|
|||
{
|
||||
int pos, opcode, len, count;
|
||||
uint32_t val;
|
||||
|
||||
|
||||
count = 0;
|
||||
pos = 0;
|
||||
while (pos < bc_buf_len) {
|
||||
|
@ -1202,7 +1201,7 @@ static int find_group_name(REParseState *s, const char *name)
|
|||
const char *p, *buf_end;
|
||||
size_t len, name_len;
|
||||
int capture_index;
|
||||
|
||||
|
||||
name_len = strlen(name);
|
||||
p = (char *)s->group_names.buf;
|
||||
buf_end = (char *)s->group_names.buf + s->group_names.size;
|
||||
|
@ -1225,7 +1224,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
int c, last_atom_start, quant_min, quant_max, last_capture_count;
|
||||
BOOL greedy, add_zero_advance_check, is_neg, is_backward_lookahead;
|
||||
CharRange cr_s, *cr = &cr_s;
|
||||
|
||||
|
||||
last_atom_start = -1;
|
||||
last_capture_count = 0;
|
||||
p = s->buf_ptr;
|
||||
|
@ -1348,15 +1347,15 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
capture_index = s->capture_count++;
|
||||
re_emit_op_u8(s, REOP_save_start + is_backward_dir,
|
||||
capture_index);
|
||||
|
||||
|
||||
s->buf_ptr = p;
|
||||
if (re_parse_disjunction(s, is_backward_dir))
|
||||
return -1;
|
||||
p = s->buf_ptr;
|
||||
|
||||
|
||||
re_emit_op_u8(s, REOP_save_start + 1 - is_backward_dir,
|
||||
capture_index);
|
||||
|
||||
|
||||
if (re_parse_expect(s, &p, ')'))
|
||||
return -1;
|
||||
}
|
||||
|
@ -1372,7 +1371,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
{
|
||||
const uint8_t *p1;
|
||||
int dummy_res;
|
||||
|
||||
|
||||
p1 = p;
|
||||
if (p1[2] != '<') {
|
||||
/* annex B: we tolerate invalid group names in non
|
||||
|
@ -1425,10 +1424,10 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
goto normal_char;
|
||||
case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8':
|
||||
case '9':
|
||||
case '9':
|
||||
{
|
||||
const uint8_t *q = ++p;
|
||||
|
||||
|
||||
c = parse_digits(&p, FALSE);
|
||||
if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) {
|
||||
if (!s->is_utf16) {
|
||||
|
@ -1569,7 +1568,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
}
|
||||
if (greedy) {
|
||||
int len, pos;
|
||||
|
||||
|
||||
if (quant_max > 0) {
|
||||
/* specific optimization for simple quantifiers */
|
||||
if (dbuf_error(&s->byte_code))
|
||||
|
@ -1578,7 +1577,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
s->byte_code.size - last_atom_start);
|
||||
if (len > 0) {
|
||||
re_emit_op(s, REOP_match);
|
||||
|
||||
|
||||
if (dbuf_insert(&s->byte_code, last_atom_start, 17))
|
||||
goto out_of_memory;
|
||||
pos = last_atom_start;
|
||||
|
@ -1595,7 +1594,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (dbuf_error(&s->byte_code))
|
||||
goto out_of_memory;
|
||||
add_zero_advance_check = (re_check_advance(s->byte_code.buf + last_atom_start,
|
||||
|
@ -1603,7 +1602,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
} else {
|
||||
add_zero_advance_check = FALSE;
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
int len, pos;
|
||||
len = s->byte_code.size - last_atom_start;
|
||||
|
@ -1638,7 +1637,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
atom (only works if the atom has no
|
||||
side effect) */
|
||||
s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos;
|
||||
re_emit_goto(s, REOP_bne_char_pos, last_atom_start);
|
||||
re_emit_goto(s, REOP_bne_char_pos, last_atom_start);
|
||||
} else {
|
||||
re_emit_goto(s, REOP_goto, last_atom_start);
|
||||
}
|
||||
|
@ -1689,7 +1688,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
re_emit_op_u32(s, REOP_split_goto_first + greedy, len + 5);
|
||||
/* copy the atom */
|
||||
dbuf_put_self(&s->byte_code, last_atom_start, len);
|
||||
|
||||
|
||||
re_emit_goto(s, REOP_loop, pos);
|
||||
re_emit_op(s, REOP_drop);
|
||||
}
|
||||
|
@ -1741,14 +1740,14 @@ static int re_parse_alternative(REParseState *s, BOOL is_backward_dir)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir)
|
||||
{
|
||||
int start, len, pos;
|
||||
|
||||
if (lre_check_stack_overflow(s->opaque, 0))
|
||||
return re_parse_error(s, "stack overflow");
|
||||
|
||||
|
||||
start = s->byte_code.size;
|
||||
if (re_parse_alternative(s, is_backward_dir))
|
||||
return -1;
|
||||
|
@ -1768,7 +1767,7 @@ static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir)
|
|||
|
||||
if (re_parse_alternative(s, is_backward_dir))
|
||||
return -1;
|
||||
|
||||
|
||||
/* patch the goto */
|
||||
len = s->byte_code.size - (pos + 4);
|
||||
put_u32(s->byte_code.buf + pos, len);
|
||||
|
@ -1781,7 +1780,7 @@ static int compute_stack_size(const uint8_t *bc_buf, int bc_buf_len)
|
|||
{
|
||||
int stack_size, stack_size_max, pos, opcode, len;
|
||||
uint32_t val;
|
||||
|
||||
|
||||
stack_size = 0;
|
||||
stack_size_max = 0;
|
||||
bc_buf += RE_HEADER_LEN;
|
||||
|
@ -1832,7 +1831,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
|
|||
REParseState s_s, *s = &s_s;
|
||||
int stack_size;
|
||||
BOOL is_sticky;
|
||||
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
s->opaque = opaque;
|
||||
s->buf_ptr = (const uint8_t *)buf;
|
||||
|
@ -1846,7 +1845,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
|
|||
s->capture_count = 1;
|
||||
s->total_capture_count = -1;
|
||||
s->has_named_captures = -1;
|
||||
|
||||
|
||||
dbuf_init2(&s->byte_code, opaque, lre_realloc);
|
||||
dbuf_init2(&s->group_names, opaque, lre_realloc);
|
||||
|
||||
|
@ -1854,7 +1853,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
|
|||
dbuf_putc(&s->byte_code, 0); /* second element is the number of captures */
|
||||
dbuf_putc(&s->byte_code, 0); /* stack size */
|
||||
dbuf_put_u32(&s->byte_code, 0); /* bytecode length */
|
||||
|
||||
|
||||
if (!is_sticky) {
|
||||
/* iterate thru all positions (about the same as .*?( ... ) )
|
||||
. We do it without an explicit loop so that lock step
|
||||
|
@ -1876,7 +1875,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
|
|||
}
|
||||
|
||||
re_emit_op_u8(s, REOP_save_end, 0);
|
||||
|
||||
|
||||
re_emit_op(s, REOP_match);
|
||||
|
||||
if (*s->buf_ptr != '\0') {
|
||||
|
@ -1888,13 +1887,13 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
|
|||
re_parse_out_of_memory(s);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
stack_size = compute_stack_size(s->byte_code.buf, s->byte_code.size);
|
||||
if (stack_size < 0) {
|
||||
re_parse_error(s, "too many imbricated quantifiers");
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count;
|
||||
s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size;
|
||||
put_u32(s->byte_code.buf + 3, s->byte_code.size - RE_HEADER_LEN);
|
||||
|
@ -1905,11 +1904,11 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
|
|||
s->byte_code.buf[RE_HEADER_FLAGS] |= LRE_FLAG_NAMED_GROUPS;
|
||||
}
|
||||
dbuf_free(&s->group_names);
|
||||
|
||||
|
||||
#ifdef DUMP_REOP
|
||||
lre_dump_bytecode(s->byte_code.buf, s->byte_code.size);
|
||||
#endif
|
||||
|
||||
|
||||
error_msg[0] = '\0';
|
||||
*plen = s->byte_code.size;
|
||||
return s->byte_code.buf;
|
||||
|
@ -2040,7 +2039,7 @@ typedef struct {
|
|||
const uint8_t *cbuf;
|
||||
const uint8_t *cbuf_end;
|
||||
/* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16 */
|
||||
int cbuf_type;
|
||||
int cbuf_type;
|
||||
int capture_count;
|
||||
int stack_size_max;
|
||||
BOOL multi_line;
|
||||
|
@ -2065,7 +2064,7 @@ static int push_state(REExecContext *s,
|
|||
size_t new_size, i, n;
|
||||
StackInt *stack_buf;
|
||||
|
||||
if (unlikely((s->state_stack_len + 1) > s->state_stack_size)) {
|
||||
if (UNLIKELY((s->state_stack_len + 1) > s->state_stack_size)) {
|
||||
/* reallocate the stack */
|
||||
new_size = s->state_stack_size * 3 / 2;
|
||||
if (new_size < 8)
|
||||
|
@ -2102,7 +2101,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
int cbuf_type;
|
||||
uint32_t val, c;
|
||||
const uint8_t *cbuf_end;
|
||||
|
||||
|
||||
cbuf_type = s->cbuf_type;
|
||||
cbuf_end = s->cbuf_end;
|
||||
|
||||
|
@ -2200,7 +2199,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
case REOP_split_next_first:
|
||||
{
|
||||
const uint8_t *pc1;
|
||||
|
||||
|
||||
val = get_u32(pc);
|
||||
pc += 4;
|
||||
if (opcode == REOP_split_next_first) {
|
||||
|
@ -2226,7 +2225,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
if (ret < 0)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
|
||||
case REOP_goto:
|
||||
val = get_u32(pc);
|
||||
pc += 4 + (int)val;
|
||||
|
@ -2332,7 +2331,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
{
|
||||
const uint8_t *cptr1, *cptr1_end, *cptr1_start;
|
||||
uint32_t c1, c2;
|
||||
|
||||
|
||||
val = *pc++;
|
||||
if (val >= s->capture_count)
|
||||
goto no_match;
|
||||
|
@ -2375,7 +2374,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
{
|
||||
int n;
|
||||
uint32_t low, high, idx_min, idx_max, idx;
|
||||
|
||||
|
||||
n = get_u16(pc); /* n must be >= 1 */
|
||||
pc += 2;
|
||||
if (cptr >= cbuf_end)
|
||||
|
@ -2391,7 +2390,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
idx_max = n - 1;
|
||||
high = get_u16(pc + idx_max * 4 + 2);
|
||||
/* 0xffff in for last value means +infinity */
|
||||
if (unlikely(c >= 0xffff) && high == 0xffff)
|
||||
if (UNLIKELY(c >= 0xffff) && high == 0xffff)
|
||||
goto range_match;
|
||||
if (c > high)
|
||||
goto no_match;
|
||||
|
@ -2415,7 +2414,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
{
|
||||
int n;
|
||||
uint32_t low, high, idx_min, idx_max, idx;
|
||||
|
||||
|
||||
n = get_u16(pc); /* n must be >= 1 */
|
||||
pc += 2;
|
||||
if (cptr >= cbuf_end)
|
||||
|
@ -2460,14 +2459,14 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
size_t q;
|
||||
intptr_t res;
|
||||
const uint8_t *pc1;
|
||||
|
||||
|
||||
next_pos = get_u32(pc);
|
||||
quant_min = get_u32(pc + 4);
|
||||
quant_max = get_u32(pc + 8);
|
||||
pc += 16;
|
||||
pc1 = pc;
|
||||
pc += (int)next_pos;
|
||||
|
||||
|
||||
q = 0;
|
||||
for(;;) {
|
||||
res = lre_exec_backtrack(s, capture, stack, stack_len,
|
||||
|
@ -2510,7 +2509,7 @@ int lre_exec(uint8_t **capture,
|
|||
REExecContext s_s, *s = &s_s;
|
||||
int re_flags, i, alloca_size, ret;
|
||||
StackInt *stack_buf;
|
||||
|
||||
|
||||
re_flags = bc_buf[RE_HEADER_FLAGS];
|
||||
s->multi_line = (re_flags & LRE_FLAG_MULTILINE) != 0;
|
||||
s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0;
|
||||
|
@ -2530,7 +2529,7 @@ int lre_exec(uint8_t **capture,
|
|||
s->state_stack = NULL;
|
||||
s->state_stack_len = 0;
|
||||
s->state_stack_size = 0;
|
||||
|
||||
|
||||
for(i = 0; i < s->capture_count * 2; i++)
|
||||
capture[i] = NULL;
|
||||
alloca_size = s->stack_size_max * sizeof(stack_buf[0]);
|
||||
|
@ -2582,7 +2581,7 @@ int main(int argc, char **argv)
|
|||
uint8_t *capture[CAPTURE_COUNT_MAX * 2];
|
||||
const char *input;
|
||||
int input_len, capture_count;
|
||||
|
||||
|
||||
if (argc < 3) {
|
||||
printf("usage: %s regexp input\n", argv[0]);
|
||||
exit(1);
|
||||
|
@ -2596,7 +2595,7 @@ int main(int argc, char **argv)
|
|||
|
||||
input = argv[2];
|
||||
input_len = strlen(input);
|
||||
|
||||
|
||||
ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL);
|
||||
printf("ret=%d\n", ret);
|
||||
if (ret == 1) {
|
||||
|
|
748
third_party/quickjs/map.c
vendored
Normal file
748
third_party/quickjs/map.c
vendored
Normal file
|
@ -0,0 +1,748 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
JSMapState *s;
|
||||
JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;
|
||||
JSValueConst arr;
|
||||
BOOL is_set, is_weak;
|
||||
is_set = magic & MAGIC_SET;
|
||||
is_weak = ((magic & MAGIC_WEAK) != 0);
|
||||
obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic);
|
||||
if (JS_IsException(obj))
|
||||
return JS_EXCEPTION;
|
||||
s = js_mallocz(ctx, sizeof(*s));
|
||||
if (!s)
|
||||
goto fail;
|
||||
init_list_head(&s->records);
|
||||
s->is_weak = is_weak;
|
||||
JS_SetOpaque(obj, s);
|
||||
s->hash_size = 1;
|
||||
s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size);
|
||||
if (!s->hash_table)
|
||||
goto fail;
|
||||
init_list_head(&s->hash_table[0]);
|
||||
s->record_count_threshold = 4;
|
||||
arr = JS_UNDEFINED;
|
||||
if (argc > 0)
|
||||
arr = argv[0];
|
||||
if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) {
|
||||
JSValue item, ret;
|
||||
BOOL done;
|
||||
adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set);
|
||||
if (JS_IsException(adder))
|
||||
goto fail;
|
||||
if (!JS_IsFunction(ctx, adder)) {
|
||||
JS_ThrowTypeError(ctx, "set/add is not a function");
|
||||
goto fail;
|
||||
}
|
||||
iter = JS_GetIterator(ctx, arr, FALSE);
|
||||
if (JS_IsException(iter))
|
||||
goto fail;
|
||||
next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
|
||||
if (JS_IsException(next_method))
|
||||
goto fail;
|
||||
for(;;) {
|
||||
item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
|
||||
if (JS_IsException(item))
|
||||
goto fail;
|
||||
if (done) {
|
||||
JS_FreeValue(ctx, item);
|
||||
break;
|
||||
}
|
||||
if (is_set) {
|
||||
ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item);
|
||||
if (JS_IsException(ret)) {
|
||||
JS_FreeValue(ctx, item);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
JSValue key, value;
|
||||
JSValueConst args[2];
|
||||
key = JS_UNDEFINED;
|
||||
value = JS_UNDEFINED;
|
||||
if (!JS_IsObject(item)) {
|
||||
JS_ThrowTypeErrorNotAnObject(ctx);
|
||||
goto fail1;
|
||||
}
|
||||
key = JS_GetPropertyUint32(ctx, item, 0);
|
||||
if (JS_IsException(key))
|
||||
goto fail1;
|
||||
value = JS_GetPropertyUint32(ctx, item, 1);
|
||||
if (JS_IsException(value))
|
||||
goto fail1;
|
||||
args[0] = key;
|
||||
args[1] = value;
|
||||
ret = JS_Call(ctx, adder, obj, 2, args);
|
||||
if (JS_IsException(ret)) {
|
||||
fail1:
|
||||
JS_FreeValue(ctx, item);
|
||||
JS_FreeValue(ctx, key);
|
||||
JS_FreeValue(ctx, value);
|
||||
goto fail;
|
||||
}
|
||||
JS_FreeValue(ctx, key);
|
||||
JS_FreeValue(ctx, value);
|
||||
}
|
||||
JS_FreeValue(ctx, ret);
|
||||
JS_FreeValue(ctx, item);
|
||||
}
|
||||
JS_FreeValue(ctx, next_method);
|
||||
JS_FreeValue(ctx, iter);
|
||||
JS_FreeValue(ctx, adder);
|
||||
}
|
||||
return obj;
|
||||
fail:
|
||||
if (JS_IsObject(iter)) {
|
||||
/* close the iterator object, preserving pending exception */
|
||||
JS_IteratorClose(ctx, iter, TRUE);
|
||||
}
|
||||
JS_FreeValue(ctx, next_method);
|
||||
JS_FreeValue(ctx, iter);
|
||||
JS_FreeValue(ctx, adder);
|
||||
JS_FreeValue(ctx, obj);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
/* XXX: could normalize strings to speed up comparison */
|
||||
static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)
|
||||
{
|
||||
uint32_t tag = JS_VALUE_GET_TAG(key);
|
||||
/* convert -0.0 to +0.0 */
|
||||
if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) {
|
||||
key = JS_NewInt32(ctx, 0);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
/* XXX: better hash ? */
|
||||
static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)
|
||||
{
|
||||
uint32_t tag = JS_VALUE_GET_NORM_TAG(key);
|
||||
uint32_t h;
|
||||
double d;
|
||||
JSFloat64Union u;
|
||||
switch(tag) {
|
||||
case JS_TAG_BOOL:
|
||||
h = JS_VALUE_GET_INT(key);
|
||||
break;
|
||||
case JS_TAG_STRING:
|
||||
h = hash_string(JS_VALUE_GET_STRING(key), 0);
|
||||
break;
|
||||
case JS_TAG_OBJECT:
|
||||
case JS_TAG_SYMBOL:
|
||||
h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163;
|
||||
break;
|
||||
case JS_TAG_INT:
|
||||
d = JS_VALUE_GET_INT(key) * 3163;
|
||||
goto hash_float64;
|
||||
case JS_TAG_FLOAT64:
|
||||
d = JS_VALUE_GET_FLOAT64(key);
|
||||
/* normalize the NaN */
|
||||
if (isnan(d))
|
||||
d = JS_FLOAT64_NAN;
|
||||
hash_float64:
|
||||
u.d = d;
|
||||
h = (u.u32[0] ^ u.u32[1]) * 3163;
|
||||
break;
|
||||
default:
|
||||
h = 0; /* XXX: bignum support */
|
||||
break;
|
||||
}
|
||||
h ^= tag;
|
||||
return h;
|
||||
}
|
||||
|
||||
static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
|
||||
JSValueConst key)
|
||||
{
|
||||
struct list_head *el;
|
||||
JSMapRecord *mr;
|
||||
uint32_t h;
|
||||
h = map_hash_key(ctx, key) & (s->hash_size - 1);
|
||||
list_for_each(el, &s->hash_table[h]) {
|
||||
mr = list_entry(el, JSMapRecord, hash_link);
|
||||
if (js_same_value_zero(ctx, mr->key, key))
|
||||
return mr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void map_hash_resize(JSContext *ctx, JSMapState *s)
|
||||
{
|
||||
uint32_t new_hash_size, i, h;
|
||||
size_t slack;
|
||||
struct list_head *new_hash_table, *el;
|
||||
JSMapRecord *mr;
|
||||
/* XXX: no reporting of memory allocation failure */
|
||||
if (s->hash_size == 1)
|
||||
new_hash_size = 4;
|
||||
else
|
||||
new_hash_size = s->hash_size * 2;
|
||||
new_hash_table = js_realloc2(ctx, s->hash_table,
|
||||
sizeof(new_hash_table[0]) * new_hash_size, &slack);
|
||||
if (!new_hash_table)
|
||||
return;
|
||||
new_hash_size += slack / sizeof(*new_hash_table);
|
||||
for(i = 0; i < new_hash_size; i++)
|
||||
init_list_head(&new_hash_table[i]);
|
||||
list_for_each(el, &s->records) {
|
||||
mr = list_entry(el, JSMapRecord, link);
|
||||
if (!mr->empty) {
|
||||
h = map_hash_key(ctx, mr->key) & (new_hash_size - 1);
|
||||
list_add_tail(&mr->hash_link, &new_hash_table[h]);
|
||||
}
|
||||
}
|
||||
s->hash_table = new_hash_table;
|
||||
s->hash_size = new_hash_size;
|
||||
s->record_count_threshold = new_hash_size * 2;
|
||||
}
|
||||
|
||||
static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
|
||||
JSValueConst key)
|
||||
{
|
||||
uint32_t h;
|
||||
JSMapRecord *mr;
|
||||
mr = js_malloc(ctx, sizeof(*mr));
|
||||
if (!mr)
|
||||
return NULL;
|
||||
mr->ref_count = 1;
|
||||
mr->map = s;
|
||||
mr->empty = FALSE;
|
||||
if (s->is_weak) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(key);
|
||||
/* Add the weak reference */
|
||||
mr->next_weak_ref = p->first_weak_ref;
|
||||
p->first_weak_ref = mr;
|
||||
} else {
|
||||
JS_DupValue(ctx, key);
|
||||
}
|
||||
mr->key = (JSValue)key;
|
||||
h = map_hash_key(ctx, key) & (s->hash_size - 1);
|
||||
list_add_tail(&mr->hash_link, &s->hash_table[h]);
|
||||
list_add_tail(&mr->link, &s->records);
|
||||
s->record_count++;
|
||||
if (s->record_count >= s->record_count_threshold) {
|
||||
map_hash_resize(ctx, s);
|
||||
}
|
||||
return mr;
|
||||
}
|
||||
|
||||
/* Remove the weak reference from the object weak
|
||||
reference list. we don't use a doubly linked list to
|
||||
save space, assuming a given object has few weak
|
||||
references to it */
|
||||
static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)
|
||||
{
|
||||
JSMapRecord **pmr, *mr1;
|
||||
JSObject *p;
|
||||
p = JS_VALUE_GET_OBJ(mr->key);
|
||||
pmr = &p->first_weak_ref;
|
||||
for(;;) {
|
||||
mr1 = *pmr;
|
||||
assert(mr1 != NULL);
|
||||
if (mr1 == mr)
|
||||
break;
|
||||
pmr = &mr1->next_weak_ref;
|
||||
}
|
||||
*pmr = mr1->next_weak_ref;
|
||||
}
|
||||
|
||||
static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
|
||||
{
|
||||
if (mr->empty)
|
||||
return;
|
||||
list_del(&mr->hash_link);
|
||||
if (s->is_weak) {
|
||||
delete_weak_ref(rt, mr);
|
||||
} else {
|
||||
JS_FreeValueRT(rt, mr->key);
|
||||
}
|
||||
JS_FreeValueRT(rt, mr->value);
|
||||
if (--mr->ref_count == 0) {
|
||||
list_del(&mr->link);
|
||||
js_free_rt(rt, mr);
|
||||
} else {
|
||||
/* keep a zombie record for iterators */
|
||||
mr->empty = TRUE;
|
||||
mr->key = JS_UNDEFINED;
|
||||
mr->value = JS_UNDEFINED;
|
||||
}
|
||||
s->record_count--;
|
||||
}
|
||||
|
||||
static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
|
||||
{
|
||||
if (--mr->ref_count == 0) {
|
||||
/* the record can be safely removed */
|
||||
assert(mr->empty);
|
||||
list_del(&mr->link);
|
||||
js_free_rt(rt, mr);
|
||||
}
|
||||
}
|
||||
|
||||
void reset_weak_ref(JSRuntime *rt, JSObject *p)
|
||||
{
|
||||
JSMapRecord *mr, *mr_next;
|
||||
JSMapState *s;
|
||||
/* first pass to remove the records from the WeakMap/WeakSet
|
||||
lists */
|
||||
for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) {
|
||||
s = mr->map;
|
||||
assert(s->is_weak);
|
||||
assert(!mr->empty); /* no iterator on WeakMap/WeakSet */
|
||||
list_del(&mr->hash_link);
|
||||
list_del(&mr->link);
|
||||
}
|
||||
/* second pass to free the values to avoid modifying the weak
|
||||
reference list while traversing it. */
|
||||
for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) {
|
||||
mr_next = mr->next_weak_ref;
|
||||
JS_FreeValueRT(rt, mr->value);
|
||||
js_free_rt(rt, mr);
|
||||
}
|
||||
p->first_weak_ref = NULL; /* fail safe */
|
||||
}
|
||||
|
||||
static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
|
||||
JSMapRecord *mr;
|
||||
JSValueConst key, value;
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
key = map_normalize_key(ctx, argv[0]);
|
||||
if (s->is_weak && !JS_IsObject(key))
|
||||
return JS_ThrowTypeErrorNotAnObject(ctx);
|
||||
if (magic & MAGIC_SET)
|
||||
value = JS_UNDEFINED;
|
||||
else
|
||||
value = argv[1];
|
||||
mr = map_find_record(ctx, s, key);
|
||||
if (mr) {
|
||||
JS_FreeValue(ctx, mr->value);
|
||||
} else {
|
||||
mr = map_add_record(ctx, s, key);
|
||||
if (!mr)
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
mr->value = JS_DupValue(ctx, value);
|
||||
return JS_DupValue(ctx, this_val);
|
||||
}
|
||||
|
||||
static JSValue js_map_get(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
|
||||
JSMapRecord *mr;
|
||||
JSValueConst key;
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
key = map_normalize_key(ctx, argv[0]);
|
||||
mr = map_find_record(ctx, s, key);
|
||||
if (!mr)
|
||||
return JS_UNDEFINED;
|
||||
else
|
||||
return JS_DupValue(ctx, mr->value);
|
||||
}
|
||||
|
||||
static JSValue js_map_has(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
|
||||
JSMapRecord *mr;
|
||||
JSValueConst key;
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
key = map_normalize_key(ctx, argv[0]);
|
||||
mr = map_find_record(ctx, s, key);
|
||||
return JS_NewBool(ctx, (mr != NULL));
|
||||
}
|
||||
|
||||
static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
|
||||
JSMapRecord *mr;
|
||||
JSValueConst key;
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
key = map_normalize_key(ctx, argv[0]);
|
||||
mr = map_find_record(ctx, s, key);
|
||||
if (!mr)
|
||||
return JS_FALSE;
|
||||
map_delete_record(ctx->rt, s, mr);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
|
||||
struct list_head *el, *el1;
|
||||
JSMapRecord *mr;
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
list_for_each_safe(el, el1, &s->records) {
|
||||
mr = list_entry(el, JSMapRecord, link);
|
||||
map_delete_record(ctx->rt, s, mr);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic)
|
||||
{
|
||||
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
return JS_NewUint32(ctx, s->record_count);
|
||||
}
|
||||
|
||||
static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
|
||||
JSValueConst func, this_arg;
|
||||
JSValue ret, args[3];
|
||||
struct list_head *el;
|
||||
JSMapRecord *mr;
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
func = argv[0];
|
||||
if (argc > 1)
|
||||
this_arg = argv[1];
|
||||
else
|
||||
this_arg = JS_UNDEFINED;
|
||||
if (check_function(ctx, func))
|
||||
return JS_EXCEPTION;
|
||||
/* Note: the list can be modified while traversing it, but the
|
||||
current element is locked */
|
||||
el = s->records.next;
|
||||
while (el != &s->records) {
|
||||
mr = list_entry(el, JSMapRecord, link);
|
||||
if (!mr->empty) {
|
||||
mr->ref_count++;
|
||||
/* must duplicate in case the record is deleted */
|
||||
args[1] = JS_DupValue(ctx, mr->key);
|
||||
if (magic)
|
||||
args[0] = args[1];
|
||||
else
|
||||
args[0] = JS_DupValue(ctx, mr->value);
|
||||
args[2] = (JSValue)this_val;
|
||||
ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);
|
||||
JS_FreeValue(ctx, args[0]);
|
||||
if (!magic)
|
||||
JS_FreeValue(ctx, args[1]);
|
||||
el = el->next;
|
||||
map_decref_record(ctx->rt, mr);
|
||||
if (JS_IsException(ret))
|
||||
return ret;
|
||||
JS_FreeValue(ctx, ret);
|
||||
} else {
|
||||
el = el->next;
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void js_map_finalizer(JSRuntime *rt, JSValue val)
|
||||
{
|
||||
JSObject *p;
|
||||
JSMapState *s;
|
||||
struct list_head *el, *el1;
|
||||
JSMapRecord *mr;
|
||||
p = JS_VALUE_GET_OBJ(val);
|
||||
s = p->u.map_state;
|
||||
if (s) {
|
||||
/* if the object is deleted we are sure that no iterator is
|
||||
using it */
|
||||
list_for_each_safe(el, el1, &s->records) {
|
||||
mr = list_entry(el, JSMapRecord, link);
|
||||
if (!mr->empty) {
|
||||
if (s->is_weak)
|
||||
delete_weak_ref(rt, mr);
|
||||
else
|
||||
JS_FreeValueRT(rt, mr->key);
|
||||
JS_FreeValueRT(rt, mr->value);
|
||||
}
|
||||
js_free_rt(rt, mr);
|
||||
}
|
||||
js_free_rt(rt, s->hash_table);
|
||||
js_free_rt(rt, s);
|
||||
}
|
||||
}
|
||||
|
||||
void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
|
||||
{
|
||||
JSObject *p = JS_VALUE_GET_OBJ(val);
|
||||
JSMapState *s;
|
||||
struct list_head *el;
|
||||
JSMapRecord *mr;
|
||||
s = p->u.map_state;
|
||||
if (s) {
|
||||
list_for_each(el, &s->records) {
|
||||
mr = list_entry(el, JSMapRecord, link);
|
||||
if (!s->is_weak)
|
||||
JS_MarkValue(rt, mr->key, mark_func);
|
||||
JS_MarkValue(rt, mr->value, mark_func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void js_map_iterator_finalizer(JSRuntime *rt, JSValue val)
|
||||
{
|
||||
JSObject *p;
|
||||
JSMapIteratorData *it;
|
||||
p = JS_VALUE_GET_OBJ(val);
|
||||
it = p->u.map_iterator_data;
|
||||
if (it) {
|
||||
/* During the GC sweep phase the Map finalizer may be
|
||||
called before the Map iterator finalizer */
|
||||
if (JS_IsLiveObject(rt, it->obj) && it->cur_record) {
|
||||
map_decref_record(rt, it->cur_record);
|
||||
}
|
||||
JS_FreeValueRT(rt, it->obj);
|
||||
js_free_rt(rt, it);
|
||||
}
|
||||
}
|
||||
|
||||
void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
|
||||
{
|
||||
JSObject *p = JS_VALUE_GET_OBJ(val);
|
||||
JSMapIteratorData *it;
|
||||
it = p->u.map_iterator_data;
|
||||
if (it) {
|
||||
/* the record is already marked by the object */
|
||||
JS_MarkValue(rt, it->obj, mark_func);
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
JSIteratorKindEnum kind;
|
||||
JSMapState *s;
|
||||
JSMapIteratorData *it;
|
||||
JSValue enum_obj;
|
||||
kind = magic >> 2;
|
||||
magic &= 3;
|
||||
s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic);
|
||||
if (JS_IsException(enum_obj))
|
||||
goto fail;
|
||||
it = js_malloc(ctx, sizeof(*it));
|
||||
if (!it) {
|
||||
JS_FreeValue(ctx, enum_obj);
|
||||
goto fail;
|
||||
}
|
||||
it->obj = JS_DupValue(ctx, this_val);
|
||||
it->kind = kind;
|
||||
it->cur_record = NULL;
|
||||
JS_SetOpaque(enum_obj, it);
|
||||
return enum_obj;
|
||||
fail:
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv,
|
||||
BOOL *pdone, int magic)
|
||||
{
|
||||
JSMapIteratorData *it;
|
||||
JSMapState *s;
|
||||
JSMapRecord *mr;
|
||||
struct list_head *el;
|
||||
it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic);
|
||||
if (!it) {
|
||||
*pdone = FALSE;
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
if (JS_IsUndefined(it->obj))
|
||||
goto done;
|
||||
s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic);
|
||||
assert(s != NULL);
|
||||
if (!it->cur_record) {
|
||||
el = s->records.next;
|
||||
} else {
|
||||
mr = it->cur_record;
|
||||
el = mr->link.next;
|
||||
map_decref_record(ctx->rt, mr); /* the record can be freed here */
|
||||
}
|
||||
for(;;) {
|
||||
if (el == &s->records) {
|
||||
/* no more record */
|
||||
it->cur_record = NULL;
|
||||
JS_FreeValue(ctx, it->obj);
|
||||
it->obj = JS_UNDEFINED;
|
||||
done:
|
||||
/* end of enumeration */
|
||||
*pdone = TRUE;
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
mr = list_entry(el, JSMapRecord, link);
|
||||
if (!mr->empty)
|
||||
break;
|
||||
/* get the next record */
|
||||
el = mr->link.next;
|
||||
}
|
||||
/* lock the record so that it won't be freed */
|
||||
mr->ref_count++;
|
||||
it->cur_record = mr;
|
||||
*pdone = FALSE;
|
||||
if (it->kind == JS_ITERATOR_KIND_KEY) {
|
||||
return JS_DupValue(ctx, mr->key);
|
||||
} else {
|
||||
JSValueConst args[2];
|
||||
args[0] = mr->key;
|
||||
if (magic)
|
||||
args[1] = mr->key;
|
||||
else
|
||||
args[1] = mr->value;
|
||||
if (it->kind == JS_ITERATOR_KIND_VALUE) {
|
||||
return JS_DupValue(ctx, args[1]);
|
||||
} else {
|
||||
return js_create_array(ctx, 2, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_map_funcs[] = {
|
||||
JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_map_proto_funcs[] = {
|
||||
JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ),
|
||||
JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ),
|
||||
JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ),
|
||||
JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ),
|
||||
JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ),
|
||||
JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0),
|
||||
JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ),
|
||||
JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ),
|
||||
JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ),
|
||||
JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ),
|
||||
JS_ALIAS_DEF("[Symbol.iterator]", "entries" ),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = {
|
||||
JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_set_proto_funcs[] = {
|
||||
JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ),
|
||||
JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ),
|
||||
JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ),
|
||||
JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ),
|
||||
JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ),
|
||||
JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ),
|
||||
JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ),
|
||||
JS_ALIAS_DEF("keys", "values" ),
|
||||
JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
|
||||
JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {
|
||||
JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_weak_map_proto_funcs[] = {
|
||||
JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ),
|
||||
JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ),
|
||||
JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ),
|
||||
JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_weak_set_proto_funcs[] = {
|
||||
JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ),
|
||||
JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ),
|
||||
JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {
|
||||
js_map_proto_funcs,
|
||||
js_set_proto_funcs,
|
||||
js_weak_map_proto_funcs,
|
||||
js_weak_set_proto_funcs,
|
||||
js_map_iterator_proto_funcs,
|
||||
js_set_iterator_proto_funcs,
|
||||
};
|
||||
|
||||
static const uint8_t js_map_proto_funcs_count[6] = {
|
||||
countof(js_map_proto_funcs),
|
||||
countof(js_set_proto_funcs),
|
||||
countof(js_weak_map_proto_funcs),
|
||||
countof(js_weak_set_proto_funcs),
|
||||
countof(js_map_iterator_proto_funcs),
|
||||
countof(js_set_iterator_proto_funcs),
|
||||
};
|
||||
|
||||
void JS_AddIntrinsicMapSet(JSContext *ctx)
|
||||
{
|
||||
int i;
|
||||
JSValue obj1;
|
||||
char buf[ATOM_GET_STR_BUF_SIZE];
|
||||
for(i = 0; i < 4; i++) {
|
||||
const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf),
|
||||
JS_ATOM_Map + i);
|
||||
ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i],
|
||||
js_map_proto_funcs_ptr[i],
|
||||
js_map_proto_funcs_count[i]);
|
||||
obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0,
|
||||
JS_CFUNC_constructor_magic, i);
|
||||
if (i < 2) {
|
||||
JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs,
|
||||
countof(js_map_funcs));
|
||||
}
|
||||
JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]);
|
||||
}
|
||||
for(i = 0; i < 2; i++) {
|
||||
ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] =
|
||||
JS_NewObjectProto(ctx, ctx->iterator_proto);
|
||||
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i],
|
||||
js_map_proto_funcs_ptr[i + 4],
|
||||
js_map_proto_funcs_count[i + 4]);
|
||||
}
|
||||
}
|
304
third_party/quickjs/math.c
vendored
Normal file
304
third_party/quickjs/math.c
vendored
Normal file
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
/* precondition: a and b are not NaN */
|
||||
static double js_fmin(double a, double b)
|
||||
{
|
||||
if (a == 0 && b == 0) {
|
||||
JSFloat64Union a1, b1;
|
||||
a1.d = a;
|
||||
b1.d = b;
|
||||
a1.u64 |= b1.u64;
|
||||
return a1.d;
|
||||
} else {
|
||||
return fmin(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/* precondition: a and b are not NaN */
|
||||
static double js_fmax(double a, double b)
|
||||
{
|
||||
if (a == 0 && b == 0) {
|
||||
JSFloat64Union a1, b1;
|
||||
a1.d = a;
|
||||
b1.d = b;
|
||||
a1.u64 &= b1.u64;
|
||||
return a1.d;
|
||||
} else {
|
||||
return fmax(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
BOOL is_max = magic;
|
||||
double r, a;
|
||||
int i;
|
||||
uint32_t tag;
|
||||
if (UNLIKELY(argc == 0)) {
|
||||
return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);
|
||||
}
|
||||
tag = JS_VALUE_GET_TAG(argv[0]);
|
||||
if (tag == JS_TAG_INT) {
|
||||
int a1, r1 = JS_VALUE_GET_INT(argv[0]);
|
||||
for(i = 1; i < argc; i++) {
|
||||
tag = JS_VALUE_GET_TAG(argv[i]);
|
||||
if (tag != JS_TAG_INT) {
|
||||
r = r1;
|
||||
goto generic_case;
|
||||
}
|
||||
a1 = JS_VALUE_GET_INT(argv[i]);
|
||||
if (is_max)
|
||||
r1 = max_int(r1, a1);
|
||||
else
|
||||
r1 = min_int(r1, a1);
|
||||
}
|
||||
return JS_NewInt32(ctx, r1);
|
||||
} else {
|
||||
if (JS_ToFloat64(ctx, &r, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
i = 1;
|
||||
generic_case:
|
||||
while (i < argc) {
|
||||
if (JS_ToFloat64(ctx, &a, argv[i]))
|
||||
return JS_EXCEPTION;
|
||||
if (!isnan(r)) {
|
||||
if (isnan(a)) {
|
||||
r = a;
|
||||
} else {
|
||||
if (is_max)
|
||||
r = js_fmax(r, a);
|
||||
else
|
||||
r = js_fmin(r, a);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return JS_NewFloat64(ctx, r);
|
||||
}
|
||||
}
|
||||
|
||||
static double js_math_sign(double a)
|
||||
{
|
||||
if (isnan(a) || a == 0.0)
|
||||
return a;
|
||||
if (a < 0)
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static double js_math_round(double a)
|
||||
{
|
||||
JSFloat64Union u;
|
||||
uint64_t frac_mask, one;
|
||||
unsigned int e, s;
|
||||
u.d = a;
|
||||
e = (u.u64 >> 52) & 0x7ff;
|
||||
if (e < 1023) {
|
||||
/* abs(a) < 1 */
|
||||
if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) {
|
||||
/* abs(a) > 0.5 or a = 0.5: return +/-1.0 */
|
||||
u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52);
|
||||
} else {
|
||||
/* return +/-0.0 */
|
||||
u.u64 &= (uint64_t)1 << 63;
|
||||
}
|
||||
} else if (e < (1023 + 52)) {
|
||||
s = u.u64 >> 63;
|
||||
one = (uint64_t)1 << (52 - (e - 1023));
|
||||
frac_mask = one - 1;
|
||||
u.u64 += (one >> 1) - s;
|
||||
u.u64 &= ~frac_mask; /* truncate to an integer */
|
||||
}
|
||||
/* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
double r, a;
|
||||
int i;
|
||||
r = 0;
|
||||
if (argc > 0) {
|
||||
if (JS_ToFloat64(ctx, &r, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
if (argc == 1) {
|
||||
r = fabs(r);
|
||||
} else {
|
||||
/* use the built-in function to minimize precision loss */
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (JS_ToFloat64(ctx, &a, argv[i]))
|
||||
return JS_EXCEPTION;
|
||||
r = hypot(r, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
return JS_NewFloat64(ctx, r);
|
||||
}
|
||||
|
||||
static double js_math_fround(double a)
|
||||
{
|
||||
return (float)a;
|
||||
}
|
||||
|
||||
static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
int a, b;
|
||||
if (JS_ToInt32(ctx, &a, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
if (JS_ToInt32(ctx, &b, argv[1]))
|
||||
return JS_EXCEPTION;
|
||||
/* purposely ignoring overflow */
|
||||
return JS_NewInt32(ctx, a * b);
|
||||
}
|
||||
|
||||
static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
uint32_t a, r;
|
||||
if (JS_ToUint32(ctx, &a, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
if (a == 0)
|
||||
r = 32;
|
||||
else
|
||||
r = clz32(a);
|
||||
return JS_NewInt32(ctx, r);
|
||||
}
|
||||
|
||||
/* xorshift* random number generator by Marsaglia */
|
||||
static uint64_t xorshift64star(uint64_t *pstate)
|
||||
{
|
||||
uint64_t x;
|
||||
x = *pstate;
|
||||
x ^= x >> 12;
|
||||
x ^= x << 25;
|
||||
x ^= x >> 27;
|
||||
*pstate = x;
|
||||
return x * 0x2545F4914F6CDD1D;
|
||||
}
|
||||
|
||||
static void js_random_init(JSContext *ctx)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
|
||||
/* the state must be non zero */
|
||||
if (ctx->random_state == 0)
|
||||
ctx->random_state = 1;
|
||||
}
|
||||
|
||||
static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSFloat64Union u;
|
||||
uint64_t v;
|
||||
|
||||
v = xorshift64star(&ctx->random_state);
|
||||
/* 1.0 <= u.d < 2 */
|
||||
u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12);
|
||||
return __JS_NewFloat64(ctx, u.d - 1.0);
|
||||
}
|
||||
|
||||
double js_pow(double a, double b)
|
||||
{
|
||||
if (UNLIKELY(!isfinite(b)) && fabs(a) == 1) {
|
||||
/* not compatible with IEEE 754 */
|
||||
return JS_FLOAT64_NAN;
|
||||
} else {
|
||||
return pow(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_math_funcs[] = {
|
||||
JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),
|
||||
JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),
|
||||
JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),
|
||||
JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ),
|
||||
JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ),
|
||||
JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),
|
||||
JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ),
|
||||
JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ),
|
||||
JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ),
|
||||
JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ),
|
||||
JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ),
|
||||
JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ),
|
||||
JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ),
|
||||
JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ),
|
||||
JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ),
|
||||
JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ),
|
||||
JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ),
|
||||
/* ES6 */
|
||||
JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ),
|
||||
JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ),
|
||||
JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ),
|
||||
JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ),
|
||||
JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ),
|
||||
JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ),
|
||||
JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ),
|
||||
JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ),
|
||||
JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ),
|
||||
JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ),
|
||||
JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ),
|
||||
JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ),
|
||||
JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),
|
||||
JS_CFUNC_DEF("hypot", 2, js_math_hypot ),
|
||||
JS_CFUNC_DEF("random", 0, js_math_random ),
|
||||
JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),
|
||||
JS_CFUNC_DEF("imul", 2, js_math_imul ),
|
||||
JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ),
|
||||
JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ),
|
||||
JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ),
|
||||
JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ),
|
||||
JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ),
|
||||
JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ),
|
||||
JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ),
|
||||
JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ),
|
||||
JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_math_obj[] = {
|
||||
JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
void JS_AddIntrinsicMath(JSContext *ctx) {
|
||||
/* Math: create as autoinit object */
|
||||
js_random_init(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj));
|
||||
}
|
189
third_party/quickjs/mem.c
vendored
Normal file
189
third_party/quickjs/mem.c
vendored
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "third_party/quickjs/internal.h"
|
||||
#include "third_party/quickjs/libregexp.h"
|
||||
#include "third_party/quickjs/quickjs.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx)
|
||||
{
|
||||
return JS_ThrowTypeError(ctx, "revoked proxy");
|
||||
}
|
||||
|
||||
JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)
|
||||
{
|
||||
JSRuntime *rt = ctx->rt;
|
||||
JSAtom name;
|
||||
name = rt->class_array[class_id].class_name;
|
||||
return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);
|
||||
}
|
||||
|
||||
/* warning: the refcount of the context is not incremented. Return
|
||||
NULL in case of exception (case of revoked proxy only) */
|
||||
JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj)
|
||||
{
|
||||
JSObject *p;
|
||||
JSContext *realm;
|
||||
|
||||
if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
|
||||
return ctx;
|
||||
p = JS_VALUE_GET_OBJ(func_obj);
|
||||
switch(p->class_id) {
|
||||
case JS_CLASS_C_FUNCTION:
|
||||
realm = p->u.cfunc.realm;
|
||||
break;
|
||||
case JS_CLASS_BYTECODE_FUNCTION:
|
||||
case JS_CLASS_GENERATOR_FUNCTION:
|
||||
case JS_CLASS_ASYNC_FUNCTION:
|
||||
case JS_CLASS_ASYNC_GENERATOR_FUNCTION:
|
||||
{
|
||||
JSFunctionBytecode *b;
|
||||
b = p->u.func.function_bytecode;
|
||||
realm = b->realm;
|
||||
}
|
||||
break;
|
||||
case JS_CLASS_PROXY:
|
||||
{
|
||||
JSProxyData *s = p->u.opaque;
|
||||
if (!s)
|
||||
return ctx;
|
||||
if (s->is_revoked) {
|
||||
JS_ThrowTypeErrorRevokedProxy(ctx);
|
||||
return NULL;
|
||||
} else {
|
||||
realm = JS_GetFunctionRealm(ctx, s->target);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_CLASS_BOUND_FUNCTION:
|
||||
{
|
||||
JSBoundFunction *bf = p->u.bound_function;
|
||||
realm = JS_GetFunctionRealm(ctx, bf->func_obj);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
realm = ctx;
|
||||
break;
|
||||
}
|
||||
return realm;
|
||||
}
|
||||
|
||||
JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor,
|
||||
int class_id)
|
||||
{
|
||||
JSValue proto, obj;
|
||||
JSContext *realm;
|
||||
|
||||
if (JS_IsUndefined(ctor)) {
|
||||
proto = JS_DupValue(ctx, ctx->class_proto[class_id]);
|
||||
} else {
|
||||
proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
|
||||
if (JS_IsException(proto))
|
||||
return proto;
|
||||
if (!JS_IsObject(proto)) {
|
||||
JS_FreeValue(ctx, proto);
|
||||
realm = JS_GetFunctionRealm(ctx, ctor);
|
||||
if (!realm)
|
||||
return JS_EXCEPTION;
|
||||
proto = JS_DupValue(ctx, realm->class_proto[class_id]);
|
||||
}
|
||||
}
|
||||
obj = JS_NewObjectProtoClass(ctx, proto, class_id);
|
||||
JS_FreeValue(ctx, proto);
|
||||
return obj;
|
||||
}
|
||||
|
||||
int JS_ToBoolFree(JSContext *ctx, JSValue val)
|
||||
{
|
||||
uint32_t tag = JS_VALUE_GET_TAG(val);
|
||||
switch(tag) {
|
||||
case JS_TAG_INT:
|
||||
return JS_VALUE_GET_INT(val) != 0;
|
||||
case JS_TAG_BOOL:
|
||||
case JS_TAG_NULL:
|
||||
case JS_TAG_UNDEFINED:
|
||||
return JS_VALUE_GET_INT(val);
|
||||
case JS_TAG_EXCEPTION:
|
||||
return -1;
|
||||
case JS_TAG_STRING:
|
||||
{
|
||||
BOOL ret = JS_VALUE_GET_STRING(val)->len != 0;
|
||||
JS_FreeValue(ctx, val);
|
||||
return ret;
|
||||
}
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_TAG_BIG_INT:
|
||||
case JS_TAG_BIG_FLOAT:
|
||||
{
|
||||
JSBigFloat *p = JS_VALUE_GET_PTR(val);
|
||||
BOOL ret;
|
||||
ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
|
||||
JS_FreeValue(ctx, val);
|
||||
return ret;
|
||||
}
|
||||
case JS_TAG_BIG_DECIMAL:
|
||||
{
|
||||
JSBigDecimal *p = JS_VALUE_GET_PTR(val);
|
||||
BOOL ret;
|
||||
ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
|
||||
JS_FreeValue(ctx, val);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
case JS_TAG_OBJECT:
|
||||
{
|
||||
JSObject *p = JS_VALUE_GET_OBJ(val);
|
||||
BOOL ret;
|
||||
ret = !p->is_HTMLDDA;
|
||||
JS_FreeValue(ctx, val);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (JS_TAG_IS_FLOAT64(tag)) {
|
||||
double d = JS_VALUE_GET_FLOAT64(val);
|
||||
return !isnan(d) && d != 0;
|
||||
} else {
|
||||
JS_FreeValue(ctx, val);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int JS_ToBool(JSContext *ctx, JSValueConst val)
|
||||
{
|
||||
return JS_ToBoolFree(ctx, JS_DupValue(ctx, val));
|
||||
}
|
||||
|
||||
JSValue js_get_this(JSContext *ctx, JSValueConst this_val)
|
||||
{
|
||||
return JS_DupValue(ctx, this_val);
|
||||
}
|
1222
third_party/quickjs/object.c
vendored
Normal file
1222
third_party/quickjs/object.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
6148
third_party/quickjs/parse.c
vendored
Normal file
6148
third_party/quickjs/parse.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
110
third_party/quickjs/prim.c
vendored
Normal file
110
third_party/quickjs/prim.c
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
|
||||
{
|
||||
int i;
|
||||
BOOL force_ordinary;
|
||||
JSAtom method_name;
|
||||
JSValue method, ret;
|
||||
if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
|
||||
return val;
|
||||
force_ordinary = hint & HINT_FORCE_ORDINARY;
|
||||
hint &= ~HINT_FORCE_ORDINARY;
|
||||
if (!force_ordinary) {
|
||||
method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive);
|
||||
if (JS_IsException(method))
|
||||
goto exception;
|
||||
/* ECMA says *If exoticToPrim is not undefined* but tests in
|
||||
test262 use null as a non callable converter */
|
||||
if (!JS_IsUndefined(method) && !JS_IsNull(method)) {
|
||||
JSAtom atom;
|
||||
JSValue arg;
|
||||
switch(hint) {
|
||||
case HINT_STRING:
|
||||
atom = JS_ATOM_string;
|
||||
break;
|
||||
case HINT_NUMBER:
|
||||
atom = JS_ATOM_number;
|
||||
break;
|
||||
default:
|
||||
case HINT_NONE:
|
||||
atom = JS_ATOM_default;
|
||||
break;
|
||||
}
|
||||
arg = JS_AtomToString(ctx, atom);
|
||||
ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg);
|
||||
JS_FreeValue(ctx, arg);
|
||||
if (JS_IsException(ret))
|
||||
goto exception;
|
||||
JS_FreeValue(ctx, val);
|
||||
if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT)
|
||||
return ret;
|
||||
JS_FreeValue(ctx, ret);
|
||||
return JS_ThrowTypeError(ctx, "toPrimitive");
|
||||
}
|
||||
}
|
||||
if (hint != HINT_STRING)
|
||||
hint = HINT_NUMBER;
|
||||
for(i = 0; i < 2; i++) {
|
||||
if ((i ^ hint) == 0) {
|
||||
method_name = JS_ATOM_toString;
|
||||
} else {
|
||||
method_name = JS_ATOM_valueOf;
|
||||
}
|
||||
method = JS_GetProperty(ctx, val, method_name);
|
||||
if (JS_IsException(method))
|
||||
goto exception;
|
||||
if (JS_IsFunction(ctx, method)) {
|
||||
ret = JS_CallFree(ctx, method, val, 0, NULL);
|
||||
if (JS_IsException(ret))
|
||||
goto exception;
|
||||
if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
|
||||
JS_FreeValue(ctx, val);
|
||||
return ret;
|
||||
}
|
||||
JS_FreeValue(ctx, ret);
|
||||
} else {
|
||||
JS_FreeValue(ctx, method);
|
||||
}
|
||||
}
|
||||
JS_ThrowTypeError(ctx, "toPrimitive");
|
||||
exception:
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint)
|
||||
{
|
||||
return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint);
|
||||
}
|
1569
third_party/quickjs/promise.c
vendored
Normal file
1569
third_party/quickjs/promise.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
961
third_party/quickjs/proxy.c
vendored
Normal file
961
third_party/quickjs/proxy.c
vendored
Normal file
|
@ -0,0 +1,961 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
static void js_proxy_finalizer(JSRuntime *rt, JSValue val)
|
||||
{
|
||||
JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
|
||||
if (s) {
|
||||
JS_FreeValueRT(rt, s->target);
|
||||
JS_FreeValueRT(rt, s->handler);
|
||||
js_free_rt(rt, s);
|
||||
}
|
||||
}
|
||||
|
||||
static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
|
||||
JS_MarkFunc *mark_func)
|
||||
{
|
||||
JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
|
||||
if (s) {
|
||||
JS_MarkValue(rt, s->target, mark_func);
|
||||
JS_MarkValue(rt, s->handler, mark_func);
|
||||
}
|
||||
}
|
||||
|
||||
static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod,
|
||||
JSValueConst obj, JSAtom name)
|
||||
{
|
||||
JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);
|
||||
JSValue method;
|
||||
/* safer to test recursion in all proxy methods */
|
||||
if (js_check_stack_overflow(ctx->rt, 0)) {
|
||||
JS_ThrowStackOverflow(ctx);
|
||||
return NULL;
|
||||
}
|
||||
/* 's' should never be NULL */
|
||||
if (s->is_revoked) {
|
||||
JS_ThrowTypeErrorRevokedProxy(ctx);
|
||||
return NULL;
|
||||
}
|
||||
method = JS_GetProperty(ctx, s->handler, name);
|
||||
if (JS_IsException(method))
|
||||
return NULL;
|
||||
if (JS_IsNull(method))
|
||||
method = JS_UNDEFINED;
|
||||
*pmethod = method;
|
||||
return s;
|
||||
}
|
||||
|
||||
JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)
|
||||
{
|
||||
JSProxyData *s;
|
||||
JSValue method, ret, proto1;
|
||||
int res;
|
||||
|
||||
s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf);
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
if (JS_IsUndefined(method))
|
||||
return JS_GetPrototype(ctx, s->target);
|
||||
ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
|
||||
if (JS_IsException(ret))
|
||||
return ret;
|
||||
if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL &&
|
||||
JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
|
||||
goto fail;
|
||||
}
|
||||
res = JS_IsExtensible(ctx, s->target);
|
||||
if (res < 0) {
|
||||
JS_FreeValue(ctx, ret);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
if (!res) {
|
||||
/* check invariant */
|
||||
proto1 = JS_GetPrototype(ctx, s->target);
|
||||
if (JS_IsException(proto1)) {
|
||||
JS_FreeValue(ctx, ret);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) {
|
||||
JS_FreeValue(ctx, proto1);
|
||||
fail:
|
||||
JS_FreeValue(ctx, ret);
|
||||
return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
|
||||
}
|
||||
JS_FreeValue(ctx, proto1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
|
||||
JSValueConst proto_val, BOOL throw_flag)
|
||||
{
|
||||
JSProxyData *s;
|
||||
JSValue method, ret, proto1;
|
||||
JSValueConst args[2];
|
||||
BOOL res;
|
||||
int res2;
|
||||
|
||||
s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf);
|
||||
if (!s)
|
||||
return -1;
|
||||
if (JS_IsUndefined(method))
|
||||
return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag);
|
||||
args[0] = s->target;
|
||||
args[1] = proto_val;
|
||||
ret = JS_CallFree(ctx, method, s->handler, 2, args);
|
||||
if (JS_IsException(ret))
|
||||
return -1;
|
||||
res = JS_ToBoolFree(ctx, ret);
|
||||
if (!res) {
|
||||
if (throw_flag) {
|
||||
JS_ThrowTypeError(ctx, "proxy: bad prototype");
|
||||
return -1;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
res2 = JS_IsExtensible(ctx, s->target);
|
||||
if (res2 < 0)
|
||||
return -1;
|
||||
if (!res2) {
|
||||
proto1 = JS_GetPrototype(ctx, s->target);
|
||||
if (JS_IsException(proto1))
|
||||
return -1;
|
||||
if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) {
|
||||
JS_FreeValue(ctx, proto1);
|
||||
JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
|
||||
return -1;
|
||||
}
|
||||
JS_FreeValue(ctx, proto1);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj)
|
||||
{
|
||||
JSProxyData *s;
|
||||
JSValue method, ret;
|
||||
BOOL res;
|
||||
int res2;
|
||||
s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible);
|
||||
if (!s)
|
||||
return -1;
|
||||
if (JS_IsUndefined(method))
|
||||
return JS_IsExtensible(ctx, s->target);
|
||||
ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
|
||||
if (JS_IsException(ret))
|
||||
return -1;
|
||||
res = JS_ToBoolFree(ctx, ret);
|
||||
res2 = JS_IsExtensible(ctx, s->target);
|
||||
if (res2 < 0)
|
||||
return res2;
|
||||
if (res != res2) {
|
||||
JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible");
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj)
|
||||
{
|
||||
JSProxyData *s;
|
||||
JSValue method, ret;
|
||||
BOOL res;
|
||||
int res2;
|
||||
s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions);
|
||||
if (!s)
|
||||
return -1;
|
||||
if (JS_IsUndefined(method))
|
||||
return JS_PreventExtensions(ctx, s->target);
|
||||
ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
|
||||
if (JS_IsException(ret))
|
||||
return -1;
|
||||
res = JS_ToBoolFree(ctx, ret);
|
||||
if (res) {
|
||||
res2 = JS_IsExtensible(ctx, s->target);
|
||||
if (res2 < 0)
|
||||
return res2;
|
||||
if (res2) {
|
||||
JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
|
||||
{
|
||||
JSProxyData *s;
|
||||
JSValue method, ret1, atom_val;
|
||||
int ret, res;
|
||||
JSObject *p;
|
||||
JSValueConst args[2];
|
||||
BOOL res2;
|
||||
s = get_proxy_method(ctx, &method, obj, JS_ATOM_has);
|
||||
if (!s)
|
||||
return -1;
|
||||
if (JS_IsUndefined(method))
|
||||
return JS_HasProperty(ctx, s->target, atom);
|
||||
atom_val = JS_AtomToValue(ctx, atom);
|
||||
if (JS_IsException(atom_val)) {
|
||||
JS_FreeValue(ctx, method);
|
||||
return -1;
|
||||
}
|
||||
args[0] = s->target;
|
||||
args[1] = atom_val;
|
||||
ret1 = JS_CallFree(ctx, method, s->handler, 2, args);
|
||||
JS_FreeValue(ctx, atom_val);
|
||||
if (JS_IsException(ret1))
|
||||
return -1;
|
||||
ret = JS_ToBoolFree(ctx, ret1);
|
||||
if (!ret) {
|
||||
JSPropertyDescriptor desc;
|
||||
p = JS_VALUE_GET_OBJ(s->target);
|
||||
res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
|
||||
if (res < 0)
|
||||
return -1;
|
||||
if (res) {
|
||||
res2 = !(desc.flags & JS_PROP_CONFIGURABLE);
|
||||
js_free_desc(ctx, &desc);
|
||||
if (res2 || !p->extensible) {
|
||||
JS_ThrowTypeError(ctx, "proxy: inconsistent has");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom,
|
||||
JSValueConst receiver)
|
||||
{
|
||||
JSProxyData *s;
|
||||
JSValue method, ret, atom_val;
|
||||
int res;
|
||||
JSValueConst args[3];
|
||||
JSPropertyDescriptor desc;
|
||||
s = get_proxy_method(ctx, &method, obj, JS_ATOM_get);
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
/* Note: recursion is possible thru the prototype of s->target */
|
||||
if (JS_IsUndefined(method))
|
||||
return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE);
|
||||
atom_val = JS_AtomToValue(ctx, atom);
|
||||
if (JS_IsException(atom_val)) {
|
||||
JS_FreeValue(ctx, method);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
args[0] = s->target;
|
||||
args[1] = atom_val;
|
||||
args[2] = receiver;
|
||||
ret = JS_CallFree(ctx, method, s->handler, 3, args);
|
||||
JS_FreeValue(ctx, atom_val);
|
||||
if (JS_IsException(ret))
|
||||
return JS_EXCEPTION;
|
||||
res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
|
||||
if (res < 0)
|
||||
return JS_EXCEPTION;
|
||||
if (res) {
|
||||
if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
|
||||
if (!js_same_value(ctx, desc.value, ret)) {
|
||||
goto fail;
|
||||
}
|
||||
} else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) {
|
||||
if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) {
|
||||
fail:
|
||||
js_free_desc(ctx, &desc);
|
||||
JS_FreeValue(ctx, ret);
|
||||
return JS_ThrowTypeError(ctx, "proxy: inconsistent get");
|
||||
}
|
||||
}
|
||||
js_free_desc(ctx, &desc);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom,
|
||||
JSValueConst value, JSValueConst receiver, int flags)
|
||||
{
|
||||
JSProxyData *s;
|
||||
JSValue method, ret1, atom_val;
|
||||
int ret, res;
|
||||
JSValueConst args[4];
|
||||
s = get_proxy_method(ctx, &method, obj, JS_ATOM_set);
|
||||
if (!s)
|
||||
return -1;
|
||||
if (JS_IsUndefined(method)) {
|
||||
return JS_SetPropertyGeneric(ctx, s->target, atom,
|
||||
JS_DupValue(ctx, value), receiver,
|
||||
flags);
|
||||
}
|
||||
atom_val = JS_AtomToValue(ctx, atom);
|
||||
if (JS_IsException(atom_val)) {
|
||||
JS_FreeValue(ctx, method);
|
||||
return -1;
|
||||
}
|
||||
args[0] = s->target;
|
||||
args[1] = atom_val;
|
||||
args[2] = value;
|
||||
args[3] = receiver;
|
||||
ret1 = JS_CallFree(ctx, method, s->handler, 4, args);
|
||||
JS_FreeValue(ctx, atom_val);
|
||||
if (JS_IsException(ret1))
|
||||
return -1;
|
||||
ret = JS_ToBoolFree(ctx, ret1);
|
||||
if (ret) {
|
||||
JSPropertyDescriptor desc;
|
||||
res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
|
||||
if (res < 0)
|
||||
return -1;
|
||||
if (res) {
|
||||
if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
|
||||
if (!js_same_value(ctx, desc.value, value)) {
|
||||
goto fail;
|
||||
}
|
||||
} else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) {
|
||||
fail:
|
||||
js_free_desc(ctx, &desc);
|
||||
JS_ThrowTypeError(ctx, "proxy: inconsistent set");
|
||||
return -1;
|
||||
}
|
||||
js_free_desc(ctx, &desc);
|
||||
}
|
||||
} else {
|
||||
if ((flags & JS_PROP_THROW) ||
|
||||
((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
|
||||
JS_ThrowTypeError(ctx, "proxy: cannot set property");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_create_desc(JSContext *ctx, JSValueConst val,
|
||||
JSValueConst getter, JSValueConst setter,
|
||||
int flags)
|
||||
{
|
||||
JSValue ret;
|
||||
ret = JS_NewObject(ctx);
|
||||
if (JS_IsException(ret))
|
||||
return ret;
|
||||
if (flags & JS_PROP_HAS_GET) {
|
||||
JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter),
|
||||
JS_PROP_C_W_E);
|
||||
}
|
||||
if (flags & JS_PROP_HAS_SET) {
|
||||
JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter),
|
||||
JS_PROP_C_W_E);
|
||||
}
|
||||
if (flags & JS_PROP_HAS_VALUE) {
|
||||
JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val),
|
||||
JS_PROP_C_W_E);
|
||||
}
|
||||
if (flags & JS_PROP_HAS_WRITABLE) {
|
||||
JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
|
||||
JS_NewBool(ctx, (flags & JS_PROP_WRITABLE) != 0),
|
||||
JS_PROP_C_W_E);
|
||||
}
|
||||
if (flags & JS_PROP_HAS_ENUMERABLE) {
|
||||
JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
|
||||
JS_NewBool(ctx, (flags & JS_PROP_ENUMERABLE) != 0),
|
||||
JS_PROP_C_W_E);
|
||||
}
|
||||
if (flags & JS_PROP_HAS_CONFIGURABLE) {
|
||||
JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
|
||||
JS_NewBool(ctx, (flags & JS_PROP_CONFIGURABLE) != 0),
|
||||
JS_PROP_C_W_E);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc,
|
||||
JSValueConst obj, JSAtom prop)
|
||||
{
|
||||
JSProxyData *s;
|
||||
JSValue method, trap_result_obj, prop_val;
|
||||
int res, target_desc_ret, ret;
|
||||
JSObject *p;
|
||||
JSValueConst args[2];
|
||||
JSPropertyDescriptor result_desc, target_desc;
|
||||
s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor);
|
||||
if (!s)
|
||||
return -1;
|
||||
p = JS_VALUE_GET_OBJ(s->target);
|
||||
if (JS_IsUndefined(method)) {
|
||||
return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop);
|
||||
}
|
||||
prop_val = JS_AtomToValue(ctx, prop);
|
||||
if (JS_IsException(prop_val)) {
|
||||
JS_FreeValue(ctx, method);
|
||||
return -1;
|
||||
}
|
||||
args[0] = s->target;
|
||||
args[1] = prop_val;
|
||||
trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args);
|
||||
JS_FreeValue(ctx, prop_val);
|
||||
if (JS_IsException(trap_result_obj))
|
||||
return -1;
|
||||
if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) {
|
||||
JS_FreeValue(ctx, trap_result_obj);
|
||||
goto fail;
|
||||
}
|
||||
target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop);
|
||||
if (target_desc_ret < 0) {
|
||||
JS_FreeValue(ctx, trap_result_obj);
|
||||
return -1;
|
||||
}
|
||||
if (target_desc_ret)
|
||||
js_free_desc(ctx, &target_desc);
|
||||
if (JS_IsUndefined(trap_result_obj)) {
|
||||
if (target_desc_ret) {
|
||||
if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible)
|
||||
goto fail;
|
||||
}
|
||||
ret = FALSE;
|
||||
} else {
|
||||
int flags1, extensible_target;
|
||||
extensible_target = JS_IsExtensible(ctx, s->target);
|
||||
if (extensible_target < 0) {
|
||||
JS_FreeValue(ctx, trap_result_obj);
|
||||
return -1;
|
||||
}
|
||||
res = js_obj_to_desc(ctx, &result_desc, trap_result_obj);
|
||||
JS_FreeValue(ctx, trap_result_obj);
|
||||
if (res < 0)
|
||||
return -1;
|
||||
if (target_desc_ret) {
|
||||
/* convert result_desc.flags to defineProperty flags */
|
||||
flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE;
|
||||
if (result_desc.flags & JS_PROP_GETSET)
|
||||
flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET;
|
||||
else
|
||||
flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE;
|
||||
/* XXX: not complete check: need to compare value &
|
||||
getter/setter as in defineproperty */
|
||||
if (!check_define_prop_flags(target_desc.flags, flags1))
|
||||
goto fail1;
|
||||
} else {
|
||||
if (!extensible_target)
|
||||
goto fail1;
|
||||
}
|
||||
if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) {
|
||||
if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE))
|
||||
goto fail1;
|
||||
if ((result_desc.flags &
|
||||
(JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 &&
|
||||
target_desc_ret &&
|
||||
(target_desc.flags & JS_PROP_WRITABLE) != 0) {
|
||||
/* proxy-missing-checks */
|
||||
fail1:
|
||||
js_free_desc(ctx, &result_desc);
|
||||
fail:
|
||||
JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ret = TRUE;
|
||||
if (pdesc) {
|
||||
*pdesc = result_desc;
|
||||
} else {
|
||||
js_free_desc(ctx, &result_desc);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj,
|
||||
JSAtom prop, JSValueConst val,
|
||||
JSValueConst getter, JSValueConst setter,
|
||||
int flags)
|
||||
{
|
||||
JSProxyData *s;
|
||||
JSValue method, ret1, prop_val, desc_val;
|
||||
int res, ret;
|
||||
JSObject *p;
|
||||
JSValueConst args[3];
|
||||
JSPropertyDescriptor desc;
|
||||
BOOL setting_not_configurable;
|
||||
s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty);
|
||||
if (!s)
|
||||
return -1;
|
||||
if (JS_IsUndefined(method)) {
|
||||
return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags);
|
||||
}
|
||||
prop_val = JS_AtomToValue(ctx, prop);
|
||||
if (JS_IsException(prop_val)) {
|
||||
JS_FreeValue(ctx, method);
|
||||
return -1;
|
||||
}
|
||||
desc_val = js_create_desc(ctx, val, getter, setter, flags);
|
||||
if (JS_IsException(desc_val)) {
|
||||
JS_FreeValue(ctx, prop_val);
|
||||
JS_FreeValue(ctx, method);
|
||||
return -1;
|
||||
}
|
||||
args[0] = s->target;
|
||||
args[1] = prop_val;
|
||||
args[2] = desc_val;
|
||||
ret1 = JS_CallFree(ctx, method, s->handler, 3, args);
|
||||
JS_FreeValue(ctx, prop_val);
|
||||
JS_FreeValue(ctx, desc_val);
|
||||
if (JS_IsException(ret1))
|
||||
return -1;
|
||||
ret = JS_ToBoolFree(ctx, ret1);
|
||||
if (!ret) {
|
||||
if (flags & JS_PROP_THROW) {
|
||||
JS_ThrowTypeError(ctx, "proxy: defineProperty exception");
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
p = JS_VALUE_GET_OBJ(s->target);
|
||||
res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
|
||||
if (res < 0)
|
||||
return -1;
|
||||
setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE |
|
||||
JS_PROP_CONFIGURABLE)) ==
|
||||
JS_PROP_HAS_CONFIGURABLE);
|
||||
if (!res) {
|
||||
if (!p->extensible || setting_not_configurable)
|
||||
goto fail;
|
||||
} else {
|
||||
if (!check_define_prop_flags(desc.flags, flags) ||
|
||||
((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) {
|
||||
goto fail1;
|
||||
}
|
||||
if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
|
||||
if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) ==
|
||||
JS_PROP_GETSET) {
|
||||
if ((flags & JS_PROP_HAS_GET) &&
|
||||
!js_same_value(ctx, getter, desc.getter)) {
|
||||
goto fail1;
|
||||
}
|
||||
if ((flags & JS_PROP_HAS_SET) &&
|
||||
!js_same_value(ctx, setter, desc.setter)) {
|
||||
goto fail1;
|
||||
}
|
||||
}
|
||||
} else if (flags & JS_PROP_HAS_VALUE) {
|
||||
if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) ==
|
||||
JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) {
|
||||
/* missing-proxy-check feature */
|
||||
goto fail1;
|
||||
} else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&
|
||||
!js_same_value(ctx, val, desc.value)) {
|
||||
goto fail1;
|
||||
}
|
||||
}
|
||||
if (flags & JS_PROP_HAS_WRITABLE) {
|
||||
if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE |
|
||||
JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) {
|
||||
/* proxy-missing-checks */
|
||||
fail1:
|
||||
js_free_desc(ctx, &desc);
|
||||
fail:
|
||||
JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
js_free_desc(ctx, &desc);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj,
|
||||
JSAtom atom)
|
||||
{
|
||||
JSProxyData *s;
|
||||
JSValue method, ret, atom_val;
|
||||
int res, res2, is_extensible;
|
||||
JSValueConst args[2];
|
||||
|
||||
s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty);
|
||||
if (!s)
|
||||
return -1;
|
||||
if (JS_IsUndefined(method)) {
|
||||
return JS_DeleteProperty(ctx, s->target, atom, 0);
|
||||
}
|
||||
atom_val = JS_AtomToValue(ctx, atom);;
|
||||
if (JS_IsException(atom_val)) {
|
||||
JS_FreeValue(ctx, method);
|
||||
return -1;
|
||||
}
|
||||
args[0] = s->target;
|
||||
args[1] = atom_val;
|
||||
ret = JS_CallFree(ctx, method, s->handler, 2, args);
|
||||
JS_FreeValue(ctx, atom_val);
|
||||
if (JS_IsException(ret))
|
||||
return -1;
|
||||
res = JS_ToBoolFree(ctx, ret);
|
||||
if (res) {
|
||||
JSPropertyDescriptor desc;
|
||||
res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
|
||||
if (res2 < 0)
|
||||
return -1;
|
||||
if (res2) {
|
||||
if (!(desc.flags & JS_PROP_CONFIGURABLE))
|
||||
goto fail;
|
||||
is_extensible = JS_IsExtensible(ctx, s->target);
|
||||
if (is_extensible < 0)
|
||||
goto fail1;
|
||||
if (!is_extensible) {
|
||||
/* proxy-missing-checks */
|
||||
fail:
|
||||
JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty");
|
||||
fail1:
|
||||
js_free_desc(ctx, &desc);
|
||||
return -1;
|
||||
}
|
||||
js_free_desc(ctx, &desc);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* return the index of the property or -1 if not found */
|
||||
static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < n; i++) {
|
||||
if (tab[i].atom == atom)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int js_proxy_get_own_property_names(JSContext *ctx,
|
||||
JSPropertyEnum **ptab,
|
||||
uint32_t *plen,
|
||||
JSValueConst obj)
|
||||
{
|
||||
JSProxyData *s;
|
||||
JSValue method, prop_array, val;
|
||||
uint32_t len, i, len2;
|
||||
JSPropertyEnum *tab, *tab2;
|
||||
JSAtom atom;
|
||||
JSPropertyDescriptor desc;
|
||||
int res, is_extensible, idx;
|
||||
s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys);
|
||||
if (!s)
|
||||
return -1;
|
||||
if (JS_IsUndefined(method)) {
|
||||
return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
|
||||
JS_VALUE_GET_OBJ(s->target),
|
||||
JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK);
|
||||
}
|
||||
prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
|
||||
if (JS_IsException(prop_array))
|
||||
return -1;
|
||||
tab = NULL;
|
||||
len = 0;
|
||||
tab2 = NULL;
|
||||
len2 = 0;
|
||||
if (js_get_length32(ctx, &len, prop_array))
|
||||
goto fail;
|
||||
if (len > 0) {
|
||||
tab = js_mallocz(ctx, sizeof(tab[0]) * len);
|
||||
if (!tab)
|
||||
goto fail;
|
||||
}
|
||||
for(i = 0; i < len; i++) {
|
||||
val = JS_GetPropertyUint32(ctx, prop_array, i);
|
||||
if (JS_IsException(val))
|
||||
goto fail;
|
||||
if (!JS_IsString(val) && !JS_IsSymbol(val)) {
|
||||
JS_FreeValue(ctx, val);
|
||||
JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols");
|
||||
goto fail;
|
||||
}
|
||||
atom = JS_ValueToAtom(ctx, val);
|
||||
JS_FreeValue(ctx, val);
|
||||
if (atom == JS_ATOM_NULL)
|
||||
goto fail;
|
||||
tab[i].atom = atom;
|
||||
tab[i].is_enumerable = FALSE; /* XXX: redundant? */
|
||||
}
|
||||
/* check duplicate properties (XXX: inefficient, could store the
|
||||
* properties an a temporary object to use the hash) */
|
||||
for(i = 1; i < len; i++) {
|
||||
if (find_prop_key(tab, i, tab[i].atom) >= 0) {
|
||||
JS_ThrowTypeError(ctx, "proxy: duplicate property");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
is_extensible = JS_IsExtensible(ctx, s->target);
|
||||
if (is_extensible < 0)
|
||||
goto fail;
|
||||
/* check if there are non configurable properties */
|
||||
if (s->is_revoked) {
|
||||
JS_ThrowTypeErrorRevokedProxy(ctx);
|
||||
goto fail;
|
||||
}
|
||||
if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target),
|
||||
JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
|
||||
goto fail;
|
||||
for(i = 0; i < len2; i++) {
|
||||
if (s->is_revoked) {
|
||||
JS_ThrowTypeErrorRevokedProxy(ctx);
|
||||
goto fail;
|
||||
}
|
||||
res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target),
|
||||
tab2[i].atom);
|
||||
if (res < 0)
|
||||
goto fail;
|
||||
if (res) { /* safety, property should be found */
|
||||
js_free_desc(ctx, &desc);
|
||||
if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) {
|
||||
idx = find_prop_key(tab, len, tab2[i].atom);
|
||||
if (idx < 0) {
|
||||
JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys");
|
||||
goto fail;
|
||||
}
|
||||
/* mark the property as found */
|
||||
if (!is_extensible)
|
||||
tab[idx].is_enumerable = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_extensible) {
|
||||
/* check that all property in 'tab' were checked */
|
||||
for(i = 0; i < len; i++) {
|
||||
if (!tab[i].is_enumerable) {
|
||||
JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
js_free_prop_enum(ctx, tab2, len2);
|
||||
JS_FreeValue(ctx, prop_array);
|
||||
*ptab = tab;
|
||||
*plen = len;
|
||||
return 0;
|
||||
fail:
|
||||
js_free_prop_enum(ctx, tab2, len2);
|
||||
js_free_prop_enum(ctx, tab, len);
|
||||
JS_FreeValue(ctx, prop_array);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj,
|
||||
JSValueConst new_target,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSProxyData *s;
|
||||
JSValue method, arg_array, ret;
|
||||
JSValueConst args[3];
|
||||
|
||||
s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct);
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
if (!JS_IsConstructor(ctx, s->target))
|
||||
return JS_ThrowTypeError(ctx, "not a constructor");
|
||||
if (JS_IsUndefined(method))
|
||||
return JS_CallConstructor2(ctx, s->target, new_target, argc, argv);
|
||||
arg_array = js_create_array(ctx, argc, argv);
|
||||
if (JS_IsException(arg_array)) {
|
||||
ret = JS_EXCEPTION;
|
||||
goto fail;
|
||||
}
|
||||
args[0] = s->target;
|
||||
args[1] = arg_array;
|
||||
args[2] = new_target;
|
||||
ret = JS_Call(ctx, method, s->handler, 3, args);
|
||||
if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
|
||||
JS_FreeValue(ctx, ret);
|
||||
ret = JS_ThrowTypeErrorNotAnObject(ctx);
|
||||
}
|
||||
fail:
|
||||
JS_FreeValue(ctx, method);
|
||||
JS_FreeValue(ctx, arg_array);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj,
|
||||
JSValueConst this_obj,
|
||||
int argc, JSValueConst *argv, int flags)
|
||||
{
|
||||
JSProxyData *s;
|
||||
JSValue method, arg_array, ret;
|
||||
JSValueConst args[3];
|
||||
|
||||
if (flags & JS_CALL_FLAG_CONSTRUCTOR)
|
||||
return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv);
|
||||
|
||||
s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply);
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
if (!s->is_func) {
|
||||
JS_FreeValue(ctx, method);
|
||||
return JS_ThrowTypeError(ctx, "not a function");
|
||||
}
|
||||
if (JS_IsUndefined(method))
|
||||
return JS_Call(ctx, s->target, this_obj, argc, argv);
|
||||
arg_array = js_create_array(ctx, argc, argv);
|
||||
if (JS_IsException(arg_array)) {
|
||||
ret = JS_EXCEPTION;
|
||||
goto fail;
|
||||
}
|
||||
args[0] = s->target;
|
||||
args[1] = this_obj;
|
||||
args[2] = arg_array;
|
||||
ret = JS_Call(ctx, method, s->handler, 3, args);
|
||||
fail:
|
||||
JS_FreeValue(ctx, method);
|
||||
JS_FreeValue(ctx, arg_array);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int js_proxy_isArray(JSContext *ctx, JSValueConst obj)
|
||||
{
|
||||
JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);
|
||||
if (!s)
|
||||
return FALSE;
|
||||
if (s->is_revoked) {
|
||||
JS_ThrowTypeErrorRevokedProxy(ctx);
|
||||
return -1;
|
||||
}
|
||||
return JS_IsArray(ctx, s->target);
|
||||
}
|
||||
|
||||
static const JSClassExoticMethods js_proxy_exotic_methods = {
|
||||
.get_own_property = js_proxy_get_own_property,
|
||||
.define_own_property = js_proxy_define_own_property,
|
||||
.delete_property = js_proxy_delete_property,
|
||||
.get_own_property_names = js_proxy_get_own_property_names,
|
||||
.has_property = js_proxy_has,
|
||||
.get_property = js_proxy_get,
|
||||
.set_property = js_proxy_set,
|
||||
};
|
||||
|
||||
static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValueConst target, handler;
|
||||
JSValue obj;
|
||||
JSProxyData *s;
|
||||
|
||||
target = argv[0];
|
||||
handler = argv[1];
|
||||
if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT ||
|
||||
JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT)
|
||||
return JS_ThrowTypeErrorNotAnObject(ctx);
|
||||
|
||||
obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY);
|
||||
if (JS_IsException(obj))
|
||||
return obj;
|
||||
s = js_malloc(ctx, sizeof(JSProxyData));
|
||||
if (!s) {
|
||||
JS_FreeValue(ctx, obj);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
s->target = JS_DupValue(ctx, target);
|
||||
s->handler = JS_DupValue(ctx, handler);
|
||||
s->is_func = JS_IsFunction(ctx, target);
|
||||
s->is_revoked = FALSE;
|
||||
JS_SetOpaque(obj, s);
|
||||
JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target));
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int magic,
|
||||
JSValue *func_data)
|
||||
{
|
||||
JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY);
|
||||
if (s) {
|
||||
/* We do not free the handler and target in case they are
|
||||
referenced as constants in the C call stack */
|
||||
s->is_revoked = TRUE;
|
||||
JS_FreeValue(ctx, func_data[0]);
|
||||
func_data[0] = JS_NULL;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue js_proxy_revoke_constructor(JSContext *ctx,
|
||||
JSValueConst proxy_obj)
|
||||
{
|
||||
return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj);
|
||||
}
|
||||
|
||||
static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj;
|
||||
proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv);
|
||||
if (JS_IsException(proxy_obj))
|
||||
goto fail;
|
||||
revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj);
|
||||
if (JS_IsException(revoke_obj))
|
||||
goto fail;
|
||||
obj = JS_NewObject(ctx);
|
||||
if (JS_IsException(obj))
|
||||
goto fail;
|
||||
// XXX: exceptions?
|
||||
JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E);
|
||||
return obj;
|
||||
fail:
|
||||
JS_FreeValue(ctx, proxy_obj);
|
||||
JS_FreeValue(ctx, revoke_obj);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_proxy_funcs[] = {
|
||||
JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ),
|
||||
};
|
||||
|
||||
static const JSClassShortDef js_proxy_class_def[] = {
|
||||
{ JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */
|
||||
};
|
||||
|
||||
void JS_AddIntrinsicProxy(JSContext *ctx)
|
||||
{
|
||||
JSRuntime *rt = ctx->rt;
|
||||
JSValue obj1;
|
||||
if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) {
|
||||
init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY,
|
||||
countof(js_proxy_class_def));
|
||||
rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods;
|
||||
rt->class_array[JS_CLASS_PROXY].call = js_proxy_call;
|
||||
}
|
||||
obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2,
|
||||
JS_CFUNC_constructor, 0);
|
||||
JS_SetConstructorBit(ctx, obj1, TRUE);
|
||||
JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs,
|
||||
countof(js_proxy_funcs));
|
||||
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy",
|
||||
obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
|
||||
}
|
4
third_party/quickjs/qjs.c
vendored
4
third_party/quickjs/qjs.c
vendored
|
@ -205,11 +205,9 @@ static void js_trace_malloc_init(struct trace_malloc_data *s)
|
|||
static void *js_trace_malloc(JSMallocState *s, size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
/* Do not allocate zero bytes: behavior is platform dependent */
|
||||
assert(size != 0);
|
||||
|
||||
if (unlikely(s->malloc_size + size > s->malloc_limit))
|
||||
if (UNLIKELY(s->malloc_size + size > s->malloc_limit))
|
||||
return NULL;
|
||||
ptr = malloc(size);
|
||||
js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr);
|
||||
|
|
45
third_party/quickjs/qjsc.c
vendored
45
third_party/quickjs/qjsc.c
vendored
|
@ -137,7 +137,6 @@ static void get_c_name(char *buf, size_t buf_size, const char *file)
|
|||
size_t len, i;
|
||||
int c;
|
||||
char *q;
|
||||
|
||||
p = strrchr(file, '/');
|
||||
if (!p)
|
||||
p = file;
|
||||
|
@ -193,16 +192,13 @@ static void output_object_code(JSContext *ctx,
|
|||
js_std_dump_error(ctx);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
namelist_add(&cname_list, c_name, NULL, load_only);
|
||||
|
||||
fprintf(fo, "const uint32_t %s_size = %u;\n\n",
|
||||
fprintf(fo, "const uint32_t %s_size = %u;\n\n",
|
||||
c_name, (unsigned int)out_buf_len);
|
||||
fprintf(fo, "const uint8_t %s[%u] = {\n",
|
||||
c_name, (unsigned int)out_buf_len);
|
||||
dump_hex(fo, out_buf, out_buf_len);
|
||||
fprintf(fo, "};\n\n");
|
||||
|
||||
js_free(ctx, out_buf);
|
||||
}
|
||||
|
||||
|
@ -242,7 +238,6 @@ JSModuleDef *jsc_module_loader(JSContext *ctx,
|
|||
{
|
||||
JSModuleDef *m;
|
||||
namelist_entry_t *e;
|
||||
|
||||
/* check if it is a declared C or system module */
|
||||
e = namelist_find(&cmodule_list, module_name);
|
||||
if (e) {
|
||||
|
@ -262,14 +257,12 @@ JSModuleDef *jsc_module_loader(JSContext *ctx,
|
|||
uint8_t *buf;
|
||||
JSValue func_val;
|
||||
char cname[1024];
|
||||
|
||||
buf = js_load_file(ctx, &buf_len, module_name);
|
||||
if (!buf) {
|
||||
JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
|
||||
module_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* compile the module */
|
||||
func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
|
||||
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
|
||||
|
@ -281,7 +274,6 @@ JSModuleDef *jsc_module_loader(JSContext *ctx,
|
|||
find_unique_cname(cname, sizeof(cname));
|
||||
}
|
||||
output_object_code(ctx, outfile, func_val, cname, TRUE);
|
||||
|
||||
/* the module is already referenced, so we must free it */
|
||||
m = JS_VALUE_GET_PTR(func_val);
|
||||
JS_FreeValue(ctx, func_val);
|
||||
|
@ -299,7 +291,6 @@ static void compile_file(JSContext *ctx, FILE *fo,
|
|||
int eval_flags;
|
||||
JSValue obj;
|
||||
size_t buf_len;
|
||||
|
||||
buf = js_load_file(ctx, &buf_len, filename);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "Could not load '%s'\n", filename);
|
||||
|
@ -388,13 +379,11 @@ void help(void)
|
|||
int exec_cmd(char **argv)
|
||||
{
|
||||
int pid, status, ret;
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
execvp(argv[0], argv);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
for(;;) {
|
||||
ret = waitpid(pid, &status, 0);
|
||||
if (ret == pid && WIFEXITED(status))
|
||||
|
@ -411,7 +400,6 @@ static int output_executable(const char *out_filename, const char *cfilename,
|
|||
char libjsname[1024];
|
||||
char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p;
|
||||
int ret;
|
||||
|
||||
/* get the directory of the executable */
|
||||
pstrcpy(exe_dir, sizeof(exe_dir), exename);
|
||||
p = strrchr(exe_dir, '/');
|
||||
|
@ -420,7 +408,6 @@ static int output_executable(const char *out_filename, const char *cfilename,
|
|||
} else {
|
||||
pstrcpy(exe_dir, sizeof(exe_dir), ".");
|
||||
}
|
||||
|
||||
/* if 'quickjs.h' is present at the same path as the executable, we
|
||||
use it as include and lib directory */
|
||||
snprintf(buf, sizeof(buf), "%s/quickjs.h", exe_dir);
|
||||
|
@ -431,10 +418,8 @@ static int output_executable(const char *out_filename, const char *cfilename,
|
|||
snprintf(inc_dir, sizeof(inc_dir), "%s/include/quickjs", CONFIG_PREFIX);
|
||||
snprintf(lib_dir, sizeof(lib_dir), "%s/lib/quickjs", CONFIG_PREFIX);
|
||||
}
|
||||
|
||||
lto_suffix = "";
|
||||
bn_suffix = "";
|
||||
|
||||
arg = argv;
|
||||
*arg++ = CONFIG_CC;
|
||||
*arg++ = "-O2";
|
||||
|
@ -462,13 +447,11 @@ static int output_executable(const char *out_filename, const char *cfilename,
|
|||
*arg++ = "-ldl";
|
||||
*arg++ = "-lpthread";
|
||||
*arg = NULL;
|
||||
|
||||
if (verbose) {
|
||||
for(arg = argv; *arg != NULL; arg++)
|
||||
printf("%s ", *arg);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
ret = exec_cmd((char **)argv);
|
||||
unlink(cfilename);
|
||||
return ret;
|
||||
|
@ -483,7 +466,6 @@ static int output_executable(const char *out_filename, const char *cfilename,
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
typedef enum {
|
||||
OUTPUT_C,
|
||||
OUTPUT_C_MAIN,
|
||||
|
@ -506,7 +488,6 @@ int main(int argc, char **argv)
|
|||
BOOL bignum_ext = FALSE;
|
||||
#endif
|
||||
namelist_t dynamic_module_list;
|
||||
|
||||
out_filename = NULL;
|
||||
output_type = OUTPUT_EXECUTABLE;
|
||||
cname = NULL;
|
||||
|
@ -517,11 +498,9 @@ int main(int argc, char **argv)
|
|||
use_lto = FALSE;
|
||||
stack_size = 0;
|
||||
memset(&dynamic_module_list, 0, sizeof(dynamic_module_list));
|
||||
|
||||
/* add system modules */
|
||||
namelist_add(&cmodule_list, "std", "std", 0);
|
||||
namelist_add(&cmodule_list, "os", "os", 0);
|
||||
|
||||
for(;;) {
|
||||
c = getopt(argc, argv, "ho:cN:f:mxevM:p:S:D:");
|
||||
if (c == -1)
|
||||
|
@ -608,10 +587,8 @@ int main(int argc, char **argv)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc)
|
||||
help();
|
||||
|
||||
if (!out_filename) {
|
||||
if (output_type == OUTPUT_EXECUTABLE) {
|
||||
out_filename = "a.out";
|
||||
|
@ -619,7 +596,6 @@ int main(int argc, char **argv)
|
|||
out_filename = "out.c";
|
||||
}
|
||||
}
|
||||
|
||||
if (output_type == OUTPUT_EXECUTABLE) {
|
||||
#if defined(_WIN32) || defined(__ANDROID__)
|
||||
/* XXX: find a /tmp directory ? */
|
||||
|
@ -630,14 +606,12 @@ int main(int argc, char **argv)
|
|||
} else {
|
||||
pstrcpy(cfilename, sizeof(cfilename), out_filename);
|
||||
}
|
||||
|
||||
fo = fopen(cfilename, "w");
|
||||
if (!fo) {
|
||||
perror(cfilename);
|
||||
exit(1);
|
||||
}
|
||||
outfile = fo;
|
||||
|
||||
rt = JS_NewRuntime();
|
||||
ctx = JS_NewContext(rt);
|
||||
#ifdef CONFIG_BIGNUM
|
||||
|
@ -648,14 +622,11 @@ int main(int argc, char **argv)
|
|||
JS_EnableBignumExt(ctx, TRUE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* loader for ES6 modules */
|
||||
JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL);
|
||||
|
||||
fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n"
|
||||
"\n"
|
||||
);
|
||||
|
||||
if (output_type != OUTPUT_C) {
|
||||
fprintf(fo, "#include \"quickjs-libc.h\"\n"
|
||||
"\n"
|
||||
|
@ -665,13 +636,11 @@ int main(int argc, char **argv)
|
|||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
for(i = optind; i < argc; i++) {
|
||||
const char *filename = argv[i];
|
||||
compile_file(ctx, fo, filename, cname, module);
|
||||
cname = NULL;
|
||||
}
|
||||
|
||||
for(i = 0; i < dynamic_module_list.count; i++) {
|
||||
if (!jsc_module_loader(ctx, dynamic_module_list.array[i].name, NULL)) {
|
||||
fprintf(stderr, "Could not load dynamic module '%s'\n",
|
||||
|
@ -679,7 +648,6 @@ int main(int argc, char **argv)
|
|||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (output_type != OUTPUT_C) {
|
||||
fprintf(fo,
|
||||
"static JSContext *JS_NewCustomContext(JSRuntime *rt)\n"
|
||||
|
@ -710,7 +678,6 @@ int main(int argc, char **argv)
|
|||
for(i = 0; i < init_module_list.count; i++) {
|
||||
namelist_entry_t *e = &init_module_list.array[i];
|
||||
/* initialize the static C modules */
|
||||
|
||||
fprintf(fo,
|
||||
" {\n"
|
||||
" extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n"
|
||||
|
@ -728,23 +695,18 @@ int main(int argc, char **argv)
|
|||
fprintf(fo,
|
||||
" return ctx;\n"
|
||||
"}\n\n");
|
||||
|
||||
fputs(main_c_template1, fo);
|
||||
|
||||
if (stack_size != 0) {
|
||||
fprintf(fo, " JS_SetMaxStackSize(rt, %u);\n",
|
||||
(unsigned int)stack_size);
|
||||
}
|
||||
|
||||
/* add the module loader if necessary */
|
||||
if (feature_bitmap & (1 << FE_MODULE_LOADER)) {
|
||||
fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n");
|
||||
}
|
||||
|
||||
fprintf(fo,
|
||||
" ctx = JS_NewCustomContext(rt);\n"
|
||||
" js_std_add_helpers(ctx, argc, argv);\n");
|
||||
|
||||
for(i = 0; i < cname_list.count; i++) {
|
||||
namelist_entry_t *e = &cname_list.array[i];
|
||||
if (!e->flags) {
|
||||
|
@ -754,12 +716,9 @@ int main(int argc, char **argv)
|
|||
}
|
||||
fputs(main_c_template2, fo);
|
||||
}
|
||||
|
||||
JS_FreeContext(ctx);
|
||||
JS_FreeRuntime(rt);
|
||||
|
||||
fclose(fo);
|
||||
|
||||
if (output_type == OUTPUT_EXECUTABLE) {
|
||||
return output_executable(out_filename, cfilename, use_lto, verbose,
|
||||
argv[0]);
|
||||
|
|
3
third_party/quickjs/quickjs-libc.c
vendored
3
third_party/quickjs/quickjs-libc.c
vendored
|
@ -2229,8 +2229,7 @@ static int js_os_poll(JSContext *ctx)
|
|||
struct timeval tv, *tvp;
|
||||
|
||||
/* only check signals in the main thread */
|
||||
if (!ts->recv_pipe &&
|
||||
unlikely(os_pending_signals != 0)) {
|
||||
if (!ts->recv_pipe && UNLIKELY(os_pending_signals != 0)) {
|
||||
JSOSSignalHandler *sh;
|
||||
uint64_t mask;
|
||||
|
||||
|
|
39687
third_party/quickjs/quickjs.c
vendored
39687
third_party/quickjs/quickjs.c
vendored
File diff suppressed because it is too large
Load diff
30
third_party/quickjs/quickjs.h
vendored
30
third_party/quickjs/quickjs.h
vendored
|
@ -1,5 +1,6 @@
|
|||
#ifndef COSMOPOLITAN_THIRD_PARTY_QUICKJS_QUICKJS_H_
|
||||
#define COSMOPOLITAN_THIRD_PARTY_QUICKJS_QUICKJS_H_
|
||||
#include "libc/bits/likely.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
@ -7,14 +8,8 @@ COSMOPOLITAN_C_START_
|
|||
/* clang-format off */
|
||||
|
||||
#if (defined(__GNUC__) || defined(__clang__)) && !defined(__STRICT_ANSI__)
|
||||
#define js_likely(x) __builtin_expect(!!(x), 1)
|
||||
#define js_unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#define js_force_inline forceinline
|
||||
#define __js_printf_like(f, a) __attribute__((__format__(__printf__, f, a)))
|
||||
#else
|
||||
#define js_likely(x) (x)
|
||||
#define js_unlikely(x) (x)
|
||||
#define js_force_inline inline
|
||||
#define __js_printf_like(a, b)
|
||||
#endif
|
||||
|
||||
|
@ -139,7 +134,7 @@ static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)
|
|||
JSValue v;
|
||||
u.d = d;
|
||||
/* normalize NaN */
|
||||
if (js_unlikely((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000))
|
||||
if (UNLIKELY((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000))
|
||||
v = JS_NAN;
|
||||
else
|
||||
v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32);
|
||||
|
@ -476,22 +471,22 @@ int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id);
|
|||
|
||||
/* value handling */
|
||||
|
||||
js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val)
|
||||
forceinline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val)
|
||||
{
|
||||
return JS_MKVAL(JS_TAG_BOOL, (val != 0));
|
||||
}
|
||||
|
||||
js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val)
|
||||
forceinline JSValue JS_NewInt32(JSContext *ctx, int32_t val)
|
||||
{
|
||||
return JS_MKVAL(JS_TAG_INT, val);
|
||||
}
|
||||
|
||||
js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val)
|
||||
forceinline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val)
|
||||
{
|
||||
return JS_MKVAL(JS_TAG_CATCH_OFFSET, val);
|
||||
}
|
||||
|
||||
js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val)
|
||||
forceinline JSValue JS_NewInt64(JSContext *ctx, int64_t val)
|
||||
{
|
||||
JSValue v;
|
||||
if (val == (int32_t)val) {
|
||||
|
@ -502,7 +497,7 @@ js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val)
|
|||
return v;
|
||||
}
|
||||
|
||||
js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val)
|
||||
forceinline JSValue JS_NewUint32(JSContext *ctx, uint32_t val)
|
||||
{
|
||||
JSValue v;
|
||||
if (val <= 0x7fffffff) {
|
||||
|
@ -516,7 +511,7 @@ js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val)
|
|||
JSValue JS_NewBigInt64(JSContext *ctx, int64_t v);
|
||||
JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v);
|
||||
|
||||
js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d)
|
||||
forceinline JSValue JS_NewFloat64(JSContext *ctx, double d)
|
||||
{
|
||||
JSValue v;
|
||||
int32_t val;
|
||||
|
@ -578,12 +573,12 @@ static inline JS_BOOL JS_IsUndefined(JSValueConst v)
|
|||
|
||||
static inline JS_BOOL JS_IsException(JSValueConst v)
|
||||
{
|
||||
return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION);
|
||||
return UNLIKELY(JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION);
|
||||
}
|
||||
|
||||
static inline JS_BOOL JS_IsUninitialized(JSValueConst v)
|
||||
{
|
||||
return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED);
|
||||
return UNLIKELY(JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED);
|
||||
}
|
||||
|
||||
static inline JS_BOOL JS_IsString(JSValueConst v)
|
||||
|
@ -697,7 +692,7 @@ int JS_IsArray(JSContext *ctx, JSValueConst val);
|
|||
JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
|
||||
JSAtom prop, JSValueConst receiver,
|
||||
JS_BOOL throw_ref_error);
|
||||
js_force_inline JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj,
|
||||
forceinline JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj,
|
||||
JSAtom prop)
|
||||
{
|
||||
return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0);
|
||||
|
@ -1013,9 +1008,6 @@ int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
|
|||
int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
|
||||
const JSCFunctionListEntry *tab, int len);
|
||||
|
||||
#undef js_unlikely
|
||||
#undef js_force_inline
|
||||
|
||||
/* clang-format on */
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
52
third_party/quickjs/quickjs.mk
vendored
52
third_party/quickjs/quickjs.mk
vendored
|
@ -10,16 +10,56 @@ THIRD_PARTY_QUICKJS_A = o/$(MODE)/third_party/quickjs/quickjs.a
|
|||
THIRD_PARTY_QUICKJS_HDRS = $(foreach x,$(THIRD_PARTY_QUICKJS_ARTIFACTS),$($(x)_HDRS))
|
||||
|
||||
THIRD_PARTY_QUICKJS_A_SRCS = \
|
||||
third_party/quickjs/array.c \
|
||||
third_party/quickjs/atof.c \
|
||||
third_party/quickjs/atom.c \
|
||||
third_party/quickjs/atomics.c \
|
||||
third_party/quickjs/bigdecimal.c \
|
||||
third_party/quickjs/bigint.c \
|
||||
third_party/quickjs/byte.c \
|
||||
third_party/quickjs/call.c \
|
||||
third_party/quickjs/cutils.c \
|
||||
third_party/quickjs/date.c \
|
||||
third_party/quickjs/dbuf.c \
|
||||
third_party/quickjs/dbuf.c \
|
||||
third_party/quickjs/diglet.c \
|
||||
third_party/quickjs/eq.c \
|
||||
third_party/quickjs/err.c \
|
||||
third_party/quickjs/float.c \
|
||||
third_party/quickjs/gc.c \
|
||||
third_party/quickjs/gen.c \
|
||||
third_party/quickjs/iter.c \
|
||||
third_party/quickjs/json.c \
|
||||
third_party/quickjs/leb128.c \
|
||||
third_party/quickjs/libbf.c \
|
||||
third_party/quickjs/libregexp.c \
|
||||
third_party/quickjs/libunicode.c \
|
||||
third_party/quickjs/map.c \
|
||||
third_party/quickjs/math.c \
|
||||
third_party/quickjs/mem.c \
|
||||
third_party/quickjs/object.c \
|
||||
third_party/quickjs/parse.c \
|
||||
third_party/quickjs/prim.c \
|
||||
third_party/quickjs/promise.c \
|
||||
third_party/quickjs/proxy.c \
|
||||
third_party/quickjs/quickjs-libc.c \
|
||||
third_party/quickjs/quickjs.c \
|
||||
third_party/quickjs/reflect.c \
|
||||
third_party/quickjs/regexp.c \
|
||||
third_party/quickjs/shape.c \
|
||||
third_party/quickjs/str.c \
|
||||
third_party/quickjs/strbuf.c \
|
||||
third_party/quickjs/tok.c \
|
||||
third_party/quickjs/typedarray.c \
|
||||
third_party/quickjs/uri.c \
|
||||
third_party/quickjs/usage.c \
|
||||
third_party/quickjs/wut.c
|
||||
|
||||
THIRD_PARTY_QUICKJS_A_HDRS = \
|
||||
third_party/quickjs/cutils.h \
|
||||
third_party/quickjs/diglet.h \
|
||||
third_party/quickjs/internal.h \
|
||||
third_party/quickjs/leb128.h \
|
||||
third_party/quickjs/libbf.h \
|
||||
third_party/quickjs/libregexp.h \
|
||||
third_party/quickjs/libunicode.h \
|
||||
|
@ -28,13 +68,7 @@ THIRD_PARTY_QUICKJS_A_HDRS = \
|
|||
third_party/quickjs/quickjs.h
|
||||
|
||||
THIRD_PARTY_QUICKJS_A_OBJS = \
|
||||
o/$(MODE)/third_party/quickjs/cutils.o \
|
||||
o/$(MODE)/third_party/quickjs/libbf.o \
|
||||
o/$(MODE)/third_party/quickjs/libregexp.o \
|
||||
o/$(MODE)/third_party/quickjs/libunicode.o \
|
||||
o/$(MODE)/third_party/quickjs/quickjs-libc.o \
|
||||
o/$(MODE)/third_party/quickjs/quickjs.o \
|
||||
o/$(MODE)/third_party/quickjs/wut.o
|
||||
$(THIRD_PARTY_QUICKJS_A_SRCS:%.c=o/$(MODE)/%.o)
|
||||
|
||||
THIRD_PARTY_QUICKJS_A_DIRECTDEPS = \
|
||||
LIBC_ALG \
|
||||
|
@ -137,6 +171,10 @@ $(THIRD_PARTY_QUICKJS_OBJS): \
|
|||
-DCONFIG_BIGNUM \
|
||||
-DCONFIG_VERSION=\"$(shell cat third_party/quickjs/VERSION)\"
|
||||
|
||||
o/tiny/third_party/quickjs/call.o: \
|
||||
OVERRIDE_CFLAGS += \
|
||||
-O2
|
||||
|
||||
o/$(MODE)/third_party/quickjs/unicode_gen.o: \
|
||||
OVERRIDE_CPPFLAGS += \
|
||||
-DSTACK_FRAME_UNLIMITED
|
||||
|
|
197
third_party/quickjs/reflect.c
vendored
Normal file
197
third_party/quickjs/reflect.c
vendored
Normal file
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2);
|
||||
}
|
||||
|
||||
static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValueConst func, array_arg, new_target;
|
||||
JSValue *tab, ret;
|
||||
uint32_t len;
|
||||
func = argv[0];
|
||||
array_arg = argv[1];
|
||||
if (argc > 2) {
|
||||
new_target = argv[2];
|
||||
if (!JS_IsConstructor(ctx, new_target))
|
||||
return JS_ThrowTypeError(ctx, "not a constructor");
|
||||
} else {
|
||||
new_target = func;
|
||||
}
|
||||
tab = build_arg_list(ctx, &len, array_arg);
|
||||
if (!tab)
|
||||
return JS_EXCEPTION;
|
||||
ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab);
|
||||
free_arg_list(ctx, tab, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValueConst obj;
|
||||
JSAtom atom;
|
||||
int ret;
|
||||
obj = argv[0];
|
||||
if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
|
||||
return JS_ThrowTypeErrorNotAnObject(ctx);
|
||||
atom = JS_ValueToAtom(ctx, argv[1]);
|
||||
if (UNLIKELY(atom == JS_ATOM_NULL))
|
||||
return JS_EXCEPTION;
|
||||
ret = JS_DeleteProperty(ctx, obj, atom, 0);
|
||||
JS_FreeAtom(ctx, atom);
|
||||
if (ret < 0)
|
||||
return JS_EXCEPTION;
|
||||
else
|
||||
return JS_NewBool(ctx, ret);
|
||||
}
|
||||
|
||||
static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValueConst obj, prop, receiver;
|
||||
JSAtom atom;
|
||||
JSValue ret;
|
||||
obj = argv[0];
|
||||
prop = argv[1];
|
||||
if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
|
||||
return JS_ThrowTypeErrorNotAnObject(ctx);
|
||||
if (argc > 2)
|
||||
receiver = argv[2];
|
||||
else
|
||||
receiver = obj;
|
||||
atom = JS_ValueToAtom(ctx, prop);
|
||||
if (UNLIKELY(atom == JS_ATOM_NULL))
|
||||
return JS_EXCEPTION;
|
||||
ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE);
|
||||
JS_FreeAtom(ctx, atom);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValueConst obj, prop;
|
||||
JSAtom atom;
|
||||
int ret;
|
||||
obj = argv[0];
|
||||
prop = argv[1];
|
||||
if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
|
||||
return JS_ThrowTypeErrorNotAnObject(ctx);
|
||||
atom = JS_ValueToAtom(ctx, prop);
|
||||
if (UNLIKELY(atom == JS_ATOM_NULL))
|
||||
return JS_EXCEPTION;
|
||||
ret = JS_HasProperty(ctx, obj, atom);
|
||||
JS_FreeAtom(ctx, atom);
|
||||
if (ret < 0)
|
||||
return JS_EXCEPTION;
|
||||
else
|
||||
return JS_NewBool(ctx, ret);
|
||||
}
|
||||
|
||||
static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValueConst obj, prop, val, receiver;
|
||||
int ret;
|
||||
JSAtom atom;
|
||||
obj = argv[0];
|
||||
prop = argv[1];
|
||||
val = argv[2];
|
||||
if (argc > 3)
|
||||
receiver = argv[3];
|
||||
else
|
||||
receiver = obj;
|
||||
if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
|
||||
return JS_ThrowTypeErrorNotAnObject(ctx);
|
||||
atom = JS_ValueToAtom(ctx, prop);
|
||||
if (UNLIKELY(atom == JS_ATOM_NULL))
|
||||
return JS_EXCEPTION;
|
||||
ret = JS_SetPropertyGeneric(ctx, obj, atom,
|
||||
JS_DupValue(ctx, val), receiver, 0);
|
||||
JS_FreeAtom(ctx, atom);
|
||||
if (ret < 0)
|
||||
return JS_EXCEPTION;
|
||||
else
|
||||
return JS_NewBool(ctx, ret);
|
||||
}
|
||||
|
||||
static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
int ret;
|
||||
ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE);
|
||||
if (ret < 0)
|
||||
return JS_EXCEPTION;
|
||||
else
|
||||
return JS_NewBool(ctx, ret);
|
||||
}
|
||||
|
||||
static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
|
||||
return JS_ThrowTypeErrorNotAnObject(ctx);
|
||||
return JS_GetOwnPropertyNames2(ctx, argv[0],
|
||||
JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK,
|
||||
JS_ITERATOR_KIND_KEY);
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_reflect_funcs[] = {
|
||||
JS_CFUNC_DEF("apply", 3, js_reflect_apply ),
|
||||
JS_CFUNC_DEF("construct", 2, js_reflect_construct ),
|
||||
JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ),
|
||||
JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ),
|
||||
JS_CFUNC_DEF("get", 2, js_reflect_get ),
|
||||
JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ),
|
||||
JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ),
|
||||
JS_CFUNC_DEF("has", 2, js_reflect_has ),
|
||||
JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ),
|
||||
JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ),
|
||||
JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ),
|
||||
JS_CFUNC_DEF("set", 3, js_reflect_set ),
|
||||
JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_reflect_obj[] = {
|
||||
JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
|
||||
};
|
||||
|
||||
void JS_AddIntrinsicReflect(JSContext *ctx) {
|
||||
JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj));
|
||||
}
|
1409
third_party/quickjs/regexp.c
vendored
Normal file
1409
third_party/quickjs/regexp.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
449
third_party/quickjs/shape.c
vendored
Normal file
449
third_party/quickjs/shape.c
vendored
Normal file
|
@ -0,0 +1,449 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
int init_shape_hash(JSRuntime *rt)
|
||||
{
|
||||
rt->shape_hash_bits = 4; /* 16 shapes */
|
||||
rt->shape_hash_size = 1 << rt->shape_hash_bits;
|
||||
rt->shape_hash_count = 0;
|
||||
rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
|
||||
rt->shape_hash_size);
|
||||
if (!rt->shape_hash)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* same magic hash multiplier as the Linux kernel */
|
||||
static uint32_t shape_hash(uint32_t h, uint32_t val)
|
||||
{
|
||||
return (h + val) * 0x9e370001;
|
||||
}
|
||||
|
||||
/* truncate the shape hash to 'hash_bits' bits */
|
||||
static uint32_t get_shape_hash(uint32_t h, int hash_bits)
|
||||
{
|
||||
return h >> (32 - hash_bits);
|
||||
}
|
||||
|
||||
static uint32_t shape_initial_hash(JSObject *proto)
|
||||
{
|
||||
uint32_t h;
|
||||
h = shape_hash(1, (uintptr_t)proto);
|
||||
if (sizeof(proto) > 4)
|
||||
h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32);
|
||||
return h;
|
||||
}
|
||||
|
||||
static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits)
|
||||
{
|
||||
int new_shape_hash_size, i;
|
||||
uint32_t h;
|
||||
JSShape **new_shape_hash, *sh, *sh_next;
|
||||
new_shape_hash_size = 1 << new_shape_hash_bits;
|
||||
new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
|
||||
new_shape_hash_size);
|
||||
if (!new_shape_hash)
|
||||
return -1;
|
||||
for(i = 0; i < rt->shape_hash_size; i++) {
|
||||
for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) {
|
||||
sh_next = sh->shape_hash_next;
|
||||
h = get_shape_hash(sh->hash, new_shape_hash_bits);
|
||||
sh->shape_hash_next = new_shape_hash[h];
|
||||
new_shape_hash[h] = sh;
|
||||
}
|
||||
}
|
||||
js_free_rt(rt, rt->shape_hash);
|
||||
rt->shape_hash_bits = new_shape_hash_bits;
|
||||
rt->shape_hash_size = new_shape_hash_size;
|
||||
rt->shape_hash = new_shape_hash;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void js_shape_hash_link(JSRuntime *rt, JSShape *sh)
|
||||
{
|
||||
uint32_t h;
|
||||
h = get_shape_hash(sh->hash, rt->shape_hash_bits);
|
||||
sh->shape_hash_next = rt->shape_hash[h];
|
||||
rt->shape_hash[h] = sh;
|
||||
rt->shape_hash_count++;
|
||||
}
|
||||
|
||||
void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh)
|
||||
{
|
||||
uint32_t h;
|
||||
JSShape **psh;
|
||||
h = get_shape_hash(sh->hash, rt->shape_hash_bits);
|
||||
psh = &rt->shape_hash[h];
|
||||
while (*psh != sh)
|
||||
psh = &(*psh)->shape_hash_next;
|
||||
*psh = sh->shape_hash_next;
|
||||
rt->shape_hash_count--;
|
||||
}
|
||||
|
||||
/* create a new empty shape with prototype 'proto' */
|
||||
JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, int hash_size, int prop_size)
|
||||
{
|
||||
JSRuntime *rt = ctx->rt;
|
||||
void *sh_alloc;
|
||||
JSShape *sh;
|
||||
/* resize the shape hash table if necessary */
|
||||
if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) {
|
||||
resize_shape_hash(rt, rt->shape_hash_bits + 1);
|
||||
}
|
||||
sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size));
|
||||
if (!sh_alloc)
|
||||
return NULL;
|
||||
sh = get_shape_from_alloc(sh_alloc, hash_size);
|
||||
sh->header.ref_count = 1;
|
||||
add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
|
||||
if (proto)
|
||||
JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));
|
||||
sh->proto = proto;
|
||||
memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) *
|
||||
hash_size);
|
||||
sh->prop_hash_mask = hash_size - 1;
|
||||
sh->prop_size = prop_size;
|
||||
sh->prop_count = 0;
|
||||
sh->deleted_prop_count = 0;
|
||||
/* insert in the hash table */
|
||||
sh->hash = shape_initial_hash(proto);
|
||||
sh->is_hashed = TRUE;
|
||||
sh->has_small_array_index = FALSE;
|
||||
js_shape_hash_link(ctx->rt, sh);
|
||||
return sh;
|
||||
}
|
||||
|
||||
JSShape *js_new_shape(JSContext *ctx, JSObject *proto)
|
||||
{
|
||||
return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE,
|
||||
JS_PROP_INITIAL_SIZE);
|
||||
}
|
||||
|
||||
/* The shape is cloned. The new shape is not inserted in the shape
|
||||
hash table */
|
||||
JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)
|
||||
{
|
||||
JSShape *sh;
|
||||
void *sh_alloc, *sh_alloc1;
|
||||
size_t size;
|
||||
JSShapeProperty *pr;
|
||||
uint32_t i, hash_size;
|
||||
hash_size = sh1->prop_hash_mask + 1;
|
||||
size = get_shape_size(hash_size, sh1->prop_size);
|
||||
sh_alloc = js_malloc(ctx, size);
|
||||
if (!sh_alloc)
|
||||
return NULL;
|
||||
sh_alloc1 = get_alloc_from_shape(sh1);
|
||||
memcpy(sh_alloc, sh_alloc1, size);
|
||||
sh = get_shape_from_alloc(sh_alloc, hash_size);
|
||||
sh->header.ref_count = 1;
|
||||
add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
|
||||
sh->is_hashed = FALSE;
|
||||
if (sh->proto) {
|
||||
JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
|
||||
}
|
||||
for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
|
||||
JS_DupAtom(ctx, pr->atom);
|
||||
}
|
||||
return sh;
|
||||
}
|
||||
|
||||
JSShape *js_dup_shape(JSShape *sh)
|
||||
{
|
||||
sh->header.ref_count++;
|
||||
return sh;
|
||||
}
|
||||
|
||||
static void js_free_shape0(JSRuntime *rt, JSShape *sh)
|
||||
{
|
||||
uint32_t i;
|
||||
JSShapeProperty *pr;
|
||||
assert(sh->header.ref_count == 0);
|
||||
if (sh->is_hashed)
|
||||
js_shape_hash_unlink(rt, sh);
|
||||
if (sh->proto != NULL) {
|
||||
JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
|
||||
}
|
||||
pr = get_shape_prop(sh);
|
||||
for(i = 0; i < sh->prop_count; i++) {
|
||||
JS_FreeAtomRT(rt, pr->atom);
|
||||
pr++;
|
||||
}
|
||||
remove_gc_object(&sh->header);
|
||||
js_free_rt(rt, get_alloc_from_shape(sh));
|
||||
}
|
||||
|
||||
void js_free_shape(JSRuntime *rt, JSShape *sh)
|
||||
{
|
||||
if (UNLIKELY(--sh->header.ref_count <= 0)) {
|
||||
js_free_shape0(rt, sh);
|
||||
}
|
||||
}
|
||||
|
||||
int add_shape_property(JSContext *ctx, JSShape **psh,
|
||||
JSObject *p, JSAtom atom, int prop_flags)
|
||||
{
|
||||
JSRuntime *rt = ctx->rt;
|
||||
JSShape *sh = *psh;
|
||||
JSShapeProperty *pr, *prop;
|
||||
uint32_t hash_mask, new_shape_hash = 0;
|
||||
intptr_t h;
|
||||
/* update the shape hash */
|
||||
if (sh->is_hashed) {
|
||||
js_shape_hash_unlink(rt, sh);
|
||||
new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags);
|
||||
}
|
||||
if (UNLIKELY(sh->prop_count >= sh->prop_size)) {
|
||||
if (resize_properties(ctx, psh, p, sh->prop_count + 1)) {
|
||||
/* in case of error, reinsert in the hash table.
|
||||
sh is still valid if resize_properties() failed */
|
||||
if (sh->is_hashed)
|
||||
js_shape_hash_link(rt, sh);
|
||||
return -1;
|
||||
}
|
||||
sh = *psh;
|
||||
}
|
||||
if (sh->is_hashed) {
|
||||
sh->hash = new_shape_hash;
|
||||
js_shape_hash_link(rt, sh);
|
||||
}
|
||||
/* Initialize the new shape property.
|
||||
The object property at p->prop[sh->prop_count] is uninitialized */
|
||||
prop = get_shape_prop(sh);
|
||||
pr = &prop[sh->prop_count++];
|
||||
pr->atom = JS_DupAtom(ctx, atom);
|
||||
pr->flags = prop_flags;
|
||||
sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom);
|
||||
/* add in hash table */
|
||||
hash_mask = sh->prop_hash_mask;
|
||||
h = atom & hash_mask;
|
||||
pr->hash_next = prop_hash_end(sh)[-h - 1];
|
||||
prop_hash_end(sh)[-h - 1] = sh->prop_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* find a hashed empty shape matching the prototype. Return NULL if
|
||||
not found */
|
||||
JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto)
|
||||
{
|
||||
JSShape *sh1;
|
||||
uint32_t h, h1;
|
||||
h = shape_initial_hash(proto);
|
||||
h1 = get_shape_hash(h, rt->shape_hash_bits);
|
||||
for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
|
||||
if (sh1->hash == h &&
|
||||
sh1->proto == proto &&
|
||||
sh1->prop_count == 0) {
|
||||
return sh1;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if
|
||||
not found */
|
||||
JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh, JSAtom atom, int prop_flags)
|
||||
{
|
||||
JSShape *sh1;
|
||||
uint32_t h, h1, i, n;
|
||||
h = sh->hash;
|
||||
h = shape_hash(h, atom);
|
||||
h = shape_hash(h, prop_flags);
|
||||
h1 = get_shape_hash(h, rt->shape_hash_bits);
|
||||
for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
|
||||
/* we test the hash first so that the rest is done only if the
|
||||
shapes really match */
|
||||
if (sh1->hash == h &&
|
||||
sh1->proto == sh->proto &&
|
||||
sh1->prop_count == ((n = sh->prop_count) + 1)) {
|
||||
for(i = 0; i < n; i++) {
|
||||
if (UNLIKELY(sh1->prop[i].atom != sh->prop[i].atom) ||
|
||||
UNLIKELY(sh1->prop[i].flags != sh->prop[i].flags))
|
||||
goto next;
|
||||
}
|
||||
if (UNLIKELY(sh1->prop[n].atom != atom) ||
|
||||
UNLIKELY(sh1->prop[n].flags != prop_flags))
|
||||
goto next;
|
||||
return sh1;
|
||||
}
|
||||
next: ;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
|
||||
{
|
||||
char atom_buf[ATOM_GET_STR_BUF_SIZE];
|
||||
int j;
|
||||
/* XXX: should output readable class prototype */
|
||||
printf("%5d %3d%c %14p %5d %5d", i,
|
||||
sh->header.ref_count, " *"[sh->is_hashed],
|
||||
(void *)sh->proto, sh->prop_size, sh->prop_count);
|
||||
for(j = 0; j < sh->prop_count; j++) {
|
||||
printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf),
|
||||
sh->prop[j].atom));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static __maybe_unused void JS_DumpShapes(JSRuntime *rt)
|
||||
{
|
||||
int i;
|
||||
JSShape *sh;
|
||||
struct list_head *el;
|
||||
JSObject *p;
|
||||
JSGCObjectHeader *gp;
|
||||
printf("JSShapes: {\n");
|
||||
printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS");
|
||||
for(i = 0; i < rt->shape_hash_size; i++) {
|
||||
for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
|
||||
JS_DumpShape(rt, i, sh);
|
||||
assert(sh->is_hashed);
|
||||
}
|
||||
}
|
||||
/* dump non-hashed shapes */
|
||||
list_for_each(el, &rt->gc_obj_list) {
|
||||
gp = list_entry(el, JSGCObjectHeader, link);
|
||||
if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
|
||||
p = (JSObject *)gp;
|
||||
if (!p->shape->is_hashed) {
|
||||
JS_DumpShape(rt, -1, p->shape);
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id)
|
||||
{
|
||||
JSObject *p;
|
||||
js_trigger_gc(ctx->rt, sizeof(JSObject));
|
||||
p = js_malloc(ctx, sizeof(JSObject));
|
||||
if (UNLIKELY(!p))
|
||||
goto fail;
|
||||
p->class_id = class_id;
|
||||
p->extensible = TRUE;
|
||||
p->free_mark = 0;
|
||||
p->is_exotic = 0;
|
||||
p->fast_array = 0;
|
||||
p->is_constructor = 0;
|
||||
p->is_uncatchable_error = 0;
|
||||
p->tmp_mark = 0;
|
||||
p->is_HTMLDDA = 0;
|
||||
p->first_weak_ref = NULL;
|
||||
p->u.opaque = NULL;
|
||||
p->shape = sh;
|
||||
p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);
|
||||
if (UNLIKELY(!p->prop)) {
|
||||
js_free(ctx, p);
|
||||
fail:
|
||||
js_free_shape(ctx->rt, sh);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
switch(class_id) {
|
||||
case JS_CLASS_OBJECT:
|
||||
break;
|
||||
case JS_CLASS_ARRAY:
|
||||
{
|
||||
JSProperty *pr;
|
||||
p->is_exotic = 1;
|
||||
p->fast_array = 1;
|
||||
p->u.array.u.values = NULL;
|
||||
p->u.array.count = 0;
|
||||
p->u.array.u1.size = 0;
|
||||
/* the length property is always the first one */
|
||||
if (LIKELY(sh == ctx->array_shape)) {
|
||||
pr = &p->prop[0];
|
||||
} else {
|
||||
/* only used for the first array */
|
||||
/* cannot fail */
|
||||
pr = add_property(ctx, p, JS_ATOM_length,
|
||||
JS_PROP_WRITABLE | JS_PROP_LENGTH);
|
||||
}
|
||||
pr->u.value = JS_NewInt32(ctx, 0);
|
||||
}
|
||||
break;
|
||||
case JS_CLASS_C_FUNCTION:
|
||||
p->prop[0].u.value = JS_UNDEFINED;
|
||||
break;
|
||||
case JS_CLASS_ARGUMENTS:
|
||||
case JS_CLASS_UINT8C_ARRAY:
|
||||
case JS_CLASS_INT8_ARRAY:
|
||||
case JS_CLASS_UINT8_ARRAY:
|
||||
case JS_CLASS_INT16_ARRAY:
|
||||
case JS_CLASS_UINT16_ARRAY:
|
||||
case JS_CLASS_INT32_ARRAY:
|
||||
case JS_CLASS_UINT32_ARRAY:
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_CLASS_BIG_INT64_ARRAY:
|
||||
case JS_CLASS_BIG_UINT64_ARRAY:
|
||||
#endif
|
||||
case JS_CLASS_FLOAT32_ARRAY:
|
||||
case JS_CLASS_FLOAT64_ARRAY:
|
||||
p->is_exotic = 1;
|
||||
p->fast_array = 1;
|
||||
p->u.array.u.ptr = NULL;
|
||||
p->u.array.count = 0;
|
||||
break;
|
||||
case JS_CLASS_DATAVIEW:
|
||||
p->u.array.u.ptr = NULL;
|
||||
p->u.array.count = 0;
|
||||
break;
|
||||
case JS_CLASS_NUMBER:
|
||||
case JS_CLASS_STRING:
|
||||
case JS_CLASS_BOOLEAN:
|
||||
case JS_CLASS_SYMBOL:
|
||||
case JS_CLASS_DATE:
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_CLASS_BIG_INT:
|
||||
case JS_CLASS_BIG_FLOAT:
|
||||
case JS_CLASS_BIG_DECIMAL:
|
||||
#endif
|
||||
p->u.object_data = JS_UNDEFINED;
|
||||
goto set_exotic;
|
||||
case JS_CLASS_REGEXP:
|
||||
p->u.regexp.pattern = NULL;
|
||||
p->u.regexp.bytecode = NULL;
|
||||
goto set_exotic;
|
||||
default:
|
||||
set_exotic:
|
||||
if (ctx->rt->class_array[class_id].exotic) {
|
||||
p->is_exotic = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
p->header.ref_count = 1;
|
||||
add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT);
|
||||
return JS_MKPTR(JS_TAG_OBJECT, p);
|
||||
}
|
2319
third_party/quickjs/str.c
vendored
Normal file
2319
third_party/quickjs/str.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
331
third_party/quickjs/strbuf.c
vendored
Normal file
331
third_party/quickjs/strbuf.c
vendored
Normal file
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "third_party/quickjs/internal.h"
|
||||
#include "third_party/quickjs/libregexp.h"
|
||||
#include "third_party/quickjs/quickjs.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
/* It is valid to call string_buffer_end() and all string_buffer functions even
|
||||
if string_buffer_init() or another string_buffer function returns an error.
|
||||
If the error_status is set, string_buffer_end() returns JS_EXCEPTION.
|
||||
*/
|
||||
int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size, int is_wide)
|
||||
{
|
||||
s->ctx = ctx;
|
||||
s->size = size;
|
||||
s->len = 0;
|
||||
s->is_wide_char = is_wide;
|
||||
s->error_status = 0;
|
||||
s->str = js_alloc_string(ctx, size, is_wide);
|
||||
if (UNLIKELY(!s->str)) {
|
||||
s->size = 0;
|
||||
return s->error_status = -1;
|
||||
}
|
||||
#ifdef DUMP_LEAKS
|
||||
/* the StringBuffer may reallocate the JSString, only link it at the end */
|
||||
list_del(&s->str->link);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)
|
||||
{
|
||||
return string_buffer_init2(ctx, s, size, 0);
|
||||
}
|
||||
|
||||
void string_buffer_free(StringBuffer *s)
|
||||
{
|
||||
js_free(s->ctx, s->str);
|
||||
s->str = NULL;
|
||||
}
|
||||
|
||||
int string_buffer_set_error(StringBuffer *s)
|
||||
{
|
||||
js_free(s->ctx, s->str);
|
||||
s->str = NULL;
|
||||
s->size = 0;
|
||||
s->len = 0;
|
||||
return s->error_status = -1;
|
||||
}
|
||||
|
||||
int string_buffer_widen(StringBuffer *s, int size)
|
||||
{
|
||||
JSString *str;
|
||||
size_t slack;
|
||||
int i;
|
||||
if (s->error_status)
|
||||
return -1;
|
||||
str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);
|
||||
if (!str)
|
||||
return string_buffer_set_error(s);
|
||||
size += slack >> 1;
|
||||
for(i = s->len; i-- > 0;) {
|
||||
str->u.str16[i] = str->u.str8[i];
|
||||
}
|
||||
s->is_wide_char = 1;
|
||||
s->size = size;
|
||||
s->str = str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int string_buffer_realloc(StringBuffer *s, int new_len, int c)
|
||||
{
|
||||
JSString *new_str;
|
||||
int new_size;
|
||||
size_t new_size_bytes, slack;
|
||||
if (s->error_status)
|
||||
return -1;
|
||||
if (new_len > JS_STRING_LEN_MAX) {
|
||||
JS_ThrowInternalError(s->ctx, "string too long");
|
||||
return string_buffer_set_error(s);
|
||||
}
|
||||
new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);
|
||||
if (!s->is_wide_char && c >= 0x100) {
|
||||
return string_buffer_widen(s, new_size);
|
||||
}
|
||||
new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;
|
||||
new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);
|
||||
if (!new_str)
|
||||
return string_buffer_set_error(s);
|
||||
new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);
|
||||
s->size = new_size;
|
||||
s->str = new_str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int string_buffer_putc_slow(StringBuffer *s, uint32_t c)
|
||||
{
|
||||
if (UNLIKELY(s->len >= s->size)) {
|
||||
if (string_buffer_realloc(s, s->len + 1, c))
|
||||
return -1;
|
||||
}
|
||||
if (s->is_wide_char) {
|
||||
s->str->u.str16[s->len++] = c;
|
||||
} else if (c < 0x100) {
|
||||
s->str->u.str8[s->len++] = c;
|
||||
} else {
|
||||
if (string_buffer_widen(s, s->size))
|
||||
return -1;
|
||||
s->str->u.str16[s->len++] = c;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 0 <= c <= 0xff */
|
||||
int string_buffer_putc8(StringBuffer *s, uint32_t c)
|
||||
{
|
||||
if (UNLIKELY(s->len >= s->size)) {
|
||||
if (string_buffer_realloc(s, s->len + 1, c))
|
||||
return -1;
|
||||
}
|
||||
if (s->is_wide_char) {
|
||||
s->str->u.str16[s->len++] = c;
|
||||
} else {
|
||||
s->str->u.str8[s->len++] = c;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 0 <= c <= 0xffff */
|
||||
int string_buffer_putc16(StringBuffer *s, uint32_t c)
|
||||
{
|
||||
if (LIKELY(s->len < s->size)) {
|
||||
if (s->is_wide_char) {
|
||||
s->str->u.str16[s->len++] = c;
|
||||
return 0;
|
||||
} else if (c < 0x100) {
|
||||
s->str->u.str8[s->len++] = c;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return string_buffer_putc_slow(s, c);
|
||||
}
|
||||
|
||||
/* 0 <= c <= 0x10ffff */
|
||||
int string_buffer_putc(StringBuffer *s, uint32_t c)
|
||||
{
|
||||
if (UNLIKELY(c >= 0x10000)) {
|
||||
/* surrogate pair */
|
||||
c -= 0x10000;
|
||||
if (string_buffer_putc16(s, (c >> 10) + 0xd800))
|
||||
return -1;
|
||||
c = (c & 0x3ff) + 0xdc00;
|
||||
}
|
||||
return string_buffer_putc16(s, c);
|
||||
}
|
||||
|
||||
int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)
|
||||
{
|
||||
int i;
|
||||
if (s->len + len > s->size) {
|
||||
if (string_buffer_realloc(s, s->len + len, 0))
|
||||
return -1;
|
||||
}
|
||||
if (s->is_wide_char) {
|
||||
for (i = 0; i < len; i++) {
|
||||
s->str->u.str16[s->len + i] = p[i];
|
||||
}
|
||||
s->len += len;
|
||||
} else {
|
||||
memcpy(&s->str->u.str8[s->len], p, len);
|
||||
s->len += len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)
|
||||
{
|
||||
int c = 0, i;
|
||||
for (i = 0; i < len; i++) {
|
||||
c |= p[i];
|
||||
}
|
||||
if (s->len + len > s->size) {
|
||||
if (string_buffer_realloc(s, s->len + len, c))
|
||||
return -1;
|
||||
} else if (!s->is_wide_char && c >= 0x100) {
|
||||
if (string_buffer_widen(s, s->size))
|
||||
return -1;
|
||||
}
|
||||
if (s->is_wide_char) {
|
||||
memcpy(&s->str->u.str16[s->len], p, len << 1);
|
||||
s->len += len;
|
||||
} else {
|
||||
for (i = 0; i < len; i++) {
|
||||
s->str->u.str8[s->len + i] = p[i];
|
||||
}
|
||||
s->len += len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* appending an ASCII string */
|
||||
int string_buffer_puts8(StringBuffer *s, const char *str)
|
||||
{
|
||||
return string_buffer_write8(s, (const uint8_t *)str, strlen(str));
|
||||
}
|
||||
|
||||
int string_buffer_concat(StringBuffer *s, const JSString *p, uint32_t from, uint32_t to)
|
||||
{
|
||||
if (to <= from)
|
||||
return 0;
|
||||
if (p->is_wide_char)
|
||||
return string_buffer_write16(s, p->u.str16 + from, to - from);
|
||||
else
|
||||
return string_buffer_write8(s, p->u.str8 + from, to - from);
|
||||
}
|
||||
|
||||
int string_buffer_concat_value(StringBuffer *s, JSValueConst v)
|
||||
{
|
||||
JSString *p;
|
||||
JSValue v1;
|
||||
int res;
|
||||
if (s->error_status) {
|
||||
/* prevent exception overload */
|
||||
return -1;
|
||||
}
|
||||
if (UNLIKELY(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
|
||||
v1 = JS_ToString(s->ctx, v);
|
||||
if (JS_IsException(v1))
|
||||
return string_buffer_set_error(s);
|
||||
p = JS_VALUE_GET_STRING(v1);
|
||||
res = string_buffer_concat(s, p, 0, p->len);
|
||||
JS_FreeValue(s->ctx, v1);
|
||||
return res;
|
||||
}
|
||||
p = JS_VALUE_GET_STRING(v);
|
||||
return string_buffer_concat(s, p, 0, p->len);
|
||||
}
|
||||
|
||||
int string_buffer_concat_value_free(StringBuffer *s, JSValue v)
|
||||
{
|
||||
JSString *p;
|
||||
int res;
|
||||
if (s->error_status) {
|
||||
/* prevent exception overload */
|
||||
JS_FreeValue(s->ctx, v);
|
||||
return -1;
|
||||
}
|
||||
if (UNLIKELY(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
|
||||
v = JS_ToStringFree(s->ctx, v);
|
||||
if (JS_IsException(v))
|
||||
return string_buffer_set_error(s);
|
||||
}
|
||||
p = JS_VALUE_GET_STRING(v);
|
||||
res = string_buffer_concat(s, p, 0, p->len);
|
||||
JS_FreeValue(s->ctx, v);
|
||||
return res;
|
||||
}
|
||||
|
||||
int string_buffer_fill(StringBuffer *s, int c, int count)
|
||||
{
|
||||
/* XXX: optimize */
|
||||
if (s->len + count > s->size) {
|
||||
if (string_buffer_realloc(s, s->len + count, c))
|
||||
return -1;
|
||||
}
|
||||
while (count-- > 0) {
|
||||
if (string_buffer_putc16(s, c))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSValue string_buffer_end(StringBuffer *s)
|
||||
{
|
||||
JSString *str;
|
||||
str = s->str;
|
||||
if (s->error_status)
|
||||
return JS_EXCEPTION;
|
||||
if (s->len == 0) {
|
||||
js_free(s->ctx, str);
|
||||
s->str = NULL;
|
||||
return JS_AtomToString(s->ctx, JS_ATOM_empty_string);
|
||||
}
|
||||
if (s->len < s->size) {
|
||||
/* smaller size so js_realloc should not fail, but OK if it does */
|
||||
/* XXX: should add some slack to avoid unnecessary calls */
|
||||
/* XXX: might need to use malloc+free to ensure smaller size */
|
||||
str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) +
|
||||
(s->len << s->is_wide_char) + 1 - s->is_wide_char);
|
||||
if (str == NULL)
|
||||
str = s->str;
|
||||
s->str = str;
|
||||
}
|
||||
if (!s->is_wide_char)
|
||||
str->u.str8[s->len] = 0;
|
||||
#ifdef DUMP_LEAKS
|
||||
list_add_tail(&str->link, &s->ctx->rt->string_list);
|
||||
#endif
|
||||
str->is_wide_char = s->is_wide_char;
|
||||
str->len = s->len;
|
||||
s->str = NULL;
|
||||
return JS_MKPTR(JS_TAG_STRING, str);
|
||||
}
|
695
third_party/quickjs/tok.c
vendored
Normal file
695
third_party/quickjs/tok.c
vendored
Normal file
|
@ -0,0 +1,695 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "third_party/quickjs/internal.h"
|
||||
#include "third_party/quickjs/libregexp.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
/* 'c' is the first character. Return JS_ATOM_NULL in case of error */
|
||||
static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,
|
||||
BOOL *pident_has_escape, int c, BOOL is_private)
|
||||
{
|
||||
const uint8_t *p, *p1;
|
||||
char ident_buf[128], *buf;
|
||||
size_t ident_size, ident_pos;
|
||||
JSAtom atom;
|
||||
p = *pp;
|
||||
buf = ident_buf;
|
||||
ident_size = sizeof(ident_buf);
|
||||
ident_pos = 0;
|
||||
if (is_private)
|
||||
buf[ident_pos++] = '#';
|
||||
for(;;) {
|
||||
p1 = p;
|
||||
if (c < 128) {
|
||||
buf[ident_pos++] = c;
|
||||
} else {
|
||||
ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c);
|
||||
}
|
||||
c = *p1++;
|
||||
if (c == '\\' && *p1 == 'u') {
|
||||
c = lre_parse_escape(&p1, TRUE);
|
||||
*pident_has_escape = TRUE;
|
||||
} else if (c >= 128) {
|
||||
c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
|
||||
}
|
||||
if (!lre_js_is_ident_next(c))
|
||||
break;
|
||||
p = p1;
|
||||
if (UNLIKELY(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
|
||||
if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
|
||||
atom = JS_ATOM_NULL;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
|
||||
done:
|
||||
if (UNLIKELY(buf != ident_buf))
|
||||
js_free(s->ctx, buf);
|
||||
*pp = p;
|
||||
return atom;
|
||||
}
|
||||
|
||||
void free_token(JSParseState *s, JSToken *token)
|
||||
{
|
||||
switch(token->val) {
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case TOK_NUMBER:
|
||||
JS_FreeValue(s->ctx, token->u.num.val);
|
||||
break;
|
||||
#endif
|
||||
case TOK_STRING:
|
||||
case TOK_TEMPLATE:
|
||||
JS_FreeValue(s->ctx, token->u.str.str);
|
||||
break;
|
||||
case TOK_REGEXP:
|
||||
JS_FreeValue(s->ctx, token->u.regexp.body);
|
||||
JS_FreeValue(s->ctx, token->u.regexp.flags);
|
||||
break;
|
||||
case TOK_IDENT:
|
||||
case TOK_PRIVATE_NAME:
|
||||
JS_FreeAtom(s->ctx, token->u.ident.atom);
|
||||
break;
|
||||
default:
|
||||
if (token->val >= TOK_FIRST_KEYWORD &&
|
||||
token->val <= TOK_LAST_KEYWORD) {
|
||||
JS_FreeAtom(s->ctx, token->u.ident.atom);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int next_token(JSParseState *s)
|
||||
{
|
||||
const uint8_t *p;
|
||||
int c;
|
||||
BOOL ident_has_escape;
|
||||
JSAtom atom;
|
||||
if (js_check_stack_overflow(s->ctx->rt, 0)) {
|
||||
return js_parse_error(s, "stack overflow");
|
||||
}
|
||||
free_token(s, &s->token);
|
||||
p = s->last_ptr = s->buf_ptr;
|
||||
s->got_lf = FALSE;
|
||||
s->last_line_num = s->token.line_num;
|
||||
redo:
|
||||
s->token.line_num = s->line_num;
|
||||
s->token.ptr = p;
|
||||
c = *p;
|
||||
switch(c) {
|
||||
case 0:
|
||||
if (p >= s->buf_end) {
|
||||
s->token.val = TOK_EOF;
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
case '`':
|
||||
if (js_parse_template_part(s, p + 1))
|
||||
goto fail;
|
||||
p = s->buf_ptr;
|
||||
break;
|
||||
case '\'':
|
||||
case '\"':
|
||||
if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
|
||||
goto fail;
|
||||
break;
|
||||
case '\r': /* accept DOS and MAC newline sequences */
|
||||
if (p[1] == '\n') {
|
||||
p++;
|
||||
}
|
||||
/* fall thru */
|
||||
case '\n':
|
||||
p++;
|
||||
line_terminator:
|
||||
s->got_lf = TRUE;
|
||||
s->line_num++;
|
||||
goto redo;
|
||||
case '\f':
|
||||
case '\v':
|
||||
case ' ':
|
||||
case '\t':
|
||||
p++;
|
||||
goto redo;
|
||||
case '/':
|
||||
if (p[1] == '*') {
|
||||
/* comment */
|
||||
p += 2;
|
||||
for(;;) {
|
||||
if (*p == '\0' && p >= s->buf_end) {
|
||||
js_parse_error(s, "unexpected end of comment");
|
||||
goto fail;
|
||||
}
|
||||
if (p[0] == '*' && p[1] == '/') {
|
||||
p += 2;
|
||||
break;
|
||||
}
|
||||
if (*p == '\n') {
|
||||
s->line_num++;
|
||||
s->got_lf = TRUE; /* considered as LF for ASI */
|
||||
p++;
|
||||
} else if (*p == '\r') {
|
||||
s->got_lf = TRUE; /* considered as LF for ASI */
|
||||
p++;
|
||||
} else if (*p >= 0x80) {
|
||||
c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
|
||||
if (c == CP_LS || c == CP_PS) {
|
||||
s->got_lf = TRUE; /* considered as LF for ASI */
|
||||
} else if (c == -1) {
|
||||
p++; /* skip invalid UTF-8 */
|
||||
}
|
||||
} else {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
goto redo;
|
||||
} else if (p[1] == '/') {
|
||||
/* line comment */
|
||||
p += 2;
|
||||
skip_line_comment:
|
||||
for(;;) {
|
||||
if (*p == '\0' && p >= s->buf_end)
|
||||
break;
|
||||
if (*p == '\r' || *p == '\n')
|
||||
break;
|
||||
if (*p >= 0x80) {
|
||||
c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
|
||||
/* LS or PS are considered as line terminator */
|
||||
if (c == CP_LS || c == CP_PS) {
|
||||
break;
|
||||
} else if (c == -1) {
|
||||
p++; /* skip invalid UTF-8 */
|
||||
}
|
||||
} else {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
goto redo;
|
||||
} else if (p[1] == '=') {
|
||||
p += 2;
|
||||
s->token.val = TOK_DIV_ASSIGN;
|
||||
} else {
|
||||
p++;
|
||||
s->token.val = c;
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
if (p[1] == 'u') {
|
||||
const uint8_t *p1 = p + 1;
|
||||
int c1 = lre_parse_escape(&p1, TRUE);
|
||||
if (c1 >= 0 && lre_js_is_ident_first(c1)) {
|
||||
c = c1;
|
||||
p = p1;
|
||||
ident_has_escape = TRUE;
|
||||
goto has_ident;
|
||||
} else {
|
||||
/* XXX: syntax error? */
|
||||
}
|
||||
}
|
||||
goto def_token;
|
||||
case 'a': case 'b': case 'c': case 'd':
|
||||
case 'e': case 'f': case 'g': case 'h':
|
||||
case 'i': case 'j': case 'k': case 'l':
|
||||
case 'm': case 'n': case 'o': case 'p':
|
||||
case 'q': case 'r': case 's': case 't':
|
||||
case 'u': case 'v': case 'w': case 'x':
|
||||
case 'y': case 'z':
|
||||
case 'A': case 'B': case 'C': case 'D':
|
||||
case 'E': case 'F': case 'G': case 'H':
|
||||
case 'I': case 'J': case 'K': case 'L':
|
||||
case 'M': case 'N': case 'O': case 'P':
|
||||
case 'Q': case 'R': case 'S': case 'T':
|
||||
case 'U': case 'V': case 'W': case 'X':
|
||||
case 'Y': case 'Z':
|
||||
case '_':
|
||||
case '$':
|
||||
/* identifier */
|
||||
p++;
|
||||
ident_has_escape = FALSE;
|
||||
has_ident:
|
||||
atom = parse_ident(s, &p, &ident_has_escape, c, FALSE);
|
||||
if (atom == JS_ATOM_NULL)
|
||||
goto fail;
|
||||
s->token.u.ident.atom = atom;
|
||||
s->token.u.ident.has_escape = ident_has_escape;
|
||||
s->token.u.ident.is_reserved = FALSE;
|
||||
if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
|
||||
(s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
|
||||
(s->cur_func->js_mode & JS_MODE_STRICT)) ||
|
||||
(s->token.u.ident.atom == JS_ATOM_yield &&
|
||||
((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||
|
||||
(s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
|
||||
!s->cur_func->in_function_body && s->cur_func->parent &&
|
||||
(s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||
|
||||
(s->token.u.ident.atom == JS_ATOM_await &&
|
||||
(s->is_module ||
|
||||
(((s->cur_func->func_kind & JS_FUNC_ASYNC) ||
|
||||
(s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
|
||||
!s->cur_func->in_function_body && s->cur_func->parent &&
|
||||
(s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) {
|
||||
if (ident_has_escape) {
|
||||
s->token.u.ident.is_reserved = TRUE;
|
||||
s->token.val = TOK_IDENT;
|
||||
} else {
|
||||
/* The keywords atoms are pre allocated */
|
||||
s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;
|
||||
}
|
||||
} else {
|
||||
s->token.val = TOK_IDENT;
|
||||
}
|
||||
break;
|
||||
case '#':
|
||||
/* private name */
|
||||
{
|
||||
const uint8_t *p1;
|
||||
p++;
|
||||
p1 = p;
|
||||
c = *p1++;
|
||||
if (c == '\\' && *p1 == 'u') {
|
||||
c = lre_parse_escape(&p1, TRUE);
|
||||
} else if (c >= 128) {
|
||||
c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
|
||||
}
|
||||
if (!lre_js_is_ident_first(c)) {
|
||||
js_parse_error(s, "invalid first character of private name");
|
||||
goto fail;
|
||||
}
|
||||
p = p1;
|
||||
ident_has_escape = FALSE; /* not used */
|
||||
atom = parse_ident(s, &p, &ident_has_escape, c, TRUE);
|
||||
if (atom == JS_ATOM_NULL)
|
||||
goto fail;
|
||||
s->token.u.ident.atom = atom;
|
||||
s->token.val = TOK_PRIVATE_NAME;
|
||||
}
|
||||
break;
|
||||
case '.':
|
||||
if (p[1] == '.' && p[2] == '.') {
|
||||
p += 3;
|
||||
s->token.val = TOK_ELLIPSIS;
|
||||
break;
|
||||
}
|
||||
if (p[1] >= '0' && p[1] <= '9') {
|
||||
goto parse_number;
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
case '0':
|
||||
/* in strict mode, octal literals are not accepted */
|
||||
if (isdigit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) {
|
||||
js_parse_error(s, "octal literals are deprecated in strict mode");
|
||||
goto fail;
|
||||
}
|
||||
goto parse_number;
|
||||
case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8':
|
||||
case '9':
|
||||
/* number */
|
||||
parse_number:
|
||||
{
|
||||
JSValue ret;
|
||||
const uint8_t *p1;
|
||||
int flags, radix;
|
||||
flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |
|
||||
ATOD_ACCEPT_UNDERSCORES;
|
||||
#ifdef CONFIG_BIGNUM
|
||||
flags |= ATOD_ACCEPT_SUFFIX;
|
||||
if (s->cur_func->js_mode & JS_MODE_MATH) {
|
||||
flags |= ATOD_MODE_BIGINT;
|
||||
if (s->cur_func->js_mode & JS_MODE_MATH)
|
||||
flags |= ATOD_TYPE_BIG_FLOAT;
|
||||
}
|
||||
#endif
|
||||
radix = 0;
|
||||
#ifdef CONFIG_BIGNUM
|
||||
s->token.u.num.exponent = 0;
|
||||
ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix,
|
||||
flags, &s->token.u.num.exponent);
|
||||
#else
|
||||
ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
|
||||
flags);
|
||||
#endif
|
||||
if (JS_IsException(ret))
|
||||
goto fail;
|
||||
/* reject `10instanceof Number` */
|
||||
if (JS_VALUE_IS_NAN(ret) ||
|
||||
lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) {
|
||||
JS_FreeValue(s->ctx, ret);
|
||||
js_parse_error(s, "invalid number literal");
|
||||
goto fail;
|
||||
}
|
||||
s->token.val = TOK_NUMBER;
|
||||
s->token.u.num.val = ret;
|
||||
}
|
||||
break;
|
||||
case '*':
|
||||
if (p[1] == '=') {
|
||||
p += 2;
|
||||
s->token.val = TOK_MUL_ASSIGN;
|
||||
} else if (p[1] == '*') {
|
||||
if (p[2] == '=') {
|
||||
p += 3;
|
||||
s->token.val = TOK_POW_ASSIGN;
|
||||
} else {
|
||||
p += 2;
|
||||
s->token.val = TOK_POW;
|
||||
}
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
case '%':
|
||||
if (p[1] == '=') {
|
||||
p += 2;
|
||||
s->token.val = TOK_MOD_ASSIGN;
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
case '+':
|
||||
if (p[1] == '=') {
|
||||
p += 2;
|
||||
s->token.val = TOK_PLUS_ASSIGN;
|
||||
} else if (p[1] == '+') {
|
||||
p += 2;
|
||||
s->token.val = TOK_INC;
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
case '-':
|
||||
if (p[1] == '=') {
|
||||
p += 2;
|
||||
s->token.val = TOK_MINUS_ASSIGN;
|
||||
} else if (p[1] == '-') {
|
||||
if (s->allow_html_comments &&
|
||||
p[2] == '>' && s->last_line_num != s->line_num) {
|
||||
/* Annex B: `-->` at beginning of line is an html comment end.
|
||||
It extends to the end of the line.
|
||||
*/
|
||||
goto skip_line_comment;
|
||||
}
|
||||
p += 2;
|
||||
s->token.val = TOK_DEC;
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
case '<':
|
||||
if (p[1] == '=') {
|
||||
p += 2;
|
||||
s->token.val = TOK_LTE;
|
||||
} else if (p[1] == '<') {
|
||||
if (p[2] == '=') {
|
||||
p += 3;
|
||||
s->token.val = TOK_SHL_ASSIGN;
|
||||
} else {
|
||||
p += 2;
|
||||
s->token.val = TOK_SHL;
|
||||
}
|
||||
} else if (s->allow_html_comments &&
|
||||
p[1] == '!' && p[2] == '-' && p[3] == '-') {
|
||||
/* Annex B: handle `<!--` single line html comments */
|
||||
goto skip_line_comment;
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
case '>':
|
||||
if (p[1] == '=') {
|
||||
p += 2;
|
||||
s->token.val = TOK_GTE;
|
||||
} else if (p[1] == '>') {
|
||||
if (p[2] == '>') {
|
||||
if (p[3] == '=') {
|
||||
p += 4;
|
||||
s->token.val = TOK_SHR_ASSIGN;
|
||||
} else {
|
||||
p += 3;
|
||||
s->token.val = TOK_SHR;
|
||||
}
|
||||
} else if (p[2] == '=') {
|
||||
p += 3;
|
||||
s->token.val = TOK_SAR_ASSIGN;
|
||||
} else {
|
||||
p += 2;
|
||||
s->token.val = TOK_SAR;
|
||||
}
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
case '=':
|
||||
if (p[1] == '=') {
|
||||
if (p[2] == '=') {
|
||||
p += 3;
|
||||
s->token.val = TOK_STRICT_EQ;
|
||||
} else {
|
||||
p += 2;
|
||||
s->token.val = TOK_EQ;
|
||||
}
|
||||
} else if (p[1] == '>') {
|
||||
p += 2;
|
||||
s->token.val = TOK_ARROW;
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
case '!':
|
||||
if (p[1] == '=') {
|
||||
if (p[2] == '=') {
|
||||
p += 3;
|
||||
s->token.val = TOK_STRICT_NEQ;
|
||||
} else {
|
||||
p += 2;
|
||||
s->token.val = TOK_NEQ;
|
||||
}
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
case '&':
|
||||
if (p[1] == '=') {
|
||||
p += 2;
|
||||
s->token.val = TOK_AND_ASSIGN;
|
||||
} else if (p[1] == '&') {
|
||||
if (p[2] == '=') {
|
||||
p += 3;
|
||||
s->token.val = TOK_LAND_ASSIGN;
|
||||
} else {
|
||||
p += 2;
|
||||
s->token.val = TOK_LAND;
|
||||
}
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_BIGNUM
|
||||
/* in math mode, '^' is the power operator. '^^' is always the
|
||||
xor operator and '**' is always the power operator */
|
||||
case '^':
|
||||
if (p[1] == '=') {
|
||||
p += 2;
|
||||
if (s->cur_func->js_mode & JS_MODE_MATH)
|
||||
s->token.val = TOK_MATH_POW_ASSIGN;
|
||||
else
|
||||
s->token.val = TOK_XOR_ASSIGN;
|
||||
} else if (p[1] == '^') {
|
||||
if (p[2] == '=') {
|
||||
p += 3;
|
||||
s->token.val = TOK_XOR_ASSIGN;
|
||||
} else {
|
||||
p += 2;
|
||||
s->token.val = '^';
|
||||
}
|
||||
} else {
|
||||
p++;
|
||||
if (s->cur_func->js_mode & JS_MODE_MATH)
|
||||
s->token.val = TOK_MATH_POW;
|
||||
else
|
||||
s->token.val = '^';
|
||||
}
|
||||
break;
|
||||
#else
|
||||
case '^':
|
||||
if (p[1] == '=') {
|
||||
p += 2;
|
||||
s->token.val = TOK_XOR_ASSIGN;
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case '|':
|
||||
if (p[1] == '=') {
|
||||
p += 2;
|
||||
s->token.val = TOK_OR_ASSIGN;
|
||||
} else if (p[1] == '|') {
|
||||
if (p[2] == '=') {
|
||||
p += 3;
|
||||
s->token.val = TOK_LOR_ASSIGN;
|
||||
} else {
|
||||
p += 2;
|
||||
s->token.val = TOK_LOR;
|
||||
}
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
if (p[1] == '?') {
|
||||
if (p[2] == '=') {
|
||||
p += 3;
|
||||
s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN;
|
||||
} else {
|
||||
p += 2;
|
||||
s->token.val = TOK_DOUBLE_QUESTION_MARK;
|
||||
}
|
||||
} else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) {
|
||||
p += 2;
|
||||
s->token.val = TOK_QUESTION_MARK_DOT;
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (c >= 128) {
|
||||
/* unicode value */
|
||||
c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
|
||||
switch(c) {
|
||||
case CP_PS:
|
||||
case CP_LS:
|
||||
/* XXX: should avoid incrementing line_number, but
|
||||
needed to handle HTML comments */
|
||||
goto line_terminator;
|
||||
default:
|
||||
if (lre_is_space(c)) {
|
||||
goto redo;
|
||||
} else if (lre_js_is_ident_first(c)) {
|
||||
ident_has_escape = FALSE;
|
||||
goto has_ident;
|
||||
} else {
|
||||
js_parse_error(s, "unexpected character");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
def_token:
|
||||
s->token.val = c;
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
s->buf_ptr = p;
|
||||
// dump_token(s, &s->token);
|
||||
return 0;
|
||||
fail:
|
||||
s->token.val = TOK_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is
|
||||
only set if TOK_IMPORT is returned */
|
||||
/* XXX: handle all unicode cases */
|
||||
int simple_next_token(const uint8_t **pp, BOOL no_line_terminator)
|
||||
{
|
||||
const uint8_t *p;
|
||||
uint32_t c;
|
||||
/* skip spaces and comments */
|
||||
p = *pp;
|
||||
for (;;) {
|
||||
switch(c = *p++) {
|
||||
case '\r':
|
||||
case '\n':
|
||||
if (no_line_terminator)
|
||||
return '\n';
|
||||
continue;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\f':
|
||||
continue;
|
||||
case '/':
|
||||
if (*p == '/') {
|
||||
if (no_line_terminator)
|
||||
return '\n';
|
||||
while (*p && *p != '\r' && *p != '\n')
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
if (*p == '*') {
|
||||
while (*++p) {
|
||||
if ((*p == '\r' || *p == '\n') && no_line_terminator)
|
||||
return '\n';
|
||||
if (*p == '*' && p[1] == '/') {
|
||||
p += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case '=':
|
||||
if (*p == '>')
|
||||
return TOK_ARROW;
|
||||
break;
|
||||
default:
|
||||
if (lre_js_is_ident_first(c)) {
|
||||
if (c == 'i') {
|
||||
if (p[0] == 'n' && !lre_js_is_ident_next(p[1])) {
|
||||
return TOK_IN;
|
||||
}
|
||||
if (p[0] == 'm' && p[1] == 'p' && p[2] == 'o' &&
|
||||
p[3] == 'r' && p[4] == 't' &&
|
||||
!lre_js_is_ident_next(p[5])) {
|
||||
*pp = p + 5;
|
||||
return TOK_IMPORT;
|
||||
}
|
||||
} else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) {
|
||||
return TOK_OF;
|
||||
} else if (c == 'e' &&
|
||||
p[0] == 'x' && p[1] == 'p' && p[2] == 'o' &&
|
||||
p[3] == 'r' && p[4] == 't' &&
|
||||
!lre_js_is_ident_next(p[5])) {
|
||||
*pp = p + 5;
|
||||
return TOK_EXPORT;
|
||||
} else if (c == 'f' && p[0] == 'u' && p[1] == 'n' &&
|
||||
p[2] == 'c' && p[3] == 't' && p[4] == 'i' &&
|
||||
p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) {
|
||||
return TOK_FUNCTION;
|
||||
}
|
||||
return TOK_IDENT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
2380
third_party/quickjs/typedarray.c
vendored
Normal file
2380
third_party/quickjs/typedarray.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
279
third_party/quickjs/uri.c
vendored
Normal file
279
third_party/quickjs/uri.c
vendored
Normal file
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
static int string_get_hex(JSString *p, int k, int n) {
|
||||
int c = 0, h;
|
||||
while (n-- > 0) {
|
||||
if ((h = from_hex(string_get(p, k++))) < 0)
|
||||
return -1;
|
||||
c = (c << 4) | h;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static int isURIReserved(int c) {
|
||||
return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;
|
||||
}
|
||||
|
||||
static int hex_decode(JSContext *ctx, JSString *p, int k) {
|
||||
int c;
|
||||
if (k >= p->len || string_get(p, k) != '%')
|
||||
return js_throw_URIError(ctx, "expecting %%");
|
||||
if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0)
|
||||
return js_throw_URIError(ctx, "expecting hex digit");
|
||||
return c;
|
||||
}
|
||||
|
||||
JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int isComponent)
|
||||
{
|
||||
JSValue str;
|
||||
StringBuffer b_s, *b = &b_s;
|
||||
JSString *p;
|
||||
int k, c, c1, n, c_min;
|
||||
str = JS_ToString(ctx, argv[0]);
|
||||
if (JS_IsException(str))
|
||||
return str;
|
||||
string_buffer_init(ctx, b, 0);
|
||||
p = JS_VALUE_GET_STRING(str);
|
||||
for (k = 0; k < p->len;) {
|
||||
c = string_get(p, k);
|
||||
if (c == '%') {
|
||||
c = hex_decode(ctx, p, k);
|
||||
if (c < 0)
|
||||
goto fail;
|
||||
k += 3;
|
||||
if (c < 0x80) {
|
||||
if (!isComponent && isURIReserved(c)) {
|
||||
c = '%';
|
||||
k -= 2;
|
||||
}
|
||||
} else {
|
||||
/* Decode URI-encoded UTF-8 sequence */
|
||||
if (c >= 0xc0 && c <= 0xdf) {
|
||||
n = 1;
|
||||
c_min = 0x80;
|
||||
c &= 0x1f;
|
||||
} else if (c >= 0xe0 && c <= 0xef) {
|
||||
n = 2;
|
||||
c_min = 0x800;
|
||||
c &= 0xf;
|
||||
} else if (c >= 0xf0 && c <= 0xf7) {
|
||||
n = 3;
|
||||
c_min = 0x10000;
|
||||
c &= 0x7;
|
||||
} else {
|
||||
n = 0;
|
||||
c_min = 1;
|
||||
c = 0;
|
||||
}
|
||||
while (n-- > 0) {
|
||||
c1 = hex_decode(ctx, p, k);
|
||||
if (c1 < 0)
|
||||
goto fail;
|
||||
k += 3;
|
||||
if ((c1 & 0xc0) != 0x80) {
|
||||
c = 0;
|
||||
break;
|
||||
}
|
||||
c = (c << 6) | (c1 & 0x3f);
|
||||
}
|
||||
if (c < c_min || c > 0x10FFFF ||
|
||||
(c >= 0xd800 && c < 0xe000)) {
|
||||
js_throw_URIError(ctx, "malformed UTF-8");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
k++;
|
||||
}
|
||||
string_buffer_putc(b, c);
|
||||
}
|
||||
JS_FreeValue(ctx, str);
|
||||
return string_buffer_end(b);
|
||||
fail:
|
||||
JS_FreeValue(ctx, str);
|
||||
string_buffer_free(b);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
static int isUnescaped(int c) {
|
||||
static char const unescaped_chars[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789"
|
||||
"@*_+-./";
|
||||
return c < 0x100 &&
|
||||
memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1);
|
||||
}
|
||||
|
||||
static int isURIUnescaped(int c, int isComponent) {
|
||||
return c < 0x100 &&
|
||||
((c >= 0x61 && c <= 0x7a) ||
|
||||
(c >= 0x41 && c <= 0x5a) ||
|
||||
(c >= 0x30 && c <= 0x39) ||
|
||||
memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL ||
|
||||
(!isComponent && isURIReserved(c)));
|
||||
}
|
||||
|
||||
static int encodeURI_hex(StringBuffer *b, int c) {
|
||||
uint8_t buf[6];
|
||||
int n = 0;
|
||||
const char *hex = "0123456789ABCDEF";
|
||||
buf[n++] = '%';
|
||||
if (c >= 256) {
|
||||
buf[n++] = 'u';
|
||||
buf[n++] = hex[(c >> 12) & 15];
|
||||
buf[n++] = hex[(c >> 8) & 15];
|
||||
}
|
||||
buf[n++] = hex[(c >> 4) & 15];
|
||||
buf[n++] = hex[(c >> 0) & 15];
|
||||
return string_buffer_write8(b, buf, n);
|
||||
}
|
||||
|
||||
JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv,
|
||||
int isComponent)
|
||||
{
|
||||
JSValue str;
|
||||
StringBuffer b_s, *b = &b_s;
|
||||
JSString *p;
|
||||
int k, c, c1;
|
||||
str = JS_ToString(ctx, argv[0]);
|
||||
if (JS_IsException(str))
|
||||
return str;
|
||||
p = JS_VALUE_GET_STRING(str);
|
||||
string_buffer_init(ctx, b, p->len);
|
||||
for (k = 0; k < p->len;) {
|
||||
c = string_get(p, k);
|
||||
k++;
|
||||
if (isURIUnescaped(c, isComponent)) {
|
||||
string_buffer_putc16(b, c);
|
||||
} else {
|
||||
if (c >= 0xdc00 && c <= 0xdfff) {
|
||||
js_throw_URIError(ctx, "invalid character");
|
||||
goto fail;
|
||||
} else if (c >= 0xd800 && c <= 0xdbff) {
|
||||
if (k >= p->len) {
|
||||
js_throw_URIError(ctx, "expecting surrogate pair");
|
||||
goto fail;
|
||||
}
|
||||
c1 = string_get(p, k);
|
||||
k++;
|
||||
if (c1 < 0xdc00 || c1 > 0xdfff) {
|
||||
js_throw_URIError(ctx, "expecting surrogate pair");
|
||||
goto fail;
|
||||
}
|
||||
c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
|
||||
}
|
||||
if (c < 0x80) {
|
||||
encodeURI_hex(b, c);
|
||||
} else {
|
||||
/* XXX: use C UTF-8 conversion ? */
|
||||
if (c < 0x800) {
|
||||
encodeURI_hex(b, (c >> 6) | 0xc0);
|
||||
} else {
|
||||
if (c < 0x10000) {
|
||||
encodeURI_hex(b, (c >> 12) | 0xe0);
|
||||
} else {
|
||||
encodeURI_hex(b, (c >> 18) | 0xf0);
|
||||
encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80);
|
||||
}
|
||||
encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80);
|
||||
}
|
||||
encodeURI_hex(b, (c & 0x3f) | 0x80);
|
||||
}
|
||||
}
|
||||
}
|
||||
JS_FreeValue(ctx, str);
|
||||
return string_buffer_end(b);
|
||||
fail:
|
||||
JS_FreeValue(ctx, str);
|
||||
string_buffer_free(b);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
JSValue js_global_escape(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValue str;
|
||||
StringBuffer b_s, *b = &b_s;
|
||||
JSString *p;
|
||||
int i, len, c;
|
||||
str = JS_ToString(ctx, argv[0]);
|
||||
if (JS_IsException(str))
|
||||
return str;
|
||||
p = JS_VALUE_GET_STRING(str);
|
||||
string_buffer_init(ctx, b, p->len);
|
||||
for (i = 0, len = p->len; i < len; i++) {
|
||||
c = string_get(p, i);
|
||||
if (isUnescaped(c)) {
|
||||
string_buffer_putc16(b, c);
|
||||
} else {
|
||||
encodeURI_hex(b, c);
|
||||
}
|
||||
}
|
||||
JS_FreeValue(ctx, str);
|
||||
return string_buffer_end(b);
|
||||
}
|
||||
|
||||
JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
JSValue str;
|
||||
StringBuffer b_s, *b = &b_s;
|
||||
JSString *p;
|
||||
int i, len, c, n;
|
||||
str = JS_ToString(ctx, argv[0]);
|
||||
if (JS_IsException(str))
|
||||
return str;
|
||||
string_buffer_init(ctx, b, 0);
|
||||
p = JS_VALUE_GET_STRING(str);
|
||||
for (i = 0, len = p->len; i < len; i++) {
|
||||
c = string_get(p, i);
|
||||
if (c == '%') {
|
||||
if (i + 6 <= len
|
||||
&& string_get(p, i + 1) == 'u'
|
||||
&& (n = string_get_hex(p, i + 2, 4)) >= 0) {
|
||||
c = n;
|
||||
i += 6 - 1;
|
||||
} else
|
||||
if (i + 3 <= len
|
||||
&& (n = string_get_hex(p, i + 1, 2)) >= 0) {
|
||||
c = n;
|
||||
i += 3 - 1;
|
||||
}
|
||||
}
|
||||
string_buffer_putc16(b, c);
|
||||
}
|
||||
JS_FreeValue(ctx, str);
|
||||
return string_buffer_end(b);
|
||||
}
|
516
third_party/quickjs/usage.c
vendored
Normal file
516
third_party/quickjs/usage.c
vendored
Normal file
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
* QuickJS Javascript Engine
|
||||
*
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "libc/inttypes.h"
|
||||
#include "third_party/quickjs/internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
QuickJS (MIT License)\\n\
|
||||
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
||||
Copyright (c) 2017-2021 Charlie Gordon\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
/* Compute memory used by various object types */
|
||||
/* XXX: poor man's approach to handling multiply referenced objects */
|
||||
typedef struct JSMemoryUsage_helper {
|
||||
double memory_used_count;
|
||||
double str_count;
|
||||
double str_size;
|
||||
int64_t js_func_count;
|
||||
double js_func_size;
|
||||
int64_t js_func_code_size;
|
||||
int64_t js_func_pc2line_count;
|
||||
int64_t js_func_pc2line_size;
|
||||
} JSMemoryUsage_helper;
|
||||
|
||||
static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp);
|
||||
|
||||
static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp)
|
||||
{
|
||||
if (!str->atom_type) { /* atoms are handled separately */
|
||||
double s_ref_count = str->header.ref_count;
|
||||
hp->str_count += 1 / s_ref_count;
|
||||
hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) +
|
||||
1 - str->is_wide_char) / s_ref_count);
|
||||
}
|
||||
}
|
||||
|
||||
static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp)
|
||||
{
|
||||
int memory_used_count, js_func_size, i;
|
||||
memory_used_count = 0;
|
||||
js_func_size = offsetof(JSFunctionBytecode, debug);
|
||||
if (b->vardefs) {
|
||||
js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs);
|
||||
}
|
||||
if (b->cpool) {
|
||||
js_func_size += b->cpool_count * sizeof(*b->cpool);
|
||||
for (i = 0; i < b->cpool_count; i++) {
|
||||
JSValueConst val = b->cpool[i];
|
||||
compute_value_size(val, hp);
|
||||
}
|
||||
}
|
||||
if (b->closure_var) {
|
||||
js_func_size += b->closure_var_count * sizeof(*b->closure_var);
|
||||
}
|
||||
if (!b->read_only_bytecode && b->byte_code_buf) {
|
||||
hp->js_func_code_size += b->byte_code_len;
|
||||
}
|
||||
if (b->has_debug) {
|
||||
js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug);
|
||||
if (b->debug.source) {
|
||||
memory_used_count++;
|
||||
js_func_size += b->debug.source_len + 1;
|
||||
}
|
||||
if (b->debug.pc2line_len) {
|
||||
memory_used_count++;
|
||||
hp->js_func_pc2line_count += 1;
|
||||
hp->js_func_pc2line_size += b->debug.pc2line_len;
|
||||
}
|
||||
}
|
||||
hp->js_func_size += js_func_size;
|
||||
hp->js_func_count += 1;
|
||||
hp->memory_used_count += memory_used_count;
|
||||
}
|
||||
|
||||
static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp)
|
||||
{
|
||||
switch(JS_VALUE_GET_TAG(val)) {
|
||||
case JS_TAG_STRING:
|
||||
compute_jsstring_size(JS_VALUE_GET_STRING(val), hp);
|
||||
break;
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_TAG_BIG_INT:
|
||||
case JS_TAG_BIG_FLOAT:
|
||||
case JS_TAG_BIG_DECIMAL:
|
||||
/* should track JSBigFloat usage */
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
|
||||
{
|
||||
struct list_head *el, *el1;
|
||||
int i;
|
||||
JSMemoryUsage_helper mem = { 0 }, *hp = &mem;
|
||||
memset(s, 0, sizeof(*s));
|
||||
s->malloc_count = rt->malloc_state.malloc_count;
|
||||
s->malloc_size = rt->malloc_state.malloc_size;
|
||||
s->malloc_limit = rt->malloc_state.malloc_limit;
|
||||
s->memory_used_count = 2; /* rt + rt->class_array */
|
||||
s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;
|
||||
list_for_each(el, &rt->context_list) {
|
||||
JSContext *ctx = list_entry(el, JSContext, link);
|
||||
JSShape *sh = ctx->array_shape;
|
||||
s->memory_used_count += 2; /* ctx + ctx->class_proto */
|
||||
s->memory_used_size += sizeof(JSContext) +
|
||||
sizeof(JSValue) * rt->class_count;
|
||||
s->binary_object_count += ctx->binary_object_count;
|
||||
s->binary_object_size += ctx->binary_object_size;
|
||||
/* the hashed shapes are counted separately */
|
||||
if (sh && !sh->is_hashed) {
|
||||
int hash_size = sh->prop_hash_mask + 1;
|
||||
s->shape_count++;
|
||||
s->shape_size += get_shape_size(hash_size, sh->prop_size);
|
||||
}
|
||||
list_for_each(el1, &ctx->loaded_modules) {
|
||||
JSModuleDef *m = list_entry(el1, JSModuleDef, link);
|
||||
s->memory_used_count += 1;
|
||||
s->memory_used_size += sizeof(*m);
|
||||
if (m->req_module_entries) {
|
||||
s->memory_used_count += 1;
|
||||
s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries);
|
||||
}
|
||||
if (m->export_entries) {
|
||||
s->memory_used_count += 1;
|
||||
s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries);
|
||||
for (i = 0; i < m->export_entries_count; i++) {
|
||||
JSExportEntry *me = &m->export_entries[i];
|
||||
if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) {
|
||||
/* potential multiple count */
|
||||
s->memory_used_count += 1;
|
||||
compute_value_size(me->u.local.var_ref->value, hp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m->star_export_entries) {
|
||||
s->memory_used_count += 1;
|
||||
s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries);
|
||||
}
|
||||
if (m->import_entries) {
|
||||
s->memory_used_count += 1;
|
||||
s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries);
|
||||
}
|
||||
compute_value_size(m->module_ns, hp);
|
||||
compute_value_size(m->func_obj, hp);
|
||||
}
|
||||
}
|
||||
list_for_each(el, &rt->gc_obj_list) {
|
||||
JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
|
||||
JSObject *p;
|
||||
JSShape *sh;
|
||||
JSShapeProperty *prs;
|
||||
/* XXX: could count the other GC object types too */
|
||||
if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {
|
||||
compute_bytecode_size((JSFunctionBytecode *)gp, hp);
|
||||
continue;
|
||||
} else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) {
|
||||
continue;
|
||||
}
|
||||
p = (JSObject *)gp;
|
||||
sh = p->shape;
|
||||
s->obj_count++;
|
||||
if (p->prop) {
|
||||
s->memory_used_count++;
|
||||
s->prop_size += sh->prop_size * sizeof(*p->prop);
|
||||
s->prop_count += sh->prop_count;
|
||||
prs = get_shape_prop(sh);
|
||||
for(i = 0; i < sh->prop_count; i++) {
|
||||
JSProperty *pr = &p->prop[i];
|
||||
if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) {
|
||||
compute_value_size(pr->u.value, hp);
|
||||
}
|
||||
prs++;
|
||||
}
|
||||
}
|
||||
/* the hashed shapes are counted separately */
|
||||
if (!sh->is_hashed) {
|
||||
int hash_size = sh->prop_hash_mask + 1;
|
||||
s->shape_count++;
|
||||
s->shape_size += get_shape_size(hash_size, sh->prop_size);
|
||||
}
|
||||
switch(p->class_id) {
|
||||
case JS_CLASS_ARRAY: /* u.array | length */
|
||||
case JS_CLASS_ARGUMENTS: /* u.array | length */
|
||||
s->array_count++;
|
||||
if (p->fast_array) {
|
||||
s->fast_array_count++;
|
||||
if (p->u.array.u.values) {
|
||||
s->memory_used_count++;
|
||||
s->memory_used_size += p->u.array.count *
|
||||
sizeof(*p->u.array.u.values);
|
||||
s->fast_array_elements += p->u.array.count;
|
||||
for (i = 0; i < p->u.array.count; i++) {
|
||||
compute_value_size(p->u.array.u.values[i], hp);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_CLASS_NUMBER: /* u.object_data */
|
||||
case JS_CLASS_STRING: /* u.object_data */
|
||||
case JS_CLASS_BOOLEAN: /* u.object_data */
|
||||
case JS_CLASS_SYMBOL: /* u.object_data */
|
||||
case JS_CLASS_DATE: /* u.object_data */
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_CLASS_BIG_INT: /* u.object_data */
|
||||
case JS_CLASS_BIG_FLOAT: /* u.object_data */
|
||||
case JS_CLASS_BIG_DECIMAL: /* u.object_data */
|
||||
#endif
|
||||
compute_value_size(p->u.object_data, hp);
|
||||
break;
|
||||
case JS_CLASS_C_FUNCTION: /* u.cfunc */
|
||||
s->c_func_count++;
|
||||
break;
|
||||
case JS_CLASS_BYTECODE_FUNCTION: /* u.func */
|
||||
{
|
||||
JSFunctionBytecode *b = p->u.func.function_bytecode;
|
||||
JSVarRef **var_refs = p->u.func.var_refs;
|
||||
/* home_object: object will be accounted for in list scan */
|
||||
if (var_refs) {
|
||||
s->memory_used_count++;
|
||||
s->js_func_size += b->closure_var_count * sizeof(*var_refs);
|
||||
for (i = 0; i < b->closure_var_count; i++) {
|
||||
if (var_refs[i]) {
|
||||
double ref_count = var_refs[i]->header.ref_count;
|
||||
s->memory_used_count += 1 / ref_count;
|
||||
s->js_func_size += sizeof(*var_refs[i]) / ref_count;
|
||||
/* handle non object closed values */
|
||||
if (var_refs[i]->pvalue == &var_refs[i]->value) {
|
||||
/* potential multiple count */
|
||||
compute_value_size(var_refs[i]->value, hp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */
|
||||
{
|
||||
JSBoundFunction *bf = p->u.bound_function;
|
||||
/* func_obj and this_val are objects */
|
||||
for (i = 0; i < bf->argc; i++) {
|
||||
compute_value_size(bf->argv[i], hp);
|
||||
}
|
||||
s->memory_used_count += 1;
|
||||
s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv);
|
||||
}
|
||||
break;
|
||||
case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */
|
||||
{
|
||||
JSCFunctionDataRecord *fd = p->u.c_function_data_record;
|
||||
if (fd) {
|
||||
for (i = 0; i < fd->data_len; i++) {
|
||||
compute_value_size(fd->data[i], hp);
|
||||
}
|
||||
s->memory_used_count += 1;
|
||||
s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_CLASS_REGEXP: /* u.regexp */
|
||||
compute_jsstring_size(p->u.regexp.pattern, hp);
|
||||
compute_jsstring_size(p->u.regexp.bytecode, hp);
|
||||
break;
|
||||
case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */
|
||||
{
|
||||
JSForInIterator *it = p->u.for_in_iterator;
|
||||
if (it) {
|
||||
compute_value_size(it->obj, hp);
|
||||
s->memory_used_count += 1;
|
||||
s->memory_used_size += sizeof(*it);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */
|
||||
case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */
|
||||
{
|
||||
JSArrayBuffer *abuf = p->u.array_buffer;
|
||||
if (abuf) {
|
||||
s->memory_used_count += 1;
|
||||
s->memory_used_size += sizeof(*abuf);
|
||||
if (abuf->data) {
|
||||
s->memory_used_count += 1;
|
||||
s->memory_used_size += abuf->byte_length;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_CLASS_GENERATOR: /* u.generator_data */
|
||||
case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */
|
||||
case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */
|
||||
case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */
|
||||
case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */
|
||||
case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */
|
||||
case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */
|
||||
case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */
|
||||
case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */
|
||||
#endif
|
||||
case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */
|
||||
case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */
|
||||
case JS_CLASS_DATAVIEW: /* u.typed_array */
|
||||
#ifdef CONFIG_BIGNUM
|
||||
case JS_CLASS_FLOAT_ENV: /* u.float_env */
|
||||
#endif
|
||||
case JS_CLASS_MAP: /* u.map_state */
|
||||
case JS_CLASS_SET: /* u.map_state */
|
||||
case JS_CLASS_WEAKMAP: /* u.map_state */
|
||||
case JS_CLASS_WEAKSET: /* u.map_state */
|
||||
case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */
|
||||
case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */
|
||||
case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */
|
||||
case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */
|
||||
case JS_CLASS_PROXY: /* u.proxy_data */
|
||||
case JS_CLASS_PROMISE: /* u.promise_data */
|
||||
case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */
|
||||
case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */
|
||||
case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */
|
||||
case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */
|
||||
case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */
|
||||
case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */
|
||||
/* TODO */
|
||||
default:
|
||||
/* XXX: class definition should have an opaque block size */
|
||||
if (p->u.opaque) {
|
||||
s->memory_used_count += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
s->obj_size += s->obj_count * sizeof(JSObject);
|
||||
/* hashed shapes */
|
||||
s->memory_used_count++; /* rt->shape_hash */
|
||||
s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size;
|
||||
for(i = 0; i < rt->shape_hash_size; i++) {
|
||||
JSShape *sh;
|
||||
for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
|
||||
int hash_size = sh->prop_hash_mask + 1;
|
||||
s->shape_count++;
|
||||
s->shape_size += get_shape_size(hash_size, sh->prop_size);
|
||||
}
|
||||
}
|
||||
/* atoms */
|
||||
s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */
|
||||
s->atom_count = rt->atom_count;
|
||||
s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size +
|
||||
sizeof(rt->atom_hash[0]) * rt->atom_hash_size;
|
||||
for(i = 0; i < rt->atom_size; i++) {
|
||||
JSAtomStruct *p = rt->atom_array[i];
|
||||
if (!atom_is_free(p)) {
|
||||
s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) +
|
||||
1 - p->is_wide_char);
|
||||
}
|
||||
}
|
||||
s->str_count = round(mem.str_count);
|
||||
s->str_size = round(mem.str_size);
|
||||
s->js_func_count = mem.js_func_count;
|
||||
s->js_func_size = round(mem.js_func_size);
|
||||
s->js_func_code_size = mem.js_func_code_size;
|
||||
s->js_func_pc2line_count = mem.js_func_pc2line_count;
|
||||
s->js_func_pc2line_size = mem.js_func_pc2line_size;
|
||||
s->memory_used_count += round(mem.memory_used_count) +
|
||||
s->atom_count + s->str_count +
|
||||
s->obj_count + s->shape_count +
|
||||
s->js_func_count + s->js_func_pc2line_count;
|
||||
s->memory_used_size += s->atom_size + s->str_size +
|
||||
s->obj_size + s->prop_size + s->shape_size +
|
||||
s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size;
|
||||
}
|
||||
|
||||
void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
|
||||
{
|
||||
fprintf(fp, "QuickJS memory usage -- %d-bit, malloc limit: %"PRId64"\n\n",
|
||||
(int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit);
|
||||
#if 1
|
||||
if (rt) {
|
||||
static const struct {
|
||||
const char *name;
|
||||
size_t size;
|
||||
} object_types[] = {
|
||||
{ "JSRuntime", sizeof(JSRuntime) },
|
||||
{ "JSContext", sizeof(JSContext) },
|
||||
{ "JSObject", sizeof(JSObject) },
|
||||
{ "JSString", sizeof(JSString) },
|
||||
{ "JSFunctionBytecode", sizeof(JSFunctionBytecode) },
|
||||
};
|
||||
int i, usage_size_ok = 0;
|
||||
for(i = 0; i < countof(object_types); i++) {
|
||||
unsigned int size = object_types[i].size;
|
||||
void *p = js_malloc_rt(rt, size);
|
||||
if (p) {
|
||||
unsigned int size1 = js_malloc_usable_size_rt(rt, p);
|
||||
if (size1 >= size) {
|
||||
usage_size_ok = 1;
|
||||
fprintf(fp, " %3u + %-2u %s\n",
|
||||
size, size1 - size, object_types[i].name);
|
||||
}
|
||||
js_free_rt(rt, p);
|
||||
}
|
||||
}
|
||||
if (!usage_size_ok) {
|
||||
fprintf(fp, " malloc_usable_size unavailable\n");
|
||||
}
|
||||
{
|
||||
int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 };
|
||||
int class_id;
|
||||
struct list_head *el;
|
||||
list_for_each(el, &rt->gc_obj_list) {
|
||||
JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
|
||||
JSObject *p;
|
||||
if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
|
||||
p = (JSObject *)gp;
|
||||
obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++;
|
||||
}
|
||||
}
|
||||
fprintf(fp, "\n" "JSObject classes\n");
|
||||
if (obj_classes[0])
|
||||
fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none");
|
||||
for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) {
|
||||
if (obj_classes[class_id]) {
|
||||
char buf[ATOM_GET_STR_BUF_SIZE];
|
||||
fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id,
|
||||
JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name));
|
||||
}
|
||||
}
|
||||
if (obj_classes[JS_CLASS_INIT_COUNT])
|
||||
fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other");
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
#endif
|
||||
fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE");
|
||||
if (s->malloc_count) {
|
||||
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n",
|
||||
"memory allocated", s->malloc_count, s->malloc_size,
|
||||
(double)s->malloc_size / s->malloc_count);
|
||||
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n",
|
||||
"memory used", s->memory_used_count, s->memory_used_size,
|
||||
MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) /
|
||||
s->memory_used_count));
|
||||
}
|
||||
if (s->atom_count) {
|
||||
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n",
|
||||
"atoms", s->atom_count, s->atom_size,
|
||||
(double)s->atom_size / s->atom_count);
|
||||
}
|
||||
if (s->str_count) {
|
||||
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n",
|
||||
"strings", s->str_count, s->str_size,
|
||||
(double)s->str_size / s->str_count);
|
||||
}
|
||||
if (s->obj_count) {
|
||||
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
|
||||
"objects", s->obj_count, s->obj_size,
|
||||
(double)s->obj_size / s->obj_count);
|
||||
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
|
||||
" properties", s->prop_count, s->prop_size,
|
||||
(double)s->prop_count / s->obj_count);
|
||||
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n",
|
||||
" shapes", s->shape_count, s->shape_size,
|
||||
(double)s->shape_size / s->shape_count);
|
||||
}
|
||||
if (s->js_func_count) {
|
||||
fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
|
||||
"bytecode functions", s->js_func_count, s->js_func_size);
|
||||
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
|
||||
" bytecode", s->js_func_count, s->js_func_code_size,
|
||||
(double)s->js_func_code_size / s->js_func_count);
|
||||
if (s->js_func_pc2line_count) {
|
||||
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
|
||||
" pc2line", s->js_func_pc2line_count,
|
||||
s->js_func_pc2line_size,
|
||||
(double)s->js_func_pc2line_size / s->js_func_pc2line_count);
|
||||
}
|
||||
}
|
||||
if (s->c_func_count) {
|
||||
fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);
|
||||
}
|
||||
if (s->array_count) {
|
||||
fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count);
|
||||
if (s->fast_array_count) {
|
||||
fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count);
|
||||
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n",
|
||||
" elements", s->fast_array_elements,
|
||||
s->fast_array_elements * (int)sizeof(JSValue),
|
||||
(double)s->fast_array_elements / s->fast_array_count);
|
||||
}
|
||||
}
|
||||
if (s->binary_object_count) {
|
||||
fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
|
||||
"binary objects", s->binary_object_count, s->binary_object_size);
|
||||
}
|
||||
}
|
100
tool/build/deltaify2.c
Normal file
100
tool/build/deltaify2.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
static int pid;
|
||||
|
||||
static void RelaySig(int sig, struct siginfo *si, struct ucontext *uc) {
|
||||
kill(pid, sig);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
FILE *f;
|
||||
bool ok;
|
||||
char *s, *p;
|
||||
int64_t micros;
|
||||
long double t1, t2;
|
||||
int ws, pipefds[2];
|
||||
setvbuf(stdout, malloc(BUFSIZ), _IOLBF, BUFSIZ);
|
||||
setvbuf(stderr, malloc(BUFSIZ), _IOLBF, BUFSIZ);
|
||||
t1 = nowl();
|
||||
if (argc < 2) {
|
||||
f = stdin;
|
||||
} else {
|
||||
sigset_t block, mask;
|
||||
struct sigaction saignore = {.sa_handler = SIG_IGN};
|
||||
struct sigaction sadefault = {.sa_handler = SIG_DFL};
|
||||
struct sigaction sarelay = {.sa_sigaction = RelaySig,
|
||||
.sa_flags = SA_RESTART};
|
||||
sigemptyset(&block);
|
||||
sigaddset(&mask, SIGINT);
|
||||
sigaddset(&mask, SIGQUIT);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &block, &mask);
|
||||
sigaction(SIGHUP, &sarelay, 0);
|
||||
sigaction(SIGTERM, &sarelay, 0);
|
||||
sigaction(SIGINT, &saignore, 0);
|
||||
sigaction(SIGQUIT, &saignore, 0);
|
||||
CHECK_NE(-1, pipe(pipefds));
|
||||
CHECK_NE(-1, (pid = vfork()));
|
||||
if (!pid) {
|
||||
close(pipefds[0]);
|
||||
dup2(pipefds[1], 1);
|
||||
sigaction(SIGHUP, &sadefault, NULL);
|
||||
sigaction(SIGTERM, &sadefault, NULL);
|
||||
sigaction(SIGINT, &sadefault, NULL);
|
||||
sigaction(SIGQUIT, &sadefault, NULL);
|
||||
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||
execvp(argv[1], argv + 1);
|
||||
_exit(127);
|
||||
}
|
||||
close(pipefds[1]);
|
||||
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||
CHECK((f = fdopen(pipefds[0], "r")));
|
||||
CHECK_NE(-1, setvbuf(f, malloc(4096), _IOLBF, 4096));
|
||||
}
|
||||
if ((p = xgetline(f))) {
|
||||
do {
|
||||
s = xgetline(f);
|
||||
t2 = nowl();
|
||||
micros = (t2 - t1) * 1e6;
|
||||
t1 = t2;
|
||||
printf("%16ld %s", micros, p);
|
||||
free(p);
|
||||
} while ((p = s));
|
||||
}
|
||||
ok = !ferror(f);
|
||||
if (argc < 2) return ok ? 0 : 1;
|
||||
fclose(f);
|
||||
while (waitpid(pid, &ws, 0) == -1) CHECK_EQ(EINTR, errno);
|
||||
return !WIFEXITED(ws) ? 128 + WTERMSIG(ws) : ok ? WEXITSTATUS(ws) : 1;
|
||||
}
|
283
tool/decode/zip2.c
Normal file
283
tool/decode/zip2.c
Normal file
|
@ -0,0 +1,283 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/safemacros.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "libc/zip.h"
|
||||
#include "tool/decode/lib/asmcodegen.h"
|
||||
#include "tool/decode/lib/disassemblehex.h"
|
||||
#include "tool/decode/lib/zipnames.h"
|
||||
|
||||
char *FormatDosDate(uint16_t dosdate) {
|
||||
return xasprintf("%04u-%02u-%02u", ((dosdate >> 9) & 0b1111111) + 1980,
|
||||
(dosdate >> 5) & 0b1111, dosdate & 0b11111);
|
||||
}
|
||||
|
||||
char *FormatDosTime(uint16_t dostime) {
|
||||
return xasprintf("%02u:%02u:%02u", (dostime >> 11) & 0b11111,
|
||||
(dostime >> 5) & 0b111111, (dostime << 1) & 0b111110);
|
||||
}
|
||||
|
||||
void ShowGeneralFlag(uint16_t generalflag) {
|
||||
puts("\
|
||||
/ ┌─utf8\n\
|
||||
/ │ ┌─strong encryption\n\
|
||||
/ │ │┌─compressed patch data\n\
|
||||
/ │ ││ ┌─crc and size go after file content\n\
|
||||
/ │ ││ │┌─{normal,max,fast,superfast}\n\
|
||||
/ │ ││ ││ ┌─encrypted\n\
|
||||
/ rrrr│uuuu││r│├┐│");
|
||||
show(".short", format(b1, "0b%016b", generalflag), "generalflag");
|
||||
}
|
||||
|
||||
void ShowCompressionMethod(uint16_t compressmethod) {
|
||||
show(".short",
|
||||
firstnonnull(findnamebyid(kZipCompressionNames, compressmethod),
|
||||
format(b1, "%hu", compressmethod)),
|
||||
"compressionmethod");
|
||||
}
|
||||
|
||||
void ShowTimestamp(uint16_t time, uint16_t date) {
|
||||
show(".short", format(b1, "%#04hx", time),
|
||||
gc(xasprintf("%s (%s)", "lastmodifiedtime", gc(FormatDosTime(time)))));
|
||||
show(".short", format(b1, "%#04hx", date),
|
||||
gc(xasprintf("%s (%s)", "lastmodifieddate", gc(FormatDosDate(date)))));
|
||||
}
|
||||
|
||||
void ShowNtfs(uint8_t *ntfs, size_t n) {
|
||||
struct timespec mtime, atime, ctime;
|
||||
mtime = FileTimeToTimeSpec(
|
||||
(struct NtFileTime){READ32LE(ntfs + 8), READ32LE(ntfs + 12)});
|
||||
atime = FileTimeToTimeSpec(
|
||||
(struct NtFileTime){READ32LE(ntfs + 16), READ32LE(ntfs + 20)});
|
||||
ctime = FileTimeToTimeSpec(
|
||||
(struct NtFileTime){READ32LE(ntfs + 24), READ32LE(ntfs + 28)});
|
||||
show(".long", gc(xasprintf("%d", READ32LE(ntfs))), "ntfs reserved");
|
||||
show(".short", gc(xasprintf("0x%04x", READ16LE(ntfs + 4))),
|
||||
"ntfs attribute tag value #1");
|
||||
show(".short", gc(xasprintf("%hu", READ16LE(ntfs + 6))),
|
||||
"ntfs attribute tag size");
|
||||
show(".quad", gc(xasprintf("%lu", READ64LE(ntfs + 8))),
|
||||
gc(xasprintf("%s (%s)", "ntfs last modified time",
|
||||
gc(xiso8601(&mtime)))));
|
||||
show(".quad", gc(xasprintf("%lu", READ64LE(ntfs + 16))),
|
||||
gc(xasprintf("%s (%s)", "ntfs last access time", gc(xiso8601(&atime)))));
|
||||
show(".quad", gc(xasprintf("%lu", READ64LE(ntfs + 24))),
|
||||
gc(xasprintf("%s (%s)", "ntfs creation time", gc(xiso8601(&ctime)))));
|
||||
}
|
||||
|
||||
void ShowExtendedTimestamp(uint8_t *p, size_t n, bool islocal) {
|
||||
int flag;
|
||||
if (n) {
|
||||
--n;
|
||||
flag = *p++;
|
||||
show(".byte", gc(xasprintf("0b%03hhb", flag)), "fields present in local");
|
||||
if ((flag & 1) && n >= 4) {
|
||||
show(".long", gc(xasprintf("%u", READ32LE(p))),
|
||||
gc(xasprintf("%s (%s)", "last modified",
|
||||
gc(xiso8601(&(struct timespec){READ32LE(p)})))));
|
||||
p += 4;
|
||||
n -= 4;
|
||||
}
|
||||
flag >>= 1;
|
||||
if (islocal) {
|
||||
if ((flag & 1) && n >= 4) {
|
||||
show(".long", gc(xasprintf("%u", READ32LE(p))),
|
||||
gc(xasprintf("%s (%s)", "access time",
|
||||
gc(xiso8601(&(struct timespec){READ32LE(p)})))));
|
||||
p += 4;
|
||||
n -= 4;
|
||||
}
|
||||
flag >>= 1;
|
||||
if ((flag & 1) && n >= 4) {
|
||||
show(".long", gc(xasprintf("%u", READ32LE(p))),
|
||||
gc(xasprintf("%s (%s)", "creation time",
|
||||
gc(xiso8601(&(struct timespec){READ32LE(p)})))));
|
||||
p += 4;
|
||||
n -= 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShowZip64(uint8_t *p, size_t n, bool islocal) {
|
||||
if (n >= 8) {
|
||||
show(".quad", gc(xasprintf("%lu", READ64LE(p))),
|
||||
gc(xasprintf("uncompressed size (%,ld)", READ64LE(p))));
|
||||
}
|
||||
if (n >= 16) {
|
||||
show(".quad", gc(xasprintf("%lu", READ64LE(p + 8))),
|
||||
gc(xasprintf("compressed size (%,ld)", READ64LE(p + 8))));
|
||||
}
|
||||
if (n >= 24) {
|
||||
show(".quad", gc(xasprintf("%lu", READ64LE(p + 16))),
|
||||
gc(xasprintf("lfile hdr offset (%,ld)", READ64LE(p + 16))));
|
||||
}
|
||||
if (n >= 28) {
|
||||
show(".long", gc(xasprintf("%u", READ32LE(p + 24))), "disk number");
|
||||
}
|
||||
}
|
||||
|
||||
void ShowInfoZipNewUnixExtra(uint8_t *p, size_t n, bool islocal) {
|
||||
if (p[0] == 1 && p[1] == 4 && p[6] == 4) {
|
||||
show(".byte", "1", "version");
|
||||
show(".byte", "4", "uid length");
|
||||
show(".long", gc(xasprintf("%u", READ32LE(p + 2))), "uid");
|
||||
show(".byte", "4", "gid length");
|
||||
show(".long", gc(xasprintf("%u", READ32LE(p + 7))), "gid");
|
||||
} else {
|
||||
disassemblehex(p, n, stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void ShowExtra(uint8_t *extra, bool islocal) {
|
||||
switch (ZIP_EXTRA_HEADERID(extra)) {
|
||||
case kZipExtraNtfs:
|
||||
ShowNtfs(ZIP_EXTRA_CONTENT(extra), ZIP_EXTRA_CONTENTSIZE(extra));
|
||||
break;
|
||||
case kZipExtraExtendedTimestamp:
|
||||
ShowExtendedTimestamp(ZIP_EXTRA_CONTENT(extra),
|
||||
ZIP_EXTRA_CONTENTSIZE(extra), islocal);
|
||||
break;
|
||||
case kZipExtraZip64:
|
||||
ShowZip64(ZIP_EXTRA_CONTENT(extra), ZIP_EXTRA_CONTENTSIZE(extra),
|
||||
islocal);
|
||||
break;
|
||||
case kZipExtraInfoZipNewUnixExtra:
|
||||
ShowInfoZipNewUnixExtra(ZIP_EXTRA_CONTENT(extra),
|
||||
ZIP_EXTRA_CONTENTSIZE(extra), islocal);
|
||||
break;
|
||||
default:
|
||||
disassemblehex(ZIP_EXTRA_CONTENT(extra), ZIP_EXTRA_CONTENTSIZE(extra),
|
||||
stdout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ShowExtras(uint8_t *extras, uint16_t extrassize, bool islocal) {
|
||||
int i;
|
||||
bool first;
|
||||
uint8_t *p, *pe;
|
||||
if (extrassize) {
|
||||
first = true;
|
||||
for (p = extras, pe = extras + extrassize, i = 0; p < pe;
|
||||
p += ZIP_EXTRA_SIZE(p), ++i) {
|
||||
show(".short",
|
||||
firstnonnull(findnamebyid(kZipExtraNames, ZIP_EXTRA_HEADERID(p)),
|
||||
gc(xasprintf("0x%04hx", ZIP_EXTRA_HEADERID(p)))),
|
||||
gc(xasprintf("%s[%d].%s", "extras", i, "headerid")));
|
||||
show(".short", gc(xasprintf("%df-%df", (i + 2) * 10, (i + 1) * 10)),
|
||||
gc(xasprintf("%s[%d].%s (%hd %s)", "extras", i, "contentsize",
|
||||
ZIP_EXTRA_CONTENTSIZE(p), "bytes")));
|
||||
if (first) {
|
||||
first = false;
|
||||
printf("%d:", (i + 1) * 10);
|
||||
}
|
||||
ShowExtra(p, islocal);
|
||||
printf("%d:", (i + 2) * 10);
|
||||
}
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
void ShowLocalFileHeader(uint8_t *lf, uint16_t idx) {
|
||||
printf("\n/\t%s #%hu (%zu %s)\n", "local file", idx + 1,
|
||||
ZIP_LFILE_HDRSIZE(lf), "bytes");
|
||||
show(".ascii", format(b1, "%`'.*s", 4, lf), "magic");
|
||||
show(".byte",
|
||||
firstnonnull(findnamebyid(kZipEraNames, ZIP_LFILE_VERSIONNEED(lf)),
|
||||
gc(xasprintf("%d", ZIP_LFILE_VERSIONNEED(lf)))),
|
||||
"pkzip version need");
|
||||
show(".byte",
|
||||
firstnonnull(findnamebyid(kZipOsNames, ZIP_LFILE_OSNEED(lf)),
|
||||
gc(xasprintf("%d", ZIP_LFILE_OSNEED(lf)))),
|
||||
"os need");
|
||||
ShowGeneralFlag(ZIP_LFILE_GENERALFLAG(lf));
|
||||
ShowCompressionMethod(ZIP_LFILE_COMPRESSIONMETHOD(lf));
|
||||
ShowTimestamp(ZIP_LFILE_LASTMODIFIEDTIME(lf), ZIP_LFILE_LASTMODIFIEDDATE(lf));
|
||||
show(
|
||||
".long",
|
||||
format(b1, "%#x", ZIP_LFILE_CRC32(lf)), gc(xasprintf("%s (%#x)", "crc32z", GetZipLfileCompressedSize(lf) /* crc32_z(0, ZIP_LFILE_CONTENT(lf), GetZipLfileCompressedSize(lf)) */)));
|
||||
if (ZIP_LFILE_COMPRESSEDSIZE(lf) == 0xFFFFFFFF) {
|
||||
show(".long", "0xFFFFFFFF", "compressedsize (zip64)");
|
||||
} else {
|
||||
show(".long", "3f-2f",
|
||||
format(b1, "%s (%u %s)", "compressedsize",
|
||||
ZIP_LFILE_COMPRESSEDSIZE(lf), "bytes"));
|
||||
}
|
||||
show(".long",
|
||||
ZIP_LFILE_UNCOMPRESSEDSIZE(lf) == 0xFFFFFFFF
|
||||
? "0xFFFFFFFF"
|
||||
: format(b1, "%u", ZIP_LFILE_UNCOMPRESSEDSIZE(lf)),
|
||||
"uncompressedsize");
|
||||
show(".short", "1f-0f",
|
||||
format(b1, "%s (%hu %s)", "namesize", ZIP_LFILE_NAMESIZE(lf), "bytes"));
|
||||
show(
|
||||
".short", "2f-1f",
|
||||
format(b1, "%s (%hu %s)", "extrasize", ZIP_LFILE_EXTRASIZE(lf), "bytes"));
|
||||
printf("0:");
|
||||
show(".ascii",
|
||||
format(b1, "%`'s",
|
||||
gc(strndup(ZIP_LFILE_NAME(lf), ZIP_LFILE_NAMESIZE(lf)))),
|
||||
"name");
|
||||
printf("1:");
|
||||
ShowExtras(ZIP_LFILE_EXTRA(lf), ZIP_LFILE_EXTRASIZE(lf), true);
|
||||
printf("2:");
|
||||
disassemblehex(ZIP_LFILE_CONTENT(lf), ZIP_LFILE_COMPRESSEDSIZE(lf), stdout);
|
||||
printf("3:\n");
|
||||
}
|
||||
|
||||
void DisassembleZip(const char *path, uint8_t *p, size_t n) {
|
||||
size_t i, records;
|
||||
uint8_t *eocd, *cdir, *cf, *lf;
|
||||
CHECK_NOTNULL((eocd = GetZipCdir(p, n)));
|
||||
records = GetZipCdirRecords(eocd);
|
||||
cdir = p + GetZipCdirOffset(eocd);
|
||||
for (i = 0, cf = cdir; i < records; ++i, cf += ZIP_CFILE_HDRSIZE(cf)) {
|
||||
CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(cf));
|
||||
lf = p + GetZipCfileOffset(cf);
|
||||
CHECK_EQ(kZipLfileHdrMagic, ZIP_LFILE_MAGIC(lf));
|
||||
ShowLocalFileHeader(lf, i);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int fd;
|
||||
uint8_t *map;
|
||||
struct stat st;
|
||||
showcrashreports();
|
||||
CHECK_EQ(2, argc);
|
||||
CHECK_NE(-1, (fd = open(argv[1], O_RDONLY)));
|
||||
CHECK_NE(-1, fstat(fd, &st));
|
||||
CHECK_GE(st.st_size, kZipCdirHdrMinSize);
|
||||
CHECK_NE(MAP_FAILED,
|
||||
(map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)));
|
||||
DisassembleZip(argv[1], map, st.st_size);
|
||||
CHECK_NE(-1, munmap(map, st.st_size));
|
||||
CHECK_NE(-1, close(fd));
|
||||
return 0;
|
||||
}
|
|
@ -307,7 +307,6 @@ static bool usessl;
|
|||
static bool suiteb;
|
||||
static bool killed;
|
||||
static bool istext;
|
||||
static bool ishtml;
|
||||
static bool zombied;
|
||||
static bool gzipped;
|
||||
static bool branded;
|
||||
|
@ -872,7 +871,7 @@ static void ProgramRedirectArg(int code, const char *s) {
|
|||
ProgramRedirect(code, s, p - s, p + 1, n - (p - s + 1));
|
||||
}
|
||||
|
||||
static void DescribeAddress(char buf[32], uint32_t addr, uint16_t port) {
|
||||
static void DescribeAddress(char buf[40], uint32_t addr, uint16_t port) {
|
||||
char *p;
|
||||
const char *s;
|
||||
p = buf;
|
||||
|
@ -906,7 +905,7 @@ static inline void GetRemoteAddr(uint32_t *ip, uint16_t *port) {
|
|||
static char *DescribeClient(void) {
|
||||
uint32_t ip;
|
||||
uint16_t port;
|
||||
static char clientaddrstr[32];
|
||||
static char clientaddrstr[40];
|
||||
GetRemoteAddr(&ip, &port);
|
||||
DescribeAddress(clientaddrstr, ip, port);
|
||||
return clientaddrstr;
|
||||
|
@ -915,7 +914,7 @@ static char *DescribeClient(void) {
|
|||
static char *DescribeServer(void) {
|
||||
uint32_t ip;
|
||||
uint16_t port;
|
||||
static char serveraddrstr[32];
|
||||
static char serveraddrstr[40];
|
||||
GetServerAddr(&ip, &port);
|
||||
DescribeAddress(serveraddrstr, ip, port);
|
||||
return serveraddrstr;
|
||||
|
@ -1175,8 +1174,8 @@ static void AppendResourceReport(struct rusage *ru, const char *nl) {
|
|||
if (ru->ru_maxrss) {
|
||||
Append("ballooned to %,ldkb in size%s", ru->ru_maxrss, nl);
|
||||
}
|
||||
if ((utime = ru->ru_utime.tv_sec * 1e6 + ru->ru_utime.tv_usec) |
|
||||
(stime = ru->ru_stime.tv_sec * 1e6 + ru->ru_stime.tv_usec)) {
|
||||
if ((utime = ru->ru_utime.tv_sec * 1000000 + ru->ru_utime.tv_usec) |
|
||||
(stime = ru->ru_stime.tv_sec * 1000000 + ru->ru_stime.tv_usec)) {
|
||||
ticks = ceill((long double)(utime + stime) / (1000000.L / CLK_TCK));
|
||||
Append("needed %,ldµs cpu (%d%% kernel)%s", utime + stime,
|
||||
(int)((long double)stime / (utime + stime) * 100), nl);
|
||||
|
@ -1226,8 +1225,8 @@ static void AppendResourceReport(struct rusage *ru, const char *nl) {
|
|||
static void AddTimeval(struct timeval *x, const struct timeval *y) {
|
||||
x->tv_sec += y->tv_sec;
|
||||
x->tv_usec += y->tv_usec;
|
||||
if (x->tv_usec >= 1e6) {
|
||||
x->tv_usec -= 1e6;
|
||||
if (x->tv_usec >= 1000000) {
|
||||
x->tv_usec -= 1000000;
|
||||
x->tv_sec += 1;
|
||||
}
|
||||
}
|
||||
|
@ -5187,6 +5186,7 @@ static const luaL_Reg kLuaFuncs[] = {
|
|||
{"GetHeaders", LuaGetHeaders}, //
|
||||
{"GetHost", LuaGetHost}, //
|
||||
{"GetHttpReason", LuaGetHttpReason}, //
|
||||
{"GetHttpVersion", LuaGetHttpVersion}, //
|
||||
{"GetLastModifiedTime", LuaGetLastModifiedTime}, //
|
||||
{"GetLogLevel", LuaGetLogLevel}, //
|
||||
{"GetMethod", LuaGetMethod}, //
|
||||
|
@ -5197,13 +5197,12 @@ static const luaL_Reg kLuaFuncs[] = {
|
|||
{"GetPath", LuaGetPath}, //
|
||||
{"GetPayload", LuaGetPayload}, //
|
||||
{"GetPort", LuaGetPort}, //
|
||||
{"GetRedbeanVersion", LuaGetRedbeanVersion}, //
|
||||
{"GetRemoteAddr", LuaGetRemoteAddr}, //
|
||||
{"GetScheme", LuaGetScheme}, //
|
||||
{"GetServerAddr", LuaGetServerAddr}, //
|
||||
{"GetUrl", LuaGetUrl}, //
|
||||
{"GetUser", LuaGetUser}, //
|
||||
{"GetHttpVersion", LuaGetHttpVersion}, //
|
||||
{"GetRedbeanVersion", LuaGetRedbeanVersion}, //
|
||||
{"GetZipPaths", LuaGetZipPaths}, //
|
||||
{"HasControlCodes", LuaHasControlCodes}, //
|
||||
{"HasParam", LuaHasParam}, //
|
||||
|
|
Loading…
Reference in a new issue