diff --git a/javascript/duktape/duk_config.h b/javascript/duktape/duk_config.h index f6ee91cdd..64c10ea79 100644 --- a/javascript/duktape/duk_config.h +++ b/javascript/duktape/duk_config.h @@ -1,9 +1,9 @@ /* * duk_config.h configuration header generated by genconfig.py. * - * Git commit: b7b1c5fd2d1d4550140d57e05a7b32f540082bfa - * Git describe: v1.3.0-383-gb7b1c5f - * Git branch: duk-config-improvements + * Git commit: cad6f595382a0cc1a7e4207794ade5be11b3e397 + * Git describe: v1.4.0 + * Git branch: master * * Supported platforms: * - Mac OSX, iPhone, Darwin @@ -199,6 +199,12 @@ #define DUK_F_NO_STDINT_H #endif +/* C++ */ +#undef DUK_F_CPP +#if defined(__cplusplus) +#define DUK_F_CPP +#endif + /* Intel x86 (32-bit), x64 (64-bit) or x32 (64-bit but 32-bit pointers), * define only one of DUK_F_X86, DUK_F_X64, DUK_F_X32. * https://sites.google.com/site/x32abi/ @@ -273,12 +279,6 @@ #define DUK_F_CLANG #endif -/* C++ */ -#undef DUK_F_CPP -#if defined(__cplusplus) -#define DUK_F_CPP -#endif - /* C99 or above */ #undef DUK_F_C99 #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) @@ -334,11 +334,6 @@ #define DUK_F_VBCC #endif -/* uclibc */ -#if defined(__UCLIBC__) -#define DUK_F_UCLIBC -#endif - /* * Platform autodetection */ @@ -688,6 +683,10 @@ #include #endif +#if defined(DUK_F_CPP) +#include /* std::exception */ +#endif + /* * Architecture autodetection */ @@ -1264,6 +1263,11 @@ #define DUK_F_VARIADIC_MACROS_PROVIDED #endif /* autodetect compiler */ +/* uclibc */ +#if defined(__UCLIBC__) +#define DUK_F_UCLIBC +#endif + /* * Wrapper typedefs and constants for integer types, also sanity check types. * @@ -2671,6 +2675,14 @@ typedef FILE duk_file; #define DUK_USE_COMMONJS_MODULES #endif +#if defined(DUK_OPT_CPP_EXCEPTIONS) +#define DUK_USE_CPP_EXCEPTIONS +#elif defined(DUK_OPT_NO_CPP_EXCEPTIONS) +#undef DUK_USE_CPP_EXCEPTIONS +#else +#undef DUK_USE_CPP_EXCEPTIONS +#endif + #if defined(DUK_OPT_DATAPTR16) #define DUK_USE_DATAPTR16 #elif defined(DUK_OPT_NO_DATAPTR16) @@ -2760,7 +2772,7 @@ typedef FILE duk_file; #elif defined(DUK_OPT_NO_DEBUGGER_THROW_NOTIFY) #undef DUK_USE_DEBUGGER_THROW_NOTIFY #else -#undef DUK_USE_DEBUGGER_THROW_NOTIFY +#define DUK_USE_DEBUGGER_THROW_NOTIFY #endif #if defined(DUK_OPT_DEBUGGER_TRANSPORT_TORTURE) @@ -3484,27 +3496,9 @@ typedef FILE duk_file; #if defined(DUK_USE_DATAPTR_ENC16) && !defined(DUK_USE_DATAPTR16) #error config option DUK_USE_DATAPTR_ENC16 requires option DUK_USE_DATAPTR16 (which is missing) #endif -#if defined(DUK_USE_DEBUGGER_DUMPHEAP) && !defined(DUK_USE_DEBUGGER_SUPPORT) -#error config option DUK_USE_DEBUGGER_DUMPHEAP requires option DUK_USE_DEBUGGER_SUPPORT (which is missing) -#endif -#if defined(DUK_USE_DEBUGGER_FWD_LOGGING) && !defined(DUK_USE_DEBUGGER_SUPPORT) -#error config option DUK_USE_DEBUGGER_FWD_LOGGING requires option DUK_USE_DEBUGGER_SUPPORT (which is missing) -#endif -#if defined(DUK_USE_DEBUGGER_FWD_PRINTALERT) && !defined(DUK_USE_DEBUGGER_SUPPORT) -#error config option DUK_USE_DEBUGGER_FWD_PRINTALERT requires option DUK_USE_DEBUGGER_SUPPORT (which is missing) -#endif -#if defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT) && !defined(DUK_USE_DEBUGGER_SUPPORT) -#error config option DUK_USE_DEBUGGER_PAUSE_UNCAUGHT requires option DUK_USE_DEBUGGER_SUPPORT (which is missing) -#endif #if defined(DUK_USE_DEBUGGER_SUPPORT) && !defined(DUK_USE_INTERRUPT_COUNTER) #error config option DUK_USE_DEBUGGER_SUPPORT requires option DUK_USE_INTERRUPT_COUNTER (which is missing) #endif -#if defined(DUK_USE_DEBUGGER_THROW_NOTIFY) && !defined(DUK_USE_DEBUGGER_SUPPORT) -#error config option DUK_USE_DEBUGGER_THROW_NOTIFY requires option DUK_USE_DEBUGGER_SUPPORT (which is missing) -#endif -#if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE) && !defined(DUK_USE_DEBUGGER_SUPPORT) -#error config option DUK_USE_DEBUGGER_TRANSPORT_TORTURE requires option DUK_USE_DEBUGGER_SUPPORT (which is missing) -#endif #if defined(DUK_USE_DEEP_C_STACK) #error unsupported config option used (option has been removed): DUK_USE_DEEP_C_STACK #endif @@ -3620,6 +3614,10 @@ typedef FILE duk_file; #error unsupported config option used (option has been removed): DUK_USE_UNALIGNED_ACCESSES_POSSIBLE #endif +#if defined(DUK_USE_CPP_EXCEPTIONS) && !defined(__cplusplus) +#error DUK_USE_CPP_EXCEPTIONS enabled but not compiling with a C++ compiler +#endif + /* * Convert DUK_USE_BYTEORDER, from whatever source, into currently used * internal defines. If detection failed, #error out. diff --git a/javascript/duktape/duktape.c b/javascript/duktape/duktape.c index ef221615f..dc62df8d1 100644 --- a/javascript/duktape/duktape.c +++ b/javascript/duktape/duktape.c @@ -1,10 +1,8 @@ -/* Omit from static analysis. */ -#ifndef __clang_analyzer__ /* - * Single source autogenerated distributable for Duktape 1.3.99. - - * Git commit b7b1c5fd2d1d4550140d57e05a7b32f540082bfa (v1.3.0-383-gb7b1c5f). - * Git branch duk-config-improvements. + * Single source autogenerated distributable for Duktape 1.4.0. + * + * Git commit cad6f595382a0cc1a7e4207794ade5be11b3e397 (v1.4.0). + * Git branch master. * * See Duktape AUTHORS.rst and LICENSE.txt for copyright and * licensing information. @@ -18,7 +16,7 @@ * * (http://opensource.org/licenses/MIT) * -* Copyright (c) 2013-2015 by Duktape authors (see AUTHORS.rst) +* Copyright (c) 2013-2016 by Duktape authors (see AUTHORS.rst) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -68,6 +66,7 @@ * * Legimet * * Karl Skomski * * Bruce Pascoe +* * Ren\u00e9 Hollander * * Other contributions * =================== @@ -104,6 +103,7 @@ * * https://github.com/sstruchtrup * * Michael Drake (https://github.com/tlsa) * * https://github.com/chris-y +* * Laurent Zubiaur (https://github.com/lzubiaur) * * If you are accidentally missing from this list, send me an e-mail * (``sami.vaarala@iki.fi``) and I'll fix the omission. @@ -201,6 +201,7 @@ DUK_INTERNAL_DECL int duk_repl_isinf(double x); #ifndef DUK_JMPBUF_H_INCLUDED #define DUK_JMPBUF_H_INCLUDED +#if !defined(DUK_USE_CPP_EXCEPTIONS) struct duk_jmpbuf { #if defined(DUK_USE_SETJMP) || defined(DUK_USE_UNDERSCORE_SETJMP) jmp_buf jb; @@ -210,8 +211,28 @@ struct duk_jmpbuf { #error internal error, no long control transfer provider #endif }; +#endif #endif /* DUK_JMPBUF_H_INCLUDED */ +#line 1 "duk_exception.h" +/* + * Exception for Duktape internal throws when C++ exceptions are used + * for long control transfers. + * + * Doesn't inherit from any exception base class to minimize the chance + * that user code would accidentally catch this exception. + */ + +#ifndef DUK_EXCEPTION_H_INCLUDED +#define DUK_EXCEPTION_H_INCLUDED + +#if defined(DUK_USE_CPP_EXCEPTIONS) +class duk_internal_exception { + /* intentionally empty */ +}; +#endif + +#endif /* DUK_EXCEPTION_H_INCLUDED */ #line 1 "duk_forwdecl.h" /* * Forward declarations for all Duktape structures. @@ -224,7 +245,11 @@ struct duk_jmpbuf { * Forward declarations */ +#if defined(DUK_USE_CPP_EXCEPTIONS) +class duk_internal_exception; +#else struct duk_jmpbuf; +#endif /* duk_tval intentionally skipped */ struct duk_heaphdr; @@ -275,7 +300,11 @@ struct duk_compiler_ctx; struct duk_re_matcher_ctx; struct duk_re_compiler_ctx; +#if defined(DUK_USE_CPP_EXCEPTIONS) +/* no typedef */ +#else typedef struct duk_jmpbuf duk_jmpbuf; +#endif /* duk_tval intentionally skipped */ typedef struct duk_heaphdr duk_heaphdr; @@ -1595,13 +1624,13 @@ DUK_INTERNAL_DECL const duk_uint8_t duk_strings_data[2624]; #if !defined(DUK_SINGLE_FILE) DUK_INTERNAL_DECL const duk_c_function duk_bi_native_functions[149]; -DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[1952]; +DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[1955]; #ifdef DUK_USE_BUILTIN_INITJS DUK_INTERNAL_DECL const duk_uint8_t duk_initjs_data[187]; #endif /* DUK_USE_BUILTIN_INITJS */ #endif /* !DUK_SINGLE_FILE */ -#define DUK_BUILTINS_DATA_LENGTH 1952 +#define DUK_BUILTINS_DATA_LENGTH 1955 #ifdef DUK_USE_BUILTIN_INITJS #define DUK_BUILTIN_INITJS_DATA_LENGTH 187 #endif /* DUK_USE_BUILTIN_INITJS */ @@ -2940,13 +2969,13 @@ DUK_INTERNAL_DECL const duk_uint8_t duk_strings_data[2624]; #if !defined(DUK_SINGLE_FILE) DUK_INTERNAL_DECL const duk_c_function duk_bi_native_functions[149]; -DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[1952]; +DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[1955]; #ifdef DUK_USE_BUILTIN_INITJS DUK_INTERNAL_DECL const duk_uint8_t duk_initjs_data[187]; #endif /* DUK_USE_BUILTIN_INITJS */ #endif /* !DUK_SINGLE_FILE */ -#define DUK_BUILTINS_DATA_LENGTH 1952 +#define DUK_BUILTINS_DATA_LENGTH 1955 #ifdef DUK_USE_BUILTIN_INITJS #define DUK_BUILTIN_INITJS_DATA_LENGTH 187 #endif /* DUK_USE_BUILTIN_INITJS */ @@ -4285,13 +4314,13 @@ DUK_INTERNAL_DECL const duk_uint8_t duk_strings_data[2624]; #if !defined(DUK_SINGLE_FILE) DUK_INTERNAL_DECL const duk_c_function duk_bi_native_functions[149]; -DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[1952]; +DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[1955]; #ifdef DUK_USE_BUILTIN_INITJS DUK_INTERNAL_DECL const duk_uint8_t duk_initjs_data[187]; #endif /* DUK_USE_BUILTIN_INITJS */ #endif /* !DUK_SINGLE_FILE */ -#define DUK_BUILTINS_DATA_LENGTH 1952 +#define DUK_BUILTINS_DATA_LENGTH 1955 #ifdef DUK_USE_BUILTIN_INITJS #define DUK_BUILTIN_INITJS_DATA_LENGTH 187 #endif /* DUK_USE_BUILTIN_INITJS */ @@ -4374,7 +4403,7 @@ DUK_INTERNAL_DECL const duk_uint8_t duk_initjs_data[187]; #error invalid endianness defines #endif #endif /* DUK_BUILTINS_H_INCLUDED */ -#line 50 "duk_internal.h" +#line 51 "duk_internal.h" #line 1 "duk_util.h" /* @@ -8202,7 +8231,7 @@ DUK_INTERNAL_DECL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).a.set = (v); \ } while (0) #define DUK_HOBJECT_E_SET_FLAGS(heap,h,i,f) do { \ - DUK_HOBJECT_E_GET_FLAGS((heap), (h), (i)) = (f); \ + DUK_HOBJECT_E_GET_FLAGS((heap), (h), (i)) = (duk_uint8_t) (f); \ } while (0) #define DUK_HOBJECT_A_SET_VALUE(heap,h,i,v) do { \ DUK_HOBJECT_A_GET_VALUE((heap), (h), (i)) = (v); \ @@ -10033,7 +10062,9 @@ struct duk_strcache { */ struct duk_ljstate { +#if !defined(DUK_USE_CPP_EXCEPTIONS) duk_jmpbuf *jmpbuf_ptr; /* current setjmp() catchpoint */ +#endif duk_small_uint_t type; /* longjmp type */ duk_bool_t iserror; /* isError flag for yield */ duk_tval value1; /* 1st related value (type specific) */ @@ -11272,12 +11303,11 @@ typedef struct { #define DUK_JS_H_INCLUDED /* Flags for call handling. */ -#define DUK_CALL_FLAG_PROTECTED (1 << 0) /* duk_handle_call: call is protected */ -#define DUK_CALL_FLAG_IGNORE_RECLIMIT (1 << 1) /* duk_handle_call: call ignores C recursion limit (for errhandler calls) */ -#define DUK_CALL_FLAG_CONSTRUCTOR_CALL (1 << 2) /* duk_handle_call: constructor call (i.e. called as 'new Foo()') */ -#define DUK_CALL_FLAG_IS_RESUME (1 << 3) /* duk_handle_ecma_call_setup: setup for a resume() */ -#define DUK_CALL_FLAG_IS_TAILCALL (1 << 4) /* duk_handle_ecma_call_setup: setup for a tail call */ -#define DUK_CALL_FLAG_DIRECT_EVAL (1 << 5) /* call is a direct eval call */ +#define DUK_CALL_FLAG_IGNORE_RECLIMIT (1 << 0) /* duk_handle_call_xxx: call ignores C recursion limit (for errhandler calls) */ +#define DUK_CALL_FLAG_CONSTRUCTOR_CALL (1 << 1) /* duk_handle_call_xxx: constructor call (i.e. called as 'new Foo()') */ +#define DUK_CALL_FLAG_IS_RESUME (1 << 2) /* duk_handle_ecma_call_setup: setup for a resume() */ +#define DUK_CALL_FLAG_IS_TAILCALL (1 << 3) /* duk_handle_ecma_call_setup: setup for a tail call */ +#define DUK_CALL_FLAG_DIRECT_EVAL (1 << 4) /* call is a direct eval call */ /* Flags for duk_js_equals_helper(). */ #define DUK_EQUALS_FLAG_SAMEVALUE (1 << 0) /* use SameValue instead of non-strict equality */ @@ -11354,7 +11384,8 @@ void duk_js_push_closure(duk_hthread *thr, duk_hobject *outer_lex_env); /* call handling */ -DUK_INTERNAL_DECL duk_int_t duk_handle_call(duk_hthread *thr, duk_idx_t num_stack_args, duk_small_uint_t call_flags); +DUK_INTERNAL_DECL duk_int_t duk_handle_call_protected(duk_hthread *thr, duk_idx_t num_stack_args, duk_small_uint_t call_flags); +DUK_INTERNAL_DECL void duk_handle_call_unprotected(duk_hthread *thr, duk_idx_t num_stack_args, duk_small_uint_t call_flags); DUK_INTERNAL_DECL duk_int_t duk_handle_safe_call(duk_hthread *thr, duk_safe_call_function func, duk_idx_t num_stack_args, duk_idx_t num_stack_res); DUK_INTERNAL_DECL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, duk_idx_t num_stack_args, duk_small_uint_t call_flags); @@ -11710,7 +11741,7 @@ DUK_INTERNAL_DECL void duk_selftest_run_tests(void); #endif #endif /* DUK_SELFTEST_H_INCLUDED */ -#line 77 "duk_internal.h" +#line 78 "duk_internal.h" #endif /* DUK_INTERNAL_H_INCLUDED */ #line 1 "duk_strings.c" @@ -12279,7 +12310,7 @@ DUK_INTERNAL const duk_c_function duk_bi_native_functions[149] = { duk_bi_typedarray_set, }; -DUK_INTERNAL const duk_uint8_t duk_builtins_data[1952] = { +DUK_INTERNAL const duk_uint8_t duk_builtins_data[1955] = { 105,195,75,32,121,40,105,53,14,252,104,54,8,131,72,0,115,225,65,165,244,55, 243,6,145,32,210,24,210,186,25,249,35,120,216,99,226,13,79,33,116,177,164, 180,44,192,4,202,52,150,220,24,0,169,70,146,219,123,0,23,40,210,91,110,96, @@ -12321,50 +12352,50 @@ DUK_INTERNAL const duk_uint8_t duk_builtins_data[1952] = { 97,236,66,80,1,151,169,10,248,0,211,208,133,124,0,109,230,66,254,0,56,242, 33,127,0,29,120,144,207,128,15,60,8,103,192,7,221,228,37,0,32,119,16,148,0, 133,218,66,190,0,68,236,33,95,0,35,117,144,191,128,18,58,136,95,192,9,92, -195,225,0,38,114,144,148,0,156,41,31,224,0,15,249,1,138,144,64,192,2,2,225, -132,221,9,70,112,70,111,198,111,72,0,0,0,0,0,0,0,0,13,198,244,71,19,217,40, -239,128,10,79,248,0,3,254,72,86,209,5,155,36,17,46,185,145,153,109,203,139, -203,78,96,98,13,28,208,1,74,255,0,2,127,202,4,218,43,131,100,130,32,5,47, -252,0,9,255,44,19,104,173,237,146,8,128,20,207,240,0,39,252,192,77,162,183, -54,72,34,0,83,127,192,0,159,243,65,54,138,218,217,32,136,1,78,255,0,2,127, -206,4,218,43,99,100,130,32,5,63,252,0,9,255,60,19,104,173,109,146,8,128,15, -255,242,27,16,26,85,197,34,194,175,193,80,26,240,5,149,109,110,236,90,192, -144,26,208,59,206,126,191,144,139,185,143,218,176,63,160,138,217,81,197, -125,207,218,144,3,185,73,133,94,242,246,207,218,112,6,11,81,21,62,200,66, -80,26,80,51,78,223,217,167,168,57,143,218,48,51,78,223,217,167,168,61,143, -210,104,40,17,158,160,80,0,22,114,129,64,0,153,170,5,0,3,102,41,33,150,160, -80,0,70,82,129,64,1,89,42,5,0,6,100,40,20,0,29,142,160,80,0,134,50,114,243, -21,61,121,136,164,144,0,22,26,136,24,74,5,0,9,96,168,20,0,41,128,160,80,0, -181,250,129,64,3,1,255,254,0,81,20,132,47,146,88,23,255,240,0,11,255,248,0, -3,255,252,81,252,0,0,0,0,8,4,252,68,0,129,167,1,26,144,9,165,0,26,177,199, -197,132,30,147,16,120,86,65,217,80,240,232,164,120,114,80,60,52,39,32,84, -223,192,15,59,34,129,156,131,6,81,224,7,253,40,0,5,81,252,0,1,255,78,0,84, -113,96,128,0,209,69,128,21,87,240,0,7,253,72,1,81,221,194,0,3,69,119,0,85, -159,192,0,31,245,97,10,100,0,0,0,0,0,0,0,32,10,164,130,97,221,191,113,3,20, -178,12,19,72,47,76,30,23,38,15,128,0,143,147,7,192,0,133,169,131,224,0,98, -196,193,240,0,65,90,96,248,0,41,63,255,194,109,65,11,137,191,174,45,153,98, -242,229,191,147,102,8,190,94,92,183,242,65,167,114,12,188,185,111,228,131, -70,29,217,54,105,221,156,0,171,255,128,9,208,68,128,255,174,0,25,168,194, -64,0,130,177,254,0,0,255,176,1,3,120,186,64,12,13,194,233,0,32,54,139,164, -0,196,216,46,144,2,19,88,186,64,12,141,66,233,0,34,52,139,164,0,140,208,46, -144,2,67,56,203,64,12,12,195,45,0,32,50,140,180,0,196,200,50,208,2,19,24, -203,64,12,140,67,45,0,34,48,140,180,0,140,192,50,208,2,64,127,255,128,21, -38,81,7,1,132,128,0,133,105,252,19,140,0,0,0,0,0,0,15,3,240,25,127,102,0,1, -91,127,4,227,0,0,0,0,0,0,3,192,252,6,95,218,128,0,87,31,193,56,192,0,0,0,0, -0,0,240,63,1,151,246,224,0,21,215,240,78,48,0,0,0,0,0,0,0,16,0,101,253,200, -0,5,121,252,19,140,0,0,0,0,0,0,0,4,0,25,127,118,0,1,95,127,4,227,0,0,0,0,0, -0,0,65,0,6,95,222,128,0,88,31,193,56,192,0,0,0,0,0,0,16,64,1,151,247,224,0, -22,23,240,78,48,0,0,0,0,0,0,4,16,0,101,254,8,0,5,137,252,19,140,0,0,0,0,0, -0,2,4,0,25,127,134,0,1,99,127,0,89,218,162,20,75,36,80,172,17,64,166,132, -248,162,64,0,193,255,138,5,137,161,116,38,69,210,0,32,152,23,72,0,10,92,93, -32,1,41,97,116,128,8,165,69,210,0,50,148,23,72,0,18,76,93,32,1,73,33,116, -128,9,36,69,210,0,52,144,23,72,0,26,60,93,32,1,104,225,116,128,2,35,69,210, -0,24,140,23,104,0,42,44,93,160,1,168,161,118,128,10,162,69,218,0,58,136,25, -98,28,101,160,2,8,97,150,128,0,161,70,90,0,18,132,25,104,0,138,12,101,160, -3,40,33,150,128,1,32,70,90,0,20,128,25,104,0,145,252,101,160,3,71,225,150, -128,1,159,70,90,0,22,124,25,104,0,33,236,101,160,1,135,161,152,128,2,158, -70,98,0,26,120,25,136,0,169,220,102,32,3,180,117,182,57,214,128,157,87,98, -112,80,137,241,66,128,0,166,213,161,53,24,66,121,114,0, +195,225,0,38,114,144,148,0,156,41,31,224,0,15,249,1,138,144,65,192,2,22,0, +88,16,46,24,77,208,148,103,4,102,252,102,244,128,0,0,0,0,0,0,0,0,220,111, +68,113,61,146,142,248,0,164,255,128,0,63,228,133,109,16,89,178,65,18,235, +153,25,150,220,184,188,180,230,6,32,209,205,0,20,175,240,0,39,252,160,77, +162,184,54,72,34,0,82,255,192,0,159,242,193,54,138,222,217,32,136,1,76,255, +0,2,127,204,4,218,43,115,100,130,32,5,55,252,0,9,255,52,19,104,173,173,146, +8,128,20,239,240,0,39,252,224,77,162,182,54,72,34,0,83,255,192,0,159,243, +193,54,138,214,217,32,136,0,255,255,33,177,1,165,92,82,44,42,252,21,1,175, +0,89,86,214,238,197,172,9,1,173,3,188,231,235,249,8,187,152,253,171,3,250, +8,173,149,28,87,220,253,169,0,59,148,152,85,239,47,108,253,167,0,96,181,17, +83,236,132,37,1,165,3,52,237,253,154,122,131,152,253,163,3,52,237,253,154, +122,131,216,253,38,130,129,25,234,5,0,1,103,40,20,0,9,154,160,80,0,54,98, +146,25,106,5,0,4,101,40,20,0,21,146,160,80,0,102,66,129,64,1,216,234,5,0,8, +99,39,47,49,83,215,152,138,73,0,1,97,168,129,132,160,80,0,150,10,129,64,2, +152,10,5,0,11,95,168,20,0,48,31,255,224,5,17,72,66,249,37,129,127,255,0,0, +191,255,128,0,63,255,197,31,192,0,0,0,0,0,80,196,64,8,26,112,17,169,0,154, +80,1,171,28,124,88,65,233,49,7,133,100,29,149,15,14,138,71,135,37,3,195,66, +114,5,77,252,0,243,178,40,25,200,48,101,30,0,127,210,128,0,85,31,192,0,31, +244,224,5,71,22,8,0,13,20,88,1,85,127,0,0,127,212,128,21,29,220,32,0,52,87, +112,5,89,252,0,1,255,86,16,166,64,0,0,0,0,0,0,2,0,170,72,38,29,219,247,16, +49,75,32,193,52,130,244,193,225,114,96,248,0,8,249,48,124,0,8,90,152,62,0, +6,44,76,31,0,4,21,166,15,128,2,147,255,252,38,212,16,184,155,250,226,217, +150,47,46,91,249,54,96,139,229,229,203,127,36,26,119,32,203,203,150,254,72, +52,97,221,147,102,157,217,192,10,191,248,0,157,4,72,15,250,224,1,154,140, +36,0,8,43,31,224,0,15,251,0,16,55,139,164,0,192,220,46,144,2,3,104,186,64, +12,77,130,233,0,33,53,139,164,0,200,212,46,144,2,35,72,186,64,8,205,2,233, +0,36,51,140,180,0,192,204,50,208,2,3,40,203,64,12,76,131,45,0,33,49,140, +180,0,200,196,50,208,2,35,8,203,64,8,204,3,45,0,36,7,255,248,1,82,101,16, +112,24,72,0,8,86,159,193,56,192,0,0,0,0,0,0,240,63,1,151,246,96,0,21,183, +240,78,48,0,0,0,0,0,0,60,15,192,101,253,168,0,5,113,252,19,140,0,0,0,0,0,0, +15,3,240,25,127,110,0,1,93,127,4,227,0,0,0,0,0,0,0,1,0,6,95,220,128,0,87, +159,193,56,192,0,0,0,0,0,0,0,64,1,151,247,96,0,21,247,240,78,48,0,0,0,0,0, +0,4,16,0,101,253,232,0,5,129,252,19,140,0,0,0,0,0,0,1,4,0,25,127,126,0,1, +97,127,4,227,0,0,0,0,0,0,0,65,0,6,95,224,128,0,88,159,193,56,192,0,0,0,0,0, +0,32,64,1,151,248,96,0,22,55,240,5,157,170,33,68,178,69,10,193,20,10,104, +79,138,36,0,12,31,248,160,88,154,23,66,100,93,32,2,9,129,116,128,0,165,197, +210,0,18,150,23,72,0,138,84,93,32,3,41,65,116,128,1,36,197,210,0,20,146,23, +72,0,146,68,93,32,3,73,1,116,128,1,163,197,210,0,22,142,23,72,0,34,52,93, +32,1,136,193,118,128,2,162,197,218,0,26,138,23,104,0,170,36,93,160,3,168, +129,150,33,198,90,0,32,134,25,104,0,10,20,101,160,1,40,65,150,128,8,160, +198,90,0,50,130,25,104,0,18,4,101,160,1,72,1,150,128,9,31,198,90,0,52,126, +25,104,0,25,244,101,160,1,103,193,150,128,2,30,198,90,0,24,122,25,136,0,41, +228,102,32,1,167,129,152,128,10,157,198,98,0,59,71,91,99,157,104,9,213,118, +39,5,8,159,20,40,0,10,109,90,19,81,132,39,151,32, }; #ifdef DUK_USE_BUILTIN_INITJS DUK_INTERNAL const duk_uint8_t duk_initjs_data[187] = { @@ -12666,7 +12697,7 @@ DUK_INTERNAL const duk_c_function duk_bi_native_functions[149] = { duk_bi_typedarray_set, }; -DUK_INTERNAL const duk_uint8_t duk_builtins_data[1952] = { +DUK_INTERNAL const duk_uint8_t duk_builtins_data[1955] = { 105,195,75,32,121,40,105,53,14,252,104,54,8,131,72,0,115,225,65,165,244,55, 243,6,145,32,210,24,210,186,25,249,35,120,216,99,226,13,79,33,116,177,164, 180,44,192,4,202,52,150,220,24,0,169,70,146,219,123,0,23,40,210,91,110,96, @@ -12708,50 +12739,50 @@ DUK_INTERNAL const duk_uint8_t duk_builtins_data[1952] = { 97,236,66,80,1,151,169,10,248,0,211,208,133,124,0,109,230,66,254,0,56,242, 33,127,0,29,120,144,207,128,15,60,8,103,192,7,221,228,37,0,32,119,16,148,0, 133,218,66,190,0,68,236,33,95,0,35,117,144,191,128,18,58,136,95,192,9,92, -195,225,0,38,114,144,148,0,156,41,31,224,0,15,249,1,138,144,64,192,2,2,225, -132,221,9,70,112,70,111,198,111,72,0,0,0,0,0,0,0,0,13,198,244,71,19,217,40, -239,128,10,79,248,0,3,254,72,86,209,5,155,36,17,46,185,145,153,109,203,139, -203,78,96,98,13,28,208,1,74,255,0,2,127,202,4,218,43,131,100,130,32,5,47, -252,0,9,255,44,19,104,173,237,146,8,128,20,207,240,0,39,252,192,77,162,183, -54,72,34,0,83,127,192,0,159,243,65,54,138,218,217,32,136,1,78,255,0,2,127, -206,4,218,43,99,100,130,32,5,63,252,0,9,255,60,19,104,173,109,146,8,128,15, -255,242,27,16,16,1,111,194,162,197,21,218,90,240,16,0,154,236,110,237,85, -69,154,208,15,249,139,144,191,190,142,123,218,176,15,253,197,81,217,74,224, -191,154,144,15,246,242,222,197,73,185,67,154,112,16,2,72,126,213,17,11,70, -26,80,15,249,168,39,153,159,206,243,90,48,15,253,168,39,153,159,206,243,82, -104,40,17,158,160,80,0,22,114,129,64,0,153,170,5,0,3,102,41,33,150,160,80, -0,70,82,129,64,1,89,42,5,0,6,100,40,20,0,29,142,160,80,0,134,50,114,243,21, -61,121,136,164,144,0,22,26,136,24,74,5,0,9,96,168,20,0,41,128,160,80,0,181, -250,129,64,3,1,255,254,0,81,20,132,47,146,88,23,255,240,0,11,255,248,0,3, -255,252,81,252,4,12,68,248,0,0,0,0,0,129,167,1,26,144,9,165,0,26,177,199, -197,132,30,147,16,120,86,65,217,80,240,232,164,120,114,80,60,52,39,32,84, -223,192,15,59,34,129,156,131,6,81,224,7,253,40,0,5,81,252,0,1,255,78,0,84, -113,96,128,0,209,69,128,21,87,240,0,7,253,72,1,81,221,194,0,3,69,119,0,85, -159,192,0,31,245,97,10,100,32,0,0,0,0,0,0,0,10,164,130,97,221,191,113,3,20, -178,12,19,72,47,76,30,23,38,15,128,0,143,147,7,192,0,133,169,131,224,0,98, -196,193,240,0,65,90,96,248,0,41,63,255,194,109,65,11,137,191,174,45,153,98, -242,229,191,147,102,8,190,94,92,183,242,65,167,114,12,188,185,111,228,131, -70,29,217,54,105,221,156,0,171,255,128,9,208,68,128,255,174,0,25,168,194, -64,0,130,177,254,0,0,255,176,1,3,120,186,64,12,13,194,233,0,32,54,139,164, -0,196,216,46,144,2,19,88,186,64,12,141,66,233,0,34,52,139,164,0,140,208,46, -144,2,67,56,203,64,12,12,195,45,0,32,50,140,180,0,196,200,50,208,2,19,24, -203,64,12,140,67,45,0,34,48,140,180,0,140,192,50,208,2,64,127,255,128,21, -38,81,7,1,132,128,0,133,105,252,19,140,3,255,0,0,0,0,0,0,0,25,127,102,0,1, -91,127,4,227,0,255,192,0,0,0,0,0,0,6,95,218,128,0,87,31,193,56,192,63,240, -0,0,0,0,0,0,1,151,246,224,0,21,215,240,78,48,16,0,0,0,0,0,0,0,0,101,253, -200,0,5,121,252,19,140,4,0,0,0,0,0,0,0,0,25,127,118,0,1,95,127,4,227,1,0, -64,0,0,0,0,0,0,6,95,222,128,0,88,31,193,56,192,64,16,0,0,0,0,0,0,1,151,247, -224,0,22,23,240,78,48,16,4,0,0,0,0,0,0,0,101,254,8,0,5,137,252,19,140,4,2, -0,0,0,0,0,0,0,25,127,134,0,1,99,127,0,89,218,162,20,75,36,80,172,17,64,166, -132,248,162,64,0,193,255,138,5,137,161,116,38,69,210,0,32,152,23,72,0,10, -92,93,32,1,41,97,116,128,8,165,69,210,0,50,148,23,72,0,18,76,93,32,1,73,33, -116,128,9,36,69,210,0,52,144,23,72,0,26,60,93,32,1,104,225,116,128,2,35,69, -210,0,24,140,23,104,0,42,44,93,160,1,168,161,118,128,10,162,69,218,0,58, -136,25,98,28,101,160,2,8,97,150,128,0,161,70,90,0,18,132,25,104,0,138,12, -101,160,3,40,33,150,128,1,32,70,90,0,20,128,25,104,0,145,252,101,160,3,71, -225,150,128,1,159,70,90,0,22,124,25,104,0,33,236,101,160,1,135,161,152,128, -2,158,70,98,0,26,120,25,136,0,169,220,102,32,3,180,117,182,57,214,128,157, -87,98,112,80,137,241,66,128,0,166,213,161,53,24,66,121,114,0, +195,225,0,38,114,144,148,0,156,41,31,224,0,15,249,1,138,144,65,192,2,22,0, +88,16,46,24,77,208,148,103,4,102,252,102,244,128,0,0,0,0,0,0,0,0,220,111, +68,113,61,146,142,248,0,164,255,128,0,63,228,133,109,16,89,178,65,18,235, +153,25,150,220,184,188,180,230,6,32,209,205,0,20,175,240,0,39,252,160,77, +162,184,54,72,34,0,82,255,192,0,159,242,193,54,138,222,217,32,136,1,76,255, +0,2,127,204,4,218,43,115,100,130,32,5,55,252,0,9,255,52,19,104,173,173,146, +8,128,20,239,240,0,39,252,224,77,162,182,54,72,34,0,83,255,192,0,159,243, +193,54,138,214,217,32,136,0,255,255,33,177,1,0,22,252,42,44,81,93,165,175, +1,0,9,174,198,238,213,84,89,173,0,255,152,185,11,251,232,231,189,171,0,255, +220,85,29,148,174,11,249,169,0,255,111,45,236,84,155,148,57,167,1,0,36,135, +237,81,16,180,97,165,0,255,154,130,121,153,252,239,53,163,0,255,218,130, +121,153,252,239,53,38,130,129,25,234,5,0,1,103,40,20,0,9,154,160,80,0,54, +98,146,25,106,5,0,4,101,40,20,0,21,146,160,80,0,102,66,129,64,1,216,234,5, +0,8,99,39,47,49,83,215,152,138,73,0,1,97,168,129,132,160,80,0,150,10,129, +64,2,152,10,5,0,11,95,168,20,0,48,31,255,224,5,17,72,66,249,37,129,127,255, +0,0,191,255,128,0,63,255,197,31,192,64,196,80,0,0,0,0,0,8,26,112,17,169,0, +154,80,1,171,28,124,88,65,233,49,7,133,100,29,149,15,14,138,71,135,37,3, +195,66,114,5,77,252,0,243,178,40,25,200,48,101,30,0,127,210,128,0,85,31, +192,0,31,244,224,5,71,22,8,0,13,20,88,1,85,127,0,0,127,212,128,21,29,220, +32,0,52,87,112,5,89,252,0,1,255,86,16,166,66,0,0,0,0,0,0,0,0,170,72,38,29, +219,247,16,49,75,32,193,52,130,244,193,225,114,96,248,0,8,249,48,124,0,8, +90,152,62,0,6,44,76,31,0,4,21,166,15,128,2,147,255,252,38,212,16,184,155, +250,226,217,150,47,46,91,249,54,96,139,229,229,203,127,36,26,119,32,203, +203,150,254,72,52,97,221,147,102,157,217,192,10,191,248,0,157,4,72,15,250, +224,1,154,140,36,0,8,43,31,224,0,15,251,0,16,55,139,164,0,192,220,46,144,2, +3,104,186,64,12,77,130,233,0,33,53,139,164,0,200,212,46,144,2,35,72,186,64, +8,205,2,233,0,36,51,140,180,0,192,204,50,208,2,3,40,203,64,12,76,131,45,0, +33,49,140,180,0,200,196,50,208,2,35,8,203,64,8,204,3,45,0,36,7,255,248,1, +82,101,16,112,24,72,0,8,86,159,193,56,192,63,240,0,0,0,0,0,0,1,151,246,96, +0,21,183,240,78,48,15,252,0,0,0,0,0,0,0,101,253,168,0,5,113,252,19,140,3, +255,0,0,0,0,0,0,0,25,127,110,0,1,93,127,4,227,1,0,0,0,0,0,0,0,0,6,95,220, +128,0,87,159,193,56,192,64,0,0,0,0,0,0,0,1,151,247,96,0,21,247,240,78,48, +16,4,0,0,0,0,0,0,0,101,253,232,0,5,129,252,19,140,4,1,0,0,0,0,0,0,0,25,127, +126,0,1,97,127,4,227,1,0,64,0,0,0,0,0,0,6,95,224,128,0,88,159,193,56,192, +64,32,0,0,0,0,0,0,1,151,248,96,0,22,55,240,5,157,170,33,68,178,69,10,193, +20,10,104,79,138,36,0,12,31,248,160,88,154,23,66,100,93,32,2,9,129,116,128, +0,165,197,210,0,18,150,23,72,0,138,84,93,32,3,41,65,116,128,1,36,197,210,0, +20,146,23,72,0,146,68,93,32,3,73,1,116,128,1,163,197,210,0,22,142,23,72,0, +34,52,93,32,1,136,193,118,128,2,162,197,218,0,26,138,23,104,0,170,36,93, +160,3,168,129,150,33,198,90,0,32,134,25,104,0,10,20,101,160,1,40,65,150, +128,8,160,198,90,0,50,130,25,104,0,18,4,101,160,1,72,1,150,128,9,31,198,90, +0,52,126,25,104,0,25,244,101,160,1,103,193,150,128,2,30,198,90,0,24,122,25, +136,0,41,228,102,32,1,167,129,152,128,10,157,198,98,0,59,71,91,99,157,104, +9,213,118,39,5,8,159,20,40,0,10,109,90,19,81,132,39,151,32, }; #ifdef DUK_USE_BUILTIN_INITJS DUK_INTERNAL const duk_uint8_t duk_initjs_data[187] = { @@ -13053,7 +13084,7 @@ DUK_INTERNAL const duk_c_function duk_bi_native_functions[149] = { duk_bi_typedarray_set, }; -DUK_INTERNAL const duk_uint8_t duk_builtins_data[1952] = { +DUK_INTERNAL const duk_uint8_t duk_builtins_data[1955] = { 105,195,75,32,121,40,105,53,14,252,104,54,8,131,72,0,115,225,65,165,244,55, 243,6,145,32,210,24,210,186,25,249,35,120,216,99,226,13,79,33,116,177,164, 180,44,192,4,202,52,150,220,24,0,169,70,146,219,123,0,23,40,210,91,110,96, @@ -13095,50 +13126,50 @@ DUK_INTERNAL const duk_uint8_t duk_builtins_data[1952] = { 97,236,66,80,1,151,169,10,248,0,211,208,133,124,0,109,230,66,254,0,56,242, 33,127,0,29,120,144,207,128,15,60,8,103,192,7,221,228,37,0,32,119,16,148,0, 133,218,66,190,0,68,236,33,95,0,35,117,144,191,128,18,58,136,95,192,9,92, -195,225,0,38,114,144,148,0,156,41,31,224,0,15,249,1,138,144,64,192,2,2,225, -132,221,9,70,112,70,111,198,111,72,0,0,0,0,0,0,0,0,13,198,244,71,19,217,40, -239,128,10,79,248,0,3,254,72,86,209,5,155,36,17,46,185,145,153,109,203,139, -203,78,96,98,13,28,208,1,74,255,0,2,127,202,4,218,43,131,100,130,32,5,47, -252,0,9,255,44,19,104,173,237,146,8,128,20,207,240,0,39,252,192,77,162,183, -54,72,34,0,83,127,192,0,159,243,65,54,138,218,217,32,136,1,78,255,0,2,127, -206,4,218,43,99,100,130,32,5,63,252,0,9,255,60,19,104,173,109,146,8,128,15, -255,242,27,16,2,175,193,80,26,85,197,34,218,240,44,90,192,144,5,149,109, -110,218,208,16,139,185,143,251,206,126,191,154,176,17,197,125,207,255,160, -138,217,90,144,30,242,246,207,195,185,73,133,90,112,62,200,66,80,6,11,81, -21,26,80,39,168,57,143,243,78,223,217,154,48,39,168,61,143,243,78,223,217, -146,104,40,17,158,160,80,0,22,114,129,64,0,153,170,5,0,3,102,41,33,150,160, -80,0,70,82,129,64,1,89,42,5,0,6,100,40,20,0,29,142,160,80,0,134,50,114,243, -21,61,121,136,164,144,0,22,26,136,24,74,5,0,9,96,168,20,0,41,128,160,80,0, -181,250,129,64,3,1,255,254,0,81,20,132,47,146,88,23,255,240,0,11,255,248,0, -3,255,252,81,252,8,4,252,68,0,0,0,0,0,129,167,1,26,144,9,165,0,26,177,199, -197,132,30,147,16,120,86,65,217,80,240,232,164,120,114,80,60,52,39,32,84, -223,192,15,59,34,129,156,131,6,81,224,7,253,40,0,5,81,252,0,1,255,78,0,84, -113,96,128,0,209,69,128,21,87,240,0,7,253,72,1,81,221,194,0,3,69,119,0,85, -159,192,0,31,245,97,10,100,0,0,0,32,0,0,0,0,10,164,130,97,221,191,113,3,20, -178,12,19,72,47,76,30,23,38,15,128,0,143,147,7,192,0,133,169,131,224,0,98, -196,193,240,0,65,90,96,248,0,41,63,255,194,109,65,11,137,191,174,45,153,98, -242,229,191,147,102,8,190,94,92,183,242,65,167,114,12,188,185,111,228,131, -70,29,217,54,105,221,156,0,171,255,128,9,208,68,128,255,174,0,25,168,194, -64,0,130,177,254,0,0,255,176,1,3,120,186,64,12,13,194,233,0,32,54,139,164, -0,196,216,46,144,2,19,88,186,64,12,141,66,233,0,34,52,139,164,0,140,208,46, -144,2,67,56,203,64,12,12,195,45,0,32,50,140,180,0,196,200,50,208,2,19,24, -203,64,12,140,67,45,0,34,48,140,180,0,140,192,50,208,2,64,127,255,128,21, -38,81,7,1,132,128,0,133,105,252,19,140,0,0,15,3,240,0,0,0,0,25,127,102,0,1, -91,127,4,227,0,0,3,192,252,0,0,0,0,6,95,218,128,0,87,31,193,56,192,0,0,240, -63,0,0,0,0,1,151,246,224,0,21,215,240,78,48,0,0,0,16,0,0,0,0,0,101,253,200, -0,5,121,252,19,140,0,0,0,4,0,0,0,0,0,25,127,118,0,1,95,127,4,227,0,0,0,65, -0,0,0,0,0,6,95,222,128,0,88,31,193,56,192,0,0,16,64,0,0,0,0,1,151,247,224, -0,22,23,240,78,48,0,0,4,16,0,0,0,0,0,101,254,8,0,5,137,252,19,140,0,0,2,4, -0,0,0,0,0,25,127,134,0,1,99,127,0,89,218,162,20,75,36,80,172,17,64,166,132, -248,162,64,0,193,255,138,5,137,161,116,38,69,210,0,32,152,23,72,0,10,92,93, -32,1,41,97,116,128,8,165,69,210,0,50,148,23,72,0,18,76,93,32,1,73,33,116, -128,9,36,69,210,0,52,144,23,72,0,26,60,93,32,1,104,225,116,128,2,35,69,210, -0,24,140,23,104,0,42,44,93,160,1,168,161,118,128,10,162,69,218,0,58,136,25, -98,28,101,160,2,8,97,150,128,0,161,70,90,0,18,132,25,104,0,138,12,101,160, -3,40,33,150,128,1,32,70,90,0,20,128,25,104,0,145,252,101,160,3,71,225,150, -128,1,159,70,90,0,22,124,25,104,0,33,236,101,160,1,135,161,152,128,2,158, -70,98,0,26,120,25,136,0,169,220,102,32,3,180,117,182,57,214,128,157,87,98, -112,80,137,241,66,128,0,166,213,161,53,24,66,121,114,0, +195,225,0,38,114,144,148,0,156,41,31,224,0,15,249,1,138,144,65,192,2,22,0, +88,16,46,24,77,208,148,103,4,102,252,102,244,128,0,0,0,0,0,0,0,0,220,111, +68,113,61,146,142,248,0,164,255,128,0,63,228,133,109,16,89,178,65,18,235, +153,25,150,220,184,188,180,230,6,32,209,205,0,20,175,240,0,39,252,160,77, +162,184,54,72,34,0,82,255,192,0,159,242,193,54,138,222,217,32,136,1,76,255, +0,2,127,204,4,218,43,115,100,130,32,5,55,252,0,9,255,52,19,104,173,173,146, +8,128,20,239,240,0,39,252,224,77,162,182,54,72,34,0,83,255,192,0,159,243, +193,54,138,214,217,32,136,0,255,255,33,177,0,42,252,21,1,165,92,82,45,175, +2,197,172,9,0,89,86,214,237,173,1,8,187,152,255,188,231,235,249,171,1,28, +87,220,255,250,8,173,149,169,1,239,47,108,252,59,148,152,85,167,3,236,132, +37,0,96,181,17,81,165,2,122,131,152,255,52,237,253,153,163,2,122,131,216, +255,52,237,253,153,38,130,129,25,234,5,0,1,103,40,20,0,9,154,160,80,0,54, +98,146,25,106,5,0,4,101,40,20,0,21,146,160,80,0,102,66,129,64,1,216,234,5, +0,8,99,39,47,49,83,215,152,138,73,0,1,97,168,129,132,160,80,0,150,10,129, +64,2,152,10,5,0,11,95,168,20,0,48,31,255,224,5,17,72,66,249,37,129,127,255, +0,0,191,255,128,0,63,255,197,31,192,0,80,196,64,0,0,0,0,8,26,112,17,169,0, +154,80,1,171,28,124,88,65,233,49,7,133,100,29,149,15,14,138,71,135,37,3, +195,66,114,5,77,252,0,243,178,40,25,200,48,101,30,0,127,210,128,0,85,31, +192,0,31,244,224,5,71,22,8,0,13,20,88,1,85,127,0,0,127,212,128,21,29,220, +32,0,52,87,112,5,89,252,0,1,255,86,16,166,64,0,0,2,0,0,0,0,0,170,72,38,29, +219,247,16,49,75,32,193,52,130,244,193,225,114,96,248,0,8,249,48,124,0,8, +90,152,62,0,6,44,76,31,0,4,21,166,15,128,2,147,255,252,38,212,16,184,155, +250,226,217,150,47,46,91,249,54,96,139,229,229,203,127,36,26,119,32,203, +203,150,254,72,52,97,221,147,102,157,217,192,10,191,248,0,157,4,72,15,250, +224,1,154,140,36,0,8,43,31,224,0,15,251,0,16,55,139,164,0,192,220,46,144,2, +3,104,186,64,12,77,130,233,0,33,53,139,164,0,200,212,46,144,2,35,72,186,64, +8,205,2,233,0,36,51,140,180,0,192,204,50,208,2,3,40,203,64,12,76,131,45,0, +33,49,140,180,0,200,196,50,208,2,35,8,203,64,8,204,3,45,0,36,7,255,248,1, +82,101,16,112,24,72,0,8,86,159,193,56,192,0,0,240,63,0,0,0,0,1,151,246,96, +0,21,183,240,78,48,0,0,60,15,192,0,0,0,0,101,253,168,0,5,113,252,19,140,0, +0,15,3,240,0,0,0,0,25,127,110,0,1,93,127,4,227,0,0,0,1,0,0,0,0,0,6,95,220, +128,0,87,159,193,56,192,0,0,0,64,0,0,0,0,1,151,247,96,0,21,247,240,78,48,0, +0,4,16,0,0,0,0,0,101,253,232,0,5,129,252,19,140,0,0,1,4,0,0,0,0,0,25,127, +126,0,1,97,127,4,227,0,0,0,65,0,0,0,0,0,6,95,224,128,0,88,159,193,56,192,0, +0,32,64,0,0,0,0,1,151,248,96,0,22,55,240,5,157,170,33,68,178,69,10,193,20, +10,104,79,138,36,0,12,31,248,160,88,154,23,66,100,93,32,2,9,129,116,128,0, +165,197,210,0,18,150,23,72,0,138,84,93,32,3,41,65,116,128,1,36,197,210,0, +20,146,23,72,0,146,68,93,32,3,73,1,116,128,1,163,197,210,0,22,142,23,72,0, +34,52,93,32,1,136,193,118,128,2,162,197,218,0,26,138,23,104,0,170,36,93, +160,3,168,129,150,33,198,90,0,32,134,25,104,0,10,20,101,160,1,40,65,150, +128,8,160,198,90,0,50,130,25,104,0,18,4,101,160,1,72,1,150,128,9,31,198,90, +0,52,126,25,104,0,25,244,101,160,1,103,193,150,128,2,30,198,90,0,24,122,25, +136,0,41,228,102,32,1,167,129,152,128,10,157,198,98,0,59,71,91,99,157,104, +9,213,118,39,5,8,159,20,40,0,10,109,90,19,81,132,39,151,32, }; #ifdef DUK_USE_BUILTIN_INITJS DUK_INTERNAL const duk_uint8_t duk_initjs_data[187] = { @@ -15778,7 +15809,6 @@ DUK_EXTERNAL void duk_call(duk_context *ctx, duk_idx_t nargs) { duk_hthread *thr = (duk_hthread *) ctx; duk_small_uint_t call_flags; duk_idx_t idx_func; - duk_int_t rc; DUK_ASSERT_CTX_VALID(ctx); DUK_ASSERT(thr != NULL); @@ -15797,17 +15827,15 @@ DUK_EXTERNAL void duk_call(duk_context *ctx, duk_idx_t nargs) { call_flags = 0; /* not protected, respect reclimit, not constructor */ - rc = duk_handle_call(thr, /* thread */ - nargs, /* num_stack_args */ - call_flags); /* call_flags */ - DUK_UNREF(rc); + duk_handle_call_unprotected(thr, /* thread */ + nargs, /* num_stack_args */ + call_flags); /* call_flags */ } DUK_EXTERNAL void duk_call_method(duk_context *ctx, duk_idx_t nargs) { duk_hthread *thr = (duk_hthread *) ctx; duk_small_uint_t call_flags; duk_idx_t idx_func; - duk_int_t rc; DUK_ASSERT_CTX_VALID(ctx); DUK_ASSERT(thr != NULL); @@ -15820,10 +15848,9 @@ DUK_EXTERNAL void duk_call_method(duk_context *ctx, duk_idx_t nargs) { call_flags = 0; /* not protected, respect reclimit, not constructor */ - rc = duk_handle_call(thr, /* thread */ - nargs, /* num_stack_args */ - call_flags); /* call_flags */ - DUK_UNREF(rc); + duk_handle_call_unprotected(thr, /* thread */ + nargs, /* num_stack_args */ + call_flags); /* call_flags */ } DUK_EXTERNAL void duk_call_prop(duk_context *ctx, duk_idx_t obj_index, duk_idx_t nargs) { @@ -15872,11 +15899,11 @@ DUK_EXTERNAL duk_int_t duk_pcall(duk_context *ctx, duk_idx_t nargs) { duk_push_undefined(ctx); duk_insert(ctx, idx_func + 1); - call_flags = DUK_CALL_FLAG_PROTECTED; /* protected, respect reclimit, not constructor */ + call_flags = 0; /* respect reclimit, not constructor */ - rc = duk_handle_call(thr, /* thread */ - nargs, /* num_stack_args */ - call_flags); /* call_flags */ + rc = duk_handle_call_protected(thr, /* thread */ + nargs, /* num_stack_args */ + call_flags); /* call_flags */ return rc; } @@ -15897,11 +15924,11 @@ DUK_EXTERNAL duk_int_t duk_pcall_method(duk_context *ctx, duk_idx_t nargs) { return DUK_EXEC_ERROR; /* unreachable */ } - call_flags = DUK_CALL_FLAG_PROTECTED; /* protected, respect reclimit, not constructor */ + call_flags = 0; /* respect reclimit, not constructor */ - rc = duk_handle_call(thr, /* thread */ - nargs, /* num_stack_args */ - call_flags); /* call_flags */ + rc = duk_handle_call_protected(thr, /* thread */ + nargs, /* num_stack_args */ + call_flags); /* call_flags */ return rc; } @@ -16010,7 +16037,6 @@ DUK_EXTERNAL void duk_new(duk_context *ctx, duk_idx_t nargs) { duk_hobject *fallback; duk_idx_t idx_cons; duk_small_uint_t call_flags; - duk_int_t rc; DUK_ASSERT_CTX_VALID(ctx); @@ -16104,15 +16130,13 @@ DUK_EXTERNAL void duk_new(duk_context *ctx, duk_idx_t nargs) { call_flags = DUK_CALL_FLAG_CONSTRUCTOR_CALL; /* not protected, respect reclimit, is a constructor call */ - rc = duk_handle_call(thr, /* thread */ - nargs, /* num_stack_args */ - call_flags); /* call_flags */ - DUK_UNREF(rc); + duk_handle_call_unprotected(thr, /* thread */ + nargs, /* num_stack_args */ + call_flags); /* call_flags */ /* [... fallback retval] */ - DUK_DDD(DUK_DDDPRINT("constructor call finished, rc=%ld, fallback=%!iT, retval=%!iT", - (long) rc, + DUK_DDD(DUK_DDDPRINT("constructor call finished, fallback=%!iT, retval=%!iT", (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1))); @@ -20672,16 +20696,16 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) { * true */ duk_small_uint_t lf_flags; - duk_small_uint_t nargs; + duk_idx_t nargs; duk_small_uint_t lf_len; duk_c_function func; duk_hnativefunction *nf; DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags); - nargs = DUK_LFUNC_FLAGS_GET_NARGS(lf_flags); + nargs = (duk_idx_t) DUK_LFUNC_FLAGS_GET_NARGS(lf_flags); if (nargs == DUK_LFUNC_NARGS_VARARGS) { - nargs = DUK_VARARGS; + nargs = (duk_idx_t) DUK_VARARGS; } flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_CONSTRUCTABLE | @@ -20691,10 +20715,10 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) { DUK_HOBJECT_FLAG_NOTAIL | /* DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC: omitted here intentionally */ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION); - (void) duk__push_c_function_raw(ctx, func, (duk_idx_t) nargs, flags); + (void) duk__push_c_function_raw(ctx, func, nargs, flags); lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags); - if (lf_len != nargs) { + if ((duk_idx_t) lf_len != nargs) { /* Explicit length is only needed if it differs from 'nargs'. */ duk_push_int(ctx, (duk_int_t) lf_len); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); @@ -21889,7 +21913,7 @@ DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_context *ctx, duk_c_function fu duk_hnativefunction *obj; duk_idx_t ret; duk_tval *tv_slot; - duk_uint16_t func_nargs; + duk_int16_t func_nargs; DUK_ASSERT_CTX_VALID(ctx); @@ -21901,7 +21925,7 @@ DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_context *ctx, duk_c_function fu goto api_error; } if (nargs >= 0 && nargs < DUK_HNATIVEFUNCTION_NARGS_MAX) { - func_nargs = (duk_uint16_t) nargs; + func_nargs = (duk_int16_t) nargs; } else if (nargs == DUK_VARARGS) { func_nargs = DUK_HNATIVEFUNCTION_NARGS_VARARGS; } else { @@ -25623,8 +25647,8 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { DUK_HBUFFER_INCREF(thr, h_val); h_bufobj->offset = h_bufarg->offset + byte_offset; h_bufobj->length = byte_length; - h_bufobj->shift = shift; - h_bufobj->elem_type = elem_type; + h_bufobj->shift = (duk_uint8_t) shift; + h_bufobj->elem_type = (duk_uint8_t) elem_type; h_bufobj->is_view = 1; DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); @@ -25727,8 +25751,8 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { DUK_HBUFFER_INCREF(thr, h_val); DUK_ASSERT(h_bufobj->offset == 0); h_bufobj->length = byte_length; - h_bufobj->shift = shift; - h_bufobj->elem_type = elem_type; + h_bufobj->shift = (duk_uint8_t) shift; + h_bufobj->elem_type = (duk_uint8_t) elem_type; h_bufobj->is_view = 1; DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); @@ -31241,7 +31265,7 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { duk_small_uint_t comp_flags; duk_int_t level = -2; - DUK_ASSERT_TOP(ctx, 1); + DUK_ASSERT(duk_get_top(ctx) == 1 || duk_get_top(ctx) == 2); /* 2 when called by debugger */ DUK_ASSERT(thr->callstack_top >= 1); /* at least this function exists */ DUK_ASSERT(((thr->callstack + thr->callstack_top - 1)->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0 || /* indirect eval */ (thr->callstack_top >= 2)); /* if direct eval, calling activation must exist */ @@ -38486,7 +38510,9 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_context *ctx) { thr->heap->lj.iserror = is_error; +#if !defined(DUK_USE_CPP_EXCEPTIONS) DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* call is from executor, so we know we have a jmpbuf */ +#endif duk_err_longjmp(thr); /* execution resumes in bytecode executor */ return 0; /* never here */ @@ -38603,7 +38629,9 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_context *ctx) { thr->heap->lj.iserror = is_error; +#if !defined(DUK_USE_CPP_EXCEPTIONS) DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* call is from executor, so we know we have a jmpbuf */ +#endif duk_err_longjmp(thr); /* execution resumes in bytecode executor */ return 0; /* never here */ @@ -41388,7 +41416,7 @@ DUK_LOCAL void duk__debug_handle_eval(duk_hthread *thr, duk_heap *heap) { /* [ ... eval "eval" eval_input level ] */ - call_flags = DUK_CALL_FLAG_PROTECTED; + call_flags = 0; if (thr->callstack_top >= (duk_size_t) -level) { duk_activation *act; duk_hobject *fun; @@ -41405,7 +41433,7 @@ DUK_LOCAL void duk__debug_handle_eval(duk_hthread *thr, duk_heap *heap) { } } - call_ret = duk_handle_call(thr, 2 /*num_stack_args*/, call_flags); + call_ret = duk_handle_call_protected(thr, 2 /*num_stack_args*/, call_flags); if (call_ret == DUK_EXEC_SUCCESS) { eval_err = 0; @@ -42151,12 +42179,11 @@ DUK_LOCAL void duk__err_augment_user(duk_hthread *thr, duk_small_uint_t stridx_c DUK_ASSERT(!DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap)); /* since no recursive error handler calls */ DUK_HEAP_SET_ERRHANDLER_RUNNING(thr->heap); - call_flags = DUK_CALL_FLAG_PROTECTED | - DUK_CALL_FLAG_IGNORE_RECLIMIT; /* protected, ignore reclimit, not constructor */ + call_flags = DUK_CALL_FLAG_IGNORE_RECLIMIT; /* ignore reclimit, not constructor */ - rc = duk_handle_call(thr, - 1, /* num args */ - call_flags); /* call_flags */ + rc = duk_handle_call_protected(thr, + 1, /* num args */ + call_flags); /* call_flags */ DUK_UNREF(rc); /* no need to check now: both success and error are OK */ DUK_ASSERT(DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap)); @@ -42591,6 +42618,12 @@ DUK_INTERNAL void duk_err_augment_error_throw(duk_hthread *thr) { DUK_INTERNAL void duk_err_longjmp(duk_hthread *thr) { DUK_ASSERT(thr != NULL); +#if defined(DUK_USE_CPP_EXCEPTIONS) + /* XXX: detecting uncaught exception case for C++ case; perhaps need + * some marker in heap->lj state that a try-catch is active. For now, + * invokes C++ uncaught exception handling. + */ +#else if (!thr->heap->lj.jmpbuf_ptr) { /* * If we don't have a jmpbuf_ptr, there is little we can do @@ -42605,8 +42638,16 @@ DUK_INTERNAL void duk_err_longjmp(duk_hthread *thr) { duk_fatal((duk_context *) thr, DUK_ERR_UNCAUGHT_ERROR, "uncaught error"); DUK_UNREACHABLE(); } +#endif +#if defined(DUK_USE_CPP_EXCEPTIONS) + { + duk_internal_exception exc; /* dummy */ + throw exc; + } +#else DUK_LONGJMP(thr->heap->lj.jmpbuf_ptr->jb); +#endif DUK_UNREACHABLE(); } #line 1 "duk_error_misc.c" @@ -43731,7 +43772,9 @@ DUK_LOCAL void duk__dump_type_sizes(void) { DUK__DUMPSZ(duk_tval); /* structs from duk_forwdecl.h */ +#if !defined(DUK_USE_CPP_EXCEPTIONS) DUK__DUMPSZ(duk_jmpbuf); +#endif DUK__DUMPSZ(duk_heaphdr); DUK__DUMPSZ(duk_heaphdr_string); DUK__DUMPSZ(duk_hstring); @@ -56652,22 +56695,80 @@ DUK_INTERNAL void duk_hthread_catchstack_unwind(duk_hthread *thr, duk_size_t new /* * Call handling. * - * The main work horse functions are: - * - duk_handle_call(): call to a C/Ecmascript functions - * - duk_handle_safe_call(): make a protected C call within current activation - * - duk_handle_ecma_call_setup(): Ecmascript-to-Ecmascript calls, including - * tail calls and coroutine resume + * Main functions are: + * + * - duk_handle_call_unprotected(): unprotected call to Ecmascript or + * Duktape/C function + * - duk_handle_call_protected(): protected call to Ecmascript or + * Duktape/C function + * - duk_handle_safe_call(): make a protected C call within current + * activation + * - duk_handle_ecma_call_setup(): Ecmascript-to-Ecmascript calls + * (not always possible), including tail calls and coroutine resume + * + * See 'execution.rst'. + * + * Note: setjmp() and local variables have a nasty interaction, + * see execution.rst; non-volatile locals modified after setjmp() + * call are not guaranteed to keep their value. */ /* include removed: duk_internal.h */ /* - * Misc + * Forward declarations. + */ + +DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, + duk_idx_t num_stack_args, + duk_small_uint_t call_flags, + duk_idx_t idx_func); +DUK_LOCAL void duk__handle_call_error(duk_hthread *thr, + duk_size_t entry_valstack_bottom_index, + duk_size_t entry_valstack_end, + duk_size_t entry_catchstack_top, + duk_size_t entry_callstack_top, + duk_int_t entry_call_recursion_depth, + duk_hthread *entry_curr_thread, + duk_uint_fast8_t entry_thread_state, + duk_instr_t **entry_ptr_curr_pc, + duk_idx_t idx_func +#if !defined(DUK_USE_CPP_EXCEPTIONS) + , duk_jmpbuf *old_jmpbuf_ptr +#endif + ); +DUK_LOCAL void duk__handle_safe_call_inner(duk_hthread *thr, + duk_safe_call_function func, + duk_idx_t idx_retbase, + duk_idx_t num_stack_rets, + duk_size_t entry_valstack_bottom_index, + duk_size_t entry_callstack_top, + duk_size_t entry_catchstack_top); +DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr, + duk_idx_t idx_retbase, + duk_idx_t num_stack_rets, + duk_size_t entry_valstack_bottom_index, + duk_size_t entry_callstack_top, + duk_size_t entry_catchstack_top +#if !defined(DUK_USE_CPP_EXCEPTIONS) + , duk_jmpbuf *old_jmpbuf_ptr +#endif + ); +DUK_LOCAL void duk__handle_safe_call_shared(duk_hthread *thr, + duk_idx_t idx_retbase, + duk_idx_t num_stack_rets, + duk_int_t entry_call_recursion_depth, + duk_hthread *entry_curr_thread, + duk_uint_fast8_t entry_thread_state, + duk_instr_t **entry_ptr_curr_pc); + +/* + * Interrupt counter fixup (for development only). */ #if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG) DUK_LOCAL void duk__interrupt_fixup(duk_hthread *thr, duk_hthread *entry_curr_thread) { - /* XXX: Currently the bytecode executor and executor interrupt + /* Currently the bytecode executor and executor interrupt * instruction counts are off because we don't execute the * interrupt handler when we're about to exit from the initial * user call into Duktape. @@ -56699,18 +56800,17 @@ DUK_LOCAL void duk__interrupt_fixup(duk_hthread *thr, duk_hthread *entry_curr_th /* * Arguments object creation. * - * Creating arguments objects is a bit finicky, see E5 Section 10.6 for the - * specific requirements. Much of the arguments object exotic behavior is - * implemented in duk_hobject_props.c, and is enabled by the object flag - * DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS. + * Creating arguments objects involves many small details, see E5 Section + * 10.6 for the specific requirements. Much of the arguments object exotic + * behavior is implemented in duk_hobject_props.c, and is enabled by the + * object flag DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS. */ -DUK_LOCAL -void duk__create_arguments_object(duk_hthread *thr, - duk_hobject *func, - duk_hobject *varenv, - duk_idx_t idx_argbase, /* idx of first argument on stack */ - duk_idx_t num_stack_args) { /* num args starting from idx_argbase */ +DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr, + duk_hobject *func, + duk_hobject *varenv, + duk_idx_t idx_argbase, /* idx of first argument on stack */ + duk_idx_t num_stack_args) { /* num args starting from idx_argbase */ duk_context *ctx = (duk_context *) thr; duk_hobject *arg; /* 'arguments' */ duk_hobject *formals; /* formals for 'func' (may be NULL if func is a C function) */ @@ -56789,7 +56889,7 @@ void duk__create_arguments_object(duk_hthread *thr, -1); /* no prototype */ DUK_ASSERT(i_mappednames >= 0); - /* [... formals arguments map mappedNames] */ + /* [ ... formals arguments map mappedNames ] */ DUK_DDD(DUK_DDDPRINT("created arguments related objects: " "arguments at index %ld -> %!O " @@ -56831,14 +56931,14 @@ void duk__create_arguments_object(duk_hthread *thr, duk_get_prop_index(ctx, i_formals, idx); DUK_ASSERT(duk_is_string(ctx, -1)); - duk_dup(ctx, -1); /* [... name name] */ + duk_dup(ctx, -1); /* [ ... name name ] */ if (!duk_has_prop(ctx, i_mappednames)) { /* steps 11.c.ii.1 - 11.c.ii.4, but our internal book-keeping * differs from the reference model */ - /* [... name] */ + /* [ ... name ] */ need_map = 1; @@ -56859,7 +56959,7 @@ void duk__create_arguments_object(duk_hthread *thr, /* duk_has_prop() popped the second 'name' */ } - /* [... name] */ + /* [ ... name ] */ duk_pop(ctx); /* pop 'name' */ } @@ -56893,18 +56993,17 @@ void duk__create_arguments_object(duk_hthread *thr, /* steps 13-14 */ if (DUK_HOBJECT_HAS_STRICT(func)) { - /* - * Note: callee/caller are throwers and are not deletable etc. - * They could be implemented as virtual properties, but currently - * there is no support for virtual properties which are accessors - * (only plain virtual properties). This would not be difficult - * to change in duk_hobject_props, but we can make the throwers - * normal, concrete properties just as easily. + /* Callee/caller are throwers and are not deletable etc. They + * could be implemented as virtual properties, but currently + * there is no support for virtual properties which are accessors + * (only plain virtual properties). This would not be difficult + * to change in duk_hobject_props, but we can make the throwers + * normal, concrete properties just as easily. * - * Note that the specification requires that the *same* thrower - * built-in object is used here! See E5 Section 10.6 main - * algoritm, step 14, and Section 13.2.3 which describes the - * thrower. See test case test-arguments-throwers.js. + * Note that the specification requires that the *same* thrower + * built-in object is used here! See E5 Section 10.6 main + * algoritm, step 14, and Section 13.2.3 which describes the + * thrower. See test case test-arguments-throwers.js. */ DUK_DDD(DUK_DDDPRINT("strict function, setting caller/callee to throwers")); @@ -56919,15 +57018,14 @@ void duk__create_arguments_object(duk_hthread *thr, /* set exotic behavior only after we're done */ if (need_map) { - /* - * Note: exotic behaviors are only enabled for arguments - * objects which have a parameter map (see E5 Section 10.6 - * main algorithm, step 12). + /* Exotic behaviors are only enabled for arguments objects + * which have a parameter map (see E5 Section 10.6 main + * algorithm, step 12). * - * In particular, a non-strict arguments object with no - * mapped formals does *NOT* get exotic behavior, even - * for e.g. "caller" property. This seems counterintuitive - * but seems to be the case. + * In particular, a non-strict arguments object with no + * mapped formals does *NOT* get exotic behavior, even + * for e.g. "caller" property. This seems counterintuitive + * but seems to be the case. */ /* cannot be strict (never mapped variables) */ @@ -56939,7 +57037,6 @@ void duk__create_arguments_object(duk_hthread *thr, DUK_DDD(DUK_DDDPRINT("not enabling exotic behavior for arguments object")); } - /* nice log */ DUK_DDD(DUK_DDDPRINT("final arguments related objects: " "arguments at index %ld -> %!O " "map at index %ld -> %!O " @@ -56948,20 +57045,22 @@ void duk__create_arguments_object(duk_hthread *thr, (long) i_map, (duk_heaphdr *) duk_get_hobject(ctx, i_map), (long) i_mappednames, (duk_heaphdr *) duk_get_hobject(ctx, i_mappednames))); - /* [args(n) [crud] formals arguments map mappednames] -> [args [crud] arguments] */ + /* [ args(n) [crud] formals arguments map mappednames ] */ + duk_pop_2(ctx); duk_remove(ctx, -2); + + /* [ args [crud] arguments ] */ } /* Helper for creating the arguments object and adding it to the env record * on top of the value stack. This helper has a very strict dependency on * the shape of the input stack. */ -DUK_LOCAL -void duk__handle_createargs_for_call(duk_hthread *thr, - duk_hobject *func, - duk_hobject *env, - duk_idx_t num_stack_args) { +DUK_LOCAL void duk__handle_createargs_for_call(duk_hthread *thr, + duk_hobject *func, + duk_hobject *env, + duk_idx_t num_stack_args) { duk_context *ctx = (duk_context *) thr; DUK_DDD(DUK_DDDPRINT("creating arguments object for function call")); @@ -56972,7 +57071,7 @@ void duk__handle_createargs_for_call(duk_hthread *thr, DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func)); DUK_ASSERT(duk_get_top(ctx) >= num_stack_args + 1); - /* [... arg1 ... argN envobj] */ + /* [ ... arg1 ... argN envobj ] */ duk__create_arguments_object(thr, func, @@ -56980,14 +57079,14 @@ void duk__handle_createargs_for_call(duk_hthread *thr, duk_get_top(ctx) - num_stack_args - 1, /* idx_argbase */ num_stack_args); - /* [... arg1 ... argN envobj argobj] */ + /* [ ... arg1 ... argN envobj argobj ] */ duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LC_ARGUMENTS, DUK_HOBJECT_HAS_STRICT(func) ? DUK_PROPDESC_FLAGS_E : /* strict: non-deletable, non-writable */ DUK_PROPDESC_FLAGS_WE); /* non-strict: non-deletable, writable */ - /* [... arg1 ... argN envobj] */ + /* [ ... arg1 ... argN envobj ] */ } /* @@ -57004,11 +57103,10 @@ void duk__handle_createargs_for_call(duk_hthread *thr, * function. This would make call time handling much easier. */ -DUK_LOCAL -void duk__handle_bound_chain_for_call(duk_hthread *thr, - duk_idx_t idx_func, - duk_idx_t *p_num_stack_args, /* may be changed by call */ - duk_bool_t is_constructor_call) { +DUK_LOCAL void duk__handle_bound_chain_for_call(duk_hthread *thr, + duk_idx_t idx_func, + duk_idx_t *p_num_stack_args, /* may be changed by call */ + duk_bool_t is_constructor_call) { duk_context *ctx = (duk_context *) thr; duk_idx_t num_stack_args; duk_tval *tv_func; @@ -57101,7 +57199,7 @@ void duk__handle_bound_chain_for_call(duk_hthread *thr, DUK_DDD(DUK_DDDPRINT("final non-bound function is: %!T", duk_get_tval(ctx, idx_func))); -#ifdef DUK_USE_ASSERTIONS +#if defined(DUK_USE_ASSERTIONS) tv_func = duk_require_tval(ctx, idx_func); DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func) || DUK_TVAL_IS_OBJECT(tv_func)); if (DUK_TVAL_IS_OBJECT(tv_func)) { @@ -57122,10 +57220,9 @@ void duk__handle_bound_chain_for_call(duk_hthread *thr, * assuming it does NOT have the DUK_HOBJECT_FLAG_NEWENV flag. */ -DUK_LOCAL -void duk__handle_oldenv_for_call(duk_hthread *thr, - duk_hobject *func, - duk_activation *act) { +DUK_LOCAL void duk__handle_oldenv_for_call(duk_hthread *thr, + duk_hobject *func, + duk_activation *act) { duk_tval *tv; DUK_ASSERT(thr != NULL); @@ -57161,7 +57258,7 @@ void duk__handle_oldenv_for_call(duk_hthread *thr, * Helper for updating callee 'caller' property. */ -#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY +#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func) { duk_tval *tv_caller; duk_hobject *h_tmp; @@ -57181,6 +57278,8 @@ DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func) act_callee = thr->callstack + thr->callstack_top - 1; act_caller = (thr->callstack_top >= 2 ? act_callee - 1 : NULL); + /* XXX: check .caller writability? */ + /* Backup 'caller' property and update its value. */ tv_caller = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, DUK_HTHREAD_STRING_CALLER(thr)); if (tv_caller) { @@ -57263,10 +57362,9 @@ DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func) * side effects, because ToObject() may be called. */ -DUK_LOCAL -void duk__coerce_effective_this_binding(duk_hthread *thr, - duk_hobject *func, - duk_idx_t idx_this) { +DUK_LOCAL void duk__coerce_effective_this_binding(duk_hthread *thr, + duk_hobject *func, + duk_idx_t idx_this) { duk_context *ctx = (duk_context *) thr; duk_tval *tv_this; duk_hobject *obj_global; @@ -57316,12 +57414,11 @@ void duk__coerce_effective_this_binding(duk_hthread *thr, * Returns duk_hobject * to the final non-bound function (NULL for lightfunc). */ -DUK_LOCAL -duk_hobject *duk__nonbound_func_lookup(duk_context *ctx, - duk_idx_t idx_func, - duk_idx_t *out_num_stack_args, - duk_tval **out_tv_func, - duk_small_uint_t call_flags) { +DUK_LOCAL duk_hobject *duk__nonbound_func_lookup(duk_context *ctx, + duk_idx_t idx_func, + duk_idx_t *out_num_stack_args, + duk_tval **out_tv_func, + duk_small_uint_t call_flags) { duk_hthread *thr = (duk_hthread *) ctx; duk_tval *tv_func; duk_hobject *func; @@ -57377,19 +57474,23 @@ duk_hobject *duk__nonbound_func_lookup(duk_context *ctx, } /* - * Value stack resize and stack top adjustment helper + * Value stack resize and stack top adjustment helper. * * XXX: This should all be merged to duk_valstack_resize_raw(). */ -DUK_LOCAL -void duk__adjust_valstack_and_top(duk_hthread *thr, duk_idx_t num_stack_args, duk_idx_t idx_args, duk_idx_t nregs, duk_idx_t nargs, duk_hobject *func) { +DUK_LOCAL void duk__adjust_valstack_and_top(duk_hthread *thr, + duk_idx_t num_stack_args, + duk_idx_t idx_args, + duk_idx_t nregs, + duk_idx_t nargs, + duk_hobject *func) { duk_context *ctx = (duk_context *) thr; duk_size_t vs_min_size; duk_bool_t adjusted_top = 0; - vs_min_size = (thr->valstack_bottom - thr->valstack) + /* bottom of current func */ - idx_args; /* bottom of new func */ + vs_min_size = (thr->valstack_bottom - thr->valstack) + /* bottom of current func */ + idx_args; /* bottom of new func */ if (nregs >= 0) { DUK_ASSERT(nargs >= 0); @@ -57400,15 +57501,17 @@ void duk__adjust_valstack_and_top(duk_hthread *thr, duk_idx_t num_stack_args, du vs_min_size += num_stack_args; /* num entries of new func at entry */ } if (func == NULL || DUK_HOBJECT_IS_NATIVEFUNCTION(func)) { - vs_min_size += DUK_VALSTACK_API_ENTRY_MINIMUM; /* Duktape/C API guaranteed entries (on top of args) */ + vs_min_size += DUK_VALSTACK_API_ENTRY_MINIMUM; /* Duktape/C API guaranteed entries (on top of args) */ } - vs_min_size += DUK_VALSTACK_INTERNAL_EXTRA; /* + spare */ + vs_min_size += DUK_VALSTACK_INTERNAL_EXTRA; /* + spare */ - /* XXX: Awkward fix for GH-107: we can't resize the value stack to - * a size smaller than the current top, so the order of the resize - * and adjusting the stack top depends on the current vs. final size - * of the value stack. Ideally duk_valstack_resize_raw() would have - * a combined algorithm to avoid this. + /* XXX: We can't resize the value stack to a size smaller than the + * current top, so the order of the resize and adjusting the stack + * top depends on the current vs. final size of the value stack. + * The operations could be combined to avoid this, but the proper + * fix is to only grow the value stack on a function call, and only + * shrink it (without throwing if the shrink fails) on function + * return. */ if (vs_min_size < (duk_size_t) (thr->valstack_top - thr->valstack)) { @@ -57435,862 +57538,6 @@ void duk__adjust_valstack_and_top(duk_hthread *thr, duk_idx_t num_stack_args, du } } -/* - * Helper for making various kinds of calls. - * - * Call flags: - * - * DUK_CALL_FLAG_PROTECTED <--> protected call - * DUK_CALL_FLAG_IGNORE_RECLIMIT <--> ignore C recursion limit, - * for errhandler calls - * DUK_CALL_FLAG_CONSTRUCTOR_CALL <--> for 'new Foo()' calls - * - * Input stack (thr): - * - * [ func this arg1 ... argN ] - * - * Output stack (thr): - * - * [ retval ] (DUK_EXEC_SUCCESS) - * [ errobj ] (DUK_EXEC_ERROR (normal error), protected call) - * - * Even when executing a protected call an error may be thrown in rare cases. - * For instance, if we run out of memory when setting up the return stack - * after a caught error, the out of memory is propagated to the caller. - * Similarly, API errors (such as invalid input stack shape and invalid - * indices) cause an error to propagate out of this function. If there is - * no catchpoint for this error, the fatal error handler is called. - * - * See 'execution.rst'. - * - * The allowed thread states for making a call are: - * - thr matches heap->curr_thread, and thr is already RUNNING - * - thr does not match heap->curr_thread (may be NULL or other), - * and thr is INACTIVE (in this case, a setjmp() catchpoint is - * always used for thread book-keeping to work properly) - * - * Like elsewhere, gotos are used to keep indent level minimal and - * avoiding a dozen helpers with awkward plumbing. - * - * Note: setjmp() and local variables have a nasty interaction, - * see execution.rst; non-volatile locals modified after setjmp() - * call are not guaranteed to keep their value. - */ - -DUK_INTERNAL -duk_int_t duk_handle_call(duk_hthread *thr, - duk_idx_t num_stack_args, - duk_small_uint_t call_flags) { - duk_context *ctx = (duk_context *) thr; - duk_size_t entry_valstack_bottom_index; - duk_size_t entry_valstack_end; - duk_size_t entry_callstack_top; - duk_size_t entry_catchstack_top; - duk_int_t entry_call_recursion_depth; - duk_hthread *entry_curr_thread; - duk_uint_fast8_t entry_thread_state; - duk_instr_t **entry_ptr_curr_pc; - volatile duk_bool_t need_setjmp; - duk_jmpbuf * volatile old_jmpbuf_ptr = NULL; /* ptr is volatile (not the target) */ - volatile duk_idx_t idx_func; /* valstack index of 'func' and retval (relative to entry valstack_bottom) */ - duk_idx_t idx_args; /* valstack index of start of args (arg1) (relative to entry valstack_bottom) */ - duk_idx_t nargs; /* # argument registers target function wants (< 0 => "as is") */ - duk_idx_t nregs; /* # total registers target function wants on entry (< 0 => "as is") */ - duk_hobject *func; /* 'func' on stack (borrowed reference) */ - duk_tval *tv_func; /* duk_tval ptr for 'func' on stack (borrowed reference) or tv_func_copy */ - duk_tval tv_func_copy; /* to avoid relookups */ - duk_activation *act; - duk_hobject *env; - duk_jmpbuf our_jmpbuf; - duk_int_t retval = DUK_EXEC_ERROR; - duk_ret_t rc; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(ctx != NULL); - DUK_ASSERT(num_stack_args >= 0); - - /* XXX: currently NULL allocations are not supported; remove if later allowed */ - DUK_ASSERT(thr->valstack != NULL); - DUK_ASSERT(thr->callstack != NULL); - DUK_ASSERT(thr->catchstack != NULL); - - /* - * Preliminaries, required by setjmp() handler. - * - * Must be careful not to throw an unintended error here. - * - * Note: careful with indices like '-x'; if 'x' is zero, it - * refers to valstack_bottom. - */ - - entry_valstack_bottom_index = (duk_size_t) (thr->valstack_bottom - thr->valstack); -#if defined(DUK_USE_PREFER_SIZE) - entry_valstack_end = (duk_size_t) (thr->valstack_end - thr->valstack); -#else - DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == thr->valstack_size); - entry_valstack_end = thr->valstack_size; -#endif - entry_callstack_top = thr->callstack_top; - entry_catchstack_top = thr->catchstack_top; - entry_call_recursion_depth = thr->heap->call_recursion_depth; - entry_curr_thread = thr->heap->curr_thread; /* Note: may be NULL if first call */ - entry_thread_state = thr->state; - entry_ptr_curr_pc = thr->ptr_curr_pc; /* may be NULL */ - - idx_func = duk_normalize_index(ctx, -num_stack_args - 2); /* idx_func must be valid, note: non-throwing! */ - idx_args = idx_func + 2; /* idx_args is not necessarily valid if num_stack_args == 0 (idx_args then equals top) */ - - /* Need a setjmp() catchpoint if a protected call OR if we need to - * do mandatory cleanup. - */ - need_setjmp = ((call_flags & DUK_CALL_FLAG_PROTECTED) != 0) || (thr->heap->curr_thread != thr); - - DUK_DD(DUK_DDPRINT("duk_handle_call: thr=%p, num_stack_args=%ld, " - "call_flags=0x%08lx (protected=%ld, ignorerec=%ld, constructor=%ld), need_setjmp=%ld, " - "valstack_top=%ld, idx_func=%ld, idx_args=%ld, rec_depth=%ld/%ld, " - "entry_valstack_bottom_index=%ld, entry_callstack_top=%ld, entry_catchstack_top=%ld, " - "entry_call_recursion_depth=%ld, entry_curr_thread=%p, entry_thread_state=%ld", - (void *) thr, - (long) num_stack_args, - (unsigned long) call_flags, - (long) ((call_flags & DUK_CALL_FLAG_PROTECTED) != 0 ? 1 : 0), - (long) ((call_flags & DUK_CALL_FLAG_IGNORE_RECLIMIT) != 0 ? 1 : 0), - (long) ((call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL) != 0 ? 1 : 0), - (long) need_setjmp, - (long) duk_get_top(ctx), - (long) idx_func, - (long) idx_args, - (long) thr->heap->call_recursion_depth, - (long) thr->heap->call_recursion_limit, - (long) entry_valstack_bottom_index, - (long) entry_callstack_top, - (long) entry_catchstack_top, - (long) entry_call_recursion_depth, - (void *) entry_curr_thread, - (long) entry_thread_state)); - - /* If thr->ptr_curr_pc is set, sync curr_pc to act->pc. Then NULL - * thr->ptr_curr_pc so that it's not accidentally used with an incorrect - * activation when side effects occur. - */ - duk_hthread_sync_and_null_currpc(thr); - - /* XXX: Multiple tv_func lookups are now avoided by making a local - * copy of tv_func. Another approach would be to compute an offset - * for tv_func from valstack bottom and recomputing the tv_func - * pointer quickly as valstack + offset instead of calling duk_get_tval(). - */ - - if (idx_func < 0 || idx_args < 0) { - /* - * Since stack indices are not reliable, we can't do anything useful - * here. Invoke the existing setjmp catcher, or if it doesn't exist, - * call the fatal error handler. - */ - - DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS); - } - - /* - * Setup a setjmp() catchpoint first because even the call setup - * may fail. - */ - - if (!need_setjmp) { - DUK_DDD(DUK_DDDPRINT("don't need a setjmp catchpoint")); - goto handle_call; - } - - old_jmpbuf_ptr = thr->heap->lj.jmpbuf_ptr; - thr->heap->lj.jmpbuf_ptr = &our_jmpbuf; - - if (DUK_SETJMP(thr->heap->lj.jmpbuf_ptr->jb) == 0) { - DUK_DDD(DUK_DDDPRINT("setjmp catchpoint setup complete")); - goto handle_call; - } - - /* - * Error during setup, call, or postprocessing of the call. - * The error value is in heap->lj.value1. - * - * Note: any local variables accessed here must have their value - * assigned *before* the setjmp() call, OR they must be declared - * volatile. Otherwise their value is not guaranteed to be correct. - * - * The following are such variables: - * - duk_handle_call() parameters - * - entry_* - * - idx_func - * - idx_args - * - * The very first thing we do is restore the previous setjmp catcher. - * This means that any error in error handling will propagate outwards - * instead of causing a setjmp() re-entry above. The *only* actual - * errors that should happen here are allocation errors. - */ - - DUK_DDD(DUK_DDDPRINT("error caught during protected duk_handle_call(): %!T", - (duk_tval *) &thr->heap->lj.value1)); - - DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW); - DUK_ASSERT(thr->callstack_top >= entry_callstack_top); - DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top); - - /* We don't need to sync back thr->curr_pc here because the - * bytecode executor always has a setjmp catchpoint which - * does that before errors propagate to here. - */ - - /* - * Restore previous setjmp catchpoint - */ - - /* Note: either pointer may be NULL (at entry), so don't assert */ - DUK_DDD(DUK_DDDPRINT("restore jmpbuf_ptr: %p -> %p", - (void *) (thr && thr->heap ? thr->heap->lj.jmpbuf_ptr : NULL), - (void *) old_jmpbuf_ptr)); - - thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr; - - if (!(call_flags & DUK_CALL_FLAG_PROTECTED)) { - /* - * Caller did not request a protected call but a setjmp - * catchpoint was set up to allow cleanup. So, clean up - * and rethrow. - * - * We must restore curr_thread here to ensure that its - * current value doesn't end up pointing to a thread object - * which has been freed. This is now a problem because some - * call sites (namely duk_safe_call()) *first* unwind stacks - * and only then deal with curr_thread. If those call sites - * were fixed, this wouldn't matter here. - * - * Note: this case happens e.g. when heap->curr_thread is - * NULL on entry. - */ - - DUK_DDD(DUK_DDDPRINT("call is not protected -> clean up and rethrow")); - - /* Restore entry thread executor curr_pc stack frame pointer. */ - thr->ptr_curr_pc = entry_ptr_curr_pc; - - DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */ - thr->state = entry_thread_state; - DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread == NULL) || /* first call */ - (thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread != NULL) || /* other call */ - (thr->state == DUK_HTHREAD_STATE_RUNNING && thr->heap->curr_thread == thr)); /* current thread */ - - /* XXX: should setjmp catcher be responsible for this instead? */ - thr->heap->call_recursion_depth = entry_call_recursion_depth; - duk_err_longjmp(thr); - DUK_UNREACHABLE(); - } - - duk_hthread_catchstack_unwind(thr, entry_catchstack_top); - duk_hthread_callstack_unwind(thr, entry_callstack_top); - thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index; - - /* [ ... func this (crud) errobj ] */ - - /* XXX: is there space? better implementation: write directly over - * 'func' slot to avoid valstack grow issues. - */ - duk_push_tval(ctx, &thr->heap->lj.value1); - - /* [ ... func this (crud) errobj ] */ - - duk_replace(ctx, idx_func); - duk_set_top(ctx, idx_func + 1); - - /* [ ... errobj ] */ - - /* Ensure there is internal valstack spare before we exit; this may - * throw an alloc error. The same guaranteed size must be available - * as before the call. This is not optimal now: we store the valstack - * allocated size during entry; this value may be higher than the - * minimal guarantee for an application. - */ - - (void) duk_valstack_resize_raw((duk_context *) thr, - entry_valstack_end, /* same as during entry */ - DUK_VSRESIZE_FLAG_SHRINK | /* flags */ - DUK_VSRESIZE_FLAG_COMPACT | - DUK_VSRESIZE_FLAG_THROW); - - /* Note: currently a second setjmp restoration is done at the target; - * this is OK, but could be refactored away. - */ - retval = DUK_EXEC_ERROR; - goto shrink_and_finished; - - handle_call: - /* - * Thread state check and book-keeping. - */ - - if (thr == thr->heap->curr_thread) { - /* same thread */ - if (thr->state != DUK_HTHREAD_STATE_RUNNING) { - /* should actually never happen, but check anyway */ - goto thread_state_error; - } - } else { - /* different thread */ - DUK_ASSERT(thr->heap->curr_thread == NULL || - thr->heap->curr_thread->state == DUK_HTHREAD_STATE_RUNNING); - if (thr->state != DUK_HTHREAD_STATE_INACTIVE) { - goto thread_state_error; - } - DUK_HEAP_SWITCH_THREAD(thr->heap, thr); - thr->state = DUK_HTHREAD_STATE_RUNNING; - - /* Note: multiple threads may be simultaneously in the RUNNING - * state, but not in the same "resume chain". - */ - } - - DUK_ASSERT(thr->heap->curr_thread == thr); - DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); - - /* - * C call recursion depth check, which provides a reasonable upper - * bound on maximum C stack size (arbitrary C stack growth is only - * possible by recursive handle_call / handle_safe_call calls). - */ - - DUK_ASSERT(thr->heap->call_recursion_depth >= 0); - DUK_ASSERT(thr->heap->call_recursion_depth <= thr->heap->call_recursion_limit); - - if (call_flags & DUK_CALL_FLAG_IGNORE_RECLIMIT) { - DUK_DD(DUK_DDPRINT("ignoring reclimit for this call (probably an errhandler call)")); - } else { - if (thr->heap->call_recursion_depth >= thr->heap->call_recursion_limit) { - /* XXX: error message is a bit misleading: we reached a recursion - * limit which is also essentially the same as a C callstack limit - * (except perhaps with some relaxed threading assumptions). - */ - DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, DUK_STR_C_CALLSTACK_LIMIT); - } - thr->heap->call_recursion_depth++; - } - - /* - * Check the function type, handle bound function chains, and prepare - * parameters for the rest of the call handling. Also figure out the - * effective 'this' binding, which replaces the current value at - * idx_func + 1. - * - * If the target function is a 'bound' one, follow the chain of 'bound' - * functions until a non-bound function is found. During this process, - * bound arguments are 'prepended' to existing ones, and the "this" - * binding is overridden. See E5 Section 15.3.4.5.1. - * - * Lightfunc detection happens here too. Note that lightweight functions - * can be wrapped by (non-lightweight) bound functions so we must resolve - * the bound function chain first. - */ - - func = duk__nonbound_func_lookup(ctx, idx_func, &num_stack_args, &tv_func, call_flags); - DUK_TVAL_SET_TVAL(&tv_func_copy, tv_func); - tv_func = &tv_func_copy; /* local copy to avoid relookups */ - - DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func)); - DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPILEDFUNCTION(func) || - DUK_HOBJECT_IS_NATIVEFUNCTION(func))); - - duk__coerce_effective_this_binding(thr, func, idx_func + 1); - DUK_DDD(DUK_DDDPRINT("effective 'this' binding is: %!T", - (duk_tval *) duk_get_tval(ctx, idx_func + 1))); - - /* These base values are never used, but if the compiler doesn't know - * that DUK_ERROR() won't return, these are needed to silence warnings. - * On the other hand, scan-build will warn about the values not being - * used, so add a DUK_UNREF. - */ - nargs = 0; DUK_UNREF(nargs); - nregs = 0; DUK_UNREF(nregs); - - if (func == NULL) { - duk_small_uint_t lf_flags; - - DUK_DDD(DUK_DDDPRINT("lightfunc call handling")); - DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func)); - lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv_func); - nargs = DUK_LFUNC_FLAGS_GET_NARGS(lf_flags); - if (nargs == DUK_LFUNC_NARGS_VARARGS) { - nargs = -1; /* vararg */ - } - nregs = nargs; - } else if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) { - nargs = ((duk_hcompiledfunction *) func)->nargs; - nregs = ((duk_hcompiledfunction *) func)->nregs; - DUK_ASSERT(nregs >= nargs); - } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(func)) { - /* Note: nargs (and nregs) may be negative for a native, - * function, which indicates that the function wants the - * input stack "as is" (i.e. handles "vararg" arguments). - */ - nargs = ((duk_hnativefunction *) func)->nargs; - nregs = nargs; - } else { - /* XXX: this should be an assert */ - DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_CALLABLE); - } - - /* [ ... func this arg1 ... argN ] */ - - /* - * Setup a preliminary activation. - * - * Don't touch valstack_bottom or valstack_top yet so that Duktape API - * calls work normally. - */ - - duk_hthread_callstack_grow(thr); - - if (thr->callstack_top > 0) { - /* - * Update idx_retval of current activation. - * - * Although it might seem this is not necessary (bytecode executor - * does this for Ecmascript-to-Ecmascript calls; other calls are - * handled here), this turns out to be necessary for handling yield - * and resume. For them, an Ecmascript-to-native call happens, and - * the Ecmascript call's idx_retval must be set for things to work. - */ - - (thr->callstack + thr->callstack_top - 1)->idx_retval = entry_valstack_bottom_index + idx_func; - } - - DUK_ASSERT(thr->callstack_top < thr->callstack_size); - act = thr->callstack + thr->callstack_top; - thr->callstack_top++; - DUK_ASSERT(thr->callstack_top <= thr->callstack_size); - DUK_ASSERT(thr->valstack_top > thr->valstack_bottom); /* at least effective 'this' */ - DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func)); - - act->flags = 0; - if (func == NULL || DUK_HOBJECT_HAS_STRICT(func)) { - act->flags |= DUK_ACT_FLAG_STRICT; - } - if (call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL) { - act->flags |= DUK_ACT_FLAG_CONSTRUCT; - /*act->flags |= DUK_ACT_FLAG_PREVENT_YIELD;*/ - } - if (func == NULL || DUK_HOBJECT_IS_NATIVEFUNCTION(func)) { - /*act->flags |= DUK_ACT_FLAG_PREVENT_YIELD;*/ - } - if (call_flags & DUK_CALL_FLAG_DIRECT_EVAL) { - act->flags |= DUK_ACT_FLAG_DIRECT_EVAL; - } - - /* As a first approximation, all calls except Ecmascript-to-Ecmascript - * calls prevent a yield. - */ - act->flags |= DUK_ACT_FLAG_PREVENT_YIELD; - - act->func = func; /* NULL for lightfunc */ - act->var_env = NULL; - act->lex_env = NULL; -#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY - act->prev_caller = NULL; -#endif - act->curr_pc = NULL; -#if defined(DUK_USE_DEBUGGER_SUPPORT) - act->prev_line = 0; -#endif - act->idx_bottom = entry_valstack_bottom_index + idx_args; -#if 0 /* topmost activation idx_retval is considered garbage, no need to init */ - act->idx_retval = 0; -#endif - DUK_TVAL_SET_TVAL(&act->tv_func, tv_func); /* borrowed, no refcount */ - - if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) { - /* duk_hthread_callstack_unwind() will decrease this on unwind */ - thr->callstack_preventcount++; - } - - /* XXX: Is this INCREF necessary? 'func' is always a borrowed - * reference reachable through the value stack? If changed, stack - * unwind code also needs to be fixed to match. - */ - DUK_HOBJECT_INCREF_ALLOWNULL(thr, func); /* act->func */ - -#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY - if (func) { - duk__update_func_caller_prop(thr, func); - } - act = thr->callstack + thr->callstack_top - 1; -#endif - - /* [... func this arg1 ... argN] */ - - /* - * Environment record creation and 'arguments' object creation. - * Named function expression name binding is handled by the - * compiler; the compiled function's parent env will contain - * the (immutable) binding already. - * - * This handling is now identical for C and Ecmascript functions. - * C functions always have the 'NEWENV' flag set, so their - * environment record initialization is delayed (which is good). - * - * Delayed creation (on demand) is handled in duk_js_var.c. - */ - - DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func)); /* bound function chain has already been resolved */ - - if (func != NULL && !DUK_HOBJECT_HAS_NEWENV(func)) { - /* use existing env (e.g. for non-strict eval); cannot have - * an own 'arguments' object (but can refer to the existing one) - */ - - DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(func)); - - duk__handle_oldenv_for_call(thr, func, act); - - DUK_ASSERT(act->lex_env != NULL); - DUK_ASSERT(act->var_env != NULL); - goto env_done; - } - - DUK_ASSERT(func == NULL || DUK_HOBJECT_HAS_NEWENV(func)); - - if (func == NULL || !DUK_HOBJECT_HAS_CREATEARGS(func)) { - /* no need to create environment record now; leave as NULL */ - DUK_ASSERT(act->lex_env == NULL); - DUK_ASSERT(act->var_env == NULL); - goto env_done; - } - - /* third arg: absolute index (to entire valstack) of idx_bottom of new activation */ - env = duk_create_activation_environment_record(thr, func, act->idx_bottom); - DUK_ASSERT(env != NULL); - - /* [... func this arg1 ... argN envobj] */ - - DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func)); - duk__handle_createargs_for_call(thr, func, env, num_stack_args); - - /* [... func this arg1 ... argN envobj] */ - - act = thr->callstack + thr->callstack_top - 1; - act->lex_env = env; - act->var_env = env; - DUK_HOBJECT_INCREF(thr, env); - DUK_HOBJECT_INCREF(thr, env); /* XXX: incref by count (2) directly */ - duk_pop(ctx); - - env_done: - /* [... func this arg1 ... argN] */ - - /* - * Setup value stack: clamp to 'nargs', fill up to 'nregs' - * - * Value stack may either grow or shrink, depending on the - * number of func registers and the number of actual arguments. - * If nregs >= 0, func wants args clamped to 'nargs'; else it - * wants all args (= 'num_stack_args'). - */ - - duk__adjust_valstack_and_top(thr, - num_stack_args, - idx_args, - nregs, - nargs, - func); - - /* - * Determine call type; then setup activation and call - */ - - if (func != NULL && DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) { - goto ecmascript_call; - } else { - goto native_call; - } - DUK_UNREACHABLE(); - - /* - * Native (C) call - */ - - native_call: - /* - * Shift to new valstack_bottom. - */ - - thr->valstack_bottom = thr->valstack_bottom + idx_args; - /* keep current valstack_top */ - DUK_ASSERT(thr->valstack_bottom >= thr->valstack); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - DUK_ASSERT(func == NULL || ((duk_hnativefunction *) func)->func != NULL); - - /* [... func this | arg1 ... argN] ('this' must precede new bottom) */ - - /* - * Actual function call and return value check. - * - * Return values: - * 0 success, no return value (default to 'undefined') - * 1 success, one return value on top of stack - * < 0 error, throw a "magic" error - * other invalid - */ - - /* For native calls must be NULL so we don't sync back */ - DUK_ASSERT(thr->ptr_curr_pc == NULL); - - if (func) { - rc = ((duk_hnativefunction *) func)->func((duk_context *) thr); - } else { - duk_c_function funcptr = DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv_func); - rc = funcptr((duk_context *) thr); - } - - if (rc < 0) { - duk_error_throw_from_negative_rc(thr, rc); - DUK_UNREACHABLE(); - } else if (rc > 1) { - DUK_ERROR_API(thr, "c function returned invalid rc"); - } - DUK_ASSERT(rc == 0 || rc == 1); - - /* - * Unwind stack(s) and shift back to old valstack_bottom. - */ - - DUK_ASSERT(thr->catchstack_top == entry_catchstack_top); - DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1); - -#if 0 /* should be no need to unwind */ - duk_hthread_catchstack_unwind(thr, entry_catchstack_top); -#endif - duk_hthread_callstack_unwind(thr, entry_callstack_top); - - thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index; - /* keep current valstack_top */ - - DUK_ASSERT(thr->valstack_bottom >= thr->valstack); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - DUK_ASSERT(thr->valstack_top - thr->valstack_bottom >= idx_func + 1); - - /* - * Manipulate value stack so that return value is on top - * (pushing an 'undefined' if necessary). - */ - - /* XXX: should this happen in the callee's activation or after unwinding? */ - if (rc == 0) { - duk_require_stack(ctx, 1); - duk_push_undefined(ctx); - } - /* [... func this (crud) retval] */ - - DUK_DDD(DUK_DDDPRINT("native call retval -> %!T (rc=%ld)", - (duk_tval *) duk_get_tval(ctx, -1), (long) rc)); - - duk_replace(ctx, idx_func); - duk_set_top(ctx, idx_func + 1); - - /* [... retval] */ - - /* Ensure there is internal valstack spare before we exit; this may - * throw an alloc error. The same guaranteed size must be available - * as before the call. This is not optimal now: we store the valstack - * allocated size during entry; this value may be higher than the - * minimal guarantee for an application. - */ - - (void) duk_valstack_resize_raw((duk_context *) thr, - entry_valstack_end, /* same as during entry */ - DUK_VSRESIZE_FLAG_SHRINK | /* flags */ - DUK_VSRESIZE_FLAG_COMPACT | - DUK_VSRESIZE_FLAG_THROW); - - - /* - * Shrink checks and return with success. - */ - - retval = DUK_EXEC_SUCCESS; - goto shrink_and_finished; - - /* - * Ecmascript call - */ - - ecmascript_call: - - /* - * Shift to new valstack_bottom. - */ - - DUK_ASSERT(func != NULL); - DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(func)); - act->curr_pc = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, (duk_hcompiledfunction *) func); - - thr->valstack_bottom = thr->valstack_bottom + idx_args; - /* keep current valstack_top */ - DUK_ASSERT(thr->valstack_bottom >= thr->valstack); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - - /* [... func this | arg1 ... argN] ('this' must precede new bottom) */ - - /* - * Bytecode executor call. - * - * Execute bytecode, handling any recursive function calls and - * thread resumptions. Returns when execution would return from - * the entry level activation. When the executor returns, a - * single return value is left on the stack top. - * - * The only possible longjmp() is an error (DUK_LJ_TYPE_THROW), - * other types are handled internally by the executor. - * - */ - - /* thr->ptr_curr_pc is set by bytecode executor early on entry */ - DUK_ASSERT(thr->ptr_curr_pc == NULL); - DUK_DDD(DUK_DDDPRINT("entering bytecode execution")); - duk_js_execute_bytecode(thr); - DUK_DDD(DUK_DDDPRINT("returned from bytecode execution")); - - /* - * Unwind stack(s) and shift back to old valstack_bottom. - */ - - DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1); - - duk_hthread_catchstack_unwind(thr, entry_catchstack_top); - duk_hthread_callstack_unwind(thr, entry_callstack_top); - - thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index; - /* keep current valstack_top */ - - DUK_ASSERT(thr->valstack_bottom >= thr->valstack); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - DUK_ASSERT(thr->valstack_top - thr->valstack_bottom >= idx_func + 1); - - /* - * Manipulate value stack so that return value is on top. - */ - - /* [... func this (crud) retval] */ - - duk_replace(ctx, idx_func); - duk_set_top(ctx, idx_func + 1); - - /* [... retval] */ - - /* Ensure there is internal valstack spare before we exit; this may - * throw an alloc error. The same guaranteed size must be available - * as before the call. This is not optimal now: we store the valstack - * allocated size during entry; this value may be higher than the - * minimal guarantee for an application. - */ - - (void) duk_valstack_resize_raw((duk_context *) thr, - entry_valstack_end, /* same as during entry */ - DUK_VSRESIZE_FLAG_SHRINK | /* flags */ - DUK_VSRESIZE_FLAG_COMPACT | - DUK_VSRESIZE_FLAG_THROW); - - /* - * Shrink checks and return with success. - */ - - retval = DUK_EXEC_SUCCESS; - goto shrink_and_finished; - - shrink_and_finished: -#if defined(DUK_USE_FASTINT) - /* Explicit check for fastint downgrade. */ - { - duk_tval *tv_fi; - tv_fi = duk_get_tval(ctx, -1); - DUK_ASSERT(tv_fi != NULL); - DUK_TVAL_CHKFAST_INPLACE(tv_fi); - } -#endif - - /* these are "soft" shrink checks, whose failures are ignored */ - /* XXX: would be nice if fast path was inlined */ - duk_hthread_catchstack_shrink_check(thr); - duk_hthread_callstack_shrink_check(thr); - goto finished; - - finished: - if (need_setjmp) { - /* Note: either pointer may be NULL (at entry), so don't assert; - * this is now done potentially twice, which is OK - */ - DUK_DDD(DUK_DDDPRINT("restore jmpbuf_ptr: %p -> %p (possibly already done)", - (void *) (thr && thr->heap ? thr->heap->lj.jmpbuf_ptr : NULL), - (void *) old_jmpbuf_ptr)); - thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr; - - /* These are just convenience "wiping" of state */ - thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN; - thr->heap->lj.iserror = 0; - - /* Side effects should not be an issue here: tv_tmp is local and - * thr->heap (and thr->heap->lj) have a stable pointer. Finalizer - * runs etc capture even out-of-memory errors so nothing should - * throw here. - */ - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value1); /* side effects */ - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value2); /* side effects */ - - DUK_DDD(DUK_DDDPRINT("setjmp catchpoint torn down")); - } - - /* Restore entry thread executor curr_pc stack frame pointer. */ - thr->ptr_curr_pc = entry_ptr_curr_pc; - - DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */ - thr->state = (duk_uint8_t) entry_thread_state; - - DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread == NULL) || /* first call */ - (thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread != NULL) || /* other call */ - (thr->state == DUK_HTHREAD_STATE_RUNNING && thr->heap->curr_thread == thr)); /* current thread */ - - thr->heap->call_recursion_depth = entry_call_recursion_depth; - - /* If the debugger is active we need to force an interrupt so that - * debugger breakpoints are rechecked. This is important for function - * calls caused by side effects (e.g. when doing a DUK_OP_GETPROP), see - * GH-303. Only needed for success path, error path always causes a - * breakpoint recheck in the executor. It would be enough to set this - * only when returning to an Ecmascript activation, but setting the flag - * on every return should have no ill effect. - */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { - DUK_DD(DUK_DDPRINT("returning to ecmascript activation with debugger enabled, force interrupt")); - DUK_ASSERT(thr->interrupt_counter <= thr->interrupt_init); - thr->interrupt_init -= thr->interrupt_counter; - thr->interrupt_counter = 0; - thr->heap->dbg_force_restart = 1; - } -#endif - -#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG) - duk__interrupt_fixup(thr, entry_curr_thread); -#endif - - return retval; - - thread_state_error: - DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid thread state for call (%ld)", (long) thr->state); - DUK_UNREACHABLE(); - return DUK_EXEC_ERROR; /* never executed */ -} - /* * Manipulate value stack so that exactly 'num_stack_rets' return * values are at 'idx_retbase' in every case, assuming there are @@ -58319,14 +57566,14 @@ DUK_LOCAL void duk__safe_call_adjust_valstack(duk_hthread *thr, duk_idx_t idx_re DUK_ASSERT(idx_rcbase >= 0); /* caller must check */ - /* ensure space for final configuration (idx_retbase + num_stack_rets) and - * intermediate configurations + /* Ensure space for final configuration (idx_retbase + num_stack_rets) + * and intermediate configurations. */ duk_require_stack_top(ctx, (idx_rcbase > idx_retbase ? idx_rcbase : idx_retbase) + num_stack_rets); - /* chop extra retvals away / extend with undefined */ + /* Chop extra retvals away / extend with undefined. */ duk_set_top(ctx, idx_rcbase + num_stack_rets); if (idx_rcbase >= idx_retbase) { @@ -58368,33 +57615,973 @@ DUK_LOCAL void duk__safe_call_adjust_valstack(duk_hthread *thr, duk_idx_t idx_re } /* - * Make a "C protected call" within the current activation. + * Misc shared helpers. + */ + +/* Get valstack index for the func argument or throw if insane stack. */ +DUK_LOCAL duk_idx_t duk__get_idx_func(duk_hthread *thr, duk_idx_t num_stack_args) { + duk_size_t off_stack_top; + duk_size_t off_stack_args; + duk_size_t off_stack_all; + duk_idx_t idx_func; /* valstack index of 'func' and retval (relative to entry valstack_bottom) */ + + /* Argument validation and func/args offset. */ + off_stack_top = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack_bottom); + off_stack_args = (duk_size_t) ((duk_size_t) num_stack_args * sizeof(duk_tval)); + off_stack_all = off_stack_args + 2 * sizeof(duk_tval); + if (DUK_UNLIKELY(off_stack_all > off_stack_top)) { + /* Since stack indices are not reliable, we can't do anything useful + * here. Invoke the existing setjmp catcher, or if it doesn't exist, + * call the fatal error handler. + */ + DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS); + return 0; + } + idx_func = (duk_idx_t) ((off_stack_top - off_stack_all) / sizeof(duk_tval)); + return idx_func; +} + +/* + * duk_handle_call_protected() and duk_handle_call_unprotected(): + * call into a Duktape/C or an Ecmascript function from any state. + * + * Input stack (thr): + * + * [ func this arg1 ... argN ] + * + * Output stack (thr): + * + * [ retval ] (DUK_EXEC_SUCCESS) + * [ errobj ] (DUK_EXEC_ERROR (normal error), protected call) + * + * Even when executing a protected call an error may be thrown in rare cases + * such as an insane num_stack_args argument. If there is no catchpoint for + * such errors, the fatal error handler is called. + * + * The error handling path should be error free, even for out-of-memory + * errors, to ensure safe sandboxing. (As of Duktape 1.4.0 this is not + * yet the case, see XXX notes below.) + */ + +DUK_INTERNAL duk_int_t duk_handle_call_protected(duk_hthread *thr, + duk_idx_t num_stack_args, + duk_small_uint_t call_flags) { + duk_context *ctx; + duk_size_t entry_valstack_bottom_index; + duk_size_t entry_valstack_end; + duk_size_t entry_callstack_top; + duk_size_t entry_catchstack_top; + duk_int_t entry_call_recursion_depth; + duk_hthread *entry_curr_thread; + duk_uint_fast8_t entry_thread_state; + duk_instr_t **entry_ptr_curr_pc; +#if !defined(DUK_USE_CPP_EXCEPTIONS) + duk_jmpbuf *old_jmpbuf_ptr = NULL; + duk_jmpbuf our_jmpbuf; +#endif + duk_idx_t idx_func; /* valstack index of 'func' and retval (relative to entry valstack_bottom) */ + + /* XXX: Multiple tv_func lookups are now avoided by making a local + * copy of tv_func. Another approach would be to compute an offset + * for tv_func from valstack bottom and recomputing the tv_func + * pointer quickly as valstack + offset instead of calling duk_get_tval(). + */ + + ctx = (duk_context *) thr; + DUK_UNREF(ctx); + DUK_ASSERT(thr != NULL); + DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT(num_stack_args >= 0); + /* XXX: currently NULL allocations are not supported; remove if later allowed */ + DUK_ASSERT(thr->valstack != NULL); + DUK_ASSERT(thr->callstack != NULL); + DUK_ASSERT(thr->catchstack != NULL); + + /* Argument validation and func/args offset. */ + idx_func = duk__get_idx_func(thr, num_stack_args); + + /* Preliminaries, required by setjmp() handler. Must be careful not + * to throw an unintended error here. + */ + + entry_valstack_bottom_index = (duk_size_t) (thr->valstack_bottom - thr->valstack); +#if defined(DUK_USE_PREFER_SIZE) + entry_valstack_end = (duk_size_t) (thr->valstack_end - thr->valstack); +#else + DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == thr->valstack_size); + entry_valstack_end = thr->valstack_size; +#endif + entry_callstack_top = thr->callstack_top; + entry_catchstack_top = thr->catchstack_top; + entry_call_recursion_depth = thr->heap->call_recursion_depth; + entry_curr_thread = thr->heap->curr_thread; /* Note: may be NULL if first call */ + entry_thread_state = thr->state; + entry_ptr_curr_pc = thr->ptr_curr_pc; /* may be NULL */ + + DUK_DD(DUK_DDPRINT("duk_handle_call_protected: thr=%p, num_stack_args=%ld, " + "call_flags=0x%08lx (ignorerec=%ld, constructor=%ld), " + "valstack_top=%ld, idx_func=%ld, idx_args=%ld, rec_depth=%ld/%ld, " + "entry_valstack_bottom_index=%ld, entry_callstack_top=%ld, entry_catchstack_top=%ld, " + "entry_call_recursion_depth=%ld, entry_curr_thread=%p, entry_thread_state=%ld", + (void *) thr, + (long) num_stack_args, + (unsigned long) call_flags, + (long) ((call_flags & DUK_CALL_FLAG_IGNORE_RECLIMIT) != 0 ? 1 : 0), + (long) ((call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL) != 0 ? 1 : 0), + (long) duk_get_top(ctx), + (long) idx_func, + (long) (idx_func + 2), + (long) thr->heap->call_recursion_depth, + (long) thr->heap->call_recursion_limit, + (long) entry_valstack_bottom_index, + (long) entry_callstack_top, + (long) entry_catchstack_top, + (long) entry_call_recursion_depth, + (void *) entry_curr_thread, + (long) entry_thread_state)); + +#if !defined(DUK_USE_CPP_EXCEPTIONS) + old_jmpbuf_ptr = thr->heap->lj.jmpbuf_ptr; + thr->heap->lj.jmpbuf_ptr = &our_jmpbuf; +#endif + +#if defined(DUK_USE_CPP_EXCEPTIONS) + try { +#else + if (DUK_LIKELY(DUK_SETJMP(thr->heap->lj.jmpbuf_ptr->jb) == 0)) { +#endif + /* Call handling and success path. Success path exit cleans + * up almost all state. + */ + duk__handle_call_inner(thr, num_stack_args, call_flags, idx_func); + + /* Success path handles */ + DUK_ASSERT(thr->heap->call_recursion_depth == entry_call_recursion_depth); + DUK_ASSERT(thr->ptr_curr_pc == entry_ptr_curr_pc); + + /* Longjmp state is kept clean in success path */ + DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN); + DUK_ASSERT(thr->heap->lj.iserror == 0); + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1)); + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2)); + +#if !defined(DUK_USE_CPP_EXCEPTIONS) + thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr; +#endif + + return DUK_EXEC_SUCCESS; +#if defined(DUK_USE_CPP_EXCEPTIONS) + } catch (duk_internal_exception &exc) { +#else + } else { +#endif + /* Error; error value is in heap->lj.value1. */ + +#if defined(DUK_USE_CPP_EXCEPTIONS) + DUK_UNREF(exc); +#endif + + duk__handle_call_error(thr, + entry_valstack_bottom_index, + entry_valstack_end, + entry_catchstack_top, + entry_callstack_top, + entry_call_recursion_depth, + entry_curr_thread, + entry_thread_state, + entry_ptr_curr_pc, + idx_func +#if !defined(DUK_USE_CPP_EXCEPTIONS) + , old_jmpbuf_ptr +#endif + ); + + /* Longjmp state is cleaned up by error handling */ + DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN); + DUK_ASSERT(thr->heap->lj.iserror == 0); + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1)); + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2)); + return DUK_EXEC_ERROR; + } +#if defined(DUK_USE_CPP_EXCEPTIONS) + catch (std::exception &exc) { + const char *what = exc.what(); + if (!what) { + what = "unknown"; + } + DUK_D(DUK_DPRINT("unexpected c++ std::exception (perhaps thrown by user code)")); + try { + DUK_ERROR(thr, DUK_ERR_API_ERROR, "caught invalid c++ std::exception '%s' (perhaps thrown by user code)", what); + } catch (duk_internal_exception exc) { + DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ std::exception")); + duk__handle_call_error(thr, + entry_valstack_bottom_index, + entry_valstack_end, + entry_catchstack_top, + entry_callstack_top, + entry_call_recursion_depth, + entry_curr_thread, + entry_thread_state, + entry_ptr_curr_pc, + idx_func); + return DUK_EXEC_ERROR; + } + } catch (...) { + DUK_D(DUK_DPRINT("unexpected c++ exception (perhaps thrown by user code)")); + try { + DUK_ERROR(thr, DUK_ERR_API_ERROR, "caught invalid c++ exception (perhaps thrown by user code)"); + } catch (duk_internal_exception exc) { + DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ exception")); + duk__handle_call_error(thr, + entry_valstack_bottom_index, + entry_valstack_end, + entry_catchstack_top, + entry_callstack_top, + entry_call_recursion_depth, + entry_curr_thread, + entry_thread_state, + entry_ptr_curr_pc, + idx_func); + return DUK_EXEC_ERROR; + } + } +#endif +} + +DUK_INTERNAL void duk_handle_call_unprotected(duk_hthread *thr, + duk_idx_t num_stack_args, + duk_small_uint_t call_flags) { + duk_idx_t idx_func; /* valstack index of 'func' and retval (relative to entry valstack_bottom) */ + + /* Argument validation and func/args offset. */ + idx_func = duk__get_idx_func(thr, num_stack_args); + + duk__handle_call_inner(thr, num_stack_args, call_flags, idx_func); +} + +DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, + duk_idx_t num_stack_args, + duk_small_uint_t call_flags, + duk_idx_t idx_func) { + duk_context *ctx; + duk_size_t entry_valstack_bottom_index; + duk_size_t entry_valstack_end; + duk_size_t entry_callstack_top; + duk_size_t entry_catchstack_top; + duk_int_t entry_call_recursion_depth; + duk_hthread *entry_curr_thread; + duk_uint_fast8_t entry_thread_state; + duk_instr_t **entry_ptr_curr_pc; + duk_idx_t nargs; /* # argument registers target function wants (< 0 => "as is") */ + duk_idx_t nregs; /* # total registers target function wants on entry (< 0 => "as is") */ + duk_hobject *func; /* 'func' on stack (borrowed reference) */ + duk_tval *tv_func; /* duk_tval ptr for 'func' on stack (borrowed reference) or tv_func_copy */ + duk_tval tv_func_copy; /* to avoid relookups */ + duk_activation *act; + duk_hobject *env; + duk_ret_t rc; + + ctx = (duk_context *) thr; + DUK_ASSERT(thr != NULL); + DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT(ctx != NULL); + DUK_ASSERT(num_stack_args >= 0); + /* XXX: currently NULL allocations are not supported; remove if later allowed */ + DUK_ASSERT(thr->valstack != NULL); + DUK_ASSERT(thr->callstack != NULL); + DUK_ASSERT(thr->catchstack != NULL); + + DUK_DD(DUK_DDPRINT("duk__handle_call_inner: num_stack_args=%ld, call_flags=0x%08lx, top=%ld", + (long) num_stack_args, (long) call_flags, (long) duk_get_top(ctx))); + + /* + * Store entry state. + */ + + entry_valstack_bottom_index = (duk_size_t) (thr->valstack_bottom - thr->valstack); +#if defined(DUK_USE_PREFER_SIZE) + entry_valstack_end = (duk_size_t) (thr->valstack_end - thr->valstack); +#else + DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == thr->valstack_size); + entry_valstack_end = thr->valstack_size; +#endif + entry_callstack_top = thr->callstack_top; + entry_catchstack_top = thr->catchstack_top; + entry_call_recursion_depth = thr->heap->call_recursion_depth; + entry_curr_thread = thr->heap->curr_thread; /* Note: may be NULL if first call */ + entry_thread_state = thr->state; + entry_ptr_curr_pc = thr->ptr_curr_pc; /* may be NULL */ + + /* If thr->ptr_curr_pc is set, sync curr_pc to act->pc. Then NULL + * thr->ptr_curr_pc so that it's not accidentally used with an incorrect + * activation when side effects occur. + */ + duk_hthread_sync_and_null_currpc(thr); + + DUK_DD(DUK_DDPRINT("duk__handle_call_inner: thr=%p, num_stack_args=%ld, " + "call_flags=0x%08lx (ignorerec=%ld, constructor=%ld), " + "valstack_top=%ld, idx_func=%ld, idx_args=%ld, rec_depth=%ld/%ld, " + "entry_valstack_bottom_index=%ld, entry_callstack_top=%ld, entry_catchstack_top=%ld, " + "entry_call_recursion_depth=%ld, entry_curr_thread=%p, entry_thread_state=%ld", + (void *) thr, + (long) num_stack_args, + (unsigned long) call_flags, + (long) ((call_flags & DUK_CALL_FLAG_IGNORE_RECLIMIT) != 0 ? 1 : 0), + (long) ((call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL) != 0 ? 1 : 0), + (long) duk_get_top(ctx), + (long) idx_func, + (long) (idx_func + 2), + (long) thr->heap->call_recursion_depth, + (long) thr->heap->call_recursion_limit, + (long) entry_valstack_bottom_index, + (long) entry_callstack_top, + (long) entry_catchstack_top, + (long) entry_call_recursion_depth, + (void *) entry_curr_thread, + (long) entry_thread_state)); + + + /* + * Thread state check and book-keeping. + */ + + if (thr == thr->heap->curr_thread) { + /* same thread */ + if (thr->state != DUK_HTHREAD_STATE_RUNNING) { + /* should actually never happen, but check anyway */ + goto thread_state_error; + } + } else { + /* different thread */ + DUK_ASSERT(thr->heap->curr_thread == NULL || + thr->heap->curr_thread->state == DUK_HTHREAD_STATE_RUNNING); + if (thr->state != DUK_HTHREAD_STATE_INACTIVE) { + goto thread_state_error; + } + DUK_HEAP_SWITCH_THREAD(thr->heap, thr); + thr->state = DUK_HTHREAD_STATE_RUNNING; + + /* Note: multiple threads may be simultaneously in the RUNNING + * state, but not in the same "resume chain". + */ + } + DUK_ASSERT(thr->heap->curr_thread == thr); + DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); + + /* + * C call recursion depth check, which provides a reasonable upper + * bound on maximum C stack size (arbitrary C stack growth is only + * possible by recursive handle_call / handle_safe_call calls). + */ + + /* XXX: remove DUK_CALL_FLAG_IGNORE_RECLIMIT flag: there's now the + * reclimit bump? + */ + + DUK_ASSERT(thr->heap->call_recursion_depth >= 0); + DUK_ASSERT(thr->heap->call_recursion_depth <= thr->heap->call_recursion_limit); + if (call_flags & DUK_CALL_FLAG_IGNORE_RECLIMIT) { + DUK_DD(DUK_DDPRINT("ignoring reclimit for this call (probably an errhandler call)")); + } else { + if (thr->heap->call_recursion_depth >= thr->heap->call_recursion_limit) { + /* XXX: error message is a bit misleading: we reached a recursion + * limit which is also essentially the same as a C callstack limit + * (except perhaps with some relaxed threading assumptions). + */ + DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, DUK_STR_C_CALLSTACK_LIMIT); + } + thr->heap->call_recursion_depth++; + } + + /* + * Check the function type, handle bound function chains, and prepare + * parameters for the rest of the call handling. Also figure out the + * effective 'this' binding, which replaces the current value at + * idx_func + 1. + * + * If the target function is a 'bound' one, follow the chain of 'bound' + * functions until a non-bound function is found. During this process, + * bound arguments are 'prepended' to existing ones, and the "this" + * binding is overridden. See E5 Section 15.3.4.5.1. + * + * Lightfunc detection happens here too. Note that lightweight functions + * can be wrapped by (non-lightweight) bound functions so we must resolve + * the bound function chain first. + */ + + func = duk__nonbound_func_lookup(ctx, idx_func, &num_stack_args, &tv_func, call_flags); + DUK_TVAL_SET_TVAL(&tv_func_copy, tv_func); + tv_func = &tv_func_copy; /* local copy to avoid relookups */ + + DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func)); + DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPILEDFUNCTION(func) || + DUK_HOBJECT_IS_NATIVEFUNCTION(func))); + + duk__coerce_effective_this_binding(thr, func, idx_func + 1); + DUK_DDD(DUK_DDDPRINT("effective 'this' binding is: %!T", + (duk_tval *) duk_get_tval(ctx, idx_func + 1))); + + /* [ ... func this arg1 ... argN ] */ + + /* + * Setup a preliminary activation and figure out nargs/nregs. + * + * Don't touch valstack_bottom or valstack_top yet so that Duktape API + * calls work normally. + */ + + duk_hthread_callstack_grow(thr); + + if (thr->callstack_top > 0) { + /* + * Update idx_retval of current activation. + * + * Although it might seem this is not necessary (bytecode executor + * does this for Ecmascript-to-Ecmascript calls; other calls are + * handled here), this turns out to be necessary for handling yield + * and resume. For them, an Ecmascript-to-native call happens, and + * the Ecmascript call's idx_retval must be set for things to work. + */ + + (thr->callstack + thr->callstack_top - 1)->idx_retval = entry_valstack_bottom_index + idx_func; + } + + DUK_ASSERT(thr->callstack_top < thr->callstack_size); + act = thr->callstack + thr->callstack_top; + thr->callstack_top++; + DUK_ASSERT(thr->callstack_top <= thr->callstack_size); + DUK_ASSERT(thr->valstack_top > thr->valstack_bottom); /* at least effective 'this' */ + DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func)); + + act->flags = 0; + + /* For now all calls except Ecma-to-Ecma calls prevent a yield. */ + act->flags |= DUK_ACT_FLAG_PREVENT_YIELD; + if (call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL) { + act->flags |= DUK_ACT_FLAG_CONSTRUCT; + } + if (call_flags & DUK_CALL_FLAG_DIRECT_EVAL) { + act->flags |= DUK_ACT_FLAG_DIRECT_EVAL; + } + + /* These base values are never used, but if the compiler doesn't know + * that DUK_ERROR() won't return, these are needed to silence warnings. + * On the other hand, scan-build will warn about the values not being + * used, so add a DUK_UNREF. + */ + nargs = 0; DUK_UNREF(nargs); + nregs = 0; DUK_UNREF(nregs); + + if (DUK_LIKELY(func != NULL)) { + if (DUK_HOBJECT_HAS_STRICT(func)) { + act->flags |= DUK_ACT_FLAG_STRICT; + } + if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) { + nargs = ((duk_hcompiledfunction *) func)->nargs; + nregs = ((duk_hcompiledfunction *) func)->nregs; + DUK_ASSERT(nregs >= nargs); + } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(func)) { + /* Note: nargs (and nregs) may be negative for a native, + * function, which indicates that the function wants the + * input stack "as is" (i.e. handles "vararg" arguments). + */ + nargs = ((duk_hnativefunction *) func)->nargs; + nregs = nargs; + } else { + /* XXX: this should be an assert */ + DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_CALLABLE); + } + } else { + duk_small_uint_t lf_flags; + + DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func)); + lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv_func); + nargs = DUK_LFUNC_FLAGS_GET_NARGS(lf_flags); + if (nargs == DUK_LFUNC_NARGS_VARARGS) { + nargs = -1; /* vararg */ + } + nregs = nargs; + + act->flags |= DUK_ACT_FLAG_STRICT; + } + + act->func = func; /* NULL for lightfunc */ + act->var_env = NULL; + act->lex_env = NULL; +#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) + act->prev_caller = NULL; +#endif + act->curr_pc = NULL; +#if defined(DUK_USE_DEBUGGER_SUPPORT) + act->prev_line = 0; +#endif + act->idx_bottom = entry_valstack_bottom_index + idx_func + 2; +#if 0 /* topmost activation idx_retval is considered garbage, no need to init */ + act->idx_retval = 0; +#endif + DUK_TVAL_SET_TVAL(&act->tv_func, tv_func); /* borrowed, no refcount */ + + /* XXX: remove the preventcount and make yield walk the callstack? + * Or perhaps just use a single flag, not a counter, faster to just + * set and restore? + */ + if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) { + /* duk_hthread_callstack_unwind() will decrease this on unwind */ + thr->callstack_preventcount++; + } + + /* XXX: Is this INCREF necessary? 'func' is always a borrowed + * reference reachable through the value stack? If changed, stack + * unwind code also needs to be fixed to match. + */ + DUK_HOBJECT_INCREF_ALLOWNULL(thr, func); /* act->func */ + +#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) + if (func) { + duk__update_func_caller_prop(thr, func); + } + act = thr->callstack + thr->callstack_top - 1; +#endif + + /* [ ... func this arg1 ... argN ] */ + + /* + * Environment record creation and 'arguments' object creation. + * Named function expression name binding is handled by the + * compiler; the compiled function's parent env will contain + * the (immutable) binding already. + * + * This handling is now identical for C and Ecmascript functions. + * C functions always have the 'NEWENV' flag set, so their + * environment record initialization is delayed (which is good). + * + * Delayed creation (on demand) is handled in duk_js_var.c. + */ + + DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func)); /* bound function chain has already been resolved */ + + if (DUK_LIKELY(func != NULL)) { + if (DUK_LIKELY(DUK_HOBJECT_HAS_NEWENV(func))) { + if (DUK_LIKELY(!DUK_HOBJECT_HAS_CREATEARGS(func))) { + /* Use a new environment but there's no 'arguments' object; + * delayed environment initialization. This is the most + * common case. + */ + DUK_ASSERT(act->lex_env == NULL); + DUK_ASSERT(act->var_env == NULL); + } else { + /* Use a new environment and there's an 'arguments' object. + * We need to initialize it right now. + */ + + /* third arg: absolute index (to entire valstack) of idx_bottom of new activation */ + env = duk_create_activation_environment_record(thr, func, act->idx_bottom); + DUK_ASSERT(env != NULL); + + /* [ ... func this arg1 ... argN envobj ] */ + + DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func)); + duk__handle_createargs_for_call(thr, func, env, num_stack_args); + + /* [ ... func this arg1 ... argN envobj ] */ + + act = thr->callstack + thr->callstack_top - 1; + act->lex_env = env; + act->var_env = env; + DUK_HOBJECT_INCREF(thr, env); + DUK_HOBJECT_INCREF(thr, env); /* XXX: incref by count (2) directly */ + duk_pop(ctx); + } + } else { + /* Use existing env (e.g. for non-strict eval); cannot have + * an own 'arguments' object (but can refer to an existing one). + */ + + DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(func)); + + duk__handle_oldenv_for_call(thr, func, act); + + DUK_ASSERT(act->lex_env != NULL); + DUK_ASSERT(act->var_env != NULL); + } + } else { + /* Lightfuncs are always native functions and have "newenv". */ + DUK_ASSERT(act->lex_env == NULL); + DUK_ASSERT(act->var_env == NULL); + } + + /* [ ... func this arg1 ... argN ] */ + + /* + * Setup value stack: clamp to 'nargs', fill up to 'nregs' + * + * Value stack may either grow or shrink, depending on the + * number of func registers and the number of actual arguments. + * If nregs >= 0, func wants args clamped to 'nargs'; else it + * wants all args (= 'num_stack_args'). + */ + + /* XXX: optimize value stack operation */ + /* XXX: don't want to shrink allocation here */ + + duk__adjust_valstack_and_top(thr, + num_stack_args, + idx_func + 2, + nregs, + nargs, + func); + + /* + * Determine call type, then finalize activation, shift to + * new value stack bottom, and call the target. + */ + + if (func != NULL && DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) { + /* + * Ecmascript call + */ + + duk_tval *tv_ret; + duk_tval *tv_funret; + + DUK_ASSERT(func != NULL); + DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(func)); + act->curr_pc = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, (duk_hcompiledfunction *) func); + + thr->valstack_bottom = thr->valstack_bottom + idx_func + 2; + /* keep current valstack_top */ + DUK_ASSERT(thr->valstack_bottom >= thr->valstack); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + DUK_ASSERT(thr->valstack_end >= thr->valstack_top); + + /* [ ... func this | arg1 ... argN ] ('this' must precede new bottom) */ + + /* + * Bytecode executor call. + * + * Execute bytecode, handling any recursive function calls and + * thread resumptions. Returns when execution would return from + * the entry level activation. When the executor returns, a + * single return value is left on the stack top. + * + * The only possible longjmp() is an error (DUK_LJ_TYPE_THROW), + * other types are handled internally by the executor. + */ + + /* thr->ptr_curr_pc is set by bytecode executor early on entry */ + DUK_ASSERT(thr->ptr_curr_pc == NULL); + DUK_DDD(DUK_DDDPRINT("entering bytecode execution")); + duk_js_execute_bytecode(thr); + DUK_DDD(DUK_DDDPRINT("returned from bytecode execution")); + + /* Unwind. */ + + DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top); /* may need unwind */ + DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1); + DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1); + duk_hthread_catchstack_unwind(thr, entry_catchstack_top); + duk_hthread_catchstack_shrink_check(thr); + duk_hthread_callstack_unwind(thr, entry_callstack_top); + duk_hthread_callstack_shrink_check(thr); + + thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index; + /* keep current valstack_top */ + DUK_ASSERT(thr->valstack_bottom >= thr->valstack); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + DUK_ASSERT(thr->valstack_end >= thr->valstack_top); + DUK_ASSERT(thr->valstack_top - thr->valstack_bottom >= idx_func + 1); + + /* Return value handling. */ + + /* [ ... func this (crud) retval ] */ + + tv_ret = thr->valstack_bottom + idx_func; + tv_funret = thr->valstack_top - 1; +#if defined(DUK_USE_FASTINT) + /* Explicit check for fastint downgrade. */ + DUK_TVAL_CHKFAST_INPLACE(tv_funret); +#endif + DUK_TVAL_SET_TVAL_UPDREF(thr, tv_ret, tv_funret); /* side effects */ + } else { + /* + * Native call. + */ + + duk_tval *tv_ret; + duk_tval *tv_funret; + + thr->valstack_bottom = thr->valstack_bottom + idx_func + 2; + /* keep current valstack_top */ + DUK_ASSERT(thr->valstack_bottom >= thr->valstack); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + DUK_ASSERT(thr->valstack_end >= thr->valstack_top); + DUK_ASSERT(func == NULL || ((duk_hnativefunction *) func)->func != NULL); + + /* [ ... func this | arg1 ... argN ] ('this' must precede new bottom) */ + + /* For native calls must be NULL so we don't sync back */ + DUK_ASSERT(thr->ptr_curr_pc == NULL); + + if (func) { + rc = ((duk_hnativefunction *) func)->func((duk_context *) thr); + } else { + duk_c_function funcptr = DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv_func); + rc = funcptr((duk_context *) thr); + } + + /* Automatic error throwing, retval check. */ + + if (rc < 0) { + duk_error_throw_from_negative_rc(thr, rc); + DUK_UNREACHABLE(); + } else if (rc > 1) { + DUK_ERROR(thr, DUK_ERR_API_ERROR, "c function returned invalid rc"); + } + DUK_ASSERT(rc == 0 || rc == 1); + + /* Unwind. */ + + DUK_ASSERT(thr->catchstack_top == entry_catchstack_top); /* no need to unwind */ + DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1); + duk_hthread_callstack_unwind(thr, entry_callstack_top); + duk_hthread_callstack_shrink_check(thr); + + thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index; + /* keep current valstack_top */ + DUK_ASSERT(thr->valstack_bottom >= thr->valstack); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + DUK_ASSERT(thr->valstack_end >= thr->valstack_top); + DUK_ASSERT(thr->valstack_top - thr->valstack_bottom >= idx_func + 1); + + /* Return value handling. */ + + /* XXX: should this happen in the callee's activation or after unwinding? */ + tv_ret = thr->valstack_bottom + idx_func; + if (rc == 0) { + DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv_ret); /* side effects */ + } else { + /* [ ... func this (crud) retval ] */ + tv_funret = thr->valstack_top - 1; +#if defined(DUK_USE_FASTINT) + /* Explicit check for fastint downgrade. */ + DUK_TVAL_CHKFAST_INPLACE(tv_funret); +#endif + DUK_TVAL_SET_TVAL_UPDREF(thr, tv_ret, tv_funret); /* side effects */ + } + } + + duk_set_top(ctx, idx_func + 1); /* XXX: unnecessary, handle in adjust */ + + /* [ ... retval ] */ + + /* Ensure there is internal valstack spare before we exit; this may + * throw an alloc error. The same guaranteed size must be available + * as before the call. This is not optimal now: we store the valstack + * allocated size during entry; this value may be higher than the + * minimal guarantee for an application. + */ + + /* XXX: we should never shrink here; when we error out later, we'd + * need to potentially grow the value stack in error unwind which could + * cause another error. + */ + + (void) duk_valstack_resize_raw((duk_context *) thr, + entry_valstack_end, /* same as during entry */ + DUK_VSRESIZE_FLAG_SHRINK | /* flags */ + DUK_VSRESIZE_FLAG_COMPACT | + DUK_VSRESIZE_FLAG_THROW); + + /* Restore entry thread executor curr_pc stack frame pointer. */ + thr->ptr_curr_pc = entry_ptr_curr_pc; + + DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */ + thr->state = (duk_uint8_t) entry_thread_state; + + DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread == NULL) || /* first call */ + (thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread != NULL) || /* other call */ + (thr->state == DUK_HTHREAD_STATE_RUNNING && thr->heap->curr_thread == thr)); /* current thread */ + + thr->heap->call_recursion_depth = entry_call_recursion_depth; + + /* If the debugger is active we need to force an interrupt so that + * debugger breakpoints are rechecked. This is important for function + * calls caused by side effects (e.g. when doing a DUK_OP_GETPROP), see + * GH-303. Only needed for success path, error path always causes a + * breakpoint recheck in the executor. It would be enough to set this + * only when returning to an Ecmascript activation, but setting the flag + * on every return should have no ill effect. + */ +#if defined(DUK_USE_DEBUGGER_SUPPORT) + if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { + DUK_DD(DUK_DDPRINT("returning with debugger enabled, force interrupt")); + DUK_ASSERT(thr->interrupt_counter <= thr->interrupt_init); + thr->interrupt_init -= thr->interrupt_counter; + thr->interrupt_counter = 0; + thr->heap->dbg_force_restart = 1; + } +#endif + +#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG) + duk__interrupt_fixup(thr, entry_curr_thread); +#endif + + return; + + thread_state_error: + DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid thread state for call (%ld)", (long) thr->state); + DUK_UNREACHABLE(); + return; /* never executed */ +} + +DUK_LOCAL void duk__handle_call_error(duk_hthread *thr, + duk_size_t entry_valstack_bottom_index, + duk_size_t entry_valstack_end, + duk_size_t entry_catchstack_top, + duk_size_t entry_callstack_top, + duk_int_t entry_call_recursion_depth, + duk_hthread *entry_curr_thread, + duk_uint_fast8_t entry_thread_state, + duk_instr_t **entry_ptr_curr_pc, + duk_idx_t idx_func +#if !defined(DUK_USE_CPP_EXCEPTIONS) + , duk_jmpbuf *old_jmpbuf_ptr +#endif + ) { + duk_context *ctx; + duk_tval *tv_ret; + + ctx = (duk_context *) thr; + + DUK_DDD(DUK_DDDPRINT("error caught during duk__handle_call_inner(): %!T", + (duk_tval *) &thr->heap->lj.value1)); + + /* Other longjmp types are handled by executor before propagating + * the error here. + */ + DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW); + DUK_ASSERT(thr->callstack_top >= entry_callstack_top); + DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top); + + /* We don't need to sync back thr->ptr_curr_pc here because + * the bytecode executor always has a setjmp catchpoint which + * does that before errors propagate to here. + */ + DUK_ASSERT(thr->ptr_curr_pc == NULL); + +#if !defined(DUK_USE_CPP_EXCEPTIONS) + /* Restore the previous setjmp catcher so that any error in + * error handling will propagate outwards rather than re-enter + * the same handler. However, the error handling path must be + * designed to be error free so that sandboxing guarantees are + * reliable, see e.g. https://github.com/svaarala/duktape/issues/476. + */ + thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr; +#endif + + /* XXX: callstack unwind may now throw an error when closing + * scopes; this is a sandboxing issue, described in: + * https://github.com/svaarala/duktape/issues/476 + */ + duk_hthread_catchstack_unwind(thr, entry_catchstack_top); + duk_hthread_catchstack_shrink_check(thr); + duk_hthread_callstack_unwind(thr, entry_callstack_top); + duk_hthread_callstack_shrink_check(thr); + + thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index; + tv_ret = thr->valstack_bottom + idx_func; /* XXX: byte offset? */ + DUK_TVAL_SET_TVAL_UPDREF(thr, tv_ret, &thr->heap->lj.value1); /* side effects */ +#if defined(DUK_USE_FASTINT) + /* Explicit check for fastint downgrade. */ + DUK_TVAL_CHKFAST_INPLACE(tv_ret); +#endif + duk_set_top(ctx, idx_func + 1); /* XXX: could be eliminated with valstack adjust */ + + /* [ ... errobj ] */ + + /* Ensure there is internal valstack spare before we exit; this may + * throw an alloc error. The same guaranteed size must be available + * as before the call. This is not optimal now: we store the valstack + * allocated size during entry; this value may be higher than the + * minimal guarantee for an application. + */ + + /* XXX: this needs to be reworked so that we never shrink the value + * stack on function entry so that we never need to grow it here. + * Needing to grow here is a sandboxing issue because we need to + * allocate which may cause an error in the error handling path + * and thus propagate an error out of a protected call. + */ + + (void) duk_valstack_resize_raw((duk_context *) thr, + entry_valstack_end, /* same as during entry */ + DUK_VSRESIZE_FLAG_SHRINK | /* flags */ + DUK_VSRESIZE_FLAG_COMPACT | + DUK_VSRESIZE_FLAG_THROW); + + + /* These are just convenience "wiping" of state. Side effects should + * not be an issue here: thr->heap and thr->heap->lj have a stable + * pointer. Finalizer runs etc capture even out-of-memory errors so + * nothing should throw here. + */ + thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN; + thr->heap->lj.iserror = 0; + DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value1); /* side effects */ + DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value2); /* side effects */ + + /* Restore entry thread executor curr_pc stack frame pointer. */ + thr->ptr_curr_pc = entry_ptr_curr_pc; + + DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */ + thr->state = (duk_uint8_t) entry_thread_state; + + DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread == NULL) || /* first call */ + (thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread != NULL) || /* other call */ + (thr->state == DUK_HTHREAD_STATE_RUNNING && thr->heap->curr_thread == thr)); /* current thread */ + + thr->heap->call_recursion_depth = entry_call_recursion_depth; + + /* If the debugger is active we need to force an interrupt so that + * debugger breakpoints are rechecked. This is important for function + * calls caused by side effects (e.g. when doing a DUK_OP_GETPROP), see + * GH-303. Only needed for success path, error path always causes a + * breakpoint recheck in the executor. It would be enough to set this + * only when returning to an Ecmascript activation, but setting the flag + * on every return should have no ill effect. + */ +#if defined(DUK_USE_DEBUGGER_SUPPORT) + if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { + DUK_DD(DUK_DDPRINT("returning with debugger enabled, force interrupt")); + DUK_ASSERT(thr->interrupt_counter <= thr->interrupt_init); + thr->interrupt_init -= thr->interrupt_counter; + thr->interrupt_counter = 0; + thr->heap->dbg_force_restart = 1; + } +#endif + +#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG) + duk__interrupt_fixup(thr, entry_curr_thread); +#endif +} + +/* + * duk_handle_safe_call(): make a "C protected call" within the + * current activation. * * The allowed thread states for making a call are the same as for - * duk_handle_call(). + * duk_handle_call_xxx(). * - * Note that like duk_handle_call(), even if this call is protected, - * there are a few situations where the current (pre-entry) setjmp - * catcher (or a fatal error handler if no such catcher exists) is - * invoked: - * - * - Blatant API argument errors (e.g. num_stack_args is invalid, - * so we can't form a reasonable return stack) - * - * - Errors during error handling, e.g. failure to reallocate - * space in the value stack due to an alloc error - * - * Such errors propagate outwards, ultimately to the fatal error - * handler if nothing else. + * Error handling is similar to duk_handle_call_xxx(); errors may be thrown + * (and result in a fatal error) for insane arguments. */ /* XXX: bump preventcount by one for the duration of this call? */ -DUK_INTERNAL -duk_int_t duk_handle_safe_call(duk_hthread *thr, - duk_safe_call_function func, - duk_idx_t num_stack_args, - duk_idx_t num_stack_rets) { +DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr, + duk_safe_call_function func, + duk_idx_t num_stack_args, + duk_idx_t num_stack_rets) { duk_context *ctx = (duk_context *) thr; duk_size_t entry_valstack_bottom_index; duk_size_t entry_callstack_top; @@ -58403,11 +58590,12 @@ duk_int_t duk_handle_safe_call(duk_hthread *thr, duk_hthread *entry_curr_thread; duk_uint_fast8_t entry_thread_state; duk_instr_t **entry_ptr_curr_pc; +#if !defined(DUK_USE_CPP_EXCEPTIONS) duk_jmpbuf *old_jmpbuf_ptr = NULL; duk_jmpbuf our_jmpbuf; +#endif duk_idx_t idx_retbase; duk_int_t retval; - duk_ret_t rc; DUK_ASSERT(thr != NULL); DUK_ASSERT(ctx != NULL); @@ -58442,10 +58630,9 @@ duk_int_t duk_handle_safe_call(duk_hthread *thr, (long) entry_thread_state)); if (idx_retbase < 0) { - /* - * Since stack indices are not reliable, we can't do anything useful - * here. Invoke the existing setjmp catcher, or if it doesn't exist, - * call the fatal error handler. + /* Since stack indices are not reliable, we can't do anything useful + * here. Invoke the existing setjmp catcher, or if it doesn't exist, + * call the fatal error handler. */ DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS); @@ -58453,74 +58640,136 @@ duk_int_t duk_handle_safe_call(duk_hthread *thr, /* setjmp catchpoint setup */ +#if !defined(DUK_USE_CPP_EXCEPTIONS) old_jmpbuf_ptr = thr->heap->lj.jmpbuf_ptr; thr->heap->lj.jmpbuf_ptr = &our_jmpbuf; +#endif - if (DUK_SETJMP(thr->heap->lj.jmpbuf_ptr->jb) == 0) { - goto handle_call; +#if defined(DUK_USE_CPP_EXCEPTIONS) + try { +#else + if (DUK_LIKELY(DUK_SETJMP(thr->heap->lj.jmpbuf_ptr->jb) == 0)) { + /* Success path. */ +#endif + DUK_DDD(DUK_DDDPRINT("safe_call setjmp catchpoint setup complete")); + + duk__handle_safe_call_inner(thr, + func, + idx_retbase, + num_stack_rets, + entry_valstack_bottom_index, + entry_callstack_top, + entry_catchstack_top); + + /* Longjmp state is kept clean in success path */ + DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN); + DUK_ASSERT(thr->heap->lj.iserror == 0); + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1)); + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2)); + +#if !defined(DUK_USE_CPP_EXCEPTIONS) + /* Note: either pointer may be NULL (at entry), so don't assert */ + thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr; +#endif + + retval = DUK_EXEC_SUCCESS; +#if defined(DUK_USE_CPP_EXCEPTIONS) + } catch (duk_internal_exception &exc) { +#else + } else { + /* Error path. */ +#endif + +#if defined(DUK_USE_CPP_EXCEPTIONS) + DUK_UNREF(exc); +#endif + + duk__handle_safe_call_error(thr, + idx_retbase, + num_stack_rets, + entry_valstack_bottom_index, + entry_callstack_top, + entry_catchstack_top +#if !defined(DUK_USE_CPP_EXCEPTIONS) + , old_jmpbuf_ptr +#endif + ); + + /* Longjmp state is cleaned up by error handling */ + DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN); + DUK_ASSERT(thr->heap->lj.iserror == 0); + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1)); + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2)); + + retval = DUK_EXEC_ERROR; } +#if defined(DUK_USE_CPP_EXCEPTIONS) + catch (std::exception &exc) { + const char *what = exc.what(); + if (!what) { + what = "unknown"; + } + DUK_D(DUK_DPRINT("unexpected c++ std::exception (perhaps thrown by user code)")); + try { + DUK_ERROR(thr, DUK_ERR_API_ERROR, "caught invalid c++ std::exception '%s' (perhaps thrown by user code)", what); + } catch (duk_internal_exception exc) { + DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ std::exception")); + duk__handle_safe_call_error(thr, + idx_retbase, + num_stack_rets, + entry_valstack_bottom_index, + entry_callstack_top, + entry_catchstack_top); + retval = DUK_EXEC_ERROR; + } + } catch (...) { + DUK_D(DUK_DPRINT("unexpected c++ exception (perhaps thrown by user code)")); + try { + DUK_ERROR(thr, DUK_ERR_API_ERROR, "caught invalid c++ exception (perhaps thrown by user code)"); + } catch (duk_internal_exception exc) { + DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ exception")); + duk__handle_safe_call_error(thr, + idx_retbase, + num_stack_rets, + entry_valstack_bottom_index, + entry_callstack_top, + entry_catchstack_top); + retval = DUK_EXEC_ERROR; + } + } +#endif - /* - * Error during call. The error value is at heap->lj.value1. - * - * Careful with variable accesses here; must be assigned to before - * setjmp() or be declared volatile. See duk_handle_call(). - * - * The following are such variables: - * - duk_handle_safe_call() parameters - * - entry_* - * - idx_retbase - * - * The very first thing we do is restore the previous setjmp catcher. - * This means that any error in error handling will propagate outwards - * instead of causing a setjmp() re-entry above. The *only* actual - * errors that should happen here are allocation errors. - */ +#if !defined(DUK_USE_CPP_EXCEPTIONS) + DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == old_jmpbuf_ptr); /* success/error path both do this */ +#endif - DUK_DDD(DUK_DDDPRINT("error caught during protected duk_handle_safe_call()")); + duk__handle_safe_call_shared(thr, + idx_retbase, + num_stack_rets, + entry_call_recursion_depth, + entry_curr_thread, + entry_thread_state, + entry_ptr_curr_pc); - DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW); - DUK_ASSERT(thr->callstack_top >= entry_callstack_top); - DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top); + return retval; +} - /* Note: either pointer may be NULL (at entry), so don't assert; - * these are now restored twice which is OK. - */ - thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr; +DUK_LOCAL void duk__handle_safe_call_inner(duk_hthread *thr, + duk_safe_call_function func, + duk_idx_t idx_retbase, + duk_idx_t num_stack_rets, + duk_size_t entry_valstack_bottom_index, + duk_size_t entry_callstack_top, + duk_size_t entry_catchstack_top) { + duk_context *ctx; + duk_ret_t rc; - duk_hthread_catchstack_unwind(thr, entry_catchstack_top); - duk_hthread_callstack_unwind(thr, entry_callstack_top); - thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index; - - /* [ ... | (crud) ] */ - - /* XXX: space in valstack? see discussion in duk_handle_call. */ - duk_push_tval(ctx, &thr->heap->lj.value1); - - /* [ ... | (crud) errobj ] */ - - DUK_ASSERT(duk_get_top(ctx) >= 1); /* at least errobj must be on stack */ - - /* check that the valstack has space for the final amount and any - * intermediate space needed; this is unoptimal but should be safe - */ - duk_require_stack_top(ctx, idx_retbase + num_stack_rets); /* final configuration */ - duk_require_stack(ctx, num_stack_rets); - - duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, 1); /* 1 = num actual 'return values' */ - - /* [ ... | ] or [ ... | errobj (M * undefined)] where M = num_stack_rets - 1 */ - - retval = DUK_EXEC_ERROR; - goto shrink_and_finished; - - /* - * Handle call (inside setjmp) - */ - - handle_call: - - DUK_DDD(DUK_DDDPRINT("safe_call setjmp catchpoint setup complete")); + DUK_ASSERT(thr != NULL); + ctx = (duk_context *) thr; + DUK_ASSERT_CTX_VALID(ctx); + DUK_UNREF(entry_valstack_bottom_index); + DUK_UNREF(entry_callstack_top); + DUK_UNREF(entry_catchstack_top); /* * Thread state check and book-keeping. @@ -58583,7 +58832,7 @@ duk_int_t duk_handle_safe_call(duk_hthread *thr, DUK_DDD(DUK_DDDPRINT("safe_call, func rc=%ld", (long) rc)); /* - * Valstack manipulation for results + * Valstack manipulation for results. */ /* we're running inside the caller's activation, so no change in call/catch stack or valstack bottom */ @@ -58603,36 +58852,108 @@ duk_int_t duk_handle_safe_call(duk_hthread *thr, DUK_ERROR_API(thr, "not enough stack values for safe_call rc"); } + DUK_ASSERT(thr->catchstack_top == entry_catchstack_top); /* no need to unwind */ + DUK_ASSERT(thr->callstack_top == entry_callstack_top); + duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, rc); + return; - /* Note: no need from callstack / catchstack shrink check */ - retval = DUK_EXEC_SUCCESS; - goto finished; + thread_state_error: + DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid thread state for safe_call (%ld)", (long) thr->state); + DUK_UNREACHABLE(); +} - shrink_and_finished: - /* these are "soft" shrink checks, whose failures are ignored */ - /* XXX: would be nice if fast path was inlined */ - duk_hthread_catchstack_shrink_check(thr); - duk_hthread_callstack_shrink_check(thr); - goto finished; +DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr, + duk_idx_t idx_retbase, + duk_idx_t num_stack_rets, + duk_size_t entry_valstack_bottom_index, + duk_size_t entry_callstack_top, + duk_size_t entry_catchstack_top +#if !defined(DUK_USE_CPP_EXCEPTIONS) + , duk_jmpbuf *old_jmpbuf_ptr +#endif + ) { + duk_context *ctx; - finished: - /* Note: either pointer may be NULL (at entry), so don't assert */ + DUK_ASSERT(thr != NULL); + ctx = (duk_context *) thr; + DUK_ASSERT_CTX_VALID(ctx); + + /* + * Error during call. The error value is at heap->lj.value1. + * + * The very first thing we do is restore the previous setjmp catcher. + * This means that any error in error handling will propagate outwards + * instead of causing a setjmp() re-entry above. + */ + + DUK_DDD(DUK_DDDPRINT("error caught during protected duk_handle_safe_call()")); + + /* Other longjmp types are handled by executor before propagating + * the error here. + */ + DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW); + DUK_ASSERT(thr->callstack_top >= entry_callstack_top); + DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top); + + /* Note: either pointer may be NULL (at entry), so don't assert. */ +#if !defined(DUK_USE_CPP_EXCEPTIONS) thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr; +#endif - /* These are just convenience "wiping" of state */ + DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top); + DUK_ASSERT(thr->callstack_top >= entry_callstack_top); + duk_hthread_catchstack_unwind(thr, entry_catchstack_top); + duk_hthread_catchstack_shrink_check(thr); + duk_hthread_callstack_unwind(thr, entry_callstack_top); + duk_hthread_callstack_shrink_check(thr); + thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index; + + /* [ ... | (crud) ] */ + + /* XXX: space in valstack? see discussion in duk_handle_call_xxx(). */ + duk_push_tval(ctx, &thr->heap->lj.value1); + + /* [ ... | (crud) errobj ] */ + + DUK_ASSERT(duk_get_top(ctx) >= 1); /* at least errobj must be on stack */ + + /* check that the valstack has space for the final amount and any + * intermediate space needed; this is unoptimal but should be safe + */ + duk_require_stack_top(ctx, idx_retbase + num_stack_rets); /* final configuration */ + duk_require_stack(ctx, num_stack_rets); + + duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, 1); /* 1 = num actual 'return values' */ + + /* [ ... | ] or [ ... | errobj (M * undefined)] where M = num_stack_rets - 1 */ + + /* These are just convenience "wiping" of state. Side effects should + * not be an issue here: thr->heap and thr->heap->lj have a stable + * pointer. Finalizer runs etc capture even out-of-memory errors so + * nothing should throw here. + */ thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN; thr->heap->lj.iserror = 0; - - /* Side effects should not be an issue here: tv_tmp is local and - * thr->heap (and thr->heap->lj) have a stable pointer. Finalizer - * runs etc capture even out-of-memory errors so nothing should - * throw here. - */ DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value1); /* side effects */ DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value2); /* side effects */ +} - DUK_DDD(DUK_DDDPRINT("setjmp catchpoint torn down")); +DUK_LOCAL void duk__handle_safe_call_shared(duk_hthread *thr, + duk_idx_t idx_retbase, + duk_idx_t num_stack_rets, + duk_int_t entry_call_recursion_depth, + duk_hthread *entry_curr_thread, + duk_uint_fast8_t entry_thread_state, + duk_instr_t **entry_ptr_curr_pc) { + duk_context *ctx; + + DUK_ASSERT(thr != NULL); + ctx = (duk_context *) thr; + DUK_ASSERT_CTX_VALID(ctx); + DUK_UNREF(ctx); + DUK_UNREF(idx_retbase); + DUK_UNREF(num_stack_rets); /* Restore entry thread executor curr_pc stack frame pointer. */ thr->ptr_curr_pc = entry_ptr_curr_pc; @@ -58662,13 +58983,6 @@ duk_int_t duk_handle_safe_call(duk_hthread *thr, #if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG) duk__interrupt_fixup(thr, entry_curr_thread); #endif - - return retval; - - thread_state_error: - DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid thread state for safe_call (%ld)", (long) thr->state); - DUK_UNREACHABLE(); - return DUK_EXEC_ERROR; /* never executed */ } /* @@ -58697,10 +59011,9 @@ duk_int_t duk_handle_safe_call(duk_hthread *thr, * return an error so caller can fall back to a normal call path. */ -DUK_INTERNAL -duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, - duk_idx_t num_stack_args, - duk_small_uint_t call_flags) { +DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, + duk_idx_t num_stack_args, + duk_small_uint_t call_flags) { duk_context *ctx = (duk_context *) thr; duk_size_t entry_valstack_bottom_index; duk_idx_t idx_func; /* valstack index of 'func' and retval (relative to entry valstack_bottom) */ @@ -58740,7 +59053,7 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, * - an Ecmascript activation must be on top of the callstack * - there cannot be any active catchstack entries */ -#ifdef DUK_USE_ASSERTIONS +#if defined(DUK_USE_ASSERTIONS) if (call_flags & DUK_CALL_FLAG_IS_TAILCALL) { duk_size_t our_callstack_index; duk_size_t i; @@ -58766,6 +59079,7 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, #endif /* DUK_USE_ASSERTIONS */ entry_valstack_bottom_index = (duk_size_t) (thr->valstack_bottom - thr->valstack); + /* XXX: rework */ idx_func = duk_normalize_index(thr, -num_stack_args - 2); idx_args = idx_func + 2; @@ -58781,7 +59095,7 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, (long) idx_args, (long) entry_valstack_bottom_index)); - if (idx_func < 0 || idx_args < 0) { + if (DUK_UNLIKELY(idx_func < 0 || idx_args < 0)) { /* XXX: assert? compiler is responsible for this never happening */ DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS); } @@ -58916,7 +59230,7 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, /* Start filling in the activation */ act->func = func; /* don't want an intermediate exposed state with func == NULL */ -#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY +#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) act->prev_caller = NULL; #endif DUK_ASSERT(func != NULL); @@ -58927,13 +59241,13 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, act->prev_line = 0; #endif DUK_TVAL_SET_OBJECT(&act->tv_func, func); /* borrowed, no refcount */ -#ifdef DUK_USE_REFERENCE_COUNTING +#if defined(DUK_USE_REFERENCE_COUNTING) DUK_HOBJECT_INCREF(thr, func); act = thr->callstack + thr->callstack_top - 1; /* side effects (currently none though) */ #endif -#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY -#ifdef DUK_USE_TAILCALL +#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) +#if defined(DUK_USE_TAILCALL) #error incorrect options: tail calls enabled with function caller property #endif /* XXX: this doesn't actually work properly for tail calls, so @@ -59019,7 +59333,7 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, act->func = func; act->var_env = NULL; act->lex_env = NULL; -#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY +#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) act->prev_caller = NULL; #endif DUK_ASSERT(func != NULL); @@ -59037,14 +59351,14 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, DUK_HOBJECT_INCREF(thr, func); /* act->func */ -#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY +#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) duk__update_func_caller_prop(thr, func); act = thr->callstack + thr->callstack_top - 1; #endif } - /* [... func this arg1 ... argN] (not tail call) - * [this | arg1 ... argN] (tail call) + /* [ ... func this arg1 ... argN ] (not tail call) + * [ this | arg1 ... argN ] (tail call) * * idx_args updated to match */ @@ -59058,6 +59372,8 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, * Delayed creation (on demand) is handled in duk_js_var.c. */ + /* XXX: unify handling with native call. */ + DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func)); /* bound function chain has already been resolved */ if (!DUK_HOBJECT_HAS_NEWENV(func)) { @@ -59085,7 +59401,7 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, env = duk_create_activation_environment_record(thr, func, act->idx_bottom); DUK_ASSERT(env != NULL); - /* [... arg1 ... argN envobj] */ + /* [ ... arg1 ... argN envobj ] */ /* original input stack before nargs/nregs handling must be * intact for 'arguments' object @@ -59093,7 +59409,7 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func)); duk__handle_createargs_for_call(thr, func, env, num_stack_args); - /* [... arg1 ... argN envobj] */ + /* [ ... arg1 ... argN envobj ] */ act = thr->callstack + thr->callstack_top - 1; act->lex_env = env; @@ -59103,7 +59419,7 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, duk_pop(ctx); env_done: - /* [... arg1 ... argN] */ + /* [ ... arg1 ... argN ] */ /* * Setup value stack: clamp to 'nargs', fill up to 'nregs' @@ -60056,8 +60372,8 @@ DUK_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx, duk_boo */ DUK_ASSERT(func->temp_max >= 0); - h_res->nregs = func->temp_max; - h_res->nargs = duk_hobject_get_length(thr, func->h_argnames); + h_res->nregs = (duk_uint16_t) func->temp_max; + h_res->nargs = (duk_uint16_t) duk_hobject_get_length(thr, func->h_argnames); DUK_ASSERT(h_res->nregs >= h_res->nargs); /* pass2 allocation handles this */ #if defined(DUK_USE_DEBUGGER_SUPPORT) h_res->start_line = (duk_uint32_t) func->min_line; @@ -67407,7 +67723,7 @@ DUK_LOCAL void duk__vm_arith_unary_op(duk_hthread *thr, duk_tval *tv_x, duk_idx_ #endif } -DUK_LOCAL void duk__vm_bitwise_not(duk_hthread *thr, duk_tval *tv_x, duk_small_uint_fast_t idx_z) { +DUK_LOCAL void duk__vm_bitwise_not(duk_hthread *thr, duk_tval *tv_x, duk_uint_fast_t idx_z) { /* * E5 Section 11.4.8 */ @@ -68311,6 +68627,9 @@ DUK_LOCAL duk_small_uint_t duk__handle_return(duk_hthread *thr, * Return value is already on the stack top: [ ... retval ]. */ + /* XXX: could unwind catchstack here, so that call handling + * didn't need to do that? + */ DUK_DDD(DUK_DDDPRINT("-> return propagated up to entry level, exit bytecode executor")); return DUK__RETHAND_FINISHED; } @@ -68895,16 +69214,66 @@ DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation * thr->ptr_curr_pc = NULL; \ } while (0) +DUK_LOCAL void duk__handle_executor_error(duk_heap *heap, + duk_hthread *entry_thread, + duk_size_t entry_callstack_top, + duk_int_t entry_call_recursion_depth +#if !defined(DUK_USE_CPP_EXCEPTIONS) + , duk_jmpbuf *entry_jmpbuf_ptr +#endif + ) { + duk_small_uint_t lj_ret; + + /* Longjmp callers are required to sync-and-null thr->ptr_curr_pc + * before longjmp. + */ + DUK_ASSERT(heap->curr_thread != NULL); + DUK_ASSERT(heap->curr_thread->ptr_curr_pc == NULL); + + /* XXX: signalling the need to shrink check (only if unwound) */ + + /* Must be restored here to handle e.g. yields properly. */ + heap->call_recursion_depth = entry_call_recursion_depth; + +#if !defined(DUK_USE_CPP_EXCEPTIONS) + /* Switch to caller's setjmp() catcher so that if an error occurs + * during error handling, it is always propagated outwards instead + * of causing an infinite loop in our own handler. + */ + heap->lj.jmpbuf_ptr = (duk_jmpbuf *) entry_jmpbuf_ptr; +#endif + + lj_ret = duk__handle_longjmp(heap->curr_thread, entry_thread, entry_callstack_top); + + if (lj_ret == DUK__LONGJMP_RESTART) { + /* Restart bytecode execution, possibly with a changed thread. */ + ; + } else { + /* Rethrow error to calling state. */ + DUK_ASSERT(lj_ret == DUK__LONGJMP_RETHROW); + +#if !defined(DUK_USE_CPP_EXCEPTIONS) + /* Longjmp handling has restored jmpbuf_ptr. */ + DUK_ASSERT(heap->lj.jmpbuf_ptr == entry_jmpbuf_ptr); +#endif + + /* Thread may have changed, e.g. YIELD converted to THROW. */ + duk_err_longjmp(heap->curr_thread); + DUK_UNREACHABLE(); + } +} + /* Outer executor with setjmp/longjmp handling. */ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { /* Entry level info. */ duk_hthread *entry_thread; duk_size_t entry_callstack_top; duk_int_t entry_call_recursion_depth; +#if !defined(DUK_USE_CPP_EXCEPTIONS) duk_jmpbuf *entry_jmpbuf_ptr; - - duk_heap *heap; duk_jmpbuf jmpbuf; +#endif + duk_heap *heap; DUK_ASSERT(exec_thr != NULL); DUK_ASSERT(exec_thr->heap != NULL); @@ -68918,7 +69287,9 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { heap = entry_thread->heap; entry_callstack_top = entry_thread->callstack_top; entry_call_recursion_depth = entry_thread->heap->call_recursion_depth; +#if !defined(DUK_USE_CPP_EXCEPTIONS) entry_jmpbuf_ptr = entry_thread->heap->lj.jmpbuf_ptr; +#endif /* * Note: we currently assume that the setjmp() catchpoint is @@ -68930,55 +69301,83 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { */ for (;;) { +#if !defined(DUK_USE_CPP_EXCEPTIONS) heap->lj.jmpbuf_ptr = &jmpbuf; DUK_ASSERT(heap->lj.jmpbuf_ptr != NULL); +#endif - if (DUK_SETJMP(heap->lj.jmpbuf_ptr->jb) != 0) { - duk_small_uint_t lj_ret; - - DUK_DDD(DUK_DDDPRINT("longjmp caught by bytecode executor")); - - /* Longjmp callers are required to sync-and-null thr->ptr_curr_pc - * before longjmp. - */ - DUK_ASSERT(heap->curr_thread != NULL); - DUK_ASSERT(heap->curr_thread->ptr_curr_pc == NULL); - - /* XXX: signalling the need to shrink check (only if unwound) */ - - /* Must be restored here to handle e.g. yields properly. */ - heap->call_recursion_depth = entry_call_recursion_depth; - - /* Switch to caller's setjmp() catcher so that if an error occurs - * during error handling, it is always propagated outwards instead - * of causing an infinite loop in our own handler. - */ - heap->lj.jmpbuf_ptr = (duk_jmpbuf *) entry_jmpbuf_ptr; - - lj_ret = duk__handle_longjmp(heap->curr_thread, entry_thread, entry_callstack_top); - - if (lj_ret == DUK__LONGJMP_RESTART) { - /* Restart bytecode execution, possibly with a changed thread. */ - ; - } else { - /* Rethrow error to calling state. */ - DUK_ASSERT(lj_ret == DUK__LONGJMP_RETHROW); - - /* Longjmp handling has restored jmpbuf_ptr. */ - DUK_ASSERT(heap->lj.jmpbuf_ptr == entry_jmpbuf_ptr); - - /* Thread may have changed, e.g. YIELD converted to THROW. */ - duk_err_longjmp(heap->curr_thread); - DUK_UNREACHABLE(); - } - } else { +#if defined(DUK_USE_CPP_EXCEPTIONS) + try { +#else + if (DUK_LIKELY(DUK_SETJMP(heap->lj.jmpbuf_ptr->jb) == 0)) { +#endif /* Execute bytecode until returned or longjmp(). */ duk__js_execute_bytecode_inner(entry_thread, entry_callstack_top); +#if !defined(DUK_USE_CPP_EXCEPTIONS) /* Successful return: restore jmpbuf and return to caller. */ heap->lj.jmpbuf_ptr = (duk_jmpbuf *) entry_jmpbuf_ptr; +#endif + return; +#if defined(DUK_USE_CPP_EXCEPTIONS) + } catch (duk_internal_exception &exc) { +#else + } else { +#endif +#if defined(DUK_USE_CPP_EXCEPTIONS) + DUK_UNREF(exc); +#endif + DUK_DDD(DUK_DDDPRINT("longjmp caught by bytecode executor")); + + duk__handle_executor_error(heap, + entry_thread, + entry_callstack_top, + entry_call_recursion_depth +#if !defined(DUK_USE_CPP_EXCEPTIONS) + , entry_jmpbuf_ptr +#endif + ); } +#if defined(DUK_USE_CPP_EXCEPTIONS) + catch (std::exception &exc) { + const char *what = exc.what(); + if (!what) { + what = "unknown"; + } + DUK_D(DUK_DPRINT("unexpected c++ std::exception (perhaps thrown by user code)")); + try { + DUK_ASSERT(heap->curr_thread != NULL); + DUK_ERROR(heap->curr_thread, DUK_ERR_API_ERROR, "caught invalid c++ std::exception '%s' (perhaps thrown by user code)", what); + } catch (duk_internal_exception exc) { + DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ std::exception")); + duk__handle_executor_error(heap, + entry_thread, + entry_callstack_top, + entry_call_recursion_depth +#if !defined(DUK_USE_CPP_EXCEPTIONS) + , entry_jmpbuf_ptr +#endif + ); + } + } catch (...) { + DUK_D(DUK_DPRINT("unexpected c++ exception (perhaps thrown by user code)")); + try { + DUK_ASSERT(heap->curr_thread != NULL); + DUK_ERROR(heap->curr_thread, DUK_ERR_API_ERROR, "caught invalid c++ exception (perhaps thrown by user code)"); + } catch (duk_internal_exception exc) { + DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ exception")); + duk__handle_executor_error(heap, + entry_thread, + entry_callstack_top, + entry_call_recursion_depth +#if !defined(DUK_USE_CPP_EXCEPTIONS) + , entry_jmpbuf_ptr +#endif + ); + } + } +#endif } DUK_UNREACHABLE(); @@ -70238,9 +70637,9 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th */ DUK_ASSERT(DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv_func) != duk_bi_global_object_eval); - duk_handle_call(thr, - num_stack_args, - call_flags); + duk_handle_call_unprotected(thr, + num_stack_args, + call_flags); /* duk_js_call.c is required to restore the stack reserve * so we only need to reset the top. @@ -70284,9 +70683,9 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th } } - duk_handle_call(thr, - num_stack_args, - call_flags); + duk_handle_call_unprotected(thr, + num_stack_args, + call_flags); /* duk_js_call.c is required to restore the stack reserve * so we only need to reset the top. @@ -71136,7 +71535,9 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th duk_err_setup_heap_ljstate(thr, (duk_small_int_t) cont_type); +#if !defined(DUK_USE_CPP_EXCEPTIONS) DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* always in executor */ +#endif duk_err_longjmp(thr); DUK_UNREACHABLE(); } @@ -71173,7 +71574,9 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_THROW); +#if !defined(DUK_USE_CPP_EXCEPTIONS) DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* always in executor */ +#endif duk_err_longjmp(thr); DUK_UNREACHABLE(); break; @@ -74846,7 +75249,7 @@ DUK_LOCAL void duk__fill_lexer_buffer(duk_lexer_ctx *lex_ctx, duk_small_uint_t s lex_ctx->input_offset = (duk_size_t) (p - lex_ctx->input); lex_ctx->input_line = input_line; - DUK_ERROR(lex_ctx->thr, DUK_ERR_SYNTAX_ERROR, "char decode failed"); + DUK_ERROR(lex_ctx->thr, DUK_ERR_SYNTAX_ERROR, "utf8 decode failed"); } DUK_LOCAL void duk__advance_bytes(duk_lexer_ctx *lex_ctx, duk_small_uint_t count_bytes) { @@ -75006,7 +75409,7 @@ DUK_LOCAL duk_codepoint_t duk__read_char(duk_lexer_ctx *lex_ctx) { error_clipped: /* clipped codepoint */ error_encoding: /* invalid codepoint encoding or codepoint */ - DUK_ERROR(lex_ctx->thr, DUK_ERR_SYNTAX_ERROR, "char decode failed"); + DUK_ERROR(lex_ctx->thr, DUK_ERR_SYNTAX_ERROR, "utf8 decode failed"); return 0; } @@ -77784,7 +78187,7 @@ DUK_LOCAL duk_small_int_t duk__dragon4_fixed_format_round(duk__numconv_stringify DUK_DDD(DUK_DDDPRINT("digit before carry: %ld", (long) t)); if (++t < nc_ctx->B) { DUK_DDD(DUK_DDDPRINT("rounding carry terminated")); - *p = t; + *p = (duk_uint8_t) t; break; } @@ -87717,4 +88120,3 @@ DUK_INTERNAL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr) { return t; } -#endif diff --git a/javascript/duktape/duktape.h b/javascript/duktape/duktape.h index d672edeba..7f7a7c327 100644 --- a/javascript/duktape/duktape.h +++ b/javascript/duktape/duktape.h @@ -1,12 +1,12 @@ /* - * Duktape public API for Duktape 1.3.99. + * Duktape public API for Duktape 1.4.0. * See the API reference for documentation on call semantics. * The exposed API is inside the DUK_API_PUBLIC_H_INCLUDED * include guard. Other parts of the header are Duktape * internal and related to platform/compiler/feature detection. * - * Git commit b7b1c5fd2d1d4550140d57e05a7b32f540082bfa (v1.3.0-383-gb7b1c5f). - * Git branch duk-config-improvements. + * Git commit cad6f595382a0cc1a7e4207794ade5be11b3e397 (v1.4.0). + * Git branch master. * * See Duktape AUTHORS.rst and LICENSE.txt for copyright and * licensing information. @@ -20,7 +20,7 @@ * * (http://opensource.org/licenses/MIT) * - * Copyright (c) 2013-2015 by Duktape authors (see AUTHORS.rst) + * Copyright (c) 2013-2016 by Duktape authors (see AUTHORS.rst) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -71,6 +71,7 @@ * * Legimet * * Karl Skomski * * Bruce Pascoe + * * Ren\u00e9 Hollander * * Other contributions * =================== @@ -107,6 +108,7 @@ * * https://github.com/sstruchtrup * * Michael Drake (https://github.com/tlsa) * * https://github.com/chris-y + * * Laurent Zubiaur (https://github.com/lzubiaur) * * If you are accidentally missing from this list, send me an e-mail * (``sami.vaarala@iki.fi``) and I'll fix the omission. @@ -211,16 +213,16 @@ struct duk_number_list_entry { * have 99 for patch level (e.g. 0.10.99 would be a development version * after 0.10.0 but before the next official release). */ -#define DUK_VERSION 10399L +#define DUK_VERSION 10400L /* Git commit, describe, and branch for Duktape build. Useful for * non-official snapshot builds so that application code can easily log * which Duktape snapshot was used. Not available in the Ecmascript * environment. */ -#define DUK_GIT_COMMIT "b7b1c5fd2d1d4550140d57e05a7b32f540082bfa" -#define DUK_GIT_DESCRIBE "v1.3.0-383-gb7b1c5f" -#define DUK_GIT_BRANCH "duk-config-improvements" +#define DUK_GIT_COMMIT "cad6f595382a0cc1a7e4207794ade5be11b3e397" +#define DUK_GIT_DESCRIBE "v1.4.0" +#define DUK_GIT_BRANCH "master" /* Duktape debug protocol version used by this build. */ #define DUK_DEBUG_PROTOCOL_VERSION 1 @@ -556,7 +558,7 @@ DUK_EXTERNAL_DECL duk_idx_t duk_push_error_object_va_raw(duk_context *ctx, duk_e DUK_EXTERNAL_DECL void *duk_push_buffer_raw(duk_context *ctx, duk_size_t size, duk_small_uint_t flags); #define duk_push_buffer(ctx,size,dynamic) \ - duk_push_buffer_raw((ctx), (size), (dynamic) ? DUK_BUF_FLAG_DYNAMIC : 0); + duk_push_buffer_raw((ctx), (size), (dynamic) ? DUK_BUF_FLAG_DYNAMIC : 0) #define duk_push_fixed_buffer(ctx,size) \ duk_push_buffer_raw((ctx), (size), 0 /*flags*/) #define duk_push_dynamic_buffer(ctx,size) \