Reduce build latency and fix old cpu bugs

This commit is contained in:
Justine Tunney 2021-08-05 14:43:53 -07:00
parent df8ab0aa0c
commit 533f3d1ef1
69 changed files with 43069 additions and 43683 deletions

View file

@ -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"

View file

@ -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);

View file

@ -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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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));

View file

@ -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,

View file

@ -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 )

View file

@ -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;

View file

@ -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
*/

View file

@ -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

View file

@ -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
}

View file

@ -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
}

View file

@ -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_ */

View file

@ -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);

View file

@ -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};

View file

@ -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

File diff suppressed because it is too large Load diff

321
third_party/quickjs/atof.c vendored Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

4107
third_party/quickjs/call.c vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -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)

View file

@ -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

File diff suppressed because it is too large Load diff

175
third_party/quickjs/dbuf.c vendored Normal file
View 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));
}

View file

@ -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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

495
third_party/quickjs/gc.c vendored Normal file
View 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
View 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

File diff suppressed because it is too large Load diff

48
third_party/quickjs/iter.c vendored Normal file
View 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
View 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
View 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
View 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_ */

View file

@ -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;

View file

@ -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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

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
View 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

File diff suppressed because it is too large Load diff

961
third_party/quickjs/proxy.c vendored Normal file
View 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);
}

View file

@ -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);

View file

@ -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]);

View file

@ -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;

File diff suppressed because it is too large Load diff

View file

@ -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) */

View file

@ -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
View 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

File diff suppressed because it is too large Load diff

449
third_party/quickjs/shape.c vendored Normal file
View 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

File diff suppressed because it is too large Load diff

331
third_party/quickjs/strbuf.c vendored Normal file
View 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
View 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

File diff suppressed because it is too large Load diff

279
third_party/quickjs/uri.c vendored Normal file
View 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
View 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
View 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
View 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\
/ rrrruuuur");
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;
}

View file

@ -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}, //