diff --git a/libc/sysv/consts/syscon.internal.h b/libc/sysv/consts/syscon.internal.h index 25ee8337f..ae0b5a3ed 100644 --- a/libc/sysv/consts/syscon.internal.h +++ b/libc/sysv/consts/syscon.internal.h @@ -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" diff --git a/test/libc/str/memcpy_test.c b/test/libc/str/memcpy_test.c index 8a6acb849..25a13668f 100644 --- a/test/libc/str/memcpy_test.c +++ b/test/libc/str/memcpy_test.c @@ -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); diff --git a/test/libc/xed/x86ild_popular_i186_test.c b/test/libc/xed/x86ild_popular_i186_test.c index d62ad766b..89294ec14 100644 --- a/test/libc/xed/x86ild_popular_i186_test.c +++ b/test/libc/xed/x86ild_popular_i186_test.c @@ -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 diff --git a/test/libc/xed/x86ild_popular_i86_2_test.c b/test/libc/xed/x86ild_popular_i86_2_test.c new file mode 100644 index 000000000..c7ad4700e --- /dev/null +++ b/test/libc/xed/x86ild_popular_i86_2_test.c @@ -0,0 +1,3741 @@ +/*-*- 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 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/testlib/testlib.h" +#include "test/libc/xed/lib.h" +#include "third_party/xed/x86.h" + +TEST(x86ild, test_4489E7) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov edi, r12d + */ + EXPECT_EQ(3, ild(u"Dëτ")); +} + +TEST(x86ild, test_4489E0) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov eax, r12d + */ + EXPECT_EQ(3, ild(u"Dëα")); +} + +TEST(x86ild, test_4489D9) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov ecx, r11d + */ + EXPECT_EQ(3, ild(u"Dë┘")); +} + +TEST(x86ild, test_4488D0) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov al, r10b + */ + EXPECT_EQ(3, ild(u"Dê╨")); +} + +TEST(x86ild, test_41BE00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_IMMv + ISA_SET: I86 + SHORT: mov r14d, 0x0 + */ + EXPECT_EQ(6, ild(u"A╛    ")); +} + +TEST(x86ild, test_41B900000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_IMMv + ISA_SET: I86 + SHORT: mov r9d, 0x0 + */ + EXPECT_EQ(6, ild(u"A╣    ")); +} + +TEST(x86ild, test_4189E8) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r8d, ebp + */ + EXPECT_EQ(3, ild(u"AëΦ")); +} + +TEST(x86ild, test_4189D0) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r8d, edx + */ + EXPECT_EQ(3, ild(u"Aë╨")); +} + +TEST(x86ild, test_4189C7) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r15d, eax + */ + EXPECT_EQ(3, ild(u"Aë╟")); +} + +TEST(x86ild, test_4188C0) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov r8b, al + */ + EXPECT_EQ(3, ild(u"Aê└")); +} + +TEST(x86ild, test_408A7B00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov dil, byte ptr [rbx] + */ + EXPECT_EQ(4, ild(u"@è{ ")); +} + +TEST(x86ild, test_4088F1) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov cl, sil + */ + EXPECT_EQ(3, ild(u"@ê±")); +} + +TEST(x86ild, test_4088D6) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov sil, dl + */ + EXPECT_EQ(3, ild(u"@ê╓")); +} + +TEST(x86ild, test_4088C7) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov dil, al + */ + EXPECT_EQ(3, ild(u"@ê╟")); +} + +TEST(x86ild, test_0F8300000000) { + /* + ICLASS: JNB + CATEGORY: COND_BR + EXTENSION: BASE + IFORM: JNB_RELBRd + ISA_SET: I86 + SHORT: jnb 0x6 + */ + EXPECT_EQ(6, ild(u"☼â    ")); +} + +TEST(x86ild, test_FF24F500000000) { + /* + ICLASS: JMP + CATEGORY: UNCOND_BR + EXTENSION: BASE + IFORM: JMP_MEMv + ISA_SET: I86 + SHORT: jmp qword ptr [rsi*8] + */ + EXPECT_EQ(7, ild(u"λ$⌡    ")); +} + +TEST(x86ild, test_E80083E200) { + /* + ICLASS: CALL_NEAR + CATEGORY: CALL + EXTENSION: BASE + IFORM: CALL_NEAR_RELBRd + ISA_SET: I86 + SHORT: call 0xe28305 + */ + EXPECT_EQ(5, ild(u"Φ âΓ ")); +} + +TEST(x86ild, test_D3E7) { + /* + ICLASS: SHL + CATEGORY: SHIFT + EXTENSION: BASE + IFORM: SHL_GPRv_CL_D3r4 + ISA_SET: I86 + SHORT: shl edi, cl + */ + EXPECT_EQ(2, ild(u"╙τ")); +} + +TEST(x86ild, test_D2C1) { + /* + ICLASS: ROL + CATEGORY: ROTATE + EXTENSION: BASE + IFORM: ROL_GPR8_CL + ISA_SET: I86 + SHORT: rol cl, cl + */ + EXPECT_EQ(2, ild(u"╥┴")); +} + +TEST(x86ild, test_C784240000000000000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_IMMz + ISA_SET: I86 + SHORT: mov dword ptr [rsp], 0x0 + */ + EXPECT_EQ(11, ild(u"╟ä$        ")); +} + +TEST(x86ild, test_C744240000000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_IMMz + ISA_SET: I86 + SHORT: mov dword ptr [rsp], 0x0 + */ + EXPECT_EQ(8, ild(u"╟D$     ")); +} + +TEST(x86ild, test_C6840E0000000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_IMMb + ISA_SET: I86 + SHORT: mov byte ptr [rsi+rcx*1], 0x0 + */ + EXPECT_EQ(8, ild(u"╞ä♫     ")); +} + +TEST(x86ild, test_C6420000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_IMMb + ISA_SET: I86 + SHORT: mov byte ptr [rdx], 0x0 + */ + EXPECT_EQ(4, ild(u"╞B  ")); +} + +TEST(x86ild, test_C60700) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_IMMb + ISA_SET: I86 + SHORT: mov byte ptr [rdi], 0x0 + */ + EXPECT_EQ(3, ild(u"╞• ")); +} + +TEST(x86ild, test_C60600) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_IMMb + ISA_SET: I86 + SHORT: mov byte ptr [rsi], 0x0 + */ + EXPECT_EQ(3, ild(u"╞♠ ")); +} + +TEST(x86ild, test_8BB200000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov esi, dword ptr [rdx] + */ + EXPECT_EQ(6, ild(u"ï▓    ")); +} + +TEST(x86ild, test_8B7F00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov edi, dword ptr [rdi] + */ + EXPECT_EQ(3, ild(u"ï⌂ ")); +} + +TEST(x86ild, test_8B7C2400) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov edi, dword ptr [rsp] + */ + EXPECT_EQ(4, ild(u"ï|$ ")); +} + +TEST(x86ild, test_8B7B00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov edi, dword ptr [rbx] + */ + EXPECT_EQ(3, ild(u"ï{ ")); +} + +TEST(x86ild, test_8B5100) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov edx, dword ptr [rcx] + */ + EXPECT_EQ(3, ild(u"ïQ ")); +} + +TEST(x86ild, test_8B4C2400) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov ecx, dword ptr [rsp] + */ + EXPECT_EQ(4, ild(u"ïL$ ")); +} + +TEST(x86ild, test_8B16) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov edx, dword ptr [rsi] + */ + EXPECT_EQ(2, ild(u"ï▬")); +} + +TEST(x86ild, test_8B10) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov edx, dword ptr [rax] + */ + EXPECT_EQ(2, ild(u"ï►")); +} + +TEST(x86ild, test_8B0C8500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov ecx, dword ptr [rax*4] + */ + EXPECT_EQ(7, ild(u"ï♀à    ")); +} + +TEST(x86ild, test_8B04FD00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov eax, dword ptr [rdi*8] + */ + EXPECT_EQ(7, ild(u"ï♦²    ")); +} + +TEST(x86ild, test_8B04C500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov eax, dword ptr [rax*8] + */ + EXPECT_EQ(7, ild(u"ï♦┼    ")); +} + +TEST(x86ild, test_8B00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov eax, dword ptr [rax] + */ + EXPECT_EQ(2, ild(u"ï ")); +} + +TEST(x86ild, test_8A14C500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov dl, byte ptr [rax*8] + */ + EXPECT_EQ(7, ild(u"è¶┼    ")); +} + +TEST(x86ild, test_8A0401) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov al, byte ptr [rcx+rax*1] + */ + EXPECT_EQ(3, ild(u"è♦☺")); +} + +TEST(x86ild, test_89FB) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov ebx, edi + */ + EXPECT_EQ(2, ild(u"ë√")); +} + +TEST(x86ild, test_89DF) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov edi, ebx + */ + EXPECT_EQ(2, ild(u"ë▀")); +} + +TEST(x86ild, test_89D8) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov eax, ebx + */ + EXPECT_EQ(2, ild(u"ë╪")); +} + +TEST(x86ild, test_89CB) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov ebx, ecx + */ + EXPECT_EQ(2, ild(u"ë╦")); +} + +TEST(x86ild, test_899A00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rdx], ebx + */ + EXPECT_EQ(6, ild(u"ëÜ    ")); +} + +TEST(x86ild, test_894B00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rbx], ecx + */ + EXPECT_EQ(3, ild(u"ëK ")); +} + +TEST(x86ild, test_890424) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rsp], eax + */ + EXPECT_EQ(3, ild(u"ë♦$")); +} + +TEST(x86ild, test_8810) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rax], dl + */ + EXPECT_EQ(2, ild(u"ê►")); +} + +TEST(x86ild, test_66C747000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_IMMz + ISA_SET: I86 + SHORT: mov word ptr [rdi], 0x0 + */ + EXPECT_EQ(6, ild(u"f╟G   ")); +} + +TEST(x86ild, test_668910) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov word ptr [rax], dx + */ + EXPECT_EQ(3, ild(u"fë►")); +} + +TEST(x86ild, test_4D89EC) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r12, r13 + */ + EXPECT_EQ(3, ild(u"Më∞")); +} + +TEST(x86ild, test_4C89FA) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rdx, r15 + */ + EXPECT_EQ(3, ild(u"Lë·")); +} + +TEST(x86ild, test_4C89E6) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rsi, r12 + */ + EXPECT_EQ(3, ild(u"Lëμ")); +} + +TEST(x86ild, test_4C89D0) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rax, r10 + */ + EXPECT_EQ(3, ild(u"Lë╨")); +} + +TEST(x86ild, test_49D3E0) { + /* + ICLASS: SHL + CATEGORY: SHIFT + EXTENSION: BASE + IFORM: SHL_GPRv_CL_D3r4 + ISA_SET: I86 + SHORT: shl r8, cl + */ + EXPECT_EQ(3, ild(u"I╙α")); +} + +TEST(x86ild, test_4989F4) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r12, rsi + */ + EXPECT_EQ(3, ild(u"Ië⌠")); +} + +TEST(x86ild, test_4989C4) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r12, rax + */ + EXPECT_EQ(3, ild(u"Ië─")); +} + +TEST(x86ild, test_48BF0000000000000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_IMMv + ISA_SET: I86 + SHORT: mov rdi, 0x0 + */ + EXPECT_EQ(10, ild(u"H┐        ")); +} + +TEST(x86ild, test_488B9100000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rdx, qword ptr [rcx] + */ + EXPECT_EQ(7, ild(u"Hïæ    ")); +} + +TEST(x86ild, test_488B7700) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rsi, qword ptr [rdi] + */ + EXPECT_EQ(4, ild(u"Hïw ")); +} + +TEST(x86ild, test_488B5200) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rdx, qword ptr [rdx] + */ + EXPECT_EQ(4, ild(u"HïR ")); +} + +TEST(x86ild, test_488B04D0) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rax, qword ptr [rax+rdx*8] + */ + EXPECT_EQ(4, ild(u"Hï♦╨")); +} + +TEST(x86ild, test_4889E9) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rcx, rbp + */ + EXPECT_EQ(3, ild(u"HëΘ")); +} + +TEST(x86ild, test_4889E7) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rdi, rsp + */ + EXPECT_EQ(3, ild(u"Hëτ")); +} + +TEST(x86ild, test_4889D7) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rdi, rdx + */ + EXPECT_EQ(3, ild(u"Hë╫")); +} + +TEST(x86ild, test_4889CD) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rbp, rcx + */ + EXPECT_EQ(3, ild(u"Hë═")); +} + +TEST(x86ild, test_48895700) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rdi], rdx + */ + EXPECT_EQ(4, ild(u"HëW ")); +} + +TEST(x86ild, test_48894700) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rdi], rax + */ + EXPECT_EQ(4, ild(u"HëG ")); +} + +TEST(x86ild, test_48894300) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rbx], rax + */ + EXPECT_EQ(4, ild(u"HëC ")); +} + +TEST(x86ild, test_488910) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rax], rdx + */ + EXPECT_EQ(3, ild(u"Hë►")); +} + +TEST(x86ild, test_48890D00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rip], rcx + */ + EXPECT_EQ(7, ild(u"Hë♪    ")); +} + +TEST(x86ild, test_488902) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rdx], rax + */ + EXPECT_EQ(3, ild(u"Hë☻")); +} + +TEST(x86ild, test_4863D2) { + /* + ICLASS: MOVSXD + CATEGORY: DATAXFER + EXTENSION: LONGMODE + IFORM: MOVSXD_GPRv_GPR32 + ISA_SET: LONGMODE + SHORT: movsxd rdx, edx + */ + EXPECT_EQ(3, ild(u"Hc╥")); +} + +TEST(x86ild, test_4589CA) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r10d, r9d + */ + EXPECT_EQ(3, ild(u"Eë╩")); +} + +TEST(x86ild, test_4588C8) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov r8b, r9b + */ + EXPECT_EQ(3, ild(u"Eê╚")); +} + +TEST(x86ild, test_4489FA) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov edx, r15d + */ + EXPECT_EQ(3, ild(u"Dë·")); +} + +TEST(x86ild, test_4489F7) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov edi, r14d + */ + EXPECT_EQ(3, ild(u"Dë≈")); +} + +TEST(x86ild, test_4489EF) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov edi, r13d + */ + EXPECT_EQ(3, ild(u"Dë∩")); +} + +TEST(x86ild, test_4489E1) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov ecx, r12d + */ + EXPECT_EQ(3, ild(u"Dëß")); +} + +TEST(x86ild, test_4489442400) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rsp], r8d + */ + EXPECT_EQ(5, ild(u"DëD$ ")); +} + +TEST(x86ild, test_4488CA) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov dl, r9b + */ + EXPECT_EQ(3, ild(u"Dê╩")); +} + +TEST(x86ild, test_44885300) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rbx], r10b + */ + EXPECT_EQ(4, ild(u"DêS ")); +} + +TEST(x86ild, test_44884300) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rbx], r8b + */ + EXPECT_EQ(4, ild(u"DêC ")); +} + +TEST(x86ild, test_428B148D00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov edx, dword ptr [r9*4] + */ + EXPECT_EQ(8, ild(u"Bï¶ì    ")); +} + +TEST(x86ild, test_41D3E0) { + /* + ICLASS: SHL + CATEGORY: SHIFT + EXTENSION: BASE + IFORM: SHL_GPRv_CL_D3r4 + ISA_SET: I86 + SHORT: shl r8d, cl + */ + EXPECT_EQ(3, ild(u"A╙α")); +} + +TEST(x86ild, test_41BF00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_IMMv + ISA_SET: I86 + SHORT: mov r15d, 0x0 + */ + EXPECT_EQ(6, ild(u"A┐    ")); +} + +TEST(x86ild, test_41BD00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_IMMv + ISA_SET: I86 + SHORT: mov r13d, 0x0 + */ + EXPECT_EQ(6, ild(u"A╜    ")); +} + +TEST(x86ild, test_41B800000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_IMMv + ISA_SET: I86 + SHORT: mov r8d, 0x0 + */ + EXPECT_EQ(6, ild(u"A╕    ")); +} + +TEST(x86ild, test_4189F4) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r12d, esi + */ + EXPECT_EQ(3, ild(u"Aë⌠")); +} + +TEST(x86ild, test_4189F2) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r10d, esi + */ + EXPECT_EQ(3, ild(u"Aë≥")); +} + +TEST(x86ild, test_4189C8) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r8d, ecx + */ + EXPECT_EQ(3, ild(u"Aë╚")); +} + +TEST(x86ild, test_4088F9) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov cl, dil + */ + EXPECT_EQ(3, ild(u"@ê∙")); +} + +TEST(x86ild, test_4088F2) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov dl, sil + */ + EXPECT_EQ(3, ild(u"@ê≥")); +} + +TEST(x86ild, test_4088E8) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov al, bpl + */ + EXPECT_EQ(3, ild(u"@êΦ")); +} + +TEST(x86ild, test_0F8900000000) { + /* + ICLASS: JNS + CATEGORY: COND_BR + EXTENSION: BASE + IFORM: JNS_RELBRd + ISA_SET: I86 + SHORT: jns 0x6 + */ + EXPECT_EQ(6, ild(u"☼ë    ")); +} + +TEST(x86ild, test_0F8600000000) { + /* + ICLASS: JBE + CATEGORY: COND_BR + EXTENSION: BASE + IFORM: JBE_RELBRd + ISA_SET: I86 + SHORT: jbe 0x6 + */ + EXPECT_EQ(6, ild(u"☼å    ")); +} + +TEST(x86ild, test_FF14C500000000) { + /* + ICLASS: CALL_NEAR + CATEGORY: CALL + EXTENSION: BASE + IFORM: CALL_NEAR_MEMv + ISA_SET: I86 + SHORT: call qword ptr [rax*8] + */ + EXPECT_EQ(7, ild(u"λ¶┼    ")); +} + +TEST(x86ild, test_F3C3) { + /* + ICLASS: RET_NEAR + CATEGORY: RET + EXTENSION: BASE + IFORM: RET_NEAR + ISA_SET: I86 + SHORT: ret + */ + EXPECT_EQ(2, ild(u"≤├")); +} + +TEST(x86ild, test_F3A4) { + /* + ICLASS: REP_MOVSB + CATEGORY: STRINGOP + EXTENSION: BASE + IFORM: REP_MOVSB + ISA_SET: I86 + SHORT: rep movsb byte ptr [rdi], byte ptr [rsi] + */ + EXPECT_EQ(2, ild(u"≤ñ")); +} + +TEST(x86ild, test_D3E2) { + /* + ICLASS: SHL + CATEGORY: SHIFT + EXTENSION: BASE + IFORM: SHL_GPRv_CL_D3r4 + ISA_SET: I86 + SHORT: shl edx, cl + */ + EXPECT_EQ(2, ild(u"╙Γ")); +} + +TEST(x86ild, test_C6040300) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_IMMb + ISA_SET: I86 + SHORT: mov byte ptr [rbx+rax*1], 0x0 + */ + EXPECT_EQ(4, ild(u"╞♦♥ ")); +} + +TEST(x86ild, test_C60100) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_IMMb + ISA_SET: I86 + SHORT: mov byte ptr [rcx], 0x0 + */ + EXPECT_EQ(3, ild(u"╞☺ ")); +} + +TEST(x86ild, test_B200) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_IMMb_B0 + ISA_SET: I86 + SHORT: mov dl, 0x0 + */ + EXPECT_EQ(2, ild(u"▓ ")); +} + +TEST(x86ild, test_8B8100000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov eax, dword ptr [rcx] + */ + EXPECT_EQ(6, ild(u"ïü    ")); +} + +TEST(x86ild, test_8B7500) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov esi, dword ptr [rbp] + */ + EXPECT_EQ(3, ild(u"ïu ")); +} + +TEST(x86ild, test_8B6800) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov ebp, dword ptr [rax] + */ + EXPECT_EQ(3, ild(u"ïh ")); +} + +TEST(x86ild, test_8B4800) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov ecx, dword ptr [rax] + */ + EXPECT_EQ(3, ild(u"ïH ")); +} + +TEST(x86ild, test_8B4500) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov eax, dword ptr [rbp] + */ + EXPECT_EQ(3, ild(u"ïE ")); +} + +TEST(x86ild, test_8B3F) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov edi, dword ptr [rdi] + */ + EXPECT_EQ(2, ild(u"ï?")); +} + +TEST(x86ild, test_8B3C24) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov edi, dword ptr [rsp] + */ + EXPECT_EQ(3, ild(u"ï<$")); +} + +TEST(x86ild, test_8B348500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov esi, dword ptr [rax*4] + */ + EXPECT_EQ(7, ild(u"ï4à    ")); +} + +TEST(x86ild, test_8B28) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov ebp, dword ptr [rax] + */ + EXPECT_EQ(2, ild(u"ï(")); +} + +TEST(x86ild, test_8B1E) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov ebx, dword ptr [rsi] + */ + EXPECT_EQ(2, ild(u"ï▲")); +} + +TEST(x86ild, test_8B1500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov edx, dword ptr [rip] + */ + EXPECT_EQ(6, ild(u"易   ")); +} + +TEST(x86ild, test_8B14F500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov edx, dword ptr [rsi*8] + */ + EXPECT_EQ(7, ild(u"ï¶⌡    ")); +} + +TEST(x86ild, test_8B14C500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov edx, dword ptr [rax*8] + */ + EXPECT_EQ(7, ild(u"ï¶┼    ")); +} + +TEST(x86ild, test_8B0CCD00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov ecx, dword ptr [rcx*8] + */ + EXPECT_EQ(7, ild(u"ï♀═    ")); +} + +TEST(x86ild, test_8B0CBD00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov ecx, dword ptr [rdi*4] + */ + EXPECT_EQ(7, ild(u"ï♀╜    ")); +} + +TEST(x86ild, test_8B0CB500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov ecx, dword ptr [rsi*4] + */ + EXPECT_EQ(7, ild(u"ï♀╡    ")); +} + +TEST(x86ild, test_8B0C8D00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov ecx, dword ptr [rcx*4] + */ + EXPECT_EQ(7, ild(u"ï♀ì    ")); +} + +TEST(x86ild, test_8B06) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov eax, dword ptr [rsi] + */ + EXPECT_EQ(2, ild(u"ï♠")); +} + +TEST(x86ild, test_8B04B500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov eax, dword ptr [rsi*4] + */ + EXPECT_EQ(7, ild(u"ï♦╡    ")); +} + +TEST(x86ild, test_8A9200000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov dl, byte ptr [rdx] + */ + EXPECT_EQ(6, ild(u"èÆ    ")); +} + +TEST(x86ild, test_8A8900000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov cl, byte ptr [rcx] + */ + EXPECT_EQ(6, ild(u"èë    ")); +} + +TEST(x86ild, test_8A841000000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov al, byte ptr [rax+rdx*1] + */ + EXPECT_EQ(7, ild(u"èä►    ")); +} + +TEST(x86ild, test_8A542400) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov dl, byte ptr [rsp] + */ + EXPECT_EQ(4, ild(u"èT$ ")); +} + +TEST(x86ild, test_8A4E00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov cl, byte ptr [rsi] + */ + EXPECT_EQ(3, ild(u"èN ")); +} + +TEST(x86ild, test_8A4D00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov cl, byte ptr [rbp] + */ + EXPECT_EQ(3, ild(u"èM ")); +} + +TEST(x86ild, test_8A14F500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov dl, byte ptr [rsi*8] + */ + EXPECT_EQ(7, ild(u"è¶⌡    ")); +} + +TEST(x86ild, test_8A149500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov dl, byte ptr [rdx*4] + */ + EXPECT_EQ(7, ild(u"è¶ò    ")); +} + +TEST(x86ild, test_8A1411) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov dl, byte ptr [rcx+rdx*1] + */ + EXPECT_EQ(3, ild(u"è¶◄")); +} + +TEST(x86ild, test_8A140A) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov dl, byte ptr [rdx+rcx*1] + */ + EXPECT_EQ(3, ild(u"è¶◙")); +} + +TEST(x86ild, test_8A1402) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov dl, byte ptr [rdx+rax*1] + */ + EXPECT_EQ(3, ild(u"è¶☻")); +} + +TEST(x86ild, test_8A0C01) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov cl, byte ptr [rcx+rax*1] + */ + EXPECT_EQ(3, ild(u"è♀☺")); +} + +TEST(x86ild, test_8A06) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov al, byte ptr [rsi] + */ + EXPECT_EQ(2, ild(u"è♠")); +} + +TEST(x86ild, test_8A0406) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov al, byte ptr [rsi+rax*1] + */ + EXPECT_EQ(3, ild(u"è♦♠")); +} + +TEST(x86ild, test_8A0402) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov al, byte ptr [rdx+rax*1] + */ + EXPECT_EQ(3, ild(u"è♦☻")); +} + +TEST(x86ild, test_89F9) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov ecx, edi + */ + EXPECT_EQ(2, ild(u"ë∙")); +} + +TEST(x86ild, test_89F3) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov ebx, esi + */ + EXPECT_EQ(2, ild(u"ë≤")); +} + +TEST(x86ild, test_89E9) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov ecx, ebp + */ + EXPECT_EQ(2, ild(u"ëΘ")); +} + +TEST(x86ild, test_89D7) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov edi, edx + */ + EXPECT_EQ(2, ild(u"ë╫")); +} + +TEST(x86ild, test_89D3) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov ebx, edx + */ + EXPECT_EQ(2, ild(u"ë╙")); +} + +TEST(x86ild, test_89AA00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rdx], ebp + */ + EXPECT_EQ(6, ild(u"묠   ")); +} + +TEST(x86ild, test_89942400000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rsp], edx + */ + EXPECT_EQ(7, ild(u"ëö$    ")); +} + +TEST(x86ild, test_898200000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rdx], eax + */ + EXPECT_EQ(6, ild(u"ëé    ")); +} + +TEST(x86ild, test_897C2400) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rsp], edi + */ + EXPECT_EQ(4, ild(u"ë|$ ")); +} + +TEST(x86ild, test_895D00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rbp], ebx + */ + EXPECT_EQ(3, ild(u"ë] ")); +} + +TEST(x86ild, test_895800) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rax], ebx + */ + EXPECT_EQ(3, ild(u"ëX ")); +} + +TEST(x86ild, test_894C2400) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rsp], ecx + */ + EXPECT_EQ(4, ild(u"ëL$ ")); +} + +TEST(x86ild, test_894500) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rbp], eax + */ + EXPECT_EQ(3, ild(u"ëE ")); +} + +TEST(x86ild, test_893D00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rip], edi + */ + EXPECT_EQ(6, ild(u"ë=    ")); +} + +TEST(x86ild, test_891500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rip], edx + */ + EXPECT_EQ(6, ild(u"맠   ")); +} + +TEST(x86ild, test_8903) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rbx], eax + */ + EXPECT_EQ(2, ild(u"ë♥")); +} + +TEST(x86ild, test_88540700) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rdi+rax*1], dl + */ + EXPECT_EQ(4, ild(u"êT• ")); +} + +TEST(x86ild, test_88540300) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rbx+rax*1], dl + */ + EXPECT_EQ(4, ild(u"êT♥ ")); +} + +TEST(x86ild, test_885100) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rcx], dl + */ + EXPECT_EQ(3, ild(u"êQ ")); +} + +TEST(x86ild, test_88442400) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rsp], al + */ + EXPECT_EQ(4, ild(u"êD$ ")); +} + +TEST(x86ild, test_88441400) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rsp+rdx*1], al + */ + EXPECT_EQ(4, ild(u"êD¶ ")); +} + +TEST(x86ild, test_880C33) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rbx+rsi*1], cl + */ + EXPECT_EQ(3, ild(u"ê♀3")); +} + +TEST(x86ild, test_66C782000000000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_IMMz + ISA_SET: I86 + SHORT: mov word ptr [rdx], 0x0 + */ + EXPECT_EQ(9, ild(u"f╟é      ")); +} + +TEST(x86ild, test_6690) { + /* + ICLASS: NOP + CATEGORY: NOP + EXTENSION: BASE + IFORM: NOP_90 + ISA_SET: I86 + SHORT: data16 nop + */ + EXPECT_EQ(2, ild(u"fÉ")); +} + +TEST(x86ild, test_668B940000000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov dx, word ptr [rax+rax*1] + */ + EXPECT_EQ(8, ild(u"fïö     ")); +} + +TEST(x86ild, test_668B4B00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov cx, word ptr [rbx] + */ + EXPECT_EQ(4, ild(u"fïK ")); +} + +TEST(x86ild, test_66897B00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov word ptr [rbx], di + */ + EXPECT_EQ(4, ild(u"fë{ ")); +} + +TEST(x86ild, test_66894B00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov word ptr [rbx], cx + */ + EXPECT_EQ(4, ild(u"fëK ")); +} + +TEST(x86ild, test_668902) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov word ptr [rdx], ax + */ + EXPECT_EQ(3, ild(u"fë☻")); +} + +TEST(x86ild, test_66448B7500) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r14w, word ptr [rbp] + */ + EXPECT_EQ(5, ild(u"fDïu ")); +} + +TEST(x86ild, test_66448B6F00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r13w, word ptr [rdi] + */ + EXPECT_EQ(5, ild(u"fDïo ")); +} + +TEST(x86ild, test_66448B4700) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r8w, word ptr [rdi] + */ + EXPECT_EQ(5, ild(u"fDïG ")); +} + +TEST(x86ild, test_4D89C3) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r11, r8 + */ + EXPECT_EQ(3, ild(u"Më├")); +} + +TEST(x86ild, test_4D63EE) { + /* + ICLASS: MOVSXD + CATEGORY: DATAXFER + EXTENSION: LONGMODE + IFORM: MOVSXD_GPRv_GPR32 + ISA_SET: LONGMODE + SHORT: movsxd r13, r14d + */ + EXPECT_EQ(3, ild(u"Mcε")); +} + +TEST(x86ild, test_4D63C0) { + /* + ICLASS: MOVSXD + CATEGORY: DATAXFER + EXTENSION: LONGMODE + IFORM: MOVSXD_GPRv_GPR32 + ISA_SET: LONGMODE + SHORT: movsxd r8, r8d + */ + EXPECT_EQ(3, ild(u"Mc└")); +} + +TEST(x86ild, test_4C8B7700) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r14, qword ptr [rdi] + */ + EXPECT_EQ(4, ild(u"Lïw ")); +} + +TEST(x86ild, test_4C8B6F00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r13, qword ptr [rdi] + */ + EXPECT_EQ(4, ild(u"Lïo ")); +} + +TEST(x86ild, test_4C8B6D00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r13, qword ptr [rbp] + */ + EXPECT_EQ(4, ild(u"Lïm ")); +} + +TEST(x86ild, test_4C8B6700) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r12, qword ptr [rdi] + */ + EXPECT_EQ(4, ild(u"Lïg ")); +} + +TEST(x86ild, test_4C8B6300) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r12, qword ptr [rbx] + */ + EXPECT_EQ(4, ild(u"Lïc ")); +} + +TEST(x86ild, test_4C8B4700) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r8, qword ptr [rdi] + */ + EXPECT_EQ(4, ild(u"LïG ")); +} + +TEST(x86ild, test_4C8B4300) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r8, qword ptr [rbx] + */ + EXPECT_EQ(4, ild(u"LïC ")); +} + +TEST(x86ild, test_4C8B2500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r12, qword ptr [rip] + */ + EXPECT_EQ(7, ild(u"Lï%    ")); +} + +TEST(x86ild, test_4C89EE) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rsi, r13 + */ + EXPECT_EQ(3, ild(u"Lëε")); +} + +TEST(x86ild, test_4C89EA) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rdx, r13 + */ + EXPECT_EQ(3, ild(u"LëΩ")); +} + +TEST(x86ild, test_4C89CE) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rsi, r9 + */ + EXPECT_EQ(3, ild(u"Lë╬")); +} + +TEST(x86ild, test_4C89C6) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rsi, r8 + */ + EXPECT_EQ(3, ild(u"Lë╞")); +} + +TEST(x86ild, test_4C897300) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rbx], r14 + */ + EXPECT_EQ(4, ild(u"Lës ")); +} + +TEST(x86ild, test_4C896300) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rbx], r12 + */ + EXPECT_EQ(4, ild(u"Lëc ")); +} + +TEST(x86ild, test_4A8B34C500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rsi, qword ptr [r8*8] + */ + EXPECT_EQ(8, ild(u"Jï4┼    ")); +} + +TEST(x86ild, test_49D3ED) { + /* + ICLASS: SHR + CATEGORY: SHIFT + EXTENSION: BASE + IFORM: SHR_GPRv_CL + ISA_SET: I86 + SHORT: shr r13, cl + */ + EXPECT_EQ(3, ild(u"I╙φ")); +} + +TEST(x86ild, test_4989FD) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r13, rdi + */ + EXPECT_EQ(3, ild(u"Ië²")); +} + +TEST(x86ild, test_4989F6) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r14, rsi + */ + EXPECT_EQ(3, ild(u"Ië÷")); +} + +TEST(x86ild, test_4989F5) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r13, rsi + */ + EXPECT_EQ(3, ild(u"Ië⌡")); +} + +TEST(x86ild, test_4989F2) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r10, rsi + */ + EXPECT_EQ(3, ild(u"Ië≥")); +} + +TEST(x86ild, test_4989E4) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r12, rsp + */ + EXPECT_EQ(3, ild(u"IëΣ")); +} + +TEST(x86ild, test_4989D0) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r8, rdx + */ + EXPECT_EQ(3, ild(u"Ië╨")); +} + +TEST(x86ild, test_4989CD) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r13, rcx + */ + EXPECT_EQ(3, ild(u"Ië═")); +} + +TEST(x86ild, test_4989C5) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r13, rax + */ + EXPECT_EQ(3, ild(u"Ië┼")); +} + +TEST(x86ild, test_4963F7) { + /* + ICLASS: MOVSXD + CATEGORY: DATAXFER + EXTENSION: LONGMODE + IFORM: MOVSXD_GPRv_GPR32 + ISA_SET: LONGMODE + SHORT: movsxd rsi, r15d + */ + EXPECT_EQ(3, ild(u"Ic≈")); +} + +TEST(x86ild, test_48D3C0) { + /* + ICLASS: ROL + CATEGORY: ROTATE + EXTENSION: BASE + IFORM: ROL_GPRv_CL + ISA_SET: I86 + SHORT: rol rax, cl + */ + EXPECT_EQ(3, ild(u"H╙└")); +} + +TEST(x86ild, test_48C7C000000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_IMMz + ISA_SET: I86 + SHORT: mov rax, 0x0 + */ + EXPECT_EQ(7, ild(u"H╟└    ")); +} + +TEST(x86ild, test_48C70300000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_IMMz + ISA_SET: I86 + SHORT: mov qword ptr [rbx], 0x0 + */ + EXPECT_EQ(7, ild(u"H╟♥    ")); +} + +TEST(x86ild, test_488B9000000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rdx, qword ptr [rax] + */ + EXPECT_EQ(7, ild(u"HïÉ    ")); +} + +TEST(x86ild, test_488B8600000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rax, qword ptr [rsi] + */ + EXPECT_EQ(7, ild(u"Hïå    ")); +} + +TEST(x86ild, test_488B7B00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rdi, qword ptr [rbx] + */ + EXPECT_EQ(4, ild(u"Hï{ ")); +} + +TEST(x86ild, test_488B7300) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rsi, qword ptr [rbx] + */ + EXPECT_EQ(4, ild(u"Hïs ")); +} + +TEST(x86ild, test_488B4500) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rax, qword ptr [rbp] + */ + EXPECT_EQ(4, ild(u"HïE ")); +} + +TEST(x86ild, test_488B4100) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rax, qword ptr [rcx] + */ + EXPECT_EQ(4, ild(u"HïA ")); +} + +TEST(x86ild, test_488B37) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rsi, qword ptr [rdi] + */ + EXPECT_EQ(3, ild(u"Hï7")); +} + +TEST(x86ild, test_488B3424) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rsi, qword ptr [rsp] + */ + EXPECT_EQ(4, ild(u"Hï4$")); +} + +TEST(x86ild, test_488B33) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rsi, qword ptr [rbx] + */ + EXPECT_EQ(3, ild(u"Hï3")); +} + +TEST(x86ild, test_488B1D00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rbx, qword ptr [rip] + */ + EXPECT_EQ(7, ild(u"Hï↔    ")); +} + +TEST(x86ild, test_488B14CA) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rdx, qword ptr [rdx+rcx*8] + */ + EXPECT_EQ(4, ild(u"Hï¶╩")); +} + +TEST(x86ild, test_488B13) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rdx, qword ptr [rbx] + */ + EXPECT_EQ(3, ild(u"Hï‼")); +} + +TEST(x86ild, test_488B12) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rdx, qword ptr [rdx] + */ + EXPECT_EQ(3, ild(u"Hï↕")); +} + +TEST(x86ild, test_488B0D00000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rcx, qword ptr [rip] + */ + EXPECT_EQ(7, ild(u"Hï♪    ")); +} + +TEST(x86ild, test_488B07) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rax, qword ptr [rdi] + */ + EXPECT_EQ(3, ild(u"Hï•")); +} + +TEST(x86ild, test_488B04F500000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rax, qword ptr [rsi*8] + */ + EXPECT_EQ(8, ild(u"Hï♦⌡    ")); +} + +TEST(x86ild, test_488B03) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rax, qword ptr [rbx] + */ + EXPECT_EQ(3, ild(u"Hï♥")); +} + +TEST(x86ild, test_488B00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov rax, qword ptr [rax] + */ + EXPECT_EQ(3, ild(u"Hï ")); +} + +TEST(x86ild, test_4889FA) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rdx, rdi + */ + EXPECT_EQ(3, ild(u"Hë·")); +} + +TEST(x86ild, test_4889F8) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rax, rdi + */ + EXPECT_EQ(3, ild(u"Hë°")); +} + +TEST(x86ild, test_4889F1) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rcx, rsi + */ + EXPECT_EQ(3, ild(u"Hë±")); +} + +TEST(x86ild, test_4889D6) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rsi, rdx + */ + EXPECT_EQ(3, ild(u"Hë╓")); +} + +TEST(x86ild, test_4889D3) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rbx, rdx + */ + EXPECT_EQ(3, ild(u"Hë╙")); +} + +TEST(x86ild, test_4889CA) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rdx, rcx + */ + EXPECT_EQ(3, ild(u"Hë╩")); +} + +TEST(x86ild, test_4889C5) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov rbp, rax + */ + EXPECT_EQ(3, ild(u"Hë┼")); +} + +TEST(x86ild, test_48895300) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rbx], rdx + */ + EXPECT_EQ(4, ild(u"HëS ")); +} + +TEST(x86ild, test_48893424) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rsp], rsi + */ + EXPECT_EQ(4, ild(u"Hë4$")); +} + +TEST(x86ild, test_488917) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rdi], rdx + */ + EXPECT_EQ(3, ild(u"Hë↨")); +} + +TEST(x86ild, test_488913) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rbx], rdx + */ + EXPECT_EQ(3, ild(u"Hë‼")); +} + +TEST(x86ild, test_488907) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rdi], rax + */ + EXPECT_EQ(3, ild(u"Hë•")); +} + +TEST(x86ild, test_488906) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov qword ptr [rsi], rax + */ + EXPECT_EQ(3, ild(u"Hë♠")); +} + +TEST(x86ild, test_4863F8) { + /* + ICLASS: MOVSXD + CATEGORY: DATAXFER + EXTENSION: LONGMODE + IFORM: MOVSXD_GPRv_GPR32 + ISA_SET: LONGMODE + SHORT: movsxd rdi, eax + */ + EXPECT_EQ(3, ild(u"Hc°")); +} + +TEST(x86ild, test_4863D0) { + /* + ICLASS: MOVSXD + CATEGORY: DATAXFER + EXTENSION: LONGMODE + IFORM: MOVSXD_GPRv_GPR32 + ISA_SET: LONGMODE + SHORT: movsxd rdx, eax + */ + EXPECT_EQ(3, ild(u"Hc╨")); +} + +TEST(x86ild, test_4863C8) { + /* + ICLASS: MOVSXD + CATEGORY: DATAXFER + EXTENSION: LONGMODE + IFORM: MOVSXD_GPRv_GPR32 + ISA_SET: LONGMODE + SHORT: movsxd rcx, eax + */ + EXPECT_EQ(3, ild(u"Hc╚")); +} + +TEST(x86ild, test_4863C7) { + /* + ICLASS: MOVSXD + CATEGORY: DATAXFER + EXTENSION: LONGMODE + IFORM: MOVSXD_GPRv_GPR32 + ISA_SET: LONGMODE + SHORT: movsxd rax, edi + */ + EXPECT_EQ(3, ild(u"Hc╟")); +} + +TEST(x86ild, test_486312) { + /* + ICLASS: MOVSXD + CATEGORY: DATAXFER + EXTENSION: LONGMODE + IFORM: MOVSXD_GPRv_MEMd + ISA_SET: LONGMODE + SHORT: movsxd rdx, dword ptr [rdx] + */ + EXPECT_EQ(3, ild(u"Hc↕")); +} + +TEST(x86ild, test_47882C0E) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [r14+r9*1], r13b + */ + EXPECT_EQ(4, ild(u"Gê,♫")); +} + +TEST(x86ild, test_458B9000000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r10d, dword ptr [r8] + */ + EXPECT_EQ(7, ild(u"EïÉ    ")); +} + +TEST(x86ild, test_4589D8) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r8d, r11d + */ + EXPECT_EQ(3, ild(u"Eë╪")); +} + +TEST(x86ild, test_4589CB) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r11d, r9d + */ + EXPECT_EQ(3, ild(u"Eë╦")); +} + +TEST(x86ild, test_4589C1) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r9d, r8d + */ + EXPECT_EQ(3, ild(u"Eë┴")); +} + +TEST(x86ild, test_4588F8) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov r8b, r15b + */ + EXPECT_EQ(3, ild(u"Eê°")); +} + +TEST(x86ild, test_4588D3) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov r11b, r10b + */ + EXPECT_EQ(3, ild(u"Eê╙")); +} + +TEST(x86ild, test_4588D1) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov r9b, r10b + */ + EXPECT_EQ(3, ild(u"Eê╤")); +} + +TEST(x86ild, test_4588C7) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov r15b, r8b + */ + EXPECT_EQ(3, ild(u"Eê╟")); +} + +TEST(x86ild, test_45883424) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [r12], r14b + */ + EXPECT_EQ(4, ild(u"Eê4$")); +} + +TEST(x86ild, test_448B8100000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r8d, dword ptr [rcx] + */ + EXPECT_EQ(7, ild(u"Dïü    ")); +} + +TEST(x86ild, test_448B7D00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r15d, dword ptr [rbp] + */ + EXPECT_EQ(4, ild(u"Dï} ")); +} + +TEST(x86ild, test_448B7700) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r14d, dword ptr [rdi] + */ + EXPECT_EQ(4, ild(u"Dïw ")); +} + +TEST(x86ild, test_448B6700) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r12d, dword ptr [rdi] + */ + EXPECT_EQ(4, ild(u"Dïg ")); +} + +TEST(x86ild, test_448B4700) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r8d, dword ptr [rdi] + */ + EXPECT_EQ(4, ild(u"DïG ")); +} + +TEST(x86ild, test_448B4500) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r8d, dword ptr [rbp] + */ + EXPECT_EQ(4, ild(u"DïE ")); +} + +TEST(x86ild, test_448B0424) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov r8d, dword ptr [rsp] + */ + EXPECT_EQ(4, ild(u"Dï♦$")); +} + +TEST(x86ild, test_448A5300) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov r10b, byte ptr [rbx] + */ + EXPECT_EQ(4, ild(u"DèS ")); +} + +TEST(x86ild, test_448A4E00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov r9b, byte ptr [rsi] + */ + EXPECT_EQ(4, ild(u"DèN ")); +} + +TEST(x86ild, test_448A4600) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov r8b, byte ptr [rsi] + */ + EXPECT_EQ(4, ild(u"DèF ")); +} + +TEST(x86ild, test_448A01) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov r8b, byte ptr [rcx] + */ + EXPECT_EQ(3, ild(u"Dè☺")); +} + +TEST(x86ild, test_4489E9) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov ecx, r13d + */ + EXPECT_EQ(3, ild(u"DëΘ")); +} + +TEST(x86ild, test_4489E8) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov eax, r13d + */ + EXPECT_EQ(3, ild(u"DëΦ")); +} + +TEST(x86ild, test_4489CE) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov esi, r9d + */ + EXPECT_EQ(3, ild(u"Dë╬")); +} + +TEST(x86ild, test_4489C8) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov eax, r9d + */ + EXPECT_EQ(3, ild(u"Dë╚")); +} + +TEST(x86ild, test_4489C2) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov edx, r8d + */ + EXPECT_EQ(3, ild(u"Dë┬")); +} + +TEST(x86ild, test_4489C0) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov eax, r8d + */ + EXPECT_EQ(3, ild(u"Dë└")); +} + +TEST(x86ild, test_44898900000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [rcx], r9d + */ + EXPECT_EQ(7, ild(u"Dëë    ")); +} + +TEST(x86ild, test_4488E8) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov al, r13b + */ + EXPECT_EQ(3, ild(u"DêΦ")); +} + +TEST(x86ild, test_4488D9) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov cl, r11b + */ + EXPECT_EQ(3, ild(u"Dê┘")); +} + +TEST(x86ild, test_4488CE) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov sil, r9b + */ + EXPECT_EQ(3, ild(u"Dê╬")); +} + +TEST(x86ild, test_4488CD) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov bpl, r9b + */ + EXPECT_EQ(3, ild(u"Dê═")); +} + +TEST(x86ild, test_4488C5) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov bpl, r8b + */ + EXPECT_EQ(3, ild(u"Dê┼")); +} + +TEST(x86ild, test_44886300) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rbx], r12b + */ + EXPECT_EQ(4, ild(u"Dêc ")); +} + +TEST(x86ild, test_44884B00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rbx], r9b + */ + EXPECT_EQ(4, ild(u"DêK ")); +} + +TEST(x86ild, test_44884700) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rdi], r8b + */ + EXPECT_EQ(4, ild(u"DêG ")); +} + +TEST(x86ild, test_44884000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rax], r8b + */ + EXPECT_EQ(4, ild(u"Dê@ ")); +} + +TEST(x86ild, test_43C6040E00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_IMMb + ISA_SET: I86 + SHORT: mov byte ptr [r14+r9*1], 0x0 + */ + EXPECT_EQ(5, ild(u"C╞♦♫ ")); +} + +TEST(x86ild, test_428A1400) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov dl, byte ptr [rax+r8*1] + */ + EXPECT_EQ(4, ild(u"B趠")); +} + +TEST(x86ild, test_428A0406) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov al, byte ptr [rsi+r8*1] + */ + EXPECT_EQ(4, ild(u"Bè♦♠")); +} + +TEST(x86ild, test_42880407) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rdi+r8*1], al + */ + EXPECT_EQ(4, ild(u"Bê♦•")); +} + +TEST(x86ild, test_41FF14C4) { + /* + ICLASS: CALL_NEAR + CATEGORY: CALL + EXTENSION: BASE + IFORM: CALL_NEAR_MEMv + ISA_SET: I86 + SHORT: call qword ptr [r12+rax*8] + */ + EXPECT_EQ(4, ild(u"Aλ¶─")); +} + +TEST(x86ild, test_41C6042400) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_IMMb + ISA_SET: I86 + SHORT: mov byte ptr [r12], 0x0 + */ + EXPECT_EQ(5, ild(u"A╞♦$ ")); +} + +TEST(x86ild, test_41C60000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_IMMb + ISA_SET: I86 + SHORT: mov byte ptr [r8], 0x0 + */ + EXPECT_EQ(4, ild(u"A╞  ")); +} + +TEST(x86ild, test_418B8000000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_MEMv + ISA_SET: I86 + SHORT: mov eax, dword ptr [r8] + */ + EXPECT_EQ(7, ild(u"AïÇ    ")); +} + +TEST(x86ild, test_418A0C14) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov cl, byte ptr [r12+rdx*1] + */ + EXPECT_EQ(4, ild(u"Aè♀¶")); +} + +TEST(x86ild, test_418A0404) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov al, byte ptr [r12+rax*1] + */ + EXPECT_EQ(4, ild(u"Aè♦♦")); +} + +TEST(x86ild, test_418A0400) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov al, byte ptr [r8+rax*1] + */ + EXPECT_EQ(4, ild(u"Aè♦ ")); +} + +TEST(x86ild, test_4189FB) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r11d, edi + */ + EXPECT_EQ(3, ild(u"Aë√")); +} + +TEST(x86ild, test_4189F8) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r8d, edi + */ + EXPECT_EQ(3, ild(u"Aë°")); +} + +TEST(x86ild, test_4189DC) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r12d, ebx + */ + EXPECT_EQ(3, ild(u"Aë▄")); +} + +TEST(x86ild, test_4189D7) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r15d, edx + */ + EXPECT_EQ(3, ild(u"Aë╫")); +} + +TEST(x86ild, test_4189D6) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r14d, edx + */ + EXPECT_EQ(3, ild(u"Aë╓")); +} + +TEST(x86ild, test_4189D3) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r11d, edx + */ + EXPECT_EQ(3, ild(u"Aë╙")); +} + +TEST(x86ild, test_4189CB) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r11d, ecx + */ + EXPECT_EQ(3, ild(u"Aë╦")); +} + +TEST(x86ild, test_4189C1) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r9d, eax + */ + EXPECT_EQ(3, ild(u"Aë┴")); +} + +TEST(x86ild, test_4189C0) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPRv_GPRv_89 + ISA_SET: I86 + SHORT: mov r8d, eax + */ + EXPECT_EQ(3, ild(u"Aë└")); +} + +TEST(x86ild, test_41898800000000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMv_GPRv + ISA_SET: I86 + SHORT: mov dword ptr [r8], ecx + */ + EXPECT_EQ(7, ild(u"Aëê    ")); +} + +TEST(x86ild, test_4188F2) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov r10b, sil + */ + EXPECT_EQ(3, ild(u"Aê≥")); +} + +TEST(x86ild, test_4188CE) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov r14b, cl + */ + EXPECT_EQ(3, ild(u"Aê╬")); +} + +TEST(x86ild, test_4188C3) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov r11b, al + */ + EXPECT_EQ(3, ild(u"Aê├")); +} + +TEST(x86ild, test_4188C2) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov r10b, al + */ + EXPECT_EQ(3, ild(u"Aê┬")); +} + +TEST(x86ild, test_4188C1) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov r9b, al + */ + EXPECT_EQ(3, ild(u"Aê┴")); +} + +TEST(x86ild, test_41885000) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [r8], dl + */ + EXPECT_EQ(4, ild(u"AêP ")); +} + +TEST(x86ild, test_4158) { + /* + ICLASS: POP + CATEGORY: POP + EXTENSION: BASE + IFORM: POP_GPRv_58 + ISA_SET: I86 + SHORT: pop r8 + */ + EXPECT_EQ(2, ild(u"AX")); +} + +TEST(x86ild, test_40B600) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_IMMb_B0 + ISA_SET: I86 + SHORT: mov sil, 0x0 + */ + EXPECT_EQ(3, ild(u"@╢ ")); +} + +TEST(x86ild, test_408A7D00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_MEMb + ISA_SET: I86 + SHORT: mov dil, byte ptr [rbp] + */ + EXPECT_EQ(4, ild(u"@è} ")); +} + +TEST(x86ild, test_4088FA) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov dl, dil + */ + EXPECT_EQ(3, ild(u"@ê·")); +} + +TEST(x86ild, test_4088EA) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov dl, bpl + */ + EXPECT_EQ(3, ild(u"@êΩ")); +} + +TEST(x86ild, test_4088D7) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov dil, dl + */ + EXPECT_EQ(3, ild(u"@ê╫")); +} + +TEST(x86ild, test_4088C5) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_GPR8_GPR8_88 + ISA_SET: I86 + SHORT: mov bpl, al + */ + EXPECT_EQ(3, ild(u"@ê┼")); +} + +TEST(x86ild, test_40887B00) { + /* + ICLASS: MOV + CATEGORY: DATAXFER + EXTENSION: BASE + IFORM: MOV_MEMb_GPR8 + ISA_SET: I86 + SHORT: mov byte ptr [rbx], dil + */ + EXPECT_EQ(4, ild(u"@ê{ ")); +} + +TEST(x86ild, test_0F8E00000000) { + /* + ICLASS: JLE + CATEGORY: COND_BR + EXTENSION: BASE + IFORM: JLE_RELBRd + ISA_SET: I86 + SHORT: jle 0x6 + */ + EXPECT_EQ(6, ild(u"☼Ä    ")); +} + +TEST(x86ild, test_0F8800000000) { + /* + ICLASS: JS + CATEGORY: COND_BR + EXTENSION: BASE + IFORM: JS_RELBRd + ISA_SET: I86 + SHORT: js 0x6 + */ + EXPECT_EQ(6, ild(u"☼ê    ")); +} + +TEST(x86ild, test_0F8200000000) { + /* + ICLASS: JB + CATEGORY: COND_BR + EXTENSION: BASE + IFORM: JB_RELBRd + ISA_SET: I86 + SHORT: jb 0x6 + */ + EXPECT_EQ(6, ild(u"☼é    ")); +} diff --git a/test/libc/xed/x86ild_popular_i86_test.c b/test/libc/xed/x86ild_popular_i86_test.c index 12dab6cbb..6224182a0 100644 --- a/test/libc/xed/x86ild_popular_i86_test.c +++ b/test/libc/xed/x86ild_popular_i86_test.c @@ -3703,3723 +3703,3 @@ TEST(x86ild, test_4489FF) { */ EXPECT_EQ(3, ild(u"Dëλ")); } - -TEST(x86ild, test_4489E7) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov edi, r12d - */ - EXPECT_EQ(3, ild(u"Dëτ")); -} - -TEST(x86ild, test_4489E0) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov eax, r12d - */ - EXPECT_EQ(3, ild(u"Dëα")); -} - -TEST(x86ild, test_4489D9) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov ecx, r11d - */ - EXPECT_EQ(3, ild(u"Dë┘")); -} - -TEST(x86ild, test_4488D0) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov al, r10b - */ - EXPECT_EQ(3, ild(u"Dê╨")); -} - -TEST(x86ild, test_41BE00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_IMMv - ISA_SET: I86 - SHORT: mov r14d, 0x0 - */ - EXPECT_EQ(6, ild(u"A╛    ")); -} - -TEST(x86ild, test_41B900000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_IMMv - ISA_SET: I86 - SHORT: mov r9d, 0x0 - */ - EXPECT_EQ(6, ild(u"A╣    ")); -} - -TEST(x86ild, test_4189E8) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r8d, ebp - */ - EXPECT_EQ(3, ild(u"AëΦ")); -} - -TEST(x86ild, test_4189D0) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r8d, edx - */ - EXPECT_EQ(3, ild(u"Aë╨")); -} - -TEST(x86ild, test_4189C7) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r15d, eax - */ - EXPECT_EQ(3, ild(u"Aë╟")); -} - -TEST(x86ild, test_4188C0) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov r8b, al - */ - EXPECT_EQ(3, ild(u"Aê└")); -} - -TEST(x86ild, test_408A7B00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov dil, byte ptr [rbx] - */ - EXPECT_EQ(4, ild(u"@è{ ")); -} - -TEST(x86ild, test_4088F1) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov cl, sil - */ - EXPECT_EQ(3, ild(u"@ê±")); -} - -TEST(x86ild, test_4088D6) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov sil, dl - */ - EXPECT_EQ(3, ild(u"@ê╓")); -} - -TEST(x86ild, test_4088C7) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov dil, al - */ - EXPECT_EQ(3, ild(u"@ê╟")); -} - -TEST(x86ild, test_0F8300000000) { - /* - ICLASS: JNB - CATEGORY: COND_BR - EXTENSION: BASE - IFORM: JNB_RELBRd - ISA_SET: I86 - SHORT: jnb 0x6 - */ - EXPECT_EQ(6, ild(u"☼â    ")); -} - -TEST(x86ild, test_FF24F500000000) { - /* - ICLASS: JMP - CATEGORY: UNCOND_BR - EXTENSION: BASE - IFORM: JMP_MEMv - ISA_SET: I86 - SHORT: jmp qword ptr [rsi*8] - */ - EXPECT_EQ(7, ild(u"λ$⌡    ")); -} - -TEST(x86ild, test_E80083E200) { - /* - ICLASS: CALL_NEAR - CATEGORY: CALL - EXTENSION: BASE - IFORM: CALL_NEAR_RELBRd - ISA_SET: I86 - SHORT: call 0xe28305 - */ - EXPECT_EQ(5, ild(u"Φ âΓ ")); -} - -TEST(x86ild, test_D3E7) { - /* - ICLASS: SHL - CATEGORY: SHIFT - EXTENSION: BASE - IFORM: SHL_GPRv_CL_D3r4 - ISA_SET: I86 - SHORT: shl edi, cl - */ - EXPECT_EQ(2, ild(u"╙τ")); -} - -TEST(x86ild, test_D2C1) { - /* - ICLASS: ROL - CATEGORY: ROTATE - EXTENSION: BASE - IFORM: ROL_GPR8_CL - ISA_SET: I86 - SHORT: rol cl, cl - */ - EXPECT_EQ(2, ild(u"╥┴")); -} - -TEST(x86ild, test_C784240000000000000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_IMMz - ISA_SET: I86 - SHORT: mov dword ptr [rsp], 0x0 - */ - EXPECT_EQ(11, ild(u"╟ä$        ")); -} - -TEST(x86ild, test_C744240000000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_IMMz - ISA_SET: I86 - SHORT: mov dword ptr [rsp], 0x0 - */ - EXPECT_EQ(8, ild(u"╟D$     ")); -} - -TEST(x86ild, test_C6840E0000000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_IMMb - ISA_SET: I86 - SHORT: mov byte ptr [rsi+rcx*1], 0x0 - */ - EXPECT_EQ(8, ild(u"╞ä♫     ")); -} - -TEST(x86ild, test_C6420000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_IMMb - ISA_SET: I86 - SHORT: mov byte ptr [rdx], 0x0 - */ - EXPECT_EQ(4, ild(u"╞B  ")); -} - -TEST(x86ild, test_C60700) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_IMMb - ISA_SET: I86 - SHORT: mov byte ptr [rdi], 0x0 - */ - EXPECT_EQ(3, ild(u"╞• ")); -} - -TEST(x86ild, test_C60600) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_IMMb - ISA_SET: I86 - SHORT: mov byte ptr [rsi], 0x0 - */ - EXPECT_EQ(3, ild(u"╞♠ ")); -} - -TEST(x86ild, test_8BB200000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov esi, dword ptr [rdx] - */ - EXPECT_EQ(6, ild(u"ï▓    ")); -} - -TEST(x86ild, test_8B7F00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov edi, dword ptr [rdi] - */ - EXPECT_EQ(3, ild(u"ï⌂ ")); -} - -TEST(x86ild, test_8B7C2400) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov edi, dword ptr [rsp] - */ - EXPECT_EQ(4, ild(u"ï|$ ")); -} - -TEST(x86ild, test_8B7B00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov edi, dword ptr [rbx] - */ - EXPECT_EQ(3, ild(u"ï{ ")); -} - -TEST(x86ild, test_8B5100) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov edx, dword ptr [rcx] - */ - EXPECT_EQ(3, ild(u"ïQ ")); -} - -TEST(x86ild, test_8B4C2400) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov ecx, dword ptr [rsp] - */ - EXPECT_EQ(4, ild(u"ïL$ ")); -} - -TEST(x86ild, test_8B16) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov edx, dword ptr [rsi] - */ - EXPECT_EQ(2, ild(u"ï▬")); -} - -TEST(x86ild, test_8B10) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov edx, dword ptr [rax] - */ - EXPECT_EQ(2, ild(u"ï►")); -} - -TEST(x86ild, test_8B0C8500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov ecx, dword ptr [rax*4] - */ - EXPECT_EQ(7, ild(u"ï♀à    ")); -} - -TEST(x86ild, test_8B04FD00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov eax, dword ptr [rdi*8] - */ - EXPECT_EQ(7, ild(u"ï♦²    ")); -} - -TEST(x86ild, test_8B04C500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov eax, dword ptr [rax*8] - */ - EXPECT_EQ(7, ild(u"ï♦┼    ")); -} - -TEST(x86ild, test_8B00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov eax, dword ptr [rax] - */ - EXPECT_EQ(2, ild(u"ï ")); -} - -TEST(x86ild, test_8A14C500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov dl, byte ptr [rax*8] - */ - EXPECT_EQ(7, ild(u"è¶┼    ")); -} - -TEST(x86ild, test_8A0401) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov al, byte ptr [rcx+rax*1] - */ - EXPECT_EQ(3, ild(u"è♦☺")); -} - -TEST(x86ild, test_89FB) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov ebx, edi - */ - EXPECT_EQ(2, ild(u"ë√")); -} - -TEST(x86ild, test_89DF) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov edi, ebx - */ - EXPECT_EQ(2, ild(u"ë▀")); -} - -TEST(x86ild, test_89D8) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov eax, ebx - */ - EXPECT_EQ(2, ild(u"ë╪")); -} - -TEST(x86ild, test_89CB) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov ebx, ecx - */ - EXPECT_EQ(2, ild(u"ë╦")); -} - -TEST(x86ild, test_899A00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rdx], ebx - */ - EXPECT_EQ(6, ild(u"ëÜ    ")); -} - -TEST(x86ild, test_894B00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rbx], ecx - */ - EXPECT_EQ(3, ild(u"ëK ")); -} - -TEST(x86ild, test_890424) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rsp], eax - */ - EXPECT_EQ(3, ild(u"ë♦$")); -} - -TEST(x86ild, test_8810) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rax], dl - */ - EXPECT_EQ(2, ild(u"ê►")); -} - -TEST(x86ild, test_66C747000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_IMMz - ISA_SET: I86 - SHORT: mov word ptr [rdi], 0x0 - */ - EXPECT_EQ(6, ild(u"f╟G   ")); -} - -TEST(x86ild, test_668910) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov word ptr [rax], dx - */ - EXPECT_EQ(3, ild(u"fë►")); -} - -TEST(x86ild, test_4D89EC) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r12, r13 - */ - EXPECT_EQ(3, ild(u"Më∞")); -} - -TEST(x86ild, test_4C89FA) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rdx, r15 - */ - EXPECT_EQ(3, ild(u"Lë·")); -} - -TEST(x86ild, test_4C89E6) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rsi, r12 - */ - EXPECT_EQ(3, ild(u"Lëμ")); -} - -TEST(x86ild, test_4C89D0) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rax, r10 - */ - EXPECT_EQ(3, ild(u"Lë╨")); -} - -TEST(x86ild, test_49D3E0) { - /* - ICLASS: SHL - CATEGORY: SHIFT - EXTENSION: BASE - IFORM: SHL_GPRv_CL_D3r4 - ISA_SET: I86 - SHORT: shl r8, cl - */ - EXPECT_EQ(3, ild(u"I╙α")); -} - -TEST(x86ild, test_4989F4) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r12, rsi - */ - EXPECT_EQ(3, ild(u"Ië⌠")); -} - -TEST(x86ild, test_4989C4) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r12, rax - */ - EXPECT_EQ(3, ild(u"Ië─")); -} - -TEST(x86ild, test_48BF0000000000000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_IMMv - ISA_SET: I86 - SHORT: mov rdi, 0x0 - */ - EXPECT_EQ(10, ild(u"H┐        ")); -} - -TEST(x86ild, test_488B9100000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rdx, qword ptr [rcx] - */ - EXPECT_EQ(7, ild(u"Hïæ    ")); -} - -TEST(x86ild, test_488B7700) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rsi, qword ptr [rdi] - */ - EXPECT_EQ(4, ild(u"Hïw ")); -} - -TEST(x86ild, test_488B5200) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rdx, qword ptr [rdx] - */ - EXPECT_EQ(4, ild(u"HïR ")); -} - -TEST(x86ild, test_488B04D0) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rax, qword ptr [rax+rdx*8] - */ - EXPECT_EQ(4, ild(u"Hï♦╨")); -} - -TEST(x86ild, test_4889E9) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rcx, rbp - */ - EXPECT_EQ(3, ild(u"HëΘ")); -} - -TEST(x86ild, test_4889E7) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rdi, rsp - */ - EXPECT_EQ(3, ild(u"Hëτ")); -} - -TEST(x86ild, test_4889D7) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rdi, rdx - */ - EXPECT_EQ(3, ild(u"Hë╫")); -} - -TEST(x86ild, test_4889CD) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rbp, rcx - */ - EXPECT_EQ(3, ild(u"Hë═")); -} - -TEST(x86ild, test_48895700) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rdi], rdx - */ - EXPECT_EQ(4, ild(u"HëW ")); -} - -TEST(x86ild, test_48894700) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rdi], rax - */ - EXPECT_EQ(4, ild(u"HëG ")); -} - -TEST(x86ild, test_48894300) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rbx], rax - */ - EXPECT_EQ(4, ild(u"HëC ")); -} - -TEST(x86ild, test_488910) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rax], rdx - */ - EXPECT_EQ(3, ild(u"Hë►")); -} - -TEST(x86ild, test_48890D00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rip], rcx - */ - EXPECT_EQ(7, ild(u"Hë♪    ")); -} - -TEST(x86ild, test_488902) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rdx], rax - */ - EXPECT_EQ(3, ild(u"Hë☻")); -} - -TEST(x86ild, test_4863D2) { - /* - ICLASS: MOVSXD - CATEGORY: DATAXFER - EXTENSION: LONGMODE - IFORM: MOVSXD_GPRv_GPR32 - ISA_SET: LONGMODE - SHORT: movsxd rdx, edx - */ - EXPECT_EQ(3, ild(u"Hc╥")); -} - -TEST(x86ild, test_4589CA) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r10d, r9d - */ - EXPECT_EQ(3, ild(u"Eë╩")); -} - -TEST(x86ild, test_4588C8) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov r8b, r9b - */ - EXPECT_EQ(3, ild(u"Eê╚")); -} - -TEST(x86ild, test_4489FA) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov edx, r15d - */ - EXPECT_EQ(3, ild(u"Dë·")); -} - -TEST(x86ild, test_4489F7) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov edi, r14d - */ - EXPECT_EQ(3, ild(u"Dë≈")); -} - -TEST(x86ild, test_4489EF) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov edi, r13d - */ - EXPECT_EQ(3, ild(u"Dë∩")); -} - -TEST(x86ild, test_4489E1) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov ecx, r12d - */ - EXPECT_EQ(3, ild(u"Dëß")); -} - -TEST(x86ild, test_4489442400) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rsp], r8d - */ - EXPECT_EQ(5, ild(u"DëD$ ")); -} - -TEST(x86ild, test_4488CA) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov dl, r9b - */ - EXPECT_EQ(3, ild(u"Dê╩")); -} - -TEST(x86ild, test_44885300) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rbx], r10b - */ - EXPECT_EQ(4, ild(u"DêS ")); -} - -TEST(x86ild, test_44884300) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rbx], r8b - */ - EXPECT_EQ(4, ild(u"DêC ")); -} - -TEST(x86ild, test_428B148D00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov edx, dword ptr [r9*4] - */ - EXPECT_EQ(8, ild(u"Bï¶ì    ")); -} - -TEST(x86ild, test_41D3E0) { - /* - ICLASS: SHL - CATEGORY: SHIFT - EXTENSION: BASE - IFORM: SHL_GPRv_CL_D3r4 - ISA_SET: I86 - SHORT: shl r8d, cl - */ - EXPECT_EQ(3, ild(u"A╙α")); -} - -TEST(x86ild, test_41BF00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_IMMv - ISA_SET: I86 - SHORT: mov r15d, 0x0 - */ - EXPECT_EQ(6, ild(u"A┐    ")); -} - -TEST(x86ild, test_41BD00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_IMMv - ISA_SET: I86 - SHORT: mov r13d, 0x0 - */ - EXPECT_EQ(6, ild(u"A╜    ")); -} - -TEST(x86ild, test_41B800000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_IMMv - ISA_SET: I86 - SHORT: mov r8d, 0x0 - */ - EXPECT_EQ(6, ild(u"A╕    ")); -} - -TEST(x86ild, test_4189F4) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r12d, esi - */ - EXPECT_EQ(3, ild(u"Aë⌠")); -} - -TEST(x86ild, test_4189F2) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r10d, esi - */ - EXPECT_EQ(3, ild(u"Aë≥")); -} - -TEST(x86ild, test_4189C8) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r8d, ecx - */ - EXPECT_EQ(3, ild(u"Aë╚")); -} - -TEST(x86ild, test_4088F9) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov cl, dil - */ - EXPECT_EQ(3, ild(u"@ê∙")); -} - -TEST(x86ild, test_4088F2) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov dl, sil - */ - EXPECT_EQ(3, ild(u"@ê≥")); -} - -TEST(x86ild, test_4088E8) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov al, bpl - */ - EXPECT_EQ(3, ild(u"@êΦ")); -} - -TEST(x86ild, test_0F8900000000) { - /* - ICLASS: JNS - CATEGORY: COND_BR - EXTENSION: BASE - IFORM: JNS_RELBRd - ISA_SET: I86 - SHORT: jns 0x6 - */ - EXPECT_EQ(6, ild(u"☼ë    ")); -} - -TEST(x86ild, test_0F8600000000) { - /* - ICLASS: JBE - CATEGORY: COND_BR - EXTENSION: BASE - IFORM: JBE_RELBRd - ISA_SET: I86 - SHORT: jbe 0x6 - */ - EXPECT_EQ(6, ild(u"☼å    ")); -} - -TEST(x86ild, test_FF14C500000000) { - /* - ICLASS: CALL_NEAR - CATEGORY: CALL - EXTENSION: BASE - IFORM: CALL_NEAR_MEMv - ISA_SET: I86 - SHORT: call qword ptr [rax*8] - */ - EXPECT_EQ(7, ild(u"λ¶┼    ")); -} - -TEST(x86ild, test_F3C3) { - /* - ICLASS: RET_NEAR - CATEGORY: RET - EXTENSION: BASE - IFORM: RET_NEAR - ISA_SET: I86 - SHORT: ret - */ - EXPECT_EQ(2, ild(u"≤├")); -} - -TEST(x86ild, test_F3A4) { - /* - ICLASS: REP_MOVSB - CATEGORY: STRINGOP - EXTENSION: BASE - IFORM: REP_MOVSB - ISA_SET: I86 - SHORT: rep movsb byte ptr [rdi], byte ptr [rsi] - */ - EXPECT_EQ(2, ild(u"≤ñ")); -} - -TEST(x86ild, test_D3E2) { - /* - ICLASS: SHL - CATEGORY: SHIFT - EXTENSION: BASE - IFORM: SHL_GPRv_CL_D3r4 - ISA_SET: I86 - SHORT: shl edx, cl - */ - EXPECT_EQ(2, ild(u"╙Γ")); -} - -TEST(x86ild, test_C6040300) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_IMMb - ISA_SET: I86 - SHORT: mov byte ptr [rbx+rax*1], 0x0 - */ - EXPECT_EQ(4, ild(u"╞♦♥ ")); -} - -TEST(x86ild, test_C60100) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_IMMb - ISA_SET: I86 - SHORT: mov byte ptr [rcx], 0x0 - */ - EXPECT_EQ(3, ild(u"╞☺ ")); -} - -TEST(x86ild, test_B200) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_IMMb_B0 - ISA_SET: I86 - SHORT: mov dl, 0x0 - */ - EXPECT_EQ(2, ild(u"▓ ")); -} - -TEST(x86ild, test_8B8100000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov eax, dword ptr [rcx] - */ - EXPECT_EQ(6, ild(u"ïü    ")); -} - -TEST(x86ild, test_8B7500) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov esi, dword ptr [rbp] - */ - EXPECT_EQ(3, ild(u"ïu ")); -} - -TEST(x86ild, test_8B6800) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov ebp, dword ptr [rax] - */ - EXPECT_EQ(3, ild(u"ïh ")); -} - -TEST(x86ild, test_8B4800) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov ecx, dword ptr [rax] - */ - EXPECT_EQ(3, ild(u"ïH ")); -} - -TEST(x86ild, test_8B4500) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov eax, dword ptr [rbp] - */ - EXPECT_EQ(3, ild(u"ïE ")); -} - -TEST(x86ild, test_8B3F) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov edi, dword ptr [rdi] - */ - EXPECT_EQ(2, ild(u"ï?")); -} - -TEST(x86ild, test_8B3C24) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov edi, dword ptr [rsp] - */ - EXPECT_EQ(3, ild(u"ï<$")); -} - -TEST(x86ild, test_8B348500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov esi, dword ptr [rax*4] - */ - EXPECT_EQ(7, ild(u"ï4à    ")); -} - -TEST(x86ild, test_8B28) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov ebp, dword ptr [rax] - */ - EXPECT_EQ(2, ild(u"ï(")); -} - -TEST(x86ild, test_8B1E) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov ebx, dword ptr [rsi] - */ - EXPECT_EQ(2, ild(u"ï▲")); -} - -TEST(x86ild, test_8B1500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov edx, dword ptr [rip] - */ - EXPECT_EQ(6, ild(u"易   ")); -} - -TEST(x86ild, test_8B14F500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov edx, dword ptr [rsi*8] - */ - EXPECT_EQ(7, ild(u"ï¶⌡    ")); -} - -TEST(x86ild, test_8B14C500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov edx, dword ptr [rax*8] - */ - EXPECT_EQ(7, ild(u"ï¶┼    ")); -} - -TEST(x86ild, test_8B0CCD00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov ecx, dword ptr [rcx*8] - */ - EXPECT_EQ(7, ild(u"ï♀═    ")); -} - -TEST(x86ild, test_8B0CBD00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov ecx, dword ptr [rdi*4] - */ - EXPECT_EQ(7, ild(u"ï♀╜    ")); -} - -TEST(x86ild, test_8B0CB500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov ecx, dword ptr [rsi*4] - */ - EXPECT_EQ(7, ild(u"ï♀╡    ")); -} - -TEST(x86ild, test_8B0C8D00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov ecx, dword ptr [rcx*4] - */ - EXPECT_EQ(7, ild(u"ï♀ì    ")); -} - -TEST(x86ild, test_8B06) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov eax, dword ptr [rsi] - */ - EXPECT_EQ(2, ild(u"ï♠")); -} - -TEST(x86ild, test_8B04B500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov eax, dword ptr [rsi*4] - */ - EXPECT_EQ(7, ild(u"ï♦╡    ")); -} - -TEST(x86ild, test_8A9200000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov dl, byte ptr [rdx] - */ - EXPECT_EQ(6, ild(u"èÆ    ")); -} - -TEST(x86ild, test_8A8900000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov cl, byte ptr [rcx] - */ - EXPECT_EQ(6, ild(u"èë    ")); -} - -TEST(x86ild, test_8A841000000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov al, byte ptr [rax+rdx*1] - */ - EXPECT_EQ(7, ild(u"èä►    ")); -} - -TEST(x86ild, test_8A542400) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov dl, byte ptr [rsp] - */ - EXPECT_EQ(4, ild(u"èT$ ")); -} - -TEST(x86ild, test_8A4E00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov cl, byte ptr [rsi] - */ - EXPECT_EQ(3, ild(u"èN ")); -} - -TEST(x86ild, test_8A4D00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov cl, byte ptr [rbp] - */ - EXPECT_EQ(3, ild(u"èM ")); -} - -TEST(x86ild, test_8A14F500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov dl, byte ptr [rsi*8] - */ - EXPECT_EQ(7, ild(u"è¶⌡    ")); -} - -TEST(x86ild, test_8A149500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov dl, byte ptr [rdx*4] - */ - EXPECT_EQ(7, ild(u"è¶ò    ")); -} - -TEST(x86ild, test_8A1411) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov dl, byte ptr [rcx+rdx*1] - */ - EXPECT_EQ(3, ild(u"è¶◄")); -} - -TEST(x86ild, test_8A140A) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov dl, byte ptr [rdx+rcx*1] - */ - EXPECT_EQ(3, ild(u"è¶◙")); -} - -TEST(x86ild, test_8A1402) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov dl, byte ptr [rdx+rax*1] - */ - EXPECT_EQ(3, ild(u"è¶☻")); -} - -TEST(x86ild, test_8A0C01) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov cl, byte ptr [rcx+rax*1] - */ - EXPECT_EQ(3, ild(u"è♀☺")); -} - -TEST(x86ild, test_8A06) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov al, byte ptr [rsi] - */ - EXPECT_EQ(2, ild(u"è♠")); -} - -TEST(x86ild, test_8A0406) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov al, byte ptr [rsi+rax*1] - */ - EXPECT_EQ(3, ild(u"è♦♠")); -} - -TEST(x86ild, test_8A0402) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov al, byte ptr [rdx+rax*1] - */ - EXPECT_EQ(3, ild(u"è♦☻")); -} - -TEST(x86ild, test_89F9) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov ecx, edi - */ - EXPECT_EQ(2, ild(u"ë∙")); -} - -TEST(x86ild, test_89F3) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov ebx, esi - */ - EXPECT_EQ(2, ild(u"ë≤")); -} - -TEST(x86ild, test_89E9) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov ecx, ebp - */ - EXPECT_EQ(2, ild(u"ëΘ")); -} - -TEST(x86ild, test_89D7) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov edi, edx - */ - EXPECT_EQ(2, ild(u"ë╫")); -} - -TEST(x86ild, test_89D3) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov ebx, edx - */ - EXPECT_EQ(2, ild(u"ë╙")); -} - -TEST(x86ild, test_89AA00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rdx], ebp - */ - EXPECT_EQ(6, ild(u"묠   ")); -} - -TEST(x86ild, test_89942400000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rsp], edx - */ - EXPECT_EQ(7, ild(u"ëö$    ")); -} - -TEST(x86ild, test_898200000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rdx], eax - */ - EXPECT_EQ(6, ild(u"ëé    ")); -} - -TEST(x86ild, test_897C2400) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rsp], edi - */ - EXPECT_EQ(4, ild(u"ë|$ ")); -} - -TEST(x86ild, test_895D00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rbp], ebx - */ - EXPECT_EQ(3, ild(u"ë] ")); -} - -TEST(x86ild, test_895800) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rax], ebx - */ - EXPECT_EQ(3, ild(u"ëX ")); -} - -TEST(x86ild, test_894C2400) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rsp], ecx - */ - EXPECT_EQ(4, ild(u"ëL$ ")); -} - -TEST(x86ild, test_894500) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rbp], eax - */ - EXPECT_EQ(3, ild(u"ëE ")); -} - -TEST(x86ild, test_893D00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rip], edi - */ - EXPECT_EQ(6, ild(u"ë=    ")); -} - -TEST(x86ild, test_891500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rip], edx - */ - EXPECT_EQ(6, ild(u"맠   ")); -} - -TEST(x86ild, test_8903) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rbx], eax - */ - EXPECT_EQ(2, ild(u"ë♥")); -} - -TEST(x86ild, test_88540700) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rdi+rax*1], dl - */ - EXPECT_EQ(4, ild(u"êT• ")); -} - -TEST(x86ild, test_88540300) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rbx+rax*1], dl - */ - EXPECT_EQ(4, ild(u"êT♥ ")); -} - -TEST(x86ild, test_885100) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rcx], dl - */ - EXPECT_EQ(3, ild(u"êQ ")); -} - -TEST(x86ild, test_88442400) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rsp], al - */ - EXPECT_EQ(4, ild(u"êD$ ")); -} - -TEST(x86ild, test_88441400) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rsp+rdx*1], al - */ - EXPECT_EQ(4, ild(u"êD¶ ")); -} - -TEST(x86ild, test_880C33) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rbx+rsi*1], cl - */ - EXPECT_EQ(3, ild(u"ê♀3")); -} - -TEST(x86ild, test_66C782000000000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_IMMz - ISA_SET: I86 - SHORT: mov word ptr [rdx], 0x0 - */ - EXPECT_EQ(9, ild(u"f╟é      ")); -} - -TEST(x86ild, test_6690) { - /* - ICLASS: NOP - CATEGORY: NOP - EXTENSION: BASE - IFORM: NOP_90 - ISA_SET: I86 - SHORT: data16 nop - */ - EXPECT_EQ(2, ild(u"fÉ")); -} - -TEST(x86ild, test_668B940000000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov dx, word ptr [rax+rax*1] - */ - EXPECT_EQ(8, ild(u"fïö     ")); -} - -TEST(x86ild, test_668B4B00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov cx, word ptr [rbx] - */ - EXPECT_EQ(4, ild(u"fïK ")); -} - -TEST(x86ild, test_66897B00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov word ptr [rbx], di - */ - EXPECT_EQ(4, ild(u"fë{ ")); -} - -TEST(x86ild, test_66894B00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov word ptr [rbx], cx - */ - EXPECT_EQ(4, ild(u"fëK ")); -} - -TEST(x86ild, test_668902) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov word ptr [rdx], ax - */ - EXPECT_EQ(3, ild(u"fë☻")); -} - -TEST(x86ild, test_66448B7500) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r14w, word ptr [rbp] - */ - EXPECT_EQ(5, ild(u"fDïu ")); -} - -TEST(x86ild, test_66448B6F00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r13w, word ptr [rdi] - */ - EXPECT_EQ(5, ild(u"fDïo ")); -} - -TEST(x86ild, test_66448B4700) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r8w, word ptr [rdi] - */ - EXPECT_EQ(5, ild(u"fDïG ")); -} - -TEST(x86ild, test_4D89C3) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r11, r8 - */ - EXPECT_EQ(3, ild(u"Më├")); -} - -TEST(x86ild, test_4D63EE) { - /* - ICLASS: MOVSXD - CATEGORY: DATAXFER - EXTENSION: LONGMODE - IFORM: MOVSXD_GPRv_GPR32 - ISA_SET: LONGMODE - SHORT: movsxd r13, r14d - */ - EXPECT_EQ(3, ild(u"Mcε")); -} - -TEST(x86ild, test_4D63C0) { - /* - ICLASS: MOVSXD - CATEGORY: DATAXFER - EXTENSION: LONGMODE - IFORM: MOVSXD_GPRv_GPR32 - ISA_SET: LONGMODE - SHORT: movsxd r8, r8d - */ - EXPECT_EQ(3, ild(u"Mc└")); -} - -TEST(x86ild, test_4C8B7700) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r14, qword ptr [rdi] - */ - EXPECT_EQ(4, ild(u"Lïw ")); -} - -TEST(x86ild, test_4C8B6F00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r13, qword ptr [rdi] - */ - EXPECT_EQ(4, ild(u"Lïo ")); -} - -TEST(x86ild, test_4C8B6D00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r13, qword ptr [rbp] - */ - EXPECT_EQ(4, ild(u"Lïm ")); -} - -TEST(x86ild, test_4C8B6700) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r12, qword ptr [rdi] - */ - EXPECT_EQ(4, ild(u"Lïg ")); -} - -TEST(x86ild, test_4C8B6300) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r12, qword ptr [rbx] - */ - EXPECT_EQ(4, ild(u"Lïc ")); -} - -TEST(x86ild, test_4C8B4700) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r8, qword ptr [rdi] - */ - EXPECT_EQ(4, ild(u"LïG ")); -} - -TEST(x86ild, test_4C8B4300) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r8, qword ptr [rbx] - */ - EXPECT_EQ(4, ild(u"LïC ")); -} - -TEST(x86ild, test_4C8B2500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r12, qword ptr [rip] - */ - EXPECT_EQ(7, ild(u"Lï%    ")); -} - -TEST(x86ild, test_4C89EE) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rsi, r13 - */ - EXPECT_EQ(3, ild(u"Lëε")); -} - -TEST(x86ild, test_4C89EA) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rdx, r13 - */ - EXPECT_EQ(3, ild(u"LëΩ")); -} - -TEST(x86ild, test_4C89CE) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rsi, r9 - */ - EXPECT_EQ(3, ild(u"Lë╬")); -} - -TEST(x86ild, test_4C89C6) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rsi, r8 - */ - EXPECT_EQ(3, ild(u"Lë╞")); -} - -TEST(x86ild, test_4C897300) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rbx], r14 - */ - EXPECT_EQ(4, ild(u"Lës ")); -} - -TEST(x86ild, test_4C896300) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rbx], r12 - */ - EXPECT_EQ(4, ild(u"Lëc ")); -} - -TEST(x86ild, test_4A8B34C500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rsi, qword ptr [r8*8] - */ - EXPECT_EQ(8, ild(u"Jï4┼    ")); -} - -TEST(x86ild, test_49D3ED) { - /* - ICLASS: SHR - CATEGORY: SHIFT - EXTENSION: BASE - IFORM: SHR_GPRv_CL - ISA_SET: I86 - SHORT: shr r13, cl - */ - EXPECT_EQ(3, ild(u"I╙φ")); -} - -TEST(x86ild, test_4989FD) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r13, rdi - */ - EXPECT_EQ(3, ild(u"Ië²")); -} - -TEST(x86ild, test_4989F6) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r14, rsi - */ - EXPECT_EQ(3, ild(u"Ië÷")); -} - -TEST(x86ild, test_4989F5) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r13, rsi - */ - EXPECT_EQ(3, ild(u"Ië⌡")); -} - -TEST(x86ild, test_4989F2) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r10, rsi - */ - EXPECT_EQ(3, ild(u"Ië≥")); -} - -TEST(x86ild, test_4989E4) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r12, rsp - */ - EXPECT_EQ(3, ild(u"IëΣ")); -} - -TEST(x86ild, test_4989D0) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r8, rdx - */ - EXPECT_EQ(3, ild(u"Ië╨")); -} - -TEST(x86ild, test_4989CD) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r13, rcx - */ - EXPECT_EQ(3, ild(u"Ië═")); -} - -TEST(x86ild, test_4989C5) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r13, rax - */ - EXPECT_EQ(3, ild(u"Ië┼")); -} - -TEST(x86ild, test_4963F7) { - /* - ICLASS: MOVSXD - CATEGORY: DATAXFER - EXTENSION: LONGMODE - IFORM: MOVSXD_GPRv_GPR32 - ISA_SET: LONGMODE - SHORT: movsxd rsi, r15d - */ - EXPECT_EQ(3, ild(u"Ic≈")); -} - -TEST(x86ild, test_48D3C0) { - /* - ICLASS: ROL - CATEGORY: ROTATE - EXTENSION: BASE - IFORM: ROL_GPRv_CL - ISA_SET: I86 - SHORT: rol rax, cl - */ - EXPECT_EQ(3, ild(u"H╙└")); -} - -TEST(x86ild, test_48C7C000000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_IMMz - ISA_SET: I86 - SHORT: mov rax, 0x0 - */ - EXPECT_EQ(7, ild(u"H╟└    ")); -} - -TEST(x86ild, test_48C70300000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_IMMz - ISA_SET: I86 - SHORT: mov qword ptr [rbx], 0x0 - */ - EXPECT_EQ(7, ild(u"H╟♥    ")); -} - -TEST(x86ild, test_488B9000000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rdx, qword ptr [rax] - */ - EXPECT_EQ(7, ild(u"HïÉ    ")); -} - -TEST(x86ild, test_488B8600000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rax, qword ptr [rsi] - */ - EXPECT_EQ(7, ild(u"Hïå    ")); -} - -TEST(x86ild, test_488B7B00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rdi, qword ptr [rbx] - */ - EXPECT_EQ(4, ild(u"Hï{ ")); -} - -TEST(x86ild, test_488B7300) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rsi, qword ptr [rbx] - */ - EXPECT_EQ(4, ild(u"Hïs ")); -} - -TEST(x86ild, test_488B4500) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rax, qword ptr [rbp] - */ - EXPECT_EQ(4, ild(u"HïE ")); -} - -TEST(x86ild, test_488B4100) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rax, qword ptr [rcx] - */ - EXPECT_EQ(4, ild(u"HïA ")); -} - -TEST(x86ild, test_488B37) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rsi, qword ptr [rdi] - */ - EXPECT_EQ(3, ild(u"Hï7")); -} - -TEST(x86ild, test_488B3424) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rsi, qword ptr [rsp] - */ - EXPECT_EQ(4, ild(u"Hï4$")); -} - -TEST(x86ild, test_488B33) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rsi, qword ptr [rbx] - */ - EXPECT_EQ(3, ild(u"Hï3")); -} - -TEST(x86ild, test_488B1D00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rbx, qword ptr [rip] - */ - EXPECT_EQ(7, ild(u"Hï↔    ")); -} - -TEST(x86ild, test_488B14CA) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rdx, qword ptr [rdx+rcx*8] - */ - EXPECT_EQ(4, ild(u"Hï¶╩")); -} - -TEST(x86ild, test_488B13) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rdx, qword ptr [rbx] - */ - EXPECT_EQ(3, ild(u"Hï‼")); -} - -TEST(x86ild, test_488B12) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rdx, qword ptr [rdx] - */ - EXPECT_EQ(3, ild(u"Hï↕")); -} - -TEST(x86ild, test_488B0D00000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rcx, qword ptr [rip] - */ - EXPECT_EQ(7, ild(u"Hï♪    ")); -} - -TEST(x86ild, test_488B07) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rax, qword ptr [rdi] - */ - EXPECT_EQ(3, ild(u"Hï•")); -} - -TEST(x86ild, test_488B04F500000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rax, qword ptr [rsi*8] - */ - EXPECT_EQ(8, ild(u"Hï♦⌡    ")); -} - -TEST(x86ild, test_488B03) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rax, qword ptr [rbx] - */ - EXPECT_EQ(3, ild(u"Hï♥")); -} - -TEST(x86ild, test_488B00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov rax, qword ptr [rax] - */ - EXPECT_EQ(3, ild(u"Hï ")); -} - -TEST(x86ild, test_4889FA) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rdx, rdi - */ - EXPECT_EQ(3, ild(u"Hë·")); -} - -TEST(x86ild, test_4889F8) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rax, rdi - */ - EXPECT_EQ(3, ild(u"Hë°")); -} - -TEST(x86ild, test_4889F1) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rcx, rsi - */ - EXPECT_EQ(3, ild(u"Hë±")); -} - -TEST(x86ild, test_4889D6) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rsi, rdx - */ - EXPECT_EQ(3, ild(u"Hë╓")); -} - -TEST(x86ild, test_4889D3) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rbx, rdx - */ - EXPECT_EQ(3, ild(u"Hë╙")); -} - -TEST(x86ild, test_4889CA) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rdx, rcx - */ - EXPECT_EQ(3, ild(u"Hë╩")); -} - -TEST(x86ild, test_4889C5) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov rbp, rax - */ - EXPECT_EQ(3, ild(u"Hë┼")); -} - -TEST(x86ild, test_48895300) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rbx], rdx - */ - EXPECT_EQ(4, ild(u"HëS ")); -} - -TEST(x86ild, test_48893424) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rsp], rsi - */ - EXPECT_EQ(4, ild(u"Hë4$")); -} - -TEST(x86ild, test_488917) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rdi], rdx - */ - EXPECT_EQ(3, ild(u"Hë↨")); -} - -TEST(x86ild, test_488913) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rbx], rdx - */ - EXPECT_EQ(3, ild(u"Hë‼")); -} - -TEST(x86ild, test_488907) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rdi], rax - */ - EXPECT_EQ(3, ild(u"Hë•")); -} - -TEST(x86ild, test_488906) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov qword ptr [rsi], rax - */ - EXPECT_EQ(3, ild(u"Hë♠")); -} - -TEST(x86ild, test_4863F8) { - /* - ICLASS: MOVSXD - CATEGORY: DATAXFER - EXTENSION: LONGMODE - IFORM: MOVSXD_GPRv_GPR32 - ISA_SET: LONGMODE - SHORT: movsxd rdi, eax - */ - EXPECT_EQ(3, ild(u"Hc°")); -} - -TEST(x86ild, test_4863D0) { - /* - ICLASS: MOVSXD - CATEGORY: DATAXFER - EXTENSION: LONGMODE - IFORM: MOVSXD_GPRv_GPR32 - ISA_SET: LONGMODE - SHORT: movsxd rdx, eax - */ - EXPECT_EQ(3, ild(u"Hc╨")); -} - -TEST(x86ild, test_4863C8) { - /* - ICLASS: MOVSXD - CATEGORY: DATAXFER - EXTENSION: LONGMODE - IFORM: MOVSXD_GPRv_GPR32 - ISA_SET: LONGMODE - SHORT: movsxd rcx, eax - */ - EXPECT_EQ(3, ild(u"Hc╚")); -} - -TEST(x86ild, test_4863C7) { - /* - ICLASS: MOVSXD - CATEGORY: DATAXFER - EXTENSION: LONGMODE - IFORM: MOVSXD_GPRv_GPR32 - ISA_SET: LONGMODE - SHORT: movsxd rax, edi - */ - EXPECT_EQ(3, ild(u"Hc╟")); -} - -TEST(x86ild, test_486312) { - /* - ICLASS: MOVSXD - CATEGORY: DATAXFER - EXTENSION: LONGMODE - IFORM: MOVSXD_GPRv_MEMd - ISA_SET: LONGMODE - SHORT: movsxd rdx, dword ptr [rdx] - */ - EXPECT_EQ(3, ild(u"Hc↕")); -} - -TEST(x86ild, test_47882C0E) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [r14+r9*1], r13b - */ - EXPECT_EQ(4, ild(u"Gê,♫")); -} - -TEST(x86ild, test_458B9000000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r10d, dword ptr [r8] - */ - EXPECT_EQ(7, ild(u"EïÉ    ")); -} - -TEST(x86ild, test_4589D8) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r8d, r11d - */ - EXPECT_EQ(3, ild(u"Eë╪")); -} - -TEST(x86ild, test_4589CB) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r11d, r9d - */ - EXPECT_EQ(3, ild(u"Eë╦")); -} - -TEST(x86ild, test_4589C1) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r9d, r8d - */ - EXPECT_EQ(3, ild(u"Eë┴")); -} - -TEST(x86ild, test_4588F8) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov r8b, r15b - */ - EXPECT_EQ(3, ild(u"Eê°")); -} - -TEST(x86ild, test_4588D3) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov r11b, r10b - */ - EXPECT_EQ(3, ild(u"Eê╙")); -} - -TEST(x86ild, test_4588D1) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov r9b, r10b - */ - EXPECT_EQ(3, ild(u"Eê╤")); -} - -TEST(x86ild, test_4588C7) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov r15b, r8b - */ - EXPECT_EQ(3, ild(u"Eê╟")); -} - -TEST(x86ild, test_45883424) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [r12], r14b - */ - EXPECT_EQ(4, ild(u"Eê4$")); -} - -TEST(x86ild, test_448B8100000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r8d, dword ptr [rcx] - */ - EXPECT_EQ(7, ild(u"Dïü    ")); -} - -TEST(x86ild, test_448B7D00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r15d, dword ptr [rbp] - */ - EXPECT_EQ(4, ild(u"Dï} ")); -} - -TEST(x86ild, test_448B7700) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r14d, dword ptr [rdi] - */ - EXPECT_EQ(4, ild(u"Dïw ")); -} - -TEST(x86ild, test_448B6700) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r12d, dword ptr [rdi] - */ - EXPECT_EQ(4, ild(u"Dïg ")); -} - -TEST(x86ild, test_448B4700) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r8d, dword ptr [rdi] - */ - EXPECT_EQ(4, ild(u"DïG ")); -} - -TEST(x86ild, test_448B4500) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r8d, dword ptr [rbp] - */ - EXPECT_EQ(4, ild(u"DïE ")); -} - -TEST(x86ild, test_448B0424) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov r8d, dword ptr [rsp] - */ - EXPECT_EQ(4, ild(u"Dï♦$")); -} - -TEST(x86ild, test_448A5300) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov r10b, byte ptr [rbx] - */ - EXPECT_EQ(4, ild(u"DèS ")); -} - -TEST(x86ild, test_448A4E00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov r9b, byte ptr [rsi] - */ - EXPECT_EQ(4, ild(u"DèN ")); -} - -TEST(x86ild, test_448A4600) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov r8b, byte ptr [rsi] - */ - EXPECT_EQ(4, ild(u"DèF ")); -} - -TEST(x86ild, test_448A01) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov r8b, byte ptr [rcx] - */ - EXPECT_EQ(3, ild(u"Dè☺")); -} - -TEST(x86ild, test_4489E9) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov ecx, r13d - */ - EXPECT_EQ(3, ild(u"DëΘ")); -} - -TEST(x86ild, test_4489E8) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov eax, r13d - */ - EXPECT_EQ(3, ild(u"DëΦ")); -} - -TEST(x86ild, test_4489CE) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov esi, r9d - */ - EXPECT_EQ(3, ild(u"Dë╬")); -} - -TEST(x86ild, test_4489C8) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov eax, r9d - */ - EXPECT_EQ(3, ild(u"Dë╚")); -} - -TEST(x86ild, test_4489C2) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov edx, r8d - */ - EXPECT_EQ(3, ild(u"Dë┬")); -} - -TEST(x86ild, test_4489C0) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov eax, r8d - */ - EXPECT_EQ(3, ild(u"Dë└")); -} - -TEST(x86ild, test_44898900000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [rcx], r9d - */ - EXPECT_EQ(7, ild(u"Dëë    ")); -} - -TEST(x86ild, test_4488E8) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov al, r13b - */ - EXPECT_EQ(3, ild(u"DêΦ")); -} - -TEST(x86ild, test_4488D9) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov cl, r11b - */ - EXPECT_EQ(3, ild(u"Dê┘")); -} - -TEST(x86ild, test_4488CE) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov sil, r9b - */ - EXPECT_EQ(3, ild(u"Dê╬")); -} - -TEST(x86ild, test_4488CD) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov bpl, r9b - */ - EXPECT_EQ(3, ild(u"Dê═")); -} - -TEST(x86ild, test_4488C5) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov bpl, r8b - */ - EXPECT_EQ(3, ild(u"Dê┼")); -} - -TEST(x86ild, test_44886300) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rbx], r12b - */ - EXPECT_EQ(4, ild(u"Dêc ")); -} - -TEST(x86ild, test_44884B00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rbx], r9b - */ - EXPECT_EQ(4, ild(u"DêK ")); -} - -TEST(x86ild, test_44884700) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rdi], r8b - */ - EXPECT_EQ(4, ild(u"DêG ")); -} - -TEST(x86ild, test_44884000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rax], r8b - */ - EXPECT_EQ(4, ild(u"Dê@ ")); -} - -TEST(x86ild, test_43C6040E00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_IMMb - ISA_SET: I86 - SHORT: mov byte ptr [r14+r9*1], 0x0 - */ - EXPECT_EQ(5, ild(u"C╞♦♫ ")); -} - -TEST(x86ild, test_428A1400) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov dl, byte ptr [rax+r8*1] - */ - EXPECT_EQ(4, ild(u"B趠")); -} - -TEST(x86ild, test_428A0406) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov al, byte ptr [rsi+r8*1] - */ - EXPECT_EQ(4, ild(u"Bè♦♠")); -} - -TEST(x86ild, test_42880407) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rdi+r8*1], al - */ - EXPECT_EQ(4, ild(u"Bê♦•")); -} - -TEST(x86ild, test_41FF14C4) { - /* - ICLASS: CALL_NEAR - CATEGORY: CALL - EXTENSION: BASE - IFORM: CALL_NEAR_MEMv - ISA_SET: I86 - SHORT: call qword ptr [r12+rax*8] - */ - EXPECT_EQ(4, ild(u"Aλ¶─")); -} - -TEST(x86ild, test_41C6042400) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_IMMb - ISA_SET: I86 - SHORT: mov byte ptr [r12], 0x0 - */ - EXPECT_EQ(5, ild(u"A╞♦$ ")); -} - -TEST(x86ild, test_41C60000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_IMMb - ISA_SET: I86 - SHORT: mov byte ptr [r8], 0x0 - */ - EXPECT_EQ(4, ild(u"A╞  ")); -} - -TEST(x86ild, test_418B8000000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_MEMv - ISA_SET: I86 - SHORT: mov eax, dword ptr [r8] - */ - EXPECT_EQ(7, ild(u"AïÇ    ")); -} - -TEST(x86ild, test_418A0C14) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov cl, byte ptr [r12+rdx*1] - */ - EXPECT_EQ(4, ild(u"Aè♀¶")); -} - -TEST(x86ild, test_418A0404) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov al, byte ptr [r12+rax*1] - */ - EXPECT_EQ(4, ild(u"Aè♦♦")); -} - -TEST(x86ild, test_418A0400) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov al, byte ptr [r8+rax*1] - */ - EXPECT_EQ(4, ild(u"Aè♦ ")); -} - -TEST(x86ild, test_4189FB) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r11d, edi - */ - EXPECT_EQ(3, ild(u"Aë√")); -} - -TEST(x86ild, test_4189F8) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r8d, edi - */ - EXPECT_EQ(3, ild(u"Aë°")); -} - -TEST(x86ild, test_4189DC) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r12d, ebx - */ - EXPECT_EQ(3, ild(u"Aë▄")); -} - -TEST(x86ild, test_4189D7) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r15d, edx - */ - EXPECT_EQ(3, ild(u"Aë╫")); -} - -TEST(x86ild, test_4189D6) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r14d, edx - */ - EXPECT_EQ(3, ild(u"Aë╓")); -} - -TEST(x86ild, test_4189D3) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r11d, edx - */ - EXPECT_EQ(3, ild(u"Aë╙")); -} - -TEST(x86ild, test_4189CB) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r11d, ecx - */ - EXPECT_EQ(3, ild(u"Aë╦")); -} - -TEST(x86ild, test_4189C1) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r9d, eax - */ - EXPECT_EQ(3, ild(u"Aë┴")); -} - -TEST(x86ild, test_4189C0) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPRv_GPRv_89 - ISA_SET: I86 - SHORT: mov r8d, eax - */ - EXPECT_EQ(3, ild(u"Aë└")); -} - -TEST(x86ild, test_41898800000000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMv_GPRv - ISA_SET: I86 - SHORT: mov dword ptr [r8], ecx - */ - EXPECT_EQ(7, ild(u"Aëê    ")); -} - -TEST(x86ild, test_4188F2) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov r10b, sil - */ - EXPECT_EQ(3, ild(u"Aê≥")); -} - -TEST(x86ild, test_4188CE) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov r14b, cl - */ - EXPECT_EQ(3, ild(u"Aê╬")); -} - -TEST(x86ild, test_4188C3) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov r11b, al - */ - EXPECT_EQ(3, ild(u"Aê├")); -} - -TEST(x86ild, test_4188C2) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov r10b, al - */ - EXPECT_EQ(3, ild(u"Aê┬")); -} - -TEST(x86ild, test_4188C1) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov r9b, al - */ - EXPECT_EQ(3, ild(u"Aê┴")); -} - -TEST(x86ild, test_41885000) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [r8], dl - */ - EXPECT_EQ(4, ild(u"AêP ")); -} - -TEST(x86ild, test_4158) { - /* - ICLASS: POP - CATEGORY: POP - EXTENSION: BASE - IFORM: POP_GPRv_58 - ISA_SET: I86 - SHORT: pop r8 - */ - EXPECT_EQ(2, ild(u"AX")); -} - -TEST(x86ild, test_40B600) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_IMMb_B0 - ISA_SET: I86 - SHORT: mov sil, 0x0 - */ - EXPECT_EQ(3, ild(u"@╢ ")); -} - -TEST(x86ild, test_408A7D00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_MEMb - ISA_SET: I86 - SHORT: mov dil, byte ptr [rbp] - */ - EXPECT_EQ(4, ild(u"@è} ")); -} - -TEST(x86ild, test_4088FA) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov dl, dil - */ - EXPECT_EQ(3, ild(u"@ê·")); -} - -TEST(x86ild, test_4088EA) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov dl, bpl - */ - EXPECT_EQ(3, ild(u"@êΩ")); -} - -TEST(x86ild, test_4088D7) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov dil, dl - */ - EXPECT_EQ(3, ild(u"@ê╫")); -} - -TEST(x86ild, test_4088C5) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_GPR8_GPR8_88 - ISA_SET: I86 - SHORT: mov bpl, al - */ - EXPECT_EQ(3, ild(u"@ê┼")); -} - -TEST(x86ild, test_40887B00) { - /* - ICLASS: MOV - CATEGORY: DATAXFER - EXTENSION: BASE - IFORM: MOV_MEMb_GPR8 - ISA_SET: I86 - SHORT: mov byte ptr [rbx], dil - */ - EXPECT_EQ(4, ild(u"@ê{ ")); -} - -TEST(x86ild, test_0F8E00000000) { - /* - ICLASS: JLE - CATEGORY: COND_BR - EXTENSION: BASE - IFORM: JLE_RELBRd - ISA_SET: I86 - SHORT: jle 0x6 - */ - EXPECT_EQ(6, ild(u"☼Ä    ")); -} - -TEST(x86ild, test_0F8800000000) { - /* - ICLASS: JS - CATEGORY: COND_BR - EXTENSION: BASE - IFORM: JS_RELBRd - ISA_SET: I86 - SHORT: js 0x6 - */ - EXPECT_EQ(6, ild(u"☼ê    ")); -} - -TEST(x86ild, test_0F8200000000) { - /* - ICLASS: JB - CATEGORY: COND_BR - EXTENSION: BASE - IFORM: JB_RELBRd - ISA_SET: I86 - SHORT: jb 0x6 - */ - EXPECT_EQ(6, ild(u"☼é    ")); -} diff --git a/test/net/https/mbedtls_test.c b/test/net/https/mbedtls_test.c index 98051ad31..049828cb5 100644 --- a/test/net/https/mbedtls_test.c +++ b/test/net/https/mbedtls_test.c @@ -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)); diff --git a/third_party/lz4cli/lz4.c b/third_party/lz4cli/lz4.c index cfaed5688..4114fea2a 100644 --- a/third_party/lz4cli/lz4.c +++ b/third_party/lz4cli/lz4.c @@ -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, diff --git a/third_party/mbedtls/bigmul.c b/third_party/mbedtls/bigmul.c index cfe5c9636..202663738 100644 --- a/third_party/mbedtls/bigmul.c +++ b/third_party/mbedtls/bigmul.c @@ -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 ) diff --git a/third_party/mbedtls/bignum.c b/third_party/mbedtls/bignum.c index 84426d3ce..4890f3841 100644 --- a/third_party/mbedtls/bignum.c +++ b/third_party/mbedtls/bignum.c @@ -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; diff --git a/third_party/mbedtls/bigshift.c b/third_party/mbedtls/bigshift.c index 4d7af72e3..dc171d2bc 100644 --- a/third_party/mbedtls/bigshift.c +++ b/third_party/mbedtls/bigshift.c @@ -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 */ diff --git a/third_party/mbedtls/config.h b/third_party/mbedtls/config.h index f3328b6ba..5c8b8ff5b 100644 --- a/third_party/mbedtls/config.h +++ b/third_party/mbedtls/config.h @@ -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 diff --git a/third_party/mbedtls/ecp256.c b/third_party/mbedtls/ecp256.c index 46c6fdcec..b149c4522 100644 --- a/third_party/mbedtls/ecp256.c +++ b/third_party/mbedtls/ecp256.c @@ -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 } diff --git a/third_party/mbedtls/ecp384.c b/third_party/mbedtls/ecp384.c index a6e2740fe..def25a586 100644 --- a/third_party/mbedtls/ecp384.c +++ b/third_party/mbedtls/ecp384.c @@ -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 } diff --git a/third_party/mbedtls/ecp_internal.h b/third_party/mbedtls/ecp_internal.h index 494494533..bdfe46b4f 100644 --- a/third_party/mbedtls/ecp_internal.h +++ b/third_party/mbedtls/ecp_internal.h @@ -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_ */ diff --git a/third_party/mbedtls/secp384r1.c b/third_party/mbedtls/secp384r1.c index 3f0b1e29d..02153e9c4 100644 --- a/third_party/mbedtls/secp384r1.c +++ b/third_party/mbedtls/secp384r1.c @@ -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); diff --git a/third_party/mbedtls/shiftright.c b/third_party/mbedtls/shiftright.c index 38cd9a2bd..b6a9fa1bb 100644 --- a/third_party/mbedtls/shiftright.c +++ b/third_party/mbedtls/shiftright.c @@ -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}; diff --git a/third_party/mbedtls/test/secp384r1_test.c b/third_party/mbedtls/test/secp384r1_test.c index 68de75ce7..b1f023785 100644 --- a/third_party/mbedtls/test/secp384r1_test.c +++ b/third_party/mbedtls/test/secp384r1_test.c @@ -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 */ diff --git a/third_party/quickjs/array.c b/third_party/quickjs/array.c new file mode 100644 index 000000000..2b439d5d1 --- /dev/null +++ b/third_party/quickjs/array.c @@ -0,0 +1,1952 @@ +/* + * 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 */ + +/* Check if an object has a generalized numeric property. Return value: + -1 for exception, + TRUE if property exists, stored into *pval, + FALSE if proprty does not exist. + */ +static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval) +{ + JSValue val = JS_UNDEFINED; + JSAtom prop; + int present; + if (LIKELY((uint64_t)idx <= JS_ATOM_MAX_INT)) { + /* fast path */ + present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx)); + if (present > 0) { + val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); + if (UNLIKELY(JS_IsException(val))) + present = -1; + } + } else { + prop = JS_NewAtomInt64(ctx, idx); + present = -1; + if (LIKELY(prop != JS_ATOM_NULL)) { + present = JS_HasProperty(ctx, obj, prop); + if (present > 0) { + val = JS_GetProperty(ctx, obj, prop); + if (UNLIKELY(JS_IsException(val))) + present = -1; + } + JS_FreeAtom(ctx, prop); + } + } + *pval = val; + return present; +} + +static int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags) +{ + JSAtom prop; + int res; + if ((uint64_t)idx <= JS_ATOM_MAX_INT) { + /* fast path for fast arrays */ + return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) + return -1; + res = JS_DeleteProperty(ctx, obj, prop, flags); + JS_FreeAtom(ctx, prop); + return res; +} + +static int JS_CopySubArray(JSContext *ctx, + JSValueConst obj, int64_t to_pos, + int64_t from_pos, int64_t count, int dir) +{ + int64_t i, from, to; + JSValue val; + int fromPresent; + /* XXX: should special case fast arrays */ + for (i = 0; i < count; i++) { + if (dir < 0) { + from = from_pos + count - i - 1; + to = to_pos + count - i - 1; + } else { + from = from_pos + i; + to = to_pos + i; + } + fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val); + if (fromPresent < 0) + goto exception; + if (fromPresent) { + if (JS_SetPropertyInt64(ctx, obj, to, val) < 0) + goto exception; + } else { + if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0) + goto exception; + } + } + return 0; + exception: + return -1; +} + +JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue obj; + int i; + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY); + if (JS_IsException(obj)) + return obj; + if (argc == 1 && JS_IsNumber(argv[0])) { + uint32_t len; + if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE)) + goto fail; + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0) + goto fail; + } else { + for(i = 0; i < argc; i++) { + if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) + goto fail; + } + } + return obj; +fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_from(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // from(items, mapfn = void 0, this_arg = void 0) + JSValueConst items = argv[0], mapfn, this_arg; + JSValueConst args[2]; + JSValue stack[2]; + JSValue iter, r, v, v2, arrayLike; + int64_t k, len; + int done, mapping; + mapping = FALSE; + mapfn = JS_UNDEFINED; + this_arg = JS_UNDEFINED; + r = JS_UNDEFINED; + arrayLike = JS_UNDEFINED; + stack[0] = JS_UNDEFINED; + stack[1] = JS_UNDEFINED; + if (argc > 1) { + mapfn = argv[1]; + if (!JS_IsUndefined(mapfn)) { + if (check_function(ctx, mapfn)) + goto exception; + mapping = 1; + if (argc > 2) + this_arg = argv[2]; + } + } + iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); + if (JS_IsException(iter)) + goto exception; + if (!JS_IsUndefined(iter)) { + JS_FreeValue(ctx, iter); + if (JS_IsConstructor(ctx, this_val)) + r = JS_CallConstructor(ctx, this_val, 0, NULL); + else + r = JS_NewArray(ctx); + if (JS_IsException(r)) + goto exception; + stack[0] = JS_DupValue(ctx, items); + if (js_for_of_start(ctx, &stack[1], FALSE)) + goto exception; + for (k = 0;; k++) { + v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); + if (JS_IsException(v)) + goto exception_close; + if (done) + break; + if (mapping) { + args[0] = v; + args[1] = JS_NewInt32(ctx, k); + v2 = JS_Call(ctx, mapfn, this_arg, 2, args); + JS_FreeValue(ctx, v); + v = v2; + if (JS_IsException(v)) + goto exception_close; + } + if (JS_DefinePropertyValueInt64(ctx, r, k, v, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception_close; + } + } else { + arrayLike = JS_ToObject(ctx, items); + if (JS_IsException(arrayLike)) + goto exception; + if (js_get_length64(ctx, &len, arrayLike) < 0) + goto exception; + v = JS_NewInt64(ctx, len); + args[0] = v; + if (JS_IsConstructor(ctx, this_val)) { + r = JS_CallConstructor(ctx, this_val, 1, args); + } else { + r = js_array_constructor(ctx, JS_UNDEFINED, 1, args); + } + JS_FreeValue(ctx, v); + if (JS_IsException(r)) + goto exception; + for(k = 0; k < len; k++) { + v = JS_GetPropertyInt64(ctx, arrayLike, k); + if (JS_IsException(v)) + goto exception; + if (mapping) { + args[0] = v; + args[1] = JS_NewInt32(ctx, k); + v2 = JS_Call(ctx, mapfn, this_arg, 2, args); + JS_FreeValue(ctx, v); + v = v2; + if (JS_IsException(v)) + goto exception; + } + if (JS_DefinePropertyValueInt64(ctx, r, k, v, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + } + if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0) + goto exception; + goto done; + exception_close: + if (!JS_IsUndefined(stack[0])) + JS_IteratorClose(ctx, stack[0], TRUE); + exception: + JS_FreeValue(ctx, r); + r = JS_EXCEPTION; + done: + JS_FreeValue(ctx, arrayLike); + JS_FreeValue(ctx, stack[0]); + JS_FreeValue(ctx, stack[1]); + return r; +} + +static JSValue js_array_of(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, args[1]; + int i; + if (JS_IsConstructor(ctx, this_val)) { + args[0] = JS_NewInt32(ctx, argc); + obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args); + } else { + obj = JS_NewArray(ctx); + } + if (JS_IsException(obj)) + return JS_EXCEPTION; + for(i = 0; i < argc; i++) { + if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]), + JS_PROP_THROW) < 0) { + goto fail; + } + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) { + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int ret; + ret = JS_IsArray(ctx, argv[0]); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj, + JSValueConst len_val) +{ + JSValue ctor, ret, species; + int res; + JSContext *realm; + res = JS_IsArray(ctx, obj); + if (res < 0) + return JS_EXCEPTION; + if (!res) + return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); + ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); + if (JS_IsException(ctor)) + return ctor; + if (JS_IsConstructor(ctx, ctor)) { + /* legacy web compatibility */ + realm = JS_GetFunctionRealm(ctx, ctor); + if (!realm) { + JS_FreeValue(ctx, ctor); + return JS_EXCEPTION; + } + if (realm != ctx && + js_same_value(ctx, ctor, realm->array_ctor)) { + JS_FreeValue(ctx, ctor); + ctor = JS_UNDEFINED; + } + } + if (JS_IsObject(ctor)) { + species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); + JS_FreeValue(ctx, ctor); + if (JS_IsException(species)) + return species; + ctor = species; + if (JS_IsNull(ctor)) + ctor = JS_UNDEFINED; + } + if (JS_IsUndefined(ctor)) { + return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); + } else { + ret = JS_CallConstructor(ctx, ctor, 1, &len_val); + JS_FreeValue(ctx, ctor); + return ret; + } +} + +static const JSCFunctionListEntry js_array_funcs[] = { + JS_CFUNC_DEF("isArray", 1, js_array_isArray ), + JS_CFUNC_DEF("from", 1, js_array_from ), + JS_CFUNC_DEF("of", 0, js_array_of ), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), +}; + +static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj) +{ + JSValue val; + if (!JS_IsObject(obj)) + return FALSE; + val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable); + if (JS_IsException(val)) + return -1; + if (!JS_IsUndefined(val)) + return JS_ToBoolFree(ctx, val); + return JS_IsArray(ctx, obj); +} + +static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, arr, val; + JSValueConst e; + int64_t len, k, n; + int i, res; + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + goto exception; + arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); + if (JS_IsException(arr)) + goto exception; + n = 0; + for (i = -1; i < argc; i++) { + if (i < 0) + e = obj; + else + e = argv[i]; + res = JS_isConcatSpreadable(ctx, e); + if (res < 0) + goto exception; + if (res) { + if (js_get_length64(ctx, &len, e)) + goto exception; + if (n + len > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + for (k = 0; k < len; k++, n++) { + res = JS_TryGetPropertyInt64(ctx, e, k, &val); + if (res < 0) + goto exception; + if (res) { + if (JS_DefinePropertyValueInt64(ctx, arr, n, val, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + } + } else { + if (n >= MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e), + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + n++; + } + } + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) + goto exception; + JS_FreeValue(ctx, obj); + return arr; +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_every(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int special) +{ + JSValue obj, val, index_val, res, ret; + JSValueConst args[3]; + JSValueConst func, this_arg; + int64_t len, k, n; + int present; + ret = JS_UNDEFINED; + val = JS_UNDEFINED; + if (special & special_TA) { + obj = JS_DupValue(ctx, this_val); + len = js_typed_array_get_length_internal(ctx, obj); + if (len < 0) + goto exception; + } else { + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + } + func = argv[0]; + this_arg = JS_UNDEFINED; + if (argc > 1) + this_arg = argv[1]; + if (check_function(ctx, func)) + goto exception; + switch (special) { + case special_every: + case special_every | special_TA: + ret = JS_TRUE; + break; + case special_some: + case special_some | special_TA: + ret = JS_FALSE; + break; + case special_map: + /* XXX: JS_ArraySpeciesCreate should take int64_t */ + ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len)); + if (JS_IsException(ret)) + goto exception; + break; + case special_filter: + ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); + if (JS_IsException(ret)) + goto exception; + break; + case special_map | special_TA: + args[0] = obj; + args[1] = JS_NewInt32(ctx, len); + ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); + if (JS_IsException(ret)) + goto exception; + break; + case special_filter | special_TA: + ret = JS_NewArray(ctx); + if (JS_IsException(ret)) + goto exception; + break; + } + n = 0; + for(k = 0; k < len; k++) { + if (special & special_TA) { + val = JS_GetPropertyInt64(ctx, obj, k); + if (JS_IsException(val)) + goto exception; + present = TRUE; + } else { + present = JS_TryGetPropertyInt64(ctx, obj, k, &val); + if (present < 0) + goto exception; + } + if (present) { + index_val = JS_NewInt64(ctx, k); + if (JS_IsException(index_val)) + goto exception; + args[0] = val; + args[1] = index_val; + args[2] = obj; + res = JS_Call(ctx, func, this_arg, 3, args); + JS_FreeValue(ctx, index_val); + if (JS_IsException(res)) + goto exception; + switch (special) { + case special_every: + case special_every | special_TA: + if (!JS_ToBoolFree(ctx, res)) { + ret = JS_FALSE; + goto done; + } + break; + case special_some: + case special_some | special_TA: + if (JS_ToBoolFree(ctx, res)) { + ret = JS_TRUE; + goto done; + } + break; + case special_map: + if (JS_DefinePropertyValueInt64(ctx, ret, k, res, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + break; + case special_map | special_TA: + if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0) + goto exception; + break; + case special_filter: + case special_filter | special_TA: + if (JS_ToBoolFree(ctx, res)) { + if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val), + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + break; + default: + JS_FreeValue(ctx, res); + break; + } + JS_FreeValue(ctx, val); + val = JS_UNDEFINED; + } + } +done: + if (special == (special_filter | special_TA)) { + JSValue arr; + args[0] = obj; + args[1] = JS_NewInt32(ctx, n); + arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); + if (JS_IsException(arr)) + goto exception; + args[0] = ret; + res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args); + if (check_exception_free(ctx, res)) + goto exception; + JS_FreeValue(ctx, ret); + ret = arr; + } + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return ret; +exception: + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int special) +{ + JSValue obj, val, index_val, acc, acc1; + JSValueConst args[4]; + JSValueConst func; + int64_t len, k, k1; + int present; + acc = JS_UNDEFINED; + val = JS_UNDEFINED; + if (special & special_TA) { + obj = JS_DupValue(ctx, this_val); + len = js_typed_array_get_length_internal(ctx, obj); + if (len < 0) + goto exception; + } else { + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + } + func = argv[0]; + if (check_function(ctx, func)) + goto exception; + k = 0; + if (argc > 1) { + acc = JS_DupValue(ctx, argv[1]); + } else { + for(;;) { + if (k >= len) { + JS_ThrowTypeError(ctx, "empty array"); + goto exception; + } + k1 = (special & special_reduceRight) ? len - k - 1 : k; + k++; + if (special & special_TA) { + acc = JS_GetPropertyInt64(ctx, obj, k1); + if (JS_IsException(acc)) + goto exception; + break; + } else { + present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc); + if (present < 0) + goto exception; + if (present) + break; + } + } + } + for (; k < len; k++) { + k1 = (special & special_reduceRight) ? len - k - 1 : k; + if (special & special_TA) { + val = JS_GetPropertyInt64(ctx, obj, k1); + if (JS_IsException(val)) + goto exception; + present = TRUE; + } else { + present = JS_TryGetPropertyInt64(ctx, obj, k1, &val); + if (present < 0) + goto exception; + } + if (present) { + index_val = JS_NewInt64(ctx, k1); + if (JS_IsException(index_val)) + goto exception; + args[0] = acc; + args[1] = val; + args[2] = index_val; + args[3] = obj; + acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args); + JS_FreeValue(ctx, index_val); + JS_FreeValue(ctx, val); + val = JS_UNDEFINED; + if (JS_IsException(acc1)) + goto exception; + JS_FreeValue(ctx, acc); + acc = acc1; + } + } + JS_FreeValue(ctx, obj); + return acc; +exception: + JS_FreeValue(ctx, acc); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj; + int64_t len, start, end; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + start = 0; + if (argc > 1 && !JS_IsUndefined(argv[1])) { + if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len)) + goto exception; + } + end = len; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len)) + goto exception; + } + /* XXX: should special case fast arrays */ + while (start < end) { + if (JS_SetPropertyInt64(ctx, obj, start, + JS_DupValue(ctx, argv[0])) < 0) + goto exception; + start++; + } + return obj; + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +/* Access an Array's internal JSValue array if available */ +BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, + JSValue **arrpp, uint32_t *countp) +{ + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + *countp = p->u.array.count; + *arrpp = p->u.array.u.values; + return TRUE; + } + } + return FALSE; +} + +JSValue js_array_includes(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, val; + int64_t len, n, res; + JSValue *arrp; + uint32_t count; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + res = FALSE; + if (len > 0) { + n = 0; + if (argc > 1) { + if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) + goto exception; + } + if (js_get_fast_array(ctx, obj, &arrp, &count)) { + for (; n < count; n++) { + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), + JS_DupValue(ctx, arrp[n]), + JS_EQ_SAME_VALUE_ZERO)) { + res = TRUE; + goto done; + } + } + } + for (; n < len; n++) { + val = JS_GetPropertyInt64(ctx, obj, n); + if (JS_IsException(val)) + goto exception; + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, + JS_EQ_SAME_VALUE_ZERO)) { + res = TRUE; + break; + } + } + } + done: + JS_FreeValue(ctx, obj); + return JS_NewBool(ctx, res); + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, val; + int64_t len, n, res; + JSValue *arrp; + uint32_t count; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + res = -1; + if (len > 0) { + n = 0; + if (argc > 1) { + if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) + goto exception; + } + if (js_get_fast_array(ctx, obj, &arrp, &count)) { + for (; n < count; n++) { + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), + JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) { + res = n; + goto done; + } + } + } + for (; n < len; n++) { + int present = JS_TryGetPropertyInt64(ctx, obj, n, &val); + if (present < 0) + goto exception; + if (present) { + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { + res = n; + break; + } + } + } + } + done: + JS_FreeValue(ctx, obj); + return JS_NewInt64(ctx, res); + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, val; + int64_t len, n, res; + int present; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + res = -1; + if (len > 0) { + n = len - 1; + if (argc > 1) { + if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len)) + goto exception; + } + /* XXX: should special case fast arrays */ + for (; n >= 0; n--) { + present = JS_TryGetPropertyInt64(ctx, obj, n, &val); + if (present < 0) + goto exception; + if (present) { + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { + res = n; + break; + } + } + } + } + JS_FreeValue(ctx, obj); + return JS_NewInt64(ctx, res); + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int findIndex) +{ + JSValueConst func, this_arg; + JSValueConst args[3]; + JSValue obj, val, index_val, res; + int64_t len, k; + index_val = JS_UNDEFINED; + val = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + func = argv[0]; + if (check_function(ctx, func)) + goto exception; + this_arg = JS_UNDEFINED; + if (argc > 1) + this_arg = argv[1]; + for(k = 0; k < len; k++) { + index_val = JS_NewInt64(ctx, k); + if (JS_IsException(index_val)) + goto exception; + val = JS_GetPropertyValue(ctx, obj, index_val); + if (JS_IsException(val)) + goto exception; + args[0] = val; + args[1] = index_val; + args[2] = this_val; + res = JS_Call(ctx, func, this_arg, 3, args); + if (JS_IsException(res)) + goto exception; + if (JS_ToBoolFree(ctx, res)) { + if (findIndex) { + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return index_val; + } else { + JS_FreeValue(ctx, index_val); + JS_FreeValue(ctx, obj); + return val; + } + } + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, index_val); + } + JS_FreeValue(ctx, obj); + if (findIndex) + return JS_NewInt32(ctx, -1); + else + return JS_UNDEFINED; +exception: + JS_FreeValue(ctx, index_val); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, method, ret; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + return JS_EXCEPTION; + method = JS_GetProperty(ctx, obj, JS_ATOM_join); + if (JS_IsException(method)) { + ret = JS_EXCEPTION; + } else + if (!JS_IsFunction(ctx, method)) { + /* Use intrinsic Object.prototype.toString */ + JS_FreeValue(ctx, method); + ret = js_object_toString(ctx, obj, 0, NULL); + } else { + ret = JS_CallFree(ctx, method, obj, 0, NULL); + } + JS_FreeValue(ctx, obj); + return ret; +} + +static JSValue js_array_join(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int toLocaleString) +{ + JSValue obj, sep = JS_UNDEFINED, el; + StringBuffer b_s, *b = &b_s; + JSString *p = NULL; + int64_t i, n; + int c; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &n, obj)) + goto exception; + c = ','; /* default separator */ + if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { + sep = JS_ToString(ctx, argv[0]); + if (JS_IsException(sep)) + goto exception; + p = JS_VALUE_GET_STRING(sep); + if (p->len == 1 && !p->is_wide_char) + c = p->u.str8[0]; + else + c = -1; + } + string_buffer_init(ctx, b, 0); + for(i = 0; i < n; i++) { + if (i > 0) { + if (c >= 0) { + string_buffer_putc8(b, c); + } else { + string_buffer_concat(b, p, 0, p->len); + } + } + el = JS_GetPropertyUint32(ctx, obj, i); + if (JS_IsException(el)) + goto fail; + if (!JS_IsNull(el) && !JS_IsUndefined(el)) { + if (toLocaleString) { + el = JS_ToLocaleStringFree(ctx, el); + } + if (string_buffer_concat_value_free(b, el)) + goto fail; + } + } + JS_FreeValue(ctx, sep); + JS_FreeValue(ctx, obj); + return string_buffer_end(b); +fail: + string_buffer_free(b); + JS_FreeValue(ctx, sep); +exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_pop(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int shift) +{ + JSValue obj, res = JS_UNDEFINED; + int64_t len, newLen; + JSValue *arrp; + uint32_t count32; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + newLen = 0; + if (len > 0) { + newLen = len - 1; + /* Special case fast arrays */ + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (shift) { + res = arrp[0]; + memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp)); + p->u.array.count--; + } else { + res = arrp[count32 - 1]; + p->u.array.count--; + } + } else { + if (shift) { + res = JS_GetPropertyInt64(ctx, obj, 0); + if (JS_IsException(res)) + goto exception; + if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1)) + goto exception; + } else { + res = JS_GetPropertyInt64(ctx, obj, newLen); + if (JS_IsException(res)) + goto exception; + } + if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0) + goto exception; + } + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) + goto exception; + JS_FreeValue(ctx, obj); + return res; + exception: + JS_FreeValue(ctx, res); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +/* return -1 if exception */ +static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len) +{ + uint32_t new_size; + size_t slack; + JSValue *new_array_prop; + /* XXX: potential arithmetic overflow */ + new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); + new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); + if (!new_array_prop) + return -1; + new_size += slack / sizeof(*new_array_prop); + p->u.array.u.values = new_array_prop; + p->u.array.u1.size = new_size; + return 0; +} + +/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array = + TRUE and p->extensible = TRUE */ +int add_fast_array_element(JSContext *ctx, JSObject *p, JSValue val, int flags) +{ + uint32_t new_len, array_len; + /* extend the array by one */ + /* XXX: convert to slow array if new_len > 2^31-1 elements */ + new_len = p->u.array.count + 1; + /* update the length if necessary. We assume that if the length is + not an integer, then if it >= 2^31. */ + if (LIKELY(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { + array_len = JS_VALUE_GET_INT(p->prop[0].u.value); + if (new_len > array_len) { + if (UNLIKELY(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + } + p->prop[0].u.value = JS_NewInt32(ctx, new_len); + } + } + if (UNLIKELY(new_len > p->u.array.u1.size)) { + if (expand_fast_array(ctx, p, new_len)) { + JS_FreeValue(ctx, val); + return -1; + } + } + p->u.array.u.values[new_len - 1] = val; + p->u.array.count = new_len; + return TRUE; +} + +JSValue js_array_push(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int unshift) +{ + JSValue obj; + int i; + int64_t len, from, newLen; + obj = JS_ToObject(ctx, this_val); + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ARRAY || + !p->fast_array || !p->extensible) + goto generic_case; + /* length must be writable */ + if (UNLIKELY(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) + goto generic_case; + /* check the length */ + if (UNLIKELY(JS_VALUE_GET_TAG(p->prop[0].u.value) != JS_TAG_INT)) + goto generic_case; + len = JS_VALUE_GET_INT(p->prop[0].u.value); + /* we don't support holes */ + if (UNLIKELY(len != p->u.array.count)) + goto generic_case; + newLen = len + argc; + if (UNLIKELY(newLen > INT32_MAX)) + goto generic_case; + if (newLen > p->u.array.u1.size) { + if (expand_fast_array(ctx, p, newLen)) + goto exception; + } + if (unshift && argc > 0) { + memmove(p->u.array.u.values + argc, p->u.array.u.values, + len * sizeof(p->u.array.u.values[0])); + from = 0; + } else { + from = len; + } + for(i = 0; i < argc; i++) { + p->u.array.u.values[from + i] = JS_DupValue(ctx, argv[i]); + } + p->u.array.count = newLen; + p->prop[0].u.value = JS_NewInt32(ctx, newLen); + } else { + generic_case: + if (js_get_length64(ctx, &len, obj)) + goto exception; + newLen = len + argc; + if (newLen > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + from = len; + if (unshift && argc > 0) { + if (JS_CopySubArray(ctx, obj, argc, 0, len, -1)) + goto exception; + from = 0; + } + for(i = 0; i < argc; i++) { + if (JS_SetPropertyInt64(ctx, obj, from + i, + JS_DupValue(ctx, argv[i])) < 0) + goto exception; + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) + goto exception; + } + JS_FreeValue(ctx, obj); + return JS_NewInt64(ctx, newLen); + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, lval, hval; + JSValue *arrp; + int64_t len, l, h; + int l_present, h_present; + uint32_t count32; + lval = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + /* Special case fast arrays */ + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + uint32_t ll, hh; + if (count32 > 1) { + for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) { + lval = arrp[ll]; + arrp[ll] = arrp[hh]; + arrp[hh] = lval; + } + } + return obj; + } + for (l = 0, h = len - 1; l < h; l++, h--) { + l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval); + if (l_present < 0) + goto exception; + h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval); + if (h_present < 0) + goto exception; + if (h_present) { + if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0) + goto exception; + if (l_present) { + if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { + lval = JS_UNDEFINED; + goto exception; + } + lval = JS_UNDEFINED; + } else { + if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0) + goto exception; + } + } else { + if (l_present) { + if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0) + goto exception; + if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { + lval = JS_UNDEFINED; + goto exception; + } + lval = JS_UNDEFINED; + } + } + } + return obj; + exception: + JS_FreeValue(ctx, lval); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj) +{ + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + return TRUE; + } + } + return FALSE; +} + +static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int splice) +{ + JSValue obj, arr, val, len_val; + int64_t len, start, k, final, n, count, del_count, new_len; + int kPresent; + JSValue *arrp; + uint32_t count32, i, item_count; + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + if (splice) { + if (argc == 0) { + item_count = 0; + del_count = 0; + } else + if (argc == 1) { + item_count = 0; + del_count = len - start; + } else { + item_count = argc - 2; + if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0)) + goto exception; + } + if (len + item_count - del_count > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + count = del_count; + } else { + item_count = 0; /* avoid warning */ + final = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len)) + goto exception; + } + count = max_int64(final - start, 0); + } + len_val = JS_NewInt64(ctx, count); + arr = JS_ArraySpeciesCreate(ctx, obj, len_val); + JS_FreeValue(ctx, len_val); + if (JS_IsException(arr)) + goto exception; + k = start; + final = start + count; + n = 0; + /* The fast array test on arr ensures that + JS_CreateDataPropertyUint32() won't modify obj in case arr is + an exotic object */ + /* Special case fast arrays */ + if (js_get_fast_array(ctx, obj, &arrp, &count32) && + js_is_fast_array(ctx, arr)) { + /* XXX: should share code with fast array constructor */ + for (; k < final && k < count32; k++, n++) { + if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0) + goto exception; + } + } + /* Copy the remaining elements if any (handle case of inherited properties) */ + for (; k < final; k++, n++) { + kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val); + if (kPresent < 0) + goto exception; + if (kPresent) { + if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0) + goto exception; + } + } + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) + goto exception; + if (splice) { + new_len = len + item_count - del_count; + if (item_count != del_count) { + if (JS_CopySubArray(ctx, obj, start + item_count, + start + del_count, len - (start + del_count), + item_count <= del_count ? +1 : -1) < 0) + goto exception; + for (k = len; k-- > new_len; ) { + if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0) + goto exception; + } + } + for (i = 0; i < item_count; i++) { + if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0) + goto exception; + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0) + goto exception; + } + JS_FreeValue(ctx, obj); + return arr; + exception: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj; + int64_t len, from, to, final, count; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len)) + goto exception; + if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len)) + goto exception; + final = len; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len)) + goto exception; + } + count = min_int64(final - from, len - to); + if (JS_CopySubArray(ctx, obj, to, from, count, + (from < to && to < from + count) ? -1 : +1)) + goto exception; + return obj; + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target, + JSValueConst source, int64_t sourceLen, + int64_t targetIndex, int depth, + JSValueConst mapperFunction, + JSValueConst thisArg) +{ + JSValue element; + int64_t sourceIndex, elementLen; + int present, is_array; + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + return -1; + } + for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { + present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element); + if (present < 0) + return -1; + if (!present) + continue; + if (!JS_IsUndefined(mapperFunction)) { + JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source }; + element = JS_Call(ctx, mapperFunction, thisArg, 3, args); + JS_FreeValue(ctx, (JSValue)args[0]); + JS_FreeValue(ctx, (JSValue)args[1]); + if (JS_IsException(element)) + return -1; + } + if (depth > 0) { + is_array = JS_IsArray(ctx, element); + if (is_array < 0) + goto fail; + if (is_array) { + if (js_get_length64(ctx, &elementLen, element) < 0) + goto fail; + targetIndex = JS_FlattenIntoArray(ctx, target, element, + elementLen, targetIndex, + depth - 1, + JS_UNDEFINED, JS_UNDEFINED); + if (targetIndex < 0) + goto fail; + JS_FreeValue(ctx, element); + continue; + } + } + if (targetIndex >= MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array too long"); + goto fail; + } + if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + return -1; + targetIndex++; + } + return targetIndex; +fail: + JS_FreeValue(ctx, element); + return -1; +} + +static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int map) +{ + JSValue obj, arr; + JSValueConst mapperFunction, thisArg; + int64_t sourceLen; + int depthNum; + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &sourceLen, obj)) + goto exception; + depthNum = 1; + mapperFunction = JS_UNDEFINED; + thisArg = JS_UNDEFINED; + if (map) { + mapperFunction = argv[0]; + if (argc > 1) { + thisArg = argv[1]; + } + if (check_function(ctx, mapperFunction)) + goto exception; + } else { + if (argc > 0 && !JS_IsUndefined(argv[0])) { + if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0) + goto exception; + } + } + arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); + if (JS_IsException(arr)) + goto exception; + if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum, + mapperFunction, thisArg) < 0) + goto exception; + JS_FreeValue(ctx, obj); + return arr; +exception: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +typedef struct ValueSlot { + JSValue val; + JSString *str; + int64_t pos; +} ValueSlot; + +struct array_sort_context { + JSContext *ctx; + int exception; + int has_method; + JSValueConst method; +}; + +static int js_array_cmp_generic(const void *a, const void *b, void *opaque) { + struct array_sort_context *psc = opaque; + JSContext *ctx = psc->ctx; + JSValueConst argv[2]; + JSValue res; + ValueSlot *ap = (ValueSlot *)(void *)a; + ValueSlot *bp = (ValueSlot *)(void *)b; + int cmp; + if (psc->exception) + return 0; + if (psc->has_method) { + /* custom sort function is specified as returning 0 for identical + * objects: avoid method call overhead. + */ + if (!memcmp(&ap->val, &bp->val, sizeof(ap->val))) + goto cmp_same; + argv[0] = ap->val; + argv[1] = bp->val; + res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv); + if (JS_IsException(res)) + goto exception; + if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { + int val = JS_VALUE_GET_INT(res); + cmp = (val > 0) - (val < 0); + } else { + double val; + if (JS_ToFloat64Free(ctx, &val, res) < 0) + goto exception; + cmp = (val > 0) - (val < 0); + } + } else { + /* Not supposed to bypass ToString even for identical objects as + * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js + */ + if (!ap->str) { + JSValue str = JS_ToString(ctx, ap->val); + if (JS_IsException(str)) + goto exception; + ap->str = JS_VALUE_GET_STRING(str); + } + if (!bp->str) { + JSValue str = JS_ToString(ctx, bp->val); + if (JS_IsException(str)) + goto exception; + bp->str = JS_VALUE_GET_STRING(str); + } + cmp = js_string_compare(ctx, ap->str, bp->str); + } + if (cmp != 0) + return cmp; +cmp_same: + /* make sort stable: compare array offsets */ + return (ap->pos > bp->pos) - (ap->pos < bp->pos); +exception: + psc->exception = 1; + return 0; +} + +static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + struct array_sort_context asc = { ctx, 0, 0, argv[0] }; + JSValue obj = JS_UNDEFINED; + ValueSlot *array = NULL; + size_t array_size = 0, pos = 0, n = 0; + int64_t i, len, undefined_count = 0; + int present; + if (!JS_IsUndefined(asc.method)) { + if (check_function(ctx, asc.method)) + goto exception; + asc.has_method = 1; + } + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + /* XXX: should special case fast arrays */ + for (i = 0; i < len; i++) { + if (pos >= array_size) { + size_t new_size, slack; + ValueSlot *new_array; + new_size = (array_size + (array_size >> 1) + 31) & ~15; + new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack); + if (new_array == NULL) + goto exception; + new_size += slack / sizeof(*new_array); + array = new_array; + array_size = new_size; + } + present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val); + if (present < 0) + goto exception; + if (present == 0) + continue; + if (JS_IsUndefined(array[pos].val)) { + undefined_count++; + continue; + } + array[pos].str = NULL; + array[pos].pos = i; + pos++; + } + rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc); + if (asc.exception) + goto exception; + /* XXX: should special case fast arrays */ + while (n < pos) { + if (array[n].str) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); + if (array[n].pos == n) { + JS_FreeValue(ctx, array[n].val); + } else { + if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) { + n++; + goto exception; + } + } + n++; + } + js_free(ctx, array); + for (i = n; undefined_count-- > 0; i++) { + if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0) + goto fail; + } + for (; i < len; i++) { + if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0) + goto fail; + } + return obj; +exception: + for (; n < pos; n++) { + JS_FreeValue(ctx, array[n].val); + if (array[n].str) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); + } + js_free(ctx, array); +fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +void js_array_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSArrayIteratorData *it = p->u.array_iterator_data; + if (it) { + JS_FreeValueRT(rt, it->obj); + js_free_rt(rt, it); + } +} + +void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSArrayIteratorData *it = p->u.array_iterator_data; + if (it) { + JS_MarkValue(rt, it->obj, mark_func); + } +} + +JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab) +{ + JSValue obj; + int i; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + for(i = 0; i < len; i++) { + if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + } + return obj; +} + +JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue enum_obj, arr; + JSArrayIteratorData *it; + JSIteratorKindEnum kind; + int class_id; + + kind = magic & 3; + if (magic & 4) { + /* string iterator case */ + arr = JS_ToStringCheckObject(ctx, this_val); + class_id = JS_CLASS_STRING_ITERATOR; + } else { + arr = JS_ToObject(ctx, this_val); + class_id = JS_CLASS_ARRAY_ITERATOR; + } + if (JS_IsException(arr)) + goto fail; + enum_obj = JS_NewObjectClass(ctx, class_id); + if (JS_IsException(enum_obj)) + goto fail; + it = js_malloc(ctx, sizeof(*it)); + if (!it) + goto fail1; + it->obj = arr; + it->kind = kind; + it->idx = 0; + JS_SetOpaque(enum_obj, it); + return enum_obj; + fail1: + JS_FreeValue(ctx, enum_obj); + fail: + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + BOOL *pdone, int magic) +{ + JSArrayIteratorData *it; + uint32_t len, idx; + JSValue val, obj; + JSObject *p; + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR); + if (!it) + goto fail1; + if (JS_IsUndefined(it->obj)) + goto done; + p = JS_VALUE_GET_OBJ(it->obj); + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + if (typed_array_is_detached(ctx, p)) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail1; + } + len = p->u.array.count; + } else { + if (js_get_length32(ctx, &len, it->obj)) { + fail1: + *pdone = FALSE; + return JS_EXCEPTION; + } + } + idx = it->idx; + if (idx >= len) { + JS_FreeValue(ctx, it->obj); + it->obj = JS_UNDEFINED; + done: + *pdone = TRUE; + return JS_UNDEFINED; + } + it->idx = idx + 1; + *pdone = FALSE; + if (it->kind == JS_ITERATOR_KIND_KEY) { + return JS_NewUint32(ctx, idx); + } else { + val = JS_GetPropertyUint32(ctx, it->obj, idx); + if (JS_IsException(val)) + return JS_EXCEPTION; + if (it->kind == JS_ITERATOR_KIND_VALUE) { + return val; + } else { + JSValueConst args[2]; + JSValue num; + num = JS_NewUint32(ctx, idx); + args[0] = num; + args[1] = val; + obj = js_create_array(ctx, 2, args); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, num); + return obj; + } + } +} + +static const JSCFunctionListEntry js_array_proto_funcs[] = { + JS_CFUNC_DEF("concat", 1, js_array_concat ), + JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ), + JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ), + JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ), + JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ), + JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ), + JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ), + JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ), + JS_CFUNC_DEF("fill", 1, js_array_fill ), + JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, 0 ), + JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, 1 ), + JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ), + JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ), + JS_CFUNC_DEF("includes", 1, js_array_includes ), + JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ), + JS_CFUNC_DEF("toString", 0, js_array_toString ), + JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ), + JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ), + JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ), + JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ), + JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ), + JS_CFUNC_DEF("reverse", 0, js_array_reverse ), + JS_CFUNC_DEF("sort", 1, js_array_sort ), + JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ), + JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ), + JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ), + JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ), + JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ), + JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ), + JS_ALIAS_DEF("[Symbol.iterator]", "values" ), + JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ), + JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ), +}; + +void JS_AddIntrinsicArray(JSContext *ctx) { + JSValueConst obj; + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY], + js_array_proto_funcs, + countof(js_array_proto_funcs)); + obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1, + ctx->class_proto[JS_CLASS_ARRAY]); + ctx->array_ctor = JS_DupValue(ctx, obj); + JS_SetPropertyFunctionList(ctx, obj, js_array_funcs, + countof(js_array_funcs)); +} + +static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ), +}; + +void JS_AddArrayIteratorProto(JSContext *ctx) { + /* needed to initialize arguments[Symbol.iterator] */ + ctx->array_proto_values = + JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values); + ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR], + js_array_iterator_proto_funcs, + countof(js_array_iterator_proto_funcs)); +} + +int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, JSValue val, BOOL is_array_ctor) +{ + uint32_t tag, len; + tag = JS_VALUE_GET_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + { + int v; + v = JS_VALUE_GET_INT(val); + if (v < 0) + goto fail; + len = v; + } + break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + bf_t a; + BOOL res; + bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD); + bf_init(ctx->bf_ctx, &a); + bf_set_ui(&a, len); + res = bf_cmp_eq(&a, &p->num); + bf_delete(&a); + JS_FreeValue(ctx, val); + if (!res) + goto fail; + } + break; +#endif + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d; + d = JS_VALUE_GET_FLOAT64(val); + len = (uint32_t)d; + if (len != d) + goto fail; + } else { + uint32_t len1; + if (is_array_ctor) { + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len, val, TRUE)) + return -1; + } else { + /* legacy behavior: must do the conversion twice and compare */ + if (JS_ToUint32(ctx, &len, val)) { + JS_FreeValue(ctx, val); + return -1; + } + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE)) + return -1; + if (len1 != len) { + fail: + JS_ThrowRangeError(ctx, "invalid array length"); + return -1; + } + } + } + break; + } + *plen = len; + return 0; +} + +/* set the array length and remove the array elements if necessary. */ +int set_array_length(JSContext *ctx, JSObject *p, JSValue val, int flags) +{ + uint32_t len, idx, cur_len; + int i, ret; + /* Note: this call can reallocate the properties of 'p' */ + ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE); + if (ret) + return -1; + /* JS_ToArrayLengthFree() must be done before the read-only test */ + if (UNLIKELY(!(p->shape->prop[0].flags & JS_PROP_WRITABLE))) + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + if (LIKELY(p->fast_array)) { + uint32_t old_len = p->u.array.count; + if (len < old_len) { + for(i = len; i < old_len; i++) { + JS_FreeValue(ctx, p->u.array.u.values[i]); + } + p->u.array.count = len; + } + p->prop[0].u.value = JS_NewUint32(ctx, len); + } else { + /* Note: length is always a uint32 because the object is an + array */ + JS_ToUint32(ctx, &cur_len, p->prop[0].u.value); + if (len < cur_len) { + uint32_t d; + JSShape *sh; + JSShapeProperty *pr; + d = cur_len - len; + sh = p->shape; + if (d <= sh->prop_count) { + JSAtom atom; + /* faster to iterate */ + while (cur_len > len) { + atom = JS_NewAtomUInt32(ctx, cur_len - 1); + ret = delete_property(ctx, p, atom); + JS_FreeAtom(ctx, atom); + if (UNLIKELY(!ret)) { + /* UNLIKELY case: property is not + configurable */ + break; + } + cur_len--; + } + } else { + /* faster to iterate thru all the properties. Need two + passes in case one of the property is not + configurable */ + cur_len = len; + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; + i++, pr++) { + if (pr->atom != JS_ATOM_NULL && + JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { + if (idx >= cur_len && + !(pr->flags & JS_PROP_CONFIGURABLE)) { + cur_len = idx + 1; + } + } + } + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; + i++, pr++) { + if (pr->atom != JS_ATOM_NULL && + JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { + if (idx >= cur_len) { + /* remove the property */ + delete_property(ctx, p, pr->atom); + /* WARNING: the shape may have been modified */ + sh = p->shape; + pr = get_shape_prop(sh) + i; + } + } + } + } + } else { + cur_len = len; + } + set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len)); + if (UNLIKELY(cur_len > len)) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable"); + } + } + return TRUE; +} + +void js_array_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + int i; + for(i = 0; i < p->u.array.count; i++) { + JS_FreeValueRT(rt, p->u.array.u.values[i]); + } + js_free_rt(rt, p->u.array.u.values); +} + +void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + int i; + for(i = 0; i < p->u.array.count; i++) { + JS_MarkValue(rt, p->u.array.u.values[i], mark_func); + } +} diff --git a/third_party/quickjs/atof.c b/third_party/quickjs/atof.c new file mode 100644 index 000000000..08f291f02 --- /dev/null +++ b/third_party/quickjs/atof.c @@ -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 diff --git a/third_party/quickjs/atom.c b/third_party/quickjs/atom.c new file mode 100644 index 000000000..e96afe71b --- /dev/null +++ b/third_party/quickjs/atom.c @@ -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, ""); + } 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; +} diff --git a/third_party/quickjs/atomics.c b/third_party/quickjs/atomics.c new file mode 100644 index 000000000..78102e3b2 --- /dev/null +++ b/third_party/quickjs/atomics.c @@ -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 */ diff --git a/third_party/quickjs/bigdecimal.c b/third_party/quickjs/bigdecimal.c new file mode 100644 index 000000000..7f6c68b4d --- /dev/null +++ b/third_party/quickjs/bigdecimal.c @@ -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; +} diff --git a/third_party/quickjs/bigint.c b/third_party/quickjs/bigint.c new file mode 100644 index 000000000..0bdf49565 --- /dev/null +++ b/third_party/quickjs/bigint.c @@ -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)); +} diff --git a/third_party/quickjs/byte.c b/third_party/quickjs/byte.c new file mode 100644 index 000000000..dc694c476 --- /dev/null +++ b/third_party/quickjs/byte.c @@ -0,0 +1,2238 @@ +/* + * 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" +#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 */ + +BOOL js_class_has_bytecode(JSClassID class_id) +{ + return (class_id == JS_CLASS_BYTECODE_FUNCTION || + class_id == JS_CLASS_GENERATOR_FUNCTION || + class_id == JS_CLASS_ASYNC_FUNCTION || + class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION); +} + +/* return NULL without exception if not a function or no bytecode */ +JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return NULL; + p = JS_VALUE_GET_OBJ(val); + if (!js_class_has_bytecode(p->class_id)) + return NULL; + return p->u.func.function_bytecode; +} + +void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) +{ + int i; +#if 0 + { + char buf[ATOM_GET_STR_BUF_SIZE]; + printf("freeing %s\n", + JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); + } +#endif + free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE); + if (b->vardefs) { + for(i = 0; i < b->arg_count + b->var_count; i++) { + JS_FreeAtomRT(rt, b->vardefs[i].var_name); + } + } + for(i = 0; i < b->cpool_count; i++) + JS_FreeValueRT(rt, b->cpool[i]); + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + JS_FreeAtomRT(rt, cv->var_name); + } + if (b->realm) + JS_FreeContext(b->realm); + JS_FreeAtomRT(rt, b->func_name); + if (b->has_debug) { + JS_FreeAtomRT(rt, b->debug.filename); + js_free_rt(rt, b->debug.pc2line_buf); + js_free_rt(rt, b->debug.source); + } + remove_gc_object(&b->header); + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) { + list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list); + } else { + js_free_rt(rt, b); + } +} + +void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p1, *p = JS_VALUE_GET_OBJ(val); + JSFunctionBytecode *b; + JSVarRef **var_refs; + int i; + p1 = p->u.func.home_object; + if (p1) { + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + b = p->u.func.function_bytecode; + if (b) { + var_refs = p->u.func.var_refs; + if (var_refs) { + for(i = 0; i < b->closure_var_count; i++) + free_var_ref(rt, var_refs[i]); + js_free_rt(rt, var_refs); + } + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b)); + } +} + +void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSVarRef **var_refs = p->u.func.var_refs; + JSFunctionBytecode *b = p->u.func.function_bytecode; + int i; + if (p->u.func.home_object) { + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object), + mark_func); + } + if (b) { + if (var_refs) { + for(i = 0; i < b->closure_var_count; i++) { + JSVarRef *var_ref = var_refs[i]; + if (var_ref && var_ref->is_detached) { + mark_func(rt, &var_ref->header); + } + } + } + /* must mark the function bytecode because template objects may be + part of a cycle */ + JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func); + } +} + +int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b, + int byte_code_offset, uint32_t bc_len) +{ + uint8_t *bc_buf; + int pos, len, op; + JSAtom atom; + uint32_t idx; + if (s->is_rom_data) { + /* directly use the input buffer */ + if (UNLIKELY(s->buf_end - s->ptr < bc_len)) + return bc_read_error_end(s); + bc_buf = (uint8_t *)s->ptr; + s->ptr += bc_len; + } else { + bc_buf = (void *)((uint8_t*)b + byte_code_offset); + if (bc_get_buf(s, bc_buf, bc_len)) + return -1; + } + b->byte_code_buf = bc_buf; + pos = 0; + while (pos < bc_len) { + op = bc_buf[pos]; + len = short_opcode_info(op).size; + switch(short_opcode_info(op).fmt) { + case OP_FMT_atom: + case OP_FMT_atom_u8: + case OP_FMT_atom_u16: + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + idx = get_u32(bc_buf + pos + 1); + if (s->is_rom_data) { + /* just increment the reference count of the atom */ + JS_DupAtom(s->ctx, (JSAtom)idx); + } else { + if (bc_idx_to_atom(s, &atom, idx)) { + /* Note: the atoms will be freed up to this position */ + b->byte_code_len = pos; + return -1; + } + put_u32(bc_buf + pos + 1, atom); +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n"); +#endif + } + break; + default: + break; + } + pos += len; + } + return 0; +} + +typedef enum BCTagEnum { + BC_TAG_NULL = 1, + BC_TAG_UNDEFINED, + BC_TAG_BOOL_FALSE, + BC_TAG_BOOL_TRUE, + BC_TAG_INT32, + BC_TAG_FLOAT64, + BC_TAG_STRING, + BC_TAG_OBJECT, + BC_TAG_ARRAY, + BC_TAG_BIG_INT, + BC_TAG_BIG_FLOAT, + BC_TAG_BIG_DECIMAL, + BC_TAG_TEMPLATE_OBJECT, + BC_TAG_FUNCTION_BYTECODE, + BC_TAG_MODULE, + BC_TAG_TYPED_ARRAY, + BC_TAG_ARRAY_BUFFER, + BC_TAG_SHARED_ARRAY_BUFFER, + BC_TAG_DATE, + BC_TAG_OBJECT_VALUE, + BC_TAG_OBJECT_REFERENCE, +} BCTagEnum; + +#ifdef CONFIG_BIGNUM +#define BC_BASE_VERSION 2 +#else +#define BC_BASE_VERSION 1 +#endif +#define BC_BE_VERSION 0x40 +#ifdef WORDS_BIGENDIAN +#define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION) +#else +#define BC_VERSION BC_BASE_VERSION +#endif + +typedef struct { + JSObject *obj; + uint32_t hash_next; /* -1 if no next entry */ +} JSObjectListEntry; + +/* XXX: reuse it to optimize weak references */ +typedef struct { + JSObjectListEntry *object_tab; + int object_count; + int object_size; + uint32_t *hash_table; + uint32_t hash_size; +} JSObjectList; + +static void js_object_list_init(JSObjectList *s) +{ + memset(s, 0, sizeof(*s)); +} + +static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size) +{ + return ((uintptr_t)p * 3163) & (hash_size - 1); +} + +static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s, + uint32_t new_hash_size) +{ + JSObjectListEntry *e; + uint32_t i, h, *new_hash_table; + new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size); + if (!new_hash_table) + return -1; + js_free(ctx, s->hash_table); + s->hash_table = new_hash_table; + s->hash_size = new_hash_size; + for(i = 0; i < s->hash_size; i++) { + s->hash_table[i] = -1; + } + for(i = 0; i < s->object_count; i++) { + e = &s->object_tab[i]; + h = js_object_list_get_hash(e->obj, s->hash_size); + e->hash_next = s->hash_table[h]; + s->hash_table[h] = i; + } + return 0; +} + +/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if + memory error */ +static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj) +{ + JSObjectListEntry *e; + uint32_t h, new_hash_size; + if (js_resize_array(ctx, (void *)&s->object_tab, + sizeof(s->object_tab[0]), + &s->object_size, s->object_count + 1)) + return -1; + if (UNLIKELY((s->object_count + 1) >= s->hash_size)) { + new_hash_size = max_uint32(s->hash_size, 4); + while (new_hash_size <= s->object_count) + new_hash_size *= 2; + if (js_object_list_resize_hash(ctx, s, new_hash_size)) + return -1; + } + e = &s->object_tab[s->object_count++]; + h = js_object_list_get_hash(obj, s->hash_size); + e->obj = obj; + e->hash_next = s->hash_table[h]; + s->hash_table[h] = s->object_count - 1; + return 0; +} + +/* return -1 if not present or the object index */ +static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj) +{ + JSObjectListEntry *e; + uint32_t h, p; + /* must test empty size because there is no hash table */ + if (s->object_count == 0) + return -1; + h = js_object_list_get_hash(obj, s->hash_size); + p = s->hash_table[h]; + while (p != -1) { + e = &s->object_tab[p]; + if (e->obj == obj) + return p; + p = e->hash_next; + } + return -1; +} + +static void js_object_list_end(JSContext *ctx, JSObjectList *s) +{ + js_free(ctx, s->object_tab); + js_free(ctx, s->hash_table); +} + +typedef struct BCWriterState { + JSContext *ctx; + DynBuf dbuf; + BOOL byte_swap : 8; + BOOL allow_bytecode : 8; + BOOL allow_sab : 8; + BOOL allow_reference : 8; + uint32_t first_atom; + uint32_t *atom_to_idx; + int atom_to_idx_size; + JSAtom *idx_to_atom; + int idx_to_atom_count; + int idx_to_atom_size; + uint8_t **sab_tab; + int sab_tab_len; + int sab_tab_size; + /* list of referenced objects (used if allow_reference = TRUE) */ + JSObjectList object_list; +} BCWriterState; + +#ifdef DUMP_READ_OBJECT +static const char * const bc_tag_str[] = { + "invalid", + "null", + "undefined", + "false", + "true", + "int32", + "float64", + "string", + "object", + "array", + "bigint", + "bigfloat", + "bigdecimal", + "template", + "function", + "module", + "TypedArray", + "ArrayBuffer", + "SharedArrayBuffer", + "Date", + "ObjectValue", + "ObjectReference", +}; +#endif + +static void bc_put_u8(BCWriterState *s, uint8_t v) +{ + dbuf_putc(&s->dbuf, v); +} + +static void bc_put_u16(BCWriterState *s, uint16_t v) +{ + if (s->byte_swap) + v = bswap16(v); + dbuf_put_u16(&s->dbuf, v); +} + +static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v) +{ + if (s->byte_swap) + v = bswap32(v); + dbuf_put_u32(&s->dbuf, v); +} + +static void bc_put_u64(BCWriterState *s, uint64_t v) +{ + if (s->byte_swap) + v = bswap64(v); + dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v)); +} + +static void bc_put_leb128(BCWriterState *s, uint32_t v) +{ + dbuf_put_leb128(&s->dbuf, v); +} + +static void bc_put_sleb128(BCWriterState *s, int32_t v) +{ + dbuf_put_sleb128(&s->dbuf, v); +} + +static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n) +{ + *pflags = *pflags | (val << *pidx); + *pidx += n; +} + +static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom) +{ + uint32_t v; + if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) { + *pres = atom; + return 0; + } + atom -= s->first_atom; + if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) { + *pres = s->atom_to_idx[atom]; + return 0; + } + if (atom >= s->atom_to_idx_size) { + int old_size, i; + old_size = s->atom_to_idx_size; + if (js_resize_array(s->ctx, (void **)&s->atom_to_idx, + sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size, + atom + 1)) + return -1; + /* XXX: could add a specific js_resize_array() function to do it */ + for(i = old_size; i < s->atom_to_idx_size; i++) + s->atom_to_idx[i] = 0; + } + if (js_resize_array(s->ctx, (void **)&s->idx_to_atom, + sizeof(s->idx_to_atom[0]), + &s->idx_to_atom_size, s->idx_to_atom_count + 1)) + goto fail; + v = s->idx_to_atom_count++; + s->idx_to_atom[v] = atom + s->first_atom; + v += s->first_atom; + s->atom_to_idx[atom] = v; + *pres = v; + return 0; + fail: + *pres = 0; + return -1; +} + +static int bc_put_atom(BCWriterState *s, JSAtom atom) +{ + uint32_t v; + if (__JS_AtomIsTaggedInt(atom)) { + v = (__JS_AtomToUInt32(atom) << 1) | 1; + } else { + if (bc_atom_to_idx(s, &v, atom)) + return -1; + v <<= 1; + } + bc_put_leb128(s, v); + return 0; +} + +static void bc_byte_swap(uint8_t *bc_buf, int bc_len) +{ + int pos, len, op, fmt; + pos = 0; + while (pos < bc_len) { + op = bc_buf[pos]; + len = short_opcode_info(op).size; + fmt = short_opcode_info(op).fmt; + switch(fmt) { + case OP_FMT_u16: + case OP_FMT_i16: + case OP_FMT_label16: + case OP_FMT_npop: + case OP_FMT_loc: + case OP_FMT_arg: + case OP_FMT_var_ref: + put_u16(bc_buf + pos + 1, + bswap16(get_u16(bc_buf + pos + 1))); + break; + case OP_FMT_i32: + case OP_FMT_u32: + case OP_FMT_const: + case OP_FMT_label: + case OP_FMT_atom: + case OP_FMT_atom_u8: + put_u32(bc_buf + pos + 1, + bswap32(get_u32(bc_buf + pos + 1))); + break; + case OP_FMT_atom_u16: + case OP_FMT_label_u16: + put_u32(bc_buf + pos + 1, + bswap32(get_u32(bc_buf + pos + 1))); + put_u16(bc_buf + pos + 1 + 4, + bswap16(get_u16(bc_buf + pos + 1 + 4))); + break; + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + put_u32(bc_buf + pos + 1, + bswap32(get_u32(bc_buf + pos + 1))); + put_u32(bc_buf + pos + 1 + 4, + bswap32(get_u32(bc_buf + pos + 1 + 4))); + if (fmt == OP_FMT_atom_label_u16) { + put_u16(bc_buf + pos + 1 + 4 + 4, + bswap16(get_u16(bc_buf + pos + 1 + 4 + 4))); + } + break; + case OP_FMT_npop_u16: + put_u16(bc_buf + pos + 1, + bswap16(get_u16(bc_buf + pos + 1))); + put_u16(bc_buf + pos + 1 + 2, + bswap16(get_u16(bc_buf + pos + 1 + 2))); + break; + default: + break; + } + pos += len; + } +} + +static int JS_WriteFunctionBytecode(BCWriterState *s, + const uint8_t *bc_buf1, int bc_len) +{ + int pos, len, op; + JSAtom atom; + uint8_t *bc_buf; + uint32_t val; + bc_buf = js_malloc(s->ctx, bc_len); + if (!bc_buf) + return -1; + memcpy(bc_buf, bc_buf1, bc_len); + pos = 0; + while (pos < bc_len) { + op = bc_buf[pos]; + len = short_opcode_info(op).size; + switch(short_opcode_info(op).fmt) { + case OP_FMT_atom: + case OP_FMT_atom_u8: + case OP_FMT_atom_u16: + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + atom = get_u32(bc_buf + pos + 1); + if (bc_atom_to_idx(s, &val, atom)) + goto fail; + put_u32(bc_buf + pos + 1, val); + break; + default: + break; + } + pos += len; + } + if (s->byte_swap) + bc_byte_swap(bc_buf, bc_len); + dbuf_put(&s->dbuf, bc_buf, bc_len); + js_free(s->ctx, bc_buf); + return 0; + fail: + js_free(s->ctx, bc_buf); + return -1; +} + +static void JS_WriteString(BCWriterState *s, JSString *p) +{ + int i; + bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char); + if (p->is_wide_char) { + for(i = 0; i < p->len; i++) + bc_put_u16(s, p->u.str16[i]); + } else { + dbuf_put(&s->dbuf, p->u.str8, p->len); + } +} + +#ifdef CONFIG_BIGNUM +static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj) +{ + uint32_t tag, tag1; + int64_t e; + JSBigFloat *bf = JS_VALUE_GET_PTR(obj); + bf_t *a = &bf->num; + size_t len, i, n1, j; + limb_t v; + tag = JS_VALUE_GET_TAG(obj); + switch(tag) { + case JS_TAG_BIG_INT: + tag1 = BC_TAG_BIG_INT; + break; + case JS_TAG_BIG_FLOAT: + tag1 = BC_TAG_BIG_FLOAT; + break; + case JS_TAG_BIG_DECIMAL: + tag1 = BC_TAG_BIG_DECIMAL; + break; + default: + abort(); + } + bc_put_u8(s, tag1); + /* sign + exponent */ + if (a->expn == BF_EXP_ZERO) + e = 0; + else if (a->expn == BF_EXP_INF) + e = 1; + else if (a->expn == BF_EXP_NAN) + e = 2; + else if (a->expn >= 0) + e = a->expn + 3; + else + e = a->expn; + e = (e << 1) | a->sign; + if (e < INT32_MIN || e > INT32_MAX) { + JS_ThrowInternalError(s->ctx, "bignum exponent is too large"); + return -1; + } + bc_put_sleb128(s, e); + /* mantissa */ + if (a->len != 0) { + if (tag != JS_TAG_BIG_DECIMAL) { + i = 0; + while (i < a->len && a->tab[i] == 0) + i++; + assert(i < a->len); + v = a->tab[i]; + n1 = sizeof(limb_t); + while ((v & 0xff) == 0) { + n1--; + v >>= 8; + } + i++; + len = (a->len - i) * sizeof(limb_t) + n1; + if (len > INT32_MAX) { + JS_ThrowInternalError(s->ctx, "bignum is too large"); + return -1; + } + bc_put_leb128(s, len); + /* always saved in byte based little endian representation */ + for(j = 0; j < n1; j++) { + dbuf_putc(&s->dbuf, v >> (j * 8)); + } + for(; i < a->len; i++) { + limb_t v = a->tab[i]; +#if LIMB_BITS == 32 +#ifdef WORDS_BIGENDIAN + v = bswap32(v); +#endif + dbuf_put_u32(&s->dbuf, v); +#else +#ifdef WORDS_BIGENDIAN + v = bswap64(v); +#endif + dbuf_put_u64(&s->dbuf, v); +#endif + } + } else { + int bpos, d; + uint8_t v8; + size_t i0; + /* little endian BCD */ + i = 0; + while (i < a->len && a->tab[i] == 0) + i++; + assert(i < a->len); + len = a->len * LIMB_DIGITS; + v = a->tab[i]; + j = 0; + while ((v % 10) == 0) { + j++; + v /= 10; + } + len -= j; + assert(len > 0); + if (len > INT32_MAX) { + JS_ThrowInternalError(s->ctx, "bignum is too large"); + return -1; + } + bc_put_leb128(s, len); + bpos = 0; + v8 = 0; + i0 = i; + for(; i < a->len; i++) { + if (i != i0) { + v = a->tab[i]; + j = 0; + } + for(; j < LIMB_DIGITS; j++) { + d = v % 10; + v /= 10; + if (bpos == 0) { + v8 = d; + bpos = 1; + } else { + dbuf_putc(&s->dbuf, v8 | (d << 4)); + bpos = 0; + } + } + } + /* flush the last digit */ + if (bpos) { + dbuf_putc(&s->dbuf, v8); + } + } + } + return 0; +} +#endif /* CONFIG_BIGNUM */ + +static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj); + +static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) +{ + JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj); + uint32_t flags; + int idx, i; + bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE); + flags = idx = 0; + bc_set_flags(&flags, &idx, b->has_prototype, 1); + bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1); + bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1); + bc_set_flags(&flags, &idx, b->need_home_object, 1); + bc_set_flags(&flags, &idx, b->func_kind, 2); + bc_set_flags(&flags, &idx, b->new_target_allowed, 1); + bc_set_flags(&flags, &idx, b->super_call_allowed, 1); + bc_set_flags(&flags, &idx, b->super_allowed, 1); + bc_set_flags(&flags, &idx, b->arguments_allowed, 1); + bc_set_flags(&flags, &idx, b->has_debug, 1); + bc_set_flags(&flags, &idx, b->backtrace_barrier, 1); + assert(idx <= 16); + bc_put_u16(s, flags); + bc_put_u8(s, b->js_mode); + bc_put_atom(s, b->func_name); + bc_put_leb128(s, b->arg_count); + bc_put_leb128(s, b->var_count); + bc_put_leb128(s, b->defined_arg_count); + bc_put_leb128(s, b->stack_size); + bc_put_leb128(s, b->closure_var_count); + bc_put_leb128(s, b->cpool_count); + bc_put_leb128(s, b->byte_code_len); + if (b->vardefs) { + /* XXX: this field is redundant */ + bc_put_leb128(s, b->arg_count + b->var_count); + for(i = 0; i < b->arg_count + b->var_count; i++) { + JSVarDef *vd = &b->vardefs[i]; + bc_put_atom(s, vd->var_name); + bc_put_leb128(s, vd->scope_level); + bc_put_leb128(s, vd->scope_next + 1); + flags = idx = 0; + bc_set_flags(&flags, &idx, vd->var_kind, 4); + bc_set_flags(&flags, &idx, vd->is_const, 1); + bc_set_flags(&flags, &idx, vd->is_lexical, 1); + bc_set_flags(&flags, &idx, vd->is_captured, 1); + assert(idx <= 8); + bc_put_u8(s, flags); + } + } else { + bc_put_leb128(s, 0); + } + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + bc_put_atom(s, cv->var_name); + bc_put_leb128(s, cv->var_idx); + flags = idx = 0; + bc_set_flags(&flags, &idx, cv->is_local, 1); + bc_set_flags(&flags, &idx, cv->is_arg, 1); + bc_set_flags(&flags, &idx, cv->is_const, 1); + bc_set_flags(&flags, &idx, cv->is_lexical, 1); + bc_set_flags(&flags, &idx, cv->var_kind, 4); + assert(idx <= 8); + bc_put_u8(s, flags); + } + if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len)) + goto fail; + if (b->has_debug) { + bc_put_atom(s, b->debug.filename); + bc_put_leb128(s, b->debug.line_num); + bc_put_leb128(s, b->debug.pc2line_len); + dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len); + } + for(i = 0; i < b->cpool_count; i++) { + if (JS_WriteObjectRec(s, b->cpool[i])) + goto fail; + } + return 0; + fail: + return -1; +} + +static int JS_WriteModule(BCWriterState *s, JSValueConst obj) +{ + JSModuleDef *m = JS_VALUE_GET_PTR(obj); + int i; + bc_put_u8(s, BC_TAG_MODULE); + bc_put_atom(s, m->module_name); + bc_put_leb128(s, m->req_module_entries_count); + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + bc_put_atom(s, rme->module_name); + } + bc_put_leb128(s, m->export_entries_count); + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + bc_put_u8(s, me->export_type); + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + bc_put_leb128(s, me->u.local.var_idx); + } else { + bc_put_leb128(s, me->u.req_module_idx); + bc_put_atom(s, me->local_name); + } + bc_put_atom(s, me->export_name); + } + bc_put_leb128(s, m->star_export_entries_count); + for(i = 0; i < m->star_export_entries_count; i++) { + JSStarExportEntry *se = &m->star_export_entries[i]; + bc_put_leb128(s, se->req_module_idx); + } + bc_put_leb128(s, m->import_entries_count); + for(i = 0; i < m->import_entries_count; i++) { + JSImportEntry *mi = &m->import_entries[i]; + bc_put_leb128(s, mi->var_idx); + bc_put_atom(s, mi->import_name); + bc_put_leb128(s, mi->req_module_idx); + } + if (JS_WriteObjectRec(s, m->func_obj)) + goto fail; + return 0; + fail: + return -1; +} + +static int JS_WriteArray(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + uint32_t i, len; + JSValue val; + int ret; + BOOL is_template; + if (s->allow_bytecode && !p->extensible) { + /* not extensible array: we consider it is a + template when we are saving bytecode */ + bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT); + is_template = TRUE; + } else { + bc_put_u8(s, BC_TAG_ARRAY); + is_template = FALSE; + } + if (js_get_length32(s->ctx, &len, obj)) + goto fail1; + bc_put_leb128(s, len); + for(i = 0; i < len; i++) { + val = JS_GetPropertyUint32(s->ctx, obj, i); + if (JS_IsException(val)) + goto fail1; + ret = JS_WriteObjectRec(s, val); + JS_FreeValue(s->ctx, val); + if (ret) + goto fail1; + } + if (is_template) { + val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw); + if (JS_IsException(val)) + goto fail1; + ret = JS_WriteObjectRec(s, val); + JS_FreeValue(s->ctx, val); + if (ret) + goto fail1; + } + return 0; + fail1: + return -1; +} + +static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v) +{ + return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING; +} + +static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + uint32_t i, prop_count; + JSShape *sh; + JSShapeProperty *pr; + int pass; + JSAtom atom; + bc_put_u8(s, BC_TAG_OBJECT); + prop_count = 0; + sh = p->shape; + for(pass = 0; pass < 2; pass++) { + if (pass == 1) + bc_put_leb128(s, prop_count); + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { + atom = pr->atom; + if (atom != JS_ATOM_NULL && + JS_AtomIsString(s->ctx, atom) && + (pr->flags & JS_PROP_ENUMERABLE)) { + if (pr->flags & JS_PROP_TMASK) { + JS_ThrowTypeError(s->ctx, "only value properties are supported"); + goto fail; + } + if (pass == 0) { + prop_count++; + } else { + bc_put_atom(s, atom); + if (JS_WriteObjectRec(s, p->prop[i].u.value)) + goto fail; + } + } + } + } + return 0; + fail: + return -1; +} + +static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + JSTypedArray *ta = p->u.typed_array; + bc_put_u8(s, BC_TAG_TYPED_ARRAY); + bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY); + bc_put_leb128(s, p->u.array.count); + bc_put_leb128(s, ta->offset); + if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) + return -1; + return 0; +} + +static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + JSArrayBuffer *abuf = p->u.array_buffer; + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx); + return -1; + } + bc_put_u8(s, BC_TAG_ARRAY_BUFFER); + bc_put_leb128(s, abuf->byte_length); + dbuf_put(&s->dbuf, abuf->data, abuf->byte_length); + return 0; +} + +static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + JSArrayBuffer *abuf = p->u.array_buffer; + assert(!abuf->detached); /* SharedArrayBuffer are never detached */ + bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER); + bc_put_leb128(s, abuf->byte_length); + bc_put_u64(s, (uintptr_t)abuf->data); + if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]), + &s->sab_tab_size, s->sab_tab_len + 1)) + return -1; + /* keep the SAB pointer so that the user can clone it or free it */ + s->sab_tab[s->sab_tab_len++] = abuf->data; + return 0; +} + +static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) +{ + uint32_t tag; + if (js_check_stack_overflow(s->ctx->rt, 0)) { + JS_ThrowStackOverflow(s->ctx); + return -1; + } + tag = JS_VALUE_GET_NORM_TAG(obj); + switch(tag) { + case JS_TAG_NULL: + bc_put_u8(s, BC_TAG_NULL); + break; + case JS_TAG_UNDEFINED: + bc_put_u8(s, BC_TAG_UNDEFINED); + break; + case JS_TAG_BOOL: + bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj)); + break; + case JS_TAG_INT: + bc_put_u8(s, BC_TAG_INT32); + bc_put_sleb128(s, JS_VALUE_GET_INT(obj)); + break; + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + bc_put_u8(s, BC_TAG_FLOAT64); + u.d = JS_VALUE_GET_FLOAT64(obj); + bc_put_u64(s, u.u64); + } + break; + case JS_TAG_STRING: + { + JSString *p = JS_VALUE_GET_STRING(obj); + bc_put_u8(s, BC_TAG_STRING); + JS_WriteString(s, p); + } + break; + case JS_TAG_FUNCTION_BYTECODE: + if (!s->allow_bytecode) + goto invalid_tag; + if (JS_WriteFunctionTag(s, obj)) + goto fail; + break; + case JS_TAG_MODULE: + if (!s->allow_bytecode) + goto invalid_tag; + if (JS_WriteModule(s, obj)) + goto fail; + break; + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(obj); + int ret, idx; + if (s->allow_reference) { + idx = js_object_list_find(s->ctx, &s->object_list, p); + if (idx >= 0) { + bc_put_u8(s, BC_TAG_OBJECT_REFERENCE); + bc_put_leb128(s, idx); + break; + } else { + if (js_object_list_add(s->ctx, &s->object_list, p)) + goto fail; + } + } else { + if (p->tmp_mark) { + JS_ThrowTypeError(s->ctx, "circular reference"); + goto fail; + } + p->tmp_mark = 1; + } + switch(p->class_id) { + case JS_CLASS_ARRAY: + ret = JS_WriteArray(s, obj); + break; + case JS_CLASS_OBJECT: + ret = JS_WriteObjectTag(s, obj); + break; + case JS_CLASS_ARRAY_BUFFER: + ret = JS_WriteArrayBuffer(s, obj); + break; + case JS_CLASS_SHARED_ARRAY_BUFFER: + if (!s->allow_sab) + goto invalid_tag; + ret = JS_WriteSharedArrayBuffer(s, obj); + break; + case JS_CLASS_DATE: + bc_put_u8(s, BC_TAG_DATE); + ret = JS_WriteObjectRec(s, p->u.object_data); + break; + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT: + case JS_CLASS_BIG_FLOAT: + case JS_CLASS_BIG_DECIMAL: +#endif + bc_put_u8(s, BC_TAG_OBJECT_VALUE); + ret = JS_WriteObjectRec(s, p->u.object_data); + break; + default: + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_WriteTypedArray(s, obj); + } else { + JS_ThrowTypeError(s->ctx, "unsupported object class"); + ret = -1; + } + break; + } + p->tmp_mark = 0; + if (ret) + goto fail; + } + break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + case JS_TAG_BIG_DECIMAL: + if (JS_WriteBigNum(s, obj)) + goto fail; + break; +#endif + default: + invalid_tag: + JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag); + goto fail; + } + return 0; + fail: + return -1; +} + +/* create the atom table */ +static int JS_WriteObjectAtoms(BCWriterState *s) +{ + JSRuntime *rt = s->ctx->rt; + DynBuf dbuf1; + int i, atoms_size; + uint8_t version; + dbuf1 = s->dbuf; + js_dbuf_init(s->ctx, &s->dbuf); + version = BC_VERSION; + if (s->byte_swap) + version ^= BC_BE_VERSION; + bc_put_u8(s, version); + bc_put_leb128(s, s->idx_to_atom_count); + for(i = 0; i < s->idx_to_atom_count; i++) { + JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]]; + JS_WriteString(s, p); + } + /* XXX: should check for OOM in above phase */ + /* move the atoms at the start */ + /* XXX: could just append dbuf1 data, but it uses more memory if + dbuf1 is larger than dbuf */ + atoms_size = s->dbuf.size; + if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size)) + goto fail; + memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size); + memcpy(dbuf1.buf, s->dbuf.buf, atoms_size); + dbuf1.size += atoms_size; + dbuf_free(&s->dbuf); + s->dbuf = dbuf1; + return 0; + fail: + dbuf_free(&dbuf1); + return -1; +} + +uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, + int flags, uint8_t ***psab_tab, size_t *psab_tab_len) +{ + BCWriterState ss, *s = &ss; + memset(s, 0, sizeof(*s)); + s->ctx = ctx; + /* XXX: byte swapped output is untested */ + s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0); + s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0); + s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0); + s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0); + /* XXX: could use a different version when bytecode is included */ + if (s->allow_bytecode) + s->first_atom = JS_ATOM_END; + else + s->first_atom = 1; + js_dbuf_init(ctx, &s->dbuf); + js_object_list_init(&s->object_list); + if (JS_WriteObjectRec(s, obj)) + goto fail; + if (JS_WriteObjectAtoms(s)) + goto fail; + js_object_list_end(ctx, &s->object_list); + js_free(ctx, s->atom_to_idx); + js_free(ctx, s->idx_to_atom); + *psize = s->dbuf.size; + if (psab_tab) + *psab_tab = s->sab_tab; + if (psab_tab_len) + *psab_tab_len = s->sab_tab_len; + return s->dbuf.buf; + fail: + js_object_list_end(ctx, &s->object_list); + js_free(ctx, s->atom_to_idx); + js_free(ctx, s->idx_to_atom); + dbuf_free(&s->dbuf); + *psize = 0; + if (psab_tab) + *psab_tab = NULL; + if (psab_tab_len) + *psab_tab_len = 0; + return NULL; +} + +uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, + int flags) +{ + return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL); +} + +#ifdef DUMP_READ_OBJECT +static void printfesque(2) bc_read_trace(BCReaderState *s, const char *fmt, ...) { + va_list ap; + int i, n, n0; + if (!s->ptr_last) + s->ptr_last = s->buf_start; + n = n0 = 0; + if (s->ptr > s->ptr_last || s->ptr == s->buf_start) { + n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start)); + n += n0; + } + for (i = 0; s->ptr_last < s->ptr; i++) { + if ((i & 7) == 0 && i > 0) { + printf("\n%*s", n0, ""); + n = n0; + } + n += printf(" %02x", *s->ptr_last++); + } + if (*fmt == '}') + s->level--; + if (n < 32 + s->level * 2) { + printf("%*s", 32 + s->level * 2 - n, ""); + } + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + if (strchr(fmt, '{')) + s->level++; +} +#else +#define bc_read_trace(...) +#endif + +int bc_read_error_end(BCReaderState *s) +{ + if (!s->error_state) { + JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer"); + } + return s->error_state = -1; +} + +static int bc_get_u8(BCReaderState *s, uint8_t *pval) +{ + if (UNLIKELY(s->buf_end - s->ptr < 1)) { + *pval = 0; /* avoid warning */ + return bc_read_error_end(s); + } + *pval = *s->ptr++; + return 0; +} + +static int bc_get_u16(BCReaderState *s, uint16_t *pval) +{ + if (UNLIKELY(s->buf_end - s->ptr < 2)) { + *pval = 0; /* avoid warning */ + return bc_read_error_end(s); + } + *pval = get_u16(s->ptr); + s->ptr += 2; + return 0; +} + +static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval) +{ + if (UNLIKELY(s->buf_end - s->ptr < 4)) { + *pval = 0; /* avoid warning */ + return bc_read_error_end(s); + } + *pval = get_u32(s->ptr); + s->ptr += 4; + return 0; +} + +static int bc_get_u64(BCReaderState *s, uint64_t *pval) +{ + if (UNLIKELY(s->buf_end - s->ptr < 8)) { + *pval = 0; /* avoid warning */ + return bc_read_error_end(s); + } + *pval = get_u64(s->ptr); + s->ptr += 8; + return 0; +} + +static int bc_get_leb128(BCReaderState *s, uint32_t *pval) +{ + int ret; + ret = get_leb128(pval, s->ptr, s->buf_end); + if (UNLIKELY(ret < 0)) + return bc_read_error_end(s); + s->ptr += ret; + return 0; +} + +static int bc_get_sleb128(BCReaderState *s, int32_t *pval) +{ + int ret; + ret = get_sleb128(pval, s->ptr, s->buf_end); + if (UNLIKELY(ret < 0)) + return bc_read_error_end(s); + s->ptr += ret; + return 0; +} + +/* XXX: used to read an `int` with a positive value */ +static int bc_get_leb128_int(BCReaderState *s, int *pval) +{ + return bc_get_leb128(s, (uint32_t *)pval); +} + +static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval) +{ + uint32_t val; + if (bc_get_leb128(s, &val)) { + *pval = 0; + return -1; + } + *pval = val; + return 0; +} + +int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len) +{ + if (buf_len != 0) { + if (UNLIKELY(!buf || s->buf_end - s->ptr < buf_len)) + return bc_read_error_end(s); + memcpy(buf, s->ptr, buf_len); + s->ptr += buf_len; + } + return 0; +} + +int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx) +{ + JSAtom atom; + if (__JS_AtomIsTaggedInt(idx)) { + atom = idx; + } else if (idx < s->first_atom) { + atom = JS_DupAtom(s->ctx, idx); + } else { + idx -= s->first_atom; + if (idx >= s->idx_to_atom_count) { + JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)", + (unsigned int)(s->ptr - s->buf_start)); + *patom = JS_ATOM_NULL; + return s->error_state = -1; + } + atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]); + } + *patom = atom; + return 0; +} + +static int bc_get_atom(BCReaderState *s, JSAtom *patom) +{ + uint32_t v; + if (bc_get_leb128(s, &v)) + return -1; + if (v & 1) { + *patom = __JS_AtomFromUInt32(v >> 1); + return 0; + } else { + return bc_idx_to_atom(s, patom, v >> 1); + } +} + +static JSString *JS_ReadString(BCReaderState *s) +{ + uint32_t len; + size_t size; + BOOL is_wide_char; + JSString *p; + if (bc_get_leb128(s, &len)) + return NULL; + is_wide_char = len & 1; + len >>= 1; + p = js_alloc_string(s->ctx, len, is_wide_char); + if (!p) { + s->error_state = -1; + return NULL; + } + size = (size_t)len << is_wide_char; + if ((s->buf_end - s->ptr) < size) { + bc_read_error_end(s); + js_free_string(s->ctx->rt, p); + return NULL; + } + memcpy(p->u.str8, s->ptr, size); + s->ptr += size; + if (!is_wide_char) { + p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */ + } +#ifdef DUMP_READ_OBJECT + JS_DumpString(s->ctx->rt, p); printf("\n"); +#endif + return p; +} + +static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n) +{ + uint32_t val; + /* XXX: this does not work for n == 32 */ + val = (flags >> *pidx) & ((1U << n) - 1); + *pidx += n; + return val; +} + +static JSBigFloat *js_new_bf(JSContext *ctx) +{ + JSBigFloat *p; + p = js_malloc(ctx, sizeof(*p)); + if (!p) + return NULL; + p->header.ref_count = 1; + bf_init(ctx->bf_ctx, &p->num); + return p; +} + +#ifdef CONFIG_BIGNUM +static JSValue JS_ReadBigNum(BCReaderState *s, int tag) +{ + JSValue obj = JS_UNDEFINED; + uint8_t v8; + int32_t e; + uint32_t len; + limb_t l, i, n, j; + JSBigFloat *p; + limb_t v; + bf_t *a; + int bpos, d; + p = js_new_bf(s->ctx); + if (!p) + goto fail; + switch(tag) { + case BC_TAG_BIG_INT: + obj = JS_MKPTR(JS_TAG_BIG_INT, p); + break; + case BC_TAG_BIG_FLOAT: + obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p); + break; + case BC_TAG_BIG_DECIMAL: + obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p); + break; + default: + abort(); + } + /* sign + exponent */ + if (bc_get_sleb128(s, &e)) + goto fail; + a = &p->num; + a->sign = e & 1; + e >>= 1; + if (e == 0) + a->expn = BF_EXP_ZERO; + else if (e == 1) + a->expn = BF_EXP_INF; + else if (e == 2) + a->expn = BF_EXP_NAN; + else if (e >= 3) + a->expn = e - 3; + else + a->expn = e; + /* mantissa */ + if (a->expn != BF_EXP_ZERO && + a->expn != BF_EXP_INF && + a->expn != BF_EXP_NAN) { + if (bc_get_leb128(s, &len)) + goto fail; + bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len); + if (len == 0) { + JS_ThrowInternalError(s->ctx, "invalid bignum length"); + goto fail; + } + if (tag != BC_TAG_BIG_DECIMAL) + l = (len + sizeof(limb_t) - 1) / sizeof(limb_t); + else + l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS; + if (bf_resize(a, l)) { + JS_ThrowOutOfMemory(s->ctx); + goto fail; + } + if (tag != BC_TAG_BIG_DECIMAL) { + n = len & (sizeof(limb_t) - 1); + if (n != 0) { + v = 0; + for(i = 0; i < n; i++) { + if (bc_get_u8(s, &v8)) + goto fail; + v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8); + } + a->tab[0] = v; + i = 1; + } else { + i = 0; + } + for(; i < l; i++) { +#if LIMB_BITS == 32 + if (bc_get_u32(s, &v)) + goto fail; +#ifdef WORDS_BIGENDIAN + v = bswap32(v); +#endif +#else + if (bc_get_u64(s, &v)) + goto fail; +#ifdef WORDS_BIGENDIAN + v = bswap64(v); +#endif +#endif + a->tab[i] = v; + } + } else { + bpos = 0; + for(i = 0; i < l; i++) { + if (i == 0 && (n = len % LIMB_DIGITS) != 0) { + j = LIMB_DIGITS - n; + } else { + j = 0; + } + v = 0; + for(; j < LIMB_DIGITS; j++) { + if (bpos == 0) { + if (bc_get_u8(s, &v8)) + goto fail; + d = v8 & 0xf; + bpos = 1; + } else { + d = v8 >> 4; + bpos = 0; + } + if (d >= 10) { + JS_ThrowInternalError(s->ctx, "invalid digit"); + goto fail; + } + v += mp_pow_dec[j] * d; + } + a->tab[i] = v; + } + } + } + bc_read_trace(s, "}\n"); + return obj; + fail: + JS_FreeValue(s->ctx, obj); + return JS_EXCEPTION; +} +#endif /* CONFIG_BIGNUM */ + +static JSValue JS_ReadObjectRec(BCReaderState *s); + +static int BC_add_object_ref1(BCReaderState *s, JSObject *p) +{ + if (s->allow_reference) { + if (js_resize_array(s->ctx, (void *)&s->objects, + sizeof(s->objects[0]), + &s->objects_size, s->objects_count + 1)) + return -1; + s->objects[s->objects_count++] = p; + } + return 0; +} + +static int BC_add_object_ref(BCReaderState *s, JSValueConst obj) +{ + return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj)); +} + +static JSValue JS_ReadFunctionTag(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSFunctionBytecode bc, *b; + JSValue obj = JS_UNDEFINED; + uint16_t v16; + uint8_t v8; + int idx, i, local_count; + int function_size, cpool_offset, byte_code_offset; + int closure_var_offset, vardefs_offset; + memset(&bc, 0, sizeof(bc)); + bc.header.ref_count = 1; + //bc.gc_header.mark = 0; + if (bc_get_u16(s, &v16)) + goto fail; + idx = 0; + bc.has_prototype = bc_get_flags(v16, &idx, 1); + bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1); + bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1); + bc.need_home_object = bc_get_flags(v16, &idx, 1); + bc.func_kind = bc_get_flags(v16, &idx, 2); + bc.new_target_allowed = bc_get_flags(v16, &idx, 1); + bc.super_call_allowed = bc_get_flags(v16, &idx, 1); + bc.super_allowed = bc_get_flags(v16, &idx, 1); + bc.arguments_allowed = bc_get_flags(v16, &idx, 1); + bc.has_debug = bc_get_flags(v16, &idx, 1); + bc.backtrace_barrier = bc_get_flags(v16, &idx, 1); + bc.read_only_bytecode = s->is_rom_data; + if (bc_get_u8(s, &v8)) + goto fail; + bc.js_mode = v8; + if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure + goto fail; + if (bc_get_leb128_u16(s, &bc.arg_count)) + goto fail; + if (bc_get_leb128_u16(s, &bc.var_count)) + goto fail; + if (bc_get_leb128_u16(s, &bc.defined_arg_count)) + goto fail; + if (bc_get_leb128_u16(s, &bc.stack_size)) + goto fail; + if (bc_get_leb128_int(s, &bc.closure_var_count)) + goto fail; + if (bc_get_leb128_int(s, &bc.cpool_count)) + goto fail; + if (bc_get_leb128_int(s, &bc.byte_code_len)) + goto fail; + if (bc_get_leb128_int(s, &local_count)) + goto fail; + if (bc.has_debug) { + function_size = sizeof(*b); + } else { + function_size = offsetof(JSFunctionBytecode, debug); + } + cpool_offset = function_size; + function_size += bc.cpool_count * sizeof(*bc.cpool); + vardefs_offset = function_size; + function_size += local_count * sizeof(*bc.vardefs); + closure_var_offset = function_size; + function_size += bc.closure_var_count * sizeof(*bc.closure_var); + byte_code_offset = function_size; + if (!bc.read_only_bytecode) { + function_size += bc.byte_code_len; + } + b = js_mallocz(ctx, function_size); + if (!b) + return JS_EXCEPTION; + memcpy(b, &bc, offsetof(JSFunctionBytecode, debug)); + b->header.ref_count = 1; + if (local_count != 0) { + b->vardefs = (void *)((uint8_t*)b + vardefs_offset); + } + if (b->closure_var_count != 0) { + b->closure_var = (void *)((uint8_t*)b + closure_var_offset); + } + if (b->cpool_count != 0) { + b->cpool = (void *)((uint8_t*)b + cpool_offset); + } + add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n"); +#endif + bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n", + b->arg_count, b->var_count, b->defined_arg_count, + b->closure_var_count, b->cpool_count); + bc_read_trace(s, "stack=%d bclen=%d locals=%d\n", + b->stack_size, b->byte_code_len, local_count); + if (local_count != 0) { + bc_read_trace(s, "vars {\n"); + for(i = 0; i < local_count; i++) { + JSVarDef *vd = &b->vardefs[i]; + if (bc_get_atom(s, &vd->var_name)) + goto fail; + if (bc_get_leb128_int(s, &vd->scope_level)) + goto fail; + if (bc_get_leb128_int(s, &vd->scope_next)) + goto fail; + vd->scope_next--; + if (bc_get_u8(s, &v8)) + goto fail; + idx = 0; + vd->var_kind = bc_get_flags(v8, &idx, 4); + vd->is_const = bc_get_flags(v8, &idx, 1); + vd->is_lexical = bc_get_flags(v8, &idx, 1); + vd->is_captured = bc_get_flags(v8, &idx, 1); +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n"); +#endif + } + bc_read_trace(s, "}\n"); + } + if (b->closure_var_count != 0) { + bc_read_trace(s, "closure vars {\n"); + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + int var_idx; + if (bc_get_atom(s, &cv->var_name)) + goto fail; + if (bc_get_leb128_int(s, &var_idx)) + goto fail; + cv->var_idx = var_idx; + if (bc_get_u8(s, &v8)) + goto fail; + idx = 0; + cv->is_local = bc_get_flags(v8, &idx, 1); + cv->is_arg = bc_get_flags(v8, &idx, 1); + cv->is_const = bc_get_flags(v8, &idx, 1); + cv->is_lexical = bc_get_flags(v8, &idx, 1); + cv->var_kind = bc_get_flags(v8, &idx, 4); +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n"); +#endif + } + bc_read_trace(s, "}\n"); + } + { + bc_read_trace(s, "bytecode {\n"); + if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len)) + goto fail; + bc_read_trace(s, "}\n"); + } + if (b->has_debug) { + /* read optional debug information */ + bc_read_trace(s, "debug {\n"); + if (bc_get_atom(s, &b->debug.filename)) + goto fail; + if (bc_get_leb128_int(s, &b->debug.line_num)) + goto fail; + if (bc_get_leb128_int(s, &b->debug.pc2line_len)) + goto fail; + if (b->debug.pc2line_len) { + b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len); + if (!b->debug.pc2line_buf) + goto fail; + if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len)) + goto fail; + } +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n"); +#endif + bc_read_trace(s, "}\n"); + } + if (b->cpool_count != 0) { + bc_read_trace(s, "cpool {\n"); + for(i = 0; i < b->cpool_count; i++) { + JSValue val; + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + b->cpool[i] = val; + } + bc_read_trace(s, "}\n"); + } + b->realm = JS_DupContext(ctx); + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadModule(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue obj; + JSModuleDef *m = NULL; + JSAtom module_name; + int i; + uint8_t v8; + if (bc_get_atom(s, &module_name)) + goto fail; +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n"); +#endif + m = js_new_module_def(ctx, module_name); + if (!m) + goto fail; + obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); + if (bc_get_leb128_int(s, &m->req_module_entries_count)) + goto fail; + if (m->req_module_entries_count != 0) { + m->req_module_entries_size = m->req_module_entries_count; + m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size); + if (!m->req_module_entries) + goto fail; + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + if (bc_get_atom(s, &rme->module_name)) + goto fail; + } + } + if (bc_get_leb128_int(s, &m->export_entries_count)) + goto fail; + if (m->export_entries_count != 0) { + m->export_entries_size = m->export_entries_count; + m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size); + if (!m->export_entries) + goto fail; + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (bc_get_u8(s, &v8)) + goto fail; + me->export_type = v8; + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + if (bc_get_leb128_int(s, &me->u.local.var_idx)) + goto fail; + } else { + if (bc_get_leb128_int(s, &me->u.req_module_idx)) + goto fail; + if (bc_get_atom(s, &me->local_name)) + goto fail; + } + if (bc_get_atom(s, &me->export_name)) + goto fail; + } + } + if (bc_get_leb128_int(s, &m->star_export_entries_count)) + goto fail; + if (m->star_export_entries_count != 0) { + m->star_export_entries_size = m->star_export_entries_count; + m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size); + if (!m->star_export_entries) + goto fail; + for(i = 0; i < m->star_export_entries_count; i++) { + JSStarExportEntry *se = &m->star_export_entries[i]; + if (bc_get_leb128_int(s, &se->req_module_idx)) + goto fail; + } + } + if (bc_get_leb128_int(s, &m->import_entries_count)) + goto fail; + if (m->import_entries_count != 0) { + m->import_entries_size = m->import_entries_count; + m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size); + if (!m->import_entries) + goto fail; + for(i = 0; i < m->import_entries_count; i++) { + JSImportEntry *mi = &m->import_entries[i]; + if (bc_get_leb128_int(s, &mi->var_idx)) + goto fail; + if (bc_get_atom(s, &mi->import_name)) + goto fail; + if (bc_get_leb128_int(s, &mi->req_module_idx)) + goto fail; + } + } + m->func_obj = JS_ReadObjectRec(s); + if (JS_IsException(m->func_obj)) + goto fail; + return obj; + fail: + if (m) { + js_free_module_def(ctx, m); + } + return JS_EXCEPTION; +} + +static JSValue JS_ReadObjectTag(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue obj; + uint32_t prop_count, i; + JSAtom atom; + JSValue val; + int ret; + obj = JS_NewObject(ctx); + if (BC_add_object_ref(s, obj)) + goto fail; + if (bc_get_leb128(s, &prop_count)) + goto fail; + for(i = 0; i < prop_count; i++) { + if (bc_get_atom(s, &atom)) + goto fail; +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n"); +#endif + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) { + JS_FreeAtom(ctx, atom); + goto fail; + } + ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E); + JS_FreeAtom(ctx, atom); + if (ret < 0) + goto fail; + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadArray(BCReaderState *s, int tag) +{ + JSContext *ctx = s->ctx; + JSValue obj; + uint32_t len, i; + JSValue val; + int ret, prop_flags; + BOOL is_template; + obj = JS_NewArray(ctx); + if (BC_add_object_ref(s, obj)) + goto fail; + is_template = (tag == BC_TAG_TEMPLATE_OBJECT); + if (bc_get_leb128(s, &len)) + goto fail; + for(i = 0; i < len; i++) { + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + if (is_template) + prop_flags = JS_PROP_ENUMERABLE; + else + prop_flags = JS_PROP_C_W_E; + ret = JS_DefinePropertyValueUint32(ctx, obj, i, val, + prop_flags); + if (ret < 0) + goto fail; + } + if (is_template) { + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + if (!JS_IsUndefined(val)) { + ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0); + if (ret < 0) + goto fail; + } + JS_PreventExtensions(ctx, obj); + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadTypedArray(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED; + uint8_t array_tag; + JSValueConst args[3]; + uint32_t offset, len, idx; + if (bc_get_u8(s, &array_tag)) + return JS_EXCEPTION; + if (array_tag >= JS_TYPED_ARRAY_COUNT) + return JS_ThrowTypeError(ctx, "invalid typed array"); + if (bc_get_leb128(s, &len)) + return JS_EXCEPTION; + if (bc_get_leb128(s, &offset)) + return JS_EXCEPTION; + /* XXX: this hack could be avoided if the typed array could be + created before the array buffer */ + idx = s->objects_count; + if (BC_add_object_ref1(s, NULL)) + goto fail; + array_buffer = JS_ReadObjectRec(s); + if (JS_IsException(array_buffer)) + return JS_EXCEPTION; + if (!js_get_array_buffer(ctx, array_buffer)) { + JS_FreeValue(ctx, array_buffer); + return JS_EXCEPTION; + } + args[0] = array_buffer; + args[1] = JS_NewInt64(ctx, offset); + args[2] = JS_NewInt64(ctx, len); + obj = js_typed_array_constructor(ctx, JS_UNDEFINED, + 3, args, + JS_CLASS_UINT8C_ARRAY + array_tag); + if (JS_IsException(obj)) + goto fail; + if (s->allow_reference) { + s->objects[idx] = JS_VALUE_GET_OBJ(obj); + } + JS_FreeValue(ctx, array_buffer); + return obj; + fail: + JS_FreeValue(ctx, array_buffer); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadArrayBuffer(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + uint32_t byte_length; + JSValue obj; + + if (bc_get_leb128(s, &byte_length)) + return JS_EXCEPTION; + if (UNLIKELY(s->buf_end - s->ptr < byte_length)) { + bc_read_error_end(s); + return JS_EXCEPTION; + } + obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + s->ptr += byte_length; + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + uint32_t byte_length; + uint8_t *data_ptr; + JSValue obj; + uint64_t u64; + if (bc_get_leb128(s, &byte_length)) + return JS_EXCEPTION; + if (bc_get_u64(s, &u64)) + return JS_EXCEPTION; + data_ptr = (uint8_t *)(uintptr_t)u64; + /* the SharedArrayBuffer is cloned */ + obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length, + JS_CLASS_SHARED_ARRAY_BUFFER, + data_ptr, + NULL, NULL, FALSE); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadDate(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue val, obj = JS_UNDEFINED; + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + if (!JS_IsNumber(val)) { + JS_ThrowTypeError(ctx, "Number tag expected for date"); + goto fail; + } + obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE], + JS_CLASS_DATE); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + JS_SetObjectData(ctx, obj, val); + return obj; + fail: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadObjectValue(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue val, obj = JS_UNDEFINED; + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + obj = JS_ToObject(ctx, val); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + JS_FreeValue(ctx, val); + return obj; + fail: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadObjectRec(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + uint8_t tag; + JSValue obj = JS_UNDEFINED; + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); + if (bc_get_u8(s, &tag)) + return JS_EXCEPTION; + bc_read_trace(s, "%s {\n", bc_tag_str[tag]); + switch(tag) { + case BC_TAG_NULL: + obj = JS_NULL; + break; + case BC_TAG_UNDEFINED: + obj = JS_UNDEFINED; + break; + case BC_TAG_BOOL_FALSE: + case BC_TAG_BOOL_TRUE: + obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE); + break; + case BC_TAG_INT32: + { + int32_t val; + if (bc_get_sleb128(s, &val)) + return JS_EXCEPTION; + bc_read_trace(s, "%d\n", val); + obj = JS_NewInt32(ctx, val); + } + break; + case BC_TAG_FLOAT64: + { + JSFloat64Union u; + if (bc_get_u64(s, &u.u64)) + return JS_EXCEPTION; + bc_read_trace(s, "%g\n", u.d); + obj = __JS_NewFloat64(ctx, u.d); + } + break; + case BC_TAG_STRING: + { + JSString *p; + p = JS_ReadString(s); + if (!p) + return JS_EXCEPTION; + obj = JS_MKPTR(JS_TAG_STRING, p); + } + break; + case BC_TAG_FUNCTION_BYTECODE: + if (!s->allow_bytecode) + goto invalid_tag; + obj = JS_ReadFunctionTag(s); + break; + case BC_TAG_MODULE: + if (!s->allow_bytecode) + goto invalid_tag; + obj = JS_ReadModule(s); + break; + case BC_TAG_OBJECT: + obj = JS_ReadObjectTag(s); + break; + case BC_TAG_ARRAY: + case BC_TAG_TEMPLATE_OBJECT: + obj = JS_ReadArray(s, tag); + break; + case BC_TAG_TYPED_ARRAY: + obj = JS_ReadTypedArray(s); + break; + case BC_TAG_ARRAY_BUFFER: + obj = JS_ReadArrayBuffer(s); + break; + case BC_TAG_SHARED_ARRAY_BUFFER: + if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup) + goto invalid_tag; + obj = JS_ReadSharedArrayBuffer(s); + break; + case BC_TAG_DATE: + obj = JS_ReadDate(s); + break; + case BC_TAG_OBJECT_VALUE: + obj = JS_ReadObjectValue(s); + break; +#ifdef CONFIG_BIGNUM + case BC_TAG_BIG_INT: + case BC_TAG_BIG_FLOAT: + case BC_TAG_BIG_DECIMAL: + obj = JS_ReadBigNum(s, tag); + break; +#endif + case BC_TAG_OBJECT_REFERENCE: + { + uint32_t val; + if (!s->allow_reference) + return JS_ThrowSyntaxError(ctx, "object references are not allowed"); + if (bc_get_leb128(s, &val)) + return JS_EXCEPTION; + bc_read_trace(s, "%u\n", val); + if (val >= s->objects_count) { + return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)", + val, s->objects_count); + } + obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val])); + } + break; + default: + invalid_tag: + return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)", + tag, (unsigned int)(s->ptr - s->buf_start)); + } + bc_read_trace(s, "}\n"); + return obj; +} + +static int JS_ReadObjectAtoms(BCReaderState *s) +{ + uint8_t v8; + JSString *p; + int i; + JSAtom atom; + if (bc_get_u8(s, &v8)) + return -1; + /* XXX: could support byte swapped input */ + if (v8 != BC_VERSION) { + JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)", + v8, BC_VERSION); + return -1; + } + if (bc_get_leb128(s, &s->idx_to_atom_count)) + return -1; + bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count); + if (s->idx_to_atom_count != 0) { + s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count * + sizeof(s->idx_to_atom[0])); + if (!s->idx_to_atom) + return s->error_state = -1; + } + for(i = 0; i < s->idx_to_atom_count; i++) { + p = JS_ReadString(s); + if (!p) + return -1; + atom = JS_NewAtomStr(s->ctx, p); + if (atom == JS_ATOM_NULL) + return s->error_state = -1; + s->idx_to_atom[i] = atom; + if (s->is_rom_data && (atom != (i + s->first_atom))) + s->is_rom_data = FALSE; /* atoms must be relocated */ + } + bc_read_trace(s, "}\n"); + return 0; +} + +static void bc_reader_free(BCReaderState *s) +{ + int i; + if (s->idx_to_atom) { + for(i = 0; i < s->idx_to_atom_count; i++) { + JS_FreeAtom(s->ctx, s->idx_to_atom[i]); + } + js_free(s->ctx, s->idx_to_atom); + } + js_free(s->ctx, s->objects); +} + +JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int flags) +{ + BCReaderState ss, *s = &ss; + JSValue obj; + ctx->binary_object_count += 1; + ctx->binary_object_size += buf_len; + memset(s, 0, sizeof(*s)); + s->ctx = ctx; + s->buf_start = buf; + s->buf_end = buf + buf_len; + s->ptr = buf; + s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0); + s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0); + s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0); + s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0); + if (s->allow_bytecode) + s->first_atom = JS_ATOM_END; + else + s->first_atom = 1; + if (JS_ReadObjectAtoms(s)) { + obj = JS_EXCEPTION; + } else { + obj = JS_ReadObjectRec(s); + } + bc_reader_free(s); + return obj; +} diff --git a/third_party/quickjs/call.c b/third_party/quickjs/call.c new file mode 100644 index 000000000..fbd860f9d --- /dev/null +++ b/third_party/quickjs/call.c @@ -0,0 +1,4107 @@ +/* + * 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/mem/mem.h" +#include "libc/runtime/gc.internal.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 const uint16_t func_kind_to_class_id[] = { + [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION, + [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION, + [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION, + [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION, +}; + +static JSValue JS_CallConstructorInternal(JSContext *ctx, + JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValue *argv, int flags); + +JSValue js_closure2(JSContext *ctx, JSValue func_obj, + JSFunctionBytecode *b, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSObject *p; + JSVarRef **var_refs; + int i; + p = JS_VALUE_GET_OBJ(func_obj); + p->u.func.function_bytecode = b; + p->u.func.home_object = NULL; + p->u.func.var_refs = NULL; + if (b->closure_var_count) { + var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); + if (!var_refs) + goto fail; + p->u.func.var_refs = var_refs; + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + JSVarRef *var_ref; + if (cv->is_local) { + /* reuse the existing variable reference if it already exists */ + var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg); + if (!var_ref) + goto fail; + } else { + var_ref = cur_var_refs[cv->var_idx]; + var_ref->header.ref_count++; + } + var_refs[i] = var_ref; + } + } + return func_obj; + fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +JSValue js_closure(JSContext *ctx, JSValue bfunc, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSFunctionBytecode *b; + JSValue func_obj; + JSAtom name_atom; + b = JS_VALUE_GET_PTR(bfunc); + func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]); + if (JS_IsException(func_obj)) { + JS_FreeValue(ctx, bfunc); + return JS_EXCEPTION; + } + func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf); + if (JS_IsException(func_obj)) { + /* bfunc has been freed */ + goto fail; + } + name_atom = b->func_name; + if (name_atom == JS_ATOM_NULL) + name_atom = JS_ATOM_empty_string; + js_function_set_properties(ctx, func_obj, name_atom, + b->defined_arg_count); + if (b->func_kind & JS_FUNC_GENERATOR) { + JSValue proto; + int proto_class_id; + /* generators have a prototype field which is used as + prototype for the generator object */ + if (b->func_kind == JS_FUNC_ASYNC_GENERATOR) + proto_class_id = JS_CLASS_ASYNC_GENERATOR; + else + proto_class_id = JS_CLASS_GENERATOR; + proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]); + if (JS_IsException(proto)) + goto fail; + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto, + JS_PROP_WRITABLE); + } else if (b->has_prototype) { + /* add the 'prototype' property: delay instantiation to avoid + creating cycles for every javascript function. The prototype + object is created on the fly when first accessed */ + JS_SetConstructorBit(ctx, func_obj, TRUE); + JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype, + JS_AUTOINIT_ID_PROTOTYPE, NULL, + JS_PROP_WRITABLE); + } + return func_obj; + fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) +{ + JSRuntime *rt = ctx->rt; + JSCFunctionType func; + JSObject *p; + JSStackFrame sf_s, *sf = &sf_s, *prev_sf; + JSValue ret_val; + JSValueConst *arg_buf; + int arg_count, i; + JSCFunctionEnum cproto; + p = JS_VALUE_GET_OBJ(func_obj); + cproto = p->u.cfunc.cproto; + arg_count = p->u.cfunc.length; + /* better to always check stack overflow */ + if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count)) + return JS_ThrowStackOverflow(ctx); + prev_sf = rt->current_stack_frame; + sf->prev_frame = prev_sf; + rt->current_stack_frame = sf; + ctx = p->u.cfunc.realm; /* change the current realm */ +#ifdef CONFIG_BIGNUM + /* we only propagate the bignum mode as some runtime functions + test it */ + if (prev_sf) + sf->js_mode = prev_sf->js_mode & JS_MODE_MATH; + else + sf->js_mode = 0; +#else + sf->js_mode = 0; +#endif + sf->cur_func = (JSValue)func_obj; + sf->arg_count = argc; + arg_buf = argv; + if (UNLIKELY(argc < arg_count)) { + /* ensure that at least argc_count arguments are readable */ + arg_buf = gc(malloc(sizeof(arg_buf[0]) * arg_count)); + for(i = 0; i < argc; i++) + arg_buf[i] = argv[i]; + for(i = argc; i < arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + sf->arg_count = arg_count; + } + sf->arg_buf = (JSValue*)arg_buf; + func = p->u.cfunc.c_function; + switch(cproto) { + case JS_CFUNC_constructor: + case JS_CFUNC_constructor_or_func: + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { + if (cproto == JS_CFUNC_constructor) { + not_a_constructor: + ret_val = JS_ThrowTypeError(ctx, "must be called with new"); + break; + } else { + this_obj = JS_UNDEFINED; + } + } + /* here this_obj is new_target */ + /* fall thru */ + case JS_CFUNC_generic: + ret_val = func.generic(ctx, this_obj, argc, arg_buf); + break; + case JS_CFUNC_constructor_magic: + case JS_CFUNC_constructor_or_func_magic: + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { + if (cproto == JS_CFUNC_constructor_magic) { + goto not_a_constructor; + } else { + this_obj = JS_UNDEFINED; + } + } + /* fall thru */ + case JS_CFUNC_generic_magic: + ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf, + p->u.cfunc.magic); + break; + case JS_CFUNC_getter: + ret_val = func.getter(ctx, this_obj); + break; + case JS_CFUNC_setter: + ret_val = func.setter(ctx, this_obj, arg_buf[0]); + break; + case JS_CFUNC_getter_magic: + ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic); + break; + case JS_CFUNC_setter_magic: + ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic); + break; + case JS_CFUNC_f_f: + { + double d1; + if (UNLIKELY(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { + ret_val = JS_EXCEPTION; + break; + } + ret_val = JS_NewFloat64(ctx, func.f_f(d1)); + } + break; + case JS_CFUNC_f_f_f: + { + double d1, d2; + if (UNLIKELY(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { + ret_val = JS_EXCEPTION; + break; + } + if (UNLIKELY(JS_ToFloat64(ctx, &d2, arg_buf[1]))) { + ret_val = JS_EXCEPTION; + break; + } + ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2)); + } + break; + case JS_CFUNC_iterator_next: + { + int done; + ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf, + &done, p->u.cfunc.magic); + if (!JS_IsException(ret_val) && done != 2) { + ret_val = js_create_iterator_result(ctx, ret_val, done); + } + } + break; + default: + abort(); + } + rt->current_stack_frame = sf->prev_frame; + return ret_val; +} + +JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) +{ + JSObject *p; + JSBoundFunction *bf; + JSValueConst *arg_buf, new_target; + int arg_count, i; + p = JS_VALUE_GET_OBJ(func_obj); + bf = p->u.bound_function; + arg_count = bf->argc + argc; + if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count)) + return JS_ThrowStackOverflow(ctx); + arg_buf = gc(malloc(sizeof(JSValue) * arg_count)); + for(i = 0; i < bf->argc; i++) { + arg_buf[i] = bf->argv[i]; + } + for(i = 0; i < argc; i++) { + arg_buf[bf->argc + i] = argv[i]; + } + if (flags & JS_CALL_FLAG_CONSTRUCTOR) { + new_target = this_obj; + if (js_same_value(ctx, func_obj, new_target)) + new_target = bf->func_obj; + return JS_CallConstructor2(ctx, bf->func_obj, new_target, + arg_count, arg_buf); + } else { + return JS_Call(ctx, bf->func_obj, bf->this_val, + arg_count, arg_buf); + } +} + +JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + return JS_CallConstructorInternal(ctx, func_obj, new_target, + argc, (JSValue *)argv, + JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, + int argc, JSValueConst *argv) +{ + return JS_CallConstructorInternal(ctx, func_obj, func_obj, + argc, (JSValue *)argv, + JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, + int argc, JSValueConst *argv) +{ + JSValue func_obj; + func_obj = JS_GetProperty(ctx, this_val, atom); + if (JS_IsException(func_obj)) + return func_obj; + return JS_CallFree(ctx, func_obj, this_val, argc, argv); +} + +JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValueConst *argv) +{ + JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv); + JS_FreeValue(ctx, this_val); + return res; +} + +static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv) +{ + JSValue val, *tab; + JSProperty *pr; + JSObject *p; + int i; + val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_ARGUMENTS); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + /* add the length field (cannot fail) */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + pr->u.value = JS_NewInt32(ctx, argc); + /* initialize the fast array part */ + tab = NULL; + if (argc > 0) { + tab = js_malloc(ctx, sizeof(tab[0]) * argc); + if (!tab) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + for(i = 0; i < argc; i++) { + tab[i] = JS_DupValue(ctx, argv[i]); + } + } + p->u.array.u.values = tab; + p->u.array.count = argc; + JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, + JS_DupValue(ctx, ctx->array_proto_values), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + /* add callee property to throw a TypeError in strict mode */ + JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED, + ctx->throw_type_error, ctx->throw_type_error, + JS_PROP_HAS_GET | JS_PROP_HAS_SET); + return val; +} + +/* legacy arguments object: add references to the function arguments */ +static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, + JSValueConst *argv, + JSStackFrame *sf, int arg_count) +{ + JSValue val; + JSProperty *pr; + JSObject *p; + int i; + val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_MAPPED_ARGUMENTS); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + /* add the length field (cannot fail) */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + pr->u.value = JS_NewInt32(ctx, argc); + for(i = 0; i < arg_count; i++) { + JSVarRef *var_ref; + var_ref = get_var_ref(ctx, sf, i, TRUE); + if (!var_ref) + goto fail; + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); + if (!pr) { + free_var_ref(ctx->rt, var_ref); + goto fail; + } + pr->u.var_ref = var_ref; + } + /* the arguments not mapped to the arguments of the function can + be normal properties */ + for(i = arg_count; i < argc; i++) { + if (JS_DefinePropertyValueUint32(ctx, val, i, + JS_DupValue(ctx, argv[i]), + JS_PROP_C_W_E) < 0) + goto fail; + } + JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, + JS_DupValue(ctx, ctx->array_proto_values), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + /* callee returns this function in non strict mode */ + JS_DefinePropertyValue(ctx, val, JS_ATOM_callee, + JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_import_meta(JSContext *ctx) +{ + JSAtom filename; + JSModuleDef *m; + filename = JS_GetScriptOrModuleName(ctx, 0); + if (filename == JS_ATOM_NULL) + goto fail; + /* XXX: inefficient, need to add a module or script pointer in + JSFunctionBytecode */ + m = js_find_loaded_module(ctx, filename); + JS_FreeAtom(ctx, filename); + if (!m) { + fail: + JS_ThrowTypeError(ctx, "import.meta not supported in this context"); + return JS_EXCEPTION; + } + return JS_GetImportMeta(ctx, m); +} + +static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv) +{ + JSValue val; + int i, ret; + val = JS_NewArray(ctx); + if (JS_IsException(val)) + return val; + for (i = first; i < argc; i++) { + ret = JS_DefinePropertyValueUint32(ctx, val, i - first, + JS_DupValue(ctx, argv[i]), + JS_PROP_C_W_E); + if (ret < 0) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + } + return val; +} + +static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) +{ + JSObject *p, *p1, *home_obj; + JSShapeProperty *prs; + JSProperty *pr; + JSValueConst brand; + /* get the home object of 'func' */ + if (UNLIKELY(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) { + not_obj: + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p1 = JS_VALUE_GET_OBJ(func); + if (!js_class_has_bytecode(p1->class_id)) + goto not_obj; + home_obj = p1->u.func.home_object; + if (!home_obj) + goto not_obj; + prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand); + if (!prs) { + JS_ThrowTypeError(ctx, "expecting private field"); + return -1; + } + brand = pr->u.value; + /* safety check */ + if (UNLIKELY(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL)) + goto not_obj; + /* get the brand array of 'obj' */ + if (UNLIKELY(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + goto not_obj; + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand)); + if (!prs) { + JS_ThrowTypeError(ctx, "invalid brand on object"); + return -1; + } + return 0; +} + +/* descr must be a non-numeric string atom */ +static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, + int atom_type) +{ + JSRuntime *rt = ctx->rt; + JSString *p; + assert(!__JS_AtomIsTaggedInt(descr)); + assert(descr < rt->atom_size); + p = rt->atom_array[descr]; + JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); + return JS_NewSymbol(ctx, p, atom_type); +} + +static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) +{ + JSObject *p, *p1; + JSShapeProperty *prs; + JSProperty *pr; + JSValue brand; + JSAtom brand_atom; + if (UNLIKELY(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(home_obj); + prs = find_own_property(&pr, p, JS_ATOM_Private_brand); + if (!prs) { + brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(brand)) + return -1; + /* if the brand is not present, add it */ + pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E); + if (!pr) { + JS_FreeValue(ctx, brand); + return -1; + } + pr->u.value = JS_DupValue(ctx, brand); + } else { + brand = JS_DupValue(ctx, pr->u.value); + } + brand_atom = js_symbol_to_atom(ctx, brand); + if (UNLIKELY(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + JS_FreeAtom(ctx, brand_atom); + return -1; + } + p1 = JS_VALUE_GET_OBJ(obj); + pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E); + JS_FreeAtom(ctx, brand_atom); + if (!pr) + return -1; + pr->u.value = JS_UNDEFINED; + return 0; +} + +static JSValue js_dynamic_import_job(JSContext *ctx, + int argc, JSValueConst *argv) +{ + JSValueConst *resolving_funcs = argv; + JSValueConst basename_val = argv[2]; + JSValueConst specifier = argv[3]; + JSModuleDef *m; + const char *basename = NULL, *filename; + JSValue ret, err, ns; + if (!JS_IsString(basename_val)) { + JS_ThrowTypeError(ctx, "no function filename for import()"); + goto exception; + } + basename = JS_ToCString(ctx, basename_val); + if (!basename) + goto exception; + filename = JS_ToCString(ctx, specifier); + if (!filename) + goto exception; + m = JS_RunModule(ctx, basename, filename); + JS_FreeCString(ctx, filename); + if (!m) + goto exception; + /* return the module namespace */ + ns = js_get_module_ns(ctx, m); + if (JS_IsException(ns)) + goto exception; + ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&ns); + JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ + JS_FreeValue(ctx, ns); + JS_FreeCString(ctx, basename); + return JS_UNDEFINED; + exception: + err = JS_GetException(ctx); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&err); + JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ + JS_FreeValue(ctx, err); + JS_FreeCString(ctx, basename); + return JS_UNDEFINED; +} + +static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier) +{ + JSAtom basename; + JSValue promise, resolving_funcs[2], basename_val; + JSValueConst args[4]; + basename = JS_GetScriptOrModuleName(ctx, 0); + if (basename == JS_ATOM_NULL) + basename_val = JS_NULL; + else + basename_val = JS_AtomToValue(ctx, basename); + JS_FreeAtom(ctx, basename); + if (JS_IsException(basename_val)) + return basename_val; + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) { + JS_FreeValue(ctx, basename_val); + return promise; + } + args[0] = resolving_funcs[0]; + args[1] = resolving_funcs[1]; + args[2] = basename_val; + args[3] = specifier; + JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args); + JS_FreeValue(ctx, basename_val); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; +} + +/* use for strict variable access: test if the variable exists */ +static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop) +{ + JSObject *p; + JSShapeProperty *prs; + int ret; + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property1(p, prop); + if (prs) { + ret = TRUE; + } else { + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + } + return ret; +} + +static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop, + BOOL throw_ref_error) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_TMASK properties */ + if (UNLIKELY(JS_IsUninitialized(pr->u.value))) + return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return JS_DupValue(ctx, pr->u.value); + } + return JS_GetPropertyInternal(ctx, ctx->global_obj, prop, + ctx->global_obj, throw_ref_error); +} + +/* flag = 0: normal variable write + flag = 1: initialize lexical variable + flag = 2: normal variable write, strict check was done before +*/ +static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, + int flag) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int flags; + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_AUTOINIT properties? */ + if (flag != 1) { + if (UNLIKELY(JS_IsUninitialized(pr->u.value))) { + JS_FreeValue(ctx, val); + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + if (UNLIKELY(!(prs->flags & JS_PROP_WRITABLE))) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); + } + } + set_value(ctx, &pr->u.value, val); + return 0; + } + flags = JS_PROP_THROW_STRICT; + if (is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD; + return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags); +} + +/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ +/* XXX: could support exotic global object. */ +static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) +{ + JSObject *p; + JSShapeProperty *prs; + p = JS_VALUE_GET_OBJ(ctx->global_obj); + prs = find_own_property1(p, prop); + /* XXX: should handle JS_PROP_AUTOINIT */ + if (flags & DEFINE_GLOBAL_LEX_VAR) { + if (prs && !(prs->flags & JS_PROP_CONFIGURABLE)) + goto fail_redeclaration; + } else { + if (!prs && !p->extensible) + goto define_error; + if (flags & DEFINE_GLOBAL_FUNC_VAR) { + if (prs) { + if (!(prs->flags & JS_PROP_CONFIGURABLE) && + ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET || + ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) != + (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) { + define_error: + JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'", + prop); + return -1; + } + } + } + } + /* check if there already is a lexical declaration */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property1(p, prop); + if (prs) { + fail_redeclaration: + JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop); + return -1; + } + return 0; +} + +/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) | + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */ +/* XXX: could support exotic global object. */ +static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSValue val; + int flags; + if (def_flags & DEFINE_GLOBAL_LEX_VAR) { + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) | + JS_PROP_CONFIGURABLE; + val = JS_UNINITIALIZED; + } else { + p = JS_VALUE_GET_OBJ(ctx->global_obj); + flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | + (def_flags & JS_PROP_CONFIGURABLE); + val = JS_UNDEFINED; + } + prs = find_own_property1(p, prop); + if (prs) + return 0; + if (!p->extensible) + return 0; + pr = add_property(ctx, p, prop, flags); + if (UNLIKELY(!pr)) + return -1; + pr->u.value = val; + return 0; +} + +/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */ +/* XXX: could support exotic global object. */ +static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop, + JSValueConst func, int def_flags) +{ + JSObject *p; + JSShapeProperty *prs; + int flags; + p = JS_VALUE_GET_OBJ(ctx->global_obj); + prs = find_own_property1(p, prop); + flags = JS_PROP_HAS_VALUE | JS_PROP_THROW; + if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) { + flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE; + } + if (JS_DefineProperty(ctx, ctx->global_obj, prop, func, + JS_UNDEFINED, JS_UNDEFINED, flags) < 0) + return -1; + return 0; +} + +static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg) +{ + struct list_head *el, *el1; + JSVarRef *var_ref; + int var_idx = idx; + list_for_each_safe(el, el1, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) { + var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]); + var_ref->pvalue = &var_ref->value; + list_del(&var_ref->header.link); + /* the reference is no longer to a local variable */ + var_ref->is_detached = TRUE; + add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + } + } +} + +/* construct a reference to a global variable */ +static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_AUTOINIT properties? */ + /* XXX: conformance: do these tests in + OP_put_var_ref/OP_get_var_ref ? */ + if (UNLIKELY(JS_IsUninitialized(pr->u.value))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + if (UNLIKELY(!(prs->flags & JS_PROP_WRITABLE))) { + return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); + } + sp[0] = JS_DupValue(ctx, ctx->global_var_obj); + } else { + int ret; + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + if (ret) { + sp[0] = JS_DupValue(ctx, ctx->global_obj); + } else { + sp[0] = JS_UNDEFINED; + } + } + sp[1] = JS_AtomToValue(ctx, prop); + return 0; +} + +static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) +{ + JSObject *p; + JSPropertyEnum *tab_atom; + int i; + JSValue enum_obj, obj1; + JSForInIterator *it; + uint32_t tag, tab_atom_count; + tag = JS_VALUE_GET_TAG(obj); + if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) { + obj = JS_ToObjectFree(ctx, obj); + } + it = js_malloc(ctx, sizeof(*it)); + if (!it) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR); + if (JS_IsException(enum_obj)) { + js_free(ctx, it); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + it->is_array = FALSE; + it->obj = obj; + it->idx = 0; + p = JS_VALUE_GET_OBJ(enum_obj); + p->u.for_in_iterator = it; + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) + return enum_obj; + /* fast path: assume no enumerable properties in the prototype chain */ + obj1 = JS_DupValue(ctx, obj); + for(;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + if (tab_atom_count != 0) { + JS_FreeValue(ctx, obj1); + goto slow_path; + } + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + } + p = JS_VALUE_GET_OBJ(obj); + if (p->fast_array) { + JSShape *sh; + JSShapeProperty *prs; + /* check that there are no enumerable normal fields */ + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + if (prs->flags & JS_PROP_ENUMERABLE) + goto normal_case; + } + /* for fast arrays, we only store the number of elements */ + it->is_array = TRUE; + it->array_length = p->u.array.count; + } else { + normal_case: + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) + goto fail; + for(i = 0; i < tab_atom_count; i++) { + JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + } + return enum_obj; + slow_path: + /* non enumerable properties hide the enumerables ones in the + prototype chain */ + obj1 = JS_DupValue(ctx, obj); + for(;;) { + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + for(i = 0; i < tab_atom_count; i++) { + JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL, + (tab_atom[i].is_enumerable ? + JS_PROP_ENUMERABLE : 0)); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + } + return enum_obj; + fail: + JS_FreeValue(ctx, enum_obj); + return JS_EXCEPTION; +} + +/* obj -> enum_obj */ +static __exception int js_for_in_start(JSContext *ctx, JSValue *sp) +{ + sp[-1] = build_for_in_iterator(ctx, sp[-1]); + if (JS_IsException(sp[-1])) + return -1; + return 0; +} + +/* enum_obj -> enum_obj value done */ +static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) +{ + JSValueConst enum_obj; + JSObject *p; + JSAtom prop; + JSForInIterator *it; + int ret; + enum_obj = sp[-1]; + /* fail safe */ + if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT) + goto done; + p = JS_VALUE_GET_OBJ(enum_obj); + if (p->class_id != JS_CLASS_FOR_IN_ITERATOR) + goto done; + it = p->u.for_in_iterator; + for(;;) { + if (it->is_array) { + if (it->idx >= it->array_length) + goto done; + prop = __JS_AtomFromUInt32(it->idx); + it->idx++; + } else { + JSShape *sh = p->shape; + JSShapeProperty *prs; + if (it->idx >= sh->prop_count) + goto done; + prs = get_shape_prop(sh) + it->idx; + prop = prs->atom; + it->idx++; + if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE)) + continue; + } + /* check if the property was deleted */ + ret = JS_HasProperty(ctx, it->obj, prop); + if (ret < 0) + return ret; + if (ret) + break; + } + /* return the property */ + sp[0] = JS_AtomToValue(ctx, prop); + sp[1] = JS_FALSE; + return 0; + done: + /* return the end */ + sp[0] = JS_UNDEFINED; + sp[1] = JS_TRUE; + return 0; +} + +/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset' + objs. If 'done' is true or in case of exception, 'enum_rec' is set + to undefined. If 'done' is true, 'value' is always set to + undefined. */ +static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) +{ + JSValue value = JS_UNDEFINED; + int done = 1; + if (LIKELY(!JS_IsUndefined(sp[offset]))) { + value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done); + if (JS_IsException(value)) + done = -1; + if (done) { + /* value is JS_UNDEFINED or JS_EXCEPTION */ + /* replace the iteration object with undefined */ + JS_FreeValue(ctx, sp[offset]); + sp[offset] = JS_UNDEFINED; + if (done < 0) { + return -1; + } else { + JS_FreeValue(ctx, value); + value = JS_UNDEFINED; + } + } + } + sp[0] = value; + sp[1] = JS_NewBool(ctx, done); + return 0; +} + +static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) +{ + JSValue obj, value; + BOOL done; + obj = sp[-1]; + if (!JS_IsObject(obj)) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + return -1; + } + value = JS_IteratorGetCompleteValue(ctx, obj, &done); + if (JS_IsException(value)) + return -1; + JS_FreeValue(ctx, obj); + sp[-1] = value; + sp[0] = JS_NewBool(ctx, done); + return 0; +} + +static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + if (UNLIKELY(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return JS_ThrowTypeErrorNotAnObject(ctx); + /* safety check */ + if (UNLIKELY(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) + return JS_ThrowTypeErrorNotASymbol(ctx); + prop = js_symbol_to_atom(ctx, (JSValue)name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + return JS_EXCEPTION; + } + return JS_DupValue(ctx, pr->u.value); +} + +static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name, JSValue val) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + if (UNLIKELY(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + /* safety check */ + if (UNLIKELY(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { + JS_ThrowTypeErrorNotASymbol(ctx); + goto fail; + } + prop = js_symbol_to_atom(ctx, (JSValue)name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + fail: + JS_FreeValue(ctx, val); + return -1; + } + set_value(ctx, &pr->u.value, val); + return 0; +} + +/* Private fields can be added even on non extensible objects or + Proxies */ +static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name, JSValue val) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + if (UNLIKELY(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + /* safety check */ + if (UNLIKELY(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { + JS_ThrowTypeErrorNotASymbol(ctx); + goto fail; + } + prop = js_symbol_to_atom(ctx, (JSValue)name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists", + prop); + goto fail; + } + pr = add_property(ctx, p, prop, JS_PROP_C_W_E); + if (UNLIKELY(!pr)) { + fail: + JS_FreeValue(ctx, val); + return -1; + } + pr->u.value = val; + return 0; +} + +/* Modify the name of a method according to the atom and + 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and + JS_PROP_HAS_SET. Also set the home object of the method. + Return < 0 if exception. */ +static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj, + JSAtom name, int flags, JSValueConst home_obj) +{ + JSValue name_str; + name_str = js_get_function_name(ctx, name); + if (flags & JS_PROP_HAS_GET) { + name_str = JS_ConcatString3(ctx, "get ", name_str, ""); + } else if (flags & JS_PROP_HAS_SET) { + name_str = JS_ConcatString3(ctx, "set ", name_str, ""); + } + if (JS_IsException(name_str)) + return -1; + if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str, + JS_PROP_CONFIGURABLE) < 0) + return -1; + js_method_set_home_object(ctx, func_obj, home_obj); + return 0; +} + +static int js_op_define_class(JSContext *ctx, JSValue *sp, + JSAtom class_name, int class_flags, + JSVarRef **cur_var_refs, + JSStackFrame *sf, BOOL is_computed_name) +{ + JSValue bfunc, parent_class, proto = JS_UNDEFINED; + JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED; + JSFunctionBytecode *b; + parent_class = sp[-2]; + bfunc = sp[-1]; + if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) { + if (JS_IsNull(parent_class)) { + parent_proto = JS_NULL; + parent_class = JS_DupValue(ctx, ctx->function_proto); + } else { + if (!JS_IsConstructor(ctx, parent_class)) { + JS_ThrowTypeError(ctx, "parent class must be constructor"); + goto fail; + } + parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype); + if (JS_IsException(parent_proto)) + goto fail; + if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) { + JS_ThrowTypeError(ctx, "parent prototype must be an object or null"); + goto fail; + } + } + } else { + /* parent_class is JS_UNDEFINED in this case */ + parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]); + parent_class = JS_DupValue(ctx, ctx->function_proto); + } + proto = JS_NewObjectProto(ctx, parent_proto); + if (JS_IsException(proto)) + goto fail; + b = JS_VALUE_GET_PTR(bfunc); + assert(b->func_kind == JS_FUNC_NORMAL); + ctor = JS_NewObjectProtoClass(ctx, parent_class, + JS_CLASS_BYTECODE_FUNCTION); + if (JS_IsException(ctor)) + goto fail; + ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf); + bfunc = JS_UNDEFINED; + if (JS_IsException(ctor)) + goto fail; + js_method_set_home_object(ctx, ctor, proto); + JS_SetConstructorBit(ctx, ctor, TRUE); + JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length, + JS_NewInt32(ctx, b->defined_arg_count), + JS_PROP_CONFIGURABLE); + if (is_computed_name) { + if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3], + JS_PROP_CONFIGURABLE) < 0) + goto fail; + } else { + if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0) + goto fail; + } + /* the constructor property must be first. It can be overriden by + computed property names */ + if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, + JS_DupValue(ctx, ctor), + JS_PROP_CONFIGURABLE | + JS_PROP_WRITABLE | JS_PROP_THROW) < 0) + goto fail; + /* set the prototype property */ + if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype, + JS_DupValue(ctx, proto), JS_PROP_THROW) < 0) + goto fail; + set_cycle_flag(ctx, ctor); + set_cycle_flag(ctx, proto); + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, parent_class); + sp[-2] = ctor; + sp[-1] = proto; + return 0; + fail: + JS_FreeValue(ctx, parent_class); + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, bfunc); + JS_FreeValue(ctx, proto); + JS_FreeValue(ctx, ctor); + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) +{ + JSValue iterator, enumobj, method, value; + int is_array_iterator; + JSValue *arrp; + uint32_t i, count32, pos; + if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { + JS_ThrowInternalError(ctx, "invalid index for append"); + return -1; + } + pos = JS_VALUE_GET_INT(sp[-2]); + /* XXX: further optimisations: + - use ctx->array_proto_values? + - check if array_iterator_prototype next method is built-in and + avoid constructing actual iterator object? + - build this into js_for_of_start and use in all `for (x of o)` loops + */ + iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator); + if (JS_IsException(iterator)) + return -1; + is_array_iterator = JS_IsCFunction(ctx, iterator, + (JSCFunction *)js_create_array_iterator, + JS_ITERATOR_KIND_VALUE); + JS_FreeValue(ctx, iterator); + enumobj = JS_GetIterator(ctx, sp[-1], FALSE); + if (JS_IsException(enumobj)) + return -1; + method = JS_GetProperty(ctx, enumobj, JS_ATOM_next); + if (JS_IsException(method)) { + JS_FreeValue(ctx, enumobj); + return -1; + } + if (is_array_iterator + && JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0) + && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { + uint32_t len; + if (js_get_length32(ctx, &len, sp[-1])) + goto exception; + /* if len > count32, the elements >= count32 might be read in + the prototypes and might have side effects */ + if (len != count32) + goto general_case; + /* Handle fast arrays explicitly */ + for (i = 0; i < count32; i++) { + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, + JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0) + goto exception; + } + } else { + general_case: + for (;;) { + BOOL done; + value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); + if (JS_IsException(value)) + goto exception; + if (done) { + /* value is JS_UNDEFINED */ + break; + } + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0) + goto exception; + } + } + /* Note: could raise an error if too many elements */ + sp[-2] = JS_NewInt32(ctx, pos); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return 0; +exception: + JS_IteratorClose(ctx, enumobj, TRUE); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return -1; +} + +static nodiscard int js_operator_instanceof(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + BOOL ret; + op1 = sp[-2]; + op2 = sp[-1]; + ret = JS_IsInstanceOf(ctx, op1, op2); + if (ret < 0) + return ret; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = JS_NewBool(ctx, ret); + return 0; +} + +static nodiscard int js_operator_typeof(JSContext *ctx, JSValueConst op1) +{ + JSAtom atom; + uint32_t tag; + tag = JS_VALUE_GET_NORM_TAG(op1); + switch(tag) { +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + atom = JS_ATOM_bigint; + break; + case JS_TAG_BIG_FLOAT: + atom = JS_ATOM_bigfloat; + break; + case JS_TAG_BIG_DECIMAL: + atom = JS_ATOM_bigdecimal; + break; +#endif + case JS_TAG_INT: + case JS_TAG_FLOAT64: + atom = JS_ATOM_number; + break; + case JS_TAG_UNDEFINED: + atom = JS_ATOM_undefined; + break; + case JS_TAG_BOOL: + atom = JS_ATOM_boolean; + break; + case JS_TAG_STRING: + atom = JS_ATOM_string; + break; + case JS_TAG_OBJECT: + { + JSObject *p; + p = JS_VALUE_GET_OBJ(op1); + if (UNLIKELY(p->is_HTMLDDA)) + atom = JS_ATOM_undefined; + else if (JS_IsFunction(ctx, op1)) + atom = JS_ATOM_function; + else + goto obj_type; + } + break; + case JS_TAG_NULL: + obj_type: + atom = JS_ATOM_object; + break; + case JS_TAG_SYMBOL: + atom = JS_ATOM_symbol; + break; + default: + atom = JS_ATOM_unknown; + break; + } + return atom; +} + +static nodiscard int js_operator_delete(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + JSAtom atom; + int ret; + op1 = sp[-2]; + op2 = sp[-1]; + atom = JS_ValueToAtom(ctx, op2); + if (UNLIKELY(atom == JS_ATOM_NULL)) + return -1; + ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT); + JS_FreeAtom(ctx, atom); + if (UNLIKELY(ret < 0)) + return -1; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = JS_NewBool(ctx, ret); + return 0; +} + +static nodiscard int js_has_unscopable(JSContext *ctx, JSValueConst obj, JSAtom atom) +{ + JSValue arr, val; + int ret; + arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables); + if (JS_IsException(arr)) + return -1; + ret = 0; + if (JS_IsObject(arr)) { + val = JS_GetProperty(ctx, arr, atom); + ret = JS_ToBoolFree(ctx, val); + } + JS_FreeValue(ctx, arr); + return ret; +} + +/* Note: it is important that no exception is returned by this function */ +static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ERROR) + return FALSE; + if (find_own_property1(p, JS_ATOM_stack)) + return FALSE; + return TRUE; +} + +/* used to avoid catching interrupt exceptions */ +static BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error; +} + +static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) +{ + struct list_head *el, *el1; + JSVarRef *var_ref; + int var_idx; + list_for_each_safe(el, el1, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + var_idx = var_ref->var_idx; + if (var_ref->is_arg) + var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]); + else + var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]); + var_ref->pvalue = &var_ref->value; + /* the reference is no longer to a local variable */ + var_ref->is_detached = TRUE; + add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + } +} + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, + JSValueConst this_obj, JSValueConst new_target, + int argc, JSValue *argv, int flags) +{ + JSRuntime *rt = caller_ctx->rt; + JSContext *ctx; + JSObject *p; + JSFunctionBytecode *b; + JSStackFrame sf_s, *sf = &sf_s; + const uint8_t *pc; + int opcode, arg_allocated_size, i; + JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval; + JSVarRef **var_refs; + size_t alloca_size; +#if !DIRECT_DISPATCH +#define SWITCH(pc) switch (opcode = *pc++) +#define CASE(op) case op +#define DEFAULT default +#define BREAK break +#else + static const void * const dispatch_table[256] = { +#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id, +#if SHORT_OPCODES +#define def(id, size, n_pop, n_push, f) +#else +#define def(id, size, n_pop, n_push, f) && case_default, +#endif +#include "third_party/quickjs/quickjs-opcode.inc" + [ OP_COUNT ... 255 ] = &&case_default + }; +#define SWITCH(pc) goto *dispatch_table[opcode = *pc++]; +#define CASE(op) case_ ## op +#define DEFAULT case_default +#define BREAK SWITCH(pc) +#endif + if (js_poll_interrupts(caller_ctx)) + return JS_EXCEPTION; + if (UNLIKELY(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { + if (flags & JS_CALL_FLAG_GENERATOR) { + JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj); + /* func_obj get contains a pointer to JSFuncAsyncState */ + /* the stack frame is already allocated */ + sf = &s->frame; + p = JS_VALUE_GET_OBJ(sf->cur_func); + b = p->u.func.function_bytecode; + ctx = b->realm; + var_refs = p->u.func.var_refs; + local_buf = arg_buf = sf->arg_buf; + var_buf = sf->var_buf; + stack_buf = sf->var_buf + b->var_count; + sp = sf->cur_sp; + sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */ + pc = sf->cur_pc; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; + if (s->throw_flag) + goto exception; + else + goto restart; + } else { + goto not_a_function; + } + } + p = JS_VALUE_GET_OBJ(func_obj); + if (UNLIKELY(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { + JSClassCall *call_func; + call_func = rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeError(caller_ctx, "not a function"); + } + return call_func(caller_ctx, func_obj, this_obj, argc, + (JSValueConst *)argv, flags); + } + b = p->u.func.function_bytecode; + if (UNLIKELY(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) { + arg_allocated_size = b->arg_count; + } else { + arg_allocated_size = 0; + } + alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count + + b->stack_size); + if (js_check_stack_overflow(rt, alloca_size)) + return JS_ThrowStackOverflow(caller_ctx); + sf->js_mode = b->js_mode; + arg_buf = argv; + sf->arg_count = argc; + sf->cur_func = (JSValue)func_obj; + init_list_head(&sf->var_ref_list); + var_refs = p->u.func.var_refs; + local_buf = gc(malloc(alloca_size)); + if (UNLIKELY(arg_allocated_size)) { + int n = min_int(argc, b->arg_count); + arg_buf = local_buf; + for(i = 0; i < n; i++) + arg_buf[i] = JS_DupValue(caller_ctx, argv[i]); + for(; i < b->arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + sf->arg_count = b->arg_count; + } + var_buf = local_buf + arg_allocated_size; + sf->var_buf = var_buf; + sf->arg_buf = arg_buf; + for(i = 0; i < b->var_count; i++) + var_buf[i] = JS_UNDEFINED; + stack_buf = var_buf + b->var_count; + sp = stack_buf; + pc = b->byte_code_buf; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; + ctx = b->realm; /* set the current realm */ + restart: + for(;;) { + int call_argc; + JSValue *call_argv; + SWITCH(pc) { + CASE(OP_push_i32): + *sp++ = JS_NewInt32(ctx, get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_push_const): + *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]); + pc += 4; + BREAK; +#if SHORT_OPCODES + CASE(OP_push_minus1): + CASE(OP_push_0): + CASE(OP_push_1): + CASE(OP_push_2): + CASE(OP_push_3): + CASE(OP_push_4): + CASE(OP_push_5): + CASE(OP_push_6): + CASE(OP_push_7): + *sp++ = JS_NewInt32(ctx, opcode - OP_push_0); + BREAK; + CASE(OP_push_i8): + *sp++ = JS_NewInt32(ctx, get_i8(pc)); + pc += 1; + BREAK; + CASE(OP_push_i16): + *sp++ = JS_NewInt32(ctx, get_i16(pc)); + pc += 2; + BREAK; + CASE(OP_push_const8): + *sp++ = JS_DupValue(ctx, b->cpool[*pc++]); + BREAK; + CASE(OP_fclosure8): + *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf); + if (UNLIKELY(JS_IsException(sp[-1]))) + goto exception; + BREAK; + CASE(OP_push_empty_string): + *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string); + BREAK; + CASE(OP_get_length): + { + JSValue val; + val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length); + if (UNLIKELY(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; +#endif + CASE(OP_push_atom_value): + *sp++ = JS_AtomToValue(ctx, get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_undefined): + *sp++ = JS_UNDEFINED; + BREAK; + CASE(OP_null): + *sp++ = JS_NULL; + BREAK; + CASE(OP_push_this): + /* OP_push_this is only called at the start of a function */ + { + JSValue val; + if (!(b->js_mode & JS_MODE_STRICT)) { + uint32_t tag = JS_VALUE_GET_TAG(this_obj); + if (LIKELY(tag == JS_TAG_OBJECT)) + goto normal_this; + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) { + val = JS_DupValue(ctx, ctx->global_obj); + } else { + val = JS_ToObject(ctx, this_obj); + if (JS_IsException(val)) + goto exception; + } + } else { + normal_this: + val = JS_DupValue(ctx, this_obj); + } + *sp++ = val; + } + BREAK; + CASE(OP_push_false): + *sp++ = JS_FALSE; + BREAK; + CASE(OP_push_true): + *sp++ = JS_TRUE; + BREAK; + CASE(OP_object): + *sp++ = JS_NewObject(ctx); + if (UNLIKELY(JS_IsException(sp[-1]))) + goto exception; + BREAK; + CASE(OP_special_object): + { + int arg = *pc++; + switch(arg) { + case OP_SPECIAL_OBJECT_ARGUMENTS: + *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv); + if (UNLIKELY(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: + *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv, + sf, min_int(argc, b->arg_count)); + if (UNLIKELY(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_THIS_FUNC: + *sp++ = JS_DupValue(ctx, sf->cur_func); + break; + case OP_SPECIAL_OBJECT_NEW_TARGET: + *sp++ = JS_DupValue(ctx, new_target); + break; + case OP_SPECIAL_OBJECT_HOME_OBJECT: + { + JSObject *p1; + p1 = p->u.func.home_object; + if (UNLIKELY(!p1)) + *sp++ = JS_UNDEFINED; + else + *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + break; + case OP_SPECIAL_OBJECT_VAR_OBJECT: + *sp++ = JS_NewObjectProto(ctx, JS_NULL); + if (UNLIKELY(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_IMPORT_META: + *sp++ = js_import_meta(ctx); + if (UNLIKELY(JS_IsException(sp[-1]))) + goto exception; + break; + default: + abort(); + } + } + BREAK; + CASE(OP_rest): + { + int first = get_u16(pc); + pc += 2; + *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv); + if (UNLIKELY(JS_IsException(sp[-1]))) + goto exception; + } + BREAK; + CASE(OP_drop): + JS_FreeValue(ctx, sp[-1]); + sp--; + BREAK; + CASE(OP_nip): + JS_FreeValue(ctx, sp[-2]); + sp[-2] = sp[-1]; + sp--; + BREAK; + CASE(OP_nip1): /* a b c -> b c */ + JS_FreeValue(ctx, sp[-3]); + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp--; + BREAK; + CASE(OP_dup): + sp[0] = JS_DupValue(ctx, sp[-1]); + sp++; + BREAK; + CASE(OP_dup2): /* a b -> a b a b */ + sp[0] = JS_DupValue(ctx, sp[-2]); + sp[1] = JS_DupValue(ctx, sp[-1]); + sp += 2; + BREAK; + CASE(OP_dup3): /* a b c -> a b c a b c */ + sp[0] = JS_DupValue(ctx, sp[-3]); + sp[1] = JS_DupValue(ctx, sp[-2]); + sp[2] = JS_DupValue(ctx, sp[-1]); + sp += 3; + BREAK; + CASE(OP_dup1): /* a b -> a a b */ + sp[0] = sp[-1]; + sp[-1] = JS_DupValue(ctx, sp[-2]); + sp++; + BREAK; + CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = JS_DupValue(ctx, sp[0]); + sp++; + BREAK; + CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = JS_DupValue(ctx, sp[0]); + sp++; + BREAK; + CASE(OP_insert4): /* this obj prop a -> a this obj prop a */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = JS_DupValue(ctx, sp[0]); + sp++; + BREAK; + CASE(OP_perm3): /* obj a b -> a obj b (213) */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = tmp; + } + BREAK; + CASE(OP_rot3l): /* x a b -> a b x (231) */ + { + JSValue tmp; + tmp = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot4l): /* x a b c -> a b c x */ + { + JSValue tmp; + tmp = sp[-4]; + sp[-4] = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot5l): /* x a b c d -> a b c d x */ + { + JSValue tmp; + tmp = sp[-5]; + sp[-5] = sp[-4]; + sp[-4] = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot3r): /* a b x -> x a b (312) */ + { + JSValue tmp; + tmp = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = tmp; + } + BREAK; + CASE(OP_perm4): /* obj prop a b -> a obj prop b */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = tmp; + } + BREAK; + CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = sp[-5]; + sp[-5] = tmp; + } + BREAK; + CASE(OP_swap): /* a b -> b a */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_swap2): /* a b c d -> c d a b */ + { + JSValue tmp1, tmp2; + tmp1 = sp[-4]; + tmp2 = sp[-3]; + sp[-4] = sp[-2]; + sp[-3] = sp[-1]; + sp[-2] = tmp1; + sp[-1] = tmp2; + } + BREAK; + CASE(OP_fclosure): + { + JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]); + pc += 4; + *sp++ = js_closure(ctx, bfunc, var_refs, sf); + if (UNLIKELY(JS_IsException(sp[-1]))) + goto exception; + } + BREAK; +#if SHORT_OPCODES + CASE(OP_call0): + CASE(OP_call1): + CASE(OP_call2): + CASE(OP_call3): + call_argc = opcode - OP_call0; + goto has_call_argc; +#endif + CASE(OP_call): + CASE(OP_tail_call): + { + call_argc = get_u16(pc); + pc += 2; + goto has_call_argc; + has_call_argc: + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, + JS_UNDEFINED, call_argc, call_argv, 0); + if (UNLIKELY(JS_IsException(ret_val))) + goto exception; + if (opcode == OP_tail_call) + goto done; + for(i = -1; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 1; + *sp++ = ret_val; + } + BREAK; + CASE(OP_call_constructor): + { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallConstructorInternal(ctx, call_argv[-2], + call_argv[-1], + call_argc, call_argv, 0); + if (UNLIKELY(JS_IsException(ret_val))) + goto exception; + for(i = -2; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 2; + *sp++ = ret_val; + } + BREAK; + CASE(OP_call_method): + CASE(OP_tail_call_method): + { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2], + JS_UNDEFINED, call_argc, call_argv, 0); + if (UNLIKELY(JS_IsException(ret_val))) + goto exception; + if (opcode == OP_tail_call_method) + goto done; + for(i = -2; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 2; + *sp++ = ret_val; + } + BREAK; + CASE(OP_array_from): + { + int i, ret; + call_argc = get_u16(pc); + pc += 2; + ret_val = JS_NewArray(ctx); + if (UNLIKELY(JS_IsException(ret_val))) + goto exception; + call_argv = sp - call_argc; + for(i = 0; i < call_argc; i++) { + ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i], + JS_PROP_C_W_E | JS_PROP_THROW); + call_argv[i] = JS_UNDEFINED; + if (ret < 0) { + JS_FreeValue(ctx, ret_val); + goto exception; + } + } + sp -= call_argc; + *sp++ = ret_val; + } + BREAK; + CASE(OP_apply): + { + int magic; + magic = get_u16(pc); + pc += 2; + ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic); + if (UNLIKELY(JS_IsException(ret_val))) + goto exception; + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 3; + *sp++ = ret_val; + } + BREAK; + CASE(OP_return): + ret_val = *--sp; + goto done; + CASE(OP_return_undef): + ret_val = JS_UNDEFINED; + goto done; + CASE(OP_check_ctor_return): + /* return TRUE if 'this' should be returned */ + if (!JS_IsObject(sp[-1])) { + if (!JS_IsUndefined(sp[-1])) { + JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined"); + goto exception; + } + sp[0] = JS_TRUE; + } else { + sp[0] = JS_FALSE; + } + sp++; + BREAK; + CASE(OP_check_ctor): + if (JS_IsUndefined(new_target)) { + JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); + goto exception; + } + BREAK; + CASE(OP_check_brand): + if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0) + goto exception; + BREAK; + CASE(OP_add_brand): + if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + BREAK; + CASE(OP_throw): + JS_Throw(ctx, *--sp); + goto exception; + CASE(OP_throw_error): + { + JSAtom atom; + int type; + atom = get_u32(pc); + type = pc[4]; + pc += 5; + if (type == JS_THROW_VAR_RO) + JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom); + else + if (type == JS_THROW_VAR_REDECL) + JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom); + else + if (type == JS_THROW_VAR_UNINITIALIZED) + JS_ThrowReferenceErrorUninitialized(ctx, atom); + else + if (type == JS_THROW_ERROR_DELETE_SUPER) + JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); + else + if (type == JS_THROW_ERROR_ITERATOR_THROW) + JS_ThrowTypeError(ctx, "iterator does not have a throw method"); + else + JS_ThrowInternalError(ctx, "invalid throw var type %d", type); + } + goto exception; + CASE(OP_eval): + { + JSValueConst obj; + int scope_idx; + call_argc = get_u16(pc); + scope_idx = get_u16(pc + 2) - 1; + pc += 4; + call_argv = sp - call_argc; + sf->cur_pc = pc; + if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) { + if (call_argc >= 1) + obj = call_argv[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, + JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, + JS_UNDEFINED, call_argc, call_argv, 0); + } + if (UNLIKELY(JS_IsException(ret_val))) + goto exception; + for(i = -1; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 1; + *sp++ = ret_val; + } + BREAK; + /* could merge with OP_apply */ + CASE(OP_apply_eval): + { + int scope_idx; + uint32_t len; + JSValue *tab; + JSValueConst obj; + scope_idx = get_u16(pc) - 1; + pc += 2; + tab = build_arg_list(ctx, &len, sp[-1]); + if (!tab) + goto exception; + if (js_same_value(ctx, sp[-2], ctx->eval_obj)) { + if (len >= 1) + obj = tab[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, + JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, + (JSValueConst *)tab); + } + free_arg_list(ctx, tab, len); + if (UNLIKELY(JS_IsException(ret_val))) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + *sp++ = ret_val; + } + BREAK; + CASE(OP_regexp): + { + sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED, + sp[-2], sp[-1]); + sp--; + } + BREAK; + CASE(OP_get_super): + { + JSValue proto; + proto = JS_GetPrototype(ctx, sp[-1]); + if (JS_IsException(proto)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = proto; + } + BREAK; + CASE(OP_import): + { + JSValue val; + val = js_dynamic_import(ctx, sp[-1]); + if (JS_IsException(val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + CASE(OP_check_var): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + ret = JS_CheckGlobalVar(ctx, atom); + if (ret < 0) + goto exception; + *sp++ = JS_NewBool(ctx, ret); + } + BREAK; + CASE(OP_get_var_undef): + CASE(OP_get_var): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef); + if (UNLIKELY(JS_IsException(val))) + goto exception; + *sp++ = val; + } + BREAK; + CASE(OP_put_var): + CASE(OP_put_var_init): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var); + sp--; + if (UNLIKELY(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_put_var_strict): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + /* sp[-2] is JS_TRUE or JS_FALSE */ + if (UNLIKELY(!JS_VALUE_GET_INT(sp[-2]))) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + goto exception; + } + ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2); + sp -= 2; + if (UNLIKELY(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_check_define_var): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_CheckDefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_var): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_func): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp--; + } + BREAK; + CASE(OP_get_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = JS_DupValue(ctx, var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1])); + } + BREAK; + CASE(OP_get_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = JS_DupValue(ctx, arg_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1])); + } + BREAK; +#if SHORT_OPCODES + CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK; + CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK; + CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK; + CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK; + CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK; + CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK; + CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK; + CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK; + CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK; + CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK; + CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK; + CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK; + CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK; + CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK; + CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK; + CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK; + CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK; + CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK; + CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK; + CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK; + CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK; + CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK; + CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK; + CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; +#endif + CASE(OP_get_var_ref): + { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + sp[0] = JS_DupValue(ctx, val); + sp++; + } + BREAK; + CASE(OP_put_var_ref): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_var_ref): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1])); + } + BREAK; + CASE(OP_get_var_ref_check): + { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + if (UNLIKELY(JS_IsUninitialized(val))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); + goto exception; + } + sp[0] = JS_DupValue(ctx, val); + sp++; + } + BREAK; + CASE(OP_put_var_ref_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (UNLIKELY(JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_var_ref_check_init): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (UNLIKELY(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc_uninitialized): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], JS_UNINITIALIZED); + } + BREAK; + CASE(OP_get_loc_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (UNLIKELY(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); + goto exception; + } + sp[0] = JS_DupValue(ctx, var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (UNLIKELY(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_loc_check_init): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (UNLIKELY(!JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceError(ctx, "'this' can be initialized only once"); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_close_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + close_lexical_var(ctx, sf, idx, FALSE); + } + BREAK; + CASE(OP_make_loc_ref): + CASE(OP_make_arg_ref): + CASE(OP_make_var_ref_ref): + { + JSVarRef *var_ref; + JSProperty *pr; + JSAtom atom; + int idx; + atom = get_u32(pc); + idx = get_u16(pc + 4); + pc += 6; + *sp++ = JS_NewObjectProto(ctx, JS_NULL); + if (UNLIKELY(JS_IsException(sp[-1]))) + goto exception; + if (opcode == OP_make_var_ref_ref) { + var_ref = var_refs[idx]; + var_ref->header.ref_count++; + } else { + var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref); + if (!var_ref) + goto exception; + } + pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom, + JS_PROP_WRITABLE | JS_PROP_VARREF); + if (!pr) { + free_var_ref(rt, var_ref); + goto exception; + } + pr->u.var_ref = var_ref; + *sp++ = JS_AtomToValue(ctx, atom); + } + BREAK; + CASE(OP_make_var_ref): + { + JSAtom atom; + atom = get_u32(pc); + pc += 4; + if (JS_GetGlobalVarRef(ctx, atom, sp)) + goto exception; + sp += 2; + } + BREAK; + CASE(OP_goto): + pc += (int32_t)get_u32(pc); + if (UNLIKELY(js_poll_interrupts(ctx))) + goto exception; + BREAK; +#if SHORT_OPCODES + CASE(OP_goto16): + pc += (int16_t)get_u16(pc); + if (UNLIKELY(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_goto8): + pc += (int8_t)pc[0]; + if (UNLIKELY(js_poll_interrupts(ctx))) + goto exception; + BREAK; +#endif + CASE(OP_if_true): + { + int res; + JSValue op1; + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (UNLIKELY(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false): + { + int res; + JSValue op1; + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (UNLIKELY(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; +#if SHORT_OPCODES + CASE(OP_if_true8): + { + int res; + JSValue op1; + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int8_t)pc[-1] - 1; + } + if (UNLIKELY(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false8): + { + int res; + JSValue op1; + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int8_t)pc[-1] - 1; + } + if (UNLIKELY(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; +#endif + CASE(OP_catch): + { + int32_t diff; + diff = get_u32(pc); + sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf); + sp++; + pc += 4; + } + BREAK; + CASE(OP_gosub): + { + int32_t diff; + diff = get_u32(pc); + /* XXX: should have a different tag to avoid security flaw */ + sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf); + sp++; + pc += diff; + } + BREAK; + CASE(OP_ret): + { + JSValue op1; + uint32_t pos; + op1 = sp[-1]; + if (UNLIKELY(JS_VALUE_GET_TAG(op1) != JS_TAG_INT)) + goto ret_fail; + pos = JS_VALUE_GET_INT(op1); + if (UNLIKELY(pos >= b->byte_code_len)) { + ret_fail: + JS_ThrowInternalError(ctx, "invalid ret value"); + goto exception; + } + sp--; + pc = b->byte_code_buf + pos; + } + BREAK; + CASE(OP_for_in_start): + if (js_for_in_start(ctx, sp)) + goto exception; + BREAK; + CASE(OP_for_in_next): + if (js_for_in_next(ctx, sp)) + goto exception; + sp += 2; + BREAK; + CASE(OP_for_of_start): + if (js_for_of_start(ctx, sp, FALSE)) + goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_for_of_next): + { + int offset = -3 - pc[0]; + pc += 1; + if (js_for_of_next(ctx, sp, offset)) + goto exception; + sp += 2; + } + BREAK; + CASE(OP_for_await_of_start): + if (js_for_of_start(ctx, sp, TRUE)) + goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_iterator_get_value_done): + if (js_iterator_get_value_done(ctx, sp)) + goto exception; + sp += 1; + BREAK; + CASE(OP_iterator_check_object): + if (UNLIKELY(!JS_IsObject(sp[-1]))) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto exception; + } + BREAK; + CASE(OP_iterator_close): + /* iter_obj next catch_offset -> */ + sp--; /* drop the catch offset to avoid getting caught by exception */ + JS_FreeValue(ctx, sp[-1]); /* drop the next method */ + sp--; + if (!JS_IsUndefined(sp[-1])) { + if (JS_IteratorClose(ctx, sp[-1], FALSE)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + } + sp--; + BREAK; + CASE(OP_iterator_close_return): + { + JSValue ret_val; + /* iter_obj next catch_offset ... ret_val -> + ret_eval iter_obj next catch_offset */ + ret_val = *--sp; + while (sp > stack_buf && + JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) { + JS_FreeValue(ctx, *--sp); + } + if (UNLIKELY(sp < stack_buf + 3)) { + JS_ThrowInternalError(ctx, "iterator_close_return"); + JS_FreeValue(ctx, ret_val); + goto exception; + } + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = ret_val; + sp++; + } + BREAK; + CASE(OP_iterator_next): + /* stack: iter_obj next catch_offset val */ + { + JSValue ret; + ret = JS_Call(ctx, sp[-3], sp[-4], + 1, (JSValueConst *)(sp - 1)); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + } + BREAK; + CASE(OP_iterator_call): + /* stack: iter_obj next catch_offset val */ + { + JSValue method, ret; + BOOL ret_flag; + int flags; + flags = *pc++; + method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? + JS_ATOM_throw : JS_ATOM_return); + if (JS_IsException(method)) + goto exception; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + ret_flag = TRUE; + } else { + if (flags & 2) { + /* no argument */ + ret = JS_CallFree(ctx, method, sp[-4], + 0, NULL); + } else { + ret = JS_CallFree(ctx, method, sp[-4], + 1, (JSValueConst *)(sp - 1)); + } + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + ret_flag = FALSE; + } + sp[0] = JS_NewBool(ctx, ret_flag); + sp += 1; + } + BREAK; + CASE(OP_lnot): + { + int res; + JSValue op1; + op1 = sp[-1]; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1) != 0; + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp[-1] = JS_NewBool(ctx, !res); + } + BREAK; + CASE(OP_get_field): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + val = JS_GetProperty(ctx, sp[-1], atom); + if (UNLIKELY(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + CASE(OP_get_field2): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + val = JS_GetProperty(ctx, sp[-1], atom); + if (UNLIKELY(JS_IsException(val))) + goto exception; + *sp++ = val; + } + BREAK; + CASE(OP_put_field): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], + JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (UNLIKELY(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_private_symbol): + { + JSAtom atom; + JSValue val; + atom = get_u32(pc); + pc += 4; + val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(val)) + goto exception; + *sp++ = val; + } + BREAK; + CASE(OP_get_private_field): + { + JSValue val; + val = JS_GetPrivateField(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (UNLIKELY(JS_IsException(val))) + goto exception; + } + BREAK; + CASE(OP_put_private_field): + { + int ret; + ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-1]); + sp -= 3; + if (UNLIKELY(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_define_private_field): + { + int ret; + ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (UNLIKELY(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_define_field): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1], + JS_PROP_C_W_E | JS_PROP_THROW); + sp--; + if (UNLIKELY(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_name): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE); + if (UNLIKELY(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_name_computed): + { + int ret; + ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE); + if (UNLIKELY(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_proto): + { + JSValue proto; + proto = sp[-1]; + if (JS_IsObject(proto) || JS_IsNull(proto)) { + if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0) + goto exception; + } + JS_FreeValue(ctx, proto); + sp--; + } + BREAK; + CASE(OP_set_home_object): + js_method_set_home_object(ctx, sp[-1], sp[-2]); + BREAK; + CASE(OP_define_method): + CASE(OP_define_method_computed): + { + JSValue getter, setter, value; + JSValueConst obj; + JSAtom atom; + int flags, ret, op_flags; + BOOL is_computed; + is_computed = (opcode == OP_define_method_computed); + if (is_computed) { + atom = JS_ValueToAtom(ctx, sp[-2]); + if (UNLIKELY(atom == JS_ATOM_NULL)) + goto exception; + opcode += OP_define_method - OP_define_method_computed; + } else { + atom = get_u32(pc); + pc += 4; + } + op_flags = *pc++; + obj = sp[-2 - is_computed]; + flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE | + JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW; + if (op_flags & OP_DEFINE_METHOD_ENUMERABLE) + flags |= JS_PROP_ENUMERABLE; + op_flags &= 3; + value = JS_UNDEFINED; + getter = JS_UNDEFINED; + setter = JS_UNDEFINED; + if (op_flags == OP_DEFINE_METHOD_METHOD) { + value = sp[-1]; + flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE; + } else if (op_flags == OP_DEFINE_METHOD_GETTER) { + getter = sp[-1]; + flags |= JS_PROP_HAS_GET; + } else { + setter = sp[-1]; + flags |= JS_PROP_HAS_SET; + } + ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj); + if (ret >= 0) { + ret = JS_DefineProperty(ctx, obj, atom, value, + getter, setter, flags); + } + JS_FreeValue(ctx, sp[-1]); + if (is_computed) { + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-2]); + } + sp -= 1 + is_computed; + if (UNLIKELY(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_define_class): + CASE(OP_define_class_computed): + { + int class_flags; + JSAtom atom; + atom = get_u32(pc); + class_flags = pc[4]; + pc += 5; + if (js_op_define_class(ctx, sp, atom, class_flags, + var_refs, sf, + (opcode == OP_define_class_computed)) < 0) + goto exception; + } + BREAK; + CASE(OP_get_array_el): + { + JSValue val; + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (UNLIKELY(JS_IsException(val))) + goto exception; + } + BREAK; + CASE(OP_get_array_el2): + { + JSValue val; + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + sp[-1] = val; + if (UNLIKELY(JS_IsException(val))) + goto exception; + } + BREAK; + CASE(OP_get_ref_value): + { + JSValue val; + if (UNLIKELY(JS_IsUndefined(sp[-2]))) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-1]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } + val = JS_GetPropertyValue(ctx, sp[-2], + JS_DupValue(ctx, sp[-1])); + if (UNLIKELY(JS_IsException(val))) + goto exception; + sp[0] = val; + sp++; + } + BREAK; + CASE(OP_get_super_value): + { + JSValue val; + JSAtom atom; + atom = JS_ValueToAtom(ctx, sp[-1]); + if (UNLIKELY(atom == JS_ATOM_NULL)) + goto exception; + val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE); + JS_FreeAtom(ctx, atom); + if (UNLIKELY(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-3]); + sp[-3] = val; + sp -= 2; + } + BREAK; + CASE(OP_put_array_el): + { + int ret; + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (UNLIKELY(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_put_ref_value): + { + int ret, flags; + flags = JS_PROP_THROW_STRICT; + if (UNLIKELY(JS_IsUndefined(sp[-3]))) { + if (is_strict_mode(ctx)) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } else { + sp[-3] = JS_DupValue(ctx, ctx->global_obj); + } + } else { + if (is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD; + } + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (UNLIKELY(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_put_super_value): + { + int ret; + JSAtom atom; + if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto exception; + } + atom = JS_ValueToAtom(ctx, sp[-2]); + if (UNLIKELY(atom == JS_ATOM_NULL)) + goto exception; + ret = JS_SetPropertyGeneric(ctx, sp[-3], atom, sp[-1], sp[-4], + JS_PROP_THROW_STRICT); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-4]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + sp -= 4; + if (ret < 0) + goto exception; + } + BREAK; + CASE(OP_define_array_el): + { + int ret; + ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1], + JS_PROP_C_W_E | JS_PROP_THROW); + sp -= 1; + if (UNLIKELY(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_append): /* array pos enumobj -- array pos */ + { + if (js_append_enumerate(ctx, sp)) + goto exception; + JS_FreeValue(ctx, *--sp); + } + BREAK; + CASE(OP_copy_data_properties): /* target source excludeList */ + { + /* stack offsets (-1 based): + 2 bits for target, + 3 bits for source, + 2 bits for exclusionList */ + int mask; + mask = *pc++; + if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)], + sp[-1 - ((mask >> 2) & 7)], + sp[-1 - ((mask >> 5) & 7)], 0)) + goto exception; + } + BREAK; + CASE(OP_add): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2); + if (UNLIKELY((int)r != r)) + goto add_slow; + sp[-2] = JS_NewInt32(ctx, r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + + JS_VALUE_GET_FLOAT64(op2)); + sp--; + } else { + add_slow: + if (js_add_slow(ctx, sp)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_add_loc): + { + JSValue *pv; + int idx; + idx = *pc; + pc += 1; + pv = &var_buf[idx]; + if (LIKELY(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(*pv) + + JS_VALUE_GET_INT(sp[-1]); + if (UNLIKELY((int)r != r)) + goto add_loc_slow; + *pv = JS_NewInt32(ctx, r); + sp--; + } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { + JSValue op1; + op1 = sp[-1]; + sp--; + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) + goto exception; + op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1); + if (JS_IsException(op1)) + goto exception; + set_value(ctx, pv, op1); + } else { + JSValue ops[2]; + add_loc_slow: + /* In case of exception, js_add_slow frees ops[0] + and ops[1], so we must duplicate *pv */ + ops[0] = JS_DupValue(ctx, *pv); + ops[1] = sp[-1]; + sp--; + if (js_add_slow(ctx, ops + 2)) + goto exception; + set_value(ctx, pv, ops[0]); + } + } + BREAK; + CASE(OP_sub): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2); + if (UNLIKELY((int)r != r)) + goto binary_arith_slow; + sp[-2] = JS_NewInt32(ctx, r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) - + JS_VALUE_GET_FLOAT64(op2)); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mul): + { + JSValue op1, op2; + double d; + op1 = sp[-2]; + op2 = sp[-1]; + if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int32_t v1, v2; + int64_t r; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + r = (int64_t)v1 * v2; + if (UNLIKELY((int)r != r)) { +#ifdef CONFIG_BIGNUM + if (UNLIKELY(sf->js_mode & JS_MODE_MATH) && + (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER)) + goto binary_arith_slow; +#endif + d = (double)r; + goto mul_fp_res; + } + /* need to test zero case for -0 result */ + if (UNLIKELY(r == 0 && (v1 | v2) < 0)) { + d = -0.0; + goto mul_fp_res; + } + sp[-2] = JS_NewInt32(ctx, r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { +#ifdef CONFIG_BIGNUM + if (UNLIKELY(sf->js_mode & JS_MODE_MATH)) + goto binary_arith_slow; +#endif + d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); + mul_fp_res: + sp[-2] = __JS_NewFloat64(ctx, d); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_div): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2; + if (UNLIKELY(sf->js_mode & JS_MODE_MATH)) + goto binary_arith_slow; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mod): +#ifdef CONFIG_BIGNUM + CASE(OP_math_mod): +#endif + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2, r; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + /* We must avoid v2 = 0, v1 = INT32_MIN and v2 = + -1 and the cases where the result is -0. */ + if (UNLIKELY(v1 < 0 || v2 <= 0)) + goto binary_arith_slow; + r = v1 % v2; + sp[-2] = JS_NewInt32(ctx, r); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_pow): + binary_arith_slow: + if (js_binary_arith_slow(ctx, sp, opcode)) + goto exception; + sp--; + BREAK; + CASE(OP_plus): + { + JSValue op1; + uint32_t tag; + op1 = sp[-1]; + tag = JS_VALUE_GET_TAG(op1); + if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) { + } else { + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_neg): + { + JSValue op1; + uint32_t tag; + int val; + double d; + op1 = sp[-1]; + tag = JS_VALUE_GET_TAG(op1); + if (tag == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + /* Note: -0 cannot be expressed as integer */ + if (UNLIKELY(val == 0)) { + d = -0.0; + goto neg_fp_res; + } + if (UNLIKELY(val == INT32_MIN)) { + d = -(double)val; + goto neg_fp_res; + } + sp[-1] = JS_NewInt32(ctx, -val); + } else if (JS_TAG_IS_FLOAT64(tag)) { + d = -JS_VALUE_GET_FLOAT64(op1); + neg_fp_res: + sp[-1] = __JS_NewFloat64(ctx, d); + } else { + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_inc): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (UNLIKELY(val == INT32_MAX)) + goto inc_slow; + sp[-1] = JS_NewInt32(ctx, val + 1); + } else { + inc_slow: + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_dec): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (UNLIKELY(val == INT32_MIN)) + goto dec_slow; + sp[-1] = JS_NewInt32(ctx, val - 1); + } else { + dec_slow: + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_post_inc): + CASE(OP_post_dec): + if (js_post_inc_slow(ctx, sp, opcode)) + goto exception; + sp++; + BREAK; + CASE(OP_inc_loc): + { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (UNLIKELY(val == INT32_MAX)) + goto inc_loc_slow; + var_buf[idx] = JS_NewInt32(ctx, val + 1); + } else { + inc_loc_slow: + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = JS_DupValue(ctx, op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc)) + goto exception; + set_value(ctx, &var_buf[idx], op1); + } + } + BREAK; + CASE(OP_dec_loc): + { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (UNLIKELY(val == INT32_MIN)) + goto dec_loc_slow; + var_buf[idx] = JS_NewInt32(ctx, val - 1); + } else { + dec_loc_slow: + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = JS_DupValue(ctx, op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec)) + goto exception; + set_value(ctx, &var_buf[idx], op1); + } + } + BREAK; + CASE(OP_not): + { + JSValue op1; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1)); + } else { + if (js_not_slow(ctx, sp)) + goto exception; + } + } + BREAK; + CASE(OP_shl): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v1, v2; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); +#ifdef CONFIG_BIGNUM + { + int64_t r; + if (UNLIKELY(sf->js_mode & JS_MODE_MATH)) { + if (v2 > 0x1f) + goto shl_slow; + r = (int64_t)v1 << v2; + if ((int)r != r) + goto shl_slow; + } else { + v2 &= 0x1f; + } + } +#else + v2 &= 0x1f; +#endif + sp[-2] = JS_NewInt32(ctx, v1 << v2); + sp--; + } else { +#ifdef CONFIG_BIGNUM + shl_slow: +#endif + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_shr): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v2; + v2 = JS_VALUE_GET_INT(op2); + /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */ + v2 &= 0x1f; + sp[-2] = JS_NewUint32(ctx, + (uint32_t)JS_VALUE_GET_INT(op1) >> + v2); + sp--; + } else { + if (js_shr_slow(ctx, sp)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_sar): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v2; + v2 = JS_VALUE_GET_INT(op2); +#ifdef CONFIG_BIGNUM + if (UNLIKELY(v2 > 0x1f)) { + if (UNLIKELY(sf->js_mode & JS_MODE_MATH)) + goto sar_slow; + else + v2 &= 0x1f; + } +#else + v2 &= 0x1f; +#endif + sp[-2] = JS_NewInt32(ctx, + (int)JS_VALUE_GET_INT(op1) >> v2); + sp--; + } else { +#ifdef CONFIG_BIGNUM + sar_slow: +#endif + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_and): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = JS_NewInt32(ctx, + JS_VALUE_GET_INT(op1) & + JS_VALUE_GET_INT(op2)); + sp--; + } else { + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_or): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = JS_NewInt32(ctx, + JS_VALUE_GET_INT(op1) | + JS_VALUE_GET_INT(op2)); + sp--; + } else { + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_xor): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = JS_NewInt32(ctx, + JS_VALUE_GET_INT(op1) ^ + JS_VALUE_GET_INT(op2)); + sp--; + } else { + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; +#define OP_CMP(opcode, binary_op, slow_call) \ + CASE(opcode): \ + { \ + JSValue op1, op2; \ + op1 = sp[-2]; \ + op2 = sp[-1]; \ + if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) { \ + sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \ + sp--; \ + } else { \ + if (slow_call) \ + goto exception; \ + sp--; \ + } \ + } \ + BREAK + OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0)); + OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1)); + OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0)); + OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1)); +#ifdef CONFIG_BIGNUM + CASE(OP_mul_pow10): + if (rt->bigfloat_ops.mul_pow10(ctx, sp)) + goto exception; + sp--; + BREAK; +#endif + CASE(OP_in): + if (js_operator_in(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_instanceof): + if (js_operator_instanceof(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_typeof): + { + JSValue op1; + JSAtom atom; + op1 = sp[-1]; + atom = js_operator_typeof(ctx, op1); + JS_FreeValue(ctx, op1); + sp[-1] = JS_AtomToString(ctx, atom); + } + BREAK; + CASE(OP_delete): + if (js_operator_delete(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_delete_var): + { + JSAtom atom; + int ret; + atom = get_u32(pc); + pc += 4; + ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0); + if (UNLIKELY(ret < 0)) + goto exception; + *sp++ = JS_NewBool(ctx, ret); + } + BREAK; + CASE(OP_to_object): + if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) { + ret_val = JS_ToObject(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + } + BREAK; + CASE(OP_to_propkey): + switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; + CASE(OP_to_propkey2): + /* must be tested first */ + if (UNLIKELY(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { + JS_ThrowTypeError(ctx, "value has no property"); + goto exception; + } + switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; +#if 0 + CASE(OP_to_string): + if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) { + ret_val = JS_ToString(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + } + BREAK; +#endif + CASE(OP_with_get_var): + CASE(OP_with_put_var): + CASE(OP_with_delete_var): + CASE(OP_with_make_ref): + CASE(OP_with_get_ref): + CASE(OP_with_get_ref_undef): + { + JSAtom atom; + int32_t diff; + JSValue obj, val; + int ret, is_with; + atom = get_u32(pc); + diff = get_u32(pc + 4); + is_with = pc[8]; + pc += 9; + obj = sp[-1]; + ret = JS_HasProperty(ctx, obj, atom); + if (UNLIKELY(ret < 0)) + goto exception; + if (ret) { + if (is_with) { + ret = js_has_unscopable(ctx, obj, atom); + if (UNLIKELY(ret < 0)) + goto exception; + if (ret) + goto no_with; + } + switch (opcode) { + case OP_with_get_var: + val = JS_GetProperty(ctx, obj, atom); + if (UNLIKELY(JS_IsException(val))) + goto exception; + set_value(ctx, &sp[-1], val); + break; + case OP_with_put_var: + /* XXX: check if strict mode */ + ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], + JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + if (UNLIKELY(ret < 0)) + goto exception; + break; + case OP_with_delete_var: + ret = JS_DeleteProperty(ctx, obj, atom, 0); + if (UNLIKELY(ret < 0)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_NewBool(ctx, ret); + break; + case OP_with_make_ref: + /* produce a pair object/propname on the stack */ + *sp++ = JS_AtomToValue(ctx, atom); + break; + case OP_with_get_ref: + /* produce a pair object/method on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (UNLIKELY(JS_IsException(val))) + goto exception; + *sp++ = val; + break; + case OP_with_get_ref_undef: + /* produce a pair undefined/function on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (UNLIKELY(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_UNDEFINED; + *sp++ = val; + break; + } + pc += diff - 5; + } else { + no_with: + /* if not jumping, drop the object argument */ + JS_FreeValue(ctx, sp[-1]); + sp--; + } + } + BREAK; + CASE(OP_await): + ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT); + goto done_generator; + CASE(OP_yield): + ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD); + goto done_generator; + CASE(OP_yield_star): + CASE(OP_async_yield_star): + ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR); + goto done_generator; + CASE(OP_return_async): + CASE(OP_initial_yield): + ret_val = JS_UNDEFINED; + goto done_generator; + CASE(OP_nop): + BREAK; + CASE(OP_is_undefined_or_null): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED || + JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } else { + goto free_and_set_false; + } +#if SHORT_OPCODES + CASE(OP_is_undefined): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) { + goto set_true; + } else { + goto free_and_set_false; + } + CASE(OP_is_null): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } else { + goto free_and_set_false; + } + /* XXX: could merge to a single opcode */ + CASE(OP_typeof_is_undefined): + /* different from OP_is_undefined because of isHTMLDDA */ + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) { + goto free_and_set_true; + } else { + goto free_and_set_false; + } + CASE(OP_typeof_is_function): + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { + goto free_and_set_true; + } else { + goto free_and_set_false; + } + free_and_set_true: + JS_FreeValue(ctx, sp[-1]); +#endif + set_true: + sp[-1] = JS_TRUE; + BREAK; + free_and_set_false: + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_FALSE; + BREAK; + CASE(OP_invalid): + DEFAULT: + JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", + (int)(pc - b->byte_code_buf - 1), opcode); + goto exception; + } + } + exception: + if (is_backtrace_needed(ctx, rt->current_exception)) { + /* add the backtrace information now (it is not done + before if the exception happens in a bytecode + operation */ + sf->cur_pc = pc; + build_backtrace(ctx, rt->current_exception, NULL, 0, 0); + } + if (!JS_IsUncatchableError(ctx, rt->current_exception)) { + while (sp > stack_buf) { + JSValue val = *--sp; + JS_FreeValue(ctx, val); + if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) { + int pos = JS_VALUE_GET_INT(val); + if (pos == 0) { + /* enumerator: close it with a throw */ + JS_FreeValue(ctx, sp[-1]); /* drop the next method */ + sp--; + JS_IteratorClose(ctx, sp[-1], TRUE); + } else { + *sp++ = rt->current_exception; + rt->current_exception = JS_NULL; + pc = b->byte_code_buf + pos; + goto restart; + } + } + } + } + ret_val = JS_EXCEPTION; + /* the local variables are freed by the caller in the generator + case. Hence the label 'done' should never be reached in a + generator function. */ + if (b->func_kind != JS_FUNC_NORMAL) { + done_generator: + sf->cur_pc = pc; + sf->cur_sp = sp; + } else { + done: + if (UNLIKELY(!list_empty(&sf->var_ref_list))) { + /* variable references reference the stack: must close them */ + close_var_refs(rt, sf); + } + /* free the local variables and stack */ + for(pval = local_buf; pval < sp; pval++) { + JS_FreeValue(ctx, *pval); + } + } + rt->current_stack_frame = sf->prev_frame; + return ret_val; +} + +JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, + argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, + argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); + JS_FreeValue(ctx, func_obj); + return res; +} + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +static JSValue JS_CallConstructorInternal(JSContext *ctx, + JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValue *argv, int flags) +{ + JSObject *p; + JSFunctionBytecode *b; + if (js_poll_interrupts(ctx)) + return JS_EXCEPTION; + flags |= JS_CALL_FLAG_CONSTRUCTOR; + if (UNLIKELY(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) + goto not_a_function; + p = JS_VALUE_GET_OBJ(func_obj); + if (UNLIKELY(!p->is_constructor)) + return JS_ThrowTypeError(ctx, "not a constructor"); + if (UNLIKELY(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { + JSClassCall *call_func; + call_func = ctx->rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeError(ctx, "not a function"); + } + return call_func(ctx, func_obj, new_target, argc, + (JSValueConst *)argv, flags); + } + b = p->u.func.function_bytecode; + if (b->is_derived_class_constructor) { + return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags); + } else { + JSValue obj, ret; + /* legacy constructor behavior */ + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); + if (JS_IsException(obj)) + return JS_EXCEPTION; + ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags); + if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT || + JS_IsException(ret)) { + JS_FreeValue(ctx, obj); + return ret; + } else { + JS_FreeValue(ctx, ret); + return obj; + } + } +} + +/* XXX: add a specific eval mode so that Function("}), ({") is rejected */ +JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv, int magic) +{ + JSFunctionKindEnum func_kind = magic; + int i, n, ret; + JSValue s, proto, obj = JS_UNDEFINED; + StringBuffer b_s, *b = &b_s; + string_buffer_init(ctx, b, 0); + string_buffer_putc8(b, '('); + if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) { + string_buffer_puts8(b, "async "); + } + string_buffer_puts8(b, "function"); + if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) { + string_buffer_putc8(b, '*'); + } + string_buffer_puts8(b, " anonymous("); + n = argc - 1; + for(i = 0; i < n; i++) { + if (i != 0) { + string_buffer_putc8(b, ','); + } + if (string_buffer_concat_value(b, argv[i])) + goto fail; + } + string_buffer_puts8(b, "\n) {\n"); + if (n >= 0) { + if (string_buffer_concat_value(b, argv[n])) + goto fail; + } + string_buffer_puts8(b, "\n})"); + s = string_buffer_end(b); + if (JS_IsException(s)) + goto fail1; + obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1); + JS_FreeValue(ctx, s); + if (JS_IsException(obj)) + goto fail1; + if (!JS_IsUndefined(new_target)) { + /* set the prototype */ + proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); + if (JS_IsException(proto)) + goto fail1; + if (!JS_IsObject(proto)) { + JSContext *realm; + JS_FreeValue(ctx, proto); + realm = JS_GetFunctionRealm(ctx, new_target); + if (!realm) + goto fail1; + proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]); + } + ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE); + JS_FreeValue(ctx, proto); + if (ret < 0) + goto fail1; + } + return obj; + fail: + string_buffer_free(b); + fail1: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +/* JSAsyncFunctionState (used by generator and async functions) */ +int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, + JSValueConst func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + JSObject *p; + JSFunctionBytecode *b; + JSStackFrame *sf; + int local_count, i, arg_buf_len, n; + sf = &s->frame; + init_list_head(&sf->var_ref_list); + p = JS_VALUE_GET_OBJ(func_obj); + b = p->u.func.function_bytecode; + sf->js_mode = b->js_mode; + sf->cur_pc = b->byte_code_buf; + arg_buf_len = max_int(b->arg_count, argc); + local_count = arg_buf_len + b->var_count + b->stack_size; + sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1)); + if (!sf->arg_buf) + return -1; + sf->cur_func = JS_DupValue(ctx, func_obj); + s->this_val = JS_DupValue(ctx, this_obj); + s->argc = argc; + sf->arg_count = arg_buf_len; + sf->var_buf = sf->arg_buf + arg_buf_len; + sf->cur_sp = sf->var_buf + b->var_count; + for(i = 0; i < argc; i++) + sf->arg_buf[i] = JS_DupValue(ctx, argv[i]); + n = arg_buf_len + b->var_count; + for(i = argc; i < n; i++) + sf->arg_buf[i] = JS_UNDEFINED; + return 0; +} + +void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, JS_MarkFunc *mark_func) +{ + JSStackFrame *sf; + JSValue *sp; + sf = &s->frame; + JS_MarkValue(rt, sf->cur_func, mark_func); + JS_MarkValue(rt, s->this_val, mark_func); + if (sf->cur_sp) { + /* if the function is running, cur_sp is not known so we + cannot mark the stack. Marking the variables is not needed + because a running function cannot be part of a removable + cycle */ + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) + JS_MarkValue(rt, *sp, mark_func); + } +} + +void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) +{ + JSStackFrame *sf; + JSValue *sp; + sf = &s->frame; + /* close the closure variables. */ + close_var_refs(rt, sf); + if (sf->arg_buf) { + /* cannot free the function if it is running */ + assert(sf->cur_sp != NULL); + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) { + JS_FreeValueRT(rt, *sp); + } + js_free_rt(rt, sf->arg_buf); + } + JS_FreeValueRT(rt, sf->cur_func); + JS_FreeValueRT(rt, s->this_val); +} + +JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) +{ + JSValue func_obj; + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); + /* the tag does not matter provided it is not an object */ + func_obj = JS_MKPTR(JS_TAG_INT, s); + return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, + s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR); +} diff --git a/third_party/quickjs/cutils.c b/third_party/quickjs/cutils.c index 89f517670..7d2483381 100644 --- a/third_party/quickjs/cutils.c +++ b/third_party/quickjs/cutils.c @@ -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) diff --git a/third_party/quickjs/cutils.h b/third_party/quickjs/cutils.h index a50ae3d22..4a8d95509 100644 --- a/third_party/quickjs/cutils.h +++ b/third_party/quickjs/cutils.h @@ -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') diff --git a/third_party/quickjs/date.c b/third_party/quickjs/date.c new file mode 100644 index 000000000..a5b0716df --- /dev/null +++ b/third_party/quickjs/date.c @@ -0,0 +1,1034 @@ +/* + * 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/calls/weirdtypes.h" +#include "libc/fmt/fmt.h" +#include "libc/time/struct/tm.h" +#include "libc/time/time.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 */ + +#if 0 +/* OS dependent: return the UTC time in ms since 1970. */ +static JSValue js___date_now(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int64_t d; + struct timeval tv; + gettimeofday(&tv, NULL); + d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); + return JS_NewInt64(ctx, d); +} +#endif + +/* OS dependent: return the UTC time in microseconds since 1970. */ +JSValue js___date_clock(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ + int64_t d; + struct timeval tv; + gettimeofday(&tv, NULL); + d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec; + return JS_NewInt64(ctx, d); +} + +/* OS dependent. d = argv[0] is in ms from 1970. Return the difference + between UTC time and local time 'd' in minutes */ +static int getTimezoneOffset(int64_t time) { +#if defined(_WIN32) + /* XXX: TODO */ + return 0; +#else + time_t ti; + struct tm tm; + time /= 1000; /* convert to seconds */ + if (sizeof(time_t) == 4) { + /* on 32-bit systems, we need to clamp the time value to the + range of `time_t`. This is better than truncating values to + 32 bits and hopefully provides the same result as 64-bit + implementation of localtime_r. + */ + if ((time_t)-1 < 0) { + if (time < INT32_MIN) { + time = INT32_MIN; + } else if (time > INT32_MAX) { + time = INT32_MAX; + } + } else { + if (time < 0) { + time = 0; + } else if (time > UINT32_MAX) { + time = UINT32_MAX; + } + } + } + ti = time; + localtime_r(&ti, &tm); + return -tm.tm_gmtoff / 60; +#endif +} + +#if 0 +static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + double dd; + + if (JS_ToFloat64(ctx, &dd, argv[0])) + return JS_EXCEPTION; + if (isnan(dd)) + return __JS_NewFloat64(ctx, dd); + else + return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd)); +} + +static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor, + JSValueConst def_proto) +{ + JSValue proto; + proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); + if (JS_IsException(proto)) + return proto; + if (!JS_IsObject(proto)) { + JS_FreeValue(ctx, proto); + proto = JS_DupValue(ctx, def_proto); + } + return proto; +} + +/* create a new date object */ +static JSValue js___date_create(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, proto; + proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]); + if (JS_IsException(proto)) + return proto; + obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE); + JS_FreeValue(ctx, proto); + if (!JS_IsException(obj)) + JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2])); + return obj; +} +#endif + +static int64_t math_mod(int64_t a, int64_t b) { + /* return positive modulo */ + int64_t m = a % b; + return m + (m < 0) * b; +} + +static int64_t floor_div(int64_t a, int64_t b) { + /* integer division rounding toward -Infinity */ + int64_t m = a % b; + return (a - (m + (m < 0) * b)) / b; +} + +static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + +static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst 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_DATE && JS_IsNumber(p->u.object_data)) + return JS_ToFloat64(ctx, valp, p->u.object_data); + } + JS_ThrowTypeError(ctx, "not a Date object"); + return -1; +} + +static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v) +{ + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_DATE) { + JS_FreeValue(ctx, p->u.object_data); + p->u.object_data = JS_NewFloat64(ctx, v); + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a Date object"); +} + +static int64_t days_from_year(int64_t y) { + return 365 * (y - 1970) + floor_div(y - 1969, 4) - + floor_div(y - 1901, 100) + floor_div(y - 1601, 400); +} + +static int64_t days_in_year(int64_t y) { + return 365 + !(y % 4) - !(y % 100) + !(y % 400); +} + +/* return the year, update days */ +static int64_t year_from_days(int64_t *days) { + int64_t y, d1, nd, d = *days; + y = floor_div(d * 10000, 3652425) + 1970; + /* the initial approximation is very good, so only a few + iterations are necessary */ + for(;;) { + d1 = d - days_from_year(y); + if (d1 < 0) { + y--; + d1 += days_in_year(y); + } else { + nd = days_in_year(y); + if (d1 < nd) + break; + d1 -= nd; + y++; + } + } + *days = d1; + return y; +} + +static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; +static char const day_names[] = "SunMonTueWedThuFriSat"; + +static __exception int get_date_fields(JSContext *ctx, JSValueConst obj, + double fields[9], int is_local, int force) +{ + double dval; + int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0; + if (JS_ThisTimeValue(ctx, &dval, obj)) + return -1; + if (isnan(dval)) { + if (!force) + return FALSE; /* NaN */ + d = 0; /* initialize all fields to 0 */ + } else { + d = dval; + if (is_local) { + tz = -getTimezoneOffset(d); + d += tz * 60000; + } + } + /* result is >= 0, we can use % */ + h = math_mod(d, 86400000); + days = (d - h) / 86400000; + ms = h % 1000; + h = (h - ms) / 1000; + s = h % 60; + h = (h - s) / 60; + m = h % 60; + h = (h - m) / 60; + wd = math_mod(days + 4, 7); /* week day */ + y = year_from_days(&days); + for(i = 0; i < 11; i++) { + md = month_days[i]; + if (i == 1) + md += days_in_year(y) - 365; + if (days < md) + break; + days -= md; + } + fields[0] = y; + fields[1] = i; + fields[2] = days + 1; + fields[3] = h; + fields[4] = m; + fields[5] = s; + fields[6] = ms; + fields[7] = wd; + fields[8] = tz; + return TRUE; +} + +static double time_clip(double t) { + if (t >= -8.64e15 && t <= 8.64e15) + return trunc(t) + 0.0; /* convert -0 to +0 */ + else + return NAN; +} + +/* The spec mandates the use of 'double' and it fixes the order + of the operations */ +static double set_date_fields(double fields[], int is_local) { + int64_t y; + double days, d, h, m1; + int i, m, md; + m1 = fields[1]; + m = fmod(m1, 12); + if (m < 0) + m += 12; + y = (int64_t)(fields[0] + floor(m1 / 12)); + days = days_from_year(y); + for(i = 0; i < m; i++) { + md = month_days[i]; + if (i == 1) + md += days_in_year(y) - 365; + days += md; + } + days += fields[2] - 1; + h = fields[3] * 3600000 + fields[4] * 60000 + + fields[5] * 1000 + fields[6]; + d = days * 86400000 + h; + if (is_local) + d += getTimezoneOffset(d) * 60000; + return time_clip(d); +} + +static JSValue get_date_field(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + // get_date_field(obj, n, is_local) + double fields[9]; + int res, n, is_local; + is_local = magic & 0x0F; + n = (magic >> 4) & 0x0F; + res = get_date_fields(ctx, this_val, fields, is_local, 0); + if (res < 0) + return JS_EXCEPTION; + if (!res) + return JS_NAN; + if (magic & 0x100) { // getYear + fields[0] -= 1900; + } + return JS_NewFloat64(ctx, fields[n]); +} + +static JSValue set_date_field(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + // _field(obj, first_field, end_field, args, is_local) + double fields[9]; + int res, first_field, end_field, is_local, i, n; + double d, a; + d = NAN; + first_field = (magic >> 8) & 0x0F; + end_field = (magic >> 4) & 0x0F; + is_local = magic & 0x0F; + res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0); + if (res < 0) + return JS_EXCEPTION; + if (res && argc > 0) { + n = end_field - first_field; + if (argc < n) + n = argc; + for(i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + goto done; + fields[first_field + i] = trunc(a); + } + d = set_date_fields(fields, is_local); + } +done: + return JS_SetThisTimeValue(ctx, this_val, d); +} + +/* fmt: + 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT" + 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)" + 2: toISOString: "2018-01-02T23:02:56.927Z" + 3: toLocaleString: "1/2/2018, 11:40:40 PM" + part: 1=date, 2=time 3=all + XXX: should use a variant of strftime(). + */ +static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + // _string(obj, fmt, part) + char buf[64]; + double fields[9]; + int res, fmt, part, pos; + int y, mon, d, h, m, s, ms, wd, tz; + fmt = (magic >> 4) & 0x0F; + part = magic & 0x0F; + res = get_date_fields(ctx, this_val, fields, fmt & 1, 0); + if (res < 0) + return JS_EXCEPTION; + if (!res) { + if (fmt == 2) + return JS_ThrowRangeError(ctx, "Date value is NaN"); + else + return JS_NewString(ctx, "Invalid Date"); + } + y = fields[0]; + mon = fields[1]; + d = fields[2]; + h = fields[3]; + m = fields[4]; + s = fields[5]; + ms = fields[6]; + wd = fields[7]; + tz = fields[8]; + pos = 0; + if (part & 1) { /* date part */ + switch(fmt) { + case 0: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%.3s, %02d %.3s %0*d ", + day_names + wd * 3, d, + month_names + mon * 3, 4 + (y < 0), y); + break; + case 1: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%.3s %.3s %02d %0*d", + day_names + wd * 3, + month_names + mon * 3, d, 4 + (y < 0), y); + if (part == 3) { + buf[pos++] = ' '; + } + break; + case 2: + if (y >= 0 && y <= 9999) { + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%04d", y); + } else { + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%+07d", y); + } + pos += snprintf(buf + pos, sizeof(buf) - pos, + "-%02d-%02dT", mon + 1, d); + break; + case 3: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y); + if (part == 3) { + buf[pos++] = ','; + buf[pos++] = ' '; + } + break; + } + } + if (part & 2) { /* time part */ + switch(fmt) { + case 0: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d:%02d:%02d GMT", h, m, s); + break; + case 1: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d:%02d:%02d GMT", h, m, s); + if (tz < 0) { + buf[pos++] = '-'; + tz = -tz; + } else { + buf[pos++] = '+'; + } + /* tz is >= 0, can use % */ + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d%02d", tz / 60, tz % 60); + /* XXX: tack the time zone code? */ + break; + case 2: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d:%02d:%02d.%03dZ", h, m, s, ms); + break; + case 3: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s, + (h < 12) ? 'A' : 'P'); + break; + } + } + return JS_NewStringLen(ctx, buf, pos); +} + +/* OS dependent: return the UTC time in ms since 1970. */ +static int64_t date_now(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); +} + +static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + // Date(y, mon, d, h, m, s, ms) + JSValue rv; + int i, n; + double a, val; + if (JS_IsUndefined(new_target)) { + /* invoked as function */ + argc = 0; + } + n = argc; + if (n == 0) { + val = date_now(); + } else if (n == 1) { + JSValue v, dv; + if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(argv[0]); + if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) { + if (JS_ToFloat64(ctx, &val, p->u.object_data)) + return JS_EXCEPTION; + val = time_clip(val); + goto has_val; + } + } + v = JS_ToPrimitive(ctx, argv[0], HINT_NONE); + if (JS_IsString(v)) { + dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v); + JS_FreeValue(ctx, v); + if (JS_IsException(dv)) + return JS_EXCEPTION; + if (JS_ToFloat64Free(ctx, &val, dv)) + return JS_EXCEPTION; + } else { + if (JS_ToFloat64Free(ctx, &val, v)) + return JS_EXCEPTION; + } + val = time_clip(val); + } else { + double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; + if (n > 7) + n = 7; + for(i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + break; + fields[i] = trunc(a); + if (i == 0 && fields[0] >= 0 && fields[0] < 100) + fields[0] += 1900; + } + val = (i == n) ? set_date_fields(fields, 1) : NAN; + } +has_val: +#if 0 + JSValueConst args[3]; + args[0] = new_target; + args[1] = ctx->class_proto[JS_CLASS_DATE]; + args[2] = JS_NewFloat64(ctx, val); + rv = js___date_create(ctx, JS_UNDEFINED, 3, args); +#else + rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE); + if (!JS_IsException(rv)) + JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val)); +#endif + if (!JS_IsException(rv) && JS_IsUndefined(new_target)) { + /* invoked as a function, return (new Date()).toString(); */ + JSValue s; + s = get_date_string(ctx, rv, 0, NULL, 0x13); + JS_FreeValue(ctx, rv); + rv = s; + } + return rv; +} + +static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // UTC(y, mon, d, h, m, s, ms) + double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; + int i, n; + double a; + n = argc; + if (n == 0) + return JS_NAN; + if (n > 7) + n = 7; + for(i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + return JS_NAN; + fields[i] = trunc(a); + if (i == 0 && fields[0] >= 0 && fields[0] < 100) + fields[0] += 1900; + } + return JS_NewFloat64(ctx, set_date_fields(fields, 0)); +} + +static void string_skip_spaces(JSString *sp, int *pp) { + while (*pp < sp->len && string_get(sp, *pp) == ' ') + *pp += 1; +} + +static void string_skip_non_spaces(JSString *sp, int *pp) { + while (*pp < sp->len && string_get(sp, *pp) != ' ') + *pp += 1; +} + +/* parse a numeric field with an optional sign if accept_sign is TRUE */ +static int string_get_digits(JSString *sp, int *pp, int64_t *pval) { + int64_t v = 0; + int c, p = *pp, p_start; + if (p >= sp->len) + return -1; + p_start = p; + while (p < sp->len) { + c = string_get(sp, p); + if (!(c >= '0' && c <= '9')) { + if (p == p_start) + return -1; + else + break; + } + v = v * 10 + c - '0'; + p++; + } + *pval = v; + *pp = p; + return 0; +} + +static int string_get_signed_digits(JSString *sp, int *pp, int64_t *pval) { + int res, sgn, p = *pp; + if (p >= sp->len) + return -1; + sgn = string_get(sp, p); + if (sgn == '-' || sgn == '+') + p++; + res = string_get_digits(sp, &p, pval); + if (res == 0 && sgn == '-') + *pval = -*pval; + *pp = p; + return res; +} + +/* parse a fixed width numeric field */ +static int string_get_fixed_width_digits(JSString *sp, int *pp, int n, int64_t *pval) { + int64_t v = 0; + int i, c, p = *pp; + for(i = 0; i < n; i++) { + if (p >= sp->len) + return -1; + c = string_get(sp, p); + if (!(c >= '0' && c <= '9')) + return -1; + v = v * 10 + c - '0'; + p++; + } + *pval = v; + *pp = p; + return 0; +} + +static int string_get_milliseconds(JSString *sp, int *pp, int64_t *pval) { + /* parse milliseconds as a fractional part, round to nearest */ + /* XXX: the spec does not indicate which rounding should be used */ + int mul = 1000, ms = 0, p = *pp, c, p_start; + if (p >= sp->len) + return -1; + p_start = p; + while (p < sp->len) { + c = string_get(sp, p); + if (!(c >= '0' && c <= '9')) { + if (p == p_start) + return -1; + else + break; + } + if (mul == 1 && c >= '5') + ms += 1; + ms += (c - '0') * (mul /= 10); + p++; + } + *pval = ms; + *pp = p; + return 0; +} + + +static int find_abbrev(JSString *sp, int p, const char *list, int count) { + int n, i; + if (p + 3 <= sp->len) { + for (n = 0; n < count; n++) { + for (i = 0; i < 3; i++) { + if (string_get(sp, p + i) != month_names[n * 3 + i]) + goto next; + } + return n; + next:; + } + } + return -1; +} + +static int string_get_month(JSString *sp, int *pp, int64_t *pval) { + int n; + string_skip_spaces(sp, pp); + n = find_abbrev(sp, *pp, month_names, 12); + if (n < 0) + return -1; + *pval = n; + *pp += 3; + return 0; +} + +static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // parse(s) + JSValue s, rv; + int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 }; + double fields1[7]; + int64_t tz, hh, mm; + double d; + int p, i, c, sgn, l; + JSString *sp; + BOOL is_local; + rv = JS_NAN; + s = JS_ToString(ctx, argv[0]); + if (JS_IsException(s)) + return JS_EXCEPTION; + sp = JS_VALUE_GET_STRING(s); + p = 0; + if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) { + /* ISO format */ + /* year field can be negative */ + if (string_get_signed_digits(sp, &p, &fields[0])) + goto done; + for (i = 1; i < 7; i++) { + if (p >= sp->len) + break; + switch(i) { + case 1: + case 2: + c = '-'; + break; + case 3: + c = 'T'; + break; + case 4: + case 5: + c = ':'; + break; + case 6: + c = '.'; + break; + } + if (string_get(sp, p) != c) + break; + p++; + if (i == 6) { + if (string_get_milliseconds(sp, &p, &fields[i])) + goto done; + } else { + if (string_get_digits(sp, &p, &fields[i])) + goto done; + } + } + /* no time: UTC by default */ + is_local = (i > 3); + fields[1] -= 1; + /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */ + tz = 0; + if (p < sp->len) { + sgn = string_get(sp, p); + if (sgn == '+' || sgn == '-') { + p++; + l = sp->len - p; + if (l != 4 && l != 5) + goto done; + if (string_get_fixed_width_digits(sp, &p, 2, &hh)) + goto done; + if (l == 5) { + if (string_get(sp, p) != ':') + goto done; + p++; + } + if (string_get_fixed_width_digits(sp, &p, 2, &mm)) + goto done; + tz = hh * 60 + mm; + if (sgn == '-') + tz = -tz; + is_local = FALSE; + } else if (sgn == 'Z') { + p++; + is_local = FALSE; + } else { + goto done; + } + /* error if extraneous characters */ + if (p != sp->len) + goto done; + } + } else { + /* toString or toUTCString format */ + /* skip the day of the week */ + string_skip_non_spaces(sp, &p); + string_skip_spaces(sp, &p); + if (p >= sp->len) + goto done; + c = string_get(sp, p); + if (c >= '0' && c <= '9') { + /* day of month first */ + if (string_get_digits(sp, &p, &fields[2])) + goto done; + if (string_get_month(sp, &p, &fields[1])) + goto done; + } else { + /* month first */ + if (string_get_month(sp, &p, &fields[1])) + goto done; + string_skip_spaces(sp, &p); + if (string_get_digits(sp, &p, &fields[2])) + goto done; + } + /* year */ + string_skip_spaces(sp, &p); + if (string_get_signed_digits(sp, &p, &fields[0])) + goto done; + /* hour, min, seconds */ + string_skip_spaces(sp, &p); + for(i = 0; i < 3; i++) { + if (i == 1 || i == 2) { + if (p >= sp->len) + goto done; + if (string_get(sp, p) != ':') + goto done; + p++; + } + if (string_get_digits(sp, &p, &fields[3 + i])) + goto done; + } + // XXX: parse optional milliseconds? + /* parse the time zone offset if present: [+-]HHmm */ + is_local = FALSE; + tz = 0; + for (tz = 0; p < sp->len; p++) { + sgn = string_get(sp, p); + if (sgn == '+' || sgn == '-') { + p++; + if (string_get_fixed_width_digits(sp, &p, 2, &hh)) + goto done; + if (string_get_fixed_width_digits(sp, &p, 2, &mm)) + goto done; + tz = hh * 60 + mm; + if (sgn == '-') + tz = -tz; + break; + } + } + } + for(i = 0; i < 7; i++) + fields1[i] = fields[i]; + d = set_date_fields(fields1, is_local) - tz * 60000; + rv = JS_NewFloat64(ctx, d); +done: + JS_FreeValue(ctx, s); + return rv; +} + +static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // now() + return JS_NewInt64(ctx, date_now()); +} + +static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // Symbol_toPrimitive(hint) + JSValueConst obj = this_val; + JSAtom hint = JS_ATOM_NULL; + int hint_num; + if (!JS_IsObject(obj)) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (JS_IsString(argv[0])) { + hint = JS_ValueToAtom(ctx, argv[0]); + if (hint == JS_ATOM_NULL) + return JS_EXCEPTION; + JS_FreeAtom(ctx, hint); + } + switch (hint) { + case JS_ATOM_number: +#ifdef CONFIG_BIGNUM + case JS_ATOM_integer: +#endif + hint_num = HINT_NUMBER; + break; + case JS_ATOM_string: + case JS_ATOM_default: + hint_num = HINT_STRING; + break; + default: + return JS_ThrowTypeError(ctx, "invalid hint"); + } + return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY); +} + +static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // getTimezoneOffset() + double v; + if (JS_ThisTimeValue(ctx, &v, this_val)) + return JS_EXCEPTION; + if (isnan(v)) + return JS_NAN; + else + return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v))); +} + +static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // getTime() + double v; + + if (JS_ThisTimeValue(ctx, &v, this_val)) + return JS_EXCEPTION; + return JS_NewFloat64(ctx, v); +} + +static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // setTime(v) + double v; + + if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0])) + return JS_EXCEPTION; + return JS_SetThisTimeValue(ctx, this_val, time_clip(v)); +} + +static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // setYear(y) + double y; + JSValueConst args[1]; + + if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0])) + return JS_EXCEPTION; + y = +y; + if (isfinite(y)) { + y = trunc(y); + if (y >= 0 && y < 100) + y += 1900; + } + args[0] = JS_NewFloat64(ctx, y); + return set_date_field(ctx, this_val, 1, args, 0x011); +} + +static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // toJSON(key) + JSValue obj, tv, method, rv; + double d; + rv = JS_EXCEPTION; + tv = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER); + if (JS_IsException(tv)) + goto exception; + if (JS_IsNumber(tv)) { + if (JS_ToFloat64(ctx, &d, tv) < 0) + goto exception; + if (!isfinite(d)) { + rv = JS_NULL; + goto done; + } + } + method = JS_GetPropertyStr(ctx, obj, "toISOString"); + if (JS_IsException(method)) + goto exception; + if (!JS_IsFunction(ctx, method)) { + JS_ThrowTypeError(ctx, "object needs toISOString method"); + JS_FreeValue(ctx, method); + goto exception; + } + rv = JS_CallFree(ctx, method, obj, 0, NULL); +exception: +done: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, tv); + return rv; +} + +static const JSCFunctionListEntry js_date_funcs[] = { + JS_CFUNC_DEF("now", 0, js_Date_now ), + JS_CFUNC_DEF("parse", 1, js_Date_parse ), + JS_CFUNC_DEF("UTC", 7, js_Date_UTC ), +}; + +static const JSCFunctionListEntry js_date_proto_funcs[] = { + JS_CFUNC_DEF("valueOf", 0, js_date_getTime ), + JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ), + JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ), + JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ), + JS_ALIAS_DEF("toGMTString", "toUTCString" ), + JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ), + JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ), + JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ), + JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ), + JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ), + JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ), + JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ), + JS_CFUNC_DEF("getTime", 0, js_date_getTime ), + JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ), + JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ), + JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ), + JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ), + JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ), + JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ), + JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ), + JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ), + JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ), + JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ), + JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ), + JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ), + JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ), + JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ), + JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ), + JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ), + JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ), + JS_CFUNC_DEF("setTime", 1, js_date_setTime ), + JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ), + JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ), + JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ), + JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ), + JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ), + JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ), + JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ), + JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ), + JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ), + JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ), + JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ), + JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ), + JS_CFUNC_DEF("setYear", 1, js_date_setYear ), + JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ), + JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ), + JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ), +}; + +void JS_AddIntrinsicDate(JSContext *ctx) +{ + JSValueConst obj; + /* Date */ + ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs, + countof(js_date_proto_funcs)); + obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7, + ctx->class_proto[JS_CLASS_DATE]); + JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs)); +} diff --git a/third_party/quickjs/dbuf.c b/third_party/quickjs/dbuf.c new file mode 100644 index 000000000..4a3ade574 --- /dev/null +++ b/third_party/quickjs/dbuf.c @@ -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)); +} diff --git a/third_party/mbedtls/shiftright-pure.c b/third_party/quickjs/diglet.c similarity index 80% rename from third_party/mbedtls/shiftright-pure.c rename to third_party/quickjs/diglet.c index d5a7b353a..242d195ab 100644 --- a/third_party/mbedtls/shiftright-pure.c +++ b/third_party/quickjs/diglet.c @@ -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; } diff --git a/third_party/quickjs/diglet.h b/third_party/quickjs/diglet.h new file mode 100644 index 000000000..b4fd9f9a3 --- /dev/null +++ b/third_party/quickjs/diglet.h @@ -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_ */ diff --git a/third_party/quickjs/eq.c b/third_party/quickjs/eq.c new file mode 100644 index 000000000..67315e7bc --- /dev/null +++ b/third_party/quickjs/eq.c @@ -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 */ +} diff --git a/third_party/quickjs/err.c b/third_party/quickjs/err.c new file mode 100644 index 000000000..57cda4e96 --- /dev/null +++ b/third_party/quickjs/err.c @@ -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 = ""; + 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 : ""); + 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); +} diff --git a/third_party/quickjs/float.c b/third_party/quickjs/float.c new file mode 100644 index 000000000..f739770cd --- /dev/null +++ b/third_party/quickjs/float.c @@ -0,0 +1,1160 @@ +/* + * 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_NewBigFloat(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_FLOAT, p); +} + +static JSValue js_float_env_constructor(JSContext *ctx, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue obj; + JSFloatEnv *fe; + int64_t prec; + int flags, rndmode; + prec = ctx->fp_env.prec; + flags = ctx->fp_env.flags; + if (!JS_IsUndefined(argv[0])) { + if (JS_ToInt64Sat(ctx, &prec, argv[0])) + return JS_EXCEPTION; + if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) + return JS_ThrowRangeError(ctx, "invalid precision"); + flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */ + if (argc > 1 && !JS_IsUndefined(argv[1])) { + if (JS_ToInt32Sat(ctx, &rndmode, argv[1])) + return JS_EXCEPTION; + if (rndmode < BF_RNDN || rndmode > BF_RNDF) + return JS_ThrowRangeError(ctx, "invalid rounding mode"); + flags = rndmode; + } + } + obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV); + if (JS_IsException(obj)) + return JS_EXCEPTION; + fe = js_malloc(ctx, sizeof(*fe)); + if (!fe) + return JS_EXCEPTION; + fe->prec = prec; + fe->flags = flags; + fe->status = 0; + JS_SetOpaque(obj, fe); + return obj; +} + +/* if the returned bigfloat is allocated it is equal to + 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return + NULL in case of error. */ +bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val) +{ + uint32_t tag; + bf_t *r; + JSBigFloat *p; + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + r = buf; + bf_init(ctx->bf_ctx, r); + if (bf_set_si(r, JS_VALUE_GET_INT(val))) + goto fail; + break; + case JS_TAG_FLOAT64: + r = buf; + bf_init(ctx->bf_ctx, r); + if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) { + fail: + bf_delete(r); + return NULL; + } + break; + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + p = JS_VALUE_GET_PTR(val); + r = &p->num; + break; + case JS_TAG_UNDEFINED: + default: + r = buf; + bf_init(ctx->bf_ctx, r); + bf_set_nan(r); + break; + } + return r; +} + +static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val) +{ + return JS_NewInt64(ctx, ctx->fp_env.prec); +} + +static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val) +{ + return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags)); +} + +static JSValue js_float_env_setPrec(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst func; + int exp_bits, flags, saved_flags; + JSValue ret; + limb_t saved_prec; + int64_t prec; + func = argv[0]; + if (JS_ToInt64Sat(ctx, &prec, argv[1])) + return JS_EXCEPTION; + if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) + return JS_ThrowRangeError(ctx, "invalid precision"); + exp_bits = BF_EXP_BITS_MAX; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt32Sat(ctx, &exp_bits, argv[2])) + return JS_EXCEPTION; + if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX) + return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); + } + flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits); + saved_prec = ctx->fp_env.prec; + saved_flags = ctx->fp_env.flags; + ctx->fp_env.prec = prec; + ctx->fp_env.flags = flags; + ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL); + /* always restore the floating point precision */ + ctx->fp_env.prec = saved_prec; + ctx->fp_env.flags = saved_flags; + return ret; +} + +static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic) +{ + JSFloatEnv *fe; + fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); + if (!fe) + return JS_EXCEPTION; + switch(magic) { + case FE_PREC: + return JS_NewInt64(ctx, fe->prec); + case FE_EXP: + return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags)); + case FE_RNDMODE: + return JS_NewInt32(ctx, fe->flags & BF_RND_MASK); + case FE_SUBNORMAL: + return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0); + default: + return JS_NewBool(ctx, (fe->status & magic) != 0); + } +} + +static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val) +{ + int rnd_mode; + if (JS_ToInt32Sat(ctx, &rnd_mode, val)) + return -1; + if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) { + JS_ThrowRangeError(ctx, "invalid rounding mode"); + return -1; + } + return rnd_mode; +} + +static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic) +{ + JSFloatEnv *fe; + int b; + int64_t prec; + fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); + if (!fe) + return JS_EXCEPTION; + switch(magic) { + case FE_PREC: + if (JS_ToInt64Sat(ctx, &prec, val)) + return JS_EXCEPTION; + if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) + return JS_ThrowRangeError(ctx, "invalid precision"); + fe->prec = prec; + break; + case FE_EXP: + if (JS_ToInt32Sat(ctx, &b, val)) + return JS_EXCEPTION; + if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX) + return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); + fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) | + bf_set_exp_bits(b); + break; + case FE_RNDMODE: + b = bigfloat_get_rnd_mode(ctx, val); + if (b < 0) + return JS_EXCEPTION; + fe->flags = (fe->flags & ~BF_RND_MASK) | b; + break; + case FE_SUBNORMAL: + b = JS_ToBool(ctx, val); + fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0); + break; + default: + b = JS_ToBool(ctx, val); + fe->status = (fe->status & ~magic) & ((-b) & magic); + break; + } + return JS_UNDEFINED; +} + +static JSValue js_float_env_clearStatus(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); + if (!fe) + return JS_EXCEPTION; + fe->status = 0; + return JS_UNDEFINED; +} + +static const JSCFunctionListEntry js_float_env_funcs[] = { + JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ), + JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ), + JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ), + JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ), + JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ), + JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ), + JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ), + JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ), + JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ), + JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ), + JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ), + JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ), + JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ), + JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ), +}; + +static const JSCFunctionListEntry js_float_env_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status, + js_float_env_proto_set_status, FE_PREC ), + JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status, + js_float_env_proto_set_status, FE_EXP ), + JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status, + js_float_env_proto_set_status, FE_RNDMODE ), + JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status, + js_float_env_proto_set_status, FE_SUBNORMAL ), + JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_INVALID_OP ), + JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ), + JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_OVERFLOW ), + JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_UNDERFLOW ), + JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_INEXACT ), + JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ), +}; + +static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val) +{ + if (JS_IsBigFloat(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_FLOAT) { + if (JS_IsBigFloat(p->u.object_data)) + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a bigfloat"); +} + +static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + int base; + JSValue ret; + val = js_thisBigFloatValue(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_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_thisBigFloatValue(ctx, this_val); +} + +static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t f; + int rnd_mode, radix; + val = js_thisBigFloatValue(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; + radix = 10; + /* XXX: swap parameter order for rounding mode and radix */ + if (argc > 1) { + rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + if (argc > 2) { + radix = js_get_radix(ctx, argv[2]); + if (radix < 0) + goto fail; + } + ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC); + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val) +{ + BOOL res; + uint32_t tag; + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_BIG_FLOAT: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + res = bf_is_finite(&p->num); + } + break; + default: + res = FALSE; + break; + } + return res; +} + +static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t f; + int rnd_mode, radix; + val = js_thisBigFloatValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToInt64Sat(ctx, &f, argv[0])) + goto fail; + if (!js_bigfloat_is_finite(ctx, val)) { + ret = JS_ToString(ctx, val); + } else if (JS_IsUndefined(argv[0])) { + ret = js_ftoa(ctx, val, 10, 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; + radix = 10; + if (argc > 1) { + rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + if (argc > 2) { + radix = js_get_radix(ctx, argv[2]); + if (radix < 0) + goto fail; + } + ret = js_ftoa(ctx, val, radix, 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_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t p; + int rnd_mode, radix; + + val = js_thisBigFloatValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_IsUndefined(argv[0])) + goto to_string; + if (JS_ToInt64Sat(ctx, &p, argv[0])) + goto fail; + if (!js_bigfloat_is_finite(ctx, val)) { + to_string: + ret = JS_ToString(ctx, this_val); + } else { + if (p < 1 || p > BF_PREC_MAX) { + JS_ThrowRangeError(ctx, "invalid number of digits"); + goto fail; + } + rnd_mode = BF_RNDNA; + radix = 10; + if (argc > 1) { + rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + if (argc > 2) { + radix = js_get_radix(ctx, argv[2]); + if (radix < 0) + goto fail; + } + ret = js_ftoa(ctx, val, radix, 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_bigfloat_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ), + JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ), + JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ), + JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ), + JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ), +}; + +static JSValue js_bigfloat_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) { + bf_t *r; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + r = JS_GetBigFloat(val); + bf_set_zero(r, 0); + } else { + val = JS_DupValue(ctx, argv[0]); + redo: + switch(JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_BIG_FLOAT: + break; + case JS_TAG_FLOAT64: + { + bf_t *r; + double d = JS_VALUE_GET_FLOAT64(val); + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + if (bf_set_float64(r, d)) + goto fail; + } + break; + case JS_TAG_INT: + { + bf_t *r; + int32_t v = JS_VALUE_GET_INT(val); + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + if (bf_set_si(r, v)) + goto fail; + } + break; + case JS_TAG_BIG_INT: + /* We keep the full precision of the integer */ + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + val = JS_MKPTR(JS_TAG_BIG_FLOAT, p); + } + break; + case JS_TAG_BIG_DECIMAL: + 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) { + bf_t *r; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + bf_set_zero(r, 0); + err = 0; + } else { + val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT | + ATOD_TYPE_BIG_FLOAT | + ATOD_ACCEPT_PREFIX_AFTER_SIGN); + 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 bigfloat literal"); + } + } + 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 bigfloat"); + } + } + return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_bigfloat_get_const(JSContext *ctx, + JSValueConst this_val, int magic) +{ + bf_t *r; + JSValue val; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + r = JS_GetBigFloat(val); + switch(magic) { + case 0: /* PI */ + bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case 1: /* LN2 */ + bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case 2: /* MIN_VALUE */ + case 3: /* MAX_VALUE */ + { + slimb_t e_range, e; + e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1); + bf_set_ui(r, 1); + if (magic == 2) { + e = -e_range + 2; + if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL) + e -= ctx->fp_env.prec - 1; + bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags); + } else { + bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec, + ctx->fp_env.flags); + bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags); + bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec, + ctx->fp_env.flags); + } + } + break; + case 4: /* EPSILON */ + bf_set_ui(r, 1); + bf_mul_2exp(r, 1 - ctx->fp_env.prec, + ctx->fp_env.prec, ctx->fp_env.flags); + break; + default: + abort(); + } + return val; +} + +static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + bf_t *a; + const char *str; + JSValue ret; + int radix; + JSFloatEnv *fe; + str = JS_ToCString(ctx, argv[0]); + if (!str) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &radix, argv[1])) { + fail: + JS_FreeCString(ctx, str); + return JS_EXCEPTION; + } + if (radix != 0 && (radix < 2 || radix > 36)) { + JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); + goto fail; + } + fe = &ctx->fp_env; + if (argc > 2) { + fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); + if (!fe) + goto fail; + } + ret = JS_NewBigFloat(ctx); + if (JS_IsException(ret)) + goto done; + a = JS_GetBigFloat(ret); + /* XXX: use js_atof() */ + bf_atof(a, str, NULL, radix, fe->prec, fe->flags); + done: + JS_FreeCString(ctx, str); + return ret; +} + +static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst val = argv[0]; + JSBigFloat *p; + if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT) + return JS_FALSE; + p = JS_VALUE_GET_PTR(val); + return JS_NewBool(ctx, bf_is_finite(&p->num)); +} + +static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst val = argv[0]; + JSBigFloat *p; + if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT) + return JS_FALSE; + p = JS_VALUE_GET_PTR(val); + return JS_NewBool(ctx, bf_is_nan(&p->num)); +} + +static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bf_t a_s, *a, *r; + JSFloatEnv *fe; + int rnd_mode; + JSValue op1, res; + op1 = JS_ToNumeric(ctx, argv[0]); + if (JS_IsException(op1)) + return op1; + a = JS_ToBigFloat(ctx, &a_s, op1); + fe = &ctx->fp_env; + if (argc > 1) { + fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV); + if (!fe) + goto fail; + } + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + fail: + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } + r = JS_GetBigFloat(res); + switch (magic) { + case MATH_OP_ABS: + bf_set(r, a); + r->sign = 0; + break; + case MATH_OP_FLOOR: + rnd_mode = BF_RNDD; + goto rint; + case MATH_OP_CEIL: + rnd_mode = BF_RNDU; + goto rint; + case MATH_OP_ROUND: + rnd_mode = BF_RNDNA; + goto rint; + case MATH_OP_TRUNC: + rnd_mode = BF_RNDZ; + rint: + bf_set(r, a); + fe->status |= bf_rint(r, rnd_mode); + break; + case MATH_OP_SQRT: + fe->status |= bf_sqrt(r, a, fe->prec, fe->flags); + break; + case MATH_OP_FPROUND: + bf_set(r, a); + fe->status |= bf_round(r, fe->prec, fe->flags); + break; + case MATH_OP_ACOS: + fe->status |= bf_acos(r, a, fe->prec, fe->flags); + break; + case MATH_OP_ASIN: + fe->status |= bf_asin(r, a, fe->prec, fe->flags); + break; + case MATH_OP_ATAN: + fe->status |= bf_atan(r, a, fe->prec, fe->flags); + break; + case MATH_OP_COS: + fe->status |= bf_cos(r, a, fe->prec, fe->flags); + break; + case MATH_OP_EXP: + fe->status |= bf_exp(r, a, fe->prec, fe->flags); + break; + case MATH_OP_LOG: + fe->status |= bf_log(r, a, fe->prec, fe->flags); + break; + case MATH_OP_SIN: + fe->status |= bf_sin(r, a, fe->prec, fe->flags); + break; + case MATH_OP_TAN: + fe->status |= bf_tan(r, a, fe->prec, fe->flags); + break; + case MATH_OP_SIGN: + if (bf_is_nan(a) || bf_is_zero(a)) { + bf_set(r, a); + } else { + bf_set_si(r, 1 - 2 * a->sign); + } + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + return res; +} + +static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bf_t a_s, *a, b_s, *b, r_s, *r = &r_s; + JSFloatEnv *fe; + JSValue op1, op2, res; + + op1 = JS_ToNumeric(ctx, argv[0]); + if (JS_IsException(op1)) + return op1; + op2 = JS_ToNumeric(ctx, argv[1]); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + return op2; + } + a = JS_ToBigFloat(ctx, &a_s, op1); + b = JS_ToBigFloat(ctx, &b_s, op2); + fe = &ctx->fp_env; + if (argc > 2) { + fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); + if (!fe) + goto fail; + } + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + fail: + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + r = JS_GetBigFloat(res); + switch (magic) { + case MATH_OP_ATAN2: + fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_POW: + fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS); + break; + case MATH_OP_FMOD: + fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ); + break; + case MATH_OP_REM: + fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN); + break; + case MATH_OP_ADD: + fe->status |= bf_add(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_SUB: + fe->status |= bf_sub(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_MUL: + fe->status |= bf_mul(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_DIV: + fe->status |= bf_div(r, a, b, fe->prec, fe->flags); + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return res; +} + +static const JSCFunctionListEntry js_bigfloat_funcs[] = { + JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ), + JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ), + JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ), + JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ), + JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ), + JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ), + JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ), + JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ), + JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ), + JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ), + JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ), + JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ), + JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ), + JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ), + JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ), + JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ), + JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ), + JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ), + JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ), + JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ), + JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ), + JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ), + JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ), + JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ), + JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ), + JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ), + JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ), + JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ), + JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ), + JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ), + JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ), + JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ), +}; + +static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf, + int radix, int flags, slimb_t *pexponent) +{ + bf_t *a; + int ret; + JSValue val; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigFloat(val); + if (flags & ATOD_ACCEPT_SUFFIX) { + /* return the exponent to get infinite precision */ + ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF, + BF_RNDZ | BF_ATOF_EXPONENT); + } else { + ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec, + ctx->fp_env.flags); + } + if (ret & BF_ST_MEM_ERROR) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + return val; +} + +static int js_unary_arith_bigfloat(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, "bigfloat argument with unary +"); + JS_FreeValue(ctx, op1); + return -1; + } + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + return -1; + } + r = JS_GetBigFloat(res); + a = JS_ToBigFloat(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, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case OP_plus: + ret = bf_set(r, a); + break; + case OP_neg: + ret = bf_set(r, a); + bf_neg(r); + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + if (UNLIKELY(ret & BF_ST_MEM_ERROR)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = res; + return 0; +} + +static int js_binary_arith_bigfloat(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_NewBigFloat(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return -1; + } + r = JS_GetBigFloat(res); + a = JS_ToBigFloat(ctx, &a_s, op1); + b = JS_ToBigFloat(ctx, &b_s, op2); + bf_init(ctx->bf_ctx, r); + switch(op) { + case OP_add: + ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case OP_sub: + ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case OP_mul: + ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case OP_div: + ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case OP_math_mod: + /* Euclidian remainder */ + ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, + BF_DIVREM_EUCLIDIAN); + break; + case OP_mod: + ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, + BF_RNDZ); + break; + case OP_pow: + ret = bf_pow(r, a, b, ctx->fp_env.prec, + ctx->fp_env.flags | BF_POW_JS_QUIRKS); + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (UNLIKELY(ret & BF_ST_MEM_ERROR)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = res; + return 0; +} + +/* Note: also used for bigint */ +int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op, JSValue op1, JSValue op2) +{ + bf_t a_s, b_s, *a, *b; + int res; + a = JS_ToBigFloat(ctx, &a_s, op1); + if (!a) { + JS_FreeValue(ctx, op2); + return -1; + } + b = JS_ToBigFloat(ctx, &b_s, op2); + if (!b) { + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + return -1; + } + switch(op) { + case OP_lt: + res = bf_cmp_lt(a, b); /* if NaN return false */ + break; + case OP_lte: + res = bf_cmp_le(a, b); /* if NaN return false */ + break; + case OP_gt: + res = bf_cmp_lt(b, a); /* if NaN return false */ + break; + case OP_gte: + res = bf_cmp_le(b, a); /* if NaN return false */ + break; + case OP_eq: + res = bf_cmp_eq(a, b); /* if NaN return false */ + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return res; +} + +static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, + int64_t exponent) +{ + bf_t r_s, *r = &r_s; + double d; + int ret; + /* always convert to Float64 */ + bf_init(ctx->bf_ctx, r); + ret = bf_mul_pow_radix(r, a, 10, exponent, + 53, bf_set_exp_bits(11) | BF_RNDN | + BF_FLAG_SUBNORMAL); + bf_get_float64(r, &d, BF_RNDN); + bf_delete(r); + if (ret & BF_ST_MEM_ERROR) + return JS_ThrowOutOfMemory(ctx); + else + return __JS_NewFloat64(ctx, d); +} + +static int js_mul_pow10(JSContext *ctx, JSValue *sp) +{ + bf_t a_s, *a, *r; + JSValue op1, op2, res; + int64_t e; + int ret; + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) + return -1; + r = JS_GetBigFloat(res); + op1 = sp[-2]; + op2 = sp[-1]; + a = JS_ToBigFloat(ctx, &a_s, op1); + if (!a) + return -1; + if (JS_IsBigInt(ctx, op2)) { + ret = JS_ToBigInt64(ctx, &e, op2); + } else { + ret = JS_ToInt64(ctx, &e, op2); + } + if (ret) { + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, res); + return -1; + } + bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags); + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = res; + return 0; +} + +void JS_AddIntrinsicBigFloat(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValueConst obj1; + rt->bigfloat_ops.to_string = js_bigfloat_to_string; + rt->bigfloat_ops.from_string = js_string_to_bigfloat; + rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat; + rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat; + rt->bigfloat_ops.compare = js_compare_bigfloat; + rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64; + rt->bigfloat_ops.mul_pow10 = js_mul_pow10; + ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT], + js_bigfloat_proto_funcs, + countof(js_bigfloat_proto_funcs)); + obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1, + ctx->class_proto[JS_CLASS_BIG_FLOAT]); + JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs, + countof(js_bigfloat_funcs)); + ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV], + js_float_env_proto_funcs, + countof(js_float_env_proto_funcs)); + obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv", + js_float_env_constructor, 1, + ctx->class_proto[JS_CLASS_FLOAT_ENV]); + JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs, + countof(js_float_env_funcs)); +} diff --git a/third_party/quickjs/gc.c b/third_party/quickjs/gc.c new file mode 100644 index 000000000..c6408d9d0 --- /dev/null +++ b/third_party/quickjs/gc.c @@ -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); +} diff --git a/third_party/quickjs/gen.c b/third_party/quickjs/gen.c new file mode 100644 index 000000000..380287f0b --- /dev/null +++ b/third_party/quickjs/gen.c @@ -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); +} diff --git a/third_party/quickjs/internal.h b/third_party/quickjs/internal.h new file mode 100644 index 000000000..c0db71b42 --- /dev/null +++ b/third_party/quickjs/internal.h @@ -0,0 +1,2214 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_QUICKJS_INTERNAL_H_ +#define COSMOPOLITAN_THIRD_PARTY_QUICKJS_INTERNAL_H_ +#include "third_party/quickjs/cutils.h" +#include "third_party/quickjs/libbf.h" +#include "third_party/quickjs/list.h" +#include "third_party/quickjs/quickjs.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ +/* clang-format off */ + +#define OPTIMIZE 1 +#define SHORT_OPCODES 1 +#if defined(EMSCRIPTEN) +#define DIRECT_DISPATCH 0 +#else +#define DIRECT_DISPATCH 1 +#endif + +#if defined(__APPLE__) +#define MALLOC_OVERHEAD 0 +#else +#define MALLOC_OVERHEAD 8 +#endif + +#if !defined(_WIN32) +/* define it if printf uses the RNDN rounding mode instead of RNDNA */ +#define CONFIG_PRINTF_RNDN +#endif + +/* define to include Atomics.* operations which depend on the OS + threads */ +#if !defined(EMSCRIPTEN) && defined(USE_WORKER) +#define CONFIG_ATOMICS +#endif + +#if !defined(EMSCRIPTEN) +/* enable stack limitation */ +#define CONFIG_STACK_CHECK +#endif + +#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) + +#define HINT_STRING 0 +#define HINT_NUMBER 1 +#define HINT_NONE 2 +#define HINT_FORCE_ORDINARY (1 << 4) /* don't try Symbol.toPrimitive */ + +/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */ +/* use as many digits as necessary */ +#define JS_DTOA_VAR_FORMAT (0 << 0) +/* use n_digits significant digits (1 <= n_digits <= 101) */ +#define JS_DTOA_FIXED_FORMAT (1 << 0) +/* force fractional format: [-]dd.dd with n_digits fractional digits */ +#define JS_DTOA_FRAC_FORMAT (2 << 0) +/* force exponential notation either in fixed or variable format */ +#define JS_DTOA_FORCE_EXP (1 << 2) + +#define ATOM_GET_STR_BUF_SIZE 64 + +#define JS_ATOM_TAG_INT (1U << 31) +#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1) +#define JS_ATOM_MAX ((1U << 30) - 1) + +/* return the max count from the hash size */ +#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) + +#if SHORT_OPCODES +/* After the final compilation pass, short opcodes are used. Their + opcodes overlap with the temporary opcodes which cannot appear in + the final bytecode. Their description is after the temporary + opcodes in opcode_info[]. */ +#define short_opcode_info(op) \ + opcode_info[(op) >= OP_TEMP_START ? \ + (op) + (OP_TEMP_END - OP_TEMP_START) : (op)] +#else +#define short_opcode_info(op) opcode_info[op] +#endif + +typedef enum { + PUT_LVALUE_NOKEEP, /* [depth] v -> */ + PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently + just disable optimizations) */ + PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */ + PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */ + PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */ +} PutLValueEnum; + +/* argument of OP_special_object */ +typedef enum { + OP_SPECIAL_OBJECT_ARGUMENTS, + OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS, + OP_SPECIAL_OBJECT_THIS_FUNC, + OP_SPECIAL_OBJECT_NEW_TARGET, + OP_SPECIAL_OBJECT_HOME_OBJECT, + OP_SPECIAL_OBJECT_VAR_OBJECT, + OP_SPECIAL_OBJECT_IMPORT_META, +} OPSpecialObjectEnum; + +typedef enum FuncCallType { + FUNC_CALL_NORMAL, + FUNC_CALL_NEW, + FUNC_CALL_SUPER_CTOR, + FUNC_CALL_TEMPLATE, +} FuncCallType; + +typedef enum { + JS_VAR_DEF_WITH, + JS_VAR_DEF_LET, + JS_VAR_DEF_CONST, + JS_VAR_DEF_FUNCTION_DECL, /* function declaration */ + JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */ + JS_VAR_DEF_CATCH, + JS_VAR_DEF_VAR, +} JSVarDefEnum; + +#define FUNC_RET_AWAIT 0 +#define FUNC_RET_YIELD 1 +#define FUNC_RET_YIELD_STAR 2 + +#define FE_PREC (-1) +#define FE_EXP (-2) +#define FE_RNDMODE (-3) +#define FE_SUBNORMAL (-4) + +#define JS_CALL_FLAG_COPY_ARGV (1 << 1) +#define JS_CALL_FLAG_GENERATOR (1 << 2) + +#define TOK_FIRST_KEYWORD TOK_NULL +#define TOK_LAST_KEYWORD TOK_AWAIT + +#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0) +/* only taken into account if filename is provided */ +#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1) + +/* unicode code points */ +#define CP_NBSP 0x00a0 +#define CP_BOM 0xfeff + +#define CP_LS 0x2028 +#define CP_PS 0x2029 + +#define MAGIC_SET (1 << 0) +#define MAGIC_WEAK (1 << 1) + +/* XXX: use enum */ +#define GEN_MAGIC_NEXT 0 +#define GEN_MAGIC_RETURN 1 +#define GEN_MAGIC_THROW 2 + +#define OP_DEFINE_METHOD_METHOD 0 +#define OP_DEFINE_METHOD_GETTER 1 +#define OP_DEFINE_METHOD_SETTER 2 +#define OP_DEFINE_METHOD_ENUMERABLE 4 + +#define JS_THROW_VAR_RO 0 +#define JS_THROW_VAR_REDECL 1 +#define JS_THROW_VAR_UNINITIALIZED 2 +#define JS_THROW_ERROR_DELETE_SUPER 3 +#define JS_THROW_ERROR_ITERATOR_THROW 4 + +#define GLOBAL_VAR_OFFSET 0x40000000 +#define ARGUMENT_VAR_OFFSET 0x20000000 + +#define DEFINE_GLOBAL_LEX_VAR (1 << 7) +#define DEFINE_GLOBAL_FUNC_VAR (1 << 6) + +#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0) + +#define ATOD_INT_ONLY (1 << 0) +/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ +#define ATOD_ACCEPT_BIN_OCT (1 << 2) +/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ +#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4) +/* accept _ between digits as a digit separator */ +#define ATOD_ACCEPT_UNDERSCORES (1 << 5) +/* allow a suffix to override the type */ +#define ATOD_ACCEPT_SUFFIX (1 << 6) +/* default type */ +#define ATOD_TYPE_MASK (3 << 7) +#define ATOD_TYPE_FLOAT64 (0 << 7) +#define ATOD_TYPE_BIG_INT (1 << 7) +#define ATOD_TYPE_BIG_FLOAT (2 << 7) +#define ATOD_TYPE_BIG_DECIMAL (3 << 7) +/* assume bigint mode: floats are parsed as integers if no decimal + point nor exponent */ +#define ATOD_MODE_BIGINT (1 << 9) +/* accept -0x1 */ +#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10) + +#define special_reduce 0 +#define special_reduceRight 1 + +#define special_every 0 +#define special_some 1 +#define special_forEach 2 +#define special_map 3 +#define special_filter 4 +#define special_TA 8 + +#define PF_IN_ACCEPTED (1 << 0) /* allow the 'in' binary operator */ +#define PF_POSTFIX_CALL (1 << 1) /* allow function calls parsing in js_parse_postfix_expr() */ +#define PF_ARROW_FUNC (1 << 2) /* allow arrow functions parsing in js_parse_postfix_expr() */ +#define PF_POW_ALLOWED (1 << 3) /* allow the exponentiation operator in js_parse_unary() */ +#define PF_POW_FORBIDDEN (1 << 4) /* forbid the exponentiation operator in js_parse_unary() */ + +#define PROP_TYPE_IDENT 0 +#define PROP_TYPE_VAR 1 +#define PROP_TYPE_GET 2 +#define PROP_TYPE_SET 3 +#define PROP_TYPE_STAR 4 +#define PROP_TYPE_ASYNC 5 +#define PROP_TYPE_ASYNC_STAR 6 + +#define PROP_TYPE_PRIVATE (1 << 4) + +#define SKIP_HAS_SEMI (1 << 0) +#define SKIP_HAS_ELLIPSIS (1 << 1) +#define SKIP_HAS_ASSIGNMENT (1 << 2) + +#define DECL_MASK_FUNC (1 << 0) /* allow normal function declaration */ +/* ored with DECL_MASK_FUNC if function declarations are allowed with a label */ +#define DECL_MASK_FUNC_WITH_LABEL (1 << 1) +#define DECL_MASK_OTHER (1 << 2) /* all other declarations */ +#define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER) + +/* dump object free */ +//#define DUMP_FREE +//#define DUMP_CLOSURE +/* dump the bytecode of the compiled functions: combination of bits + 1: dump pass 3 final byte code + 2: dump pass 2 code + 4: dump pass 1 code + 8: dump stdlib functions + 16: dump bytecode in hex + 32: dump line number table + */ +//#define DUMP_BYTECODE (1) +/* dump the occurence of the automatic GC */ +//#define DUMP_GC +/* dump objects freed by the garbage collector */ +//#define DUMP_GC_FREE +/* dump objects leaking when freeing the runtime */ +//#define DUMP_LEAKS 1 +/* dump memory usage before running the garbage collector */ +//#define DUMP_MEM +//#define DUMP_OBJECTS /* dump objects in JS_FreeContext */ +//#define DUMP_ATOMS /* dump atoms in JS_FreeContext */ +//#define DUMP_SHAPES /* dump shapes in JS_FreeContext */ +//#define DUMP_MODULE_RESOLVE +//#define DUMP_PROMISE +//#define DUMP_READ_OBJECT + +/* test the GC by forcing it before each object allocation */ +//#define FORCE_GC_AT_MALLOC + +enum { + MATH_OP_ABS, + MATH_OP_FLOOR, + MATH_OP_CEIL, + MATH_OP_ROUND, + MATH_OP_TRUNC, + MATH_OP_SQRT, + MATH_OP_FPROUND, + MATH_OP_ACOS, + MATH_OP_ASIN, + MATH_OP_ATAN, + MATH_OP_ATAN2, + MATH_OP_COS, + MATH_OP_EXP, + MATH_OP_LOG, + MATH_OP_POW, + MATH_OP_SIN, + MATH_OP_TAN, + MATH_OP_FMOD, + MATH_OP_REM, + MATH_OP_SIGN, + MATH_OP_ADD, + MATH_OP_SUB, + MATH_OP_MUL, + MATH_OP_DIV, +}; + +enum { + /* classid tag */ /* union usage | properties */ + JS_CLASS_OBJECT = 1, /* must be first */ + JS_CLASS_ARRAY, /* u.array | length */ + JS_CLASS_ERROR, + JS_CLASS_NUMBER, /* u.object_data */ + JS_CLASS_STRING, /* u.object_data */ + JS_CLASS_BOOLEAN, /* u.object_data */ + JS_CLASS_SYMBOL, /* u.object_data */ + JS_CLASS_ARGUMENTS, /* u.array | length */ + JS_CLASS_MAPPED_ARGUMENTS, /* | length */ + JS_CLASS_DATE, /* u.object_data */ + JS_CLASS_MODULE_NS, + JS_CLASS_C_FUNCTION, /* u.cfunc */ + JS_CLASS_BYTECODE_FUNCTION, /* u.func */ + JS_CLASS_BOUND_FUNCTION, /* u.bound_function */ + JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */ + JS_CLASS_GENERATOR_FUNCTION, /* u.func */ + JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */ + JS_CLASS_REGEXP, /* u.regexp */ + JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ +#ifdef CONFIG_BIGNUM + JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ +#endif + JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_DATAVIEW, /* u.typed_array */ +#ifdef CONFIG_BIGNUM + JS_CLASS_BIG_INT, /* u.object_data */ + JS_CLASS_BIG_FLOAT, /* u.object_data */ + JS_CLASS_FLOAT_ENV, /* u.float_env */ + JS_CLASS_BIG_DECIMAL, /* u.object_data */ + JS_CLASS_OPERATOR_SET, /* u.operator_set */ +#endif + JS_CLASS_MAP, /* u.map_state */ + JS_CLASS_SET, /* u.map_state */ + JS_CLASS_WEAKMAP, /* u.map_state */ + JS_CLASS_WEAKSET, /* u.map_state */ + JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ + JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */ + JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */ + JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */ + JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */ + JS_CLASS_GENERATOR, /* u.generator_data */ + JS_CLASS_PROXY, /* u.proxy_data */ + JS_CLASS_PROMISE, /* u.promise_data */ + JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */ + JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */ + JS_CLASS_ASYNC_FUNCTION, /* u.func */ + JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */ + JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */ + JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */ + JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */ + JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ + JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ +}; + +typedef enum OPCodeFormat { +#define FMT(f) OP_FMT_ ## f, +#define DEF(id, size, n_pop, n_push, f) +#include "third_party/quickjs/quickjs-opcode.inc" +#undef DEF +#undef FMT +} OPCodeFormat; + +enum OPCodeEnum { +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) OP_ ## id, +#define def(id, size, n_pop, n_push, f) +#include "third_party/quickjs/quickjs-opcode.inc" +#undef def +#undef DEF +#undef FMT + OP_COUNT, /* excluding temporary opcodes */ + /* temporary opcodes : overlap with the short opcodes */ + OP_TEMP_START = OP_nop + 1, + OP___dummy = OP_TEMP_START - 1, +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) +#define def(id, size, n_pop, n_push, f) OP_ ## id, +#include "third_party/quickjs/quickjs-opcode.inc" +#undef def +#undef DEF +#undef FMT + OP_TEMP_END, +}; + +enum { + __JS_ATOM_NULL = JS_ATOM_NULL, +#define DEF(name, str) JS_ATOM_ ## name, +#include "third_party/quickjs/quickjs-atom.inc" +#undef DEF + JS_ATOM_END, +}; +#define JS_ATOM_LAST_KEYWORD JS_ATOM_super +#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield + +typedef enum JSStrictEqModeEnum { + JS_EQ_STRICT, + JS_EQ_SAME_VALUE, + JS_EQ_SAME_VALUE_ZERO, +} JSStrictEqModeEnum; + +/* number of typed array types */ +#define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1) +static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT]; +#define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY]) + +typedef enum JSErrorEnum { + JS_EVAL_ERROR, + JS_RANGE_ERROR, + JS_REFERENCE_ERROR, + JS_SYNTAX_ERROR, + JS_TYPE_ERROR, + JS_URI_ERROR, + JS_INTERNAL_ERROR, + JS_AGGREGATE_ERROR, + JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ +} JSErrorEnum; + +#define JS_MAX_LOCAL_VARS 65536 +#define JS_STACK_SIZE_MAX 65534 +#define JS_STRING_LEN_MAX ((1 << 30) - 1) + +#define __exception nodiscard + +typedef struct JSShape JSShape; +typedef struct JSString JSString; +typedef struct JSString JSAtomStruct; + +typedef enum { + JS_GC_PHASE_NONE, + JS_GC_PHASE_DECREF, + JS_GC_PHASE_REMOVE_CYCLES, +} JSGCPhaseEnum; + +typedef enum OPCodeEnum OPCodeEnum; + +#ifdef CONFIG_BIGNUM +/* function pointers are used for numeric operations so that it is + possible to remove some numeric types */ +typedef struct { + JSValue (*to_string)(JSContext *ctx, JSValueConst val); + JSValue (*from_string)(JSContext *ctx, const char *buf, + int radix, int flags, slimb_t *pexponent); + int (*unary_arith)(JSContext *ctx, + JSValue *pres, OPCodeEnum op, JSValue op1); + int (*binary_arith)(JSContext *ctx, OPCodeEnum op, + JSValue *pres, JSValue op1, JSValue op2); + int (*compare)(JSContext *ctx, OPCodeEnum op, + JSValue op1, JSValue op2); + /* only for bigfloat: */ + JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a, + int64_t exponent); + int (*mul_pow10)(JSContext *ctx, JSValue *sp); +} JSNumericOperations; +#endif + +typedef enum JSIteratorKindEnum { + JS_ITERATOR_KIND_KEY, + JS_ITERATOR_KIND_VALUE, + JS_ITERATOR_KIND_KEY_AND_VALUE, +} JSIteratorKindEnum; + +typedef struct JSMapRecord { + int ref_count; /* used during enumeration to avoid freeing the record */ + BOOL empty; /* TRUE if the record is deleted */ + struct JSMapState *map; + struct JSMapRecord *next_weak_ref; + struct list_head link; + struct list_head hash_link; + JSValue key; + JSValue value; +} JSMapRecord; + +typedef struct JSMapIteratorData { + JSValue obj; + JSIteratorKindEnum kind; + JSMapRecord *cur_record; +} JSMapIteratorData; + +typedef struct JSMapState { + BOOL is_weak; /* TRUE if WeakSet/WeakMap */ + struct list_head records; /* list of JSMapRecord.link */ + uint32_t record_count; + struct list_head *hash_table; + uint32_t hash_size; /* must be a power of two */ + uint32_t record_count_threshold; /* count at which a hash table + resize is needed */ +} JSMapState; + +struct JSRuntime { + JSMallocFunctions mf; + JSMallocState malloc_state; + const char *rt_info; + int atom_hash_size; /* power of two */ + int atom_count; + int atom_size; + int atom_count_resize; /* resize hash table at this count */ + uint32_t *atom_hash; + JSAtomStruct **atom_array; + int atom_free_index; /* 0 = none */ + int class_count; /* size of class_array */ + JSClass *class_array; + struct list_head context_list; /* list of JSContext.link */ + /* list of JSGCObjectHeader.link. List of allocated GC objects (used + by the garbage collector) */ + struct list_head gc_obj_list; + /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */ + struct list_head gc_zero_ref_count_list; + struct list_head tmp_obj_list; /* used during GC */ + JSGCPhaseEnum gc_phase : 8; + size_t malloc_gc_threshold; +#ifdef DUMP_LEAKS + struct list_head string_list; /* list of JSString.link */ +#endif + /* stack limitation */ + uintptr_t stack_size; /* in bytes, 0 if no limit */ + uintptr_t stack_top; + uintptr_t stack_limit; /* lower stack limit */ + JSValue current_exception; + /* true if inside an out of memory error, to avoid recursing */ + BOOL in_out_of_memory : 8; + struct JSStackFrame *current_stack_frame; + JSInterruptHandler *interrupt_handler; + void *interrupt_opaque; + JSHostPromiseRejectionTracker *host_promise_rejection_tracker; + void *host_promise_rejection_tracker_opaque; + struct list_head job_list; /* list of JSJobEntry.link */ + JSModuleNormalizeFunc *module_normalize_func; + JSModuleLoaderFunc *module_loader_func; + void *module_loader_opaque; + BOOL can_block : 8; /* TRUE if Atomics.wait can block */ + /* used to allocate, free and clone SharedArrayBuffers */ + JSSharedArrayBufferFunctions sab_funcs; + /* Shape hash table */ + int shape_hash_bits; + int shape_hash_size; + int shape_hash_count; /* number of hashed shapes */ + JSShape **shape_hash; +#ifdef CONFIG_BIGNUM + bf_context_t bf_ctx; + JSNumericOperations bigint_ops; + JSNumericOperations bigfloat_ops; + JSNumericOperations bigdecimal_ops; + uint32_t operator_count; +#endif + void *user_opaque; +}; + +struct JSClass { + uint32_t class_id; /* 0 means free entry */ + JSAtom class_name; + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; + JSClassCall *call; + /* pointers for exotic behavior, can be NULL if none are present */ + const JSClassExoticMethods *exotic; +}; + +#define JS_MODE_STRICT (1 << 0) +#define JS_MODE_STRIP (1 << 1) +#define JS_MODE_MATH (1 << 2) + +typedef struct JSStackFrame { + struct JSStackFrame *prev_frame; /* NULL if first stack frame */ + JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */ + JSValue *arg_buf; /* arguments */ + JSValue *var_buf; /* variables */ + struct list_head var_ref_list; /* list of JSVarRef.link */ + const uint8_t *cur_pc; /* only used in bytecode functions : PC of the + instruction after the call */ + int arg_count; + int js_mode; /* 0 or JS_MODE_MATH for C functions */ + /* only used in generators. Current stack pointer value. NULL if + the function is running. */ + JSValue *cur_sp; +} JSStackFrame; + +typedef enum { + JS_GC_OBJ_TYPE_JS_OBJECT, + JS_GC_OBJ_TYPE_FUNCTION_BYTECODE, + JS_GC_OBJ_TYPE_SHAPE, + JS_GC_OBJ_TYPE_VAR_REF, + JS_GC_OBJ_TYPE_ASYNC_FUNCTION, + JS_GC_OBJ_TYPE_JS_CONTEXT, +} JSGCObjectTypeEnum; + +/* header for GC objects. GC objects are C data structures with a + reference count that can reference other GC objects. JS Objects are + a particular type of GC object. */ +struct JSGCObjectHeader { + int ref_count; /* must come first, 32-bit */ + JSGCObjectTypeEnum gc_obj_type : 4; + uint8_t mark : 4; /* used by the GC */ + uint8_t dummy1; /* not used by the GC */ + uint16_t dummy2; /* not used by the GC */ + struct list_head link; +}; + +typedef struct JSVarRef { + union { + JSGCObjectHeader header; /* must come first */ + struct { + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ + /* 0 : the JSVarRef is on the stack. header.link is an element + of JSStackFrame.var_ref_list. + 1 : the JSVarRef is detached. header.link has the normal meanning + */ + uint8_t is_detached : 1; + uint8_t is_arg : 1; + uint16_t var_idx; /* index of the corresponding function variable on + the stack */ + }; + }; + JSValue *pvalue; /* pointer to the value, either on the stack or + to 'value' */ + JSValue value; /* used when the variable is no longer on the stack */ +} JSVarRef; + +typedef struct JSFloatEnv { + limb_t prec; + bf_flags_t flags; + unsigned int status; +} JSFloatEnv; + +/* the same structure is used for big integers and big floats. Big + integers are never infinite or NaNs */ +typedef struct JSBigFloat { + JSRefCountHeader header; /* must come first, 32-bit */ + bf_t num; +} JSBigFloat; + +typedef struct JSBigDecimal { + JSRefCountHeader header; /* must come first, 32-bit */ + bfdec_t num; +} JSBigDecimal; + +typedef enum { + JS_AUTOINIT_ID_PROTOTYPE, + JS_AUTOINIT_ID_MODULE_NS, + JS_AUTOINIT_ID_PROP, +} JSAutoInitIDEnum; + +/* must be large enough to have a negligible runtime cost and small + enough to call the interrupt callback often. */ +#define JS_INTERRUPT_COUNTER_INIT 10000 + +struct JSContext { + JSGCObjectHeader header; /* must come first */ + JSRuntime *rt; + struct list_head link; + uint16_t binary_object_count; + int binary_object_size; + JSShape *array_shape; /* initial shape for Array objects */ + JSValue *class_proto; + JSValue function_proto; + JSValue function_ctor; + JSValue array_ctor; + JSValue regexp_ctor; + JSValue promise_ctor; + JSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; + JSValue iterator_proto; + JSValue async_iterator_proto; + JSValue array_proto_values; + JSValue throw_type_error; + JSValue eval_obj; + JSValue global_obj; /* global object */ + JSValue global_var_obj; /* contains the global let/const definitions */ + uint64_t random_state; +#ifdef CONFIG_BIGNUM + bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ + JSFloatEnv fp_env; /* global FP environment */ + BOOL bignum_ext : 8; /* enable math mode */ + BOOL allow_operator_overloading : 8; +#endif + /* when the counter reaches zero, JSRutime.interrupt_handler is called */ + int interrupt_counter; + BOOL is_error_property_enabled; + struct list_head loaded_modules; /* list of JSModuleDef.link */ + /* if NULL, RegExp compilation is not supported */ + JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern, + JSValueConst flags); + /* if NULL, eval is not supported */ + JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + const char *filename, int flags, int scope_idx); + void *user_opaque; +}; + +typedef union JSFloat64Union { + double d; + uint64_t u64; + uint32_t u32[2]; +} JSFloat64Union; + +enum { + JS_ATOM_TYPE_STRING = 1, + JS_ATOM_TYPE_GLOBAL_SYMBOL, + JS_ATOM_TYPE_SYMBOL, + JS_ATOM_TYPE_PRIVATE, +}; + +enum { + JS_ATOM_HASH_SYMBOL, + JS_ATOM_HASH_PRIVATE, +}; + +typedef enum { + JS_ATOM_KIND_STRING, + JS_ATOM_KIND_SYMBOL, + JS_ATOM_KIND_PRIVATE, +} JSAtomKindEnum; + +#define JS_ATOM_HASH_MASK ((1 << 30) - 1) + +struct JSString { + JSRefCountHeader header; /* must come first, 32-bit */ + uint32_t len : 31; + uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ + /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, + for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3 + XXX: could change encoding to have one more bit in hash */ + uint32_t hash : 30; + uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ + uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ +#ifdef DUMP_LEAKS + struct list_head link; /* string list */ +#endif + union { + uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */ + uint16_t str16[0]; + } u; +}; + +typedef struct JSClosureVar { + uint8_t is_local : 1; + uint8_t is_arg : 1; + uint8_t is_const : 1; + uint8_t is_lexical : 1; + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* 8 bits available */ + uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the + parent function. otherwise: index to a closure + variable of the parent function */ + JSAtom var_name; +} JSClosureVar; + +#define ARG_SCOPE_INDEX 1 +#define ARG_SCOPE_END (-2) + +typedef struct JSVarScope { + int parent; /* index into fd->scopes of the enclosing scope */ + int first; /* index into fd->vars of the last variable in this scope */ +} JSVarScope; + +typedef enum { + /* XXX: add more variable kinds here instead of using bit fields */ + JS_VAR_NORMAL, + JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */ + JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator + function declaration */ + JS_VAR_CATCH, + JS_VAR_FUNCTION_NAME, /* function expression name */ + JS_VAR_PRIVATE_FIELD, + JS_VAR_PRIVATE_METHOD, + JS_VAR_PRIVATE_GETTER, + JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */ + JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */ +} JSVarKindEnum; + +/* XXX: could use a different structure in bytecode functions to save + memory */ +typedef struct JSVarDef { + JSAtom var_name; + /* index into fd->scopes of this variable lexical scope */ + int scope_level; + /* during compilation: + - if scope_level = 0: scope in which the variable is defined + - if scope_level != 0: index into fd->vars of the next + variable in the same or enclosing lexical scope + in a bytecode function: + index into fd->vars of the next + variable in the same or enclosing lexical scope + */ + int scope_next; + uint8_t is_const : 1; + uint8_t is_lexical : 1; + uint8_t is_captured : 1; + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* only used during compilation: function pool index for lexical + variables with var_kind = + JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of + the definition of the 'var' variables (they have scope_level = + 0) */ + int func_pool_idx : 24; /* only used during compilation : index in + the constant pool for hoisted function + definition */ +} JSVarDef; + +/* for the encoding of the pc2line table */ +#define PC2LINE_BASE (-1) +#define PC2LINE_RANGE 5 +#define PC2LINE_OP_FIRST 1 +#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE) + +typedef enum JSFunctionKindEnum { + JS_FUNC_NORMAL = 0, + JS_FUNC_GENERATOR = (1 << 0), + JS_FUNC_ASYNC = (1 << 1), + JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), +} JSFunctionKindEnum; + +typedef struct JSRegExpStringIteratorData { + JSValue iterating_regexp; + JSValue iterated_string; + BOOL global; + BOOL unicode; + BOOL done; +} JSRegExpStringIteratorData; + +typedef struct JSFunctionBytecode { + JSGCObjectHeader header; /* must come first */ + uint8_t js_mode; + uint8_t has_prototype : 1; /* true if a prototype field is necessary */ + uint8_t has_simple_parameter_list : 1; + uint8_t is_derived_class_constructor : 1; + /* true if home_object needs to be initialized */ + uint8_t need_home_object : 1; + uint8_t func_kind : 2; + uint8_t new_target_allowed : 1; + uint8_t super_call_allowed : 1; + uint8_t super_allowed : 1; + uint8_t arguments_allowed : 1; + uint8_t has_debug : 1; + uint8_t backtrace_barrier : 1; /* stop backtrace on this function */ + uint8_t read_only_bytecode : 1; + /* XXX: 4 bits available */ + uint8_t *byte_code_buf; /* (self pointer) */ + int byte_code_len; + JSAtom func_name; + JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */ + JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */ + uint16_t arg_count; + uint16_t var_count; + uint16_t defined_arg_count; /* for length function property */ + uint16_t stack_size; /* maximum stack size */ + JSContext *realm; /* function realm */ + JSValue *cpool; /* constant pool (self pointer) */ + int cpool_count; + int closure_var_count; + struct { + /* debug info, move to separate structure to save memory? */ + JSAtom filename; + int line_num; + int source_len; + int pc2line_len; + uint8_t *pc2line_buf; + char *source; + } debug; +} JSFunctionBytecode; + +typedef struct JSBoundFunction { + JSValue func_obj; + JSValue this_val; + int argc; + JSValue argv[0]; +} JSBoundFunction; + +typedef struct JSForInIterator { + JSValue obj; + BOOL is_array; + uint32_t array_length; + uint32_t idx; +} JSForInIterator; + +typedef struct JSRegExp { + JSString *pattern; + JSString *bytecode; /* also contains the flags */ +} JSRegExp; + +typedef struct JSProxyData { + JSValue target; + JSValue handler; + uint8_t is_func; + uint8_t is_revoked; +} JSProxyData; + +typedef struct JSArrayBuffer { + int byte_length; /* 0 if detached */ + uint8_t detached; + uint8_t shared; /* if shared, the array buffer cannot be detached */ + uint8_t *data; /* NULL if detached */ + struct list_head array_list; + void *opaque; + JSFreeArrayBufferDataFunc *free_func; +} JSArrayBuffer; + +typedef struct JSTypedArray { + struct list_head link; /* link to arraybuffer */ + JSObject *obj; /* back pointer to the TypedArray/DataView object */ + JSObject *buffer; /* based array buffer */ + uint32_t offset; /* offset in the array buffer */ + uint32_t length; /* length in the array buffer */ +} JSTypedArray; + +typedef struct JSAsyncFunctionState { + JSValue this_val; /* 'this' generator argument */ + int argc; /* number of function arguments */ + BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */ + JSStackFrame frame; +} JSAsyncFunctionState; + +/* XXX: could use an object instead to avoid the + JS_TAG_ASYNC_FUNCTION tag for the GC */ +typedef struct JSAsyncFunctionData { + JSGCObjectHeader header; /* must come first */ + JSValue resolving_funcs[2]; + BOOL is_active; /* true if the async function state is valid */ + JSAsyncFunctionState func_state; +} JSAsyncFunctionData; + +typedef enum { + /* binary operators */ + JS_OVOP_ADD, + JS_OVOP_SUB, + JS_OVOP_MUL, + JS_OVOP_DIV, + JS_OVOP_MOD, + JS_OVOP_POW, + JS_OVOP_OR, + JS_OVOP_AND, + JS_OVOP_XOR, + JS_OVOP_SHL, + JS_OVOP_SAR, + JS_OVOP_SHR, + JS_OVOP_EQ, + JS_OVOP_LESS, + JS_OVOP_BINARY_COUNT, + /* unary operators */ + JS_OVOP_POS = JS_OVOP_BINARY_COUNT, + JS_OVOP_NEG, + JS_OVOP_INC, + JS_OVOP_DEC, + JS_OVOP_NOT, + JS_OVOP_COUNT, +} JSOverloadableOperatorEnum; + +typedef struct StringBuffer { + JSContext *ctx; + JSString *str; + int len; + int size; + int is_wide_char; + int error_status; +} StringBuffer; + +typedef struct { + uint32_t operator_index; + JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */ +} JSBinaryOperatorDefEntry; + +typedef struct { + int count; + JSBinaryOperatorDefEntry *tab; +} JSBinaryOperatorDef; + +typedef struct { + uint32_t operator_counter; + BOOL is_primitive; /* OperatorSet for a primitive type */ + /* NULL if no operator is defined */ + JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */ + JSBinaryOperatorDef left; + JSBinaryOperatorDef right; +} JSOperatorSetData; + +typedef struct JSReqModuleEntry { + JSAtom module_name; + JSModuleDef *module; /* used using resolution */ +} JSReqModuleEntry; + +typedef enum JSExportTypeEnum { + JS_EXPORT_TYPE_LOCAL, + JS_EXPORT_TYPE_INDIRECT, +} JSExportTypeEnum; + +typedef struct JSExportEntry { + union { + struct { + int var_idx; /* closure variable index */ + JSVarRef *var_ref; /* if != NULL, reference to the variable */ + } local; /* for local export */ + int req_module_idx; /* module for indirect export */ + } u; + JSExportTypeEnum export_type; + JSAtom local_name; /* '*' if export ns from. not used for local + export after compilation */ + JSAtom export_name; /* exported variable name */ +} JSExportEntry; + +typedef struct JSStarExportEntry { + int req_module_idx; /* in req_module_entries */ +} JSStarExportEntry; + +typedef struct JSImportEntry { + int var_idx; /* closure variable index */ + JSAtom import_name; + int req_module_idx; /* in req_module_entries */ +} JSImportEntry; + +struct JSModuleDef { + JSRefCountHeader header; /* must come first, 32-bit */ + JSAtom module_name; + struct list_head link; + JSReqModuleEntry *req_module_entries; + int req_module_entries_count; + int req_module_entries_size; + JSExportEntry *export_entries; + int export_entries_count; + int export_entries_size; + JSStarExportEntry *star_export_entries; + int star_export_entries_count; + int star_export_entries_size; + JSImportEntry *import_entries; + int import_entries_count; + int import_entries_size; + JSValue module_ns; + JSValue func_obj; /* only used for JS modules */ + JSModuleInitFunc *init_func; /* only used for C modules */ + BOOL resolved : 8; + BOOL func_created : 8; + BOOL instantiated : 8; + BOOL evaluated : 8; + BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */ + /* true if evaluation yielded an exception. It is saved in + eval_exception */ + BOOL eval_has_exception : 8; + JSValue eval_exception; + JSValue meta_obj; /* for import.meta */ +}; + +typedef struct JSJobEntry { + struct list_head link; + JSContext *ctx; + JSJobFunc *job_func; + int argc; + JSValue argv[0]; +} JSJobEntry; + +typedef struct JSProperty { + union { + JSValue value; /* JS_PROP_NORMAL */ + struct { /* JS_PROP_GETSET */ + JSObject *getter; /* NULL if undefined */ + JSObject *setter; /* NULL if undefined */ + } getset; + JSVarRef *var_ref; /* JS_PROP_VARREF */ + struct { /* JS_PROP_AUTOINIT */ + /* in order to use only 2 pointers, we compress the realm + and the init function pointer */ + uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x) + in the 2 low bits */ + void *opaque; + } init; + } u; +} JSProperty; + +#define JS_PROP_INITIAL_SIZE 2 +#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ +#define JS_ARRAY_INITIAL_SIZE 2 + +typedef struct JSShapeProperty { + uint32_t hash_next : 26; /* 0 if last in list */ + uint32_t flags : 6; /* JS_PROP_XXX */ + JSAtom atom; /* JS_ATOM_NULL = free property entry */ +} JSShapeProperty; + +struct JSShape { + /* hash table of size hash_mask + 1 before the start of the + structure (see prop_hash_end()). */ + JSGCObjectHeader header; + /* true if the shape is inserted in the shape hash table. If not, + JSShape.hash is not valid */ + uint8_t is_hashed; + /* If true, the shape may have small array index properties 'n' with 0 + <= n <= 2^31-1. If false, the shape is guaranteed not to have + small array index properties */ + uint8_t has_small_array_index; + uint32_t hash; /* current hash value */ + uint32_t prop_hash_mask; + int prop_size; /* allocated properties */ + int prop_count; /* include deleted properties */ + int deleted_prop_count; + JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ + JSObject *proto; + JSShapeProperty prop[0]; /* prop_size elements */ +}; + +struct JSObject { + union { + JSGCObjectHeader header; + struct { + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ + + uint8_t extensible : 1; + uint8_t free_mark : 1; /* only used when freeing objects with cycles */ + uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ + uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */ + uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ + uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */ + uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ + uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ + uint16_t class_id; /* see JS_CLASS_x */ + }; + }; + /* byte offsets: 16/24 */ + JSShape *shape; /* prototype and property names + flag */ + JSProperty *prop; /* array of properties */ + /* byte offsets: 24/40 */ + struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */ + /* byte offsets: 28/48 */ + union { + void *opaque; + struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ + struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ + struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ + struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */ + struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ +#ifdef CONFIG_BIGNUM + struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */ + struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */ +#endif + struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ + struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ + struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ + struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ + struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */ + struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ + struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */ + struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */ + struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */ + struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ + struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */ + struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */ + /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */ + struct JSFunctionBytecode *function_bytecode; + JSVarRef **var_refs; + JSObject *home_object; /* for 'super' access */ + } func; + struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */ + JSContext *realm; + JSCFunctionType c_function; + uint8_t length; + uint8_t cproto; + int16_t magic; + } cfunc; + /* array part for fast arrays and typed arrays */ + struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + union { + uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + } u1; + union { + JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */ + uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */ + int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */ + uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */ + int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */ + uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ + int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ + uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ + float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ + double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ + } u; + uint32_t count; /* <= 2^31-1. 0 for a detached typed array */ + } array; /* 12/20 bytes */ + JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ + JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ + } u; + /* byte sizes: 40/48/72 */ +}; + +enum { + TOK_NUMBER = -128, + TOK_STRING, + TOK_TEMPLATE, + TOK_IDENT, + TOK_REGEXP, + /* warning: order matters (see js_parse_assign_expr) */ + TOK_MUL_ASSIGN, + TOK_DIV_ASSIGN, + TOK_MOD_ASSIGN, + TOK_PLUS_ASSIGN, + TOK_MINUS_ASSIGN, + TOK_SHL_ASSIGN, + TOK_SAR_ASSIGN, + TOK_SHR_ASSIGN, + TOK_AND_ASSIGN, + TOK_XOR_ASSIGN, + TOK_OR_ASSIGN, +#ifdef CONFIG_BIGNUM + TOK_MATH_POW_ASSIGN, +#endif + TOK_POW_ASSIGN, + TOK_LAND_ASSIGN, + TOK_LOR_ASSIGN, + TOK_DOUBLE_QUESTION_MARK_ASSIGN, + TOK_DEC, + TOK_INC, + TOK_SHL, + TOK_SAR, + TOK_SHR, + TOK_LT, + TOK_LTE, + TOK_GT, + TOK_GTE, + TOK_EQ, + TOK_STRICT_EQ, + TOK_NEQ, + TOK_STRICT_NEQ, + TOK_LAND, + TOK_LOR, +#ifdef CONFIG_BIGNUM + TOK_MATH_POW, +#endif + TOK_POW, + TOK_ARROW, + TOK_ELLIPSIS, + TOK_DOUBLE_QUESTION_MARK, + TOK_QUESTION_MARK_DOT, + TOK_ERROR, + TOK_PRIVATE_NAME, + TOK_EOF, + /* keywords: WARNING: same order as atoms */ + TOK_NULL, /* must be first */ + TOK_FALSE, + TOK_TRUE, + TOK_IF, + TOK_ELSE, + TOK_RETURN, + TOK_VAR, + TOK_THIS, + TOK_DELETE, + TOK_VOID, + TOK_TYPEOF, + TOK_NEW, + TOK_IN, + TOK_INSTANCEOF, + TOK_DO, + TOK_WHILE, + TOK_FOR, + TOK_BREAK, + TOK_CONTINUE, + TOK_SWITCH, + TOK_CASE, + TOK_DEFAULT, + TOK_THROW, + TOK_TRY, + TOK_CATCH, + TOK_FINALLY, + TOK_FUNCTION, + TOK_DEBUGGER, + TOK_WITH, + /* FutureReservedWord */ + TOK_CLASS, + TOK_CONST, + TOK_ENUM, + TOK_EXPORT, + TOK_EXTENDS, + TOK_IMPORT, + TOK_SUPER, + /* FutureReservedWords when parsing strict mode code */ + TOK_IMPLEMENTS, + TOK_INTERFACE, + TOK_LET, + TOK_PACKAGE, + TOK_PRIVATE, + TOK_PROTECTED, + TOK_PUBLIC, + TOK_STATIC, + TOK_YIELD, + TOK_AWAIT, /* must be last */ + TOK_OF, /* only used for js_parse_skip_parens_token() */ +}; + +typedef struct BlockEnv { + struct BlockEnv *prev; + JSAtom label_name; /* JS_ATOM_NULL if none */ + int label_break; /* -1 if none */ + int label_cont; /* -1 if none */ + int drop_count; /* number of stack elements to drop */ + int label_finally; /* -1 if none */ + int scope_level; + int has_iterator; +} BlockEnv; + +typedef struct JSGlobalVar { + int cpool_idx; /* if >= 0, index in the constant pool for hoisted + function defintion*/ + uint8_t force_init : 1; /* force initialization to undefined */ + uint8_t is_lexical : 1; /* global let/const definition */ + uint8_t is_const : 1; /* const definition */ + int scope_level; /* scope of definition */ + JSAtom var_name; /* variable name */ +} JSGlobalVar; + +typedef struct RelocEntry { + struct RelocEntry *next; + uint32_t addr; /* address to patch */ + int size; /* address size: 1, 2 or 4 bytes */ +} RelocEntry; + +typedef struct JumpSlot { + int op; + int size; + int pos; + int label; +} JumpSlot; + +typedef struct LabelSlot { + int ref_count; + int pos; /* phase 1 address, -1 means not resolved yet */ + int pos2; /* phase 2 address, -1 means not resolved yet */ + int addr; /* phase 3 address, -1 means not resolved yet */ + RelocEntry *first_reloc; +} LabelSlot; + +typedef struct LineNumberSlot { + uint32_t pc; + int line_num; +} LineNumberSlot; + +typedef enum JSParseFunctionEnum { + JS_PARSE_FUNC_STATEMENT, + JS_PARSE_FUNC_VAR, + JS_PARSE_FUNC_EXPR, + JS_PARSE_FUNC_ARROW, + JS_PARSE_FUNC_GETTER, + JS_PARSE_FUNC_SETTER, + JS_PARSE_FUNC_METHOD, + JS_PARSE_FUNC_CLASS_CONSTRUCTOR, + JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR, +} JSParseFunctionEnum; + +typedef enum JSParseExportEnum { + JS_PARSE_EXPORT_NONE, + JS_PARSE_EXPORT_NAMED, + JS_PARSE_EXPORT_DEFAULT, +} JSParseExportEnum; + +typedef struct JSFunctionDef { + JSContext *ctx; + struct JSFunctionDef *parent; + int parent_cpool_idx; /* index in the constant pool of the parent + or -1 if none */ + int parent_scope_level; /* scope level in parent at point of definition */ + struct list_head child_list; /* list of JSFunctionDef.link */ + struct list_head link; + BOOL is_eval; /* TRUE if eval code */ + int eval_type; /* only valid if is_eval = TRUE */ + BOOL is_global_var; /* TRUE if variables are not defined locally: + eval global, eval module or non strict eval */ + BOOL is_func_expr; /* TRUE if function expression */ + BOOL has_home_object; /* TRUE if the home object is available */ + BOOL has_prototype; /* true if a prototype field is necessary */ + BOOL has_simple_parameter_list; + BOOL has_parameter_expressions; /* if true, an argument scope is created */ + BOOL has_use_strict; /* to reject directive in special cases */ + BOOL has_eval_call; /* true if the function contains a call to eval() */ + BOOL has_arguments_binding; /* true if the 'arguments' binding is + available in the function */ + BOOL has_this_binding; /* true if the 'this' and new.target binding are + available in the function */ + BOOL new_target_allowed; /* true if the 'new.target' does not + throw a syntax error */ + BOOL super_call_allowed; /* true if super() is allowed */ + BOOL super_allowed; /* true if super. or super[] is allowed */ + BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */ + BOOL is_derived_class_constructor; + BOOL in_function_body; + BOOL backtrace_barrier; + JSFunctionKindEnum func_kind : 8; + JSParseFunctionEnum func_type : 8; + uint8_t js_mode; /* bitmap of JS_MODE_x */ + JSAtom func_name; /* JS_ATOM_NULL if no name */ + JSVarDef *vars; + int var_size; /* allocated size for vars[] */ + int var_count; + JSVarDef *args; + int arg_size; /* allocated size for args[] */ + int arg_count; /* number of arguments */ + int defined_arg_count; + int var_object_idx; /* -1 if none */ + int arg_var_object_idx; /* -1 if none (var object for the argument scope) */ + int arguments_var_idx; /* -1 if none */ + int arguments_arg_idx; /* argument variable definition in argument scope, + -1 if none */ + int func_var_idx; /* variable containing the current function (-1 + if none, only used if is_func_expr is true) */ + int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */ + int this_var_idx; /* variable containg the 'this' value, -1 if none */ + int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */ + int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */ + int home_object_var_idx; + BOOL need_home_object; + int scope_level; /* index into fd->scopes if the current lexical scope */ + int scope_first; /* index into vd->vars of first lexically scoped variable */ + int scope_size; /* allocated size of fd->scopes array */ + int scope_count; /* number of entries used in the fd->scopes array */ + JSVarScope *scopes; + JSVarScope def_scope_array[4]; + int body_scope; /* scope of the body of the function or eval */ + int global_var_count; + int global_var_size; + JSGlobalVar *global_vars; + DynBuf byte_code; + int last_opcode_pos; /* -1 if no last opcode */ + int last_opcode_line_num; + BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */ + LabelSlot *label_slots; + int label_size; /* allocated size for label_slots[] */ + int label_count; + BlockEnv *top_break; /* break/continue label stack */ + /* constant pool (strings, functions, numbers) */ + JSValue *cpool; + int cpool_count; + int cpool_size; + /* list of variables in the closure */ + int closure_var_count; + int closure_var_size; + JSClosureVar *closure_var; + JumpSlot *jump_slots; + int jump_size; + int jump_count; + LineNumberSlot *line_number_slots; + int line_number_size; + int line_number_count; + int line_number_last; + int line_number_last_pc; + /* pc2line table */ + JSAtom filename; + int line_num; + DynBuf pc2line; + char *source; /* raw source, utf-8 encoded */ + int source_len; + JSModuleDef *module; /* != NULL when parsing a module */ +} JSFunctionDef; + +typedef struct JSCFunctionDataRecord { + JSCFunctionData *func; + uint8_t length; + uint8_t data_len; + uint16_t magic; + JSValue data[0]; +} JSCFunctionDataRecord; + +typedef struct { + JSFunctionDef *fields_init_fd; + int computed_fields_count; + BOOL has_brand; + int brand_push_pos; +} ClassFieldsDef; + +typedef struct JSToken { + int val; + int line_num; /* line number of token start */ + const uint8_t *ptr; + union { + struct { + JSValue str; + int sep; + } str; + struct { + JSValue val; +#ifdef CONFIG_BIGNUM + slimb_t exponent; /* may be != 0 only if val is a float */ +#endif + } num; + struct { + JSAtom atom; + BOOL has_escape; + BOOL is_reserved; + } ident; + struct { + JSValue body; + JSValue flags; + } regexp; + } u; +} JSToken; + +typedef struct JSParseState { + JSContext *ctx; + int last_line_num; /* line number of last token */ + int line_num; /* line number of current offset */ + const char *filename; + JSToken token; + BOOL got_lf; /* true if got line feed before the current token */ + const uint8_t *last_ptr; + const uint8_t *buf_ptr; + const uint8_t *buf_end; + /* current function code */ + JSFunctionDef *cur_func; + BOOL is_module; /* parsing a module */ + BOOL allow_html_comments; + BOOL ext_json; /* true if accepting JSON superset */ +} JSParseState; + +typedef struct JSOpCode { +#ifdef DUMP_BYTECODE + const char *name; +#endif + uint8_t size; /* in bytes */ + /* the opcodes remove n_pop items from the top of the stack, then + pushes n_push items */ + uint8_t n_pop; + uint8_t n_push; + uint8_t fmt; +} JSOpCode; + +typedef struct JSArrayIteratorData { + JSValue obj; + JSIteratorKindEnum kind; + uint32_t idx; +} JSArrayIteratorData; + +typedef struct JSParsePos { + int last_line_num; + int line_num; + BOOL got_lf; + const uint8_t *ptr; +} JSParsePos; + +typedef struct JSClassShortDef { + JSAtom class_name; + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; +} JSClassShortDef; + +typedef enum JSAsyncGeneratorStateEnum { + JS_ASYNC_GENERATOR_STATE_SUSPENDED_START, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_ASYNC_GENERATOR_STATE_EXECUTING, + JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN, + JS_ASYNC_GENERATOR_STATE_COMPLETED, +} JSAsyncGeneratorStateEnum; + +typedef struct JSAsyncGeneratorRequest { + struct list_head link; + /* completion */ + int completion_type; /* GEN_MAGIC_x */ + JSValue result; + /* promise capability */ + JSValue promise; + JSValue resolving_funcs[2]; +} JSAsyncGeneratorRequest; + +typedef struct JSAsyncGeneratorData { + JSObject *generator; /* back pointer to the object (const) */ + JSAsyncGeneratorStateEnum state; + JSAsyncFunctionState func_state; + struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ +} JSAsyncGeneratorData; + +typedef struct JSAsyncFromSyncIteratorData { + JSValue sync_iter; + JSValue next_method; +} JSAsyncFromSyncIteratorData; + +typedef struct JSONStringifyContext { + JSValueConst replacer_func; + JSValue stack; + JSValue property_list; + JSValue gap; + JSValue empty; + StringBuffer *b; +} JSONStringifyContext; + +typedef struct BCReaderState { + JSContext *ctx; + const uint8_t *buf_start, *ptr, *buf_end; + uint32_t first_atom; + uint32_t idx_to_atom_count; + JSAtom *idx_to_atom; + int error_state; + BOOL allow_sab : 8; + BOOL allow_bytecode : 8; + BOOL is_rom_data : 8; + BOOL allow_reference : 8; + /* object references */ + JSObject **objects; + int objects_count; + int objects_size; +#ifdef DUMP_READ_OBJECT + const uint8_t *ptr_last; + int level; +#endif +} BCReaderState; + +extern const JSClassShortDef js_std_class_def[]; +extern const JSOpCode opcode_info[]; + +void JS_FreeAtomStruct(JSRuntime *, JSAtomStruct *); + +JSString *js_alloc_string(JSContext *, int, int); +JSString *js_alloc_string_rt(JSRuntime *, int, int); +JSValue js_new_string16(JSContext *, const uint16_t *, int); +JSValue js_new_string8(JSContext *, const uint8_t *, int); +JSValue js_new_string_char(JSContext *, uint16_t); +int js_string_compare(JSContext *, const JSString *, const JSString *); +int js_string_memcmp(const JSString *, const JSString *, int); +void js_free_string(JSRuntime *, JSString *); + +JSValue string_buffer_end(StringBuffer *); +int string_buffer_concat(StringBuffer *, const JSString *, uint32_t, uint32_t); +int string_buffer_concat_value(StringBuffer *, JSValueConst); +int string_buffer_concat_value_free(StringBuffer *, JSValue); +int string_buffer_fill(StringBuffer *, int, int); +int string_buffer_init(JSContext *, StringBuffer *, int); +int string_buffer_init2(JSContext *, StringBuffer *, int, int); +int string_buffer_putc(StringBuffer *, uint32_t); +int string_buffer_putc16(StringBuffer *, uint32_t); +int string_buffer_putc8(StringBuffer *, uint32_t); +int string_buffer_putc_slow(StringBuffer *, uint32_t); +int string_buffer_puts8(StringBuffer *, const char *); +int string_buffer_realloc(StringBuffer *, int, int); +int string_buffer_set_error(StringBuffer *); +int string_buffer_widen(StringBuffer *, int); +int string_buffer_write16(StringBuffer *, const uint16_t *, int); +int string_buffer_write8(StringBuffer *, const uint8_t *, int); +void string_buffer_free(StringBuffer *); + +JSContext *JS_GetFunctionRealm(JSContext *, JSValueConst); +JSValue JS_ThrowTypeErrorInvalidClass(JSContext *, int); +JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *); +JSValue js_create_from_ctor(JSContext *, JSValueConst, int); +int JS_ToBoolFree(JSContext *, JSValue); + +BOOL js_same_value(JSContext *, JSValueConst, JSValueConst); +BOOL js_same_value_zero(JSContext *, JSValueConst, JSValueConst); +BOOL js_strict_eq(JSContext *, JSValue, JSValue); +BOOL js_strict_eq2(JSContext *, JSValue, JSValue, JSStrictEqModeEnum); +int js_strict_eq_slow(JSContext *, JSValue *, BOOL); + +JSRegExp *js_get_regexp(JSContext *, JSValueConst, BOOL); +JSValue JS_RegExpExec(JSContext *, JSValueConst, JSValueConst); +JSValue js_compile_regexp(JSContext *, JSValueConst, JSValueConst); +JSValue js_regexp_Symbol_matchAll(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_regexp_compile(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_regexp_constructor(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_regexp_constructor_internal(JSContext *, JSValueConst, JSValue, JSValue); +JSValue js_regexp_exec(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_regexp_get_flag(JSContext *, JSValueConst, int); +JSValue js_regexp_get_flags(JSContext *, JSValueConst); +JSValue js_regexp_get_source(JSContext *, JSValueConst); +JSValue js_regexp_string_iterator_next(JSContext *, JSValueConst, int, JSValueConst *, BOOL *, int); +JSValue js_regexp_toString(JSContext *, JSValueConst, int, JSValueConst *); +int js_is_regexp(JSContext *, JSValueConst); +void js_regexp_finalizer(JSRuntime *, JSValue); +void js_regexp_string_iterator_finalizer(JSRuntime *, JSValue); +void js_regexp_string_iterator_mark(JSRuntime *, JSValueConst, JS_MarkFunc *); + +JSValue JS_GetPropertyInt64(JSContext *, JSValueConst, int64_t); +JSValue JS_ThrowError(JSContext *, JSErrorEnum, const char *, va_list); +JSValue JS_ThrowError2(JSContext *, JSErrorEnum, const char *, va_list, BOOL); +JSValue JS_ThrowReferenceErrorNotDefined(JSContext *, JSAtom); +JSValue JS_ThrowReferenceErrorUninitialized(JSContext *, JSAtom); +JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *, JSFunctionBytecode *, int, BOOL); +JSValue JS_ThrowStackOverflow(JSContext *); +JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *, JSAtom); +JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *); +JSValue JS_ThrowTypeErrorNotASymbol(JSContext *); +JSValue JS_ThrowTypeErrorNotAnObject(JSContext *); +JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *, JSAtom); +JSValue __JS_ThrowSyntaxErrorAtom(JSContext *, JSAtom, const char *, ...) printfesque(3); +JSValue __JS_ThrowTypeErrorAtom(JSContext *, JSAtom, const char *, ...) printfesque(3); +JSValue throw_bf_exception(JSContext *, int); +int JS_ThrowTypeErrorOrFalse(JSContext *, int, const char *, ...) printfesque(3); +int JS_ThrowTypeErrorReadOnly(JSContext *, int, JSAtom); +int js_parse_error(JSParseState *, const char *, ...) printfesque(2); +int js_throw_URIError(JSContext *, const char *, ...) printfesque(2); +void build_backtrace(JSContext *, JSValueConst, const char *, int, int); +int js_compare_bigfloat(JSContext *, OPCodeEnum, JSValue, JSValue); + +/* %s is replaced by 'atom'. The macro is used so that gcc can check the format string. */ +#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "") +#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "") + +JSValue JS_ToLocaleStringFree(JSContext *, JSValue); +JSValue JS_ToStringFree(JSContext *, JSValue); +JSValue JS_ToStringInternal(JSContext *, JSValueConst, BOOL); +JSValue js_atof(JSContext *, const char *, const char **, int, int); +JSValue js_atof2(JSContext *, const char *, const char **, int, int, slimb_t *); +JSValue js_bigdecimal_to_string(JSContext *, JSValueConst); +JSValue js_bigdecimal_to_string1(JSContext *, JSValueConst, limb_t, int); +JSValue js_bigfloat_to_string(JSContext *, JSValueConst); +JSValue js_bigint_to_string(JSContext *, JSValueConst); +JSValue js_bigint_to_string1(JSContext *, JSValueConst, int); +JSValue js_dtoa(JSContext *, double, int, int, int); +JSValue js_ftoa(JSContext *, JSValueConst, int, limb_t, bf_flags_t); +bfdec_t *JS_ToBigDecimal(JSContext *, JSValueConst); + +bf_t *JS_ToBigInt(JSContext *, bf_t *, JSValueConst); +void JS_FreeBigInt(JSContext *, bf_t *, bf_t *); +JSValue JS_ToNumeric(JSContext *, JSValueConst); +bf_t *JS_ToBigFloat(JSContext *, bf_t *, JSValueConst); +JSValue JS_NewBigInt64_1(JSContext *, int64_t); + +JSValue js_sub_string(JSContext *, JSString *, int, int); +int string_cmp(JSString *, JSString *, int, int, int); +int string_getc(const JSString *, int *); +int string_indexof(JSString *, JSString *, int); +int string_indexof_char(JSString *, int, int); +int64_t string_advance_index(JSString *, int64_t, BOOL); + +BOOL js_class_has_bytecode(JSClassID); +JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst); +int check_function(JSContext *, JSValueConst); +int find_line_num(JSContext *, JSFunctionBytecode *, uint32_t); +void js_bytecode_function_mark(JSRuntime *, JSValueConst, JS_MarkFunc *); + +JSAtom js_get_atom_index(JSRuntime *, JSAtomStruct *); +const char *JS_AtomGetStrRT(JSRuntime *, char *, int, JSAtom); +const char *JS_AtomGetStr(JSContext *, char *, int, JSAtom); +BOOL JS_AtomIsArrayIndex(JSContext *, uint32_t *, JSAtom); +BOOL is_num_string(uint32_t *, const JSString *); +JSAtom js_symbol_to_atom(JSContext *, JSValue); +JSValue JS_NewSymbol(JSContext *, JSString *, int); +int JS_DefineObjectNameComputed(JSContext *, JSValueConst, JSValueConst, int); +int JS_SetPrototypeInternal(JSContext *, JSValueConst, JSValueConst, BOOL); +void js_method_set_home_object(JSContext *, JSValueConst, JSValueConst); +JSValue js_get_function_name(JSContext *, JSAtom); +JSValue JS_ConcatString3(JSContext *, const char *, JSValue, const char *); + +BOOL JS_IsCFunction(JSContext *, JSValueConst, JSCFunction *, int); +JSAtom JS_NewAtomInt64(JSContext *, int64_t); +JSAtom JS_NewAtomStr(JSContext *, JSString *); +JSAtomKindEnum JS_AtomGetKind(JSContext *, JSAtom); +JSValue JS_CallFree(JSContext *, JSValue, JSValueConst, int, JSValueConst *); +JSValue JS_ConcatString(JSContext *, JSValue, JSValue); +JSValue JS_EvalObject(JSContext *, JSValueConst, JSValueConst, int, int); +JSValue JS_GetIterator(JSContext *, JSValueConst, BOOL); +JSValue JS_GetPropertyValue(JSContext *, JSValueConst, JSValue); +JSValue JS_GetPrototypeFree(JSContext *, JSValue); +JSValue JS_InvokeFree(JSContext *, JSValue, JSAtom, int, JSValueConst *); +JSValue JS_IteratorNext(JSContext *, JSValueConst, JSValueConst, int, JSValueConst *, BOOL *); +JSValue JS_IteratorNext2(JSContext *, JSValueConst, JSValueConst, int, JSValueConst *, int *); +JSValue JS_NewBigFloat(JSContext *); +JSValue JS_SpeciesConstructor(JSContext *, JSValueConst, JSValueConst); +JSValue JS_ToNumberFree(JSContext *, JSValue); +JSValue JS_ToNumericFree(JSContext *, JSValue); +JSValue JS_ToObjectFree(JSContext *, JSValue); +JSValue JS_ToPrimitive(JSContext *, JSValueConst, int); +JSValue JS_ToPrimitiveFree(JSContext *, JSValue, int); +JSValue JS_ToQuotedString(JSContext *, JSValueConst); +JSValue JS_ToStringCheckObject(JSContext *, JSValueConst); +int JS_CreateDataPropertyUint32(JSContext *, JSValueConst, int64_t, JSValue, int); +int JS_DefinePropertyValueInt64(JSContext *, JSValueConst, int64_t, JSValue, int); +int JS_DefinePropertyValueValue(JSContext *, JSValueConst, JSValue, JSValue, int); +int JS_ReadFunctionBytecode(BCReaderState *, JSFunctionBytecode *, int, uint32_t); +int JS_SetPropertyGeneric(JSContext *, JSValueConst, JSAtom, JSValue, JSValueConst, int); +int JS_SetPropertyValue(JSContext *, JSValueConst, JSValue, JSValue, int); +int JS_ToInt32Free(JSContext *, int32_t *, JSValue); +int JS_ToInt32Sat(JSContext *, int *, JSValueConst); +int JS_ToInt64Clamp(JSContext *, int64_t *, JSValueConst, int64_t, int64_t, int64_t); +int JS_ToInt64SatFree(JSContext *, int64_t *, JSValue); +void JS_SetConstructor2(JSContext *, JSValueConst, JSValueConst, int, int); + +JSValue JS_NewBigInt(JSContext *); +JSValue JS_CompactBigInt(JSContext *, JSValue); +JSValue JS_StringToBigInt(JSContext *, JSValue); +int JS_ToBigInt64Free(JSContext *, int64_t *, JSValue); + +BOOL check_define_prop_flags(int, int); +JSAtom get_private_setter_name(JSContext *, JSAtom); +JSAtom js_atom_concat_str(JSContext *, JSAtom, const char *); +JSExportEntry *add_export_entry(JSParseState *, JSModuleDef *, JSAtom, JSAtom, JSExportTypeEnum); +JSFunctionDef *js_new_function_def(JSContext *, JSFunctionDef *, BOOL, BOOL, const char *, int); +JSGlobalVar *add_global_var(JSContext *, JSFunctionDef *, JSAtom); +JSGlobalVar *find_global_var(JSFunctionDef *, JSAtom); +JSModuleDef *js_new_module_def(JSContext *, JSAtom); +JSValue JS_GetIterator2(JSContext *, JSValueConst, JSValueConst); +JSValue JS_GetOwnPropertyNames2(JSContext *, JSValueConst, int, int); +JSValue JS_NewBigDecimal(JSContext *); +JSValue JS_ToObject(JSContext *, JSValueConst); +JSValue js___date_clock(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_generator_function_call(JSContext *, JSValueConst, JSValueConst, int, JSValueConst *, int); +JSValue js_global_decodeURI(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_global_encodeURI(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_global_escape(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_global_unescape(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_object_defineProperty(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_object_getOwnPropertyDescriptor(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_object_getPrototypeOf(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_object_isExtensible(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_object_keys(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_object_preventExtensions(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_object_toString(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_promise_resolve(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_string___GetSubstitution(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_string_constructor(JSContext *, JSValueConst, int, JSValueConst *); +JSValueConst JS_NewGlobalCConstructor(JSContext *, const char *, JSCFunction *, int, JSValueConst); +int JS_CopyDataProperties(JSContext *, JSValueConst, JSValueConst, JSValueConst, BOOL) nodiscard; +int JS_GetOwnPropertyInternal(JSContext *, JSPropertyDescriptor *, JSObject *, JSAtom); +int JS_GetOwnPropertyNamesInternal(JSContext *, JSPropertyEnum **, uint32_t *, JSObject *, int) nodiscard; +int JS_SetObjectData(JSContext *, JSValueConst, JSValue); +int JS_ToArrayLengthFree(JSContext *, uint32_t *, JSValue, BOOL) nodiscard; +int JS_ToInt32Clamp(JSContext *, int *, JSValueConst, int, int, int); +int JS_ToInt64Sat(JSContext *, int64_t *, JSValueConst); +int JS_ToLengthFree(JSContext *, int64_t *, JSValue) nodiscard; +int JS_ToUint8ClampFree(JSContext *, int32_t *, JSValue); +int __JS_ToFloat64Free(JSContext *, double *, JSValue) nodiscard; +int __js_poll_interrupts(JSContext *) nodiscard; +int add_closure_var(JSContext *, JSFunctionDef *, BOOL, BOOL, int, JSAtom, BOOL, BOOL, JSVarKindEnum); +int add_scope_var(JSContext *, JSFunctionDef *, JSAtom, JSVarKindEnum); +int add_var(JSContext *, JSFunctionDef *, JSAtom); +int bc_get_buf(BCReaderState *, uint8_t *, uint32_t); +int bc_idx_to_atom(BCReaderState *, JSAtom *, uint32_t); +int bc_read_error_end(BCReaderState *); +int check_exception_free(JSContext *, JSValue); +int cpool_add(JSParseState *, JSValue); +int define_var(JSParseState *, JSFunctionDef *, JSAtom, JSVarDefEnum); +int delete_property(JSContext *, JSObject *, JSAtom); +int emit_label(JSParseState *, int); +int find_lexical_decl(JSContext *, JSFunctionDef *, JSAtom, int, BOOL); +int find_private_class_field(JSContext *, JSFunctionDef *, JSAtom, int); +int find_var(JSContext *, JSFunctionDef *, JSAtom); +int get_ovop_from_opcode(OPCodeEnum); +int ident_realloc(JSContext *, char **, size_t *, char *) nodiscard; +int init_class_range(JSRuntime *, JSClassShortDef const *, int, int); +int js_add_slow(JSContext *, JSValue *) nodiscard; +int js_binary_arith_slow(JSContext *, JSValue *, OPCodeEnum) nodiscard; +int js_binary_logic_slow(JSContext *, JSValue *, OPCodeEnum); +int js_call_binary_op_fallback(JSContext *, JSValue *, JSValueConst, JSValueConst, OPCodeEnum, BOOL, int); +int js_eq_slow(JSContext *, JSValue *, BOOL) nodiscard; +int js_for_of_start(JSContext *, JSValue *, BOOL) nodiscard; +int js_get_length32(JSContext *, uint32_t *, JSValueConst) nodiscard; +int js_get_length64(JSContext *, int64_t *, JSValueConst) nodiscard; +int js_get_radix(JSContext *, JSValueConst); +int js_not_slow(JSContext *, JSValue *); +int js_obj_to_desc(JSContext *, JSPropertyDescriptor *, JSValueConst); +int js_operator_in(JSContext *, JSValue *) nodiscard; +int js_parse_export(JSParseState *) nodiscard; +int js_parse_string(JSParseState *, int, BOOL, const uint8_t *, JSToken *, const uint8_t **); +int js_parse_template_part(JSParseState *, const uint8_t *) nodiscard; +int js_post_inc_slow(JSContext *, JSValue *, OPCodeEnum) nodiscard; +int js_relational_slow(JSContext *, JSValue *, OPCodeEnum); +int js_shr_slow(JSContext *, JSValue *); +int js_unary_arith_slow(JSContext *, JSValue *, OPCodeEnum) nodiscard; +int js_update_property_flags(JSContext *, JSObject *, JSShapeProperty **, int); +int new_label(JSParseState *); +int perform_promise_then(JSContext *, JSValueConst, JSValueConst *, JSValueConst *); +int push_scope(JSParseState *); +int resize_properties(JSContext *, JSShape **, JSObject *, uint32_t); +int skip_spaces(const char *); +int to_digit(int); +int update_label(JSFunctionDef *, int, int); +uint32_t hash_string(const JSString *, uint32_t); +uint32_t js_string_obj_get_length(JSContext *, JSValueConst); +void close_scopes(JSParseState *, int, int); +void emit_atom(JSParseState *, JSAtom); +void emit_op(JSParseState *, uint8_t); +void js_async_generator_resume_next(JSContext *, JSAsyncGeneratorData *); +void js_bytecode_function_finalizer(JSRuntime *, JSValue); +void js_free_desc(JSContext *, JSPropertyDescriptor *); +void js_free_function_def(JSContext *, JSFunctionDef *); +void js_free_module_def(JSContext *, JSModuleDef *); +void js_free_prop_enum(JSContext *, JSPropertyEnum *, uint32_t); +void js_generator_finalizer(JSRuntime *, JSValue); +void js_generator_mark(JSRuntime *, JSValueConst, JS_MarkFunc *); +void js_parse_init(JSContext *, JSParseState *, const char *, size_t, const char *); + +int js_proxy_setPrototypeOf(JSContext *, JSValueConst, JSValueConst, BOOL); +JSValue js_proxy_getPrototypeOf(JSContext *, JSValueConst); +int js_proxy_isExtensible(JSContext *, JSValueConst); +int js_proxy_isArray(JSContext *, JSValueConst); +int js_proxy_preventExtensions(JSContext *, JSValueConst); + +void js_map_iterator_finalizer(JSRuntime *, JSValue); +void js_map_finalizer(JSRuntime *, JSValue); +void js_map_mark(JSRuntime *, JSValueConst, JS_MarkFunc *); +void js_map_iterator_mark(JSRuntime *, JSValueConst, JS_MarkFunc *); +void reset_weak_ref(JSRuntime *, JSObject *); + +void free_property(JSRuntime *, JSProperty *, int); +void js_autoinit_mark(JSRuntime *, JSProperty *, JS_MarkFunc *); +void free_zero_refcount(JSRuntime *); + +void free_token(JSParseState *, JSToken *); +int next_token(JSParseState *) nodiscard; +int simple_next_token(const uint8_t **, BOOL); +int js_parse_program(JSParseState *) nodiscard; + +JSValue JS_IteratorGetCompleteValue(JSContext *, JSValueConst, BOOL *); +JSValue js_create_iterator_result(JSContext *, JSValue, BOOL); +JSValue js_iterator_proto_iterator(JSContext *, JSValueConst, int, JSValueConst *); +int JS_IteratorClose(JSContext *, JSValueConst, BOOL); +void JS_AddIteratorProto(JSContext *); + +JSValue *build_arg_list(JSContext *, uint32_t *, JSValueConst); +void free_arg_list(JSContext *, JSValue *, uint32_t); + +JSValue js_get_module_ns(JSContext *, JSModuleDef *); + +static inline BOOL JS_IsEmptyString(JSValueConst v) { return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0; } +static inline BOOL __JS_AtomIsTaggedInt(JSAtom v) { return (v & JS_ATOM_TAG_INT) != 0; } +static inline BOOL atom_is_free(const JSAtomStruct *p) { return (uintptr_t)p & 1; } +static inline JSAtom __JS_AtomFromUInt32(uint32_t v) { return v | JS_ATOM_TAG_INT; } +static inline JSAtomStruct *atom_set_free(uint32_t v) { return (JSAtomStruct *)(((uintptr_t)v << 1) | 1); } +static inline JSValueConst JS_GetActiveFunction(JSContext *ctx) { return ctx->rt->current_stack_frame->cur_func; } /* only valid inside C functions */ +static inline int string_get(const JSString *p, int idx) { return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; } +static inline uint32_t __JS_AtomToUInt32(JSAtom atom) { return atom & ~JS_ATOM_TAG_INT; } +static inline uint32_t atom_get_free(const JSAtomStruct *p) { return (uintptr_t)p >> 1; } + +JSValue async_func_resume(JSContext *, JSAsyncFunctionState *); +JSValue js_call_bound_function(JSContext *, JSValueConst, JSValueConst, int, JSValueConst *, int); +JSValue js_call_c_function(JSContext *, JSValueConst, JSValueConst, int, JSValueConst *, int); +JSValue js_closure(JSContext *, JSValue, JSVarRef **, JSStackFrame *); +JSValue js_closure2(JSContext *, JSValue, JSFunctionBytecode *, JSVarRef **, JSStackFrame *); +JSValue js_function_apply(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_function_constructor(JSContext *, JSValueConst, int, JSValueConst *, int); +JSVarRef *get_var_ref(JSContext *, JSStackFrame *, int, BOOL); +int async_func_init(JSContext *, JSAsyncFunctionState *, JSValueConst, JSValueConst, int, JSValueConst *) nodiscard; +void async_func_free(JSRuntime *, JSAsyncFunctionState *); +void async_func_mark(JSRuntime *, JSAsyncFunctionState *, JS_MarkFunc *); +void js_async_generator_free(JSRuntime *, JSAsyncGeneratorData *); + +JSProperty *add_property(JSContext *, JSObject *, JSAtom, int); +int JS_DefineAutoInitProperty(JSContext *, JSValueConst, JSAtom, JSAutoInitIDEnum, void *, int); +void free_var_ref(JSRuntime *, JSVarRef *); +void js_function_set_properties(JSContext *, JSValueConst, JSAtom, int); +int JS_DefineObjectName(JSContext *, JSValueConst, JSAtom, int); +JSValue js_get_this(JSContext *, JSValueConst); + +BOOL js_get_fast_array(JSContext *, JSValueConst, JSValue **, uint32_t *); +JSValue js_array_constructor(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_array_every(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_array_from(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_array_includes(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_array_iterator_next(JSContext *, JSValueConst, int, JSValueConst *, BOOL *, int); +JSValue js_array_pop(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_array_push(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_array_reduce(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_create_array(JSContext *, int, JSValueConst *); +JSValue js_create_array_iterator(JSContext *, JSValueConst, int, JSValueConst *, int); +int add_fast_array_element(JSContext *, JSObject *, JSValue, int); +int js_realloc_array(JSContext *, void **, int, int *, int); +int set_array_length(JSContext *, JSObject *, JSValue, int); +void JS_AddArrayIteratorProto(JSContext *); +void JS_AddIntrinsicArray(JSContext *); +void JS_AddIntrinsicGenerator(JSContext *); +void JS_AddIntrinsicMath(JSContext *); +void JS_AddIntrinsicObject(JSContext *); +void JS_AddIntrinsicReflect(JSContext *); +void JS_AddIntrinsicString(JSContext *); +void js_array_finalizer(JSRuntime *, JSValue); +void js_array_iterator_finalizer(JSRuntime *, JSValue); +void js_array_iterator_mark(JSRuntime *, JSValueConst, JS_MarkFunc *); +void js_array_mark(JSRuntime *, JSValueConst, JS_MarkFunc *); + +void js_object_data_finalizer(JSRuntime *, JSValue); +void js_object_data_mark(JSRuntime *, JSValueConst, JS_MarkFunc *); +JSValue js_object_seal(JSContext *, JSValueConst, int, JSValueConst *, int); + +void free_function_bytecode(JSRuntime *, JSFunctionBytecode *); +void free_bytecode_atoms(JSRuntime *, const uint8_t *, int, BOOL); + +JSShape *find_hashed_shape_proto(JSRuntime *, JSObject *); +JSShape *js_dup_shape(JSShape *); +JSShape *js_new_shape(JSContext *, JSObject *); +JSValue JS_NewObjectFromShape(JSContext *, JSShape *, JSClassID); +int add_shape_property(JSContext *, JSShape **, JSObject *, JSAtom, int); +void js_shape_hash_unlink(JSRuntime *, JSShape *); +void js_shape_hash_link(JSRuntime *, JSShape *); +JSShape *find_hashed_shape_prop(JSRuntime *, JSShape *, JSAtom, int); +JSShape *js_clone_shape(JSContext *, JSShape *); +JSShape *js_new_shape2(JSContext *, JSObject *, int, int); +int init_shape_hash(JSRuntime *); +void js_free_shape(JSRuntime *, JSShape *); +double js_pow(double, double); + +void js_trigger_gc(JSRuntime *, size_t); +void js_async_function_free0(JSRuntime *, JSAsyncFunctionData *); + +BOOL typed_array_is_detached(JSContext *, JSObject *); +JSValue js_typed_array___speciesCreate(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_typed_array_constructor(JSContext *, JSValueConst, int, JSValueConst *, int); +JSValue js_typed_array_get_buffer(JSContext *, JSValueConst, int); +JSValue js_typed_array_get_byteLength(JSContext *, JSValueConst, int); +JSValue js_typed_array_get_byteOffset(JSContext *, JSValueConst, int); +int js_typed_array_get_length_internal(JSContext *, JSValueConst); +uint32_t typed_array_get_length(JSContext *, JSObject *); +void js_typed_array_finalizer(JSRuntime *, JSValue); +void js_typed_array_mark(JSRuntime *, JSValueConst, JS_MarkFunc *); + +JSArrayBuffer *js_get_array_buffer(JSContext *, JSValueConst); +JSValue js_array_buffer_constructor(JSContext *, JSValueConst, int, JSValueConst *); +JSValue js_array_buffer_constructor1(JSContext *, JSValueConst, uint64_t); +JSValue js_array_buffer_constructor2(JSContext *, JSValueConst, uint64_t, JSClassID); +JSValue js_array_buffer_constructor3(JSContext *, JSValueConst, uint64_t, JSClassID, uint8_t *, JSFreeArrayBufferDataFunc *, void *, BOOL); +void js_array_buffer_finalizer(JSRuntime *, JSValue); + +JSValue JS_NewCFunction3(JSContext *, JSCFunction *, const char *, int, JSCFunctionEnum, int, JSValueConst); +JSValueConst JS_NewGlobalCConstructorOnly(JSContext *, const char *, JSCFunction *, int, JSValueConst); +void JS_NewGlobalCConstructor2(JSContext *, JSValue, const char *, JSValueConst); + +JSModuleDef *js_find_loaded_module(JSContext *, JSAtom); + +void dbuf_put_leb128(DynBuf *, uint32_t); +void dbuf_put_sleb128(DynBuf *, int32_t); + +static inline BOOL is_strict_mode(JSContext *ctx) +{ + JSStackFrame *sf = ctx->rt->current_stack_frame; + return (sf && (sf->js_mode & JS_MODE_STRICT)); +} + +#ifdef CONFIG_BIGNUM +static inline BOOL is_math_mode(JSContext *ctx) +{ + JSStackFrame *sf = ctx->rt->current_stack_frame; + return (sf && (sf->js_mode & JS_MODE_MATH)); +} +#endif + +static inline BOOL __JS_AtomIsConst(JSAtom v) +{ +#if defined(DUMP_LEAKS) && DUMP_LEAKS > 1 + return (int32_t)v <= 0; +#else + return (int32_t)v < JS_ATOM_END; +#endif +} + +static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) { + uint32_t tag; + tag = JS_VALUE_GET_TAG(val); + if (tag <= JS_TAG_NULL) { + *pres = JS_VALUE_GET_INT(val); + return 0; + } else if (JS_TAG_IS_FLOAT64(tag)) { + *pres = JS_VALUE_GET_FLOAT64(val); + return 0; + } else { + return __JS_ToFloat64Free(ctx, pres, val); + } +} + +#if !defined(CONFIG_STACK_CHECK) +static inline uintptr_t js_get_stack_pointer(void) { return 0; } +static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) { return FALSE; } +#else +static inline uintptr_t js_get_stack_pointer(void) { return (uintptr_t)__builtin_frame_address(0); } +static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) { + uintptr_t sp; + sp = js_get_stack_pointer() - alloca_size; + return UNLIKELY(sp < rt->stack_limit); +} +#endif + +static inline __exception int js_poll_interrupts(JSContext *ctx) { + if (UNLIKELY(--ctx->interrupt_counter <= 0)) { + return __js_poll_interrupts(ctx); + } else { + return 0; + } +} + +static inline uint32_t *prop_hash_end(JSShape *sh) { + return (uint32_t *)sh; +} + +static inline JSShapeProperty *get_shape_prop(JSShape *sh) { + return sh->prop; +} + +forceinline JSShapeProperty *find_own_property1(JSObject *p, JSAtom atom) { + JSShape *sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (LIKELY(pr->atom == atom)) { + return pr; + } + h = pr->hash_next; + } + return NULL; +} + +forceinline JSShapeProperty *find_own_property(JSProperty **ppr, + JSObject *p, + JSAtom atom) { + JSShape *sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (LIKELY(pr->atom == atom)) { + *ppr = &p->prop[h - 1]; + /* the compiler should be able to assume that pr != NULL here */ + return pr; + } + h = pr->hash_next; + } + *ppr = NULL; + return NULL; +} + +/* set the new value and free the old value after (freeing the value + can reallocate the object data) */ +static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val) { + JSValue old_val; + old_val = *pval; + *pval = new_val; + JS_FreeValue(ctx, old_val); +} + +/* indicate that the object may be part of a function prototype cycle */ +static inline void set_cycle_flag(JSContext *ctx, JSValueConst obj) {} + +static inline void js_dbuf_init(JSContext *ctx, DynBuf *s) { + dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt); +} + +static inline void emit_u8(JSParseState *s, uint8_t val) { + dbuf_putc(&s->cur_func->byte_code, val); +} + +static inline void emit_u16(JSParseState *s, uint16_t val) { + dbuf_put_u16(&s->cur_func->byte_code, val); +} + +static inline void emit_u32(JSParseState *s, uint32_t val) { + dbuf_put_u32(&s->cur_func->byte_code, val); +} + +static inline int get_prev_opcode(JSFunctionDef *fd) { + if (fd->last_opcode_pos < 0) + return OP_invalid; + else + return fd->byte_code.buf[fd->last_opcode_pos]; +} + +/* resize the array and update its size if req_size > *psize */ +static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size, + int *psize, int req_size) { + if (UNLIKELY(req_size > *psize)) + return js_realloc_array(ctx, parray, elem_size, psize, req_size); + else + return 0; +} + +static inline bf_t *JS_GetBigFloat(JSValueConst val) { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + return &p->num; +} + +static inline bfdec_t *JS_GetBigDecimal(JSValueConst val) { + JSBigDecimal *p = JS_VALUE_GET_PTR(val); + return &p->num; +} + +static inline void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, JSGCObjectTypeEnum type) { + h->mark = 0; + h->gc_obj_type = type; + list_add_tail(&h->link, &rt->gc_obj_list); +} + +static inline void remove_gc_object(JSGCObjectHeader *h) { + list_del(&h->link); +} + +static inline size_t get_shape_size(size_t hash_size, size_t prop_size) { + return hash_size * sizeof(uint32_t) + sizeof(JSShape) + + prop_size * sizeof(JSShapeProperty); +} + +static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size) { + return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); +} + +static inline void *get_alloc_from_shape(JSShape *sh) { + return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1); +} + +static inline JSObject *get_proto_obj(JSValueConst proto_val) { + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) + return NULL; + else + return JS_VALUE_GET_OBJ(proto_val); +} + +static inline void js_free_shape_null(JSRuntime *rt, JSShape *sh) { + if (sh) js_free_shape(rt, sh); +} + +static inline bf_t *JS_GetBigInt(JSValueConst val) { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + return &p->num; +} + +static inline void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s) { + if (--s->header.ref_count == 0) { + js_async_function_free0(rt, s); + } +} + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_THIRD_PARTY_QUICKJS_INTERNAL_H_ */ diff --git a/third_party/quickjs/iter.c b/third_party/quickjs/iter.c new file mode 100644 index 000000000..9e6c21fd3 --- /dev/null +++ b/third_party/quickjs/iter.c @@ -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)); +} diff --git a/third_party/quickjs/json.c b/third_party/quickjs/json.c new file mode 100644 index 000000000..8168af45b --- /dev/null +++ b/third_party/quickjs/json.c @@ -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 , nor */ + 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, ""); + 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)); +} diff --git a/third_party/quickjs/leb128.c b/third_party/quickjs/leb128.c new file mode 100644 index 000000000..13020e057 --- /dev/null +++ b/third_party/quickjs/leb128.c @@ -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; +} diff --git a/third_party/quickjs/leb128.h b/third_party/quickjs/leb128.h new file mode 100644 index 000000000..8c6a9dfe0 --- /dev/null +++ b/third_party/quickjs/leb128.h @@ -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_ */ diff --git a/third_party/quickjs/libbf.c b/third_party/quickjs/libbf.c index 4400b3f5a..e4aaff8c3 100644 --- a/third_party/quickjs/libbf.c +++ b/third_party/quickjs/libbf.c @@ -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; diff --git a/third_party/quickjs/libregexp.c b/third_party/quickjs/libregexp.c index 49314c3d3..29d052b03 100644 --- a/third_party/quickjs/libregexp.c +++ b/third_party/quickjs/libregexp.c @@ -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) { diff --git a/third_party/quickjs/map.c b/third_party/quickjs/map.c new file mode 100644 index 000000000..1119d09f1 --- /dev/null +++ b/third_party/quickjs/map.c @@ -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]); + } +} diff --git a/third_party/quickjs/math.c b/third_party/quickjs/math.c new file mode 100644 index 000000000..f7951bab6 --- /dev/null +++ b/third_party/quickjs/math.c @@ -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)); +} diff --git a/third_party/quickjs/mem.c b/third_party/quickjs/mem.c new file mode 100644 index 000000000..9dcb5c8a2 --- /dev/null +++ b/third_party/quickjs/mem.c @@ -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); +} diff --git a/third_party/quickjs/object.c b/third_party/quickjs/object.c new file mode 100644 index 000000000..f932029ef --- /dev/null +++ b/third_party/quickjs/object.c @@ -0,0 +1,1222 @@ +/* + * 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 */ + +void js_object_data_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JS_FreeValueRT(rt, p->u.object_data); + p->u.object_data = JS_UNDEFINED; +} + +void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JS_MarkValue(rt, p->u.object_data, mark_func); +} + +static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue ret; + if (!JS_IsUndefined(new_target) && + JS_VALUE_GET_OBJ(new_target) != + JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) { + ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); + } else { + int tag = JS_VALUE_GET_NORM_TAG(argv[0]); + switch(tag) { + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_NewObject(ctx); + break; + default: + ret = JS_ToObject(ctx, argv[0]); + break; + } + } + return ret; +} + +static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValueConst desc, + int flags) +{ + JSPropertyDescriptor d; + int ret; + if (js_obj_to_desc(ctx, &d, desc) < 0) + return -1; + ret = JS_DefineProperty(ctx, obj, prop, + d.value, d.getter, d.setter, d.flags | flags); + js_free_desc(ctx, &d); + return ret; +} + +static __exception int JS_ObjectDefineProperties(JSContext *ctx, + JSValueConst obj, + JSValueConst properties) +{ + JSValue props, desc; + JSObject *p; + JSPropertyEnum *atoms; + uint32_t len, i; + int ret = -1; + if (!JS_IsObject(obj)) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + desc = JS_UNDEFINED; + props = JS_ToObject(ctx, properties); + if (JS_IsException(props)) + return -1; + p = JS_VALUE_GET_OBJ(props); + if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0) + goto exception; + for(i = 0; i < len; i++) { + JS_FreeValue(ctx, desc); + desc = JS_GetProperty(ctx, props, atoms[i].atom); + if (JS_IsException(desc)) + goto exception; + if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0) + goto exception; + } + ret = 0; +exception: + js_free_prop_enum(ctx, atoms, len); + JS_FreeValue(ctx, props); + JS_FreeValue(ctx, desc); + return ret; +} + +static JSValue js_object_create(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst proto, props; + JSValue obj; + proto = argv[0]; + if (!JS_IsObject(proto) && !JS_IsNull(proto)) + return JS_ThrowTypeError(ctx, "not a prototype"); + obj = JS_NewObjectProto(ctx, proto); + if (JS_IsException(obj)) + return JS_EXCEPTION; + props = argv[1]; + if (!JS_IsUndefined(props)) { + if (JS_ObjectDefineProperties(ctx, obj, props)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + } + return obj; +} + +JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValueConst val; + val = argv[0]; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) { + /* ES6 feature non compatible with ES5.1: primitive types are + accepted */ + if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL || + JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED) + return JS_ThrowTypeErrorNotAnObject(ctx); + } + return JS_GetPrototype(ctx, val); +} + +static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst obj; + obj = argv[0]; + if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0) + return JS_EXCEPTION; + return JS_DupValue(ctx, obj); +} + +/* magic = 1 if called as Reflect.defineProperty */ +JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValueConst obj, prop, desc; + int ret, flags; + JSAtom atom; + obj = argv[0]; + prop = argv[1]; + desc = argv[2]; + 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; + flags = 0; + if (!magic) + flags |= JS_PROP_THROW; + ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags); + JS_FreeAtom(ctx, atom); + if (ret < 0) { + return JS_EXCEPTION; + } else if (magic) { + return JS_NewBool(ctx, ret); + } else { + return JS_DupValue(ctx, obj); + } +} + +static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // defineProperties(obj, properties) + JSValueConst obj = argv[0]; + if (JS_ObjectDefineProperties(ctx, obj, argv[1])) + return JS_EXCEPTION; + else + return JS_DupValue(ctx, obj); +} + +/* magic = 1 if called as __defineSetter__ */ +static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue obj; + JSValueConst prop, value, get, set; + int ret, flags; + JSAtom atom; + prop = argv[0]; + value = argv[1]; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + return JS_EXCEPTION; + if (check_function(ctx, value)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + atom = JS_ValueToAtom(ctx, prop); + if (UNLIKELY(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + flags = JS_PROP_THROW | + JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE; + if (magic) { + get = JS_UNDEFINED; + set = value; + flags |= JS_PROP_HAS_SET; + } else { + get = value; + set = JS_UNDEFINED; + flags |= JS_PROP_HAS_GET; + } + ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags); + JS_FreeValue(ctx, obj); + JS_FreeAtom(ctx, atom); + if (ret < 0) { + return JS_EXCEPTION; + } else { + return JS_UNDEFINED; + } +} + +JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValueConst prop; + JSAtom atom; + JSValue ret, obj; + JSPropertyDescriptor desc; + int res, flags; + if (magic) { + /* Reflect.getOwnPropertyDescriptor case */ + if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + obj = JS_DupValue(ctx, argv[0]); + } else { + obj = JS_ToObject(ctx, argv[0]); + if (JS_IsException(obj)) + return obj; + } + prop = argv[1]; + atom = JS_ValueToAtom(ctx, prop); + if (UNLIKELY(atom == JS_ATOM_NULL)) + goto exception; + ret = JS_UNDEFINED; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom); + if (res < 0) + goto exception; + if (res) { + ret = JS_NewObject(ctx); + if (JS_IsException(ret)) + goto exception1; + flags = JS_PROP_C_W_E | JS_PROP_THROW; + if (desc.flags & JS_PROP_GETSET) { + if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0 + || JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0) + goto exception1; + } else { + if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0 + || JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, + JS_NewBool(ctx, (desc.flags & JS_PROP_WRITABLE) != 0), flags) < 0) + goto exception1; + } + if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, + JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0), flags) < 0 + || JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, + JS_NewBool(ctx, (desc.flags & JS_PROP_CONFIGURABLE) != 0), flags) < 0) + goto exception1; + js_free_desc(ctx, &desc); + } + } + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, obj); + return ret; +exception1: + js_free_desc(ctx, &desc); + JS_FreeValue(ctx, ret); +exception: + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + //getOwnPropertyDescriptors(obj) + JSValue obj, r; + JSObject *p; + JSPropertyEnum *props; + uint32_t len, i; + r = JS_UNDEFINED; + obj = JS_ToObject(ctx, argv[0]); + if (JS_IsException(obj)) + return JS_EXCEPTION; + p = JS_VALUE_GET_OBJ(obj); + if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, + JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) + goto exception; + r = JS_NewObject(ctx); + if (JS_IsException(r)) + goto exception; + for(i = 0; i < len; i++) { + JSValue atomValue, desc; + JSValueConst args[2]; + atomValue = JS_AtomToValue(ctx, props[i].atom); + if (JS_IsException(atomValue)) + goto exception; + args[0] = obj; + args[1] = atomValue; + desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0); + JS_FreeValue(ctx, atomValue); + if (JS_IsException(desc)) + goto exception; + if (!JS_IsUndefined(desc)) { + if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + } + js_free_prop_enum(ctx, props, len); + JS_FreeValue(ctx, obj); + return r; +exception: + js_free_prop_enum(ctx, props, len); + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, r); + return JS_EXCEPTION; +} + +JSValue JS_ToObject(JSContext *ctx, JSValueConst val) +{ + int tag = JS_VALUE_GET_NORM_TAG(val); + JSValue obj; + switch(tag) { + default: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + return JS_ThrowTypeError(ctx, "cannot convert to object"); + case JS_TAG_OBJECT: + case JS_TAG_EXCEPTION: + return JS_DupValue(ctx, val); +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); + goto set_value; + case JS_TAG_BIG_FLOAT: + obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT); + goto set_value; + case JS_TAG_BIG_DECIMAL: + obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL); + goto set_value; +#endif + case JS_TAG_INT: + case JS_TAG_FLOAT64: + obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); + goto set_value; + case JS_TAG_STRING: + /* XXX: should call the string constructor */ + { + JSString *p1 = JS_VALUE_GET_STRING(val); + obj = JS_NewObjectClass(ctx, JS_CLASS_STRING); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0); + } + goto set_value; + case JS_TAG_BOOL: + obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN); + goto set_value; + case JS_TAG_SYMBOL: + obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL); + set_value: + if (!JS_IsException(obj)) + JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val)); + return obj; + } +} + +JSValue JS_ToObjectFree(JSContext *ctx, JSValue val) +{ + JSValue obj = JS_ToObject(ctx, val); + JS_FreeValue(ctx, val); + return obj; +} + +int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, JSValueConst desc) +{ + JSValue val, getter, setter; + int flags; + if (!JS_IsObject(desc)) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + flags = 0; + val = JS_UNDEFINED; + getter = JS_UNDEFINED; + setter = JS_UNDEFINED; + if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) { + JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable); + if (JS_IsException(prop)) + goto fail; + flags |= JS_PROP_HAS_CONFIGURABLE; + if (JS_ToBoolFree(ctx, prop)) + flags |= JS_PROP_CONFIGURABLE; + } + if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) { + JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable); + if (JS_IsException(prop)) + goto fail; + flags |= JS_PROP_HAS_WRITABLE; + if (JS_ToBoolFree(ctx, prop)) + flags |= JS_PROP_WRITABLE; + } + if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) { + JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable); + if (JS_IsException(prop)) + goto fail; + flags |= JS_PROP_HAS_ENUMERABLE; + if (JS_ToBoolFree(ctx, prop)) + flags |= JS_PROP_ENUMERABLE; + } + if (JS_HasProperty(ctx, desc, JS_ATOM_value)) { + flags |= JS_PROP_HAS_VALUE; + val = JS_GetProperty(ctx, desc, JS_ATOM_value); + if (JS_IsException(val)) + goto fail; + } + if (JS_HasProperty(ctx, desc, JS_ATOM_get)) { + flags |= JS_PROP_HAS_GET; + getter = JS_GetProperty(ctx, desc, JS_ATOM_get); + if (JS_IsException(getter) || + !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) { + JS_ThrowTypeError(ctx, "invalid getter"); + goto fail; + } + } + if (JS_HasProperty(ctx, desc, JS_ATOM_set)) { + flags |= JS_PROP_HAS_SET; + setter = JS_GetProperty(ctx, desc, JS_ATOM_set); + if (JS_IsException(setter) || + !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) { + JS_ThrowTypeError(ctx, "invalid setter"); + goto fail; + } + } + if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) && + (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) { + JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable"); + goto fail; + } + d->flags = flags; + d->value = val; + d->getter = getter; + d->setter = setter; + return 0; + fail: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, getter); + JS_FreeValue(ctx, setter); + return -1; +} + +JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1, int flags, int kind) +{ + JSValue obj, r, val, key, value; + JSObject *p; + JSPropertyEnum *atoms; + uint32_t len, i, j; + r = JS_UNDEFINED; + val = JS_UNDEFINED; + obj = JS_ToObject(ctx, obj1); + if (JS_IsException(obj)) + return JS_EXCEPTION; + p = JS_VALUE_GET_OBJ(obj); + if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY)) + goto exception; + r = JS_NewArray(ctx); + if (JS_IsException(r)) + goto exception; + for(j = i = 0; i < len; i++) { + JSAtom atom = atoms[i].atom; + if (flags & JS_GPN_ENUM_ONLY) { + JSPropertyDescriptor desc; + int res; + + /* Check if property is still enumerable */ + res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); + if (res < 0) + goto exception; + if (!res) + continue; + js_free_desc(ctx, &desc); + if (!(desc.flags & JS_PROP_ENUMERABLE)) + continue; + } + switch(kind) { + default: + case JS_ITERATOR_KIND_KEY: + val = JS_AtomToValue(ctx, atom); + if (JS_IsException(val)) + goto exception; + break; + case JS_ITERATOR_KIND_VALUE: + val = JS_GetProperty(ctx, obj, atom); + if (JS_IsException(val)) + goto exception; + break; + case JS_ITERATOR_KIND_KEY_AND_VALUE: + val = JS_NewArray(ctx); + if (JS_IsException(val)) + goto exception; + key = JS_AtomToValue(ctx, atom); + if (JS_IsException(key)) + goto exception1; + if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0) + goto exception1; + value = JS_GetProperty(ctx, obj, atom); + if (JS_IsException(value)) + goto exception1; + if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0) + goto exception1; + break; + } + if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0) + goto exception; + } + goto done; + +exception1: + JS_FreeValue(ctx, val); +exception: + JS_FreeValue(ctx, r); + r = JS_EXCEPTION; +done: + js_free_prop_enum(ctx, atoms, len); + JS_FreeValue(ctx, obj); + return r; +} + +static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_GetOwnPropertyNames2(ctx, argv[0], + JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY); +} + +static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_GetOwnPropertyNames2(ctx, argv[0], + JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY); +} + +JSValue js_object_keys(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int kind) +{ + return JS_GetOwnPropertyNames2(ctx, argv[0], + JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind); +} + +JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int reflect) +{ + JSValueConst obj; + int ret; + obj = argv[0]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + if (reflect) + return JS_ThrowTypeErrorNotAnObject(ctx); + else + return JS_FALSE; + } + ret = JS_IsExtensible(ctx, obj); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int reflect) +{ + JSValueConst obj; + int ret; + obj = argv[0]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + if (reflect) + return JS_ThrowTypeErrorNotAnObject(ctx); + else + return JS_DupValue(ctx, obj); + } + ret = JS_PreventExtensions(ctx, obj); + if (ret < 0) + return JS_EXCEPTION; + if (reflect) { + return JS_NewBool(ctx, ret); + } else { + if (!ret) + return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); + return JS_DupValue(ctx, obj); + } +} + +static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj; + JSAtom atom; + JSObject *p; + BOOL ret; + atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */ + if (UNLIKELY(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) { + JS_FreeAtom(ctx, atom); + return obj; + } + p = JS_VALUE_GET_OBJ(obj); + ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, obj); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ToObject(ctx, this_val); +} + +JSValue js_object_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, tag; + int is_array; + JSAtom atom; + JSObject *p; + if (JS_IsNull(this_val)) { + tag = JS_NewString(ctx, "Null"); + } else if (JS_IsUndefined(this_val)) { + tag = JS_NewString(ctx, "Undefined"); + } else { + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + return obj; + is_array = JS_IsArray(ctx, obj); + if (is_array < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + if (is_array) { + atom = JS_ATOM_Array; + } else if (JS_IsFunction(ctx, obj)) { + atom = JS_ATOM_Function; + } else { + p = JS_VALUE_GET_OBJ(obj); + switch(p->class_id) { + case JS_CLASS_STRING: + case JS_CLASS_ARGUMENTS: + case JS_CLASS_MAPPED_ARGUMENTS: + case JS_CLASS_ERROR: + case JS_CLASS_BOOLEAN: + case JS_CLASS_NUMBER: + case JS_CLASS_DATE: + case JS_CLASS_REGEXP: + atom = ctx->rt->class_array[p->class_id].class_name; + break; + default: + atom = JS_ATOM_Object; + break; + } + } + tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag); + JS_FreeValue(ctx, obj); + if (JS_IsException(tag)) + return JS_EXCEPTION; + if (!JS_IsString(tag)) { + JS_FreeValue(ctx, tag); + tag = JS_AtomToString(ctx, atom); + } + } + return JS_ConcatString3(ctx, "[object ", tag, "]"); +} + +static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL); +} + +static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // Object.assign(obj, source1) + JSValue obj, s; + int i; + s = JS_UNDEFINED; + obj = JS_ToObject(ctx, argv[0]); + if (JS_IsException(obj)) + goto exception; + for (i = 1; i < argc; i++) { + if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) { + s = JS_ToObject(ctx, argv[i]); + if (JS_IsException(s)) + goto exception; + if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE)) + goto exception; + JS_FreeValue(ctx, s); + } + } + return obj; +exception: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, s); + return JS_EXCEPTION; +} + +JSValue js_object_seal(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int freeze_flag) +{ + JSValueConst obj = argv[0]; + JSObject *p; + JSPropertyEnum *props; + uint32_t len, i; + int flags, desc_flags, res; + if (!JS_IsObject(obj)) + return JS_DupValue(ctx, obj); + res = JS_PreventExtensions(ctx, obj); + if (res < 0) + return JS_EXCEPTION; + if (!res) { + return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); + } + p = JS_VALUE_GET_OBJ(obj); + flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; + if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) + return JS_EXCEPTION; + for(i = 0; i < len; i++) { + JSPropertyDescriptor desc; + JSAtom prop = props[i].atom; + desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE; + if (freeze_flag) { + res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (res < 0) + goto exception; + if (res) { + if (desc.flags & JS_PROP_WRITABLE) + desc_flags |= JS_PROP_HAS_WRITABLE; + js_free_desc(ctx, &desc); + } + } + if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED, + JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0) + goto exception; + } + js_free_prop_enum(ctx, props, len); + return JS_DupValue(ctx, obj); + exception: + js_free_prop_enum(ctx, props, len); + return JS_EXCEPTION; +} + +static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int is_frozen) +{ + JSValueConst obj = argv[0]; + JSObject *p; + JSPropertyEnum *props; + uint32_t len, i; + int flags, res; + if (!JS_IsObject(obj)) + return JS_TRUE; + p = JS_VALUE_GET_OBJ(obj); + flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; + if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) + return JS_EXCEPTION; + for(i = 0; i < len; i++) { + JSPropertyDescriptor desc; + JSAtom prop = props[i].atom; + res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (res < 0) + goto exception; + if (res) { + js_free_desc(ctx, &desc); + if ((desc.flags & JS_PROP_CONFIGURABLE) + || (is_frozen && (desc.flags & JS_PROP_WRITABLE))) { + res = FALSE; + goto done; + } + } + } + res = JS_IsExtensible(ctx, obj); + if (res < 0) + return JS_EXCEPTION; + res ^= 1; +done: + js_free_prop_enum(ctx, props, len); + return JS_NewBool(ctx, res); +exception: + js_free_prop_enum(ctx, props, len); + return JS_EXCEPTION; +} + +static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, iter, next_method = JS_UNDEFINED; + JSValueConst iterable; + BOOL done; + /* RequireObjectCoercible() not necessary because it is tested in + JS_GetIterator() by JS_GetProperty() */ + iterable = argv[0]; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return obj; + iter = JS_GetIterator(ctx, iterable, FALSE); + if (JS_IsException(iter)) + goto fail; + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail; + for(;;) { + JSValue key, value, item; + item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(item)) + goto fail; + if (done) { + JS_FreeValue(ctx, item); + break; + } + 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)) { + JS_FreeValue(ctx, key); + goto fail1; + } + if (JS_DefinePropertyValueValue(ctx, obj, key, value, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) { + fail1: + JS_FreeValue(ctx, item); + goto fail; + } + JS_FreeValue(ctx, item); + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + 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, obj); + return JS_EXCEPTION; +} + +#if 0 +/* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */ +static JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int ret; + ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]), + JS_DupValue(ctx, argv[2]), + JS_PROP_C_W_E | JS_PROP_THROW); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +static JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ToObject(ctx, argv[0]); +} + +static JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int hint = HINT_NONE; + + if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT) + hint = JS_VALUE_GET_INT(argv[1]); + + return JS_ToPrimitive(ctx, argv[0], hint); +} +#endif + +/* return an empty string if not an object */ +static JSValue js_object___getClass(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSAtom atom; + JSObject *p; + uint32_t tag; + int class_id; + + tag = JS_VALUE_GET_NORM_TAG(argv[0]); + if (tag == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(argv[0]); + class_id = p->class_id; + if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0])) + class_id = JS_CLASS_BYTECODE_FUNCTION; + atom = ctx->rt->class_array[class_id].class_name; + } else { + atom = JS_ATOM_empty_string; + } + return JS_AtomToString(ctx, atom); +} + +static JSValue js_object_is(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1])); +} + +#if 0 +static JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_GetObjectData(ctx, argv[0]); +} + +static JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1]))) + return JS_EXCEPTION; + return JS_DupValue(ctx, argv[1]); +} + +static JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ToPropertyKey(ctx, argv[0]); +} + +static JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_NewBool(ctx, JS_IsObject(argv[0])); +} + +static JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1])); +} + +static JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0])); +} +#endif + +JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj, JSValueConst defaultConstructor) +{ + JSValue ctor, species; + if (!JS_IsObject(obj)) + return JS_ThrowTypeErrorNotAnObject(ctx); + ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); + if (JS_IsException(ctor)) + return ctor; + if (JS_IsUndefined(ctor)) + return JS_DupValue(ctx, defaultConstructor); + if (!JS_IsObject(ctor)) { + JS_FreeValue(ctx, ctor); + return JS_ThrowTypeErrorNotAnObject(ctx); + } + species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); + JS_FreeValue(ctx, ctor); + if (JS_IsException(species)) + return species; + if (JS_IsUndefined(species) || JS_IsNull(species)) + return JS_DupValue(ctx, defaultConstructor); + if (!JS_IsConstructor(ctx, species)) { + JS_FreeValue(ctx, species); + return JS_ThrowTypeError(ctx, "not a constructor"); + } + return species; +} + +#if 0 +static JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_SpeciesConstructor(ctx, argv[0], argv[1]); +} +#endif + +static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val) +{ + JSValue val, ret; + val = JS_ToObject(ctx, this_val); + if (JS_IsException(val)) + return val; + ret = JS_GetPrototype(ctx, val); + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val, + JSValueConst proto) +{ + if (JS_IsUndefined(this_val) || JS_IsNull(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (!JS_IsObject(proto) && !JS_IsNull(proto)) + return JS_UNDEFINED; + if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0) + return JS_EXCEPTION; + else + return JS_UNDEFINED; +} + +static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, v1; + JSValueConst v; + int res; + v = argv[0]; + if (!JS_IsObject(v)) + return JS_FALSE; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + return JS_EXCEPTION; + v1 = JS_DupValue(ctx, v); + for(;;) { + v1 = JS_GetPrototypeFree(ctx, v1); + if (JS_IsException(v1)) + goto exception; + if (JS_IsNull(v1)) { + res = FALSE; + break; + } + if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) { + res = TRUE; + break; + } + /* avoid infinite loop (possible with proxies) */ + if (js_poll_interrupts(ctx)) + goto exception; + } + JS_FreeValue(ctx, v1); + JS_FreeValue(ctx, obj); + return JS_NewBool(ctx, res); +exception: + JS_FreeValue(ctx, v1); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, res = JS_EXCEPTION; + JSAtom prop = JS_ATOM_NULL; + JSPropertyDescriptor desc; + int has_prop; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + goto exception; + prop = JS_ValueToAtom(ctx, argv[0]); + if (UNLIKELY(prop == JS_ATOM_NULL)) + goto exception; + has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); + if (has_prop < 0) + goto exception; + if (has_prop) { + res = JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0); + js_free_desc(ctx, &desc); + } else { + res = JS_FALSE; + } +exception: + JS_FreeAtom(ctx, prop); + JS_FreeValue(ctx, obj); + return res; +} + +static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int setter) +{ + JSValue obj, res = JS_EXCEPTION; + JSAtom prop = JS_ATOM_NULL; + JSPropertyDescriptor desc; + int has_prop; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + goto exception; + prop = JS_ValueToAtom(ctx, argv[0]); + if (UNLIKELY(prop == JS_ATOM_NULL)) + goto exception; + for (;;) { + has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); + if (has_prop < 0) + goto exception; + if (has_prop) { + if (desc.flags & JS_PROP_GETSET) + res = JS_DupValue(ctx, setter ? desc.setter : desc.getter); + else + res = JS_UNDEFINED; + js_free_desc(ctx, &desc); + break; + } + obj = JS_GetPrototypeFree(ctx, obj); + if (JS_IsException(obj)) + goto exception; + if (JS_IsNull(obj)) { + res = JS_UNDEFINED; + break; + } + /* avoid infinite loop (possible with proxies) */ + if (js_poll_interrupts(ctx)) + goto exception; + } +exception: + JS_FreeAtom(ctx, prop); + JS_FreeValue(ctx, obj); + return res; +} + +static const JSCFunctionListEntry js_object_funcs[] = { + JS_CFUNC_DEF("create", 2, js_object_create ), + JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ), + JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ), + JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ), + JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ), + JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ), + JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ), + JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ), + JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ), + JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ), + JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ), + JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ), + JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ), + JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ), + JS_CFUNC_DEF("is", 2, js_object_is ), + JS_CFUNC_DEF("assign", 2, js_object_assign ), + JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ), + JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ), + JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ), + JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ), + JS_CFUNC_DEF("__getClass", 1, js_object___getClass ), + //JS_CFUNC_DEF("__isObject", 1, js_object___isObject ), + //JS_CFUNC_DEF("__isConstructor", 1, js_object___isConstructor ), + //JS_CFUNC_DEF("__toObject", 1, js_object___toObject ), + //JS_CFUNC_DEF("__setOwnProperty", 3, js_object___setOwnProperty ), + //JS_CFUNC_DEF("__toPrimitive", 2, js_object___toPrimitive ), + //JS_CFUNC_DEF("__toPropertyKey", 1, js_object___toPropertyKey ), + //JS_CFUNC_DEF("__speciesConstructor", 2, js_object___speciesConstructor ), + //JS_CFUNC_DEF("__isSameValueZero", 2, js_object___isSameValueZero ), + //JS_CFUNC_DEF("__getObjectData", 1, js_object___getObjectData ), + //JS_CFUNC_DEF("__setObjectData", 2, js_object___setObjectData ), + JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ), +}; + +static const JSCFunctionListEntry js_object_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_object_toString ), + JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ), + JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ), + JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ), + JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ), + JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ), + JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ), + JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ), + JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ), + JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ), + JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ), +}; + +void JS_AddIntrinsicObject(JSContext *ctx) { + JSValueConst obj; + obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1, + ctx->class_proto[JS_CLASS_OBJECT]); + JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs)); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT], + js_object_proto_funcs, countof(js_object_proto_funcs)); +} diff --git a/third_party/quickjs/parse.c b/third_party/quickjs/parse.c new file mode 100644 index 000000000..bdffa4103 --- /dev/null +++ b/third_party/quickjs/parse.c @@ -0,0 +1,6148 @@ +/* + * 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" + +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 __exception int js_parse_assign_expr(JSParseState *); +static __exception int js_parse_assign_expr2(JSParseState *, int); +static __exception int js_parse_expr(JSParseState *); +static __exception int js_parse_function_decl(JSParseState *, JSParseFunctionEnum, JSFunctionKindEnum, JSAtom, const uint8_t *, int); +static __exception int js_parse_function_decl2(JSParseState *, JSParseFunctionEnum, JSFunctionKindEnum, JSAtom, const uint8_t *, int, JSParseExportEnum, JSFunctionDef **); +static __exception int js_parse_postfix_expr(JSParseState *, int); +static __exception int js_parse_unary(JSParseState *, int); + +void js_parse_init(JSContext *ctx, JSParseState *s, + const char *input, size_t input_len, + const char *filename) +{ + memset(s, 0, sizeof(*s)); + s->ctx = ctx; + s->filename = filename; + s->line_num = 1; + s->buf_ptr = (const uint8_t *)input; + s->buf_end = s->buf_ptr + input_len; + s->token.val = ' '; + s->token.line_num = 1; +} + +/* Note: all the fields are already sealed except length */ +static int seal_template_obj(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + JSShapeProperty *prs; + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property1(p, JS_ATOM_length); + if (prs) { + if (js_update_property_flags(ctx, p, &prs, + prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE))) + return -1; + } + p->extensible = FALSE; + return 0; +} + +static __exception int emit_push_const(JSParseState *s, JSValueConst val, + BOOL as_atom) +{ + int idx; + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) { + JSAtom atom; + /* warning: JS_NewAtomStr frees the string value */ + JS_DupValue(s->ctx, val); + atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val)); + if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) { + emit_op(s, OP_push_atom_value); + emit_u32(s, atom); + return 0; + } + } + idx = cpool_add(s, JS_DupValue(s->ctx, val)); + if (idx < 0) + return -1; + emit_op(s, OP_push_const); + emit_u32(s, idx); + return 0; +} + +int js_parse_template_part(JSParseState *s, const uint8_t *p) +{ + uint32_t c; + StringBuffer b_s, *b = &b_s; + /* p points to the first byte of the template part */ + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + for(;;) { + if (p >= s->buf_end) + goto unexpected_eof; + c = *p++; + if (c == '`') { + /* template end part */ + break; + } + if (c == '$' && *p == '{') { + /* template start or middle part */ + p++; + break; + } + if (c == '\\') { + if (string_buffer_putc8(b, c)) + goto fail; + if (p >= s->buf_end) + goto unexpected_eof; + c = *p++; + } + /* newline sequences are normalized as single '\n' bytes */ + if (c == '\r') { + if (*p == '\n') + p++; + c = '\n'; + } + if (c == '\n') { + s->line_num++; + } else if (c >= 0x80) { + const uint8_t *p_next; + c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); + if (c > 0x10FFFF) { + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + } + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + s->token.val = TOK_TEMPLATE; + s->token.u.str.sep = c; + s->token.u.str.str = string_buffer_end(b); + s->buf_ptr = p; + return 0; + unexpected_eof: + js_parse_error(s, "unexpected end of string"); + fail: + string_buffer_free(b); + return -1; +} + +int js_parse_string(JSParseState *s, int sep, + BOOL do_throw, const uint8_t *p, + JSToken *token, const uint8_t **pp) +{ + int ret; + uint32_t c; + StringBuffer b_s, *b = &b_s; + /* string */ + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + for(;;) { + if (p >= s->buf_end) + goto invalid_char; + c = *p; + if (c < 0x20) { + if (!s->cur_func) { + if (do_throw) + js_parse_error(s, "invalid character in a JSON string"); + goto fail; + } + if (sep == '`') { + if (c == '\r') { + if (p[1] == '\n') + p++; + c = '\n'; + } + /* do not update s->line_num */ + } else if (c == '\n' || c == '\r') + goto invalid_char; + } + p++; + if (c == sep) + break; + if (c == '$' && *p == '{' && sep == '`') { + /* template start or middle part */ + p++; + break; + } + if (c == '\\') { + c = *p; + /* XXX: need a specific JSON case to avoid + accepting invalid escapes */ + switch(c) { + case '\0': + if (p >= s->buf_end) + goto invalid_char; + p++; + break; + case '\'': + case '\"': + case '\\': + p++; + break; + case '\r': /* accept DOS and MAC newline sequences */ + if (p[1] == '\n') { + p++; + } + /* fall thru */ + case '\n': + /* ignore escaped newline sequence */ + p++; + if (sep != '`') + s->line_num++; + continue; + default: + if (c >= '0' && c <= '9') { + if (!s->cur_func) + goto invalid_escape; /* JSON case */ + if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`') + goto parse_escape; + if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { + p++; + c = '\0'; + } else { + if (c >= '8' || sep == '`') { + /* Note: according to ES2021, \8 and \9 are not + accepted in strict mode or in templates. */ + goto invalid_escape; + } else { + if (do_throw) + js_parse_error(s, "octal escape sequences are not allowed in strict mode"); + } + goto fail; + } + } else if (c >= 0x80) { + const uint8_t *p_next; + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); + if (c > 0x10FFFF) { + goto invalid_utf8; + } + p = p_next; + /* LS or PS are skipped */ + if (c == CP_LS || c == CP_PS) + continue; + } else { + parse_escape: + ret = lre_parse_escape(&p, TRUE); + if (ret == -1) { + invalid_escape: + if (do_throw) + js_parse_error(s, "malformed escape sequence in string literal"); + goto fail; + } else if (ret < 0) { + /* ignore the '\' (could output a warning) */ + p++; + } else { + c = ret; + } + } + break; + } + } else if (c >= 0x80) { + const uint8_t *p_next; + c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); + if (c > 0x10FFFF) + goto invalid_utf8; + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + token->val = TOK_STRING; + token->u.str.sep = c; + token->u.str.str = string_buffer_end(b); + *pp = p; + return 0; + invalid_utf8: + if (do_throw) + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + invalid_char: + if (do_throw) + js_parse_error(s, "unexpected end of string"); + fail: + string_buffer_free(b); + return -1; +} + +int js_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 next_token(s); +} + +static int js_parse_expect_semi(JSParseState *s) +{ + if (s->token.val != ';') { + /* automatic insertion of ';' */ + if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) { + return 0; + } + return js_parse_error(s, "expecting '%c'", ';'); + } + return next_token(s); +} + +static __exception int js_parse_statement_or_decl(JSParseState *s, + int decl_mask); + +static __exception int js_parse_statement(JSParseState *s) +{ + return js_parse_statement_or_decl(s, 0); +} + +static int get_first_lexical_var(JSFunctionDef *fd, int scope) +{ + while (scope >= 0) { + int scope_idx = fd->scopes[scope].first; + if (scope_idx >= 0) + return scope_idx; + scope = fd->scopes[scope].parent; + } + return -1; +} + +static void pop_scope(JSParseState *s) { + if (s->cur_func) { + /* disable scoped variables */ + JSFunctionDef *fd = s->cur_func; + int scope = fd->scope_level; + emit_op(s, OP_leave_scope); + emit_u16(s, scope); + fd->scope_level = fd->scopes[scope].parent; + fd->scope_first = get_first_lexical_var(fd, fd->scope_level); + } +} + +static __exception int js_parse_block(JSParseState *s) +{ + if (js_parse_expect(s, '{')) + return -1; + if (s->token.val != '}') { + push_scope(s); + for(;;) { + if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) + return -1; + if (s->token.val == '}') + break; + } + pop_scope(s); + } + if (next_token(s)) + return -1; + return 0; +} + +static __exception int js_parse_template(JSParseState *s, int call, int *argc) +{ + JSContext *ctx = s->ctx; + JSValue raw_array, template_object; + JSToken cooked; + int depth, ret; + raw_array = JS_UNDEFINED; /* avoid warning */ + template_object = JS_UNDEFINED; /* avoid warning */ + if (call) { + /* Create a template object: an array of cooked strings */ + /* Create an array of raw strings and store it to the raw property */ + template_object = JS_NewArray(ctx); + if (JS_IsException(template_object)) + return -1; + // pool_idx = s->cur_func->cpool_count; + ret = emit_push_const(s, template_object, 0); + JS_FreeValue(ctx, template_object); + if (ret) + return -1; + raw_array = JS_NewArray(ctx); + if (JS_IsException(raw_array)) + return -1; + if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw, + raw_array, JS_PROP_THROW) < 0) { + return -1; + } + } + depth = 0; + while (s->token.val == TOK_TEMPLATE) { + const uint8_t *p = s->token.ptr + 1; + cooked = s->token; + if (call) { + if (JS_DefinePropertyValueUint32(ctx, raw_array, depth, + JS_DupValue(ctx, s->token.u.str.str), + JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) { + return -1; + } + /* re-parse the string with escape sequences but do not throw a + syntax error if it contains invalid sequences + */ + if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) { + cooked.u.str.str = JS_UNDEFINED; + } + if (JS_DefinePropertyValueUint32(ctx, template_object, depth, + cooked.u.str.str, + JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) { + return -1; + } + } else { + JSString *str; + /* re-parse the string with escape sequences and throw a + syntax error if it contains invalid sequences + */ + JS_FreeValue(ctx, s->token.u.str.str); + s->token.u.str.str = JS_UNDEFINED; + if (js_parse_string(s, '`', TRUE, p, &cooked, &p)) + return -1; + str = JS_VALUE_GET_STRING(cooked.u.str.str); + if (str->len != 0 || depth == 0) { + ret = emit_push_const(s, cooked.u.str.str, 1); + JS_FreeValue(s->ctx, cooked.u.str.str); + if (ret) + return -1; + if (depth == 0) { + if (s->token.u.str.sep == '`') + goto done1; + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_concat); + } + depth++; + } else { + JS_FreeValue(s->ctx, cooked.u.str.str); + } + } + if (s->token.u.str.sep == '`') + goto done; + if (next_token(s)) + return -1; + if (js_parse_expr(s)) + return -1; + depth++; + if (s->token.val != '}') { + return js_parse_error(s, "expected '}' after template expression"); + } + /* XXX: should convert to string at this stage? */ + free_token(s, &s->token); + /* Resume TOK_TEMPLATE parsing (s->token.line_num and + * s->token.ptr are OK) */ + s->got_lf = FALSE; + s->last_line_num = s->token.line_num; + if (js_parse_template_part(s, s->buf_ptr)) + return -1; + } + return js_parse_expect(s, TOK_TEMPLATE); + done: + if (call) { + /* Seal the objects */ + seal_template_obj(ctx, raw_array); + seal_template_obj(ctx, template_object); + *argc = depth + 1; + } else { + emit_op(s, OP_call_method); + emit_u16(s, depth - 1); + } + done1: + return next_token(s); +} + +static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) { + return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom && + !s->token.u.ident.has_escape; +} + +static int js_parse_get_pos(JSParseState *s, JSParsePos *sp) +{ + sp->last_line_num = s->last_line_num; + sp->line_num = s->token.line_num; + sp->ptr = s->token.ptr; + sp->got_lf = s->got_lf; + return 0; +} + +static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp) +{ + s->token.line_num = sp->last_line_num; + s->line_num = sp->line_num; + s->buf_ptr = sp->ptr; + s->got_lf = sp->got_lf; + return next_token(s); +} + +/* test if the current token is a let keyword. Use simplistic look-ahead scanner */ +static int is_let(JSParseState *s, int decl_mask) +{ + int res = FALSE; + if (token_is_pseudo_keyword(s, JS_ATOM_let)) { +#if 1 + JSParsePos pos; + js_parse_get_pos(s, &pos); + for (;;) { + if (next_token(s)) { + res = -1; + break; + } + if (s->token.val == '[') { + /* let [ is a syntax restriction: + it never introduces an ExpressionStatement */ + res = TRUE; + break; + } + if (s->token.val == '{' || + (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) || + s->token.val == TOK_LET || + s->token.val == TOK_YIELD || + s->token.val == TOK_AWAIT) { + /* Check for possible ASI if not scanning for Declaration */ + /* XXX: should also check that `{` introduces a BindingPattern, + but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */ + if (s->last_line_num == s->token.line_num || (decl_mask & DECL_MASK_OTHER)) { + res = TRUE; + break; + } + break; + } + break; + } + if (js_parse_seek_token(s, &pos)) { + res = -1; + } +#else + int tok = peek_token(s, TRUE); + if (tok == '{' || tok == TOK_IDENT || peek_token(s, FALSE) == '[') { + res = TRUE; + } +#endif + } + return res; +} + +static int peek_token(JSParseState *s, BOOL no_line_terminator) +{ + const uint8_t *p = s->buf_ptr; + return simple_next_token(&p, no_line_terminator); +} + +/* test if the current token is a label. Use simplistic look-ahead scanner */ +static BOOL is_label(JSParseState *s) +{ + return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && + peek_token(s, FALSE) == ':'); +} + +static BOOL token_is_ident(int tok) +{ + /* Accept keywords and reserved words as property names */ + return (tok == TOK_IDENT || + (tok >= TOK_FIRST_KEYWORD && + tok <= TOK_LAST_KEYWORD)); +} + +/* if the property is an expression, name = JS_ATOM_NULL */ +static int __exception js_parse_property_name(JSParseState *s, + JSAtom *pname, + BOOL allow_method, BOOL allow_var, + BOOL allow_private) +{ + int is_private = 0; + BOOL is_non_reserved_ident; + JSAtom name; + int prop_type; + prop_type = PROP_TYPE_IDENT; + if (allow_method) { + if (token_is_pseudo_keyword(s, JS_ATOM_get) + || token_is_pseudo_keyword(s, JS_ATOM_set)) { + /* get x(), set x() */ + name = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail1; + if (s->token.val == ':' || s->token.val == ',' || + s->token.val == '}' || s->token.val == '(') { + is_non_reserved_ident = TRUE; + goto ident_found; + } + prop_type = PROP_TYPE_GET + (name == JS_ATOM_set); + JS_FreeAtom(s->ctx, name); + } else if (s->token.val == '*') { + if (next_token(s)) + goto fail; + prop_type = PROP_TYPE_STAR; + } else if (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) != '\n') { + name = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail1; + if (s->token.val == ':' || s->token.val == ',' || + s->token.val == '}' || s->token.val == '(') { + is_non_reserved_ident = TRUE; + goto ident_found; + } + JS_FreeAtom(s->ctx, name); + if (s->token.val == '*') { + if (next_token(s)) + goto fail; + prop_type = PROP_TYPE_ASYNC_STAR; + } else { + prop_type = PROP_TYPE_ASYNC; + } + } + } + if (token_is_ident(s->token.val)) { + /* variable can only be a non-reserved identifier */ + is_non_reserved_ident = + (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved); + /* keywords and reserved words have a valid atom */ + name = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail1; + ident_found: + if (is_non_reserved_ident && + prop_type == PROP_TYPE_IDENT && allow_var) { + if (!(s->token.val == ':' || + (s->token.val == '(' && allow_method))) { + prop_type = PROP_TYPE_VAR; + } + } + } else if (s->token.val == TOK_STRING) { + name = JS_ValueToAtom(s->ctx, s->token.u.str.str); + if (name == JS_ATOM_NULL) + goto fail; + if (next_token(s)) + goto fail1; + } else if (s->token.val == TOK_NUMBER) { + JSValue val; + val = s->token.u.num.val; +#ifdef CONFIG_BIGNUM + if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + val = s->ctx->rt->bigfloat_ops. + mul_pow10_to_float64(s->ctx, &p->num, + s->token.u.num.exponent); + if (JS_IsException(val)) + goto fail; + name = JS_ValueToAtom(s->ctx, val); + JS_FreeValue(s->ctx, val); + } else +#endif + { + name = JS_ValueToAtom(s->ctx, val); + } + if (name == JS_ATOM_NULL) + goto fail; + if (next_token(s)) + goto fail1; + } else if (s->token.val == '[') { + if (next_token(s)) + goto fail; + if (js_parse_expr(s)) + goto fail; + if (js_parse_expect(s, ']')) + goto fail; + name = JS_ATOM_NULL; + } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) { + name = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail1; + is_private = PROP_TYPE_PRIVATE; + } else { + goto invalid_prop; + } + if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR && + s->token.val != '(') { + JS_FreeAtom(s->ctx, name); + invalid_prop: + js_parse_error(s, "invalid property name"); + goto fail; + } + *pname = name; + return prop_type | is_private; + fail1: + JS_FreeAtom(s->ctx, name); + fail: + *pname = JS_ATOM_NULL; + return -1; +} + +static void set_object_name_computed(JSParseState *s) +{ + JSFunctionDef *fd = s->cur_func; + int opcode; + opcode = get_prev_opcode(fd); + if (opcode == OP_set_name) { + /* XXX: should free atom after OP_set_name? */ + fd->byte_code.size = fd->last_opcode_pos; + fd->last_opcode_pos = -1; + emit_op(s, OP_set_name_computed); + } else if (opcode == OP_set_class_name) { + int define_class_pos; + define_class_pos = fd->last_opcode_pos + 1 - + get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + assert(fd->byte_code.buf[define_class_pos] == OP_define_class); + fd->byte_code.buf[define_class_pos] = OP_define_class_computed; + fd->last_opcode_pos = -1; + } +} + +static void set_object_name(JSParseState *s, JSAtom name) +{ + JSFunctionDef *fd = s->cur_func; + int opcode; + opcode = get_prev_opcode(fd); + if (opcode == OP_set_name) { + /* XXX: should free atom after OP_set_name? */ + fd->byte_code.size = fd->last_opcode_pos; + fd->last_opcode_pos = -1; + emit_op(s, OP_set_name); + emit_atom(s, name); + } else if (opcode == OP_set_class_name) { + int define_class_pos; + JSAtom atom; + define_class_pos = fd->last_opcode_pos + 1 - + get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + assert(fd->byte_code.buf[define_class_pos] == OP_define_class); + /* for consistency we free the previous atom which is + JS_ATOM_empty_string */ + atom = get_u32(fd->byte_code.buf + define_class_pos + 1); + JS_FreeAtom(s->ctx, atom); + put_u32(fd->byte_code.buf + define_class_pos + 1, + JS_DupAtom(s->ctx, name)); + fd->last_opcode_pos = -1; + } +} + +static __exception int js_parse_object_literal(JSParseState *s) +{ + JSAtom name = JS_ATOM_NULL; + const uint8_t *start_ptr; + int start_line, prop_type; + BOOL has_proto; + if (next_token(s)) + goto fail; + /* XXX: add an initial length that will be patched back */ + emit_op(s, OP_object); + has_proto = FALSE; + while (s->token.val != '}') { + /* specific case for getter/setter */ + start_ptr = s->token.ptr; + start_line = s->token.line_num; + if (s->token.val == TOK_ELLIPSIS) { + if (next_token(s)) + return -1; + if (js_parse_assign_expr(s)) + return -1; + emit_op(s, OP_null); /* dummy excludeList */ + emit_op(s, OP_copy_data_properties); + emit_u8(s, 2 | (1 << 2) | (0 << 5)); + emit_op(s, OP_drop); /* pop excludeList */ + emit_op(s, OP_drop); /* pop src object */ + goto next; + } + prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE); + if (prop_type < 0) + goto fail; + if (prop_type == PROP_TYPE_VAR) { + /* shortcut for x: x */ + emit_op(s, OP_scope_get_var); + emit_atom(s, name); + emit_u16(s, s->cur_func->scope_level); + emit_op(s, OP_define_field); + emit_atom(s, name); + } else if (s->token.val == '(') { + BOOL is_getset = (prop_type == PROP_TYPE_GET || + prop_type == PROP_TYPE_SET); + JSParseFunctionEnum func_type; + JSFunctionKindEnum func_kind; + int op_flags; + func_kind = JS_FUNC_NORMAL; + if (is_getset) { + func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET; + } else { + func_type = JS_PARSE_FUNC_METHOD; + if (prop_type == PROP_TYPE_STAR) + func_kind = JS_FUNC_GENERATOR; + else if (prop_type == PROP_TYPE_ASYNC) + func_kind = JS_FUNC_ASYNC; + else if (prop_type == PROP_TYPE_ASYNC_STAR) + func_kind = JS_FUNC_ASYNC_GENERATOR; + } + if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL, + start_ptr, start_line)) + goto fail; + if (name == JS_ATOM_NULL) { + emit_op(s, OP_define_method_computed); + } else { + emit_op(s, OP_define_method); + emit_atom(s, name); + } + if (is_getset) { + op_flags = OP_DEFINE_METHOD_GETTER + + prop_type - PROP_TYPE_GET; + } else { + op_flags = OP_DEFINE_METHOD_METHOD; + } + emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE); + } else { + if (js_parse_expect(s, ':')) + goto fail; + if (js_parse_assign_expr(s)) + goto fail; + if (name == JS_ATOM_NULL) { + set_object_name_computed(s); + emit_op(s, OP_define_array_el); + emit_op(s, OP_drop); + } else if (name == JS_ATOM___proto__) { + if (has_proto) { + js_parse_error(s, "duplicate __proto__ property name"); + goto fail; + } + emit_op(s, OP_set_proto); + has_proto = TRUE; + } else { + set_object_name(s, name); + emit_op(s, OP_define_field); + emit_atom(s, name); + } + } + JS_FreeAtom(s->ctx, name); + next: + name = JS_ATOM_NULL; + if (s->token.val != ',') + break; + if (next_token(s)) + goto fail; + } + if (js_parse_expect(s, '}')) + goto fail; + return 0; + fail: + JS_FreeAtom(s->ctx, name); + return -1; +} + +/* return TRUE if a regexp literal is allowed after this token */ +static BOOL is_regexp_allowed(int tok) +{ + switch (tok) { + case TOK_NUMBER: + case TOK_STRING: + case TOK_REGEXP: + case TOK_DEC: + case TOK_INC: + case TOK_NULL: + case TOK_FALSE: + case TOK_TRUE: + case TOK_THIS: + case ')': + case ']': + case '}': /* XXX: regexp may occur after */ + case TOK_IDENT: + return FALSE; + default: + return TRUE; + } +} + +static __exception int js_parse_regexp(JSParseState *s) +{ + const uint8_t *p; + BOOL in_class; + StringBuffer b_s, *b = &b_s; + StringBuffer b2_s, *b2 = &b2_s; + uint32_t c; + p = s->buf_ptr; + p++; + in_class = FALSE; + if (string_buffer_init(s->ctx, b, 32)) + return -1; + if (string_buffer_init(s->ctx, b2, 1)) + goto fail; + for(;;) { + if (p >= s->buf_end) { + eof_error: + js_parse_error(s, "unexpected end of regexp"); + goto fail; + } + c = *p++; + if (c == '\n' || c == '\r') { + goto eol_error; + } else if (c == '/') { + if (!in_class) + break; + } else if (c == '[') { + in_class = TRUE; + } else if (c == ']') { + /* XXX: incorrect as the first character in a class */ + in_class = FALSE; + } else if (c == '\\') { + if (string_buffer_putc8(b, c)) + goto fail; + c = *p++; + if (c == '\n' || c == '\r') + goto eol_error; + else if (c == '\0' && p >= s->buf_end) + goto eof_error; + else if (c >= 0x80) { + const uint8_t *p_next; + c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); + if (c > 0x10FFFF) { + goto invalid_utf8; + } + p = p_next; + if (c == CP_LS || c == CP_PS) + goto eol_error; + } + } else if (c >= 0x80) { + const uint8_t *p_next; + c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); + if (c > 0x10FFFF) { + invalid_utf8: + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + } + p = p_next; + /* LS or PS are considered as line terminator */ + if (c == CP_LS || c == CP_PS) { + eol_error: + js_parse_error(s, "unexpected line terminator in regexp"); + goto fail; + } + } + if (string_buffer_putc(b, c)) + goto fail; + } + /* flags */ + for(;;) { + const uint8_t *p_next = p; + c = *p_next++; + if (c >= 0x80) { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); + if (c > 0x10FFFF) { + goto invalid_utf8; + } + } + if (!lre_js_is_ident_next(c)) + break; + if (string_buffer_putc(b2, c)) + goto fail; + p = p_next; + } + s->token.val = TOK_REGEXP; + s->token.u.regexp.body = string_buffer_end(b); + s->token.u.regexp.flags = string_buffer_end(b2); + s->buf_ptr = p; + return 0; + fail: + string_buffer_free(b); + string_buffer_free(b2); + return -1; +} + +/* XXX: improve speed with early bailout */ +/* XXX: no longer works if regexps are present. Could use previous + regexp parsing heuristics to handle most cases */ +static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator) +{ + char state[256]; + size_t level = 0; + JSParsePos pos; + int last_tok, tok = TOK_EOF; + int c, tok_len, bits = 0; + /* protect from underflow */ + state[level++] = 0; + js_parse_get_pos(s, &pos); + last_tok = 0; + for (;;) { + switch(s->token.val) { + case '(': + case '[': + case '{': + if (level >= sizeof(state)) + goto done; + state[level++] = s->token.val; + break; + case ')': + if (state[--level] != '(') + goto done; + break; + case ']': + if (state[--level] != '[') + goto done; + break; + case '}': + c = state[--level]; + if (c == '`') { + /* continue the parsing of the template */ + free_token(s, &s->token); + /* Resume TOK_TEMPLATE parsing (s->token.line_num and + * s->token.ptr are OK) */ + s->got_lf = FALSE; + s->last_line_num = s->token.line_num; + if (js_parse_template_part(s, s->buf_ptr)) + goto done; + goto handle_template; + } else if (c != '{') { + goto done; + } + break; + case TOK_TEMPLATE: + handle_template: + if (s->token.u.str.sep != '`') { + /* '${' inside the template : closing '}' and continue + parsing the template */ + if (level >= sizeof(state)) + goto done; + state[level++] = '`'; + } + break; + case TOK_EOF: + goto done; + case ';': + if (level == 2) { + bits |= SKIP_HAS_SEMI; + } + break; + case TOK_ELLIPSIS: + if (level == 2) { + bits |= SKIP_HAS_ELLIPSIS; + } + break; + case '=': + bits |= SKIP_HAS_ASSIGNMENT; + break; + case TOK_DIV_ASSIGN: + tok_len = 2; + goto parse_regexp; + case '/': + tok_len = 1; + parse_regexp: + if (is_regexp_allowed(last_tok)) { + s->buf_ptr -= tok_len; + if (js_parse_regexp(s)) { + /* XXX: should clear the exception */ + goto done; + } + } + break; + } + /* last_tok is only used to recognize regexps */ + if (s->token.val == TOK_IDENT && + (token_is_pseudo_keyword(s, JS_ATOM_of) || + token_is_pseudo_keyword(s, JS_ATOM_yield))) { + last_tok = TOK_OF; + } else { + last_tok = s->token.val; + } + if (next_token(s)) { + /* XXX: should clear the exception generated by next_token() */ + break; + } + if (level <= 1) { + tok = s->token.val; + if (token_is_pseudo_keyword(s, JS_ATOM_of)) + tok = TOK_OF; + if (no_line_terminator && s->last_line_num != s->token.line_num) + tok = '\n'; + break; + } + } + done: + if (pbits) { + *pbits = bits; + } + if (js_parse_seek_token(s, &pos)) + return -1; + return tok; +} + + +static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name) +{ + /* Check for duplicate parameter names */ + JSFunctionDef *fd = s->cur_func; + int i; + for (i = 0; i < fd->arg_count; i++) { + if (fd->args[i].var_name == name) + goto duplicate; + } + for (i = 0; i < fd->var_count; i++) { + if (fd->vars[i].var_name == name) + goto duplicate; + } + return 0; +duplicate: + return js_parse_error(s, "duplicate parameter names not allowed in this context"); +} + +static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg) +{ + JSAtom name; + if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) + || ((s->cur_func->js_mode & JS_MODE_STRICT) && + (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) { + js_parse_error(s, "invalid destructuring target"); + return JS_ATOM_NULL; + } + name = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (is_arg && js_parse_check_duplicate_parameter(s, name)) + goto fail; + if (next_token(s)) + goto fail; + return name; +fail: + JS_FreeAtom(s->ctx, name); + return JS_ATOM_NULL; +} + +static __exception int js_parse_expr_paren(JSParseState *s) +{ + if (js_parse_expect(s, '(')) + return -1; + if (js_parse_expr(s)) + return -1; + if (js_parse_expect(s, ')')) + return -1; + return 0; +} + +static int js_parse_error_reserved_identifier(JSParseState *s) +{ + char buf1[ATOM_GET_STR_BUF_SIZE]; + return js_parse_error(s, "'%s' is a reserved identifier", + JS_AtomGetStr(s->ctx, buf1, sizeof(buf1), + s->token.u.ident.atom)); +} + +static __exception int js_parse_left_hand_side_expr(JSParseState *s) +{ + return js_parse_postfix_expr(s, PF_POSTFIX_CALL); +} + +/* add a private field variable in the current scope */ +static int add_private_class_field(JSParseState *s, JSFunctionDef *fd, + JSAtom name, JSVarKindEnum var_kind) +{ + JSContext *ctx = s->ctx; + JSVarDef *vd; + int idx; + idx = add_scope_var(ctx, fd, name, var_kind); + if (idx < 0) + return idx; + vd = &fd->vars[idx]; + vd->is_lexical = 1; + vd->is_const = 1; + return idx; +} + +static BOOL js_is_live_code(JSParseState *s) { + switch (get_prev_opcode(s->cur_func)) { + case OP_tail_call: + case OP_tail_call_method: + case OP_return: + case OP_return_undef: + case OP_return_async: + case OP_throw: + case OP_throw_error: + case OP_goto: +#if SHORT_OPCODES + case OP_goto8: + case OP_goto16: +#endif + case OP_ret: + return FALSE; + default: + return TRUE; + } +} + +/* return label or -1 if dead code */ +static int emit_goto(JSParseState *s, int opcode, int label) +{ + if (js_is_live_code(s)) { + if (label < 0) + label = new_label(s); + emit_op(s, opcode); + emit_u32(s, label); + s->cur_func->label_slots[label].ref_count++; + return label; + } + return -1; +} + +/* create a function to initialize class fields */ +static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s) +{ + JSFunctionDef *fd; + fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE, + s->filename, 0); + if (!fd) + return NULL; + fd->func_name = JS_ATOM_NULL; + fd->has_prototype = FALSE; + fd->has_home_object = TRUE; + fd->has_arguments_binding = FALSE; + fd->has_this_binding = TRUE; + fd->is_derived_class_constructor = FALSE; + fd->new_target_allowed = TRUE; + fd->super_call_allowed = FALSE; + fd->super_allowed = fd->has_home_object; + fd->arguments_allowed = FALSE; + fd->func_kind = JS_FUNC_NORMAL; + fd->func_type = JS_PARSE_FUNC_METHOD; + return fd; +} + +static __exception int emit_class_init_start(JSParseState *s, + ClassFieldsDef *cf) +{ + int label_add_brand; + cf->fields_init_fd = js_parse_function_class_fields_init(s); + if (!cf->fields_init_fd) + return -1; + s->cur_func = cf->fields_init_fd; + /* XXX: would be better to add the code only if needed, maybe in a + later pass */ + emit_op(s, OP_push_false); /* will be patched later */ + cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos; + label_add_brand = emit_goto(s, OP_if_false, -1); + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_home_object); + emit_u16(s, 0); + emit_op(s, OP_add_brand); + emit_label(s, label_add_brand); + s->cur_func = s->cur_func->parent; + return 0; +} + +static __exception int add_brand(JSParseState *s, ClassFieldsDef *cf) +{ + if (!cf->has_brand) { + /* define the brand field in 'this' of the initializer */ + if (!cf->fields_init_fd) { + if (emit_class_init_start(s, cf)) + return -1; + } + /* patch the start of the function to enable the OP_add_brand code */ + cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true; + cf->has_brand = TRUE; + } + return 0; +} + +static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) +{ + char buf[16]; + snprintf(buf, sizeof(buf), "%u", n); + return js_atom_concat_str(ctx, name, buf); +} + +/* XXX: could generate specific bytecode */ +static __exception int js_parse_class_default_ctor(JSParseState *s, + BOOL has_super, + JSFunctionDef **pfd) +{ + JSParsePos pos; + const char *str; + int ret, line_num; + JSParseFunctionEnum func_type; + const uint8_t *saved_buf_end; + js_parse_get_pos(s, &pos); + if (has_super) { + /* spec change: no argument evaluation */ + str = "(){super(...arguments);}"; + func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR; + } else { + str = "(){}"; + func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR; + } + line_num = s->token.line_num; + saved_buf_end = s->buf_end; + s->buf_ptr = (uint8_t *)str; + s->buf_end = (uint8_t *)(str + strlen(str)); + ret = next_token(s); + if (!ret) { + ret = js_parse_function_decl2(s, func_type, JS_FUNC_NORMAL, + JS_ATOM_NULL, (uint8_t *)str, + line_num, JS_PARSE_EXPORT_NONE, pfd); + } + s->buf_end = saved_buf_end; + ret |= js_parse_seek_token(s, &pos); + return ret; +} + +static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf) +{ + int cpool_idx; + s->cur_func = cf->fields_init_fd; + emit_op(s, OP_return_undef); + s->cur_func = s->cur_func->parent; + cpool_idx = cpool_add(s, JS_NULL); + cf->fields_init_fd->parent_cpool_idx = cpool_idx; + emit_op(s, OP_fclosure); + emit_u32(s, cpool_idx); + emit_op(s, OP_set_home_object); +} + +static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, + JSParseExportEnum export_flag) +{ + JSContext *ctx = s->ctx; + JSFunctionDef *fd = s->cur_func; + JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1; + JSAtom class_var_name = JS_ATOM_NULL; + JSFunctionDef *method_fd, *ctor_fd; + int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset; + int class_flags = 0, i, define_class_offset; + BOOL is_static, is_private; + const uint8_t *class_start_ptr = s->token.ptr; + const uint8_t *start_ptr; + ClassFieldsDef class_fields[2]; + /* classes are parsed and executed in strict mode */ + saved_js_mode = fd->js_mode; + fd->js_mode |= JS_MODE_STRICT; + if (next_token(s)) + goto fail; + if (s->token.val == TOK_IDENT) { + if (s->token.u.ident.is_reserved) { + js_parse_error_reserved_identifier(s); + goto fail; + } + class_name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail; + } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) { + js_parse_error(s, "class statement requires a name"); + goto fail; + } + if (!is_class_expr) { + if (class_name == JS_ATOM_NULL) + class_var_name = JS_ATOM__default_; /* export default */ + else + class_var_name = class_name; + class_var_name = JS_DupAtom(ctx, class_var_name); + } + push_scope(s); + if (s->token.val == TOK_EXTENDS) { + class_flags = JS_DEFINE_CLASS_HAS_HERITAGE; + if (next_token(s)) + goto fail; + if (js_parse_left_hand_side_expr(s)) + goto fail; + } else { + emit_op(s, OP_undefined); + } + /* add a 'const' definition for the class name */ + if (class_name != JS_ATOM_NULL) { + class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST); + if (class_name_var_idx < 0) + goto fail; + } + if (js_parse_expect(s, '{')) + goto fail; + /* this scope contains the private fields */ + push_scope(s); + emit_op(s, OP_push_const); + ctor_cpool_offset = fd->byte_code.size; + emit_u32(s, 0); /* will be patched at the end of the class parsing */ + if (class_name == JS_ATOM_NULL) { + if (class_var_name != JS_ATOM_NULL) + class_name1 = JS_ATOM_default; + else + class_name1 = JS_ATOM_empty_string; + } else { + class_name1 = class_name; + } + emit_op(s, OP_define_class); + emit_atom(s, class_name1); + emit_u8(s, class_flags); + define_class_offset = fd->last_opcode_pos; + for(i = 0; i < 2; i++) { + ClassFieldsDef *cf = &class_fields[i]; + cf->fields_init_fd = NULL; + cf->computed_fields_count = 0; + cf->has_brand = FALSE; + } + ctor_fd = NULL; + while (s->token.val != '}') { + if (s->token.val == ';') { + if (next_token(s)) + goto fail; + continue; + } + is_static = (s->token.val == TOK_STATIC); + prop_type = -1; + if (is_static) { + if (next_token(s)) + goto fail; + /* allow "static" field name */ + if (s->token.val == ';' || s->token.val == '=') { + is_static = FALSE; + name = JS_DupAtom(ctx, JS_ATOM_static); + prop_type = PROP_TYPE_IDENT; + } + } + if (is_static) + emit_op(s, OP_swap); + start_ptr = s->token.ptr; + if (prop_type < 0) { + prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE); + if (prop_type < 0) + goto fail; + } + is_private = prop_type & PROP_TYPE_PRIVATE; + prop_type &= ~PROP_TYPE_PRIVATE; + if ((name == JS_ATOM_constructor && !is_static && + prop_type != PROP_TYPE_IDENT) || + (name == JS_ATOM_prototype && is_static) || + name == JS_ATOM_hash_constructor) { + js_parse_error(s, "invalid method name"); + goto fail; + } + if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) { + BOOL is_set = prop_type - PROP_TYPE_GET; + JSFunctionDef *method_fd; + if (is_private) { + int idx, var_kind; + idx = find_private_class_field(ctx, fd, name, fd->scope_level); + if (idx >= 0) { + var_kind = fd->vars[idx].var_kind; + if (var_kind == JS_VAR_PRIVATE_FIELD || + var_kind == JS_VAR_PRIVATE_METHOD || + var_kind == JS_VAR_PRIVATE_GETTER_SETTER || + var_kind == (JS_VAR_PRIVATE_GETTER + is_set)) { + goto private_field_already_defined; + } + fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER; + } else { + if (add_private_class_field(s, fd, name, + JS_VAR_PRIVATE_GETTER + is_set) < 0) + goto fail; + } + if (add_brand(s, &class_fields[is_static]) < 0) + goto fail; + } + if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set, + JS_FUNC_NORMAL, JS_ATOM_NULL, + start_ptr, s->token.line_num, + JS_PARSE_EXPORT_NONE, &method_fd)) + goto fail; + if (is_private) { + method_fd->need_home_object = TRUE; /* needed for brand check */ + emit_op(s, OP_set_home_object); + /* XXX: missing function name */ + emit_op(s, OP_scope_put_var_init); + if (is_set) { + JSAtom setter_name; + int ret; + setter_name = get_private_setter_name(ctx, name); + if (setter_name == JS_ATOM_NULL) + goto fail; + emit_atom(s, setter_name); + ret = add_private_class_field(s, fd, setter_name, + JS_VAR_PRIVATE_SETTER); + JS_FreeAtom(ctx, setter_name); + if (ret < 0) + goto fail; + } else { + emit_atom(s, name); + } + emit_u16(s, s->cur_func->scope_level); + } else { + if (name == JS_ATOM_NULL) { + emit_op(s, OP_define_method_computed); + } else { + emit_op(s, OP_define_method); + emit_atom(s, name); + } + emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set); + } + } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') { + ClassFieldsDef *cf = &class_fields[is_static]; + JSAtom field_var_name = JS_ATOM_NULL; + /* class field */ + /* XXX: spec: not consistent with method name checks */ + if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) { + js_parse_error(s, "invalid field name"); + goto fail; + } + if (is_private) { + if (find_private_class_field(ctx, fd, name, + fd->scope_level) >= 0) { + goto private_field_already_defined; + } + if (add_private_class_field(s, fd, name, + JS_VAR_PRIVATE_FIELD) < 0) + goto fail; + emit_op(s, OP_private_symbol); + emit_atom(s, name); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, s->cur_func->scope_level); + } + if (!cf->fields_init_fd) { + if (emit_class_init_start(s, cf)) + goto fail; + } + if (name == JS_ATOM_NULL ) { + /* save the computed field name into a variable */ + field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count); + if (field_var_name == JS_ATOM_NULL) + goto fail; + if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) { + JS_FreeAtom(ctx, field_var_name); + goto fail; + } + emit_op(s, OP_to_propkey); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, field_var_name); + emit_u16(s, s->cur_func->scope_level); + } + s->cur_func = cf->fields_init_fd; + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + if (name == JS_ATOM_NULL) { + emit_op(s, OP_scope_get_var); + emit_atom(s, field_var_name); + emit_u16(s, s->cur_func->scope_level); + cf->computed_fields_count++; + JS_FreeAtom(ctx, field_var_name); + } else if (is_private) { + emit_op(s, OP_scope_get_var); + emit_atom(s, name); + emit_u16(s, s->cur_func->scope_level); + } + if (s->token.val == '=') { + if (next_token(s)) + goto fail; + if (js_parse_assign_expr(s)) + goto fail; + } else { + emit_op(s, OP_undefined); + } + if (is_private) { + set_object_name_computed(s); + emit_op(s, OP_define_private_field); + } else if (name == JS_ATOM_NULL) { + set_object_name_computed(s); + emit_op(s, OP_define_array_el); + emit_op(s, OP_drop); + } else { + set_object_name(s, name); + emit_op(s, OP_define_field); + emit_atom(s, name); + } + s->cur_func = s->cur_func->parent; + if (js_parse_expect_semi(s)) + goto fail; + } else { + JSParseFunctionEnum func_type; + JSFunctionKindEnum func_kind; + func_type = JS_PARSE_FUNC_METHOD; + func_kind = JS_FUNC_NORMAL; + if (prop_type == PROP_TYPE_STAR) { + func_kind = JS_FUNC_GENERATOR; + } else if (prop_type == PROP_TYPE_ASYNC) { + func_kind = JS_FUNC_ASYNC; + } else if (prop_type == PROP_TYPE_ASYNC_STAR) { + func_kind = JS_FUNC_ASYNC_GENERATOR; + } else if (name == JS_ATOM_constructor && !is_static) { + if (ctor_fd) { + js_parse_error(s, "property constructor appears more than once"); + goto fail; + } + if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) + func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR; + else + func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR; + } + if (is_private) { + if (add_brand(s, &class_fields[is_static]) < 0) + goto fail; + } + if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, s->token.line_num, JS_PARSE_EXPORT_NONE, &method_fd)) + goto fail; + if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR || + func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) { + ctor_fd = method_fd; + } else if (is_private) { + method_fd->need_home_object = TRUE; /* needed for brand check */ + if (find_private_class_field(ctx, fd, name, + fd->scope_level) >= 0) { + private_field_already_defined: + js_parse_error(s, "private class field is already defined"); + goto fail; + } + if (add_private_class_field(s, fd, name, + JS_VAR_PRIVATE_METHOD) < 0) + goto fail; + emit_op(s, OP_set_home_object); + emit_op(s, OP_set_name); + emit_atom(s, name); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, s->cur_func->scope_level); + } else { + if (name == JS_ATOM_NULL) { + emit_op(s, OP_define_method_computed); + } else { + emit_op(s, OP_define_method); + emit_atom(s, name); + } + emit_u8(s, OP_DEFINE_METHOD_METHOD); + } + } + if (is_static) + emit_op(s, OP_swap); + JS_FreeAtom(ctx, name); + name = JS_ATOM_NULL; + } + if (s->token.val != '}') { + js_parse_error(s, "expecting '%c'", '}'); + goto fail; + } + if (!ctor_fd) { + if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd)) + goto fail; + } + /* patch the constant pool index for the constructor */ + put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx); + /* store the class source code in the constructor. */ + if (!(fd->js_mode & JS_MODE_STRIP)) { + js_free(ctx, ctor_fd->source); + ctor_fd->source_len = s->buf_ptr - class_start_ptr; + ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr, + ctor_fd->source_len); + if (!ctor_fd->source) + goto fail; + } + /* consume the '}' */ + if (next_token(s)) + goto fail; + /* store the function to initialize the fields to that it can be + referenced by the constructor */ + { + ClassFieldsDef *cf = &class_fields[0]; + int var_idx; + var_idx = define_var(s, fd, JS_ATOM_class_fields_init, + JS_VAR_DEF_CONST); + if (var_idx < 0) + goto fail; + if (cf->fields_init_fd) { + emit_class_init_end(s, cf); + } else { + emit_op(s, OP_undefined); + } + emit_op(s, OP_scope_put_var_init); + emit_atom(s, JS_ATOM_class_fields_init); + emit_u16(s, s->cur_func->scope_level); + } + /* drop the prototype */ + emit_op(s, OP_drop); + /* initialize the static fields */ + if (class_fields[1].fields_init_fd != NULL) { + ClassFieldsDef *cf = &class_fields[1]; + emit_op(s, OP_dup); + emit_class_init_end(s, cf); + emit_op(s, OP_call_method); + emit_u16(s, 0); + emit_op(s, OP_drop); + } + if (class_name != JS_ATOM_NULL) { + /* store the class name in the scoped class name variable (it + is independent from the class statement variable + definition) */ + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, class_name); + emit_u16(s, fd->scope_level); + } + pop_scope(s); + pop_scope(s); + /* the class statements have a block level scope */ + if (class_var_name != JS_ATOM_NULL) { + if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0) + goto fail; + emit_op(s, OP_scope_put_var_init); + emit_atom(s, class_var_name); + emit_u16(s, fd->scope_level); + } else { + if (class_name == JS_ATOM_NULL) { + /* cannot use OP_set_name because the name of the class + must be defined before the static initializers are + executed */ + emit_op(s, OP_set_class_name); + emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset); + } + } + if (export_flag != JS_PARSE_EXPORT_NONE) { + if (!add_export_entry(s, fd->module, + class_var_name, + export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default, + JS_EXPORT_TYPE_LOCAL)) + goto fail; + } + JS_FreeAtom(ctx, class_name); + JS_FreeAtom(ctx, class_var_name); + fd->js_mode = saved_js_mode; + return 0; + fail: + JS_FreeAtom(ctx, name); + JS_FreeAtom(ctx, class_name); + JS_FreeAtom(ctx, class_var_name); + fd->js_mode = saved_js_mode; + return -1; +} + +static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, + JSAtom *pname, int *plabel, int *pdepth, BOOL keep, + int tok) +{ + JSFunctionDef *fd; + int opcode, scope, label, depth; + JSAtom name; + /* we check the last opcode to get the lvalue type */ + fd = s->cur_func; + scope = 0; + name = JS_ATOM_NULL; + label = -1; + depth = 0; + switch(opcode = get_prev_opcode(fd)) { + case OP_scope_get_var: + name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); + if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) && + (fd->js_mode & JS_MODE_STRICT)) { + return js_parse_error(s, "invalid lvalue in strict mode"); + } + if (name == JS_ATOM_this || name == JS_ATOM_new_target) + goto invalid_lvalue; + depth = 2; /* will generate OP_get_ref_value */ + break; + case OP_get_field: + name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + depth = 1; + break; + case OP_scope_get_private_field: + name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); + depth = 1; + break; + case OP_get_array_el: + depth = 2; + break; + case OP_get_super_value: + depth = 3; + break; + default: + invalid_lvalue: + if (tok == TOK_FOR) { + return js_parse_error(s, "invalid for in/of left hand-side"); + } else if (tok == TOK_INC || tok == TOK_DEC) { + return js_parse_error(s, "invalid increment/decrement operand"); + } else if (tok == '[' || tok == '{') { + return js_parse_error(s, "invalid destructuring target"); + } else { + return js_parse_error(s, "invalid assignment left-hand side"); + } + } + /* remove the last opcode */ + fd->byte_code.size = fd->last_opcode_pos; + fd->last_opcode_pos = -1; + if (keep) { + /* get the value but keep the object/fields on the stack */ + switch(opcode) { + case OP_scope_get_var: + label = new_label(s); + emit_op(s, OP_scope_make_ref); + emit_atom(s, name); + emit_u32(s, label); + emit_u16(s, scope); + update_label(fd, label, 1); + emit_op(s, OP_get_ref_value); + opcode = OP_get_ref_value; + break; + case OP_get_field: + emit_op(s, OP_get_field2); + emit_atom(s, name); + break; + case OP_scope_get_private_field: + emit_op(s, OP_scope_get_private_field2); + emit_atom(s, name); + emit_u16(s, scope); + break; + case OP_get_array_el: + /* XXX: replace by a single opcode ? */ + emit_op(s, OP_to_propkey2); + emit_op(s, OP_dup2); + emit_op(s, OP_get_array_el); + break; + case OP_get_super_value: + emit_op(s, OP_to_propkey); + emit_op(s, OP_dup3); + emit_op(s, OP_get_super_value); + break; + default: + abort(); + } + } else { + switch(opcode) { + case OP_scope_get_var: + label = new_label(s); + emit_op(s, OP_scope_make_ref); + emit_atom(s, name); + emit_u32(s, label); + emit_u16(s, scope); + update_label(fd, label, 1); + opcode = OP_get_ref_value; + break; + case OP_get_array_el: + emit_op(s, OP_to_propkey2); + break; + case OP_get_super_value: + emit_op(s, OP_to_propkey); + break; + } + } + *popcode = opcode; + *pscope = scope; + /* name has refcount for OP_get_field and OP_get_ref_value, + and JS_ATOM_NULL for other opcodes */ + *pname = name; + *plabel = label; + if (pdepth) + *pdepth = depth; + return 0; +} + +static __exception int js_define_var(JSParseState *s, JSAtom name, int tok) +{ + JSFunctionDef *fd = s->cur_func; + JSVarDefEnum var_def_type; + if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) { + return js_parse_error(s, "yield is a reserved identifier"); + } + if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) + && (fd->js_mode & JS_MODE_STRICT)) { + return js_parse_error(s, "invalid variable name in strict mode"); + } + if ((name == JS_ATOM_let || name == JS_ATOM_undefined) + && (tok == TOK_LET || tok == TOK_CONST)) { + return js_parse_error(s, "invalid lexical variable name"); + } + switch(tok) { + case TOK_LET: + var_def_type = JS_VAR_DEF_LET; + break; + case TOK_CONST: + var_def_type = JS_VAR_DEF_CONST; + break; + case TOK_VAR: + var_def_type = JS_VAR_DEF_VAR; + break; + case TOK_CATCH: + var_def_type = JS_VAR_DEF_CATCH; + break; + default: + abort(); + } + if (define_var(s, fd, name, var_def_type) < 0) + return -1; + return 0; +} + +/* name has a live reference. 'is_let' is only used with opcode = + OP_scope_get_var which is never generated by get_lvalue(). */ +static void put_lvalue(JSParseState *s, int opcode, int scope, + JSAtom name, int label, PutLValueEnum special, + BOOL is_let) +{ + switch(opcode) { + case OP_get_field: + case OP_scope_get_private_field: + /* depth = 1 */ + switch(special) { + case PUT_LVALUE_NOKEEP: + case PUT_LVALUE_NOKEEP_DEPTH: + break; + case PUT_LVALUE_KEEP_TOP: + emit_op(s, OP_insert2); /* obj v -> v obj v */ + break; + case PUT_LVALUE_KEEP_SECOND: + emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */ + break; + case PUT_LVALUE_NOKEEP_BOTTOM: + emit_op(s, OP_swap); + break; + default: + abort(); + } + break; + case OP_get_array_el: + case OP_get_ref_value: + /* depth = 2 */ + if (opcode == OP_get_ref_value) { + JS_FreeAtom(s->ctx, name); + emit_label(s, label); + } + switch(special) { + case PUT_LVALUE_NOKEEP: + emit_op(s, OP_nop); /* will trigger optimization */ + break; + case PUT_LVALUE_NOKEEP_DEPTH: + break; + case PUT_LVALUE_KEEP_TOP: + emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */ + break; + case PUT_LVALUE_KEEP_SECOND: + emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */ + break; + case PUT_LVALUE_NOKEEP_BOTTOM: + emit_op(s, OP_rot3l); + break; + default: + abort(); + } + break; + case OP_get_super_value: + /* depth = 3 */ + switch(special) { + case PUT_LVALUE_NOKEEP: + case PUT_LVALUE_NOKEEP_DEPTH: + break; + case PUT_LVALUE_KEEP_TOP: + emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */ + break; + case PUT_LVALUE_KEEP_SECOND: + emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */ + break; + case PUT_LVALUE_NOKEEP_BOTTOM: + emit_op(s, OP_rot4l); + break; + default: + abort(); + } + break; + default: + break; + } + switch(opcode) { + case OP_scope_get_var: /* val -- */ + assert(special == PUT_LVALUE_NOKEEP || + special == PUT_LVALUE_NOKEEP_DEPTH); + emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var); + emit_u32(s, name); /* has refcount */ + emit_u16(s, scope); + break; + case OP_get_field: + emit_op(s, OP_put_field); + emit_u32(s, name); /* name has refcount */ + break; + case OP_scope_get_private_field: + emit_op(s, OP_scope_put_private_field); + emit_u32(s, name); /* name has refcount */ + emit_u16(s, scope); + break; + case OP_get_array_el: + emit_op(s, OP_put_array_el); + break; + case OP_get_ref_value: + emit_op(s, OP_put_ref_value); + break; + case OP_get_super_value: + emit_op(s, OP_put_super_value); + break; + default: + abort(); + } +} + +static void js_emit_spread_code(JSParseState *s, int depth) +{ + int label_rest_next, label_rest_done; + /* XXX: could check if enum object is an actual array and optimize + slice extraction. enumeration record and target array are in a + different order from OP_append case. */ + /* enum_rec xxx -- enum_rec xxx array 0 */ + emit_op(s, OP_array_from); + emit_u16(s, 0); + emit_op(s, OP_push_i32); + emit_u32(s, 0); + emit_label(s, label_rest_next = new_label(s)); + emit_op(s, OP_for_of_next); + emit_u8(s, 2 + depth); + label_rest_done = emit_goto(s, OP_if_true, -1); + /* array idx val -- array idx */ + emit_op(s, OP_define_array_el); + emit_op(s, OP_inc); + emit_goto(s, OP_goto, label_rest_next); + emit_label(s, label_rest_done); + /* enum_rec xxx array idx undef -- enum_rec xxx array */ + emit_op(s, OP_drop); + emit_op(s, OP_drop); +} + +static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, + JSAtom label_name, + int label_break, int label_cont, + int drop_count) +{ + be->prev = fd->top_break; + fd->top_break = be; + be->label_name = label_name; + be->label_break = label_break; + be->label_cont = label_cont; + be->drop_count = drop_count; + be->label_finally = -1; + be->scope_level = fd->scope_level; + be->has_iterator = FALSE; +} + +static void pop_break_entry(JSFunctionDef *fd) +{ + BlockEnv *be; + be = fd->top_break; + fd->top_break = be->prev; +} + +/* Return -1 if error, 0 if no initializer, 1 if an initializer is + present at the top level. */ +static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, + int hasval, int has_ellipsis, + BOOL allow_initializer) +{ + int label_parse, label_assign, label_done, label_lvalue, depth_lvalue; + int start_addr, assign_addr; + JSAtom prop_name, var_name; + int opcode, scope, tok1, skip_bits; + BOOL has_initializer; + if (has_ellipsis < 0) { + /* pre-parse destructuration target for spread detection */ + js_parse_skip_parens_token(s, &skip_bits, FALSE); + has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS; + } + label_parse = new_label(s); + label_assign = new_label(s); + start_addr = s->cur_func->byte_code.size; + if (hasval) { + /* consume value from the stack */ + emit_op(s, OP_dup); + emit_op(s, OP_undefined); + emit_op(s, OP_strict_eq); + emit_goto(s, OP_if_true, label_parse); + emit_label(s, label_assign); + } else { + emit_goto(s, OP_goto, label_parse); + emit_label(s, label_assign); + /* leave value on the stack */ + emit_op(s, OP_dup); + } + assign_addr = s->cur_func->byte_code.size; + if (s->token.val == '{') { + if (next_token(s)) + return -1; + /* throw an exception if the value cannot be converted to an object */ + emit_op(s, OP_to_object); + if (has_ellipsis) { + /* add excludeList on stack just below src object */ + emit_op(s, OP_object); + emit_op(s, OP_swap); + } + while (s->token.val != '}') { + int prop_type; + if (s->token.val == TOK_ELLIPSIS) { + if (!has_ellipsis) { + JS_ThrowInternalError(s->ctx, "unexpected ellipsis token"); + return -1; + } + if (next_token(s)) + return -1; + if (tok) { + var_name = js_parse_destructuring_var(s, tok, is_arg); + if (var_name == JS_ATOM_NULL) + return -1; + opcode = OP_scope_get_var; + scope = s->cur_func->scope_level; + label_lvalue = -1; + depth_lvalue = 0; + } else { + if (js_parse_left_hand_side_expr(s)) + return -1; + if (get_lvalue(s, &opcode, &scope, &var_name, + &label_lvalue, &depth_lvalue, FALSE, '{')) + return -1; + } + if (s->token.val != '}') { + js_parse_error(s, "assignment rest property must be last"); + goto var_error; + } + emit_op(s, OP_object); /* target */ + emit_op(s, OP_copy_data_properties); + emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5)); + goto set_val; + } + prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE); + if (prop_type < 0) + return -1; + var_name = JS_ATOM_NULL; + opcode = OP_scope_get_var; + scope = s->cur_func->scope_level; + label_lvalue = -1; + depth_lvalue = 0; + if (prop_type == PROP_TYPE_IDENT) { + if (next_token(s)) + goto prop_error; + if ((s->token.val == '[' || s->token.val == '{') + && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' || + tok1 == '=' || tok1 == '}')) { + if (prop_name == JS_ATOM_NULL) { + /* computed property name on stack */ + if (has_ellipsis) { + /* define the property in excludeList */ + emit_op(s, OP_to_propkey); /* avoid calling ToString twice */ + emit_op(s, OP_perm3); /* TOS: src excludeList prop */ + emit_op(s, OP_null); /* TOS: src excludeList prop null */ + emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */ + emit_op(s, OP_perm3); /* TOS: excludeList src prop */ + } + /* get the computed property from the source object */ + emit_op(s, OP_get_array_el2); + } else { + /* named property */ + if (has_ellipsis) { + /* define the property in excludeList */ + emit_op(s, OP_swap); /* TOS: src excludeList */ + emit_op(s, OP_null); /* TOS: src excludeList null */ + emit_op(s, OP_define_field); /* TOS: src excludeList */ + emit_atom(s, prop_name); + emit_op(s, OP_swap); /* TOS: excludeList src */ + } + /* get the named property from the source object */ + emit_op(s, OP_get_field2); + emit_u32(s, prop_name); + } + if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0) + return -1; + if (s->token.val == '}') + break; + /* accept a trailing comma before the '}' */ + if (js_parse_expect(s, ',')) + return -1; + continue; + } + if (prop_name == JS_ATOM_NULL) { + emit_op(s, OP_to_propkey2); + if (has_ellipsis) { + /* define the property in excludeList */ + emit_op(s, OP_perm3); + emit_op(s, OP_null); + emit_op(s, OP_define_array_el); + emit_op(s, OP_perm3); + } + /* source prop -- source source prop */ + emit_op(s, OP_dup1); + } else { + if (has_ellipsis) { + /* define the property in excludeList */ + emit_op(s, OP_swap); + emit_op(s, OP_null); + emit_op(s, OP_define_field); + emit_atom(s, prop_name); + emit_op(s, OP_swap); + } + /* source -- source source */ + emit_op(s, OP_dup); + } + if (tok) { + var_name = js_parse_destructuring_var(s, tok, is_arg); + if (var_name == JS_ATOM_NULL) + goto prop_error; + } else { + if (js_parse_left_hand_side_expr(s)) + goto prop_error; + lvalue: + if (get_lvalue(s, &opcode, &scope, &var_name, + &label_lvalue, &depth_lvalue, FALSE, '{')) + goto prop_error; + /* swap ref and lvalue object if any */ + if (prop_name == JS_ATOM_NULL) { + switch(depth_lvalue) { + case 1: + /* source prop x -> x source prop */ + emit_op(s, OP_rot3r); + break; + case 2: + /* source prop x y -> x y source prop */ + emit_op(s, OP_swap2); /* t p2 s p1 */ + break; + case 3: + /* source prop x y z -> x y z source prop */ + emit_op(s, OP_rot5l); + emit_op(s, OP_rot5l); + break; + } + } else { + switch(depth_lvalue) { + case 1: + /* source x -> x source */ + emit_op(s, OP_swap); + break; + case 2: + /* source x y -> x y source */ + emit_op(s, OP_rot3l); + break; + case 3: + /* source x y z -> x y z source */ + emit_op(s, OP_rot4l); + break; + } + } + } + if (prop_name == JS_ATOM_NULL) { + /* computed property name on stack */ + /* XXX: should have OP_get_array_el2x with depth */ + /* source prop -- val */ + emit_op(s, OP_get_array_el); + } else { + /* named property */ + /* XXX: should have OP_get_field2x with depth */ + /* source -- val */ + emit_op(s, OP_get_field); + emit_u32(s, prop_name); + } + } else { + /* prop_type = PROP_TYPE_VAR, cannot be a computed property */ + if (is_arg && js_parse_check_duplicate_parameter(s, prop_name)) + goto prop_error; + if ((s->cur_func->js_mode & JS_MODE_STRICT) && + (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) { + js_parse_error(s, "invalid destructuring target"); + goto prop_error; + } + if (has_ellipsis) { + /* define the property in excludeList */ + emit_op(s, OP_swap); + emit_op(s, OP_null); + emit_op(s, OP_define_field); + emit_atom(s, prop_name); + emit_op(s, OP_swap); + } + if (!tok || tok == TOK_VAR) { + /* generate reference */ + /* source -- source source */ + emit_op(s, OP_dup); + emit_op(s, OP_scope_get_var); + emit_atom(s, prop_name); + emit_u16(s, s->cur_func->scope_level); + goto lvalue; + } + var_name = JS_DupAtom(s->ctx, prop_name); + /* source -- source val */ + emit_op(s, OP_get_field2); + emit_u32(s, prop_name); + } + set_val: + if (tok) { + if (js_define_var(s, var_name, tok)) + goto var_error; + scope = s->cur_func->scope_level; + } + if (s->token.val == '=') { /* handle optional default value */ + int label_hasval; + emit_op(s, OP_dup); + emit_op(s, OP_undefined); + emit_op(s, OP_strict_eq); + label_hasval = emit_goto(s, OP_if_false, -1); + if (next_token(s)) + goto var_error; + emit_op(s, OP_drop); + if (js_parse_assign_expr(s)) + goto var_error; + if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) + set_object_name(s, var_name); + emit_label(s, label_hasval); + } + /* store value into lvalue object */ + put_lvalue(s, opcode, scope, var_name, label_lvalue, + PUT_LVALUE_NOKEEP_DEPTH, + (tok == TOK_CONST || tok == TOK_LET)); + if (s->token.val == '}') + break; + /* accept a trailing comma before the '}' */ + if (js_parse_expect(s, ',')) + return -1; + } + /* drop the source object */ + emit_op(s, OP_drop); + if (has_ellipsis) { + emit_op(s, OP_drop); /* pop excludeList */ + } + if (next_token(s)) + return -1; + } else if (s->token.val == '[') { + BOOL has_spread; + int enum_depth; + BlockEnv block_env; + if (next_token(s)) + return -1; + /* the block environment is only needed in generators in case + 'yield' triggers a 'return' */ + push_break_entry(s->cur_func, &block_env, + JS_ATOM_NULL, -1, -1, 2); + block_env.has_iterator = TRUE; + emit_op(s, OP_for_of_start); + has_spread = FALSE; + while (s->token.val != ']') { + /* get the next value */ + if (s->token.val == TOK_ELLIPSIS) { + if (next_token(s)) + return -1; + if (s->token.val == ',' || s->token.val == ']') + return js_parse_error(s, "missing binding pattern..."); + has_spread = TRUE; + } + if (s->token.val == ',') { + /* do nothing, skip the value, has_spread is false */ + emit_op(s, OP_for_of_next); + emit_u8(s, 0); + emit_op(s, OP_drop); + emit_op(s, OP_drop); + } else if ((s->token.val == '[' || s->token.val == '{') + && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' || + tok1 == '=' || tok1 == ']')) { + if (has_spread) { + if (tok1 == '=') + return js_parse_error(s, "rest element cannot have a default value"); + js_emit_spread_code(s, 0); + } else { + emit_op(s, OP_for_of_next); + emit_u8(s, 0); + emit_op(s, OP_drop); + } + if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) + return -1; + } else { + var_name = JS_ATOM_NULL; + enum_depth = 0; + if (tok) { + var_name = js_parse_destructuring_var(s, tok, is_arg); + if (var_name == JS_ATOM_NULL) + goto var_error; + if (js_define_var(s, var_name, tok)) + goto var_error; + opcode = OP_scope_get_var; + scope = s->cur_func->scope_level; + } else { + if (js_parse_left_hand_side_expr(s)) + return -1; + if (get_lvalue(s, &opcode, &scope, &var_name, + &label_lvalue, &enum_depth, FALSE, '[')) { + return -1; + } + } + if (has_spread) { + js_emit_spread_code(s, enum_depth); + } else { + emit_op(s, OP_for_of_next); + emit_u8(s, enum_depth); + emit_op(s, OP_drop); + } + if (s->token.val == '=' && !has_spread) { + /* handle optional default value */ + int label_hasval; + emit_op(s, OP_dup); + emit_op(s, OP_undefined); + emit_op(s, OP_strict_eq); + label_hasval = emit_goto(s, OP_if_false, -1); + if (next_token(s)) + goto var_error; + emit_op(s, OP_drop); + if (js_parse_assign_expr(s)) + goto var_error; + if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) + set_object_name(s, var_name); + emit_label(s, label_hasval); + } + /* store value into lvalue object */ + put_lvalue(s, opcode, scope, var_name, + label_lvalue, PUT_LVALUE_NOKEEP_DEPTH, + (tok == TOK_CONST || tok == TOK_LET)); + } + if (s->token.val == ']') + break; + if (has_spread) + return js_parse_error(s, "rest element must be the last one"); + /* accept a trailing comma before the ']' */ + if (js_parse_expect(s, ',')) + return -1; + } + /* close iterator object: + if completed, enum_obj has been replaced by undefined */ + emit_op(s, OP_iterator_close); + pop_break_entry(s->cur_func); + if (next_token(s)) + return -1; + } else { + return js_parse_error(s, "invalid assignment syntax"); + } + if (s->token.val == '=' && allow_initializer) { + label_done = emit_goto(s, OP_goto, -1); + if (next_token(s)) + return -1; + emit_label(s, label_parse); + if (hasval) + emit_op(s, OP_drop); + if (js_parse_assign_expr(s)) + return -1; + emit_goto(s, OP_goto, label_assign); + emit_label(s, label_done); + has_initializer = TRUE; + } else { + /* normally hasval is true except if + js_parse_skip_parens_token() was wrong in the parsing */ + // assert(hasval); + if (!hasval) { + js_parse_error(s, "too complicated destructuring expression"); + return -1; + } + /* remove test and decrement label ref count */ + memset(s->cur_func->byte_code.buf + start_addr, OP_nop, + assign_addr - start_addr); + s->cur_func->label_slots[label_parse].ref_count--; + has_initializer = FALSE; + } + return has_initializer; + prop_error: + JS_FreeAtom(s->ctx, prop_name); + var_error: + JS_FreeAtom(s->ctx, var_name); + return -1; +} + +static __exception int js_parse_array_literal(JSParseState *s) +{ + uint32_t idx; + BOOL need_length; + if (next_token(s)) + return -1; + /* small regular arrays are created on the stack */ + idx = 0; + while (s->token.val != ']' && idx < 32) { + if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS) + break; + if (js_parse_assign_expr(s)) + return -1; + idx++; + /* accept trailing comma */ + if (s->token.val == ',') { + if (next_token(s)) + return -1; + } else + if (s->token.val != ']') + goto done; + } + emit_op(s, OP_array_from); + emit_u16(s, idx); + /* larger arrays and holes are handled with explicit indices */ + need_length = FALSE; + while (s->token.val != ']' && idx < 0x7fffffff) { + if (s->token.val == TOK_ELLIPSIS) + break; + need_length = TRUE; + if (s->token.val != ',') { + if (js_parse_assign_expr(s)) + return -1; + emit_op(s, OP_define_field); + emit_u32(s, __JS_AtomFromUInt32(idx)); + need_length = FALSE; + } + idx++; + /* accept trailing comma */ + if (s->token.val == ',') { + if (next_token(s)) + return -1; + } + } + if (s->token.val == ']') { + if (need_length) { + /* Set the length: Cannot use OP_define_field because + length is not configurable */ + emit_op(s, OP_dup); + emit_op(s, OP_push_i32); + emit_u32(s, idx); + emit_op(s, OP_put_field); + emit_atom(s, JS_ATOM_length); + } + goto done; + } + /* huge arrays and spread elements require a dynamic index on the stack */ + emit_op(s, OP_push_i32); + emit_u32(s, idx); + /* stack has array, index */ + while (s->token.val != ']') { + if (s->token.val == TOK_ELLIPSIS) { + if (next_token(s)) + return -1; + if (js_parse_assign_expr(s)) + return -1; +#if 1 + emit_op(s, OP_append); +#else + int label_next, label_done; + label_next = new_label(s); + label_done = new_label(s); + /* enumerate object */ + emit_op(s, OP_for_of_start); + emit_op(s, OP_rot5l); + emit_op(s, OP_rot5l); + emit_label(s, label_next); + /* on stack: enum_rec array idx */ + emit_op(s, OP_for_of_next); + emit_u8(s, 2); + emit_goto(s, OP_if_true, label_done); + /* append element */ + /* enum_rec array idx val -> enum_rec array new_idx */ + emit_op(s, OP_define_array_el); + emit_op(s, OP_inc); + emit_goto(s, OP_goto, label_next); + emit_label(s, label_done); + /* close enumeration */ + emit_op(s, OP_drop); /* drop undef val */ + emit_op(s, OP_nip1); /* drop enum_rec */ + emit_op(s, OP_nip1); + emit_op(s, OP_nip1); +#endif + } else { + need_length = TRUE; + if (s->token.val != ',') { + if (js_parse_assign_expr(s)) + return -1; + /* a idx val */ + emit_op(s, OP_define_array_el); + need_length = FALSE; + } + emit_op(s, OP_inc); + } + if (s->token.val != ',') + break; + if (next_token(s)) + return -1; + } + if (need_length) { + /* Set the length: cannot use OP_define_field because + length is not configurable */ + emit_op(s, OP_dup1); /* array length - array array length */ + emit_op(s, OP_put_field); + emit_atom(s, JS_ATOM_length); + } else { + emit_op(s, OP_drop); /* array length - array */ + } +done: + return js_parse_expect(s, ']'); +} + +/* XXX: remove */ +static BOOL has_with_scope(JSFunctionDef *s, int scope_level) +{ + /* check if scope chain contains a with statement */ + while (s) { + int scope_idx = s->scopes[scope_level].first; + while (scope_idx >= 0) { + JSVarDef *vd = &s->vars[scope_idx]; + if (vd->var_name == JS_ATOM__with_) + return TRUE; + scope_idx = vd->scope_next; + } + /* check parent scopes */ + scope_level = s->parent_scope_level; + s = s->parent; + } + return FALSE; +} + +static void optional_chain_test(JSParseState *s, int *poptional_chaining_label, + int drop_count) +{ + int label_next, i; + if (*poptional_chaining_label < 0) + *poptional_chaining_label = new_label(s); + /* XXX: could be more efficient with a specific opcode */ + emit_op(s, OP_dup); + emit_op(s, OP_is_undefined_or_null); + label_next = emit_goto(s, OP_if_false, -1); + for(i = 0; i < drop_count; i++) + emit_op(s, OP_drop); + emit_op(s, OP_undefined); + emit_goto(s, OP_goto, *poptional_chaining_label); + emit_label(s, label_next); +} + +/* initialize the class fields, called by the constructor. Note: + super() can be called in an arrow function, so and + can be variable references */ +static void emit_class_field_init(JSParseState *s) +{ + int label_next; + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_class_fields_init); + emit_u16(s, s->cur_func->scope_level); + /* no need to call the class field initializer if not defined */ + emit_op(s, OP_dup); + label_next = emit_goto(s, OP_if_false, -1); + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + emit_op(s, OP_swap); + emit_op(s, OP_call_method); + emit_u16(s, 0); + emit_label(s, label_next); + emit_op(s, OP_drop); +} + +/* allowed parse_flags: PF_POSTFIX_CALL, PF_ARROW_FUNC */ +static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) +{ + FuncCallType call_type; + int optional_chaining_label; + BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0; + call_type = FUNC_CALL_NORMAL; + switch(s->token.val) { + case TOK_NUMBER: + { + JSValue val; + val = s->token.u.num.val; + if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { + emit_op(s, OP_push_i32); + emit_u32(s, JS_VALUE_GET_INT(val)); + } else +#ifdef CONFIG_BIGNUM + if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { + slimb_t e; + int ret; + /* need a runtime conversion */ + /* XXX: could add a cache and/or do it once at + the start of the function */ + if (emit_push_const(s, val, 0) < 0) + return -1; + e = s->token.u.num.exponent; + if (e == (int32_t)e) { + emit_op(s, OP_push_i32); + emit_u32(s, e); + } else { + val = JS_NewBigInt64_1(s->ctx, e); + if (JS_IsException(val)) + return -1; + ret = emit_push_const(s, val, 0); + JS_FreeValue(s->ctx, val); + if (ret < 0) + return -1; + } + emit_op(s, OP_mul_pow10); + } else +#endif + { + if (emit_push_const(s, val, 0) < 0) + return -1; + } + } + if (next_token(s)) + return -1; + break; + case TOK_TEMPLATE: + if (js_parse_template(s, 0, NULL)) + return -1; + break; + case TOK_STRING: + if (emit_push_const(s, s->token.u.str.str, 1)) + return -1; + if (next_token(s)) + return -1; + break; + case TOK_DIV_ASSIGN: + s->buf_ptr -= 2; + goto parse_regexp; + case '/': + s->buf_ptr--; + parse_regexp: + { + JSValue str; + int ret, backtrace_flags; + if (!s->ctx->compile_regexp) + return js_parse_error(s, "RegExp are not supported"); + /* the previous token is '/' or '/=', so no need to free */ + if (js_parse_regexp(s)) + return -1; + ret = emit_push_const(s, s->token.u.regexp.body, 0); + str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body, + s->token.u.regexp.flags); + if (JS_IsException(str)) { + /* add the line number info */ + backtrace_flags = 0; + if (s->cur_func && s->cur_func->backtrace_barrier) + backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; + build_backtrace(s->ctx, s->ctx->rt->current_exception, + s->filename, s->token.line_num, + backtrace_flags); + return -1; + } + ret = emit_push_const(s, str, 0); + JS_FreeValue(s->ctx, str); + if (ret) + return -1; + /* we use a specific opcode to be sure the correct + function is called (otherwise the bytecode would have + to be verified by the RegExp constructor) */ + emit_op(s, OP_regexp); + if (next_token(s)) + return -1; + } + break; + case '(': + if ((parse_flags & PF_ARROW_FUNC) && + js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) { + if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, s->token.line_num)) + return -1; + } else { + if (js_parse_expr_paren(s)) + return -1; + } + break; + case TOK_FUNCTION: + if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, s->token.line_num)) + return -1; + break; + case TOK_CLASS: + if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE)) + return -1; + break; + case TOK_NULL: + if (next_token(s)) + return -1; + emit_op(s, OP_null); + break; + case TOK_THIS: + if (next_token(s)) + return -1; + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + break; + case TOK_FALSE: + if (next_token(s)) + return -1; + emit_op(s, OP_push_false); + break; + case TOK_TRUE: + if (next_token(s)) + return -1; + emit_op(s, OP_push_true); + break; + case TOK_IDENT: + { + JSAtom name; + if (s->token.u.ident.is_reserved) { + return js_parse_error_reserved_identifier(s); + } + if ((parse_flags & PF_ARROW_FUNC) && + peek_token(s, TRUE) == TOK_ARROW) { + if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, s->token.line_num)) + return -1; + } else if (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) != '\n') { + const uint8_t *source_ptr; + int source_line_num; + source_ptr = s->token.ptr; + source_line_num = s->token.line_num; + if (next_token(s)) + return -1; + if (s->token.val == TOK_FUNCTION) { + if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, + JS_FUNC_ASYNC, JS_ATOM_NULL, + source_ptr, source_line_num)) + return -1; + } else if ((parse_flags & PF_ARROW_FUNC) && + ((s->token.val == '(' && + js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) || + (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && + peek_token(s, TRUE) == TOK_ARROW))) { + if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, + JS_FUNC_ASYNC, JS_ATOM_NULL, + source_ptr, source_line_num)) + return -1; + } else { + name = JS_DupAtom(s->ctx, JS_ATOM_async); + goto do_get_var; + } + } else { + if (s->token.u.ident.atom == JS_ATOM_arguments && + !s->cur_func->arguments_allowed) { + js_parse_error(s, "'arguments' identifier is not allowed in class field initializer"); + return -1; + } + name = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (next_token(s)) /* update line number before emitting code */ + return -1; + do_get_var: + emit_op(s, OP_scope_get_var); + emit_u32(s, name); + emit_u16(s, s->cur_func->scope_level); + } + } + break; + case '{': + case '[': + { + int skip_bits; + if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { + if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) + return -1; + } else { + if (s->token.val == '{') { + if (js_parse_object_literal(s)) + return -1; + } else { + if (js_parse_array_literal(s)) + return -1; + } + } + } + break; + case TOK_NEW: + if (next_token(s)) + return -1; + if (s->token.val == '.') { + if (next_token(s)) + return -1; + if (!token_is_pseudo_keyword(s, JS_ATOM_target)) + return js_parse_error(s, "expecting target"); + if (!s->cur_func->new_target_allowed) + return js_parse_error(s, "new.target only allowed within functions"); + if (next_token(s)) + return -1; + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_new_target); + emit_u16(s, 0); + } else { + if (js_parse_postfix_expr(s, 0)) + return -1; + accept_lparen = TRUE; + if (s->token.val != '(') { + /* new operator on an object */ + emit_op(s, OP_dup); + emit_op(s, OP_call_constructor); + emit_u16(s, 0); + } else { + call_type = FUNC_CALL_NEW; + } + } + break; + case TOK_SUPER: + if (next_token(s)) + return -1; + if (s->token.val == '(') { + if (!s->cur_func->super_call_allowed) + return js_parse_error(s, "super() is only valid in a derived class constructor"); + call_type = FUNC_CALL_SUPER_CTOR; + } else if (s->token.val == '.' || s->token.val == '[') { + if (!s->cur_func->super_allowed) + return js_parse_error(s, "'super' is only valid in a method"); + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_home_object); + emit_u16(s, 0); + emit_op(s, OP_get_super); + } else { + return js_parse_error(s, "invalid use of 'super'"); + } + break; + case TOK_IMPORT: + if (next_token(s)) + return -1; + if (s->token.val == '.') { + if (next_token(s)) + return -1; + if (!token_is_pseudo_keyword(s, JS_ATOM_meta)) + return js_parse_error(s, "meta expected"); + if (!s->is_module) + return js_parse_error(s, "import.meta only valid in module code"); + if (next_token(s)) + return -1; + emit_op(s, OP_special_object); + emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META); + } else { + if (js_parse_expect(s, '(')) + return -1; + if (!accept_lparen) + return js_parse_error(s, "invalid use of 'import()'"); + if (js_parse_assign_expr(s)) + return -1; + if (js_parse_expect(s, ')')) + return -1; + emit_op(s, OP_import); + } + break; + default: + return js_parse_error(s, "unexpected token in expression: '%.*s'", + (int)(s->buf_ptr - s->token.ptr), s->token.ptr); + } + optional_chaining_label = -1; + for(;;) { + JSFunctionDef *fd = s->cur_func; + BOOL has_optional_chain = FALSE; + if (s->token.val == TOK_QUESTION_MARK_DOT) { + /* optional chaining */ + if (next_token(s)) + return -1; + has_optional_chain = TRUE; + if (s->token.val == '(' && accept_lparen) { + goto parse_func_call; + } else if (s->token.val == '[') { + goto parse_array_access; + } else { + goto parse_property; + } + } else if (s->token.val == TOK_TEMPLATE && + call_type == FUNC_CALL_NORMAL) { + if (optional_chaining_label >= 0) { + return js_parse_error(s, "template literal cannot appear in an optional chain"); + } + call_type = FUNC_CALL_TEMPLATE; + goto parse_func_call2; + } else if (s->token.val == '(' && accept_lparen) { + int opcode, arg_count, drop_count; + /* function call */ + parse_func_call: + if (next_token(s)) + return -1; + if (call_type == FUNC_CALL_NORMAL) { + parse_func_call2: + switch(opcode = get_prev_opcode(fd)) { + case OP_get_field: + /* keep the object on the stack */ + fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2; + drop_count = 2; + break; + case OP_scope_get_private_field: + /* keep the object on the stack */ + fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2; + drop_count = 2; + break; + case OP_get_array_el: + /* keep the object on the stack */ + fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2; + drop_count = 2; + break; + case OP_scope_get_var: + { + JSAtom name; + int scope; + name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); + if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) { + /* direct 'eval' */ + opcode = OP_eval; + } else { + /* verify if function name resolves to a simple + get_loc/get_arg: a function call inside a `with` + statement can resolve to a method call of the + `with` context object + */ + /* XXX: always generate the OP_scope_get_ref + and remove it in variable resolution + pass ? */ + if (has_with_scope(fd, scope)) { + opcode = OP_scope_get_ref; + fd->byte_code.buf[fd->last_opcode_pos] = opcode; + } + } + drop_count = 1; + } + break; + case OP_get_super_value: + fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el; + /* on stack: this func_obj */ + opcode = OP_get_array_el; + drop_count = 2; + break; + default: + opcode = OP_invalid; + drop_count = 1; + break; + } + if (has_optional_chain) { + optional_chain_test(s, &optional_chaining_label, + drop_count); + } + } else { + opcode = OP_invalid; + } + if (call_type == FUNC_CALL_TEMPLATE) { + if (js_parse_template(s, 1, &arg_count)) + return -1; + goto emit_func_call; + } else if (call_type == FUNC_CALL_SUPER_CTOR) { + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this_active_func); + emit_u16(s, 0); + emit_op(s, OP_get_super); + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_new_target); + emit_u16(s, 0); + } else if (call_type == FUNC_CALL_NEW) { + emit_op(s, OP_dup); /* new.target = function */ + } + /* parse arguments */ + arg_count = 0; + while (s->token.val != ')') { + if (arg_count >= 65535) { + return js_parse_error(s, "Too many call arguments"); + } + if (s->token.val == TOK_ELLIPSIS) + break; + if (js_parse_assign_expr(s)) + return -1; + arg_count++; + if (s->token.val == ')') + break; + /* accept a trailing comma before the ')' */ + if (js_parse_expect(s, ',')) + return -1; + } + if (s->token.val == TOK_ELLIPSIS) { + emit_op(s, OP_array_from); + emit_u16(s, arg_count); + emit_op(s, OP_push_i32); + emit_u32(s, arg_count); + /* on stack: array idx */ + while (s->token.val != ')') { + if (s->token.val == TOK_ELLIPSIS) { + if (next_token(s)) + return -1; + if (js_parse_assign_expr(s)) + return -1; +#if 1 + /* XXX: could pass is_last indicator? */ + emit_op(s, OP_append); +#else + int label_next, label_done; + label_next = new_label(s); + label_done = new_label(s); + /* push enumerate object below array/idx pair */ + emit_op(s, OP_for_of_start); + emit_op(s, OP_rot5l); + emit_op(s, OP_rot5l); + emit_label(s, label_next); + /* on stack: enum_rec array idx */ + emit_op(s, OP_for_of_next); + emit_u8(s, 2); + emit_goto(s, OP_if_true, label_done); + /* append element */ + /* enum_rec array idx val -> enum_rec array new_idx */ + emit_op(s, OP_define_array_el); + emit_op(s, OP_inc); + emit_goto(s, OP_goto, label_next); + emit_label(s, label_done); + /* close enumeration, drop enum_rec and idx */ + emit_op(s, OP_drop); /* drop undef */ + emit_op(s, OP_nip1); /* drop enum_rec */ + emit_op(s, OP_nip1); + emit_op(s, OP_nip1); +#endif + } else { + if (js_parse_assign_expr(s)) + return -1; + /* array idx val */ + emit_op(s, OP_define_array_el); + emit_op(s, OP_inc); + } + if (s->token.val == ')') + break; + /* accept a trailing comma before the ')' */ + if (js_parse_expect(s, ',')) + return -1; + } + if (next_token(s)) + return -1; + /* drop the index */ + emit_op(s, OP_drop); + /* apply function call */ + switch(opcode) { + case OP_get_field: + case OP_scope_get_private_field: + case OP_get_array_el: + case OP_scope_get_ref: + /* obj func array -> func obj array */ + emit_op(s, OP_perm3); + emit_op(s, OP_apply); + emit_u16(s, call_type == FUNC_CALL_NEW); + break; + case OP_eval: + emit_op(s, OP_apply_eval); + emit_u16(s, fd->scope_level); + fd->has_eval_call = TRUE; + break; + default: + if (call_type == FUNC_CALL_SUPER_CTOR) { + emit_op(s, OP_apply); + emit_u16(s, 1); + /* set the 'this' value */ + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + emit_class_field_init(s); + } else if (call_type == FUNC_CALL_NEW) { + /* obj func array -> func obj array */ + emit_op(s, OP_perm3); + emit_op(s, OP_apply); + emit_u16(s, 1); + } else { + /* func array -> func undef array */ + emit_op(s, OP_undefined); + emit_op(s, OP_swap); + emit_op(s, OP_apply); + emit_u16(s, 0); + } + break; + } + } else { + if (next_token(s)) + return -1; + emit_func_call: + switch(opcode) { + case OP_get_field: + case OP_scope_get_private_field: + case OP_get_array_el: + case OP_scope_get_ref: + emit_op(s, OP_call_method); + emit_u16(s, arg_count); + break; + case OP_eval: + emit_op(s, OP_eval); + emit_u16(s, arg_count); + emit_u16(s, fd->scope_level); + fd->has_eval_call = TRUE; + break; + default: + if (call_type == FUNC_CALL_SUPER_CTOR) { + emit_op(s, OP_call_constructor); + emit_u16(s, arg_count); + /* set the 'this' value */ + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + emit_class_field_init(s); + } else if (call_type == FUNC_CALL_NEW) { + emit_op(s, OP_call_constructor); + emit_u16(s, arg_count); + } else { + emit_op(s, OP_call); + emit_u16(s, arg_count); + } + break; + } + } + call_type = FUNC_CALL_NORMAL; + } else if (s->token.val == '.') { + if (next_token(s)) + return -1; + parse_property: + if (s->token.val == TOK_PRIVATE_NAME) { + /* private class field */ + if (get_prev_opcode(fd) == OP_get_super) { + return js_parse_error(s, "private class field forbidden after super"); + } + if (has_optional_chain) { + optional_chain_test(s, &optional_chaining_label, 1); + } + emit_op(s, OP_scope_get_private_field); + emit_atom(s, s->token.u.ident.atom); + emit_u16(s, s->cur_func->scope_level); + } else { + if (!token_is_ident(s->token.val)) { + return js_parse_error(s, "expecting field name"); + } + if (get_prev_opcode(fd) == OP_get_super) { + JSValue val; + int ret; + val = JS_AtomToValue(s->ctx, s->token.u.ident.atom); + ret = emit_push_const(s, val, 1); + JS_FreeValue(s->ctx, val); + if (ret) + return -1; + emit_op(s, OP_get_super_value); + } else { + if (has_optional_chain) { + optional_chain_test(s, &optional_chaining_label, 1); + } + emit_op(s, OP_get_field); + emit_atom(s, s->token.u.ident.atom); + } + } + if (next_token(s)) + return -1; + } else if (s->token.val == '[') { + int prev_op; + parse_array_access: + prev_op = get_prev_opcode(fd); + if (has_optional_chain) { + optional_chain_test(s, &optional_chaining_label, 1); + } + if (next_token(s)) + return -1; + if (js_parse_expr(s)) + return -1; + if (js_parse_expect(s, ']')) + return -1; + if (prev_op == OP_get_super) { + emit_op(s, OP_get_super_value); + } else { + emit_op(s, OP_get_array_el); + } + } else { + break; + } + } + if (optional_chaining_label >= 0) + emit_label(s, optional_chaining_label); + return 0; +} + +static int js_unsupported_keyword(JSParseState *s, JSAtom atom) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return js_parse_error(s, "unsupported keyword: %s", + JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom)); +} + +/* XXX: handle IteratorClose when exiting the loop before the + enumeration is done */ +static __exception int js_parse_for_in_of(JSParseState *s, int label_name, + BOOL is_async) +{ + JSContext *ctx = s->ctx; + JSFunctionDef *fd = s->cur_func; + JSAtom var_name; + BOOL has_initializer, is_for_of, has_destructuring; + int tok, tok1, opcode, scope, block_scope_level; + int label_next, label_expr, label_cont, label_body, label_break; + int pos_next, pos_expr; + BlockEnv break_entry; + has_initializer = FALSE; + has_destructuring = FALSE; + is_for_of = FALSE; + block_scope_level = fd->scope_level; + label_cont = new_label(s); + label_body = new_label(s); + label_break = new_label(s); + label_next = new_label(s); + /* create scope for the lexical variables declared in the enumeration + expressions. XXX: Not completely correct because of weird capturing + semantics in `for (i of o) a.push(function(){return i})` */ + push_scope(s); + /* local for_in scope starts here so individual elements + can be closed in statement. */ + push_break_entry(s->cur_func, &break_entry, + label_name, label_break, label_cont, 1); + break_entry.scope_level = block_scope_level; + label_expr = emit_goto(s, OP_goto, -1); + pos_next = s->cur_func->byte_code.size; + emit_label(s, label_next); + tok = s->token.val; + switch (is_let(s, DECL_MASK_OTHER)) { + case TRUE: + tok = TOK_LET; + break; + case FALSE: + break; + default: + return -1; + } + if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) { + if (next_token(s)) + return -1; + if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { + if (s->token.val == '[' || s->token.val == '{') { + if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0) + return -1; + has_destructuring = TRUE; + } else { + return js_parse_error(s, "variable name expected"); + } + var_name = JS_ATOM_NULL; + } else { + var_name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s)) { + JS_FreeAtom(s->ctx, var_name); + return -1; + } + if (js_define_var(s, var_name, tok)) { + JS_FreeAtom(s->ctx, var_name); + return -1; + } + emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ? + OP_scope_put_var_init : OP_scope_put_var); + emit_atom(s, var_name); + emit_u16(s, fd->scope_level); + } + } else { + int skip_bits; + if ((s->token.val == '[' || s->token.val == '{') + && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) { + if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) + return -1; + } else { + int lvalue_label; + if (js_parse_left_hand_side_expr(s)) + return -1; + if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label, + NULL, FALSE, TOK_FOR)) + return -1; + put_lvalue(s, opcode, scope, var_name, lvalue_label, + PUT_LVALUE_NOKEEP_BOTTOM, FALSE); + } + var_name = JS_ATOM_NULL; + } + emit_goto(s, OP_goto, label_body); + pos_expr = s->cur_func->byte_code.size; + emit_label(s, label_expr); + if (s->token.val == '=') { + /* XXX: potential scoping issue if inside `with` statement */ + has_initializer = TRUE; + /* parse and evaluate initializer prior to evaluating the + object (only used with "for in" with a non lexical variable + in non strict mode */ + if (next_token(s) || js_parse_assign_expr2(s, 0)) { + JS_FreeAtom(ctx, var_name); + return -1; + } + if (var_name != JS_ATOM_NULL) { + emit_op(s, OP_scope_put_var); + emit_atom(s, var_name); + emit_u16(s, fd->scope_level); + } + } + JS_FreeAtom(ctx, var_name); + if (token_is_pseudo_keyword(s, JS_ATOM_of)) { + break_entry.has_iterator = is_for_of = TRUE; + break_entry.drop_count += 2; + if (has_initializer) + goto initializer_error; + } else if (s->token.val == TOK_IN) { + if (is_async) + return js_parse_error(s, "'for await' loop should be used with 'of'"); + if (has_initializer && + (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) || + has_destructuring)) { + initializer_error: + return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer", + is_for_of ? "of" : "in"); + } + } else { + return js_parse_error(s, "expected 'of' or 'in' in for control expression"); + } + if (next_token(s)) + return -1; + if (is_for_of) { + if (js_parse_assign_expr(s)) + return -1; + } else { + if (js_parse_expr(s)) + return -1; + } + /* close the scope after having evaluated the expression so that + the TDZ values are in the closures */ + close_scopes(s, s->cur_func->scope_level, block_scope_level); + if (is_for_of) { + if (is_async) + emit_op(s, OP_for_await_of_start); + else + emit_op(s, OP_for_of_start); + /* on stack: enum_rec */ + } else { + emit_op(s, OP_for_in_start); + /* on stack: enum_obj */ + } + emit_goto(s, OP_goto, label_cont); + if (js_parse_expect(s, ')')) + return -1; + if (OPTIMIZE) { + /* move the `next` code here */ + DynBuf *bc = &s->cur_func->byte_code; + int chunk_size = pos_expr - pos_next; + int offset = bc->size - pos_next; + int i; + dbuf_realloc(bc, bc->size + chunk_size); + dbuf_put(bc, bc->buf + pos_next, chunk_size); + memset(bc->buf + pos_next, OP_nop, chunk_size); + /* `next` part ends with a goto */ + s->cur_func->last_opcode_pos = bc->size - 5; + /* relocate labels */ + for (i = label_cont; i < s->cur_func->label_count; i++) { + LabelSlot *ls = &s->cur_func->label_slots[i]; + if (ls->pos >= pos_next && ls->pos < pos_expr) + ls->pos += offset; + } + } + emit_label(s, label_body); + if (js_parse_statement(s)) + return -1; + close_scopes(s, s->cur_func->scope_level, block_scope_level); + emit_label(s, label_cont); + if (is_for_of) { + if (is_async) { + /* call the next method */ + /* stack: iter_obj next catch_offset */ + emit_op(s, OP_dup3); + emit_op(s, OP_drop); + emit_op(s, OP_call_method); + emit_u16(s, 0); + /* get the result of the promise */ + emit_op(s, OP_await); + /* unwrap the value and done values */ + emit_op(s, OP_iterator_get_value_done); + } else { + emit_op(s, OP_for_of_next); + emit_u8(s, 0); + } + } else { + emit_op(s, OP_for_in_next); + } + /* on stack: enum_rec / enum_obj value bool */ + emit_goto(s, OP_if_false, label_next); + /* drop the undefined value from for_xx_next */ + emit_op(s, OP_drop); + emit_label(s, label_break); + if (is_for_of) { + /* close and drop enum_rec */ + emit_op(s, OP_iterator_close); + } else { + emit_op(s, OP_drop); + } + pop_break_entry(s->cur_func); + pop_scope(s); + return 0; +} + +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok, + BOOL export_flag) +{ + JSContext *ctx = s->ctx; + JSFunctionDef *fd = s->cur_func; + JSAtom name = JS_ATOM_NULL; + for (;;) { + if (s->token.val == TOK_IDENT) { + if (s->token.u.ident.is_reserved) { + return js_parse_error_reserved_identifier(s); + } + name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) { + js_parse_error(s, "'let' is not a valid lexical identifier"); + goto var_error; + } + if (next_token(s)) + goto var_error; + if (js_define_var(s, name, tok)) + goto var_error; + if (export_flag) { + if (!add_export_entry(s, s->cur_func->module, name, name, + JS_EXPORT_TYPE_LOCAL)) + goto var_error; + } + if (s->token.val == '=') { + if (next_token(s)) + goto var_error; + if (tok == TOK_VAR) { + /* Must make a reference for proper `with` semantics */ + int opcode, scope, label; + JSAtom name1; + emit_op(s, OP_scope_get_var); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0) + goto var_error; + if (js_parse_assign_expr2(s, parse_flags)) { + JS_FreeAtom(ctx, name1); + goto var_error; + } + set_object_name(s, name); + put_lvalue(s, opcode, scope, name1, label, + PUT_LVALUE_NOKEEP, FALSE); + } else { + if (js_parse_assign_expr2(s, parse_flags)) + goto var_error; + set_object_name(s, name); + emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ? + OP_scope_put_var_init : OP_scope_put_var); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } + } else { + if (tok == TOK_CONST) { + js_parse_error(s, "missing initializer for const variable"); + goto var_error; + } + if (tok == TOK_LET) { + /* initialize lexical variable upon entering its scope */ + emit_op(s, OP_undefined); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } + } + JS_FreeAtom(ctx, name); + } else { + int skip_bits; + if ((s->token.val == '[' || s->token.val == '{') + && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { + emit_op(s, OP_undefined); + if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) + return -1; + } else { + return js_parse_error(s, "variable name expected"); + } + } + if (s->token.val != ',') + break; + if (next_token(s)) + return -1; + } + return 0; + var_error: + JS_FreeAtom(ctx, name); + return -1; +} + +/* execute the finally blocks before return */ +static void emit_return(JSParseState *s, BOOL hasval) +{ + BlockEnv *top; + int drop_count; + drop_count = 0; + top = s->cur_func->top_break; + while (top != NULL) { + /* XXX: emit the appropriate OP_leave_scope opcodes? Probably not + required as all local variables will be closed upon returning + from JS_CallInternal, but not in the same order. */ + if (top->has_iterator) { + /* with 'yield', the exact number of OP_drop to emit is + unknown, so we use a specific operation to look for + the catch offset */ + if (!hasval) { + emit_op(s, OP_undefined); + hasval = TRUE; + } + emit_op(s, OP_iterator_close_return); + if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { + int label_next, label_next2; + emit_op(s, OP_drop); /* catch offset */ + emit_op(s, OP_drop); /* next */ + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_return); + /* stack: iter_obj return_func */ + emit_op(s, OP_dup); + emit_op(s, OP_is_undefined_or_null); + label_next = emit_goto(s, OP_if_true, -1); + emit_op(s, OP_call_method); + emit_u16(s, 0); + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_await); + label_next2 = emit_goto(s, OP_goto, -1); + emit_label(s, label_next); + emit_op(s, OP_drop); + emit_label(s, label_next2); + emit_op(s, OP_drop); + } else { + emit_op(s, OP_iterator_close); + } + drop_count = -3; + } + drop_count += top->drop_count; + if (top->label_finally != -1) { + while(drop_count) { + /* must keep the stack top if hasval */ + emit_op(s, hasval ? OP_nip : OP_drop); + drop_count--; + } + if (!hasval) { + /* must push return value to keep same stack size */ + emit_op(s, OP_undefined); + hasval = TRUE; + } + emit_goto(s, OP_gosub, top->label_finally); + } + top = top->prev; + } + if (s->cur_func->is_derived_class_constructor) { + int label_return; + /* 'this' can be uninitialized, so it may be accessed only if + the derived class constructor does not return an object */ + if (hasval) { + emit_op(s, OP_check_ctor_return); + label_return = emit_goto(s, OP_if_false, -1); + emit_op(s, OP_drop); + } else { + label_return = -1; + } + /* XXX: if this is not initialized, should throw the + ReferenceError in the caller realm */ + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + emit_label(s, label_return); + emit_op(s, OP_return); + } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) { + if (!hasval) { + emit_op(s, OP_undefined); + } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { + emit_op(s, OP_await); + } + emit_op(s, OP_return_async); + } else { + emit_op(s, hasval ? OP_return : OP_return_undef); + } +} + +static void set_eval_ret_undefined(JSParseState *s) +{ + if (s->cur_func->eval_ret_idx >= 0) { + emit_op(s, OP_undefined); + emit_op(s, OP_put_loc); + emit_u16(s, s->cur_func->eval_ret_idx); + } +} + +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_expr2(JSParseState *s, int parse_flags) +{ + BOOL comma = FALSE; + for(;;) { + if (js_parse_assign_expr2(s, parse_flags)) + return -1; + if (comma) { + /* prevent get_lvalue from using the last expression + as an lvalue. This also prevents the conversion of + of get_var to get_ref for method lookup in function + call inside `with` statement. + */ + s->cur_func->last_opcode_pos = -1; + } + if (s->token.val != ',') + break; + comma = TRUE; + if (next_token(s)) + return -1; + emit_op(s, OP_drop); + } + return 0; +} + +static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont) +{ + BlockEnv *top; + int i, scope_level; + scope_level = s->cur_func->scope_level; + top = s->cur_func->top_break; + while (top != NULL) { + close_scopes(s, scope_level, top->scope_level); + scope_level = top->scope_level; + if (is_cont && + top->label_cont != -1 && + (name == JS_ATOM_NULL || top->label_name == name)) { + /* continue stays inside the same block */ + emit_goto(s, OP_goto, top->label_cont); + return 0; + } + if (!is_cont && + top->label_break != -1 && + (name == JS_ATOM_NULL || top->label_name == name)) { + emit_goto(s, OP_goto, top->label_break); + return 0; + } + i = 0; + if (top->has_iterator) { + emit_op(s, OP_iterator_close); + i += 3; + } + for(; i < top->drop_count; i++) + emit_op(s, OP_drop); + if (top->label_finally != -1) { + /* must push dummy value to keep same stack depth */ + emit_op(s, OP_undefined); + emit_goto(s, OP_gosub, top->label_finally); + emit_op(s, OP_drop); + } + top = top->prev; + } + if (name == JS_ATOM_NULL) { + if (is_cont) + return js_parse_error(s, "continue must be inside loop"); + else + return js_parse_error(s, "break must be inside loop or switch"); + } else { + return js_parse_error(s, "break/continue label not found"); + } +} + +static int js_parse_statement_or_decl(JSParseState *s, int decl_mask) +{ + JSContext *ctx = s->ctx; + JSAtom label_name; + int tok; + /* specific label handling */ + /* XXX: support multiple labels on loop statements */ + label_name = JS_ATOM_NULL; + if (is_label(s)) { + BlockEnv *be; + label_name = JS_DupAtom(ctx, s->token.u.ident.atom); + for (be = s->cur_func->top_break; be; be = be->prev) { + if (be->label_name == label_name) { + js_parse_error(s, "duplicate label name"); + goto fail; + } + } + if (next_token(s)) + goto fail; + if (js_parse_expect(s, ':')) + goto fail; + if (s->token.val != TOK_FOR + && s->token.val != TOK_DO + && s->token.val != TOK_WHILE) { + /* labelled regular statement */ + int label_break, mask; + BlockEnv break_entry; + label_break = new_label(s); + push_break_entry(s->cur_func, &break_entry, + label_name, label_break, -1, 0); + if (!(s->cur_func->js_mode & JS_MODE_STRICT) && + (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) { + mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL; + } else { + mask = 0; + } + if (js_parse_statement_or_decl(s, mask)) + goto fail; + emit_label(s, label_break); + pop_break_entry(s->cur_func); + goto done; + } + } + switch(tok = s->token.val) { + case '{': + if (js_parse_block(s)) + goto fail; + break; + case TOK_RETURN: + if (s->cur_func->is_eval) { + js_parse_error(s, "return not in a function"); + goto fail; + } + if (next_token(s)) + goto fail; + if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) { + if (js_parse_expr(s)) + goto fail; + emit_return(s, TRUE); + } else { + emit_return(s, FALSE); + } + if (js_parse_expect_semi(s)) + goto fail; + break; + case TOK_THROW: + if (next_token(s)) + goto fail; + if (s->got_lf) { + js_parse_error(s, "line terminator not allowed after throw"); + goto fail; + } + if (js_parse_expr(s)) + goto fail; + emit_op(s, OP_throw); + if (js_parse_expect_semi(s)) + goto fail; + break; + case TOK_LET: + case TOK_CONST: + haslet: + if (!(decl_mask & DECL_MASK_OTHER)) { + js_parse_error(s, "lexical declarations can't appear in single-statement context"); + goto fail; + } + /* fall thru */ + case TOK_VAR: + if (next_token(s)) + goto fail; + if (js_parse_var(s, TRUE, tok, FALSE)) + goto fail; + if (js_parse_expect_semi(s)) + goto fail; + break; + case TOK_IF: + { + int label1, label2, mask; + if (next_token(s)) + goto fail; + /* create a new scope for `let f;if(1) function f(){}` */ + push_scope(s); + set_eval_ret_undefined(s); + if (js_parse_expr_paren(s)) + goto fail; + label1 = emit_goto(s, OP_if_false, -1); + if (s->cur_func->js_mode & JS_MODE_STRICT) + mask = 0; + else + mask = DECL_MASK_FUNC; /* Annex B.3.4 */ + if (js_parse_statement_or_decl(s, mask)) + goto fail; + if (s->token.val == TOK_ELSE) { + label2 = emit_goto(s, OP_goto, -1); + if (next_token(s)) + goto fail; + emit_label(s, label1); + if (js_parse_statement_or_decl(s, mask)) + goto fail; + label1 = label2; + } + emit_label(s, label1); + pop_scope(s); + } + break; + case TOK_WHILE: + { + int label_cont, label_break; + BlockEnv break_entry; + label_cont = new_label(s); + label_break = new_label(s); + push_break_entry(s->cur_func, &break_entry, + label_name, label_break, label_cont, 0); + if (next_token(s)) + goto fail; + set_eval_ret_undefined(s); + emit_label(s, label_cont); + if (js_parse_expr_paren(s)) + goto fail; + emit_goto(s, OP_if_false, label_break); + if (js_parse_statement(s)) + goto fail; + emit_goto(s, OP_goto, label_cont); + emit_label(s, label_break); + pop_break_entry(s->cur_func); + } + break; + case TOK_DO: + { + int label_cont, label_break, label1; + BlockEnv break_entry; + label_cont = new_label(s); + label_break = new_label(s); + label1 = new_label(s); + push_break_entry(s->cur_func, &break_entry, + label_name, label_break, label_cont, 0); + if (next_token(s)) + goto fail; + emit_label(s, label1); + set_eval_ret_undefined(s); + if (js_parse_statement(s)) + goto fail; + emit_label(s, label_cont); + if (js_parse_expect(s, TOK_WHILE)) + goto fail; + if (js_parse_expr_paren(s)) + goto fail; + /* Insert semicolon if missing */ + if (s->token.val == ';') { + if (next_token(s)) + goto fail; + } + emit_goto(s, OP_if_true, label1); + emit_label(s, label_break); + pop_break_entry(s->cur_func); + } + break; + case TOK_FOR: + { + int label_cont, label_break, label_body, label_test; + int pos_cont, pos_body, block_scope_level; + BlockEnv break_entry; + int tok, bits; + BOOL is_async; + if (next_token(s)) + goto fail; + set_eval_ret_undefined(s); + bits = 0; + is_async = FALSE; + if (s->token.val == '(') { + js_parse_skip_parens_token(s, &bits, FALSE); + } else if (s->token.val == TOK_AWAIT) { + if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) { + js_parse_error(s, "for await is only valid in asynchronous functions"); + goto fail; + } + is_async = TRUE; + if (next_token(s)) + goto fail; + } + if (js_parse_expect(s, '(')) + goto fail; + if (!(bits & SKIP_HAS_SEMI)) { + /* parse for/in or for/of */ + if (js_parse_for_in_of(s, label_name, is_async)) + goto fail; + break; + } + block_scope_level = s->cur_func->scope_level; + /* create scope for the lexical variables declared in the initial, + test and increment expressions */ + push_scope(s); + /* initial expression */ + tok = s->token.val; + if (tok != ';') { + switch (is_let(s, DECL_MASK_OTHER)) { + case TRUE: + tok = TOK_LET; + break; + case FALSE: + break; + default: + goto fail; + } + if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) { + if (next_token(s)) + goto fail; + if (js_parse_var(s, FALSE, tok, FALSE)) + goto fail; + } else { + if (js_parse_expr2(s, FALSE)) + goto fail; + emit_op(s, OP_drop); + } + /* close the closures before the first iteration */ + close_scopes(s, s->cur_func->scope_level, block_scope_level); + } + if (js_parse_expect(s, ';')) + goto fail; + label_test = new_label(s); + label_cont = new_label(s); + label_body = new_label(s); + label_break = new_label(s); + push_break_entry(s->cur_func, &break_entry, + label_name, label_break, label_cont, 0); + /* test expression */ + if (s->token.val == ';') { + /* no test expression */ + label_test = label_body; + } else { + emit_label(s, label_test); + if (js_parse_expr(s)) + goto fail; + emit_goto(s, OP_if_false, label_break); + } + if (js_parse_expect(s, ';')) + goto fail; + if (s->token.val == ')') { + /* no end expression */ + break_entry.label_cont = label_cont = label_test; + pos_cont = 0; /* avoid warning */ + } else { + /* skip the end expression */ + emit_goto(s, OP_goto, label_body); + pos_cont = s->cur_func->byte_code.size; + emit_label(s, label_cont); + if (js_parse_expr(s)) + goto fail; + emit_op(s, OP_drop); + if (label_test != label_body) + emit_goto(s, OP_goto, label_test); + } + if (js_parse_expect(s, ')')) + goto fail; + pos_body = s->cur_func->byte_code.size; + emit_label(s, label_body); + if (js_parse_statement(s)) + goto fail; + /* close the closures before the next iteration */ + /* XXX: check continue case */ + close_scopes(s, s->cur_func->scope_level, block_scope_level); + if (OPTIMIZE && label_test != label_body && label_cont != label_test) { + /* move the increment code here */ + DynBuf *bc = &s->cur_func->byte_code; + int chunk_size = pos_body - pos_cont; + int offset = bc->size - pos_cont; + int i; + dbuf_realloc(bc, bc->size + chunk_size); + dbuf_put(bc, bc->buf + pos_cont, chunk_size); + memset(bc->buf + pos_cont, OP_nop, chunk_size); + /* increment part ends with a goto */ + s->cur_func->last_opcode_pos = bc->size - 5; + /* relocate labels */ + for (i = label_cont; i < s->cur_func->label_count; i++) { + LabelSlot *ls = &s->cur_func->label_slots[i]; + if (ls->pos >= pos_cont && ls->pos < pos_body) + ls->pos += offset; + } + } else { + emit_goto(s, OP_goto, label_cont); + } + emit_label(s, label_break); + pop_break_entry(s->cur_func); + pop_scope(s); + } + break; + case TOK_BREAK: + case TOK_CONTINUE: + { + int is_cont = s->token.val - TOK_BREAK; + int label; + if (next_token(s)) + goto fail; + if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) + label = s->token.u.ident.atom; + else + label = JS_ATOM_NULL; + if (emit_break(s, label, is_cont)) + goto fail; + if (label != JS_ATOM_NULL) { + if (next_token(s)) + goto fail; + } + if (js_parse_expect_semi(s)) + goto fail; + } + break; + case TOK_SWITCH: + { + int label_case, label_break, label1; + int default_label_pos; + BlockEnv break_entry; + if (next_token(s)) + goto fail; + set_eval_ret_undefined(s); + if (js_parse_expr_paren(s)) + goto fail; + push_scope(s); + label_break = new_label(s); + push_break_entry(s->cur_func, &break_entry, + label_name, label_break, -1, 1); + if (js_parse_expect(s, '{')) + goto fail; + default_label_pos = -1; + label_case = -1; + while (s->token.val != '}') { + if (s->token.val == TOK_CASE) { + label1 = -1; + if (label_case >= 0) { + /* skip the case if needed */ + label1 = emit_goto(s, OP_goto, -1); + } + emit_label(s, label_case); + label_case = -1; + for (;;) { + /* parse a sequence of case clauses */ + if (next_token(s)) + goto fail; + emit_op(s, OP_dup); + if (js_parse_expr(s)) + goto fail; + if (js_parse_expect(s, ':')) + goto fail; + emit_op(s, OP_strict_eq); + if (s->token.val == TOK_CASE) { + label1 = emit_goto(s, OP_if_true, label1); + } else { + label_case = emit_goto(s, OP_if_false, -1); + emit_label(s, label1); + break; + } + } + } else if (s->token.val == TOK_DEFAULT) { + if (next_token(s)) + goto fail; + if (js_parse_expect(s, ':')) + goto fail; + if (default_label_pos >= 0) { + js_parse_error(s, "duplicate default"); + goto fail; + } + if (label_case < 0) { + /* falling thru direct from switch expression */ + label_case = emit_goto(s, OP_goto, -1); + } + /* Emit a dummy label opcode. Label will be patched after + the end of the switch body. Do not use emit_label(s, 0) + because it would clobber label 0 address, preventing + proper optimizer operation. + */ + emit_op(s, OP_label); + emit_u32(s, 0); + default_label_pos = s->cur_func->byte_code.size - 4; + } else { + if (label_case < 0) { + /* falling thru direct from switch expression */ + js_parse_error(s, "invalid switch statement"); + goto fail; + } + if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) + goto fail; + } + } + if (js_parse_expect(s, '}')) + goto fail; + if (default_label_pos >= 0) { + /* Ugly patch for the the `default` label, shameful and risky */ + put_u32(s->cur_func->byte_code.buf + default_label_pos, + label_case); + s->cur_func->label_slots[label_case].pos = default_label_pos + 4; + } else { + emit_label(s, label_case); + } + emit_label(s, label_break); + emit_op(s, OP_drop); /* drop the switch expression */ + pop_break_entry(s->cur_func); + pop_scope(s); + } + break; + case TOK_TRY: + { + int label_catch, label_catch2, label_finally, label_end; + JSAtom name; + BlockEnv block_env; + set_eval_ret_undefined(s); + if (next_token(s)) + goto fail; + label_catch = new_label(s); + label_catch2 = new_label(s); + label_finally = new_label(s); + label_end = new_label(s); + emit_goto(s, OP_catch, label_catch); + push_break_entry(s->cur_func, &block_env, + JS_ATOM_NULL, -1, -1, 1); + block_env.label_finally = label_finally; + if (js_parse_block(s)) + goto fail; + pop_break_entry(s->cur_func); + if (js_is_live_code(s)) { + /* drop the catch offset */ + emit_op(s, OP_drop); + /* must push dummy value to keep same stack size */ + emit_op(s, OP_undefined); + emit_goto(s, OP_gosub, label_finally); + emit_op(s, OP_drop); + emit_goto(s, OP_goto, label_end); + } + if (s->token.val == TOK_CATCH) { + if (next_token(s)) + goto fail; + push_scope(s); /* catch variable */ + emit_label(s, label_catch); + if (s->token.val == '{') { + /* support optional-catch-binding feature */ + emit_op(s, OP_drop); /* pop the exception object */ + } else { + if (js_parse_expect(s, '(')) + goto fail; + if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { + if (s->token.val == '[' || s->token.val == '{') { + /* XXX: TOK_LET is not completely correct */ + if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0) + goto fail; + } else { + js_parse_error(s, "identifier expected"); + goto fail; + } + } else { + name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s) + || js_define_var(s, name, TOK_CATCH) < 0) { + JS_FreeAtom(ctx, name); + goto fail; + } + /* store the exception value in the catch variable */ + emit_op(s, OP_scope_put_var); + emit_u32(s, name); + emit_u16(s, s->cur_func->scope_level); + } + if (js_parse_expect(s, ')')) + goto fail; + } + /* XXX: should keep the address to nop it out if there is no finally block */ + emit_goto(s, OP_catch, label_catch2); + push_scope(s); /* catch block */ + push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL, + -1, -1, 1); + block_env.label_finally = label_finally; + if (js_parse_block(s)) + goto fail; + pop_break_entry(s->cur_func); + pop_scope(s); /* catch block */ + pop_scope(s); /* catch variable */ + if (js_is_live_code(s)) { + /* drop the catch2 offset */ + emit_op(s, OP_drop); + /* XXX: should keep the address to nop it out if there is no finally block */ + /* must push dummy value to keep same stack size */ + emit_op(s, OP_undefined); + emit_goto(s, OP_gosub, label_finally); + emit_op(s, OP_drop); + emit_goto(s, OP_goto, label_end); + } + /* catch exceptions thrown in the catch block to execute the + * finally clause and rethrow the exception */ + emit_label(s, label_catch2); + /* catch value is at TOS, no need to push undefined */ + emit_goto(s, OP_gosub, label_finally); + emit_op(s, OP_throw); + } else if (s->token.val == TOK_FINALLY) { + /* finally without catch : execute the finally clause + * and rethrow the exception */ + emit_label(s, label_catch); + /* catch value is at TOS, no need to push undefined */ + emit_goto(s, OP_gosub, label_finally); + emit_op(s, OP_throw); + } else { + js_parse_error(s, "expecting catch or finally"); + goto fail; + } + emit_label(s, label_finally); + if (s->token.val == TOK_FINALLY) { + int saved_eval_ret_idx = 0; /* avoid warning */ + if (next_token(s)) + goto fail; + /* on the stack: ret_value gosub_ret_value */ + push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL, + -1, -1, 2); + if (s->cur_func->eval_ret_idx >= 0) { + /* 'finally' updates eval_ret only if not a normal + termination */ + saved_eval_ret_idx = + add_var(s->ctx, s->cur_func, JS_ATOM__ret_); + if (saved_eval_ret_idx < 0) + goto fail; + emit_op(s, OP_get_loc); + emit_u16(s, s->cur_func->eval_ret_idx); + emit_op(s, OP_put_loc); + emit_u16(s, saved_eval_ret_idx); + set_eval_ret_undefined(s); + } + if (js_parse_block(s)) + goto fail; + if (s->cur_func->eval_ret_idx >= 0) { + emit_op(s, OP_get_loc); + emit_u16(s, saved_eval_ret_idx); + emit_op(s, OP_put_loc); + emit_u16(s, s->cur_func->eval_ret_idx); + } + pop_break_entry(s->cur_func); + } + emit_op(s, OP_ret); + emit_label(s, label_end); + } + break; + case ';': + /* empty statement */ + if (next_token(s)) + goto fail; + break; + case TOK_WITH: + if (s->cur_func->js_mode & JS_MODE_STRICT) { + js_parse_error(s, "invalid keyword: with"); + goto fail; + } else { + int with_idx; + if (next_token(s)) + goto fail; + if (js_parse_expr_paren(s)) + goto fail; + push_scope(s); + with_idx = define_var(s, s->cur_func, JS_ATOM__with_, + JS_VAR_DEF_WITH); + if (with_idx < 0) + goto fail; + emit_op(s, OP_to_object); + emit_op(s, OP_put_loc); + emit_u16(s, with_idx); + set_eval_ret_undefined(s); + if (js_parse_statement(s)) + goto fail; + /* Popping scope drops lexical context for the with object variable */ + pop_scope(s); + } + break; + case TOK_FUNCTION: + /* ES6 Annex B.3.2 and B.3.3 semantics */ + if (!(decl_mask & DECL_MASK_FUNC)) + goto func_decl_error; + if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*') + goto func_decl_error; + goto parse_func_var; + case TOK_IDENT: + if (s->token.u.ident.is_reserved) { + js_parse_error_reserved_identifier(s); + goto fail; + } + /* Determine if `let` introduces a Declaration or an ExpressionStatement */ + switch (is_let(s, decl_mask)) { + case TRUE: + tok = TOK_LET; + goto haslet; + case FALSE: + break; + default: + goto fail; + } + if (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) == TOK_FUNCTION) { + if (!(decl_mask & DECL_MASK_OTHER)) { + func_decl_error: + js_parse_error(s, "function declarations can't appear in single-statement context"); + goto fail; + } + parse_func_var: + if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, s->token.line_num)) + goto fail; + break; + } + goto hasexpr; + case TOK_CLASS: + if (!(decl_mask & DECL_MASK_OTHER)) { + js_parse_error(s, "class declarations can't appear in single-statement context"); + goto fail; + } + if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE)) + return -1; + break; + case TOK_DEBUGGER: + /* currently no debugger, so just skip the keyword */ + if (next_token(s)) + goto fail; + if (js_parse_expect_semi(s)) + goto fail; + break; + case TOK_ENUM: + case TOK_EXPORT: + case TOK_EXTENDS: + js_unsupported_keyword(s, s->token.u.ident.atom); + goto fail; + default: + hasexpr: + if (js_parse_expr(s)) + goto fail; + if (s->cur_func->eval_ret_idx >= 0) { + /* store the expression value so that it can be returned + by eval() */ + emit_op(s, OP_put_loc); + emit_u16(s, s->cur_func->eval_ret_idx); + } else { + emit_op(s, OP_drop); /* drop the result */ + } + if (js_parse_expect_semi(s)) + goto fail; + break; + } +done: + JS_FreeAtom(ctx, label_name); + return 0; +fail: + JS_FreeAtom(ctx, label_name); + return -1; +} + +static __exception int js_parse_delete(JSParseState *s) +{ + JSFunctionDef *fd = s->cur_func; + JSAtom name; + int opcode; + if (next_token(s)) + return -1; + if (js_parse_unary(s, PF_POW_FORBIDDEN)) + return -1; + switch(opcode = get_prev_opcode(fd)) { + case OP_get_field: + { + JSValue val; + int ret; + name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + fd->byte_code.size = fd->last_opcode_pos; + fd->last_opcode_pos = -1; + val = JS_AtomToValue(s->ctx, name); + ret = emit_push_const(s, val, 1); + JS_FreeValue(s->ctx, val); + JS_FreeAtom(s->ctx, name); + if (ret) + return ret; + } + goto do_delete; + case OP_get_array_el: + fd->byte_code.size = fd->last_opcode_pos; + fd->last_opcode_pos = -1; + do_delete: + emit_op(s, OP_delete); + break; + case OP_scope_get_var: + /* 'delete this': this is not a reference */ + name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + if (name == JS_ATOM_this || name == JS_ATOM_new_target) + goto ret_true; + if (fd->js_mode & JS_MODE_STRICT) { + return js_parse_error(s, "cannot delete a direct reference in strict mode"); + } else { + fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var; + } + break; + case OP_scope_get_private_field: + return js_parse_error(s, "cannot delete a private class field"); + case OP_get_super_value: + emit_op(s, OP_throw_error); + emit_atom(s, JS_ATOM_NULL); + emit_u8(s, JS_THROW_ERROR_DELETE_SUPER); + break; + default: + ret_true: + emit_op(s, OP_drop); + emit_op(s, OP_push_true); + break; + } + return 0; +} + +/* allowed parse_flags: PF_ARROW_FUNC, PF_POW_ALLOWED, PF_POW_FORBIDDEN */ +static __exception int js_parse_unary(JSParseState *s, int parse_flags) +{ + int op; + switch(s->token.val) { + case '+': + case '-': + case '!': + case '~': + case TOK_VOID: + op = s->token.val; + if (next_token(s)) + return -1; + if (js_parse_unary(s, PF_POW_FORBIDDEN)) + return -1; + switch(op) { + case '-': + emit_op(s, OP_neg); + break; + case '+': + emit_op(s, OP_plus); + break; + case '!': + emit_op(s, OP_lnot); + break; + case '~': + emit_op(s, OP_not); + break; + case TOK_VOID: + emit_op(s, OP_drop); + emit_op(s, OP_undefined); + break; + default: + abort(); + } + parse_flags = 0; + break; + case TOK_DEC: + case TOK_INC: + { + int opcode, op, scope, label; + JSAtom name; + op = s->token.val; + if (next_token(s)) + return -1; + if (js_parse_unary(s, 0)) + return -1; + if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) + return -1; + emit_op(s, OP_dec + op - TOK_DEC); + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, + FALSE); + } + break; + case TOK_TYPEOF: + { + JSFunctionDef *fd; + if (next_token(s)) + return -1; + if (js_parse_unary(s, PF_POW_FORBIDDEN)) + return -1; + /* reference access should not return an exception, so we + patch the get_var */ + fd = s->cur_func; + if (get_prev_opcode(fd) == OP_scope_get_var) { + fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef; + } + emit_op(s, OP_typeof); + parse_flags = 0; + } + break; + case TOK_DELETE: + if (js_parse_delete(s)) + return -1; + parse_flags = 0; + break; + case TOK_AWAIT: + if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) + return js_parse_error(s, "unexpected 'await' keyword"); + if (!s->cur_func->in_function_body) + return js_parse_error(s, "await in default expression"); + if (next_token(s)) + return -1; + if (js_parse_unary(s, PF_POW_FORBIDDEN)) + return -1; + emit_op(s, OP_await); + parse_flags = 0; + break; + default: + if (js_parse_postfix_expr(s, (parse_flags & PF_ARROW_FUNC) | + PF_POSTFIX_CALL)) + return -1; + if (!s->got_lf && + (s->token.val == TOK_DEC || s->token.val == TOK_INC)) { + int opcode, op, scope, label; + JSAtom name; + op = s->token.val; + if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) + return -1; + emit_op(s, OP_post_dec + op - TOK_DEC); + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND, + FALSE); + if (next_token(s)) + return -1; + } + break; + } + if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) { +#ifdef CONFIG_BIGNUM + if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) { + /* Extended exponentiation syntax rules: we extend the ES7 + grammar in order to have more intuitive semantics: + -2**2 evaluates to -4. */ + if (!(s->cur_func->js_mode & JS_MODE_MATH)) { + if (parse_flags & PF_POW_FORBIDDEN) { + JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'"); + return -1; + } + } + if (next_token(s)) + return -1; + if (js_parse_unary(s, PF_POW_ALLOWED)) + return -1; + emit_op(s, OP_pow); + } +#else + if (s->token.val == TOK_POW) { + /* Strict ES7 exponentiation syntax rules: To solve + conficting semantics between different implementations + regarding the precedence of prefix operators and the + postifx exponential, ES7 specifies that -2**2 is a + syntax error. */ + if (parse_flags & PF_POW_FORBIDDEN) { + JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'"); + return -1; + } + if (next_token(s)) + return -1; + if (js_parse_unary(s, PF_POW_ALLOWED)) + return -1; + emit_op(s, OP_pow); + } +#endif + } + return 0; +} + +/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ +static __exception int js_parse_expr_binary(JSParseState *s, int level, + int parse_flags) +{ + int op, opcode; + if (level == 0) { + return js_parse_unary(s, (parse_flags & PF_ARROW_FUNC) | + PF_POW_ALLOWED); + } + if (js_parse_expr_binary(s, level - 1, parse_flags)) + return -1; + for(;;) { + op = s->token.val; + switch(level) { + case 1: + switch(op) { + case '*': + opcode = OP_mul; + break; + case '/': + opcode = OP_div; + break; + case '%': +#ifdef CONFIG_BIGNUM + if (s->cur_func->js_mode & JS_MODE_MATH) + opcode = OP_math_mod; + else +#endif + opcode = OP_mod; + break; + default: + return 0; + } + break; + case 2: + switch(op) { + case '+': + opcode = OP_add; + break; + case '-': + opcode = OP_sub; + break; + default: + return 0; + } + break; + case 3: + switch(op) { + case TOK_SHL: + opcode = OP_shl; + break; + case TOK_SAR: + opcode = OP_sar; + break; + case TOK_SHR: + opcode = OP_shr; + break; + default: + return 0; + } + break; + case 4: + switch(op) { + case '<': + opcode = OP_lt; + break; + case '>': + opcode = OP_gt; + break; + case TOK_LTE: + opcode = OP_lte; + break; + case TOK_GTE: + opcode = OP_gte; + break; + case TOK_INSTANCEOF: + opcode = OP_instanceof; + break; + case TOK_IN: + if (parse_flags & PF_IN_ACCEPTED) { + opcode = OP_in; + } else { + return 0; + } + break; + default: + return 0; + } + break; + case 5: + switch(op) { + case TOK_EQ: + opcode = OP_eq; + break; + case TOK_NEQ: + opcode = OP_neq; + break; + case TOK_STRICT_EQ: + opcode = OP_strict_eq; + break; + case TOK_STRICT_NEQ: + opcode = OP_strict_neq; + break; + default: + return 0; + } + break; + case 6: + switch(op) { + case '&': + opcode = OP_and; + break; + default: + return 0; + } + break; + case 7: + switch(op) { + case '^': + opcode = OP_xor; + break; + default: + return 0; + } + break; + case 8: + switch(op) { + case '|': + opcode = OP_or; + break; + default: + return 0; + } + break; + default: + abort(); + } + if (next_token(s)) + return -1; + if (js_parse_expr_binary(s, level - 1, parse_flags & ~PF_ARROW_FUNC)) + return -1; + emit_op(s, opcode); + } + return 0; +} + +/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ +static __exception int js_parse_logical_and_or(JSParseState *s, int op, + int parse_flags) +{ + int label1; + if (op == TOK_LAND) { + if (js_parse_expr_binary(s, 8, parse_flags)) + return -1; + } else { + if (js_parse_logical_and_or(s, TOK_LAND, parse_flags)) + return -1; + } + if (s->token.val == op) { + label1 = new_label(s); + for(;;) { + if (next_token(s)) + return -1; + emit_op(s, OP_dup); + emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1); + emit_op(s, OP_drop); + if (op == TOK_LAND) { + if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC)) + return -1; + } else { + if (js_parse_logical_and_or(s, TOK_LAND, + parse_flags & ~PF_ARROW_FUNC)) + return -1; + } + if (s->token.val != op) { + if (s->token.val == TOK_DOUBLE_QUESTION_MARK) + return js_parse_error(s, "cannot mix ?? with && or ||"); + break; + } + } + emit_label(s, label1); + } + return 0; +} + +static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags) +{ + int label1; + if (js_parse_logical_and_or(s, TOK_LOR, parse_flags)) + return -1; + if (s->token.val == TOK_DOUBLE_QUESTION_MARK) { + label1 = new_label(s); + for(;;) { + if (next_token(s)) + return -1; + emit_op(s, OP_dup); + emit_op(s, OP_is_undefined_or_null); + emit_goto(s, OP_if_false, label1); + emit_op(s, OP_drop); + if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC)) + return -1; + if (s->token.val != TOK_DOUBLE_QUESTION_MARK) + break; + } + emit_label(s, label1); + } + return 0; +} + +/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ +static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags) +{ + int label1, label2; + if (js_parse_coalesce_expr(s, parse_flags)) + return -1; + if (s->token.val == '?') { + if (next_token(s)) + return -1; + label1 = emit_goto(s, OP_if_false, -1); + if (js_parse_assign_expr(s)) + return -1; + if (js_parse_expect(s, ':')) + return -1; + label2 = emit_goto(s, OP_goto, -1); + emit_label(s, label1); + if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED)) + return -1; + emit_label(s, label2); + } + return 0; +} + +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) +{ + int opcode, op, scope; + JSAtom name0 = JS_ATOM_NULL; + JSAtom name; + if (s->token.val == TOK_YIELD) { + BOOL is_star = FALSE, is_async; + if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR)) + return js_parse_error(s, "unexpected 'yield' keyword"); + if (!s->cur_func->in_function_body) + return js_parse_error(s, "yield in default expression"); + if (next_token(s)) + return -1; + /* XXX: is there a better method to detect 'yield' without + parameters ? */ + if (s->token.val != ';' && s->token.val != ')' && + s->token.val != ']' && s->token.val != '}' && + s->token.val != ',' && s->token.val != ':' && !s->got_lf) { + if (s->token.val == '*') { + is_star = TRUE; + if (next_token(s)) + return -1; + } + if (js_parse_assign_expr2(s, parse_flags)) + return -1; + } else { + emit_op(s, OP_undefined); + } + is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR); + if (is_star) { + int label_loop, label_return, label_next; + int label_return1, label_yield, label_throw, label_throw1; + int label_throw2; + label_loop = new_label(s); + label_yield = new_label(s); + emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start); + /* remove the catch offset (XXX: could avoid pushing back + undefined) */ + emit_op(s, OP_drop); + emit_op(s, OP_undefined); + emit_op(s, OP_undefined); /* initial value */ + emit_label(s, label_loop); + emit_op(s, OP_iterator_next); + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_done); + label_next = emit_goto(s, OP_if_true, -1); /* end of loop */ + emit_label(s, label_yield); + if (is_async) { + /* OP_async_yield_star takes the value as parameter */ + emit_op(s, OP_get_field); + emit_atom(s, JS_ATOM_value); + emit_op(s, OP_await); + emit_op(s, OP_async_yield_star); + } else { + /* OP_yield_star takes (value, done) as parameter */ + emit_op(s, OP_yield_star); + } + emit_op(s, OP_dup); + label_return = emit_goto(s, OP_if_true, -1); + emit_op(s, OP_drop); + emit_goto(s, OP_goto, label_loop); + emit_label(s, label_return); + emit_op(s, OP_push_i32); + emit_u32(s, 2); + emit_op(s, OP_strict_eq); + label_throw = emit_goto(s, OP_if_true, -1); + /* return handling */ + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_iterator_call); + emit_u8(s, 0); + label_return1 = emit_goto(s, OP_if_true, -1); + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_done); + emit_goto(s, OP_if_false, label_yield); + emit_op(s, OP_get_field); + emit_atom(s, JS_ATOM_value); + emit_label(s, label_return1); + emit_op(s, OP_nip); + emit_op(s, OP_nip); + emit_op(s, OP_nip); + emit_return(s, TRUE); + /* throw handling */ + emit_label(s, label_throw); + emit_op(s, OP_iterator_call); + emit_u8(s, 1); + label_throw1 = emit_goto(s, OP_if_true, -1); + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_done); + emit_goto(s, OP_if_false, label_yield); + emit_goto(s, OP_goto, label_next); + /* close the iterator and throw a type error exception */ + emit_label(s, label_throw1); + emit_op(s, OP_iterator_call); + emit_u8(s, 2); + label_throw2 = emit_goto(s, OP_if_true, -1); + if (is_async) + emit_op(s, OP_await); + emit_label(s, label_throw2); + emit_op(s, OP_throw_error); + emit_atom(s, JS_ATOM_NULL); + emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW); + emit_label(s, label_next); + emit_op(s, OP_get_field); + emit_atom(s, JS_ATOM_value); + emit_op(s, OP_nip); /* keep the value associated with + done = true */ + emit_op(s, OP_nip); + emit_op(s, OP_nip); + } else { + int label_next; + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_yield); + label_next = emit_goto(s, OP_if_false, -1); + emit_return(s, TRUE); + emit_label(s, label_next); + } + return 0; + } + if (s->token.val == TOK_IDENT) { + /* name0 is used to check for OP_set_name pattern, not duplicated */ + name0 = s->token.u.ident.atom; + } + if (js_parse_cond_expr(s, parse_flags | PF_ARROW_FUNC)) + return -1; + op = s->token.val; + if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) { + int label; + if (next_token(s)) + return -1; + if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0) + return -1; + if (js_parse_assign_expr2(s, parse_flags)) { + JS_FreeAtom(s->ctx, name); + return -1; + } + if (op == '=') { + if (opcode == OP_get_ref_value && name == name0) { + set_object_name(s, name); + } + } else { + static const uint8_t assign_opcodes[] = { + OP_mul, OP_div, OP_mod, OP_add, OP_sub, + OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or, +#ifdef CONFIG_BIGNUM + OP_pow, +#endif + OP_pow, + }; + op = assign_opcodes[op - TOK_MUL_ASSIGN]; +#ifdef CONFIG_BIGNUM + if (s->cur_func->js_mode & JS_MODE_MATH) { + if (op == OP_mod) + op = OP_math_mod; + } +#endif + emit_op(s, op); + } + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE); + } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) { + int label, label1, depth_lvalue, label2; + if (next_token(s)) + return -1; + if (get_lvalue(s, &opcode, &scope, &name, &label, + &depth_lvalue, TRUE, op) < 0) + return -1; + emit_op(s, OP_dup); + if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN) + emit_op(s, OP_is_undefined_or_null); + label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false, + -1); + emit_op(s, OP_drop); + if (js_parse_assign_expr2(s, parse_flags)) { + JS_FreeAtom(s->ctx, name); + return -1; + } + if (opcode == OP_get_ref_value && name == name0) { + set_object_name(s, name); + } + switch(depth_lvalue) { + case 1: + emit_op(s, OP_insert2); + break; + case 2: + emit_op(s, OP_insert3); + break; + case 3: + emit_op(s, OP_insert4); + break; + default: + abort(); + } + /* XXX: we disable the OP_put_ref_value optimization by not + using put_lvalue() otherwise depth_lvalue is not correct */ + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH, + FALSE); + label2 = emit_goto(s, OP_goto, -1); + emit_label(s, label1); + /* remove the lvalue stack entries */ + while (depth_lvalue != 0) { + emit_op(s, OP_nip); + depth_lvalue--; + } + emit_label(s, label2); + } + return 0; +} + +static __exception int js_parse_assign_expr(JSParseState *s) +{ + return js_parse_assign_expr2(s, PF_IN_ACCEPTED); +} + +static __exception int js_parse_expr(JSParseState *s) +{ + return js_parse_expr2(s, PF_IN_ACCEPTED); +} + +static __exception JSAtom js_parse_from_clause(JSParseState *s) +{ + JSAtom module_name; + if (!token_is_pseudo_keyword(s, JS_ATOM_from)) { + js_parse_error(s, "from clause expected"); + return JS_ATOM_NULL; + } + if (next_token(s)) + return JS_ATOM_NULL; + if (s->token.val != TOK_STRING) { + js_parse_error(s, "string expected"); + return JS_ATOM_NULL; + } + module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str); + if (module_name == JS_ATOM_NULL) + return JS_ATOM_NULL; + if (next_token(s)) { + JS_FreeAtom(s->ctx, module_name); + return JS_ATOM_NULL; + } + return module_name; +} + +static int add_import(JSParseState *s, JSModuleDef *m, + JSAtom local_name, JSAtom import_name) +{ + JSContext *ctx = s->ctx; + int i, var_idx; + JSImportEntry *mi; + BOOL is_local; + if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval) + return js_parse_error(s, "invalid import binding"); + if (local_name != JS_ATOM_default) { + for (i = 0; i < s->cur_func->closure_var_count; i++) { + if (s->cur_func->closure_var[i].var_name == local_name) + return js_parse_error(s, "duplicate import binding"); + } + } + is_local = (import_name == JS_ATOM__star_); + var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE, + m->import_entries_count, + local_name, TRUE, TRUE, FALSE); + if (var_idx < 0) + return -1; + if (js_resize_array(ctx, (void **)&m->import_entries, + sizeof(JSImportEntry), + &m->import_entries_size, + m->import_entries_count + 1)) + return -1; + mi = &m->import_entries[m->import_entries_count++]; + mi->import_name = JS_DupAtom(ctx, import_name); + mi->var_idx = var_idx; + return 0; +} + +static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, + JSAtom module_name) +{ + JSReqModuleEntry *rme; + int i; + /* no need to add the module request if it is already present */ + for(i = 0; i < m->req_module_entries_count; i++) { + rme = &m->req_module_entries[i]; + if (rme->module_name == module_name) + return i; + } + if (js_resize_array(ctx, (void **)&m->req_module_entries, + sizeof(JSReqModuleEntry), + &m->req_module_entries_size, + m->req_module_entries_count + 1)) + return -1; + rme = &m->req_module_entries[m->req_module_entries_count++]; + rme->module_name = JS_DupAtom(ctx, module_name); + rme->module = NULL; + return i; +} + +static __exception int js_parse_import(JSParseState *s) +{ + JSContext *ctx = s->ctx; + JSModuleDef *m = s->cur_func->module; + JSAtom local_name, import_name, module_name; + int first_import, i, idx; + if (next_token(s)) + return -1; + first_import = m->import_entries_count; + if (s->token.val == TOK_STRING) { + module_name = JS_ValueToAtom(ctx, s->token.u.str.str); + if (module_name == JS_ATOM_NULL) + return -1; + if (next_token(s)) { + JS_FreeAtom(ctx, module_name); + return -1; + } + } else { + if (s->token.val == TOK_IDENT) { + if (s->token.u.ident.is_reserved) { + return js_parse_error_reserved_identifier(s); + } + /* "default" import */ + local_name = JS_DupAtom(ctx, s->token.u.ident.atom); + import_name = JS_ATOM_default; + if (next_token(s)) + goto fail; + if (add_import(s, m, local_name, import_name)) + goto fail; + JS_FreeAtom(ctx, local_name); + if (s->token.val != ',') + goto end_import_clause; + if (next_token(s)) + return -1; + } + if (s->token.val == '*') { + /* name space import */ + if (next_token(s)) + return -1; + if (!token_is_pseudo_keyword(s, JS_ATOM_as)) + return js_parse_error(s, "expecting 'as'"); + if (next_token(s)) + return -1; + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + return -1; + } + local_name = JS_DupAtom(ctx, s->token.u.ident.atom); + import_name = JS_ATOM__star_; + if (next_token(s)) + goto fail; + if (add_import(s, m, local_name, import_name)) + goto fail; + JS_FreeAtom(ctx, local_name); + } else if (s->token.val == '{') { + if (next_token(s)) + return -1; + while (s->token.val != '}') { + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + return -1; + } + import_name = JS_DupAtom(ctx, s->token.u.ident.atom); + local_name = JS_ATOM_NULL; + if (next_token(s)) + goto fail; + if (token_is_pseudo_keyword(s, JS_ATOM_as)) { + if (next_token(s)) + goto fail; + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + goto fail; + } + local_name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s)) { + fail: + JS_FreeAtom(ctx, local_name); + JS_FreeAtom(ctx, import_name); + return -1; + } + } else { + local_name = JS_DupAtom(ctx, import_name); + } + if (add_import(s, m, local_name, import_name)) + goto fail; + JS_FreeAtom(ctx, local_name); + JS_FreeAtom(ctx, import_name); + if (s->token.val != ',') + break; + if (next_token(s)) + return -1; + } + if (js_parse_expect(s, '}')) + return -1; + } + end_import_clause: + module_name = js_parse_from_clause(s); + if (module_name == JS_ATOM_NULL) + return -1; + } + idx = add_req_module_entry(ctx, m, module_name); + JS_FreeAtom(ctx, module_name); + if (idx < 0) + return -1; + for(i = first_import; i < m->import_entries_count; i++) + m->import_entries[i].req_module_idx = idx; + return js_parse_expect_semi(s); +} + +static int add_star_export_entry(JSContext *ctx, JSModuleDef *m, + int req_module_idx) +{ + JSStarExportEntry *se; + if (js_resize_array(ctx, (void **)&m->star_export_entries, + sizeof(JSStarExportEntry), + &m->star_export_entries_size, + m->star_export_entries_count + 1)) + return -1; + se = &m->star_export_entries[m->star_export_entries_count++]; + se->req_module_idx = req_module_idx; + return 0; +} + +int js_parse_export(JSParseState *s) +{ + JSContext *ctx = s->ctx; + JSModuleDef *m = s->cur_func->module; + JSAtom local_name, export_name; + int first_export, idx, i, tok; + JSAtom module_name; + JSExportEntry *me; + if (next_token(s)) + return -1; + tok = s->token.val; + if (tok == TOK_CLASS) { + return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED); + } else if (tok == TOK_FUNCTION || + (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) == TOK_FUNCTION)) { + return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, s->token.line_num, + JS_PARSE_EXPORT_NAMED, NULL); + } + if (next_token(s)) + return -1; + switch(tok) { + case '{': + first_export = m->export_entries_count; + while (s->token.val != '}') { + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + return -1; + } + local_name = JS_DupAtom(ctx, s->token.u.ident.atom); + export_name = JS_ATOM_NULL; + if (next_token(s)) + goto fail; + if (token_is_pseudo_keyword(s, JS_ATOM_as)) { + if (next_token(s)) + goto fail; + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + goto fail; + } + export_name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s)) { + fail: + JS_FreeAtom(ctx, local_name); + fail1: + JS_FreeAtom(ctx, export_name); + return -1; + } + } else { + export_name = JS_DupAtom(ctx, local_name); + } + me = add_export_entry(s, m, local_name, export_name, + JS_EXPORT_TYPE_LOCAL); + JS_FreeAtom(ctx, local_name); + JS_FreeAtom(ctx, export_name); + if (!me) + return -1; + if (s->token.val != ',') + break; + if (next_token(s)) + return -1; + } + if (js_parse_expect(s, '}')) + return -1; + if (token_is_pseudo_keyword(s, JS_ATOM_from)) { + module_name = js_parse_from_clause(s); + if (module_name == JS_ATOM_NULL) + return -1; + idx = add_req_module_entry(ctx, m, module_name); + JS_FreeAtom(ctx, module_name); + if (idx < 0) + return -1; + for(i = first_export; i < m->export_entries_count; i++) { + me = &m->export_entries[i]; + me->export_type = JS_EXPORT_TYPE_INDIRECT; + me->u.req_module_idx = idx; + } + } + break; + case '*': + if (token_is_pseudo_keyword(s, JS_ATOM_as)) { + /* export ns from */ + if (next_token(s)) + return -1; + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + return -1; + } + export_name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail1; + module_name = js_parse_from_clause(s); + if (module_name == JS_ATOM_NULL) + goto fail1; + idx = add_req_module_entry(ctx, m, module_name); + JS_FreeAtom(ctx, module_name); + if (idx < 0) + goto fail1; + me = add_export_entry(s, m, JS_ATOM__star_, export_name, + JS_EXPORT_TYPE_INDIRECT); + JS_FreeAtom(ctx, export_name); + if (!me) + return -1; + me->u.req_module_idx = idx; + } else { + module_name = js_parse_from_clause(s); + if (module_name == JS_ATOM_NULL) + return -1; + idx = add_req_module_entry(ctx, m, module_name); + JS_FreeAtom(ctx, module_name); + if (idx < 0) + return -1; + if (add_star_export_entry(ctx, m, idx) < 0) + return -1; + } + break; + case TOK_DEFAULT: + if (s->token.val == TOK_CLASS) { + return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT); + } else if (s->token.val == TOK_FUNCTION || + (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) == TOK_FUNCTION)) { + return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, s->token.line_num, + JS_PARSE_EXPORT_DEFAULT, NULL); + } else { + if (js_parse_assign_expr(s)) + return -1; + } + /* set the name of anonymous functions */ + set_object_name(s, JS_ATOM_default); + /* store the value in the _default_ global variable and export + it */ + local_name = JS_ATOM__default_; + if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0) + return -1; + emit_op(s, OP_scope_put_var_init); + emit_atom(s, local_name); + emit_u16(s, 0); + if (!add_export_entry(s, m, local_name, JS_ATOM_default, + JS_EXPORT_TYPE_LOCAL)) + return -1; + break; + case TOK_VAR: + case TOK_LET: + case TOK_CONST: + return js_parse_var(s, TRUE, tok, TRUE); + default: + return js_parse_error(s, "invalid export syntax"); + } + return js_parse_expect_semi(s); +} + +static __exception int js_parse_source_element(JSParseState *s) +{ + JSFunctionDef *fd = s->cur_func; + int tok; + if (s->token.val == TOK_FUNCTION || + (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) == TOK_FUNCTION)) { + if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, s->token.line_num)) + return -1; + } else if (s->token.val == TOK_EXPORT && fd->module) { + if (js_parse_export(s)) + return -1; + } else if (s->token.val == TOK_IMPORT && fd->module && + ((tok = peek_token(s, FALSE)) != '(' && tok != '.')) { + /* the peek_token is needed to avoid confusion with ImportCall + (dynamic import) or import.meta */ + if (js_parse_import(s)) + return -1; + } else { + if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) + return -1; + } + return 0; +} + +static __exception int js_parse_directives(JSParseState *s) +{ + char str[20]; + JSParsePos pos; + BOOL has_semi; + if (s->token.val != TOK_STRING) + return 0; + js_parse_get_pos(s, &pos); + while(s->token.val == TOK_STRING) { + /* Copy actual source string representation */ + snprintf(str, sizeof str, "%.*s", + (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1); + if (next_token(s)) + return -1; + has_semi = FALSE; + switch (s->token.val) { + case ';': + if (next_token(s)) + return -1; + has_semi = TRUE; + break; + case '}': + case TOK_EOF: + has_semi = TRUE; + break; + case TOK_NUMBER: + case TOK_STRING: + case TOK_TEMPLATE: + case TOK_IDENT: + case TOK_REGEXP: + case TOK_DEC: + case TOK_INC: + case TOK_NULL: + case TOK_FALSE: + case TOK_TRUE: + case TOK_IF: + case TOK_RETURN: + case TOK_VAR: + case TOK_THIS: + case TOK_DELETE: + case TOK_TYPEOF: + case TOK_NEW: + case TOK_DO: + case TOK_WHILE: + case TOK_FOR: + case TOK_SWITCH: + case TOK_THROW: + case TOK_TRY: + case TOK_FUNCTION: + case TOK_DEBUGGER: + case TOK_WITH: + case TOK_CLASS: + case TOK_CONST: + case TOK_ENUM: + case TOK_EXPORT: + case TOK_IMPORT: + case TOK_SUPER: + case TOK_INTERFACE: + case TOK_LET: + case TOK_PACKAGE: + case TOK_PRIVATE: + case TOK_PROTECTED: + case TOK_PUBLIC: + case TOK_STATIC: + /* automatic insertion of ';' */ + if (s->got_lf) + has_semi = TRUE; + break; + default: + break; + } + if (!has_semi) + break; + if (!strcmp(str, "use strict")) { + s->cur_func->has_use_strict = TRUE; + s->cur_func->js_mode |= JS_MODE_STRICT; + } +#if !defined(DUMP_BYTECODE) || !(DUMP_BYTECODE & 8) + else if (!strcmp(str, "use strip")) { + s->cur_func->js_mode |= JS_MODE_STRIP; + } +#endif +#ifdef CONFIG_BIGNUM + else if (s->ctx->bignum_ext && !strcmp(str, "use math")) { + s->cur_func->js_mode |= JS_MODE_MATH; + } +#endif + } + return js_parse_seek_token(s, &pos); +} + +static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, + JSAtom func_name) +{ + JSAtom name; + int i, idx; + if (fd->js_mode & JS_MODE_STRICT) { + if (!fd->has_simple_parameter_list && fd->has_use_strict) { + return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter"); + } + if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments) { + return js_parse_error(s, "invalid function name in strict code"); + } + for (idx = 0; idx < fd->arg_count; idx++) { + name = fd->args[idx].var_name; + if (name == JS_ATOM_eval || name == JS_ATOM_arguments) { + return js_parse_error(s, "invalid argument name in strict code"); + } + } + } + /* check async_generator case */ + if ((fd->js_mode & JS_MODE_STRICT) + || !fd->has_simple_parameter_list + || (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC) + || fd->func_type == JS_PARSE_FUNC_ARROW + || fd->func_type == JS_PARSE_FUNC_METHOD) { + for (idx = 0; idx < fd->arg_count; idx++) { + name = fd->args[idx].var_name; + if (name != JS_ATOM_NULL) { + for (i = 0; i < idx; i++) { + if (fd->args[i].var_name == name) + goto duplicate; + } + /* Check if argument name duplicates a destructuring parameter */ + /* XXX: should have a flag for such variables */ + for (i = 0; i < fd->var_count; i++) { + if (fd->vars[i].var_name == name && + fd->vars[i].scope_level == 0) + goto duplicate; + } + } + } + } + return 0; +duplicate: + return js_parse_error(s, "duplicate argument names not allowed in this context"); +} + +static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) +{ + JSVarDef *vd; + /* the local variable indexes are currently stored on 16 bits */ + if (fd->arg_count >= JS_MAX_LOCAL_VARS) { + JS_ThrowInternalError(ctx, "too many arguments"); + return -1; + } + if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]), + &fd->arg_size, fd->arg_count + 1)) + return -1; + vd = &fd->args[fd->arg_count++]; + memset(vd, 0, sizeof(*vd)); + vd->var_name = JS_DupAtom(ctx, name); + vd->func_pool_idx = -1; + return fd->arg_count - 1; +} + +/* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and + JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */ +static __exception int js_parse_function_decl2(JSParseState *s, + JSParseFunctionEnum func_type, + JSFunctionKindEnum func_kind, + JSAtom func_name, + const uint8_t *ptr, + int function_line_num, + JSParseExportEnum export_flag, + JSFunctionDef **pfd) +{ + JSContext *ctx = s->ctx; + JSFunctionDef *fd = s->cur_func; + BOOL is_expr; + int func_idx, lexical_func_idx = -1; + BOOL has_opt_arg; + BOOL create_func_var = FALSE; + is_expr = (func_type != JS_PARSE_FUNC_STATEMENT && + func_type != JS_PARSE_FUNC_VAR); + if (func_type == JS_PARSE_FUNC_STATEMENT || + func_type == JS_PARSE_FUNC_VAR || + func_type == JS_PARSE_FUNC_EXPR) { + if (func_kind == JS_FUNC_NORMAL && + token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) != '\n') { + if (next_token(s)) + return -1; + func_kind = JS_FUNC_ASYNC; + } + if (next_token(s)) + return -1; + if (s->token.val == '*') { + if (next_token(s)) + return -1; + func_kind |= JS_FUNC_GENERATOR; + } + if (s->token.val == TOK_IDENT) { + if (s->token.u.ident.is_reserved || + (s->token.u.ident.atom == JS_ATOM_yield && + func_type == JS_PARSE_FUNC_EXPR && + (func_kind & JS_FUNC_GENERATOR)) || + (s->token.u.ident.atom == JS_ATOM_await && + func_type == JS_PARSE_FUNC_EXPR && + (func_kind & JS_FUNC_ASYNC))) { + return js_parse_error_reserved_identifier(s); + } + } + if (s->token.val == TOK_IDENT || + (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) || + (s->token.val == TOK_AWAIT && !s->is_module)) && + func_type == JS_PARSE_FUNC_EXPR)) { + func_name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s)) { + JS_FreeAtom(ctx, func_name); + return -1; + } + } else { + if (func_type != JS_PARSE_FUNC_EXPR && + export_flag != JS_PARSE_EXPORT_DEFAULT) { + return js_parse_error(s, "function name expected"); + } + } + } else if (func_type != JS_PARSE_FUNC_ARROW) { + func_name = JS_DupAtom(ctx, func_name); + } + if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE && + (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) { + JSGlobalVar *hf; + hf = find_global_var(fd, func_name); + /* XXX: should check scope chain */ + if (hf && hf->scope_level == fd->scope_level) { + js_parse_error(s, "invalid redefinition of global identifier in module code"); + JS_FreeAtom(ctx, func_name); + return -1; + } + } + if (func_type == JS_PARSE_FUNC_VAR) { + if (!(fd->js_mode & JS_MODE_STRICT) + && func_kind == JS_FUNC_NORMAL + && find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0 + && !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET)) + && !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) { + create_func_var = TRUE; + } + /* Create the lexical name here so that the function closure + contains it */ + if (fd->is_eval && + (fd->eval_type == JS_EVAL_TYPE_GLOBAL || + fd->eval_type == JS_EVAL_TYPE_MODULE) && + fd->scope_level == fd->body_scope) { + /* avoid creating a lexical variable in the global + scope. XXX: check annex B */ + JSGlobalVar *hf; + hf = find_global_var(fd, func_name); + /* XXX: should check scope chain */ + if (hf && hf->scope_level == fd->scope_level) { + js_parse_error(s, "invalid redefinition of global identifier"); + JS_FreeAtom(ctx, func_name); + return -1; + } + } else { + /* Always create a lexical name, fail if at the same scope as + existing name */ + /* Lexical variable will be initialized upon entering scope */ + lexical_func_idx = define_var(s, fd, func_name, + func_kind != JS_FUNC_NORMAL ? + JS_VAR_DEF_NEW_FUNCTION_DECL : + JS_VAR_DEF_FUNCTION_DECL); + if (lexical_func_idx < 0) { + JS_FreeAtom(ctx, func_name); + return -1; + } + } + } + fd = js_new_function_def(ctx, fd, FALSE, is_expr, + s->filename, function_line_num); + if (!fd) { + JS_FreeAtom(ctx, func_name); + return -1; + } + if (pfd) + *pfd = fd; + s->cur_func = fd; + fd->func_name = func_name; + /* XXX: test !fd->is_generator is always false */ + fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT || + func_type == JS_PARSE_FUNC_VAR || + func_type == JS_PARSE_FUNC_EXPR) && + func_kind == JS_FUNC_NORMAL; + fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD || + func_type == JS_PARSE_FUNC_GETTER || + func_type == JS_PARSE_FUNC_SETTER || + func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR || + func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR); + fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW); + fd->has_this_binding = fd->has_arguments_binding; + fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR); + if (func_type == JS_PARSE_FUNC_ARROW) { + fd->new_target_allowed = fd->parent->new_target_allowed; + fd->super_call_allowed = fd->parent->super_call_allowed; + fd->super_allowed = fd->parent->super_allowed; + fd->arguments_allowed = fd->parent->arguments_allowed; + } else { + fd->new_target_allowed = TRUE; + fd->super_call_allowed = fd->is_derived_class_constructor; + fd->super_allowed = fd->has_home_object; + fd->arguments_allowed = TRUE; + } + /* fd->in_function_body == FALSE prevents yield/await during the parsing + of the arguments in generator/async functions. They are parsed as + regular identifiers for other function kinds. */ + fd->func_kind = func_kind; + fd->func_type = func_type; + if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR || + func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) { + /* error if not invoked as a constructor */ + emit_op(s, OP_check_ctor); + } + if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) { + emit_class_field_init(s); + } + /* parse arguments */ + fd->has_simple_parameter_list = TRUE; + fd->has_parameter_expressions = FALSE; + has_opt_arg = FALSE; + if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) { + JSAtom name; + if (s->token.u.ident.is_reserved) { + js_parse_error_reserved_identifier(s); + goto fail; + } + name = s->token.u.ident.atom; + if (add_arg(ctx, fd, name) < 0) + goto fail; + fd->defined_arg_count = 1; + } else { + if (s->token.val == '(') { + int skip_bits; + /* if there is an '=' inside the parameter list, we + consider there is a parameter expression inside */ + js_parse_skip_parens_token(s, &skip_bits, FALSE); + if (skip_bits & SKIP_HAS_ASSIGNMENT) + fd->has_parameter_expressions = TRUE; + if (next_token(s)) + goto fail; + } else { + if (js_parse_expect(s, '(')) + goto fail; + } + if (fd->has_parameter_expressions) { + fd->scope_level = -1; /* force no parent scope */ + if (push_scope(s) < 0) + return -1; + } + while (s->token.val != ')') { + JSAtom name; + BOOL rest = FALSE; + int idx, has_initializer; + if (s->token.val == TOK_ELLIPSIS) { + fd->has_simple_parameter_list = FALSE; + rest = TRUE; + if (next_token(s)) + goto fail; + } + if (s->token.val == '[' || s->token.val == '{') { + fd->has_simple_parameter_list = FALSE; + if (rest) { + emit_op(s, OP_rest); + emit_u16(s, fd->arg_count); + } else { + /* unnamed arg for destructuring */ + idx = add_arg(ctx, fd, JS_ATOM_NULL); + emit_op(s, OP_get_arg); + emit_u16(s, idx); + } + has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE); + if (has_initializer < 0) + goto fail; + if (has_initializer) + has_opt_arg = TRUE; + if (!has_opt_arg) + fd->defined_arg_count++; + } else if (s->token.val == TOK_IDENT) { + if (s->token.u.ident.is_reserved) { + js_parse_error_reserved_identifier(s); + goto fail; + } + name = s->token.u.ident.atom; + if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) { + js_parse_error_reserved_identifier(s); + goto fail; + } + if (fd->has_parameter_expressions) { + if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0) + goto fail; + } + /* XXX: could avoid allocating an argument if rest is true */ + idx = add_arg(ctx, fd, name); + if (idx < 0) + goto fail; + if (next_token(s)) + goto fail; + if (rest) { + emit_op(s, OP_rest); + emit_u16(s, idx); + if (fd->has_parameter_expressions) { + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } + emit_op(s, OP_put_arg); + emit_u16(s, idx); + fd->has_simple_parameter_list = FALSE; + has_opt_arg = TRUE; + } else if (s->token.val == '=') { + int label; + fd->has_simple_parameter_list = FALSE; + has_opt_arg = TRUE; + if (next_token(s)) + goto fail; + label = new_label(s); + emit_op(s, OP_get_arg); + emit_u16(s, idx); + emit_op(s, OP_dup); + emit_op(s, OP_undefined); + emit_op(s, OP_strict_eq); + emit_goto(s, OP_if_false, label); + emit_op(s, OP_drop); + if (js_parse_assign_expr(s)) + goto fail; + set_object_name(s, name); + emit_op(s, OP_dup); + emit_op(s, OP_put_arg); + emit_u16(s, idx); + emit_label(s, label); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } else { + if (!has_opt_arg) { + fd->defined_arg_count++; + } + if (fd->has_parameter_expressions) { + /* copy the argument to the argument scope */ + emit_op(s, OP_get_arg); + emit_u16(s, idx); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } + } + } else { + js_parse_error(s, "missing formal parameter"); + goto fail; + } + if (rest && s->token.val != ')') { + js_parse_expect(s, ')'); + goto fail; + } + if (s->token.val == ')') + break; + if (js_parse_expect(s, ',')) + goto fail; + } + if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) || + (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) { + js_parse_error(s, "invalid number of arguments for getter or setter"); + goto fail; + } + } + if (fd->has_parameter_expressions) { + int idx; + /* Copy the variables in the argument scope to the variable + scope (see FunctionDeclarationInstantiation() in spec). The + normal arguments are already present, so no need to copy + them. */ + idx = fd->scopes[fd->scope_level].first; + while (idx >= 0) { + JSVarDef *vd = &fd->vars[idx]; + if (vd->scope_level != fd->scope_level) + break; + if (find_var(ctx, fd, vd->var_name) < 0) { + if (add_var(ctx, fd, vd->var_name) < 0) + goto fail; + vd = &fd->vars[idx]; /* fd->vars may have been reallocated */ + emit_op(s, OP_scope_get_var); + emit_atom(s, vd->var_name); + emit_u16(s, fd->scope_level); + emit_op(s, OP_scope_put_var); + emit_atom(s, vd->var_name); + emit_u16(s, 0); + } + idx = vd->scope_next; + } + /* the argument scope has no parent, hence we don't use pop_scope(s) */ + emit_op(s, OP_leave_scope); + emit_u16(s, fd->scope_level); + /* set the variable scope as the current scope */ + fd->scope_level = 0; + fd->scope_first = fd->scopes[fd->scope_level].first; + } + if (next_token(s)) + goto fail; + /* generator function: yield after the parameters are evaluated */ + if (func_kind == JS_FUNC_GENERATOR || + func_kind == JS_FUNC_ASYNC_GENERATOR) + emit_op(s, OP_initial_yield); + /* in generators, yield expression is forbidden during the parsing + of the arguments */ + fd->in_function_body = TRUE; + push_scope(s); /* enter body scope */ + fd->body_scope = fd->scope_level; + if (s->token.val == TOK_ARROW) { + if (next_token(s)) + goto fail; + if (s->token.val != '{') { + if (js_parse_function_check_names(s, fd, func_name)) + goto fail; + if (js_parse_assign_expr(s)) + goto fail; + if (func_kind != JS_FUNC_NORMAL) + emit_op(s, OP_return_async); + else + emit_op(s, OP_return); + if (!(fd->js_mode & JS_MODE_STRIP)) { + /* save the function source code */ + /* the end of the function source code is after the last + token of the function source stored into s->last_ptr */ + fd->source_len = s->last_ptr - ptr; + fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len); + if (!fd->source) + goto fail; + } + goto done; + } + } + if (js_parse_expect(s, '{')) + goto fail; + if (js_parse_directives(s)) + goto fail; + /* in strict_mode, check function and argument names */ + if (js_parse_function_check_names(s, fd, func_name)) + goto fail; + while (s->token.val != '}') { + if (js_parse_source_element(s)) + goto fail; + } + if (!(fd->js_mode & JS_MODE_STRIP)) { + /* save the function source code */ + fd->source_len = s->buf_ptr - ptr; + fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len); + if (!fd->source) + goto fail; + } + if (next_token(s)) { + /* consume the '}' */ + goto fail; + } + /* in case there is no return, add one */ + if (js_is_live_code(s)) { + emit_return(s, FALSE); + } +done: + s->cur_func = fd->parent; + /* create the function object */ + { + int idx; + JSAtom func_name = fd->func_name; + /* the real object will be set at the end of the compilation */ + idx = cpool_add(s, JS_NULL); + fd->parent_cpool_idx = idx; + if (is_expr) { + /* for constructors, no code needs to be generated here */ + if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR && + func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) { + /* OP_fclosure creates the function object from the bytecode + and adds the scope information */ + emit_op(s, OP_fclosure); + emit_u32(s, idx); + if (func_name == JS_ATOM_NULL) { + emit_op(s, OP_set_name); + emit_u32(s, JS_ATOM_NULL); + } + } + } else if (func_type == JS_PARSE_FUNC_VAR) { + emit_op(s, OP_fclosure); + emit_u32(s, idx); + if (create_func_var) { + if (s->cur_func->is_global_var) { + JSGlobalVar *hf; + /* the global variable must be defined at the start of the + function */ + hf = add_global_var(ctx, s->cur_func, func_name); + if (!hf) + goto fail; + /* it is considered as defined at the top level + (needed for annex B.3.3.4 and B.3.3.5 + checks) */ + hf->scope_level = 0; + hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0); + /* store directly into global var, bypass lexical scope */ + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var); + emit_atom(s, func_name); + emit_u16(s, 0); + } else { + /* do not call define_var to bypass lexical scope check */ + func_idx = find_var(ctx, s->cur_func, func_name); + if (func_idx < 0) { + func_idx = add_var(ctx, s->cur_func, func_name); + if (func_idx < 0) + goto fail; + } + /* store directly into local var, bypass lexical catch scope */ + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var); + emit_atom(s, func_name); + emit_u16(s, 0); + } + } + if (lexical_func_idx >= 0) { + /* lexical variable will be initialized upon entering scope */ + s->cur_func->vars[lexical_func_idx].func_pool_idx = idx; + emit_op(s, OP_drop); + } else { + /* store function object into its lexical name */ + /* XXX: could use OP_put_loc directly */ + emit_op(s, OP_scope_put_var_init); + emit_atom(s, func_name); + emit_u16(s, s->cur_func->scope_level); + } + } else { + if (!s->cur_func->is_global_var) { + int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR); + if (var_idx < 0) + goto fail; + /* the variable will be assigned at the top of the function */ + if (var_idx & ARGUMENT_VAR_OFFSET) { + s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx; + } else { + s->cur_func->vars[var_idx].func_pool_idx = idx; + } + } else { + JSAtom func_var_name; + JSGlobalVar *hf; + if (func_name == JS_ATOM_NULL) + func_var_name = JS_ATOM__default_; /* export default */ + else + func_var_name = func_name; + /* the variable will be assigned at the top of the function */ + hf = add_global_var(ctx, s->cur_func, func_var_name); + if (!hf) + goto fail; + hf->cpool_idx = idx; + if (export_flag != JS_PARSE_EXPORT_NONE) { + if (!add_export_entry(s, s->cur_func->module, func_var_name, + export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL)) + goto fail; + } + } + } + } + return 0; + fail: + s->cur_func = fd->parent; + js_free_function_def(ctx, fd); + if (pfd) + *pfd = NULL; + return -1; +} + +static __exception int js_parse_function_decl(JSParseState *s, + JSParseFunctionEnum func_type, + JSFunctionKindEnum func_kind, + JSAtom func_name, + const uint8_t *ptr, + int function_line_num) +{ + return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr, + function_line_num, JS_PARSE_EXPORT_NONE, + NULL); +} + +int js_parse_program(JSParseState *s) +{ + JSFunctionDef *fd = s->cur_func; + int idx; + if (next_token(s)) + return -1; + if (js_parse_directives(s)) + return -1; + fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) || + (fd->eval_type == JS_EVAL_TYPE_MODULE) || + !(fd->js_mode & JS_MODE_STRICT); + if (!s->is_module) { + /* hidden variable for the return value */ + fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_); + if (idx < 0) + return -1; + } + while (s->token.val != TOK_EOF) { + if (js_parse_source_element(s)) + return -1; + } + if (!s->is_module) { + /* return the value of the hidden variable eval_ret_idx */ + emit_op(s, OP_get_loc); + emit_u16(s, fd->eval_ret_idx); + emit_op(s, OP_return); + } else { + emit_op(s, OP_return_undef); + } + return 0; +} diff --git a/third_party/quickjs/prim.c b/third_party/quickjs/prim.c new file mode 100644 index 000000000..de1244675 --- /dev/null +++ b/third_party/quickjs/prim.c @@ -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); +} diff --git a/third_party/quickjs/promise.c b/third_party/quickjs/promise.c new file mode 100644 index 000000000..f5a2cb2d1 --- /dev/null +++ b/third_party/quickjs/promise.c @@ -0,0 +1,1569 @@ +/* + * 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 JSPromiseStateEnum { + JS_PROMISE_PENDING, + JS_PROMISE_FULFILLED, + JS_PROMISE_REJECTED, +} JSPromiseStateEnum; + +typedef struct JSPromiseData { + JSPromiseStateEnum promise_state; + /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */ + struct list_head promise_reactions[2]; + BOOL is_handled; /* Note: only useful to debug */ + JSValue promise_result; +} JSPromiseData; + +typedef struct JSPromiseFunctionDataResolved { + int ref_count; + BOOL already_resolved; +} JSPromiseFunctionDataResolved; + +typedef struct JSPromiseFunctionData { + JSValue promise; + JSPromiseFunctionDataResolved *presolved; +} JSPromiseFunctionData; + +typedef struct JSPromiseReactionData { + struct list_head link; /* not used in promise_reaction_job */ + JSValue resolving_funcs[2]; + JSValue handler; +} JSPromiseReactionData; + +static int js_create_resolving_functions(JSContext *ctx, JSValue *args, + JSValueConst promise); + +static void promise_reaction_data_free(JSRuntime *rt, + JSPromiseReactionData *rd) +{ + JS_FreeValueRT(rt, rd->resolving_funcs[0]); + JS_FreeValueRT(rt, rd->resolving_funcs[1]); + JS_FreeValueRT(rt, rd->handler); + js_free_rt(rt, rd); +} + +static JSValue promise_reaction_job(JSContext *ctx, int argc, + JSValueConst *argv) +{ + JSValueConst handler, arg, func; + JSValue res, res2; + BOOL is_reject; + + assert(argc == 5); + handler = argv[2]; + is_reject = JS_ToBool(ctx, argv[3]); + arg = argv[4]; +#ifdef DUMP_PROMISE + printf("promise_reaction_job: is_reject=%d\n", is_reject); +#endif + + if (JS_IsUndefined(handler)) { + if (is_reject) { + res = JS_Throw(ctx, JS_DupValue(ctx, arg)); + } else { + res = JS_DupValue(ctx, arg); + } + } else { + res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg); + } + is_reject = JS_IsException(res); + if (is_reject) + res = JS_GetException(ctx); + func = argv[is_reject]; + /* as an extension, we support undefined as value to avoid + creating a dummy promise in the 'await' implementation of async + functions */ + if (!JS_IsUndefined(func)) { + res2 = JS_Call(ctx, func, JS_UNDEFINED, + 1, (JSValueConst *)&res); + } else { + res2 = JS_UNDEFINED; + } + JS_FreeValue(ctx, res); + + return res2; +} + +void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, + JSHostPromiseRejectionTracker *cb, + void *opaque) +{ + rt->host_promise_rejection_tracker = cb; + rt->host_promise_rejection_tracker_opaque = opaque; +} + +static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise, + JSValueConst value, BOOL is_reject) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + struct list_head *el, *el1; + JSPromiseReactionData *rd; + JSValueConst args[5]; + + if (!s || s->promise_state != JS_PROMISE_PENDING) + return; /* should never happen */ + set_value(ctx, &s->promise_result, JS_DupValue(ctx, value)); + s->promise_state = JS_PROMISE_FULFILLED + is_reject; +#ifdef DUMP_PROMISE + printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject); +#endif + if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { + JSRuntime *rt = ctx->rt; + if (rt->host_promise_rejection_tracker) { + rt->host_promise_rejection_tracker(ctx, promise, value, FALSE, + rt->host_promise_rejection_tracker_opaque); + } + } + + list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) { + rd = list_entry(el, JSPromiseReactionData, link); + args[0] = rd->resolving_funcs[0]; + args[1] = rd->resolving_funcs[1]; + args[2] = rd->handler; + args[3] = JS_NewBool(ctx, is_reject); + args[4] = value; + JS_EnqueueJob(ctx, promise_reaction_job, 5, args); + list_del(&rd->link); + promise_reaction_data_free(ctx->rt, rd); + } + + list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) { + rd = list_entry(el, JSPromiseReactionData, link); + list_del(&rd->link); + promise_reaction_data_free(ctx->rt, rd); + } +} + +static void reject_promise(JSContext *ctx, JSValueConst promise, + JSValueConst value) +{ + fulfill_or_reject_promise(ctx, promise, value, TRUE); +} + +static JSValue js_promise_resolve_thenable_job(JSContext *ctx, + int argc, JSValueConst *argv) +{ + JSValueConst promise, thenable, then; + JSValue args[2], res; + +#ifdef DUMP_PROMISE + printf("js_promise_resolve_thenable_job\n"); +#endif + assert(argc == 3); + promise = argv[0]; + thenable = argv[1]; + then = argv[2]; + if (js_create_resolving_functions(ctx, args, promise) < 0) + return JS_EXCEPTION; + res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args); + if (JS_IsException(res)) { + JSValue error = JS_GetException(ctx); + res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); + } + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + return res; +} + +static void js_promise_resolve_function_free_resolved(JSRuntime *rt, + JSPromiseFunctionDataResolved *sr) +{ + if (--sr->ref_count == 0) { + js_free_rt(rt, sr); + } +} + +static int js_create_resolving_functions(JSContext *ctx, + JSValue *resolving_funcs, + JSValueConst promise) + +{ + JSValue obj; + JSPromiseFunctionData *s; + JSPromiseFunctionDataResolved *sr; + int i, ret; + + sr = js_malloc(ctx, sizeof(*sr)); + if (!sr) + return -1; + sr->ref_count = 1; + sr->already_resolved = FALSE; /* must be shared between the two functions */ + ret = 0; + for(i = 0; i < 2; i++) { + obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_PROMISE_RESOLVE_FUNCTION + i); + if (JS_IsException(obj)) + goto fail; + s = js_malloc(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, obj); + fail: + + if (i != 0) + JS_FreeValue(ctx, resolving_funcs[0]); + ret = -1; + break; + } + sr->ref_count++; + s->presolved = sr; + s->promise = JS_DupValue(ctx, promise); + JS_SetOpaque(obj, s); + js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1); + resolving_funcs[i] = obj; + } + js_promise_resolve_function_free_resolved(ctx->rt, sr); + return ret; +} + +static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; + if (s) { + js_promise_resolve_function_free_resolved(rt, s->presolved); + JS_FreeValueRT(rt, s->promise); + js_free_rt(rt, s); + } +} + +static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; + if (s) { + JS_MarkValue(rt, s->promise, mark_func); + } +} + +static JSValue js_promise_resolve_function_call(JSContext *ctx, + JSValueConst func_obj, + JSValueConst this_val, + int argc, JSValueConst *argv, + int flags) +{ + JSObject *p = JS_VALUE_GET_OBJ(func_obj); + JSPromiseFunctionData *s; + JSValueConst resolution, args[3]; + JSValue then; + BOOL is_reject; + + s = p->u.promise_function_data; + if (!s || s->presolved->already_resolved) + return JS_UNDEFINED; + s->presolved->already_resolved = TRUE; + is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION; + if (argc > 0) + resolution = argv[0]; + else + resolution = JS_UNDEFINED; +#ifdef DUMP_PROMISE + printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject); + JS_DumpValue(ctx, resolution); + printf("\n"); +#endif + if (is_reject || !JS_IsObject(resolution)) { + goto done; + } else if (js_same_value(ctx, resolution, s->promise)) { + JS_ThrowTypeError(ctx, "promise self resolution"); + goto fail_reject; + } + then = JS_GetProperty(ctx, resolution, JS_ATOM_then); + if (JS_IsException(then)) { + JSValue error; + fail_reject: + error = JS_GetException(ctx); + reject_promise(ctx, s->promise, error); + JS_FreeValue(ctx, error); + } else if (!JS_IsFunction(ctx, then)) { + JS_FreeValue(ctx, then); + done: + fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject); + } else { + args[0] = s->promise; + args[1] = resolution; + args[2] = then; + JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args); + JS_FreeValue(ctx, then); + } + return JS_UNDEFINED; +} + +static void js_promise_finalizer(JSRuntime *rt, JSValue val) +{ + JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); + struct list_head *el, *el1; + int i; + + if (!s) + return; + for(i = 0; i < 2; i++) { + list_for_each_safe(el, el1, &s->promise_reactions[i]) { + JSPromiseReactionData *rd = + list_entry(el, JSPromiseReactionData, link); + promise_reaction_data_free(rt, rd); + } + } + JS_FreeValueRT(rt, s->promise_result); + js_free_rt(rt, s); +} + +static void js_promise_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); + struct list_head *el; + int i; + + if (!s) + return; + for(i = 0; i < 2; i++) { + list_for_each(el, &s->promise_reactions[i]) { + JSPromiseReactionData *rd = + list_entry(el, JSPromiseReactionData, link); + JS_MarkValue(rt, rd->resolving_funcs[0], mark_func); + JS_MarkValue(rt, rd->resolving_funcs[1], mark_func); + JS_MarkValue(rt, rd->handler, mark_func); + } + } + JS_MarkValue(rt, s->promise_result, mark_func); +} + +static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValueConst executor; + JSValue obj; + JSPromiseData *s; + JSValue args[2], ret; + int i; + + executor = argv[0]; + if (check_function(ctx, executor)) + return JS_EXCEPTION; + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE); + if (JS_IsException(obj)) + return JS_EXCEPTION; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + goto fail; + s->promise_state = JS_PROMISE_PENDING; + s->is_handled = FALSE; + for(i = 0; i < 2; i++) + init_list_head(&s->promise_reactions[i]); + s->promise_result = JS_UNDEFINED; + JS_SetOpaque(obj, s); + if (js_create_resolving_functions(ctx, args, obj)) + goto fail; + ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args); + if (JS_IsException(ret)) { + JSValue ret2, error; + error = JS_GetException(ctx); + ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); + if (JS_IsException(ret2)) + goto fail1; + JS_FreeValue(ctx, ret2); + } + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + return obj; + fail1: + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_promise_executor(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + int i; + + for(i = 0; i < 2; i++) { + if (!JS_IsUndefined(func_data[i])) + return JS_ThrowTypeError(ctx, "resolving function already set"); + func_data[i] = JS_DupValue(ctx, argv[i]); + } + return JS_UNDEFINED; +} + +static JSValue js_promise_executor_new(JSContext *ctx) +{ + JSValueConst func_data[2]; + + func_data[0] = JS_UNDEFINED; + func_data[1] = JS_UNDEFINED; + return JS_NewCFunctionData(ctx, js_promise_executor, 2, + 0, 2, func_data); +} + +static JSValue js_new_promise_capability(JSContext *ctx, + JSValue *resolving_funcs, + JSValueConst ctor) +{ + JSValue executor, result_promise; + JSCFunctionDataRecord *s; + int i; + + executor = js_promise_executor_new(ctx); + if (JS_IsException(executor)) + return executor; + + if (JS_IsUndefined(ctor)) { + result_promise = js_promise_constructor(ctx, ctor, 1, + (JSValueConst *)&executor); + } else { + result_promise = JS_CallConstructor(ctx, ctor, 1, + (JSValueConst *)&executor); + } + if (JS_IsException(result_promise)) + goto fail; + s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA); + for(i = 0; i < 2; i++) { + if (check_function(ctx, s->data[i])) + goto fail; + } + for(i = 0; i < 2; i++) + resolving_funcs[i] = JS_DupValue(ctx, s->data[i]); + JS_FreeValue(ctx, executor); + return result_promise; + fail: + JS_FreeValue(ctx, executor); + JS_FreeValue(ctx, result_promise); + return JS_EXCEPTION; +} + +JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs) +{ + return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED); +} + +JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue result_promise, resolving_funcs[2], ret; + BOOL is_reject = magic; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) { + JSValue ctor; + BOOL is_same; + ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor); + if (JS_IsException(ctor)) + return ctor; + is_same = js_same_value(ctx, ctor, this_val); + JS_FreeValue(ctx, ctor); + if (is_same) + return JS_DupValue(ctx, argv[0]); + } + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); + if (JS_IsException(result_promise)) + return result_promise; + ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, result_promise); + return ret; + } + JS_FreeValue(ctx, ret); + return result_promise; +} + +#if 0 +static JSValue js_promise___newPromiseCapability(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue result_promise, resolving_funcs[2], obj; + JSValueConst ctor; + ctor = argv[0]; + if (!JS_IsObject(ctor)) + return JS_ThrowTypeErrorNotAnObject(ctx); + result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); + if (JS_IsException(result_promise)) + return result_promise; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, result_promise); + return JS_EXCEPTION; + } + JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E); + return obj; +} +#endif + +static __exception int remainingElementsCount_add(JSContext *ctx, + JSValueConst resolve_element_env, + int addend) +{ + JSValue val; + int remainingElementsCount; + + val = JS_GetPropertyUint32(ctx, resolve_element_env, 0); + if (JS_IsException(val)) + return -1; + if (JS_ToInt32Free(ctx, &remainingElementsCount, val)) + return -1; + remainingElementsCount += addend; + if (JS_SetPropertyUint32(ctx, resolve_element_env, 0, + JS_NewInt32(ctx, remainingElementsCount)) < 0) + return -1; + return (remainingElementsCount == 0); +} + +#define PROMISE_MAGIC_all 0 +#define PROMISE_MAGIC_allSettled 1 +#define PROMISE_MAGIC_any 2 + +/* used by C code. */ +static JSValue js_aggregate_error_constructor(JSContext *ctx, + JSValueConst errors) +{ + JSValue obj; + obj = JS_NewObjectProtoClass(ctx, + ctx->native_error_proto[JS_AGGREGATE_ERROR], + JS_CLASS_ERROR); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + return obj; +} + +static JSValue js_promise_all_resolve_element(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, + JSValue *func_data) +{ + int resolve_type = magic & 3; + int is_reject = magic & 4; + BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]); + JSValueConst values = func_data[2]; + JSValueConst resolve = func_data[3]; + JSValueConst resolve_element_env = func_data[4]; + JSValue ret, obj; + int is_zero, index; + if (JS_ToInt32(ctx, &index, func_data[1])) + return JS_EXCEPTION; + if (alreadyCalled) + return JS_UNDEFINED; + func_data[0] = JS_NewBool(ctx, TRUE); + if (resolve_type == PROMISE_MAGIC_allSettled) { + JSValue str; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled"); + if (JS_IsException(str)) + goto fail1; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status, + str, + JS_PROP_C_W_E) < 0) + goto fail1; + if (JS_DefinePropertyValue(ctx, obj, + is_reject ? JS_ATOM_reason : JS_ATOM_value, + JS_DupValue(ctx, argv[0]), + JS_PROP_C_W_E) < 0) { + fail1: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + } else { + obj = JS_DupValue(ctx, argv[0]); + } + if (JS_DefinePropertyValueUint32(ctx, values, index, + obj, JS_PROP_C_W_E) < 0) + return JS_EXCEPTION; + is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); + if (is_zero < 0) + return JS_EXCEPTION; + if (is_zero) { + if (resolve_type == PROMISE_MAGIC_any) { + JSValue error; + error = js_aggregate_error_constructor(ctx, values); + if (JS_IsException(error)) + return JS_EXCEPTION; + ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); + } else { + ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values); + } + if (JS_IsException(ret)) + return ret; + JS_FreeValue(ctx, ret); + } + return JS_UNDEFINED; +} + +/* magic = 0: Promise.all 1: Promise.allSettled */ +static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue result_promise, resolving_funcs[2], item, next_promise, ret; + JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED; + JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element; + JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED; + JSValueConst then_args[2], resolve_element_data[5]; + BOOL done; + int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any); + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); + if (JS_IsException(result_promise)) + return result_promise; + promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); + if (JS_IsException(promise_resolve) || + check_function(ctx, promise_resolve)) + goto fail_reject; + iter = JS_GetIterator(ctx, argv[0], FALSE); + if (JS_IsException(iter)) { + JSValue error; + fail_reject: + error = JS_GetException(ctx); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, + (JSValueConst *)&error); + JS_FreeValue(ctx, error); + if (JS_IsException(ret)) + goto fail; + JS_FreeValue(ctx, ret); + } else { + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail_reject; + values = JS_NewArray(ctx); + if (JS_IsException(values)) + goto fail_reject; + resolve_element_env = JS_NewArray(ctx); + if (JS_IsException(resolve_element_env)) + goto fail_reject; + /* remainingElementsCount field */ + if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0, + JS_NewInt32(ctx, 1), + JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) + goto fail_reject; + + index = 0; + for(;;) { + /* XXX: conformance: should close the iterator if error on 'done' + access, but not on 'value' access */ + item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(item)) + goto fail_reject; + if (done) + break; + next_promise = JS_Call(ctx, promise_resolve, + this_val, 1, (JSValueConst *)&item); + JS_FreeValue(ctx, item); + if (JS_IsException(next_promise)) { + fail_reject1: + JS_IteratorClose(ctx, iter, TRUE); + goto fail_reject; + } + resolve_element_data[0] = JS_NewBool(ctx, FALSE); + resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index); + resolve_element_data[2] = values; + resolve_element_data[3] = resolving_funcs[is_promise_any]; + resolve_element_data[4] = resolve_element_env; + resolve_element = + JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, + magic, 5, resolve_element_data); + if (JS_IsException(resolve_element)) { + JS_FreeValue(ctx, next_promise); + goto fail_reject1; + } + + if (magic == PROMISE_MAGIC_allSettled) { + reject_element = + JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, + magic | 4, 5, resolve_element_data); + if (JS_IsException(reject_element)) { + JS_FreeValue(ctx, next_promise); + goto fail_reject1; + } + } else if (magic == PROMISE_MAGIC_any) { + if (JS_DefinePropertyValueUint32(ctx, values, index, + JS_UNDEFINED, JS_PROP_C_W_E) < 0) + goto fail_reject1; + reject_element = resolve_element; + resolve_element = JS_DupValue(ctx, resolving_funcs[0]); + } else { + reject_element = JS_DupValue(ctx, resolving_funcs[1]); + } + + if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) { + JS_FreeValue(ctx, next_promise); + JS_FreeValue(ctx, resolve_element); + JS_FreeValue(ctx, reject_element); + goto fail_reject1; + } + + then_args[0] = resolve_element; + then_args[1] = reject_element; + ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args); + JS_FreeValue(ctx, resolve_element); + JS_FreeValue(ctx, reject_element); + if (check_exception_free(ctx, ret)) + goto fail_reject1; + index++; + } + + is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); + if (is_zero < 0) + goto fail_reject; + if (is_zero) { + if (magic == PROMISE_MAGIC_any) { + JSValue error; + error = js_aggregate_error_constructor(ctx, values); + if (JS_IsException(error)) + goto fail_reject; + JS_FreeValue(ctx, values); + values = error; + } + ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED, + 1, (JSValueConst *)&values); + if (check_exception_free(ctx, ret)) + goto fail_reject; + } + } + done: + JS_FreeValue(ctx, promise_resolve); + JS_FreeValue(ctx, resolve_element_env); + JS_FreeValue(ctx, values); + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return result_promise; + fail: + JS_FreeValue(ctx, result_promise); + result_promise = JS_EXCEPTION; + goto done; +} + +static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue result_promise, resolving_funcs[2], item, next_promise, ret; + JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED; + JSValue promise_resolve = JS_UNDEFINED; + BOOL done; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); + if (JS_IsException(result_promise)) + return result_promise; + promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); + if (JS_IsException(promise_resolve) || + check_function(ctx, promise_resolve)) + goto fail_reject; + iter = JS_GetIterator(ctx, argv[0], FALSE); + if (JS_IsException(iter)) { + JSValue error; + fail_reject: + error = JS_GetException(ctx); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, + (JSValueConst *)&error); + JS_FreeValue(ctx, error); + if (JS_IsException(ret)) + goto fail; + JS_FreeValue(ctx, ret); + } else { + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail_reject; + + for(;;) { + /* XXX: conformance: should close the iterator if error on 'done' + access, but not on 'value' access */ + item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(item)) + goto fail_reject; + if (done) + break; + next_promise = JS_Call(ctx, promise_resolve, + this_val, 1, (JSValueConst *)&item); + JS_FreeValue(ctx, item); + if (JS_IsException(next_promise)) { + fail_reject1: + JS_IteratorClose(ctx, iter, TRUE); + goto fail_reject; + } + ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, + (JSValueConst *)resolving_funcs); + if (check_exception_free(ctx, ret)) + goto fail_reject1; + } + } + done: + JS_FreeValue(ctx, promise_resolve); + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return result_promise; + fail: + //JS_FreeValue(ctx, next_method); // why not??? + JS_FreeValue(ctx, result_promise); + result_promise = JS_EXCEPTION; + goto done; +} + +int perform_promise_then(JSContext *ctx, + JSValueConst promise, + JSValueConst *resolve_reject, + JSValueConst *cap_resolving_funcs) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + JSPromiseReactionData *rd_array[2], *rd; + int i, j; + rd_array[0] = NULL; + rd_array[1] = NULL; + for(i = 0; i < 2; i++) { + JSValueConst handler; + rd = js_mallocz(ctx, sizeof(*rd)); + if (!rd) { + if (i == 1) + promise_reaction_data_free(ctx->rt, rd_array[0]); + return -1; + } + for(j = 0; j < 2; j++) + rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]); + handler = resolve_reject[i]; + if (!JS_IsFunction(ctx, handler)) + handler = JS_UNDEFINED; + rd->handler = JS_DupValue(ctx, handler); + rd_array[i] = rd; + } + if (s->promise_state == JS_PROMISE_PENDING) { + for(i = 0; i < 2; i++) + list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]); + } else { + JSValueConst args[5]; + if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { + JSRuntime *rt = ctx->rt; + if (rt->host_promise_rejection_tracker) { + rt->host_promise_rejection_tracker(ctx, promise, s->promise_result, + TRUE, rt->host_promise_rejection_tracker_opaque); + } + } + i = s->promise_state - JS_PROMISE_FULFILLED; + rd = rd_array[i]; + args[0] = rd->resolving_funcs[0]; + args[1] = rd->resolving_funcs[1]; + args[2] = rd->handler; + args[3] = JS_NewBool(ctx, i); + args[4] = s->promise_result; + JS_EnqueueJob(ctx, promise_reaction_job, 5, args); + for(i = 0; i < 2; i++) + promise_reaction_data_free(ctx->rt, rd_array[i]); + } + s->is_handled = TRUE; + return 0; +} + +static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue ctor, result_promise, resolving_funcs[2]; + JSPromiseData *s; + int i, ret; + + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE); + if (!s) + return JS_EXCEPTION; + + ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); + if (JS_IsException(ctor)) + return ctor; + result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); + JS_FreeValue(ctx, ctor); + if (JS_IsException(result_promise)) + return result_promise; + ret = perform_promise_then(ctx, this_val, argv, + (JSValueConst *)resolving_funcs); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (ret) { + JS_FreeValue(ctx, result_promise); + return JS_EXCEPTION; + } + return result_promise; +} + +static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst args[2]; + args[0] = JS_UNDEFINED; + args[1] = argv[0]; + return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args); +} + +static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + return JS_DupValue(ctx, func_data[0]); +} + +static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + return JS_Throw(ctx, JS_DupValue(ctx, func_data[0])); +} + +static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + JSValueConst ctor = func_data[0]; + JSValueConst onFinally = func_data[1]; + JSValue res, promise, ret, then_func; + + res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL); + if (JS_IsException(res)) + return res; + promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0); + JS_FreeValue(ctx, res); + if (JS_IsException(promise)) + return promise; + if (magic == 0) { + then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0, + 0, 1, argv); + } else { + then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0, + 0, 1, argv); + } + if (JS_IsException(then_func)) { + JS_FreeValue(ctx, promise); + return then_func; + } + ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func); + JS_FreeValue(ctx, then_func); + return ret; +} + +static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst onFinally = argv[0]; + JSValue ctor, ret; + JSValue then_funcs[2]; + JSValueConst func_data[2]; + int i; + + ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); + if (JS_IsException(ctor)) + return ctor; + if (!JS_IsFunction(ctx, onFinally)) { + then_funcs[0] = JS_DupValue(ctx, onFinally); + then_funcs[1] = JS_DupValue(ctx, onFinally); + } else { + func_data[0] = ctor; + func_data[1] = onFinally; + for(i = 0; i < 2; i++) { + then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data); + if (JS_IsException(then_funcs[i])) { + if (i == 1) + JS_FreeValue(ctx, then_funcs[0]); + JS_FreeValue(ctx, ctor); + return JS_EXCEPTION; + } + } + } + JS_FreeValue(ctx, ctor); + ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs); + JS_FreeValue(ctx, then_funcs[0]); + JS_FreeValue(ctx, then_funcs[1]); + return ret; +} + +static const JSCFunctionListEntry js_promise_funcs[] = { + JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ), + JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ), + JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ), + JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ), + JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ), + JS_CFUNC_DEF("race", 1, js_promise_race ), + //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), +}; + +static const JSCFunctionListEntry js_promise_proto_funcs[] = { + JS_CFUNC_DEF("then", 2, js_promise_then ), + JS_CFUNC_DEF("catch", 1, js_promise_catch ), + JS_CFUNC_DEF("finally", 1, js_promise_finally ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ), +}; + +/* magic = GEN_MAGIC_x */ +static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR); + JSValue promise, resolving_funcs[2]; + JSAsyncGeneratorRequest *req; + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + if (!s) { + JSValue err, res2; + JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); + err = JS_GetException(ctx); + res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&err); + JS_FreeValue(ctx, err); + JS_FreeValue(ctx, res2); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; + } + req = js_mallocz(ctx, sizeof(*req)); + if (!req) + goto fail; + req->completion_type = magic; + req->result = JS_DupValue(ctx, argv[0]); + req->promise = JS_DupValue(ctx, promise); + req->resolving_funcs[0] = resolving_funcs[0]; + req->resolving_funcs[1] = resolving_funcs[1]; + list_add_tail(&req->link, &s->queue); + if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) { + js_async_generator_resume_next(ctx, s); + } + return promise; + fail: + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_async_generator_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ), + JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ), + JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ), +}; + +static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + js_async_function_free(rt, s); + } +} + +static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + mark_func(rt, &s->header); + } +} + +static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSAsyncFromSyncIteratorData *s = + JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (s) { + JS_FreeValueRT(rt, s->sync_iter); + JS_FreeValueRT(rt, s->next_method); + js_free_rt(rt, s); + } +} + +static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSAsyncFromSyncIteratorData *s = + JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (s) { + JS_MarkValue(rt, s->sync_iter, mark_func); + JS_MarkValue(rt, s->next_method, mark_func); + } +} + +static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR); + if (s) { + js_async_generator_free(rt, s); + } +} + +static void js_async_generator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR); + struct list_head *el; + JSAsyncGeneratorRequest *req; + if (s) { + list_for_each(el, &s->queue) { + req = list_entry(el, JSAsyncGeneratorRequest, link); + JS_MarkValue(rt, req->result, mark_func); + JS_MarkValue(rt, req->promise, mark_func); + JS_MarkValue(rt, req->resolving_funcs[0], mark_func); + JS_MarkValue(rt, req->resolving_funcs[1], mark_func); + } + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && + s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { + async_func_mark(rt, &s->func_state, mark_func); + } + } +} + +static JSClassShortDef const js_async_class_def[] = { + { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */ + { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */ + { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */ + { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */ + { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */ + { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */ + { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ + { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */ + { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */ +}; + +static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s) +{ + if (s->is_active) { + async_func_free(rt, &s->func_state); + s->is_active = FALSE; + } +} + +void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s) +{ + js_async_function_terminate(rt, s); + JS_FreeValueRT(rt, s->resolving_funcs[0]); + JS_FreeValueRT(rt, s->resolving_funcs[1]); + remove_gc_object(&s->header); + js_free_rt(rt, s); +} + +static int js_async_function_resolve_create(JSContext *ctx, + JSAsyncFunctionData *s, + JSValue *resolving_funcs) +{ + int i; + JSObject *p; + for(i = 0; i < 2; i++) { + resolving_funcs[i] = + JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_ASYNC_FUNCTION_RESOLVE + i); + if (JS_IsException(resolving_funcs[i])) { + if (i == 1) + JS_FreeValue(ctx, resolving_funcs[0]); + return -1; + } + p = JS_VALUE_GET_OBJ(resolving_funcs[i]); + s->header.ref_count++; + p->u.async_function_data = s; + } + return 0; +} + +static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) +{ + JSValue func_ret, ret2; + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) { + JSValue error; + fail: + error = JS_GetException(ctx); + ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); + js_async_function_terminate(ctx->rt, s); + JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ + } else { + JSValue value; + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + if (JS_IsUndefined(func_ret)) { + /* function returned */ + ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&value); + JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ + JS_FreeValue(ctx, value); + js_async_function_terminate(ctx->rt, s); + } else { + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; + /* await */ + JS_FreeValue(ctx, func_ret); /* not used */ + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, (JSValueConst *)&value, 0); + JS_FreeValue(ctx, value); + if (JS_IsException(promise)) + goto fail; + if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { + 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; + } + } +} + +static JSValue js_async_function_resolve_call(JSContext *ctx, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSObject *p = JS_VALUE_GET_OBJ(func_obj); + JSAsyncFunctionData *s = p->u.async_function_data; + BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; + JSValueConst arg; + if (argc > 0) + arg = argv[0]; + else + arg = JS_UNDEFINED; + 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_function_resume(ctx, s); + return JS_UNDEFINED; +} + +static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) +{ + JSValue promise; + JSAsyncFunctionData *s; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->header.ref_count = 1; + add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); + s->is_active = FALSE; + s->resolving_funcs[0] = JS_UNDEFINED; + s->resolving_funcs[1] = JS_UNDEFINED; + promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); + if (JS_IsException(promise)) + goto fail; + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + fail: + JS_FreeValue(ctx, promise); + js_async_function_free(ctx->rt, s); + return JS_EXCEPTION; + } + s->is_active = TRUE; + js_async_function_resume(ctx, s); + js_async_function_free(ctx->rt, s); + return promise; +} + +static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSValue obj, func_ret; + JSAsyncGeneratorData *s; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START; + init_list_head(&s->queue); + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + goto fail; + } + /* execute the function up to 'OP_initial_yield' (no yield nor + await are possible) */ + 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_ASYNC_GENERATOR); + if (JS_IsException(obj)) + goto fail; + s->generator = JS_VALUE_GET_OBJ(obj); + JS_SetOpaque(obj, s); + return obj; + fail: + js_async_generator_free(ctx->rt, s); + return JS_EXCEPTION; +} + +/* AsyncFunction */ +static const JSCFunctionListEntry js_async_function_proto_funcs[] = { + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = { + JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ), +}; + +static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), + JS_ToBool(ctx, func_data[0])); +} + +static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx, + BOOL done) +{ + JSValueConst func_data[1]; + func_data[0] = (JSValueConst)JS_NewBool(ctx, done); + return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap, + 1, 0, 1, func_data); +} + +static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic) +{ + JSValue promise, resolving_funcs[2], value, err, method; + JSAsyncFromSyncIteratorData *s; + int done; + int is_reject; + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (!s) { + JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator"); + goto reject; + } + if (magic == GEN_MAGIC_NEXT) { + method = JS_DupValue(ctx, s->next_method); + } else { + method = JS_GetProperty(ctx, s->sync_iter, + magic == GEN_MAGIC_RETURN ? JS_ATOM_return : + JS_ATOM_throw); + if (JS_IsException(method)) + goto reject; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + if (magic == GEN_MAGIC_RETURN) { + err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE); + is_reject = 0; + } else { + err = JS_DupValue(ctx, argv[0]); + is_reject = 1; + } + goto done_resolve; + } + } + value = JS_IteratorNext2(ctx, s->sync_iter, method, + argc >= 1 ? 1 : 0, argv, &done); + JS_FreeValue(ctx, method); + if (JS_IsException(value)) + goto reject; + if (done == 2) { + JSValue obj = value; + value = JS_IteratorGetCompleteValue(ctx, obj, &done); + JS_FreeValue(ctx, obj); + if (JS_IsException(value)) + goto reject; + } + if (JS_IsException(value)) { + JSValue res2; + reject: + err = JS_GetException(ctx); + is_reject = 1; + done_resolve: + res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, + 1, (JSValueConst *)&err); + JS_FreeValue(ctx, err); + JS_FreeValue(ctx, res2); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; + } + { + JSValue value_wrapper_promise, resolve_reject[2]; + int res; + value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, (JSValueConst *)&value, 0); + if (JS_IsException(value_wrapper_promise)) { + JS_FreeValue(ctx, value); + goto reject; + } + resolve_reject[0] = + js_async_from_sync_iterator_unwrap_func_create(ctx, done); + if (JS_IsException(resolve_reject[0])) { + JS_FreeValue(ctx, value_wrapper_promise); + goto fail; + } + JS_FreeValue(ctx, value); + resolve_reject[1] = JS_UNDEFINED; + res = perform_promise_then(ctx, value_wrapper_promise, + (JSValueConst *)resolve_reject, + (JSValueConst *)resolving_funcs); + JS_FreeValue(ctx, resolve_reject[0]); + JS_FreeValue(ctx, value_wrapper_promise); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + if (res) { + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; + } + } + return promise; + fail: + JS_FreeValue(ctx, value); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ), + JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ), + JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ), +}; + +static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = { + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ), +}; + +void JS_AddIntrinsicPromise(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValue obj1; + if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) { + init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE, + countof(js_async_class_def)); + rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call; + rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call; + rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call; + rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call; + rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call; + rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call; + } + /* Promise */ + ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE], + js_promise_proto_funcs, + countof(js_promise_proto_funcs)); + obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1, + JS_CFUNC_constructor, 0); + ctx->promise_ctor = JS_DupValue(ctx, obj1); + JS_SetPropertyFunctionList(ctx, obj1, + js_promise_funcs, + countof(js_promise_funcs)); + JS_NewGlobalCConstructor2(ctx, obj1, "Promise", + ctx->class_proto[JS_CLASS_PROMISE]); + /* AsyncFunction */ + ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); + obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, + "AsyncFunction", 1, + JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC, + ctx->function_ctor); + JS_SetPropertyFunctionList(ctx, + ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], + js_async_function_proto_funcs, + countof(js_async_function_proto_funcs)); + JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], + 0, JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, obj1); + /* AsyncIteratorPrototype */ + ctx->async_iterator_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto, + js_async_iterator_proto_funcs, + countof(js_async_iterator_proto_funcs)); + /* AsyncFromSyncIteratorPrototype */ + ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] = + JS_NewObjectProto(ctx, ctx->async_iterator_proto); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR], + js_async_from_sync_iterator_proto_funcs, + countof(js_async_from_sync_iterator_proto_funcs)); + /* AsyncGeneratorPrototype */ + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] = + JS_NewObjectProto(ctx, ctx->async_iterator_proto); + JS_SetPropertyFunctionList(ctx, + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], + js_async_generator_proto_funcs, + countof(js_async_generator_proto_funcs)); + /* AsyncGeneratorFunction */ + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] = + JS_NewObjectProto(ctx, ctx->function_proto); + obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, + "AsyncGeneratorFunction", 1, + JS_CFUNC_constructor_or_func_magic, + JS_FUNC_ASYNC_GENERATOR, + ctx->function_ctor); + JS_SetPropertyFunctionList(ctx, + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], + js_async_generator_function_proto_funcs, + countof(js_async_generator_function_proto_funcs)); + JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], + JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE); + JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], + 0, JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, obj1); +} diff --git a/third_party/quickjs/proxy.c b/third_party/quickjs/proxy.c new file mode 100644 index 000000000..73cfb6cb7 --- /dev/null +++ b/third_party/quickjs/proxy.c @@ -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); +} diff --git a/third_party/quickjs/qjs.c b/third_party/quickjs/qjs.c index 9deaa5299..b3fab4a0b 100644 --- a/third_party/quickjs/qjs.c +++ b/third_party/quickjs/qjs.c @@ -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); diff --git a/third_party/quickjs/qjsc.c b/third_party/quickjs/qjsc.c index 588ba3312..707c7a230 100644 --- a/third_party/quickjs/qjsc.c +++ b/third_party/quickjs/qjsc.c @@ -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]); diff --git a/third_party/quickjs/quickjs-libc.c b/third_party/quickjs/quickjs-libc.c index c9bd6e3c5..8ebc583b5 100644 --- a/third_party/quickjs/quickjs-libc.c +++ b/third_party/quickjs/quickjs-libc.c @@ -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; diff --git a/third_party/quickjs/quickjs.c b/third_party/quickjs/quickjs.c index 7a0cc47d9..79cf233cc 100644 --- a/third_party/quickjs/quickjs.c +++ b/third_party/quickjs/quickjs.c @@ -39,6 +39,12 @@ #include "libc/calls/weirdtypes.h" #include "libc/time/struct/tm.h" #include "libc/log/log.h" +#include "third_party/quickjs/internal.h" +#include "third_party/quickjs/leb128.h" +#include "third_party/quickjs/internal.h" +#include "third_party/quickjs/internal.h" +#include "third_party/quickjs/internal.h" +#include "third_party/quickjs/internal.h" #include "third_party/quickjs/libbf.h" asm(".ident\t\"\\n\\n\ @@ -49,1181 +55,77 @@ asm(".include \"libc/disclaimer.inc\""); /* clang-format off */ -#define OPTIMIZE 1 -#define SHORT_OPCODES 1 -#if defined(EMSCRIPTEN) -#define DIRECT_DISPATCH 0 -#else -#define DIRECT_DISPATCH 1 -#endif - -#if defined(__APPLE__) -#define MALLOC_OVERHEAD 0 -#else -#define MALLOC_OVERHEAD 8 -#endif - -#if !defined(_WIN32) -/* define it if printf uses the RNDN rounding mode instead of RNDNA */ -#define CONFIG_PRINTF_RNDN -#endif - -/* define to include Atomics.* operations which depend on the OS - threads */ -#if !defined(EMSCRIPTEN) && defined(USE_WORKER) -#define CONFIG_ATOMICS -#endif - -#if !defined(EMSCRIPTEN) -/* enable stack limitation */ -#define CONFIG_STACK_CHECK -#endif - - -/* dump object free */ -//#define DUMP_FREE -//#define DUMP_CLOSURE -/* dump the bytecode of the compiled functions: combination of bits - 1: dump pass 3 final byte code - 2: dump pass 2 code - 4: dump pass 1 code - 8: dump stdlib functions - 16: dump bytecode in hex - 32: dump line number table - */ -//#define DUMP_BYTECODE (1) -/* dump the occurence of the automatic GC */ -//#define DUMP_GC -/* dump objects freed by the garbage collector */ -//#define DUMP_GC_FREE -/* dump objects leaking when freeing the runtime */ -//#define DUMP_LEAKS 1 -/* dump memory usage before running the garbage collector */ -//#define DUMP_MEM -//#define DUMP_OBJECTS /* dump objects in JS_FreeContext */ -//#define DUMP_ATOMS /* dump atoms in JS_FreeContext */ -//#define DUMP_SHAPES /* dump shapes in JS_FreeContext */ -//#define DUMP_MODULE_RESOLVE -//#define DUMP_PROMISE -//#define DUMP_READ_OBJECT - -/* test the GC by forcing it before each object allocation */ -//#define FORCE_GC_AT_MALLOC - -enum { - /* classid tag */ /* union usage | properties */ - JS_CLASS_OBJECT = 1, /* must be first */ - JS_CLASS_ARRAY, /* u.array | length */ - JS_CLASS_ERROR, - JS_CLASS_NUMBER, /* u.object_data */ - JS_CLASS_STRING, /* u.object_data */ - JS_CLASS_BOOLEAN, /* u.object_data */ - JS_CLASS_SYMBOL, /* u.object_data */ - JS_CLASS_ARGUMENTS, /* u.array | length */ - JS_CLASS_MAPPED_ARGUMENTS, /* | length */ - JS_CLASS_DATE, /* u.object_data */ - JS_CLASS_MODULE_NS, - JS_CLASS_C_FUNCTION, /* u.cfunc */ - JS_CLASS_BYTECODE_FUNCTION, /* u.func */ - JS_CLASS_BOUND_FUNCTION, /* u.bound_function */ - JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */ - JS_CLASS_GENERATOR_FUNCTION, /* u.func */ - JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */ - JS_CLASS_REGEXP, /* u.regexp */ - JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */ - JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */ - JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */ - JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */ - JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */ - JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */ - JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */ - JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */ - JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ -#ifdef CONFIG_BIGNUM - JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ - JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ -#endif - JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ - JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ - JS_CLASS_DATAVIEW, /* u.typed_array */ -#ifdef CONFIG_BIGNUM - JS_CLASS_BIG_INT, /* u.object_data */ - JS_CLASS_BIG_FLOAT, /* u.object_data */ - JS_CLASS_FLOAT_ENV, /* u.float_env */ - JS_CLASS_BIG_DECIMAL, /* u.object_data */ - JS_CLASS_OPERATOR_SET, /* u.operator_set */ -#endif - JS_CLASS_MAP, /* u.map_state */ - JS_CLASS_SET, /* u.map_state */ - JS_CLASS_WEAKMAP, /* u.map_state */ - JS_CLASS_WEAKSET, /* u.map_state */ - JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ - JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */ - JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */ - JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */ - JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */ - JS_CLASS_GENERATOR, /* u.generator_data */ - JS_CLASS_PROXY, /* u.proxy_data */ - JS_CLASS_PROMISE, /* u.promise_data */ - JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */ - JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */ - JS_CLASS_ASYNC_FUNCTION, /* u.func */ - JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */ - JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */ - JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */ - JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */ - JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ - - JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ -}; - -/* number of typed array types */ -#define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1) -static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT]; -#define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY]) - -typedef enum JSErrorEnum { - JS_EVAL_ERROR, - JS_RANGE_ERROR, - JS_REFERENCE_ERROR, - JS_SYNTAX_ERROR, - JS_TYPE_ERROR, - JS_URI_ERROR, - JS_INTERNAL_ERROR, - JS_AGGREGATE_ERROR, - - JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ -} JSErrorEnum; - -#define JS_MAX_LOCAL_VARS 65536 -#define JS_STACK_SIZE_MAX 65534 -#define JS_STRING_LEN_MAX ((1 << 30) - 1) - -#define __exception __attribute__((warn_unused_result)) - -typedef struct JSShape JSShape; -typedef struct JSString JSString; -typedef struct JSString JSAtomStruct; - -typedef enum { - JS_GC_PHASE_NONE, - JS_GC_PHASE_DECREF, - JS_GC_PHASE_REMOVE_CYCLES, -} JSGCPhaseEnum; - -typedef enum OPCodeEnum OPCodeEnum; - -#ifdef CONFIG_BIGNUM -/* function pointers are used for numeric operations so that it is - possible to remove some numeric types */ -typedef struct { - JSValue (*to_string)(JSContext *ctx, JSValueConst val); - JSValue (*from_string)(JSContext *ctx, const char *buf, - int radix, int flags, slimb_t *pexponent); - int (*unary_arith)(JSContext *ctx, - JSValue *pres, OPCodeEnum op, JSValue op1); - int (*binary_arith)(JSContext *ctx, OPCodeEnum op, - JSValue *pres, JSValue op1, JSValue op2); - int (*compare)(JSContext *ctx, OPCodeEnum op, - JSValue op1, JSValue op2); - /* only for bigfloat: */ - JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a, - int64_t exponent); - int (*mul_pow10)(JSContext *ctx, JSValue *sp); -} JSNumericOperations; -#endif - -struct JSRuntime { - JSMallocFunctions mf; - JSMallocState malloc_state; - const char *rt_info; - - int atom_hash_size; /* power of two */ - int atom_count; - int atom_size; - int atom_count_resize; /* resize hash table at this count */ - uint32_t *atom_hash; - JSAtomStruct **atom_array; - int atom_free_index; /* 0 = none */ - - int class_count; /* size of class_array */ - JSClass *class_array; - - struct list_head context_list; /* list of JSContext.link */ - /* list of JSGCObjectHeader.link. List of allocated GC objects (used - by the garbage collector) */ - struct list_head gc_obj_list; - /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */ - struct list_head gc_zero_ref_count_list; - struct list_head tmp_obj_list; /* used during GC */ - JSGCPhaseEnum gc_phase : 8; - size_t malloc_gc_threshold; -#ifdef DUMP_LEAKS - struct list_head string_list; /* list of JSString.link */ -#endif - /* stack limitation */ - uintptr_t stack_size; /* in bytes, 0 if no limit */ - uintptr_t stack_top; - uintptr_t stack_limit; /* lower stack limit */ - - JSValue current_exception; - /* true if inside an out of memory error, to avoid recursing */ - BOOL in_out_of_memory : 8; - - struct JSStackFrame *current_stack_frame; - - JSInterruptHandler *interrupt_handler; - void *interrupt_opaque; - - JSHostPromiseRejectionTracker *host_promise_rejection_tracker; - void *host_promise_rejection_tracker_opaque; - - struct list_head job_list; /* list of JSJobEntry.link */ - - JSModuleNormalizeFunc *module_normalize_func; - JSModuleLoaderFunc *module_loader_func; - void *module_loader_opaque; - - BOOL can_block : 8; /* TRUE if Atomics.wait can block */ - /* used to allocate, free and clone SharedArrayBuffers */ - JSSharedArrayBufferFunctions sab_funcs; - - /* Shape hash table */ - int shape_hash_bits; - int shape_hash_size; - int shape_hash_count; /* number of hashed shapes */ - JSShape **shape_hash; -#ifdef CONFIG_BIGNUM - bf_context_t bf_ctx; - JSNumericOperations bigint_ops; - JSNumericOperations bigfloat_ops; - JSNumericOperations bigdecimal_ops; - uint32_t operator_count; -#endif - void *user_opaque; -}; - -struct JSClass { - uint32_t class_id; /* 0 means free entry */ - JSAtom class_name; - JSClassFinalizer *finalizer; - JSClassGCMark *gc_mark; - JSClassCall *call; - /* pointers for exotic behavior, can be NULL if none are present */ - const JSClassExoticMethods *exotic; -}; - -#define JS_MODE_STRICT (1 << 0) -#define JS_MODE_STRIP (1 << 1) -#define JS_MODE_MATH (1 << 2) - -typedef struct JSStackFrame { - struct JSStackFrame *prev_frame; /* NULL if first stack frame */ - JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */ - JSValue *arg_buf; /* arguments */ - JSValue *var_buf; /* variables */ - struct list_head var_ref_list; /* list of JSVarRef.link */ - const uint8_t *cur_pc; /* only used in bytecode functions : PC of the - instruction after the call */ - int arg_count; - int js_mode; /* 0 or JS_MODE_MATH for C functions */ - /* only used in generators. Current stack pointer value. NULL if - the function is running. */ - JSValue *cur_sp; -} JSStackFrame; - -typedef enum { - JS_GC_OBJ_TYPE_JS_OBJECT, - JS_GC_OBJ_TYPE_FUNCTION_BYTECODE, - JS_GC_OBJ_TYPE_SHAPE, - JS_GC_OBJ_TYPE_VAR_REF, - JS_GC_OBJ_TYPE_ASYNC_FUNCTION, - JS_GC_OBJ_TYPE_JS_CONTEXT, -} JSGCObjectTypeEnum; - -/* header for GC objects. GC objects are C data structures with a - reference count that can reference other GC objects. JS Objects are - a particular type of GC object. */ -struct JSGCObjectHeader { - int ref_count; /* must come first, 32-bit */ - JSGCObjectTypeEnum gc_obj_type : 4; - uint8_t mark : 4; /* used by the GC */ - uint8_t dummy1; /* not used by the GC */ - uint16_t dummy2; /* not used by the GC */ - struct list_head link; -}; - -typedef struct JSVarRef { - union { - JSGCObjectHeader header; /* must come first */ - struct { - int __gc_ref_count; /* corresponds to header.ref_count */ - uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ - - /* 0 : the JSVarRef is on the stack. header.link is an element - of JSStackFrame.var_ref_list. - 1 : the JSVarRef is detached. header.link has the normal meanning - */ - uint8_t is_detached : 1; - uint8_t is_arg : 1; - uint16_t var_idx; /* index of the corresponding function variable on - the stack */ - }; - }; - JSValue *pvalue; /* pointer to the value, either on the stack or - to 'value' */ - JSValue value; /* used when the variable is no longer on the stack */ -} JSVarRef; - -#ifdef CONFIG_BIGNUM -typedef struct JSFloatEnv { - limb_t prec; - bf_flags_t flags; - unsigned int status; -} JSFloatEnv; - -/* the same structure is used for big integers and big floats. Big - integers are never infinite or NaNs */ -typedef struct JSBigFloat { - JSRefCountHeader header; /* must come first, 32-bit */ - bf_t num; -} JSBigFloat; - -typedef struct JSBigDecimal { - JSRefCountHeader header; /* must come first, 32-bit */ - bfdec_t num; -} JSBigDecimal; -#endif - -typedef enum { - JS_AUTOINIT_ID_PROTOTYPE, - JS_AUTOINIT_ID_MODULE_NS, - JS_AUTOINIT_ID_PROP, -} JSAutoInitIDEnum; - -/* must be large enough to have a negligible runtime cost and small - enough to call the interrupt callback often. */ -#define JS_INTERRUPT_COUNTER_INIT 10000 - -struct JSContext { - JSGCObjectHeader header; /* must come first */ - JSRuntime *rt; - struct list_head link; - - uint16_t binary_object_count; - int binary_object_size; - - JSShape *array_shape; /* initial shape for Array objects */ - - JSValue *class_proto; - JSValue function_proto; - JSValue function_ctor; - JSValue array_ctor; - JSValue regexp_ctor; - JSValue promise_ctor; - JSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; - JSValue iterator_proto; - JSValue async_iterator_proto; - JSValue array_proto_values; - JSValue throw_type_error; - JSValue eval_obj; - - JSValue global_obj; /* global object */ - JSValue global_var_obj; /* contains the global let/const definitions */ - - uint64_t random_state; -#ifdef CONFIG_BIGNUM - bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ - JSFloatEnv fp_env; /* global FP environment */ - BOOL bignum_ext : 8; /* enable math mode */ - BOOL allow_operator_overloading : 8; -#endif - /* when the counter reaches zero, JSRutime.interrupt_handler is called */ - int interrupt_counter; - BOOL is_error_property_enabled; - - struct list_head loaded_modules; /* list of JSModuleDef.link */ - - /* if NULL, RegExp compilation is not supported */ - JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern, - JSValueConst flags); - /* if NULL, eval is not supported */ - JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj, - const char *input, size_t input_len, - const char *filename, int flags, int scope_idx); - void *user_opaque; -}; - -typedef union JSFloat64Union { - double d; - uint64_t u64; - uint32_t u32[2]; -} JSFloat64Union; - -enum { - JS_ATOM_TYPE_STRING = 1, - JS_ATOM_TYPE_GLOBAL_SYMBOL, - JS_ATOM_TYPE_SYMBOL, - JS_ATOM_TYPE_PRIVATE, -}; - -enum { - JS_ATOM_HASH_SYMBOL, - JS_ATOM_HASH_PRIVATE, -}; - -typedef enum { - JS_ATOM_KIND_STRING, - JS_ATOM_KIND_SYMBOL, - JS_ATOM_KIND_PRIVATE, -} JSAtomKindEnum; - -#define JS_ATOM_HASH_MASK ((1 << 30) - 1) - -struct JSString { - JSRefCountHeader header; /* must come first, 32-bit */ - uint32_t len : 31; - uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ - /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, - for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3 - XXX: could change encoding to have one more bit in hash */ - uint32_t hash : 30; - uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ - uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ -#ifdef DUMP_LEAKS - struct list_head link; /* string list */ -#endif - union { - uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */ - uint16_t str16[0]; - } u; -}; - -typedef struct JSClosureVar { - uint8_t is_local : 1; - uint8_t is_arg : 1; - uint8_t is_const : 1; - uint8_t is_lexical : 1; - uint8_t var_kind : 4; /* see JSVarKindEnum */ - /* 8 bits available */ - uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the - parent function. otherwise: index to a closure - variable of the parent function */ - JSAtom var_name; -} JSClosureVar; - -#define ARG_SCOPE_INDEX 1 -#define ARG_SCOPE_END (-2) - -typedef struct JSVarScope { - int parent; /* index into fd->scopes of the enclosing scope */ - int first; /* index into fd->vars of the last variable in this scope */ -} JSVarScope; - -typedef enum { - /* XXX: add more variable kinds here instead of using bit fields */ - JS_VAR_NORMAL, - JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */ - JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator - function declaration */ - JS_VAR_CATCH, - JS_VAR_FUNCTION_NAME, /* function expression name */ - JS_VAR_PRIVATE_FIELD, - JS_VAR_PRIVATE_METHOD, - JS_VAR_PRIVATE_GETTER, - JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */ - JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */ -} JSVarKindEnum; - -/* XXX: could use a different structure in bytecode functions to save - memory */ -typedef struct JSVarDef { - JSAtom var_name; - /* index into fd->scopes of this variable lexical scope */ - int scope_level; - /* during compilation: - - if scope_level = 0: scope in which the variable is defined - - if scope_level != 0: index into fd->vars of the next - variable in the same or enclosing lexical scope - in a bytecode function: - index into fd->vars of the next - variable in the same or enclosing lexical scope - */ - int scope_next; - uint8_t is_const : 1; - uint8_t is_lexical : 1; - uint8_t is_captured : 1; - uint8_t var_kind : 4; /* see JSVarKindEnum */ - /* only used during compilation: function pool index for lexical - variables with var_kind = - JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of - the definition of the 'var' variables (they have scope_level = - 0) */ - int func_pool_idx : 24; /* only used during compilation : index in - the constant pool for hoisted function - definition */ -} JSVarDef; - -/* for the encoding of the pc2line table */ -#define PC2LINE_BASE (-1) -#define PC2LINE_RANGE 5 -#define PC2LINE_OP_FIRST 1 -#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE) - -typedef enum JSFunctionKindEnum { - JS_FUNC_NORMAL = 0, - JS_FUNC_GENERATOR = (1 << 0), - JS_FUNC_ASYNC = (1 << 1), - JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), -} JSFunctionKindEnum; - -typedef struct JSFunctionBytecode { - JSGCObjectHeader header; /* must come first */ - uint8_t js_mode; - uint8_t has_prototype : 1; /* true if a prototype field is necessary */ - uint8_t has_simple_parameter_list : 1; - uint8_t is_derived_class_constructor : 1; - /* true if home_object needs to be initialized */ - uint8_t need_home_object : 1; - uint8_t func_kind : 2; - uint8_t new_target_allowed : 1; - uint8_t super_call_allowed : 1; - uint8_t super_allowed : 1; - uint8_t arguments_allowed : 1; - uint8_t has_debug : 1; - uint8_t backtrace_barrier : 1; /* stop backtrace on this function */ - uint8_t read_only_bytecode : 1; - /* XXX: 4 bits available */ - uint8_t *byte_code_buf; /* (self pointer) */ - int byte_code_len; - JSAtom func_name; - JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */ - JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */ - uint16_t arg_count; - uint16_t var_count; - uint16_t defined_arg_count; /* for length function property */ - uint16_t stack_size; /* maximum stack size */ - JSContext *realm; /* function realm */ - JSValue *cpool; /* constant pool (self pointer) */ - int cpool_count; - int closure_var_count; - struct { - /* debug info, move to separate structure to save memory? */ - JSAtom filename; - int line_num; - int source_len; - int pc2line_len; - uint8_t *pc2line_buf; - char *source; - } debug; -} JSFunctionBytecode; - -typedef struct JSBoundFunction { - JSValue func_obj; - JSValue this_val; - int argc; - JSValue argv[0]; -} JSBoundFunction; - -typedef enum JSIteratorKindEnum { - JS_ITERATOR_KIND_KEY, - JS_ITERATOR_KIND_VALUE, - JS_ITERATOR_KIND_KEY_AND_VALUE, -} JSIteratorKindEnum; - -typedef struct JSForInIterator { - JSValue obj; - BOOL is_array; - uint32_t array_length; - uint32_t idx; -} JSForInIterator; - -typedef struct JSRegExp { - JSString *pattern; - JSString *bytecode; /* also contains the flags */ -} JSRegExp; - -typedef struct JSProxyData { - JSValue target; - JSValue handler; - uint8_t is_func; - uint8_t is_revoked; -} JSProxyData; - -typedef struct JSArrayBuffer { - int byte_length; /* 0 if detached */ - uint8_t detached; - uint8_t shared; /* if shared, the array buffer cannot be detached */ - uint8_t *data; /* NULL if detached */ - struct list_head array_list; - void *opaque; - JSFreeArrayBufferDataFunc *free_func; -} JSArrayBuffer; - -typedef struct JSTypedArray { - struct list_head link; /* link to arraybuffer */ - JSObject *obj; /* back pointer to the TypedArray/DataView object */ - JSObject *buffer; /* based array buffer */ - uint32_t offset; /* offset in the array buffer */ - uint32_t length; /* length in the array buffer */ -} JSTypedArray; - -typedef struct JSAsyncFunctionState { - JSValue this_val; /* 'this' generator argument */ - int argc; /* number of function arguments */ - BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */ - JSStackFrame frame; -} JSAsyncFunctionState; - -/* XXX: could use an object instead to avoid the - JS_TAG_ASYNC_FUNCTION tag for the GC */ -typedef struct JSAsyncFunctionData { - JSGCObjectHeader header; /* must come first */ - JSValue resolving_funcs[2]; - BOOL is_active; /* true if the async function state is valid */ - JSAsyncFunctionState func_state; -} JSAsyncFunctionData; - -typedef enum { - /* binary operators */ - JS_OVOP_ADD, - JS_OVOP_SUB, - JS_OVOP_MUL, - JS_OVOP_DIV, - JS_OVOP_MOD, - JS_OVOP_POW, - JS_OVOP_OR, - JS_OVOP_AND, - JS_OVOP_XOR, - JS_OVOP_SHL, - JS_OVOP_SAR, - JS_OVOP_SHR, - JS_OVOP_EQ, - JS_OVOP_LESS, - - JS_OVOP_BINARY_COUNT, - /* unary operators */ - JS_OVOP_POS = JS_OVOP_BINARY_COUNT, - JS_OVOP_NEG, - JS_OVOP_INC, - JS_OVOP_DEC, - JS_OVOP_NOT, - - JS_OVOP_COUNT, -} JSOverloadableOperatorEnum; - -typedef struct { - uint32_t operator_index; - JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */ -} JSBinaryOperatorDefEntry; - -typedef struct { - int count; - JSBinaryOperatorDefEntry *tab; -} JSBinaryOperatorDef; - -typedef struct { - uint32_t operator_counter; - BOOL is_primitive; /* OperatorSet for a primitive type */ - /* NULL if no operator is defined */ - JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */ - JSBinaryOperatorDef left; - JSBinaryOperatorDef right; -} JSOperatorSetData; - -typedef struct JSReqModuleEntry { - JSAtom module_name; - JSModuleDef *module; /* used using resolution */ -} JSReqModuleEntry; - -typedef enum JSExportTypeEnum { - JS_EXPORT_TYPE_LOCAL, - JS_EXPORT_TYPE_INDIRECT, -} JSExportTypeEnum; - -typedef struct JSExportEntry { - union { - struct { - int var_idx; /* closure variable index */ - JSVarRef *var_ref; /* if != NULL, reference to the variable */ - } local; /* for local export */ - int req_module_idx; /* module for indirect export */ - } u; - JSExportTypeEnum export_type; - JSAtom local_name; /* '*' if export ns from. not used for local - export after compilation */ - JSAtom export_name; /* exported variable name */ -} JSExportEntry; - -typedef struct JSStarExportEntry { - int req_module_idx; /* in req_module_entries */ -} JSStarExportEntry; - -typedef struct JSImportEntry { - int var_idx; /* closure variable index */ - JSAtom import_name; - int req_module_idx; /* in req_module_entries */ -} JSImportEntry; - -struct JSModuleDef { - JSRefCountHeader header; /* must come first, 32-bit */ - JSAtom module_name; - struct list_head link; - - JSReqModuleEntry *req_module_entries; - int req_module_entries_count; - int req_module_entries_size; - - JSExportEntry *export_entries; - int export_entries_count; - int export_entries_size; - - JSStarExportEntry *star_export_entries; - int star_export_entries_count; - int star_export_entries_size; - - JSImportEntry *import_entries; - int import_entries_count; - int import_entries_size; - - JSValue module_ns; - JSValue func_obj; /* only used for JS modules */ - JSModuleInitFunc *init_func; /* only used for C modules */ - BOOL resolved : 8; - BOOL func_created : 8; - BOOL instantiated : 8; - BOOL evaluated : 8; - BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */ - /* true if evaluation yielded an exception. It is saved in - eval_exception */ - BOOL eval_has_exception : 8; - JSValue eval_exception; - JSValue meta_obj; /* for import.meta */ -}; - -typedef struct JSJobEntry { - struct list_head link; - JSContext *ctx; - JSJobFunc *job_func; - int argc; - JSValue argv[0]; -} JSJobEntry; - -typedef struct JSProperty { - union { - JSValue value; /* JS_PROP_NORMAL */ - struct { /* JS_PROP_GETSET */ - JSObject *getter; /* NULL if undefined */ - JSObject *setter; /* NULL if undefined */ - } getset; - JSVarRef *var_ref; /* JS_PROP_VARREF */ - struct { /* JS_PROP_AUTOINIT */ - /* in order to use only 2 pointers, we compress the realm - and the init function pointer */ - uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x) - in the 2 low bits */ - void *opaque; - } init; - } u; -} JSProperty; - -#define JS_PROP_INITIAL_SIZE 2 -#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ -#define JS_ARRAY_INITIAL_SIZE 2 - -typedef struct JSShapeProperty { - uint32_t hash_next : 26; /* 0 if last in list */ - uint32_t flags : 6; /* JS_PROP_XXX */ - JSAtom atom; /* JS_ATOM_NULL = free property entry */ -} JSShapeProperty; - -struct JSShape { - /* hash table of size hash_mask + 1 before the start of the - structure (see prop_hash_end()). */ - JSGCObjectHeader header; - /* true if the shape is inserted in the shape hash table. If not, - JSShape.hash is not valid */ - uint8_t is_hashed; - /* If true, the shape may have small array index properties 'n' with 0 - <= n <= 2^31-1. If false, the shape is guaranteed not to have - small array index properties */ - uint8_t has_small_array_index; - uint32_t hash; /* current hash value */ - uint32_t prop_hash_mask; - int prop_size; /* allocated properties */ - int prop_count; /* include deleted properties */ - int deleted_prop_count; - JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ - JSObject *proto; - JSShapeProperty prop[0]; /* prop_size elements */ -}; - -struct JSObject { - union { - JSGCObjectHeader header; - struct { - int __gc_ref_count; /* corresponds to header.ref_count */ - uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ - - uint8_t extensible : 1; - uint8_t free_mark : 1; /* only used when freeing objects with cycles */ - uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ - uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */ - uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ - uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */ - uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ - uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ - uint16_t class_id; /* see JS_CLASS_x */ - }; - }; - /* byte offsets: 16/24 */ - JSShape *shape; /* prototype and property names + flag */ - JSProperty *prop; /* array of properties */ - /* byte offsets: 24/40 */ - struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */ - /* byte offsets: 28/48 */ - union { - void *opaque; - struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ - struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ - struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ - struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */ - struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ -#ifdef CONFIG_BIGNUM - struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */ - struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */ -#endif - struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ - struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ - struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ - struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ - struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */ - struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ - struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */ - struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */ - struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */ - struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ - struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */ - struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */ - /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */ - struct JSFunctionBytecode *function_bytecode; - JSVarRef **var_refs; - JSObject *home_object; /* for 'super' access */ - } func; - struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */ - JSContext *realm; - JSCFunctionType c_function; - uint8_t length; - uint8_t cproto; - int16_t magic; - } cfunc; - /* array part for fast arrays and typed arrays */ - struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ - union { - uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ - struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ - } u1; - union { - JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ - void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ - int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */ - uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */ - int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */ - uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */ - int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */ - uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ - int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ - uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ - float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ - double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ - } u; - uint32_t count; /* <= 2^31-1. 0 for a detached typed array */ - } array; /* 12/20 bytes */ - JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ - JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ - } u; - /* byte sizes: 40/48/72 */ -}; -enum { - __JS_ATOM_NULL = JS_ATOM_NULL, -#define DEF(name, str) JS_ATOM_ ## name, -#include "third_party/quickjs/quickjs-atom.inc" -#undef DEF - JS_ATOM_END, -}; -#define JS_ATOM_LAST_KEYWORD JS_ATOM_super -#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield - static const char js_atom_init[] = #define DEF(name, str) str "\0" #include "third_party/quickjs/quickjs-atom.inc" #undef DEF ; -typedef enum OPCodeFormat { -#define FMT(f) OP_FMT_ ## f, -#define DEF(id, size, n_pop, n_push, f) -#include "third_party/quickjs/quickjs-opcode.inc" -#undef DEF -#undef FMT -} OPCodeFormat; - -enum OPCodeEnum { -#define FMT(f) -#define DEF(id, size, n_pop, n_push, f) OP_ ## id, -#define def(id, size, n_pop, n_push, f) -#include "third_party/quickjs/quickjs-opcode.inc" -#undef def -#undef DEF -#undef FMT - OP_COUNT, /* excluding temporary opcodes */ - /* temporary opcodes : overlap with the short opcodes */ - OP_TEMP_START = OP_nop + 1, - OP___dummy = OP_TEMP_START - 1, -#define FMT(f) -#define DEF(id, size, n_pop, n_push, f) -#define def(id, size, n_pop, n_push, f) OP_ ## id, -#include "third_party/quickjs/quickjs-opcode.inc" -#undef def -#undef DEF -#undef FMT - OP_TEMP_END, -}; - static int JS_InitAtoms(JSRuntime *rt); static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, int atom_type); -static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p); -static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); -static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags); -static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags); -static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, JSValueConst new_target, - int argc, JSValue *argv, int flags); -static JSValue JS_CallConstructorInternal(JSContext *ctx, - JSValueConst func_obj, - JSValueConst new_target, - int argc, JSValue *argv, int flags); -static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv); -static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, - int argc, JSValueConst *argv); -static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, - JSValue val, BOOL is_array_ctor); -static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, - JSValueConst val, int flags, int scope_idx); static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); static __maybe_unused void JS_DumpString(JSRuntime *rt, - const JSString *p); + const JSString *p); static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, - JSValueConst val); + JSValueConst val); static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val); static __maybe_unused void JS_PrintValue(JSContext *ctx, - const char *str, - JSValueConst val); + const char *str, + JSValueConst val); static __maybe_unused void JS_DumpShapes(JSRuntime *rt); -static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); -static void js_array_finalizer(JSRuntime *rt, JSValue val); -static void js_array_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_object_data_finalizer(JSRuntime *rt, JSValue val); -static void js_object_data_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); static void js_c_function_finalizer(JSRuntime *rt, JSValue val); static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val); -static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); static void js_bound_function_finalizer(JSRuntime *rt, JSValue val); static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); + JS_MarkFunc *mark_func); static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val); static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_regexp_finalizer(JSRuntime *rt, JSValue val); -static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val); -static void js_typed_array_finalizer(JSRuntime *rt, JSValue val); -static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); + JS_MarkFunc *mark_func); static void js_proxy_finalizer(JSRuntime *rt, JSValue val); static void js_proxy_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_map_finalizer(JSRuntime *rt, JSValue val); -static void js_map_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_generator_finalizer(JSRuntime *rt, JSValue obj); -static void js_generator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); + JS_MarkFunc *mark_func); static void js_promise_finalizer(JSRuntime *rt, JSValue val); static void js_promise_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); + JS_MarkFunc *mark_func); static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val); static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); + JS_MarkFunc *mark_func); #ifdef CONFIG_BIGNUM static void js_operator_set_finalizer(JSRuntime *rt, JSValue val); static void js_operator_set_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); #endif -static JSValue JS_ToStringFree(JSContext *ctx, JSValue val); -static int JS_ToBoolFree(JSContext *ctx, JSValue val); -static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val); -static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val); -static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val); -static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, - JSValueConst flags); -static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, - JSValue pattern, JSValue bc); static void gc_decref(JSRuntime *rt); static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def, JSAtom name); -typedef enum JSStrictEqModeEnum { - JS_EQ_STRICT, - JS_EQ_SAME_VALUE, - JS_EQ_SAME_VALUE_ZERO, -} JSStrictEqModeEnum; - -static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, - JSStrictEqModeEnum eq_mode); -static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2); -static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2); -static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2); -static JSValue JS_ToObject(JSContext *ctx, JSValueConst val); -static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val); -static JSProperty *add_property(JSContext *ctx, - JSObject *p, JSAtom prop, int prop_flags); #ifdef CONFIG_BIGNUM static void js_float_env_finalizer(JSRuntime *rt, JSValue val); -static JSValue JS_NewBigFloat(JSContext *ctx); -static inline bf_t *JS_GetBigFloat(JSValueConst val) -{ - JSBigFloat *p = JS_VALUE_GET_PTR(val); - return &p->num; -} -static JSValue JS_NewBigDecimal(JSContext *ctx); -static inline bfdec_t *JS_GetBigDecimal(JSValueConst val) -{ - JSBigDecimal *p = JS_VALUE_GET_PTR(val); - return &p->num; -} -static JSValue JS_NewBigInt(JSContext *ctx); -static inline bf_t *JS_GetBigInt(JSValueConst val) -{ - JSBigFloat *p = JS_VALUE_GET_PTR(val); - return &p->num; -} -static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, - BOOL convert_to_safe_integer); -static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val); -static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); -static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val); -static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf); -static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val); static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, BOOL allow_null_or_undefined); -static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val); #endif -static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); -static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); -static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, - JSValueConst proto_val, BOOL throw_flag); -static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); -static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj); -static int js_proxy_isArray(JSContext *ctx, JSValueConst obj); static int JS_CreateProperty(JSContext *ctx, JSObject *p, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags); -static int js_string_memcmp(const JSString *p1, const JSString *p2, int len); -static void reset_weak_ref(JSRuntime *rt, JSObject *p); -static JSValue js_array_buffer_constructor3(JSContext *ctx, - JSValueConst new_target, - uint64_t len, JSClassID class_id, - uint8_t *buf, - JSFreeArrayBufferDataFunc *free_func, - void *opaque, BOOL alloc_flag); -static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj); -static JSValue js_typed_array_constructor(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int classid); -static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p); -static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p); -static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); -static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, - BOOL is_arg); -static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int flags); -static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val); -static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, const char *input, size_t input_len, const char *filename, int flags, int scope_idx); -static void js_free_module_def(JSContext *ctx, JSModuleDef *m); -static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, - JS_MarkFunc *mark_func); -static JSValue js_import_meta(JSContext *ctx); static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier); -static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); static JSValue js_new_promise_capability(JSContext *ctx, JSValue *resolving_funcs, JSValueConst ctor); -static __exception int perform_promise_then(JSContext *ctx, - JSValueConst promise, - JSValueConst *resolve_reject, - JSValueConst *cap_resolving_funcs); -static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); -static int js_string_compare(JSContext *ctx, - const JSString *p1, const JSString *p2); static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val); -static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, - JSValue prop, JSValue val, int flags); static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val); static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val); -static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val); -static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, - JSObject *p, JSAtom prop); -static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc); -static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, - JS_MarkFunc *mark_func); static void JS_AddIntrinsicBasicObjects(JSContext *ctx); -static void js_free_shape(JSRuntime *rt, JSShape *sh); -static void js_free_shape_null(JSRuntime *rt, JSShape *sh); static int js_shape_prepare_update(JSContext *ctx, JSObject *p, JSShapeProperty **pprs); -static int init_shape_hash(JSRuntime *rt); -static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, - JSValueConst obj); -static __exception int js_get_length64(JSContext *ctx, int64_t *pres, - JSValueConst obj); -static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len); -static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, - JSValueConst array_arg); -static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, - JSValue **arrpp, uint32_t *countp); static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, JSValueConst sync_iter); static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val); @@ -1232,11 +134,6 @@ static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_val, int argc, JSValueConst *argv, int flags); -static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val); -static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, - JSGCObjectTypeEnum type); -static void remove_gc_object(JSGCObjectHeader *h); -static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s); static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); @@ -1250,26 +147,6 @@ static const JSClassExoticMethods js_proxy_exotic_methods; static const JSClassExoticMethods js_module_ns_exotic_methods; static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT; -static 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); - } -} - static size_t js_malloc_usable_size_unknown(const void *ptr) { return 0; @@ -1318,7 +195,7 @@ void *js_malloc(JSContext *ctx, size_t size) { void *ptr; ptr = js_malloc_rt(ctx->rt, size); - if (unlikely(!ptr)) { + if (UNLIKELY(!ptr)) { JS_ThrowOutOfMemory(ctx); return NULL; } @@ -1330,7 +207,7 @@ void *js_mallocz(JSContext *ctx, size_t size) { void *ptr; ptr = js_mallocz_rt(ctx->rt, size); - if (unlikely(!ptr)) { + if (UNLIKELY(!ptr)) { JS_ThrowOutOfMemory(ctx); return NULL; } @@ -1347,7 +224,7 @@ void *js_realloc(JSContext *ctx, void *ptr, size_t size) { void *ret; ret = js_realloc_rt(ctx->rt, ptr, size); - if (unlikely(!ret && size != 0)) { + if (UNLIKELY(!ret && size != 0)) { JS_ThrowOutOfMemory(ctx); return NULL; } @@ -1359,7 +236,7 @@ void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack) { void *ret; ret = js_realloc_rt(ctx->rt, ptr, size); - if (unlikely(!ret && size != 0)) { + if (UNLIKELY(!ret && size != 0)) { JS_ThrowOutOfMemory(ctx); return NULL; } @@ -1392,8 +269,8 @@ char *js_strdup(JSContext *ctx, const char *str) return js_strndup(ctx, str, strlen(str)); } -static no_inline int js_realloc_array(JSContext *ctx, void **parray, - int elem_size, int *psize, int req_size) +int js_realloc_array(JSContext *ctx, void **parray, + int elem_size, int *psize, int req_size) { int new_size; size_t slack; @@ -1409,32 +286,13 @@ static no_inline int js_realloc_array(JSContext *ctx, void **parray, return 0; } -/* resize the array and update its size if req_size > *psize */ -static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size, - int *psize, int req_size) +static void js_float_env_finalizer(JSRuntime *rt, JSValue val) { - if (unlikely(req_size > *psize)) - return js_realloc_array(ctx, parray, elem_size, psize, req_size); - else - return 0; + JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV); + js_free_rt(rt, fe); } -static inline void js_dbuf_init(JSContext *ctx, DynBuf *s) -{ - dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt); -} - -static inline int is_digit(int c) { - return c >= '0' && c <= '9'; -} - -typedef struct JSClassShortDef { - JSAtom class_name; - JSClassFinalizer *finalizer; - JSClassGCMark *gc_mark; -} JSClassShortDef; - -static JSClassShortDef const js_std_class_def[] = { +JSClassShortDef const js_std_class_def[] = { { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */ { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */ { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */ @@ -1488,12 +346,10 @@ static JSClassShortDef const js_std_class_def[] = { { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */ }; -static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, - int start, int count) +int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, int start, int count) { JSClassDef cm_s, *cm = &cm_s; int i, class_id; - for(i = 0; i < count; i++) { class_id = i + start; memset(cm, 0, sizeof(*cm)); @@ -1563,41 +419,13 @@ static void set_dummy_numeric_ops(JSNumericOperations *ops) #endif /* CONFIG_BIGNUM */ -#if !defined(CONFIG_STACK_CHECK) -/* no stack limitation */ -static inline uintptr_t js_get_stack_pointer(void) -{ - return 0; -} - -static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) -{ - return FALSE; -} -#else -/* Note: OS and CPU dependent */ -static inline uintptr_t js_get_stack_pointer(void) -{ - return (uintptr_t)__builtin_frame_address(0); -} - -static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) -{ - uintptr_t sp; - sp = js_get_stack_pointer() - alloca_size; - return unlikely(sp < rt->stack_limit); -} -#endif - JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) { JSRuntime *rt; JSMallocState ms; - memset(&ms, 0, sizeof(ms)); ms.opaque = opaque; ms.malloc_limit = -1; - rt = mf->js_malloc(&ms, sizeof(JSRuntime)); if (!rt) return NULL; @@ -1609,27 +437,22 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) } rt->malloc_state = ms; rt->malloc_gc_threshold = 256 * 1024; - #ifdef CONFIG_BIGNUM bf_context_init(&rt->bf_ctx, js_bf_realloc, rt); set_dummy_numeric_ops(&rt->bigint_ops); set_dummy_numeric_ops(&rt->bigfloat_ops); set_dummy_numeric_ops(&rt->bigdecimal_ops); #endif - init_list_head(&rt->context_list); init_list_head(&rt->gc_obj_list); init_list_head(&rt->gc_zero_ref_count_list); rt->gc_phase = JS_GC_PHASE_NONE; - #ifdef DUMP_LEAKS init_list_head(&rt->string_list); #endif init_list_head(&rt->job_list); - if (JS_InitAtoms(rt)) goto fail; - /* create the object, array and function classes */ if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT, countof(js_std_class_def)) < 0) @@ -1637,19 +460,15 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods; rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods; rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods; - rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function; rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call; rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function; rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call; if (init_shape_hash(rt)) goto fail; - rt->stack_size = JS_DEFAULT_STACK_SIZE; JS_UpdateStackTop(rt); - rt->current_exception = JS_NULL; - return rt; fail: JS_FreeRuntime(rt); @@ -1686,17 +505,13 @@ static inline size_t js_def_malloc_usable_size(void *ptr) static void *js_def_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); if (!ptr) return NULL; - s->malloc_count++; s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; return ptr; @@ -1706,7 +521,6 @@ static void js_def_free(JSMallocState *s, void *ptr) { if (!ptr) return; - s->malloc_count--; s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; free(ptr); @@ -1801,7 +615,6 @@ int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, JSRuntime *rt = ctx->rt; JSJobEntry *e; int i; - e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue)); if (!e) return -1; @@ -1828,12 +641,10 @@ int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx) JSJobEntry *e; JSValue res; int i, ret; - if (list_empty(&rt->job_list)) { *pctx = NULL; return 0; } - /* get the first pending job and execute it */ e = list_entry(rt->job_list.next, JSJobEntry, link); list_del(&e->link); @@ -1851,66 +662,6 @@ int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx) return ret; } -static inline uint32_t atom_get_free(const JSAtomStruct *p) -{ - return (uintptr_t)p >> 1; -} - -static inline BOOL atom_is_free(const JSAtomStruct *p) -{ - return (uintptr_t)p & 1; -} - -static inline JSAtomStruct *atom_set_free(uint32_t v) -{ - return (JSAtomStruct *)(((uintptr_t)v << 1) | 1); -} - -/* Note: the string contents are uninitialized */ -static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char) -{ - JSString *str; - str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char); - if (unlikely(!str)) - return NULL; - str->header.ref_count = 1; - str->is_wide_char = is_wide_char; - str->len = max_len; - str->atom_type = 0; - str->hash = 0; /* optional but costless */ - str->hash_next = 0; /* optional */ -#ifdef DUMP_LEAKS - list_add_tail(&str->link, &rt->string_list); -#endif - return str; -} - -static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char) -{ - JSString *p; - p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char); - if (unlikely(!p)) { - JS_ThrowOutOfMemory(ctx); - return NULL; - } - return p; -} - -/* same as JS_FreeValueRT() but faster */ -static inline void js_free_string(JSRuntime *rt, JSString *str) -{ - if (--str->header.ref_count <= 0) { - if (str->atom_type) { - JS_FreeAtomStruct(rt, str); - } else { -#ifdef DUMP_LEAKS - list_del(&str->link); -#endif - js_free_rt(rt, str); - } - } -} - void JS_SetRuntimeInfo(JSRuntime *rt, const char *s) { if (rt) @@ -1921,9 +672,7 @@ void JS_FreeRuntime(JSRuntime *rt) { struct list_head *el, *el1; int i; - JS_FreeValueRT(rt, rt->current_exception); - list_for_each_safe(el, el1, &rt->job_list) { JSJobEntry *e = list_entry(el, JSJobEntry, link); for(i = 0; i < e->argc; i++) @@ -1931,16 +680,13 @@ void JS_FreeRuntime(JSRuntime *rt) js_free_rt(rt, e); } init_list_head(&rt->job_list); - JS_RunGC(rt); - #ifdef DUMP_LEAKS /* leaking objects */ { BOOL header_done; JSGCObjectHeader *p; int count; - /* remove the internal refcounts to display only the object referenced externally */ list_for_each(el, &rt->gc_obj_list) { @@ -1948,7 +694,6 @@ void JS_FreeRuntime(JSRuntime *rt) p->mark = 0; } gc_decref(rt); - header_done = FALSE; list_for_each(el, &rt->gc_obj_list) { p = list_entry(el, JSGCObjectHeader, link); @@ -1961,7 +706,6 @@ void JS_FreeRuntime(JSRuntime *rt) JS_DumpGCObject(rt, p); } } - count = 0; list_for_each(el, &rt->gc_obj_list) { p = list_entry(el, JSGCObjectHeader, link); @@ -1974,7 +718,6 @@ void JS_FreeRuntime(JSRuntime *rt) } #endif assert(list_empty(&rt->gc_obj_list)); - /* free the classes */ for(i = 0; i < rt->class_count; i++) { JSClass *cl = &rt->class_array[i]; @@ -1983,16 +726,13 @@ void JS_FreeRuntime(JSRuntime *rt) } } js_free_rt(rt, rt->class_array); - #ifdef CONFIG_BIGNUM bf_context_end(&rt->bf_ctx); #endif - #ifdef DUMP_LEAKS /* only the atoms defined in JS_InitAtoms() should be left */ { BOOL header_done = FALSE; - for(i = 0; i < rt->atom_size; i++) { JSAtomStruct *p = rt->atom_array[i]; if (!atom_is_free(p) /* && p->str*/) { @@ -2045,7 +785,6 @@ void JS_FreeRuntime(JSRuntime *rt) printf("\n"); } #endif - /* free the atoms */ for(i = 0; i < rt->atom_size; i++) { JSAtomStruct *p = rt->atom_array[i]; @@ -2098,7 +837,6 @@ void JS_FreeRuntime(JSRuntime *rt) } } #endif - { JSMallocState ms = rt->malloc_state; rt->mf.js_free(&ms, rt); @@ -2109,13 +847,11 @@ JSContext *JS_NewContextRaw(JSRuntime *rt) { JSContext *ctx; int i; - ctx = js_mallocz_rt(rt, sizeof(JSContext)); if (!ctx) return NULL; ctx->header.ref_count = 1; add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT); - ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) * rt->class_count); if (!ctx->class_proto) { @@ -2135,7 +871,6 @@ JSContext *JS_NewContextRaw(JSRuntime *rt) ctx->regexp_ctor = JS_NULL; ctx->promise_ctor = JS_NULL; init_list_head(&ctx->loaded_modules); - JS_AddIntrinsicBasicObjects(ctx); return ctx; } @@ -2143,11 +878,9 @@ JSContext *JS_NewContextRaw(JSRuntime *rt) JSContext *JS_NewContext(JSRuntime *rt) { JSContext *ctx; - ctx = JS_NewContextRaw(rt); if (!ctx) return NULL; - JS_AddIntrinsicBaseObjects(ctx); JS_AddIntrinsicDate(ctx); JS_AddIntrinsicEval(ctx); @@ -2174,16 +907,6 @@ void JS_SetContextOpaque(JSContext *ctx, void *opaque) ctx->user_opaque = opaque; } -/* set the new value and free the old value after (freeing the value - can reallocate the object data) */ -static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val) -{ - JSValue old_val; - old_val = *pval; - *pval = new_val; - JS_FreeValue(ctx, old_val); -} - void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj) { JSRuntime *rt = ctx->rt; @@ -2224,54 +947,13 @@ JSContext *JS_DupContext(JSContext *ctx) return ctx; } -/* used by the GC */ -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); -} - void JS_FreeContext(JSContext *ctx) { JSRuntime *rt = ctx->rt; int i; - if (--ctx->header.ref_count > 0) return; assert(ctx->header.ref_count == 0); - #ifdef DUMP_ATOMS JS_DumpAtoms(ctx->rt); #endif @@ -2298,15 +980,11 @@ void JS_FreeContext(JSContext *ctx) JS_DumpMemoryUsage(stdout, &stats, rt); } #endif - js_free_modules(ctx, JS_FREE_MODULE_ALL); - JS_FreeValue(ctx, ctx->global_obj); JS_FreeValue(ctx, ctx->global_var_obj); - JS_FreeValue(ctx, ctx->throw_type_error); JS_FreeValue(ctx, ctx->eval_obj); - JS_FreeValue(ctx, ctx->array_proto_values); for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { JS_FreeValue(ctx, ctx->native_error_proto[i]); @@ -2322,9 +1000,7 @@ void JS_FreeContext(JSContext *ctx) JS_FreeValue(ctx, ctx->regexp_ctor); JS_FreeValue(ctx, ctx->function_ctor); JS_FreeValue(ctx, ctx->function_proto); - js_free_shape_null(ctx->rt, ctx->array_shape); - list_del(&ctx->link); remove_gc_object(&ctx->header); js_free_rt(ctx->rt, ctx); @@ -2356,104 +1032,12 @@ void JS_UpdateStackTop(JSRuntime *rt) update_stack_limit(rt); } -static inline BOOL is_strict_mode(JSContext *ctx) -{ - JSStackFrame *sf = ctx->rt->current_stack_frame; - return (sf && (sf->js_mode & JS_MODE_STRICT)); -} - -#ifdef CONFIG_BIGNUM -static inline BOOL is_math_mode(JSContext *ctx) -{ - JSStackFrame *sf = ctx->rt->current_stack_frame; - return (sf && (sf->js_mode & JS_MODE_MATH)); -} -#endif - /* JSAtom support */ -#define JS_ATOM_TAG_INT (1U << 31) -#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1) -#define JS_ATOM_MAX ((1U << 30) - 1) - -/* return the max count from the hash size */ -#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) - -static inline BOOL __JS_AtomIsConst(JSAtom v) -{ -#if defined(DUMP_LEAKS) && DUMP_LEAKS > 1 - return (int32_t)v <= 0; -#else - return (int32_t)v < JS_ATOM_END; -#endif -} - -static inline BOOL __JS_AtomIsTaggedInt(JSAtom v) -{ - return (v & JS_ATOM_TAG_INT) != 0; -} - -static inline JSAtom __JS_AtomFromUInt32(uint32_t v) -{ - return v | JS_ATOM_TAG_INT; -} - -static inline uint32_t __JS_AtomToUInt32(JSAtom atom) -{ - return atom & ~JS_ATOM_TAG_INT; -} - -static inline int is_num(int c) -{ - return c >= '0' && c <= '9'; -} - -/* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */ -static inline 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 (is_num(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 (!is_num(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; - } -} - /* XXX: could use faster version ? */ static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h) { size_t i; - for(i = 0; i < len; i++) h = h * 263 + str[i]; return h; @@ -2463,13 +1047,12 @@ static inline uint32_t hash_string16(const uint16_t *str, size_t len, uint32_t h) { size_t i; - for(i = 0; i < len; i++) h = h * 263 + str[i]; return h; } -static uint32_t hash_string(const JSString *str, uint32_t h) +uint32_t hash_string(const JSString *str, uint32_t h) { if (str->is_wide_char) h = hash_string16(str->u.str16, str->len, h); @@ -2478,8 +1061,7 @@ static uint32_t hash_string(const JSString *str, uint32_t h) return h; } -static __maybe_unused void JS_DumpString(JSRuntime *rt, - const JSString *p) +static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p) { int i, c, sep; @@ -2549,7 +1131,6 @@ static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size) { JSAtomStruct *p; uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash; - assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */ new_hash_mask = new_hash_size - 1; new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size); @@ -2579,7 +1160,6 @@ static int JS_InitAtoms(JSRuntime *rt) { int i, len, atom_type; const char *p; - rt->atom_hash_size = 0; rt->atom_hash = NULL; rt->atom_count = 0; @@ -2587,7 +1167,6 @@ static int JS_InitAtoms(JSRuntime *rt) rt->atom_free_index = 0; if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */ return -1; - p = js_atom_init; for(i = 1; i < JS_ATOM_END; i++) { if (i == JS_ATOM_Private_brand) @@ -2607,7 +1186,6 @@ static int JS_InitAtoms(JSRuntime *rt) static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v) { JSAtomStruct *p; - if (!__JS_AtomIsConst(v)) { p = rt->atom_array[v]; p->header.ref_count++; @@ -2619,7 +1197,6 @@ JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) { JSRuntime *rt; JSAtomStruct *p; - if (!__JS_AtomIsConst(v)) { rt = ctx->rt; p = rt->atom_array[v]; @@ -2628,11 +1205,10 @@ JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) return v; } -static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) +JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) { JSRuntime *rt; JSAtomStruct *p; - rt = ctx->rt; if (__JS_AtomIsTaggedInt(v)) return JS_ATOM_KIND_STRING; @@ -2656,28 +1232,6 @@ static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) } } -static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v) -{ - return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING; -} - -static 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; -} - /* string case (internal). Return JS_ATOM_NULL if error. 'str' is freed. */ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) @@ -2685,7 +1239,6 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) uint32_t h, h1, i; JSAtomStruct *p; int len; - #if 0 printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n"); #endif @@ -2726,12 +1279,10 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) atom_type = JS_ATOM_TYPE_SYMBOL; } } - if (rt->atom_free_index == 0) { /* allow new atom entries */ uint32_t new_size, start; JSAtomStruct **new_array; - /* alloc new with size progression 3/2: 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092 preallocating space for predefined atoms (at least 195). @@ -2773,7 +1324,6 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) rt->atom_array[i] = atom_set_free(next); } } - if (str) { if (str->atom_type == 0) { p = str; @@ -2782,7 +1332,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) p = js_malloc_rt(rt, sizeof(JSString) + (str->len << str->is_wide_char) + 1 - str->is_wide_char); - if (unlikely(!p)) + if (UNLIKELY(!p)) goto fail; p->header.ref_count = 1; p->is_wide_char = str->is_wide_char; @@ -2805,28 +1355,22 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) list_add_tail(&p->link, &rt->string_list); #endif } - /* use an already free entry */ i = rt->atom_free_index; rt->atom_free_index = atom_get_free(rt->atom_array[i]); rt->atom_array[i] = p; - p->hash = h; p->hash_next = i; /* atom_index */ p->atom_type = atom_type; - rt->atom_count++; - if (atom_type != JS_ATOM_TYPE_SYMBOL) { p->hash_next = rt->atom_hash[h1]; rt->atom_hash[h1] = i; - if (unlikely(rt->atom_count >= rt->atom_count_resize)) + if (UNLIKELY(rt->atom_count >= rt->atom_count_resize)) JS_ResizeAtomHash(rt, rt->atom_hash_size * 2); } - // JS_DumpAtoms(rt); return i; - fail: i = JS_ATOM_NULL; done: @@ -2853,7 +1397,6 @@ static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, { uint32_t h, h1, i; JSAtomStruct *p; - h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING); h &= JS_ATOM_HASH_MASK; h1 = h & (rt->atom_hash_size - 1); @@ -2874,53 +1417,9 @@ static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, return JS_ATOM_NULL; } -static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p) -{ -#if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */ - if (unlikely(i == JS_ATOM_NULL)) { - p->header.ref_count = INT32_MAX / 2; - return; - } -#endif - uint32_t i = p->hash_next; /* atom_index */ - if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { - JSAtomStruct *p0, *p1; - uint32_t h0; - - h0 = p->hash & (rt->atom_hash_size - 1); - i = rt->atom_hash[h0]; - p1 = rt->atom_array[i]; - if (p1 == p) { - rt->atom_hash[h0] = p1->hash_next; - } else { - for(;;) { - assert(i != 0); - p0 = p1; - i = p1->hash_next; - p1 = rt->atom_array[i]; - if (p1 == p) { - p0->hash_next = p1->hash_next; - break; - } - } - } - } - /* insert in free atom list */ - rt->atom_array[i] = atom_set_free(rt->atom_free_index); - rt->atom_free_index = i; - /* free the string structure */ -#ifdef DUMP_LEAKS - list_del(&p->link); -#endif - js_free_rt(rt, p); - rt->atom_count--; - assert(rt->atom_count >= 0); -} - static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) { JSAtomStruct *p; - p = rt->atom_array[i]; if (--p->header.ref_count > 0) return; @@ -2928,7 +1427,7 @@ static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) } /* Warning: 'p' is freed */ -static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) +JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) { JSRuntime *rt = ctx->rt; uint32_t n; @@ -2945,8 +1444,7 @@ static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) { JSValue val; - - if (len == 0 || !is_digit(*str)) { + if (len == 0 || !isdigit(*str)) { JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); if (atom) return atom; @@ -2978,7 +1476,7 @@ JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) } } -static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) +JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) { if ((uint64_t)n <= JS_ATOM_MAX_INT) { return __JS_AtomFromUInt32((uint32_t)n); @@ -2995,7 +1493,7 @@ static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) } /* 'p' is freed */ -static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type) +JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type) { JSRuntime *rt = ctx->rt; JSAtom atom; @@ -3005,139 +1503,6 @@ static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type) return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]); } -/* descr must be a non-numeric string atom */ -static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, - int atom_type) -{ - JSRuntime *rt = ctx->rt; - JSString *p; - - assert(!__JS_AtomIsTaggedInt(descr)); - assert(descr < rt->atom_size); - p = rt->atom_array[descr]; - JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); - return JS_NewSymbol(ctx, p, atom_type); -} - -#define ATOM_GET_STR_BUF_SIZE 64 - -/* Should only be used for debug. */ -static 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, ""); - } 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; -} - -static 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 */ -static 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; - } - } -} - /* This test must be fast if atom is not a numeric index (e.g. a method name). Return JS_UNDEFINED if not a numeric index. JS_EXCEPTION can also be returned. */ @@ -3172,7 +1537,7 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) goto minus_zero; } /* XXX: should test NaN, but the tests do not check it */ - if (!is_num(c)) { + if (!isdigit(c)) { /* XXX: String should be normalized, therefore 8-bit only */ const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' }; if (!(c =='I' && (r_end - r) == 8 && @@ -3195,7 +1560,7 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) return __JS_NewFloat64(ctx, -0.0); } } - if (!is_num(c)) { + if (!isdigit(c)) { if (!(c =='I' && (r_end - r) == 8 && !memcmp(r + 1, "nfinity", 7))) return JS_UNDEFINED; @@ -3227,7 +1592,7 @@ static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom) { JSValue num; num = JS_AtomIsNumericIndex1(ctx, atom); - if (likely(JS_IsUndefined(num))) + if (LIKELY(JS_IsUndefined(num))) return FALSE; if (JS_IsException(num)) return -1; @@ -3252,7 +1617,6 @@ static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) { JSRuntime *rt; JSAtomStruct *p; - rt = ctx->rt; if (__JS_AtomIsTaggedInt(v)) return FALSE; @@ -3268,7 +1632,6 @@ static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) char buf[ATOM_GET_STR_BUF_SIZE]; const char *p; int i; - /* XXX: should handle embedded null characters */ /* XXX: should move encoding code to JS_AtomGetStr */ p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom); @@ -3306,7 +1669,6 @@ const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) { JSValue str; const char *cstr; - str = JS_AtomToString(ctx, atom); if (JS_IsException(str)) return NULL; @@ -3315,51 +1677,6 @@ const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) return cstr; } -/* return a string atom containing name concatenated with str1 */ -static 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; -} - -static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) -{ - char buf[16]; - snprintf(buf, sizeof(buf), "%u", n); - return js_atom_concat_str(ctx, name, buf); -} - -static inline BOOL JS_IsEmptyString(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0; -} - /* JSClass support */ /* a new class ID is allocated if *pclass_id != 0 */ @@ -3389,17 +1706,14 @@ static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, int new_size, i; JSClass *cl, *new_class_array; struct list_head *el; - if (class_id >= (1 << 16)) return -1; if (class_id < rt->class_count && rt->class_array[class_id].class_id != 0) return -1; - if (class_id >= rt->class_count) { new_size = max_int(JS_CLASS_INIT_COUNT, max_int(class_id + 1, rt->class_count * 3 / 2)); - /* reallocate the context class prototype array, if any */ list_for_each(el, &rt->context_list) { JSContext *ctx = list_entry(el, JSContext, link); @@ -3436,7 +1750,6 @@ int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) { int ret, len; JSAtom name; - len = strlen(class_def->class_name); name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); if (name == JS_ATOM_NULL) { @@ -3449,411 +1762,6 @@ int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) return ret; } -static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len) -{ - JSString *str; - - if (len <= 0) { - return JS_AtomToString(ctx, JS_ATOM_empty_string); - } - str = js_alloc_string(ctx, len, 0); - if (!str) - return JS_EXCEPTION; - memcpy(str->u.str8, buf, len); - str->u.str8[len] = '\0'; - return JS_MKPTR(JS_TAG_STRING, str); -} - -static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len) -{ - JSString *str; - str = js_alloc_string(ctx, len, 1); - if (!str) - return JS_EXCEPTION; - memcpy(str->u.str16, buf, len * 2); - return JS_MKPTR(JS_TAG_STRING, str); -} - -static JSValue js_new_string_char(JSContext *ctx, uint16_t c) -{ - if (c < 0x100) { - uint8_t ch8 = c; - return js_new_string8(ctx, &ch8, 1); - } else { - uint16_t ch16 = c; - return js_new_string16(ctx, &ch16, 1); - } -} - -static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) -{ - int len = end - start; - if (start == 0 && end == p->len) { - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); - } - if (p->is_wide_char && len > 0) { - JSString *str; - int i; - uint16_t c = 0; - for (i = start; i < end; i++) { - c |= p->u.str16[i]; - } - if (c > 0xFF) - return js_new_string16(ctx, p->u.str16 + start, len); - - str = js_alloc_string(ctx, len, 0); - if (!str) - return JS_EXCEPTION; - for (i = 0; i < len; i++) { - str->u.str8[i] = p->u.str16[start + i]; - } - str->u.str8[len] = '\0'; - return JS_MKPTR(JS_TAG_STRING, str); - } else { - return js_new_string8(ctx, p->u.str8 + start, len); - } -} - -typedef struct StringBuffer { - JSContext *ctx; - JSString *str; - int len; - int size; - int is_wide_char; - int error_status; -} StringBuffer; - -/* 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. - */ -static 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; -} - -static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size) -{ - return string_buffer_init2(ctx, s, size, 0); -} - -static void string_buffer_free(StringBuffer *s) -{ - js_free(s->ctx, s->str); - s->str = NULL; -} - -static 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; -} - -static no_inline 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; -} - -static no_inline 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; -} - -static no_inline 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 */ -static 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 */ -static 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 */ -static 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); -} - -static int string_get(const JSString *p, int idx) { - return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; -} - -static int string_getc(const JSString *p, int *pidx) -{ - int idx, c, c1; - idx = *pidx; - if (p->is_wide_char) { - c = p->u.str16[idx++]; - if (c >= 0xd800 && c < 0xdc00 && idx < p->len) { - c1 = p->u.str16[idx]; - if (c1 >= 0xdc00 && c1 < 0xe000) { - c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; - idx++; - } - } - } else { - c = p->u.str8[idx++]; - } - *pidx = idx; - return c; -} - -static 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; -} - -static 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 */ -static int string_buffer_puts8(StringBuffer *s, const char *str) -{ - return string_buffer_write8(s, (const uint8_t *)str, strlen(str)); -} - -static 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); -} - -static 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); -} - -static 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; -} - -static 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; -} - -static 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); -} - /* create a string from a UTF-8 buffer */ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) { @@ -3861,7 +1769,6 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) uint32_t c; StringBuffer b_s, *b = &b_s; size_t len1; - p_start = (const uint8_t *)buf; p_end = p_start + buf_len; p = p_start; @@ -3909,20 +1816,18 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) } } return string_buffer_end(b); - fail: string_buffer_free(b); return JS_EXCEPTION; } -static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, - JSValue str2, const char *str3) +JSValue JS_ConcatString3(JSContext *ctx, const char *str1, + JSValue str2, const char *str3) { StringBuffer b_s, *b = &b_s; int len1, len3; JSString *p; - - if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) { + if (UNLIKELY(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) { str2 = JS_ToStringFree(ctx, str2); if (JS_IsException(str2)) goto fail; @@ -3930,17 +1835,13 @@ static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, p = JS_VALUE_GET_STRING(str2); len1 = strlen(str1); len3 = strlen(str3); - if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char)) goto fail; - string_buffer_write8(b, (const uint8_t *)str1, len1); string_buffer_concat(b, p, 0, p->len); string_buffer_write8(b, (const uint8_t *)str3, len3); - JS_FreeValue(ctx, str2); return string_buffer_end(b); - fail: JS_FreeValue(ctx, str2); return JS_EXCEPTION; @@ -3970,7 +1871,6 @@ const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BO JSString *str, *str_new; int pos, len, c, c1; uint8_t *q; - if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) { val = JS_ToString(ctx, val1); if (JS_IsException(val)) @@ -3978,13 +1878,11 @@ const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BO } else { val = JS_DupValue(ctx, val1); } - str = JS_VALUE_GET_STRING(val); len = str->len; if (!str->is_wide_char) { const uint8_t *src = str->u.str8; int count; - /* count the number of non-ASCII characters */ /* Scanning the whole string is required for ASCII strings, and computing the number of non-ASCII bytes is less expensive @@ -4048,7 +1946,6 @@ const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BO } } } - *q = '\0'; str_new->len = q - str_new->u.str8; JS_FreeValue(ctx, val); @@ -4071,64 +1968,6 @@ void JS_FreeCString(JSContext *ctx, const char *ptr) JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); } -static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len) -{ - int c, i; - for(i = 0; i < len; i++) { - c = src1[i] - src2[i]; - if (c != 0) - return c; - } - return 0; -} - -static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len) -{ - int c, i; - for(i = 0; i < len; i++) { - c = src1[i] - src2[i]; - if (c != 0) - return c; - } - return 0; -} - -static int js_string_memcmp(const JSString *p1, const JSString *p2, int len) -{ - int res; - - if (likely(!p1->is_wide_char)) { - if (likely(!p2->is_wide_char)) - res = memcmp(p1->u.str8, p2->u.str8, len); - else - res = -memcmp16_8(p2->u.str16, p1->u.str8, len); - } else { - if (!p2->is_wide_char) - res = memcmp16_8(p1->u.str16, p2->u.str8, len); - else - res = memcmp16(p1->u.str16, p2->u.str16, len); - } - return res; -} - -/* return < 0, 0 or > 0 */ -static int js_string_compare(JSContext *ctx, - const JSString *p1, const JSString *p2) -{ - int res, len; - len = min_int(p1->len, p2->len); - res = js_string_memcmp(p1, p2, len); - if (res == 0) { - if (p1->len == p2->len) - res = 0; - else if (p1->len < p2->len) - res = -1; - else - res = 1; - } - return res; -} - static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len) { if (p->is_wide_char) { @@ -4136,7 +1975,6 @@ static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len) } else { const uint8_t *src1 = p->u.str8 + offset; int i; - for(i = 0; i < len; i++) dst[i] = src1[i]; } @@ -4169,19 +2007,18 @@ static JSValue JS_ConcatString1(JSContext *ctx, /* op1 and op2 are converted to strings. For convience, op1 or op2 = JS_EXCEPTION are accepted and return JS_EXCEPTION. */ -static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) +JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) { JSValue ret; JSString *p1, *p2; - - if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) { + if (UNLIKELY(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) { op1 = JS_ToStringFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); return JS_EXCEPTION; } } - if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) { + if (UNLIKELY(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) { op2 = JS_ToStringFree(ctx, op2); if (JS_IsException(op2)) { JS_FreeValue(ctx, op1); @@ -4190,7 +2027,6 @@ static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) } p1 = JS_VALUE_GET_STRING(op1); p2 = JS_VALUE_GET_STRING(op2); - /* XXX: could also check if p1 is empty */ if (p2->len == 0) { goto ret_op1; @@ -4216,237 +2052,14 @@ static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) return ret; } -/* Shape support */ - -static inline size_t get_shape_size(size_t hash_size, size_t prop_size) -{ - return hash_size * sizeof(uint32_t) + sizeof(JSShape) + - prop_size * sizeof(JSShapeProperty); -} - -static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size) -{ - return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); -} - -static inline uint32_t *prop_hash_end(JSShape *sh) -{ - return (uint32_t *)sh; -} - -static inline void *get_alloc_from_shape(JSShape *sh) -{ - return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1); -} - -static inline JSShapeProperty *get_shape_prop(JSShape *sh) -{ - return sh->prop; -} - -static 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; -} - -static 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++; -} - -static 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' */ -static no_inline 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; -} - -static 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 */ -static 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; -} - -static 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)); -} - -static void js_free_shape(JSRuntime *rt, JSShape *sh) -{ - if (unlikely(--sh->header.ref_count <= 0)) { - js_free_shape0(rt, sh); - } -} - -static void js_free_shape_null(JSRuntime *rt, JSShape *sh) -{ - if (sh) - js_free_shape(rt, sh); -} - /* make space to hold at least 'count' properties */ -static no_inline int resize_properties(JSContext *ctx, JSShape **psh, - JSObject *p, uint32_t count) +int resize_properties(JSContext *ctx, JSShape **psh, JSObject *p, uint32_t count) { JSShape *sh; uint32_t new_size, new_hash_size, new_hash_mask, i; JSShapeProperty *pr; void *sh_alloc; intptr_t h; - sh = *psh; new_size = max_int(count, sh->prop_size * 3 / 2); /* Reallocate prop array first to avoid crash or size inconsistency @@ -4454,7 +2067,7 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh, if (p) { JSProperty *new_prop; new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); - if (unlikely(!new_prop)) + if (UNLIKELY(!new_prop)) return -1; p->prop = new_prop; } @@ -4491,7 +2104,7 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh, list_del(&sh->header.link); sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh), get_shape_size(new_hash_size, new_size)); - if (unlikely(!sh_alloc)) { + if (UNLIKELY(!sh_alloc)) { /* insert again in the GC list */ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); return -1; @@ -4513,19 +2126,15 @@ static int compact_properties(JSContext *ctx, JSObject *p) uint32_t new_hash_size, i, j, new_hash_mask, new_size; JSShapeProperty *old_pr, *pr; JSProperty *prop, *new_prop; - sh = p->shape; assert(!sh->is_hashed); - new_size = max_int(JS_PROP_INITIAL_SIZE, sh->prop_count - sh->deleted_prop_count); assert(new_size <= sh->prop_size); - new_hash_size = sh->prop_hash_mask + 1; while ((new_hash_size / 2) >= new_size) new_hash_size = new_hash_size / 2; new_hash_mask = new_hash_size - 1; - /* resize the hash table and the properties */ old_sh = sh; sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); @@ -4535,10 +2144,8 @@ static int compact_properties(JSContext *ctx, JSObject *p) list_del(&old_sh->header.link); memcpy(sh, old_sh, sizeof(JSShape)); list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); - memset(prop_hash_end(sh) - new_hash_size, 0, sizeof(prop_hash_end(sh)[0]) * new_hash_size); - j = 0; old_pr = old_sh->prop; pr = sh->prop; @@ -4561,10 +2168,8 @@ static int compact_properties(JSContext *ctx, JSObject *p) sh->prop_size = new_size; sh->deleted_prop_count = 0; sh->prop_count = j; - p->shape = sh; js_free(ctx, get_alloc_from_shape(old_sh)); - /* reduce the size of the object properties */ new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); if (new_prop) @@ -4572,270 +2177,15 @@ static int compact_properties(JSContext *ctx, JSObject *p) return 0; } -static 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 */ -static 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 */ -static 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"); -} - -static 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); -} - -static JSObject *get_proto_obj(JSValueConst proto_val) -{ - if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) - return NULL; - else - return JS_VALUE_GET_OBJ(proto_val); -} - /* WARNING: proto must be an object or JS_NULL */ JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val, JSClassID class_id) { JSShape *sh; JSObject *proto; - proto = get_proto_obj(proto_val); sh = find_hashed_shape_proto(ctx->rt, proto); - if (likely(sh)) { + if (LIKELY(sh)) { sh = js_dup_shape(sh); } else { sh = js_new_shape(ctx, proto); @@ -4849,7 +2199,6 @@ JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val, static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj) { JSObject *p; - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { p = JS_VALUE_GET_OBJ(obj); switch(p->class_id) { @@ -4870,10 +2219,9 @@ static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj) } #endif -static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val) +int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val) { JSObject *p; - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { p = JS_VALUE_GET_OBJ(obj); switch(p->class_id) { @@ -4920,8 +2268,7 @@ JSValue JS_NewObject(JSContext *ctx) return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT); } -static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj, - JSAtom name, int len) +void js_function_set_properties(JSContext *ctx, JSValueConst func_obj, JSAtom name, int len) { /* ES6 feature non compatible with ES5.1: length is configurable */ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len), @@ -4930,28 +2277,7 @@ static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj, JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE); } -static BOOL js_class_has_bytecode(JSClassID class_id) -{ - return (class_id == JS_CLASS_BYTECODE_FUNCTION || - class_id == JS_CLASS_GENERATOR_FUNCTION || - class_id == JS_CLASS_ASYNC_FUNCTION || - class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION); -} - -/* return NULL without exception if not a function or no bytecode */ -static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return NULL; - p = JS_VALUE_GET_OBJ(val); - if (!js_class_has_bytecode(p->class_id)) - return NULL; - return p->u.func.function_bytecode; -} - -static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj, - JSValueConst home_obj) +void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj, JSValueConst home_obj) { JSObject *p, *p1; JSFunctionBytecode *b; @@ -4975,10 +2301,9 @@ static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj, } } -static JSValue js_get_function_name(JSContext *ctx, JSAtom name) +JSValue js_get_function_name(JSContext *ctx, JSAtom name) { JSValue name_str; - name_str = JS_AtomToString(ctx, name); if (JS_AtomSymbolHasDescription(ctx, name)) { name_str = JS_ConcatString3(ctx, "[", name_str, "]"); @@ -4986,40 +2311,15 @@ static JSValue js_get_function_name(JSContext *ctx, JSAtom name) return name_str; } -/* Modify the name of a method according to the atom and - 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and - JS_PROP_HAS_SET. Also set the home object of the method. - Return < 0 if exception. */ -static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj, - JSAtom name, int flags, JSValueConst home_obj) -{ - JSValue name_str; - - name_str = js_get_function_name(ctx, name); - if (flags & JS_PROP_HAS_GET) { - name_str = JS_ConcatString3(ctx, "get ", name_str, ""); - } else if (flags & JS_PROP_HAS_SET) { - name_str = JS_ConcatString3(ctx, "set ", name_str, ""); - } - if (JS_IsException(name_str)) - return -1; - if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str, - JS_PROP_CONFIGURABLE) < 0) - return -1; - js_method_set_home_object(ctx, func_obj, home_obj); - return 0; -} - /* Note: at least 'length' arguments will be readable in 'argv' */ -static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, - const char *name, - int length, JSCFunctionEnum cproto, int magic, - JSValueConst proto_val) +JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic, + JSValueConst proto_val) { JSValue func_obj; JSObject *p; JSAtom name_atom; - func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); if (JS_IsException(func_obj)) return func_obj; @@ -5050,19 +2350,10 @@ JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, ctx->function_proto); } -typedef struct JSCFunctionDataRecord { - JSCFunctionData *func; - uint8_t length; - uint8_t data_len; - uint16_t magic; - JSValue data[0]; -} JSCFunctionDataRecord; - static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val) { JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); int i; - if (s) { for(i = 0; i < s->data_len; i++) { JS_FreeValueRT(rt, s->data[i]); @@ -5076,7 +2367,6 @@ static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, { JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); int i; - if (s) { for(i = 0; i < s->data_len; i++) { JS_MarkValue(rt, s->data[i], mark_func); @@ -5093,7 +2383,7 @@ static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, int i; /* XXX: could add the function on the stack for debug */ - if (unlikely(argc < s->length)) { + if (UNLIKELY(argc < s->length)) { arg_buf = alloca(sizeof(arg_buf[0]) * s->length); for(i = 0; i < argc; i++) arg_buf[i] = argv[i]; @@ -5150,15 +2440,14 @@ static void js_autoinit_free(JSRuntime *rt, JSProperty *pr) JS_FreeContext(js_autoinit_get_realm(pr)); } -static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr, - JS_MarkFunc *mark_func) +void js_autoinit_mark(JSRuntime *rt, JSProperty *pr, JS_MarkFunc *mark_func) { mark_func(rt, &js_autoinit_get_realm(pr)->header); } -static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) +void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) { - if (unlikely(prop_flags & JS_PROP_TMASK)) { + if (UNLIKELY(prop_flags & JS_PROP_TMASK)) { if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) { if (pr->u.getset.getter) JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); @@ -5174,56 +2463,7 @@ static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) } } -force_inline JSShapeProperty *find_own_property1(JSObject *p, - JSAtom atom) -{ - JSShape *sh; - JSShapeProperty *pr, *prop; - intptr_t h; - sh = p->shape; - h = (uintptr_t)atom & sh->prop_hash_mask; - h = prop_hash_end(sh)[-h - 1]; - prop = get_shape_prop(sh); - while (h) { - pr = &prop[h - 1]; - if (likely(pr->atom == atom)) { - return pr; - } - h = pr->hash_next; - } - return NULL; -} - -force_inline JSShapeProperty *find_own_property(JSProperty **ppr, - JSObject *p, - JSAtom atom) -{ - JSShape *sh; - JSShapeProperty *pr, *prop; - intptr_t h; - sh = p->shape; - h = (uintptr_t)atom & sh->prop_hash_mask; - h = prop_hash_end(sh)[-h - 1]; - prop = get_shape_prop(sh); - while (h) { - pr = &prop[h - 1]; - if (likely(pr->atom == atom)) { - *ppr = &p->prop[h - 1]; - /* the compiler should be able to assume that pr != NULL here */ - return pr; - } - h = pr->hash_next; - } - *ppr = NULL; - return NULL; -} - -/* indicate that the object may be part of a function prototype cycle */ -static void set_cycle_flag(JSContext *ctx, JSValueConst obj) -{ -} - -static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) +void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) { if (var_ref) { assert(var_ref->header.ref_count > 0); @@ -5239,42 +2479,6 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) } } -static void js_array_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - int i; - - for(i = 0; i < p->u.array.count; i++) { - JS_FreeValueRT(rt, p->u.array.u.values[i]); - } - js_free_rt(rt, p->u.array.u.values); -} - -static void js_array_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - int i; - - for(i = 0; i < p->u.array.count; i++) { - JS_MarkValue(rt, p->u.array.u.values[i], mark_func); - } -} - -static void js_object_data_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JS_FreeValueRT(rt, p->u.object_data); - p->u.object_data = JS_UNDEFINED; -} - -static void js_object_data_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JS_MarkValue(rt, p->u.object_data, mark_func); -} - static void js_c_function_finalizer(JSRuntime *rt, JSValue val) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -5292,56 +2496,6 @@ static void js_c_function_mark(JSRuntime *rt, JSValueConst val, mark_func(rt, &p->u.cfunc.realm->header); } -static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p1, *p = JS_VALUE_GET_OBJ(val); - JSFunctionBytecode *b; - JSVarRef **var_refs; - int i; - - p1 = p->u.func.home_object; - if (p1) { - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1)); - } - b = p->u.func.function_bytecode; - if (b) { - var_refs = p->u.func.var_refs; - if (var_refs) { - for(i = 0; i < b->closure_var_count; i++) - free_var_ref(rt, var_refs[i]); - js_free_rt(rt, var_refs); - } - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b)); - } -} - -static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSVarRef **var_refs = p->u.func.var_refs; - JSFunctionBytecode *b = p->u.func.function_bytecode; - int i; - - if (p->u.func.home_object) { - JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object), - mark_func); - } - if (b) { - if (var_refs) { - for(i = 0; i < b->closure_var_count; i++) { - JSVarRef *var_ref = var_refs[i]; - if (var_ref && var_ref->is_detached) { - mark_func(rt, &var_ref->header); - } - } - } - /* must mark the function bytecode because template objects may be - part of a cycle */ - JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func); - } -} - static void js_bound_function_finalizer(JSRuntime *rt, JSValue val) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -5385,931 +2539,8 @@ static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkValue(rt, it->obj, mark_func); } -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(); - } -} - -static 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; -} - -/* 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); -} - /* garbage collection */ -static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, - JSGCObjectTypeEnum type) -{ - h->mark = 0; - h->gc_obj_type = type; - list_add_tail(&h->link, &rt->gc_obj_list); -} - -static void remove_gc_object(JSGCObjectHeader *h) -{ - list_del(&h->link); -} - -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 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 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; -} - -/* 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 -- " -#ifdef CONFIG_BIGNUM - "BigNum " -#endif - CONFIG_VERSION " version, %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); - } -} - JSValue JS_GetGlobalObject(JSContext *ctx) { return JS_DupValue(ctx, ctx->global_obj); @@ -6334,436 +2565,7 @@ JSValue JS_GetException(JSContext *ctx) return val; } -static 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; - } - } -} - -static void dbuf_put_sleb128(DynBuf *s, int32_t v1) -{ - uint32_t v = v1; - dbuf_put_leb128(s, (2 * v) ^ -(v >> 31)); -} - -static 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; -} - -static 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; -} - -static 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; -} - -/* 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); -} - -#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0) -/* only taken into account if filename is provided */ -#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1) - -/* if filename != NULL, an additional level is added with the filename - and line number information (used for parse error). */ -static 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 = ""; - 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 : ""); - 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); -} - -/* Note: it is important that no exception is returned by this function */ -static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(obj); - if (p->class_id != JS_CLASS_ERROR) - return FALSE; - if (find_own_property1(p, JS_ATOM_stack)) - return FALSE; - return TRUE; -} - -JSValue JS_NewError(JSContext *ctx) -{ - return JS_NewObjectClass(ctx, JS_CLASS_ERROR); -} - -static 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; -} - -static 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 __attribute__((format(printf, 2, 3))) 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 __attribute__((format(printf, 2, 3))) 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; -} - -static int __attribute__((format(printf, 3, 4))) 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 */ -static JSValue __attribute__((format(printf, 3, 4))) __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 */ -static JSValue __attribute__((format(printf, 3, 4))) __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)); -} - -/* %s is replaced by 'atom'. The macro is used so that gcc can check - the format string. */ -#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "") -#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "") - -static 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 __attribute__((format(printf, 2, 3))) 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 __attribute__((format(printf, 2, 3))) 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 __attribute__((format(printf, 2, 3))) 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; -} - -static JSValue JS_ThrowStackOverflow(JSContext *ctx) -{ - return JS_ThrowInternalError(ctx, "stack overflow"); -} - -static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx) -{ - return JS_ThrowTypeError(ctx, "not an object"); -} - -static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx) -{ - return JS_ThrowTypeError(ctx, "not a symbol"); -} - -static 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)); -} - -static 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)); -} - -static 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); -} - -static 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); -} - -static no_inline __exception int __js_poll_interrupts(JSContext *ctx) +int __js_poll_interrupts(JSContext *ctx) { JSRuntime *rt = ctx->rt; ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; @@ -6778,23 +2580,11 @@ static no_inline __exception int __js_poll_interrupts(JSContext *ctx) return 0; } -static inline __exception int js_poll_interrupts(JSContext *ctx) -{ - if (unlikely(--ctx->interrupt_counter <= 0)) { - return __js_poll_interrupts(ctx); - } else { - return 0; - } -} - /* return -1 (exception) or TRUE/FALSE */ -static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj, - JSValueConst proto_val, - BOOL throw_flag) +int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj, JSValueConst proto_val, BOOL throw_flag) { JSObject *proto, *p, *p1; JSShape *sh; - if (throw_flag) { if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL || JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED) @@ -6814,11 +2604,9 @@ static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj, } else { proto = JS_VALUE_GET_OBJ(proto_val); } - if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) return TRUE; - - if (unlikely(p->class_id == JS_CLASS_PROXY)) + if (UNLIKELY(p->class_id == JS_CLASS_PROXY)) return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag); sh = p->shape; if (sh->proto == proto) @@ -6848,7 +2636,6 @@ static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj, } while (p1 != NULL); JS_DupValue(ctx, proto_val); } - if (js_shape_prepare_update(ctx, p, NULL)) return -1; sh = p->shape; @@ -6909,7 +2696,7 @@ JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj) if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { JSObject *p; p = JS_VALUE_GET_OBJ(obj); - if (unlikely(p->class_id == JS_CLASS_PROXY)) { + if (UNLIKELY(p->class_id == JS_CLASS_PROXY)) { val = js_proxy_getPrototypeOf(ctx, obj); } else { p = p->shape->proto; @@ -6924,7 +2711,7 @@ JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj) return val; } -static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj) +JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj) { JSValue obj1; obj1 = JS_GetPrototype(ctx, obj); @@ -6940,7 +2727,6 @@ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, JSObject *proto; const JSObject *p, *proto1; BOOL ret; - if (!JS_IsFunction(ctx, obj)) return FALSE; p = JS_VALUE_GET_OBJ(obj); @@ -6948,7 +2734,6 @@ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, JSBoundFunction *s = p->u.bound_function; return JS_IsInstanceOf(ctx, val, s->func_obj); } - /* Only explicitly boxed values are instances of constructors */ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) return FALSE; @@ -6965,7 +2750,7 @@ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, proto1 = p->shape->proto; if (!proto1) { /* slow case if proxy in the prototype chain */ - if (unlikely(p->class_id == JS_CLASS_PROXY)) { + if (UNLIKELY(p->class_id == JS_CLASS_PROXY)) { JSValue obj1; obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p)); for(;;) { @@ -7010,7 +2795,6 @@ done: int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) { JSValue method; - if (!JS_IsObject(obj)) goto fail; method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance); @@ -7047,10 +2831,8 @@ static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, JSValue val; JSContext *realm; JSAutoInitFunc *func; - if (js_shape_prepare_update(ctx, p, &prs)) return -1; - realm = js_autoinit_get_realm(pr); func = js_autoinit_func_table[js_autoinit_get_id(pr)]; /* 'func' shall not modify the object properties 'pr' */ @@ -7072,9 +2854,8 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, JSProperty *pr; JSShapeProperty *prs; uint32_t tag; - tag = JS_VALUE_GET_TAG(obj); - if (unlikely(tag != JS_TAG_OBJECT)) { + if (UNLIKELY(tag != JS_TAG_OBJECT)) { switch(tag) { case JS_TAG_NULL: return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop); @@ -7110,14 +2891,13 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, } else { p = JS_VALUE_GET_OBJ(obj); } - for(;;) { prs = find_own_property(&pr, p, prop); if (prs) { /* found */ - if (unlikely(prs->flags & JS_PROP_TMASK)) { + if (UNLIKELY(prs->flags & JS_PROP_TMASK)) { if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - if (unlikely(!pr->u.getset.getter)) { + if (UNLIKELY(!pr->u.getset.getter)) { return JS_UNDEFINED; } else { JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter); @@ -7127,7 +2907,7 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, } } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { JSValue val = *pr->u.var_ref->pvalue; - if (unlikely(JS_IsUninitialized(val))) + if (UNLIKELY(JS_IsUninitialized(val))) return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); return JS_DupValue(ctx, val); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { @@ -7140,7 +2920,7 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, return JS_DupValue(ctx, pr->u.value); } } - if (unlikely(p->is_exotic)) { + if (UNLIKELY(p->is_exotic)) { /* exotic behaviors */ if (p->fast_array) { if (__JS_AtomIsTaggedInt(prop)) { @@ -7179,7 +2959,6 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, JSPropertyDescriptor desc; int ret; JSValue obj1; - /* Note: if 'p' is a prototype, it can be freed in the called function */ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); @@ -7203,201 +2982,18 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, if (!p) break; } - if (unlikely(throw_ref_error)) { + if (UNLIKELY(throw_ref_error)) { return JS_ThrowReferenceErrorNotDefined(ctx, prop); } else { return JS_UNDEFINED; } } -static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) -{ - return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist", - atom); -} - -/* Private fields can be added even on non extensible objects or - Proxies */ -static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, - JSValueConst name, JSValue val) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - JSAtom prop; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { - JS_ThrowTypeErrorNotAnObject(ctx); - goto fail; - } - /* safety check */ - if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { - JS_ThrowTypeErrorNotASymbol(ctx); - goto fail; - } - prop = js_symbol_to_atom(ctx, (JSValue)name); - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, prop); - if (prs) { - JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists", - prop); - goto fail; - } - pr = add_property(ctx, p, prop, JS_PROP_C_W_E); - if (unlikely(!pr)) { - fail: - JS_FreeValue(ctx, val); - return -1; - } - pr->u.value = val; - return 0; -} - -static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, - JSValueConst name) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - JSAtom prop; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) - return JS_ThrowTypeErrorNotAnObject(ctx); - /* safety check */ - if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) - return JS_ThrowTypeErrorNotASymbol(ctx); - prop = js_symbol_to_atom(ctx, (JSValue)name); - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, prop); - if (!prs) { - JS_ThrowTypeErrorPrivateNotFound(ctx, prop); - return JS_EXCEPTION; - } - return JS_DupValue(ctx, pr->u.value); -} - -static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, - JSValueConst name, JSValue val) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - JSAtom prop; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { - JS_ThrowTypeErrorNotAnObject(ctx); - goto fail; - } - /* safety check */ - if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { - JS_ThrowTypeErrorNotASymbol(ctx); - goto fail; - } - prop = js_symbol_to_atom(ctx, (JSValue)name); - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, prop); - if (!prs) { - JS_ThrowTypeErrorPrivateNotFound(ctx, prop); - fail: - JS_FreeValue(ctx, val); - return -1; - } - set_value(ctx, &pr->u.value, val); - return 0; -} - -static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) -{ - JSObject *p, *p1; - JSShapeProperty *prs; - JSProperty *pr; - JSValue brand; - JSAtom brand_atom; - - if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - p = JS_VALUE_GET_OBJ(home_obj); - prs = find_own_property(&pr, p, JS_ATOM_Private_brand); - if (!prs) { - brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE); - if (JS_IsException(brand)) - return -1; - /* if the brand is not present, add it */ - pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E); - if (!pr) { - JS_FreeValue(ctx, brand); - return -1; - } - pr->u.value = JS_DupValue(ctx, brand); - } else { - brand = JS_DupValue(ctx, pr->u.value); - } - brand_atom = js_symbol_to_atom(ctx, brand); - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { - JS_ThrowTypeErrorNotAnObject(ctx); - JS_FreeAtom(ctx, brand_atom); - return -1; - } - p1 = JS_VALUE_GET_OBJ(obj); - pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E); - JS_FreeAtom(ctx, brand_atom); - if (!pr) - return -1; - pr->u.value = JS_UNDEFINED; - return 0; -} - -static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) -{ - JSObject *p, *p1, *home_obj; - JSShapeProperty *prs; - JSProperty *pr; - JSValueConst brand; - - /* get the home object of 'func' */ - if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) { - not_obj: - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - p1 = JS_VALUE_GET_OBJ(func); - if (!js_class_has_bytecode(p1->class_id)) - goto not_obj; - home_obj = p1->u.func.home_object; - if (!home_obj) - goto not_obj; - prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand); - if (!prs) { - JS_ThrowTypeError(ctx, "expecting private field"); - return -1; - } - brand = pr->u.value; - /* safety check */ - if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL)) - goto not_obj; - - /* get the brand array of 'obj' */ - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) - goto not_obj; - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand)); - if (!prs) { - JS_ThrowTypeError(ctx, "invalid brand on object"); - return -1; - } - return 0; -} - -static uint32_t js_string_obj_get_length(JSContext *ctx, - JSValueConst obj) +uint32_t js_string_obj_get_length(JSContext *ctx, JSValueConst obj) { JSObject *p; JSString *p1; uint32_t len = 0; - /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ p = JS_VALUE_GET_OBJ(obj); if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { @@ -7414,7 +3010,6 @@ static int num_keys_cmp(const void *p1, const void *p2, void *opaque) JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom; uint32_t v1, v2; BOOL atom1_is_integer, atom2_is_integer; - atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1); atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2); assert(atom1_is_integer && atom2_is_integer); @@ -7426,7 +3021,7 @@ static int num_keys_cmp(const void *p1, const void *p2, void *opaque) return 1; } -static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) +void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) { uint32_t i; if (tab) { @@ -7438,10 +3033,10 @@ static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) /* return < 0 in case if exception, 0 if OK. ptab and its atoms must be freed by the user. */ -static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, - JSPropertyEnum **ptab, - uint32_t *plen, - JSObject *p, int flags) +int JS_GetOwnPropertyNamesInternal(JSContext *ctx, + JSPropertyEnum **ptab, + uint32_t *plen, + JSObject *p, int flags) { int i, j; JSShape *sh; @@ -7453,11 +3048,9 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, BOOL is_enumerable, num_sorted; uint32_t num_key; JSAtomKindEnum kind; - /* clear pointer for consistency in case of failure */ *ptab = NULL; *plen = 0; - /* compute the number of returned properties */ num_keys_count = 0; str_keys_count = 0; @@ -7475,10 +3068,10 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, ((flags >> kind) & 1) != 0) { /* need to raise an exception in case of the module name space (implicit GetOwnProperty) */ - if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) && + if (UNLIKELY((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) && (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) { JSVarRef *var_ref = p->prop[i].u.var_ref; - if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) { + if (UNLIKELY(JS_IsUninitialized(*var_ref->pvalue))) { JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); return -1; } @@ -7493,7 +3086,6 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, } } } - if (p->is_exotic) { if (p->fast_array) { if (flags & JS_GPN_STRING_MASK) { @@ -7538,9 +3130,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, } } } - /* fill them */ - atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count; /* avoid allocating 0 bytes */ tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); @@ -7548,11 +3138,9 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, js_free_prop_enum(ctx, tab_exotic, exotic_count); return -1; } - num_index = 0; str_index = num_keys_count; sym_index = str_index + str_keys_count; - num_sorted = TRUE; sh = p->shape; for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { @@ -7575,7 +3163,6 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, } } } - if (p->is_exotic) { int len; if (p->fast_array) { @@ -7615,11 +3202,9 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, js_free(ctx, tab_exotic); } } - assert(num_index == num_keys_count); assert(str_index == num_keys_count + str_keys_count); assert(sym_index == atom_count); - if (num_keys_count != 0 && !num_sorted) { rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp, ctx); @@ -7643,12 +3228,11 @@ int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, /* Return -1 if exception, FALSE if the property does not exist, TRUE if it exists. If TRUE is returned, the property descriptor 'desc' is filled present. */ -static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, - JSObject *p, JSAtom prop) +int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, + JSObject *p, JSAtom prop) { JSShapeProperty *prs; JSProperty *pr; - retry: prs = find_own_property(&pr, p, prop); if (prs) { @@ -7657,7 +3241,7 @@ retry: desc->getter = JS_UNDEFINED; desc->setter = JS_UNDEFINED; desc->value = JS_UNDEFINED; - if (unlikely(prs->flags & JS_PROP_TMASK)) { + if (UNLIKELY(prs->flags & JS_PROP_TMASK)) { if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { desc->flags |= JS_PROP_GETSET; if (pr->u.getset.getter) @@ -7666,7 +3250,7 @@ retry: desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { JSValue val = *pr->u.var_ref->pvalue; - if (unlikely(JS_IsUninitialized(val))) { + if (UNLIKELY(JS_IsUninitialized(val))) { JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); return -1; } @@ -7682,8 +3266,8 @@ retry: } } else { /* for consistency, send the exception even if desc is NULL */ - if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) { - if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { + if (UNLIKELY((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) { + if (UNLIKELY(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); return -1; } @@ -7735,11 +3319,10 @@ int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, int JS_IsExtensible(JSContext *ctx, JSValueConst obj) { JSObject *p; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + if (UNLIKELY(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) return FALSE; p = JS_VALUE_GET_OBJ(obj); - if (unlikely(p->class_id == JS_CLASS_PROXY)) + if (UNLIKELY(p->class_id == JS_CLASS_PROXY)) return js_proxy_isExtensible(ctx, obj); else return p->extensible; @@ -7749,11 +3332,10 @@ int JS_IsExtensible(JSContext *ctx, JSValueConst obj) int JS_PreventExtensions(JSContext *ctx, JSValueConst obj) { JSObject *p; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + if (UNLIKELY(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) return FALSE; p = JS_VALUE_GET_OBJ(obj); - if (unlikely(p->class_id == JS_CLASS_PROXY)) + if (UNLIKELY(p->class_id == JS_CLASS_PROXY)) return js_proxy_preventExtensions(ctx, obj); p->extensible = FALSE; return TRUE; @@ -7765,8 +3347,7 @@ int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) JSObject *p; int ret; JSValue obj1; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + if (UNLIKELY(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) return FALSE; p = JS_VALUE_GET_OBJ(obj); for(;;) { @@ -7802,13 +3383,6 @@ int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) return FALSE; } -/* val must be a symbol */ -static 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 JS_ATOM_NULL in case of exception */ JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val) { @@ -7836,13 +3410,11 @@ JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val) return atom; } -static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, - JSValue prop) +JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, JSValue prop) { JSAtom atom; JSValue ret; - - if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && + if (LIKELY(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { JSObject *p; uint32_t idx, len; @@ -7850,7 +3422,7 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, p = JS_VALUE_GET_OBJ(this_obj); idx = JS_VALUE_GET_INT(prop); len = (uint32_t)p->u.array.count; - if (unlikely(idx >= len)) + if (UNLIKELY(idx >= len)) goto slow_path; switch(p->class_id) { case JS_CLASS_ARRAY: @@ -7886,7 +3458,7 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, slow_path: atom = JS_ValueToAtom(ctx, prop); JS_FreeValue(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) + if (UNLIKELY(atom == JS_ATOM_NULL)) return JS_EXCEPTION; ret = JS_GetProperty(ctx, this_obj, atom); JS_FreeAtom(ctx, atom); @@ -7900,47 +3472,10 @@ JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx)); } -/* Check if an object has a generalized numeric property. Return value: - -1 for exception, - TRUE if property exists, stored into *pval, - FALSE if proprty does not exist. - */ -static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval) -{ - JSValue val = JS_UNDEFINED; - JSAtom prop; - int present; - - if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) { - /* fast path */ - present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx)); - if (present > 0) { - val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); - if (unlikely(JS_IsException(val))) - present = -1; - } - } else { - prop = JS_NewAtomInt64(ctx, idx); - present = -1; - if (likely(prop != JS_ATOM_NULL)) { - present = JS_HasProperty(ctx, obj, prop); - if (present > 0) { - val = JS_GetProperty(ctx, obj, prop); - if (unlikely(JS_IsException(val))) - present = -1; - } - JS_FreeAtom(ctx, prop); - } - } - *pval = val; - return present; -} - -static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx) +JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx) { JSAtom prop; JSValue val; - if ((uint64_t)idx <= INT32_MAX) { /* fast path for fast arrays */ return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); @@ -7948,7 +3483,6 @@ static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx prop = JS_NewAtomInt64(ctx, idx); if (prop == JS_ATOM_NULL) return JS_EXCEPTION; - val = JS_GetProperty(ctx, obj, prop); JS_FreeAtom(ctx, prop); return val; @@ -7967,11 +3501,9 @@ JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, /* Note: the property value is not initialized. Return NULL if memory error. */ -static JSProperty *add_property(JSContext *ctx, - JSObject *p, JSAtom prop, int prop_flags) +JSProperty *add_property(JSContext *ctx, JSObject *p, JSAtom prop, int prop_flags) { JSShape *sh, *new_sh; - sh = p->shape; if (sh->is_hashed) { /* try to find an existing shape */ @@ -8010,14 +3542,13 @@ static JSProperty *add_property(JSContext *ctx, /* can be called on Array or Arguments objects. return < 0 if memory alloc error. */ -static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, +static noinline __exception int convert_fast_array_to_array(JSContext *ctx, JSObject *p) { JSProperty *pr; JSShape *sh; JSValue *tab; uint32_t i, len, new_count; - if (js_shape_prepare_update(ctx, p, NULL)) return -1; len = p->u.array.count; @@ -8028,7 +3559,6 @@ static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, if (resize_properties(ctx, &p->shape, p, new_count)) return -1; } - tab = p->u.array.u.values; for(i = 0; i < len; i++) { /* add_property cannot fail here but @@ -8044,14 +3574,13 @@ static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, return 0; } -static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) +int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) { JSShape *sh; JSShapeProperty *pr, *lpr, *prop; JSProperty *pr1; uint32_t lpr_idx; intptr_t h, h1; - redo: sh = p->shape; h1 = atom & sh->prop_hash_mask; @@ -8061,7 +3590,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) lpr_idx = 0; /* prevent warning */ while (h != 0) { pr = &prop[h - 1]; - if (likely(pr->atom == atom)) { + if (LIKELY(pr->atom == atom)) { /* found ! */ if (!(pr->flags & JS_PROP_CONFIGURABLE)) return FALSE; @@ -8087,7 +3616,6 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) pr->flags = 0; pr->atom = JS_ATOM_NULL; pr1->u.value = JS_UNDEFINED; - /* compact the properties if too many deleted properties */ if (sh->deleted_prop_count >= 8 && sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { @@ -8098,7 +3626,6 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) lpr = pr; h = pr->hash_next; } - if (p->is_exotic) { if (p->fast_array) { uint32_t idx; @@ -8134,7 +3661,7 @@ static int call_setter(JSContext *ctx, JSObject *setter, JSValueConst this_obj, JSValue val, int flags) { JSValue ret, func; - if (likely(setter)) { + if (LIKELY(setter)) { func = JS_MKPTR(JS_TAG_OBJECT, setter); /* Note: the field could be removed in the setter */ func = JS_DupValue(ctx, func); @@ -8155,147 +3682,7 @@ static int call_setter(JSContext *ctx, JSObject *setter, } } -/* set the array length and remove the array elements if necessary. */ -static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, - int flags) -{ - uint32_t len, idx, cur_len; - int i, ret; - - /* Note: this call can reallocate the properties of 'p' */ - ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE); - if (ret) - return -1; - /* JS_ToArrayLengthFree() must be done before the read-only test */ - if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE))) - return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); - - if (likely(p->fast_array)) { - uint32_t old_len = p->u.array.count; - if (len < old_len) { - for(i = len; i < old_len; i++) { - JS_FreeValue(ctx, p->u.array.u.values[i]); - } - p->u.array.count = len; - } - p->prop[0].u.value = JS_NewUint32(ctx, len); - } else { - /* Note: length is always a uint32 because the object is an - array */ - JS_ToUint32(ctx, &cur_len, p->prop[0].u.value); - if (len < cur_len) { - uint32_t d; - JSShape *sh; - JSShapeProperty *pr; - - d = cur_len - len; - sh = p->shape; - if (d <= sh->prop_count) { - JSAtom atom; - - /* faster to iterate */ - while (cur_len > len) { - atom = JS_NewAtomUInt32(ctx, cur_len - 1); - ret = delete_property(ctx, p, atom); - JS_FreeAtom(ctx, atom); - if (unlikely(!ret)) { - /* unlikely case: property is not - configurable */ - break; - } - cur_len--; - } - } else { - /* faster to iterate thru all the properties. Need two - passes in case one of the property is not - configurable */ - cur_len = len; - for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; - i++, pr++) { - if (pr->atom != JS_ATOM_NULL && - JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { - if (idx >= cur_len && - !(pr->flags & JS_PROP_CONFIGURABLE)) { - cur_len = idx + 1; - } - } - } - - for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; - i++, pr++) { - if (pr->atom != JS_ATOM_NULL && - JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { - if (idx >= cur_len) { - /* remove the property */ - delete_property(ctx, p, pr->atom); - /* WARNING: the shape may have been modified */ - sh = p->shape; - pr = get_shape_prop(sh) + i; - } - } - } - } - } else { - cur_len = len; - } - set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len)); - if (unlikely(cur_len > len)) { - return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable"); - } - } - return TRUE; -} - -/* return -1 if exception */ -static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len) -{ - uint32_t new_size; - size_t slack; - JSValue *new_array_prop; - /* XXX: potential arithmetic overflow */ - new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); - new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); - if (!new_array_prop) - return -1; - new_size += slack / sizeof(*new_array_prop); - p->u.array.u.values = new_array_prop; - p->u.array.u1.size = new_size; - return 0; -} - -/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array = - TRUE and p->extensible = TRUE */ -static int add_fast_array_element(JSContext *ctx, JSObject *p, - JSValue val, int flags) -{ - uint32_t new_len, array_len; - /* extend the array by one */ - /* XXX: convert to slow array if new_len > 2^31-1 elements */ - new_len = p->u.array.count + 1; - /* update the length if necessary. We assume that if the length is - not an integer, then if it >= 2^31. */ - if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { - array_len = JS_VALUE_GET_INT(p->prop[0].u.value); - if (new_len > array_len) { - if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); - } - p->prop[0].u.value = JS_NewInt32(ctx, new_len); - } - } - if (unlikely(new_len > p->u.array.u1.size)) { - if (expand_fast_array(ctx, p, new_len)) { - JS_FreeValue(ctx, val); - return -1; - } - } - p->u.array.u.values[new_len - 1] = val; - p->u.array.count = new_len; - return TRUE; -} - -static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) +void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) { JS_FreeValue(ctx, desc->getter); JS_FreeValue(ctx, desc->setter); @@ -8304,16 +3691,15 @@ static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) /* generic (and slower) version of JS_SetProperty() for * Reflect.set(). 'obj' must be an object. */ -static int JS_SetPropertyGeneric(JSContext *ctx, - JSValueConst obj, JSAtom prop, - JSValue val, JSValueConst this_obj, - int flags) +int JS_SetPropertyGeneric(JSContext *ctx, + JSValueConst obj, JSAtom prop, + JSValue val, JSValueConst this_obj, + int flags) { int ret; JSPropertyDescriptor desc; JSValue obj1; JSObject *p; - obj1 = JS_DupValue(ctx, obj); for(;;) { p = JS_VALUE_GET_OBJ(obj1); @@ -8327,7 +3713,6 @@ static int JS_SetPropertyGeneric(JSContext *ctx, return ret; } } - ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); if (ret < 0) { JS_FreeValue(ctx, obj1); @@ -8362,14 +3747,11 @@ static int JS_SetPropertyGeneric(JSContext *ctx, break; } JS_FreeValue(ctx, obj1); - if (!JS_IsObject(this_obj)) { JS_FreeValue(ctx, val); return JS_ThrowTypeErrorOrFalse(ctx, flags, "receiver is not an object"); } - p = JS_VALUE_GET_OBJ(this_obj); - /* modify the property in this_obj if it already exists */ ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); if (ret < 0) { @@ -8397,7 +3779,6 @@ static int JS_SetPropertyGeneric(JSContext *ctx, JS_FreeValue(ctx, val); return ret; } - ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, flags | JS_PROP_HAS_VALUE | @@ -8426,7 +3807,7 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj, printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n"); #endif tag = JS_VALUE_GET_TAG(this_obj); - if (unlikely(tag != JS_TAG_OBJECT)) { + if (UNLIKELY(tag != JS_TAG_OBJECT)) { switch(tag) { case JS_TAG_NULL: JS_FreeValue(ctx, val); @@ -8447,7 +3828,7 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj, retry: prs = find_own_property(&pr, p, prop); if (prs) { - if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | + if (LIKELY((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { /* fast case */ set_value(ctx, &pr->u.value, val); @@ -8477,7 +3858,6 @@ retry: goto read_only_prop; } } - p1 = p; for(;;) { if (p1->is_exotic) { @@ -8485,7 +3865,7 @@ retry: if (__JS_AtomIsTaggedInt(prop)) { uint32_t idx = __JS_AtomToUInt32(prop); if (idx < p1->u.array.count) { - if (unlikely(p == p1)) + if (UNLIKELY(p == p1)) return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags); else break; @@ -8547,7 +3927,7 @@ retry: JS_FreeValue(ctx, desc.value); if (!(desc.flags & JS_PROP_WRITABLE)) goto read_only_prop; - if (likely(p == p1)) { + if (LIKELY(p == p1)) { ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, JS_PROP_HAS_VALUE); @@ -8566,7 +3946,6 @@ retry: prototype_lookup: if (!p1) break; - retry2: prs = find_own_property(&pr, p1, prop); if (prs) { @@ -8584,23 +3963,19 @@ retry: } } } - - if (unlikely(flags & JS_PROP_NO_ADD)) { + if (UNLIKELY(flags & JS_PROP_NO_ADD)) { JS_FreeValue(ctx, val); JS_ThrowReferenceErrorNotDefined(ctx, prop); return -1; } - - if (unlikely(!p)) { + if (UNLIKELY(!p)) { JS_FreeValue(ctx, val); return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object"); } - - if (unlikely(!p->extensible)) { + if (UNLIKELY(!p->extensible)) { JS_FreeValue(ctx, val); return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); } - if (p->is_exotic) { if (p->class_id == JS_CLASS_ARRAY && p->fast_array && __JS_AtomIsTaggedInt(prop)) { @@ -8624,9 +3999,8 @@ retry: return ret; } } - pr = add_property(ctx, p, prop, JS_PROP_C_W_E); - if (unlikely(!pr)) { + if (UNLIKELY(!pr)) { JS_FreeValue(ctx, val); return -1; } @@ -8635,25 +4009,22 @@ retry: } /* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ -static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, - JSValue prop, JSValue val, int flags) +int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags) { - if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && + if (LIKELY(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { JSObject *p; uint32_t idx; double d; int32_t v; - /* fast path for array access */ p = JS_VALUE_GET_OBJ(this_obj); idx = JS_VALUE_GET_INT(prop); switch(p->class_id) { case JS_CLASS_ARRAY: - if (unlikely(idx >= (uint32_t)p->u.array.count)) { + if (UNLIKELY(idx >= (uint32_t)p->u.array.count)) { JSObject *p1; JSShape *sh1; - /* fast path to add an element to the array */ if (idx != (uint32_t)p->u.array.count || !p->fast_array || !p->extensible) @@ -8663,10 +4034,10 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, while (p1 != NULL) { sh1 = p1->shape; if (p1->class_id == JS_CLASS_ARRAY) { - if (unlikely(!p1->fast_array)) + if (UNLIKELY(!p1->fast_array)) goto slow_path; } else if (p1->class_id == JS_CLASS_OBJECT) { - if (unlikely(sh1->has_small_array_index)) + if (UNLIKELY(sh1->has_small_array_index)) goto slow_path; } else { goto slow_path; @@ -8679,7 +4050,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, set_value(ctx, &p->u.array.u.values[idx], val); break; case JS_CLASS_ARGUMENTS: - if (unlikely(idx >= (uint32_t)p->u.array.count)) + if (UNLIKELY(idx >= (uint32_t)p->u.array.count)) goto slow_path; set_value(ctx, &p->u.array.u.values[idx], val); break; @@ -8688,7 +4059,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, return -1; /* Note: the conversion can detach the typed array, so the array bound check must be done after */ - if (unlikely(idx >= (uint32_t)p->u.array.count)) + if (UNLIKELY(idx >= (uint32_t)p->u.array.count)) goto ta_out_of_bound; p->u.array.u.uint8_ptr[idx] = v; break; @@ -8696,7 +4067,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, case JS_CLASS_UINT8_ARRAY: if (JS_ToInt32Free(ctx, &v, val)) return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) + if (UNLIKELY(idx >= (uint32_t)p->u.array.count)) goto ta_out_of_bound; p->u.array.u.uint8_ptr[idx] = v; break; @@ -8704,7 +4075,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, case JS_CLASS_UINT16_ARRAY: if (JS_ToInt32Free(ctx, &v, val)) return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) + if (UNLIKELY(idx >= (uint32_t)p->u.array.count)) goto ta_out_of_bound; p->u.array.u.uint16_ptr[idx] = v; break; @@ -8712,7 +4083,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, case JS_CLASS_UINT32_ARRAY: if (JS_ToInt32Free(ctx, &v, val)) return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) + if (UNLIKELY(idx >= (uint32_t)p->u.array.count)) goto ta_out_of_bound; p->u.array.u.uint32_ptr[idx] = v; break; @@ -8724,7 +4095,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, int64_t v; if (JS_ToBigInt64Free(ctx, &v, val)) return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) + if (UNLIKELY(idx >= (uint32_t)p->u.array.count)) goto ta_out_of_bound; p->u.array.u.uint64_ptr[idx] = v; } @@ -8733,14 +4104,14 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, case JS_CLASS_FLOAT32_ARRAY: if (JS_ToFloat64Free(ctx, &d, val)) return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) + if (UNLIKELY(idx >= (uint32_t)p->u.array.count)) goto ta_out_of_bound; p->u.array.u.float_ptr[idx] = d; break; case JS_CLASS_FLOAT64_ARRAY: if (JS_ToFloat64Free(ctx, &d, val)) return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) { + if (UNLIKELY(idx >= (uint32_t)p->u.array.count)) { ta_out_of_bound: return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); } @@ -8756,7 +4127,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, slow_path: atom = JS_ValueToAtom(ctx, prop); JS_FreeValue(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) { + if (UNLIKELY(atom == JS_ATOM_NULL)) { JS_FreeValue(ctx, val); return -1; } @@ -8778,7 +4149,6 @@ int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, { JSAtom prop; int res; - if ((uint64_t)idx <= INT32_MAX) { /* fast path for fast arrays */ return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, @@ -8823,12 +4193,10 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p, { JSProperty *pr; int ret, prop_flags; - /* add a new property or modify an existing exotic one */ if (p->is_exotic) { if (p->class_id == JS_CLASS_ARRAY) { uint32_t idx, len; - if (p->fast_array) { if (__JS_AtomIsTaggedInt(prop)) { idx = __JS_AtomToUInt32(prop); @@ -8861,7 +4229,7 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p, JS_ToUint32(ctx, &len, plen->u.value); if ((idx + 1) > len) { pslen = get_shape_prop(p->shape); - if (unlikely(!(pslen->flags & JS_PROP_WRITABLE))) + if (UNLIKELY(!(pslen->flags & JS_PROP_WRITABLE))) return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); /* XXX: should update the length after defining the property */ @@ -8892,12 +4260,10 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p, } } } - if (!p->extensible) { not_extensible: return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); } - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | JS_PROP_GETSET; @@ -8905,7 +4271,7 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p, prop_flags = flags & JS_PROP_C_W_E; } pr = add_property(ctx, p, prop, prop_flags); - if (unlikely(!pr)) + if (UNLIKELY(!pr)) return -1; if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { pr->u.getset.getter = NULL; @@ -8929,10 +4295,9 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p, } /* return FALSE if not OK */ -static BOOL check_define_prop_flags(int prop_flags, int flags) +BOOL check_define_prop_flags(int prop_flags, int flags) { BOOL has_accessor, is_getset; - if (!(prop_flags & JS_PROP_CONFIGURABLE)) { if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) == (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) { @@ -8966,7 +4331,6 @@ static int js_shape_prepare_update(JSContext *ctx, JSObject *p, { JSShape *sh; uint32_t idx = 0; /* prevent warning */ - sh = p->shape; if (sh->is_hashed) { if (sh->header.ref_count != 1) { @@ -8988,8 +4352,8 @@ static int js_shape_prepare_update(JSContext *ctx, JSObject *p, return 0; } -static int js_update_property_flags(JSContext *ctx, JSObject *p, - JSShapeProperty **pprs, int flags) +int js_update_property_flags(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs, int flags) { if (flags != (*pprs)->flags) { if (js_shape_prepare_update(ctx, p, pprs)) @@ -9017,13 +4381,11 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, JSShapeProperty *prs; JSProperty *pr; int mask, res; - if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) { JS_ThrowTypeErrorNotAnObject(ctx); return -1; } p = JS_VALUE_GET_OBJ(this_obj); - redo_prop_update: prs = find_own_property(&pr, p, prop); if (prs) { @@ -9045,19 +4407,16 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, not_configurable: return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); } - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { /* Instantiate property and retry */ if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) return -1; goto redo_prop_update; } - if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { JSObject *new_getter, *new_setter; - if (JS_IsFunction(ctx, getter)) { new_getter = JS_VALUE_GET_OBJ(getter); } else { @@ -9068,7 +4427,6 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, } else { new_setter = NULL; } - if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) { if (js_shape_prepare_update(ctx, p, &prs)) return -1; @@ -9201,7 +4559,6 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, return -1; return TRUE; } - /* handle modification of fast array elements */ if (p->fast_array) { uint32_t idx; @@ -9230,7 +4587,6 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, p->class_id <= JS_CLASS_FLOAT64_ARRAY) { JSValue num; int ret; - if (!__JS_AtomIsTaggedInt(prop)) { /* slow path with to handle all numeric indexes */ num = JS_AtomIsNumericIndex1(ctx, prop); @@ -9273,13 +4629,12 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, typed_array_done: ; } } - return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags); } -static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSAutoInitIDEnum id, - void *opaque, int flags) +int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSAutoInitIDEnum id, + void *opaque, int flags) { JSObject *p; JSProperty *pr; @@ -9297,7 +4652,7 @@ static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj, /* Specialized CreateProperty */ pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT); - if (unlikely(!pr)) + if (UNLIKELY(!pr)) return -1; pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx); assert((pr->u.init.realm_and_id & 3) == 0); @@ -9325,7 +4680,7 @@ int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj, int ret; atom = JS_ValueToAtom(ctx, prop); JS_FreeValue(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) { + if (UNLIKELY(atom == JS_ATOM_NULL)) { JS_FreeValue(ctx, val); return -1; } @@ -9373,8 +4728,8 @@ int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, return ret; } -static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj, - int64_t idx, JSValue val, int flags) +int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj, + int64_t idx, JSValue val, int flags) { return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx), val, flags | JS_PROP_CONFIGURABLE | @@ -9389,7 +4744,6 @@ static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj) JSShapeProperty *prs; JSValueConst val; JSString *p; - prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name); if (!prs) return FALSE; @@ -9402,8 +4756,7 @@ static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj) return (p->len != 0); } -static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj, - JSAtom name, int flags) +int JS_DefineObjectName(JSContext *ctx, JSValueConst obj, JSAtom name, int flags) { if (name != JS_ATOM_NULL && JS_IsObject(obj) @@ -9414,8 +4767,7 @@ static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj, return 0; } -static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj, - JSValueConst str, int flags) +int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj, JSValueConst str, int flags) { if (JS_IsObject(obj) && !js_object_has_name(ctx, obj)) { @@ -9434,226 +4786,6 @@ static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj, return 0; } -#define DEFINE_GLOBAL_LEX_VAR (1 << 7) -#define DEFINE_GLOBAL_FUNC_VAR (1 << 6) - -static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop) -{ - return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop); -} - -/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ -/* XXX: could support exotic global object. */ -static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) -{ - JSObject *p; - JSShapeProperty *prs; - - p = JS_VALUE_GET_OBJ(ctx->global_obj); - prs = find_own_property1(p, prop); - /* XXX: should handle JS_PROP_AUTOINIT */ - if (flags & DEFINE_GLOBAL_LEX_VAR) { - if (prs && !(prs->flags & JS_PROP_CONFIGURABLE)) - goto fail_redeclaration; - } else { - if (!prs && !p->extensible) - goto define_error; - if (flags & DEFINE_GLOBAL_FUNC_VAR) { - if (prs) { - if (!(prs->flags & JS_PROP_CONFIGURABLE) && - ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET || - ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) != - (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) { - define_error: - JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'", - prop); - return -1; - } - } - } - } - /* check if there already is a lexical declaration */ - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - prs = find_own_property1(p, prop); - if (prs) { - fail_redeclaration: - JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop); - return -1; - } - return 0; -} - -/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) | - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */ -/* XXX: could support exotic global object. */ -static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - JSValue val; - int flags; - - if (def_flags & DEFINE_GLOBAL_LEX_VAR) { - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) | - JS_PROP_CONFIGURABLE; - val = JS_UNINITIALIZED; - } else { - p = JS_VALUE_GET_OBJ(ctx->global_obj); - flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | - (def_flags & JS_PROP_CONFIGURABLE); - val = JS_UNDEFINED; - } - prs = find_own_property1(p, prop); - if (prs) - return 0; - if (!p->extensible) - return 0; - pr = add_property(ctx, p, prop, flags); - if (unlikely(!pr)) - return -1; - pr->u.value = val; - return 0; -} - -/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */ -/* XXX: could support exotic global object. */ -static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop, - JSValueConst func, int def_flags) -{ - - JSObject *p; - JSShapeProperty *prs; - int flags; - - p = JS_VALUE_GET_OBJ(ctx->global_obj); - prs = find_own_property1(p, prop); - flags = JS_PROP_HAS_VALUE | JS_PROP_THROW; - if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) { - flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags | - JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE; - } - if (JS_DefineProperty(ctx, ctx->global_obj, prop, func, - JS_UNDEFINED, JS_UNDEFINED, flags) < 0) - return -1; - return 0; -} - -static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop, - BOOL throw_ref_error) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - - /* no exotic behavior is possible in global_var_obj */ - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - prs = find_own_property(&pr, p, prop); - if (prs) { - /* XXX: should handle JS_PROP_TMASK properties */ - if (unlikely(JS_IsUninitialized(pr->u.value))) - return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return JS_DupValue(ctx, pr->u.value); - } - return JS_GetPropertyInternal(ctx, ctx->global_obj, prop, - ctx->global_obj, throw_ref_error); -} - -/* construct a reference to a global variable */ -static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - - /* no exotic behavior is possible in global_var_obj */ - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - prs = find_own_property(&pr, p, prop); - if (prs) { - /* XXX: should handle JS_PROP_AUTOINIT properties? */ - /* XXX: conformance: do these tests in - OP_put_var_ref/OP_get_var_ref ? */ - if (unlikely(JS_IsUninitialized(pr->u.value))) { - JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return -1; - } - if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { - return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); - } - sp[0] = JS_DupValue(ctx, ctx->global_var_obj); - } else { - int ret; - ret = JS_HasProperty(ctx, ctx->global_obj, prop); - if (ret < 0) - return -1; - if (ret) { - sp[0] = JS_DupValue(ctx, ctx->global_obj); - } else { - sp[0] = JS_UNDEFINED; - } - } - sp[1] = JS_AtomToValue(ctx, prop); - return 0; -} - -/* use for strict variable access: test if the variable exists */ -static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop) -{ - JSObject *p; - JSShapeProperty *prs; - int ret; - - /* no exotic behavior is possible in global_var_obj */ - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - prs = find_own_property1(p, prop); - if (prs) { - ret = TRUE; - } else { - ret = JS_HasProperty(ctx, ctx->global_obj, prop); - if (ret < 0) - return -1; - } - return ret; -} - -/* flag = 0: normal variable write - flag = 1: initialize lexical variable - flag = 2: normal variable write, strict check was done before -*/ -static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, - int flag) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - int flags; - - /* no exotic behavior is possible in global_var_obj */ - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - prs = find_own_property(&pr, p, prop); - if (prs) { - /* XXX: should handle JS_PROP_AUTOINIT properties? */ - if (flag != 1) { - if (unlikely(JS_IsUninitialized(pr->u.value))) { - JS_FreeValue(ctx, val); - JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return -1; - } - if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); - } - } - set_value(ctx, &pr->u.value, val); - return 0; - } - flags = JS_PROP_THROW_STRICT; - if (is_strict_mode(ctx)) - flags |= JS_PROP_NO_ADD; - return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags); -} - /* return -1, FALSE or TRUE. return FALSE if not configurable or invalid object. return -1 in case of exception. flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */ @@ -9662,7 +4794,6 @@ int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags) JSValue obj1; JSObject *p; int res; - obj1 = JS_ToObject(ctx, obj); if (JS_IsException(obj1)) return -1; @@ -9679,23 +4810,6 @@ int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags) return FALSE; } -int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags) -{ - JSAtom prop; - int res; - - if ((uint64_t)idx <= JS_ATOM_MAX_INT) { - /* fast path for fast arrays */ - return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags); - } - prop = JS_NewAtomInt64(ctx, idx); - if (prop == JS_ATOM_NULL) - return -1; - res = JS_DeleteProperty(ctx, obj, prop, flags); - JS_FreeAtom(ctx, prop); - return res; -} - BOOL JS_IsFunction(JSContext *ctx, JSValueConst val) { JSObject *p; @@ -9752,16 +4866,6 @@ BOOL JS_IsError(JSContext *ctx, JSValueConst val) return (p->class_id == JS_CLASS_ERROR); } -/* used to avoid catching interrupt exceptions */ -BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(val); - return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error; -} - void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag) { JSObject *p; @@ -9801,97 +4905,12 @@ void *JS_GetOpaque(JSValueConst obj, JSClassID class_id) void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id) { void *p = JS_GetOpaque(obj, class_id); - if (unlikely(!p)) { + if (UNLIKELY(!p)) { JS_ThrowTypeErrorInvalidClass(ctx, class_id); } return p; } -#define HINT_STRING 0 -#define HINT_NUMBER 1 -#define HINT_NONE 2 -/* don't try Symbol.toPrimitive */ -#define HINT_FORCE_ORDINARY (1 << 4) - -static 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; -} - -static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint) -{ - return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint); -} - void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj) { JSObject *p; @@ -9909,74 +4928,11 @@ static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj) p = JS_VALUE_GET_OBJ(obj); return p->is_HTMLDDA; } - -static 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)); -} - -static int skip_spaces(const char *pc) +int skip_spaces(const char *pc) { const uint8_t *p, *p_next, *p_start; uint32_t c; - p = p_start = (const uint8_t *)pc; for (;;) { c = *p; @@ -9994,406 +4950,6 @@ static int skip_spaces(const char *pc) return p - p_start; } -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; -} - -/* 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; -} - -#define ATOD_INT_ONLY (1 << 0) -/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ -#define ATOD_ACCEPT_BIN_OCT (1 << 2) -/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ -#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4) -/* accept _ between digits as a digit separator */ -#define ATOD_ACCEPT_UNDERSCORES (1 << 5) -/* allow a suffix to override the type */ -#define ATOD_ACCEPT_SUFFIX (1 << 6) -/* default type */ -#define ATOD_TYPE_MASK (3 << 7) -#define ATOD_TYPE_FLOAT64 (0 << 7) -#define ATOD_TYPE_BIG_INT (1 << 7) -#define ATOD_TYPE_BIG_FLOAT (2 << 7) -#define ATOD_TYPE_BIG_DECIMAL (3 << 7) -/* assume bigint mode: floats are parsed as integers if no decimal - point nor exponent */ -#define ATOD_MODE_BIGINT (1 << 9) -/* accept -0x1 */ -#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10) - -#ifdef CONFIG_BIGNUM -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 JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf, - int radix, int flags, slimb_t *pexponent) -{ - bf_t *a; - int ret; - JSValue val; - - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigFloat(val); - if (flags & ATOD_ACCEPT_SUFFIX) { - /* return the exponent to get infinite precision */ - ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF, - BF_RNDZ | BF_ATOF_EXPONENT); - } else { - ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec, - ctx->fp_env.flags); - } - if (ret & BF_ST_MEM_ERROR) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); - } - return val; -} - -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; -} - -#endif - -/* return an exception in case of memory error. Return JS_NAN if - invalid syntax */ -#ifdef CONFIG_BIGNUM -static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp, - int radix, int flags, slimb_t *pexponent) -#else -static 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 (is_digit((uint8_t)*p1)) { - p = p1 + 1; - while (is_digit((uint8_t)*p) || (*p == sep && is_digit((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 -static 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 - typedef enum JSToNumberHintEnum { TON_FLAG_NUMBER, TON_FLAG_NUMERIC, @@ -10404,7 +4960,6 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, { uint32_t tag; JSValue ret; - redo: tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { @@ -10453,7 +5008,6 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, const char *str; const char *p; size_t len; - str = JS_ToCStringLen(ctx, &len, val); JS_FreeValue(ctx, val); if (!str) @@ -10487,27 +5041,25 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, return ret; } -static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val) +JSValue JS_ToNumberFree(JSContext *ctx, JSValue val) { return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); } -static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) +JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) { return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); } -static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val) +JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val) { return JS_ToNumericFree(ctx, JS_DupValue(ctx, val)); } -static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, - JSValue val) +__exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) { double d; uint32_t tag; - val = JS_ToNumberFree(ctx, val); if (JS_IsException(val)) { *pres = JS_FLOAT64_NAN; @@ -10541,22 +5093,6 @@ static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, return 0; } -static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) -{ - uint32_t tag; - - tag = JS_VALUE_GET_TAG(val); - if (tag <= JS_TAG_NULL) { - *pres = JS_VALUE_GET_INT(val); - return 0; - } else if (JS_TAG_IS_FLOAT64(tag)) { - *pres = JS_VALUE_GET_FLOAT64(val); - return 0; - } else { - return __JS_ToFloat64Free(ctx, pres, val); - } -} - int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val) { return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val)); @@ -10572,7 +5108,6 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) { uint32_t tag; JSValue ret; - redo: tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { @@ -10599,7 +5134,6 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) { bf_t a_s, *a, r_s, *r = &r_s; BOOL is_nan; - a = JS_ToBigFloat(ctx, &a_s, val); if (!bf_is_finite(a)) { is_nan = bf_is_nan(a); @@ -10707,10 +5241,9 @@ int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val, return res; } -static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) +int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) { uint32_t tag; - redo: tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { @@ -10802,7 +5335,7 @@ static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) u.d = d; /* we avoid doing fmod(x, 2^64) */ e = (u.u64 >> 52) & 0x7ff; - if (likely(e <= (1023 + 62))) { + if (LIKELY(e <= (1023 + 62))) { /* fast case */ ret = (int64_t)d; } else if (e <= (1023 + 62 + 53)) { @@ -10853,11 +5386,10 @@ int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val) } /* return (<0, 0) in case of exception */ -static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) +int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) { uint32_t tag; int32_t ret; - redo: tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { @@ -10876,7 +5408,7 @@ static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) u.d = d; /* we avoid doing fmod(x, 2^32) */ e = (u.u64 >> 52) & 0x7ff; - if (likely(e <= (1023 + 30))) { + if (LIKELY(e <= (1023 + 30))) { /* fast case */ ret = (int32_t)d; } else if (e <= (1023 + 30 + 53)) { @@ -10924,11 +5456,10 @@ static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val) return JS_ToInt32Free(ctx, (int32_t *)pres, val); } -static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) +int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) { uint32_t tag; int res; - redo: tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { @@ -10983,86 +5514,6 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) return 0; } -static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, - JSValue val, BOOL is_array_ctor) -{ - uint32_t tag, len; - - tag = JS_VALUE_GET_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - { - int v; - v = JS_VALUE_GET_INT(val); - if (v < 0) - goto fail; - len = v; - } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_t a; - BOOL res; - bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD); - bf_init(ctx->bf_ctx, &a); - bf_set_ui(&a, len); - res = bf_cmp_eq(&a, &p->num); - bf_delete(&a); - JS_FreeValue(ctx, val); - if (!res) - goto fail; - } - break; -#endif - default: - if (JS_TAG_IS_FLOAT64(tag)) { - double d; - d = JS_VALUE_GET_FLOAT64(val); - len = (uint32_t)d; - if (len != d) - goto fail; - } else { - uint32_t len1; - - if (is_array_ctor) { - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return -1; - /* cannot recurse because val is a number */ - if (JS_ToArrayLengthFree(ctx, &len, val, TRUE)) - return -1; - } else { - /* legacy behavior: must do the conversion twice and compare */ - if (JS_ToUint32(ctx, &len, val)) { - JS_FreeValue(ctx, val); - return -1; - } - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return -1; - /* cannot recurse because val is a number */ - if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE)) - return -1; - if (len1 != len) { - fail: - JS_ThrowRangeError(ctx, "invalid array length"); - return -1; - } - } - } - break; - } - *plen = len; - return 0; -} - -#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) - static BOOL is_safe_integer(double d) { return isfinite(d) && floor(d) == d && @@ -11085,8 +5536,7 @@ int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) /* convert a value to a length between 0 and MAX_SAFE_INTEGER. return -1 for exception */ -static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, - JSValue val) +int JS_ToLengthFree(JSContext *ctx, int64_t *plen, JSValue val) { int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0); JS_FreeValue(ctx, val); @@ -11099,7 +5549,7 @@ static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val) double d; if (!JS_IsNumber(val)) return FALSE; - if (unlikely(JS_ToFloat64(ctx, &d, val))) + if (UNLIKELY(JS_ToFloat64(ctx, &d, val))) return -1; return isfinite(d) && floor(d) == d; } @@ -11147,478 +5597,7 @@ static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) } } -#ifdef CONFIG_BIGNUM - -static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix) -{ - JSValue ret; - bf_t a_s, *a; - char *str; - int saved_sign; - - a = JS_ToBigInt(ctx, &a_s, val); - if (!a) - return JS_EXCEPTION; - saved_sign = a->sign; - if (a->expn == BF_EXP_ZERO) - a->sign = 0; - str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC | - BF_FTOA_JS_QUIRKS); - a->sign = saved_sign; - JS_FreeBigInt(ctx, a, &a_s); - if (!str) - return JS_ThrowOutOfMemory(ctx); - ret = JS_NewString(ctx, str); - bf_free(ctx->bf_ctx, str); - return ret; -} - -static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val) -{ - return js_bigint_to_string1(ctx, val, 10); -} - -static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix, - limb_t prec, bf_flags_t flags) -{ - JSValue val, ret; - bf_t a_s, *a; - char *str; - int saved_sign; - - val = JS_ToNumeric(ctx, val1); - if (JS_IsException(val)) - return val; - a = JS_ToBigFloat(ctx, &a_s, val); - saved_sign = a->sign; - if (a->expn == BF_EXP_ZERO) - a->sign = 0; - flags |= BF_FTOA_JS_QUIRKS; - if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) { - /* Note: for floating point numbers with a radix which is not - a power of two, the current precision is used to compute - the number of digits. */ - if ((radix & (radix - 1)) != 0) { - bf_t r_s, *r = &r_s; - int prec, flags1; - /* must round first */ - if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { - prec = ctx->fp_env.prec; - flags1 = ctx->fp_env.flags & - (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)); - } else { - prec = 53; - flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL; - } - bf_init(ctx->bf_ctx, r); - bf_set(r, a); - bf_round(r, prec, flags1 | BF_RNDN); - str = bf_ftoa(NULL, r, radix, prec, flags1 | flags); - bf_delete(r); - } else { - str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags); - } - } else { - str = bf_ftoa(NULL, a, radix, prec, flags); - } - a->sign = saved_sign; - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, val); - if (!str) - return JS_ThrowOutOfMemory(ctx); - ret = JS_NewString(ctx, str); - bf_free(ctx->bf_ctx, str); - return ret; -} - -static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val) -{ - return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); -} - -static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val, - limb_t prec, int flags) -{ - JSValue ret; - bfdec_t *a; - char *str; - int saved_sign; - - a = JS_ToBigDecimal(ctx, val); - saved_sign = a->sign; - if (a->expn == BF_EXP_ZERO) - a->sign = 0; - str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS); - a->sign = saved_sign; - if (!str) - return JS_ThrowOutOfMemory(ctx); - ret = JS_NewString(ctx, str); - bf_free(ctx->bf_ctx, str); - return ret; -} - -static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) -{ - return js_bigdecimal_to_string1(ctx, val, 0, - BF_RNDZ | BF_FTOA_FORMAT_FREE); -} - -#endif /* CONFIG_BIGNUM */ - -/* 2 <= base <= 36 */ -static char *i64toa(char *buf_end, int64_t n, unsigned int base) -{ - char *q = buf_end; - int digit, is_neg; - - is_neg = 0; - if (n < 0) { - is_neg = 1; - n = -n; - } - *--q = '\0'; - do { - digit = (uint64_t)n % base; - n = (uint64_t)n / base; - if (digit < 10) - digit += '0'; - else - digit += 'a' - 10; - *--q = digit; - } while (n != 0); - if (is_neg) - *--q = '-'; - return q; -} - -/* buf1 contains the printf result */ -static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf, - int rounding_mode, char *buf1, int buf1_size) -{ - if (rounding_mode != FE_TONEAREST) - fesetround(rounding_mode); - snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d); - if (rounding_mode != FE_TONEAREST) - fesetround(FE_TONEAREST); - *sign = (buf1[0] == '-'); - /* mantissa */ - buf[0] = buf1[1]; - if (n_digits > 1) - memcpy(buf + 1, buf1 + 3, n_digits - 1); - buf[n_digits] = '\0'; - /* exponent */ - *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1; -} - -/* maximum buffer size for js_dtoa */ -#define JS_DTOA_BUF_SIZE 128 - -/* needed because ecvt usually limits the number of digits to - 17. Return the number of digits. */ -static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf, - BOOL is_fixed) -{ - int rounding_mode; - char buf_tmp[JS_DTOA_BUF_SIZE]; - - if (!is_fixed) { - unsigned int n_digits_min, n_digits_max; - /* find the minimum amount of digits (XXX: inefficient but simple) */ - n_digits_min = 1; - n_digits_max = 17; - while (n_digits_min < n_digits_max) { - n_digits = (n_digits_min + n_digits_max) / 2; - js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST, - buf_tmp, sizeof(buf_tmp)); - if (strtod(buf_tmp, NULL) == d) { - /* no need to keep the trailing zeros */ - while (n_digits >= 2 && buf[n_digits - 1] == '0') - n_digits--; - n_digits_max = n_digits; - } else { - n_digits_min = n_digits + 1; - } - } - n_digits = n_digits_max; - rounding_mode = FE_TONEAREST; - } else { - rounding_mode = FE_TONEAREST; -#ifdef CONFIG_PRINTF_RNDN - { - char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE]; - int decpt1, sign1, decpt2, sign2; - /* The JS rounding is specified as round to nearest ties away - from zero (RNDNA), but in printf the "ties" case is not - specified (for example it is RNDN for glibc, RNDNA for - Windows), so we must round manually. */ - js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST, - buf_tmp, sizeof(buf_tmp)); - /* XXX: could use 2 digits to reduce the average running time */ - if (buf1[n_digits] == '5') { - js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD, - buf_tmp, sizeof(buf_tmp)); - js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD, - buf_tmp, sizeof(buf_tmp)); - if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) { - /* exact result: round away from zero */ - if (sign1) - rounding_mode = FE_DOWNWARD; - else - rounding_mode = FE_UPWARD; - } - } - } -#endif /* CONFIG_PRINTF_RNDN */ - } - js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode, - buf_tmp, sizeof(buf_tmp)); - return n_digits; -} - -static int js_fcvt1(char *buf, int buf_size, double d, int n_digits, - int rounding_mode) -{ - int n; - if (rounding_mode != FE_TONEAREST) - fesetround(rounding_mode); - n = snprintf(buf, buf_size, "%.*f", n_digits, d); - if (rounding_mode != FE_TONEAREST) - fesetround(FE_TONEAREST); - assert(n < buf_size); - return n; -} - -static void js_fcvt(char *buf, int buf_size, double d, int n_digits) -{ - int rounding_mode; - rounding_mode = FE_TONEAREST; -#ifdef CONFIG_PRINTF_RNDN - { - int n1, n2; - char buf1[JS_DTOA_BUF_SIZE]; - char buf2[JS_DTOA_BUF_SIZE]; - - /* The JS rounding is specified as round to nearest ties away from - zero (RNDNA), but in printf the "ties" case is not specified - (for example it is RNDN for glibc, RNDNA for Windows), so we - must round manually. */ - n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST); - rounding_mode = FE_TONEAREST; - /* XXX: could use 2 digits to reduce the average running time */ - if (buf1[n1 - 1] == '5') { - n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD); - n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD); - if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) { - /* exact result: round away from zero */ - if (buf1[0] == '-') - rounding_mode = FE_DOWNWARD; - else - rounding_mode = FE_UPWARD; - } - } - } -#endif /* CONFIG_PRINTF_RNDN */ - js_fcvt1(buf, buf_size, d, n_digits, rounding_mode); -} - -/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */ -/* use as many digits as necessary */ -#define JS_DTOA_VAR_FORMAT (0 << 0) -/* use n_digits significant digits (1 <= n_digits <= 101) */ -#define JS_DTOA_FIXED_FORMAT (1 << 0) -/* force fractional format: [-]dd.dd with n_digits fractional digits */ -#define JS_DTOA_FRAC_FORMAT (2 << 0) -/* force exponential notation either in fixed or variable format */ -#define JS_DTOA_FORCE_EXP (1 << 2) - -/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough. - XXX: radix != 10 is only supported for small integers -*/ -static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags) -{ - char *q; - - if (!isfinite(d)) { - if (isnan(d)) { - strcpy(buf, "NaN"); - } else { - q = buf; - if (d < 0) - *q++ = '-'; - strcpy(q, "Infinity"); - } - } else if (flags == JS_DTOA_VAR_FORMAT) { - int64_t i64; - char buf1[70], *ptr; - i64 = (int64_t)d; - if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER) - goto generic_conv; - /* fast path for integers */ - ptr = i64toa(buf1 + sizeof(buf1), i64, radix); - strcpy(buf, ptr); - } else { - if (d == 0.0) - d = 0.0; /* convert -0 to 0 */ - if (flags == JS_DTOA_FRAC_FORMAT) { - js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits); - } else { - char buf1[JS_DTOA_BUF_SIZE]; - int sign, decpt, k, n, i, p, n_max; - BOOL is_fixed; - generic_conv: - is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT); - if (is_fixed) { - n_max = n_digits; - } else { - n_max = 21; - } - /* the number has k digits (k >= 1) */ - k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed); - n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */ - q = buf; - if (sign) - *q++ = '-'; - if (flags & JS_DTOA_FORCE_EXP) - goto force_exp; - if (n >= 1 && n <= n_max) { - if (k <= n) { - memcpy(q, buf1, k); - q += k; - for(i = 0; i < (n - k); i++) - *q++ = '0'; - *q = '\0'; - } else { - /* k > n */ - memcpy(q, buf1, n); - q += n; - *q++ = '.'; - for(i = 0; i < (k - n); i++) - *q++ = buf1[n + i]; - *q = '\0'; - } - } else if (n >= -5 && n <= 0) { - *q++ = '0'; - *q++ = '.'; - for(i = 0; i < -n; i++) - *q++ = '0'; - memcpy(q, buf1, k); - q += k; - *q = '\0'; - } else { - force_exp: - /* exponential notation */ - *q++ = buf1[0]; - if (k > 1) { - *q++ = '.'; - for(i = 1; i < k; i++) - *q++ = buf1[i]; - } - *q++ = 'e'; - p = n - 1; - if (p >= 0) - *q++ = '+'; - sprintf(q, "%d", p); - } - } - } -} - -static JSValue js_dtoa(JSContext *ctx, - double d, int radix, int n_digits, int flags) -{ - char buf[JS_DTOA_BUF_SIZE]; - js_dtoa1(buf, d, radix, n_digits, flags); - return JS_NewString(ctx, buf); -} - -JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey) -{ - uint32_t tag; - const char *str; - char buf[32]; - - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_STRING: - return JS_DupValue(ctx, val); - case JS_TAG_INT: - snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val)); - str = buf; - goto new_string; - case JS_TAG_BOOL: - return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? - JS_ATOM_true : JS_ATOM_false); - case JS_TAG_NULL: - return JS_AtomToString(ctx, JS_ATOM_null); - case JS_TAG_UNDEFINED: - return JS_AtomToString(ctx, JS_ATOM_undefined); - case JS_TAG_EXCEPTION: - return JS_EXCEPTION; - case JS_TAG_OBJECT: - { - JSValue val1, ret; - val1 = JS_ToPrimitive(ctx, val, HINT_STRING); - if (JS_IsException(val1)) - return val1; - ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey); - JS_FreeValue(ctx, val1); - return ret; - } - break; - case JS_TAG_FUNCTION_BYTECODE: - str = "[function bytecode]"; - goto new_string; - case JS_TAG_SYMBOL: - if (is_ToPropertyKey) { - return JS_DupValue(ctx, val); - } else { - return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); - } - case JS_TAG_FLOAT64: - return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0, - JS_DTOA_VAR_FORMAT); -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - return ctx->rt->bigint_ops.to_string(ctx, val); - case JS_TAG_BIG_FLOAT: - return ctx->rt->bigfloat_ops.to_string(ctx, val); - case JS_TAG_BIG_DECIMAL: - return ctx->rt->bigdecimal_ops.to_string(ctx, val); -#endif - default: - str = "[unsupported type]"; - new_string: - return JS_NewString(ctx, str); - } -} - -JSValue JS_ToString(JSContext *ctx, JSValueConst val) -{ - return JS_ToStringInternal(ctx, val, FALSE); -} - -static JSValue JS_ToStringFree(JSContext *ctx, JSValue val) -{ - JSValue ret; - ret = JS_ToString(ctx, val); - JS_FreeValue(ctx, val); - return ret; -} - -static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val) -{ - if (JS_IsUndefined(val) || JS_IsNull(val)) - return JS_ToStringFree(ctx, val); - return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL); -} - -JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val) -{ - return JS_ToStringInternal(ctx, val, TRUE); -} - -static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val) +JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val) { uint32_t tag = JS_VALUE_GET_TAG(val); if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) @@ -11626,73 +5605,6 @@ static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val) return JS_ToString(ctx, val); } -static 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 __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) { printf("%14s %4s %4s %14s %10s %s\n", @@ -11708,7 +5620,6 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) JSShapeProperty *prs; JSProperty *pr; BOOL is_first = TRUE; - /* XXX: should encode atoms with special characters */ sh = p->shape; /* the shape can be NULL while freeing an object */ printf("%14p %4d ", @@ -11758,7 +5669,6 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) } printf(" ] "); } - if (sh) { printf("{ "); for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { @@ -11786,7 +5696,6 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) } printf(" }"); } - if (js_class_has_bytecode(p->class_id)) { JSFunctionBytecode *b = p->u.func.function_bytecode; JSVarRef **var_refs; @@ -11839,11 +5748,10 @@ static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) } static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, - JSValueConst val) + JSValueConst val) { uint32_t tag = JS_VALUE_GET_NORM_TAG(val); const char *str; - switch(tag) { case JS_TAG_INT: printf("%d", JS_VALUE_GET_INT(val)); @@ -11964,7 +5872,7 @@ int JS_IsArray(JSContext *ctx, JSValueConst val) JSObject *p; if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { p = JS_VALUE_GET_OBJ(val); - if (unlikely(p->class_id == JS_CLASS_PROXY)) + if (UNLIKELY(p->class_id == JS_CLASS_PROXY)) return js_proxy_isArray(ctx, val); else return p->class_id == JS_CLASS_ARRAY; @@ -11973,16 +5881,6 @@ int JS_IsArray(JSContext *ctx, JSValueConst val) } } -static 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); - } -} - #ifdef CONFIG_BIGNUM JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v) @@ -12029,319 +5927,6 @@ JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) return val; } -/* if the returned bigfloat is allocated it is equal to - 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return - NULL in case of error. */ -static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val) -{ - uint32_t tag; - bf_t *r; - JSBigFloat *p; - - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - r = buf; - bf_init(ctx->bf_ctx, r); - if (bf_set_si(r, JS_VALUE_GET_INT(val))) - goto fail; - break; - case JS_TAG_FLOAT64: - r = buf; - bf_init(ctx->bf_ctx, r); - if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) { - fail: - bf_delete(r); - return NULL; - } - break; - case JS_TAG_BIG_INT: - case JS_TAG_BIG_FLOAT: - p = JS_VALUE_GET_PTR(val); - r = &p->num; - break; - case JS_TAG_UNDEFINED: - default: - r = buf; - bf_init(ctx->bf_ctx, r); - bf_set_nan(r); - break; - } - return r; -} - -/* return NULL if invalid type */ -static 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; -} - -/* return NaN if bad bigint literal */ -static 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; -} - -/* 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; -} - -static 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 */ -static 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 */ -static 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)); -} - -static JSBigFloat *js_new_bf(JSContext *ctx) -{ - JSBigFloat *p; - p = js_malloc(ctx, sizeof(*p)); - if (!p) - return NULL; - p->header.ref_count = 1; - bf_init(ctx->bf_ctx, &p->num); - return p; -} - -static JSValue JS_NewBigFloat(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_FLOAT, p); -} - -static 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); -} - -static 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 */ -static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val) -{ - return JS_CompactBigInt1(ctx, val, is_math_mode(ctx)); -} - /* must be kept in sync with JSOverloadableOperatorEnum */ /* XXX: use atoms ? */ static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = { @@ -12366,7 +5951,7 @@ static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = { "~", }; -static int get_ovop_from_opcode(OPCodeEnum op) +int get_ovop_from_opcode(OPCodeEnum op) { switch(op) { case OP_add: @@ -12432,23 +6017,21 @@ static JSObject *find_binary_op(JSBinaryOperatorDef *def, /* return -1 if exception, 0 if no operator overloading, 1 if overloaded operator called */ -static __exception int js_call_binary_op_fallback(JSContext *ctx, - JSValue *pret, - JSValueConst op1, - JSValueConst op2, - OPCodeEnum op, - BOOL is_numeric, - int hint) +int js_call_binary_op_fallback(JSContext *ctx, + JSValue *pret, + JSValueConst op1, + JSValueConst op2, + OPCodeEnum op, + BOOL is_numeric, + int hint) { JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2; JSOperatorSetData *opset1, *opset2; JSOverloadableOperatorEnum ovop; JSObject *p; JSValueConst args[2]; - if (!ctx->allow_operator_overloading) return 0; - opset2_obj = JS_UNDEFINED; opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); if (JS_IsException(opset1_obj)) @@ -12458,7 +6041,6 @@ static __exception int js_call_binary_op_fallback(JSContext *ctx, opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); if (!opset1) goto exception; - opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet); if (JS_IsException(opset2_obj)) goto exception; @@ -12469,15 +6051,12 @@ static __exception int js_call_binary_op_fallback(JSContext *ctx, opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET); if (!opset2) goto exception; - if (opset1->is_primitive && opset2->is_primitive) { JS_FreeValue(ctx, opset1_obj); JS_FreeValue(ctx, opset2_obj); return 0; } - ovop = get_ovop_from_opcode(op); - if (opset1->operator_counter == opset2->operator_counter) { p = opset1->self_ops[ovop]; } else if (opset1->operator_counter > opset2->operator_counter) { @@ -12490,7 +6069,6 @@ static __exception int js_call_binary_op_fallback(JSContext *ctx, js_overloadable_operator_names[ovop]); goto exception; } - if (opset1->is_primitive) { if (is_numeric) { new_op1 = JS_ToNumeric(ctx, op1); @@ -12502,7 +6080,6 @@ static __exception int js_call_binary_op_fallback(JSContext *ctx, } else { new_op1 = JS_DupValue(ctx, op1); } - if (opset2->is_primitive) { if (is_numeric) { new_op2 = JS_ToNumeric(ctx, op2); @@ -12516,10 +6093,8 @@ static __exception int js_call_binary_op_fallback(JSContext *ctx, } else { new_op2 = JS_DupValue(ctx, op2); } - /* XXX: could apply JS_ToPrimitive() if primitive type so that the operator function does not get a value object */ - method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) { args[0] = new_op2; @@ -12559,63 +6134,6 @@ static __exception int js_call_binary_op_fallback(JSContext *ctx, return -1; } -/* 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; -} - /* return -1 if exception, 0 if no operator overloading, 1 if overloaded operator called */ static __exception int js_call_unary_op_fallback(JSContext *ctx, @@ -12627,10 +6145,8 @@ static __exception int js_call_unary_op_fallback(JSContext *ctx, JSOperatorSetData *opset1; JSOverloadableOperatorEnum ovop; JSObject *p; - if (!ctx->allow_operator_overloading) return 0; - opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); if (JS_IsException(opset1_obj)) goto exception; @@ -12643,9 +6159,7 @@ static __exception int js_call_unary_op_fallback(JSContext *ctx, JS_FreeValue(ctx, opset1_obj); return 0; } - ovop = get_ovop_from_opcode(op); - p = opset1->self_ops[ovop]; if (!p) { JS_ThrowTypeError(ctx, "no overloaded operator %s", @@ -12665,177 +6179,11 @@ static __exception int js_call_unary_op_fallback(JSContext *ctx, return -1; } -static 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); -} - -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; -} - -static int js_unary_arith_bigfloat(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, "bigfloat argument with unary +"); - JS_FreeValue(ctx, op1); - return -1; - } - - res = JS_NewBigFloat(ctx); - if (JS_IsException(res)) { - JS_FreeValue(ctx, op1); - return -1; - } - r = JS_GetBigFloat(res); - a = JS_ToBigFloat(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, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case OP_plus: - ret = bf_set(r, a); - break; - case OP_neg: - ret = bf_set(r, a); - bf_neg(r); - break; - default: - abort(); - } - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - if (unlikely(ret & BF_ST_MEM_ERROR)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - *pres = res; - return 0; -} - -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; -} - -static no_inline __exception int js_unary_arith_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) +int js_unary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { JSValue op1, val; int v, ret; uint32_t tag; - op1 = sp[-1]; /* fast path for float64 */ if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) @@ -12850,7 +6198,6 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, return 0; } } - op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) goto exception; @@ -12926,11 +6273,9 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, return -1; } -static __exception int js_post_inc_slow(JSContext *ctx, - JSValue *sp, OPCodeEnum op) +int js_post_inc_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { JSValue op1; - /* XXX: allow custom operators */ op1 = sp[-1]; op1 = JS_ToNumericFree(ctx, op1); @@ -12943,11 +6288,10 @@ static __exception int js_post_inc_slow(JSContext *ctx, return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec); } -static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) +int js_not_slow(JSContext *ctx, JSValue *sp) { JSValue op1, val; int ret; - op1 = sp[-1]; if (JS_IsObject(op1)) { ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not); @@ -12959,7 +6303,6 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) return 0; } } - op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) goto exception; @@ -12968,7 +6311,7 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) goto exception; } else { int32_t v1; - if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) + if (UNLIKELY(JS_ToInt32Free(ctx, &v1, op1))) goto exception; sp[-1] = JS_NewInt32(ctx, ~v1); } @@ -12978,317 +6321,12 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) return -1; } -static int js_binary_arith_bigfloat(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_NewBigFloat(ctx); - if (JS_IsException(res)) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return -1; - } - r = JS_GetBigFloat(res); - a = JS_ToBigFloat(ctx, &a_s, op1); - b = JS_ToBigFloat(ctx, &b_s, op2); - bf_init(ctx->bf_ctx, r); - switch(op) { - case OP_add: - ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case OP_sub: - ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case OP_mul: - ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case OP_div: - ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case OP_math_mod: - /* Euclidian remainder */ - ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, - BF_DIVREM_EUCLIDIAN); - break; - case OP_mod: - ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, - BF_RNDZ); - break; - case OP_pow: - ret = bf_pow(r, a, b, ctx->fp_env.prec, - ctx->fp_env.flags | BF_POW_JS_QUIRKS); - break; - default: - abort(); - } - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (unlikely(ret & BF_ST_MEM_ERROR)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - *pres = res; - return 0; -} - -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; -} - -/* 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_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; -} - -static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op) +int js_binary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { JSValue op1, op2, res; uint32_t tag1, tag2; int ret; double d1, d2; - op1 = sp[-2]; op2 = sp[-1]; tag1 = JS_VALUE_GET_NORM_TAG(op1); @@ -13299,7 +6337,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s d2 = JS_VALUE_GET_FLOAT64(op2); goto handle_float64; } - /* try to call an overloaded operator */ if ((tag1 == JS_TAG_OBJECT && (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || @@ -13317,7 +6354,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s } } } - op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -13330,7 +6366,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s } tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); - if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { int32_t v1, v2; int64_t v; @@ -13356,7 +6391,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2); return 0; case OP_math_mod: - if (unlikely(v2 == 0)) { + if (UNLIKELY(v2 == 0)) { throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO); goto exception; } @@ -13445,15 +6480,13 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s return -1; } -static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) +int js_add_slow(JSContext *ctx, JSValue *sp) { JSValue op1, op2, res; uint32_t tag1, tag2; int ret; - op1 = sp[-2]; op2 = sp[-1]; - tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); /* fast path for float64 */ @@ -13464,7 +6497,6 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) sp[-2] = __JS_NewFloat64(ctx, d1 + d2); return 0; } - if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { /* try to call an overloaded operator */ if ((tag1 == JS_TAG_OBJECT && @@ -13486,13 +6518,11 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) } } } - 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); @@ -13501,14 +6531,12 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); } - if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { sp[-2] = JS_ConcatString(ctx, op1, op2); if (JS_IsException(sp[-2])) goto exception; return 0; } - op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -13521,7 +6549,6 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) } tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); - if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { int32_t v1, v2; int64_t v; @@ -13559,20 +6586,16 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) return -1; } -static no_inline __exception int js_binary_logic_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) +int js_binary_logic_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { JSValue op1, op2, res; int ret; uint32_t tag1, tag2; uint32_t v1, v2, r; - op1 = sp[-2]; op2 = sp[-1]; tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); - /* try to call an overloaded operator */ if ((tag1 == JS_TAG_OBJECT && (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || @@ -13590,7 +6613,6 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, } } } - op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -13601,10 +6623,8 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, JS_FreeValue(ctx, op1); goto exception; } - if (is_math_mode(ctx)) goto bigint_op; - tag1 = JS_VALUE_GET_TAG(op1); tag2 = JS_VALUE_GET_TAG(op2); if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { @@ -13619,11 +6639,11 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, goto exception; } } else { - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { + if (UNLIKELY(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { JS_FreeValue(ctx, op2); goto exception; } - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) + if (UNLIKELY(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) goto exception; switch(op) { case OP_shl: @@ -13653,106 +6673,11 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, return -1; } -/* Note: also used for bigint */ -static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op, - JSValue op1, JSValue op2) -{ - bf_t a_s, b_s, *a, *b; - int res; - - a = JS_ToBigFloat(ctx, &a_s, op1); - if (!a) { - JS_FreeValue(ctx, op2); - return -1; - } - b = JS_ToBigFloat(ctx, &b_s, op2); - if (!b) { - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - return -1; - } - switch(op) { - case OP_lt: - res = bf_cmp_lt(a, b); /* if NaN return false */ - break; - case OP_lte: - res = bf_cmp_le(a, b); /* if NaN return false */ - break; - case OP_gt: - res = bf_cmp_lt(b, a); /* if NaN return false */ - break; - case OP_gte: - res = bf_cmp_le(b, a); /* if NaN return false */ - break; - case OP_eq: - res = bf_cmp_eq(a, b); /* if NaN return false */ - break; - default: - abort(); - } - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return res; -} - -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 no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op) +int js_relational_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { JSValue op1, op2, ret; int res; uint32_t tag1, tag2; - op1 = sp[-2]; op2 = sp[-1]; tag1 = JS_VALUE_GET_NORM_TAG(op1); @@ -13787,7 +6712,6 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, } tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); - if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { JSString *p1, *p2; p1 = JS_VALUE_GET_STRING(op1); @@ -13845,10 +6769,8 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, goto exception; } } - tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); - if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2); if (res < 0) @@ -13863,7 +6785,6 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, goto exception; } else { double d1, d2; - float64_compare: /* can use floating point comparison */ if (tag1 == JS_TAG_FLOAT64) { @@ -13902,171 +6823,7 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, return -1; } -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 no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, - BOOL is_neq) -{ - 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; -} - -static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) +int js_shr_slow(JSContext *ctx, JSValue *sp) { JSValue op1, op2; uint32_t v1, v2, r; @@ -14104,63 +6861,6 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) return -1; } -static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, - int64_t exponent) -{ - bf_t r_s, *r = &r_s; - double d; - int ret; - - /* always convert to Float64 */ - bf_init(ctx->bf_ctx, r); - ret = bf_mul_pow_radix(r, a, 10, exponent, - 53, bf_set_exp_bits(11) | BF_RNDN | - BF_FLAG_SUBNORMAL); - bf_get_float64(r, &d, BF_RNDN); - bf_delete(r); - if (ret & BF_ST_MEM_ERROR) - return JS_ThrowOutOfMemory(ctx); - else - return __JS_NewFloat64(ctx, d); -} - -static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp) -{ - bf_t a_s, *a, *r; - JSValue op1, op2, res; - int64_t e; - int ret; - - res = JS_NewBigFloat(ctx); - if (JS_IsException(res)) - return -1; - r = JS_GetBigFloat(res); - op1 = sp[-2]; - op2 = sp[-1]; - a = JS_ToBigFloat(ctx, &a_s, op1); - if (!a) - return -1; - if (JS_IsBigInt(ctx, op2)) { - ret = JS_ToBigInt64(ctx, &e, op2); - } else { - ret = JS_ToInt64(ctx, &e, op2); - } - if (ret) { - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, res); - return -1; - } - - bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags); - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - sp[-2] = res; - return 0; -} - #else /* !CONFIG_BIGNUM */ static JSValue JS_ThrowUnsupportedBigint(JSContext *ctx) @@ -14185,15 +6885,12 @@ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) return -1; } -static no_inline __exception int js_unary_arith_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) +int js_unary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { JSValue op1; double d; - op1 = sp[-1]; - if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) { + if (UNLIKELY(JS_ToFloat64Free(ctx, &d, op1))) { sp[-1] = JS_UNDEFINED; return -1; } @@ -14217,14 +6914,12 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, } /* specific case necessary for correct return value semantics */ -static __exception int js_post_inc_slow(JSContext *ctx, - JSValue *sp, OPCodeEnum op) +int js_post_inc_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { JSValue op1; double d, r; - op1 = sp[-1]; - if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) { + if (UNLIKELY(JS_ToFloat64Free(ctx, &d, op1))) { sp[-1] = JS_UNDEFINED; return -1; } @@ -14234,19 +6929,17 @@ static __exception int js_post_inc_slow(JSContext *ctx, return 0; } -static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op) +int js_binary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { JSValue op1, op2; double d1, d2, r; - op1 = sp[-2]; op2 = sp[-1]; - if (unlikely(JS_ToFloat64Free(ctx, &d1, op1))) { + if (UNLIKELY(JS_ToFloat64Free(ctx, &d1, op1))) { JS_FreeValue(ctx, op2); goto exception; } - if (unlikely(JS_ToFloat64Free(ctx, &d2, op2))) { + if (UNLIKELY(JS_ToFloat64Free(ctx, &d2, op2))) { goto exception; } switch(op) { @@ -14276,11 +6969,10 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s return -1; } -static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) +int js_add_slow(JSContext *ctx, JSValue *sp) { JSValue op1, op2; uint32_t tag1, tag2; - op1 = sp[-2]; op2 = sp[-1]; tag1 = JS_VALUE_GET_TAG(op1); @@ -14324,20 +7016,17 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) return -1; } -static no_inline __exception int js_binary_logic_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) +int js_binary_logic_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { JSValue op1, op2; uint32_t v1, v2, r; - op1 = sp[-2]; op2 = sp[-1]; - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { + if (UNLIKELY(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { JS_FreeValue(ctx, op2); goto exception; } - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) + if (UNLIKELY(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) goto exception; switch(op) { case OP_shl: @@ -14366,11 +7055,10 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, return -1; } -static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) +int js_not_slow(JSContext *ctx, JSValue *sp) { int32_t v1; - - if (unlikely(JS_ToInt32Free(ctx, &v1, sp[-1]))) { + if (UNLIKELY(JS_ToInt32Free(ctx, &v1, sp[-1]))) { sp[-1] = JS_UNDEFINED; return -1; } @@ -14378,12 +7066,10 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) return 0; } -static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op) +int js_relational_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { JSValue op1, op2; int res; - op1 = sp[-2]; op2 = sp[-1]; op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); @@ -14451,93 +7137,17 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, return -1; } -static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, - BOOL is_neq) -{ - 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; -} - -static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) +int js_shr_slow(JSContext *ctx, JSValue *sp) { JSValue op1, op2; uint32_t v1, v2, r; - op1 = sp[-2]; op2 = sp[-1]; - if (unlikely(JS_ToUint32Free(ctx, &v1, op1))) { + if (UNLIKELY(JS_ToUint32Free(ctx, &v1, op1))) { JS_FreeValue(ctx, op2); goto exception; } - if (unlikely(JS_ToUint32Free(ctx, &v2, op2))) + if (UNLIKELY(JS_ToUint32Free(ctx, &v2, op2))) goto exception; r = v1 >> (v2 & 0x1f); sp[-2] = JS_NewUint32(ctx, r); @@ -14550,208 +7160,19 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) #endif /* !CONFIG_BIGNUM */ -/* XXX: Should take JSValueConst arguments */ -static 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; -} - -static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2) -{ - return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); -} - -static 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); -} - -static 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); -} - -static no_inline 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 __exception int js_operator_in(JSContext *ctx, JSValue *sp) +int js_operator_in(JSContext *ctx, JSValue *sp) { JSValue op1, op2; JSAtom atom; int ret; - op1 = sp[-2]; op2 = sp[-1]; - if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) { JS_ThrowTypeError(ctx, "invalid 'in' operand"); return -1; } atom = JS_ValueToAtom(ctx, op1); - if (unlikely(atom == JS_ATOM_NULL)) + if (UNLIKELY(atom == JS_ATOM_NULL)) return -1; ret = JS_HasProperty(ctx, op2, atom); JS_FreeAtom(ctx, atom); @@ -14763,118 +7184,6 @@ static __exception int js_operator_in(JSContext *ctx, JSValue *sp) return 0; } -static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj, - JSAtom atom) -{ - JSValue arr, val; - int ret; - - arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables); - if (JS_IsException(arr)) - return -1; - ret = 0; - if (JS_IsObject(arr)) { - val = JS_GetProperty(ctx, arr, atom); - ret = JS_ToBoolFree(ctx, val); - } - JS_FreeValue(ctx, arr); - return ret; -} - -static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) -{ - JSValue op1, op2; - BOOL ret; - - op1 = sp[-2]; - op2 = sp[-1]; - ret = JS_IsInstanceOf(ctx, op1, op2); - if (ret < 0) - return ret; - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - sp[-2] = JS_NewBool(ctx, ret); - return 0; -} - -static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1) -{ - JSAtom atom; - uint32_t tag; - - tag = JS_VALUE_GET_NORM_TAG(op1); - switch(tag) { -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - atom = JS_ATOM_bigint; - break; - case JS_TAG_BIG_FLOAT: - atom = JS_ATOM_bigfloat; - break; - case JS_TAG_BIG_DECIMAL: - atom = JS_ATOM_bigdecimal; - break; -#endif - case JS_TAG_INT: - case JS_TAG_FLOAT64: - atom = JS_ATOM_number; - break; - case JS_TAG_UNDEFINED: - atom = JS_ATOM_undefined; - break; - case JS_TAG_BOOL: - atom = JS_ATOM_boolean; - break; - case JS_TAG_STRING: - atom = JS_ATOM_string; - break; - case JS_TAG_OBJECT: - { - JSObject *p; - p = JS_VALUE_GET_OBJ(op1); - if (unlikely(p->is_HTMLDDA)) - atom = JS_ATOM_undefined; - else if (JS_IsFunction(ctx, op1)) - atom = JS_ATOM_function; - else - goto obj_type; - } - break; - case JS_TAG_NULL: - obj_type: - atom = JS_ATOM_object; - break; - case JS_TAG_SYMBOL: - atom = JS_ATOM_symbol; - break; - default: - atom = JS_ATOM_unknown; - break; - } - return atom; -} - -static __exception int js_operator_delete(JSContext *ctx, JSValue *sp) -{ - JSValue op1, op2; - JSAtom atom; - int ret; - - op1 = sp[-2]; - op2 = sp[-1]; - atom = JS_ValueToAtom(ctx, op2); - if (unlikely(atom == JS_ATOM_NULL)) - return -1; - ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT); - JS_FreeAtom(ctx, atom); - if (unlikely(ret < 0)) - return -1; - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - sp[-2] = JS_NewBool(ctx, ret); - return 0; -} - static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -14937,313 +7246,7 @@ static const JSClassExoticMethods js_arguments_exotic_methods = { .define_own_property = js_arguments_define_own_property, }; -static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv) -{ - JSValue val, *tab; - JSProperty *pr; - JSObject *p; - int i; - - val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], - JS_CLASS_ARGUMENTS); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_OBJ(val); - - /* add the length field (cannot fail) */ - pr = add_property(ctx, p, JS_ATOM_length, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - pr->u.value = JS_NewInt32(ctx, argc); - - /* initialize the fast array part */ - tab = NULL; - if (argc > 0) { - tab = js_malloc(ctx, sizeof(tab[0]) * argc); - if (!tab) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - for(i = 0; i < argc; i++) { - tab[i] = JS_DupValue(ctx, argv[i]); - } - } - p->u.array.u.values = tab; - p->u.array.count = argc; - - JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, - JS_DupValue(ctx, ctx->array_proto_values), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); - /* add callee property to throw a TypeError in strict mode */ - JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED, - ctx->throw_type_error, ctx->throw_type_error, - JS_PROP_HAS_GET | JS_PROP_HAS_SET); - return val; -} - -#define GLOBAL_VAR_OFFSET 0x40000000 -#define ARGUMENT_VAR_OFFSET 0x20000000 - -/* legacy arguments object: add references to the function arguments */ -static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, - JSValueConst *argv, - JSStackFrame *sf, int arg_count) -{ - JSValue val; - JSProperty *pr; - JSObject *p; - int i; - - val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], - JS_CLASS_MAPPED_ARGUMENTS); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_OBJ(val); - - /* add the length field (cannot fail) */ - pr = add_property(ctx, p, JS_ATOM_length, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - pr->u.value = JS_NewInt32(ctx, argc); - - for(i = 0; i < arg_count; i++) { - JSVarRef *var_ref; - var_ref = get_var_ref(ctx, sf, i, TRUE); - if (!var_ref) - goto fail; - pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); - if (!pr) { - free_var_ref(ctx->rt, var_ref); - goto fail; - } - pr->u.var_ref = var_ref; - } - - /* the arguments not mapped to the arguments of the function can - be normal properties */ - for(i = arg_count; i < argc; i++) { - if (JS_DefinePropertyValueUint32(ctx, val, i, - JS_DupValue(ctx, argv[i]), - JS_PROP_C_W_E) < 0) - goto fail; - } - - JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, - JS_DupValue(ctx, ctx->array_proto_values), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); - /* callee returns this function in non strict mode */ - JS_DefinePropertyValue(ctx, val, JS_ATOM_callee, - JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); - return val; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv) -{ - JSValue val; - int i, ret; - - val = JS_NewArray(ctx); - if (JS_IsException(val)) - return val; - for (i = first; i < argc; i++) { - ret = JS_DefinePropertyValueUint32(ctx, val, i - first, - JS_DupValue(ctx, argv[i]), - JS_PROP_C_W_E); - if (ret < 0) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - } - return val; -} - -static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) -{ - JSObject *p; - JSPropertyEnum *tab_atom; - int i; - JSValue enum_obj, obj1; - JSForInIterator *it; - uint32_t tag, tab_atom_count; - - tag = JS_VALUE_GET_TAG(obj); - if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) { - obj = JS_ToObjectFree(ctx, obj); - } - - it = js_malloc(ctx, sizeof(*it)); - if (!it) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR); - if (JS_IsException(enum_obj)) { - js_free(ctx, it); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - it->is_array = FALSE; - it->obj = obj; - it->idx = 0; - p = JS_VALUE_GET_OBJ(enum_obj); - p->u.for_in_iterator = it; - - if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) - return enum_obj; - - /* fast path: assume no enumerable properties in the prototype chain */ - obj1 = JS_DupValue(ctx, obj); - for(;;) { - obj1 = JS_GetPrototypeFree(ctx, obj1); - if (JS_IsNull(obj1)) - break; - if (JS_IsException(obj1)) - goto fail; - if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, - JS_VALUE_GET_OBJ(obj1), - JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { - JS_FreeValue(ctx, obj1); - goto fail; - } - js_free_prop_enum(ctx, tab_atom, tab_atom_count); - if (tab_atom_count != 0) { - JS_FreeValue(ctx, obj1); - goto slow_path; - } - /* must check for timeout to avoid infinite loop */ - if (js_poll_interrupts(ctx)) { - JS_FreeValue(ctx, obj1); - goto fail; - } - } - - p = JS_VALUE_GET_OBJ(obj); - - if (p->fast_array) { - JSShape *sh; - JSShapeProperty *prs; - /* check that there are no enumerable normal fields */ - sh = p->shape; - for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { - if (prs->flags & JS_PROP_ENUMERABLE) - goto normal_case; - } - /* for fast arrays, we only store the number of elements */ - it->is_array = TRUE; - it->array_length = p->u.array.count; - } else { - normal_case: - if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, - JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) - goto fail; - for(i = 0; i < tab_atom_count; i++) { - JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0); - } - js_free_prop_enum(ctx, tab_atom, tab_atom_count); - } - return enum_obj; - - slow_path: - /* non enumerable properties hide the enumerables ones in the - prototype chain */ - obj1 = JS_DupValue(ctx, obj); - for(;;) { - if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, - JS_VALUE_GET_OBJ(obj1), - JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { - JS_FreeValue(ctx, obj1); - goto fail; - } - for(i = 0; i < tab_atom_count; i++) { - JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL, - (tab_atom[i].is_enumerable ? - JS_PROP_ENUMERABLE : 0)); - } - js_free_prop_enum(ctx, tab_atom, tab_atom_count); - obj1 = JS_GetPrototypeFree(ctx, obj1); - if (JS_IsNull(obj1)) - break; - if (JS_IsException(obj1)) - goto fail; - /* must check for timeout to avoid infinite loop */ - if (js_poll_interrupts(ctx)) { - JS_FreeValue(ctx, obj1); - goto fail; - } - } - return enum_obj; - - fail: - JS_FreeValue(ctx, enum_obj); - return JS_EXCEPTION; -} - -/* obj -> enum_obj */ -static __exception int js_for_in_start(JSContext *ctx, JSValue *sp) -{ - sp[-1] = build_for_in_iterator(ctx, sp[-1]); - if (JS_IsException(sp[-1])) - return -1; - return 0; -} - -/* enum_obj -> enum_obj value done */ -static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) -{ - JSValueConst enum_obj; - JSObject *p; - JSAtom prop; - JSForInIterator *it; - int ret; - - enum_obj = sp[-1]; - /* fail safe */ - if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT) - goto done; - p = JS_VALUE_GET_OBJ(enum_obj); - if (p->class_id != JS_CLASS_FOR_IN_ITERATOR) - goto done; - it = p->u.for_in_iterator; - - for(;;) { - if (it->is_array) { - if (it->idx >= it->array_length) - goto done; - prop = __JS_AtomFromUInt32(it->idx); - it->idx++; - } else { - JSShape *sh = p->shape; - JSShapeProperty *prs; - if (it->idx >= sh->prop_count) - goto done; - prs = get_shape_prop(sh) + it->idx; - prop = prs->atom; - it->idx++; - if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE)) - continue; - } - /* check if the property was deleted */ - ret = JS_HasProperty(ctx, it->obj, prop); - if (ret < 0) - return ret; - if (ret) - break; - } - /* return the property */ - sp[0] = JS_AtomToValue(ctx, prop); - sp[1] = JS_FALSE; - return 0; - done: - /* return the end */ - sp[0] = JS_UNDEFINED; - sp[1] = JS_TRUE; - return 0; -} - -static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj, - JSValueConst method) +JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj, JSValueConst method) { JSValue enum_obj; @@ -15257,10 +7260,9 @@ static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj, return enum_obj; } -static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async) +JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async) { JSValue method, ret, sync_iter; - if (is_async) { method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator); if (JS_IsException(method)) @@ -15292,9 +7294,9 @@ static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async) } /* return *pdone = 2 if the iterator object is not parsed */ -static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj, - JSValueConst method, - int argc, JSValueConst *argv, int *pdone) +JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj, + JSValueConst method, + int argc, JSValueConst *argv, int *pdone) { JSValue obj; @@ -15332,13 +7334,12 @@ static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj, return JS_EXCEPTION; } -static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, - JSValueConst method, - int argc, JSValueConst *argv, BOOL *pdone) +JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, + JSValueConst method, + int argc, JSValueConst *argv, BOOL *pdone) { JSValue obj, value, done_val; int done; - obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); if (JS_IsException(obj)) goto fail; @@ -15364,12 +7365,10 @@ static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, } /* return < 0 in case of exception */ -static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, - BOOL is_exception_pending) +int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, BOOL is_exception_pending) { JSValue method, ret, ex_obj; int res; - if (is_exception_pending) { ex_obj = ctx->rt->current_exception; ctx->rt->current_exception = JS_NULL; @@ -15404,8 +7403,7 @@ static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, } /* obj -> enum_rec (3 slots) */ -static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, - BOOL is_async) +int js_for_of_start(JSContext *ctx, JSValue *sp, BOOL is_async) { JSValue op1, obj, method; op1 = sp[-1]; @@ -15421,39 +7419,7 @@ static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, return 0; } -/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset' - objs. If 'done' is true or in case of exception, 'enum_rec' is set - to undefined. If 'done' is true, 'value' is always set to - undefined. */ -static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) -{ - JSValue value = JS_UNDEFINED; - int done = 1; - - if (likely(!JS_IsUndefined(sp[offset]))) { - value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done); - if (JS_IsException(value)) - done = -1; - if (done) { - /* value is JS_UNDEFINED or JS_EXCEPTION */ - /* replace the iteration object with undefined */ - JS_FreeValue(ctx, sp[offset]); - sp[offset] = JS_UNDEFINED; - if (done < 0) { - return -1; - } else { - JS_FreeValue(ctx, value); - value = JS_UNDEFINED; - } - } - } - sp[0] = value; - sp[1] = JS_NewBool(ctx, done); - return 0; -} - -static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj, - BOOL *pdone) +JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj, BOOL *pdone) { JSValue done_val, value; BOOL done; @@ -15471,27 +7437,7 @@ static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj, return JS_EXCEPTION; } -static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) -{ - JSValue obj, value; - BOOL done; - obj = sp[-1]; - if (!JS_IsObject(obj)) { - JS_ThrowTypeError(ctx, "iterator must return an object"); - return -1; - } - value = JS_IteratorGetCompleteValue(ctx, obj, &done); - if (JS_IsException(value)) - return -1; - JS_FreeValue(ctx, obj); - sp[-1] = value; - sp[0] = JS_NewBool(ctx, done); - return 0; -} - -static JSValue js_create_iterator_result(JSContext *ctx, - JSValue val, - BOOL done) +JSValue js_create_iterator_result(JSContext *ctx, JSValue val, BOOL done) { JSValue obj; obj = JS_NewObject(ctx); @@ -15512,126 +7458,11 @@ static JSValue js_create_iterator_result(JSContext *ctx, return obj; } -static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - BOOL *pdone, int magic); - -static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); - -static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj) -{ - /* Try and handle fast arrays explicitly */ - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(obj); - if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { - return TRUE; - } - } - return FALSE; -} - -/* Access an Array's internal JSValue array if available */ -static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, - JSValue **arrpp, uint32_t *countp) -{ - /* Try and handle fast arrays explicitly */ - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(obj); - if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { - *countp = p->u.array.count; - *arrpp = p->u.array.u.values; - return TRUE; - } - } - return FALSE; -} - -static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) -{ - JSValue iterator, enumobj, method, value; - int is_array_iterator; - JSValue *arrp; - uint32_t i, count32, pos; - - if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { - JS_ThrowInternalError(ctx, "invalid index for append"); - return -1; - } - - pos = JS_VALUE_GET_INT(sp[-2]); - - /* XXX: further optimisations: - - use ctx->array_proto_values? - - check if array_iterator_prototype next method is built-in and - avoid constructing actual iterator object? - - build this into js_for_of_start and use in all `for (x of o)` loops - */ - iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator); - if (JS_IsException(iterator)) - return -1; - is_array_iterator = JS_IsCFunction(ctx, iterator, - (JSCFunction *)js_create_array_iterator, - JS_ITERATOR_KIND_VALUE); - JS_FreeValue(ctx, iterator); - - enumobj = JS_GetIterator(ctx, sp[-1], FALSE); - if (JS_IsException(enumobj)) - return -1; - method = JS_GetProperty(ctx, enumobj, JS_ATOM_next); - if (JS_IsException(method)) { - JS_FreeValue(ctx, enumobj); - return -1; - } - if (is_array_iterator - && JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0) - && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { - uint32_t len; - if (js_get_length32(ctx, &len, sp[-1])) - goto exception; - /* if len > count32, the elements >= count32 might be read in - the prototypes and might have side effects */ - if (len != count32) - goto general_case; - /* Handle fast arrays explicitly */ - for (i = 0; i < count32; i++) { - if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, - JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0) - goto exception; - } - } else { - general_case: - for (;;) { - BOOL done; - value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); - if (JS_IsException(value)) - goto exception; - if (done) { - /* value is JS_UNDEFINED */ - break; - } - if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0) - goto exception; - } - } - /* Note: could raise an error if too many elements */ - sp[-2] = JS_NewInt32(ctx, pos); - JS_FreeValue(ctx, enumobj); - JS_FreeValue(ctx, method); - return 0; - -exception: - JS_IteratorClose(ctx, enumobj, TRUE); - JS_FreeValue(ctx, enumobj); - JS_FreeValue(ctx, method); - return -1; -} - -static __exception int JS_CopyDataProperties(JSContext *ctx, - JSValueConst target, - JSValueConst source, - JSValueConst excluded, - BOOL setprop) +int JS_CopyDataProperties(JSContext *ctx, + JSValueConst target, + JSValueConst source, + JSValueConst excluded, + BOOL setprop) { JSPropertyEnum *tab_atom; JSValue val; @@ -15641,15 +7472,11 @@ static __exception int JS_CopyDataProperties(JSContext *ctx, int ret, gpn_flags; JSPropertyDescriptor desc; BOOL is_enumerable; - if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) return 0; - if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT) pexcl = JS_VALUE_GET_OBJ(excluded); - p = JS_VALUE_GET_OBJ(source); - gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY; if (p->is_exotic) { const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; @@ -15662,7 +7489,6 @@ static __exception int JS_CopyDataProperties(JSContext *ctx, if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, gpn_flags)) return -1; - for (i = 0; i < tab_atom_count; i++) { if (pexcl) { ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom); @@ -15702,14 +7528,7 @@ static __exception int JS_CopyDataProperties(JSContext *ctx, return -1; } -/* only valid inside C functions */ -static JSValueConst JS_GetActiveFunction(JSContext *ctx) -{ - return ctx->rt->current_stack_frame->cur_func; -} - -static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, - int var_idx, BOOL is_arg) +JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, BOOL is_arg) { JSVarRef *var_ref; struct list_head *el; @@ -15738,51 +7557,10 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, return var_ref; } -static JSValue js_closure2(JSContext *ctx, JSValue func_obj, - JSFunctionBytecode *b, - JSVarRef **cur_var_refs, - JSStackFrame *sf) -{ - JSObject *p; - JSVarRef **var_refs; - int i; - - p = JS_VALUE_GET_OBJ(func_obj); - p->u.func.function_bytecode = b; - p->u.func.home_object = NULL; - p->u.func.var_refs = NULL; - if (b->closure_var_count) { - var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); - if (!var_refs) - goto fail; - p->u.func.var_refs = var_refs; - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - JSVarRef *var_ref; - if (cv->is_local) { - /* reuse the existing variable reference if it already exists */ - var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg); - if (!var_ref) - goto fail; - } else { - var_ref = cur_var_refs[cv->var_idx]; - var_ref->header.ref_count++; - } - var_refs[i] = var_ref; - } - } - return func_obj; - fail: - /* bfunc is freed when func_obj is freed */ - JS_FreeValue(ctx, func_obj); - return JS_EXCEPTION; -} - static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) { JSValue obj, this_val; int ret; - this_val = JS_MKPTR(JS_TAG_OBJECT, p); obj = JS_NewObject(ctx); if (JS_IsException(obj)) @@ -15799,4315 +7577,13 @@ static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom return obj; } -static const uint16_t func_kind_to_class_id[] = { - [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION, - [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION, - [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION, - [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION, -}; - -static JSValue js_closure(JSContext *ctx, JSValue bfunc, - JSVarRef **cur_var_refs, - JSStackFrame *sf) -{ - JSFunctionBytecode *b; - JSValue func_obj; - JSAtom name_atom; - - b = JS_VALUE_GET_PTR(bfunc); - func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]); - if (JS_IsException(func_obj)) { - JS_FreeValue(ctx, bfunc); - return JS_EXCEPTION; - } - func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf); - if (JS_IsException(func_obj)) { - /* bfunc has been freed */ - goto fail; - } - name_atom = b->func_name; - if (name_atom == JS_ATOM_NULL) - name_atom = JS_ATOM_empty_string; - js_function_set_properties(ctx, func_obj, name_atom, - b->defined_arg_count); - - if (b->func_kind & JS_FUNC_GENERATOR) { - JSValue proto; - int proto_class_id; - /* generators have a prototype field which is used as - prototype for the generator object */ - if (b->func_kind == JS_FUNC_ASYNC_GENERATOR) - proto_class_id = JS_CLASS_ASYNC_GENERATOR; - else - proto_class_id = JS_CLASS_GENERATOR; - proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]); - if (JS_IsException(proto)) - goto fail; - JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto, - JS_PROP_WRITABLE); - } else if (b->has_prototype) { - /* add the 'prototype' property: delay instantiation to avoid - creating cycles for every javascript function. The prototype - object is created on the fly when first accessed */ - JS_SetConstructorBit(ctx, func_obj, TRUE); - JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype, - JS_AUTOINIT_ID_PROTOTYPE, NULL, - JS_PROP_WRITABLE); - } - return func_obj; - fail: - /* bfunc is freed when func_obj is freed */ - JS_FreeValue(ctx, func_obj); - return JS_EXCEPTION; -} - -#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0) - -static int js_op_define_class(JSContext *ctx, JSValue *sp, - JSAtom class_name, int class_flags, - JSVarRef **cur_var_refs, - JSStackFrame *sf, BOOL is_computed_name) -{ - JSValue bfunc, parent_class, proto = JS_UNDEFINED; - JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED; - JSFunctionBytecode *b; - - parent_class = sp[-2]; - bfunc = sp[-1]; - - if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) { - if (JS_IsNull(parent_class)) { - parent_proto = JS_NULL; - parent_class = JS_DupValue(ctx, ctx->function_proto); - } else { - if (!JS_IsConstructor(ctx, parent_class)) { - JS_ThrowTypeError(ctx, "parent class must be constructor"); - goto fail; - } - parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype); - if (JS_IsException(parent_proto)) - goto fail; - if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) { - JS_ThrowTypeError(ctx, "parent prototype must be an object or null"); - goto fail; - } - } - } else { - /* parent_class is JS_UNDEFINED in this case */ - parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]); - parent_class = JS_DupValue(ctx, ctx->function_proto); - } - proto = JS_NewObjectProto(ctx, parent_proto); - if (JS_IsException(proto)) - goto fail; - - b = JS_VALUE_GET_PTR(bfunc); - assert(b->func_kind == JS_FUNC_NORMAL); - ctor = JS_NewObjectProtoClass(ctx, parent_class, - JS_CLASS_BYTECODE_FUNCTION); - if (JS_IsException(ctor)) - goto fail; - ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf); - bfunc = JS_UNDEFINED; - if (JS_IsException(ctor)) - goto fail; - js_method_set_home_object(ctx, ctor, proto); - JS_SetConstructorBit(ctx, ctor, TRUE); - - JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length, - JS_NewInt32(ctx, b->defined_arg_count), - JS_PROP_CONFIGURABLE); - - if (is_computed_name) { - if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3], - JS_PROP_CONFIGURABLE) < 0) - goto fail; - } else { - if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0) - goto fail; - } - - /* the constructor property must be first. It can be overriden by - computed property names */ - if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, - JS_DupValue(ctx, ctor), - JS_PROP_CONFIGURABLE | - JS_PROP_WRITABLE | JS_PROP_THROW) < 0) - goto fail; - /* set the prototype property */ - if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype, - JS_DupValue(ctx, proto), JS_PROP_THROW) < 0) - goto fail; - set_cycle_flag(ctx, ctor); - set_cycle_flag(ctx, proto); - - JS_FreeValue(ctx, parent_proto); - JS_FreeValue(ctx, parent_class); - - sp[-2] = ctor; - sp[-1] = proto; - return 0; - fail: - JS_FreeValue(ctx, parent_class); - JS_FreeValue(ctx, parent_proto); - JS_FreeValue(ctx, bfunc); - JS_FreeValue(ctx, proto); - JS_FreeValue(ctx, ctor); - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) -{ - struct list_head *el, *el1; - JSVarRef *var_ref; - int var_idx; - - list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); - var_idx = var_ref->var_idx; - if (var_ref->is_arg) - var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]); - else - var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]); - var_ref->pvalue = &var_ref->value; - /* the reference is no longer to a local variable */ - var_ref->is_detached = TRUE; - add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); - } -} - -static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg) -{ - struct list_head *el, *el1; - JSVarRef *var_ref; - int var_idx = idx; - - list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); - if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) { - var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]); - var_ref->pvalue = &var_ref->value; - list_del(&var_ref->header.link); - /* the reference is no longer to a local variable */ - var_ref->is_detached = TRUE; - add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); - } - } -} - -#define JS_CALL_FLAG_COPY_ARGV (1 << 1) -#define JS_CALL_FLAG_GENERATOR (1 << 2) - -static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags) -{ - JSRuntime *rt = ctx->rt; - JSCFunctionType func; - JSObject *p; - JSStackFrame sf_s, *sf = &sf_s, *prev_sf; - JSValue ret_val; - JSValueConst *arg_buf; - int arg_count, i; - JSCFunctionEnum cproto; - - p = JS_VALUE_GET_OBJ(func_obj); - cproto = p->u.cfunc.cproto; - arg_count = p->u.cfunc.length; - - /* better to always check stack overflow */ - if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count)) - return JS_ThrowStackOverflow(ctx); - - prev_sf = rt->current_stack_frame; - sf->prev_frame = prev_sf; - rt->current_stack_frame = sf; - ctx = p->u.cfunc.realm; /* change the current realm */ - -#ifdef CONFIG_BIGNUM - /* we only propagate the bignum mode as some runtime functions - test it */ - if (prev_sf) - sf->js_mode = prev_sf->js_mode & JS_MODE_MATH; - else - sf->js_mode = 0; -#else - sf->js_mode = 0; -#endif - sf->cur_func = (JSValue)func_obj; - sf->arg_count = argc; - arg_buf = argv; - - if (unlikely(argc < arg_count)) { - /* ensure that at least argc_count arguments are readable */ - arg_buf = alloca(sizeof(arg_buf[0]) * arg_count); - for(i = 0; i < argc; i++) - arg_buf[i] = argv[i]; - for(i = argc; i < arg_count; i++) - arg_buf[i] = JS_UNDEFINED; - sf->arg_count = arg_count; - } - sf->arg_buf = (JSValue*)arg_buf; - - func = p->u.cfunc.c_function; - switch(cproto) { - case JS_CFUNC_constructor: - case JS_CFUNC_constructor_or_func: - if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { - if (cproto == JS_CFUNC_constructor) { - not_a_constructor: - ret_val = JS_ThrowTypeError(ctx, "must be called with new"); - break; - } else { - this_obj = JS_UNDEFINED; - } - } - /* here this_obj is new_target */ - /* fall thru */ - case JS_CFUNC_generic: - ret_val = func.generic(ctx, this_obj, argc, arg_buf); - break; - case JS_CFUNC_constructor_magic: - case JS_CFUNC_constructor_or_func_magic: - if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { - if (cproto == JS_CFUNC_constructor_magic) { - goto not_a_constructor; - } else { - this_obj = JS_UNDEFINED; - } - } - /* fall thru */ - case JS_CFUNC_generic_magic: - ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf, - p->u.cfunc.magic); - break; - case JS_CFUNC_getter: - ret_val = func.getter(ctx, this_obj); - break; - case JS_CFUNC_setter: - ret_val = func.setter(ctx, this_obj, arg_buf[0]); - break; - case JS_CFUNC_getter_magic: - ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic); - break; - case JS_CFUNC_setter_magic: - ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic); - break; - case JS_CFUNC_f_f: - { - double d1; - - if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { - ret_val = JS_EXCEPTION; - break; - } - ret_val = JS_NewFloat64(ctx, func.f_f(d1)); - } - break; - case JS_CFUNC_f_f_f: - { - double d1, d2; - - if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { - ret_val = JS_EXCEPTION; - break; - } - if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) { - ret_val = JS_EXCEPTION; - break; - } - ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2)); - } - break; - case JS_CFUNC_iterator_next: - { - int done; - ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf, - &done, p->u.cfunc.magic); - if (!JS_IsException(ret_val) && done != 2) { - ret_val = js_create_iterator_result(ctx, ret_val, done); - } - } - break; - default: - abort(); - } - - rt->current_stack_frame = sf->prev_frame; - return ret_val; -} - -static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags) -{ - JSObject *p; - JSBoundFunction *bf; - JSValueConst *arg_buf, new_target; - int arg_count, i; - - p = JS_VALUE_GET_OBJ(func_obj); - bf = p->u.bound_function; - arg_count = bf->argc + argc; - if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count)) - return JS_ThrowStackOverflow(ctx); - arg_buf = alloca(sizeof(JSValue) * arg_count); - for(i = 0; i < bf->argc; i++) { - arg_buf[i] = bf->argv[i]; - } - for(i = 0; i < argc; i++) { - arg_buf[bf->argc + i] = argv[i]; - } - if (flags & JS_CALL_FLAG_CONSTRUCTOR) { - new_target = this_obj; - if (js_same_value(ctx, func_obj, new_target)) - new_target = bf->func_obj; - return JS_CallConstructor2(ctx, bf->func_obj, new_target, - arg_count, arg_buf); - } else { - return JS_Call(ctx, bf->func_obj, bf->this_val, - arg_count, arg_buf); - } -} - -/* argument of OP_special_object */ -typedef enum { - OP_SPECIAL_OBJECT_ARGUMENTS, - OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS, - OP_SPECIAL_OBJECT_THIS_FUNC, - OP_SPECIAL_OBJECT_NEW_TARGET, - OP_SPECIAL_OBJECT_HOME_OBJECT, - OP_SPECIAL_OBJECT_VAR_OBJECT, - OP_SPECIAL_OBJECT_IMPORT_META, -} OPSpecialObjectEnum; - -#define FUNC_RET_AWAIT 0 -#define FUNC_RET_YIELD 1 -#define FUNC_RET_YIELD_STAR 2 - -/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ -static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - JSValueConst this_obj, JSValueConst new_target, - int argc, JSValue *argv, int flags) -{ - JSRuntime *rt = caller_ctx->rt; - JSContext *ctx; - JSObject *p; - JSFunctionBytecode *b; - JSStackFrame sf_s, *sf = &sf_s; - const uint8_t *pc; - int opcode, arg_allocated_size, i; - JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval; - JSVarRef **var_refs; - size_t alloca_size; - -#if !DIRECT_DISPATCH -#define SWITCH(pc) switch (opcode = *pc++) -#define CASE(op) case op -#define DEFAULT default -#define BREAK break -#else - static const void * const dispatch_table[256] = { -#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id, -#if SHORT_OPCODES -#define def(id, size, n_pop, n_push, f) -#else -#define def(id, size, n_pop, n_push, f) && case_default, -#endif -#include "third_party/quickjs/quickjs-opcode.inc" - [ OP_COUNT ... 255 ] = &&case_default - }; -#define SWITCH(pc) goto *dispatch_table[opcode = *pc++]; -#define CASE(op) case_ ## op -#define DEFAULT case_default -#define BREAK SWITCH(pc) -#endif - - if (js_poll_interrupts(caller_ctx)) - return JS_EXCEPTION; - if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { - if (flags & JS_CALL_FLAG_GENERATOR) { - JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj); - /* func_obj get contains a pointer to JSFuncAsyncState */ - /* the stack frame is already allocated */ - sf = &s->frame; - p = JS_VALUE_GET_OBJ(sf->cur_func); - b = p->u.func.function_bytecode; - ctx = b->realm; - var_refs = p->u.func.var_refs; - local_buf = arg_buf = sf->arg_buf; - var_buf = sf->var_buf; - stack_buf = sf->var_buf + b->var_count; - sp = sf->cur_sp; - sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */ - pc = sf->cur_pc; - sf->prev_frame = rt->current_stack_frame; - rt->current_stack_frame = sf; - if (s->throw_flag) - goto exception; - else - goto restart; - } else { - goto not_a_function; - } - } - p = JS_VALUE_GET_OBJ(func_obj); - if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { - JSClassCall *call_func; - call_func = rt->class_array[p->class_id].call; - if (!call_func) { - not_a_function: - return JS_ThrowTypeError(caller_ctx, "not a function"); - } - return call_func(caller_ctx, func_obj, this_obj, argc, - (JSValueConst *)argv, flags); - } - b = p->u.func.function_bytecode; - - if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) { - arg_allocated_size = b->arg_count; - } else { - arg_allocated_size = 0; - } - - alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count + - b->stack_size); - if (js_check_stack_overflow(rt, alloca_size)) - return JS_ThrowStackOverflow(caller_ctx); - - sf->js_mode = b->js_mode; - arg_buf = argv; - sf->arg_count = argc; - sf->cur_func = (JSValue)func_obj; - init_list_head(&sf->var_ref_list); - var_refs = p->u.func.var_refs; - - local_buf = alloca(alloca_size); - if (unlikely(arg_allocated_size)) { - int n = min_int(argc, b->arg_count); - arg_buf = local_buf; - for(i = 0; i < n; i++) - arg_buf[i] = JS_DupValue(caller_ctx, argv[i]); - for(; i < b->arg_count; i++) - arg_buf[i] = JS_UNDEFINED; - sf->arg_count = b->arg_count; - } - var_buf = local_buf + arg_allocated_size; - sf->var_buf = var_buf; - sf->arg_buf = arg_buf; - - for(i = 0; i < b->var_count; i++) - var_buf[i] = JS_UNDEFINED; - - stack_buf = var_buf + b->var_count; - sp = stack_buf; - pc = b->byte_code_buf; - sf->prev_frame = rt->current_stack_frame; - rt->current_stack_frame = sf; - ctx = b->realm; /* set the current realm */ - - restart: - for(;;) { - int call_argc; - JSValue *call_argv; - - SWITCH(pc) { - CASE(OP_push_i32): - *sp++ = JS_NewInt32(ctx, get_u32(pc)); - pc += 4; - BREAK; - CASE(OP_push_const): - *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]); - pc += 4; - BREAK; -#if SHORT_OPCODES - CASE(OP_push_minus1): - CASE(OP_push_0): - CASE(OP_push_1): - CASE(OP_push_2): - CASE(OP_push_3): - CASE(OP_push_4): - CASE(OP_push_5): - CASE(OP_push_6): - CASE(OP_push_7): - *sp++ = JS_NewInt32(ctx, opcode - OP_push_0); - BREAK; - CASE(OP_push_i8): - *sp++ = JS_NewInt32(ctx, get_i8(pc)); - pc += 1; - BREAK; - CASE(OP_push_i16): - *sp++ = JS_NewInt32(ctx, get_i16(pc)); - pc += 2; - BREAK; - CASE(OP_push_const8): - *sp++ = JS_DupValue(ctx, b->cpool[*pc++]); - BREAK; - CASE(OP_fclosure8): - *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - BREAK; - CASE(OP_push_empty_string): - *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string); - BREAK; - CASE(OP_get_length): - { - JSValue val; - - val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length); - if (unlikely(JS_IsException(val))) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = val; - } - BREAK; -#endif - CASE(OP_push_atom_value): - *sp++ = JS_AtomToValue(ctx, get_u32(pc)); - pc += 4; - BREAK; - CASE(OP_undefined): - *sp++ = JS_UNDEFINED; - BREAK; - CASE(OP_null): - *sp++ = JS_NULL; - BREAK; - CASE(OP_push_this): - /* OP_push_this is only called at the start of a function */ - { - JSValue val; - if (!(b->js_mode & JS_MODE_STRICT)) { - uint32_t tag = JS_VALUE_GET_TAG(this_obj); - if (likely(tag == JS_TAG_OBJECT)) - goto normal_this; - if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) { - val = JS_DupValue(ctx, ctx->global_obj); - } else { - val = JS_ToObject(ctx, this_obj); - if (JS_IsException(val)) - goto exception; - } - } else { - normal_this: - val = JS_DupValue(ctx, this_obj); - } - *sp++ = val; - } - BREAK; - CASE(OP_push_false): - *sp++ = JS_FALSE; - BREAK; - CASE(OP_push_true): - *sp++ = JS_TRUE; - BREAK; - CASE(OP_object): - *sp++ = JS_NewObject(ctx); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - BREAK; - CASE(OP_special_object): - { - int arg = *pc++; - switch(arg) { - case OP_SPECIAL_OBJECT_ARGUMENTS: - *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: - *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv, - sf, min_int(argc, b->arg_count)); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - case OP_SPECIAL_OBJECT_THIS_FUNC: - *sp++ = JS_DupValue(ctx, sf->cur_func); - break; - case OP_SPECIAL_OBJECT_NEW_TARGET: - *sp++ = JS_DupValue(ctx, new_target); - break; - case OP_SPECIAL_OBJECT_HOME_OBJECT: - { - JSObject *p1; - p1 = p->u.func.home_object; - if (unlikely(!p1)) - *sp++ = JS_UNDEFINED; - else - *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); - } - break; - case OP_SPECIAL_OBJECT_VAR_OBJECT: - *sp++ = JS_NewObjectProto(ctx, JS_NULL); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - case OP_SPECIAL_OBJECT_IMPORT_META: - *sp++ = js_import_meta(ctx); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - default: - abort(); - } - } - BREAK; - CASE(OP_rest): - { - int first = get_u16(pc); - pc += 2; - *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - } - BREAK; - - CASE(OP_drop): - JS_FreeValue(ctx, sp[-1]); - sp--; - BREAK; - CASE(OP_nip): - JS_FreeValue(ctx, sp[-2]); - sp[-2] = sp[-1]; - sp--; - BREAK; - CASE(OP_nip1): /* a b c -> b c */ - JS_FreeValue(ctx, sp[-3]); - sp[-3] = sp[-2]; - sp[-2] = sp[-1]; - sp--; - BREAK; - CASE(OP_dup): - sp[0] = JS_DupValue(ctx, sp[-1]); - sp++; - BREAK; - CASE(OP_dup2): /* a b -> a b a b */ - sp[0] = JS_DupValue(ctx, sp[-2]); - sp[1] = JS_DupValue(ctx, sp[-1]); - sp += 2; - BREAK; - CASE(OP_dup3): /* a b c -> a b c a b c */ - sp[0] = JS_DupValue(ctx, sp[-3]); - sp[1] = JS_DupValue(ctx, sp[-2]); - sp[2] = JS_DupValue(ctx, sp[-1]); - sp += 3; - BREAK; - CASE(OP_dup1): /* a b -> a a b */ - sp[0] = sp[-1]; - sp[-1] = JS_DupValue(ctx, sp[-2]); - sp++; - BREAK; - CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */ - sp[0] = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = JS_DupValue(ctx, sp[0]); - sp++; - BREAK; - CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */ - sp[0] = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = JS_DupValue(ctx, sp[0]); - sp++; - BREAK; - CASE(OP_insert4): /* this obj prop a -> a this obj prop a */ - sp[0] = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = sp[-4]; - sp[-4] = JS_DupValue(ctx, sp[0]); - sp++; - BREAK; - CASE(OP_perm3): /* obj a b -> a obj b (213) */ - { - JSValue tmp; - tmp = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = tmp; - } - BREAK; - CASE(OP_rot3l): /* x a b -> a b x (231) */ - { - JSValue tmp; - tmp = sp[-3]; - sp[-3] = sp[-2]; - sp[-2] = sp[-1]; - sp[-1] = tmp; - } - BREAK; - CASE(OP_rot4l): /* x a b c -> a b c x */ - { - JSValue tmp; - tmp = sp[-4]; - sp[-4] = sp[-3]; - sp[-3] = sp[-2]; - sp[-2] = sp[-1]; - sp[-1] = tmp; - } - BREAK; - CASE(OP_rot5l): /* x a b c d -> a b c d x */ - { - JSValue tmp; - tmp = sp[-5]; - sp[-5] = sp[-4]; - sp[-4] = sp[-3]; - sp[-3] = sp[-2]; - sp[-2] = sp[-1]; - sp[-1] = tmp; - } - BREAK; - CASE(OP_rot3r): /* a b x -> x a b (312) */ - { - JSValue tmp; - tmp = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = tmp; - } - BREAK; - CASE(OP_perm4): /* obj prop a b -> a obj prop b */ - { - JSValue tmp; - tmp = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = sp[-4]; - sp[-4] = tmp; - } - BREAK; - CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */ - { - JSValue tmp; - tmp = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = sp[-4]; - sp[-4] = sp[-5]; - sp[-5] = tmp; - } - BREAK; - CASE(OP_swap): /* a b -> b a */ - { - JSValue tmp; - tmp = sp[-2]; - sp[-2] = sp[-1]; - sp[-1] = tmp; - } - BREAK; - CASE(OP_swap2): /* a b c d -> c d a b */ - { - JSValue tmp1, tmp2; - tmp1 = sp[-4]; - tmp2 = sp[-3]; - sp[-4] = sp[-2]; - sp[-3] = sp[-1]; - sp[-2] = tmp1; - sp[-1] = tmp2; - } - BREAK; - - CASE(OP_fclosure): - { - JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]); - pc += 4; - *sp++ = js_closure(ctx, bfunc, var_refs, sf); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - } - BREAK; -#if SHORT_OPCODES - CASE(OP_call0): - CASE(OP_call1): - CASE(OP_call2): - CASE(OP_call3): - call_argc = opcode - OP_call0; - goto has_call_argc; -#endif - CASE(OP_call): - CASE(OP_tail_call): - { - call_argc = get_u16(pc); - pc += 2; - goto has_call_argc; - has_call_argc: - call_argv = sp - call_argc; - sf->cur_pc = pc; - ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, - JS_UNDEFINED, call_argc, call_argv, 0); - if (unlikely(JS_IsException(ret_val))) - goto exception; - if (opcode == OP_tail_call) - goto done; - for(i = -1; i < call_argc; i++) - JS_FreeValue(ctx, call_argv[i]); - sp -= call_argc + 1; - *sp++ = ret_val; - } - BREAK; - CASE(OP_call_constructor): - { - call_argc = get_u16(pc); - pc += 2; - call_argv = sp - call_argc; - sf->cur_pc = pc; - ret_val = JS_CallConstructorInternal(ctx, call_argv[-2], - call_argv[-1], - call_argc, call_argv, 0); - if (unlikely(JS_IsException(ret_val))) - goto exception; - for(i = -2; i < call_argc; i++) - JS_FreeValue(ctx, call_argv[i]); - sp -= call_argc + 2; - *sp++ = ret_val; - } - BREAK; - CASE(OP_call_method): - CASE(OP_tail_call_method): - { - call_argc = get_u16(pc); - pc += 2; - call_argv = sp - call_argc; - sf->cur_pc = pc; - ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2], - JS_UNDEFINED, call_argc, call_argv, 0); - if (unlikely(JS_IsException(ret_val))) - goto exception; - if (opcode == OP_tail_call_method) - goto done; - for(i = -2; i < call_argc; i++) - JS_FreeValue(ctx, call_argv[i]); - sp -= call_argc + 2; - *sp++ = ret_val; - } - BREAK; - CASE(OP_array_from): - { - int i, ret; - - call_argc = get_u16(pc); - pc += 2; - ret_val = JS_NewArray(ctx); - if (unlikely(JS_IsException(ret_val))) - goto exception; - call_argv = sp - call_argc; - for(i = 0; i < call_argc; i++) { - ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i], - JS_PROP_C_W_E | JS_PROP_THROW); - call_argv[i] = JS_UNDEFINED; - if (ret < 0) { - JS_FreeValue(ctx, ret_val); - goto exception; - } - } - sp -= call_argc; - *sp++ = ret_val; - } - BREAK; - - CASE(OP_apply): - { - int magic; - magic = get_u16(pc); - pc += 2; - - ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic); - if (unlikely(JS_IsException(ret_val))) - goto exception; - JS_FreeValue(ctx, sp[-3]); - JS_FreeValue(ctx, sp[-2]); - JS_FreeValue(ctx, sp[-1]); - sp -= 3; - *sp++ = ret_val; - } - BREAK; - CASE(OP_return): - ret_val = *--sp; - goto done; - CASE(OP_return_undef): - ret_val = JS_UNDEFINED; - goto done; - - CASE(OP_check_ctor_return): - /* return TRUE if 'this' should be returned */ - if (!JS_IsObject(sp[-1])) { - if (!JS_IsUndefined(sp[-1])) { - JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined"); - goto exception; - } - sp[0] = JS_TRUE; - } else { - sp[0] = JS_FALSE; - } - sp++; - BREAK; - CASE(OP_check_ctor): - if (JS_IsUndefined(new_target)) { - JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); - goto exception; - } - BREAK; - CASE(OP_check_brand): - if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0) - goto exception; - BREAK; - CASE(OP_add_brand): - if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0) - goto exception; - JS_FreeValue(ctx, sp[-2]); - JS_FreeValue(ctx, sp[-1]); - sp -= 2; - BREAK; - - CASE(OP_throw): - JS_Throw(ctx, *--sp); - goto exception; - - CASE(OP_throw_error): -#define JS_THROW_VAR_RO 0 -#define JS_THROW_VAR_REDECL 1 -#define JS_THROW_VAR_UNINITIALIZED 2 -#define JS_THROW_ERROR_DELETE_SUPER 3 -#define JS_THROW_ERROR_ITERATOR_THROW 4 - { - JSAtom atom; - int type; - atom = get_u32(pc); - type = pc[4]; - pc += 5; - if (type == JS_THROW_VAR_RO) - JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom); - else - if (type == JS_THROW_VAR_REDECL) - JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom); - else - if (type == JS_THROW_VAR_UNINITIALIZED) - JS_ThrowReferenceErrorUninitialized(ctx, atom); - else - if (type == JS_THROW_ERROR_DELETE_SUPER) - JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); - else - if (type == JS_THROW_ERROR_ITERATOR_THROW) - JS_ThrowTypeError(ctx, "iterator does not have a throw method"); - else - JS_ThrowInternalError(ctx, "invalid throw var type %d", type); - } - goto exception; - - CASE(OP_eval): - { - JSValueConst obj; - int scope_idx; - call_argc = get_u16(pc); - scope_idx = get_u16(pc + 2) - 1; - pc += 4; - call_argv = sp - call_argc; - sf->cur_pc = pc; - if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) { - if (call_argc >= 1) - obj = call_argv[0]; - else - obj = JS_UNDEFINED; - ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, - JS_EVAL_TYPE_DIRECT, scope_idx); - } else { - ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, - JS_UNDEFINED, call_argc, call_argv, 0); - } - if (unlikely(JS_IsException(ret_val))) - goto exception; - for(i = -1; i < call_argc; i++) - JS_FreeValue(ctx, call_argv[i]); - sp -= call_argc + 1; - *sp++ = ret_val; - } - BREAK; - /* could merge with OP_apply */ - CASE(OP_apply_eval): - { - int scope_idx; - uint32_t len; - JSValue *tab; - JSValueConst obj; - - scope_idx = get_u16(pc) - 1; - pc += 2; - tab = build_arg_list(ctx, &len, sp[-1]); - if (!tab) - goto exception; - if (js_same_value(ctx, sp[-2], ctx->eval_obj)) { - if (len >= 1) - obj = tab[0]; - else - obj = JS_UNDEFINED; - ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, - JS_EVAL_TYPE_DIRECT, scope_idx); - } else { - ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, - (JSValueConst *)tab); - } - free_arg_list(ctx, tab, len); - if (unlikely(JS_IsException(ret_val))) - goto exception; - JS_FreeValue(ctx, sp[-2]); - JS_FreeValue(ctx, sp[-1]); - sp -= 2; - *sp++ = ret_val; - } - BREAK; - - CASE(OP_regexp): - { - sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED, - sp[-2], sp[-1]); - sp--; - } - BREAK; - - CASE(OP_get_super): - { - JSValue proto; - proto = JS_GetPrototype(ctx, sp[-1]); - if (JS_IsException(proto)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = proto; - } - BREAK; - - CASE(OP_import): - { - JSValue val; - val = js_dynamic_import(ctx, sp[-1]); - if (JS_IsException(val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = val; - } - BREAK; - - CASE(OP_check_var): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_CheckGlobalVar(ctx, atom); - if (ret < 0) - goto exception; - *sp++ = JS_NewBool(ctx, ret); - } - BREAK; - - CASE(OP_get_var_undef): - CASE(OP_get_var): - { - JSValue val; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef); - if (unlikely(JS_IsException(val))) - goto exception; - *sp++ = val; - } - BREAK; - - CASE(OP_put_var): - CASE(OP_put_var_init): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var); - sp--; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_put_var_strict): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - /* sp[-2] is JS_TRUE or JS_FALSE */ - if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - goto exception; - } - ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2); - sp -= 2; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_check_define_var): - { - JSAtom atom; - int flags; - atom = get_u32(pc); - flags = pc[4]; - pc += 5; - if (JS_CheckDefineGlobalVar(ctx, atom, flags)) - goto exception; - } - BREAK; - CASE(OP_define_var): - { - JSAtom atom; - int flags; - atom = get_u32(pc); - flags = pc[4]; - pc += 5; - if (JS_DefineGlobalVar(ctx, atom, flags)) - goto exception; - } - BREAK; - CASE(OP_define_func): - { - JSAtom atom; - int flags; - atom = get_u32(pc); - flags = pc[4]; - pc += 5; - if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp--; - } - BREAK; - - CASE(OP_get_loc): - { - int idx; - idx = get_u16(pc); - pc += 2; - sp[0] = JS_DupValue(ctx, var_buf[idx]); - sp++; - } - BREAK; - CASE(OP_put_loc): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &var_buf[idx], sp[-1]); - sp--; - } - BREAK; - CASE(OP_set_loc): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1])); - } - BREAK; - CASE(OP_get_arg): - { - int idx; - idx = get_u16(pc); - pc += 2; - sp[0] = JS_DupValue(ctx, arg_buf[idx]); - sp++; - } - BREAK; - CASE(OP_put_arg): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &arg_buf[idx], sp[-1]); - sp--; - } - BREAK; - CASE(OP_set_arg): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1])); - } - BREAK; - -#if SHORT_OPCODES - CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK; - CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK; - CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK; - - CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK; - CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK; - CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK; - CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK; - CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK; - CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK; - CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK; - CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK; - CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK; - CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK; - CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK; - CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK; - CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK; - CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK; - CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK; - CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK; - CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK; - CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK; - CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK; - CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK; - CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK; - CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK; - CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK; - CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK; - CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; -#endif - - CASE(OP_get_var_ref): - { - int idx; - JSValue val; - idx = get_u16(pc); - pc += 2; - val = *var_refs[idx]->pvalue; - sp[0] = JS_DupValue(ctx, val); - sp++; - } - BREAK; - CASE(OP_put_var_ref): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, var_refs[idx]->pvalue, sp[-1]); - sp--; - } - BREAK; - CASE(OP_set_var_ref): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1])); - } - BREAK; - CASE(OP_get_var_ref_check): - { - int idx; - JSValue val; - idx = get_u16(pc); - pc += 2; - val = *var_refs[idx]->pvalue; - if (unlikely(JS_IsUninitialized(val))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); - goto exception; - } - sp[0] = JS_DupValue(ctx, val); - sp++; - } - BREAK; - CASE(OP_put_var_ref_check): - { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); - goto exception; - } - set_value(ctx, var_refs[idx]->pvalue, sp[-1]); - sp--; - } - BREAK; - CASE(OP_put_var_ref_check_init): - { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); - goto exception; - } - set_value(ctx, var_refs[idx]->pvalue, sp[-1]); - sp--; - } - BREAK; - CASE(OP_set_loc_uninitialized): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &var_buf[idx], JS_UNINITIALIZED); - } - BREAK; - CASE(OP_get_loc_check): - { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); - goto exception; - } - sp[0] = JS_DupValue(ctx, var_buf[idx]); - sp++; - } - BREAK; - CASE(OP_put_loc_check): - { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); - goto exception; - } - set_value(ctx, &var_buf[idx], sp[-1]); - sp--; - } - BREAK; - CASE(OP_put_loc_check_init): - { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(!JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceError(ctx, "'this' can be initialized only once"); - goto exception; - } - set_value(ctx, &var_buf[idx], sp[-1]); - sp--; - } - BREAK; - CASE(OP_close_loc): - { - int idx; - idx = get_u16(pc); - pc += 2; - close_lexical_var(ctx, sf, idx, FALSE); - } - BREAK; - - CASE(OP_make_loc_ref): - CASE(OP_make_arg_ref): - CASE(OP_make_var_ref_ref): - { - JSVarRef *var_ref; - JSProperty *pr; - JSAtom atom; - int idx; - atom = get_u32(pc); - idx = get_u16(pc + 4); - pc += 6; - *sp++ = JS_NewObjectProto(ctx, JS_NULL); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - if (opcode == OP_make_var_ref_ref) { - var_ref = var_refs[idx]; - var_ref->header.ref_count++; - } else { - var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref); - if (!var_ref) - goto exception; - } - pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom, - JS_PROP_WRITABLE | JS_PROP_VARREF); - if (!pr) { - free_var_ref(rt, var_ref); - goto exception; - } - pr->u.var_ref = var_ref; - *sp++ = JS_AtomToValue(ctx, atom); - } - BREAK; - CASE(OP_make_var_ref): - { - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - if (JS_GetGlobalVarRef(ctx, atom, sp)) - goto exception; - sp += 2; - } - BREAK; - - CASE(OP_goto): - pc += (int32_t)get_u32(pc); - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - BREAK; -#if SHORT_OPCODES - CASE(OP_goto16): - pc += (int16_t)get_u16(pc); - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - BREAK; - CASE(OP_goto8): - pc += (int8_t)pc[0]; - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - BREAK; -#endif - CASE(OP_if_true): - { - int res; - JSValue op1; - - op1 = sp[-1]; - pc += 4; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1); - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp--; - if (res) { - pc += (int32_t)get_u32(pc - 4) - 4; - } - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - } - BREAK; - CASE(OP_if_false): - { - int res; - JSValue op1; - - op1 = sp[-1]; - pc += 4; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1); - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp--; - if (!res) { - pc += (int32_t)get_u32(pc - 4) - 4; - } - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - } - BREAK; -#if SHORT_OPCODES - CASE(OP_if_true8): - { - int res; - JSValue op1; - - op1 = sp[-1]; - pc += 1; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1); - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp--; - if (res) { - pc += (int8_t)pc[-1] - 1; - } - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - } - BREAK; - CASE(OP_if_false8): - { - int res; - JSValue op1; - - op1 = sp[-1]; - pc += 1; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1); - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp--; - if (!res) { - pc += (int8_t)pc[-1] - 1; - } - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - } - BREAK; -#endif - CASE(OP_catch): - { - int32_t diff; - diff = get_u32(pc); - sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf); - sp++; - pc += 4; - } - BREAK; - CASE(OP_gosub): - { - int32_t diff; - diff = get_u32(pc); - /* XXX: should have a different tag to avoid security flaw */ - sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf); - sp++; - pc += diff; - } - BREAK; - CASE(OP_ret): - { - JSValue op1; - uint32_t pos; - op1 = sp[-1]; - if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT)) - goto ret_fail; - pos = JS_VALUE_GET_INT(op1); - if (unlikely(pos >= b->byte_code_len)) { - ret_fail: - JS_ThrowInternalError(ctx, "invalid ret value"); - goto exception; - } - sp--; - pc = b->byte_code_buf + pos; - } - BREAK; - - CASE(OP_for_in_start): - if (js_for_in_start(ctx, sp)) - goto exception; - BREAK; - CASE(OP_for_in_next): - if (js_for_in_next(ctx, sp)) - goto exception; - sp += 2; - BREAK; - CASE(OP_for_of_start): - if (js_for_of_start(ctx, sp, FALSE)) - goto exception; - sp += 1; - *sp++ = JS_NewCatchOffset(ctx, 0); - BREAK; - CASE(OP_for_of_next): - { - int offset = -3 - pc[0]; - pc += 1; - if (js_for_of_next(ctx, sp, offset)) - goto exception; - sp += 2; - } - BREAK; - CASE(OP_for_await_of_start): - if (js_for_of_start(ctx, sp, TRUE)) - goto exception; - sp += 1; - *sp++ = JS_NewCatchOffset(ctx, 0); - BREAK; - CASE(OP_iterator_get_value_done): - if (js_iterator_get_value_done(ctx, sp)) - goto exception; - sp += 1; - BREAK; - CASE(OP_iterator_check_object): - if (unlikely(!JS_IsObject(sp[-1]))) { - JS_ThrowTypeError(ctx, "iterator must return an object"); - goto exception; - } - BREAK; - - CASE(OP_iterator_close): - /* iter_obj next catch_offset -> */ - sp--; /* drop the catch offset to avoid getting caught by exception */ - JS_FreeValue(ctx, sp[-1]); /* drop the next method */ - sp--; - if (!JS_IsUndefined(sp[-1])) { - if (JS_IteratorClose(ctx, sp[-1], FALSE)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - } - sp--; - BREAK; - CASE(OP_iterator_close_return): - { - JSValue ret_val; - /* iter_obj next catch_offset ... ret_val -> - ret_eval iter_obj next catch_offset */ - ret_val = *--sp; - while (sp > stack_buf && - JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) { - JS_FreeValue(ctx, *--sp); - } - if (unlikely(sp < stack_buf + 3)) { - JS_ThrowInternalError(ctx, "iterator_close_return"); - JS_FreeValue(ctx, ret_val); - goto exception; - } - sp[0] = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = ret_val; - sp++; - } - BREAK; - - CASE(OP_iterator_next): - /* stack: iter_obj next catch_offset val */ - { - JSValue ret; - ret = JS_Call(ctx, sp[-3], sp[-4], - 1, (JSValueConst *)(sp - 1)); - if (JS_IsException(ret)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret; - } - BREAK; - - CASE(OP_iterator_call): - /* stack: iter_obj next catch_offset val */ - { - JSValue method, ret; - BOOL ret_flag; - int flags; - flags = *pc++; - method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? - JS_ATOM_throw : JS_ATOM_return); - if (JS_IsException(method)) - goto exception; - if (JS_IsUndefined(method) || JS_IsNull(method)) { - ret_flag = TRUE; - } else { - if (flags & 2) { - /* no argument */ - ret = JS_CallFree(ctx, method, sp[-4], - 0, NULL); - } else { - ret = JS_CallFree(ctx, method, sp[-4], - 1, (JSValueConst *)(sp - 1)); - } - if (JS_IsException(ret)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret; - ret_flag = FALSE; - } - sp[0] = JS_NewBool(ctx, ret_flag); - sp += 1; - } - BREAK; - - CASE(OP_lnot): - { - int res; - JSValue op1; - - op1 = sp[-1]; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1) != 0; - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp[-1] = JS_NewBool(ctx, !res); - } - BREAK; - - CASE(OP_get_field): - { - JSValue val; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - val = JS_GetProperty(ctx, sp[-1], atom); - if (unlikely(JS_IsException(val))) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = val; - } - BREAK; - - CASE(OP_get_field2): - { - JSValue val; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - val = JS_GetProperty(ctx, sp[-1], atom); - if (unlikely(JS_IsException(val))) - goto exception; - *sp++ = val; - } - BREAK; - - CASE(OP_put_field): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], - JS_PROP_THROW_STRICT); - JS_FreeValue(ctx, sp[-2]); - sp -= 2; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_private_symbol): - { - JSAtom atom; - JSValue val; - - atom = get_u32(pc); - pc += 4; - val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE); - if (JS_IsException(val)) - goto exception; - *sp++ = val; - } - BREAK; - - CASE(OP_get_private_field): - { - JSValue val; - - val = JS_GetPrivateField(ctx, sp[-2], sp[-1]); - JS_FreeValue(ctx, sp[-1]); - JS_FreeValue(ctx, sp[-2]); - sp[-2] = val; - sp--; - if (unlikely(JS_IsException(val))) - goto exception; - } - BREAK; - - CASE(OP_put_private_field): - { - int ret; - ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]); - JS_FreeValue(ctx, sp[-3]); - JS_FreeValue(ctx, sp[-1]); - sp -= 3; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_define_private_field): - { - int ret; - ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]); - JS_FreeValue(ctx, sp[-2]); - sp -= 2; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_define_field): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1], - JS_PROP_C_W_E | JS_PROP_THROW); - sp--; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_set_name): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE); - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - CASE(OP_set_name_computed): - { - int ret; - ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE); - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - CASE(OP_set_proto): - { - JSValue proto; - proto = sp[-1]; - if (JS_IsObject(proto) || JS_IsNull(proto)) { - if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0) - goto exception; - } - JS_FreeValue(ctx, proto); - sp--; - } - BREAK; - CASE(OP_set_home_object): - js_method_set_home_object(ctx, sp[-1], sp[-2]); - BREAK; - CASE(OP_define_method): - CASE(OP_define_method_computed): - { - JSValue getter, setter, value; - JSValueConst obj; - JSAtom atom; - int flags, ret, op_flags; - BOOL is_computed; -#define OP_DEFINE_METHOD_METHOD 0 -#define OP_DEFINE_METHOD_GETTER 1 -#define OP_DEFINE_METHOD_SETTER 2 -#define OP_DEFINE_METHOD_ENUMERABLE 4 - - is_computed = (opcode == OP_define_method_computed); - if (is_computed) { - atom = JS_ValueToAtom(ctx, sp[-2]); - if (unlikely(atom == JS_ATOM_NULL)) - goto exception; - opcode += OP_define_method - OP_define_method_computed; - } else { - atom = get_u32(pc); - pc += 4; - } - op_flags = *pc++; - - obj = sp[-2 - is_computed]; - flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE | - JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW; - if (op_flags & OP_DEFINE_METHOD_ENUMERABLE) - flags |= JS_PROP_ENUMERABLE; - op_flags &= 3; - value = JS_UNDEFINED; - getter = JS_UNDEFINED; - setter = JS_UNDEFINED; - if (op_flags == OP_DEFINE_METHOD_METHOD) { - value = sp[-1]; - flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE; - } else if (op_flags == OP_DEFINE_METHOD_GETTER) { - getter = sp[-1]; - flags |= JS_PROP_HAS_GET; - } else { - setter = sp[-1]; - flags |= JS_PROP_HAS_SET; - } - ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj); - if (ret >= 0) { - ret = JS_DefineProperty(ctx, obj, atom, value, - getter, setter, flags); - } - JS_FreeValue(ctx, sp[-1]); - if (is_computed) { - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, sp[-2]); - } - sp -= 1 + is_computed; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_define_class): - CASE(OP_define_class_computed): - { - int class_flags; - JSAtom atom; - - atom = get_u32(pc); - class_flags = pc[4]; - pc += 5; - if (js_op_define_class(ctx, sp, atom, class_flags, - var_refs, sf, - (opcode == OP_define_class_computed)) < 0) - goto exception; - } - BREAK; - - CASE(OP_get_array_el): - { - JSValue val; - - val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); - JS_FreeValue(ctx, sp[-2]); - sp[-2] = val; - sp--; - if (unlikely(JS_IsException(val))) - goto exception; - } - BREAK; - - CASE(OP_get_array_el2): - { - JSValue val; - - val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); - sp[-1] = val; - if (unlikely(JS_IsException(val))) - goto exception; - } - BREAK; - - CASE(OP_get_ref_value): - { - JSValue val; - if (unlikely(JS_IsUndefined(sp[-2]))) { - JSAtom atom = JS_ValueToAtom(ctx, sp[-1]); - if (atom != JS_ATOM_NULL) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - JS_FreeAtom(ctx, atom); - } - goto exception; - } - val = JS_GetPropertyValue(ctx, sp[-2], - JS_DupValue(ctx, sp[-1])); - if (unlikely(JS_IsException(val))) - goto exception; - sp[0] = val; - sp++; - } - BREAK; - - CASE(OP_get_super_value): - { - JSValue val; - JSAtom atom; - atom = JS_ValueToAtom(ctx, sp[-1]); - if (unlikely(atom == JS_ATOM_NULL)) - goto exception; - val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE); - JS_FreeAtom(ctx, atom); - if (unlikely(JS_IsException(val))) - goto exception; - JS_FreeValue(ctx, sp[-1]); - JS_FreeValue(ctx, sp[-2]); - JS_FreeValue(ctx, sp[-3]); - sp[-3] = val; - sp -= 2; - } - BREAK; - - CASE(OP_put_array_el): - { - int ret; - - ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); - JS_FreeValue(ctx, sp[-3]); - sp -= 3; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_put_ref_value): - { - int ret, flags; - flags = JS_PROP_THROW_STRICT; - if (unlikely(JS_IsUndefined(sp[-3]))) { - if (is_strict_mode(ctx)) { - JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); - if (atom != JS_ATOM_NULL) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - JS_FreeAtom(ctx, atom); - } - goto exception; - } else { - sp[-3] = JS_DupValue(ctx, ctx->global_obj); - } - } else { - if (is_strict_mode(ctx)) - flags |= JS_PROP_NO_ADD; - } - ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); - JS_FreeValue(ctx, sp[-3]); - sp -= 3; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_put_super_value): - { - int ret; - JSAtom atom; - if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) { - JS_ThrowTypeErrorNotAnObject(ctx); - goto exception; - } - atom = JS_ValueToAtom(ctx, sp[-2]); - if (unlikely(atom == JS_ATOM_NULL)) - goto exception; - ret = JS_SetPropertyGeneric(ctx, sp[-3], atom, sp[-1], sp[-4], - JS_PROP_THROW_STRICT); - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, sp[-4]); - JS_FreeValue(ctx, sp[-3]); - JS_FreeValue(ctx, sp[-2]); - sp -= 4; - if (ret < 0) - goto exception; - } - BREAK; - - CASE(OP_define_array_el): - { - int ret; - ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1], - JS_PROP_C_W_E | JS_PROP_THROW); - sp -= 1; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_append): /* array pos enumobj -- array pos */ - { - if (js_append_enumerate(ctx, sp)) - goto exception; - JS_FreeValue(ctx, *--sp); - } - BREAK; - - CASE(OP_copy_data_properties): /* target source excludeList */ - { - /* stack offsets (-1 based): - 2 bits for target, - 3 bits for source, - 2 bits for exclusionList */ - int mask; - - mask = *pc++; - if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)], - sp[-1 - ((mask >> 2) & 7)], - sp[-1 - ((mask >> 5) & 7)], 0)) - goto exception; - } - BREAK; - - CASE(OP_add): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int64_t r; - r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2); - if (unlikely((int)r != r)) - goto add_slow; - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { - sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + - JS_VALUE_GET_FLOAT64(op2)); - sp--; - } else { - add_slow: - if (js_add_slow(ctx, sp)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_add_loc): - { - JSValue *pv; - int idx; - idx = *pc; - pc += 1; - - pv = &var_buf[idx]; - if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) { - int64_t r; - r = (int64_t)JS_VALUE_GET_INT(*pv) + - JS_VALUE_GET_INT(sp[-1]); - if (unlikely((int)r != r)) - goto add_loc_slow; - *pv = JS_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { - JSValue op1; - op1 = sp[-1]; - sp--; - op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); - if (JS_IsException(op1)) - goto exception; - op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1); - if (JS_IsException(op1)) - goto exception; - set_value(ctx, pv, op1); - } else { - JSValue ops[2]; - add_loc_slow: - /* In case of exception, js_add_slow frees ops[0] - and ops[1], so we must duplicate *pv */ - ops[0] = JS_DupValue(ctx, *pv); - ops[1] = sp[-1]; - sp--; - if (js_add_slow(ctx, ops + 2)) - goto exception; - set_value(ctx, pv, ops[0]); - } - } - BREAK; - CASE(OP_sub): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int64_t r; - r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2); - if (unlikely((int)r != r)) - goto binary_arith_slow; - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { - sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) - - JS_VALUE_GET_FLOAT64(op2)); - sp--; - } else { - goto binary_arith_slow; - } - } - BREAK; - CASE(OP_mul): - { - JSValue op1, op2; - double d; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int32_t v1, v2; - int64_t r; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); - r = (int64_t)v1 * v2; - if (unlikely((int)r != r)) { -#ifdef CONFIG_BIGNUM - if (unlikely(sf->js_mode & JS_MODE_MATH) && - (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER)) - goto binary_arith_slow; -#endif - d = (double)r; - goto mul_fp_res; - } - /* need to test zero case for -0 result */ - if (unlikely(r == 0 && (v1 | v2) < 0)) { - d = -0.0; - goto mul_fp_res; - } - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { -#ifdef CONFIG_BIGNUM - if (unlikely(sf->js_mode & JS_MODE_MATH)) - goto binary_arith_slow; -#endif - d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); - mul_fp_res: - sp[-2] = __JS_NewFloat64(ctx, d); - sp--; - } else { - goto binary_arith_slow; - } - } - BREAK; - CASE(OP_div): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int v1, v2; - if (unlikely(sf->js_mode & JS_MODE_MATH)) - goto binary_arith_slow; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); - sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); - sp--; - } else { - goto binary_arith_slow; - } - } - BREAK; - CASE(OP_mod): -#ifdef CONFIG_BIGNUM - CASE(OP_math_mod): -#endif - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int v1, v2, r; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); - /* We must avoid v2 = 0, v1 = INT32_MIN and v2 = - -1 and the cases where the result is -0. */ - if (unlikely(v1 < 0 || v2 <= 0)) - goto binary_arith_slow; - r = v1 % v2; - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else { - goto binary_arith_slow; - } - } - BREAK; - CASE(OP_pow): - binary_arith_slow: - if (js_binary_arith_slow(ctx, sp, opcode)) - goto exception; - sp--; - BREAK; - - CASE(OP_plus): - { - JSValue op1; - uint32_t tag; - op1 = sp[-1]; - tag = JS_VALUE_GET_TAG(op1); - if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) { - } else { - if (js_unary_arith_slow(ctx, sp, opcode)) - goto exception; - } - } - BREAK; - CASE(OP_neg): - { - JSValue op1; - uint32_t tag; - int val; - double d; - op1 = sp[-1]; - tag = JS_VALUE_GET_TAG(op1); - if (tag == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - /* Note: -0 cannot be expressed as integer */ - if (unlikely(val == 0)) { - d = -0.0; - goto neg_fp_res; - } - if (unlikely(val == INT32_MIN)) { - d = -(double)val; - goto neg_fp_res; - } - sp[-1] = JS_NewInt32(ctx, -val); - } else if (JS_TAG_IS_FLOAT64(tag)) { - d = -JS_VALUE_GET_FLOAT64(op1); - neg_fp_res: - sp[-1] = __JS_NewFloat64(ctx, d); - } else { - if (js_unary_arith_slow(ctx, sp, opcode)) - goto exception; - } - } - BREAK; - CASE(OP_inc): - { - JSValue op1; - int val; - op1 = sp[-1]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - if (unlikely(val == INT32_MAX)) - goto inc_slow; - sp[-1] = JS_NewInt32(ctx, val + 1); - } else { - inc_slow: - if (js_unary_arith_slow(ctx, sp, opcode)) - goto exception; - } - } - BREAK; - CASE(OP_dec): - { - JSValue op1; - int val; - op1 = sp[-1]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - if (unlikely(val == INT32_MIN)) - goto dec_slow; - sp[-1] = JS_NewInt32(ctx, val - 1); - } else { - dec_slow: - if (js_unary_arith_slow(ctx, sp, opcode)) - goto exception; - } - } - BREAK; - CASE(OP_post_inc): - CASE(OP_post_dec): - if (js_post_inc_slow(ctx, sp, opcode)) - goto exception; - sp++; - BREAK; - CASE(OP_inc_loc): - { - JSValue op1; - int val; - int idx; - idx = *pc; - pc += 1; - - op1 = var_buf[idx]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - if (unlikely(val == INT32_MAX)) - goto inc_loc_slow; - var_buf[idx] = JS_NewInt32(ctx, val + 1); - } else { - inc_loc_slow: - /* must duplicate otherwise the variable value may - be destroyed before JS code accesses it */ - op1 = JS_DupValue(ctx, op1); - if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc)) - goto exception; - set_value(ctx, &var_buf[idx], op1); - } - } - BREAK; - CASE(OP_dec_loc): - { - JSValue op1; - int val; - int idx; - idx = *pc; - pc += 1; - - op1 = var_buf[idx]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - if (unlikely(val == INT32_MIN)) - goto dec_loc_slow; - var_buf[idx] = JS_NewInt32(ctx, val - 1); - } else { - dec_loc_slow: - /* must duplicate otherwise the variable value may - be destroyed before JS code accesses it */ - op1 = JS_DupValue(ctx, op1); - if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec)) - goto exception; - set_value(ctx, &var_buf[idx], op1); - } - } - BREAK; - CASE(OP_not): - { - JSValue op1; - op1 = sp[-1]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1)); - } else { - if (js_not_slow(ctx, sp)) - goto exception; - } - } - BREAK; - - CASE(OP_shl): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - uint32_t v1, v2; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); -#ifdef CONFIG_BIGNUM - { - int64_t r; - if (unlikely(sf->js_mode & JS_MODE_MATH)) { - if (v2 > 0x1f) - goto shl_slow; - r = (int64_t)v1 << v2; - if ((int)r != r) - goto shl_slow; - } else { - v2 &= 0x1f; - } - } -#else - v2 &= 0x1f; -#endif - sp[-2] = JS_NewInt32(ctx, v1 << v2); - sp--; - } else { -#ifdef CONFIG_BIGNUM - shl_slow: -#endif - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_shr): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - uint32_t v2; - v2 = JS_VALUE_GET_INT(op2); - /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */ - v2 &= 0x1f; - sp[-2] = JS_NewUint32(ctx, - (uint32_t)JS_VALUE_GET_INT(op1) >> - v2); - sp--; - } else { - if (js_shr_slow(ctx, sp)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_sar): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - uint32_t v2; - v2 = JS_VALUE_GET_INT(op2); -#ifdef CONFIG_BIGNUM - if (unlikely(v2 > 0x1f)) { - if (unlikely(sf->js_mode & JS_MODE_MATH)) - goto sar_slow; - else - v2 &= 0x1f; - } -#else - v2 &= 0x1f; -#endif - sp[-2] = JS_NewInt32(ctx, - (int)JS_VALUE_GET_INT(op1) >> v2); - sp--; - } else { -#ifdef CONFIG_BIGNUM - sar_slow: -#endif - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_and): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - sp[-2] = JS_NewInt32(ctx, - JS_VALUE_GET_INT(op1) & - JS_VALUE_GET_INT(op2)); - sp--; - } else { - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_or): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - sp[-2] = JS_NewInt32(ctx, - JS_VALUE_GET_INT(op1) | - JS_VALUE_GET_INT(op2)); - sp--; - } else { - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_xor): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - sp[-2] = JS_NewInt32(ctx, - JS_VALUE_GET_INT(op1) ^ - JS_VALUE_GET_INT(op2)); - sp--; - } else { - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - - -#define OP_CMP(opcode, binary_op, slow_call) \ - CASE(opcode): \ - { \ - JSValue op1, op2; \ - op1 = sp[-2]; \ - op2 = sp[-1]; \ - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \ - sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \ - sp--; \ - } else { \ - if (slow_call) \ - goto exception; \ - sp--; \ - } \ - } \ - BREAK - - OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode)); - OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode)); - OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode)); - OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode)); - OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0)); - OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1)); - OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0)); - OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1)); - -#ifdef CONFIG_BIGNUM - CASE(OP_mul_pow10): - if (rt->bigfloat_ops.mul_pow10(ctx, sp)) - goto exception; - sp--; - BREAK; -#endif - CASE(OP_in): - if (js_operator_in(ctx, sp)) - goto exception; - sp--; - BREAK; - CASE(OP_instanceof): - if (js_operator_instanceof(ctx, sp)) - goto exception; - sp--; - BREAK; - CASE(OP_typeof): - { - JSValue op1; - JSAtom atom; - - op1 = sp[-1]; - atom = js_operator_typeof(ctx, op1); - JS_FreeValue(ctx, op1); - sp[-1] = JS_AtomToString(ctx, atom); - } - BREAK; - CASE(OP_delete): - if (js_operator_delete(ctx, sp)) - goto exception; - sp--; - BREAK; - CASE(OP_delete_var): - { - JSAtom atom; - int ret; - - atom = get_u32(pc); - pc += 4; - - ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0); - if (unlikely(ret < 0)) - goto exception; - *sp++ = JS_NewBool(ctx, ret); - } - BREAK; - - CASE(OP_to_object): - if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) { - ret_val = JS_ToObject(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - } - BREAK; - - CASE(OP_to_propkey): - switch (JS_VALUE_GET_TAG(sp[-1])) { - case JS_TAG_INT: - case JS_TAG_STRING: - case JS_TAG_SYMBOL: - break; - default: - ret_val = JS_ToPropertyKey(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - break; - } - BREAK; - - CASE(OP_to_propkey2): - /* must be tested first */ - if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { - JS_ThrowTypeError(ctx, "value has no property"); - goto exception; - } - switch (JS_VALUE_GET_TAG(sp[-1])) { - case JS_TAG_INT: - case JS_TAG_STRING: - case JS_TAG_SYMBOL: - break; - default: - ret_val = JS_ToPropertyKey(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - break; - } - BREAK; -#if 0 - CASE(OP_to_string): - if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) { - ret_val = JS_ToString(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - } - BREAK; -#endif - CASE(OP_with_get_var): - CASE(OP_with_put_var): - CASE(OP_with_delete_var): - CASE(OP_with_make_ref): - CASE(OP_with_get_ref): - CASE(OP_with_get_ref_undef): - { - JSAtom atom; - int32_t diff; - JSValue obj, val; - int ret, is_with; - atom = get_u32(pc); - diff = get_u32(pc + 4); - is_with = pc[8]; - pc += 9; - - obj = sp[-1]; - ret = JS_HasProperty(ctx, obj, atom); - if (unlikely(ret < 0)) - goto exception; - if (ret) { - if (is_with) { - ret = js_has_unscopable(ctx, obj, atom); - if (unlikely(ret < 0)) - goto exception; - if (ret) - goto no_with; - } - switch (opcode) { - case OP_with_get_var: - val = JS_GetProperty(ctx, obj, atom); - if (unlikely(JS_IsException(val))) - goto exception; - set_value(ctx, &sp[-1], val); - break; - case OP_with_put_var: - /* XXX: check if strict mode */ - ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], - JS_PROP_THROW_STRICT); - JS_FreeValue(ctx, sp[-1]); - sp -= 2; - if (unlikely(ret < 0)) - goto exception; - break; - case OP_with_delete_var: - ret = JS_DeleteProperty(ctx, obj, atom, 0); - if (unlikely(ret < 0)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = JS_NewBool(ctx, ret); - break; - case OP_with_make_ref: - /* produce a pair object/propname on the stack */ - *sp++ = JS_AtomToValue(ctx, atom); - break; - case OP_with_get_ref: - /* produce a pair object/method on the stack */ - val = JS_GetProperty(ctx, obj, atom); - if (unlikely(JS_IsException(val))) - goto exception; - *sp++ = val; - break; - case OP_with_get_ref_undef: - /* produce a pair undefined/function on the stack */ - val = JS_GetProperty(ctx, obj, atom); - if (unlikely(JS_IsException(val))) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = JS_UNDEFINED; - *sp++ = val; - break; - } - pc += diff - 5; - } else { - no_with: - /* if not jumping, drop the object argument */ - JS_FreeValue(ctx, sp[-1]); - sp--; - } - } - BREAK; - - CASE(OP_await): - ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT); - goto done_generator; - CASE(OP_yield): - ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD); - goto done_generator; - CASE(OP_yield_star): - CASE(OP_async_yield_star): - ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR); - goto done_generator; - CASE(OP_return_async): - CASE(OP_initial_yield): - ret_val = JS_UNDEFINED; - goto done_generator; - - CASE(OP_nop): - BREAK; - CASE(OP_is_undefined_or_null): - if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED || - JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { - goto set_true; - } else { - goto free_and_set_false; - } -#if SHORT_OPCODES - CASE(OP_is_undefined): - if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) { - goto set_true; - } else { - goto free_and_set_false; - } - CASE(OP_is_null): - if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { - goto set_true; - } else { - goto free_and_set_false; - } - /* XXX: could merge to a single opcode */ - CASE(OP_typeof_is_undefined): - /* different from OP_is_undefined because of isHTMLDDA */ - if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) { - goto free_and_set_true; - } else { - goto free_and_set_false; - } - CASE(OP_typeof_is_function): - if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { - goto free_and_set_true; - } else { - goto free_and_set_false; - } - free_and_set_true: - JS_FreeValue(ctx, sp[-1]); -#endif - set_true: - sp[-1] = JS_TRUE; - BREAK; - free_and_set_false: - JS_FreeValue(ctx, sp[-1]); - sp[-1] = JS_FALSE; - BREAK; - CASE(OP_invalid): - DEFAULT: - JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", - (int)(pc - b->byte_code_buf - 1), opcode); - goto exception; - } - } - exception: - if (is_backtrace_needed(ctx, rt->current_exception)) { - /* add the backtrace information now (it is not done - before if the exception happens in a bytecode - operation */ - sf->cur_pc = pc; - build_backtrace(ctx, rt->current_exception, NULL, 0, 0); - } - if (!JS_IsUncatchableError(ctx, rt->current_exception)) { - while (sp > stack_buf) { - JSValue val = *--sp; - JS_FreeValue(ctx, val); - if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) { - int pos = JS_VALUE_GET_INT(val); - if (pos == 0) { - /* enumerator: close it with a throw */ - JS_FreeValue(ctx, sp[-1]); /* drop the next method */ - sp--; - JS_IteratorClose(ctx, sp[-1], TRUE); - } else { - *sp++ = rt->current_exception; - rt->current_exception = JS_NULL; - pc = b->byte_code_buf + pos; - goto restart; - } - } - } - } - ret_val = JS_EXCEPTION; - /* the local variables are freed by the caller in the generator - case. Hence the label 'done' should never be reached in a - generator function. */ - if (b->func_kind != JS_FUNC_NORMAL) { - done_generator: - sf->cur_pc = pc; - sf->cur_sp = sp; - } else { - done: - if (unlikely(!list_empty(&sf->var_ref_list))) { - /* variable references reference the stack: must close them */ - close_var_refs(rt, sf); - } - /* free the local variables and stack */ - for(pval = local_buf; pval < sp; pval++) { - JS_FreeValue(ctx, *pval); - } - } - rt->current_stack_frame = sf->prev_frame; - return ret_val; -} - -JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, - argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); -} - -static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, - argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); - JS_FreeValue(ctx, func_obj); - return res; -} - -/* warning: the refcount of the context is not incremented. Return - NULL in case of exception (case of revoked proxy only) */ -static 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; -} - -static 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; -} - -/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ -static JSValue JS_CallConstructorInternal(JSContext *ctx, - JSValueConst func_obj, - JSValueConst new_target, - int argc, JSValue *argv, int flags) -{ - JSObject *p; - JSFunctionBytecode *b; - - if (js_poll_interrupts(ctx)) - return JS_EXCEPTION; - flags |= JS_CALL_FLAG_CONSTRUCTOR; - if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) - goto not_a_function; - p = JS_VALUE_GET_OBJ(func_obj); - if (unlikely(!p->is_constructor)) - return JS_ThrowTypeError(ctx, "not a constructor"); - if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { - JSClassCall *call_func; - call_func = ctx->rt->class_array[p->class_id].call; - if (!call_func) { - not_a_function: - return JS_ThrowTypeError(ctx, "not a function"); - } - return call_func(ctx, func_obj, new_target, argc, - (JSValueConst *)argv, flags); - } - - b = p->u.func.function_bytecode; - if (b->is_derived_class_constructor) { - return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags); - } else { - JSValue obj, ret; - /* legacy constructor behavior */ - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); - if (JS_IsException(obj)) - return JS_EXCEPTION; - ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags); - if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT || - JS_IsException(ret)) { - JS_FreeValue(ctx, obj); - return ret; - } else { - JS_FreeValue(ctx, ret); - return obj; - } - } -} - -JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - return JS_CallConstructorInternal(ctx, func_obj, new_target, - argc, (JSValue *)argv, - JS_CALL_FLAG_COPY_ARGV); -} - -JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, - int argc, JSValueConst *argv) -{ - return JS_CallConstructorInternal(ctx, func_obj, func_obj, - argc, (JSValue *)argv, - JS_CALL_FLAG_COPY_ARGV); -} - -JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, - int argc, JSValueConst *argv) -{ - JSValue func_obj; - func_obj = JS_GetProperty(ctx, this_val, atom); - if (JS_IsException(func_obj)) - return func_obj; - return JS_CallFree(ctx, func_obj, this_val, argc, argv); -} - -static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, - int argc, JSValueConst *argv) -{ - JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv); - JS_FreeValue(ctx, this_val); - return res; -} - -/* JSAsyncFunctionState (used by generator and async functions) */ -static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, - JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - JSObject *p; - JSFunctionBytecode *b; - JSStackFrame *sf; - int local_count, i, arg_buf_len, n; - - sf = &s->frame; - init_list_head(&sf->var_ref_list); - p = JS_VALUE_GET_OBJ(func_obj); - b = p->u.func.function_bytecode; - sf->js_mode = b->js_mode; - sf->cur_pc = b->byte_code_buf; - arg_buf_len = max_int(b->arg_count, argc); - local_count = arg_buf_len + b->var_count + b->stack_size; - sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1)); - if (!sf->arg_buf) - return -1; - sf->cur_func = JS_DupValue(ctx, func_obj); - s->this_val = JS_DupValue(ctx, this_obj); - s->argc = argc; - sf->arg_count = arg_buf_len; - sf->var_buf = sf->arg_buf + arg_buf_len; - sf->cur_sp = sf->var_buf + b->var_count; - for(i = 0; i < argc; i++) - sf->arg_buf[i] = JS_DupValue(ctx, argv[i]); - n = arg_buf_len + b->var_count; - for(i = argc; i < n; i++) - sf->arg_buf[i] = JS_UNDEFINED; - return 0; -} - -static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, - JS_MarkFunc *mark_func) -{ - JSStackFrame *sf; - JSValue *sp; - - sf = &s->frame; - JS_MarkValue(rt, sf->cur_func, mark_func); - JS_MarkValue(rt, s->this_val, mark_func); - if (sf->cur_sp) { - /* if the function is running, cur_sp is not known so we - cannot mark the stack. Marking the variables is not needed - because a running function cannot be part of a removable - cycle */ - for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) - JS_MarkValue(rt, *sp, mark_func); - } -} - -static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) -{ - JSStackFrame *sf; - JSValue *sp; - - sf = &s->frame; - - /* close the closure variables. */ - close_var_refs(rt, sf); - - if (sf->arg_buf) { - /* cannot free the function if it is running */ - assert(sf->cur_sp != NULL); - for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) { - JS_FreeValueRT(rt, *sp); - } - js_free_rt(rt, sf->arg_buf); - } - JS_FreeValueRT(rt, sf->cur_func); - JS_FreeValueRT(rt, s->this_val); -} - -static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) -{ - JSValue func_obj; - - if (js_check_stack_overflow(ctx->rt, 0)) - return JS_ThrowStackOverflow(ctx); - - /* the tag does not matter provided it is not an object */ - func_obj = JS_MKPTR(JS_TAG_INT, s); - return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, - s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR); -} - - -/* Generators */ - -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; -} - -static 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); -} - -static 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); -} - -/* XXX: use enum */ -#define GEN_MAGIC_NEXT 0 -#define GEN_MAGIC_RETURN 1 -#define GEN_MAGIC_THROW 2 - -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; -} - -static 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; -} - /* AsyncFunction */ -static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s) -{ - if (s->is_active) { - async_func_free(rt, &s->func_state); - s->is_active = FALSE; - } -} - -static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s) -{ - js_async_function_terminate(rt, s); - JS_FreeValueRT(rt, s->resolving_funcs[0]); - JS_FreeValueRT(rt, s->resolving_funcs[1]); - remove_gc_object(&s->header); - js_free_rt(rt, s); -} - -static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s) -{ - if (--s->header.ref_count == 0) { - js_async_function_free0(rt, s); - } -} - -static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSAsyncFunctionData *s = p->u.async_function_data; - if (s) { - js_async_function_free(rt, s); - } -} - -static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSAsyncFunctionData *s = p->u.async_function_data; - if (s) { - mark_func(rt, &s->header); - } -} - -static int js_async_function_resolve_create(JSContext *ctx, - JSAsyncFunctionData *s, - JSValue *resolving_funcs) -{ - int i; - JSObject *p; - - for(i = 0; i < 2; i++) { - resolving_funcs[i] = - JS_NewObjectProtoClass(ctx, ctx->function_proto, - JS_CLASS_ASYNC_FUNCTION_RESOLVE + i); - if (JS_IsException(resolving_funcs[i])) { - if (i == 1) - JS_FreeValue(ctx, resolving_funcs[0]); - return -1; - } - p = JS_VALUE_GET_OBJ(resolving_funcs[i]); - s->header.ref_count++; - p->u.async_function_data = s; - } - return 0; -} - -static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) -{ - JSValue func_ret, ret2; - - func_ret = async_func_resume(ctx, &s->func_state); - if (JS_IsException(func_ret)) { - JSValue error; - fail: - error = JS_GetException(ctx); - ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, - 1, (JSValueConst *)&error); - JS_FreeValue(ctx, error); - js_async_function_terminate(ctx->rt, s); - JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ - } else { - JSValue value; - value = s->func_state.frame.cur_sp[-1]; - s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; - if (JS_IsUndefined(func_ret)) { - /* function returned */ - ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, - 1, (JSValueConst *)&value); - JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ - JS_FreeValue(ctx, value); - js_async_function_terminate(ctx->rt, s); - } else { - JSValue promise, resolving_funcs[2], resolving_funcs1[2]; - int i, res; - - /* await */ - JS_FreeValue(ctx, func_ret); /* not used */ - promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, (JSValueConst *)&value, 0); - JS_FreeValue(ctx, value); - if (JS_IsException(promise)) - goto fail; - if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { - 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; - } - } -} - -static JSValue js_async_function_resolve_call(JSContext *ctx, - JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int flags) -{ - JSObject *p = JS_VALUE_GET_OBJ(func_obj); - JSAsyncFunctionData *s = p->u.async_function_data; - BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; - JSValueConst arg; - - if (argc > 0) - arg = argv[0]; - else - arg = JS_UNDEFINED; - 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_function_resume(ctx, s); - return JS_UNDEFINED; -} - -static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags) -{ - JSValue promise; - JSAsyncFunctionData *s; - - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - return JS_EXCEPTION; - s->header.ref_count = 1; - add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); - s->is_active = FALSE; - s->resolving_funcs[0] = JS_UNDEFINED; - s->resolving_funcs[1] = JS_UNDEFINED; - - promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); - if (JS_IsException(promise)) - goto fail; - - if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { - fail: - JS_FreeValue(ctx, promise); - js_async_function_free(ctx->rt, s); - return JS_EXCEPTION; - } - s->is_active = TRUE; - - js_async_function_resume(ctx, s); - - js_async_function_free(ctx->rt, s); - - return promise; -} - /* AsyncGenerator */ -typedef enum JSAsyncGeneratorStateEnum { - JS_ASYNC_GENERATOR_STATE_SUSPENDED_START, - JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD, - JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR, - JS_ASYNC_GENERATOR_STATE_EXECUTING, - JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN, - JS_ASYNC_GENERATOR_STATE_COMPLETED, -} JSAsyncGeneratorStateEnum; - -typedef struct JSAsyncGeneratorRequest { - struct list_head link; - /* completion */ - int completion_type; /* GEN_MAGIC_x */ - JSValue result; - /* promise capability */ - JSValue promise; - JSValue resolving_funcs[2]; -} JSAsyncGeneratorRequest; - -typedef struct JSAsyncGeneratorData { - JSObject *generator; /* back pointer to the object (const) */ - JSAsyncGeneratorStateEnum state; - JSAsyncFunctionState func_state; - struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ -} JSAsyncGeneratorData; - -static 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 void js_async_generator_finalizer(JSRuntime *rt, JSValue obj) -{ - JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR); - - if (s) { - js_async_generator_free(rt, s); - } -} - -static void js_async_generator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR); - struct list_head *el; - JSAsyncGeneratorRequest *req; - if (s) { - list_for_each(el, &s->queue) { - req = list_entry(el, JSAsyncGeneratorRequest, link); - JS_MarkValue(rt, req->result, mark_func); - JS_MarkValue(rt, req->promise, mark_func); - JS_MarkValue(rt, req->resolving_funcs[0], mark_func); - JS_MarkValue(rt, req->resolving_funcs[1], mark_func); - } - if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && - s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { - async_func_mark(rt, &s->func_state, mark_func); - } - } -} - -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; -} - -static 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; -} - -/* magic = GEN_MAGIC_x */ -static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int magic) -{ - JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR); - JSValue promise, resolving_funcs[2]; - JSAsyncGeneratorRequest *req; - - promise = JS_NewPromiseCapability(ctx, resolving_funcs); - if (JS_IsException(promise)) - return JS_EXCEPTION; - if (!s) { - JSValue err, res2; - JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); - err = JS_GetException(ctx); - res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, - 1, (JSValueConst *)&err); - JS_FreeValue(ctx, err); - JS_FreeValue(ctx, res2); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return promise; - } - req = js_mallocz(ctx, sizeof(*req)); - if (!req) - goto fail; - req->completion_type = magic; - req->result = JS_DupValue(ctx, argv[0]); - req->promise = JS_DupValue(ctx, promise); - req->resolving_funcs[0] = resolving_funcs[0]; - req->resolving_funcs[1] = resolving_funcs[1]; - list_add_tail(&req->link, &s->queue); - if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) { - js_async_generator_resume_next(ctx, s); - } - return promise; - fail: - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - JS_FreeValue(ctx, promise); - return JS_EXCEPTION; -} - -static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int flags) -{ - JSValue obj, func_ret; - JSAsyncGeneratorData *s; - - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - return JS_EXCEPTION; - s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START; - init_list_head(&s->queue); - if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { - s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; - goto fail; - } - - /* execute the function up to 'OP_initial_yield' (no yield nor - await are possible) */ - 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_ASYNC_GENERATOR); - if (JS_IsException(obj)) - goto fail; - s->generator = JS_VALUE_GET_OBJ(obj); - JS_SetOpaque(obj, s); - return obj; - fail: - js_async_generator_free(ctx->rt, s); - return JS_EXCEPTION; -} - /* JS parser */ -enum { - TOK_NUMBER = -128, - TOK_STRING, - TOK_TEMPLATE, - TOK_IDENT, - TOK_REGEXP, - /* warning: order matters (see js_parse_assign_expr) */ - TOK_MUL_ASSIGN, - TOK_DIV_ASSIGN, - TOK_MOD_ASSIGN, - TOK_PLUS_ASSIGN, - TOK_MINUS_ASSIGN, - TOK_SHL_ASSIGN, - TOK_SAR_ASSIGN, - TOK_SHR_ASSIGN, - TOK_AND_ASSIGN, - TOK_XOR_ASSIGN, - TOK_OR_ASSIGN, -#ifdef CONFIG_BIGNUM - TOK_MATH_POW_ASSIGN, -#endif - TOK_POW_ASSIGN, - TOK_LAND_ASSIGN, - TOK_LOR_ASSIGN, - TOK_DOUBLE_QUESTION_MARK_ASSIGN, - TOK_DEC, - TOK_INC, - TOK_SHL, - TOK_SAR, - TOK_SHR, - TOK_LT, - TOK_LTE, - TOK_GT, - TOK_GTE, - TOK_EQ, - TOK_STRICT_EQ, - TOK_NEQ, - TOK_STRICT_NEQ, - TOK_LAND, - TOK_LOR, -#ifdef CONFIG_BIGNUM - TOK_MATH_POW, -#endif - TOK_POW, - TOK_ARROW, - TOK_ELLIPSIS, - TOK_DOUBLE_QUESTION_MARK, - TOK_QUESTION_MARK_DOT, - TOK_ERROR, - TOK_PRIVATE_NAME, - TOK_EOF, - /* keywords: WARNING: same order as atoms */ - TOK_NULL, /* must be first */ - TOK_FALSE, - TOK_TRUE, - TOK_IF, - TOK_ELSE, - TOK_RETURN, - TOK_VAR, - TOK_THIS, - TOK_DELETE, - TOK_VOID, - TOK_TYPEOF, - TOK_NEW, - TOK_IN, - TOK_INSTANCEOF, - TOK_DO, - TOK_WHILE, - TOK_FOR, - TOK_BREAK, - TOK_CONTINUE, - TOK_SWITCH, - TOK_CASE, - TOK_DEFAULT, - TOK_THROW, - TOK_TRY, - TOK_CATCH, - TOK_FINALLY, - TOK_FUNCTION, - TOK_DEBUGGER, - TOK_WITH, - /* FutureReservedWord */ - TOK_CLASS, - TOK_CONST, - TOK_ENUM, - TOK_EXPORT, - TOK_EXTENDS, - TOK_IMPORT, - TOK_SUPER, - /* FutureReservedWords when parsing strict mode code */ - TOK_IMPLEMENTS, - TOK_INTERFACE, - TOK_LET, - TOK_PACKAGE, - TOK_PRIVATE, - TOK_PROTECTED, - TOK_PUBLIC, - TOK_STATIC, - TOK_YIELD, - TOK_AWAIT, /* must be last */ - TOK_OF, /* only used for js_parse_skip_parens_token() */ -}; - -#define TOK_FIRST_KEYWORD TOK_NULL -#define TOK_LAST_KEYWORD TOK_AWAIT - -/* unicode code points */ -#define CP_NBSP 0x00a0 -#define CP_BOM 0xfeff - -#define CP_LS 0x2028 -#define CP_PS 0x2029 - -typedef struct BlockEnv { - struct BlockEnv *prev; - JSAtom label_name; /* JS_ATOM_NULL if none */ - int label_break; /* -1 if none */ - int label_cont; /* -1 if none */ - int drop_count; /* number of stack elements to drop */ - int label_finally; /* -1 if none */ - int scope_level; - int has_iterator; -} BlockEnv; - -typedef struct JSGlobalVar { - int cpool_idx; /* if >= 0, index in the constant pool for hoisted - function defintion*/ - uint8_t force_init : 1; /* force initialization to undefined */ - uint8_t is_lexical : 1; /* global let/const definition */ - uint8_t is_const : 1; /* const definition */ - int scope_level; /* scope of definition */ - JSAtom var_name; /* variable name */ -} JSGlobalVar; - -typedef struct RelocEntry { - struct RelocEntry *next; - uint32_t addr; /* address to patch */ - int size; /* address size: 1, 2 or 4 bytes */ -} RelocEntry; - -typedef struct JumpSlot { - int op; - int size; - int pos; - int label; -} JumpSlot; - -typedef struct LabelSlot { - int ref_count; - int pos; /* phase 1 address, -1 means not resolved yet */ - int pos2; /* phase 2 address, -1 means not resolved yet */ - int addr; /* phase 3 address, -1 means not resolved yet */ - RelocEntry *first_reloc; -} LabelSlot; - -typedef struct LineNumberSlot { - uint32_t pc; - int line_num; -} LineNumberSlot; - -typedef enum JSParseFunctionEnum { - JS_PARSE_FUNC_STATEMENT, - JS_PARSE_FUNC_VAR, - JS_PARSE_FUNC_EXPR, - JS_PARSE_FUNC_ARROW, - JS_PARSE_FUNC_GETTER, - JS_PARSE_FUNC_SETTER, - JS_PARSE_FUNC_METHOD, - JS_PARSE_FUNC_CLASS_CONSTRUCTOR, - JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR, -} JSParseFunctionEnum; - -typedef enum JSParseExportEnum { - JS_PARSE_EXPORT_NONE, - JS_PARSE_EXPORT_NAMED, - JS_PARSE_EXPORT_DEFAULT, -} JSParseExportEnum; - -typedef struct JSFunctionDef { - JSContext *ctx; - struct JSFunctionDef *parent; - int parent_cpool_idx; /* index in the constant pool of the parent - or -1 if none */ - int parent_scope_level; /* scope level in parent at point of definition */ - struct list_head child_list; /* list of JSFunctionDef.link */ - struct list_head link; - - BOOL is_eval; /* TRUE if eval code */ - int eval_type; /* only valid if is_eval = TRUE */ - BOOL is_global_var; /* TRUE if variables are not defined locally: - eval global, eval module or non strict eval */ - BOOL is_func_expr; /* TRUE if function expression */ - BOOL has_home_object; /* TRUE if the home object is available */ - BOOL has_prototype; /* true if a prototype field is necessary */ - BOOL has_simple_parameter_list; - BOOL has_parameter_expressions; /* if true, an argument scope is created */ - BOOL has_use_strict; /* to reject directive in special cases */ - BOOL has_eval_call; /* true if the function contains a call to eval() */ - BOOL has_arguments_binding; /* true if the 'arguments' binding is - available in the function */ - BOOL has_this_binding; /* true if the 'this' and new.target binding are - available in the function */ - BOOL new_target_allowed; /* true if the 'new.target' does not - throw a syntax error */ - BOOL super_call_allowed; /* true if super() is allowed */ - BOOL super_allowed; /* true if super. or super[] is allowed */ - BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */ - BOOL is_derived_class_constructor; - BOOL in_function_body; - BOOL backtrace_barrier; - JSFunctionKindEnum func_kind : 8; - JSParseFunctionEnum func_type : 8; - uint8_t js_mode; /* bitmap of JS_MODE_x */ - JSAtom func_name; /* JS_ATOM_NULL if no name */ - - JSVarDef *vars; - int var_size; /* allocated size for vars[] */ - int var_count; - JSVarDef *args; - int arg_size; /* allocated size for args[] */ - int arg_count; /* number of arguments */ - int defined_arg_count; - int var_object_idx; /* -1 if none */ - int arg_var_object_idx; /* -1 if none (var object for the argument scope) */ - int arguments_var_idx; /* -1 if none */ - int arguments_arg_idx; /* argument variable definition in argument scope, - -1 if none */ - int func_var_idx; /* variable containing the current function (-1 - if none, only used if is_func_expr is true) */ - int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */ - int this_var_idx; /* variable containg the 'this' value, -1 if none */ - int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */ - int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */ - int home_object_var_idx; - BOOL need_home_object; - - int scope_level; /* index into fd->scopes if the current lexical scope */ - int scope_first; /* index into vd->vars of first lexically scoped variable */ - int scope_size; /* allocated size of fd->scopes array */ - int scope_count; /* number of entries used in the fd->scopes array */ - JSVarScope *scopes; - JSVarScope def_scope_array[4]; - int body_scope; /* scope of the body of the function or eval */ - - int global_var_count; - int global_var_size; - JSGlobalVar *global_vars; - - DynBuf byte_code; - int last_opcode_pos; /* -1 if no last opcode */ - int last_opcode_line_num; - BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */ - - LabelSlot *label_slots; - int label_size; /* allocated size for label_slots[] */ - int label_count; - BlockEnv *top_break; /* break/continue label stack */ - - /* constant pool (strings, functions, numbers) */ - JSValue *cpool; - int cpool_count; - int cpool_size; - - /* list of variables in the closure */ - int closure_var_count; - int closure_var_size; - JSClosureVar *closure_var; - - JumpSlot *jump_slots; - int jump_size; - int jump_count; - - LineNumberSlot *line_number_slots; - int line_number_size; - int line_number_count; - int line_number_last; - int line_number_last_pc; - - /* pc2line table */ - JSAtom filename; - int line_num; - DynBuf pc2line; - - char *source; /* raw source, utf-8 encoded */ - int source_len; - - JSModuleDef *module; /* != NULL when parsing a module */ -} JSFunctionDef; - -typedef struct JSToken { - int val; - int line_num; /* line number of token start */ - const uint8_t *ptr; - union { - struct { - JSValue str; - int sep; - } str; - struct { - JSValue val; -#ifdef CONFIG_BIGNUM - slimb_t exponent; /* may be != 0 only if val is a float */ -#endif - } num; - struct { - JSAtom atom; - BOOL has_escape; - BOOL is_reserved; - } ident; - struct { - JSValue body; - JSValue flags; - } regexp; - } u; -} JSToken; - -typedef struct JSParseState { - JSContext *ctx; - int last_line_num; /* line number of last token */ - int line_num; /* line number of current offset */ - const char *filename; - JSToken token; - BOOL got_lf; /* true if got line feed before the current token */ - const uint8_t *last_ptr; - const uint8_t *buf_ptr; - const uint8_t *buf_end; - - /* current function code */ - JSFunctionDef *cur_func; - BOOL is_module; /* parsing a module */ - BOOL allow_html_comments; - BOOL ext_json; /* true if accepting JSON superset */ -} JSParseState; - -typedef struct JSOpCode { -#ifdef DUMP_BYTECODE - const char *name; -#endif - uint8_t size; /* in bytes */ - /* the opcodes remove n_pop items from the top of the stack, then - pushes n_push items */ - uint8_t n_pop; - uint8_t n_push; - uint8_t fmt; -} JSOpCode; - -static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { +const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { #define FMT(f) #ifdef DUMP_BYTECODE #define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f }, @@ -20119,49 +7595,6 @@ static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { #undef FMT }; -#if SHORT_OPCODES -/* After the final compilation pass, short opcodes are used. Their - opcodes overlap with the temporary opcodes which cannot appear in - the final bytecode. Their description is after the temporary - opcodes in opcode_info[]. */ -#define short_opcode_info(op) \ - opcode_info[(op) >= OP_TEMP_START ? \ - (op) + (OP_TEMP_END - OP_TEMP_START) : (op)] -#else -#define short_opcode_info(op) opcode_info[op] -#endif - -static __exception int next_token(JSParseState *s); - -static 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; - } -} - static void __attribute((unused)) dump_token(JSParseState *s, const JSToken *token) { @@ -20223,362 +7656,10 @@ static void __attribute((unused)) dump_token(JSParseState *s, } } -int __attribute__((format(printf, 2, 3))) 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; -} - -static int js_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 next_token(s); -} - -static int js_parse_expect_semi(JSParseState *s) -{ - if (s->token.val != ';') { - /* automatic insertion of ';' */ - if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) { - return 0; - } - return js_parse_error(s, "expecting '%c'", ';'); - } - return next_token(s); -} - -static int js_parse_error_reserved_identifier(JSParseState *s) -{ - char buf1[ATOM_GET_STR_BUF_SIZE]; - return js_parse_error(s, "'%s' is a reserved identifier", - JS_AtomGetStr(s->ctx, buf1, sizeof(buf1), - s->token.u.ident.atom)); -} - -static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p) -{ - uint32_t c; - StringBuffer b_s, *b = &b_s; - - /* p points to the first byte of the template part */ - if (string_buffer_init(s->ctx, b, 32)) - goto fail; - for(;;) { - if (p >= s->buf_end) - goto unexpected_eof; - c = *p++; - if (c == '`') { - /* template end part */ - break; - } - if (c == '$' && *p == '{') { - /* template start or middle part */ - p++; - break; - } - if (c == '\\') { - if (string_buffer_putc8(b, c)) - goto fail; - if (p >= s->buf_end) - goto unexpected_eof; - c = *p++; - } - /* newline sequences are normalized as single '\n' bytes */ - if (c == '\r') { - if (*p == '\n') - p++; - c = '\n'; - } - if (c == '\n') { - s->line_num++; - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - js_parse_error(s, "invalid UTF-8 sequence"); - goto fail; - } - p = p_next; - } - if (string_buffer_putc(b, c)) - goto fail; - } - s->token.val = TOK_TEMPLATE; - s->token.u.str.sep = c; - s->token.u.str.str = string_buffer_end(b); - s->buf_ptr = p; - return 0; - - unexpected_eof: - js_parse_error(s, "unexpected end of string"); - fail: - string_buffer_free(b); - return -1; -} - -static __exception int js_parse_string(JSParseState *s, int sep, - BOOL do_throw, const uint8_t *p, - JSToken *token, const uint8_t **pp) -{ - int ret; - uint32_t c; - StringBuffer b_s, *b = &b_s; - - /* string */ - if (string_buffer_init(s->ctx, b, 32)) - goto fail; - for(;;) { - if (p >= s->buf_end) - goto invalid_char; - c = *p; - if (c < 0x20) { - if (!s->cur_func) { - if (do_throw) - js_parse_error(s, "invalid character in a JSON string"); - goto fail; - } - if (sep == '`') { - if (c == '\r') { - if (p[1] == '\n') - p++; - c = '\n'; - } - /* do not update s->line_num */ - } else if (c == '\n' || c == '\r') - goto invalid_char; - } - p++; - if (c == sep) - break; - if (c == '$' && *p == '{' && sep == '`') { - /* template start or middle part */ - p++; - break; - } - if (c == '\\') { - c = *p; - /* XXX: need a specific JSON case to avoid - accepting invalid escapes */ - switch(c) { - case '\0': - if (p >= s->buf_end) - goto invalid_char; - p++; - break; - case '\'': - case '\"': - case '\\': - p++; - break; - case '\r': /* accept DOS and MAC newline sequences */ - if (p[1] == '\n') { - p++; - } - /* fall thru */ - case '\n': - /* ignore escaped newline sequence */ - p++; - if (sep != '`') - s->line_num++; - continue; - default: - if (c >= '0' && c <= '9') { - if (!s->cur_func) - goto invalid_escape; /* JSON case */ - if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`') - goto parse_escape; - if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { - p++; - c = '\0'; - } else { - if (c >= '8' || sep == '`') { - /* Note: according to ES2021, \8 and \9 are not - accepted in strict mode or in templates. */ - goto invalid_escape; - } else { - if (do_throw) - js_parse_error(s, "octal escape sequences are not allowed in strict mode"); - } - goto fail; - } - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - goto invalid_utf8; - } - p = p_next; - /* LS or PS are skipped */ - if (c == CP_LS || c == CP_PS) - continue; - } else { - parse_escape: - ret = lre_parse_escape(&p, TRUE); - if (ret == -1) { - invalid_escape: - if (do_throw) - js_parse_error(s, "malformed escape sequence in string literal"); - goto fail; - } else if (ret < 0) { - /* ignore the '\' (could output a warning) */ - p++; - } else { - c = ret; - } - } - break; - } - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) - goto invalid_utf8; - p = p_next; - } - if (string_buffer_putc(b, c)) - goto fail; - } - token->val = TOK_STRING; - token->u.str.sep = c; - token->u.str.str = string_buffer_end(b); - *pp = p; - return 0; - - invalid_utf8: - if (do_throw) - js_parse_error(s, "invalid UTF-8 sequence"); - goto fail; - invalid_char: - if (do_throw) - js_parse_error(s, "unexpected end of string"); - fail: - string_buffer_free(b); - return -1; -} - -static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) { - return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom && - !s->token.u.ident.has_escape; -} - -static __exception int js_parse_regexp(JSParseState *s) -{ - const uint8_t *p; - BOOL in_class; - StringBuffer b_s, *b = &b_s; - StringBuffer b2_s, *b2 = &b2_s; - uint32_t c; - - p = s->buf_ptr; - p++; - in_class = FALSE; - if (string_buffer_init(s->ctx, b, 32)) - return -1; - if (string_buffer_init(s->ctx, b2, 1)) - goto fail; - for(;;) { - if (p >= s->buf_end) { - eof_error: - js_parse_error(s, "unexpected end of regexp"); - goto fail; - } - c = *p++; - if (c == '\n' || c == '\r') { - goto eol_error; - } else if (c == '/') { - if (!in_class) - break; - } else if (c == '[') { - in_class = TRUE; - } else if (c == ']') { - /* XXX: incorrect as the first character in a class */ - in_class = FALSE; - } else if (c == '\\') { - if (string_buffer_putc8(b, c)) - goto fail; - c = *p++; - if (c == '\n' || c == '\r') - goto eol_error; - else if (c == '\0' && p >= s->buf_end) - goto eof_error; - else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - goto invalid_utf8; - } - p = p_next; - if (c == CP_LS || c == CP_PS) - goto eol_error; - } - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - invalid_utf8: - js_parse_error(s, "invalid UTF-8 sequence"); - goto fail; - } - p = p_next; - /* LS or PS are considered as line terminator */ - if (c == CP_LS || c == CP_PS) { - eol_error: - js_parse_error(s, "unexpected line terminator in regexp"); - goto fail; - } - } - if (string_buffer_putc(b, c)) - goto fail; - } - - /* flags */ - for(;;) { - const uint8_t *p_next = p; - c = *p_next++; - if (c >= 0x80) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - goto invalid_utf8; - } - } - if (!lre_js_is_ident_next(c)) - break; - if (string_buffer_putc(b2, c)) - goto fail; - p = p_next; - } - - s->token.val = TOK_REGEXP; - s->token.u.regexp.body = string_buffer_end(b); - s->token.u.regexp.flags = string_buffer_end(b2); - s->buf_ptr = p; - return 0; - fail: - string_buffer_free(b); - string_buffer_free(b2); - return -1; -} - -static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, - char *static_buf) +int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, char *static_buf) { char *buf, *new_buf; size_t size, new_size; - buf = *pbuf; size = *psize; if (size >= (SIZE_MAX / 3) * 2) @@ -20600,888 +7681,13 @@ static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, return 0; } -/* '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; -} - - -static __exception 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 (is_digit(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 `` 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 `