diff --git a/test-app/app/src/main/assets/internal/ts_helpers.js b/test-app/app/src/main/assets/internal/ts_helpers.js index 8ad7bbc7..83823ac8 100644 --- a/test-app/app/src/main/assets/internal/ts_helpers.js +++ b/test-app/app/src/main/assets/internal/ts_helpers.js @@ -390,4 +390,99 @@ }, }); } + + const pendingUnhandledRejections = []; + const hasBeenNotifiedProperty = new WeakMap(); + function emitPendingUnhandledRejections() { + while (pendingUnhandledRejections.length > 0) { + var promise = pendingUnhandledRejections.shift(); + var reason = pendingUnhandledRejections.shift(); + if ( + hasBeenNotifiedProperty.get(promise) === false && + globalThis.__onUncaughtError + ) { + globalThis.__onUncaughtError(reason); + } + } + } + + function unhandledPromise(promise, reason) { + pendingUnhandledRejections.push(promise, reason); + __ns__setTimeout(() => { + emitPendingUnhandledRejections(); + }, 1); + } + + function handledPromise(promise) { + const hasBeenNotified = hasBeenNotifiedProperty.get(promise); + if (hasBeenNotified != undefined) { + hasBeenNotifiedProperty.delete(promise); + } + } + + function makeRejectionError(reason) { + const stringValue = Object.prototype.toString.call(reason); + if ( + stringValue === "[object Error]" || + reason instanceof Error || + typeof reason === "object" + ) { + reason.message = `(Unhandled promise rejection): ${reason.message}`; + + if (!reason.stack) { + reason.stack = new Error("").stack; + } + return reason; + } else { + const error = new Error(reason, { + cause: reason, + }); + error.message = `(Unhandled promise rejection): ${error.message}`; + return error; + } + } + + if (globalThis.__engine === "V8") { + // Only report errors for promise rejections that go unhandled. + globalThis.onUnhandledPromiseRejectionTracker = ( + event, + promise, + reason + ) => { + if (event === globalThis.__promiseUnhandledEvent) { + hasBeenNotifiedProperty.set(promise, false); + const error = makeRejectionError(reason); + unhandledPromise(promise, error); + } else { + handledPromise(promise); + } + }; + } else if (globalThis.__engine === "QuickJS") { + globalThis.onUnhandledPromiseRejectionTracker = ( + promise, + reason, + isHandled + ) => { + if (!isHandled) { + hasBeenNotifiedProperty.set(promise, false); + const error = makeRejectionError(reason); + // Preserve original stack trace. + if (!promise.then["[[stack]]"]) { + promise.then["[[stack]]"] = error.stack; + } else { + error.stack = promise.then["[[stack]]"]; + } + unhandledPromise(promise, error); + } else { + handledPromise(promise); + } + }; + } else if (globalThis.__engine === "Hermes") { + HermesInternal.enablePromiseRejectionTracker({ + allRejections: true, + onUnhandled: function (id, error) { + globalThis.__onUncaughtError(makeRejectionError(error)); + }, + }); + } })(); diff --git a/test-app/runtime/CMakeLists.txt b/test-app/runtime/CMakeLists.txt index 64c7922b..4df1dc24 100644 --- a/test-app/runtime/CMakeLists.txt +++ b/test-app/runtime/CMakeLists.txt @@ -104,9 +104,9 @@ if (QUICKJS) # quickjs src/main/cpp/napi/quickjs/source/cutils.c src/main/cpp/napi/quickjs/source/libregexp.c - src/main/cpp/napi/quickjs/source/libbf.c src/main/cpp/napi/quickjs/source/libunicode.c src/main/cpp/napi/quickjs/source/quickjs.c + src/main/cpp/napi/quickjs/source/dtoa.c # napi src/main/cpp/napi/quickjs/quickjs-api.c src/main/cpp/napi/quickjs/jsr.cpp diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/quickjs-api.c b/test-app/runtime/src/main/cpp/napi/quickjs/quickjs-api.c index 6018cc9e..f028be60 100644 --- a/test-app/runtime/src/main/cpp/napi/quickjs/quickjs-api.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/quickjs-api.c @@ -3,7 +3,6 @@ #include #include #include "js_native_api.h" -#include "libbf.h" #include "quicks-runtime.h" #ifdef __ANDROID__ @@ -1043,48 +1042,48 @@ bool JS_GetBigIntWords(JSContext *context, JSValue value, int *signBit, size_t * return rev; } -typedef struct JS_BigFloatExt { - JSRefCountHeader header; - bf_t num; -} JS_BigFloatExt; - -bool JS_ToInt64WithBigInt(JSContext *context, JSValueConst value, int64_t *pres, bool *lossless) { - if (pres == NULL || lossless == NULL) { - return 0; - } - - bool rev = false; - JSValue val = JS_DupValue(context, value); - JS_BigFloatExt *p = (JS_BigFloatExt *) JS_VALUE_GET_PTR(val); - if (p) { - int opFlag = bf_get_int64(pres, &p->num, 0); - if (lossless != NULL) { - *lossless = (opFlag == 0); - } - rev = true; - } - JS_FreeValue(context, val); - return rev; -} - -bool JS_ToUInt64WithBigInt(JSContext *context, JSValueConst value, uint64_t *pres, bool *lossless) { - if (pres == NULL || lossless == NULL) { - return false; - } - - bool rev = false; - JSValue val = JS_DupValue(context, value); - JS_BigFloatExt *p = (JS_BigFloatExt *) JS_VALUE_GET_PTR(val); - if (p) { - int opFlag = bf_get_uint64(pres, &p->num); - if (lossless != NULL) { - *lossless = (opFlag == 0); - } - rev = true; - } - JS_FreeValue(context, val); - return rev; -} +//typedef struct JS_BigFloatExt { +// JSRefCountHeader header; +// bf_t num; +//} JS_BigFloatExt; +// +//bool JS_ToInt64WithBigInt(JSContext *context, JSValueConst value, int64_t *pres, bool *lossless) { +// if (pres == NULL || lossless == NULL) { +// return 0; +// } +// +// bool rev = false; +// JSValue val = JS_DupValue(context, value); +// JS_BigFloatExt *p = (JS_BigFloatExt *) JS_VALUE_GET_PTR(val); +// if (p) { +// int opFlag = bf_get_int64(pres, &p->num, 0); +// if (lossless != NULL) { +// *lossless = (opFlag == 0); +// } +// rev = true; +// } +// JS_FreeValue(context, val); +// return rev; +//} +// +//bool JS_ToUInt64WithBigInt(JSContext *context, JSValueConst value, uint64_t *pres, bool *lossless) { +// if (pres == NULL || lossless == NULL) { +// return false; +// } +// +// bool rev = false; +// JSValue val = JS_DupValue(context, value); +// JS_BigFloatExt *p = (JS_BigFloatExt *) JS_VALUE_GET_PTR(val); +// if (p) { +// int opFlag = bf_get_uint64(pres, &p->num); +// if (lossless != NULL) { +// *lossless = (opFlag == 0); +// } +// rev = true; +// } +// JS_FreeValue(context, val); +// return rev; +//} napi_status napi_create_bigint_int64(napi_env env, int64_t value, napi_value *result) { CHECK_ARG(env) @@ -1508,7 +1507,7 @@ napi_status napi_get_array_length(napi_env env, JSValue jsValue = *((JSValue *) value); - if (!JS_IsArray(env->context, jsValue)) + if (!JS_IsArray(jsValue)) return napi_set_last_error(env, napi_array_expected, NULL, 0, NULL); int64_t length = 0; @@ -1706,18 +1705,6 @@ napi_status napi_get_typedarray_info(napi_env env, return napi_clear_last_error(env); } -bool JS_IsDataView(JSContext *context, JSValue value) { - bool result = false; - JSValue constructor = JS_GetPropertyStr(context, value, "constructor"); - JSValue name = JS_GetPropertyStr(context, constructor, "name"); - const char *cName = JS_ToCString(context, name); - result = !strcmp("DataView", cName ? cName : ""); - JS_FreeCString(context, cName); - JS_FreeValue(context, name); - JS_FreeValue(context, constructor); - return result; -} - napi_status napi_get_dataview_info(napi_env env, napi_value dataview, size_t *byte_length, @@ -1729,7 +1716,7 @@ napi_status napi_get_dataview_info(napi_env env, JSValue value = *((JSValue *) dataview); - if (!JS_IsDataView(env->context, value)) { + if (!JS_IsDataView(value)) { return napi_set_last_error(env, napi_invalid_arg, NULL, 0, NULL); } @@ -1835,11 +1822,11 @@ napi_status napi_get_value_bigint_int64(napi_env env, CHECK_ARG(value) CHECK_ARG(result) - if (!JS_IsBigInt(env->context, *(JSValue *) value)) { + if (!JS_IsBigInt(*(JSValue *) value)) { return napi_set_last_error(env, napi_bigint_expected, NULL, 0, NULL); } - JS_ToInt64WithBigInt(env->context, *(JSValue *) value, result, lossless); + JS_ToBigInt64(env->context, result,*(JSValue *) value); return napi_clear_last_error(env); } @@ -1852,11 +1839,11 @@ napi_status napi_get_value_bigint_uint64(napi_env env, CHECK_ARG(value) CHECK_ARG(result) - if (!JS_IsBigInt(env->context, *(JSValue *) value)) { + if (!JS_IsBigInt(*(JSValue *) value)) { return napi_set_last_error(env, napi_bigint_expected, NULL, 0, NULL); } - JS_ToUInt64WithBigInt(env->context, *(JSValue *) value, result, lossless); + JS_ToBigUint64(env->context, result, *(JSValue *) value); return napi_clear_last_error(env); } @@ -1874,7 +1861,7 @@ napi_status napi_get_value_bigint_words(napi_env env, JSValue jsValue = *(JSValue *) value; - if (!JS_IsBigInt(env->context, jsValue)) { + if (!JS_IsBigInt(jsValue)) { return napi_set_last_error(env, napi_bigint_expected, NULL, 0, NULL); } @@ -2271,7 +2258,7 @@ napi_status napi_typeof(napi_env env, napi_value value, napi_valuetype *result) *result = napi_string; } else if (JS_IsSymbol(jsValue)) { *result = napi_symbol; - } else if (JS_IsBigInt(env->context, jsValue)) { + } else if (JS_IsBigInt(jsValue)) { *result = napi_bigint; } else if (JS_IsFunction(env->context, jsValue)) { *result = napi_function; @@ -2319,7 +2306,7 @@ napi_status napi_is_array(napi_env env, napi_value value, bool *result) { CHECK_ARG(result) JSValue jsValue = *((JSValue *) value); - int status = JS_IsArray(env->context, jsValue); + int status = JS_IsArray(jsValue); RETURN_STATUS_IF_FALSE(status != -1, napi_pending_exception); *result = status; @@ -2388,7 +2375,7 @@ napi_status napi_is_error(napi_env env, napi_value value, bool *result) { CHECK_ARG(value) CHECK_ARG(result) - int status = JS_IsError(env->context, *((JSValue *) value)); + int status = JS_IsError(*((JSValue *) value)); *result = status; return napi_clear_last_error(env); } @@ -2411,7 +2398,7 @@ napi_status napi_is_dataview(napi_env env, napi_value value, bool *result) { CHECK_ARG(value) CHECK_ARG(result) - int status = JS_IsDataView(env->context, *((JSValue *) value)); + int status = JS_IsDataView(*((JSValue *) value)); *result = status; return napi_clear_last_error(env); @@ -4001,23 +3988,6 @@ napi_status qjs_create_runtime(napi_runtime *runtime) { return napi_ok; } -static void JS_AfterGCCallback(JSRuntime *rt) { - napi_env env = (napi_env) JS_GetRuntimeOpaque(rt); - if (env->gcAfter != NULL) { - env->gcAfter->finalizeCallback(env, env->gcAfter->data, env->gcAfter->finalizeHint); - } -} - -static int JS_BeforeGCCallback(JSRuntime *rt) { - napi_env env = (napi_env) JS_GetRuntimeOpaque(rt); - bool hint = true; - if (env->gcAfter != NULL) { - env->gcAfter->finalizeCallback(env, env->gcAfter->data, &hint); - } - - return hint; -} - static JSValue JSRunGCCallback(JSContext *ctx, JSValue this_val, int argc, JSValue @@ -4047,6 +4017,21 @@ JSEngineCallback(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst * return JS_UNDEFINED; } +void JSR_PromiseRejectionTracker(JSContext *ctx, JSValue promise, + JSValue reason, + bool is_handled, void *opaque) { + JSValue global = JS_GetGlobalObject(ctx); + JSValue onUnhandledRejection = JS_GetPropertyStr(ctx, global, "onUnhandledPromiseRejectionTracker"); + if (JS_IsFunction(ctx, onUnhandledRejection)) { + JSValue isHandled = JS_NewBool(ctx, is_handled); + JSValue argv[3] = {promise, reason, isHandled}; + JS_Call(ctx, onUnhandledRejection, global, 3, argv); + JS_FreeValue(ctx, isHandled); + } + JS_FreeValue(ctx, onUnhandledRejection); + JS_FreeValue(ctx, global); +} + napi_status qjs_create_napi_env(napi_env *env, napi_runtime runtime) { assert(env && runtime); @@ -4064,10 +4049,6 @@ napi_status qjs_create_napi_env(napi_env *env, napi_runtime runtime) { JS_SetRuntimeOpaque(runtime->runtime, *env); - JS_SetGCAfterCallback(runtime->runtime, JS_AfterGCCallback); - - JS_SetGCBeforeCallback(runtime->runtime, JS_BeforeGCCallback); - // Create runtime atoms (*env)->atoms.napi_external = JS_NewAtom(context, "napi_external"); (*env)->atoms.registerFinalizer = JS_NewAtom(context, "register"); @@ -4106,6 +4087,8 @@ napi_status qjs_create_napi_env(napi_env *env, napi_runtime runtime) { JSValue EngineCallback = JS_NewCFunction(context, JSEngineCallback, NULL, 0); JS_SetPropertyStr(context, globalValue, "directFunction", EngineCallback); + JS_SetHostPromiseRejectionTracker(runtime->runtime, JSR_PromiseRejectionTracker, *env); + (*env)->instanceData = NULL; (*env)->isThrowNull = false; (*env)->gcBefore = NULL; diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/CMakeLists.txt b/test-app/runtime/src/main/cpp/napi/quickjs/source/CMakeLists.txt old mode 100644 new mode 100755 index 8683dcf3..f2ada27e --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/CMakeLists.txt +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/CMakeLists.txt @@ -10,6 +10,28 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS ON) set(CMAKE_C_STANDARD 11) +# MINGW doesn't exist in older cmake versions, newer versions don't know +# about CMAKE_COMPILER_IS_MINGW, and there is no unique CMAKE_C_COMPILER_ID +# for mingw-based compilers... +if(MINGW) + # do nothing +elseif(CMAKE_C_COMPILER MATCHES "mingw") + set(MINGW TRUE) +else() + set(MINGW FALSE) +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "tvOS") + set(TVOS TRUE) +else() + set(TVOS FALSE) +endif() +if(CMAKE_SYSTEM_NAME STREQUAL "watchOS") + set(WATCHOS TRUE) +else() + set(WATCHOS FALSE) +endif() + if(NOT CMAKE_BUILD_TYPE) message(STATUS "No build type selected, default to Release") set(CMAKE_BUILD_TYPE "Release") @@ -27,17 +49,19 @@ macro(xcheck_add_c_compiler_flag FLAG) endmacro() xcheck_add_c_compiler_flag(-Wall) -if(NOT MSVC AND NOT IOS) +if(NOT MSVC AND NOT IOS AND NOT TVOS AND NOT WATCHOS) xcheck_add_c_compiler_flag(-Werror) xcheck_add_c_compiler_flag(-Wextra) endif() +xcheck_add_c_compiler_flag(-Wformat=2) xcheck_add_c_compiler_flag(-Wno-implicit-fallthrough) xcheck_add_c_compiler_flag(-Wno-sign-compare) xcheck_add_c_compiler_flag(-Wno-missing-field-initializers) xcheck_add_c_compiler_flag(-Wno-unused-parameter) xcheck_add_c_compiler_flag(-Wno-unused-but-set-variable) +xcheck_add_c_compiler_flag(-Wno-unused-result) +xcheck_add_c_compiler_flag(-Wno-stringop-truncation) xcheck_add_c_compiler_flag(-Wno-array-bounds) -xcheck_add_c_compiler_flag(-Wno-format-truncation) xcheck_add_c_compiler_flag(-funsigned-char) # ClangCL is command line compatible with MSVC, so 'MSVC' is set. @@ -61,6 +85,8 @@ if(MSVC) xcheck_add_c_compiler_flag(/wd4267) # -Wno-shorten-64-to-32 xcheck_add_c_compiler_flag(/wd4388) # -Wno-sign-compare xcheck_add_c_compiler_flag(/wd4389) # -Wno-sign-compare + xcheck_add_c_compiler_flag(/wd4456) # Hides previous local declaration + xcheck_add_c_compiler_flag(/wd4457) # Hides function parameter xcheck_add_c_compiler_flag(/wd4710) # Function not inlined xcheck_add_c_compiler_flag(/wd4711) # Function was inlined xcheck_add_c_compiler_flag(/wd4820) # Padding added after construct @@ -68,6 +94,18 @@ if(MSVC) xcheck_add_c_compiler_flag(/wd5045) # Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified endif() +# Set a 8MB default stack size on Windows. +# It defaults to 1MB on MSVC, which is the same as our current JS stack size, +# so it will overflow and crash otherwise. +# On MinGW it defaults to 2MB. +if(WIN32) + if(MSVC) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:8388608") + else() + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--stack,8388608") + endif() +endif() + # MacOS and GCC 11 or later need -Wno-maybe-uninitialized # https://github.com/quickjs-ng/quickjs/issues/453 if(APPLE AND CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11) @@ -86,7 +124,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "WASI") endif() if(CMAKE_BUILD_TYPE MATCHES "Debug") - add_compile_options(-O0) + xcheck_add_c_compiler_flag(/Od) + xcheck_add_c_compiler_flag(-O0) xcheck_add_c_compiler_flag(-ggdb) xcheck_add_c_compiler_flag(-fno-omit-frame-pointer) endif() @@ -108,20 +147,21 @@ if(BUILD_SHARED_LIBS) message(STATUS "Building a shared library") endif() -# note: CONFIG_TSAN is currently incompatible with the other sanitizers but we +# note: QJS_ENABLE_TSAN is currently incompatible with the other sanitizers but we # don't explicitly check for that because who knows what the future will bring? -# CONFIG_MSAN only works with clang at the time of writing; also not checked +# QJS_ENABLE_MSAN only works with clang at the time of writing; also not checked # for the same reason -xoption(BUILD_EXAMPLES "Build examples" OFF) -xoption(BUILD_STATIC_QJS_EXE "Build a static qjs executable" OFF) -xoption(BUILD_CLI_WITH_MIMALLOC "Build the qjs executable with mimalloc" OFF) -xoption(BUILD_CLI_WITH_STATIC_MIMALLOC "Build the qjs executable with mimalloc (statically linked)" OFF) -xoption(CONFIG_ASAN "Enable AddressSanitizer (ASan)" OFF) -xoption(CONFIG_MSAN "Enable MemorySanitizer (MSan)" OFF) -xoption(CONFIG_TSAN "Enable ThreadSanitizer (TSan)" OFF) -xoption(CONFIG_UBSAN "Enable UndefinedBehaviorSanitizer (UBSan)" OFF) - -if(CONFIG_ASAN) +xoption(QJS_BUILD_EXAMPLES "Build examples" OFF) +xoption(QJS_BUILD_CLI_STATIC "Build a static qjs executable" OFF) +xoption(QJS_BUILD_CLI_WITH_MIMALLOC "Build the qjs executable with mimalloc" OFF) +xoption(QJS_BUILD_CLI_WITH_STATIC_MIMALLOC "Build the qjs executable with mimalloc (statically linked)" OFF) +xoption(QJS_DISABLE_PARSER "Disable JS source code parser" OFF) +xoption(QJS_ENABLE_ASAN "Enable AddressSanitizer (ASan)" OFF) +xoption(QJS_ENABLE_MSAN "Enable MemorySanitizer (MSan)" OFF) +xoption(QJS_ENABLE_TSAN "Enable ThreadSanitizer (TSan)" OFF) +xoption(QJS_ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer (UBSan)" OFF) + +if(QJS_ENABLE_ASAN) message(STATUS "Building with ASan") add_compile_options( -fsanitize=address @@ -135,7 +175,7 @@ add_link_options( ) endif() -if(CONFIG_MSAN) +if(QJS_ENABLE_MSAN) message(STATUS "Building with MSan") add_compile_options( -fsanitize=memory @@ -149,7 +189,7 @@ add_link_options( ) endif() -if(CONFIG_TSAN) +if(QJS_ENABLE_TSAN) message(STATUS "Building with TSan") add_compile_options( -fsanitize=thread @@ -163,7 +203,7 @@ add_link_options( ) endif() -if(CONFIG_UBSAN) +if(QJS_ENABLE_UBSAN) message(STATUS "Building with UBSan") add_compile_options( -fsanitize=undefined @@ -181,27 +221,42 @@ endif() # QuickJS library # -xoption(BUILD_QJS_LIBC "Build standard library modules as part of the library" OFF) +xoption(QJS_BUILD_LIBC "Build standard library modules as part of the library" OFF) macro(add_qjs_libc_if_needed target) - if(NOT BUILD_QJS_LIBC) + if(NOT QJS_BUILD_LIBC) target_sources(${target} PRIVATE quickjs-libc.c) endif() endmacro() +macro(add_static_if_needed target) + if(QJS_BUILD_CLI_STATIC OR MINGW) + target_link_options(${target} PRIVATE -static) + if(MINGW) + target_link_options(${target} PRIVATE -static-libgcc) + endif() + endif() +endmacro() set(qjs_sources cutils.c - libbf.c + dtoa.c libregexp.c libunicode.c quickjs.c ) -if(BUILD_QJS_LIBC) +if(QJS_BUILD_LIBC) list(APPEND qjs_sources quickjs-libc.c) endif() list(APPEND qjs_defines _GNU_SOURCE) if(WIN32) - list(APPEND qjs_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602) + # NB: Windows 7 is EOL and we are only supporting in so far as it doesn't interfere with progress. + list(APPEND qjs_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0601) +endif() +if(TVOS) + list(APPEND qjs_defines _TVOS) +endif() +if(WATCHOS) + list(APPEND qjs_defines _WATCHOS) endif() list(APPEND qjs_libs ${CMAKE_DL_LIBS}) find_package(Threads) @@ -249,6 +304,7 @@ add_executable(qjsc qjsc.c ) add_qjs_libc_if_needed(qjsc) +add_static_if_needed(qjsc) target_compile_definitions(qjsc PRIVATE ${qjs_defines}) target_link_libraries(qjsc qjs) @@ -262,24 +318,19 @@ add_executable(qjs_exe qjs.c ) add_qjs_libc_if_needed(qjs_exe) +add_static_if_needed(qjs_exe) set_target_properties(qjs_exe PROPERTIES OUTPUT_NAME "qjs" ) target_compile_definitions(qjs_exe PRIVATE ${qjs_defines}) target_link_libraries(qjs_exe qjs) -if(BUILD_STATIC_QJS_EXE OR MINGW) - target_link_options(qjs_exe PRIVATE -static) - if(MINGW) - target_link_options(qjs_exe PRIVATE -static-libgcc) - endif() -endif() if(NOT WIN32) set_target_properties(qjs_exe PROPERTIES ENABLE_EXPORTS TRUE) endif() -if(BUILD_CLI_WITH_MIMALLOC OR BUILD_CLI_WITH_STATIC_MIMALLOC) +if(QJS_BUILD_CLI_WITH_MIMALLOC OR QJS_BUILD_CLI_WITH_STATIC_MIMALLOC) find_package(mimalloc REQUIRED) # Upstream mimalloc doesn't provide a way to know if both libraries are supported. - if(BUILD_CLI_WITH_STATIC_MIMALLOC) + if(QJS_BUILD_CLI_WITH_STATIC_MIMALLOC) target_link_libraries(qjs_exe mimalloc-static) else() target_link_libraries(qjs_exe mimalloc) @@ -301,11 +352,11 @@ endif() # Interrupt test # -add_executable(interrupt-test - interrupt-test.c +add_executable(api-test + api-test.c ) -target_compile_definitions(interrupt-test PRIVATE ${qjs_defines}) -target_link_libraries(interrupt-test qjs) +target_compile_definitions(api-test PRIVATE ${qjs_defines}) +target_link_libraries(api-test qjs) # Unicode generator # @@ -328,7 +379,7 @@ target_link_libraries(function_source qjs) # Examples # -if(BUILD_EXAMPLES) +if(QJS_BUILD_EXAMPLES) add_executable(hello gen/hello.c ) @@ -382,27 +433,29 @@ endif() # Install target # -if(NOT IOS) - file(STRINGS quickjs.h quickjs_h REGEX QJS_VERSION) - string(REGEX MATCHALL "([0-9])" QJS_VERSION "${quickjs_h}") - list(GET QJS_VERSION 0 QJS_VERSION_MAJOR) - list(GET QJS_VERSION 1 QJS_VERSION_MINOR) - list(GET QJS_VERSION 2 QJS_VERSION_PATCH) - set_target_properties(qjs PROPERTIES - VERSION ${QJS_VERSION_MAJOR}.${QJS_VERSION_MINOR}.${QJS_VERSION_PATCH} - SOVERSION ${QJS_VERSION_MAJOR} - ) - install(FILES quickjs.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - if(BUILD_QJS_LIBC) - install(FILES quickjs-libc.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - endif() +file(STRINGS quickjs.h quickjs_h REGEX QJS_VERSION) +string(REGEX MATCH "QJS_VERSION_MAJOR ([0-9]*)" _ "${quickjs_h}") +set(QJS_VERSION_MAJOR ${CMAKE_MATCH_1}) +string(REGEX MATCH "QJS_VERSION_MINOR ([0-9]*)" _ "${quickjs_h}") +set(QJS_VERSION_MINOR ${CMAKE_MATCH_1}) +string(REGEX MATCH "QJS_VERSION_PATCH ([0-9]*)" _ "${quickjs_h}") +set(QJS_VERSION_PATCH ${CMAKE_MATCH_1}) +set_target_properties(qjs PROPERTIES + VERSION ${QJS_VERSION_MAJOR}.${QJS_VERSION_MINOR}.${QJS_VERSION_PATCH} + SOVERSION ${QJS_VERSION_MAJOR} +) +install(FILES quickjs.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +if(QJS_BUILD_LIBC) + install(FILES quickjs-libc.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +endif() +if(NOT IOS AND NOT TVOS AND NOT WATCHOS) install(TARGETS qjs_exe RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS qjsc RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - install(TARGETS qjs EXPORT qjsConfig - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - install(EXPORT qjsConfig DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/quickjs) - install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR}) - install(DIRECTORY examples DESTINATION ${CMAKE_INSTALL_DOCDIR}) endif() +install(TARGETS qjs EXPORT qjsConfig + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install(EXPORT qjsConfig DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/quickjs) +install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR}) +install(DIRECTORY examples DESTINATION ${CMAKE_INSTALL_DOCDIR}) diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/LICENSE b/test-app/runtime/src/main/cpp/napi/quickjs/source/LICENSE old mode 100644 new mode 100755 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/Makefile b/test-app/runtime/src/main/cpp/napi/quickjs/source/Makefile old mode 100644 new mode 100755 index 982ce16c..4ff8ca17 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/Makefile +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/Makefile @@ -45,6 +45,15 @@ endif all: $(QJS) +amalgam: TEMP := $(shell mktemp -d) +amalgam: $(QJS) + $(QJS) amalgam.js $(TEMP)/quickjs-amalgam.c + cp quickjs.h quickjs-libc.h $(TEMP) + cd $(TEMP) && zip -9 quickjs-amalgam.zip quickjs-amalgam.c quickjs.h quickjs-libc.h + cp $(TEMP)/quickjs-amalgam.zip $(BUILD_DIR) + cd $(TEMP) && $(RM) quickjs-amalgam.zip quickjs-amalgam.c quickjs.h quickjs-libc.h + $(RM) -d $(TEMP) + fuzz: clang -g -O1 -fsanitize=address,undefined,fuzzer -o fuzz fuzz.c ./fuzz @@ -70,7 +79,8 @@ codegen: $(QJSC) $(QJSC) -e -o gen/function_source.c tests/function_source.js $(QJSC) -e -o gen/hello.c examples/hello.js $(QJSC) -e -o gen/hello_module.c -m examples/hello_module.js - $(QJSC) -e -o gen/test_fib.c -M examples/fib.so,fib -m examples/test_fib.js + $(QJSC) -e -o gen/test_fib.c -m examples/test_fib.js + $(QJSC) -C -ss -o builtin-array-fromasync.h builtin-array-fromasync.js debug: BUILD_TYPE=Debug $(MAKE) @@ -81,6 +91,23 @@ distclean: stats: $(QJS) $(QJS) -qd +jscheck: CFLAGS=-I. -D_GNU_SOURCE -DJS_CHECK_JSVALUE -Wall -Werror -fsyntax-only -c -o /dev/null +jscheck: + $(CC) $(CFLAGS) api-test.c + $(CC) $(CFLAGS) ctest.c + $(CC) $(CFLAGS) fuzz.c + $(CC) $(CFLAGS) gen/function_source.c + $(CC) $(CFLAGS) gen/hello.c + $(CC) $(CFLAGS) gen/hello_module.c + $(CC) $(CFLAGS) gen/repl.c + $(CC) $(CFLAGS) gen/standalone.c + $(CC) $(CFLAGS) gen/test_fib.c + $(CC) $(CFLAGS) qjs.c + $(CC) $(CFLAGS) qjsc.c + $(CC) $(CFLAGS) quickjs-libc.c + $(CC) $(CFLAGS) quickjs.c + $(CC) $(CFLAGS) run-test262.c + # effectively .PHONY because it doesn't generate output ctest: CFLAGS=-std=c11 -fsyntax-only -Wall -Wextra -Werror -pedantic ctest: ctest.c quickjs.h @@ -117,4 +144,4 @@ unicode_gen: $(BUILD_DIR) libunicode-table.h: unicode_gen $(BUILD_DIR)/unicode_gen unicode $@ -.PHONY: all ctest cxxtest debug fuzz install clean codegen distclean stats test test262 test262-update test262-check microbench unicode_gen $(QJS) $(QJSC) +.PHONY: all amalgam ctest cxxtest debug fuzz jscheck install clean codegen distclean stats test test262 test262-update test262-check microbench unicode_gen $(QJS) $(QJSC) diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/README.md b/test-app/runtime/src/main/cpp/napi/quickjs/source/README.md old mode 100644 new mode 100755 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/amalgam.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/amalgam.js new file mode 100755 index 00000000..028d7798 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/amalgam.js @@ -0,0 +1,49 @@ +import {loadFile, writeFile} from "qjs:std" + +const cutils_c = loadFile("cutils.c") +const cutils_h = loadFile("cutils.h") +const dtoa_c = loadFile("dtoa.c") +const dtoa_h = loadFile("dtoa.h") +const libregexp_c = loadFile("libregexp.c") +const libregexp_h = loadFile("libregexp.h") +const libregexp_opcode_h = loadFile("libregexp-opcode.h") +const libunicode_c = loadFile("libunicode.c") +const libunicode_h = loadFile("libunicode.h") +const libunicode_table_h = loadFile("libunicode-table.h") +const list_h = loadFile("list.h") +const quickjs_atom_h = loadFile("quickjs-atom.h") +const quickjs_c = loadFile("quickjs.c") +const quickjs_c_atomics_h = loadFile("quickjs-c-atomics.h") +const quickjs_h = loadFile("quickjs.h") +const quickjs_libc_c = loadFile("quickjs-libc.c") +const quickjs_libc_h = loadFile("quickjs-libc.h") +const quickjs_opcode_h = loadFile("quickjs-opcode.h") +const gen_builtin_array_fromasync_h = loadFile("builtin-array-fromasync.h") + +let source = "#if defined(QJS_BUILD_LIBC) && defined(__linux__) && !defined(_GNU_SOURCE)\n" + + "#define _GNU_SOURCE\n" + + "#endif\n" + + quickjs_c_atomics_h + + cutils_h + + dtoa_h + + list_h + + libunicode_h // exports lre_is_id_start, used by libregexp.h + + libregexp_h + + libunicode_table_h + + quickjs_h + + quickjs_c + + cutils_c + + dtoa_c + + libregexp_c + + libunicode_c + + "#ifdef QJS_BUILD_LIBC\n" + + quickjs_libc_h + + quickjs_libc_c + + "#endif // QJS_BUILD_LIBC\n" +source = source.replace(/#include "quickjs-atom.h"/g, quickjs_atom_h) +source = source.replace(/#include "quickjs-opcode.h"/g, quickjs_opcode_h) +source = source.replace(/#include "libregexp-opcode.h"/g, libregexp_opcode_h) +source = source.replace(/#include "builtin-array-fromasync.h"/g, + gen_builtin_array_fromasync_h) +source = source.replace(/#include "[^"]+"/g, "") +writeFile(execArgv[2] ?? "quickjs-amalgam.c", source) diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/api-test.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/api-test.c new file mode 100755 index 00000000..ef28a4f5 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/api-test.c @@ -0,0 +1,763 @@ +#ifdef NDEBUG +#undef NDEBUG +#endif +#include +#include +#include +#include "quickjs.h" +#include "cutils.h" + +static JSValue eval(JSContext *ctx, const char *code) +{ + return JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_GLOBAL); +} + +static JSValue cfunc_callback(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ThrowTypeError(ctx, "from cfunc"); +} + +static JSValue cfuncdata_callback(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValueConst *func_data) +{ + return JS_ThrowTypeError(ctx, "from cfuncdata"); +} + +static void cfunctions(void) +{ + uint32_t length; + const char *s; + JSValue ret, stack; + + JSRuntime *rt = JS_NewRuntime(); + JSContext *ctx = JS_NewContext(rt); + JSValue cfunc = JS_NewCFunction(ctx, cfunc_callback, "cfunc", 42); + JSValue cfuncdata = + JS_NewCFunctionData2(ctx, cfuncdata_callback, "cfuncdata", + /*length*/1337, /*magic*/0, /*data_len*/0, NULL); + JSValue global = JS_GetGlobalObject(ctx); + JS_SetPropertyStr(ctx, global, "cfunc", cfunc); + JS_SetPropertyStr(ctx, global, "cfuncdata", cfuncdata); + JS_FreeValue(ctx, global); + + ret = eval(ctx, "cfunc.name"); + assert(!JS_IsException(ret)); + assert(JS_IsString(ret)); + s = JS_ToCString(ctx, ret); + JS_FreeValue(ctx, ret); + assert(s); + assert(!strcmp(s, "cfunc")); + JS_FreeCString(ctx, s); + ret = eval(ctx, "cfunc.length"); + assert(!JS_IsException(ret)); + assert(JS_IsNumber(ret)); + assert(0 == JS_ToUint32(ctx, &length, ret)); + assert(length == 42); + + ret = eval(ctx, "cfuncdata.name"); + assert(!JS_IsException(ret)); + assert(JS_IsString(ret)); + s = JS_ToCString(ctx, ret); + JS_FreeValue(ctx, ret); + assert(s); + assert(!strcmp(s, "cfuncdata")); + JS_FreeCString(ctx, s); + ret = eval(ctx, "cfuncdata.length"); + assert(!JS_IsException(ret)); + assert(JS_IsNumber(ret)); + assert(0 == JS_ToUint32(ctx, &length, ret)); + assert(length == 1337); + + ret = eval(ctx, "cfunc()"); + assert(JS_IsException(ret)); + ret = JS_GetException(ctx); + assert(JS_IsError(ret)); + stack = JS_GetPropertyStr(ctx, ret, "stack"); + assert(JS_IsString(stack)); + s = JS_ToCString(ctx, stack); + JS_FreeValue(ctx, stack); + assert(s); + assert(!strcmp(s, " at cfunc (native)\n at (:1:1)\n")); + JS_FreeCString(ctx, s); + s = JS_ToCString(ctx, ret); + JS_FreeValue(ctx, ret); + assert(s); + assert(!strcmp(s, "TypeError: from cfunc")); + JS_FreeCString(ctx, s); + + ret = eval(ctx, "cfuncdata()"); + assert(JS_IsException(ret)); + ret = JS_GetException(ctx); + assert(JS_IsError(ret)); + stack = JS_GetPropertyStr(ctx, ret, "stack"); + assert(JS_IsString(stack)); + s = JS_ToCString(ctx, stack); + JS_FreeValue(ctx, stack); + assert(s); + assert(!strcmp(s, " at cfuncdata (native)\n at (:1:1)\n")); + JS_FreeCString(ctx, s); + s = JS_ToCString(ctx, ret); + JS_FreeValue(ctx, ret); + assert(s); + assert(!strcmp(s, "TypeError: from cfuncdata")); + JS_FreeCString(ctx, s); + + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +#define MAX_TIME 10 + +static int timeout_interrupt_handler(JSRuntime *rt, void *opaque) +{ + int *time = (int *)opaque; + if (*time <= MAX_TIME) + *time += 1; + return *time > MAX_TIME; +} + +static void sync_call(void) +{ + static const char code[] = +"(function() { \ + try { \ + while (true) {} \ + } catch (e) {} \ +})();"; + + JSRuntime *rt = JS_NewRuntime(); + JSContext *ctx = JS_NewContext(rt); + int time = 0; + JS_SetInterruptHandler(rt, timeout_interrupt_handler, &time); + JSValue ret = eval(ctx, code); + assert(time > MAX_TIME); + assert(JS_IsException(ret)); + JS_FreeValue(ctx, ret); + assert(JS_HasException(ctx)); + JSValue e = JS_GetException(ctx); + assert(JS_IsUncatchableError(e)); + JS_FreeValue(ctx, e); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +static void async_call(void) +{ + static const char code[] = +"(async function() { \ + const loop = async () => { \ + await Promise.resolve(); \ + while (true) {} \ + }; \ + await loop().catch(() => {}); \ +})();"; + + JSRuntime *rt = JS_NewRuntime(); + JSContext *ctx = JS_NewContext(rt); + int time = 0; + JS_SetInterruptHandler(rt, timeout_interrupt_handler, &time); + JSValue ret = eval(ctx, code); + assert(!JS_IsException(ret)); + JS_FreeValue(ctx, ret); + assert(JS_IsJobPending(rt)); + int r = 0; + while (JS_IsJobPending(rt)) { + r = JS_ExecutePendingJob(rt, &ctx); + } + assert(time > MAX_TIME); + assert(r == -1); + assert(JS_HasException(ctx)); + JSValue e = JS_GetException(ctx); + assert(JS_IsUncatchableError(e)); + JS_FreeValue(ctx, e); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +static JSValue save_value(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + assert(argc == 1); + JSValue *p = (JSValue *)JS_GetContextOpaque(ctx); + *p = JS_DupValue(ctx, argv[0]); + return JS_UNDEFINED; +} + +static void async_call_stack_overflow(void) +{ + static const char code[] = +"(async function() { \ + const f = () => f(); \ + try { \ + await Promise.resolve(); \ + f(); \ + } catch (e) { \ + save_value(e); \ + } \ +})();"; + + JSRuntime *rt = JS_NewRuntime(); + JSContext *ctx = JS_NewContext(rt); + JSValue value = JS_UNDEFINED; + JS_SetContextOpaque(ctx, &value); + JSValue global = JS_GetGlobalObject(ctx); + JS_SetPropertyStr(ctx, global, "save_value", JS_NewCFunction(ctx, save_value, "save_value", 1)); + JS_FreeValue(ctx, global); + JSValue ret = eval(ctx, code); + assert(!JS_IsException(ret)); + JS_FreeValue(ctx, ret); + assert(JS_IsJobPending(rt)); + int r = 0; + while (JS_IsJobPending(rt)) { + r = JS_ExecutePendingJob(rt, &ctx); + } + assert(r == 1); + assert(!JS_HasException(ctx)); + assert(JS_IsError(value)); // stack overflow should be caught + JS_FreeValue(ctx, value); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +// https://github.com/quickjs-ng/quickjs/issues/914 +static void raw_context_global_var(void) +{ + JSRuntime *rt = JS_NewRuntime(); + JSContext *ctx = JS_NewContextRaw(rt); + JS_AddIntrinsicEval(ctx); + { + JSValue ret = eval(ctx, "globalThis"); + assert(JS_IsException(ret)); + JS_FreeValue(ctx, ret); + } + { + JSValue ret = eval(ctx, "var x = 42"); + assert(JS_IsUndefined(ret)); + JS_FreeValue(ctx, ret); + } + { + JSValue ret = eval(ctx, "function f() {}"); + assert(JS_IsUndefined(ret)); + JS_FreeValue(ctx, ret); + } + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +static void is_array(void) +{ + JSRuntime *rt = JS_NewRuntime(); + JSContext *ctx = JS_NewContext(rt); + { + JSValue ret = eval(ctx, "[]"); + assert(!JS_IsException(ret)); + assert(JS_IsArray(ret)); + JS_FreeValue(ctx, ret); + } + { + JSValue ret = eval(ctx, "new Proxy([], {})"); + assert(!JS_IsException(ret)); + assert(!JS_IsArray(ret)); + assert(JS_IsProxy(ret)); + JSValue handler = JS_GetProxyHandler(ctx, ret); + JSValue target = JS_GetProxyTarget(ctx, ret); + assert(!JS_IsException(handler)); + assert(!JS_IsException(target)); + assert(!JS_IsProxy(handler)); + assert(!JS_IsProxy(target)); + assert(JS_IsObject(handler)); + assert(JS_IsArray(target)); + JS_FreeValue(ctx, handler); + JS_FreeValue(ctx, target); + JS_FreeValue(ctx, ret); + } + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +static int loader_calls; + +static JSModuleDef *loader(JSContext *ctx, const char *name, void *opaque) +{ + loader_calls++; + assert(!strcmp(name, "b")); + static const char code[] = "export function f(x){}"; + JSValue ret = JS_Eval(ctx, code, strlen(code), "b", + JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY); + assert(!JS_IsException(ret)); + JSModuleDef *m = JS_VALUE_GET_PTR(ret); + assert(m); + JS_FreeValue(ctx, ret); + return m; +} + +static void module_serde(void) +{ + JSRuntime *rt = JS_NewRuntime(); + //JS_SetDumpFlags(rt, JS_DUMP_MODULE_RESOLVE); + JS_SetModuleLoaderFunc(rt, NULL, loader, NULL); + JSContext *ctx = JS_NewContext(rt); + static const char code[] = "import {f} from 'b'; f()"; + assert(loader_calls == 0); + JSValue mod = JS_Eval(ctx, code, strlen(code), "a", + JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY); + assert(loader_calls == 1); + assert(!JS_IsException(mod)); + assert(JS_IsModule(mod)); + size_t len = 0; + uint8_t *buf = JS_WriteObject(ctx, &len, mod, + JS_WRITE_OBJ_BYTECODE|JS_WRITE_OBJ_REFERENCE); + assert(buf); + assert(len > 0); + JS_FreeValue(ctx, mod); + assert(loader_calls == 1); + mod = JS_ReadObject(ctx, buf, len, JS_READ_OBJ_BYTECODE); + js_free(ctx, buf); + assert(loader_calls == 1); // 'b' is returned from cache + assert(!JS_IsException(mod)); + JSValue ret = JS_EvalFunction(ctx, mod); + assert(!JS_IsException(ret)); + assert(JS_IsPromise(ret)); + JSValue result = JS_PromiseResult(ctx, ret); + assert(!JS_IsException(result)); + assert(JS_IsUndefined(result)); + JS_FreeValue(ctx, result); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, mod); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +static void two_byte_string(void) +{ + JSRuntime *rt = JS_NewRuntime(); + JSContext *ctx = JS_NewContext(rt); + { + JSValue v = JS_NewTwoByteString(ctx, NULL, 0); + assert(!JS_IsException(v)); + const char *s = JS_ToCString(ctx, v); + assert(s); + assert(!strcmp(s, "")); + JS_FreeCString(ctx, s); + JS_FreeValue(ctx, v); + } + { + JSValue v = JS_NewTwoByteString(ctx, (uint16_t[]){'o','k'}, 2); + assert(!JS_IsException(v)); + const char *s = JS_ToCString(ctx, v); + assert(s); + assert(!strcmp(s, "ok")); + JS_FreeCString(ctx, s); + JS_FreeValue(ctx, v); + } + { + JSValue v = JS_NewTwoByteString(ctx, (uint16_t[]){0xD800}, 1); + assert(!JS_IsException(v)); + const char *s = JS_ToCString(ctx, v); + assert(s); + // questionable but surrogates don't map to UTF-8 without WTF-8 + assert(!strcmp(s, "\xED\xA0\x80")); + JS_FreeCString(ctx, s); + JS_FreeValue(ctx, v); + } + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +static void weak_map_gc_check(void) +{ + static const char init_code[] = +"const map = new WeakMap(); \ +function addItem() { \ + const k = { \ + text: 'a', \ + }; \ + map.set(k, {k}); \ +}"; + static const char test_code[] = "addItem()"; + + JSRuntime *rt = JS_NewRuntime(); + JSContext *ctx = JS_NewContext(rt); + + JSValue ret = eval(ctx, init_code); + assert(!JS_IsException(ret)); + + JSValue ret_test = eval(ctx, test_code); + assert(!JS_IsException(ret_test)); + JS_RunGC(rt); + JSMemoryUsage memory_usage; + JS_ComputeMemoryUsage(rt, &memory_usage); + + for (int i = 0; i < 3; i++) { + JSValue ret_test2 = eval(ctx, test_code); + assert(!JS_IsException(ret_test2)); + JS_RunGC(rt); + JSMemoryUsage memory_usage2; + JS_ComputeMemoryUsage(rt, &memory_usage2); + + assert(memory_usage.memory_used_count == memory_usage2.memory_used_count); + assert(memory_usage.memory_used_size == memory_usage2.memory_used_size); + JS_FreeValue(ctx, ret_test2); + } + + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, ret_test); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +struct { + int hook_type_call_count[4]; +} promise_hook_state; + +static void promise_hook_cb(JSContext *ctx, JSPromiseHookType type, + JSValueConst promise, JSValueConst parent_promise, + void *opaque) +{ + assert(type == JS_PROMISE_HOOK_INIT || + type == JS_PROMISE_HOOK_BEFORE || + type == JS_PROMISE_HOOK_AFTER || + type == JS_PROMISE_HOOK_RESOLVE); + promise_hook_state.hook_type_call_count[type]++; + assert(opaque == (void *)&promise_hook_state); + if (!JS_IsUndefined(parent_promise)) { + JSValue global_object = JS_GetGlobalObject(ctx); + JS_SetPropertyStr(ctx, global_object, "actual", + JS_DupValue(ctx, parent_promise)); + JS_FreeValue(ctx, global_object); + } +} + +static void promise_hook(void) +{ + int *cc = promise_hook_state.hook_type_call_count; + JSContext *unused; + JSRuntime *rt = JS_NewRuntime(); + //JS_SetDumpFlags(rt, JS_DUMP_PROMISE); + JS_SetPromiseHook(rt, promise_hook_cb, &promise_hook_state); + JSContext *ctx = JS_NewContext(rt); + JSValue global_object = JS_GetGlobalObject(ctx); + { + // empty module; creates an outer and inner module promise; + // JS_Eval returns the outer promise + JSValue ret = JS_Eval(ctx, "", 0, "", JS_EVAL_TYPE_MODULE); + assert(!JS_IsException(ret)); + assert(JS_IsPromise(ret)); + assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); + JS_FreeValue(ctx, ret); + assert(2 == cc[JS_PROMISE_HOOK_INIT]); + assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); + assert(0 == cc[JS_PROMISE_HOOK_AFTER]); + assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]); + assert(!JS_IsJobPending(rt)); + } + memset(&promise_hook_state, 0, sizeof(promise_hook_state)); + { + // module with unresolved promise; the outer and inner module promises + // are resolved but not the user's promise + static const char code[] = "new Promise(() => {})"; + JSValue ret = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_MODULE); + assert(!JS_IsException(ret)); + assert(JS_IsPromise(ret)); + assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise + JS_FreeValue(ctx, ret); + assert(3 == cc[JS_PROMISE_HOOK_INIT]); + assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); + assert(0 == cc[JS_PROMISE_HOOK_AFTER]); + assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]); // outer and inner module promise + assert(!JS_IsJobPending(rt)); + } + memset(&promise_hook_state, 0, sizeof(promise_hook_state)); + { + // module with resolved promise + static const char code[] = "new Promise((resolve,reject) => resolve())"; + JSValue ret = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_MODULE); + assert(!JS_IsException(ret)); + assert(JS_IsPromise(ret)); + assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise + JS_FreeValue(ctx, ret); + assert(3 == cc[JS_PROMISE_HOOK_INIT]); + assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); + assert(0 == cc[JS_PROMISE_HOOK_AFTER]); + assert(3 == cc[JS_PROMISE_HOOK_RESOLVE]); + assert(!JS_IsJobPending(rt)); + } + memset(&promise_hook_state, 0, sizeof(promise_hook_state)); + { + // module with rejected promise + static const char code[] = "new Promise((resolve,reject) => reject())"; + JSValue ret = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_MODULE); + assert(!JS_IsException(ret)); + assert(JS_IsPromise(ret)); + assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise + JS_FreeValue(ctx, ret); + assert(3 == cc[JS_PROMISE_HOOK_INIT]); + assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); + assert(0 == cc[JS_PROMISE_HOOK_AFTER]); + assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]); + assert(!JS_IsJobPending(rt)); + } + memset(&promise_hook_state, 0, sizeof(promise_hook_state)); + { + // module with promise chain + static const char code[] = + "globalThis.count = 0;" + "globalThis.actual = undefined;" // set by promise_hook_cb + "globalThis.expected = new Promise(resolve => resolve());" + "expected.then(_ => count++)"; + JSValue ret = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_MODULE); + assert(!JS_IsException(ret)); + assert(JS_IsPromise(ret)); + assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise + JS_FreeValue(ctx, ret); + assert(4 == cc[JS_PROMISE_HOOK_INIT]); + assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); + assert(0 == cc[JS_PROMISE_HOOK_AFTER]); + assert(3 == cc[JS_PROMISE_HOOK_RESOLVE]); + JSValue v = JS_GetPropertyStr(ctx, global_object, "count"); + assert(!JS_IsException(v)); + int32_t count; + assert(0 == JS_ToInt32(ctx, &count, v)); + assert(0 == count); + JS_FreeValue(ctx, v); + assert(JS_IsJobPending(rt)); + assert(1 == JS_ExecutePendingJob(rt, &unused)); + assert(!JS_HasException(ctx)); + assert(4 == cc[JS_PROMISE_HOOK_INIT]); + assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); + assert(0 == cc[JS_PROMISE_HOOK_AFTER]); + assert(4 == cc[JS_PROMISE_HOOK_RESOLVE]); + assert(!JS_IsJobPending(rt)); + v = JS_GetPropertyStr(ctx, global_object, "count"); + assert(!JS_IsException(v)); + assert(0 == JS_ToInt32(ctx, &count, v)); + assert(1 == count); + JS_FreeValue(ctx, v); + JSValue actual = JS_GetPropertyStr(ctx, global_object, "actual"); + JSValue expected = JS_GetPropertyStr(ctx, global_object, "expected"); + assert(!JS_IsException(actual)); + assert(!JS_IsException(expected)); + assert(JS_IsSameValue(ctx, actual, expected)); + JS_FreeValue(ctx, actual); + JS_FreeValue(ctx, expected); + } + memset(&promise_hook_state, 0, sizeof(promise_hook_state)); + { + // module with thenable; fires before and after hooks + static const char code[] = + "new Promise(resolve => resolve({then(resolve){ resolve() }}))"; + JSValue ret = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_MODULE); + assert(!JS_IsException(ret)); + assert(JS_IsPromise(ret)); + assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise + JS_FreeValue(ctx, ret); + assert(3 == cc[JS_PROMISE_HOOK_INIT]); + assert(0 == cc[JS_PROMISE_HOOK_BEFORE]); + assert(0 == cc[JS_PROMISE_HOOK_AFTER]); + assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]); + assert(JS_IsJobPending(rt)); + assert(1 == JS_ExecutePendingJob(rt, &unused)); + assert(!JS_HasException(ctx)); + assert(3 == cc[JS_PROMISE_HOOK_INIT]); + assert(1 == cc[JS_PROMISE_HOOK_BEFORE]); + assert(1 == cc[JS_PROMISE_HOOK_AFTER]); + assert(3 == cc[JS_PROMISE_HOOK_RESOLVE]); + assert(!JS_IsJobPending(rt)); + } + JS_FreeValue(ctx, global_object); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +static void dump_memory_usage(void) +{ + JSMemoryUsage stats; + + JSRuntime *rt = NULL; + JSContext *ctx = NULL; + + rt = JS_NewRuntime(); + ctx = JS_NewContext(rt); + + //JS_SetDumpFlags(rt, JS_DUMP_PROMISE); + + static const char code[] = + "globalThis.count = 0;" + "globalThis.actual = undefined;" // set by promise_hook_cb + "globalThis.expected = new Promise(resolve => resolve());" + "expected.then(_ => count++)"; + + JSValue evalVal = JS_Eval(ctx, code, strlen(code), "", 0); + JS_FreeValue(ctx, evalVal); + + FILE *temp = tmpfile(); + assert(temp != NULL); + JS_ComputeMemoryUsage(rt, &stats); + JS_DumpMemoryUsage(temp, &stats, rt); + // JS_DumpMemoryUsage(stdout, &stats, rt); + fclose(temp); + + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +static void new_errors(void) +{ + typedef struct { + const char name[16]; + JSValue (*func)(JSContext *, const char *, ...); + } Entry; + static const Entry entries[] = { + {"Error", JS_NewPlainError}, + {"InternalError", JS_NewInternalError}, + {"RangeError", JS_NewRangeError}, + {"ReferenceError", JS_NewReferenceError}, + {"SyntaxError", JS_NewSyntaxError}, + {"TypeError", JS_NewTypeError}, + }; + const Entry *e; + + JSRuntime *rt = JS_NewRuntime(); + JSContext *ctx = JS_NewContext(rt); + for (e = entries; e < endof(entries); e++) { + JSValue obj = (*e->func)(ctx, "the %s", "needle"); + assert(!JS_IsException(obj)); + assert(JS_IsObject(obj)); + assert(JS_IsError(obj)); + const char *haystack = JS_ToCString(ctx, obj); + char needle[256]; + snprintf(needle, sizeof(needle), "%s: the needle", e->name); + assert(strstr(haystack, needle)); + JS_FreeCString(ctx, haystack); + JSValue stack = JS_GetPropertyStr(ctx, obj, "stack"); + assert(!JS_IsException(stack)); + assert(JS_IsString(stack)); + JS_FreeValue(ctx, stack); + JS_FreeValue(ctx, obj); + } + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +static int gop_get_own_property(JSContext *ctx, JSPropertyDescriptor *desc, + JSValueConst obj, JSAtom prop) +{ + const char *name; + int found; + + found = 0; + name = JS_AtomToCString(ctx, prop); + if (!name) + return -1; + if (!strcmp(name, "answer")) { + found = 1; + *desc = (JSPropertyDescriptor){ + .value = JS_NewInt32(ctx, 42), + .flags = JS_PROP_C_W_E | JS_PROP_HAS_VALUE, + }; + } + JS_FreeCString(ctx, name); + return found; +} + +static void global_object_prototype(void) +{ + static const char code[] = "answer"; + JSValue global_object, proto, ret; + JSClassID class_id; + JSRuntime *rt; + JSContext *ctx; + int32_t answer; + int res; + + { + rt = JS_NewRuntime(); + ctx = JS_NewContext(rt); + proto = JS_NewObject(ctx); + assert(JS_IsObject(proto)); + JSCFunctionListEntry prop = JS_PROP_INT32_DEF("answer", 42, JS_PROP_C_W_E); + JS_SetPropertyFunctionList(ctx, proto, &prop, 1); + global_object = JS_GetGlobalObject(ctx); + res = JS_SetPrototype(ctx, global_object, proto); + assert(res == true); + JS_FreeValue(ctx, global_object); + JS_FreeValue(ctx, proto); + ret = eval(ctx, code); + assert(!JS_IsException(ret)); + assert(JS_IsNumber(ret)); + res = JS_ToInt32(ctx, &answer, ret); + assert(res == 0); + assert(answer == 42); + JS_FreeValue(ctx, ret); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + } + { + JSClassExoticMethods exotic = (JSClassExoticMethods){ + .get_own_property = gop_get_own_property, + }; + JSClassDef def = (JSClassDef){ + .class_name = "Global Object", + .exotic = &exotic, + }; + rt = JS_NewRuntime(); + class_id = 0; + JS_NewClassID(rt, &class_id); + res = JS_NewClass(rt, class_id, &def); + assert(res == 0); + ctx = JS_NewContext(rt); + proto = JS_NewObjectClass(ctx, class_id); + assert(JS_IsObject(proto)); + global_object = JS_GetGlobalObject(ctx); + res = JS_SetPrototype(ctx, global_object, proto); + assert(res == true); + JS_FreeValue(ctx, global_object); + JS_FreeValue(ctx, proto); + ret = eval(ctx, code); + assert(!JS_IsException(ret)); + assert(JS_IsNumber(ret)); + res = JS_ToInt32(ctx, &answer, ret); + assert(res == 0); + assert(answer == 42); + JS_FreeValue(ctx, ret); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + } +} + +// https://github.com/quickjs-ng/quickjs/issues/1178 +static void slice_string_tocstring(void) +{ + JSRuntime *rt = JS_NewRuntime(); + JSContext *ctx = JS_NewContext(rt); + JSValue ret = eval(ctx, "'.'.repeat(16384).slice(1, -1)"); + assert(!JS_IsException(ret)); + assert(JS_IsString(ret)); + const char *str = JS_ToCString(ctx, ret); + assert(strlen(str) == 16382); + JS_FreeCString(ctx, str); + JS_FreeValue(ctx, ret); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +int main(void) +{ + cfunctions(); + sync_call(); + async_call(); + async_call_stack_overflow(); + raw_context_global_var(); + is_array(); + module_serde(); + two_byte_string(); + weak_map_gc_check(); + promise_hook(); + dump_memory_usage(); + new_errors(); + global_object_prototype(); + slice_string_tocstring(); + return 0; +} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/builtin-array-fromasync.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/builtin-array-fromasync.h new file mode 100755 index 00000000..19f1f077 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/builtin-array-fromasync.h @@ -0,0 +1,113 @@ +/* File generated automatically by the QuickJS-ng compiler. */ + +#include + +const uint32_t qjsc_builtin_array_fromasync_size = 826; + +const uint8_t qjsc_builtin_array_fromasync[826] = { + 0x15, 0x0d, 0x01, 0x1a, 0x61, 0x73, 0x79, 0x6e, + 0x63, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x01, 0x10, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x01, 0x12, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x4c, 0x69, 0x6b, 0x65, 0x01, 0x0a, + 0x6d, 0x61, 0x70, 0x46, 0x6e, 0x01, 0x0e, 0x74, + 0x68, 0x69, 0x73, 0x41, 0x72, 0x67, 0x01, 0x0c, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x01, 0x02, + 0x69, 0x01, 0x1a, 0x69, 0x73, 0x43, 0x6f, 0x6e, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x6f, 0x72, + 0x01, 0x08, 0x73, 0x79, 0x6e, 0x63, 0x01, 0x0c, + 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x01, 0x08, + 0x69, 0x74, 0x65, 0x72, 0x01, 0x1c, 0x6e, 0x6f, + 0x74, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x01, 0x08, 0x63, 0x61, + 0x6c, 0x6c, 0x0c, 0x00, 0x02, 0x00, 0xa2, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x04, 0x01, + 0xa4, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x43, 0x02, + 0x01, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x01, + 0x03, 0x05, 0xaa, 0x02, 0x00, 0x01, 0x40, 0xa0, + 0x03, 0x00, 0x01, 0x40, 0xc6, 0x03, 0x00, 0x01, + 0x40, 0xcc, 0x01, 0x00, 0x01, 0x40, 0xc8, 0x03, + 0x00, 0x01, 0x40, 0x0c, 0x60, 0x02, 0x01, 0xf8, + 0x01, 0x03, 0x0e, 0x01, 0x06, 0x05, 0x00, 0x86, + 0x04, 0x11, 0xca, 0x03, 0x00, 0x01, 0x00, 0xcc, + 0x03, 0x00, 0x01, 0x00, 0xce, 0x03, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, + 0x0f, 0x20, 0xcc, 0x03, 0x01, 0x01, 0x20, 0xce, + 0x03, 0x01, 0x02, 0x20, 0xd0, 0x03, 0x02, 0x00, + 0x20, 0xd2, 0x03, 0x02, 0x04, 0x20, 0xd4, 0x03, + 0x02, 0x05, 0x20, 0xd6, 0x03, 0x02, 0x06, 0x20, + 0xd8, 0x03, 0x02, 0x07, 0x20, 0x64, 0x06, 0x08, + 0x20, 0x82, 0x01, 0x07, 0x09, 0x20, 0xda, 0x03, + 0x0a, 0x08, 0x30, 0x82, 0x01, 0x0d, 0x0b, 0x20, + 0xd4, 0x01, 0x0d, 0x0c, 0x20, 0x10, 0x00, 0x01, + 0x00, 0xa0, 0x03, 0x01, 0x03, 0xc6, 0x03, 0x02, + 0x03, 0xc8, 0x03, 0x04, 0x03, 0xaa, 0x02, 0x00, + 0x03, 0xcc, 0x01, 0x03, 0x03, 0x08, 0xc4, 0x0d, + 0x62, 0x02, 0x00, 0x62, 0x01, 0x00, 0x62, 0x00, + 0x00, 0xd3, 0xcb, 0xd4, 0x11, 0xf4, 0xec, 0x08, + 0x0e, 0x39, 0x46, 0x00, 0x00, 0x00, 0xdc, 0xcc, + 0xd5, 0x11, 0xf4, 0xec, 0x08, 0x0e, 0x39, 0x46, + 0x00, 0x00, 0x00, 0xdd, 0xcd, 0x62, 0x07, 0x00, + 0x62, 0x06, 0x00, 0x62, 0x05, 0x00, 0x62, 0x04, + 0x00, 0x62, 0x03, 0x00, 0xd4, 0x39, 0x46, 0x00, + 0x00, 0x00, 0xb0, 0xec, 0x16, 0xd4, 0x98, 0x04, + 0x1b, 0x00, 0x00, 0x00, 0xb0, 0xec, 0x0c, 0xdf, + 0x11, 0x04, 0xee, 0x00, 0x00, 0x00, 0x21, 0x01, + 0x00, 0x30, 0x06, 0xce, 0xb6, 0xc4, 0x04, 0xc3, + 0x0d, 0xf7, 0xc4, 0x05, 0x09, 0xc4, 0x06, 0xd3, + 0xe0, 0x48, 0xc4, 0x07, 0x63, 0x07, 0x00, 0x07, + 0xad, 0xec, 0x0f, 0x0a, 0x11, 0x64, 0x06, 0x00, + 0x0e, 0xd3, 0xe1, 0x48, 0x11, 0x64, 0x07, 0x00, + 0x0e, 0x63, 0x07, 0x00, 0x07, 0xad, 0x6a, 0xa6, + 0x00, 0x00, 0x00, 0x62, 0x08, 0x00, 0x06, 0x11, + 0xf4, 0xed, 0x0c, 0x71, 0x43, 0x32, 0x00, 0x00, + 0x00, 0xc4, 0x08, 0x0e, 0xee, 0x05, 0x0e, 0xd3, + 0xee, 0xf2, 0x63, 0x08, 0x00, 0x8e, 0x11, 0xed, + 0x03, 0x0e, 0xb6, 0x11, 0x64, 0x08, 0x00, 0x0e, + 0x63, 0x05, 0x00, 0xec, 0x0c, 0xc3, 0x0d, 0x11, + 0x63, 0x08, 0x00, 0x21, 0x01, 0x00, 0xee, 0x06, + 0xe2, 0x63, 0x08, 0x00, 0xf1, 0x11, 0x64, 0x03, + 0x00, 0x0e, 0x63, 0x04, 0x00, 0x63, 0x08, 0x00, + 0xa7, 0x6a, 0x2a, 0x01, 0x00, 0x00, 0x62, 0x09, + 0x00, 0xd3, 0x63, 0x04, 0x00, 0x48, 0xc4, 0x09, + 0x63, 0x06, 0x00, 0xec, 0x0a, 0x63, 0x09, 0x00, + 0x8c, 0x11, 0x64, 0x09, 0x00, 0x0e, 0xd4, 0xec, + 0x17, 0xd4, 0x43, 0xef, 0x00, 0x00, 0x00, 0xd5, + 0x63, 0x09, 0x00, 0x63, 0x04, 0x00, 0x24, 0x03, + 0x00, 0x8c, 0x11, 0x64, 0x09, 0x00, 0x0e, 0x5f, + 0x04, 0x00, 0x63, 0x03, 0x00, 0x63, 0x04, 0x00, + 0x92, 0x64, 0x04, 0x00, 0x0b, 0x63, 0x09, 0x00, + 0x4d, 0x41, 0x00, 0x00, 0x00, 0x0a, 0x4d, 0x3e, + 0x00, 0x00, 0x00, 0x0a, 0x4d, 0x3f, 0x00, 0x00, + 0x00, 0xf3, 0x0e, 0xee, 0x9e, 0x62, 0x0a, 0x00, + 0x63, 0x07, 0x00, 0x43, 0xef, 0x00, 0x00, 0x00, + 0xd3, 0x24, 0x01, 0x00, 0xc4, 0x0a, 0x63, 0x05, + 0x00, 0xec, 0x09, 0xc3, 0x0d, 0x11, 0x21, 0x00, + 0x00, 0xee, 0x03, 0xe2, 0xf0, 0x11, 0x64, 0x03, + 0x00, 0x0e, 0x6d, 0x8c, 0x00, 0x00, 0x00, 0x62, + 0x0c, 0x00, 0x62, 0x0b, 0x00, 0x06, 0x11, 0xf4, + 0xed, 0x13, 0x71, 0x43, 0x41, 0x00, 0x00, 0x00, + 0xc4, 0x0b, 0x43, 0x6a, 0x00, 0x00, 0x00, 0xc4, + 0x0c, 0x0e, 0xee, 0x10, 0x0e, 0x63, 0x0a, 0x00, + 0x43, 0x6b, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, + 0x8c, 0xee, 0xe0, 0x63, 0x0c, 0x00, 0xed, 0x4e, + 0x63, 0x06, 0x00, 0xec, 0x0a, 0x63, 0x0b, 0x00, + 0x8c, 0x11, 0x64, 0x0b, 0x00, 0x0e, 0xd4, 0xec, + 0x17, 0xd4, 0x43, 0xef, 0x00, 0x00, 0x00, 0xd5, + 0x63, 0x0b, 0x00, 0x63, 0x04, 0x00, 0x24, 0x03, + 0x00, 0x8c, 0x11, 0x64, 0x0b, 0x00, 0x0e, 0x5f, + 0x04, 0x00, 0x63, 0x03, 0x00, 0x63, 0x04, 0x00, + 0x92, 0x64, 0x04, 0x00, 0x0b, 0x63, 0x0b, 0x00, + 0x4d, 0x41, 0x00, 0x00, 0x00, 0x0a, 0x4d, 0x3e, + 0x00, 0x00, 0x00, 0x0a, 0x4d, 0x3f, 0x00, 0x00, + 0x00, 0xf3, 0x0e, 0xee, 0x83, 0x0e, 0x06, 0x6e, + 0x0d, 0x00, 0x00, 0x00, 0x0e, 0xee, 0x1e, 0x6e, + 0x05, 0x00, 0x00, 0x00, 0x30, 0x63, 0x0a, 0x00, + 0x42, 0x06, 0x00, 0x00, 0x00, 0xec, 0x0d, 0x63, + 0x0a, 0x00, 0x43, 0x06, 0x00, 0x00, 0x00, 0x24, + 0x00, 0x00, 0x0e, 0x6f, 0x63, 0x03, 0x00, 0x63, + 0x04, 0x00, 0x44, 0x32, 0x00, 0x00, 0x00, 0x63, + 0x03, 0x00, 0x2f, 0xc1, 0x00, 0x28, 0xc1, 0x00, + 0xcf, 0x28, +}; + diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/builtin-array-fromasync.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/builtin-array-fromasync.js new file mode 100755 index 00000000..a07e40ef --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/builtin-array-fromasync.js @@ -0,0 +1,36 @@ +;(function(Array, TypeError, asyncIterator, defineProperty, iterator) { + "use strict" // result.length=i should throw if .length is not writable + return async function fromAsync(arrayLike, mapFn=undefined, thisArg=undefined) { + if (mapFn !== undefined && typeof mapFn !== "function") throw new TypeError("not a function") + let result, i = 0, isConstructor = typeof this === "function" + let sync = false, method = arrayLike[asyncIterator] + if (method == null) sync = true, method = arrayLike[iterator] + if (method == null) { + let {length} = arrayLike + length = +length || 0 + result = isConstructor ? new this(length) : Array(length) + while (i < length) { + let value = arrayLike[i] + if (sync) value = await value + if (mapFn) value = await mapFn.call(thisArg, value, i) + defineProperty(result, i++, {value, configurable: true, writable: true}) + } + } else { + const iter = method.call(arrayLike) + result = isConstructor ? new this() : Array() + try { + for (;;) { + let {value, done} = await iter.next() + if (done) break + if (sync) value = await value + if (mapFn) value = await mapFn.call(thisArg, value, i) + defineProperty(result, i++, {value, configurable: true, writable: true}) + } + } finally { + if (iter.return) iter.return() + } + } + result.length = i + return result + } +}) diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/ctest.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/ctest.c old mode 100644 new mode 100755 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.c old mode 100644 new mode 100755 index 438087d0..7d36ddfd --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.c @@ -31,6 +31,13 @@ #if !defined(_MSC_VER) #include #endif +#if defined(_WIN32) +#include +#include // _beginthread +#endif +#if defined(__APPLE__) +#include +#endif #include "cutils.h" @@ -95,6 +102,10 @@ int js__has_suffix(const char *str, const char *suffix) static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size) { + if (unlikely(size == 0)) { + free(ptr); + return NULL; + } return realloc(ptr, size); } @@ -579,258 +590,6 @@ size_t utf8_encode_buf16(char *dest, size_t dest_len, const uint16_t *src, size_ return j; } -/*--- integer to string conversions --*/ - -/* All conversion functions: - - require a destination array `buf` of sufficient length - - write the string representation at the beginning of `buf` - - null terminate the string - - return the string length - */ - -/* 2 <= base <= 36 */ -char const digits36[36] = { - '0','1','2','3','4','5','6','7','8','9', - 'a','b','c','d','e','f','g','h','i','j', - 'k','l','m','n','o','p','q','r','s','t', - 'u','v','w','x','y','z' -}; - - -#define USE_SPECIAL_RADIX_10 1 // special case base 10 radix conversions -#define USE_SINGLE_CASE_FAST 1 // special case single digit numbers - -/* using u32toa_shift variant */ - -#define gen_digit(buf, c) if (is_be()) \ - buf = (buf >> 8) | ((uint64_t)(c) << ((sizeof(buf) - 1) * 8)); \ - else \ - buf = (buf << 8) | (c) - -static size_t u7toa_shift(char dest[minimum_length(8)], uint32_t n) -{ - size_t len = 1; - uint64_t buf = 0; - while (n >= 10) { - uint32_t quo = n % 10; - n /= 10; - gen_digit(buf, '0' + quo); - len++; - } - gen_digit(buf, '0' + n); - memcpy(dest, &buf, sizeof buf); - return len; -} - -static size_t u07toa_shift(char dest[minimum_length(8)], uint32_t n, size_t len) -{ - size_t i; - dest += len; - dest[7] = '\0'; - for (i = 7; i-- > 1;) { - uint32_t quo = n % 10; - n /= 10; - dest[i] = (char)('0' + quo); - } - dest[i] = (char)('0' + n); - return len + 7; -} - -size_t u32toa(char buf[minimum_length(11)], uint32_t n) -{ -#ifdef USE_SINGLE_CASE_FAST /* 10% */ - if (n < 10) { - buf[0] = (char)('0' + n); - buf[1] = '\0'; - return 1; - } -#endif -#define TEN_POW_7 10000000 - if (n >= TEN_POW_7) { - uint32_t quo = n / TEN_POW_7; - n %= TEN_POW_7; - size_t len = u7toa_shift(buf, quo); - return u07toa_shift(buf, n, len); - } - return u7toa_shift(buf, n); -} - -size_t u64toa(char buf[minimum_length(21)], uint64_t n) -{ - if (likely(n < 0x100000000)) - return u32toa(buf, n); - - size_t len; - if (n >= TEN_POW_7) { - uint64_t n1 = n / TEN_POW_7; - n %= TEN_POW_7; - if (n1 >= TEN_POW_7) { - uint32_t quo = n1 / TEN_POW_7; - n1 %= TEN_POW_7; - len = u7toa_shift(buf, quo); - len = u07toa_shift(buf, n1, len); - } else { - len = u7toa_shift(buf, n1); - } - return u07toa_shift(buf, n, len); - } - return u7toa_shift(buf, n); -} - -size_t i32toa(char buf[minimum_length(12)], int32_t n) -{ - if (likely(n >= 0)) - return u32toa(buf, n); - - buf[0] = '-'; - return 1 + u32toa(buf + 1, -(uint32_t)n); -} - -size_t i64toa(char buf[minimum_length(22)], int64_t n) -{ - if (likely(n >= 0)) - return u64toa(buf, n); - - buf[0] = '-'; - return 1 + u64toa(buf + 1, -(uint64_t)n); -} - -/* using u32toa_radix_length variant */ - -static uint8_t const radix_shift[64] = { - 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, - 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -size_t u32toa_radix(char buf[minimum_length(33)], uint32_t n, unsigned base) -{ - int shift; - -#ifdef USE_SPECIAL_RADIX_10 - if (likely(base == 10)) - return u32toa(buf, n); -#endif - if (n < base) { - buf[0] = digits36[n]; - buf[1] = '\0'; - return 1; - } - shift = radix_shift[base & 63]; - if (shift) { - uint32_t mask = (1 << shift) - 1; - size_t len = (32 - clz32(n) + shift - 1) / shift; - size_t last = n & mask; - char *end = buf + len; - n >>= shift; - *end-- = '\0'; - *end-- = digits36[last]; - while (n >= base) { - size_t quo = n & mask; - n >>= shift; - *end-- = digits36[quo]; - } - *end = digits36[n]; - return len; - } else { - size_t len = 2; - size_t last = n % base; - n /= base; - uint32_t nbase = base; - while (n >= nbase) { - nbase *= base; - len++; - } - char *end = buf + len; - *end-- = '\0'; - *end-- = digits36[last]; - while (n >= base) { - size_t quo = n % base; - n /= base; - *end-- = digits36[quo]; - } - *end = digits36[n]; - return len; - } -} - -size_t u64toa_radix(char buf[minimum_length(65)], uint64_t n, unsigned base) -{ - int shift; - -#ifdef USE_SPECIAL_RADIX_10 - if (likely(base == 10)) - return u64toa(buf, n); -#endif - shift = radix_shift[base & 63]; - if (shift) { - if (n < base) { - buf[0] = digits36[n]; - buf[1] = '\0'; - return 1; - } - uint64_t mask = (1 << shift) - 1; - size_t len = (64 - clz64(n) + shift - 1) / shift; - size_t last = n & mask; - char *end = buf + len; - n >>= shift; - *end-- = '\0'; - *end-- = digits36[last]; - while (n >= base) { - size_t quo = n & mask; - n >>= shift; - *end-- = digits36[quo]; - } - *end = digits36[n]; - return len; - } else { - if (likely(n < 0x100000000)) - return u32toa_radix(buf, n, base); - size_t last = n % base; - n /= base; - uint64_t nbase = base; - size_t len = 2; - while (n >= nbase) { - nbase *= base; - len++; - } - char *end = buf + len; - *end-- = '\0'; - *end-- = digits36[last]; - while (n >= base) { - size_t quo = n % base; - n /= base; - *end-- = digits36[quo]; - } - *end = digits36[n]; - return len; - } -} - -size_t i32toa_radix(char buf[minimum_length(34)], int32_t n, unsigned int base) -{ - if (likely(n >= 0)) - return u32toa_radix(buf, n, base); - - buf[0] = '-'; - return 1 + u32toa_radix(buf + 1, -(uint32_t)n, base); -} - -size_t i64toa_radix(char buf[minimum_length(66)], int64_t n, unsigned int base) -{ - if (likely(n >= 0)) - return u64toa_radix(buf, n, base); - - buf[0] = '-'; - return 1 + u64toa_radix(buf + 1, -(uint64_t)n, base); -} - -#undef gen_digit -#undef TEN_POW_7 -#undef USE_SPECIAL_RADIX_10 -#undef USE_SINGLE_CASE_FAST - /*---- sorting with opaque argument ----*/ typedef void (*exchange_f)(void *a, void *b, size_t size); @@ -1197,10 +956,112 @@ int64_t js__gettimeofday_us(void) { return ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec; } -/*--- Cross-platform threading APIs. ----*/ +#if defined(_WIN32) +int js_exepath(char *buffer, size_t *size_ptr) { + int utf8_len, utf16_buffer_len, utf16_len; + WCHAR* utf16_buffer; + + if (buffer == NULL || size_ptr == NULL || *size_ptr == 0) + return -1; + + if (*size_ptr > 32768) { + /* Windows paths can never be longer than this. */ + utf16_buffer_len = 32768; + } else { + utf16_buffer_len = (int)*size_ptr; + } + + utf16_buffer = malloc(sizeof(WCHAR) * utf16_buffer_len); + if (!utf16_buffer) + return -1; + + /* Get the path as UTF-16. */ + utf16_len = GetModuleFileNameW(NULL, utf16_buffer, utf16_buffer_len); + if (utf16_len <= 0) + goto error; + + /* Convert to UTF-8 */ + utf8_len = WideCharToMultiByte(CP_UTF8, + 0, + utf16_buffer, + -1, + buffer, + (int)*size_ptr, + NULL, + NULL); + if (utf8_len == 0) + goto error; + + free(utf16_buffer); + + /* utf8_len *does* include the terminating null at this point, but the + * returned size shouldn't. */ + *size_ptr = utf8_len - 1; + return 0; -#if !defined(EMSCRIPTEN) && !defined(__wasi__) +error: + free(utf16_buffer); + return -1; +} +#elif defined(__APPLE__) +int js_exepath(char *buffer, size_t *size) { + /* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */ + char abspath[PATH_MAX * 2 + 1]; + char exepath[PATH_MAX + 1]; + uint32_t exepath_size; + size_t abspath_size; + + if (buffer == NULL || size == NULL || *size == 0) + return -1; + + exepath_size = sizeof(exepath); + if (_NSGetExecutablePath(exepath, &exepath_size)) + return -1; + + if (realpath(exepath, abspath) != abspath) + return -1; + + abspath_size = strlen(abspath); + if (abspath_size == 0) + return -1; + + *size -= 1; + if (*size > abspath_size) + *size = abspath_size; + + memcpy(buffer, abspath, *size); + buffer[*size] = '\0'; + + return 0; +} +#elif defined(__linux__) || defined(__GNU__) +int js_exepath(char *buffer, size_t *size) { + ssize_t n; + + if (buffer == NULL || size == NULL || *size == 0) + return -1; + n = *size - 1; + if (n > 0) + n = readlink("/proc/self/exe", buffer, n); + + if (n == -1) + return n; + + buffer[n] = '\0'; + *size = n; + + return 0; +} +#else +int js_exepath(char* buffer, size_t* size_ptr) { + return -1; +} +#endif + +/*--- Cross-platform threading APIs. ----*/ + +#if JS_HAVE_THREADS #if defined(_WIN32) typedef void (*js__once_cb)(void); @@ -1267,6 +1128,37 @@ int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) { return -1; } +int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg, + int flags) +{ + HANDLE h, cp; + + *thrd = INVALID_HANDLE_VALUE; + if (flags & ~JS_THREAD_CREATE_DETACHED) + return -1; + h = (HANDLE)_beginthread(start, /*stacksize*/2<<20, arg); + if (!h) + return -1; + if (flags & JS_THREAD_CREATE_DETACHED) + return 0; + // _endthread() automatically closes the handle but we want to wait on + // it so make a copy. Race-y for very short-lived threads. Can be solved + // by switching to _beginthreadex(CREATE_SUSPENDED) but means changing + // |start| from __cdecl to __stdcall. + cp = GetCurrentProcess(); + if (DuplicateHandle(cp, h, cp, thrd, 0, FALSE, DUPLICATE_SAME_ACCESS)) + return 0; + return -1; +} + +int js_thread_join(js_thread_t thrd) +{ + if (WaitForSingleObject(thrd, INFINITE)) + return -1; + CloseHandle(thrd); + return 0; +} + #else /* !defined(_WIN32) */ void js_once(js_once_t *guard, void (*callback)(void)) { @@ -1407,9 +1299,43 @@ int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) { return -1; } -#endif +int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg, + int flags) +{ + union { + void (*x)(void *); + void *(*f)(void *); + } u = {start}; + pthread_attr_t attr; + int ret; + + if (flags & ~JS_THREAD_CREATE_DETACHED) + return -1; + if (pthread_attr_init(&attr)) + return -1; + ret = -1; + if (pthread_attr_setstacksize(&attr, 2<<20)) + goto fail; + if (flags & JS_THREAD_CREATE_DETACHED) + if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) + goto fail; + if (pthread_create(thrd, &attr, u.f, arg)) + goto fail; + ret = 0; +fail: + pthread_attr_destroy(&attr); + return ret; +} + +int js_thread_join(js_thread_t thrd) +{ + if (pthread_join(thrd, NULL)) + return -1; + return 0; +} -#endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */ +#endif /* !defined(_WIN32) */ +#endif /* JS_HAVE_THREADS */ #ifdef __GNUC__ #pragma GCC visibility pop diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.h old mode 100644 new mode 100755 index 4639a6b9..f5d2a8bc --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/cutils.h @@ -43,7 +43,7 @@ extern "C" { #endif #if defined(__APPLE__) #include -#elif defined(__linux__) || defined(__ANDROID__) || defined(__CYGWIN__) +#elif defined(__linux__) || defined(__ANDROID__) || defined(__CYGWIN__) || defined(__GLIBC__) #include #elif defined(__FreeBSD__) #include @@ -54,6 +54,10 @@ extern "C" { #include #include #endif +#if !defined(_WIN32) +#include +#include +#endif #if defined(_MSC_VER) && !defined(__clang__) # define likely(x) (x) @@ -94,7 +98,7 @@ extern "C" { #define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member))) #endif -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__cplusplus) #define minimum_length(n) n #else #define minimum_length(n) static n @@ -118,6 +122,14 @@ extern "C" { #endif #endif +#if defined(PATH_MAX) +# define JS__PATH_MAX PATH_MAX +#elif defined(_WIN32) +# define JS__PATH_MAX 32767 +#else +# define JS__PATH_MAX 8192 +#endif + void js__pstrcpy(char *buf, int buf_size, const char *str); char *js__pstrcat(char *buf, int buf_size, const char *s); int js__strstart(const char *str, const char *val, const char **ptr); @@ -531,20 +543,30 @@ static inline uint8_t to_upper_ascii(uint8_t c) { return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c; } -extern char const digits36[36]; -size_t u32toa(char buf[minimum_length(11)], uint32_t n); -size_t i32toa(char buf[minimum_length(12)], int32_t n); -size_t u64toa(char buf[minimum_length(21)], uint64_t n); -size_t i64toa(char buf[minimum_length(22)], int64_t n); -size_t u32toa_radix(char buf[minimum_length(33)], uint32_t n, unsigned int base); -size_t i32toa_radix(char buf[minimum_length(34)], int32_t n, unsigned base); -size_t u64toa_radix(char buf[minimum_length(65)], uint64_t n, unsigned int base); -size_t i64toa_radix(char buf[minimum_length(66)], int64_t n, unsigned int base); - void rqsort(void *base, size_t nmemb, size_t size, int (*cmp)(const void *, const void *, void *), void *arg); +static inline uint64_t float64_as_uint64(double d) +{ + union { + double d; + uint64_t u64; + } u; + u.d = d; + return u.u64; +} + +static inline double uint64_as_float64(uint64_t u64) +{ + union { + double d; + uint64_t u64; + } u; + u.u64 = u64; + return u.d; +} + int64_t js__gettimeofday_us(void); uint64_t js__hrtime_ns(void); @@ -554,27 +576,37 @@ static inline size_t js__malloc_usable_size(const void *ptr) return malloc_size(ptr); #elif defined(_WIN32) return _msize((void *)ptr); -#elif defined(__linux__) || defined(__ANDROID__) || defined(__CYGWIN__) || defined(__FreeBSD__) +#elif defined(__linux__) || defined(__ANDROID__) || defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__GLIBC__) return malloc_usable_size((void *)ptr); #else return 0; #endif } +int js_exepath(char* buffer, size_t* size); + /* Cross-platform threading APIs. */ -#if !defined(EMSCRIPTEN) && !defined(__wasi__) +#if defined(EMSCRIPTEN) || defined(__wasi__) + +#define JS_HAVE_THREADS 0 + +#else + +#define JS_HAVE_THREADS 1 #if defined(_WIN32) #define JS_ONCE_INIT INIT_ONCE_STATIC_INIT typedef INIT_ONCE js_once_t; typedef CRITICAL_SECTION js_mutex_t; typedef CONDITION_VARIABLE js_cond_t; +typedef HANDLE js_thread_t; #else #define JS_ONCE_INIT PTHREAD_ONCE_INIT typedef pthread_once_t js_once_t; typedef pthread_mutex_t js_mutex_t; typedef pthread_cond_t js_cond_t; +typedef pthread_t js_thread_t; #endif void js_once(js_once_t *guard, void (*callback)(void)); @@ -591,8 +623,36 @@ void js_cond_broadcast(js_cond_t *cond); void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex); int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout); +enum { + JS_THREAD_CREATE_DETACHED = 1, +}; + +// creates threads with 2 MB stacks (glibc default) +int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg, + int flags); +int js_thread_join(js_thread_t thrd); + #endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */ +// JS requires strict rounding behavior. Turn on 64-bits double precision +// and disable x87 80-bits extended precision for intermediate floating-point +// results. 0x300 is extended precision, 0x200 is double precision. +// Note that `*&cw` in the asm constraints looks redundant but isn't. +#if defined(__i386__) && !defined(_MSC_VER) +#define JS_X87_FPCW_SAVE_AND_ADJUST(cw) \ + unsigned short cw; \ + __asm__ __volatile__("fnstcw %0" : "=m"(*&cw)); \ + do { \ + unsigned short t = 0x200 | (cw & ~0x300); \ + __asm__ __volatile__("fldcw %0" : /*empty*/ : "m"(*&t)); \ + } while (0) +#define JS_X87_FPCW_RESTORE(cw) \ + __asm__ __volatile__("fldcw %0" : /*empty*/ : "m"(*&cw)) +#else +#define JS_X87_FPCW_SAVE_AND_ADJUST(cw) +#define JS_X87_FPCW_RESTORE(cw) +#endif + #ifdef __cplusplus } /* extern "C" { */ #endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/cxxtest.cc b/test-app/runtime/src/main/cpp/napi/quickjs/source/cxxtest.cc old mode 100644 new mode 100755 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/dirent_compat.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/dirent_compat.h deleted file mode 100644 index 1d6c4802..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/dirent_compat.h +++ /dev/null @@ -1,1166 +0,0 @@ -/* - * Dirent interface for Microsoft Visual Studio - * - * Copyright (C) 1998-2019 Toni Ronkko - * This file is part of dirent. Dirent may be freely distributed - * under the MIT license. For all details and documentation, see - * https://github.com/tronkko/dirent - */ -#ifndef DIRENT_H -#define DIRENT_H - -/* Hide warnings about unreferenced local functions */ -#if defined(__clang__) -# pragma clang diagnostic ignored "-Wunused-function" -#elif defined(_MSC_VER) -# pragma warning(disable:4505) -#elif defined(__GNUC__) -# pragma GCC diagnostic ignored "-Wunused-function" -#endif - -/* - * Include windows.h without Windows Sockets 1.1 to prevent conflicts with - * Windows Sockets 2.0. - */ -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Indicates that d_type field is available in dirent structure */ -#define _DIRENT_HAVE_D_TYPE - -/* Indicates that d_namlen field is available in dirent structure */ -#define _DIRENT_HAVE_D_NAMLEN - -/* Entries missing from MSVC 6.0 */ -#if !defined(FILE_ATTRIBUTE_DEVICE) -# define FILE_ATTRIBUTE_DEVICE 0x40 -#endif - -/* File type and permission flags for stat(), general mask */ -#if !defined(S_IFMT) -# define S_IFMT _S_IFMT -#endif - -/* Directory bit */ -#if !defined(S_IFDIR) -# define S_IFDIR _S_IFDIR -#endif - -/* Character device bit */ -#if !defined(S_IFCHR) -# define S_IFCHR _S_IFCHR -#endif - -/* Pipe bit */ -#if !defined(S_IFFIFO) -# define S_IFFIFO _S_IFFIFO -#endif - -/* Regular file bit */ -#if !defined(S_IFREG) -# define S_IFREG _S_IFREG -#endif - -/* Read permission */ -#if !defined(S_IREAD) -# define S_IREAD _S_IREAD -#endif - -/* Write permission */ -#if !defined(S_IWRITE) -# define S_IWRITE _S_IWRITE -#endif - -/* Execute permission */ -#if !defined(S_IEXEC) -# define S_IEXEC _S_IEXEC -#endif - -/* Pipe */ -#if !defined(S_IFIFO) -# define S_IFIFO _S_IFIFO -#endif - -/* Block device */ -#if !defined(S_IFBLK) -# define S_IFBLK 0 -#endif - -/* Link */ -#if !defined(S_IFLNK) -# define S_IFLNK 0 -#endif - -/* Socket */ -#if !defined(S_IFSOCK) -# define S_IFSOCK 0 -#endif - -/* Read user permission */ -#if !defined(S_IRUSR) -# define S_IRUSR S_IREAD -#endif - -/* Write user permission */ -#if !defined(S_IWUSR) -# define S_IWUSR S_IWRITE -#endif - -/* Execute user permission */ -#if !defined(S_IXUSR) -# define S_IXUSR 0 -#endif - -/* Read group permission */ -#if !defined(S_IRGRP) -# define S_IRGRP 0 -#endif - -/* Write group permission */ -#if !defined(S_IWGRP) -# define S_IWGRP 0 -#endif - -/* Execute group permission */ -#if !defined(S_IXGRP) -# define S_IXGRP 0 -#endif - -/* Read others permission */ -#if !defined(S_IROTH) -# define S_IROTH 0 -#endif - -/* Write others permission */ -#if !defined(S_IWOTH) -# define S_IWOTH 0 -#endif - -/* Execute others permission */ -#if !defined(S_IXOTH) -# define S_IXOTH 0 -#endif - -/* Maximum length of file name */ -#if !defined(PATH_MAX) -# define PATH_MAX MAX_PATH -#endif -#if !defined(FILENAME_MAX) -# define FILENAME_MAX MAX_PATH -#endif -#if !defined(NAME_MAX) -# define NAME_MAX FILENAME_MAX -#endif - -/* File type flags for d_type */ -#define DT_UNKNOWN 0 -#define DT_REG S_IFREG -#define DT_DIR S_IFDIR -#define DT_FIFO S_IFIFO -#define DT_SOCK S_IFSOCK -#define DT_CHR S_IFCHR -#define DT_BLK S_IFBLK -#define DT_LNK S_IFLNK - -/* Macros for converting between st_mode and d_type */ -#define IFTODT(mode) ((mode) & S_IFMT) -#define DTTOIF(type) (type) - -/* - * File type macros. Note that block devices, sockets and links cannot be - * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are - * only defined for compatibility. These macros should always return false - * on Windows. - */ -#if !defined(S_ISFIFO) -# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) -#endif -#if !defined(S_ISDIR) -# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) -#endif -#if !defined(S_ISREG) -# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) -#endif -#if !defined(S_ISLNK) -# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) -#endif -#if !defined(S_ISSOCK) -# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) -#endif -#if !defined(S_ISCHR) -# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) -#endif -#if !defined(S_ISBLK) -# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) -#endif - -/* Return the exact length of the file name without zero terminator */ -#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) - -/* Return the maximum size of a file name */ -#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) - - -#ifdef __cplusplus -extern "C" { -#endif - - -/* Wide-character version */ -struct _wdirent { - /* Always zero */ - long d_ino; - - /* File position within stream */ - long d_off; - - /* Structure size */ - unsigned short d_reclen; - - /* Length of name without \0 */ - size_t d_namlen; - - /* File type */ - int d_type; - - /* File name */ - wchar_t d_name[PATH_MAX+1]; -}; -typedef struct _wdirent _wdirent; - -struct _WDIR { - /* Current directory entry */ - struct _wdirent ent; - - /* Private file data */ - WIN32_FIND_DATAW data; - - /* True if data is valid */ - int cached; - - /* Win32 search handle */ - HANDLE handle; - - /* Initial directory name */ - wchar_t *patt; -}; -typedef struct _WDIR _WDIR; - -/* Multi-byte character version */ -struct dirent { - /* Always zero */ - long d_ino; - - /* File position within stream */ - long d_off; - - /* Structure size */ - unsigned short d_reclen; - - /* Length of name without \0 */ - size_t d_namlen; - - /* File type */ - int d_type; - - /* File name */ - char d_name[PATH_MAX+1]; -}; -typedef struct dirent dirent; - -struct DIR { - struct dirent ent; - struct _WDIR *wdirp; -}; -typedef struct DIR DIR; - - -/* Dirent functions */ -static DIR *opendir (const char *dirname); -static _WDIR *_wopendir (const wchar_t *dirname); - -static struct dirent *readdir (DIR *dirp); -static struct _wdirent *_wreaddir (_WDIR *dirp); - -static int readdir_r( - DIR *dirp, struct dirent *entry, struct dirent **result); -static int _wreaddir_r( - _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); - -static int closedir (DIR *dirp); -static int _wclosedir (_WDIR *dirp); - -static void rewinddir (DIR* dirp); -static void _wrewinddir (_WDIR* dirp); - -static int scandir (const char *dirname, struct dirent ***namelist, - int (*filter)(const struct dirent*), - int (*compare)(const struct dirent**, const struct dirent**)); - -static int alphasort (const struct dirent **a, const struct dirent **b); - -static int versionsort (const struct dirent **a, const struct dirent **b); - - -/* For compatibility with Symbian */ -#define wdirent _wdirent -#define WDIR _WDIR -#define wopendir _wopendir -#define wreaddir _wreaddir -#define wclosedir _wclosedir -#define wrewinddir _wrewinddir - - -/* Internal utility functions */ -static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); -static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); - -static int dirent_mbstowcs_s( - size_t *pReturnValue, - wchar_t *wcstr, - size_t sizeInWords, - const char *mbstr, - size_t count); - -static int dirent_wcstombs_s( - size_t *pReturnValue, - char *mbstr, - size_t sizeInBytes, - const wchar_t *wcstr, - size_t count); - -static void dirent_set_errno (int error); - - -/* - * Open directory stream DIRNAME for read and return a pointer to the - * internal working area that is used to retrieve individual directory - * entries. - */ -static _WDIR* -_wopendir( - const wchar_t *dirname) -{ - _WDIR *dirp; -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - /* Desktop */ - DWORD n; -#else - /* WinRT */ - size_t n; -#endif - wchar_t *p; - - /* Must have directory name */ - if (dirname == NULL || dirname[0] == '\0') { - dirent_set_errno (ENOENT); - return NULL; - } - - /* Allocate new _WDIR structure */ - dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); - if (!dirp) { - return NULL; - } - - /* Reset _WDIR structure */ - dirp->handle = INVALID_HANDLE_VALUE; - dirp->patt = NULL; - dirp->cached = 0; - - /* - * Compute the length of full path plus zero terminator - * - * Note that on WinRT there's no way to convert relative paths - * into absolute paths, so just assume it is an absolute path. - */ -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - /* Desktop */ - n = GetFullPathNameW (dirname, 0, NULL, NULL); -#else - /* WinRT */ - n = wcslen (dirname); -#endif - - /* Allocate room for absolute directory name and search pattern */ - dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); - if (dirp->patt == NULL) { - goto exit_closedir; - } - - /* - * Convert relative directory name to an absolute one. This - * allows rewinddir() to function correctly even when current - * working directory is changed between opendir() and rewinddir(). - * - * Note that on WinRT there's no way to convert relative paths - * into absolute paths, so just assume it is an absolute path. - */ -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - /* Desktop */ - n = GetFullPathNameW (dirname, n, dirp->patt, NULL); - if (n <= 0) { - goto exit_closedir; - } -#else - /* WinRT */ - wcsncpy_s (dirp->patt, n+1, dirname, n); -#endif - - /* Append search pattern \* to the directory name */ - p = dirp->patt + n; - switch (p[-1]) { - case '\\': - case '/': - case ':': - /* Directory ends in path separator, e.g. c:\temp\ */ - /*NOP*/; - break; - - default: - /* Directory name doesn't end in path separator */ - *p++ = '\\'; - } - *p++ = '*'; - *p = '\0'; - - /* Open directory stream and retrieve the first entry */ - if (!dirent_first (dirp)) { - goto exit_closedir; - } - - /* Success */ - return dirp; - - /* Failure */ -exit_closedir: - _wclosedir (dirp); - return NULL; -} - -/* - * Read next directory entry. - * - * Returns pointer to static directory entry which may be overwritten by - * subsequent calls to _wreaddir(). - */ -static struct _wdirent* -_wreaddir( - _WDIR *dirp) -{ - struct _wdirent *entry; - - /* - * Read directory entry to buffer. We can safely ignore the return value - * as entry will be set to NULL in case of error. - */ - (void) _wreaddir_r (dirp, &dirp->ent, &entry); - - /* Return pointer to statically allocated directory entry */ - return entry; -} - -/* - * Read next directory entry. - * - * Returns zero on success. If end of directory stream is reached, then sets - * result to NULL and returns zero. - */ -static int -_wreaddir_r( - _WDIR *dirp, - struct _wdirent *entry, - struct _wdirent **result) -{ - WIN32_FIND_DATAW *datap; - - /* Read next directory entry */ - datap = dirent_next (dirp); - if (datap) { - size_t n; - DWORD attr; - - /* - * Copy file name as wide-character string. If the file name is too - * long to fit in to the destination buffer, then truncate file name - * to PATH_MAX characters and zero-terminate the buffer. - */ - n = 0; - while (n < PATH_MAX && datap->cFileName[n] != 0) { - entry->d_name[n] = datap->cFileName[n]; - n++; - } - entry->d_name[n] = 0; - - /* Length of file name excluding zero terminator */ - entry->d_namlen = n; - - /* File type */ - attr = datap->dwFileAttributes; - if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { - entry->d_type = DT_CHR; - } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { - entry->d_type = DT_DIR; - } else { - entry->d_type = DT_REG; - } - - /* Reset dummy fields */ - entry->d_ino = 0; - entry->d_off = 0; - entry->d_reclen = sizeof (struct _wdirent); - - /* Set result address */ - *result = entry; - - } else { - - /* Return NULL to indicate end of directory */ - *result = NULL; - - } - - return /*OK*/0; -} - -/* - * Close directory stream opened by opendir() function. This invalidates the - * DIR structure as well as any directory entry read previously by - * _wreaddir(). - */ -static int -_wclosedir( - _WDIR *dirp) -{ - int ok; - if (dirp) { - - /* Release search handle */ - if (dirp->handle != INVALID_HANDLE_VALUE) { - FindClose (dirp->handle); - } - - /* Release search pattern */ - free (dirp->patt); - - /* Release directory structure */ - free (dirp); - ok = /*success*/0; - - } else { - - /* Invalid directory stream */ - dirent_set_errno (EBADF); - ok = /*failure*/-1; - - } - return ok; -} - -/* - * Rewind directory stream such that _wreaddir() returns the very first - * file name again. - */ -static void -_wrewinddir( - _WDIR* dirp) -{ - if (dirp) { - /* Release existing search handle */ - if (dirp->handle != INVALID_HANDLE_VALUE) { - FindClose (dirp->handle); - } - - /* Open new search handle */ - dirent_first (dirp); - } -} - -/* Get first directory entry (internal) */ -static WIN32_FIND_DATAW* -dirent_first( - _WDIR *dirp) -{ - WIN32_FIND_DATAW *datap; - DWORD error; - - /* Open directory and retrieve the first entry */ - dirp->handle = FindFirstFileExW( - dirp->patt, FindExInfoStandard, &dirp->data, - FindExSearchNameMatch, NULL, 0); - if (dirp->handle != INVALID_HANDLE_VALUE) { - - /* a directory entry is now waiting in memory */ - datap = &dirp->data; - dirp->cached = 1; - - } else { - - /* Failed to open directory: no directory entry in memory */ - dirp->cached = 0; - datap = NULL; - - /* Set error code */ - error = GetLastError (); - switch (error) { - case ERROR_ACCESS_DENIED: - /* No read access to directory */ - dirent_set_errno (EACCES); - break; - - case ERROR_DIRECTORY: - /* Directory name is invalid */ - dirent_set_errno (ENOTDIR); - break; - - case ERROR_PATH_NOT_FOUND: - default: - /* Cannot find the file */ - dirent_set_errno (ENOENT); - } - - } - return datap; -} - -/* - * Get next directory entry (internal). - * - * Returns - */ -static WIN32_FIND_DATAW* -dirent_next( - _WDIR *dirp) -{ - WIN32_FIND_DATAW *p; - - /* Get next directory entry */ - if (dirp->cached != 0) { - - /* A valid directory entry already in memory */ - p = &dirp->data; - dirp->cached = 0; - - } else if (dirp->handle != INVALID_HANDLE_VALUE) { - - /* Get the next directory entry from stream */ - if (FindNextFileW (dirp->handle, &dirp->data) != 0) { - /* Got a file */ - p = &dirp->data; - } else { - /* The very last entry has been processed or an error occurred */ - FindClose (dirp->handle); - dirp->handle = INVALID_HANDLE_VALUE; - p = NULL; - } - - } else { - - /* End of directory stream reached */ - p = NULL; - - } - - return p; -} - -/* - * Open directory stream using plain old C-string. - */ -static DIR* -opendir( - const char *dirname) -{ - struct DIR *dirp; - - /* Must have directory name */ - if (dirname == NULL || dirname[0] == '\0') { - dirent_set_errno (ENOENT); - return NULL; - } - - /* Allocate memory for DIR structure */ - dirp = (DIR*) malloc (sizeof (struct DIR)); - if (!dirp) { - return NULL; - } - { - int error; - wchar_t wname[PATH_MAX + 1]; - size_t n; - - /* Convert directory name to wide-character string */ - error = dirent_mbstowcs_s( - &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); - if (error) { - /* - * Cannot convert file name to wide-character string. This - * occurs if the string contains invalid multi-byte sequences or - * the output buffer is too small to contain the resulting - * string. - */ - goto exit_free; - } - - - /* Open directory stream using wide-character name */ - dirp->wdirp = _wopendir (wname); - if (!dirp->wdirp) { - goto exit_free; - } - - } - - /* Success */ - return dirp; - - /* Failure */ -exit_free: - free (dirp); - return NULL; -} - -/* - * Read next directory entry. - */ -static struct dirent* -readdir( - DIR *dirp) -{ - struct dirent *entry; - - /* - * Read directory entry to buffer. We can safely ignore the return value - * as entry will be set to NULL in case of error. - */ - (void) readdir_r (dirp, &dirp->ent, &entry); - - /* Return pointer to statically allocated directory entry */ - return entry; -} - -/* - * Read next directory entry into called-allocated buffer. - * - * Returns zero on success. If the end of directory stream is reached, then - * sets result to NULL and returns zero. - */ -static int -readdir_r( - DIR *dirp, - struct dirent *entry, - struct dirent **result) -{ - WIN32_FIND_DATAW *datap; - - /* Read next directory entry */ - datap = dirent_next (dirp->wdirp); - if (datap) { - size_t n; - int error; - - /* Attempt to convert file name to multi-byte string */ - error = dirent_wcstombs_s( - &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); - - /* - * If the file name cannot be represented by a multi-byte string, - * then attempt to use old 8+3 file name. This allows traditional - * Unix-code to access some file names despite of unicode - * characters, although file names may seem unfamiliar to the user. - * - * Be ware that the code below cannot come up with a short file - * name unless the file system provides one. At least - * VirtualBox shared folders fail to do this. - */ - if (error && datap->cAlternateFileName[0] != '\0') { - error = dirent_wcstombs_s( - &n, entry->d_name, PATH_MAX + 1, - datap->cAlternateFileName, PATH_MAX + 1); - } - - if (!error) { - DWORD attr; - - /* Length of file name excluding zero terminator */ - entry->d_namlen = n - 1; - - /* File attributes */ - attr = datap->dwFileAttributes; - if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { - entry->d_type = DT_CHR; - } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { - entry->d_type = DT_DIR; - } else { - entry->d_type = DT_REG; - } - - /* Reset dummy fields */ - entry->d_ino = 0; - entry->d_off = 0; - entry->d_reclen = sizeof (struct dirent); - - } else { - - /* - * Cannot convert file name to multi-byte string so construct - * an erroneous directory entry and return that. Note that - * we cannot return NULL as that would stop the processing - * of directory entries completely. - */ - entry->d_name[0] = '?'; - entry->d_name[1] = '\0'; - entry->d_namlen = 1; - entry->d_type = DT_UNKNOWN; - entry->d_ino = 0; - entry->d_off = -1; - entry->d_reclen = 0; - - } - - /* Return pointer to directory entry */ - *result = entry; - - } else { - - /* No more directory entries */ - *result = NULL; - - } - - return /*OK*/0; -} - -/* - * Close directory stream. - */ -static int -closedir( - DIR *dirp) -{ - int ok; - if (dirp) { - - /* Close wide-character directory stream */ - ok = _wclosedir (dirp->wdirp); - dirp->wdirp = NULL; - - /* Release multi-byte character version */ - free (dirp); - - } else { - - /* Invalid directory stream */ - dirent_set_errno (EBADF); - ok = /*failure*/-1; - - } - return ok; -} - -/* - * Rewind directory stream to beginning. - */ -static void -rewinddir( - DIR* dirp) -{ - /* Rewind wide-character string directory stream */ - _wrewinddir (dirp->wdirp); -} - -/* - * Scan directory for entries. - */ -static int -scandir( - const char *dirname, - struct dirent ***namelist, - int (*filter)(const struct dirent*), - int (*compare)(const struct dirent**, const struct dirent**)) -{ - struct dirent **files = NULL; - size_t size = 0; - size_t allocated = 0; - const size_t init_size = 1; - DIR *dir = NULL; - struct dirent *entry; - struct dirent *tmp = NULL; - size_t i; - int result = 0; - - /* Open directory stream */ - dir = opendir (dirname); - if (dir) { - - /* Read directory entries to memory */ - while (1) { - - /* Enlarge pointer table to make room for another pointer */ - if (size >= allocated) { - void *p; - size_t num_entries; - - /* Compute number of entries in the enlarged pointer table */ - if (size < init_size) { - /* Allocate initial pointer table */ - num_entries = init_size; - } else { - /* Double the size */ - num_entries = size * 2; - } - - /* Allocate first pointer table or enlarge existing table */ - p = realloc (files, sizeof (void*) * num_entries); - if (p != NULL) { - /* Got the memory */ - files = (dirent**) p; - allocated = num_entries; - } else { - /* Out of memory */ - result = -1; - break; - } - - } - - /* Allocate room for temporary directory entry */ - if (tmp == NULL) { - tmp = (struct dirent*) malloc (sizeof (struct dirent)); - if (tmp == NULL) { - /* Cannot allocate temporary directory entry */ - result = -1; - break; - } - } - - /* Read directory entry to temporary area */ - if (readdir_r (dir, tmp, &entry) == /*OK*/0) { - - /* Did we get an entry? */ - if (entry != NULL) { - int pass; - - /* Determine whether to include the entry in result */ - if (filter) { - /* Let the filter function decide */ - pass = filter (tmp); - } else { - /* No filter function, include everything */ - pass = 1; - } - - if (pass) { - /* Store the temporary entry to pointer table */ - files[size++] = tmp; - tmp = NULL; - - /* Keep up with the number of files */ - result++; - } - - } else { - - /* - * End of directory stream reached => sort entries and - * exit. - */ - qsort (files, size, sizeof (void*), - (int (*) (const void*, const void*)) compare); - break; - - } - - } else { - /* Error reading directory entry */ - result = /*Error*/ -1; - break; - } - - } - - } else { - /* Cannot open directory */ - result = /*Error*/ -1; - } - - /* Release temporary directory entry */ - free (tmp); - - /* Release allocated memory on error */ - if (result < 0) { - for (i = 0; i < size; i++) { - free (files[i]); - } - free (files); - files = NULL; - } - - /* Close directory stream */ - if (dir) { - closedir (dir); - } - - /* Pass pointer table to caller */ - if (namelist) { - *namelist = files; - } - return result; -} - -/* Alphabetical sorting */ -static int -alphasort( - const struct dirent **a, const struct dirent **b) -{ - return strcoll ((*a)->d_name, (*b)->d_name); -} - -/* Sort versions */ -static int -versionsort( - const struct dirent **a, const struct dirent **b) -{ - /* FIXME: implement strverscmp and use that */ - return alphasort (a, b); -} - -/* Convert multi-byte string to wide character string */ -static int -dirent_mbstowcs_s( - size_t *pReturnValue, - wchar_t *wcstr, - size_t sizeInWords, - const char *mbstr, - size_t count) -{ - int error; - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - - /* Microsoft Visual Studio 2005 or later */ - error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); - -#else - - /* Older Visual Studio or non-Microsoft compiler */ - size_t n; - - /* Convert to wide-character string (or count characters) */ - n = mbstowcs (wcstr, mbstr, sizeInWords); - if (!wcstr || n < count) { - - /* Zero-terminate output buffer */ - if (wcstr && sizeInWords) { - if (n >= sizeInWords) { - n = sizeInWords - 1; - } - wcstr[n] = 0; - } - - /* Length of resulting multi-byte string WITH zero terminator */ - if (pReturnValue) { - *pReturnValue = n + 1; - } - - /* Success */ - error = 0; - - } else { - - /* Could not convert string */ - error = 1; - - } - -#endif - return error; -} - -/* Convert wide-character string to multi-byte string */ -static int -dirent_wcstombs_s( - size_t *pReturnValue, - char *mbstr, - size_t sizeInBytes, /* max size of mbstr */ - const wchar_t *wcstr, - size_t count) -{ - int error; - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - - /* Microsoft Visual Studio 2005 or later */ - error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); - -#else - - /* Older Visual Studio or non-Microsoft compiler */ - size_t n; - - /* Convert to multi-byte string (or count the number of bytes needed) */ - n = wcstombs (mbstr, wcstr, sizeInBytes); - if (!mbstr || n < count) { - - /* Zero-terminate output buffer */ - if (mbstr && sizeInBytes) { - if (n >= sizeInBytes) { - n = sizeInBytes - 1; - } - mbstr[n] = '\0'; - } - - /* Length of resulting multi-bytes string WITH zero-terminator */ - if (pReturnValue) { - *pReturnValue = n + 1; - } - - /* Success */ - error = 0; - - } else { - - /* Cannot convert string */ - error = 1; - - } - -#endif - return error; -} - -/* Set errno variable */ -static void -dirent_set_errno( - int error) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - - /* Microsoft Visual Studio 2005 and later */ - _set_errno (error); - -#else - - /* Non-Microsoft compiler or older Microsoft compiler */ - errno = error; - -#endif -} - - -#ifdef __cplusplus -} -#endif -#endif /*DIRENT_H*/ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/dtoa.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/dtoa.c new file mode 100755 index 00000000..a89e824f --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/dtoa.c @@ -0,0 +1,1619 @@ +/* + * Tiny float64 printing and parsing library + * + * Copyright (c) 2024 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +// #include +#include +// #include + +#include "cutils.h" +#include "dtoa.h" + +/* + TODO: + - simplify subnormal handling + - reduce max memory usage + - free format: could add shortcut if exact result + - use 64 bit limb_t when possible + - use another algorithm for free format dtoa in base 10 (ryu ?) +*/ + +#define USE_POW5_TABLE +/* use fast path to print small integers in free format */ +#define USE_FAST_INT + +#define LIMB_LOG2_BITS 5 + +#define LIMB_BITS (1 << LIMB_LOG2_BITS) + +typedef int32_t slimb_t; +typedef uint32_t limb_t; +typedef uint64_t dlimb_t; + +#define LIMB_DIGITS 9 + +#define JS_RADIX_MAX 36 + +#define DBIGNUM_LEN_MAX 52 /* ~ 2^(1072+53)*36^100 (dtoa) */ +#define MANT_LEN_MAX 18 /* < 36^100 */ + +typedef intptr_t mp_size_t; + +/* the represented number is sum(i, tab[i]*2^(LIMB_BITS * i)) */ +typedef struct { + int len; /* >= 1 */ + limb_t tab[]; +} mpb_t; + +static limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n) +{ + size_t i; + limb_t k, a; + + k=b; + for(i=0;i> LIMB_BITS; + } + return l; +} + +/* WARNING: d must be >= 2^(LIMB_BITS-1) */ +static inline limb_t udiv1norm_init(limb_t d) +{ + limb_t a0, a1; + a1 = -d - 1; + a0 = -1; + return (((dlimb_t)a1 << LIMB_BITS) | a0) / d; +} + +/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0 + / d' with 0 <= a1 < d. */ +static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0, + limb_t d, limb_t d_inv) +{ + limb_t n1m, n_adj, q, r, ah; + dlimb_t a; + n1m = ((slimb_t)a0 >> (LIMB_BITS - 1)); + n_adj = a0 + (n1m & d); + a = (dlimb_t)d_inv * (a1 - n1m) + n_adj; + q = (a >> LIMB_BITS) + a1; + /* compute a - q * r and update q so that the remainder is between + 0 and d - 1 */ + a = ((dlimb_t)a1 << LIMB_BITS) | a0; + a = a - (dlimb_t)q * d - d; + ah = a >> LIMB_BITS; + q += 1 + ah; + r = (limb_t)a + (ah & d); + *pr = r; + return q; +} + +static limb_t mp_div1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b, limb_t r) +{ + slimb_t i; + dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((dlimb_t)r << LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; + } + return r; +} + +/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). + 1 <= shift <= LIMB_BITS - 1 */ +static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n, + int shift, limb_t high) +{ + mp_size_t i; + limb_t l, a; + + assert(shift >= 1 && shift < LIMB_BITS); + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift)); + l = a; + } + return l & (((limb_t)1 << shift) - 1); +} + +/* r = (a << shift) + low. 1 <= shift <= LIMB_BITS - 1, 0 <= low < + 2^shift. */ +static limb_t mp_shl(limb_t *tab_r, const limb_t *tab, mp_size_t n, + int shift, limb_t low) +{ + mp_size_t i; + limb_t l, a; + + assert(shift >= 1 && shift < LIMB_BITS); + l = low; + for(i = 0; i < n; i++) { + a = tab[i]; + tab_r[i] = (a << shift) | l; + l = (a >> (LIMB_BITS - shift)); + } + return l; +} + +static no_inline limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b, limb_t r, limb_t b_inv, int shift) +{ + slimb_t i; + + if (shift != 0) { + r = (r << shift) | mp_shl(tabr, taba, n, shift, 0); + } + for(i = n - 1; i >= 0; i--) { + tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); + } + r >>= shift; + return r; +} + +static __maybe_unused void mpb_dump(const char *str, const mpb_t *a) +{ + int i; + + printf("%s= 0x", str); + for(i = a->len - 1; i >= 0; i--) { + printf("%08x", a->tab[i]); + if (i != 0) + printf("_"); + } + printf("\n"); +} + +static void mpb_renorm(mpb_t *r) +{ + while (r->len > 1 && r->tab[r->len - 1] == 0) + r->len--; +} + +#ifdef USE_POW5_TABLE +static const uint32_t pow5_table[17] = { + 0x00000005, 0x00000019, 0x0000007d, 0x00000271, + 0x00000c35, 0x00003d09, 0x0001312d, 0x0005f5e1, + 0x001dcd65, 0x009502f9, 0x02e90edd, 0x0e8d4a51, + 0x48c27395, 0x6bcc41e9, 0x1afd498d, 0x86f26fc1, + 0xa2bc2ec5, +}; + +static const uint8_t pow5h_table[4] = { + 0x00000001, 0x00000007, 0x00000023, 0x000000b1, +}; + +static const uint32_t pow5_inv_table[13] = { + 0x99999999, 0x47ae147a, 0x0624dd2f, 0xa36e2eb1, + 0x4f8b588e, 0x0c6f7a0b, 0xad7f29ab, 0x5798ee23, + 0x12e0be82, 0xb7cdfd9d, 0x5fd7fe17, 0x19799812, + 0xc25c2684, +}; +#endif + +/* return a^b */ +static uint64_t pow_ui(uint32_t a, uint32_t b) +{ + int i, n_bits; + uint64_t r; + if (b == 0) + return 1; + if (b == 1) + return a; +#ifdef USE_POW5_TABLE + if ((a == 5 || a == 10) && b <= 17) { + r = pow5_table[b - 1]; + if (b >= 14) { + r |= (uint64_t)pow5h_table[b - 14] << 32; + } + if (a == 10) + r <<= b; + return r; + } +#endif + r = a; + n_bits = 32 - clz32(b); + for(i = n_bits - 2; i >= 0; i--) { + r *= r; + if ((b >> i) & 1) + r *= a; + } + return r; +} + +static uint32_t pow_ui_inv(uint32_t *pr_inv, int *pshift, uint32_t a, uint32_t b) +{ + uint32_t r_inv, r; + int shift; +#ifdef USE_POW5_TABLE + if (a == 5 && b >= 1 && b <= 13) { + r = pow5_table[b - 1]; + shift = clz32(r); + r <<= shift; + r_inv = pow5_inv_table[b - 1]; + } else +#endif + { + r = pow_ui(a, b); + shift = clz32(r); + r <<= shift; + r_inv = udiv1norm_init(r); + } + *pshift = shift; + *pr_inv = r_inv; + return r; +} + +enum { + JS_RNDN, /* round to nearest, ties to even */ + JS_RNDNA, /* round to nearest, ties away from zero */ + JS_RNDZ, +}; + +static int mpb_get_bit(const mpb_t *r, int k) +{ + int l; + + l = (unsigned)k / LIMB_BITS; + k = k & (LIMB_BITS - 1); + if (l >= r->len) + return 0; + else + return (r->tab[l] >> k) & 1; +} + +/* compute round(r / 2^shift). 'shift' can be negative */ +static void mpb_shr_round(mpb_t *r, int shift, int rnd_mode) +{ + int l, i; + + if (shift == 0) + return; + if (shift < 0) { + shift = -shift; + l = (unsigned)shift / LIMB_BITS; + shift = shift & (LIMB_BITS - 1); + if (shift != 0) { + r->tab[r->len] = mp_shl(r->tab, r->tab, r->len, shift, 0); + r->len++; + mpb_renorm(r); + } + if (l > 0) { + for(i = r->len - 1; i >= 0; i--) + r->tab[i + l] = r->tab[i]; + for(i = 0; i < l; i++) + r->tab[i] = 0; + r->len += l; + } + } else { + limb_t bit1, bit2; + int k, add_one; + + switch(rnd_mode) { + default: + case JS_RNDZ: + add_one = 0; + break; + case JS_RNDN: + case JS_RNDNA: + bit1 = mpb_get_bit(r, shift - 1); + if (bit1) { + if (rnd_mode == JS_RNDNA) { + bit2 = 1; + } else { + /* bit2 = oring of all the bits after bit1 */ + bit2 = 0; + if (shift >= 2) { + k = shift - 1; + l = (unsigned)k / LIMB_BITS; + k = k & (LIMB_BITS - 1); + for(i = 0; i < min_int(l, r->len); i++) + bit2 |= r->tab[i]; + if (l < r->len) + bit2 |= r->tab[l] & (((limb_t)1 << k) - 1); + } + } + if (bit2) { + add_one = 1; + } else { + /* round to even */ + add_one = mpb_get_bit(r, shift); + } + } else { + add_one = 0; + } + break; + } + + l = (unsigned)shift / LIMB_BITS; + shift = shift & (LIMB_BITS - 1); + if (l >= r->len) { + r->len = 1; + r->tab[0] = add_one; + } else { + if (l > 0) { + r->len -= l; + for(i = 0; i < r->len; i++) + r->tab[i] = r->tab[i + l]; + } + if (shift != 0) { + mp_shr(r->tab, r->tab, r->len, shift, 0); + mpb_renorm(r); + } + if (add_one) { + limb_t a; + a = mp_add_ui(r->tab, 1, r->len); + if (a) + r->tab[r->len++] = a; + } + } + } +} + +/* return -1, 0 or 1 */ +static int mpb_cmp(const mpb_t *a, const mpb_t *b) +{ + mp_size_t i; + if (a->len < b->len) + return -1; + else if (a->len > b->len) + return 1; + for(i = a->len - 1; i >= 0; i--) { + if (a->tab[i] != b->tab[i]) { + if (a->tab[i] < b->tab[i]) + return -1; + else + return 1; + } + } + return 0; +} + +static void mpb_set_u64(mpb_t *r, uint64_t m) +{ +#if LIMB_BITS == 64 + r->tab[0] = m; + r->len = 1; +#else + r->tab[0] = m; + r->tab[1] = m >> LIMB_BITS; + if (r->tab[1] == 0) + r->len = 1; + else + r->len = 2; +#endif +} + +static uint64_t mpb_get_u64(mpb_t *r) +{ +#if LIMB_BITS == 64 + return r->tab[0]; +#else + if (r->len == 1) { + return r->tab[0]; + } else { + return r->tab[0] | ((uint64_t)r->tab[1] << LIMB_BITS); + } +#endif +} + +/* floor_log2() = position of the first non zero bit or -1 if zero. */ +static int mpb_floor_log2(mpb_t *a) +{ + limb_t v; + v = a->tab[a->len - 1]; + if (v == 0) + return -1; + else + return a->len * LIMB_BITS - 1 - clz32(v); +} + +#define MUL_LOG2_RADIX_BASE_LOG2 24 + +/* round((1 << MUL_LOG2_RADIX_BASE_LOG2)/log2(i + 2)) */ +static const uint32_t mul_log2_radix_table[JS_RADIX_MAX - 1] = { + 0x000000, 0xa1849d, 0x000000, 0x6e40d2, + 0x6308c9, 0x5b3065, 0x000000, 0x50c24e, + 0x4d104d, 0x4a0027, 0x4768ce, 0x452e54, + 0x433d00, 0x418677, 0x000000, 0x3ea16b, + 0x3d645a, 0x3c43c2, 0x3b3b9a, 0x3a4899, + 0x39680b, 0x3897b3, 0x37d5af, 0x372069, + 0x367686, 0x35d6df, 0x354072, 0x34b261, + 0x342bea, 0x33ac62, 0x000000, 0x32bfd9, + 0x3251dd, 0x31e8d6, 0x318465, +}; + +/* return floor(a / log2(radix)) for -2048 <= a <= 2047 */ +static int mul_log2_radix(int a, int radix) +{ + int radix_bits, mult; + + if ((radix & (radix - 1)) == 0) { + /* if the radix is a power of two better to do it exactly */ + radix_bits = 31 - clz32(radix); + if (a < 0) + a -= radix_bits - 1; + return a / radix_bits; + } else { + mult = mul_log2_radix_table[radix - 2]; + return ((int64_t)a * mult) >> MUL_LOG2_RADIX_BASE_LOG2; + } +} + +#if 0 +static void build_mul_log2_radix_table(void) +{ + int base, radix, mult, col, base_log2; + + base_log2 = 24; + base = 1 << base_log2; + col = 0; + for(radix = 2; radix <= 36; radix++) { + if ((radix & (radix - 1)) == 0) + mult = 0; + else + mult = lrint((double)base / log2(radix)); + printf("0x%06x, ", mult); + if (++col == 4) { + printf("\n"); + col = 0; + } + } + printf("\n"); +} + +static void mul_log2_radix_test(void) +{ + int radix, i, ref, r; + + for(radix = 2; radix <= 36; radix++) { + for(i = -2048; i <= 2047; i++) { + ref = (int)floor((double)i / log2(radix)); + r = mul_log2_radix(i, radix); + if (ref != r) { + printf("ERROR: radix=%d i=%d r=%d ref=%d\n", + radix, i, r, ref); + exit(1); + } + } + } + if (0) + build_mul_log2_radix_table(); +} +#endif + +static void u32toa_len(char *buf, uint32_t n, size_t len) +{ + int digit, i; + for(i = len - 1; i >= 0; i--) { + digit = n % 10; + n = n / 10; + buf[i] = digit + '0'; + } +} + +/* for power of 2 radixes. len >= 1 */ +static void u64toa_bin_len(char *buf, uint64_t n, unsigned int radix_bits, int len) +{ + int digit, i; + unsigned int mask; + + mask = (1 << radix_bits) - 1; + for(i = len - 1; i >= 0; i--) { + digit = n & mask; + n >>= radix_bits; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + buf[i] = digit; + } +} + +/* len >= 1. 2 <= radix <= 36 */ +static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len) +{ + int digit, i; + + if (radix == 10) { + /* specific case with constant divisor */ +#if LIMB_BITS == 32 + u32toa_len(buf, n, len); +#else + /* XXX: optimize */ + for(i = len - 1; i >= 0; i--) { + digit = (limb_t)n % 10; + n = (limb_t)n / 10; + buf[i] = digit + '0'; + } +#endif + } else { + for(i = len - 1; i >= 0; i--) { + digit = (limb_t)n % radix; + n = (limb_t)n / radix; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + buf[i] = digit; + } + } +} + +size_t u32toa(char *buf, uint32_t n) +{ + char buf1[10], *q; + size_t len; + + q = buf1 + sizeof(buf1); + do { + *--q = n % 10 + '0'; + n /= 10; + } while (n != 0); + len = buf1 + sizeof(buf1) - q; + memcpy(buf, q, len); + return len; +} + +size_t i32toa(char *buf, int32_t n) +{ + if (n >= 0) { + return u32toa(buf, n); + } else { + buf[0] = '-'; + return u32toa(buf + 1, -(uint32_t)n) + 1; + } +} + +#ifdef USE_FAST_INT +size_t u64toa(char *buf, uint64_t n) +{ + if (n < 0x100000000) { + return u32toa(buf, n); + } else { + uint64_t n1; + char *q = buf; + uint32_t n2; + + n1 = n / 1000000000; + n %= 1000000000; + if (n1 >= 0x100000000) { + n2 = n1 / 1000000000; + n1 = n1 % 1000000000; + /* at most two digits */ + if (n2 >= 10) { + *q++ = n2 / 10 + '0'; + n2 %= 10; + } + *q++ = n2 + '0'; + u32toa_len(q, n1, 9); + q += 9; + } else { + q += u32toa(q, n1); + } + u32toa_len(q, n, 9); + q += 9; + return q - buf; + } +} + +size_t i64toa(char *buf, int64_t n) +{ + if (n >= 0) { + return u64toa(buf, n); + } else { + buf[0] = '-'; + return u64toa(buf + 1, -(uint64_t)n) + 1; + } +} + +/* XXX: only tested for 1 <= n < 2^53 */ +size_t u64toa_radix(char *buf, uint64_t n, unsigned int radix) +{ + int radix_bits, l; + if (likely(radix == 10)) + return u64toa(buf, n); + if ((radix & (radix - 1)) == 0) { + radix_bits = 31 - clz32(radix); + if (n == 0) + l = 1; + else + l = (64 - clz64(n) + radix_bits - 1) / radix_bits; + u64toa_bin_len(buf, n, radix_bits, l); + return l; + } else { + char buf1[41], *q; /* maximum length for radix = 3 */ + size_t len; + int digit; + q = buf1 + sizeof(buf1); + do { + digit = n % radix; + n /= radix; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + *--q = digit; + } while (n != 0); + len = buf1 + sizeof(buf1) - q; + memcpy(buf, q, len); + return len; + } +} + +size_t i64toa_radix(char *buf, int64_t n, unsigned int radix) +{ + if (n >= 0) { + return u64toa_radix(buf, n, radix); + } else { + buf[0] = '-'; + return u64toa_radix(buf + 1, -(uint64_t)n, radix) + 1; + } +} +#endif /* USE_FAST_INT */ + +static const uint8_t digits_per_limb_table[JS_RADIX_MAX - 1] = { +#if LIMB_BITS == 32 +32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +#else +64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12, +#endif +}; + +static const uint32_t radix_base_table[JS_RADIX_MAX - 1] = { + 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395, + 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91, + 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021, + 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571, + 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d, + 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51, + 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899, + 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1, + 0x5c13d840, 0x6d91b519, 0x81bf1000, +}; + +/* XXX: remove the table ? */ +static uint8_t dtoa_max_digits_table[JS_RADIX_MAX - 1] = { + 54, 35, 28, 24, 22, 20, 19, 18, 17, 17, 16, 16, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, +}; + +/* we limit the maximum number of significant digits for atod to about + 128 bits of precision for non power of two bases. The only + requirement for Javascript is at least 20 digits in base 10. For + power of two bases, we do an exact rounding in all the cases. */ +static uint8_t atod_max_digits_table[JS_RADIX_MAX - 1] = { + 64, 80, 32, 55, 49, 45, 21, 40, 38, 37, 35, 34, 33, 32, 16, 31, 30, 30, 29, 29, 28, 28, 27, 27, 27, 26, 26, 26, 26, 25, 12, 25, 25, 24, 24, +}; + +/* if abs(d) >= B^max_exponent, it is an overflow */ +static const int16_t max_exponent[JS_RADIX_MAX - 1] = { + 1024, 647, 512, 442, 397, 365, 342, 324, + 309, 297, 286, 277, 269, 263, 256, 251, + 246, 242, 237, 234, 230, 227, 224, 221, + 218, 216, 214, 211, 209, 207, 205, 203, + 202, 200, 199, +}; + +/* if abs(d) <= B^min_exponent, it is an underflow */ +static const int16_t min_exponent[JS_RADIX_MAX - 1] = { +-1075, -679, -538, -463, -416, -383, -359, -340, + -324, -311, -300, -291, -283, -276, -269, -263, + -258, -254, -249, -245, -242, -238, -235, -232, + -229, -227, -224, -222, -220, -217, -215, -214, + -212, -210, -208, +}; + +#if 0 +void build_tables(void) +{ + int r, j, radix, n, col, i; + + /* radix_base_table */ + for(radix = 2; radix <= 36; radix++) { + r = 1; + for(j = 0; j < digits_per_limb_table[radix - 2]; j++) { + r *= radix; + } + printf(" 0x%08x,", r); + if ((radix % 4) == 1) + printf("\n"); + } + printf("\n"); + + /* dtoa_max_digits_table */ + for(radix = 2; radix <= 36; radix++) { + /* Note: over estimated when the radix is a power of two */ + printf(" %d,", 1 + (int)ceil(53.0 / log2(radix))); + } + printf("\n"); + + /* atod_max_digits_table */ + for(radix = 2; radix <= 36; radix++) { + if ((radix & (radix - 1)) == 0) { + /* 64 bits is more than enough */ + n = (int)floor(64.0 / log2(radix)); + } else { + n = (int)floor(128.0 / log2(radix)); + } + printf(" %d,", n); + } + printf("\n"); + + printf("static const int16_t max_exponent[JS_RADIX_MAX - 1] = {\n"); + col = 0; + for(radix = 2; radix <= 36; radix++) { + printf("%5d, ", (int)ceil(1024 / log2(radix))); + if (++col == 8) { + col = 0; + printf("\n"); + } + } + printf("\n};\n\n"); + + printf("static const int16_t min_exponent[JS_RADIX_MAX - 1] = {\n"); + col = 0; + for(radix = 2; radix <= 36; radix++) { + printf("%5d, ", (int)floor(-1075 / log2(radix))); + if (++col == 8) { + col = 0; + printf("\n"); + } + } + printf("\n};\n\n"); + + printf("static const uint32_t pow5_table[16] = {\n"); + col = 0; + for(i = 2; i <= 17; i++) { + r = 1; + for(j = 0; j < i; j++) { + r *= 5; + } + printf("0x%08x, ", r); + if (++col == 4) { + col = 0; + printf("\n"); + } + } + printf("\n};\n\n"); + + /* high part */ + printf("static const uint8_t pow5h_table[4] = {\n"); + col = 0; + for(i = 14; i <= 17; i++) { + uint64_t r1; + r1 = 1; + for(j = 0; j < i; j++) { + r1 *= 5; + } + printf("0x%08x, ", (uint32_t)(r1 >> 32)); + if (++col == 4) { + col = 0; + printf("\n"); + } + } + printf("\n};\n\n"); +} +#endif + +/* n_digits >= 1. 0 <= dot_pos <= n_digits. If dot_pos == n_digits, + the dot is not displayed. 'a' is modified. */ +static int output_digits(char *buf, + mpb_t *a, int radix, int n_digits1, + int dot_pos) +{ + int n_digits, digits_per_limb, radix_bits, n, len; + + n_digits = n_digits1; + if ((radix & (radix - 1)) == 0) { + /* radix = 2^radix_bits */ + radix_bits = 31 - clz32(radix); + } else { + radix_bits = 0; + } + digits_per_limb = digits_per_limb_table[radix - 2]; + if (radix_bits != 0) { + for(;;) { + n = min_int(n_digits, digits_per_limb); + n_digits -= n; + u64toa_bin_len(buf + n_digits, a->tab[0], radix_bits, n); + if (n_digits == 0) + break; + mpb_shr_round(a, digits_per_limb * radix_bits, JS_RNDZ); + } + } else { + limb_t r; + while (n_digits != 0) { + n = min_int(n_digits, digits_per_limb); + n_digits -= n; + r = mp_div1(a->tab, a->tab, a->len, radix_base_table[radix - 2], 0); + mpb_renorm(a); + limb_to_a(buf + n_digits, r, radix, n); + } + } + + /* add the dot */ + len = n_digits1; + if (dot_pos != n_digits1) { + memmove(buf + dot_pos + 1, buf + dot_pos, n_digits1 - dot_pos); + buf[dot_pos] = '.'; + len++; + } + return len; +} + +/* return (a, e_offset) such that a = a * (radix1*2^radix_shift)^f * + 2^-e_offset. 'f' can be negative. */ +static int mul_pow(mpb_t *a, int radix1, int radix_shift, int f, bool is_int, int e) +{ + int e_offset, d, n, n0; + + e_offset = -f * radix_shift; + if (radix1 != 1) { + d = digits_per_limb_table[radix1 - 2]; + if (f >= 0) { + limb_t h, b; + + b = 0; + n0 = 0; + while (f != 0) { + n = min_int(f, d); + if (n != n0) { + b = pow_ui(radix1, n); + n0 = n; + } + h = mp_mul1(a->tab, a->tab, a->len, b, 0); + if (h != 0) { + a->tab[a->len++] = h; + } + f -= n; + } + } else { + int extra_bits, l, shift; + limb_t r, rem, b, b_inv; + + f = -f; + l = (f + d - 1) / d; /* high bound for the number of limbs (XXX: make it better) */ + e_offset += l * LIMB_BITS; + if (!is_int) { + /* at least 'e' bits are needed in the final result for rounding */ + extra_bits = max_int(e - mpb_floor_log2(a), 0); + } else { + /* at least two extra bits are needed in the final result + for rounding */ + extra_bits = max_int(2 + e - e_offset, 0); + } + e_offset += extra_bits; + mpb_shr_round(a, -(l * LIMB_BITS + extra_bits), JS_RNDZ); + + b = 0; + b_inv = 0; + shift = 0; + n0 = 0; + rem = 0; + while (f != 0) { + n = min_int(f, d); + if (n != n0) { + b = pow_ui_inv(&b_inv, &shift, radix1, n); + n0 = n; + } + r = mp_div1norm(a->tab, a->tab, a->len, b, 0, b_inv, shift); + rem |= r; + mpb_renorm(a); + f -= n; + } + /* if the remainder is non zero, use it for rounding */ + a->tab[0] |= (rem != 0); + } + } + return e_offset; +} + +/* tmp1 = round(m*2^e*radix^f). 'tmp0' is a temporary storage */ +static void mul_pow_round(mpb_t *tmp1, uint64_t m, int e, int radix1, int radix_shift, int f, + int rnd_mode) +{ + int e_offset; + + mpb_set_u64(tmp1, m); + e_offset = mul_pow(tmp1, radix1, radix_shift, f, true, e); + mpb_shr_round(tmp1, -e + e_offset, rnd_mode); +} + +/* return round(a*2^e_offset) rounded as a float64. 'a' is modified */ +static uint64_t round_to_d(int *pe, mpb_t *a, int e_offset, int rnd_mode) +{ + int e; + uint64_t m; + + if (a->tab[0] == 0 && a->len == 1) { + /* zero result */ + m = 0; + e = 0; /* don't care */ + } else { + int prec, prec1, e_min; + e = mpb_floor_log2(a) + 1 - e_offset; + prec1 = 53; + e_min = -1021; + if (e < e_min) { + /* subnormal result or zero */ + prec = prec1 - (e_min - e); + } else { + prec = prec1; + } + mpb_shr_round(a, e + e_offset - prec, rnd_mode); + m = mpb_get_u64(a); + m <<= (53 - prec); + /* mantissa overflow due to rounding */ + if (m >= (uint64_t)1 << 53) { + m >>= 1; + e++; + } + } + *pe = e; + return m; +} + +/* return (m, e) such that m*2^(e-53) = round(a * radix^f) with 2^52 + <= m < 2^53 or m = 0. + 'a' is modified. */ +static uint64_t mul_pow_round_to_d(int *pe, mpb_t *a, + int radix1, int radix_shift, int f, int rnd_mode) +{ + int e_offset; + + e_offset = mul_pow(a, radix1, radix_shift, f, false, 55); + return round_to_d(pe, a, e_offset, rnd_mode); +} + +#ifdef JS_DTOA_DUMP_STATS +static int out_len_count[17]; + +void js_dtoa_dump_stats(void) +{ + int i, sum; + sum = 0; + for(i = 0; i < 17; i++) + sum += out_len_count[i]; + for(i = 0; i < 17; i++) { + printf("%2d %8d %5.2f%%\n", + i + 1, out_len_count[i], (double)out_len_count[i] / sum * 100); + } +} +#endif + +/* return a maximum bound of the string length. The bound depends on + 'd' only if format = JS_DTOA_FORMAT_FRAC or if JS_DTOA_EXP_DISABLED + is enabled. */ +int js_dtoa_max_len(double d, int radix, int n_digits, int flags) +{ + int fmt = flags & JS_DTOA_FORMAT_MASK; + int n, e; + uint64_t a; + + if (fmt != JS_DTOA_FORMAT_FRAC) { + if (fmt == JS_DTOA_FORMAT_FREE) { + n = dtoa_max_digits_table[radix - 2]; + } else { + n = n_digits; + } + if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_DISABLED) { + /* no exponential */ + a = float64_as_uint64(d); + e = (a >> 52) & 0x7ff; + if (e == 0x7ff) { + /* NaN, Infinity */ + n = 0; + } else { + e -= 1023; + /* XXX: adjust */ + n += 10 + abs(mul_log2_radix(e - 1, radix)); + } + } else { + /* extra: sign, 1 dot and exponent "e-1000" */ + n += 1 + 1 + 6; + } + } else { + a = float64_as_uint64(d); + e = (a >> 52) & 0x7ff; + if (e == 0x7ff) { + /* NaN, Infinity */ + n = 0; + } else { + /* high bound for the integer part */ + e -= 1023; + /* x < 2^(e + 1) */ + if (e < 0) { + n = 1; + } else { + n = 2 + mul_log2_radix(e - 1, radix); + } + /* sign, extra digit, 1 dot */ + n += 1 + 1 + 1 + n_digits; + } + } + return max_int(n, 9); /* also include NaN and [-]Infinity */ +} + +#if defined(__SANITIZE_ADDRESS__) && 0 +static void *dtoa_malloc(uint64_t **pptr, size_t size) +{ + return malloc(size); +} +static void dtoa_free(void *ptr) +{ + free(ptr); +} +#else +static void *dtoa_malloc(uint64_t **pptr, size_t size) +{ + void *ret; + ret = *pptr; + *pptr += (size + 7) / 8; + return ret; +} + +static void dtoa_free(void *ptr) +{ +} +#endif + +/* return the length */ +int js_dtoa(char *buf, double d, int radix, int n_digits, int flags, + JSDTOATempMem *tmp_mem) +{ + uint64_t a, m, *mptr = tmp_mem->mem; + int e, sgn, l, E, P, i, E_max, radix1, radix_shift; + char *q; + mpb_t *tmp1, *mant_max; + int fmt = flags & JS_DTOA_FORMAT_MASK; + + tmp1 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX); + mant_max = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * MANT_LEN_MAX); + assert((mptr - tmp_mem->mem) <= sizeof(JSDTOATempMem) / sizeof(mptr[0])); + + radix_shift = ctz32(radix); + radix1 = radix >> radix_shift; + a = float64_as_uint64(d); + sgn = a >> 63; + e = (a >> 52) & 0x7ff; + m = a & (((uint64_t)1 << 52) - 1); + q = buf; + if (e == 0x7ff) { + if (m == 0) { + if (sgn) + *q++ = '-'; + memcpy(q, "Infinity", 8); + q += 8; + } else { + memcpy(q, "NaN", 3); + q += 3; + } + goto done; + } else if (e == 0) { + if (m == 0) { + tmp1->len = 1; + tmp1->tab[0] = 0; + E = 1; + if (fmt == JS_DTOA_FORMAT_FREE) + P = 1; + else if (fmt == JS_DTOA_FORMAT_FRAC) + P = n_digits + 1; + else + P = n_digits; + /* "-0" is displayed as "0" if JS_DTOA_MINUS_ZERO is not present */ + if (sgn && (flags & JS_DTOA_MINUS_ZERO)) + *q++ = '-'; + goto output; + } + /* denormal number: convert to a normal number */ + l = clz64(m) - 11; + e -= l - 1; + m <<= l; + } else { + m |= (uint64_t)1 << 52; + } + if (sgn) + *q++ = '-'; + /* remove the bias */ + e -= 1022; + /* d = 2^(e-53)*m */ + // printf("m=0x%016" PRIx64 " e=%d\n", m, e); +#ifdef USE_FAST_INT + if (fmt == JS_DTOA_FORMAT_FREE && + e >= 1 && e <= 53 && + (m & (((uint64_t)1 << (53 - e)) - 1)) == 0 && + (flags & JS_DTOA_EXP_MASK) != JS_DTOA_EXP_ENABLED) { + m >>= 53 - e; + /* 'm' is never zero */ + q += u64toa_radix(q, m, radix); + goto done; + } +#endif + + /* this choice of E implies F=round(x*B^(P-E) is such as: + B^(P-1) <= F < 2.B^P. */ + E = 1 + mul_log2_radix(e - 1, radix); + + if (fmt == JS_DTOA_FORMAT_FREE) { + int P_max, E0, e1, E_found, P_found; + uint64_t m1, mant_found, mant, mant_max1; + /* P_max is guarranteed to work by construction */ + P_max = dtoa_max_digits_table[radix - 2]; + E0 = E; + E_found = 0; + P_found = 0; + mant_found = 0; + /* find the minimum number of digits by successive tries */ + P = P_max; /* P_max is guarateed to work */ + for(;;) { + /* mant_max always fits on 64 bits */ + mant_max1 = pow_ui(radix, P); + /* compute the mantissa in base B */ + E = E0; + for(;;) { + /* XXX: add inexact flag */ + mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDN); + mant = mpb_get_u64(tmp1); + if (mant < mant_max1) + break; + E++; /* at most one iteration is possible */ + } + /* remove useless trailing zero digits */ + while ((mant % radix) == 0) { + mant /= radix; + P--; + } + /* garanteed to work for P = P_max */ + if (P_found == 0) + goto prec_found; + /* convert back to base 2 */ + mpb_set_u64(tmp1, mant); + m1 = mul_pow_round_to_d(&e1, tmp1, radix1, radix_shift, E - P, JS_RNDN); + // printf("P=%2d: m=0x%016" PRIx64 " e=%d m1=0x%016" PRIx64 " e1=%d\n", P, m, e, m1, e1); + /* Note: (m, e) is never zero here, so the exponent for m1 + = 0 does not matter */ + if (m1 == m && e1 == e) { + prec_found: + P_found = P; + E_found = E; + mant_found = mant; + if (P == 1) + break; + P--; /* try lower exponent */ + } else { + break; + } + } + P = P_found; + E = E_found; + mpb_set_u64(tmp1, mant_found); +#ifdef JS_DTOA_DUMP_STATS + if (radix == 10) { + out_len_count[P - 1]++; + } +#endif + } else if (fmt == JS_DTOA_FORMAT_FRAC) { + int len; + + assert(n_digits >= 0 && n_digits <= JS_DTOA_MAX_DIGITS); + /* P = max_int(E, 1) + n_digits; */ + /* frac is rounded using RNDNA */ + mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, n_digits, JS_RNDNA); + + /* we add one extra digit on the left and remove it if needed + to avoid testing if the result is < radix^P */ + len = output_digits(q, tmp1, radix, max_int(E + 1, 1) + n_digits, + max_int(E + 1, 1)); + if (q[0] == '0' && len >= 2 && q[1] != '.') { + len--; + memmove(q, q + 1, len); + } + q += len; + goto done; + } else { + int pow_shift; + assert(n_digits >= 1 && n_digits <= JS_DTOA_MAX_DIGITS); + P = n_digits; + /* mant_max = radix^P */ + mant_max->len = 1; + mant_max->tab[0] = 1; + pow_shift = mul_pow(mant_max, radix1, radix_shift, P, false, 0); + mpb_shr_round(mant_max, pow_shift, JS_RNDZ); + + for(;;) { + /* fixed and frac are rounded using RNDNA */ + mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDNA); + if (mpb_cmp(tmp1, mant_max) < 0) + break; + E++; /* at most one iteration is possible */ + } + } + output: + if (fmt == JS_DTOA_FORMAT_FIXED) + E_max = n_digits; + else + E_max = dtoa_max_digits_table[radix - 2] + 4; + if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_ENABLED || + ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_AUTO && (E <= -6 || E > E_max))) { + q += output_digits(q, tmp1, radix, P, 1); + E--; + if (radix == 10) { + *q++ = 'e'; + } else if (radix1 == 1 && radix_shift <= 4) { + E *= radix_shift; + *q++ = 'p'; + } else { + *q++ = '@'; + } + if (E < 0) { + *q++ = '-'; + E = -E; + } else { + *q++ = '+'; + } + q += u32toa(q, E); + } else if (E <= 0) { + *q++ = '0'; + *q++ = '.'; + for(i = 0; i < -E; i++) + *q++ = '0'; + q += output_digits(q, tmp1, radix, P, P); + } else { + q += output_digits(q, tmp1, radix, P, min_int(P, E)); + for(i = 0; i < E - P; i++) + *q++ = '0'; + } + done: + *q = '\0'; + dtoa_free(mant_max); + dtoa_free(tmp1); + return q - buf; +} + +static inline int to_digit(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'Z') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + return c - 'a' + 10; + else + return 36; +} + +/* r = r * radix_base + a. radix_base = 0 means radix_base = 2^32 */ +static void mpb_mul1_base(mpb_t *r, limb_t radix_base, limb_t a) +{ + int i; + if (r->tab[0] == 0 && r->len == 1) { + r->tab[0] = a; + } else { + if (radix_base == 0) { + for(i = r->len; i >= 0; i--) { + r->tab[i + 1] = r->tab[i]; + } + r->tab[0] = a; + } else { + r->tab[r->len] = mp_mul1(r->tab, r->tab, r->len, + radix_base, a); + } + r->len++; + mpb_renorm(r); + } +} + +/* XXX: add fast path for small integers */ +double js_atod(const char *str, const char **pnext, int radix, int flags, + JSATODTempMem *tmp_mem) +{ + uint64_t *mptr = tmp_mem->mem; + const char *p, *p_start; + limb_t cur_limb, radix_base, extra_digits; + int is_neg, digit_count, limb_digit_count, digits_per_limb, sep, radix1, radix_shift; + int radix_bits, expn, e, max_digits, expn_offset, dot_pos, sig_pos, pos; + mpb_t *tmp0; + double dval; + bool is_bin_exp, is_zero, expn_overflow; + uint64_t m, a; + + tmp0 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX); + assert((mptr - tmp_mem->mem) <= sizeof(JSATODTempMem) / sizeof(mptr[0])); + /* optional separator between digits */ + sep = (flags & JS_ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; + + p = str; + is_neg = 0; + if (p[0] == '+') { + p++; + p_start = p; + } else if (p[0] == '-') { + is_neg = 1; + p++; + p_start = p; + } else { + p_start = p; + } + + if (p[0] == '0') { + if ((p[1] == 'x' || p[1] == 'X') && + (radix == 0 || radix == 16)) { + p += 2; + radix = 16; + } else if ((p[1] == 'o' || p[1] == 'O') && + radix == 0 && (flags & JS_ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 8; + } else if ((p[1] == 'b' || p[1] == 'B') && + radix == 0 && (flags & JS_ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 2; + } else if ((p[1] >= '0' && p[1] <= '9') && + radix == 0 && (flags & JS_ATOD_ACCEPT_LEGACY_OCTAL)) { + int i; + sep = 256; + for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++) + continue; + if (p[i] == '8' || p[i] == '9') + goto no_prefix; + p += 1; + radix = 8; + } else { + goto no_prefix; + } + /* there must be a digit after the prefix */ + if (to_digit((uint8_t)*p) >= radix) + goto fail; + no_prefix: ; + } else { + if (!(flags & JS_ATOD_INT_ONLY) && js__strstart(p, "Infinity", &p)) + goto overflow; + } + if (radix == 0) + radix = 10; + + cur_limb = 0; + expn_offset = 0; + digit_count = 0; + limb_digit_count = 0; + max_digits = atod_max_digits_table[radix - 2]; + digits_per_limb = digits_per_limb_table[radix - 2]; + radix_base = radix_base_table[radix - 2]; + radix_shift = ctz32(radix); + radix1 = radix >> radix_shift; + if (radix1 == 1) { + /* radix = 2^radix_bits */ + radix_bits = radix_shift; + } else { + radix_bits = 0; + } + tmp0->len = 1; + tmp0->tab[0] = 0; + extra_digits = 0; + pos = 0; + dot_pos = -1; + /* skip leading zeros */ + for(;;) { + if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) && + !(flags & JS_ATOD_INT_ONLY)) { + if (*p == sep) + goto fail; + if (dot_pos >= 0) + break; + dot_pos = pos; + p++; + } + if (*p == sep && p > p_start && p[1] == '0') + p++; + if (*p != '0') + break; + p++; + pos++; + } + + sig_pos = pos; + for(;;) { + limb_t c; + if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) && + !(flags & JS_ATOD_INT_ONLY)) { + if (*p == sep) + goto fail; + if (dot_pos >= 0) + break; + dot_pos = pos; + p++; + } + if (*p == sep && p > p_start && to_digit(p[1]) < radix) + p++; + c = to_digit(*p); + if (c >= radix) + break; + p++; + pos++; + if (digit_count < max_digits) { + /* XXX: could be faster when radix_bits != 0 */ + cur_limb = cur_limb * radix + c; + limb_digit_count++; + if (limb_digit_count == digits_per_limb) { + mpb_mul1_base(tmp0, radix_base, cur_limb); + cur_limb = 0; + limb_digit_count = 0; + } + digit_count++; + } else { + extra_digits |= c; + } + } + if (limb_digit_count != 0) { + mpb_mul1_base(tmp0, pow_ui(radix, limb_digit_count), cur_limb); + } + if (digit_count == 0) { + is_zero = true; + expn_offset = 0; + } else { + is_zero = false; + if (dot_pos < 0) + dot_pos = pos; + expn_offset = sig_pos + digit_count - dot_pos; + } + + /* Use the extra digits for rounding if the base is a power of + two. Otherwise they are just truncated. */ + if (radix_bits != 0 && extra_digits != 0) { + tmp0->tab[0] |= 1; + } + + /* parse the exponent, if any */ + expn = 0; + expn_overflow = false; + is_bin_exp = false; + if (!(flags & JS_ATOD_INT_ONLY) && + ((radix == 10 && (*p == 'e' || *p == 'E')) || + (radix != 10 && (*p == '@' || + (radix_bits >= 1 && radix_bits <= 4 && (*p == 'p' || *p == 'P'))))) && + p > p_start) { + bool exp_is_neg; + int c; + is_bin_exp = (*p == 'p' || *p == 'P'); + p++; + exp_is_neg = false; + if (*p == '+') { + p++; + } else if (*p == '-') { + exp_is_neg = true; + p++; + } + c = to_digit(*p); + if (c >= 10) + goto fail; /* XXX: could stop before the exponent part */ + expn = c; + p++; + for(;;) { + if (*p == sep && to_digit(p[1]) < 10) + p++; + c = to_digit(*p); + if (c >= 10) + break; + if (!expn_overflow) { + if (unlikely(expn > ((INT32_MAX - 2 - 9) / 10))) { + expn_overflow = true; + } else { + expn = expn * 10 + c; + } + } + p++; + } + if (exp_is_neg) + expn = -expn; + /* if zero result, the exponent can be arbitrarily large */ + if (!is_zero && expn_overflow) { + if (exp_is_neg) + a = 0; + else + a = (uint64_t)0x7ff << 52; /* infinity */ + goto done; + } + } + + if (p == p_start) + goto fail; + + if (is_zero) { + a = 0; + } else { + int expn1; + if (radix_bits != 0) { + if (!is_bin_exp) + expn *= radix_bits; + expn -= expn_offset * radix_bits; + expn1 = expn + digit_count * radix_bits; + if (expn1 >= 1024 + radix_bits) + goto overflow; + else if (expn1 <= -1075) + goto underflow; + m = round_to_d(&e, tmp0, -expn, JS_RNDN); + } else { + expn -= expn_offset; + expn1 = expn + digit_count; + if (expn1 >= max_exponent[radix - 2] + 1) + goto overflow; + else if (expn1 <= min_exponent[radix - 2]) + goto underflow; + m = mul_pow_round_to_d(&e, tmp0, radix1, radix_shift, expn, JS_RNDN); + } + if (m == 0) { + underflow: + a = 0; + } else if (e > 1024) { + overflow: + /* overflow */ + a = (uint64_t)0x7ff << 52; + } else if (e < -1073) { + /* underflow */ + /* XXX: check rounding */ + a = 0; + } else if (e < -1021) { + /* subnormal */ + a = m >> (-e - 1021); + } else { + a = ((uint64_t)(e + 1022) << 52) | (m & (((uint64_t)1 << 52) - 1)); + } + } + done: + a |= (uint64_t)is_neg << 63; + dval = uint64_as_float64(a); + done1: + if (pnext) + *pnext = p; + dtoa_free(tmp0); + return dval; + fail: + dval = NAN; + goto done1; +} diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/dtoa.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/dtoa.h new file mode 100755 index 00000000..85be0406 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/dtoa.h @@ -0,0 +1,87 @@ +/* + * Tiny float64 printing and parsing library + * + * Copyright (c) 2024 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef DTOA_H +#define DTOA_H + +//#define JS_DTOA_DUMP_STATS + +/* maximum number of digits for fixed and frac formats */ +#define JS_DTOA_MAX_DIGITS 101 + +/* radix != 10 is only supported with flags = JS_DTOA_FORMAT_FREE */ +/* use as many digits as necessary */ +#define JS_DTOA_FORMAT_FREE (0 << 0) +/* use n_digits significant digits (1 <= n_digits <= JS_DTOA_MAX_DIGITS) */ +#define JS_DTOA_FORMAT_FIXED (1 << 0) +/* force fractional format: [-]dd.dd with n_digits fractional digits. + 0 <= n_digits <= JS_DTOA_MAX_DIGITS */ +#define JS_DTOA_FORMAT_FRAC (2 << 0) +#define JS_DTOA_FORMAT_MASK (3 << 0) + +/* select exponential notation either in fixed or free format */ +#define JS_DTOA_EXP_AUTO (0 << 2) +#define JS_DTOA_EXP_ENABLED (1 << 2) +#define JS_DTOA_EXP_DISABLED (2 << 2) +#define JS_DTOA_EXP_MASK (3 << 2) + +#define JS_DTOA_MINUS_ZERO (1 << 4) /* show the minus sign for -0 */ + +/* only accepts integers (no dot, no exponent) */ +#define JS_ATOD_INT_ONLY (1 << 0) +/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ +#define JS_ATOD_ACCEPT_BIN_OCT (1 << 1) +/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ +#define JS_ATOD_ACCEPT_LEGACY_OCTAL (1 << 2) +/* accept _ between digits as a digit separator */ +#define JS_ATOD_ACCEPT_UNDERSCORES (1 << 3) + +typedef struct { + uint64_t mem[37]; +} JSDTOATempMem; + +typedef struct { + uint64_t mem[27]; +} JSATODTempMem; + +/* return a maximum bound of the string length */ +int js_dtoa_max_len(double d, int radix, int n_digits, int flags); +/* return the string length */ +int js_dtoa(char *buf, double d, int radix, int n_digits, int flags, + JSDTOATempMem *tmp_mem); +double js_atod(const char *str, const char **pnext, int radix, int flags, + JSATODTempMem *tmp_mem); + +#ifdef JS_DTOA_DUMP_STATS +void js_dtoa_dump_stats(void); +#endif + +/* additional exported functions */ +size_t u32toa(char *buf, uint32_t n); +size_t i32toa(char *buf, int32_t n); +size_t u64toa(char *buf, uint64_t n); +size_t i64toa(char *buf, int64_t n); +size_t u64toa_radix(char *buf, uint64_t n, unsigned int radix); +size_t i64toa_radix(char *buf, int64_t n, unsigned int radix); + +#endif /* DTOA_H */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz.c old mode 100644 new mode 100755 index cb5381de..fca8fa41 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/fuzz.c @@ -2,7 +2,6 @@ #include "quickjs.h" #include "quickjs.c" #include "cutils.c" -#include "libbf.c" #include "libregexp.c" #include "libunicode.c" #include @@ -15,7 +14,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) JSContext *ctx = JS_NewContext(rt); if (!ctx) exit(1); - JSValueConst val = JS_ReadObject(ctx, buf, len, /*flags*/0); + JSValue val = JS_ReadObject(ctx, buf, len, /*flags*/0); JS_FreeValue(ctx, val); JS_FreeContext(ctx); JS_FreeRuntime(rt); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/getopt_compat.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/getopt_compat.h deleted file mode 100644 index f98ba273..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/getopt_compat.h +++ /dev/null @@ -1,653 +0,0 @@ -#ifndef __GETOPT_H__ -/** - * DISCLAIMER - * This file is part of the mingw-w64 runtime package. - * - * The mingw-w64 runtime package and its code is distributed in the hope that it - * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR - * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to - * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - */ - /* - * Copyright (c) 2002 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Sponsored in part by the Defense Advanced Research Projects - * Agency (DARPA) and Air Force Research Laboratory, Air Force - * Materiel Command, USAF, under agreement number F39502-99-1-0512. - */ -/*- - * Copyright (c) 2000 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Dieter Baron and Thomas Klausner. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma warning(disable:4996) - -#define __GETOPT_H__ - -/* All the headers include this file. */ -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ - -#ifdef REPLACE_GETOPT -int opterr = 1; /* if error message should be printed */ -int optind = 1; /* index into parent argv vector */ -int optopt = '?'; /* character checked for validity */ -#undef optreset /* see getopt.h */ -#define optreset __mingw_optreset -int optreset; /* reset getopt */ -char *optarg; /* argument associated with option */ -#endif - -//extern int optind; /* index of first non-option in argv */ -//extern int optopt; /* single option character, as parsed */ -//extern int opterr; /* flag to enable built-in diagnostics... */ -// /* (user may set to zero, to suppress) */ -// -//extern char *optarg; /* pointer to argument of current option */ - -#define PRINT_ERROR ((opterr) && (*options != ':')) - -#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ -#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ -#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ - -/* return values */ -#define BADCH (int)'?' -#define BADARG ((*options == ':') ? (int)':' : (int)'?') -#define INORDER (int)1 - -#ifndef __CYGWIN__ -#define __progname __argv[0] -#else -extern char __declspec(dllimport) *__progname; -#endif - -#ifdef __CYGWIN__ -static char EMSG[] = ""; -#else -#define EMSG "" -#endif - -struct option /* specification for a long form option... */ -{ - const char *name; /* option name, without leading hyphens */ - int has_arg; /* does it take an argument? */ - int *flag; /* where to save its status, or NULL */ - int val; /* its associated status value */ -}; - -static int getopt_internal(int, char * const *, const char *, - const struct option *, int *, int); -static int parse_long_options(char * const *, const char *, - const struct option *, int *, int); -static int gcd(int, int); -static void permute_args(int, int, int, char * const *); - -static char *place = EMSG; /* option letter processing */ - -/* XXX: set optreset to 1 rather than these two */ -static int nonopt_start = -1; /* first non option argument (for permute) */ -static int nonopt_end = -1; /* first option after non options (for permute) */ - -/* Error messages */ -static const char recargchar[] = "option requires an argument -- %c"; -static const char recargstring[] = "option requires an argument -- %s"; -static const char ambig[] = "ambiguous option -- %.*s"; -static const char noarg[] = "option doesn't take an argument -- %.*s"; -static const char illoptchar[] = "unknown option -- %c"; -static const char illoptstring[] = "unknown option -- %s"; - -static void -_vwarnx(const char *fmt,va_list ap) -{ - (void)fprintf(stderr,"%s: ",__progname); - if (fmt != NULL) - (void)vfprintf(stderr,fmt,ap); - (void)fprintf(stderr,"\n"); -} - -static void -warnx(const char *fmt,...) -{ - va_list ap; - va_start(ap,fmt); - _vwarnx(fmt,ap); - va_end(ap); -} - -/* - * Compute the greatest common divisor of a and b. - */ -static int -gcd(int a, int b) -{ - int c; - - c = a % b; - while (c != 0) { - a = b; - b = c; - c = a % b; - } - - return (b); -} - -/* - * Exchange the block from nonopt_start to nonopt_end with the block - * from nonopt_end to opt_end (keeping the same order of arguments - * in each block). - */ -static void -permute_args(int panonopt_start, int panonopt_end, int opt_end, - char * const *nargv) -{ - int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; - char *swap; - - /* - * compute lengths of blocks and number and size of cycles - */ - nnonopts = panonopt_end - panonopt_start; - nopts = opt_end - panonopt_end; - ncycle = gcd(nnonopts, nopts); - cyclelen = (opt_end - panonopt_start) / ncycle; - - for (i = 0; i < ncycle; i++) { - cstart = panonopt_end+i; - pos = cstart; - for (j = 0; j < cyclelen; j++) { - if (pos >= panonopt_end) - pos -= nnonopts; - else - pos += nopts; - swap = nargv[pos]; - /* LINTED const cast */ - ((char **) nargv)[pos] = nargv[cstart]; - /* LINTED const cast */ - ((char **)nargv)[cstart] = swap; - } - } -} - -#ifdef REPLACE_GETOPT -/* - * getopt -- - * Parse argc/argv argument vector. - * - * [eventually this will replace the BSD getopt] - */ -int -getopt(int nargc, char * const *nargv, const char *options) -{ - - /* - * We don't pass FLAG_PERMUTE to getopt_internal() since - * the BSD getopt(3) (unlike GNU) has never done this. - * - * Furthermore, since many privileged programs call getopt() - * before dropping privileges it makes sense to keep things - * as simple (and bug-free) as possible. - */ - return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); -} -#endif /* REPLACE_GETOPT */ - -//extern int getopt(int nargc, char * const *nargv, const char *options); - -#ifdef _BSD_SOURCE -/* - * BSD adds the non-standard `optreset' feature, for reinitialisation - * of `getopt' parsing. We support this feature, for applications which - * proclaim their BSD heritage, before including this header; however, - * to maintain portability, developers are advised to avoid it. - */ -# define optreset __mingw_optreset -extern int optreset; -#endif -#ifdef __cplusplus -} -#endif -/* - * POSIX requires the `getopt' API to be specified in `unistd.h'; - * thus, `unistd.h' includes this header. However, we do not want - * to expose the `getopt_long' or `getopt_long_only' APIs, when - * included in this manner. Thus, close the standard __GETOPT_H__ - * declarations block, and open an additional __GETOPT_LONG_H__ - * specific block, only when *not* __UNISTD_H_SOURCED__, in which - * to declare the extended API. - */ -#endif /* !defined(__GETOPT_H__) */ - -#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) -#define __GETOPT_LONG_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -enum /* permitted values for its `has_arg' field... */ -{ - no_argument = 0, /* option never takes an argument */ - required_argument, /* option always requires an argument */ - optional_argument /* option may take an argument */ -}; - -/* - * parse_long_options -- - * Parse long options in argc/argv argument vector. - * Returns -1 if short_too is set and the option does not match long_options. - */ -static int -parse_long_options(char * const *nargv, const char *options, - const struct option *long_options, int *idx, int short_too) -{ - char *current_argv, *has_equal; - size_t current_argv_len; - int i, ambiguous, match; - -#define IDENTICAL_INTERPRETATION(_x, _y) \ - (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ - long_options[(_x)].flag == long_options[(_y)].flag && \ - long_options[(_x)].val == long_options[(_y)].val) - - current_argv = place; - match = -1; - ambiguous = 0; - - optind++; - - if ((has_equal = strchr(current_argv, '=')) != NULL) { - /* argument found (--option=arg) */ - current_argv_len = has_equal - current_argv; - has_equal++; - } else - current_argv_len = strlen(current_argv); - - for (i = 0; long_options[i].name; i++) { - /* find matching long option */ - if (strncmp(current_argv, long_options[i].name, - current_argv_len)) - continue; - - if (strlen(long_options[i].name) == current_argv_len) { - /* exact match */ - match = i; - ambiguous = 0; - break; - } - /* - * If this is a known short option, don't allow - * a partial match of a single character. - */ - if (short_too && current_argv_len == 1) - continue; - - if (match == -1) /* partial match */ - match = i; - else if (!IDENTICAL_INTERPRETATION(i, match)) - ambiguous = 1; - } - if (ambiguous) { - /* ambiguous abbreviation */ - if (PRINT_ERROR) - warnx(ambig, (int)current_argv_len, - current_argv); - optopt = 0; - return (BADCH); - } - if (match != -1) { /* option found */ - if (long_options[match].has_arg == no_argument - && has_equal) { - if (PRINT_ERROR) - warnx(noarg, (int)current_argv_len, - current_argv); - /* - * XXX: GNU sets optopt to val regardless of flag - */ - if (long_options[match].flag == NULL) - optopt = long_options[match].val; - else - optopt = 0; - return (BADARG); - } - if (long_options[match].has_arg == required_argument || - long_options[match].has_arg == optional_argument) { - if (has_equal) - optarg = has_equal; - else if (long_options[match].has_arg == - required_argument) { - /* - * optional argument doesn't use next nargv - */ - optarg = nargv[optind++]; - } - } - if ((long_options[match].has_arg == required_argument) - && (optarg == NULL)) { - /* - * Missing argument; leading ':' indicates no error - * should be generated. - */ - if (PRINT_ERROR) - warnx(recargstring, - current_argv); - /* - * XXX: GNU sets optopt to val regardless of flag - */ - if (long_options[match].flag == NULL) - optopt = long_options[match].val; - else - optopt = 0; - --optind; - return (BADARG); - } - } else { /* unknown option */ - if (short_too) { - --optind; - return (-1); - } - if (PRINT_ERROR) - warnx(illoptstring, current_argv); - optopt = 0; - return (BADCH); - } - if (idx) - *idx = match; - if (long_options[match].flag) { - *long_options[match].flag = long_options[match].val; - return (0); - } else - return (long_options[match].val); -#undef IDENTICAL_INTERPRETATION -} - -/* - * getopt_internal -- - * Parse argc/argv argument vector. Called by user level routines. - */ -static int -getopt_internal(int nargc, char * const *nargv, const char *options, - const struct option *long_options, int *idx, int flags) -{ - char *oli; /* option letter list index */ - int optchar, short_too; - static int posixly_correct = -1; - - if (options == NULL) - return (-1); - - /* - * XXX Some GNU programs (like cvs) set optind to 0 instead of - * XXX using optreset. Work around this braindamage. - */ - if (optind == 0) - optind = optreset = 1; - - /* - * Disable GNU extensions if POSIXLY_CORRECT is set or options - * string begins with a '+'. - * - * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or - * optreset != 0 for GNU compatibility. - */ - if (posixly_correct == -1 || optreset != 0) - posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); - if (*options == '-') - flags |= FLAG_ALLARGS; - else if (posixly_correct || *options == '+') - flags &= ~FLAG_PERMUTE; - if (*options == '+' || *options == '-') - options++; - - optarg = NULL; - if (optreset) - nonopt_start = nonopt_end = -1; -start: - if (optreset || !*place) { /* update scanning pointer */ - optreset = 0; - if (optind >= nargc) { /* end of argument vector */ - place = EMSG; - if (nonopt_end != -1) { - /* do permutation, if we have to */ - permute_args(nonopt_start, nonopt_end, - optind, nargv); - optind -= nonopt_end - nonopt_start; - } - else if (nonopt_start != -1) { - /* - * If we skipped non-options, set optind - * to the first of them. - */ - optind = nonopt_start; - } - nonopt_start = nonopt_end = -1; - return (-1); - } - if (*(place = nargv[optind]) != '-' || - (place[1] == '\0' && strchr(options, '-') == NULL)) { - place = EMSG; /* found non-option */ - if (flags & FLAG_ALLARGS) { - /* - * GNU extension: - * return non-option as argument to option 1 - */ - optarg = nargv[optind++]; - return (INORDER); - } - if (!(flags & FLAG_PERMUTE)) { - /* - * If no permutation wanted, stop parsing - * at first non-option. - */ - return (-1); - } - /* do permutation */ - if (nonopt_start == -1) - nonopt_start = optind; - else if (nonopt_end != -1) { - permute_args(nonopt_start, nonopt_end, - optind, nargv); - nonopt_start = optind - - (nonopt_end - nonopt_start); - nonopt_end = -1; - } - optind++; - /* process next argument */ - goto start; - } - if (nonopt_start != -1 && nonopt_end == -1) - nonopt_end = optind; - - /* - * If we have "-" do nothing, if "--" we are done. - */ - if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { - optind++; - place = EMSG; - /* - * We found an option (--), so if we skipped - * non-options, we have to permute. - */ - if (nonopt_end != -1) { - permute_args(nonopt_start, nonopt_end, - optind, nargv); - optind -= nonopt_end - nonopt_start; - } - nonopt_start = nonopt_end = -1; - return (-1); - } - } - - /* - * Check long options if: - * 1) we were passed some - * 2) the arg is not just "-" - * 3) either the arg starts with -- we are getopt_long_only() - */ - if (long_options != NULL && place != nargv[optind] && - (*place == '-' || (flags & FLAG_LONGONLY))) { - short_too = 0; - if (*place == '-') - place++; /* --foo long option */ - else if (*place != ':' && strchr(options, *place) != NULL) - short_too = 1; /* could be short option too */ - - optchar = parse_long_options(nargv, options, long_options, - idx, short_too); - if (optchar != -1) { - place = EMSG; - return (optchar); - } - } - - if ((optchar = (int)*place++) == (int)':' || - (optchar == (int)'-' && *place != '\0') || - (oli = (char*)strchr(options, optchar)) == NULL) { - /* - * If the user specified "-" and '-' isn't listed in - * options, return -1 (non-option) as per POSIX. - * Otherwise, it is an unknown option character (or ':'). - */ - if (optchar == (int)'-' && *place == '\0') - return (-1); - if (!*place) - ++optind; - if (PRINT_ERROR) - warnx(illoptchar, optchar); - optopt = optchar; - return (BADCH); - } - if (long_options != NULL && optchar == 'W' && oli[1] == ';') { - /* -W long-option */ - if (*place) /* no space */ - /* NOTHING */; - else if (++optind >= nargc) { /* no arg */ - place = EMSG; - if (PRINT_ERROR) - warnx(recargchar, optchar); - optopt = optchar; - return (BADARG); - } else /* white space */ - place = nargv[optind]; - optchar = parse_long_options(nargv, options, long_options, - idx, 0); - place = EMSG; - return (optchar); - } - if (*++oli != ':') { /* doesn't take argument */ - if (!*place) - ++optind; - } else { /* takes (optional) argument */ - optarg = NULL; - if (*place) /* no white space */ - optarg = place; - else if (oli[1] != ':') { /* arg not optional */ - if (++optind >= nargc) { /* no arg */ - place = EMSG; - if (PRINT_ERROR) - warnx(recargchar, optchar); - optopt = optchar; - return (BADARG); - } else - optarg = nargv[optind]; - } - place = EMSG; - ++optind; - } - /* dump back option letter */ - return (optchar); -} - -/* - * getopt_long -- - * Parse argc/argv argument vector. - */ -int -getopt_long(int nargc, char * const *nargv, const char *options, - const struct option *long_options, int *idx) -{ - - return (getopt_internal(nargc, nargv, options, long_options, idx, - FLAG_PERMUTE)); -} - -/* - * getopt_long_only -- - * Parse argc/argv argument vector. - */ -int -getopt_long_only(int nargc, char * const *nargv, const char *options, - const struct option *long_options, int *idx) -{ - - return (getopt_internal(nargc, nargv, options, long_options, idx, - FLAG_PERMUTE|FLAG_LONGONLY)); -} - -//extern int getopt_long(int nargc, char * const *nargv, const char *options, -// const struct option *long_options, int *idx); -//extern int getopt_long_only(int nargc, char * const *nargv, const char *options, -// const struct option *long_options, int *idx); -/* - * Previous MinGW implementation had... - */ -#ifndef HAVE_DECL_GETOPT -/* - * ...for the long form API only; keep this for compatibility. - */ -# define HAVE_DECL_GETOPT 1 -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/interrupt-test.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/interrupt-test.c deleted file mode 100644 index c216a942..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/interrupt-test.c +++ /dev/null @@ -1,134 +0,0 @@ -#include -#include "quickjs.h" - -#define MAX_TIME 10 - -#define expect(condition) \ - do { \ - if (!(condition)) { \ - fprintf(stderr, "Failed: %s, file %s, line %d\n", \ - #condition, __FILE__, __LINE__); \ - exit(EXIT_FAILURE); \ - } \ - } while (0) - -static int timeout_interrupt_handler(JSRuntime *rt, void *opaque) -{ - int *time = (int *)opaque; - if (*time <= MAX_TIME) - *time += 1; - return *time > MAX_TIME; -} - -static void sync_call(void) -{ - const char *code = -"(function() { \ - try { \ - while (true) {} \ - } catch (e) {} \ -})();"; - - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - int time = 0; - JS_SetInterruptHandler(rt, timeout_interrupt_handler, &time); - JSValue ret = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_GLOBAL); - expect(time > MAX_TIME); - expect(JS_IsException(ret)); - JS_FreeValue(ctx, ret); - expect(JS_HasException(ctx)); - JSValue e = JS_GetException(ctx); - expect(JS_IsUncatchableError(ctx, e)); - JS_FreeValue(ctx, e); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -static void async_call(void) -{ - const char *code = -"(async function() { \ - const loop = async () => { \ - await Promise.resolve(); \ - while (true) {} \ - }; \ - await loop().catch(() => {}); \ -})();"; - - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - int time = 0; - JS_SetInterruptHandler(rt, timeout_interrupt_handler, &time); - JSValue ret = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_GLOBAL); - expect(!JS_IsException(ret)); - JS_FreeValue(ctx, ret); - expect(JS_IsJobPending(rt)); - int r = 0; - while (JS_IsJobPending(rt)) { - r = JS_ExecutePendingJob(rt, &ctx); - } - expect(time > MAX_TIME); - expect(r == -1); - expect(JS_HasException(ctx)); - JSValue e = JS_GetException(ctx); - expect(JS_IsUncatchableError(ctx, e)); - JS_FreeValue(ctx, e); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -static JSValue save_value(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) -{ - expect(argc == 1); - JSValue *p = (JSValue *)JS_GetContextOpaque(ctx); - *p = JS_DupValue(ctx, argv[0]); - return JS_UNDEFINED; -} - -static void async_call_stack_overflow(void) -{ - const char *code = -"(async function() { \ - const f = () => f(); \ - try { \ - await Promise.resolve(); \ - f(); \ - } catch (e) { \ - save_value(e); \ - } \ -})();"; - - JSRuntime *rt = JS_NewRuntime(); - JSContext *ctx = JS_NewContext(rt); - JS_SetMaxStackSize(rt, 128 * 1024); - JS_UpdateStackTop(rt); - JSValue value = JS_UNDEFINED; - JS_SetContextOpaque(ctx, &value); - JSValue global = JS_GetGlobalObject(ctx); - JS_SetPropertyStr(ctx, global, "save_value", JS_NewCFunction(ctx, save_value, "save_value", 1)); - JS_FreeValue(ctx, global); - JSValue ret = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_GLOBAL); - expect(!JS_IsException(ret)); - JS_FreeValue(ctx, ret); - expect(JS_IsJobPending(rt)); - int r = 0; - while (JS_IsJobPending(rt)) { - r = JS_ExecutePendingJob(rt, &ctx); - } - expect(r == 1); - expect(!JS_HasException(ctx)); - expect(JS_IsError(ctx, value)); /* StackOverflow should be caught */ - JS_FreeValue(ctx, value); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); -} - -int main() -{ - sync_call(); - async_call(); - async_call_stack_overflow(); - printf("interrupt-test passed\n"); - return 0; -} \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libbf.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/libbf.c deleted file mode 100644 index 07d27cfa..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/libbf.c +++ /dev/null @@ -1,8424 +0,0 @@ -/* - * Tiny arbitrary precision floating point library - * - * Copyright (c) 2017-2021 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include -#include -#include -#include -#include -#include - -#ifdef __AVX2__ -#include -#endif - -#include "cutils.h" -#include "libbf.h" - -/* enable it to check the multiplication result */ -//#define USE_MUL_CHECK -/* enable it to use FFT/NTT multiplication */ -#define USE_FFT_MUL -/* enable decimal floating point support */ -#define USE_BF_DEC - -//#define inline __attribute__((always_inline)) - -#ifdef __AVX2__ -#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */ -#else -#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */ -#endif - -/* XXX: adjust */ -#define DIVNORM_LARGE_THRESHOLD 50 -#define UDIV1NORM_THRESHOLD 3 - -#if LIMB_BITS == 64 -#define FMT_LIMB1 "%" PRIx64 -#define FMT_LIMB "%016" PRIx64 -#define PRId_LIMB PRId64 -#define PRIu_LIMB PRIu64 - -#else - -#define FMT_LIMB1 "%x" -#define FMT_LIMB "%08x" -#define PRId_LIMB "d" -#define PRIu_LIMB "u" - -#endif - -typedef intptr_t mp_size_t; - -typedef int bf_op2_func_t(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags); - -#ifdef USE_FFT_MUL - -#define FFT_MUL_R_OVERLAP_A (1 << 0) -#define FFT_MUL_R_OVERLAP_B (1 << 1) -#define FFT_MUL_R_NORESIZE (1 << 2) - -static no_inline int fft_mul(bf_context_t *s, - bf_t *res, limb_t *a_tab, limb_t a_len, - limb_t *b_tab, limb_t b_len, int mul_flags); -static void fft_clear_cache(bf_context_t *s); -#endif -#ifdef USE_BF_DEC -static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos); -#endif - - -/* could leading zeros */ -static inline int clz(limb_t a) -{ - if (a == 0) { - return LIMB_BITS; - } else { -#if LIMB_BITS == 64 - return clz64(a); -#else - return clz32(a); -#endif - } -} - -static inline int ctz(limb_t a) -{ - if (a == 0) { - return LIMB_BITS; - } else { -#if LIMB_BITS == 64 - return ctz64(a); -#else - return ctz32(a); -#endif - } -} - -static inline int ceil_log2(limb_t a) -{ - if (a <= 1) - return 0; - else - return LIMB_BITS - clz(a - 1); -} - -/* b must be >= 1 */ -static inline slimb_t ceil_div(slimb_t a, slimb_t b) -{ - if (a >= 0) - return (a + b - 1) / b; - else - return a / b; -} - -/* b must be >= 1 */ -static inline slimb_t floor_div(slimb_t a, slimb_t b) -{ - if (a >= 0) { - return a / b; - } else { - return (a - b + 1) / b; - } -} - -/* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */ -static inline limb_t smod(slimb_t a, slimb_t b) -{ - a = a % (slimb_t)b; - if (a < 0) - a += b; - return a; -} - -/* signed addition with saturation */ -static inline slimb_t sat_add(slimb_t a, slimb_t b) -{ - slimb_t r; - r = a + b; - /* overflow ? */ - if (((a ^ r) & (b ^ r)) < 0) - r = (a >> (LIMB_BITS - 1)) ^ (((limb_t)1 << (LIMB_BITS - 1)) - 1); - return r; -} - -static inline __maybe_unused limb_t shrd(limb_t low, limb_t high, long shift) -{ - if (shift != 0) - low = (low >> shift) | (high << (LIMB_BITS - shift)); - return low; -} - -static inline __maybe_unused limb_t shld(limb_t a1, limb_t a0, long shift) -{ - if (shift != 0) - return (a1 << shift) | (a0 >> (LIMB_BITS - shift)); - else - return a1; -} - -#define malloc(s) malloc_is_forbidden(s) -#define free(p) free_is_forbidden(p) -#define realloc(p, s) realloc_is_forbidden(p, s) - -void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, - void *realloc_opaque) -{ - memset(s, 0, sizeof(*s)); - s->realloc_func = realloc_func; - s->realloc_opaque = realloc_opaque; -} - -void bf_context_end(bf_context_t *s) -{ - bf_clear_cache(s); -} - -void bf_init(bf_context_t *s, bf_t *r) -{ - r->ctx = s; - r->sign = 0; - r->expn = BF_EXP_ZERO; - r->len = 0; - r->tab = NULL; -} - -/* return 0 if OK, -1 if alloc error */ -int bf_resize(bf_t *r, limb_t len) -{ - limb_t *tab; - - if (len != r->len) { - tab = bf_realloc(r->ctx, r->tab, len * sizeof(limb_t)); - if (!tab && len != 0) - return -1; - r->tab = tab; - r->len = len; - } - return 0; -} - -/* return 0 or BF_ST_MEM_ERROR */ -int bf_set_ui(bf_t *r, uint64_t a) -{ - r->sign = 0; - if (a == 0) { - r->expn = BF_EXP_ZERO; - bf_resize(r, 0); /* cannot fail */ - } -#if LIMB_BITS == 32 - else if (a <= 0xffffffff) -#else - else -#endif - { - int shift; - if (bf_resize(r, 1)) - goto fail; - shift = clz(a); - r->tab[0] = a << shift; - r->expn = LIMB_BITS - shift; - } -#if LIMB_BITS == 32 - else { - uint32_t a1, a0; - int shift; - if (bf_resize(r, 2)) - goto fail; - a0 = a; - a1 = a >> 32; - shift = clz(a1); - r->tab[0] = a0 << shift; - r->tab[1] = shld(a1, a0, shift); - r->expn = 2 * LIMB_BITS - shift; - } -#endif - return 0; - fail: - bf_set_nan(r); - return BF_ST_MEM_ERROR; -} - -/* return 0 or BF_ST_MEM_ERROR */ -int bf_set_si(bf_t *r, int64_t a) -{ - int ret; - - if (a < 0) { - ret = bf_set_ui(r, -a); - r->sign = 1; - } else { - ret = bf_set_ui(r, a); - } - return ret; -} - -void bf_set_nan(bf_t *r) -{ - bf_resize(r, 0); /* cannot fail */ - r->expn = BF_EXP_NAN; - r->sign = 0; -} - -void bf_set_zero(bf_t *r, int is_neg) -{ - bf_resize(r, 0); /* cannot fail */ - r->expn = BF_EXP_ZERO; - r->sign = is_neg; -} - -void bf_set_inf(bf_t *r, int is_neg) -{ - bf_resize(r, 0); /* cannot fail */ - r->expn = BF_EXP_INF; - r->sign = is_neg; -} - -/* return 0 or BF_ST_MEM_ERROR */ -int bf_set(bf_t *r, const bf_t *a) -{ - if (r == a) - return 0; - if (bf_resize(r, a->len)) { - bf_set_nan(r); - return BF_ST_MEM_ERROR; - } - r->sign = a->sign; - r->expn = a->expn; - if (a->len > 0) - memcpy(r->tab, a->tab, a->len * sizeof(limb_t)); - return 0; -} - -/* equivalent to bf_set(r, a); bf_delete(a) */ -void bf_move(bf_t *r, bf_t *a) -{ - bf_context_t *s = r->ctx; - if (r == a) - return; - bf_free(s, r->tab); - *r = *a; -} - -static limb_t get_limbz(const bf_t *a, limb_t idx) -{ - if (idx >= a->len) - return 0; - else - return a->tab[idx]; -} - -/* get LIMB_BITS at bit position 'pos' in tab */ -static inline limb_t get_bits(const limb_t *tab, limb_t len, slimb_t pos) -{ - limb_t i, a0, a1; - int p; - - i = pos >> LIMB_LOG2_BITS; - p = pos & (LIMB_BITS - 1); - if (i < len) - a0 = tab[i]; - else - a0 = 0; - if (p == 0) { - return a0; - } else { - i++; - if (i < len) - a1 = tab[i]; - else - a1 = 0; - return (a0 >> p) | (a1 << (LIMB_BITS - p)); - } -} - -static inline limb_t get_bit(const limb_t *tab, limb_t len, slimb_t pos) -{ - slimb_t i; - i = pos >> LIMB_LOG2_BITS; - if (i < 0 || i >= len) - return 0; - return (tab[i] >> (pos & (LIMB_BITS - 1))) & 1; -} - -static inline limb_t limb_mask(int start, int last) -{ - limb_t v; - int n; - n = last - start + 1; - if (n == LIMB_BITS) - v = -1; - else - v = (((limb_t)1 << n) - 1) << start; - return v; -} - -static limb_t mp_scan_nz(const limb_t *tab, mp_size_t n) -{ - mp_size_t i; - for(i = 0; i < n; i++) { - if (tab[i] != 0) - return 1; - } - return 0; -} - -/* return != 0 if one bit between 0 and bit_pos inclusive is not zero. */ -static inline limb_t scan_bit_nz(const bf_t *r, slimb_t bit_pos) -{ - slimb_t pos; - limb_t v; - - pos = bit_pos >> LIMB_LOG2_BITS; - if (pos < 0) - return 0; - v = r->tab[pos] & limb_mask(0, bit_pos & (LIMB_BITS - 1)); - if (v != 0) - return 1; - pos--; - while (pos >= 0) { - if (r->tab[pos] != 0) - return 1; - pos--; - } - return 0; -} - -/* return the addend for rounding. Note that prec can be <= 0 (for - BF_FLAG_RADPNT_PREC) */ -static int bf_get_rnd_add(int *pret, const bf_t *r, limb_t l, - slimb_t prec, int rnd_mode) -{ - int add_one, inexact; - limb_t bit1, bit0; - - if (rnd_mode == BF_RNDF) { - bit0 = 1; /* faithful rounding does not honor the INEXACT flag */ - } else { - /* starting limb for bit 'prec + 1' */ - bit0 = scan_bit_nz(r, l * LIMB_BITS - 1 - bf_max(0, prec + 1)); - } - - /* get the bit at 'prec' */ - bit1 = get_bit(r->tab, l, l * LIMB_BITS - 1 - prec); - inexact = (bit1 | bit0) != 0; - - add_one = 0; - switch(rnd_mode) { - case BF_RNDZ: - break; - case BF_RNDN: - if (bit1) { - if (bit0) { - add_one = 1; - } else { - /* round to even */ - add_one = - get_bit(r->tab, l, l * LIMB_BITS - 1 - (prec - 1)); - } - } - break; - case BF_RNDD: - case BF_RNDU: - if (r->sign == (rnd_mode == BF_RNDD)) - add_one = inexact; - break; - case BF_RNDA: - add_one = inexact; - break; - case BF_RNDNA: - case BF_RNDF: - add_one = bit1; - break; - default: - abort(); - } - - if (inexact) - *pret |= BF_ST_INEXACT; - return add_one; -} - -static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags) -{ - slimb_t i, l, e_max; - int rnd_mode; - - rnd_mode = flags & BF_RND_MASK; - if (prec == BF_PREC_INF || - rnd_mode == BF_RNDN || - rnd_mode == BF_RNDNA || - rnd_mode == BF_RNDA || - (rnd_mode == BF_RNDD && sign == 1) || - (rnd_mode == BF_RNDU && sign == 0)) { - bf_set_inf(r, sign); - } else { - /* set to maximum finite number */ - l = (prec + LIMB_BITS - 1) / LIMB_BITS; - if (bf_resize(r, l)) { - bf_set_nan(r); - return BF_ST_MEM_ERROR; - } - r->tab[0] = limb_mask((-prec) & (LIMB_BITS - 1), - LIMB_BITS - 1); - for(i = 1; i < l; i++) - r->tab[i] = (limb_t)-1; - e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); - r->expn = e_max; - r->sign = sign; - } - return BF_ST_OVERFLOW | BF_ST_INEXACT; -} - -/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is - assumed to have length 'l' (1 <= l <= r->len). Note: 'prec1' can be - infinite (BF_PREC_INF). 'ret' is 0 or BF_ST_INEXACT if the result - is known to be inexact. Can fail with BF_ST_MEM_ERROR in case of - overflow not returning infinity. */ -static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l, - int ret) -{ - limb_t v, a; - int shift, add_one, rnd_mode; - slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; - - /* e_min and e_max are computed to match the IEEE 754 conventions */ - e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); - e_min = -e_range + 3; - e_max = e_range; - - if (flags & BF_FLAG_RADPNT_PREC) { - /* 'prec' is the precision after the radix point */ - if (prec1 != BF_PREC_INF) - prec = r->expn + prec1; - else - prec = prec1; - } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { - /* restrict the precision in case of potentially subnormal - result */ - assert(prec1 != BF_PREC_INF); - prec = prec1 - (e_min - r->expn); - } else { - prec = prec1; - } - - /* round to prec bits */ - rnd_mode = flags & BF_RND_MASK; - add_one = bf_get_rnd_add(&ret, r, l, prec, rnd_mode); - - if (prec <= 0) { - if (add_one) { - bf_resize(r, 1); /* cannot fail */ - r->tab[0] = (limb_t)1 << (LIMB_BITS - 1); - r->expn += 1 - prec; - ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; - return ret; - } else { - goto underflow; - } - } else if (add_one) { - limb_t carry; - - /* add one starting at digit 'prec - 1' */ - bit_pos = l * LIMB_BITS - 1 - (prec - 1); - pos = bit_pos >> LIMB_LOG2_BITS; - carry = (limb_t)1 << (bit_pos & (LIMB_BITS - 1)); - - for(i = pos; i < l; i++) { - v = r->tab[i] + carry; - carry = (v < carry); - r->tab[i] = v; - if (carry == 0) - break; - } - if (carry) { - /* shift right by one digit */ - v = 1; - for(i = l - 1; i >= pos; i--) { - a = r->tab[i]; - r->tab[i] = (a >> 1) | (v << (LIMB_BITS - 1)); - v = a; - } - r->expn++; - } - } - - /* check underflow */ - if (unlikely(r->expn < e_min)) { - if (flags & BF_FLAG_SUBNORMAL) { - /* if inexact, also set the underflow flag */ - if (ret & BF_ST_INEXACT) - ret |= BF_ST_UNDERFLOW; - } else { - underflow: - ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; - bf_set_zero(r, r->sign); - return ret; - } - } - - /* check overflow */ - if (unlikely(r->expn > e_max)) - return bf_set_overflow(r, r->sign, prec1, flags); - - /* keep the bits starting at 'prec - 1' */ - bit_pos = l * LIMB_BITS - 1 - (prec - 1); - i = bit_pos >> LIMB_LOG2_BITS; - if (i >= 0) { - shift = bit_pos & (LIMB_BITS - 1); - if (shift != 0) - r->tab[i] &= limb_mask(shift, LIMB_BITS - 1); - } else { - i = 0; - } - /* remove trailing zeros */ - while (r->tab[i] == 0) - i++; - if (i > 0) { - l -= i; - memmove(r->tab, r->tab + i, l * sizeof(limb_t)); - } - bf_resize(r, l); /* cannot fail */ - return ret; -} - -/* 'r' must be a finite number. */ -int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags) -{ - limb_t l, v, a; - int shift, ret; - slimb_t i; - - // bf_print_str("bf_renorm", r); - l = r->len; - while (l > 0 && r->tab[l - 1] == 0) - l--; - if (l == 0) { - /* zero */ - r->expn = BF_EXP_ZERO; - bf_resize(r, 0); /* cannot fail */ - ret = 0; - } else { - r->expn -= (r->len - l) * LIMB_BITS; - /* shift to have the MSB set to '1' */ - v = r->tab[l - 1]; - shift = clz(v); - if (shift != 0) { - v = 0; - for(i = 0; i < l; i++) { - a = r->tab[i]; - r->tab[i] = (a << shift) | (v >> (LIMB_BITS - shift)); - v = a; - } - r->expn -= shift; - } - ret = __bf_round(r, prec1, flags, l, 0); - } - // bf_print_str("r_final", r); - return ret; -} - -/* return true if rounding can be done at precision 'prec' assuming - the exact result r is such that |r-a| <= 2^(EXP(a)-k). */ -/* XXX: check the case where the exponent would be incremented by the - rounding */ -int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k) -{ - bool is_rndn; - slimb_t bit_pos, n; - limb_t bit; - - if (a->expn == BF_EXP_INF || a->expn == BF_EXP_NAN) - return false; - if (rnd_mode == BF_RNDF) { - return (k >= (prec + 1)); - } - if (a->expn == BF_EXP_ZERO) - return false; - is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); - if (k < (prec + 2)) - return false; - bit_pos = a->len * LIMB_BITS - 1 - prec; - n = k - prec; - /* bit pattern for RNDN or RNDNA: 0111.. or 1000... - for other rounding modes: 000... or 111... - */ - bit = get_bit(a->tab, a->len, bit_pos); - bit_pos--; - n--; - bit ^= is_rndn; - /* XXX: slow, but a few iterations on average */ - while (n != 0) { - if (get_bit(a->tab, a->len, bit_pos) != bit) - return true; - bit_pos--; - n--; - } - return false; -} - -/* Cannot fail with BF_ST_MEM_ERROR. */ -int bf_round(bf_t *r, limb_t prec, bf_flags_t flags) -{ - if (r->len == 0) - return 0; - return __bf_round(r, prec, flags, r->len, 0); -} - -/* for debugging */ -static __maybe_unused void dump_limbs(const char *str, const limb_t *tab, limb_t n) -{ - limb_t i; - printf("%s: len=%" PRId_LIMB "\n", str, n); - for(i = 0; i < n; i++) { - printf("%" PRId_LIMB ": " FMT_LIMB "\n", - i, tab[i]); - } -} - -void mp_print_str(const char *str, const limb_t *tab, limb_t n) -{ - slimb_t i; - printf("%s= 0x", str); - for(i = n - 1; i >= 0; i--) { - if (i != (n - 1)) - printf("_"); - printf(FMT_LIMB, tab[i]); - } - printf("\n"); -} - -static __maybe_unused void mp_print_str_h(const char *str, - const limb_t *tab, limb_t n, - limb_t high) -{ - slimb_t i; - printf("%s= 0x", str); - printf(FMT_LIMB, high); - for(i = n - 1; i >= 0; i--) { - printf("_"); - printf(FMT_LIMB, tab[i]); - } - printf("\n"); -} - -/* for debugging */ -void bf_print_str(const char *str, const bf_t *a) -{ - slimb_t i; - printf("%s=", str); - - if (a->expn == BF_EXP_NAN) { - printf("NaN"); - } else { - if (a->sign) - putchar('-'); - if (a->expn == BF_EXP_ZERO) { - putchar('0'); - } else if (a->expn == BF_EXP_INF) { - printf("Inf"); - } else { - printf("0x0."); - for(i = a->len - 1; i >= 0; i--) - printf(FMT_LIMB, a->tab[i]); - printf("p%" PRId_LIMB, a->expn); - } - } - printf("\n"); -} - -/* compare the absolute value of 'a' and 'b'. Return < 0 if a < b, 0 - if a = b and > 0 otherwise. */ -int bf_cmpu(const bf_t *a, const bf_t *b) -{ - slimb_t i; - limb_t len, v1, v2; - - if (a->expn != b->expn) { - if (a->expn < b->expn) - return -1; - else - return 1; - } - len = bf_max(a->len, b->len); - for(i = len - 1; i >= 0; i--) { - v1 = get_limbz(a, a->len - len + i); - v2 = get_limbz(b, b->len - len + i); - if (v1 != v2) { - if (v1 < v2) - return -1; - else - return 1; - } - } - return 0; -} - -/* Full order: -0 < 0, NaN == NaN and NaN is larger than all other numbers */ -int bf_cmp_full(const bf_t *a, const bf_t *b) -{ - int res; - - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - if (a->expn == b->expn) - res = 0; - else if (a->expn == BF_EXP_NAN) - res = 1; - else - res = -1; - } else if (a->sign != b->sign) { - res = 1 - 2 * a->sign; - } else { - res = bf_cmpu(a, b); - if (a->sign) - res = -res; - } - return res; -} - -/* Standard floating point comparison: return 2 if one of the operands - is NaN (unordered) or -1, 0, 1 depending on the ordering assuming - -0 == +0 */ -int bf_cmp(const bf_t *a, const bf_t *b) -{ - int res; - - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - res = 2; - } else if (a->sign != b->sign) { - if (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) - res = 0; - else - res = 1 - 2 * a->sign; - } else { - res = bf_cmpu(a, b); - if (a->sign) - res = -res; - } - return res; -} - -/* Compute the number of bits 'n' matching the pattern: - a= X1000..0 - b= X0111..1 - - When computing a-b, the result will have at least n leading zero - bits. - - Precondition: a > b and a.expn - b.expn = 0 or 1 -*/ -static limb_t count_cancelled_bits(const bf_t *a, const bf_t *b) -{ - slimb_t bit_offset, b_offset, n; - int p, p1; - limb_t v1, v2, mask; - - bit_offset = a->len * LIMB_BITS - 1; - b_offset = (b->len - a->len) * LIMB_BITS - (LIMB_BITS - 1) + - a->expn - b->expn; - n = 0; - - /* first search the equals bits */ - for(;;) { - v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); - v2 = get_bits(b->tab, b->len, bit_offset + b_offset); - // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); - if (v1 != v2) - break; - n += LIMB_BITS; - bit_offset -= LIMB_BITS; - } - /* find the position of the first different bit */ - p = clz(v1 ^ v2) + 1; - n += p; - /* then search for '0' in a and '1' in b */ - p = LIMB_BITS - p; - if (p > 0) { - /* search in the trailing p bits of v1 and v2 */ - mask = limb_mask(0, p - 1); - p1 = bf_min(clz(v1 & mask), clz((~v2) & mask)) - (LIMB_BITS - p); - n += p1; - if (p1 != p) - goto done; - } - bit_offset -= LIMB_BITS; - for(;;) { - v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); - v2 = get_bits(b->tab, b->len, bit_offset + b_offset); - // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); - if (v1 != 0 || v2 != -1) { - /* different: count the matching bits */ - p1 = bf_min(clz(v1), clz(~v2)); - n += p1; - break; - } - n += LIMB_BITS; - bit_offset -= LIMB_BITS; - } - done: - return n; -} - -static int bf_add_internal(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int b_neg) -{ - const bf_t *tmp; - int is_sub, ret, cmp_res, a_sign, b_sign; - - a_sign = a->sign; - b_sign = b->sign ^ b_neg; - is_sub = a_sign ^ b_sign; - cmp_res = bf_cmpu(a, b); - if (cmp_res < 0) { - tmp = a; - a = b; - b = tmp; - a_sign = b_sign; /* b_sign is never used later */ - } - /* abs(a) >= abs(b) */ - if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { - /* zero result */ - bf_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); - ret = 0; - } else if (a->len == 0 || b->len == 0) { - ret = 0; - if (a->expn >= BF_EXP_INF) { - if (a->expn == BF_EXP_NAN) { - /* at least one operand is NaN */ - bf_set_nan(r); - } else if (b->expn == BF_EXP_INF && is_sub) { - /* infinities with different signs */ - bf_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - bf_set_inf(r, a_sign); - } - } else { - /* at least one zero and not subtract */ - bf_set(r, a); - r->sign = a_sign; - goto renorm; - } - } else { - slimb_t d, a_offset, b_bit_offset, i, cancelled_bits; - limb_t carry, v1, v2, u, r_len, carry1, precl, tot_len, z, sub_mask; - - r->sign = a_sign; - r->expn = a->expn; - d = a->expn - b->expn; - /* must add more precision for the leading cancelled bits in - subtraction */ - if (is_sub) { - if (d <= 1) - cancelled_bits = count_cancelled_bits(a, b); - else - cancelled_bits = 1; - } else { - cancelled_bits = 0; - } - - /* add two extra bits for rounding */ - precl = (cancelled_bits + prec + 2 + LIMB_BITS - 1) / LIMB_BITS; - tot_len = bf_max(a->len, b->len + (d + LIMB_BITS - 1) / LIMB_BITS); - r_len = bf_min(precl, tot_len); - if (bf_resize(r, r_len)) - goto fail; - a_offset = a->len - r_len; - b_bit_offset = (b->len - r_len) * LIMB_BITS + d; - - /* compute the bits before for the rounding */ - carry = is_sub; - z = 0; - sub_mask = -is_sub; - i = r_len - tot_len; - while (i < 0) { - slimb_t ap, bp; - bool inflag; - - ap = a_offset + i; - bp = b_bit_offset + i * LIMB_BITS; - inflag = false; - if (ap >= 0 && ap < a->len) { - v1 = a->tab[ap]; - inflag = true; - } else { - v1 = 0; - } - if (bp + LIMB_BITS > 0 && bp < (slimb_t)(b->len * LIMB_BITS)) { - v2 = get_bits(b->tab, b->len, bp); - inflag = true; - } else { - v2 = 0; - } - if (!inflag) { - /* outside 'a' and 'b': go directly to the next value - inside a or b so that the running time does not - depend on the exponent difference */ - i = 0; - if (ap < 0) - i = bf_min(i, -a_offset); - /* b_bit_offset + i * LIMB_BITS + LIMB_BITS >= 1 - equivalent to - i >= ceil(-b_bit_offset + 1 - LIMB_BITS) / LIMB_BITS) - */ - if (bp + LIMB_BITS <= 0) - i = bf_min(i, (-b_bit_offset) >> LIMB_LOG2_BITS); - } else { - i++; - } - v2 ^= sub_mask; - u = v1 + v2; - carry1 = u < v1; - u += carry; - carry = (u < carry) | carry1; - z |= u; - } - /* and the result */ - for(i = 0; i < r_len; i++) { - v1 = get_limbz(a, a_offset + i); - v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS); - v2 ^= sub_mask; - u = v1 + v2; - carry1 = u < v1; - u += carry; - carry = (u < carry) | carry1; - r->tab[i] = u; - } - /* set the extra bits for the rounding */ - r->tab[0] |= (z != 0); - - /* carry is only possible in add case */ - if (!is_sub && carry) { - if (bf_resize(r, r_len + 1)) - goto fail; - r->tab[r_len] = 1; - r->expn += LIMB_BITS; - } - renorm: - ret = bf_normalize_and_round(r, prec, flags); - } - return ret; - fail: - bf_set_nan(r); - return BF_ST_MEM_ERROR; -} - -static int __bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_add_internal(r, a, b, prec, flags, 0); -} - -static int __bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_add_internal(r, a, b, prec, flags, 1); -} - -limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, - limb_t n, limb_t carry) -{ - slimb_t i; - limb_t k, a, v, k1; - - k = carry; - for(i=0;i v; - v = a - k; - k = (v > a) | k1; - res[i] = v; - } - return k; -} - -/* compute 0 - op2 */ -static limb_t mp_neg(limb_t *res, const limb_t *op2, mp_size_t n, limb_t carry) -{ - int i; - limb_t k, a, v, k1; - - k = carry; - for(i=0;i v; - v = a - k; - k = (v > a) | k1; - res[i] = v; - } - return k; -} - -limb_t mp_sub_ui(limb_t *tab, limb_t b, mp_size_t n) -{ - mp_size_t i; - limb_t k, a, v; - - k=b; - for(i=0;i v; - tab[i] = a; - if (k == 0) - break; - } - return k; -} - -/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). - 1 <= shift <= LIMB_BITS - 1 */ -static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n, - int shift, limb_t high) -{ - mp_size_t i; - limb_t l, a; - - assert(shift >= 1 && shift < LIMB_BITS); - l = high; - for(i = n - 1; i >= 0; i--) { - a = tab[i]; - tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift)); - l = a; - } - return l & (((limb_t)1 << shift) - 1); -} - -/* tabr[] = taba[] * b + l. Return the high carry */ -static limb_t mp_mul1(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b, limb_t l) -{ - limb_t i; - dlimb_t t; - - for(i = 0; i < n; i++) { - t = (dlimb_t)taba[i] * (dlimb_t)b + l; - tabr[i] = t; - l = t >> LIMB_BITS; - } - return l; -} - -/* tabr[] += taba[] * b, return the high word. */ -static limb_t mp_add_mul1(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b) -{ - limb_t i, l; - dlimb_t t; - - l = 0; - for(i = 0; i < n; i++) { - t = (dlimb_t)taba[i] * (dlimb_t)b + l + tabr[i]; - tabr[i] = t; - l = t >> LIMB_BITS; - } - return l; -} - -/* size of the result : op1_size + op2_size. */ -static void mp_mul_basecase(limb_t *result, - const limb_t *op1, limb_t op1_size, - const limb_t *op2, limb_t op2_size) -{ - limb_t i, r; - - result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0); - for(i=1;i= FFT_MUL_THRESHOLD)) { - bf_t r_s, *r = &r_s; - r->tab = result; - /* XXX: optimize memory usage in API */ - if (fft_mul(s, r, (limb_t *)op1, op1_size, - (limb_t *)op2, op2_size, FFT_MUL_R_NORESIZE)) - return -1; - } else -#endif - { - mp_mul_basecase(result, op1, op1_size, op2, op2_size); - } - return 0; -} - -/* tabr[] -= taba[] * b. Return the value to substract to the high - word. */ -static limb_t mp_sub_mul1(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b) -{ - limb_t i, l; - dlimb_t t; - - l = 0; - for(i = 0; i < n; i++) { - t = tabr[i] - (dlimb_t)taba[i] * (dlimb_t)b - l; - tabr[i] = t; - l = -(t >> LIMB_BITS); - } - return l; -} - -/* WARNING: d must be >= 2^(LIMB_BITS-1) */ -static inline limb_t udiv1norm_init(limb_t d) -{ - limb_t a0, a1; - a1 = -d - 1; - a0 = -1; - return (((dlimb_t)a1 << LIMB_BITS) | a0) / d; -} - -/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0 - / d' with 0 <= a1 < d. */ -static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0, - limb_t d, limb_t d_inv) -{ - limb_t n1m, n_adj, q, r, ah; - dlimb_t a; - n1m = ((slimb_t)a0 >> (LIMB_BITS - 1)); - n_adj = a0 + (n1m & d); - a = (dlimb_t)d_inv * (a1 - n1m) + n_adj; - q = (a >> LIMB_BITS) + a1; - /* compute a - q * r and update q so that the remainder is\ - between 0 and d - 1 */ - a = ((dlimb_t)a1 << LIMB_BITS) | a0; - a = a - (dlimb_t)q * d - d; - ah = a >> LIMB_BITS; - q += 1 + ah; - r = (limb_t)a + (ah & d); - *pr = r; - return q; -} - -/* b must be >= 1 << (LIMB_BITS - 1) */ -static limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b, limb_t r) -{ - slimb_t i; - - if (n >= UDIV1NORM_THRESHOLD) { - limb_t b_inv; - b_inv = udiv1norm_init(b); - for(i = n - 1; i >= 0; i--) { - tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); - } - } else { - dlimb_t a1; - for(i = n - 1; i >= 0; i--) { - a1 = ((dlimb_t)r << LIMB_BITS) | taba[i]; - tabr[i] = a1 / b; - r = a1 % b; - } - } - return r; -} - -static int mp_divnorm_large(bf_context_t *s, - limb_t *tabq, limb_t *taba, limb_t na, - const limb_t *tabb, limb_t nb); - -/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb - - 1] must be >= 1 << (LIMB_BITS - 1). na - nb must be >= 0. 'taba' - is modified and contains the remainder (nb limbs). tabq[0..na-nb] - contains the quotient with tabq[na - nb] <= 1. */ -static int mp_divnorm(bf_context_t *s, limb_t *tabq, limb_t *taba, limb_t na, - const limb_t *tabb, limb_t nb) -{ - limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r; - slimb_t i, j; - - b1 = tabb[nb - 1]; - if (nb == 1) { - taba[0] = mp_div1norm(tabq, taba, na, b1, 0); - return 0; - } - n = na - nb; - if (bf_min(n, nb) >= DIVNORM_LARGE_THRESHOLD) { - return mp_divnorm_large(s, tabq, taba, na, tabb, nb); - } - - if (n >= UDIV1NORM_THRESHOLD) - b1_inv = udiv1norm_init(b1); - else - b1_inv = 0; - - /* first iteration: the quotient is only 0 or 1 */ - q = 1; - for(j = nb - 1; j >= 0; j--) { - if (taba[n + j] != tabb[j]) { - if (taba[n + j] < tabb[j]) - q = 0; - break; - } - } - tabq[n] = q; - if (q) { - mp_sub(taba + n, taba + n, tabb, nb, 0); - } - - for(i = n - 1; i >= 0; i--) { - if (unlikely(taba[i + nb] >= b1)) { - q = -1; - } else if (b1_inv) { - q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv); - } else { - dlimb_t al; - al = ((dlimb_t)taba[i + nb] << LIMB_BITS) | taba[i + nb - 1]; - q = al / b1; - r = al % b1; - } - r = mp_sub_mul1(taba + i, tabb, nb, q); - - v = taba[i + nb]; - a = v - r; - c = (a > v); - taba[i + nb] = a; - - if (c != 0) { - /* negative result */ - for(;;) { - q--; - c = mp_add(taba + i, taba + i, tabb, nb, 0); - /* propagate carry and test if positive result */ - if (c != 0) { - if (++taba[i + nb] == 0) { - break; - } - } - } - } - tabq[i] = q; - } - return 0; -} - -/* compute r=B^(2*n)/a such as a*r < B^(2*n) < a*r + 2 with n >= 1. 'a' - has n limbs with a[n-1] >= B/2 and 'r' has n+1 limbs with r[n] = 1. - - See Modern Computer Arithmetic by Richard P. Brent and Paul - Zimmermann, algorithm 3.5 */ -int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n) -{ - mp_size_t l, h, k, i; - limb_t *tabxh, *tabt, c, *tabu; - - if (n <= 2) { - /* return ceil(B^(2*n)/a) - 1 */ - /* XXX: could avoid allocation */ - tabu = bf_malloc(s, sizeof(limb_t) * (2 * n + 1)); - tabt = bf_malloc(s, sizeof(limb_t) * (n + 2)); - if (!tabt || !tabu) - goto fail; - for(i = 0; i < 2 * n; i++) - tabu[i] = 0; - tabu[2 * n] = 1; - if (mp_divnorm(s, tabt, tabu, 2 * n + 1, taba, n)) - goto fail; - for(i = 0; i < n + 1; i++) - tabr[i] = tabt[i]; - if (mp_scan_nz(tabu, n) == 0) { - /* only happens for a=B^n/2 */ - mp_sub_ui(tabr, 1, n + 1); - } - } else { - l = (n - 1) / 2; - h = n - l; - /* n=2p -> l=p-1, h = p + 1, k = p + 3 - n=2p+1-> l=p, h = p + 1; k = p + 2 - */ - tabt = bf_malloc(s, sizeof(limb_t) * (n + h + 1)); - tabu = bf_malloc(s, sizeof(limb_t) * (n + 2 * h - l + 2)); - if (!tabt || !tabu) - goto fail; - tabxh = tabr + l; - if (mp_recip(s, tabxh, taba + l, h)) - goto fail; - if (mp_mul(s, tabt, taba, n, tabxh, h + 1)) /* n + h + 1 limbs */ - goto fail; - while (tabt[n + h] != 0) { - mp_sub_ui(tabxh, 1, h + 1); - c = mp_sub(tabt, tabt, taba, n, 0); - mp_sub_ui(tabt + n, c, h + 1); - } - /* T = B^(n+h) - T */ - mp_neg(tabt, tabt, n + h + 1, 0); - tabt[n + h]++; - if (mp_mul(s, tabu, tabt + l, n + h + 1 - l, tabxh, h + 1)) - goto fail; - /* n + 2*h - l + 2 limbs */ - k = 2 * h - l; - for(i = 0; i < l; i++) - tabr[i] = tabu[i + k]; - mp_add(tabr + l, tabr + l, tabu + 2 * h, h, 0); - } - bf_free(s, tabt); - bf_free(s, tabu); - return 0; - fail: - bf_free(s, tabt); - bf_free(s, tabu); - return -1; -} - -/* return -1, 0 or 1 */ -static int mp_cmp(const limb_t *taba, const limb_t *tabb, mp_size_t n) -{ - mp_size_t i; - for(i = n - 1; i >= 0; i--) { - if (taba[i] != tabb[i]) { - if (taba[i] < tabb[i]) - return -1; - else - return 1; - } - } - return 0; -} - -//#define DEBUG_DIVNORM_LARGE -//#define DEBUG_DIVNORM_LARGE2 - -/* subquadratic divnorm */ -static int mp_divnorm_large(bf_context_t *s, - limb_t *tabq, limb_t *taba, limb_t na, - const limb_t *tabb, limb_t nb) -{ - limb_t *tabb_inv, nq, *tabt, i, n; - nq = na - nb; -#ifdef DEBUG_DIVNORM_LARGE - printf("na=%d nb=%d nq=%d\n", (int)na, (int)nb, (int)nq); - mp_print_str("a", taba, na); - mp_print_str("b", tabb, nb); -#endif - assert(nq >= 1); - n = nq; - if (nq < nb) - n++; - tabb_inv = bf_malloc(s, sizeof(limb_t) * (n + 1)); - tabt = bf_malloc(s, sizeof(limb_t) * 2 * (n + 1)); - if (!tabb_inv || !tabt) - goto fail; - - if (n >= nb) { - for(i = 0; i < n - nb; i++) - tabt[i] = 0; - for(i = 0; i < nb; i++) - tabt[i + n - nb] = tabb[i]; - } else { - /* truncate B: need to increment it so that the approximate - inverse is smaller that the exact inverse */ - for(i = 0; i < n; i++) - tabt[i] = tabb[i + nb - n]; - if (mp_add_ui(tabt, 1, n)) { - /* tabt = B^n : tabb_inv = B^n */ - memset(tabb_inv, 0, n * sizeof(limb_t)); - tabb_inv[n] = 1; - goto recip_done; - } - } - if (mp_recip(s, tabb_inv, tabt, n)) - goto fail; - recip_done: - /* Q=A*B^-1 */ - if (mp_mul(s, tabt, tabb_inv, n + 1, taba + na - (n + 1), n + 1)) - goto fail; - - for(i = 0; i < nq + 1; i++) - tabq[i] = tabt[i + 2 * (n + 1) - (nq + 1)]; -#ifdef DEBUG_DIVNORM_LARGE - mp_print_str("q", tabq, nq + 1); -#endif - - bf_free(s, tabt); - bf_free(s, tabb_inv); - tabb_inv = NULL; - - /* R=A-B*Q */ - tabt = bf_malloc(s, sizeof(limb_t) * (na + 1)); - if (!tabt) - goto fail; - if (mp_mul(s, tabt, tabq, nq + 1, tabb, nb)) - goto fail; - /* we add one more limb for the result */ - mp_sub(taba, taba, tabt, nb + 1, 0); - bf_free(s, tabt); - /* the approximated quotient is smaller than than the exact one, - hence we may have to increment it */ -#ifdef DEBUG_DIVNORM_LARGE2 - int cnt = 0; - static int cnt_max; -#endif - for(;;) { - if (taba[nb] == 0 && mp_cmp(taba, tabb, nb) < 0) - break; - taba[nb] -= mp_sub(taba, taba, tabb, nb, 0); - mp_add_ui(tabq, 1, nq + 1); -#ifdef DEBUG_DIVNORM_LARGE2 - cnt++; -#endif - } -#ifdef DEBUG_DIVNORM_LARGE2 - if (cnt > cnt_max) { - cnt_max = cnt; - printf("\ncnt=%d nq=%d nb=%d\n", cnt_max, (int)nq, (int)nb); - } -#endif - return 0; - fail: - bf_free(s, tabb_inv); - bf_free(s, tabt); - return -1; -} - -int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - int ret, r_sign; - - if (a->len < b->len) { - const bf_t *tmp = a; - a = b; - b = tmp; - } - r_sign = a->sign ^ b->sign; - /* here b->len <= a->len */ - if (b->len == 0) { - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bf_set_nan(r); - ret = 0; - } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { - if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || - (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { - bf_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - bf_set_inf(r, r_sign); - ret = 0; - } - } else { - bf_set_zero(r, r_sign); - ret = 0; - } - } else { - bf_t tmp, *r1 = NULL; - limb_t a_len, b_len, precl; - limb_t *a_tab, *b_tab; - - a_len = a->len; - b_len = b->len; - - if ((flags & BF_RND_MASK) == BF_RNDF) { - /* faithful rounding does not require using the full inputs */ - precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS; - a_len = bf_min(a_len, precl); - b_len = bf_min(b_len, precl); - } - a_tab = a->tab + a->len - a_len; - b_tab = b->tab + b->len - b_len; - -#ifdef USE_FFT_MUL - if (b_len >= FFT_MUL_THRESHOLD) { - int mul_flags = 0; - if (r == a) - mul_flags |= FFT_MUL_R_OVERLAP_A; - if (r == b) - mul_flags |= FFT_MUL_R_OVERLAP_B; - if (fft_mul(r->ctx, r, a_tab, a_len, b_tab, b_len, mul_flags)) - goto fail; - } else -#endif - { - if (r == a || r == b) { - bf_init(r->ctx, &tmp); - r1 = r; - r = &tmp; - } - if (bf_resize(r, a_len + b_len)) { - fail: - bf_set_nan(r); - ret = BF_ST_MEM_ERROR; - goto done; - } - mp_mul_basecase(r->tab, a_tab, a_len, b_tab, b_len); - } - r->sign = r_sign; - r->expn = a->expn + b->expn; - ret = bf_normalize_and_round(r, prec, flags); - done: - if (r == &tmp) - bf_move(r1, &tmp); - } - return ret; -} - -/* multiply 'r' by 2^e */ -int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags) -{ - slimb_t e_max; - if (r->len == 0) - return 0; - e_max = ((limb_t)1 << BF_EXT_EXP_BITS_MAX) - 1; - e = bf_max(e, -e_max); - e = bf_min(e, e_max); - r->expn += e; - return __bf_round(r, prec, flags, r->len, 0); -} - -/* Return e such as a=m*2^e with m odd integer. return 0 if a is zero, - Infinite or Nan. */ -slimb_t bf_get_exp_min(const bf_t *a) -{ - slimb_t i; - limb_t v; - int k; - - for(i = 0; i < a->len; i++) { - v = a->tab[i]; - if (v != 0) { - k = ctz(v); - return a->expn - (a->len - i) * LIMB_BITS + k; - } - } - return 0; -} - -/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the - integer defined as floor(a/b) and r = a - q * b. */ -static void bf_tdivremu(bf_t *q, bf_t *r, - const bf_t *a, const bf_t *b) -{ - if (bf_cmpu(a, b) < 0) { - bf_set_ui(q, 0); - bf_set(r, a); - } else { - bf_div(q, a, b, bf_max(a->expn - b->expn + 1, 2), BF_RNDZ); - bf_rint(q, BF_RNDZ); - bf_mul(r, q, b, BF_PREC_INF, BF_RNDZ); - bf_sub(r, a, r, BF_PREC_INF, BF_RNDZ); - } -} - -static int __bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - int ret, r_sign; - limb_t n, nb, precl; - - r_sign = a->sign ^ b->sign; - if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) { - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else if (a->expn == BF_EXP_INF) { - bf_set_inf(r, r_sign); - return 0; - } else { - bf_set_zero(r, r_sign); - return 0; - } - } else if (a->expn == BF_EXP_ZERO) { - if (b->expn == BF_EXP_ZERO) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set_zero(r, r_sign); - return 0; - } - } else if (b->expn == BF_EXP_ZERO) { - bf_set_inf(r, r_sign); - return BF_ST_DIVIDE_ZERO; - } - - /* number of limbs of the quotient (2 extra bits for rounding) */ - precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS; - nb = b->len; - n = bf_max(a->len, precl); - - { - limb_t *taba, na; - slimb_t d; - - na = n + nb; - -#if LIMB_LOG2_BITS == 6 - if (na >= (SIZE_MAX / sizeof(limb_t)) - 1) { - return BF_ST_MEM_ERROR; /* Return memory error status */ - } -#endif - - taba = bf_malloc(s, (na + 1) * sizeof(limb_t)); - if (!taba) - goto fail; - d = na - a->len; - memset(taba, 0, d * sizeof(limb_t)); - memcpy(taba + d, a->tab, a->len * sizeof(limb_t)); - if (bf_resize(r, n + 1)) - goto fail1; - if (mp_divnorm(s, r->tab, taba, na, b->tab, nb)) { - fail1: - bf_free(s, taba); - goto fail; - } - /* see if non zero remainder */ - if (mp_scan_nz(taba, nb)) - r->tab[0] |= 1; - bf_free(r->ctx, taba); - r->expn = a->expn - b->expn + LIMB_BITS; - r->sign = r_sign; - ret = bf_normalize_and_round(r, prec, flags); - } - return ret; - fail: - bf_set_nan(r); - return BF_ST_MEM_ERROR; -} - -/* division and remainder. - - rnd_mode is the rounding mode for the quotient. The additional - rounding mode BF_RND_EUCLIDIAN is supported. - - 'q' is an integer. 'r' is rounded with prec and flags (prec can be - BF_PREC_INF). -*/ -int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, - limb_t prec, bf_flags_t flags, int rnd_mode) -{ - bf_t a1_s, *a1 = &a1_s; - bf_t b1_s, *b1 = &b1_s; - int q_sign, ret; - bool is_ceil, is_rndn; - - assert(q != a && q != b); - assert(r != a && r != b); - assert(q != r); - - if (a->len == 0 || b->len == 0) { - bf_set_zero(q, 0); - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set(r, a); - return bf_round(r, prec, flags); - } - } - - q_sign = a->sign ^ b->sign; - is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); - switch(rnd_mode) { - default: - case BF_RNDZ: - case BF_RNDN: - case BF_RNDNA: - is_ceil = false; - break; - case BF_RNDD: - is_ceil = q_sign; - break; - case BF_RNDU: - is_ceil = q_sign ^ 1; - break; - case BF_RNDA: - is_ceil = true; - break; - case BF_DIVREM_EUCLIDIAN: - is_ceil = a->sign; - break; - } - - a1->expn = a->expn; - a1->tab = a->tab; - a1->len = a->len; - a1->sign = 0; - - b1->expn = b->expn; - b1->tab = b->tab; - b1->len = b->len; - b1->sign = 0; - - /* XXX: could improve to avoid having a large 'q' */ - bf_tdivremu(q, r, a1, b1); - if (bf_is_nan(q) || bf_is_nan(r)) - goto fail; - - if (r->len != 0) { - if (is_rndn) { - int res; - b1->expn--; - res = bf_cmpu(r, b1); - b1->expn++; - if (res > 0 || - (res == 0 && - (rnd_mode == BF_RNDNA || - get_bit(q->tab, q->len, q->len * LIMB_BITS - q->expn)))) { - goto do_sub_r; - } - } else if (is_ceil) { - do_sub_r: - ret = bf_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ); - ret |= bf_sub(r, r, b1, BF_PREC_INF, BF_RNDZ); - if (ret & BF_ST_MEM_ERROR) - goto fail; - } - } - - r->sign ^= a->sign; - q->sign = q_sign; - return bf_round(r, prec, flags); - fail: - bf_set_nan(q); - bf_set_nan(r); - return BF_ST_MEM_ERROR; -} - -int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode) -{ - bf_t q_s, *q = &q_s; - int ret; - - bf_init(r->ctx, q); - ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); - bf_delete(q); - return ret; -} - -static inline int bf_get_limb(slimb_t *pres, const bf_t *a, int flags) -{ -#if LIMB_BITS == 32 - return bf_get_int32(pres, a, flags); -#else - return bf_get_int64(pres, a, flags); -#endif -} - -int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode) -{ - bf_t q_s, *q = &q_s; - int ret; - - bf_init(r->ctx, q); - ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); - bf_get_limb(pq, q, BF_GET_INT_MOD); - bf_delete(q); - return ret; -} - -static __maybe_unused inline limb_t mul_mod(limb_t a, limb_t b, limb_t m) -{ - dlimb_t t; - t = (dlimb_t)a * (dlimb_t)b; - return t % m; -} - -#if defined(USE_MUL_CHECK) -static limb_t mp_mod1(const limb_t *tab, limb_t n, limb_t m, limb_t r) -{ - slimb_t i; - dlimb_t t; - - for(i = n - 1; i >= 0; i--) { - t = ((dlimb_t)r << LIMB_BITS) | tab[i]; - r = t % m; - } - return r; -} -#endif - -static const uint16_t sqrt_table[192] = { -128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,157,158,159,160,160,161,162,163,163,164,165,166,167,167,168,169,170,170,171,172,173,173,174,175,176,176,177,178,178,179,180,181,181,182,183,183,184,185,185,186,187,187,188,189,189,190,191,192,192,193,193,194,195,195,196,197,197,198,199,199,200,201,201,202,203,203,204,204,205,206,206,207,208,208,209,209,210,211,211,212,212,213,214,214,215,215,216,217,217,218,218,219,219,220,221,221,222,222,223,224,224,225,225,226,226,227,227,228,229,229,230,230,231,231,232,232,233,234,234,235,235,236,236,237,237,238,238,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,253,254,254,255, -}; - -/* a >= 2^(LIMB_BITS - 2). Return (s, r) with s=floor(sqrt(a)) and - r=a-s^2. 0 <= r <= 2 * s */ -static limb_t mp_sqrtrem1(limb_t *pr, limb_t a) -{ - limb_t s1, r1, s, r, q, u, num; - - /* use a table for the 16 -> 8 bit sqrt */ - s1 = sqrt_table[(a >> (LIMB_BITS - 8)) - 64]; - r1 = (a >> (LIMB_BITS - 16)) - s1 * s1; - if (r1 > 2 * s1) { - r1 -= 2 * s1 + 1; - s1++; - } - - /* one iteration to get a 32 -> 16 bit sqrt */ - num = (r1 << 8) | ((a >> (LIMB_BITS - 32 + 8)) & 0xff); - q = num / (2 * s1); /* q <= 2^8 */ - u = num % (2 * s1); - s = (s1 << 8) + q; - r = (u << 8) | ((a >> (LIMB_BITS - 32)) & 0xff); - r -= q * q; - if ((slimb_t)r < 0) { - s--; - r += 2 * s + 1; - } - -#if LIMB_BITS == 64 - s1 = s; - r1 = r; - /* one more iteration for 64 -> 32 bit sqrt */ - num = (r1 << 16) | ((a >> (LIMB_BITS - 64 + 16)) & 0xffff); - q = num / (2 * s1); /* q <= 2^16 */ - u = num % (2 * s1); - s = (s1 << 16) + q; - r = (u << 16) | ((a >> (LIMB_BITS - 64)) & 0xffff); - r -= q * q; - if ((slimb_t)r < 0) { - s--; - r += 2 * s + 1; - } -#endif - *pr = r; - return s; -} - -/* return floor(sqrt(a)) */ -limb_t bf_isqrt(limb_t a) -{ - limb_t s, r; - int k; - - if (a == 0) - return 0; - k = clz(a) & ~1; - s = mp_sqrtrem1(&r, a << k); - s >>= (k >> 1); - return s; -} - -static limb_t mp_sqrtrem2(limb_t *tabs, limb_t *taba) -{ - limb_t s1, r1, s, q, u, a0, a1; - dlimb_t r, num; - int l; - - a0 = taba[0]; - a1 = taba[1]; - s1 = mp_sqrtrem1(&r1, a1); - l = LIMB_BITS / 2; - num = ((dlimb_t)r1 << l) | (a0 >> l); - q = num / (2 * s1); - u = num % (2 * s1); - s = (s1 << l) + q; - r = ((dlimb_t)u << l) | (a0 & (((limb_t)1 << l) - 1)); - if (unlikely((q >> l) != 0)) - r -= (dlimb_t)1 << LIMB_BITS; /* special case when q=2^l */ - else - r -= q * q; - if ((slimb_t)(r >> LIMB_BITS) < 0) { - s--; - r += 2 * (dlimb_t)s + 1; - } - tabs[0] = s; - taba[0] = r; - return r >> LIMB_BITS; -} - -//#define DEBUG_SQRTREM - -/* tmp_buf must contain (n / 2 + 1 limbs). *prh contains the highest - limb of the remainder. */ -static int mp_sqrtrem_rec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n, - limb_t *tmp_buf, limb_t *prh) -{ - limb_t l, h, rh, ql, qh, c, i; - - if (n == 1) { - *prh = mp_sqrtrem2(tabs, taba); - return 0; - } -#ifdef DEBUG_SQRTREM - mp_print_str("a", taba, 2 * n); -#endif - l = n / 2; - h = n - l; - if (mp_sqrtrem_rec(s, tabs + l, taba + 2 * l, h, tmp_buf, &qh)) - return -1; -#ifdef DEBUG_SQRTREM - mp_print_str("s1", tabs + l, h); - mp_print_str_h("r1", taba + 2 * l, h, qh); - mp_print_str_h("r2", taba + l, n, qh); -#endif - - /* the remainder is in taba + 2 * l. Its high bit is in qh */ - if (qh) { - mp_sub(taba + 2 * l, taba + 2 * l, tabs + l, h, 0); - } - /* instead of dividing by 2*s, divide by s (which is normalized) - and update q and r */ - if (mp_divnorm(s, tmp_buf, taba + l, n, tabs + l, h)) - return -1; - qh += tmp_buf[l]; - for(i = 0; i < l; i++) - tabs[i] = tmp_buf[i]; - ql = mp_shr(tabs, tabs, l, 1, qh & 1); - qh = qh >> 1; /* 0 or 1 */ - if (ql) - rh = mp_add(taba + l, taba + l, tabs + l, h, 0); - else - rh = 0; -#ifdef DEBUG_SQRTREM - mp_print_str_h("q", tabs, l, qh); - mp_print_str_h("u", taba + l, h, rh); -#endif - - mp_add_ui(tabs + l, qh, h); -#ifdef DEBUG_SQRTREM - mp_print_str_h("s2", tabs, n, sh); -#endif - - /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */ - /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */ - if (qh) { - c = qh; - } else { - if (mp_mul(s, taba + n, tabs, l, tabs, l)) - return -1; - c = mp_sub(taba, taba, taba + n, 2 * l, 0); - } - rh -= mp_sub_ui(taba + 2 * l, c, n - 2 * l); - if ((slimb_t)rh < 0) { - mp_sub_ui(tabs, 1, n); - rh += mp_add_mul1(taba, tabs, n, 2); - rh += mp_add_ui(taba, 1, n); - } - *prh = rh; - return 0; -} - -/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= 2 ^ (LIMB_BITS - - 2). Return (s, r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 - * s. tabs has n limbs. r is returned in the lower n limbs of - taba. Its r[n] is the returned value of the function. */ -/* Algorithm from the article "Karatsuba Square Root" by Paul Zimmermann and - inspirated from its GMP implementation */ -int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n) -{ - limb_t tmp_buf1[8]; - limb_t *tmp_buf; - mp_size_t n2; - int ret; - n2 = n / 2 + 1; - if (n2 <= countof(tmp_buf1)) { - tmp_buf = tmp_buf1; - } else { - tmp_buf = bf_malloc(s, sizeof(limb_t) * n2); - if (!tmp_buf) - return -1; - } - ret = mp_sqrtrem_rec(s, tabs, taba, n, tmp_buf, taba + n); - if (tmp_buf != tmp_buf1) - bf_free(s, tmp_buf); - return ret; -} - -/* Integer square root with remainder. 'a' must be an integer. r = - floor(sqrt(a)) and rem = a - r^2. BF_ST_INEXACT is set if the result - is inexact. 'rem' can be NULL if the remainder is not needed. */ -int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a) -{ - int ret; - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - } else if (a->expn == BF_EXP_INF && a->sign) { - goto invalid_op; - } else { - bf_set(r, a); - } - if (rem1) - bf_set_ui(rem1, 0); - ret = 0; - } else if (a->sign) { - invalid_op: - bf_set_nan(r); - if (rem1) - bf_set_ui(rem1, 0); - ret = BF_ST_INVALID_OP; - } else { - bf_t rem_s, *rem; - - bf_sqrt(r, a, (a->expn + 1) / 2, BF_RNDZ); - bf_rint(r, BF_RNDZ); - /* see if the result is exact by computing the remainder */ - if (rem1) { - rem = rem1; - } else { - rem = &rem_s; - bf_init(r->ctx, rem); - } - /* XXX: could avoid recomputing the remainder */ - bf_mul(rem, r, r, BF_PREC_INF, BF_RNDZ); - bf_neg(rem); - bf_add(rem, rem, a, BF_PREC_INF, BF_RNDZ); - if (bf_is_nan(rem)) { - ret = BF_ST_MEM_ERROR; - goto done; - } - if (rem->len != 0) { - ret = BF_ST_INEXACT; - } else { - ret = 0; - } - done: - if (!rem1) - bf_delete(rem); - } - return ret; -} - -int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = a->ctx; - int ret; - - assert(r != a); - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - } else if (a->expn == BF_EXP_INF && a->sign) { - goto invalid_op; - } else { - bf_set(r, a); - } - ret = 0; - } else if (a->sign) { - invalid_op: - bf_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - limb_t *a1; - slimb_t n, n1; - limb_t res; - - /* convert the mantissa to an integer with at least 2 * - prec + 4 bits */ - n = (2 * (prec + 2) + 2 * LIMB_BITS - 1) / (2 * LIMB_BITS); - if (bf_resize(r, n)) - goto fail; - a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); - if (!a1) - goto fail; - n1 = bf_min(2 * n, a->len); - memset(a1, 0, (2 * n - n1) * sizeof(limb_t)); - memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t)); - if (a->expn & 1) { - res = mp_shr(a1, a1, 2 * n, 1, 0); - } else { - res = 0; - } - if (mp_sqrtrem(s, r->tab, a1, n)) { - bf_free(s, a1); - goto fail; - } - if (!res) { - res = mp_scan_nz(a1, n + 1); - } - bf_free(s, a1); - if (!res) { - res = mp_scan_nz(a->tab, a->len - n1); - } - if (res != 0) - r->tab[0] |= 1; - r->sign = 0; - r->expn = (a->expn + 1) >> 1; - ret = bf_round(r, prec, flags); - } - return ret; - fail: - bf_set_nan(r); - return BF_ST_MEM_ERROR; -} - -static no_inline int bf_op2(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, bf_op2_func_t *func) -{ - bf_t tmp; - int ret; - - if (r == a || r == b) { - bf_init(r->ctx, &tmp); - ret = func(&tmp, a, b, prec, flags); - bf_move(r, &tmp); - } else { - ret = func(r, a, b, prec, flags); - } - return ret; -} - -int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2(r, a, b, prec, flags, __bf_add); -} - -int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2(r, a, b, prec, flags, __bf_sub); -} - -int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2(r, a, b, prec, flags, __bf_div); -} - -int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, - bf_flags_t flags) -{ - bf_t b; - int ret; - bf_init(r->ctx, &b); - ret = bf_set_ui(&b, b1); - ret |= bf_mul(r, a, &b, prec, flags); - bf_delete(&b); - return ret; -} - -int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, - bf_flags_t flags) -{ - bf_t b; - int ret; - bf_init(r->ctx, &b); - ret = bf_set_si(&b, b1); - ret |= bf_mul(r, a, &b, prec, flags); - bf_delete(&b); - return ret; -} - -int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, - bf_flags_t flags) -{ - bf_t b; - int ret; - - bf_init(r->ctx, &b); - ret = bf_set_si(&b, b1); - ret |= bf_add(r, a, &b, prec, flags); - bf_delete(&b); - return ret; -} - -static int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec, - bf_flags_t flags) -{ - int ret, n_bits, i; - - assert(r != a); - if (b == 0) - return bf_set_ui(r, 1); - ret = bf_set(r, a); - n_bits = LIMB_BITS - clz(b); - for(i = n_bits - 2; i >= 0; i--) { - ret |= bf_mul(r, r, r, prec, flags); - if ((b >> i) & 1) - ret |= bf_mul(r, r, a, prec, flags); - } - return ret; -} - -static int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b, - limb_t prec, bf_flags_t flags) -{ - bf_t a; - int ret; - - if (a1 == 10 && b <= LIMB_DIGITS) { - /* use precomputed powers. We do not round at this point - because we expect the caller to do it */ - ret = bf_set_ui(r, mp_pow_dec[b]); - } else { - bf_init(r->ctx, &a); - ret = bf_set_ui(&a, a1); - ret |= bf_pow_ui(r, &a, b, prec, flags); - bf_delete(&a); - } - return ret; -} - -/* convert to integer (infinite precision) */ -int bf_rint(bf_t *r, int rnd_mode) -{ - return bf_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); -} - -/* logical operations */ -#define BF_LOGIC_OR 0 -#define BF_LOGIC_XOR 1 -#define BF_LOGIC_AND 2 - -static inline limb_t bf_logic_op1(limb_t a, limb_t b, int op) -{ - switch(op) { - case BF_LOGIC_OR: - return a | b; - case BF_LOGIC_XOR: - return a ^ b; - default: - case BF_LOGIC_AND: - return a & b; - } -} - -static int bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op) -{ - bf_t b1_s, a1_s, *a, *b; - limb_t a_sign, b_sign, r_sign; - slimb_t l, i, a_bit_offset, b_bit_offset; - limb_t v1, v2, v1_mask, v2_mask, r_mask; - int ret; - - assert(r != a1 && r != b1); - - if (a1->expn <= 0) - a_sign = 0; /* minus zero is considered as positive */ - else - a_sign = a1->sign; - - if (b1->expn <= 0) - b_sign = 0; /* minus zero is considered as positive */ - else - b_sign = b1->sign; - - if (a_sign) { - a = &a1_s; - bf_init(r->ctx, a); - if (bf_add_si(a, a1, 1, BF_PREC_INF, BF_RNDZ)) { - b = NULL; - goto fail; - } - } else { - a = (bf_t *)a1; - } - - if (b_sign) { - b = &b1_s; - bf_init(r->ctx, b); - if (bf_add_si(b, b1, 1, BF_PREC_INF, BF_RNDZ)) - goto fail; - } else { - b = (bf_t *)b1; - } - - r_sign = bf_logic_op1(a_sign, b_sign, op); - if (op == BF_LOGIC_AND && r_sign == 0) { - /* no need to compute extra zeros for and */ - if (a_sign == 0 && b_sign == 0) - l = bf_min(a->expn, b->expn); - else if (a_sign == 0) - l = a->expn; - else - l = b->expn; - } else { - l = bf_max(a->expn, b->expn); - } - /* Note: a or b can be zero */ - l = (bf_max(l, 1) + LIMB_BITS - 1) / LIMB_BITS; - if (bf_resize(r, l)) - goto fail; - a_bit_offset = a->len * LIMB_BITS - a->expn; - b_bit_offset = b->len * LIMB_BITS - b->expn; - v1_mask = -a_sign; - v2_mask = -b_sign; - r_mask = -r_sign; - for(i = 0; i < l; i++) { - v1 = get_bits(a->tab, a->len, a_bit_offset + i * LIMB_BITS) ^ v1_mask; - v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS) ^ v2_mask; - r->tab[i] = bf_logic_op1(v1, v2, op) ^ r_mask; - } - r->expn = l * LIMB_BITS; - r->sign = r_sign; - bf_normalize_and_round(r, BF_PREC_INF, BF_RNDZ); /* cannot fail */ - if (r_sign) { - if (bf_add_si(r, r, -1, BF_PREC_INF, BF_RNDZ)) - goto fail; - } - ret = 0; - done: - if (a == &a1_s) - bf_delete(a); - if (b == &b1_s) - bf_delete(b); - return ret; - fail: - bf_set_nan(r); - ret = BF_ST_MEM_ERROR; - goto done; -} - -/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ -int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b) -{ - return bf_logic_op(r, a, b, BF_LOGIC_OR); -} - -/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ -int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b) -{ - return bf_logic_op(r, a, b, BF_LOGIC_XOR); -} - -/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ -int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b) -{ - return bf_logic_op(r, a, b, BF_LOGIC_AND); -} - -/* conversion between fixed size types */ - -typedef union { - double d; - uint64_t u; -} Float64Union; - -int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode) -{ - Float64Union u; - int e, ret; - uint64_t m; - - ret = 0; - if (a->expn == BF_EXP_NAN) { - u.u = 0x7ff8000000000000; /* quiet nan */ - } else { - bf_t b_s, *b = &b_s; - - bf_init(a->ctx, b); - bf_set(b, a); - if (bf_is_finite(b)) { - ret = bf_round(b, 53, rnd_mode | BF_FLAG_SUBNORMAL | bf_set_exp_bits(11)); - } - if (b->expn == BF_EXP_INF) { - e = (1 << 11) - 1; - m = 0; - } else if (b->expn == BF_EXP_ZERO) { - e = 0; - m = 0; - } else { - e = b->expn + 1023 - 1; -#if LIMB_BITS == 32 - if (b->len == 2) { - m = ((uint64_t)b->tab[1] << 32) | b->tab[0]; - } else { - m = ((uint64_t)b->tab[0] << 32); - } -#else - m = b->tab[0]; -#endif - if (e <= 0) { - /* subnormal */ - m = m >> (12 - e); - e = 0; - } else { - m = (m << 1) >> 12; - } - } - u.u = m | ((uint64_t)e << 52) | ((uint64_t)b->sign << 63); - bf_delete(b); - } - *pres = u.d; - return ret; -} - -int bf_set_float64(bf_t *a, double d) -{ - Float64Union u; - uint64_t m; - int shift, e, sgn; - - u.d = d; - sgn = u.u >> 63; - e = (u.u >> 52) & ((1 << 11) - 1); - m = u.u & (((uint64_t)1 << 52) - 1); - if (e == ((1 << 11) - 1)) { - if (m != 0) { - bf_set_nan(a); - } else { - bf_set_inf(a, sgn); - } - } else if (e == 0) { - if (m == 0) { - bf_set_zero(a, sgn); - } else { - /* subnormal number */ - m <<= 12; - shift = clz64(m); - m <<= shift; - e = -shift; - goto norm; - } - } else { - m = (m << 11) | ((uint64_t)1 << 63); - norm: - a->expn = e - 1023 + 1; -#if LIMB_BITS == 32 - if (bf_resize(a, 2)) - goto fail; - a->tab[0] = m; - a->tab[1] = m >> 32; -#else - if (bf_resize(a, 1)) - goto fail; - a->tab[0] = m; -#endif - a->sign = sgn; - } - return 0; -fail: - bf_set_nan(a); - return BF_ST_MEM_ERROR; -} - -/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there - is an overflow and 0 otherwise. */ -int bf_get_int32(int *pres, const bf_t *a, int flags) -{ - uint32_t v; - int ret; - if (a->expn >= BF_EXP_INF) { - ret = BF_ST_INVALID_OP; - if (flags & BF_GET_INT_MOD) { - v = 0; - } else if (a->expn == BF_EXP_INF) { - v = (uint32_t)INT32_MAX + a->sign; - } else { - v = INT32_MAX; - } - } else if (a->expn <= 0) { - v = 0; - ret = 0; - } else if (a->expn <= 31) { - v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); - if (a->sign) - v = -v; - ret = 0; - } else if (!(flags & BF_GET_INT_MOD)) { - ret = BF_ST_INVALID_OP; - if (a->sign) { - v = (uint32_t)INT32_MAX + 1; - if (a->expn == 32 && - (a->tab[a->len - 1] >> (LIMB_BITS - 32)) == v) { - ret = 0; - } - } else { - v = INT32_MAX; - } - } else { - v = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn); - if (a->sign) - v = -v; - ret = 0; - } - *pres = v; - return ret; -} - -/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there - is an overflow and 0 otherwise. */ -int bf_get_int64(int64_t *pres, const bf_t *a, int flags) -{ - uint64_t v; - int ret; - if (a->expn >= BF_EXP_INF) { - ret = BF_ST_INVALID_OP; - if (flags & BF_GET_INT_MOD) { - v = 0; - } else if (a->expn == BF_EXP_INF) { - v = (uint64_t)INT64_MAX + a->sign; - } else { - v = INT64_MAX; - } - } else if (a->expn <= 0) { - v = 0; - ret = 0; - } else if (a->expn <= 63) { -#if LIMB_BITS == 32 - if (a->expn <= 32) - v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); - else - v = (((uint64_t)a->tab[a->len - 1] << 32) | - get_limbz(a, a->len - 2)) >> (64 - a->expn); -#else - v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); -#endif - if (a->sign) - v = -v; - ret = 0; - } else if (!(flags & BF_GET_INT_MOD)) { - ret = BF_ST_INVALID_OP; - if (a->sign) { - uint64_t v1; - v = (uint64_t)INT64_MAX + 1; - if (a->expn == 64) { - v1 = a->tab[a->len - 1]; -#if LIMB_BITS == 32 - v1 = (v1 << 32) | get_limbz(a, a->len - 2); -#endif - if (v1 == v) - ret = 0; - } - } else { - v = INT64_MAX; - } - } else { - slimb_t bit_pos = a->len * LIMB_BITS - a->expn; - v = get_bits(a->tab, a->len, bit_pos); -#if LIMB_BITS == 32 - v |= (uint64_t)get_bits(a->tab, a->len, bit_pos + 32) << 32; -#endif - if (a->sign) - v = -v; - ret = 0; - } - *pres = v; - return ret; -} - -/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there - is an overflow and 0 otherwise. */ -int bf_get_uint64(uint64_t *pres, const bf_t *a) -{ - uint64_t v; - int ret; - if (a->expn == BF_EXP_NAN) { - goto overflow; - } else if (a->expn <= 0) { - v = 0; - ret = 0; - } else if (a->sign) { - v = 0; - ret = BF_ST_INVALID_OP; - } else if (a->expn <= 64) { -#if LIMB_BITS == 32 - if (a->expn <= 32) - v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); - else - v = (((uint64_t)a->tab[a->len - 1] << 32) | - get_limbz(a, a->len - 2)) >> (64 - a->expn); -#else - v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); -#endif - ret = 0; - } else { - overflow: - v = UINT64_MAX; - ret = BF_ST_INVALID_OP; - } - *pres = v; - return ret; -} - -/* base conversion from radix */ - -static const uint8_t digits_per_limb_table[BF_RADIX_MAX - 1] = { -#if LIMB_BITS == 32 -32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, -#else -64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12, -#endif -}; - -static limb_t get_limb_radix(int radix) -{ - int i, k; - limb_t radixl; - - k = digits_per_limb_table[radix - 2]; - radixl = radix; - for(i = 1; i < k; i++) - radixl *= radix; - return radixl; -} - -/* return != 0 if error */ -static int bf_integer_from_radix_rec(bf_t *r, const limb_t *tab, - limb_t n, int level, limb_t n0, - limb_t radix, bf_t *pow_tab) -{ - int ret; - if (n == 1) { - ret = bf_set_ui(r, tab[0]); - } else { - bf_t T_s, *T = &T_s, *B; - limb_t n1, n2; - - n2 = (((n0 * 2) >> (level + 1)) + 1) / 2; - n1 = n - n2; - // printf("level=%d n0=%ld n1=%ld n2=%ld\n", level, n0, n1, n2); - B = &pow_tab[level]; - if (B->len == 0) { - ret = bf_pow_ui_ui(B, radix, n2, BF_PREC_INF, BF_RNDZ); - if (ret) - return ret; - } - ret = bf_integer_from_radix_rec(r, tab + n2, n1, level + 1, n0, - radix, pow_tab); - if (ret) - return ret; - ret = bf_mul(r, r, B, BF_PREC_INF, BF_RNDZ); - if (ret) - return ret; - bf_init(r->ctx, T); - ret = bf_integer_from_radix_rec(T, tab, n2, level + 1, n0, - radix, pow_tab); - if (!ret) - ret = bf_add(r, r, T, BF_PREC_INF, BF_RNDZ); - bf_delete(T); - } - return ret; - // bf_print_str(" r=", r); -} - -/* return 0 if OK != 0 if memory error */ -static int bf_integer_from_radix(bf_t *r, const limb_t *tab, - limb_t n, limb_t radix) -{ - bf_context_t *s = r->ctx; - int pow_tab_len, i, ret; - limb_t radixl; - bf_t *pow_tab; - - radixl = get_limb_radix(radix); - pow_tab_len = ceil_log2(n) + 2; /* XXX: check */ - pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len); - if (!pow_tab) - return -1; - for(i = 0; i < pow_tab_len; i++) - bf_init(r->ctx, &pow_tab[i]); - ret = bf_integer_from_radix_rec(r, tab, n, 0, n, radixl, pow_tab); - for(i = 0; i < pow_tab_len; i++) { - bf_delete(&pow_tab[i]); - } - bf_free(s, pow_tab); - return ret; -} - -/* compute and round T * radix^expn. */ -int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, - slimb_t expn, limb_t prec, bf_flags_t flags) -{ - int ret, expn_sign, overflow; - slimb_t e, extra_bits, prec1, ziv_extra_bits; - bf_t B_s, *B = &B_s; - - if (T->len == 0) { - return bf_set(r, T); - } else if (expn == 0) { - ret = bf_set(r, T); - ret |= bf_round(r, prec, flags); - return ret; - } - - e = expn; - expn_sign = 0; - if (e < 0) { - e = -e; - expn_sign = 1; - } - bf_init(r->ctx, B); - if (prec == BF_PREC_INF) { - /* infinite precision: only used if the result is known to be exact */ - ret = bf_pow_ui_ui(B, radix, e, BF_PREC_INF, BF_RNDN); - if (expn_sign) { - ret |= bf_div(r, T, B, T->len * LIMB_BITS, BF_RNDN); - } else { - ret |= bf_mul(r, T, B, BF_PREC_INF, BF_RNDN); - } - } else { - ziv_extra_bits = 16; - for(;;) { - prec1 = prec + ziv_extra_bits; - /* XXX: correct overflow/underflow handling */ - /* XXX: rigorous error analysis needed */ - extra_bits = ceil_log2(e) * 2 + 1; - ret = bf_pow_ui_ui(B, radix, e, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); - overflow = !bf_is_finite(B); - /* XXX: if bf_pow_ui_ui returns an exact result, can stop - after the next operation */ - if (expn_sign) - ret |= bf_div(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); - else - ret |= bf_mul(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); - if (ret & BF_ST_MEM_ERROR) - break; - if ((ret & BF_ST_INEXACT) && - !bf_can_round(r, prec, flags & BF_RND_MASK, prec1) && - !overflow) { - /* and more precision and retry */ - ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); - } else { - /* XXX: need to use __bf_round() to pass the inexact - flag for the subnormal case */ - ret = bf_round(r, prec, flags) | (ret & BF_ST_INEXACT); - break; - } - } - } - bf_delete(B); - return ret; -} - -static inline int bf_to_digit(int c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - else if (c >= 'A' && c <= 'Z') - return c - 'A' + 10; - else if (c >= 'a' && c <= 'z') - return c - 'a' + 10; - else - return 36; -} - -/* add a limb at 'pos' and decrement pos. new space is created if - needed. Return 0 if OK, -1 if memory error */ -static int bf_add_limb(bf_t *a, slimb_t *ppos, limb_t v) -{ - slimb_t pos; - pos = *ppos; - if (unlikely(pos < 0)) { - limb_t new_size, d, *new_tab; - new_size = bf_max(a->len + 1, a->len * 3 / 2); - new_tab = bf_realloc(a->ctx, a->tab, sizeof(limb_t) * new_size); - if (!new_tab) - return -1; - a->tab = new_tab; - d = new_size - a->len; - memmove(a->tab + d, a->tab, a->len * sizeof(limb_t)); - a->len = new_size; - pos += d; - } - a->tab[pos--] = v; - *ppos = pos; - return 0; -} - -static int bf_tolower(int c) -{ - if (c >= 'A' && c <= 'Z') - c = c - 'A' + 'a'; - return c; -} - -static int strcasestart(const char *str, const char *val, const char **ptr) -{ - const char *p, *q; - p = str; - q = val; - while (*q != '\0') { - if (bf_tolower(*p) != *q) - return 0; - p++; - q++; - } - if (ptr) - *ptr = p; - return 1; -} - -static int bf_atof_internal(bf_t *r, slimb_t *pexponent, - const char *str, const char **pnext, int radix, - limb_t prec, bf_flags_t flags, bool is_dec) -{ - const char *p, *p_start; - int is_neg, radix_bits, exp_is_neg, ret, digits_per_limb, shift; - limb_t cur_limb; - slimb_t pos, expn, int_len, digit_count; - bool has_decpt, is_bin_exp; - bf_t a_s, *a; - - *pexponent = 0; - p = str; - if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 && - strcasestart(p, "nan", &p)) { - bf_set_nan(r); - ret = 0; - goto done; - } - is_neg = 0; - - if (p[0] == '+') { - p++; - p_start = p; - } else if (p[0] == '-') { - is_neg = 1; - p++; - p_start = p; - } else { - p_start = p; - } - if (p[0] == '0') { - if ((p[1] == 'x' || p[1] == 'X') && - (radix == 0 || radix == 16) && - !(flags & BF_ATOF_NO_HEX)) { - radix = 16; - p += 2; - } else if ((p[1] == 'o' || p[1] == 'O') && - radix == 0 && (flags & BF_ATOF_BIN_OCT)) { - p += 2; - radix = 8; - } else if ((p[1] == 'b' || p[1] == 'B') && - radix == 0 && (flags & BF_ATOF_BIN_OCT)) { - p += 2; - radix = 2; - } else { - goto no_prefix; - } - /* there must be a digit after the prefix */ - if (bf_to_digit((uint8_t)*p) >= radix) { - bf_set_nan(r); - ret = 0; - goto done; - } - no_prefix: ; - } else { - if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 && - strcasestart(p, "inf", &p)) { - bf_set_inf(r, is_neg); - ret = 0; - goto done; - } - } - - if (radix == 0) - radix = 10; - if (is_dec) { - assert(radix == 10); - radix_bits = 0; - a = r; - } else if ((radix & (radix - 1)) != 0) { - radix_bits = 0; /* base is not a power of two */ - a = &a_s; - bf_init(r->ctx, a); - } else { - radix_bits = ceil_log2(radix); - a = r; - } - - /* skip leading zeros */ - /* XXX: could also skip zeros after the decimal point */ - while (*p == '0') - p++; - - if (radix_bits) { - shift = digits_per_limb = LIMB_BITS; - } else { - radix_bits = 0; - shift = digits_per_limb = digits_per_limb_table[radix - 2]; - } - cur_limb = 0; - bf_resize(a, 1); - pos = 0; - has_decpt = false; - int_len = digit_count = 0; - for(;;) { - limb_t c; - if (*p == '.' && (p > p_start || bf_to_digit(p[1]) < radix)) { - if (has_decpt) - break; - has_decpt = true; - int_len = digit_count; - p++; - } - c = bf_to_digit(*p); - if (c >= radix) - break; - digit_count++; - p++; - if (radix_bits) { - shift -= radix_bits; - if (shift <= 0) { - cur_limb |= c >> (-shift); - if (bf_add_limb(a, &pos, cur_limb)) - goto mem_error; - if (shift < 0) - cur_limb = c << (LIMB_BITS + shift); - else - cur_limb = 0; - shift += LIMB_BITS; - } else { - cur_limb |= c << shift; - } - } else { - cur_limb = cur_limb * radix + c; - shift--; - if (shift == 0) { - if (bf_add_limb(a, &pos, cur_limb)) - goto mem_error; - shift = digits_per_limb; - cur_limb = 0; - } - } - } - if (!has_decpt) - int_len = digit_count; - - /* add the last limb and pad with zeros */ - if (shift != digits_per_limb) { - if (radix_bits == 0) { - while (shift != 0) { - cur_limb *= radix; - shift--; - } - } - if (bf_add_limb(a, &pos, cur_limb)) { - mem_error: - ret = BF_ST_MEM_ERROR; - if (!radix_bits) - bf_delete(a); - bf_set_nan(r); - goto done; - } - } - - /* reset the next limbs to zero (we prefer to reallocate in the - renormalization) */ - memset(a->tab, 0, (pos + 1) * sizeof(limb_t)); - - if (p == p_start) { - ret = 0; - if (!radix_bits) - bf_delete(a); - bf_set_nan(r); - goto done; - } - - /* parse the exponent, if any */ - expn = 0; - is_bin_exp = false; - if (((radix == 10 && (*p == 'e' || *p == 'E')) || - (radix != 10 && (*p == '@' || - (radix_bits && (*p == 'p' || *p == 'P'))))) && - p > p_start) { - is_bin_exp = (*p == 'p' || *p == 'P'); - p++; - exp_is_neg = 0; - if (*p == '+') { - p++; - } else if (*p == '-') { - exp_is_neg = 1; - p++; - } - for(;;) { - int c; - c = bf_to_digit(*p); - if (c >= 10) - break; - if (unlikely(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) { - /* exponent overflow */ - if (exp_is_neg) { - bf_set_zero(r, is_neg); - ret = BF_ST_UNDERFLOW | BF_ST_INEXACT; - } else { - bf_set_inf(r, is_neg); - ret = BF_ST_OVERFLOW | BF_ST_INEXACT; - } - goto done; - } - p++; - expn = expn * 10 + c; - } - if (exp_is_neg) - expn = -expn; - } - if (is_dec) { - a->expn = expn + int_len; - a->sign = is_neg; - ret = bfdec_normalize_and_round((bfdec_t *)a, prec, flags); - } else if (radix_bits) { - /* XXX: may overflow */ - if (!is_bin_exp) - expn *= radix_bits; - a->expn = expn + (int_len * radix_bits); - a->sign = is_neg; - ret = bf_normalize_and_round(a, prec, flags); - } else { - limb_t l; - pos++; - l = a->len - pos; /* number of limbs */ - if (l == 0) { - bf_set_zero(r, is_neg); - ret = 0; - } else { - bf_t T_s, *T = &T_s; - - expn -= l * digits_per_limb - int_len; - bf_init(r->ctx, T); - if (bf_integer_from_radix(T, a->tab + pos, l, radix)) { - bf_set_nan(r); - ret = BF_ST_MEM_ERROR; - } else { - T->sign = is_neg; - if (flags & BF_ATOF_EXPONENT) { - /* return the exponent */ - *pexponent = expn; - ret = bf_set(r, T); - } else { - ret = bf_mul_pow_radix(r, T, radix, expn, prec, flags); - } - } - bf_delete(T); - } - bf_delete(a); - } - done: - if (pnext) - *pnext = p; - return ret; -} - -/* - Return (status, n, exp). 'status' is the floating point status. 'n' - is the parsed number. - - If (flags & BF_ATOF_EXPONENT) and if the radix is not a power of - two, the parsed number is equal to r * - (*pexponent)^radix. Otherwise *pexponent = 0. -*/ -int bf_atof2(bf_t *r, slimb_t *pexponent, - const char *str, const char **pnext, int radix, - limb_t prec, bf_flags_t flags) -{ - return bf_atof_internal(r, pexponent, str, pnext, radix, prec, flags, - false); -} - -int bf_atof(bf_t *r, const char *str, const char **pnext, int radix, - limb_t prec, bf_flags_t flags) -{ - slimb_t dummy_exp; - return bf_atof_internal(r, &dummy_exp, str, pnext, radix, prec, flags, false); -} - -/* base conversion to radix */ - -#if LIMB_BITS == 64 -#define RADIXL_10 UINT64_C(10000000000000000000) -#else -#define RADIXL_10 UINT64_C(1000000000) -#endif - -static const uint32_t inv_log2_radix[BF_RADIX_MAX - 1][LIMB_BITS / 32 + 1] = { -#if LIMB_BITS == 32 -{ 0x80000000, 0x00000000,}, -{ 0x50c24e60, 0xd4d4f4a7,}, -{ 0x40000000, 0x00000000,}, -{ 0x372068d2, 0x0a1ee5ca,}, -{ 0x3184648d, 0xb8153e7a,}, -{ 0x2d983275, 0x9d5369c4,}, -{ 0x2aaaaaaa, 0xaaaaaaab,}, -{ 0x28612730, 0x6a6a7a54,}, -{ 0x268826a1, 0x3ef3fde6,}, -{ 0x25001383, 0xbac8a744,}, -{ 0x23b46706, 0x82c0c709,}, -{ 0x229729f1, 0xb2c83ded,}, -{ 0x219e7ffd, 0xa5ad572b,}, -{ 0x20c33b88, 0xda7c29ab,}, -{ 0x20000000, 0x00000000,}, -{ 0x1f50b57e, 0xac5884b3,}, -{ 0x1eb22cc6, 0x8aa6e26f,}, -{ 0x1e21e118, 0x0c5daab2,}, -{ 0x1d9dcd21, 0x439834e4,}, -{ 0x1d244c78, 0x367a0d65,}, -{ 0x1cb40589, 0xac173e0c,}, -{ 0x1c4bd95b, 0xa8d72b0d,}, -{ 0x1bead768, 0x98f8ce4c,}, -{ 0x1b903469, 0x050f72e5,}, -{ 0x1b3b433f, 0x2eb06f15,}, -{ 0x1aeb6f75, 0x9c46fc38,}, -{ 0x1aa038eb, 0x0e3bfd17,}, -{ 0x1a593062, 0xb38d8c56,}, -{ 0x1a15f4c3, 0x2b95a2e6,}, -{ 0x19d630dc, 0xcc7ddef9,}, -{ 0x19999999, 0x9999999a,}, -{ 0x195fec80, 0x8a609431,}, -{ 0x1928ee7b, 0x0b4f22f9,}, -{ 0x18f46acf, 0x8c06e318,}, -{ 0x18c23246, 0xdc0a9f3d,}, -#else -{ 0x80000000, 0x00000000, 0x00000000,}, -{ 0x50c24e60, 0xd4d4f4a7, 0x021f57bc,}, -{ 0x40000000, 0x00000000, 0x00000000,}, -{ 0x372068d2, 0x0a1ee5ca, 0x19ea911b,}, -{ 0x3184648d, 0xb8153e7a, 0x7fc2d2e1,}, -{ 0x2d983275, 0x9d5369c4, 0x4dec1661,}, -{ 0x2aaaaaaa, 0xaaaaaaaa, 0xaaaaaaab,}, -{ 0x28612730, 0x6a6a7a53, 0x810fabde,}, -{ 0x268826a1, 0x3ef3fde6, 0x23e2566b,}, -{ 0x25001383, 0xbac8a744, 0x385a3349,}, -{ 0x23b46706, 0x82c0c709, 0x3f891718,}, -{ 0x229729f1, 0xb2c83ded, 0x15fba800,}, -{ 0x219e7ffd, 0xa5ad572a, 0xe169744b,}, -{ 0x20c33b88, 0xda7c29aa, 0x9bddee52,}, -{ 0x20000000, 0x00000000, 0x00000000,}, -{ 0x1f50b57e, 0xac5884b3, 0x70e28eee,}, -{ 0x1eb22cc6, 0x8aa6e26f, 0x06d1a2a2,}, -{ 0x1e21e118, 0x0c5daab1, 0x81b4f4bf,}, -{ 0x1d9dcd21, 0x439834e3, 0x81667575,}, -{ 0x1d244c78, 0x367a0d64, 0xc8204d6d,}, -{ 0x1cb40589, 0xac173e0c, 0x3b7b16ba,}, -{ 0x1c4bd95b, 0xa8d72b0d, 0x5879f25a,}, -{ 0x1bead768, 0x98f8ce4c, 0x66cc2858,}, -{ 0x1b903469, 0x050f72e5, 0x0cf5488e,}, -{ 0x1b3b433f, 0x2eb06f14, 0x8c89719c,}, -{ 0x1aeb6f75, 0x9c46fc37, 0xab5fc7e9,}, -{ 0x1aa038eb, 0x0e3bfd17, 0x1bd62080,}, -{ 0x1a593062, 0xb38d8c56, 0x7998ab45,}, -{ 0x1a15f4c3, 0x2b95a2e6, 0x46aed6a0,}, -{ 0x19d630dc, 0xcc7ddef9, 0x5aadd61b,}, -{ 0x19999999, 0x99999999, 0x9999999a,}, -{ 0x195fec80, 0x8a609430, 0xe1106014,}, -{ 0x1928ee7b, 0x0b4f22f9, 0x5f69791d,}, -{ 0x18f46acf, 0x8c06e318, 0x4d2aeb2c,}, -{ 0x18c23246, 0xdc0a9f3d, 0x3fe16970,}, -#endif -}; - -static const limb_t log2_radix[BF_RADIX_MAX - 1] = { -#if LIMB_BITS == 32 -0x20000000, -0x32b80347, -0x40000000, -0x4a4d3c26, -0x52b80347, -0x59d5d9fd, -0x60000000, -0x6570068e, -0x6a4d3c26, -0x6eb3a9f0, -0x72b80347, -0x766a008e, -0x79d5d9fd, -0x7d053f6d, -0x80000000, -0x82cc7edf, -0x8570068e, -0x87ef05ae, -0x8a4d3c26, -0x8c8ddd45, -0x8eb3a9f0, -0x90c10501, -0x92b80347, -0x949a784c, -0x966a008e, -0x982809d6, -0x99d5d9fd, -0x9b74948f, -0x9d053f6d, -0x9e88c6b3, -0xa0000000, -0xa16bad37, -0xa2cc7edf, -0xa4231623, -0xa570068e, -#else -0x2000000000000000, -0x32b803473f7ad0f4, -0x4000000000000000, -0x4a4d3c25e68dc57f, -0x52b803473f7ad0f4, -0x59d5d9fd5010b366, -0x6000000000000000, -0x6570068e7ef5a1e8, -0x6a4d3c25e68dc57f, -0x6eb3a9f01975077f, -0x72b803473f7ad0f4, -0x766a008e4788cbcd, -0x79d5d9fd5010b366, -0x7d053f6d26089673, -0x8000000000000000, -0x82cc7edf592262d0, -0x8570068e7ef5a1e8, -0x87ef05ae409a0289, -0x8a4d3c25e68dc57f, -0x8c8ddd448f8b845a, -0x8eb3a9f01975077f, -0x90c10500d63aa659, -0x92b803473f7ad0f4, -0x949a784bcd1b8afe, -0x966a008e4788cbcd, -0x982809d5be7072dc, -0x99d5d9fd5010b366, -0x9b74948f5532da4b, -0x9d053f6d26089673, -0x9e88c6b3626a72aa, -0xa000000000000000, -0xa16bad3758efd873, -0xa2cc7edf592262d0, -0xa4231623369e78e6, -0xa570068e7ef5a1e8, -#endif -}; - -/* compute floor(a*b) or ceil(a*b) with b = log2(radix) or - b=1/log2(radix). For is_inv = 0, strict accuracy is not guaranteed - when radix is not a power of two. */ -slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, - int is_ceil1) -{ - int is_neg; - limb_t a; - bool is_ceil; - - is_ceil = is_ceil1; - a = a1; - if (a1 < 0) { - a = -a; - is_neg = 1; - } else { - is_neg = 0; - } - is_ceil ^= is_neg; - if ((radix & (radix - 1)) == 0) { - int radix_bits; - /* radix is a power of two */ - radix_bits = ceil_log2(radix); - if (is_inv) { - if (is_ceil) - a += radix_bits - 1; - a = a / radix_bits; - } else { - a = a * radix_bits; - } - } else { - const uint32_t *tab; - limb_t b0, b1; - dlimb_t t; - - if (is_inv) { - tab = inv_log2_radix[radix - 2]; -#if LIMB_BITS == 32 - b1 = tab[0]; - b0 = tab[1]; -#else - b1 = ((limb_t)tab[0] << 32) | tab[1]; - b0 = (limb_t)tab[2] << 32; -#endif - t = (dlimb_t)b0 * (dlimb_t)a; - t = (dlimb_t)b1 * (dlimb_t)a + (t >> LIMB_BITS); - a = t >> (LIMB_BITS - 1); - } else { - b0 = log2_radix[radix - 2]; - t = (dlimb_t)b0 * (dlimb_t)a; - a = t >> (LIMB_BITS - 3); - } - /* a = floor(result) and 'result' cannot be an integer */ - a += is_ceil; - } - if (is_neg) - a = -a; - return a; -} - -/* 'n' is the number of output limbs */ -static int bf_integer_to_radix_rec(bf_t *pow_tab, - limb_t *out, const bf_t *a, limb_t n, - int level, limb_t n0, limb_t radixl, - unsigned int radixl_bits) -{ - limb_t n1, n2, q_prec; - int ret; - - assert(n >= 1); - if (n == 1) { - out[0] = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn); - } else if (n == 2) { - dlimb_t t; - slimb_t pos; - pos = a->len * LIMB_BITS - a->expn; - t = ((dlimb_t)get_bits(a->tab, a->len, pos + LIMB_BITS) << LIMB_BITS) | - get_bits(a->tab, a->len, pos); - if (likely(radixl == RADIXL_10)) { - /* use division by a constant when possible */ - out[0] = t % RADIXL_10; - out[1] = t / RADIXL_10; - } else { - out[0] = t % radixl; - out[1] = t / radixl; - } - } else { - bf_t Q, R, *B, *B_inv; - int q_add; - bf_init(a->ctx, &Q); - bf_init(a->ctx, &R); - n2 = (((n0 * 2) >> (level + 1)) + 1) / 2; - n1 = n - n2; - B = &pow_tab[2 * level]; - B_inv = &pow_tab[2 * level + 1]; - ret = 0; - if (B->len == 0) { - /* compute BASE^n2 */ - ret |= bf_pow_ui_ui(B, radixl, n2, BF_PREC_INF, BF_RNDZ); - /* we use enough bits for the maximum possible 'n1' value, - i.e. n2 + 1 */ - ret |= bf_set_ui(&R, 1); - ret |= bf_div(B_inv, &R, B, (n2 + 1) * radixl_bits + 2, BF_RNDN); - } - // printf("%d: n1=% " PRId64 " n2=%" PRId64 "\n", level, n1, n2); - q_prec = n1 * radixl_bits; - ret |= bf_mul(&Q, a, B_inv, q_prec, BF_RNDN); - ret |= bf_rint(&Q, BF_RNDZ); - - ret |= bf_mul(&R, &Q, B, BF_PREC_INF, BF_RNDZ); - ret |= bf_sub(&R, a, &R, BF_PREC_INF, BF_RNDZ); - - if (ret & BF_ST_MEM_ERROR) - goto fail; - /* adjust if necessary */ - q_add = 0; - while (R.sign && R.len != 0) { - if (bf_add(&R, &R, B, BF_PREC_INF, BF_RNDZ)) - goto fail; - q_add--; - } - while (bf_cmpu(&R, B) >= 0) { - if (bf_sub(&R, &R, B, BF_PREC_INF, BF_RNDZ)) - goto fail; - q_add++; - } - if (q_add != 0) { - if (bf_add_si(&Q, &Q, q_add, BF_PREC_INF, BF_RNDZ)) - goto fail; - } - if (bf_integer_to_radix_rec(pow_tab, out + n2, &Q, n1, level + 1, n0, - radixl, radixl_bits)) - goto fail; - if (bf_integer_to_radix_rec(pow_tab, out, &R, n2, level + 1, n0, - radixl, radixl_bits)) { - fail: - bf_delete(&Q); - bf_delete(&R); - return -1; - } - bf_delete(&Q); - bf_delete(&R); - } - return 0; -} - -/* return 0 if OK != 0 if memory error */ -static int bf_integer_to_radix(bf_t *r, const bf_t *a, limb_t radixl) -{ - bf_context_t *s = r->ctx; - limb_t r_len; - bf_t *pow_tab; - int i, pow_tab_len, ret; - - r_len = r->len; - pow_tab_len = (ceil_log2(r_len) + 2) * 2; /* XXX: check */ - pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len); - if (!pow_tab) - return -1; - for(i = 0; i < pow_tab_len; i++) - bf_init(r->ctx, &pow_tab[i]); - - ret = bf_integer_to_radix_rec(pow_tab, r->tab, a, r_len, 0, r_len, radixl, - ceil_log2(radixl)); - - for(i = 0; i < pow_tab_len; i++) { - bf_delete(&pow_tab[i]); - } - bf_free(s, pow_tab); - return ret; -} - -/* a must be >= 0. 'P' is the wanted number of digits in radix - 'radix'. 'r' is the mantissa represented as an integer. *pE - contains the exponent. Return != 0 if memory error. */ -static int bf_convert_to_radix(bf_t *r, slimb_t *pE, - const bf_t *a, int radix, - limb_t P, bf_rnd_t rnd_mode, - bool is_fixed_exponent) -{ - slimb_t E, e, prec, extra_bits, ziv_extra_bits, prec0; - bf_t B_s, *B = &B_s; - int e_sign, ret, res; - - if (a->len == 0) { - /* zero case */ - *pE = 0; - return bf_set(r, a); - } - - if (is_fixed_exponent) { - E = *pE; - } else { - /* compute the new exponent */ - E = 1 + bf_mul_log2_radix(a->expn - 1, radix, true, false); - } - // bf_print_str("a", a); - // printf("E=%ld P=%ld radix=%d\n", E, P, radix); - - for(;;) { - e = P - E; - e_sign = 0; - if (e < 0) { - e = -e; - e_sign = 1; - } - /* Note: precision for log2(radix) is not critical here */ - prec0 = bf_mul_log2_radix(P, radix, false, true); - ziv_extra_bits = 16; - for(;;) { - prec = prec0 + ziv_extra_bits; - /* XXX: rigorous error analysis needed */ - extra_bits = ceil_log2(e) * 2 + 1; - ret = bf_pow_ui_ui(r, radix, e, prec + extra_bits, - BF_RNDN | BF_FLAG_EXT_EXP); - if (!e_sign) - ret |= bf_mul(r, r, a, prec + extra_bits, - BF_RNDN | BF_FLAG_EXT_EXP); - else - ret |= bf_div(r, a, r, prec + extra_bits, - BF_RNDN | BF_FLAG_EXT_EXP); - if (ret & BF_ST_MEM_ERROR) - return BF_ST_MEM_ERROR; - /* if the result is not exact, check that it can be safely - rounded to an integer */ - if ((ret & BF_ST_INEXACT) && - !bf_can_round(r, r->expn, rnd_mode, prec)) { - /* and more precision and retry */ - ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); - continue; - } else { - ret = bf_rint(r, rnd_mode); - if (ret & BF_ST_MEM_ERROR) - return BF_ST_MEM_ERROR; - break; - } - } - if (is_fixed_exponent) - break; - /* check that the result is < B^P */ - /* XXX: do a fast approximate test first ? */ - bf_init(r->ctx, B); - ret = bf_pow_ui_ui(B, radix, P, BF_PREC_INF, BF_RNDZ); - if (ret) { - bf_delete(B); - return ret; - } - res = bf_cmpu(r, B); - bf_delete(B); - if (res < 0) - break; - /* try a larger exponent */ - E++; - } - *pE = E; - return 0; -} - -static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len) -{ - int digit, i; - - if (radix == 10) { - /* specific case with constant divisor */ - for(i = len - 1; i >= 0; i--) { - digit = (limb_t)n % 10; - n = (limb_t)n / 10; - buf[i] = digit + '0'; - } - } else { - for(i = len - 1; i >= 0; i--) { - digit = (limb_t)n % radix; - n = (limb_t)n / radix; - if (digit < 10) - digit += '0'; - else - digit += 'a' - 10; - buf[i] = digit; - } - } -} - -/* for power of 2 radixes */ -static void limb_to_a2(char *buf, limb_t n, unsigned int radix_bits, int len) -{ - int digit, i; - unsigned int mask; - - mask = (1 << radix_bits) - 1; - for(i = len - 1; i >= 0; i--) { - digit = n & mask; - n >>= radix_bits; - if (digit < 10) - digit += '0'; - else - digit += 'a' - 10; - buf[i] = digit; - } -} - -/* 'a' must be an integer if the is_dec = false or if the radix is not - a power of two. A dot is added before the 'dot_pos' digit. dot_pos - = n_digits does not display the dot. 0 <= dot_pos <= - n_digits. n_digits >= 1. */ -static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits, - limb_t dot_pos, bool is_dec) -{ - limb_t i, v, l; - slimb_t pos, pos_incr; - int digits_per_limb, buf_pos, radix_bits, first_buf_pos; - char buf[65]; - bf_t a_s, *a; - - if (is_dec) { - digits_per_limb = LIMB_DIGITS; - a = (bf_t *)a1; - radix_bits = 0; - pos = a->len; - pos_incr = 1; - first_buf_pos = 0; - } else if ((radix & (radix - 1)) == 0) { - a = (bf_t *)a1; - radix_bits = ceil_log2(radix); - digits_per_limb = LIMB_BITS / radix_bits; - pos_incr = digits_per_limb * radix_bits; - /* digits are aligned relative to the radix point */ - pos = a->len * LIMB_BITS + smod(-a->expn, radix_bits); - first_buf_pos = 0; - } else { - limb_t n, radixl; - - digits_per_limb = digits_per_limb_table[radix - 2]; - radixl = get_limb_radix(radix); - a = &a_s; - bf_init(a1->ctx, a); - n = (n_digits + digits_per_limb - 1) / digits_per_limb; - if (bf_resize(a, n)) { - dbuf_set_error(s); - goto done; - } - if (bf_integer_to_radix(a, a1, radixl)) { - dbuf_set_error(s); - goto done; - } - radix_bits = 0; - pos = n; - pos_incr = 1; - first_buf_pos = pos * digits_per_limb - n_digits; - } - buf_pos = digits_per_limb; - i = 0; - while (i < n_digits) { - if (buf_pos == digits_per_limb) { - pos -= pos_incr; - if (radix_bits == 0) { - v = get_limbz(a, pos); - limb_to_a(buf, v, radix, digits_per_limb); - } else { - v = get_bits(a->tab, a->len, pos); - limb_to_a2(buf, v, radix_bits, digits_per_limb); - } - buf_pos = first_buf_pos; - first_buf_pos = 0; - } - if (i < dot_pos) { - l = dot_pos; - } else { - if (i == dot_pos) - dbuf_putc(s, '.'); - l = n_digits; - } - l = bf_min(digits_per_limb - buf_pos, l - i); - dbuf_put(s, (uint8_t *)(buf + buf_pos), l); - buf_pos += l; - i += l; - } - done: - if (a != a1) - bf_delete(a); -} - -static void *bf_dbuf_realloc(void *opaque, void *ptr, size_t size) -{ - bf_context_t *s = opaque; - return bf_realloc(s, ptr, size); -} - -/* return the length in bytes. A trailing '\0' is added */ -static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix, - limb_t prec, bf_flags_t flags, bool is_dec) -{ - bf_context_t *ctx = a2->ctx; - DynBuf s_s, *s = &s_s; - int radix_bits; - - // bf_print_str("ftoa", a2); - // printf("radix=%d\n", radix); - dbuf_init2(s, ctx, bf_dbuf_realloc); - if (a2->expn == BF_EXP_NAN) { - dbuf_putstr(s, "NaN"); - } else { - if (a2->sign) - dbuf_putc(s, '-'); - if (a2->expn == BF_EXP_INF) { - if (flags & BF_FTOA_JS_QUIRKS) - dbuf_putstr(s, "Infinity"); - else - dbuf_putstr(s, "Inf"); - } else { - int fmt, ret; - slimb_t n_digits, n, i, n_max, n1; - bf_t a1_s, *a1 = &a1_s; - - if ((radix & (radix - 1)) != 0) - radix_bits = 0; - else - radix_bits = ceil_log2(radix); - - fmt = flags & BF_FTOA_FORMAT_MASK; - bf_init(ctx, a1); - if (fmt == BF_FTOA_FORMAT_FRAC) { - if (is_dec || radix_bits != 0) { - if (bf_set(a1, a2)) - goto fail1; -#ifdef USE_BF_DEC - if (is_dec) { - if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) - goto fail1; - n = a1->expn; - } else -#endif - { - if (bf_round(a1, prec * radix_bits, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) - goto fail1; - n = ceil_div(a1->expn, radix_bits); - } - if (flags & BF_FTOA_ADD_PREFIX) { - if (radix == 16) - dbuf_putstr(s, "0x"); - else if (radix == 8) - dbuf_putstr(s, "0o"); - else if (radix == 2) - dbuf_putstr(s, "0b"); - } - if (a1->expn == BF_EXP_ZERO) { - dbuf_putstr(s, "0"); - if (prec > 0) { - dbuf_putstr(s, "."); - for(i = 0; i < prec; i++) { - dbuf_putc(s, '0'); - } - } - } else { - n_digits = prec + n; - if (n <= 0) { - /* 0.x */ - dbuf_putstr(s, "0."); - for(i = 0; i < -n; i++) { - dbuf_putc(s, '0'); - } - if (n_digits > 0) { - output_digits(s, a1, radix, n_digits, n_digits, is_dec); - } - } else { - output_digits(s, a1, radix, n_digits, n, is_dec); - } - } - } else { - size_t pos, start; - bf_t a_s, *a = &a_s; - - /* make a positive number */ - a->tab = a2->tab; - a->len = a2->len; - a->expn = a2->expn; - a->sign = 0; - - /* one more digit for the rounding */ - n = 1 + bf_mul_log2_radix(bf_max(a->expn, 0), radix, true, true); - n_digits = n + prec; - n1 = n; - if (bf_convert_to_radix(a1, &n1, a, radix, n_digits, - flags & BF_RND_MASK, true)) - goto fail1; - start = s->size; - output_digits(s, a1, radix, n_digits, n, is_dec); - /* remove leading zeros because we allocated one more digit */ - pos = start; - while ((pos + 1) < s->size && s->buf[pos] == '0' && - s->buf[pos + 1] != '.') - pos++; - if (pos > start) { - memmove(s->buf + start, s->buf + pos, s->size - pos); - s->size -= (pos - start); - } - } - } else { -#ifdef USE_BF_DEC - if (is_dec) { - if (bf_set(a1, a2)) - goto fail1; - if (fmt == BF_FTOA_FORMAT_FIXED) { - n_digits = prec; - n_max = n_digits; - if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) - goto fail1; - } else { - /* prec is ignored */ - prec = n_digits = a1->len * LIMB_DIGITS; - /* remove the trailing zero digits */ - while (n_digits > 1 && - get_digit(a1->tab, a1->len, prec - n_digits) == 0) { - n_digits--; - } - n_max = n_digits + 4; - } - n = a1->expn; - } else -#endif - if (radix_bits != 0) { - if (bf_set(a1, a2)) - goto fail1; - if (fmt == BF_FTOA_FORMAT_FIXED) { - slimb_t prec_bits; - n_digits = prec; - n_max = n_digits; - /* align to the radix point */ - prec_bits = prec * radix_bits - - smod(-a1->expn, radix_bits); - if (bf_round(a1, prec_bits, - (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) - goto fail1; - } else { - limb_t digit_mask; - slimb_t pos; - /* position of the digit before the most - significant digit in bits */ - pos = a1->len * LIMB_BITS + - smod(-a1->expn, radix_bits); - n_digits = ceil_div(pos, radix_bits); - /* remove the trailing zero digits */ - digit_mask = ((limb_t)1 << radix_bits) - 1; - while (n_digits > 1 && - (get_bits(a1->tab, a1->len, pos - n_digits * radix_bits) & digit_mask) == 0) { - n_digits--; - } - n_max = n_digits + 4; - } - n = ceil_div(a1->expn, radix_bits); - } else { - bf_t a_s, *a = &a_s; - - /* make a positive number */ - a->tab = a2->tab; - a->len = a2->len; - a->expn = a2->expn; - a->sign = 0; - - if (fmt == BF_FTOA_FORMAT_FIXED) { - n_digits = prec; - n_max = n_digits; - } else { - slimb_t n_digits_max, n_digits_min; - - assert(prec != BF_PREC_INF); - n_digits = 1 + bf_mul_log2_radix(prec, radix, true, true); - /* max number of digits for non exponential - notation. The rational is to have the same rule - as JS i.e. n_max = 21 for 64 bit float in base 10. */ - n_max = n_digits + 4; - if (fmt == BF_FTOA_FORMAT_FREE_MIN) { - bf_t b_s, *b = &b_s; - - /* find the minimum number of digits by - dichotomy. */ - /* XXX: inefficient */ - n_digits_max = n_digits; - n_digits_min = 1; - bf_init(ctx, b); - while (n_digits_min < n_digits_max) { - n_digits = (n_digits_min + n_digits_max) / 2; - if (bf_convert_to_radix(a1, &n, a, radix, n_digits, - flags & BF_RND_MASK, false)) { - bf_delete(b); - goto fail1; - } - /* convert back to a number and compare */ - ret = bf_mul_pow_radix(b, a1, radix, n - n_digits, - prec, - (flags & ~BF_RND_MASK) | - BF_RNDN); - if (ret & BF_ST_MEM_ERROR) { - bf_delete(b); - goto fail1; - } - if (bf_cmpu(b, a) == 0) { - n_digits_max = n_digits; - } else { - n_digits_min = n_digits + 1; - } - } - bf_delete(b); - n_digits = n_digits_max; - } - } - if (bf_convert_to_radix(a1, &n, a, radix, n_digits, - flags & BF_RND_MASK, false)) { - fail1: - bf_delete(a1); - goto fail; - } - } - if (a1->expn == BF_EXP_ZERO && - fmt != BF_FTOA_FORMAT_FIXED && - !(flags & BF_FTOA_FORCE_EXP)) { - /* just output zero */ - dbuf_putstr(s, "0"); - } else { - if (flags & BF_FTOA_ADD_PREFIX) { - if (radix == 16) - dbuf_putstr(s, "0x"); - else if (radix == 8) - dbuf_putstr(s, "0o"); - else if (radix == 2) - dbuf_putstr(s, "0b"); - } - if (a1->expn == BF_EXP_ZERO) - n = 1; - if ((flags & BF_FTOA_FORCE_EXP) || - n <= -6 || n > n_max) { - const char *fmt; - /* exponential notation */ - output_digits(s, a1, radix, n_digits, 1, is_dec); - if (radix_bits != 0 && radix <= 16) { - if (flags & BF_FTOA_JS_QUIRKS) - fmt = "p%+" PRId_LIMB; - else - fmt = "p%" PRId_LIMB; - dbuf_printf(s, fmt, (n - 1) * radix_bits); - } else { - if (flags & BF_FTOA_JS_QUIRKS) - fmt = "%c%+" PRId_LIMB; - else - fmt = "%c%" PRId_LIMB; - dbuf_printf(s, fmt, - radix <= 10 ? 'e' : '@', n - 1); - } - } else if (n <= 0) { - /* 0.x */ - dbuf_putstr(s, "0."); - for(i = 0; i < -n; i++) { - dbuf_putc(s, '0'); - } - output_digits(s, a1, radix, n_digits, n_digits, is_dec); - } else { - if (n_digits <= n) { - /* no dot */ - output_digits(s, a1, radix, n_digits, n_digits, is_dec); - for(i = 0; i < (n - n_digits); i++) - dbuf_putc(s, '0'); - } else { - output_digits(s, a1, radix, n_digits, n, is_dec); - } - } - } - } - bf_delete(a1); - } - } - dbuf_putc(s, '\0'); - if (dbuf_error(s)) - goto fail; - if (plen) - *plen = s->size - 1; - return (char *)s->buf; - fail: - bf_free(ctx, s->buf); - if (plen) - *plen = 0; - return NULL; -} - -char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, - bf_flags_t flags) -{ - return bf_ftoa_internal(plen, a, radix, prec, flags, false); -} - -/***************************************************************/ -/* transcendental functions */ - -/* Note: the algorithm is from MPFR */ -static void bf_const_log2_rec(bf_t *T, bf_t *P, bf_t *Q, limb_t n1, - limb_t n2, bool need_P) -{ - bf_context_t *s = T->ctx; - if ((n2 - n1) == 1) { - if (n1 == 0) { - bf_set_ui(P, 3); - } else { - bf_set_ui(P, n1); - P->sign = 1; - } - bf_set_ui(Q, 2 * n1 + 1); - Q->expn += 2; - bf_set(T, P); - } else { - limb_t m; - bf_t T1_s, *T1 = &T1_s; - bf_t P1_s, *P1 = &P1_s; - bf_t Q1_s, *Q1 = &Q1_s; - - m = n1 + ((n2 - n1) >> 1); - bf_const_log2_rec(T, P, Q, n1, m, true); - bf_init(s, T1); - bf_init(s, P1); - bf_init(s, Q1); - bf_const_log2_rec(T1, P1, Q1, m, n2, need_P); - bf_mul(T, T, Q1, BF_PREC_INF, BF_RNDZ); - bf_mul(T1, T1, P, BF_PREC_INF, BF_RNDZ); - bf_add(T, T, T1, BF_PREC_INF, BF_RNDZ); - if (need_P) - bf_mul(P, P, P1, BF_PREC_INF, BF_RNDZ); - bf_mul(Q, Q, Q1, BF_PREC_INF, BF_RNDZ); - bf_delete(T1); - bf_delete(P1); - bf_delete(Q1); - } -} - -/* compute log(2) with faithful rounding at precision 'prec' */ -static void bf_const_log2_internal(bf_t *T, limb_t prec) -{ - limb_t w, N; - bf_t P_s, *P = &P_s; - bf_t Q_s, *Q = &Q_s; - - w = prec + 15; - N = w / 3 + 1; - bf_init(T->ctx, P); - bf_init(T->ctx, Q); - bf_const_log2_rec(T, P, Q, 0, N, false); - bf_div(T, T, Q, prec, BF_RNDN); - bf_delete(P); - bf_delete(Q); -} - -/* PI constant */ - -#define CHUD_A 13591409 -#define CHUD_B 545140134 -#define CHUD_C 640320 -#define CHUD_BITS_PER_TERM 47 - -static void chud_bs(bf_t *P, bf_t *Q, bf_t *G, int64_t a, int64_t b, int need_g, - limb_t prec) -{ - bf_context_t *s = P->ctx; - int64_t c; - - if (a == (b - 1)) { - bf_t T0, T1; - - bf_init(s, &T0); - bf_init(s, &T1); - bf_set_ui(G, 2 * b - 1); - bf_mul_ui(G, G, 6 * b - 1, prec, BF_RNDN); - bf_mul_ui(G, G, 6 * b - 5, prec, BF_RNDN); - bf_set_ui(&T0, CHUD_B); - bf_mul_ui(&T0, &T0, b, prec, BF_RNDN); - bf_set_ui(&T1, CHUD_A); - bf_add(&T0, &T0, &T1, prec, BF_RNDN); - bf_mul(P, G, &T0, prec, BF_RNDN); - P->sign = b & 1; - - bf_set_ui(Q, b); - bf_mul_ui(Q, Q, b, prec, BF_RNDN); - bf_mul_ui(Q, Q, b, prec, BF_RNDN); - bf_mul_ui(Q, Q, (uint64_t)CHUD_C * CHUD_C * CHUD_C / 24, prec, BF_RNDN); - bf_delete(&T0); - bf_delete(&T1); - } else { - bf_t P2, Q2, G2; - - bf_init(s, &P2); - bf_init(s, &Q2); - bf_init(s, &G2); - - c = (a + b) / 2; - chud_bs(P, Q, G, a, c, 1, prec); - chud_bs(&P2, &Q2, &G2, c, b, need_g, prec); - - /* Q = Q1 * Q2 */ - /* G = G1 * G2 */ - /* P = P1 * Q2 + P2 * G1 */ - bf_mul(&P2, &P2, G, prec, BF_RNDN); - if (!need_g) - bf_set_ui(G, 0); - bf_mul(P, P, &Q2, prec, BF_RNDN); - bf_add(P, P, &P2, prec, BF_RNDN); - bf_delete(&P2); - - bf_mul(Q, Q, &Q2, prec, BF_RNDN); - bf_delete(&Q2); - if (need_g) - bf_mul(G, G, &G2, prec, BF_RNDN); - bf_delete(&G2); - } -} - -/* compute Pi with faithful rounding at precision 'prec' using the - Chudnovsky formula */ -static void bf_const_pi_internal(bf_t *Q, limb_t prec) -{ - bf_context_t *s = Q->ctx; - int64_t n, prec1; - bf_t P, G; - - /* number of serie terms */ - n = prec / CHUD_BITS_PER_TERM + 1; - /* XXX: precision analysis */ - prec1 = prec + 32; - - bf_init(s, &P); - bf_init(s, &G); - - chud_bs(&P, Q, &G, 0, n, 0, BF_PREC_INF); - - bf_mul_ui(&G, Q, CHUD_A, prec1, BF_RNDN); - bf_add(&P, &G, &P, prec1, BF_RNDN); - bf_div(Q, Q, &P, prec1, BF_RNDF); - - bf_set_ui(&P, CHUD_C); - bf_sqrt(&G, &P, prec1, BF_RNDF); - bf_mul_ui(&G, &G, (uint64_t)CHUD_C / 12, prec1, BF_RNDF); - bf_mul(Q, Q, &G, prec, BF_RNDN); - bf_delete(&P); - bf_delete(&G); -} - -static int bf_const_get(bf_t *T, limb_t prec, bf_flags_t flags, - BFConstCache *c, - void (*func)(bf_t *res, limb_t prec), int sign) -{ - limb_t ziv_extra_bits, prec1; - - ziv_extra_bits = 32; - for(;;) { - prec1 = prec + ziv_extra_bits; - if (c->prec < prec1) { - if (c->val.len == 0) - bf_init(T->ctx, &c->val); - func(&c->val, prec1); - c->prec = prec1; - } else { - prec1 = c->prec; - } - bf_set(T, &c->val); - T->sign = sign; - if (!bf_can_round(T, prec, flags & BF_RND_MASK, prec1)) { - /* and more precision and retry */ - ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); - } else { - break; - } - } - return bf_round(T, prec, flags); -} - -static void bf_const_free(BFConstCache *c) -{ - bf_delete(&c->val); - memset(c, 0, sizeof(*c)); -} - -int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = T->ctx; - return bf_const_get(T, prec, flags, &s->log2_cache, bf_const_log2_internal, 0); -} - -/* return rounded pi * (1 - 2 * sign) */ -static int bf_const_pi_signed(bf_t *T, int sign, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = T->ctx; - return bf_const_get(T, prec, flags, &s->pi_cache, bf_const_pi_internal, - sign); -} - -int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags) -{ - return bf_const_pi_signed(T, 0, prec, flags); -} - -void bf_clear_cache(bf_context_t *s) -{ -#ifdef USE_FFT_MUL - fft_clear_cache(s); -#endif - bf_const_free(&s->log2_cache); - bf_const_free(&s->pi_cache); -} - -/* ZivFunc should compute the result 'r' with faithful rounding at - precision 'prec'. For efficiency purposes, the final bf_round() - does not need to be done in the function. */ -typedef int ZivFunc(bf_t *r, const bf_t *a, limb_t prec, void *opaque); - -static int bf_ziv_rounding(bf_t *r, const bf_t *a, - limb_t prec, bf_flags_t flags, - ZivFunc *f, void *opaque) -{ - int rnd_mode, ret; - slimb_t prec1, ziv_extra_bits; - - rnd_mode = flags & BF_RND_MASK; - if (rnd_mode == BF_RNDF) { - /* no need to iterate */ - f(r, a, prec, opaque); - ret = 0; - } else { - ziv_extra_bits = 32; - for(;;) { - prec1 = prec + ziv_extra_bits; - ret = f(r, a, prec1, opaque); - if (ret & (BF_ST_OVERFLOW | BF_ST_UNDERFLOW | BF_ST_MEM_ERROR)) { - /* overflow or underflow should never happen because - it indicates the rounding cannot be done correctly, - but we do not catch all the cases */ - return ret; - } - /* if the result is exact, we can stop */ - if (!(ret & BF_ST_INEXACT)) { - ret = 0; - break; - } - if (bf_can_round(r, prec, rnd_mode, prec1)) { - ret = BF_ST_INEXACT; - break; - } - ziv_extra_bits = ziv_extra_bits * 2; - // printf("ziv_extra_bits=%" PRId64 "\n", (int64_t)ziv_extra_bits); - } - } - if (r->len == 0) - return ret; - else - return __bf_round(r, prec, flags, r->len, ret); -} - -/* add (1 - 2*e_sign) * 2^e */ -static int bf_add_epsilon(bf_t *r, const bf_t *a, slimb_t e, int e_sign, - limb_t prec, int flags) -{ - bf_t T_s, *T = &T_s; - int ret; - /* small argument case: result = 1 + epsilon * sign(x) */ - bf_init(a->ctx, T); - bf_set_ui(T, 1); - T->sign = e_sign; - T->expn += e; - ret = bf_add(r, r, T, prec, flags); - bf_delete(T); - return ret; -} - -/* Compute the exponential using faithful rounding at precision 'prec'. - Note: the algorithm is from MPFR */ -static int bf_exp_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - slimb_t n, K, l, i, prec1; - - assert(r != a); - - /* argument reduction: - T = a - n*log(2) with 0 <= T < log(2) and n integer. - */ - bf_init(s, T); - if (a->expn <= -1) { - /* 0 <= abs(a) <= 0.5 */ - if (a->sign) - n = -1; - else - n = 0; - } else { - bf_const_log2(T, LIMB_BITS, BF_RNDZ); - bf_div(T, a, T, LIMB_BITS, BF_RNDD); - bf_get_limb(&n, T, 0); - } - - K = bf_isqrt((prec + 1) / 2); - l = (prec - 1) / K + 1; - /* XXX: precision analysis ? */ - prec1 = prec + (K + 2 * l + 18) + K + 8; - if (a->expn > 0) - prec1 += a->expn; - // printf("n=%ld K=%ld prec1=%ld\n", n, K, prec1); - - bf_const_log2(T, prec1, BF_RNDF); - bf_mul_si(T, T, n, prec1, BF_RNDN); - bf_sub(T, a, T, prec1, BF_RNDN); - - /* reduce the range of T */ - bf_mul_2exp(T, -K, BF_PREC_INF, BF_RNDZ); - - /* Taylor expansion around zero : - 1 + x + x^2/2 + ... + x^n/n! - = (1 + x * (1 + x/2 * (1 + ... (x/n)))) - */ - { - bf_t U_s, *U = &U_s; - - bf_init(s, U); - bf_set_ui(r, 1); - for(i = l ; i >= 1; i--) { - bf_set_ui(U, i); - bf_div(U, T, U, prec1, BF_RNDN); - bf_mul(r, r, U, prec1, BF_RNDN); - bf_add_si(r, r, 1, prec1, BF_RNDN); - } - bf_delete(U); - } - bf_delete(T); - - /* undo the range reduction */ - for(i = 0; i < K; i++) { - bf_mul(r, r, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); - } - - /* undo the argument reduction */ - bf_mul_2exp(r, n, BF_PREC_INF, BF_RNDZ | BF_FLAG_EXT_EXP); - - return BF_ST_INEXACT; -} - -/* crude overflow and underflow tests for exp(a). a_low <= a <= a_high */ -static int check_exp_underflow_overflow(bf_context_t *s, bf_t *r, - const bf_t *a_low, const bf_t *a_high, - limb_t prec, bf_flags_t flags) -{ - bf_t T_s, *T = &T_s; - bf_t log2_s, *log2 = &log2_s; - slimb_t e_min, e_max; - - if (a_high->expn <= 0) - return 0; - - e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); - e_min = -e_max + 3; - if (flags & BF_FLAG_SUBNORMAL) - e_min -= (prec - 1); - - bf_init(s, T); - bf_init(s, log2); - bf_const_log2(log2, LIMB_BITS, BF_RNDU); - bf_mul_ui(T, log2, e_max, LIMB_BITS, BF_RNDU); - /* a_low > e_max * log(2) implies exp(a) > e_max */ - if (bf_cmp_lt(T, a_low) > 0) { - /* overflow */ - bf_delete(T); - bf_delete(log2); - return bf_set_overflow(r, 0, prec, flags); - } - /* a_high < (e_min - 2) * log(2) implies exp(a) < (e_min - 2) */ - bf_const_log2(log2, LIMB_BITS, BF_RNDD); - bf_mul_si(T, log2, e_min - 2, LIMB_BITS, BF_RNDD); - if (bf_cmp_lt(a_high, T)) { - int rnd_mode = flags & BF_RND_MASK; - - /* underflow */ - bf_delete(T); - bf_delete(log2); - if (rnd_mode == BF_RNDU) { - /* set the smallest value */ - bf_set_ui(r, 1); - r->expn = e_min; - } else { - bf_set_zero(r, 0); - } - return BF_ST_UNDERFLOW | BF_ST_INEXACT; - } - bf_delete(log2); - bf_delete(T); - return 0; -} - -int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - int ret; - assert(r != a); - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - } else if (a->expn == BF_EXP_INF) { - if (a->sign) - bf_set_zero(r, 0); - else - bf_set_inf(r, 0); - } else { - bf_set_ui(r, 1); - } - return 0; - } - - ret = check_exp_underflow_overflow(s, r, a, a, prec, flags); - if (ret) - return ret; - if (a->expn < 0 && (-a->expn) >= (prec + 2)) { - /* small argument case: result = 1 + epsilon * sign(x) */ - bf_set_ui(r, 1); - return bf_add_epsilon(r, r, -(prec + 2), a->sign, prec, flags); - } - - return bf_ziv_rounding(r, a, prec, flags, bf_exp_internal, NULL); -} - -static int bf_log_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - bf_t U_s, *U = &U_s; - bf_t V_s, *V = &V_s; - slimb_t n, prec1, l, i, K; - - assert(r != a); - - bf_init(s, T); - /* argument reduction 1 */ - /* T=a*2^n with 2/3 <= T <= 4/3 */ - { - bf_t U_s, *U = &U_s; - bf_set(T, a); - n = T->expn; - T->expn = 0; - /* U= ~ 2/3 */ - bf_init(s, U); - bf_set_ui(U, 0xaaaaaaaa); - U->expn = 0; - if (bf_cmp_lt(T, U)) { - T->expn++; - n--; - } - bf_delete(U); - } - // printf("n=%ld\n", n); - // bf_print_str("T", T); - - /* XXX: precision analysis */ - /* number of iterations for argument reduction 2 */ - K = bf_isqrt((prec + 1) / 2); - /* order of Taylor expansion */ - l = prec / (2 * K) + 1; - /* precision of the intermediate computations */ - prec1 = prec + K + 2 * l + 32; - - bf_init(s, U); - bf_init(s, V); - - /* Note: cancellation occurs here, so we use more precision (XXX: - reduce the precision by computing the exact cancellation) */ - bf_add_si(T, T, -1, BF_PREC_INF, BF_RNDN); - - /* argument reduction 2 */ - for(i = 0; i < K; i++) { - /* T = T / (1 + sqrt(1 + T)) */ - bf_add_si(U, T, 1, prec1, BF_RNDN); - bf_sqrt(V, U, prec1, BF_RNDF); - bf_add_si(U, V, 1, prec1, BF_RNDN); - bf_div(T, T, U, prec1, BF_RNDN); - } - - { - bf_t Y_s, *Y = &Y_s; - bf_t Y2_s, *Y2 = &Y2_s; - bf_init(s, Y); - bf_init(s, Y2); - - /* compute ln(1+x) = ln((1+y)/(1-y)) with y=x/(2+x) - = y + y^3/3 + ... + y^(2*l + 1) / (2*l+1) - with Y=Y^2 - = y*(1+Y/3+Y^2/5+...) = y*(1+Y*(1/3+Y*(1/5 + ...))) - */ - bf_add_si(Y, T, 2, prec1, BF_RNDN); - bf_div(Y, T, Y, prec1, BF_RNDN); - - bf_mul(Y2, Y, Y, prec1, BF_RNDN); - bf_set_ui(r, 0); - for(i = l; i >= 1; i--) { - bf_set_ui(U, 1); - bf_set_ui(V, 2 * i + 1); - bf_div(U, U, V, prec1, BF_RNDN); - bf_add(r, r, U, prec1, BF_RNDN); - bf_mul(r, r, Y2, prec1, BF_RNDN); - } - bf_add_si(r, r, 1, prec1, BF_RNDN); - bf_mul(r, r, Y, prec1, BF_RNDN); - bf_delete(Y); - bf_delete(Y2); - } - bf_delete(V); - bf_delete(U); - - /* multiplication by 2 for the Taylor expansion and undo the - argument reduction 2*/ - bf_mul_2exp(r, K + 1, BF_PREC_INF, BF_RNDZ); - - /* undo the argument reduction 1 */ - bf_const_log2(T, prec1, BF_RNDF); - bf_mul_si(T, T, n, prec1, BF_RNDN); - bf_add(r, r, T, prec1, BF_RNDN); - - bf_delete(T); - return BF_ST_INEXACT; -} - -int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - - assert(r != a); - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - if (a->sign) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set_inf(r, 0); - return 0; - } - } else { - bf_set_inf(r, 1); - return 0; - } - } - if (a->sign) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } - bf_init(s, T); - bf_set_ui(T, 1); - if (bf_cmp_eq(a, T)) { - bf_set_zero(r, 0); - bf_delete(T); - return 0; - } - bf_delete(T); - - return bf_ziv_rounding(r, a, prec, flags, bf_log_internal, NULL); -} - -/* x and y finite and x > 0 */ -static int bf_pow_generic(bf_t *r, const bf_t *x, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - const bf_t *y = opaque; - bf_t T_s, *T = &T_s; - limb_t prec1; - - bf_init(s, T); - /* XXX: proof for the added precision */ - prec1 = prec + 32; - bf_log(T, x, prec1, BF_RNDF | BF_FLAG_EXT_EXP); - bf_mul(T, T, y, prec1, BF_RNDF | BF_FLAG_EXT_EXP); - if (bf_is_nan(T)) - bf_set_nan(r); - else - bf_exp_internal(r, T, prec1, NULL); /* no overflow/underlow test needed */ - bf_delete(T); - return BF_ST_INEXACT; -} - -/* x and y finite, x > 0, y integer and y fits on one limb */ -static int bf_pow_int(bf_t *r, const bf_t *x, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - const bf_t *y = opaque; - bf_t T_s, *T = &T_s; - limb_t prec1; - int ret; - slimb_t y1; - - bf_get_limb(&y1, y, 0); - if (y1 < 0) - y1 = -y1; - /* XXX: proof for the added precision */ - prec1 = prec + ceil_log2(y1) * 2 + 8; - ret = bf_pow_ui(r, x, y1 < 0 ? -y1 : y1, prec1, BF_RNDN | BF_FLAG_EXT_EXP); - if (y->sign) { - bf_init(s, T); - bf_set_ui(T, 1); - ret |= bf_div(r, T, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); - bf_delete(T); - } - return ret; -} - -/* x must be a finite non zero float. Return true if there is a - floating point number r such as x=r^(2^n) and return this floating - point number 'r'. Otherwise return false and r is undefined. */ -static bool check_exact_power2n(bf_t *r, const bf_t *x, slimb_t n) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - slimb_t e, i, er; - limb_t v; - - /* x = m*2^e with m odd integer */ - e = bf_get_exp_min(x); - /* fast check on the exponent */ - if (n > (LIMB_BITS - 1)) { - if (e != 0) - return false; - er = 0; - } else { - if ((e & (((limb_t)1 << n) - 1)) != 0) - return false; - er = e >> n; - } - /* every perfect odd square = 1 modulo 8 */ - v = get_bits(x->tab, x->len, x->len * LIMB_BITS - x->expn + e); - if ((v & 7) != 1) - return false; - - bf_init(s, T); - bf_set(T, x); - T->expn -= e; - for(i = 0; i < n; i++) { - if (i != 0) - bf_set(T, r); - if (bf_sqrtrem(r, NULL, T) != 0) - return false; - } - r->expn += er; - return true; -} - -/* prec = BF_PREC_INF is accepted for x and y integers and y >= 0 */ -int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - bf_t ytmp_s; - bool y_is_int, y_is_odd; - int r_sign, ret, rnd_mode; - slimb_t y_emin; - - if (x->len == 0 || y->len == 0) { - if (y->expn == BF_EXP_ZERO) { - /* pow(x, 0) = 1 */ - bf_set_ui(r, 1); - } else if (x->expn == BF_EXP_NAN) { - bf_set_nan(r); - } else { - int cmp_x_abs_1; - bf_set_ui(r, 1); - cmp_x_abs_1 = bf_cmpu(x, r); - if (cmp_x_abs_1 == 0 && (flags & BF_POW_JS_QUIRKS) && - (y->expn >= BF_EXP_INF)) { - bf_set_nan(r); - } else if (cmp_x_abs_1 == 0 && - (!x->sign || y->expn != BF_EXP_NAN)) { - /* pow(1, y) = 1 even if y = NaN */ - /* pow(-1, +/-inf) = 1 */ - } else if (y->expn == BF_EXP_NAN) { - bf_set_nan(r); - } else if (y->expn == BF_EXP_INF) { - if (y->sign == (cmp_x_abs_1 > 0)) { - bf_set_zero(r, 0); - } else { - bf_set_inf(r, 0); - } - } else { - y_emin = bf_get_exp_min(y); - y_is_odd = (y_emin == 0); - if (y->sign == (x->expn == BF_EXP_ZERO)) { - bf_set_inf(r, y_is_odd & x->sign); - if (y->sign) { - /* pow(0, y) with y < 0 */ - return BF_ST_DIVIDE_ZERO; - } - } else { - bf_set_zero(r, y_is_odd & x->sign); - } - } - } - return 0; - } - bf_init(s, T); - bf_set(T, x); - y_emin = bf_get_exp_min(y); - y_is_int = (y_emin >= 0); - rnd_mode = flags & BF_RND_MASK; - if (x->sign) { - if (!y_is_int) { - bf_set_nan(r); - bf_delete(T); - return BF_ST_INVALID_OP; - } - y_is_odd = (y_emin == 0); - r_sign = y_is_odd; - /* change the directed rounding mode if the sign of the result - is changed */ - if (r_sign && (rnd_mode == BF_RNDD || rnd_mode == BF_RNDU)) - flags ^= 1; - bf_neg(T); - } else { - r_sign = 0; - } - - bf_set_ui(r, 1); - if (bf_cmp_eq(T, r)) { - /* abs(x) = 1: nothing more to do */ - ret = 0; - } else { - /* check the overflow/underflow cases */ - { - bf_t al_s, *al = &al_s; - bf_t ah_s, *ah = &ah_s; - limb_t precl = LIMB_BITS; - - bf_init(s, al); - bf_init(s, ah); - /* compute bounds of log(abs(x)) * y with a low precision */ - /* XXX: compute bf_log() once */ - /* XXX: add a fast test before this slow test */ - bf_log(al, T, precl, BF_RNDD); - bf_log(ah, T, precl, BF_RNDU); - bf_mul(al, al, y, precl, BF_RNDD ^ y->sign); - bf_mul(ah, ah, y, precl, BF_RNDU ^ y->sign); - ret = check_exp_underflow_overflow(s, r, al, ah, prec, flags); - bf_delete(al); - bf_delete(ah); - if (ret) - goto done; - } - - if (y_is_int) { - slimb_t T_bits, e; - int_pow: - T_bits = T->expn - bf_get_exp_min(T); - if (T_bits == 1) { - /* pow(2^b, y) = 2^(b*y) */ - bf_mul_si(T, y, T->expn - 1, LIMB_BITS, BF_RNDZ); - bf_get_limb(&e, T, 0); - bf_set_ui(r, 1); - ret = bf_mul_2exp(r, e, prec, flags); - } else if (prec == BF_PREC_INF) { - slimb_t y1; - /* specific case for infinite precision (integer case) */ - bf_get_limb(&y1, y, 0); - assert(!y->sign); - /* x must be an integer, so abs(x) >= 2 */ - if (y1 >= ((slimb_t)1 << BF_EXP_BITS_MAX)) { - bf_delete(T); - return bf_set_overflow(r, 0, BF_PREC_INF, flags); - } - ret = bf_pow_ui(r, T, y1, BF_PREC_INF, BF_RNDZ); - } else { - if (y->expn <= 31) { - /* small enough power: use exponentiation in all cases */ - } else if (y->sign) { - /* cannot be exact */ - goto general_case; - } else { - if (rnd_mode == BF_RNDF) - goto general_case; /* no need to track exact results */ - /* see if the result has a chance to be exact: - if x=a*2^b (a odd), x^y=a^y*2^(b*y) - x^y needs a precision of at least floor_log2(a)*y bits - */ - bf_mul_si(r, y, T_bits - 1, LIMB_BITS, BF_RNDZ); - bf_get_limb(&e, r, 0); - if (prec < e) - goto general_case; - } - ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_int, (void *)y); - } - } else { - if (rnd_mode != BF_RNDF) { - bf_t *y1; - if (y_emin < 0 && check_exact_power2n(r, T, -y_emin)) { - /* the problem is reduced to a power to an integer */ - bf_set(T, r); - y1 = &ytmp_s; - y1->tab = y->tab; - y1->len = y->len; - y1->sign = y->sign; - y1->expn = y->expn - y_emin; - y = y1; - goto int_pow; - } - } - general_case: - ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_generic, (void *)y); - } - } - done: - bf_delete(T); - r->sign = r_sign; - return ret; -} - -/* compute sqrt(-2*x-x^2) to get |sin(x)| from cos(x) - 1. */ -static void bf_sqrt_sin(bf_t *r, const bf_t *x, limb_t prec1) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - bf_init(s, T); - bf_set(T, x); - bf_mul(r, T, T, prec1, BF_RNDN); - bf_mul_2exp(T, 1, BF_PREC_INF, BF_RNDZ); - bf_add(T, T, r, prec1, BF_RNDN); - bf_neg(T); - bf_sqrt(r, T, prec1, BF_RNDF); - bf_delete(T); -} - -static int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec) -{ - bf_context_t *s1 = a->ctx; - bf_t T_s, *T = &T_s; - bf_t U_s, *U = &U_s; - bf_t r_s, *r = &r_s; - slimb_t K, prec1, i, l, mod, prec2; - int is_neg; - - assert(c != a && s != a); - - bf_init(s1, T); - bf_init(s1, U); - bf_init(s1, r); - - /* XXX: precision analysis */ - K = bf_isqrt(prec / 2); - l = prec / (2 * K) + 1; - prec1 = prec + 2 * K + l + 8; - - /* after the modulo reduction, -pi/4 <= T <= pi/4 */ - if (a->expn <= -1) { - /* abs(a) <= 0.25: no modulo reduction needed */ - bf_set(T, a); - mod = 0; - } else { - slimb_t cancel; - cancel = 0; - for(;;) { - prec2 = prec1 + a->expn + cancel; - bf_const_pi(U, prec2, BF_RNDF); - bf_mul_2exp(U, -1, BF_PREC_INF, BF_RNDZ); - bf_remquo(&mod, T, a, U, prec2, BF_RNDN, BF_RNDN); - // printf("T.expn=%ld prec2=%ld\n", T->expn, prec2); - if (mod == 0 || (T->expn != BF_EXP_ZERO && - (T->expn + prec2) >= (prec1 - 1))) - break; - /* increase the number of bits until the precision is good enough */ - cancel = bf_max(-T->expn, (cancel + 1) * 3 / 2); - } - mod &= 3; - } - - is_neg = T->sign; - - /* compute cosm1(x) = cos(x) - 1 */ - bf_mul(T, T, T, prec1, BF_RNDN); - bf_mul_2exp(T, -2 * K, BF_PREC_INF, BF_RNDZ); - - /* Taylor expansion: - -x^2/2 + x^4/4! - x^6/6! + ... - */ - bf_set_ui(r, 1); - for(i = l ; i >= 1; i--) { - bf_set_ui(U, 2 * i - 1); - bf_mul_ui(U, U, 2 * i, BF_PREC_INF, BF_RNDZ); - bf_div(U, T, U, prec1, BF_RNDN); - bf_mul(r, r, U, prec1, BF_RNDN); - bf_neg(r); - if (i != 1) - bf_add_si(r, r, 1, prec1, BF_RNDN); - } - bf_delete(U); - - /* undo argument reduction: - cosm1(2*x)= 2*(2*cosm1(x)+cosm1(x)^2) - */ - for(i = 0; i < K; i++) { - bf_mul(T, r, r, prec1, BF_RNDN); - bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ); - bf_add(r, r, T, prec1, BF_RNDN); - bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ); - } - bf_delete(T); - - if (c) { - if ((mod & 1) == 0) { - bf_add_si(c, r, 1, prec1, BF_RNDN); - } else { - bf_sqrt_sin(c, r, prec1); - c->sign = is_neg ^ 1; - } - c->sign ^= mod >> 1; - } - if (s) { - if ((mod & 1) == 0) { - bf_sqrt_sin(s, r, prec1); - s->sign = is_neg; - } else { - bf_add_si(s, r, 1, prec1, BF_RNDN); - } - s->sign ^= mod >> 1; - } - bf_delete(r); - return BF_ST_INEXACT; -} - -static int bf_cos_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) -{ - return bf_sincos(NULL, r, a, prec); -} - -int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set_ui(r, 1); - return 0; - } - } - - /* small argument case: result = 1+r(x) with r(x) = -x^2/2 + - O(X^4). We assume r(x) < 2^(2*EXP(x) - 1). */ - if (a->expn < 0) { - slimb_t e; - e = 2 * a->expn - 1; - if (e < -(prec + 2)) { - bf_set_ui(r, 1); - return bf_add_epsilon(r, r, e, 1, prec, flags); - } - } - - return bf_ziv_rounding(r, a, prec, flags, bf_cos_internal, NULL); -} - -static int bf_sin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) -{ - return bf_sincos(r, NULL, a, prec); -} - -int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set_zero(r, a->sign); - return 0; - } - } - - /* small argument case: result = x+r(x) with r(x) = -x^3/6 + - O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ - if (a->expn < 0) { - slimb_t e; - e = sat_add(2 * a->expn, a->expn - 2); - if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { - bf_set(r, a); - return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); - } - } - - return bf_ziv_rounding(r, a, prec, flags, bf_sin_internal, NULL); -} - -static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - limb_t prec1; - - /* XXX: precision analysis */ - prec1 = prec + 8; - bf_init(s, T); - bf_sincos(r, T, a, prec1); - bf_div(r, r, T, prec1, BF_RNDF); - bf_delete(T); - return BF_ST_INEXACT; -} - -int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - assert(r != a); - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set_zero(r, a->sign); - return 0; - } - } - - /* small argument case: result = x+r(x) with r(x) = x^3/3 + - O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ - if (a->expn < 0) { - slimb_t e; - e = sat_add(2 * a->expn, a->expn - 1); - if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { - bf_set(r, a); - return bf_add_epsilon(r, r, e, a->sign, prec, flags); - } - } - - return bf_ziv_rounding(r, a, prec, flags, bf_tan_internal, NULL); -} - -/* if add_pi2 is true, add pi/2 to the result (used for acos(x) to - avoid cancellation) */ -static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec, - void *opaque) -{ - bf_context_t *s = r->ctx; - bool add_pi2 = (bool)(intptr_t)opaque; - bf_t T_s, *T = &T_s; - bf_t U_s, *U = &U_s; - bf_t V_s, *V = &V_s; - bf_t X2_s, *X2 = &X2_s; - int cmp_1; - slimb_t prec1, i, K, l; - - /* XXX: precision analysis */ - K = bf_isqrt((prec + 1) / 2); - l = prec / (2 * K) + 1; - prec1 = prec + K + 2 * l + 32; - // printf("prec=%d K=%d l=%d prec1=%d\n", (int)prec, (int)K, (int)l, (int)prec1); - - bf_init(s, T); - cmp_1 = (a->expn >= 1); /* a >= 1 */ - if (cmp_1) { - bf_set_ui(T, 1); - bf_div(T, T, a, prec1, BF_RNDN); - } else { - bf_set(T, a); - } - - /* abs(T) <= 1 */ - - /* argument reduction */ - - bf_init(s, U); - bf_init(s, V); - bf_init(s, X2); - for(i = 0; i < K; i++) { - /* T = T / (1 + sqrt(1 + T^2)) */ - bf_mul(U, T, T, prec1, BF_RNDN); - bf_add_si(U, U, 1, prec1, BF_RNDN); - bf_sqrt(V, U, prec1, BF_RNDN); - bf_add_si(V, V, 1, prec1, BF_RNDN); - bf_div(T, T, V, prec1, BF_RNDN); - } - - /* Taylor series: - x - x^3/3 + ... + (-1)^ l * y^(2*l + 1) / (2*l+1) - */ - bf_mul(X2, T, T, prec1, BF_RNDN); - bf_set_ui(r, 0); - for(i = l; i >= 1; i--) { - bf_set_si(U, 1); - bf_set_ui(V, 2 * i + 1); - bf_div(U, U, V, prec1, BF_RNDN); - bf_neg(r); - bf_add(r, r, U, prec1, BF_RNDN); - bf_mul(r, r, X2, prec1, BF_RNDN); - } - bf_neg(r); - bf_add_si(r, r, 1, prec1, BF_RNDN); - bf_mul(r, r, T, prec1, BF_RNDN); - - /* undo the argument reduction */ - bf_mul_2exp(r, K, BF_PREC_INF, BF_RNDZ); - - bf_delete(U); - bf_delete(V); - bf_delete(X2); - - i = add_pi2; - if (cmp_1 > 0) { - /* undo the inversion : r = sign(a)*PI/2 - r */ - bf_neg(r); - i += 1 - 2 * a->sign; - } - /* add i*(pi/2) with -1 <= i <= 2 */ - if (i != 0) { - bf_const_pi(T, prec1, BF_RNDF); - if (i != 2) - bf_mul_2exp(T, -1, BF_PREC_INF, BF_RNDZ); - T->sign = (i < 0); - bf_add(r, T, r, prec1, BF_RNDN); - } - - bf_delete(T); - return BF_ST_INEXACT; -} - -int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - int res; - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - /* -PI/2 or PI/2 */ - bf_const_pi_signed(r, a->sign, prec, flags); - bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); - return BF_ST_INEXACT; - } else { - bf_set_zero(r, a->sign); - return 0; - } - } - - bf_init(s, T); - bf_set_ui(T, 1); - res = bf_cmpu(a, T); - bf_delete(T); - if (res == 0) { - /* short cut: abs(a) == 1 -> +/-pi/4 */ - bf_const_pi_signed(r, a->sign, prec, flags); - bf_mul_2exp(r, -2, BF_PREC_INF, BF_RNDZ); - return BF_ST_INEXACT; - } - - /* small argument case: result = x+r(x) with r(x) = -x^3/3 + - O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ - if (a->expn < 0) { - slimb_t e; - e = sat_add(2 * a->expn, a->expn - 1); - if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { - bf_set(r, a); - return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); - } - } - - return bf_ziv_rounding(r, a, prec, flags, bf_atan_internal, (void *)false); -} - -static int bf_atan2_internal(bf_t *r, const bf_t *y, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - const bf_t *x = opaque; - bf_t T_s, *T = &T_s; - limb_t prec1; - int ret; - - if (y->expn == BF_EXP_NAN || x->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } - - /* compute atan(y/x) assumming inf/inf = 1 and 0/0 = 0 */ - bf_init(s, T); - prec1 = prec + 32; - if (y->expn == BF_EXP_INF && x->expn == BF_EXP_INF) { - bf_set_ui(T, 1); - T->sign = y->sign ^ x->sign; - } else if (y->expn == BF_EXP_ZERO && x->expn == BF_EXP_ZERO) { - bf_set_zero(T, y->sign ^ x->sign); - } else { - bf_div(T, y, x, prec1, BF_RNDF); - } - ret = bf_atan(r, T, prec1, BF_RNDF); - - if (x->sign) { - /* if x < 0 (it includes -0), return sign(y)*pi + atan(y/x) */ - bf_const_pi(T, prec1, BF_RNDF); - T->sign = y->sign; - bf_add(r, r, T, prec1, BF_RNDN); - ret |= BF_ST_INEXACT; - } - - bf_delete(T); - return ret; -} - -int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, - limb_t prec, bf_flags_t flags) -{ - return bf_ziv_rounding(r, y, prec, flags, bf_atan2_internal, (void *)x); -} - -static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - bool is_acos = (bool)(intptr_t)opaque; - bf_t T_s, *T = &T_s; - limb_t prec1, prec2; - - /* asin(x) = atan(x/sqrt(1-x^2)) - acos(x) = pi/2 - asin(x) */ - prec1 = prec + 8; - /* increase the precision in x^2 to compensate the cancellation in - (1-x^2) if x is close to 1 */ - /* XXX: use less precision when possible */ - if (a->expn >= 0) - prec2 = BF_PREC_INF; - else - prec2 = prec1; - bf_init(s, T); - bf_mul(T, a, a, prec2, BF_RNDN); - bf_neg(T); - bf_add_si(T, T, 1, prec2, BF_RNDN); - - bf_sqrt(r, T, prec1, BF_RNDN); - bf_div(T, a, r, prec1, BF_RNDN); - if (is_acos) - bf_neg(T); - bf_atan_internal(r, T, prec1, (void *)(intptr_t)is_acos); - bf_delete(T); - return BF_ST_INEXACT; -} - -int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - int res; - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set_zero(r, a->sign); - return 0; - } - } - bf_init(s, T); - bf_set_ui(T, 1); - res = bf_cmpu(a, T); - bf_delete(T); - if (res > 0) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } - - /* small argument case: result = x+r(x) with r(x) = x^3/6 + - O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ - if (a->expn < 0) { - slimb_t e; - e = sat_add(2 * a->expn, a->expn - 2); - if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { - bf_set(r, a); - return bf_add_epsilon(r, r, e, a->sign, prec, flags); - } - } - - return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)false); -} - -int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - int res; - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_const_pi(r, prec, flags); - bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); - return BF_ST_INEXACT; - } - } - bf_init(s, T); - bf_set_ui(T, 1); - res = bf_cmpu(a, T); - bf_delete(T); - if (res > 0) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else if (res == 0 && a->sign == 0) { - bf_set_zero(r, 0); - return 0; - } - - return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)true); -} - -/***************************************************************/ -/* decimal floating point numbers */ - -#ifdef USE_BF_DEC - -#define adddq(r1, r0, a1, a0) \ - do { \ - limb_t __t = r0; \ - r0 += (a0); \ - r1 += (a1) + (r0 < __t); \ - } while (0) - -#define subdq(r1, r0, a1, a0) \ - do { \ - limb_t __t = r0; \ - r0 -= (a0); \ - r1 -= (a1) + (r0 > __t); \ - } while (0) - -#if LIMB_BITS == 64 - -/* Note: we assume __int128 is available */ -/* uint128_t defined in libbf.h */ -#define muldq(r1, r0, a, b) \ - do { \ - uint128_t __t; \ - __t = (uint128_t)(a) * (uint128_t)(b); \ - r0 = __t; \ - r1 = __t >> 64; \ - } while (0) - -#define divdq(q, r, a1, a0, b) \ - do { \ - uint128_t __t; \ - limb_t __b = (b); \ - __t = ((uint128_t)(a1) << 64) | (a0); \ - q = __t / __b; \ - r = __t % __b; \ - } while (0) - -#else - -#define muldq(r1, r0, a, b) \ - do { \ - uint64_t __t; \ - __t = (uint64_t)(a) * (uint64_t)(b); \ - r0 = __t; \ - r1 = __t >> 32; \ - } while (0) - -#define divdq(q, r, a1, a0, b) \ - do { \ - uint64_t __t; \ - limb_t __b = (b); \ - __t = ((uint64_t)(a1) << 32) | (a0); \ - q = __t / __b; \ - r = __t % __b; \ - } while (0) - -#endif /* LIMB_BITS != 64 */ - -#if LIMB_DIGITS == 19 - -/* WARNING: hardcoded for b = 1e19. It is assumed that: - 0 <= a1 < 2^63 */ -#define divdq_base(q, r, a1, a0)\ -do {\ - uint64_t __a0, __a1, __t0, __t1, __b = BF_DEC_BASE; \ - __a0 = a0;\ - __a1 = a1;\ - __t0 = __a1;\ - __t0 = shld(__t0, __a0, 1);\ - muldq(q, __t1, __t0, UINT64_C(17014118346046923173)); \ - muldq(__t1, __t0, q, __b);\ - subdq(__a1, __a0, __t1, __t0);\ - subdq(__a1, __a0, 1, __b * 2); \ - __t0 = (slimb_t)__a1 >> 1; \ - q += 2 + __t0;\ - adddq(__a1, __a0, 0, __b & __t0);\ - q += __a1; \ - __a0 += __b & __a1; \ - r = __a0;\ -} while(0) - -#elif LIMB_DIGITS == 9 - -/* WARNING: hardcoded for b = 1e9. It is assumed that: - 0 <= a1 < 2^29 */ -#define divdq_base(q, r, a1, a0)\ -do {\ - uint32_t __t0, __t1, __b = BF_DEC_BASE; \ - __t0 = a1;\ - __t1 = a0;\ - __t0 = (__t0 << 3) | (__t1 >> (32 - 3)); \ - muldq(q, __t1, __t0, 2305843009U);\ - r = a0 - q * __b;\ - __t1 = (r >= __b);\ - q += __t1;\ - if (__t1)\ - r -= __b;\ -} while(0) - -#endif - -/* fast integer division by a fixed constant */ - -typedef struct FastDivData { - limb_t m1; /* multiplier */ - int8_t shift1; - int8_t shift2; -} FastDivData; - -/* From "Division by Invariant Integers using Multiplication" by - Torborn Granlund and Peter L. Montgomery */ -/* d must be != 0 */ -static inline __maybe_unused void fast_udiv_init(FastDivData *s, limb_t d) -{ - int l; - limb_t q, r, m1; - if (d == 1) - l = 0; - else - l = 64 - clz64(d - 1); - divdq(q, r, ((limb_t)1 << l) - d, 0, d); - (void)r; - m1 = q + 1; - // printf("d=%lu l=%d m1=0x%016lx\n", d, l, m1); - s->m1 = m1; - s->shift1 = l; - if (s->shift1 > 1) - s->shift1 = 1; - s->shift2 = l - 1; - if (s->shift2 < 0) - s->shift2 = 0; -} - -static inline limb_t fast_udiv(limb_t a, const FastDivData *s) -{ - limb_t t0, t1; - muldq(t1, t0, s->m1, a); - t0 = (a - t1) >> s->shift1; - return (t1 + t0) >> s->shift2; -} - -/* contains 10^i */ -const limb_t mp_pow_dec[LIMB_DIGITS + 1] = { - 1U, - 10U, - 100U, - 1000U, - 10000U, - 100000U, - 1000000U, - 10000000U, - 100000000U, - 1000000000U, -#if LIMB_BITS == 64 - 10000000000U, - 100000000000U, - 1000000000000U, - 10000000000000U, - 100000000000000U, - 1000000000000000U, - 10000000000000000U, - 100000000000000000U, - 1000000000000000000U, - 10000000000000000000U, -#endif -}; - -/* precomputed from fast_udiv_init(10^i) */ -static const FastDivData mp_pow_div[LIMB_DIGITS + 1] = { -#if LIMB_BITS == 32 - { 0x00000001, 0, 0 }, - { 0x9999999a, 1, 3 }, - { 0x47ae147b, 1, 6 }, - { 0x0624dd30, 1, 9 }, - { 0xa36e2eb2, 1, 13 }, - { 0x4f8b588f, 1, 16 }, - { 0x0c6f7a0c, 1, 19 }, - { 0xad7f29ac, 1, 23 }, - { 0x5798ee24, 1, 26 }, - { 0x12e0be83, 1, 29 }, -#else - { 0x0000000000000001, 0, 0 }, - { 0x999999999999999a, 1, 3 }, - { 0x47ae147ae147ae15, 1, 6 }, - { 0x0624dd2f1a9fbe77, 1, 9 }, - { 0xa36e2eb1c432ca58, 1, 13 }, - { 0x4f8b588e368f0847, 1, 16 }, - { 0x0c6f7a0b5ed8d36c, 1, 19 }, - { 0xad7f29abcaf48579, 1, 23 }, - { 0x5798ee2308c39dfa, 1, 26 }, - { 0x12e0be826d694b2f, 1, 29 }, - { 0xb7cdfd9d7bdbab7e, 1, 33 }, - { 0x5fd7fe17964955fe, 1, 36 }, - { 0x19799812dea11198, 1, 39 }, - { 0xc25c268497681c27, 1, 43 }, - { 0x6849b86a12b9b01f, 1, 46 }, - { 0x203af9ee756159b3, 1, 49 }, - { 0xcd2b297d889bc2b7, 1, 53 }, - { 0x70ef54646d496893, 1, 56 }, - { 0x2725dd1d243aba0f, 1, 59 }, - { 0xd83c94fb6d2ac34d, 1, 63 }, -#endif -}; - -/* divide by 10^shift with 0 <= shift <= LIMB_DIGITS */ -static inline limb_t fast_shr_dec(limb_t a, int shift) -{ - return fast_udiv(a, &mp_pow_div[shift]); -} - -/* division and remainder by 10^shift */ -#define fast_shr_rem_dec(q, r, a, shift) q = fast_shr_dec(a, shift), r = a - q * mp_pow_dec[shift] - -limb_t mp_add_dec(limb_t *res, const limb_t *op1, const limb_t *op2, - mp_size_t n, limb_t carry) -{ - limb_t base = BF_DEC_BASE; - mp_size_t i; - limb_t k, a, v; - - k=carry; - for(i=0;i v; - if (k) - a += base; - res[i] = a; - } - return k; -} - -limb_t mp_sub_ui_dec(limb_t *tab, limb_t b, mp_size_t n) -{ - limb_t base = BF_DEC_BASE; - mp_size_t i; - limb_t k, v, a; - - k=b; - for(i=0;i v; - if (k) - a += base; - tab[i]=a; - if (k == 0) - break; - } - return k; -} - -/* taba[] = taba[] * b + l. 0 <= b, l <= base - 1. Return the high carry */ -limb_t mp_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, - limb_t b, limb_t l) -{ - mp_size_t i; - limb_t t0, t1, r; - - for(i = 0; i < n; i++) { - muldq(t1, t0, taba[i], b); - adddq(t1, t0, 0, l); - divdq_base(l, r, t1, t0); - tabr[i] = r; - } - return l; -} - -/* tabr[] += taba[] * b. 0 <= b <= base - 1. Return the value to add - to the high word */ -limb_t mp_add_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, - limb_t b) -{ - mp_size_t i; - limb_t l, t0, t1, r; - - l = 0; - for(i = 0; i < n; i++) { - muldq(t1, t0, taba[i], b); - adddq(t1, t0, 0, l); - adddq(t1, t0, 0, tabr[i]); - divdq_base(l, r, t1, t0); - tabr[i] = r; - } - return l; -} - -/* tabr[] -= taba[] * b. 0 <= b <= base - 1. Return the value to - substract to the high word. */ -limb_t mp_sub_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, - limb_t b) -{ - limb_t base = BF_DEC_BASE; - mp_size_t i; - limb_t l, t0, t1, r, a, v, c; - - /* XXX: optimize */ - l = 0; - for(i = 0; i < n; i++) { - muldq(t1, t0, taba[i], b); - adddq(t1, t0, 0, l); - divdq_base(l, r, t1, t0); - v = tabr[i]; - a = v - r; - c = a > v; - if (c) - a += base; - /* never bigger than base because r = 0 when l = base - 1 */ - l += c; - tabr[i] = a; - } - return l; -} - -/* size of the result : op1_size + op2_size. */ -void mp_mul_basecase_dec(limb_t *result, - const limb_t *op1, mp_size_t op1_size, - const limb_t *op2, mp_size_t op2_size) -{ - mp_size_t i; - limb_t r; - - result[op1_size] = mp_mul1_dec(result, op1, op1_size, op2[0], 0); - - for(i=1;i> 1; - if (r) - r = base_div2; - for(i = na - 1; i >= 0; i--) { - t0 = taba[i]; - tabr[i] = (t0 >> 1) + r; - r = 0; - if (t0 & 1) - r = base_div2; - } - if (r) - r = 1; - } else -#endif - if (na >= UDIV1NORM_THRESHOLD) { - shift = clz(b); - if (shift == 0) { - /* normalized case: b >= 2^(LIMB_BITS-1) */ - limb_t b_inv; - b_inv = udiv1norm_init(b); - for(i = na - 1; i >= 0; i--) { - muldq(t1, t0, r, base); - adddq(t1, t0, 0, taba[i]); - q = udiv1norm(&r, t1, t0, b, b_inv); - tabr[i] = q; - } - } else { - limb_t b_inv; - b <<= shift; - b_inv = udiv1norm_init(b); - for(i = na - 1; i >= 0; i--) { - muldq(t1, t0, r, base); - adddq(t1, t0, 0, taba[i]); - t1 = (t1 << shift) | (t0 >> (LIMB_BITS - shift)); - t0 <<= shift; - q = udiv1norm(&r, t1, t0, b, b_inv); - r >>= shift; - tabr[i] = q; - } - } - } else { - for(i = na - 1; i >= 0; i--) { - muldq(t1, t0, r, base); - adddq(t1, t0, 0, taba[i]); - divdq(q, r, t1, t0, b); - tabr[i] = q; - } - } - return r; -} - -static __maybe_unused void mp_print_str_dec(const char *str, - const limb_t *tab, slimb_t n) -{ - slimb_t i; - printf("%s=", str); - for(i = n - 1; i >= 0; i--) { - if (i != n - 1) - printf("_"); - printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); - } - printf("\n"); -} - -static __maybe_unused void mp_print_str_h_dec(const char *str, - const limb_t *tab, slimb_t n, - limb_t high) -{ - slimb_t i; - printf("%s=", str); - printf("%0*" PRIu_LIMB, LIMB_DIGITS, high); - for(i = n - 1; i >= 0; i--) { - printf("_"); - printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); - } - printf("\n"); -} - -//#define DEBUG_DIV_SLOW - -#define DIV_STATIC_ALLOC_LEN 16 - -/* return q = a / b and r = a % b. - - taba[na] must be allocated if tabb1[nb - 1] < B / 2. tabb1[nb - 1] - must be != zero. na must be >= nb. 's' can be NULL if tabb1[nb - 1] - >= B / 2. - - The remainder is is returned in taba and contains nb libms. tabq - contains na - nb + 1 limbs. No overlap is permitted. - - Running time of the standard method: (na - nb + 1) * nb - Return 0 if OK, -1 if memory alloc error -*/ -/* XXX: optimize */ -static int mp_div_dec(bf_context_t *s, limb_t *tabq, - limb_t *taba, mp_size_t na, - const limb_t *tabb1, mp_size_t nb) -{ - limb_t base = BF_DEC_BASE; - limb_t r, mult, t0, t1, a, c, q, v, *tabb; - mp_size_t i, j; - limb_t static_tabb[DIV_STATIC_ALLOC_LEN]; - -#ifdef DEBUG_DIV_SLOW - mp_print_str_dec("a", taba, na); - mp_print_str_dec("b", tabb1, nb); -#endif - - /* normalize tabb */ - r = tabb1[nb - 1]; - assert(r != 0); - i = na - nb; - if (r >= BF_DEC_BASE / 2) { - mult = 1; - tabb = (limb_t *)tabb1; - q = 1; - for(j = nb - 1; j >= 0; j--) { - if (taba[i + j] != tabb[j]) { - if (taba[i + j] < tabb[j]) - q = 0; - break; - } - } - tabq[i] = q; - if (q) { - mp_sub_dec(taba + i, taba + i, tabb, nb, 0); - } - i--; - } else { - mult = base / (r + 1); - if (likely(nb <= DIV_STATIC_ALLOC_LEN)) { - tabb = static_tabb; - } else { - tabb = bf_malloc(s, sizeof(limb_t) * nb); - if (!tabb) - return -1; - } - mp_mul1_dec(tabb, tabb1, nb, mult, 0); - taba[na] = mp_mul1_dec(taba, taba, na, mult, 0); - } - -#ifdef DEBUG_DIV_SLOW - printf("mult=" FMT_LIMB "\n", mult); - mp_print_str_dec("a_norm", taba, na + 1); - mp_print_str_dec("b_norm", tabb, nb); -#endif - - for(; i >= 0; i--) { - if (unlikely(taba[i + nb] >= tabb[nb - 1])) { - /* XXX: check if it is really possible */ - q = base - 1; - } else { - muldq(t1, t0, taba[i + nb], base); - adddq(t1, t0, 0, taba[i + nb - 1]); - divdq(q, r, t1, t0, tabb[nb - 1]); - } - // printf("i=%d q1=%ld\n", i, q); - - r = mp_sub_mul1_dec(taba + i, tabb, nb, q); - // mp_dump("r1", taba + i, nb, bd); - // printf("r2=%ld\n", r); - - v = taba[i + nb]; - a = v - r; - c = a > v; - if (c) - a += base; - taba[i + nb] = a; - - if (c != 0) { - /* negative result */ - for(;;) { - q--; - c = mp_add_dec(taba + i, taba + i, tabb, nb, 0); - /* propagate carry and test if positive result */ - if (c != 0) { - if (++taba[i + nb] == base) { - break; - } - } - } - } - tabq[i] = q; - } - -#ifdef DEBUG_DIV_SLOW - mp_print_str_dec("q", tabq, na - nb + 1); - mp_print_str_dec("r", taba, nb); -#endif - - /* remove the normalization */ - if (mult != 1) { - mp_div1_dec(taba, taba, nb, mult, 0); - if (unlikely(tabb != static_tabb)) - bf_free(s, tabb); - } - return 0; -} - -/* divide by 10^shift */ -static limb_t mp_shr_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, - limb_t shift, limb_t high) -{ - mp_size_t i; - limb_t l, a, q, r; - - assert(shift >= 1 && shift < LIMB_DIGITS); - l = high; - for(i = n - 1; i >= 0; i--) { - a = tab[i]; - fast_shr_rem_dec(q, r, a, shift); - tab_r[i] = q + l * mp_pow_dec[LIMB_DIGITS - shift]; - l = r; - } - return l; -} - -/* multiply by 10^shift */ -static limb_t mp_shl_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, - limb_t shift, limb_t low) -{ - mp_size_t i; - limb_t l, a, q, r; - - assert(shift >= 1 && shift < LIMB_DIGITS); - l = low; - for(i = 0; i < n; i++) { - a = tab[i]; - fast_shr_rem_dec(q, r, a, LIMB_DIGITS - shift); - tab_r[i] = r * mp_pow_dec[shift] + l; - l = q; - } - return l; -} - -static limb_t mp_sqrtrem2_dec(limb_t *tabs, limb_t *taba) -{ - int k; - dlimb_t a, b, r; - limb_t taba1[2], s, r0, r1; - - /* convert to binary and normalize */ - a = (dlimb_t)taba[1] * BF_DEC_BASE + taba[0]; - k = clz(a >> LIMB_BITS) & ~1; - b = a << k; - taba1[0] = b; - taba1[1] = b >> LIMB_BITS; - mp_sqrtrem2(&s, taba1); - s >>= (k >> 1); - /* convert the remainder back to decimal */ - r = a - (dlimb_t)s * (dlimb_t)s; - divdq_base(r1, r0, r >> LIMB_BITS, r); - taba[0] = r0; - tabs[0] = s; - return r1; -} - -//#define DEBUG_SQRTREM_DEC - -/* tmp_buf must contain (n / 2 + 1 limbs) */ -static limb_t mp_sqrtrem_rec_dec(limb_t *tabs, limb_t *taba, limb_t n, - limb_t *tmp_buf) -{ - limb_t l, h, rh, ql, qh, c, i; - - if (n == 1) - return mp_sqrtrem2_dec(tabs, taba); -#ifdef DEBUG_SQRTREM_DEC - mp_print_str_dec("a", taba, 2 * n); -#endif - l = n / 2; - h = n - l; - qh = mp_sqrtrem_rec_dec(tabs + l, taba + 2 * l, h, tmp_buf); -#ifdef DEBUG_SQRTREM_DEC - mp_print_str_dec("s1", tabs + l, h); - mp_print_str_h_dec("r1", taba + 2 * l, h, qh); - mp_print_str_h_dec("r2", taba + l, n, qh); -#endif - - /* the remainder is in taba + 2 * l. Its high bit is in qh */ - if (qh) { - mp_sub_dec(taba + 2 * l, taba + 2 * l, tabs + l, h, 0); - } - /* instead of dividing by 2*s, divide by s (which is normalized) - and update q and r */ - mp_div_dec(NULL, tmp_buf, taba + l, n, tabs + l, h); - qh += tmp_buf[l]; - for(i = 0; i < l; i++) - tabs[i] = tmp_buf[i]; - ql = mp_div1_dec(tabs, tabs, l, 2, qh & 1); - qh = qh >> 1; /* 0 or 1 */ - if (ql) - rh = mp_add_dec(taba + l, taba + l, tabs + l, h, 0); - else - rh = 0; -#ifdef DEBUG_SQRTREM_DEC - mp_print_str_h_dec("q", tabs, l, qh); - mp_print_str_h_dec("u", taba + l, h, rh); -#endif - - mp_add_ui_dec(tabs + l, qh, h); -#ifdef DEBUG_SQRTREM_DEC - mp_print_str_dec("s2", tabs, n); -#endif - - /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */ - /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */ - if (qh) { - c = qh; - } else { - mp_mul_basecase_dec(taba + n, tabs, l, tabs, l); - c = mp_sub_dec(taba, taba, taba + n, 2 * l, 0); - } - rh -= mp_sub_ui_dec(taba + 2 * l, c, n - 2 * l); - if ((slimb_t)rh < 0) { - mp_sub_ui_dec(tabs, 1, n); - rh += mp_add_mul1_dec(taba, tabs, n, 2); - rh += mp_add_ui_dec(taba, 1, n); - } - return rh; -} - -/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= B/4. Return (s, - r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 * s. tabs has n - limbs. r is returned in the lower n limbs of taba. Its r[n] is the - returned value of the function. */ -int mp_sqrtrem_dec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n) -{ - limb_t tmp_buf1[8]; - limb_t *tmp_buf; - mp_size_t n2; - n2 = n / 2 + 1; - if (n2 <= countof(tmp_buf1)) { - tmp_buf = tmp_buf1; - } else { - tmp_buf = bf_malloc(s, sizeof(limb_t) * n2); - if (!tmp_buf) - return -1; - } - taba[n] = mp_sqrtrem_rec_dec(tabs, taba, n, tmp_buf); - if (tmp_buf != tmp_buf1) - bf_free(s, tmp_buf); - return 0; -} - -/* return the number of leading zero digits, from 0 to LIMB_DIGITS */ -static int clz_dec(limb_t a) -{ - if (a == 0) - return LIMB_DIGITS; - switch(LIMB_BITS - 1 - clz(a)) { - case 0: /* 1-1 */ - return LIMB_DIGITS - 1; - case 1: /* 2-3 */ - return LIMB_DIGITS - 1; - case 2: /* 4-7 */ - return LIMB_DIGITS - 1; - case 3: /* 8-15 */ - if (a < 10) - return LIMB_DIGITS - 1; - else - return LIMB_DIGITS - 2; - case 4: /* 16-31 */ - return LIMB_DIGITS - 2; - case 5: /* 32-63 */ - return LIMB_DIGITS - 2; - case 6: /* 64-127 */ - if (a < 100) - return LIMB_DIGITS - 2; - else - return LIMB_DIGITS - 3; - case 7: /* 128-255 */ - return LIMB_DIGITS - 3; - case 8: /* 256-511 */ - return LIMB_DIGITS - 3; - case 9: /* 512-1023 */ - if (a < 1000) - return LIMB_DIGITS - 3; - else - return LIMB_DIGITS - 4; - case 10: /* 1024-2047 */ - return LIMB_DIGITS - 4; - case 11: /* 2048-4095 */ - return LIMB_DIGITS - 4; - case 12: /* 4096-8191 */ - return LIMB_DIGITS - 4; - case 13: /* 8192-16383 */ - if (a < 10000) - return LIMB_DIGITS - 4; - else - return LIMB_DIGITS - 5; - case 14: /* 16384-32767 */ - return LIMB_DIGITS - 5; - case 15: /* 32768-65535 */ - return LIMB_DIGITS - 5; - case 16: /* 65536-131071 */ - if (a < 100000) - return LIMB_DIGITS - 5; - else - return LIMB_DIGITS - 6; - case 17: /* 131072-262143 */ - return LIMB_DIGITS - 6; - case 18: /* 262144-524287 */ - return LIMB_DIGITS - 6; - case 19: /* 524288-1048575 */ - if (a < 1000000) - return LIMB_DIGITS - 6; - else - return LIMB_DIGITS - 7; - case 20: /* 1048576-2097151 */ - return LIMB_DIGITS - 7; - case 21: /* 2097152-4194303 */ - return LIMB_DIGITS - 7; - case 22: /* 4194304-8388607 */ - return LIMB_DIGITS - 7; - case 23: /* 8388608-16777215 */ - if (a < 10000000) - return LIMB_DIGITS - 7; - else - return LIMB_DIGITS - 8; - case 24: /* 16777216-33554431 */ - return LIMB_DIGITS - 8; - case 25: /* 33554432-67108863 */ - return LIMB_DIGITS - 8; - case 26: /* 67108864-134217727 */ - if (a < 100000000) - return LIMB_DIGITS - 8; - else - return LIMB_DIGITS - 9; -#if LIMB_BITS == 64 - case 27: /* 134217728-268435455 */ - return LIMB_DIGITS - 9; - case 28: /* 268435456-536870911 */ - return LIMB_DIGITS - 9; - case 29: /* 536870912-1073741823 */ - if (a < 1000000000) - return LIMB_DIGITS - 9; - else - return LIMB_DIGITS - 10; - case 30: /* 1073741824-2147483647 */ - return LIMB_DIGITS - 10; - case 31: /* 2147483648-4294967295 */ - return LIMB_DIGITS - 10; - case 32: /* 4294967296-8589934591 */ - return LIMB_DIGITS - 10; - case 33: /* 8589934592-17179869183 */ - if (a < 10000000000) - return LIMB_DIGITS - 10; - else - return LIMB_DIGITS - 11; - case 34: /* 17179869184-34359738367 */ - return LIMB_DIGITS - 11; - case 35: /* 34359738368-68719476735 */ - return LIMB_DIGITS - 11; - case 36: /* 68719476736-137438953471 */ - if (a < 100000000000) - return LIMB_DIGITS - 11; - else - return LIMB_DIGITS - 12; - case 37: /* 137438953472-274877906943 */ - return LIMB_DIGITS - 12; - case 38: /* 274877906944-549755813887 */ - return LIMB_DIGITS - 12; - case 39: /* 549755813888-1099511627775 */ - if (a < 1000000000000) - return LIMB_DIGITS - 12; - else - return LIMB_DIGITS - 13; - case 40: /* 1099511627776-2199023255551 */ - return LIMB_DIGITS - 13; - case 41: /* 2199023255552-4398046511103 */ - return LIMB_DIGITS - 13; - case 42: /* 4398046511104-8796093022207 */ - return LIMB_DIGITS - 13; - case 43: /* 8796093022208-17592186044415 */ - if (a < 10000000000000) - return LIMB_DIGITS - 13; - else - return LIMB_DIGITS - 14; - case 44: /* 17592186044416-35184372088831 */ - return LIMB_DIGITS - 14; - case 45: /* 35184372088832-70368744177663 */ - return LIMB_DIGITS - 14; - case 46: /* 70368744177664-140737488355327 */ - if (a < 100000000000000) - return LIMB_DIGITS - 14; - else - return LIMB_DIGITS - 15; - case 47: /* 140737488355328-281474976710655 */ - return LIMB_DIGITS - 15; - case 48: /* 281474976710656-562949953421311 */ - return LIMB_DIGITS - 15; - case 49: /* 562949953421312-1125899906842623 */ - if (a < 1000000000000000) - return LIMB_DIGITS - 15; - else - return LIMB_DIGITS - 16; - case 50: /* 1125899906842624-2251799813685247 */ - return LIMB_DIGITS - 16; - case 51: /* 2251799813685248-4503599627370495 */ - return LIMB_DIGITS - 16; - case 52: /* 4503599627370496-9007199254740991 */ - return LIMB_DIGITS - 16; - case 53: /* 9007199254740992-18014398509481983 */ - if (a < 10000000000000000) - return LIMB_DIGITS - 16; - else - return LIMB_DIGITS - 17; - case 54: /* 18014398509481984-36028797018963967 */ - return LIMB_DIGITS - 17; - case 55: /* 36028797018963968-72057594037927935 */ - return LIMB_DIGITS - 17; - case 56: /* 72057594037927936-144115188075855871 */ - if (a < 100000000000000000) - return LIMB_DIGITS - 17; - else - return LIMB_DIGITS - 18; - case 57: /* 144115188075855872-288230376151711743 */ - return LIMB_DIGITS - 18; - case 58: /* 288230376151711744-576460752303423487 */ - return LIMB_DIGITS - 18; - case 59: /* 576460752303423488-1152921504606846975 */ - if (a < 1000000000000000000) - return LIMB_DIGITS - 18; - else - return LIMB_DIGITS - 19; -#endif - default: - return 0; - } -} - -/* for debugging */ -void bfdec_print_str(const char *str, const bfdec_t *a) -{ - slimb_t i; - printf("%s=", str); - - if (a->expn == BF_EXP_NAN) { - printf("NaN"); - } else { - if (a->sign) - putchar('-'); - if (a->expn == BF_EXP_ZERO) { - putchar('0'); - } else if (a->expn == BF_EXP_INF) { - printf("Inf"); - } else { - printf("0."); - for(i = a->len - 1; i >= 0; i--) - printf("%0*" PRIu_LIMB, LIMB_DIGITS, a->tab[i]); - printf("e%" PRId_LIMB, a->expn); - } - } - printf("\n"); -} - -/* return != 0 if one digit between 0 and bit_pos inclusive is not zero. */ -static inline limb_t scan_digit_nz(const bfdec_t *r, slimb_t bit_pos) -{ - slimb_t pos; - limb_t v, q; - int shift; - - if (bit_pos < 0) - return 0; - pos = (limb_t)bit_pos / LIMB_DIGITS; - shift = (limb_t)bit_pos % LIMB_DIGITS; - fast_shr_rem_dec(q, v, r->tab[pos], shift + 1); - (void)q; - if (v != 0) - return 1; - pos--; - while (pos >= 0) { - if (r->tab[pos] != 0) - return 1; - pos--; - } - return 0; -} - -static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos) -{ - slimb_t i; - int shift; - i = floor_div(pos, LIMB_DIGITS); - if (i < 0 || i >= len) - return 0; - shift = pos - i * LIMB_DIGITS; - return fast_shr_dec(tab[i], shift) % 10; -} - -/* return the addend for rounding. Note that prec can be <= 0 for bf_rint() */ -static int bfdec_get_rnd_add(int *pret, const bfdec_t *r, limb_t l, - slimb_t prec, int rnd_mode) -{ - int add_one, inexact; - limb_t digit1, digit0; - - // bfdec_print_str("get_rnd_add", r); - if (rnd_mode == BF_RNDF) { - digit0 = 1; /* faithful rounding does not honor the INEXACT flag */ - } else { - /* starting limb for bit 'prec + 1' */ - digit0 = scan_digit_nz(r, l * LIMB_DIGITS - 1 - bf_max(0, prec + 1)); - } - - /* get the digit at 'prec' */ - digit1 = get_digit(r->tab, l, l * LIMB_DIGITS - 1 - prec); - inexact = (digit1 | digit0) != 0; - - add_one = 0; - switch(rnd_mode) { - case BF_RNDZ: - break; - case BF_RNDN: - if (digit1 == 5) { - if (digit0) { - add_one = 1; - } else { - /* round to even */ - add_one = - get_digit(r->tab, l, l * LIMB_DIGITS - 1 - (prec - 1)) & 1; - } - } else if (digit1 > 5) { - add_one = 1; - } - break; - case BF_RNDD: - case BF_RNDU: - if (r->sign == (rnd_mode == BF_RNDD)) - add_one = inexact; - break; - case BF_RNDNA: - case BF_RNDF: - add_one = (digit1 >= 5); - break; - case BF_RNDA: - add_one = inexact; - break; - default: - abort(); - } - - if (inexact) - *pret |= BF_ST_INEXACT; - return add_one; -} - -/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is - assumed to have length 'l' (1 <= l <= r->len). prec1 can be - BF_PREC_INF. BF_FLAG_SUBNORMAL is not supported. Cannot fail with - BF_ST_MEM_ERROR. - */ -static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l) -{ - int shift, add_one, rnd_mode, ret; - slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; - - /* XXX: align to IEEE 754 2008 for decimal numbers ? */ - e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); - e_min = -e_range + 3; - e_max = e_range; - - if (flags & BF_FLAG_RADPNT_PREC) { - /* 'prec' is the precision after the decimal point */ - if (prec1 != BF_PREC_INF) - prec = r->expn + prec1; - else - prec = prec1; - } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { - /* restrict the precision in case of potentially subnormal - result */ - assert(prec1 != BF_PREC_INF); - prec = prec1 - (e_min - r->expn); - } else { - prec = prec1; - } - - /* round to prec bits */ - rnd_mode = flags & BF_RND_MASK; - ret = 0; - add_one = bfdec_get_rnd_add(&ret, r, l, prec, rnd_mode); - - if (prec <= 0) { - if (add_one) { - bfdec_resize(r, 1); /* cannot fail because r is non zero */ - r->tab[0] = BF_DEC_BASE / 10; - r->expn += 1 - prec; - ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; - return ret; - } else { - goto underflow; - } - } else if (add_one) { - limb_t carry; - - /* add one starting at digit 'prec - 1' */ - bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); - pos = bit_pos / LIMB_DIGITS; - carry = mp_pow_dec[bit_pos % LIMB_DIGITS]; - carry = mp_add_ui_dec(r->tab + pos, carry, l - pos); - if (carry) { - /* shift right by one digit */ - mp_shr_dec(r->tab + pos, r->tab + pos, l - pos, 1, 1); - r->expn++; - } - } - - /* check underflow */ - if (unlikely(r->expn < e_min)) { - if (flags & BF_FLAG_SUBNORMAL) { - /* if inexact, also set the underflow flag */ - if (ret & BF_ST_INEXACT) - ret |= BF_ST_UNDERFLOW; - } else { - underflow: - bfdec_set_zero(r, r->sign); - ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; - return ret; - } - } - - /* check overflow */ - if (unlikely(r->expn > e_max)) { - bfdec_set_inf(r, r->sign); - ret |= BF_ST_OVERFLOW | BF_ST_INEXACT; - return ret; - } - - /* keep the bits starting at 'prec - 1' */ - bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); - i = floor_div(bit_pos, LIMB_DIGITS); - if (i >= 0) { - shift = smod(bit_pos, LIMB_DIGITS); - if (shift != 0) { - r->tab[i] = fast_shr_dec(r->tab[i], shift) * - mp_pow_dec[shift]; - } - } else { - i = 0; - } - /* remove trailing zeros */ - while (r->tab[i] == 0) - i++; - if (i > 0) { - l -= i; - memmove(r->tab, r->tab + i, l * sizeof(limb_t)); - } - bfdec_resize(r, l); /* cannot fail */ - return ret; -} - -/* Cannot fail with BF_ST_MEM_ERROR. */ -int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags) -{ - if (r->len == 0) - return 0; - return __bfdec_round(r, prec, flags, r->len); -} - -/* 'r' must be a finite number. Cannot fail with BF_ST_MEM_ERROR. */ -int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags) -{ - limb_t l, v; - int shift, ret; - - // bfdec_print_str("bf_renorm", r); - l = r->len; - while (l > 0 && r->tab[l - 1] == 0) - l--; - if (l == 0) { - /* zero */ - r->expn = BF_EXP_ZERO; - bfdec_resize(r, 0); /* cannot fail */ - ret = 0; - } else { - r->expn -= (r->len - l) * LIMB_DIGITS; - /* shift to have the MSB set to '1' */ - v = r->tab[l - 1]; - shift = clz_dec(v); - if (shift != 0) { - mp_shl_dec(r->tab, r->tab, l, shift, 0); - r->expn -= shift; - } - ret = __bfdec_round(r, prec1, flags, l); - } - // bf_print_str("r_final", r); - return ret; -} - -int bfdec_set_ui(bfdec_t *r, uint64_t v) -{ -#if LIMB_BITS == 32 - if (v >= BF_DEC_BASE * BF_DEC_BASE) { - if (bfdec_resize(r, 3)) - goto fail; - r->tab[0] = v % BF_DEC_BASE; - v /= BF_DEC_BASE; - r->tab[1] = v % BF_DEC_BASE; - r->tab[2] = v / BF_DEC_BASE; - r->expn = 3 * LIMB_DIGITS; - } else -#endif - if (v >= BF_DEC_BASE) { - if (bfdec_resize(r, 2)) - goto fail; - r->tab[0] = v % BF_DEC_BASE; - r->tab[1] = v / BF_DEC_BASE; - r->expn = 2 * LIMB_DIGITS; - } else { - if (bfdec_resize(r, 1)) - goto fail; - r->tab[0] = v; - r->expn = LIMB_DIGITS; - } - r->sign = 0; - return bfdec_normalize_and_round(r, BF_PREC_INF, 0); - fail: - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -int bfdec_set_si(bfdec_t *r, int64_t v) -{ - int ret; - if (v < 0) { - ret = bfdec_set_ui(r, -v); - r->sign = 1; - } else { - ret = bfdec_set_ui(r, v); - } - return ret; -} - -static int bfdec_add_internal(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, bf_flags_t flags, int b_neg) -{ - bf_context_t *s = r->ctx; - int is_sub, cmp_res, a_sign, b_sign, ret; - - a_sign = a->sign; - b_sign = b->sign ^ b_neg; - is_sub = a_sign ^ b_sign; - cmp_res = bfdec_cmpu(a, b); - if (cmp_res < 0) { - const bfdec_t *tmp; - tmp = a; - a = b; - b = tmp; - a_sign = b_sign; /* b_sign is never used later */ - } - /* abs(a) >= abs(b) */ - if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { - /* zero result */ - bfdec_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); - ret = 0; - } else if (a->len == 0 || b->len == 0) { - ret = 0; - if (a->expn >= BF_EXP_INF) { - if (a->expn == BF_EXP_NAN) { - /* at least one operand is NaN */ - bfdec_set_nan(r); - ret = 0; - } else if (b->expn == BF_EXP_INF && is_sub) { - /* infinities with different signs */ - bfdec_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - bfdec_set_inf(r, a_sign); - } - } else { - /* at least one zero and not subtract */ - if (bfdec_set(r, a)) - return BF_ST_MEM_ERROR; - r->sign = a_sign; - goto renorm; - } - } else { - slimb_t d, a_offset, b_offset, i, r_len; - limb_t carry; - limb_t *b1_tab; - int b_shift; - mp_size_t b1_len; - - d = a->expn - b->expn; - - /* XXX: not efficient in time and memory if the precision is - not infinite */ - r_len = bf_max(a->len, b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); - if (bfdec_resize(r, r_len)) - goto fail; - r->sign = a_sign; - r->expn = a->expn; - - a_offset = r_len - a->len; - for(i = 0; i < a_offset; i++) - r->tab[i] = 0; - for(i = 0; i < a->len; i++) - r->tab[a_offset + i] = a->tab[i]; - - b_shift = d % LIMB_DIGITS; - if (b_shift == 0) { - b1_len = b->len; - b1_tab = (limb_t *)b->tab; - } else { - b1_len = b->len + 1; - b1_tab = bf_malloc(s, sizeof(limb_t) * b1_len); - if (!b1_tab) - goto fail; - b1_tab[0] = mp_shr_dec(b1_tab + 1, b->tab, b->len, b_shift, 0) * - mp_pow_dec[LIMB_DIGITS - b_shift]; - } - b_offset = r_len - (b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); - - if (is_sub) { - carry = mp_sub_dec(r->tab + b_offset, r->tab + b_offset, - b1_tab, b1_len, 0); - if (carry != 0) { - carry = mp_sub_ui_dec(r->tab + b_offset + b1_len, carry, - r_len - (b_offset + b1_len)); - assert(carry == 0); - } - } else { - carry = mp_add_dec(r->tab + b_offset, r->tab + b_offset, - b1_tab, b1_len, 0); - if (carry != 0) { - carry = mp_add_ui_dec(r->tab + b_offset + b1_len, carry, - r_len - (b_offset + b1_len)); - } - if (carry != 0) { - if (bfdec_resize(r, r_len + 1)) { - if (b_shift != 0) - bf_free(s, b1_tab); - goto fail; - } - r->tab[r_len] = 1; - r->expn += LIMB_DIGITS; - } - } - if (b_shift != 0) - bf_free(s, b1_tab); - renorm: - ret = bfdec_normalize_and_round(r, prec, flags); - } - return ret; - fail: - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -static int __bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bfdec_add_internal(r, a, b, prec, flags, 0); -} - -static int __bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bfdec_add_internal(r, a, b, prec, flags, 1); -} - -int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, - (bf_op2_func_t *)__bfdec_add); -} - -int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, - (bf_op2_func_t *)__bfdec_sub); -} - -int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - int ret, r_sign; - - if (a->len < b->len) { - const bfdec_t *tmp = a; - a = b; - b = tmp; - } - r_sign = a->sign ^ b->sign; - /* here b->len <= a->len */ - if (b->len == 0) { - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bfdec_set_nan(r); - ret = 0; - } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { - if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || - (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { - bfdec_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - bfdec_set_inf(r, r_sign); - ret = 0; - } - } else { - bfdec_set_zero(r, r_sign); - ret = 0; - } - } else { - bfdec_t tmp, *r1 = NULL; - limb_t a_len, b_len; - limb_t *a_tab, *b_tab; - - a_len = a->len; - b_len = b->len; - a_tab = a->tab; - b_tab = b->tab; - - if (r == a || r == b) { - bfdec_init(r->ctx, &tmp); - r1 = r; - r = &tmp; - } - if (bfdec_resize(r, a_len + b_len)) { - bfdec_set_nan(r); - ret = BF_ST_MEM_ERROR; - goto done; - } - mp_mul_basecase_dec(r->tab, a_tab, a_len, b_tab, b_len); - r->sign = r_sign; - r->expn = a->expn + b->expn; - ret = bfdec_normalize_and_round(r, prec, flags); - done: - if (r == &tmp) - bfdec_move(r1, &tmp); - } - return ret; -} - -int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, - bf_flags_t flags) -{ - bfdec_t b; - int ret; - bfdec_init(r->ctx, &b); - ret = bfdec_set_si(&b, b1); - ret |= bfdec_mul(r, a, &b, prec, flags); - bfdec_delete(&b); - return ret; -} - -int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, - bf_flags_t flags) -{ - bfdec_t b; - int ret; - - bfdec_init(r->ctx, &b); - ret = bfdec_set_si(&b, b1); - ret |= bfdec_add(r, a, &b, prec, flags); - bfdec_delete(&b); - return ret; -} - -static int __bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, - limb_t prec, bf_flags_t flags) -{ - int ret, r_sign; - limb_t n, nb, precl; - - r_sign = a->sign ^ b->sign; - if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) { - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bfdec_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) { - bfdec_set_nan(r); - return BF_ST_INVALID_OP; - } else if (a->expn == BF_EXP_INF) { - bfdec_set_inf(r, r_sign); - return 0; - } else { - bfdec_set_zero(r, r_sign); - return 0; - } - } else if (a->expn == BF_EXP_ZERO) { - if (b->expn == BF_EXP_ZERO) { - bfdec_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bfdec_set_zero(r, r_sign); - return 0; - } - } else if (b->expn == BF_EXP_ZERO) { - bfdec_set_inf(r, r_sign); - return BF_ST_DIVIDE_ZERO; - } - - nb = b->len; - if (prec == BF_PREC_INF) { - /* infinite precision: return BF_ST_INVALID_OP if not an exact - result */ - /* XXX: check */ - precl = nb + 1; - } else if (flags & BF_FLAG_RADPNT_PREC) { - /* number of digits after the decimal point */ - /* XXX: check (2 extra digits for rounding + 2 digits) */ - precl = (bf_max(a->expn - b->expn, 0) + 2 + - prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; - } else { - /* number of limbs of the quotient (2 extra digits for rounding) */ - precl = (prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; - } - n = bf_max(a->len, precl); - - { - limb_t *taba, na, i; - slimb_t d; - - na = n + nb; - taba = bf_malloc(r->ctx, (na + 1) * sizeof(limb_t)); - if (!taba) - goto fail; - d = na - a->len; - memset(taba, 0, d * sizeof(limb_t)); - memcpy(taba + d, a->tab, a->len * sizeof(limb_t)); - if (bfdec_resize(r, n + 1)) - goto fail1; - if (mp_div_dec(r->ctx, r->tab, taba, na, b->tab, nb)) { - fail1: - bf_free(r->ctx, taba); - goto fail; - } - /* see if non zero remainder */ - for(i = 0; i < nb; i++) { - if (taba[i] != 0) - break; - } - bf_free(r->ctx, taba); - if (i != nb) { - if (prec == BF_PREC_INF) { - bfdec_set_nan(r); - return BF_ST_INVALID_OP; - } else { - r->tab[0] |= 1; - } - } - r->expn = a->expn - b->expn + LIMB_DIGITS; - r->sign = r_sign; - ret = bfdec_normalize_and_round(r, prec, flags); - } - return ret; - fail: - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, - (bf_op2_func_t *)__bfdec_div); -} - -/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the - integer defined as floor(a/b) and r = a - q * b. */ -static void bfdec_tdivremu(bf_context_t *s, bfdec_t *q, bfdec_t *r, - const bfdec_t *a, const bfdec_t *b) -{ - if (bfdec_cmpu(a, b) < 0) { - bfdec_set_ui(q, 0); - bfdec_set(r, a); - } else { - bfdec_div(q, a, b, 0, BF_RNDZ | BF_FLAG_RADPNT_PREC); - bfdec_mul(r, q, b, BF_PREC_INF, BF_RNDZ); - bfdec_sub(r, a, r, BF_PREC_INF, BF_RNDZ); - } -} - -/* division and remainder. - - rnd_mode is the rounding mode for the quotient. The additional - rounding mode BF_RND_EUCLIDIAN is supported. - - 'q' is an integer. 'r' is rounded with prec and flags (prec can be - BF_PREC_INF). -*/ -int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, - limb_t prec, bf_flags_t flags, int rnd_mode) -{ - bf_context_t *s = q->ctx; - bfdec_t a1_s, *a1 = &a1_s; - bfdec_t b1_s, *b1 = &b1_s; - bfdec_t r1_s, *r1 = &r1_s; - int q_sign, res; - bool is_ceil, is_rndn; - - assert(q != a && q != b); - assert(r != a && r != b); - assert(q != r); - - if (a->len == 0 || b->len == 0) { - bfdec_set_zero(q, 0); - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bfdec_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) { - bfdec_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bfdec_set(r, a); - return bfdec_round(r, prec, flags); - } - } - - q_sign = a->sign ^ b->sign; - is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); - switch(rnd_mode) { - default: - case BF_RNDZ: - case BF_RNDN: - case BF_RNDNA: - is_ceil = false; - break; - case BF_RNDD: - is_ceil = q_sign; - break; - case BF_RNDU: - is_ceil = q_sign ^ 1; - break; - case BF_RNDA: - is_ceil = true; - break; - case BF_DIVREM_EUCLIDIAN: - is_ceil = a->sign; - break; - } - - a1->expn = a->expn; - a1->tab = a->tab; - a1->len = a->len; - a1->sign = 0; - - b1->expn = b->expn; - b1->tab = b->tab; - b1->len = b->len; - b1->sign = 0; - - // bfdec_print_str("a1", a1); - // bfdec_print_str("b1", b1); - /* XXX: could improve to avoid having a large 'q' */ - bfdec_tdivremu(s, q, r, a1, b1); - if (bfdec_is_nan(q) || bfdec_is_nan(r)) - goto fail; - // bfdec_print_str("q", q); - // bfdec_print_str("r", r); - - if (r->len != 0) { - if (is_rndn) { - bfdec_init(s, r1); - if (bfdec_set(r1, r)) - goto fail; - if (bfdec_mul_si(r1, r1, 2, BF_PREC_INF, BF_RNDZ)) { - bfdec_delete(r1); - goto fail; - } - res = bfdec_cmpu(r1, b); - bfdec_delete(r1); - if (res > 0 || - (res == 0 && - (rnd_mode == BF_RNDNA || - (get_digit(q->tab, q->len, q->len * LIMB_DIGITS - q->expn) & 1) != 0))) { - goto do_sub_r; - } - } else if (is_ceil) { - do_sub_r: - res = bfdec_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ); - res |= bfdec_sub(r, r, b1, BF_PREC_INF, BF_RNDZ); - if (res & BF_ST_MEM_ERROR) - goto fail; - } - } - - r->sign ^= a->sign; - q->sign = q_sign; - return bfdec_round(r, prec, flags); - fail: - bfdec_set_nan(q); - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode) -{ - bfdec_t q_s, *q = &q_s; - int ret; - - bfdec_init(r->ctx, q); - ret = bfdec_divrem(q, r, a, b, prec, flags, rnd_mode); - bfdec_delete(q); - return ret; -} - -/* convert to integer (infinite precision) */ -int bfdec_rint(bfdec_t *r, int rnd_mode) -{ - return bfdec_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); -} - -int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = a->ctx; - int ret, k; - limb_t *a1, v; - slimb_t n, n1, prec1; - limb_t res; - - assert(r != a); - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bfdec_set_nan(r); - } else if (a->expn == BF_EXP_INF && a->sign) { - goto invalid_op; - } else { - bfdec_set(r, a); - } - ret = 0; - } else if (a->sign || prec == BF_PREC_INF) { - invalid_op: - bfdec_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - if (flags & BF_FLAG_RADPNT_PREC) { - prec1 = bf_max(floor_div(a->expn + 1, 2) + prec, 1); - } else { - prec1 = prec; - } - /* convert the mantissa to an integer with at least 2 * - prec + 4 digits */ - n = (2 * (prec1 + 2) + 2 * LIMB_DIGITS - 1) / (2 * LIMB_DIGITS); - if (bfdec_resize(r, n)) - goto fail; - a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); - if (!a1) - goto fail; - n1 = bf_min(2 * n, a->len); - memset(a1, 0, (2 * n - n1) * sizeof(limb_t)); - memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t)); - if (a->expn & 1) { - res = mp_shr_dec(a1, a1, 2 * n, 1, 0); - } else { - res = 0; - } - /* normalize so that a1 >= B^(2*n)/4. Not need for n = 1 - because mp_sqrtrem2_dec already does it */ - k = 0; - if (n > 1) { - v = a1[2 * n - 1]; - while (v < BF_DEC_BASE / 4) { - k++; - v *= 4; - } - if (k != 0) - mp_mul1_dec(a1, a1, 2 * n, 1 << (2 * k), 0); - } - if (mp_sqrtrem_dec(s, r->tab, a1, n)) { - bf_free(s, a1); - goto fail; - } - if (k != 0) - mp_div1_dec(r->tab, r->tab, n, 1 << k, 0); - if (!res) { - res = mp_scan_nz(a1, n + 1); - } - bf_free(s, a1); - if (!res) { - res = mp_scan_nz(a->tab, a->len - n1); - } - if (res != 0) - r->tab[0] |= 1; - r->sign = 0; - r->expn = (a->expn + 1) >> 1; - ret = bfdec_round(r, prec, flags); - } - return ret; - fail: - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -/* The rounding mode is always BF_RNDZ. Return BF_ST_OVERFLOW if there - is an overflow and 0 otherwise. No memory error is possible. */ -int bfdec_get_int32(int *pres, const bfdec_t *a) -{ - uint32_t v; - int ret; - if (a->expn >= BF_EXP_INF) { - ret = 0; - if (a->expn == BF_EXP_INF) { - v = (uint32_t)INT32_MAX + a->sign; - /* XXX: return overflow ? */ - } else { - v = INT32_MAX; - } - } else if (a->expn <= 0) { - v = 0; - ret = 0; - } else if (a->expn <= 9) { - v = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); - if (a->sign) - v = -v; - ret = 0; - } else if (a->expn == 10) { - uint64_t v1; - uint32_t v_max; -#if LIMB_BITS == 64 - v1 = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); -#else - v1 = (uint64_t)a->tab[a->len - 1] * 10 + - get_digit(a->tab, a->len, (a->len - 1) * LIMB_DIGITS - 1); -#endif - v_max = (uint32_t)INT32_MAX + a->sign; - if (v1 > v_max) { - v = v_max; - ret = BF_ST_OVERFLOW; - } else { - v = v1; - if (a->sign) - v = -v; - ret = 0; - } - } else { - v = (uint32_t)INT32_MAX + a->sign; - ret = BF_ST_OVERFLOW; - } - *pres = v; - return ret; -} - -/* power to an integer with infinite precision */ -int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b) -{ - int ret, n_bits, i; - - assert(r != a); - if (b == 0) - return bfdec_set_ui(r, 1); - ret = bfdec_set(r, a); - n_bits = LIMB_BITS - clz(b); - for(i = n_bits - 2; i >= 0; i--) { - ret |= bfdec_mul(r, r, r, BF_PREC_INF, BF_RNDZ); - if ((b >> i) & 1) - ret |= bfdec_mul(r, r, a, BF_PREC_INF, BF_RNDZ); - } - return ret; -} - -char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags) -{ - return bf_ftoa_internal(plen, (const bf_t *)a, 10, prec, flags, true); -} - -int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, - limb_t prec, bf_flags_t flags) -{ - slimb_t dummy_exp; - return bf_atof_internal((bf_t *)r, &dummy_exp, str, pnext, 10, prec, - flags, true); -} - -#endif /* USE_BF_DEC */ - -#ifdef USE_FFT_MUL -/***************************************************************/ -/* Integer multiplication with FFT */ - -/* or LIMB_BITS at bit position 'pos' in tab */ -static inline void put_bits(limb_t *tab, limb_t len, slimb_t pos, limb_t val) -{ - limb_t i; - int p; - - i = pos >> LIMB_LOG2_BITS; - p = pos & (LIMB_BITS - 1); - if (i < len) - tab[i] |= val << p; - if (p != 0) { - i++; - if (i < len) { - tab[i] |= val >> (LIMB_BITS - p); - } - } -} - -#if defined(__AVX2__) - -typedef double NTTLimb; - -/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */ -#define NTT_MOD_LOG2_MIN 50 -#define NTT_MOD_LOG2_MAX 51 -#define NB_MODS 5 -#define NTT_PROOT_2EXP 39 -static const int ntt_int_bits[NB_MODS] = { 254, 203, 152, 101, 50, }; - -static const limb_t ntt_mods[NB_MODS] = { 0x00073a8000000001, 0x0007858000000001, 0x0007a38000000001, 0x0007a68000000001, 0x0007fd8000000001, -}; - -static const limb_t ntt_proot[2][NB_MODS] = { - { 0x00056198d44332c8, 0x0002eb5d640aad39, 0x00047e31eaa35fd0, 0x0005271ac118a150, 0x00075e0ce8442bd5, }, - { 0x000461169761bcc5, 0x0002dac3cb2da688, 0x0004abc97751e3bf, 0x000656778fc8c485, 0x0000dc6469c269fa, }, -}; - -static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { - 0x00020e4da740da8e, 0x0004c3dc09c09c1d, 0x000063bd097b4271, 0x000799d8f18f18fd, - 0x0005384222222264, 0x000572b07c1f07fe, 0x00035cd08888889a, - 0x00066015555557e3, 0x000725960b60b623, - 0x0002fc1fa1d6ce12, -}; - -#else - -typedef limb_t NTTLimb; - -#if LIMB_BITS == 64 - -#define NTT_MOD_LOG2_MIN 61 -#define NTT_MOD_LOG2_MAX 62 -#define NB_MODS 5 -#define NTT_PROOT_2EXP 51 -static const int ntt_int_bits[NB_MODS] = { 307, 246, 185, 123, 61, }; - -static const limb_t ntt_mods[NB_MODS] = { 0x28d8000000000001, 0x2a88000000000001, 0x2ed8000000000001, 0x3508000000000001, 0x3aa8000000000001, -}; - -static const limb_t ntt_proot[2][NB_MODS] = { - { 0x1b8ea61034a2bea7, 0x21a9762de58206fb, 0x02ca782f0756a8ea, 0x278384537a3e50a1, 0x106e13fee74ce0ab, }, - { 0x233513af133e13b8, 0x1d13140d1c6f75f1, 0x12cde57f97e3eeda, 0x0d6149e23cbe654f, 0x36cd204f522a1379, }, -}; - -static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { - 0x08a9ed097b425eea, 0x18a44aaaaaaaaab3, 0x2493f57f57f57f5d, 0x126b8d0649a7f8d4, - 0x09d80ed7303b5ccc, 0x25b8bcf3cf3cf3d5, 0x2ce6ce63398ce638, - 0x0e31fad40a57eb59, 0x02a3529fd4a7f52f, - 0x3a5493e93e93e94a, -}; - -#elif LIMB_BITS == 32 - -/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */ -#define NTT_MOD_LOG2_MIN 29 -#define NTT_MOD_LOG2_MAX 30 -#define NB_MODS 5 -#define NTT_PROOT_2EXP 20 -static const int ntt_int_bits[NB_MODS] = { 148, 119, 89, 59, 29, }; - -static const limb_t ntt_mods[NB_MODS] = { 0x0000000032b00001, 0x0000000033700001, 0x0000000036d00001, 0x0000000037300001, 0x000000003e500001, -}; - -static const limb_t ntt_proot[2][NB_MODS] = { - { 0x0000000032525f31, 0x0000000005eb3b37, 0x00000000246eda9f, 0x0000000035f25901, 0x00000000022f5768, }, - { 0x00000000051eba1a, 0x00000000107be10e, 0x000000001cd574e0, 0x00000000053806e6, 0x000000002cd6bf98, }, -}; - -static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { - 0x000000000449559a, 0x000000001eba6ca9, 0x000000002ec18e46, 0x000000000860160b, - 0x000000000d321307, 0x000000000bf51120, 0x000000000f662938, - 0x000000000932ab3e, 0x000000002f40eef8, - 0x000000002e760905, -}; - -#endif /* LIMB_BITS */ - -#endif /* !AVX2 */ - -#if defined(__AVX2__) -#define NTT_TRIG_K_MAX 18 -#else -#define NTT_TRIG_K_MAX 19 -#endif - -typedef struct BFNTTState { - bf_context_t *ctx; - - /* used for mul_mod_fast() */ - limb_t ntt_mods_div[NB_MODS]; - - limb_t ntt_proot_pow[NB_MODS][2][NTT_PROOT_2EXP + 1]; - limb_t ntt_proot_pow_inv[NB_MODS][2][NTT_PROOT_2EXP + 1]; - NTTLimb *ntt_trig[NB_MODS][2][NTT_TRIG_K_MAX + 1]; - /* 1/2^n mod m */ - limb_t ntt_len_inv[NB_MODS][NTT_PROOT_2EXP + 1][2]; -#if defined(__AVX2__) - __m256d ntt_mods_cr_vec[NB_MODS * (NB_MODS - 1) / 2]; - __m256d ntt_mods_vec[NB_MODS]; - __m256d ntt_mods_inv_vec[NB_MODS]; -#else - limb_t ntt_mods_cr_inv[NB_MODS * (NB_MODS - 1) / 2]; -#endif -} BFNTTState; - -static NTTLimb *get_trig(BFNTTState *s, int k, int inverse, int m_idx); - -/* add modulo with up to (LIMB_BITS-1) bit modulo */ -static inline limb_t add_mod(limb_t a, limb_t b, limb_t m) -{ - limb_t r; - r = a + b; - if (r >= m) - r -= m; - return r; -} - -/* sub modulo with up to LIMB_BITS bit modulo */ -static inline limb_t sub_mod(limb_t a, limb_t b, limb_t m) -{ - limb_t r; - r = a - b; - if (r > a) - r += m; - return r; -} - -/* return (r0+r1*B) mod m - precondition: 0 <= r0+r1*B < 2^(64+NTT_MOD_LOG2_MIN) -*/ -static inline limb_t mod_fast(dlimb_t r, - limb_t m, limb_t m_inv) -{ - limb_t a1, q, t0, r1, r0; - - a1 = r >> NTT_MOD_LOG2_MIN; - - q = ((dlimb_t)a1 * m_inv) >> LIMB_BITS; - r = r - (dlimb_t)q * m - m * 2; - r1 = r >> LIMB_BITS; - t0 = (slimb_t)r1 >> 1; - r += m & t0; - r0 = r; - r1 = r >> LIMB_BITS; - r0 += m & r1; - return r0; -} - -/* faster version using precomputed modulo inverse. - precondition: 0 <= a * b < 2^(64+NTT_MOD_LOG2_MIN) */ -static inline limb_t mul_mod_fast(limb_t a, limb_t b, - limb_t m, limb_t m_inv) -{ - dlimb_t r; - r = (dlimb_t)a * (dlimb_t)b; - return mod_fast(r, m, m_inv); -} - -static inline limb_t init_mul_mod_fast(limb_t m) -{ - dlimb_t t; - assert(m < (limb_t)1 << NTT_MOD_LOG2_MAX); - assert(m >= (limb_t)1 << NTT_MOD_LOG2_MIN); - t = (dlimb_t)1 << (LIMB_BITS + NTT_MOD_LOG2_MIN); - return t / m; -} - -/* Faster version used when the multiplier is constant. 0 <= a < 2^64, - 0 <= b < m. */ -static inline limb_t mul_mod_fast2(limb_t a, limb_t b, - limb_t m, limb_t b_inv) -{ - limb_t r, q; - - q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS; - r = a * b - q * m; - if (r >= m) - r -= m; - return r; -} - -/* Faster version used when the multiplier is constant. 0 <= a < 2^64, - 0 <= b < m. Let r = a * b mod m. The return value is 'r' or 'r + - m'. */ -static inline limb_t mul_mod_fast3(limb_t a, limb_t b, - limb_t m, limb_t b_inv) -{ - limb_t r, q; - - q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS; - r = a * b - q * m; - return r; -} - -static inline limb_t init_mul_mod_fast2(limb_t b, limb_t m) -{ - return ((dlimb_t)b << LIMB_BITS) / m; -} - -#ifdef __AVX2__ - -static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m) -{ - slimb_t v; - v = a; - if (v < 0) - v += m; - if (v >= m) - v -= m; - return v; -} - -static inline NTTLimb int_to_ntt_limb(limb_t a, limb_t m) -{ - return (slimb_t)a; -} - -static inline NTTLimb int_to_ntt_limb2(limb_t a, limb_t m) -{ - if (a >= (m / 2)) - a -= m; - return (slimb_t)a; -} - -/* return r + m if r < 0 otherwise r. */ -static inline __m256d ntt_mod1(__m256d r, __m256d m) -{ - return _mm256_blendv_pd(r, r + m, r); -} - -/* input: abs(r) < 2 * m. Output: abs(r) < m */ -static inline __m256d ntt_mod(__m256d r, __m256d mf, __m256d m2f) -{ - return _mm256_blendv_pd(r, r + m2f, r) - mf; -} - -/* input: abs(a*b) < 2 * m^2, output: abs(r) < m */ -static inline __m256d ntt_mul_mod(__m256d a, __m256d b, __m256d mf, - __m256d m_inv) -{ - __m256d r, q, ab1, ab0, qm0, qm1; - ab1 = a * b; - q = _mm256_round_pd(ab1 * m_inv, 0); /* round to nearest */ - qm1 = q * mf; - qm0 = _mm256_fmsub_pd(q, mf, qm1); /* low part */ - ab0 = _mm256_fmsub_pd(a, b, ab1); /* low part */ - r = (ab1 - qm1) + (ab0 - qm0); - return r; -} - -static void *bf_aligned_malloc(bf_context_t *s, size_t size, size_t align) -{ - void *ptr; - void **ptr1; - ptr = bf_malloc(s, size + sizeof(void *) + align - 1); - if (!ptr) - return NULL; - ptr1 = (void **)(((uintptr_t)ptr + sizeof(void *) + align - 1) & - ~(align - 1)); - ptr1[-1] = ptr; - return ptr1; -} - -static void bf_aligned_free(bf_context_t *s, void *ptr) -{ - if (!ptr) - return; - bf_free(s, ((void **)ptr)[-1]); -} - -static void *ntt_malloc(BFNTTState *s, size_t size) -{ - return bf_aligned_malloc(s->ctx, size, 64); -} - -static void ntt_free(BFNTTState *s, void *ptr) -{ - bf_aligned_free(s->ctx, ptr); -} - -static no_inline int ntt_fft(BFNTTState *s, - NTTLimb *out_buf, NTTLimb *in_buf, - NTTLimb *tmp_buf, int fft_len_log2, - int inverse, int m_idx) -{ - limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j; - NTTLimb *tab_in, *tab_out, *tmp, *trig; - __m256d m_inv, mf, m2f, c, a0, a1, b0, b1; - limb_t m; - int l; - - m = ntt_mods[m_idx]; - - m_inv = _mm256_set1_pd(1.0 / (double)m); - mf = _mm256_set1_pd(m); - m2f = _mm256_set1_pd(m * 2); - - n = (limb_t)1 << fft_len_log2; - assert(n >= 8); - stride_in = n / 2; - - tab_in = in_buf; - tab_out = tmp_buf; - trig = get_trig(s, fft_len_log2, inverse, m_idx); - if (!trig) - return -1; - p = 0; - for(k = 0; k < stride_in; k += 4) { - a0 = _mm256_load_pd(&tab_in[k]); - a1 = _mm256_load_pd(&tab_in[k + stride_in]); - c = _mm256_load_pd(trig); - trig += 4; - b0 = ntt_mod(a0 + a1, mf, m2f); - b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); - a0 = _mm256_permute2f128_pd(b0, b1, 0x20); - a1 = _mm256_permute2f128_pd(b0, b1, 0x31); - a0 = _mm256_permute4x64_pd(a0, 0xd8); - a1 = _mm256_permute4x64_pd(a1, 0xd8); - _mm256_store_pd(&tab_out[p], a0); - _mm256_store_pd(&tab_out[p + 4], a1); - p += 2 * 4; - } - tmp = tab_in; - tab_in = tab_out; - tab_out = tmp; - - trig = get_trig(s, fft_len_log2 - 1, inverse, m_idx); - if (!trig) - return -1; - p = 0; - for(k = 0; k < stride_in; k += 4) { - a0 = _mm256_load_pd(&tab_in[k]); - a1 = _mm256_load_pd(&tab_in[k + stride_in]); - c = _mm256_setr_pd(trig[0], trig[0], trig[1], trig[1]); - trig += 2; - b0 = ntt_mod(a0 + a1, mf, m2f); - b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); - a0 = _mm256_permute2f128_pd(b0, b1, 0x20); - a1 = _mm256_permute2f128_pd(b0, b1, 0x31); - _mm256_store_pd(&tab_out[p], a0); - _mm256_store_pd(&tab_out[p + 4], a1); - p += 2 * 4; - } - tmp = tab_in; - tab_in = tab_out; - tab_out = tmp; - - nb_blocks = n / 4; - fft_per_block = 4; - - l = fft_len_log2 - 2; - while (nb_blocks != 2) { - nb_blocks >>= 1; - p = 0; - k = 0; - trig = get_trig(s, l, inverse, m_idx); - if (!trig) - return -1; - for(i = 0; i < nb_blocks; i++) { - c = _mm256_set1_pd(trig[0]); - trig++; - for(j = 0; j < fft_per_block; j += 4) { - a0 = _mm256_load_pd(&tab_in[k + j]); - a1 = _mm256_load_pd(&tab_in[k + j + stride_in]); - b0 = ntt_mod(a0 + a1, mf, m2f); - b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); - _mm256_store_pd(&tab_out[p + j], b0); - _mm256_store_pd(&tab_out[p + j + fft_per_block], b1); - } - k += fft_per_block; - p += 2 * fft_per_block; - } - fft_per_block <<= 1; - l--; - tmp = tab_in; - tab_in = tab_out; - tab_out = tmp; - } - - tab_out = out_buf; - for(k = 0; k < stride_in; k += 4) { - a0 = _mm256_load_pd(&tab_in[k]); - a1 = _mm256_load_pd(&tab_in[k + stride_in]); - b0 = ntt_mod(a0 + a1, mf, m2f); - b1 = ntt_mod(a0 - a1, mf, m2f); - _mm256_store_pd(&tab_out[k], b0); - _mm256_store_pd(&tab_out[k + stride_in], b1); - } - return 0; -} - -static void ntt_vec_mul(BFNTTState *s, - NTTLimb *tab1, NTTLimb *tab2, limb_t fft_len_log2, - int k_tot, int m_idx) -{ - limb_t i, c_inv, n, m; - __m256d m_inv, mf, a, b, c; - - m = ntt_mods[m_idx]; - c_inv = s->ntt_len_inv[m_idx][k_tot][0]; - m_inv = _mm256_set1_pd(1.0 / (double)m); - mf = _mm256_set1_pd(m); - c = _mm256_set1_pd(int_to_ntt_limb(c_inv, m)); - n = (limb_t)1 << fft_len_log2; - for(i = 0; i < n; i += 4) { - a = _mm256_load_pd(&tab1[i]); - b = _mm256_load_pd(&tab2[i]); - a = ntt_mul_mod(a, b, mf, m_inv); - a = ntt_mul_mod(a, c, mf, m_inv); - _mm256_store_pd(&tab1[i], a); - } -} - -static no_inline void mul_trig(NTTLimb *buf, - limb_t n, limb_t c1, limb_t m, limb_t m_inv1) -{ - limb_t i, c2, c3, c4; - __m256d c, c_mul, a0, mf, m_inv; - assert(n >= 2); - - mf = _mm256_set1_pd(m); - m_inv = _mm256_set1_pd(1.0 / (double)m); - - c2 = mul_mod_fast(c1, c1, m, m_inv1); - c3 = mul_mod_fast(c2, c1, m, m_inv1); - c4 = mul_mod_fast(c2, c2, m, m_inv1); - c = _mm256_setr_pd(1, int_to_ntt_limb(c1, m), - int_to_ntt_limb(c2, m), int_to_ntt_limb(c3, m)); - c_mul = _mm256_set1_pd(int_to_ntt_limb(c4, m)); - for(i = 0; i < n; i += 4) { - a0 = _mm256_load_pd(&buf[i]); - a0 = ntt_mul_mod(a0, c, mf, m_inv); - _mm256_store_pd(&buf[i], a0); - c = ntt_mul_mod(c, c_mul, mf, m_inv); - } -} - -#else - -static void *ntt_malloc(BFNTTState *s, size_t size) -{ - return bf_malloc(s->ctx, size); -} - -static void ntt_free(BFNTTState *s, void *ptr) -{ - bf_free(s->ctx, ptr); -} - -static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m) -{ - if (a >= m) - a -= m; - return a; -} - -static inline NTTLimb int_to_ntt_limb(slimb_t a, limb_t m) -{ - return a; -} - -static no_inline int ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf, - NTTLimb *tmp_buf, int fft_len_log2, - int inverse, int m_idx) -{ - limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j, m, m2; - NTTLimb *tab_in, *tab_out, *tmp, a0, a1, b0, b1, c, *trig, c_inv; - int l; - - m = ntt_mods[m_idx]; - m2 = 2 * m; - n = (limb_t)1 << fft_len_log2; - nb_blocks = n; - fft_per_block = 1; - stride_in = n / 2; - tab_in = in_buf; - tab_out = tmp_buf; - l = fft_len_log2; - while (nb_blocks != 2) { - nb_blocks >>= 1; - p = 0; - k = 0; - trig = get_trig(s, l, inverse, m_idx); - if (!trig) - return -1; - for(i = 0; i < nb_blocks; i++) { - c = trig[0]; - c_inv = trig[1]; - trig += 2; - for(j = 0; j < fft_per_block; j++) { - a0 = tab_in[k + j]; - a1 = tab_in[k + j + stride_in]; - b0 = add_mod(a0, a1, m2); - b1 = a0 - a1 + m2; - b1 = mul_mod_fast3(b1, c, m, c_inv); - tab_out[p + j] = b0; - tab_out[p + j + fft_per_block] = b1; - } - k += fft_per_block; - p += 2 * fft_per_block; - } - fft_per_block <<= 1; - l--; - tmp = tab_in; - tab_in = tab_out; - tab_out = tmp; - } - /* no twiddle in last step */ - tab_out = out_buf; - for(k = 0; k < stride_in; k++) { - a0 = tab_in[k]; - a1 = tab_in[k + stride_in]; - b0 = add_mod(a0, a1, m2); - b1 = sub_mod(a0, a1, m2); - tab_out[k] = b0; - tab_out[k + stride_in] = b1; - } - return 0; -} - -static void ntt_vec_mul(BFNTTState *s, - NTTLimb *tab1, NTTLimb *tab2, int fft_len_log2, - int k_tot, int m_idx) -{ - limb_t i, norm, norm_inv, a, n, m, m_inv; - - m = ntt_mods[m_idx]; - m_inv = s->ntt_mods_div[m_idx]; - norm = s->ntt_len_inv[m_idx][k_tot][0]; - norm_inv = s->ntt_len_inv[m_idx][k_tot][1]; - n = (limb_t)1 << fft_len_log2; - for(i = 0; i < n; i++) { - a = tab1[i]; - /* need to reduce the range so that the product is < - 2^(LIMB_BITS+NTT_MOD_LOG2_MIN) */ - if (a >= m) - a -= m; - a = mul_mod_fast(a, tab2[i], m, m_inv); - a = mul_mod_fast3(a, norm, m, norm_inv); - tab1[i] = a; - } -} - -static no_inline void mul_trig(NTTLimb *buf, - limb_t n, limb_t c_mul, limb_t m, limb_t m_inv) -{ - limb_t i, c0, c_mul_inv; - - c0 = 1; - c_mul_inv = init_mul_mod_fast2(c_mul, m); - for(i = 0; i < n; i++) { - buf[i] = mul_mod_fast(buf[i], c0, m, m_inv); - c0 = mul_mod_fast2(c0, c_mul, m, c_mul_inv); - } -} - -#endif /* !AVX2 */ - -static no_inline NTTLimb *get_trig(BFNTTState *s, - int k, int inverse, int m_idx) -{ - NTTLimb *tab; - limb_t i, n2, c, c_mul, m, c_mul_inv; - - if (k > NTT_TRIG_K_MAX) - return NULL; - - tab = s->ntt_trig[m_idx][inverse][k]; - if (tab) - return tab; - n2 = (limb_t)1 << (k - 1); - m = ntt_mods[m_idx]; -#ifdef __AVX2__ - tab = ntt_malloc(s, sizeof(NTTLimb) * n2); -#else - tab = ntt_malloc(s, sizeof(NTTLimb) * n2 * 2); -#endif - if (!tab) - return NULL; - c = 1; - c_mul = s->ntt_proot_pow[m_idx][inverse][k]; - c_mul_inv = s->ntt_proot_pow_inv[m_idx][inverse][k]; - for(i = 0; i < n2; i++) { -#ifdef __AVX2__ - tab[i] = int_to_ntt_limb2(c, m); -#else - tab[2 * i] = int_to_ntt_limb(c, m); - tab[2 * i + 1] = init_mul_mod_fast2(c, m); -#endif - c = mul_mod_fast2(c, c_mul, m, c_mul_inv); - } - s->ntt_trig[m_idx][inverse][k] = tab; - return tab; -} - -void fft_clear_cache(bf_context_t *s1) -{ - int m_idx, inverse, k; - BFNTTState *s = s1->ntt_state; - if (s) { - for(m_idx = 0; m_idx < NB_MODS; m_idx++) { - for(inverse = 0; inverse < 2; inverse++) { - for(k = 0; k < NTT_TRIG_K_MAX + 1; k++) { - if (s->ntt_trig[m_idx][inverse][k]) { - ntt_free(s, s->ntt_trig[m_idx][inverse][k]); - s->ntt_trig[m_idx][inverse][k] = NULL; - } - } - } - } -#if defined(__AVX2__) - bf_aligned_free(s1, s); -#else - bf_free(s1, s); -#endif - s1->ntt_state = NULL; - } -} - -#define STRIP_LEN 16 - -/* dst = buf1, src = buf2 */ -static int ntt_fft_partial(BFNTTState *s, NTTLimb *buf1, - int k1, int k2, limb_t n1, limb_t n2, int inverse, - limb_t m_idx) -{ - limb_t i, j, c_mul, c0, m, m_inv, strip_len, l; - NTTLimb *buf2, *buf3; - - buf2 = NULL; - buf3 = ntt_malloc(s, sizeof(NTTLimb) * n1); - if (!buf3) - goto fail; - if (k2 == 0) { - if (ntt_fft(s, buf1, buf1, buf3, k1, inverse, m_idx)) - goto fail; - } else { - strip_len = STRIP_LEN; - buf2 = ntt_malloc(s, sizeof(NTTLimb) * n1 * strip_len); - if (!buf2) - goto fail; - m = ntt_mods[m_idx]; - m_inv = s->ntt_mods_div[m_idx]; - c0 = s->ntt_proot_pow[m_idx][inverse][k1 + k2]; - c_mul = 1; - assert((n2 % strip_len) == 0); - for(j = 0; j < n2; j += strip_len) { - for(i = 0; i < n1; i++) { - for(l = 0; l < strip_len; l++) { - buf2[i + l * n1] = buf1[i * n2 + (j + l)]; - } - } - for(l = 0; l < strip_len; l++) { - if (inverse) - mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv); - if (ntt_fft(s, buf2 + l * n1, buf2 + l * n1, buf3, k1, inverse, m_idx)) - goto fail; - if (!inverse) - mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv); - c_mul = mul_mod_fast(c_mul, c0, m, m_inv); - } - - for(i = 0; i < n1; i++) { - for(l = 0; l < strip_len; l++) { - buf1[i * n2 + (j + l)] = buf2[i + l *n1]; - } - } - } - ntt_free(s, buf2); - } - ntt_free(s, buf3); - return 0; - fail: - ntt_free(s, buf2); - ntt_free(s, buf3); - return -1; -} - - -/* dst = buf1, src = buf2, tmp = buf3 */ -static int ntt_conv(BFNTTState *s, NTTLimb *buf1, NTTLimb *buf2, - int k, int k_tot, limb_t m_idx) -{ - limb_t n1, n2, i; - int k1, k2; - - if (k <= NTT_TRIG_K_MAX) { - k1 = k; - } else { - /* recursive split of the FFT */ - k1 = bf_min(k / 2, NTT_TRIG_K_MAX); - } - k2 = k - k1; - n1 = (limb_t)1 << k1; - n2 = (limb_t)1 << k2; - - if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 0, m_idx)) - return -1; - if (ntt_fft_partial(s, buf2, k1, k2, n1, n2, 0, m_idx)) - return -1; - if (k2 == 0) { - ntt_vec_mul(s, buf1, buf2, k, k_tot, m_idx); - } else { - for(i = 0; i < n1; i++) { - ntt_conv(s, buf1 + i * n2, buf2 + i * n2, k2, k_tot, m_idx); - } - } - if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 1, m_idx)) - return -1; - return 0; -} - - -static no_inline void limb_to_ntt(BFNTTState *s, - NTTLimb *tabr, limb_t fft_len, - const limb_t *taba, limb_t a_len, int dpl, - int first_m_idx, int nb_mods) -{ - slimb_t i, n; - dlimb_t a, b; - int j, shift; - limb_t base_mask1, a0, a1, a2, r, m, m_inv; - - memset(tabr, 0, sizeof(NTTLimb) * fft_len * nb_mods); - shift = dpl & (LIMB_BITS - 1); - if (shift == 0) - base_mask1 = -1; - else - base_mask1 = ((limb_t)1 << shift) - 1; - n = bf_min(fft_len, (a_len * LIMB_BITS + dpl - 1) / dpl); - for(i = 0; i < n; i++) { - a0 = get_bits(taba, a_len, i * dpl); - if (dpl <= LIMB_BITS) { - a0 &= base_mask1; - a = a0; - } else { - a1 = get_bits(taba, a_len, i * dpl + LIMB_BITS); - if (dpl <= (LIMB_BITS + NTT_MOD_LOG2_MIN)) { - a = a0 | ((dlimb_t)(a1 & base_mask1) << LIMB_BITS); - } else { - if (dpl > 2 * LIMB_BITS) { - a2 = get_bits(taba, a_len, i * dpl + LIMB_BITS * 2) & - base_mask1; - } else { - a1 &= base_mask1; - a2 = 0; - } - // printf("a=0x%016lx%016lx%016lx\n", a2, a1, a0); - a = (a0 >> (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | - ((dlimb_t)a1 << (NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)) | - ((dlimb_t)a2 << (LIMB_BITS + NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)); - a0 &= ((limb_t)1 << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) - 1; - } - } - for(j = 0; j < nb_mods; j++) { - m = ntt_mods[first_m_idx + j]; - m_inv = s->ntt_mods_div[first_m_idx + j]; - r = mod_fast(a, m, m_inv); - if (dpl > (LIMB_BITS + NTT_MOD_LOG2_MIN)) { - b = ((dlimb_t)r << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | a0; - r = mod_fast(b, m, m_inv); - } - tabr[i + j * fft_len] = int_to_ntt_limb(r, m); - } - } -} - -#if defined(__AVX2__) - -#define VEC_LEN 4 - -typedef union { - __m256d v; - double d[4]; -} VecUnion; - -static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len, - const NTTLimb *buf, int fft_len_log2, int dpl, - int nb_mods) -{ - const limb_t *mods = ntt_mods + NB_MODS - nb_mods; - const __m256d *mods_cr_vec, *mf, *m_inv; - VecUnion y[NB_MODS]; - limb_t u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r; - slimb_t i, len, pos; - int j, k, l, shift, n_limb1, p; - dlimb_t t; - - j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2; - mods_cr_vec = s->ntt_mods_cr_vec + j; - mf = s->ntt_mods_vec + NB_MODS - nb_mods; - m_inv = s->ntt_mods_inv_vec + NB_MODS - nb_mods; - - shift = dpl & (LIMB_BITS - 1); - if (shift == 0) - base_mask1 = -1; - else - base_mask1 = ((limb_t)1 << shift) - 1; - n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS; - for(j = 0; j < NB_MODS; j++) - carry[j] = 0; - for(j = 0; j < NB_MODS; j++) - u[j] = 0; /* avoid warnings */ - memset(tabr, 0, sizeof(limb_t) * r_len); - fft_len = (limb_t)1 << fft_len_log2; - len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl); - len = (len + VEC_LEN - 1) & ~(VEC_LEN - 1); - i = 0; - while (i < len) { - for(j = 0; j < nb_mods; j++) - y[j].v = *(__m256d *)&buf[i + fft_len * j]; - - /* Chinese remainder to get mixed radix representation */ - l = 0; - for(j = 0; j < nb_mods - 1; j++) { - y[j].v = ntt_mod1(y[j].v, mf[j]); - for(k = j + 1; k < nb_mods; k++) { - y[k].v = ntt_mul_mod(y[k].v - y[j].v, - mods_cr_vec[l], mf[k], m_inv[k]); - l++; - } - } - y[j].v = ntt_mod1(y[j].v, mf[j]); - - for(p = 0; p < VEC_LEN; p++) { - /* back to normal representation */ - u[0] = (int64_t)y[nb_mods - 1].d[p]; - l = 1; - for(j = nb_mods - 2; j >= 1; j--) { - r = (int64_t)y[j].d[p]; - for(k = 0; k < l; k++) { - t = (dlimb_t)u[k] * mods[j] + r; - r = t >> LIMB_BITS; - u[k] = t; - } - u[l] = r; - l++; - } - /* XXX: for nb_mods = 5, l should be 4 */ - - /* last step adds the carry */ - r = (int64_t)y[0].d[p]; - for(k = 0; k < l; k++) { - t = (dlimb_t)u[k] * mods[j] + r + carry[k]; - r = t >> LIMB_BITS; - u[k] = t; - } - u[l] = r + carry[l]; - - /* write the digits */ - pos = i * dpl; - for(j = 0; j < n_limb1; j++) { - put_bits(tabr, r_len, pos, u[j]); - pos += LIMB_BITS; - } - put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1); - /* shift by dpl digits and set the carry */ - if (shift == 0) { - for(j = n_limb1 + 1; j < nb_mods; j++) - carry[j - (n_limb1 + 1)] = u[j]; - } else { - for(j = n_limb1; j < nb_mods - 1; j++) { - carry[j - n_limb1] = (u[j] >> shift) | - (u[j + 1] << (LIMB_BITS - shift)); - } - carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift; - } - i++; - } - } -} -#else -static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len, - const NTTLimb *buf, int fft_len_log2, int dpl, - int nb_mods) -{ - const limb_t *mods = ntt_mods + NB_MODS - nb_mods; - const limb_t *mods_cr, *mods_cr_inv; - limb_t y[NB_MODS], u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r; - slimb_t i, len, pos; - int j, k, l, shift, n_limb1; - dlimb_t t; - - j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2; - mods_cr = ntt_mods_cr + j; - mods_cr_inv = s->ntt_mods_cr_inv + j; - - shift = dpl & (LIMB_BITS - 1); - if (shift == 0) - base_mask1 = -1; - else - base_mask1 = ((limb_t)1 << shift) - 1; - n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS; - for(j = 0; j < NB_MODS; j++) - carry[j] = 0; - for(j = 0; j < NB_MODS; j++) - u[j] = 0; /* avoid warnings */ - memset(tabr, 0, sizeof(limb_t) * r_len); - fft_len = (limb_t)1 << fft_len_log2; - len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl); - for(i = 0; i < len; i++) { - for(j = 0; j < nb_mods; j++) { - y[j] = ntt_limb_to_int(buf[i + fft_len * j], mods[j]); - } - - /* Chinese remainder to get mixed radix representation */ - l = 0; - for(j = 0; j < nb_mods - 1; j++) { - for(k = j + 1; k < nb_mods; k++) { - limb_t m; - m = mods[k]; - /* Note: there is no overflow in the sub_mod() because - the modulos are sorted by increasing order */ - y[k] = mul_mod_fast2(y[k] - y[j] + m, - mods_cr[l], m, mods_cr_inv[l]); - l++; - } - } - - /* back to normal representation */ - u[0] = y[nb_mods - 1]; - l = 1; - for(j = nb_mods - 2; j >= 1; j--) { - r = y[j]; - for(k = 0; k < l; k++) { - t = (dlimb_t)u[k] * mods[j] + r; - r = t >> LIMB_BITS; - u[k] = t; - } - u[l] = r; - l++; - } - - /* last step adds the carry */ - r = y[0]; - for(k = 0; k < l; k++) { - t = (dlimb_t)u[k] * mods[j] + r + carry[k]; - r = t >> LIMB_BITS; - u[k] = t; - } - u[l] = r + carry[l]; - - /* write the digits */ - pos = i * dpl; - for(j = 0; j < n_limb1; j++) { - put_bits(tabr, r_len, pos, u[j]); - pos += LIMB_BITS; - } - put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1); - /* shift by dpl digits and set the carry */ - if (shift == 0) { - for(j = n_limb1 + 1; j < nb_mods; j++) - carry[j - (n_limb1 + 1)] = u[j]; - } else { - for(j = n_limb1; j < nb_mods - 1; j++) { - carry[j - n_limb1] = (u[j] >> shift) | - (u[j + 1] << (LIMB_BITS - shift)); - } - carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift; - } - } -} -#endif - -static int ntt_static_init(bf_context_t *s1) -{ - BFNTTState *s; - int inverse, i, j, k, l; - limb_t c, c_inv, c_inv2, m, m_inv; - - if (s1->ntt_state) - return 0; -#if defined(__AVX2__) - s = bf_aligned_malloc(s1, sizeof(*s), 64); -#else - s = bf_malloc(s1, sizeof(*s)); -#endif - if (!s) - return -1; - memset(s, 0, sizeof(*s)); - s1->ntt_state = s; - s->ctx = s1; - - for(j = 0; j < NB_MODS; j++) { - m = ntt_mods[j]; - m_inv = init_mul_mod_fast(m); - s->ntt_mods_div[j] = m_inv; -#if defined(__AVX2__) - s->ntt_mods_vec[j] = _mm256_set1_pd(m); - s->ntt_mods_inv_vec[j] = _mm256_set1_pd(1.0 / (double)m); -#endif - c_inv2 = (m + 1) / 2; /* 1/2 */ - c_inv = 1; - for(i = 0; i <= NTT_PROOT_2EXP; i++) { - s->ntt_len_inv[j][i][0] = c_inv; - s->ntt_len_inv[j][i][1] = init_mul_mod_fast2(c_inv, m); - c_inv = mul_mod_fast(c_inv, c_inv2, m, m_inv); - } - - for(inverse = 0; inverse < 2; inverse++) { - c = ntt_proot[inverse][j]; - for(i = 0; i < NTT_PROOT_2EXP; i++) { - s->ntt_proot_pow[j][inverse][NTT_PROOT_2EXP - i] = c; - s->ntt_proot_pow_inv[j][inverse][NTT_PROOT_2EXP - i] = - init_mul_mod_fast2(c, m); - c = mul_mod_fast(c, c, m, m_inv); - } - } - } - - l = 0; - for(j = 0; j < NB_MODS - 1; j++) { - for(k = j + 1; k < NB_MODS; k++) { -#if defined(__AVX2__) - s->ntt_mods_cr_vec[l] = _mm256_set1_pd(int_to_ntt_limb2(ntt_mods_cr[l], - ntt_mods[k])); -#else - s->ntt_mods_cr_inv[l] = init_mul_mod_fast2(ntt_mods_cr[l], - ntt_mods[k]); -#endif - l++; - } - } - return 0; -} - -int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len) -{ - int dpl, fft_len_log2, n_bits, nb_mods, dpl_found, fft_len_log2_found; - int int_bits, nb_mods_found; - limb_t cost, min_cost; - - min_cost = -1; - dpl_found = 0; - nb_mods_found = 4; - fft_len_log2_found = 0; - for(nb_mods = 3; nb_mods <= NB_MODS; nb_mods++) { - int_bits = ntt_int_bits[NB_MODS - nb_mods]; - dpl = bf_min((int_bits - 4) / 2, - 2 * LIMB_BITS + 2 * NTT_MOD_LOG2_MIN - NTT_MOD_LOG2_MAX); - for(;;) { - fft_len_log2 = ceil_log2((len * LIMB_BITS + dpl - 1) / dpl); - if (fft_len_log2 > NTT_PROOT_2EXP) - goto next; - n_bits = fft_len_log2 + 2 * dpl; - if (n_bits <= int_bits) { - cost = ((limb_t)(fft_len_log2 + 1) << fft_len_log2) * nb_mods; - // printf("n=%d dpl=%d: cost=%" PRId64 "\n", nb_mods, dpl, (int64_t)cost); - if (cost < min_cost) { - min_cost = cost; - dpl_found = dpl; - nb_mods_found = nb_mods; - fft_len_log2_found = fft_len_log2; - } - break; - } - dpl--; - if (dpl == 0) - break; - } - next: ; - } - if (!dpl_found) - abort(); - /* limit dpl if possible to reduce fixed cost of limb/NTT conversion */ - if (dpl_found > (LIMB_BITS + NTT_MOD_LOG2_MIN) && - ((limb_t)(LIMB_BITS + NTT_MOD_LOG2_MIN) << fft_len_log2_found) >= - len * LIMB_BITS) { - dpl_found = LIMB_BITS + NTT_MOD_LOG2_MIN; - } - *pnb_mods = nb_mods_found; - *pdpl = dpl_found; - return fft_len_log2_found; -} - -/* return 0 if OK, -1 if memory error */ -static no_inline int fft_mul(bf_context_t *s1, - bf_t *res, limb_t *a_tab, limb_t a_len, - limb_t *b_tab, limb_t b_len, int mul_flags) -{ - BFNTTState *s; - int dpl, fft_len_log2, j, nb_mods, reduced_mem; - slimb_t len, fft_len; - NTTLimb *buf1, *buf2, *ptr; -#if defined(USE_MUL_CHECK) - limb_t ha, hb, hr, h_ref; -#endif - - if (ntt_static_init(s1)) - return -1; - s = s1->ntt_state; - - /* find the optimal number of digits per limb (dpl) */ - len = a_len + b_len; - fft_len_log2 = bf_get_fft_size(&dpl, &nb_mods, len); - fft_len = (uint64_t)1 << fft_len_log2; - // printf("len=%" PRId64 " fft_len_log2=%d dpl=%d\n", len, fft_len_log2, dpl); -#if defined(USE_MUL_CHECK) - ha = mp_mod1(a_tab, a_len, BF_CHKSUM_MOD, 0); - hb = mp_mod1(b_tab, b_len, BF_CHKSUM_MOD, 0); -#endif - if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == 0) { - if (!(mul_flags & FFT_MUL_R_NORESIZE)) - bf_resize(res, 0); - } else if (mul_flags & FFT_MUL_R_OVERLAP_B) { - limb_t *tmp_tab, tmp_len; - /* it is better to free 'b' first */ - tmp_tab = a_tab; - a_tab = b_tab; - b_tab = tmp_tab; - tmp_len = a_len; - a_len = b_len; - b_len = tmp_len; - } - buf1 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods); - if (!buf1) - return -1; - limb_to_ntt(s, buf1, fft_len, a_tab, a_len, dpl, - NB_MODS - nb_mods, nb_mods); - if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == - FFT_MUL_R_OVERLAP_A) { - if (!(mul_flags & FFT_MUL_R_NORESIZE)) - bf_resize(res, 0); - } - reduced_mem = (fft_len_log2 >= 14); - if (!reduced_mem) { - buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods); - if (!buf2) - goto fail; - limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl, - NB_MODS - nb_mods, nb_mods); - if (!(mul_flags & FFT_MUL_R_NORESIZE)) - bf_resize(res, 0); /* in case res == b */ - } else { - buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len); - if (!buf2) - goto fail; - } - for(j = 0; j < nb_mods; j++) { - if (reduced_mem) { - limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl, - NB_MODS - nb_mods + j, 1); - ptr = buf2; - } else { - ptr = buf2 + fft_len * j; - } - if (ntt_conv(s, buf1 + fft_len * j, ptr, - fft_len_log2, fft_len_log2, j + NB_MODS - nb_mods)) - goto fail; - } - if (!(mul_flags & FFT_MUL_R_NORESIZE)) - bf_resize(res, 0); /* in case res == b and reduced mem */ - ntt_free(s, buf2); - buf2 = NULL; - if (!(mul_flags & FFT_MUL_R_NORESIZE)) { - if (bf_resize(res, len)) - goto fail; - } - ntt_to_limb(s, res->tab, len, buf1, fft_len_log2, dpl, nb_mods); - ntt_free(s, buf1); -#if defined(USE_MUL_CHECK) - hr = mp_mod1(res->tab, len, BF_CHKSUM_MOD, 0); - h_ref = mul_mod(ha, hb, BF_CHKSUM_MOD); - if (hr != h_ref) { - printf("ntt_mul_error: len=%" PRId_LIMB " fft_len_log2=%d dpl=%d nb_mods=%d\n", - len, fft_len_log2, dpl, nb_mods); - // printf("ha=0x" FMT_LIMB" hb=0x" FMT_LIMB " hr=0x" FMT_LIMB " expected=0x" FMT_LIMB "\n", ha, hb, hr, h_ref); - exit(1); - } -#endif - return 0; - fail: - ntt_free(s, buf1); - ntt_free(s, buf2); - return -1; -} - -#else /* USE_FFT_MUL */ - -int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len) -{ - return 0; -} - -#endif /* !USE_FFT_MUL */ - -#undef malloc -#undef free -#undef realloc diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libbf.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/libbf.h deleted file mode 100644 index 3586532e..00000000 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/libbf.h +++ /dev/null @@ -1,545 +0,0 @@ -/* - * Tiny arbitrary precision floating point library - * - * Copyright (c) 2017-2021 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef LIBBF_H -#define LIBBF_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#if INTPTR_MAX >= INT64_MAX && !defined(_WIN32) && !defined(__TINYC__) -#define LIMB_LOG2_BITS 6 -#else -#define LIMB_LOG2_BITS 5 -#endif - -#define LIMB_BITS (1 << LIMB_LOG2_BITS) - -#if LIMB_BITS == 64 -#ifndef INT128_MAX -__extension__ typedef __int128 int128_t; -__extension__ typedef unsigned __int128 uint128_t; -#endif -typedef int64_t slimb_t; -typedef uint64_t limb_t; -typedef uint128_t dlimb_t; -#define BF_RAW_EXP_MIN INT64_MIN -#define BF_RAW_EXP_MAX INT64_MAX - -#define LIMB_DIGITS 19 -#define BF_DEC_BASE UINT64_C(10000000000000000000) - -#else - -typedef int32_t slimb_t; -typedef uint32_t limb_t; -typedef uint64_t dlimb_t; -#define BF_RAW_EXP_MIN INT32_MIN -#define BF_RAW_EXP_MAX INT32_MAX - -#define LIMB_DIGITS 9 -#define BF_DEC_BASE 1000000000U - -#endif - -/* in bits */ -/* minimum number of bits for the exponent */ -#define BF_EXP_BITS_MIN 3 -/* maximum number of bits for the exponent */ -#define BF_EXP_BITS_MAX (LIMB_BITS - 3) -/* extended range for exponent, used internally */ -#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1) -/* minimum possible precision */ -#define BF_PREC_MIN 2 -/* minimum possible precision */ -#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2) -/* some operations support infinite precision */ -#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */ - -#if LIMB_BITS == 64 -#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197)) -#else -#define BF_CHKSUM_MOD 975620677U -#endif - -#define BF_EXP_ZERO BF_RAW_EXP_MIN -#define BF_EXP_INF (BF_RAW_EXP_MAX - 1) -#define BF_EXP_NAN BF_RAW_EXP_MAX - -/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0, - +/-infinity is represented with expn = BF_EXP_INF and len = 0, - NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored) - */ -typedef struct { - struct bf_context_t *ctx; - int sign; - slimb_t expn; - limb_t len; - limb_t *tab; -} bf_t; - -typedef struct { - /* must be kept identical to bf_t */ - struct bf_context_t *ctx; - int sign; - slimb_t expn; - limb_t len; - limb_t *tab; -} bfdec_t; - -typedef enum { - BF_RNDN, /* round to nearest, ties to even */ - BF_RNDZ, /* round to zero */ - BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */ - BF_RNDU, /* round to +inf */ - BF_RNDNA, /* round to nearest, ties away from zero */ - BF_RNDA, /* round away from zero */ - BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU, - inexact flag is always set) */ -} bf_rnd_t; - -/* allow subnormal numbers. Only available if the number of exponent - bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */ -#define BF_FLAG_SUBNORMAL (1 << 3) -/* 'prec' is the precision after the radix point instead of the whole - mantissa. Can only be used with bf_round() and - bfdec_[add|sub|mul|div|sqrt|round](). */ -#define BF_FLAG_RADPNT_PREC (1 << 4) - -#define BF_RND_MASK 0x7 -#define BF_EXP_BITS_SHIFT 5 -#define BF_EXP_BITS_MASK 0x3f - -/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */ -#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT) - -/* contains the rounding mode and number of exponents bits */ -typedef uint32_t bf_flags_t; - -typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size); - -typedef struct { - bf_t val; - limb_t prec; -} BFConstCache; - -typedef struct bf_context_t { - void *realloc_opaque; - bf_realloc_func_t *realloc_func; - BFConstCache log2_cache; - BFConstCache pi_cache; - struct BFNTTState *ntt_state; -} bf_context_t; - -static inline int bf_get_exp_bits(bf_flags_t flags) -{ - int e; - e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK; - if (e == BF_EXP_BITS_MASK) - return BF_EXP_BITS_MAX + 1; - else - return BF_EXP_BITS_MAX - e; -} - -static inline bf_flags_t bf_set_exp_bits(int n) -{ - return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT; -} - -/* returned status */ -#define BF_ST_INVALID_OP (1 << 0) -#define BF_ST_DIVIDE_ZERO (1 << 1) -#define BF_ST_OVERFLOW (1 << 2) -#define BF_ST_UNDERFLOW (1 << 3) -#define BF_ST_INEXACT (1 << 4) -/* indicate that a memory allocation error occured. NaN is returned */ -#define BF_ST_MEM_ERROR (1 << 5) - -#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */ - -static inline slimb_t bf_max(slimb_t a, slimb_t b) -{ - if (a > b) - return a; - else - return b; -} - -static inline slimb_t bf_min(slimb_t a, slimb_t b) -{ - if (a < b) - return a; - else - return b; -} - -void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, - void *realloc_opaque); -void bf_context_end(bf_context_t *s); -/* free memory allocated for the bf cache data */ -void bf_clear_cache(bf_context_t *s); - -static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size) -{ - return s->realloc_func(s->realloc_opaque, ptr, size); -} - -/* 'size' must be != 0 */ -static inline void *bf_malloc(bf_context_t *s, size_t size) -{ - return bf_realloc(s, NULL, size); -} - -static inline void bf_free(bf_context_t *s, void *ptr) -{ - /* must test ptr otherwise equivalent to malloc(0) */ - if (ptr) - bf_realloc(s, ptr, 0); -} - -void bf_init(bf_context_t *s, bf_t *r); - -static inline void bf_delete(bf_t *r) -{ - bf_context_t *s = r->ctx; - /* we accept to delete a zeroed bf_t structure */ - if (s && r->tab) { - bf_realloc(s, r->tab, 0); - } -} - -static inline void bf_neg(bf_t *r) -{ - r->sign ^= 1; -} - -static inline int bf_is_finite(const bf_t *a) -{ - return (a->expn < BF_EXP_INF); -} - -static inline int bf_is_nan(const bf_t *a) -{ - return (a->expn == BF_EXP_NAN); -} - -static inline int bf_is_zero(const bf_t *a) -{ - return (a->expn == BF_EXP_ZERO); -} - -static inline void bf_memcpy(bf_t *r, const bf_t *a) -{ - *r = *a; -} - -int bf_set_ui(bf_t *r, uint64_t a); -int bf_set_si(bf_t *r, int64_t a); -void bf_set_nan(bf_t *r); -void bf_set_zero(bf_t *r, int is_neg); -void bf_set_inf(bf_t *r, int is_neg); -int bf_set(bf_t *r, const bf_t *a); -void bf_move(bf_t *r, bf_t *a); -int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode); -int bf_set_float64(bf_t *a, double d); - -int bf_cmpu(const bf_t *a, const bf_t *b); -int bf_cmp_full(const bf_t *a, const bf_t *b); -int bf_cmp(const bf_t *a, const bf_t *b); -static inline int bf_cmp_eq(const bf_t *a, const bf_t *b) -{ - return bf_cmp(a, b) == 0; -} - -static inline int bf_cmp_le(const bf_t *a, const bf_t *b) -{ - return bf_cmp(a, b) <= 0; -} - -static inline int bf_cmp_lt(const bf_t *a, const bf_t *b) -{ - return bf_cmp(a, b) < 0; -} - -int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); -int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); -int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags); -int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); -int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags); -int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, - bf_flags_t flags); -int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags); -int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); -#define BF_DIVREM_EUCLIDIAN BF_RNDF -int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, - limb_t prec, bf_flags_t flags, int rnd_mode); -int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode); -int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode); -/* round to integer with infinite precision */ -int bf_rint(bf_t *r, int rnd_mode); -int bf_round(bf_t *r, limb_t prec, bf_flags_t flags); -int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a); -int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -slimb_t bf_get_exp_min(const bf_t *a); -int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b); -int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b); -int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b); - -/* additional flags for bf_atof */ -/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */ -#define BF_ATOF_NO_HEX (1 << 16) -/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */ -#define BF_ATOF_BIN_OCT (1 << 17) -/* Do not parse NaN or Inf */ -#define BF_ATOF_NO_NAN_INF (1 << 18) -/* return the exponent separately */ -#define BF_ATOF_EXPONENT (1 << 19) - -int bf_atof(bf_t *a, const char *str, const char **pnext, int radix, - limb_t prec, bf_flags_t flags); -/* this version accepts prec = BF_PREC_INF and returns the radix - exponent */ -int bf_atof2(bf_t *r, slimb_t *pexponent, - const char *str, const char **pnext, int radix, - limb_t prec, bf_flags_t flags); -int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, - slimb_t expn, limb_t prec, bf_flags_t flags); - - -/* Conversion of floating point number to string. Return a null - terminated string or NULL if memory error. *plen contains its - length if plen != NULL. The exponent letter is "e" for base 10, - "p" for bases 2, 8, 16 with a binary exponent and "@" for the other - bases. */ - -#define BF_FTOA_FORMAT_MASK (3 << 16) - -/* fixed format: prec significant digits rounded with (flags & - BF_RND_MASK). Exponential notation is used if too many zeros are - needed.*/ -#define BF_FTOA_FORMAT_FIXED (0 << 16) -/* fractional format: prec digits after the decimal point rounded with - (flags & BF_RND_MASK) */ -#define BF_FTOA_FORMAT_FRAC (1 << 16) -/* free format: - - For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum - number of digits to represent 'a'. The precision and the rounding - mode are ignored. - - For the non binary radices with bf_ftoa(): use as many digits as - necessary so that bf_atof() return the same number when using - precision 'prec', rounding to nearest and the subnormal - configuration of 'flags'. The result is meaningful only if 'a' is - already rounded to 'prec' bits. If the subnormal flag is set, the - exponent in 'flags' must also be set to the desired exponent range. -*/ -#define BF_FTOA_FORMAT_FREE (2 << 16) -/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits - (takes more computation time). Identical to BF_FTOA_FORMAT_FREE for - binary radices with bf_ftoa() and for bfdec_ftoa(). */ -#define BF_FTOA_FORMAT_FREE_MIN (3 << 16) - -/* force exponential notation for fixed or free format */ -#define BF_FTOA_FORCE_EXP (1 << 20) -/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for - base 2 if non zero value */ -#define BF_FTOA_ADD_PREFIX (1 << 21) -/* return "Infinity" instead of "Inf" and add a "+" for positive - exponents */ -#define BF_FTOA_JS_QUIRKS (1 << 22) - -char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, - bf_flags_t flags); - -/* modulo 2^n instead of saturation. NaN and infinity return 0 */ -#define BF_GET_INT_MOD (1 << 0) -int bf_get_int32(int *pres, const bf_t *a, int flags); -int bf_get_int64(int64_t *pres, const bf_t *a, int flags); -int bf_get_uint64(uint64_t *pres, const bf_t *a); - -/* the following functions are exported for testing only. */ -void mp_print_str(const char *str, const limb_t *tab, limb_t n); -void bf_print_str(const char *str, const bf_t *a); -int bf_resize(bf_t *r, limb_t len); -int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len); -int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags); -int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k); -slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, - int is_ceil1); -int mp_mul(bf_context_t *s, limb_t *result, - const limb_t *op1, limb_t op1_size, - const limb_t *op2, limb_t op2_size); -limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, - limb_t n, limb_t carry); -limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n); -int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n); -int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n); -limb_t bf_isqrt(limb_t a); - -/* transcendental functions */ -int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags); -int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags); -int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */ -int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags); -int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, - limb_t prec, bf_flags_t flags); -int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); - -/* decimal floating point */ - -static inline void bfdec_init(bf_context_t *s, bfdec_t *r) -{ - bf_init(s, (bf_t *)r); -} -static inline void bfdec_delete(bfdec_t *r) -{ - bf_delete((bf_t *)r); -} - -static inline void bfdec_neg(bfdec_t *r) -{ - r->sign ^= 1; -} - -static inline int bfdec_is_finite(const bfdec_t *a) -{ - return (a->expn < BF_EXP_INF); -} - -static inline int bfdec_is_nan(const bfdec_t *a) -{ - return (a->expn == BF_EXP_NAN); -} - -static inline int bfdec_is_zero(const bfdec_t *a) -{ - return (a->expn == BF_EXP_ZERO); -} - -static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a) -{ - bf_memcpy((bf_t *)r, (const bf_t *)a); -} - -int bfdec_set_ui(bfdec_t *r, uint64_t a); -int bfdec_set_si(bfdec_t *r, int64_t a); - -static inline void bfdec_set_nan(bfdec_t *r) -{ - bf_set_nan((bf_t *)r); -} -static inline void bfdec_set_zero(bfdec_t *r, int is_neg) -{ - bf_set_zero((bf_t *)r, is_neg); -} -static inline void bfdec_set_inf(bfdec_t *r, int is_neg) -{ - bf_set_inf((bf_t *)r, is_neg); -} -static inline int bfdec_set(bfdec_t *r, const bfdec_t *a) -{ - return bf_set((bf_t *)r, (bf_t *)a); -} -static inline void bfdec_move(bfdec_t *r, bfdec_t *a) -{ - bf_move((bf_t *)r, (bf_t *)a); -} -static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b) -{ - return bf_cmpu((const bf_t *)a, (const bf_t *)b); -} -static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b) -{ - return bf_cmp_full((const bf_t *)a, (const bf_t *)b); -} -static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b) -{ - return bf_cmp((const bf_t *)a, (const bf_t *)b); -} -static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b) -{ - return bfdec_cmp(a, b) == 0; -} -static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b) -{ - return bfdec_cmp(a, b) <= 0; -} -static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b) -{ - return bfdec_cmp(a, b) < 0; -} - -int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags); -int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags); -int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, - bf_flags_t flags); -int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags); -int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, - bf_flags_t flags); -int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags); -int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, - limb_t prec, bf_flags_t flags, int rnd_mode); -int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode); -int bfdec_rint(bfdec_t *r, int rnd_mode); -int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags); -int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags); -int bfdec_get_int32(int *pres, const bfdec_t *a); -int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b); - -char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags); -int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, - limb_t prec, bf_flags_t flags); - -/* the following functions are exported for testing only. */ -extern const limb_t mp_pow_dec[LIMB_DIGITS + 1]; -void bfdec_print_str(const char *str, const bfdec_t *a); -static inline int bfdec_resize(bfdec_t *r, limb_t len) -{ - return bf_resize((bf_t *)r, len); -} -int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags); - -#ifdef __cplusplus -} /* extern "C" { */ -#endif - -#endif /* LIBBF_H */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp-opcode.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp-opcode.h old mode 100644 new mode 100755 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.c old mode 100644 new mode 100755 index 04c2888a..ac60650c --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.c @@ -53,6 +53,9 @@ typedef enum { #define CAPTURE_COUNT_MAX 255 #define STACK_SIZE_MAX 255 +/* must be large enough to have a negligible runtime cost and small + enough to call the interrupt callback often. */ +#define INTERRUPT_COUNTER_INIT 10000 /* unicode code points */ #define CP_LS 0x2028 @@ -388,6 +391,13 @@ static int re_parse_out_of_memory(REParseState *s) return re_parse_error(s, "out of memory"); } +static int lre_check_size(REParseState *s) +{ + if (s->byte_code.size < 64*1024*1024) + return 0; + return re_parse_out_of_memory(s); +} + /* If allow_overflow is false, return -1 in case of overflow. Otherwise return INT32_MAX. */ static int parse_digits(const uint8_t **pp, bool allow_overflow) @@ -1173,6 +1183,8 @@ static int re_parse_term(REParseState *s, bool is_backward_dir) bool greedy, add_zero_advance_check, is_neg, is_backward_lookahead; CharRange cr_s, *cr = &cr_s; + if (lre_check_size(s)) + return -1; last_atom_start = -1; last_capture_count = 0; p = s->buf_ptr; @@ -1664,6 +1676,8 @@ static int re_parse_alternative(REParseState *s, bool is_backward_dir) int ret; size_t start, term_start, end, term_size; + if (lre_check_size(s)) + return -1; start = s->byte_code.size; for(;;) { p = s->buf_ptr; @@ -1698,7 +1712,8 @@ static int re_parse_disjunction(REParseState *s, bool is_backward_dir) if (lre_check_stack_overflow(s->opaque, 0)) return re_parse_error(s, "stack overflow"); - + if (lre_check_size(s)) + return -1; start = s->byte_code.size; if (re_parse_alternative(s, is_backward_dir)) return -1; @@ -1992,6 +2007,7 @@ typedef struct { bool multi_line; bool ignore_case; bool is_unicode; + int interrupt_counter; void *opaque; /* used for stack overflow check */ size_t state_size; @@ -2038,7 +2054,17 @@ static int push_state(REExecContext *s, return 0; } -/* return 1 if match, 0 if not match or -1 if error. */ +static int lre_poll_timeout(REExecContext *s) +{ + if (unlikely(--s->interrupt_counter <= 0)) { + s->interrupt_counter = INTERRUPT_COUNTER_INIT; + if (lre_check_timeout(s->opaque)) + return LRE_RET_TIMEOUT; + } + return 0; +} + +/* return 1 if match, 0 if not match or < 0 if error. */ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, StackInt *stack, int stack_len, const uint8_t *pc, const uint8_t *cptr, @@ -2069,6 +2095,8 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, ret = 0; recurse: for(;;) { + if (lre_poll_timeout(s)) + return LRE_RET_TIMEOUT; if (s->state_stack_len == 0) return ret; rs = (REExecState *)(s->state_stack + @@ -2162,7 +2190,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, ret = push_state(s, capture, stack, stack_len, pc1, cptr, RE_EXEC_STATE_SPLIT, 0); if (ret < 0) - return -1; + return LRE_RET_MEMORY_ERROR; break; } case REOP_lookahead: @@ -2174,12 +2202,14 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead, 0); if (ret < 0) - return -1; + return LRE_RET_MEMORY_ERROR; break; case REOP_goto: val = get_u32(pc); pc += 4 + (int)val; + if (lre_poll_timeout(s)) + return LRE_RET_TIMEOUT; break; case REOP_line_start: if (cptr == s->cbuf) @@ -2244,6 +2274,8 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, pc += 4; if (--stack[stack_len - 1] != 0) { pc += (int)val; + if (lre_poll_timeout(s)) + return LRE_RET_TIMEOUT; } break; case REOP_push_char_pos: @@ -2418,9 +2450,12 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, q = 0; for(;;) { + if (lre_poll_timeout(s)) + return LRE_RET_TIMEOUT; res = lre_exec_backtrack(s, capture, stack, stack_len, pc1, cptr, true); - if (res == -1) + if (res == LRE_RET_MEMORY_ERROR || + res == LRE_RET_TIMEOUT) return res; if (!res) break; @@ -2438,7 +2473,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, RE_EXEC_STATE_GREEDY_QUANT, q - quant_min); if (ret < 0) - return -1; + return LRE_RET_MEMORY_ERROR; } } break; @@ -2448,7 +2483,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, } } -/* Return 1 if match, 0 if not match or -1 if error. cindex is the +/* Return 1 if match, 0 if not match or < 0 if error (see LRE_RET_x). cindex is the starting position of the match and must be such as 0 <= cindex <= clen. */ int lre_exec(uint8_t **capture, @@ -2470,6 +2505,7 @@ int lre_exec(uint8_t **capture, s->cbuf_type = cbuf_type; if (s->cbuf_type == 1 && s->is_unicode) s->cbuf_type = 2; + s->interrupt_counter = INTERRUPT_COUNTER_INIT; s->opaque = opaque; s->state_size = sizeof(REExecState) + diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.h old mode 100644 new mode 100755 index 0b8fec52..898e9a7a --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/libregexp.h @@ -43,6 +43,9 @@ extern "C" { #define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */ #define LRE_FLAG_UNICODE_SETS (1 << 8) +#define LRE_RET_MEMORY_ERROR (-1) +#define LRE_RET_TIMEOUT (-2) + uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, const char *buf, size_t buf_len, int re_flags, void *opaque); @@ -60,6 +63,8 @@ void lre_byte_swap(uint8_t *buf, size_t len, bool is_byte_swapped); /* must be provided by the user */ bool lre_check_stack_overflow(void *opaque, size_t alloca_size); +/* must be provided by the user, return non zero if time out */ +int lre_check_timeout(void *opaque); void *lre_realloc(void *opaque, void *ptr, size_t size); /* JS identifier test */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode-table.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode-table.h old mode 100644 new mode 100755 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode.c old mode 100644 new mode 100755 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/libunicode.h old mode 100644 new mode 100755 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/list.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/list.h old mode 100644 new mode 100755 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/meson.build b/test-app/runtime/src/main/cpp/napi/quickjs/source/meson.build new file mode 100755 index 00000000..1c178513 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/meson.build @@ -0,0 +1,635 @@ +project( + 'quickjs-ng', + 'c', + version: '0.11.0', + default_options: [ + 'c_std=gnu11,c11', + 'warning_level=3', + 'default_library=static', + ], + license: 'MIT', + license_files: 'LICENSE', + meson_version: '>=1.3.0', +) + +host_system = host_machine.system() +cc = meson.get_compiler('c') + +qjs_gcc_warning_args = [ + '-Wno-unsafe-buffer-usage', + '-Wno-sign-conversion', + '-Wno-nonportable-system-include-path', + '-Wno-implicit-int-conversion', + '-Wno-shorten-64-to-32', + '-Wno-reserved-macro-identifier', + '-Wno-reserved-identifier', + '-Wdeprecated-declarations', + + '-Wno-implicit-fallthrough', + '-Wno-sign-compare', + '-Wno-missing-field-initializers', + '-Wno-unused-parameter', + '-Wno-unused-but-set-variable', + '-Wno-array-bounds', + '-Wno-format-truncation', +] +qjs_gcc_args = [ + '-funsigned-char', +] + +if host_system == 'darwin' + # https://github.com/quickjs-ng/quickjs/issues/453 + qjs_gcc_warning_args += '-Wno-maybe-uninitialized' +endif + +# https://github.com/microsoft/cpp-docs/tree/main/docs/error-messages/compiler-warnings +qjs_msvc_warning_args = [ + '/wd4018', # -Wno-sign-conversion + '/wd4061', # -Wno-implicit-fallthrough + '/wd4100', # -Wno-unused-parameter + '/wd4200', # -Wno-zero-length-array + '/wd4242', # -Wno-shorten-64-to-32 + '/wd4244', # -Wno-shorten-64-to-32 + '/wd4245', # -Wno-sign-compare + '/wd4267', # -Wno-shorten-64-to-32 + '/wd4388', # -Wno-sign-compare + '/wd4389', # -Wno-sign-compare + '/wd4710', # Function not inlined + '/wd4711', # Function was inlined + '/wd4820', # Padding added after construct + '/wd4996', # -Wdeprecated-declarations + '/wd5045', # Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified +] +qjs_msvc_args = [ + '/experimental:c11atomics', + '/J', # -funsigned-char +] + +if cc.get_argument_syntax() == 'msvc' + add_project_arguments( + cc.get_supported_arguments(qjs_msvc_warning_args), + cc.get_id().contains('clang') ? cc.get_supported_arguments(qjs_gcc_warning_args) : [], + qjs_msvc_args, + language: 'c', + ) +else + add_project_arguments( + cc.get_supported_arguments(qjs_gcc_warning_args), + qjs_gcc_args, + language: 'c', + ) +endif + +if host_system == 'windows' + # Set a 8MB default stack size on Windows. + # It defaults to 1MB on MSVC, which is the same as our current JS stack size, + # so it will overflow and crash otherwise. + # On MinGW it defaults to 2MB. + stack_size = 8 * 1024 * 1024 + if cc.get_argument_syntax() == 'msvc' + add_project_link_arguments(f'/STACK:@stack_size@', language: 'c') + else + add_project_link_arguments(f'-Wl,--stack,@stack_size@', language: 'c') + endif +endif + +if meson.is_cross_build() + native_cc = meson.get_compiler('c', native: true) + + if native_cc.get_argument_syntax() == 'msvc' + # https://github.com/microsoft/cpp-docs/tree/main/docs/error-messages/compiler-warnings + add_project_arguments( + native_cc.get_supported_arguments(qjs_msvc_warning_args), + native_cc.get_id().contains('clang') ? native_cc.get_supported_arguments(qjs_gcc_warning_args) : [], + qjs_msvc_args, + + language: 'c', + native: true, + ) + else + add_project_arguments( + native_cc.get_supported_arguments(qjs_gcc_warning_args), + qjs_gcc_args, + + language: 'c', + native: true, + ) + endif +endif +if get_option('debug') + add_project_arguments( + cc.get_supported_arguments('-fno-omit-frame-pointer'), + language: 'c', + ) +endif + +qjs_sys_deps = [] + +m_dep = cc.find_library('m', required: false) +qjs_sys_deps += m_dep +qjs_sys_deps += dependency('threads', required: false) +qjs_sys_deps += dependency('dl', required: false) + +qjs_srcs = files( + 'cutils.c', + 'dtoa.c', + 'libregexp.c', + 'libunicode.c', + 'quickjs.c', +) +qjs_hdrs = files( + 'quickjs.h', +) + +qjs_libc = get_option('libc') +qjs_libc_srcs = files('quickjs-libc.c') +qjs_libc_hdrs = files('quickjs-libc.h') + +if qjs_libc + qjs_hdrs += qjs_libc_hdrs +endif + +qjs_parser = get_option('parser') + +qjs_c_args = ['-D_GNU_SOURCE'] + +if host_system == 'windows' + qjs_c_args += ['-DWIN32_LEAN_AND_MEAN', '-D_WIN32_WINNT=0x0601'] +endif + +if not qjs_parser + qjs_c_args += ['-DQJS_DISABLE_PARSER'] +endif + +qjs_libc_lib = static_library( + 'quickjs-libc', + qjs_libc_srcs, + + dependencies: qjs_sys_deps, + c_args: qjs_c_args, + gnu_symbol_visibility: 'hidden', +) + +qjs_lib = library( + 'qjs', + qjs_srcs, + + # export public headers + generator( + find_program('cp', 'xcopy'), + output: ['@PLAINNAME@'], + arguments: ['@INPUT@', '@OUTPUT@'], + ).process(qjs_hdrs), + + dependencies: qjs_sys_deps, + link_whole: qjs_libc ? qjs_libc_lib : [], + c_args: qjs_c_args, + gnu_symbol_visibility: 'hidden', + + install: true, + version: meson.project_version(), +) + +qjs_export_variables = [ + f'have_parser=@qjs_parser@' +] + +qjs_dep = declare_dependency( + link_with: qjs_lib, + dependencies: qjs_sys_deps, + include_directories: qjs_lib.private_dir_include(), + variables: qjs_export_variables, +) + +if host_system == 'emscripten' + qjs_wasm_export_name = 'getQuickJs' + executable( + 'qjs_wasm', + qjs_srcs, + link_args: cc.get_supported_link_arguments( + # in emscripten 3.x, this will be set to 16k which is too small for quickjs. + '-sSTACK_SIZE=@0@'.format(2 * 1024 * 1024), # let it be 2m = 2 * 1024 * 1024, otherwise, stack overflow may be occured at bootstrap + '-sNO_INVOKE_RUN', + '-sNO_EXIT_RUNTIME', + '-sMODULARIZE', # do not mess the global + '-sEXPORT_ES6', # export js file to morden es module + '-sEXPORT_NAME=@0@'.format(qjs_wasm_export_name), # give a name + '-sTEXTDECODER=1', # it will be 2 if we use -Oz, and that will cause js -> c string convertion fail + '-sNO_DEFAULT_TO_CXX', # this project is pure c project, no need for c plus plus handle + '-sEXPORTED_RUNTIME_METHODS=ccall,cwrap', + ), + dependencies: m_dep, + c_args: qjs_c_args, + ) +endif + +install_headers(qjs_hdrs, subdir: 'quickjs-ng') + +if not meson.is_subproject() + docdir = get_option('docdir') + datadir = get_option('datadir') + + if docdir == '' + docdir = datadir / 'doc' / meson.project_name() + endif + install_data( + 'LICENSE', + install_dir: docdir, + + install_tag: 'doc', + ) + install_subdir( + 'examples', + install_dir: docdir, + + strip_directory: false, + install_tag: 'doc', + ) +endif + +meson.override_dependency('quickjs-ng', qjs_dep) + +import('pkgconfig').generate( + qjs_lib, + subdirs: 'quickjs-ng', + name: 'quickjs-ng', + description: 'QuickJS, the Next Generation: a mighty JavaScript engine', + url: 'https://github.com/quickjs-ng/quickjs', + version: meson.project_version(), + variables: qjs_export_variables, +) + +if not qjs_parser + subdir_done() +endif + +# QuickJS bytecode compiler +qjsc_srcs = files( + 'qjsc.c', +) +qjsc_exe = executable( + 'qjsc', + qjsc_srcs, + + c_args: qjs_c_args, + link_with: qjs_libc ? [] : qjs_libc_lib, + dependencies: qjs_dep, + + install: true, +) + +mimalloc_dep = [] +mimalloc_sys_dep = dependency('mimalloc', required: get_option('cli_mimalloc')) +if mimalloc_sys_dep.found() + mimalloc_dep = declare_dependency( + dependencies: mimalloc_sys_dep, + compile_args: '-DQJS_USE_MIMALLOC', + ) +endif + +# QuickJS CLI +qjs_exe_srcs = files( + 'gen/repl.c', + 'gen/standalone.c', + 'qjs.c', +) +qjs_exe = executable( + 'qjs', + qjs_exe_srcs, + + c_args: qjs_c_args, + link_with: qjs_libc ? [] : qjs_libc_lib, + dependencies: [qjs_dep, mimalloc_dep], + export_dynamic: true, + + install: true, +) + +if meson.is_cross_build() + mimalloc_native_dep = [] + mimalloc_sys_native_dep = dependency('mimalloc', required: get_option('cli_mimalloc'), native: true) + if mimalloc_sys_dep.found() + mimalloc_native_dep = declare_dependency( + dependencies: mimalloc_sys_native_dep, + compile_args: '-DQJS_USE_MIMALLOC', + ) + endif + + qjs_sys_native_deps = [ + native_cc.find_library('m', required: false), + dependency('threads', required: false, native: true), + dependency('dl', required: false, native: true), + ] + qjs_native_lib = static_library( + 'qjs_native', + qjs_srcs, + qjs_libc_srcs, + + dependencies: qjs_sys_native_deps, + c_args: qjs_c_args, + gnu_symbol_visibility: 'hidden', + + build_by_default: false, + native: true, + install: false, + ) + + meson.override_find_program( + 'qjsc', + executable( + 'qjsc_native', + qjsc_srcs, + + c_args: qjs_c_args, + link_with: qjs_native_lib, + dependencies: qjs_sys_native_deps, + + build_by_default: false, + native: true, + install: false, + ), + ) + meson.override_find_program( + 'qjs', + executable( + 'qjs_native', + qjs_exe_srcs, + + c_args: qjs_c_args, + link_with: qjs_native_lib, + dependencies: [qjs_sys_native_deps, mimalloc_native_dep], + export_dynamic: true, + + build_by_default: false, + native: true, + install: false, + ), + ) +else + meson.override_find_program('qjsc', qjsc_exe) + meson.override_find_program('qjs', qjs_exe) +endif + +tests = get_option('tests').disable_auto_if(meson.is_subproject()) +examples = get_option('examples').disable_auto_if(meson.is_subproject()) + +if tests.allowed() + if host_system != 'emscripten' + # Test262 runner + run262_exe = executable( + 'run-test262', + 'run-test262.c', + qjs_libc_srcs, + + c_args: qjs_c_args, + dependencies: qjs_dep, + ) + + test( + 'test', + run262_exe, + args: ['-c', files('tests.conf')], + workdir: meson.current_source_dir(), + ) + + foreach bench : [ + 'empty_loop', + + 'date_now', + + 'prop_read', + 'prop_write', + 'prop_create', + 'prop_delete', + + 'array_read', + 'array_write', + 'array_prop_create', + 'array_length_decr', + 'array_hole_length_decr', + 'array_push', + 'array_pop', + + 'typed_array_read', + 'typed_array_write', + + 'global_read', + 'global_write', + 'global_write_strict', + + 'local_destruct', + 'global_destruct', + 'global_destruct_strict', + + 'func_call', + 'closure_var', + + 'int_arith', + 'float_arith', + + 'map_set', + 'map_delete', + 'weak_map_set', + 'weak_map_delete', + + 'array_for', + 'array_for_in', + 'array_for_of', + + 'math_min', + + 'object_null', + + 'regexp_ascii', + 'regexp_utf16', + + 'string_build1', + 'string_build2', + + 'string_slice1', + 'string_slice2', + 'string_slice3', + + 'sort_bench', + + 'int_to_string', + 'int_toString', + + 'float_to_string', + 'float_toString', + 'float_toFixed', + 'float_toPrecision', + 'float_toExponential', + + 'string_to_int', + 'string_to_float', + + 'bigint64_arith', + 'bigint256_arith', + ] + benchmark( + bench, + qjs_exe, + args: [files('tests/microbench.js'), bench], + ) + endforeach + endif + + # API test + test( + 'api', + executable( + 'api-test', + 'api-test.c', + + c_args: qjs_c_args, + dependencies: qjs_dep, + build_by_default: false, + ), + ) + + # Function.toString() test + test( + 'function_source', + executable( + 'function_source', + 'gen/function_source.c', + qjs_libc_srcs, + + c_args: qjs_c_args, + dependencies: qjs_dep, + build_by_default: false, + ), + ) +endif + +# Unicode generator +unicode_gen = executable( + 'unicode_gen', + 'cutils.c', + 'unicode_gen.c', + + c_args: qjs_c_args, + build_by_default: false, +) + +run_target( + 'libunicode-table.h', + command: [ + unicode_gen, + meson.current_source_dir() / 'unicode', + files('libunicode-table.h'), + ], +) + +# bytecode to c source code for builtin and examples +alias_target('codegen', + run_target( + 'codegen_repl.c', + command: [ + qjsc_exe, + '-ss', + '-o', files('gen/repl.c'), + '-m', + '-n', 'repl.js', + files('repl.js'), + ], + ), + run_target( + 'codegen_standalone.c', + command: [ + qjsc_exe, + '-ss', + '-o', files('gen/standalone.c'), + '-m', + '-n', 'standalone.js', + files('standalone.js'), + ], + ), + run_target( + 'codegen_function_source.c', + command: [ + qjsc_exe, + '-e', + '-o', files('gen/function_source.c'), + '-n', 'tests/function_source.js', + files('tests/function_source.js'), + ], + ), + run_target( + 'codegen_hello.c', + command: [ + qjsc_exe, + '-e', + '-o', files('gen/hello.c'), + '-n', 'examples/hello.js', + files('examples/hello.js'), + ], + ), + run_target( + 'codegen_hello_module.c', + command: [ + qjsc_exe, + '-e', + '-o', files('gen/hello_module.c'), + '-m', + '-n', 'examples/hello_module.js', + files('examples/hello_module.js'), + ], + ), + run_target( + 'codegen_test_fib.c', + command: [ + qjsc_exe, + '-e', + '-o', files('gen/test_fib.c'), + '-m', + '-n', 'examples/test_fib.js', + files('examples/test_fib.js'), + ], + ), + run_target( + 'codegen_builtin-array-fromasync.h', + command: [ + qjsc_exe, + '-C', + '-ss', + '-o', files('builtin-array-fromasync.h'), + '-n', 'builtin-array-fromasync.js', + files('builtin-array-fromasync.js'), + ], + ), +) + +if examples.allowed() + executable( + 'hello', + 'gen/hello.c', + qjs_libc_srcs, + + c_args: qjs_c_args, + dependencies: qjs_dep, + ) + + executable( + 'hello_module', + 'gen/hello_module.c', + qjs_libc_srcs, + + c_args: qjs_c_args, + dependencies: qjs_dep, + ) + + subdir('examples') + + executable( + 'test_fib', + 'examples/fib.c', + 'gen/test_fib.c', + qjs_libc_srcs, + + c_args: qjs_c_args, + dependencies: qjs_dep, + export_dynamic: true, + ) +endif diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/meson_options.txt b/test-app/runtime/src/main/cpp/napi/quickjs/source/meson_options.txt new file mode 100755 index 00000000..20e661e1 --- /dev/null +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/meson_options.txt @@ -0,0 +1,6 @@ +option('tests', type: 'feature', description: 'build tests') +option('examples', type: 'feature', description: 'build examples') +option('libc', type: 'boolean', value: false, description: 'build qjs standard library modules as part of the library') +option('cli_mimalloc', type: 'feature', value: 'disabled', description: 'build qjs cli with mimalloc') +option('docdir', type: 'string', description: 'documentation directory') +option('parser', type: 'boolean', value: true, description: 'Enable JS source code parser') diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/qjs.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/qjs.c old mode 100644 new mode 100755 index fb5d53a4..4aed6837 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/qjs.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/qjs.c @@ -28,9 +28,6 @@ #include #include #include -#if !defined(_MSC_VER) -#include -#endif #include #include #include @@ -110,6 +107,7 @@ static JSValue load_standalone_module(JSContext *ctx) static int eval_buf(JSContext *ctx, const void *buf, int buf_len, const char *filename, int eval_flags) { + bool use_realpath; JSValue val; int ret; @@ -119,7 +117,10 @@ static int eval_buf(JSContext *ctx, const void *buf, int buf_len, val = JS_Eval(ctx, buf, buf_len, filename, eval_flags | JS_EVAL_FLAG_COMPILE_ONLY); if (!JS_IsException(val)) { - if (js_module_set_import_meta(ctx, val, true, true) < 0) { + // ex. "" pr "/dev/stdin" + use_realpath = + !(*filename == '<' || !strncmp(filename, "/dev/", 5)); + if (js_module_set_import_meta(ctx, val, use_realpath, true) < 0) { js_std_dump_error(ctx); ret = -1; goto end; @@ -195,14 +196,14 @@ static int64_t parse_limit(const char *arg) { return (int64_t)(d * unit); } -static JSValue js_gc(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_gc(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JS_RunGC(JS_GetRuntime(ctx)); return JS_UNDEFINED; } -static JSValue js_navigator_get_userAgent(JSContext *ctx, JSValue this_val) +static JSValue js_navigator_get_userAgent(JSContext *ctx, JSValueConst this_val) { char version[32]; snprintf(version, sizeof(version), "quickjs-ng/%s", JS_GetVersion()); @@ -378,8 +379,8 @@ void help(void) "-h --help list options\n" "-e --eval EXPR evaluate EXPR\n" "-i --interactive go to interactive mode\n" - "-m --module load as ES6 module (default=autodetect)\n" - " --script load as ES6 script (default=autodetect)\n" + "-C --script load as JS classic script (default=autodetect)\n" + "-m --module load as ES module (default=autodetect)\n" "-I --include file include an additional file\n" " --std make 'std', 'os' and 'bjson' available to script\n" "-T --trace trace memory allocation\n" @@ -402,6 +403,8 @@ int main(int argc, char **argv) struct trace_malloc_data trace_data = { NULL }; int r = 0; int optind = 1; + char exebuf[JS__PATH_MAX]; + size_t exebuf_size = sizeof(exebuf); char *compile_file = NULL; char *exe = NULL; char *expr = NULL; @@ -424,7 +427,9 @@ int main(int argc, char **argv) qjs__argc = argc; qjs__argv = argv; - if (is_standalone(argv[0])) { + /* check if this is a standalone executable */ + + if (!js_exepath(exebuf, &exebuf_size) && is_standalone(exebuf)) { standalone = 1; goto start; } @@ -437,16 +442,16 @@ int main(int argc, char **argv) while (optind < argc && *argv[optind] == '-') { char *arg = argv[optind] + 1; const char *longopt = ""; - char *opt_arg = NULL; + char *optarg = NULL; /* a single - is not an option, it also stops argument scanning */ if (!*arg) break; optind++; if (*arg == '-') { longopt = arg + 1; - opt_arg = strchr(longopt, '='); - if (opt_arg) - *opt_arg++ = '\0'; + optarg = strchr(longopt, '='); + if (optarg) + *optarg++ = '\0'; arg += strlen(arg); /* -- stops argument scanning */ if (!*longopt) @@ -456,22 +461,22 @@ int main(int argc, char **argv) char opt = *arg; if (opt) { arg++; - if (!opt_arg && *arg) - opt_arg = arg; + if (!optarg && *arg) + optarg = arg; } if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { help(); continue; } if (opt == 'e' || !strcmp(longopt, "eval")) { - if (!opt_arg) { + if (!optarg) { if (optind >= argc) { fprintf(stderr, "qjs: missing expression for -e\n"); exit(1); } - opt_arg = argv[optind++]; + optarg = argv[optind++]; } - expr = opt_arg; + expr = optarg; break; } if (opt == 'I' || !strcmp(longopt, "include")) { @@ -494,7 +499,7 @@ int main(int argc, char **argv) module = 1; continue; } - if (!strcmp(longopt, "script")) { + if (opt == 'C' || !strcmp(longopt, "script")) { module = 0; continue; } @@ -503,7 +508,7 @@ int main(int argc, char **argv) continue; } if (opt == 'D' || !strcmp(longopt, "dump-flags")) { - dump_flags = opt_arg ? strtol(opt_arg, NULL, 16) : 0; + dump_flags = optarg ? strtol(optarg, NULL, 16) : 0; break; } if (opt == 'T' || !strcmp(longopt, "trace")) { @@ -519,58 +524,58 @@ int main(int argc, char **argv) continue; } if (!strcmp(longopt, "memory-limit")) { - if (!opt_arg) { + if (!optarg) { if (optind >= argc) { fprintf(stderr, "expecting memory limit"); exit(1); } - opt_arg = argv[optind++]; + optarg = argv[optind++]; } - memory_limit = parse_limit(opt_arg); + memory_limit = parse_limit(optarg); break; } if (!strcmp(longopt, "stack-size")) { - if (!opt_arg) { + if (!optarg) { if (optind >= argc) { fprintf(stderr, "expecting stack size"); exit(1); } - opt_arg = argv[optind++]; + optarg = argv[optind++]; } - stack_size = parse_limit(opt_arg); + stack_size = parse_limit(optarg); break; } if (opt == 'c' || !strcmp(longopt, "compile")) { - if (!opt_arg) { + if (!optarg) { if (optind >= argc) { fprintf(stderr, "qjs: missing file for -c\n"); exit(1); } - opt_arg = argv[optind++]; + optarg = argv[optind++]; } - compile_file = opt_arg; + compile_file = optarg; break; } if (opt == 'o' || !strcmp(longopt, "out")) { - if (!opt_arg) { + if (!optarg) { if (optind >= argc) { fprintf(stderr, "qjs: missing file for -o\n"); exit(1); } - opt_arg = argv[optind++]; + optarg = argv[optind++]; } - out = opt_arg; + out = optarg; break; } if (!strcmp(longopt, "exe")) { - if (!opt_arg) { + if (!optarg) { if (optind >= argc) { fprintf(stderr, "qjs: missing file for --exe\n"); exit(1); } - opt_arg = argv[optind++]; + optarg = argv[optind++]; } - exe = opt_arg; + exe = optarg; break; } if (opt) { @@ -662,14 +667,15 @@ int main(int argc, char **argv) JSValue args[3]; args[0] = JS_NewString(ctx, compile_file); args[1] = JS_NewString(ctx, out); - args[2] = JS_NewString(ctx, exe != NULL ? exe : argv[0]); - ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args); + args[2] = exe != NULL ? JS_NewString(ctx, exe) : JS_UNDEFINED; + ret = JS_Call(ctx, func, JS_UNDEFINED, 3, (JSValueConst *)args); JS_FreeValue(ctx, func); JS_FreeValue(ctx, args[0]); JS_FreeValue(ctx, args[1]); JS_FreeValue(ctx, args[2]); } else if (expr) { - if (eval_buf(ctx, expr, strlen(expr), "", 0)) + int flags = module ? JS_EVAL_TYPE_MODULE : 0; + if (eval_buf(ctx, expr, strlen(expr), "", flags)) goto fail; } else if (optind >= argc) { /* interactive mode */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/qjsc.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/qjsc.c old mode 100644 new mode 100755 index 6e12b341..395bcb34 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/qjsc.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/qjsc.c @@ -29,11 +29,6 @@ #include #include #include -#if defined(_MSC_VER) -#include "getopt_compat.h" -#else -#include -#endif #include #include "cutils.h" @@ -242,7 +237,7 @@ JSModuleDef *jsc_module_loader(JSContext *ctx, size_t buf_len; uint8_t *buf; JSValue func_val; - char cname[1024]; + char cname[1000]; buf = js_load_file(ctx, &buf_len, module_name); if (!buf) { @@ -347,10 +342,12 @@ void help(void) "-o output set the output filename\n" "-n script_name set the script name (as used in stack traces)\n" "-N cname set the C name of the generated data\n" - "-m compile as Javascript module (default=autodetect)\n" + "-C compile as JS classic script (default=autodetect)\n" + "-m compile as ES module (default=autodetect)\n" "-D module_name compile a dynamically loaded module or worker\n" "-M module_name[,cname] add initialization code for an external C module\n" "-p prefix set the prefix of the generated C names\n" + "-P do not add default system modules\n" "-s strip the source code, specify twice to also strip debug info\n" "-S n set the maximum stack size to 'n' bytes (default=%d)\n", JS_GetVersion(), @@ -358,9 +355,48 @@ void help(void) exit(1); } +// TODO(bnoordhuis) share with qjs.c maybe +static int64_t parse_limit(const char *arg) { + char *p; + unsigned long unit = 1; // bytes for backcompat; qjs defaults to kilobytes + double d = strtod(arg, &p); + + if (p == arg) { + fprintf(stderr, "qjsc: invalid limit: %s\n", arg); + return -1; + } + + if (*p) { + switch (*p++) { + case 'b': case 'B': unit = 1UL << 0; break; + case 'k': case 'K': unit = 1UL << 10; break; /* IEC kibibytes */ + case 'm': case 'M': unit = 1UL << 20; break; /* IEC mebibytes */ + case 'g': case 'G': unit = 1UL << 30; break; /* IEC gigibytes */ + default: + fprintf(stderr, "qjsc: invalid limit: %s, unrecognized suffix, only k,m,g are allowed\n", arg); + return -1; + } + if (*p) { + fprintf(stderr, "qjsc: invalid limit: %s, only one suffix allowed\n", arg); + return -1; + } + } + + return (int64_t)(d * unit); +} + +static void check_hasarg(int optind, int argc, int opt) +{ + if (optind >= argc) { + fprintf(stderr, "qjsc: missing file for -%c\n", opt); + exit(1); + } +} + int main(int argc, char **argv) { - int c, i, verbose; + int optind = 1; + int i; const char *out_filename, *cname, *script_name; char cfilename[1024]; FILE *fo; @@ -369,56 +405,95 @@ int main(int argc, char **argv) int module; size_t stack_size; namelist_t dynamic_module_list; + bool load_system_modules = true; out_filename = NULL; script_name = NULL; output_type = OUTPUT_C; cname = NULL; module = -1; - verbose = 0; strip = 0; stack_size = 0; memset(&dynamic_module_list, 0, sizeof(dynamic_module_list)); - /* add system modules */ - namelist_add(&cmodule_list, "qjs:std", "std", 0); - namelist_add(&cmodule_list, "qjs:os", "os", 0); - namelist_add(&cmodule_list, "qjs:bjson", "bjson", 0); - namelist_add(&cmodule_list, "std", "std", 0); - namelist_add(&cmodule_list, "os", "os", 0); - namelist_add(&cmodule_list, "bjson", "bjson", 0); - - for(;;) { - c = getopt(argc, argv, "ho:N:mn:bxesvM:p:S:D:"); - if (c == -1) - break; - switch(c) { - case 'h': - help(); - case 'b': - output_type = OUTPUT_RAW; - break; - case 'o': - out_filename = optarg; - break; - case 'e': - output_type = OUTPUT_C_MAIN; + while (optind < argc && *argv[optind] == '-') { + char *arg = argv[optind] + 1; + const char *longopt = ""; + char *optarg = NULL; + /* a single - is not an option, it also stops argument scanning */ + if (!*arg) break; - case 'n': - script_name = optarg; - break; - case 'N': - cname = optarg; - break; - case 'm': - module = 1; - break; - case 'M': - { + optind++; + if (*arg == '-') { + longopt = arg + 1; + optarg = strchr(longopt, '='); + if (optarg) + *optarg++ = '\0'; + arg += strlen(arg); + /* -- stops argument scanning */ + if (!*longopt) + break; + } + for (; *arg || *longopt; longopt = "") { + char opt = *arg; + if (opt) { + arg++; + if (!optarg && *arg) + optarg = arg; + } + if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { + help(); + continue; + } + if (opt == 'b') { + output_type = OUTPUT_RAW; + continue; + } + if (opt == 'o') { + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } + out_filename = optarg; + continue; + } + if (opt == 'e') { + output_type = OUTPUT_C_MAIN; + continue; + } + if (opt == 'n') { + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } + script_name = optarg; + continue; + } + if (opt == 'N') { + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } + cname = optarg; + continue; + } + if (opt == 'C') { + module = 0; + continue; + } + if (opt == 'm') { + module = 1; + continue; + } + if (opt == 'M') { char *p; char path[1024]; char cname[1024]; + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } js__pstrcpy(path, sizeof(path), optarg); p = strchr(path, ','); if (p) { @@ -428,28 +503,54 @@ int main(int argc, char **argv) get_c_name(cname, sizeof(cname), path); } namelist_add(&cmodule_list, path, cname, 0); + continue; } - break; - case 'D': - namelist_add(&dynamic_module_list, optarg, NULL, 0); - break; - case 's': - strip++; - break; - case 'v': - verbose++; - break; - case 'p': - c_ident_prefix = optarg; - break; - case 'S': - stack_size = (size_t)strtod(optarg, NULL); - break; - default: - break; + if (opt == 'D') { + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } + namelist_add(&dynamic_module_list, optarg, NULL, 0); + continue; + } + if (opt == 'P') { + load_system_modules = false; + continue; + } + if (opt == 's') { + strip++; + continue; + } + if (opt == 'p') { + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } + c_ident_prefix = optarg; + continue; + } + if (opt == 'S') { + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } + stack_size = parse_limit(optarg); + continue; + } + help(); } } + if (load_system_modules) { + /* add system modules */ + namelist_add(&cmodule_list, "qjs:std", "std", 0); + namelist_add(&cmodule_list, "qjs:os", "os", 0); + namelist_add(&cmodule_list, "qjs:bjson", "bjson", 0); + namelist_add(&cmodule_list, "std", "std", 0); + namelist_add(&cmodule_list, "os", "os", 0); + namelist_add(&cmodule_list, "bjson", "bjson", 0); + } + if (optind >= argc) help(); diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-atom.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-atom.h old mode 100644 new mode 100755 index 358fe230..2a123284 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-atom.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-atom.h @@ -154,6 +154,7 @@ DEF(brand, "") DEF(hash_constructor, "#constructor") DEF(as, "as") DEF(from, "from") +DEF(fromAsync, "fromAsync") DEF(meta, "meta") DEF(_default_, "*default*") DEF(_star_, "*") @@ -217,6 +218,7 @@ DEF(Set, "Set") /* Map + 1 */ DEF(WeakMap, "WeakMap") /* Map + 2 */ DEF(WeakSet, "WeakSet") /* Map + 3 */ DEF(Iterator, "Iterator") +DEF(IteratorConcat, "Iterator Concat") DEF(IteratorHelper, "Iterator Helper") DEF(IteratorWrap, "Iterator Wrap") DEF(Map_Iterator, "Map Iterator") @@ -241,6 +243,7 @@ DEF(SyntaxError, "SyntaxError") DEF(TypeError, "TypeError") DEF(URIError, "URIError") DEF(InternalError, "InternalError") +DEF(DOMException, "DOMException") DEF(CallSite, "CallSite") /* private symbols */ DEF(Private_brand, "") diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-c-atomics.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-c-atomics.h old mode 100644 new mode 100755 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.c old mode 100644 new mode 100755 index 0222b2e5..2c7ac770 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.c @@ -22,27 +22,24 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "quickjs.h" #include #include #include #include #include #include -#if !defined(_MSC_VER) -#include -#endif #include #include #if !defined(_MSC_VER) #include +#include #endif #include #include #include #include -#if defined(_MSC_VER) -#include "dirent_compat.h" -#else +#if !defined(_MSC_VER) #include #endif #if defined(_WIN32) @@ -60,16 +57,19 @@ #define chdir _chdir #else #include +#include #if !defined(__wasi__) #include #include #include #include +#include #endif #if defined(__APPLE__) typedef sig_t sighandler_t; #include +#include #define environ (*_NSGetEnviron()) #endif @@ -80,21 +80,26 @@ extern char **environ; #endif /* _WIN32 */ -#if !defined(_WIN32) && !defined(__wasi__) -/* enable the os.Worker API. IT relies on POSIX threads */ -#define USE_WORKER -#endif +#include "cutils.h" +#include "list.h" +#include "quickjs-libc.h" -#ifdef USE_WORKER -#include +#if JS_HAVE_THREADS #include "quickjs-c-atomics.h" +#define USE_WORKER // enable os.Worker #endif -#include "cutils.h" -#include "list.h" -#include "quickjs-libc.h" +#ifndef S_IFBLK +#define S_IFBLK 0 +#endif + +#ifndef S_IFIFO +#define S_IFIFO 0 +#endif +#ifndef MAX_SAFE_INTEGER // already defined in amalgamation builds #define MAX_SAFE_INTEGER (((int64_t) 1 << 53) - 1) +#endif #ifndef QJS_NATIVE_MODULE_SUFFIX #ifdef _WIN32 @@ -129,6 +134,14 @@ typedef struct { JSValue func; } JSOSTimer; +typedef struct { + struct list_head link; + JSValue promise; + JSValue reason; +} JSRejectedPromiseEntry; + +#ifdef USE_WORKER + typedef struct { struct list_head link; uint8_t *data; @@ -138,14 +151,20 @@ typedef struct { size_t sab_tab_len; } JSWorkerMessage; +typedef struct JSWaker { +#ifdef _WIN32 + HANDLE handle; +#else + int read_fd; + int write_fd; +#endif +} JSWaker; + typedef struct { int ref_count; -#ifdef USE_WORKER - pthread_mutex_t mutex; -#endif + js_mutex_t mutex; struct list_head msg_queue; /* list of JSWorkerMessage.link */ - int read_fd; - int write_fd; + JSWaker waker; } JSWorkerMessagePipe; typedef struct { @@ -154,25 +173,38 @@ typedef struct { JSValue on_message_func; } JSWorkerMessageHandler; +#endif // USE_WORKER + typedef struct JSThreadState { struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */ struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */ struct list_head os_timers; /* list of JSOSTimer.link */ struct list_head port_list; /* list of JSWorkerMessageHandler.link */ + struct list_head rejected_promise_list; /* list of JSRejectedPromiseEntry.link */ int eval_script_recurse; /* only used in the main thread */ int64_t next_timer_id; /* for setTimeout / setInterval */ bool can_js_os_poll; /* not used in the main thread */ +#ifdef USE_WORKER JSWorkerMessagePipe *recv_pipe, *send_pipe; +#else + void *recv_pipe; +#endif // USE_WORKER JSClassID std_file_class_id; JSClassID worker_class_id; } JSThreadState; static uint64_t os_pending_signals; +static void *js_std_dbuf_realloc(void *opaque, void *ptr, size_t size) +{ + JSRuntime *rt = opaque; + return js_realloc_rt(rt, ptr, size); +} + static void js_std_dbuf_init(JSContext *ctx, DynBuf *s) { - dbuf_init2(s, JS_GetRuntime(ctx), (DynBufReallocFunc *)js_realloc_rt); + dbuf_init2(s, JS_GetRuntime(ctx), js_std_dbuf_realloc); } static bool my_isdigit(int c) @@ -190,8 +222,12 @@ static void js_set_thread_state(JSRuntime *rt, JSThreadState *ts) js_std_cmd(/*SetOpaque*/1, rt, ts); } +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif // __GNUC__ static JSValue js_printf_internal(JSContext *ctx, - int argc, JSValue *argv, FILE *fp) + int argc, JSValueConst *argv, FILE *fp) { char fmtbuf[32]; uint8_t cbuf[UTF8_CHAR_LEN_MAX+1]; @@ -207,7 +243,6 @@ static JSValue js_printf_internal(JSContext *ctx, int64_t int64_arg; double double_arg; const char *string_arg; - int (*dbuf_printf_fun)(DynBuf *s, const char *fmt, ...) = dbuf_printf; js_std_dbuf_init(ctx, &dbuf); @@ -332,17 +367,17 @@ static JSValue js_printf_internal(JSContext *ctx, q[0] = '6'; q[1] = '4'; q[3] = '\0'; - dbuf_printf_fun(&dbuf, fmtbuf, (int64_t)int64_arg); + dbuf_printf(&dbuf, fmtbuf, (int64_t)int64_arg); #else if (q >= fmtbuf + sizeof(fmtbuf) - 2) goto invalid; q[1] = q[-1]; q[-1] = q[0] = 'l'; q[2] = '\0'; - dbuf_printf_fun(&dbuf, fmtbuf, (long long)int64_arg); + dbuf_printf(&dbuf, fmtbuf, (long long)int64_arg); #endif } else { - dbuf_printf_fun(&dbuf, fmtbuf, (int)int64_arg); + dbuf_printf(&dbuf, fmtbuf, (int)int64_arg); } break; @@ -353,7 +388,7 @@ static JSValue js_printf_internal(JSContext *ctx, string_arg = JS_ToCString(ctx, argv[i++]); if (!string_arg) goto fail; - dbuf_printf_fun(&dbuf, fmtbuf, string_arg); + dbuf_printf(&dbuf, fmtbuf, string_arg); JS_FreeCString(ctx, string_arg); break; @@ -369,7 +404,7 @@ static JSValue js_printf_internal(JSContext *ctx, goto missing; if (JS_ToFloat64(ctx, &double_arg, argv[i++])) goto fail; - dbuf_printf_fun(&dbuf, fmtbuf, double_arg); + dbuf_printf(&dbuf, fmtbuf, double_arg); break; case '%': @@ -406,55 +441,50 @@ static JSValue js_printf_internal(JSContext *ctx, dbuf_free(&dbuf); return JS_EXCEPTION; } +#ifdef __GNUC__ +#pragma GCC diagnostic pop // ignored "-Wformat-nonliteral" +#endif // __GNUC__ uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename) { FILE *f; - uint8_t *buf; - size_t buf_len; - long lret; + size_t n, len; + uint8_t *p, *buf, tmp[8192]; f = fopen(filename, "rb"); if (!f) return NULL; - if (fseek(f, 0, SEEK_END) < 0) - goto fail; - lret = ftell(f); - if (lret < 0) - goto fail; - /* XXX: on Linux, ftell() return LONG_MAX for directories */ - if (lret == LONG_MAX) { - errno = EISDIR; - goto fail; - } - buf_len = lret; - if (fseek(f, 0, SEEK_SET) < 0) - goto fail; - if (ctx) - buf = js_malloc(ctx, buf_len + 1); - else - buf = malloc(buf_len + 1); - if (!buf) - goto fail; - if (fread(buf, 1, buf_len, f) != buf_len) { - errno = EIO; - if (ctx) - js_free(ctx, buf); - else - free(buf); - fail: - fclose(f); - return NULL; - } - buf[buf_len] = '\0'; + buf = NULL; + len = 0; + do { + n = fread(tmp, 1, sizeof(tmp), f); + if (ctx) { + p = js_realloc(ctx, buf, len + n + 1); + } else { + p = realloc(buf, len + n + 1); + } + if (!p) { + if (ctx) { + js_free(ctx, buf); + } else { + free(buf); + } + fclose(f); + return NULL; + } + memcpy(&p[len], tmp, n); + buf = p; + len += n; + buf[len] = '\0'; + } while (n == sizeof(tmp)); fclose(f); - *pbuf_len = buf_len; + *pbuf_len = len; return buf; } /* load and evaluate a file */ -static JSValue js_loadScript(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_loadScript(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { uint8_t *buf; const char *filename; @@ -478,7 +508,7 @@ static JSValue js_loadScript(JSContext *ctx, JSValue this_val, } static int get_bool_option(JSContext *ctx, bool *pbool, - JSValue obj, + JSValueConst obj, const char *option) { JSValue val; @@ -497,12 +527,13 @@ static void free_buf(JSRuntime *rt, void *opaque, void *ptr) { } /* load a file as a UTF-8 encoded string or Uint8Array */ -static JSValue js_std_loadFile(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_loadFile(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { uint8_t *buf; const char *filename; - JSValue ret, options_obj; + JSValueConst options_obj; + JSValue ret; size_t buf_len; bool binary = false; @@ -530,6 +561,67 @@ static JSValue js_std_loadFile(JSContext *ctx, JSValue this_val, return ret; } +static JSValue js_std_writeFile(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename; + const char *mode; + const void *buf; + size_t len, n; + JSValueConst data; + JSValue val, ret, unref; + bool release; + FILE *fp; + + ret = JS_EXCEPTION; + len = 0; + buf = ""; + mode = "w"; + data = argv[1]; + unref = JS_UNDEFINED; + release = false; + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + if (JS_IsObject(data)) { + val = JS_GetPropertyStr(ctx, data, "buffer"); + if (JS_IsException(val)) + goto exception; + if (JS_IsArrayBuffer(val)) { + data = unref = val; + } else { + JS_FreeValue(ctx, val); + } + } + if (JS_IsArrayBuffer(data)) { + buf = JS_GetArrayBuffer(ctx, &len, data); + mode = "wb"; + } else if (!JS_IsUndefined(data)) { + buf = JS_ToCStringLen(ctx, &len, data); + release = true; + } + if (!buf) + goto exception; + fp = fopen(filename, mode); + if (!fp) { + JS_ThrowPlainError(ctx, "error opening %s for writing", filename); + goto exception; + } + n = fwrite(buf, len, 1, fp); + fclose(fp); + if (n != 1) { + JS_ThrowPlainError(ctx, "error writing to %s", filename); + goto exception; + } + ret = JS_UNDEFINED; +exception: + JS_FreeCString(ctx, filename); + if (release) + JS_FreeCString(ctx, buf); + JS_FreeValue(ctx, unref); + return ret; +} + typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx, const char *module_name); @@ -638,11 +730,11 @@ static JSModuleDef *js_module_loader_so(JSContext *ctx, } #endif /* !_WIN32 */ -int js_module_set_import_meta(JSContext *ctx, JSValue func_val, +int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, bool use_realpath, bool is_main) { JSModuleDef *m; - char buf[PATH_MAX + 16]; + char buf[JS__PATH_MAX + 16]; JSValue meta_obj; JSAtom module_name_atom; const char *module_name; @@ -727,8 +819,8 @@ JSModuleDef *js_module_loader(JSContext *ctx, return m; } -static JSValue js_std_exit(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_exit(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int status; if (JS_ToInt32(ctx, &status, argv[0])) @@ -737,8 +829,8 @@ static JSValue js_std_exit(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_std_getenv(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_getenv(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *name, *str; name = JS_ToCString(ctx, argv[0]); @@ -774,8 +866,8 @@ static void unsetenv(const char *name) } #endif /* _WIN32 */ -static JSValue js_std_setenv(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_setenv(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *name, *value; name = JS_ToCString(ctx, argv[0]); @@ -792,8 +884,8 @@ static JSValue js_std_setenv(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_std_unsetenv(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_unsetenv(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *name; name = JS_ToCString(ctx, argv[0]); @@ -806,8 +898,8 @@ static JSValue js_std_unsetenv(JSContext *ctx, JSValue this_val, /* return an object containing the list of the available environment variables. */ -static JSValue js_std_getenviron(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_getenviron(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { char **envp; const char *name, *p, *value; @@ -843,8 +935,8 @@ static JSValue js_std_getenviron(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_std_gc(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_gc(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JS_RunGC(JS_GetRuntime(ctx)); return JS_UNDEFINED; @@ -855,15 +947,15 @@ static int interrupt_handler(JSRuntime *rt, void *opaque) return (os_pending_signals >> SIGINT) & 1; } -static JSValue js_evalScript(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = js_get_thread_state(rt); const char *str = NULL; size_t len; JSValue ret, obj; - JSValue options_obj; + JSValueConst options_obj, arg; bool backtrace_barrier = false; bool eval_function = false; bool eval_module = false; @@ -895,17 +987,17 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val, } if (eval_module) { - obj = argv[0]; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_MODULE) + arg = argv[0]; + if (JS_VALUE_GET_TAG(arg) != JS_TAG_MODULE) return JS_ThrowTypeError(ctx, "not a module"); - if (JS_ResolveModule(ctx, obj) < 0) + if (JS_ResolveModule(ctx, arg) < 0) return JS_EXCEPTION; - if (js_module_set_import_meta(ctx, obj, false, false) < 0) + if (js_module_set_import_meta(ctx, arg, false, false) < 0) return JS_EXCEPTION; - return JS_EvalFunction(ctx, obj); + return JS_EvalFunction(ctx, JS_DupValue(ctx, arg)); } if (!eval_function) { @@ -953,7 +1045,7 @@ static bool is_stdio(FILE *f) return f == stdin || f == stdout || f == stderr; } -static void js_std_file_finalizer(JSRuntime *rt, JSValue val) +static void js_std_file_finalizer(JSRuntime *rt, JSValueConst val) { JSThreadState *ts = js_get_thread_state(rt); JSSTDFile *s = JS_GetOpaque(val, ts->std_file_class_id); @@ -977,8 +1069,8 @@ static ssize_t js_get_errno(ssize_t ret) return ret; } -static JSValue js_std_strerror(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int err; if (JS_ToInt32(ctx, &err, argv[0])) @@ -1006,15 +1098,15 @@ static JSValue js_new_std_file(JSContext *ctx, FILE *f, bool is_popen) return obj; } -static void js_set_error_object(JSContext *ctx, JSValue obj, int err) +static void js_set_error_object(JSContext *ctx, JSValueConst obj, int err) { if (!JS_IsUndefined(obj)) { JS_SetPropertyStr(ctx, obj, "errno", JS_NewInt32(ctx, err)); } } -static JSValue js_std_open(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_open(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *filename, *mode = NULL; FILE *f; @@ -1050,8 +1142,8 @@ static JSValue js_std_open(JSContext *ctx, JSValue this_val, } #if !defined(__wasi__) -static JSValue js_std_popen(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_popen(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *filename, *mode = NULL; FILE *f; @@ -1063,7 +1155,7 @@ static JSValue js_std_popen(JSContext *ctx, JSValue this_val, mode = JS_ToCString(ctx, argv[1]); if (!mode) goto fail; - if (mode[strspn(mode, "rw")] != '\0') { + if (strcmp(mode, "r") && strcmp(mode, "w")) { JS_ThrowTypeError(ctx, "invalid file mode"); goto fail; } @@ -1087,8 +1179,8 @@ static JSValue js_std_popen(JSContext *ctx, JSValue this_val, } #endif // !defined(__wasi__) -static JSValue js_std_fdopen(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *mode; FILE *f; @@ -1121,8 +1213,8 @@ static JSValue js_std_fdopen(JSContext *ctx, JSValue this_val, } #if !defined(__wasi__) -static JSValue js_std_tmpfile(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { FILE *f; f = tmpfile(); @@ -1134,19 +1226,19 @@ static JSValue js_std_tmpfile(JSContext *ctx, JSValue this_val, } #endif -static JSValue js_std_sprintf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_sprintf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return js_printf_internal(ctx, argc, argv, NULL); } -static JSValue js_std_printf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_printf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return js_printf_internal(ctx, argc, argv, stdout); } -static FILE *js_std_file_get(JSContext *ctx, JSValue obj) +static FILE *js_std_file_get(JSContext *ctx, JSValueConst obj) { JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = js_get_thread_state(rt); @@ -1160,8 +1252,8 @@ static FILE *js_std_file_get(JSContext *ctx, JSValue obj) return s->f; } -static JSValue js_std_file_puts(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_std_file_puts(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { FILE *f; int i; @@ -1186,8 +1278,8 @@ static JSValue js_std_file_puts(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_std_file_close(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_file_close(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = js_get_thread_state(rt); @@ -1209,8 +1301,8 @@ static JSValue js_std_file_close(JSContext *ctx, JSValue this_val, return JS_NewInt32(ctx, err); } -static JSValue js_std_file_printf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { FILE *f = js_std_file_get(ctx, this_val); if (!f) @@ -1218,8 +1310,8 @@ static JSValue js_std_file_printf(JSContext *ctx, JSValue this_val, return js_printf_internal(ctx, argc, argv, f); } -static JSValue js_std_file_flush(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { FILE *f = js_std_file_get(ctx, this_val); if (!f) @@ -1228,14 +1320,14 @@ static JSValue js_std_file_flush(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_std_file_tell(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int is_bigint) +static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int is_bigint) { FILE *f = js_std_file_get(ctx, this_val); int64_t pos; if (!f) return JS_EXCEPTION; -#if defined(__linux__) +#if defined(__linux__) || defined(__GLIBC__) pos = ftello(f); #else pos = ftell(f); @@ -1246,8 +1338,8 @@ static JSValue js_std_file_tell(JSContext *ctx, JSValue this_val, return JS_NewInt64(ctx, pos); } -static JSValue js_std_file_seek(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { FILE *f = js_std_file_get(ctx, this_val); int64_t pos; @@ -1258,7 +1350,7 @@ static JSValue js_std_file_seek(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; if (JS_ToInt32(ctx, &whence, argv[1])) return JS_EXCEPTION; -#if defined(__linux__) +#if defined(__linux__) || defined(__GLIBC__) ret = fseeko(f, pos, whence); #else ret = fseek(f, pos, whence); @@ -1268,8 +1360,8 @@ static JSValue js_std_file_seek(JSContext *ctx, JSValue this_val, return JS_NewInt32(ctx, ret); } -static JSValue js_std_file_eof(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_file_eof(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { FILE *f = js_std_file_get(ctx, this_val); if (!f) @@ -1277,8 +1369,8 @@ static JSValue js_std_file_eof(JSContext *ctx, JSValue this_val, return JS_NewBool(ctx, feof(f)); } -static JSValue js_std_file_error(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_file_error(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { FILE *f = js_std_file_get(ctx, this_val); if (!f) @@ -1286,8 +1378,8 @@ static JSValue js_std_file_error(JSContext *ctx, JSValue this_val, return JS_NewBool(ctx, ferror(f)); } -static JSValue js_std_file_clearerr(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_file_clearerr(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { FILE *f = js_std_file_get(ctx, this_val); if (!f) @@ -1296,8 +1388,8 @@ static JSValue js_std_file_clearerr(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_std_file_fileno(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_file_fileno(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { FILE *f = js_std_file_get(ctx, this_val); if (!f) @@ -1305,8 +1397,8 @@ static JSValue js_std_file_fileno(JSContext *ctx, JSValue this_val, return JS_NewInt32(ctx, fileno(f)); } -static JSValue js_std_file_read_write(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_std_file_read_write(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { FILE *f = js_std_file_get(ctx, this_val); uint64_t pos, len; @@ -1332,8 +1424,8 @@ static JSValue js_std_file_read_write(JSContext *ctx, JSValue this_val, } /* XXX: could use less memory and go faster */ -static JSValue js_std_file_getline(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_file_getline(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { FILE *f = js_std_file_get(ctx, this_val); int c; @@ -1368,8 +1460,8 @@ static JSValue js_std_file_getline(JSContext *ctx, JSValue this_val, } /* XXX: could use less memory and go faster */ -static JSValue js_std_file_readAs(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_std_file_readAs(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { FILE *f = js_std_file_get(ctx, this_val); int c; @@ -1377,7 +1469,7 @@ static JSValue js_std_file_readAs(JSContext *ctx, JSValue this_val, JSValue obj; uint64_t max_size64; size_t max_size; - JSValue max_size_val; + JSValueConst max_size_val; if (!f) return JS_EXCEPTION; @@ -1414,8 +1506,8 @@ static JSValue js_std_file_readAs(JSContext *ctx, JSValue this_val, return obj; } -static JSValue js_std_file_getByte(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_file_getByte(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { FILE *f = js_std_file_get(ctx, this_val); if (!f) @@ -1423,8 +1515,8 @@ static JSValue js_std_file_getByte(JSContext *ctx, JSValue this_val, return JS_NewInt32(ctx, fgetc(f)); } -static JSValue js_std_file_putByte(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { FILE *f = js_std_file_get(ctx, this_val); int c; @@ -1476,8 +1568,8 @@ static int http_get_status(const char *buf) return atoi(p); } -static JSValue js_std_urlGet(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *url; DynBuf cmd_buf; @@ -1487,7 +1579,7 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValue this_val, size_t i, len; int status; JSValue response = JS_UNDEFINED, ret_obj; - JSValue options_obj; + JSValueConst options_obj; FILE *f; bool binary_flag, full_flag; @@ -1668,6 +1760,7 @@ static const JSCFunctionListEntry js_std_funcs[] = { JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ), #endif JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ), + JS_CFUNC_DEF("writeFile", 2, js_std_writeFile ), JS_CFUNC_DEF("strerror", 1, js_std_strerror ), /* FILE I/O */ @@ -1748,8 +1841,8 @@ JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name) /**********************************************************/ /* 'os' object */ -static JSValue js_os_open(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_open(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *filename; int flags, mode, ret; @@ -1778,8 +1871,8 @@ static JSValue js_os_open(JSContext *ctx, JSValue this_val, return JS_NewInt32(ctx, ret); } -static JSValue js_os_close(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_close(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int fd, ret; if (JS_ToInt32(ctx, &fd, argv[0])) @@ -1788,8 +1881,8 @@ static JSValue js_os_close(JSContext *ctx, JSValue this_val, return JS_NewInt32(ctx, ret); } -static JSValue js_os_seek(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_seek(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int fd, whence; int64_t pos, ret; @@ -1797,7 +1890,7 @@ static JSValue js_os_seek(JSContext *ctx, JSValue this_val, if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; - is_bigint = JS_IsBigInt(ctx, argv[1]); + is_bigint = JS_IsBigInt(argv[1]); if (JS_ToInt64Ext(ctx, &pos, argv[1])) return JS_EXCEPTION; if (JS_ToInt32(ctx, &whence, argv[2])) @@ -1811,8 +1904,8 @@ static JSValue js_os_seek(JSContext *ctx, JSValue this_val, return JS_NewInt64(ctx, ret); } -static JSValue js_os_read_write(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { int fd; uint64_t pos, len; @@ -1838,8 +1931,8 @@ static JSValue js_os_read_write(JSContext *ctx, JSValue this_val, return JS_NewInt64(ctx, ret); } -static JSValue js_os_isatty(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int fd; if (JS_ToInt32(ctx, &fd, argv[0])) @@ -1848,8 +1941,8 @@ static JSValue js_os_isatty(JSContext *ctx, JSValue this_val, } #if defined(_WIN32) -static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int fd; HANDLE handle; @@ -1874,8 +1967,8 @@ static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValue this_val, #define __ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #define __ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 -static JSValue js_os_ttySetRaw(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int fd; HANDLE handle; @@ -1892,8 +1985,8 @@ static JSValue js_os_ttySetRaw(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } #elif !defined(__wasi__) -static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int fd; struct winsize ws; @@ -1922,8 +2015,8 @@ static void term_exit(void) } /* XXX: should add a way to go back to normal mode */ -static JSValue js_os_ttySetRaw(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { struct termios tty; int fd; @@ -1952,8 +2045,8 @@ static JSValue js_os_ttySetRaw(JSContext *ctx, JSValue this_val, #endif /* !_WIN32 && !__wasi__ */ -static JSValue js_os_remove(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *filename; int ret; @@ -1964,7 +2057,7 @@ static JSValue js_os_remove(JSContext *ctx, JSValue this_val, #if defined(_WIN32) { struct stat st; - if (stat(filename, &st) == 0 && S_ISDIR(st.st_mode)) { + if (stat(filename, &st) == 0 && (st.st_mode & _S_IFDIR)) { ret = rmdir(filename); } else { ret = unlink(filename); @@ -1978,8 +2071,8 @@ static JSValue js_os_remove(JSContext *ctx, JSValue this_val, return JS_NewInt32(ctx, ret); } -static JSValue js_os_rename(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *oldpath, *newpath; int ret; @@ -2027,14 +2120,14 @@ static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh) js_free_rt(rt, rh); } -static JSValue js_os_setReadHandler(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = js_get_thread_state(rt); JSOSRWHandler *rh; int fd; - JSValue func; + JSValueConst func; if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; @@ -2097,14 +2190,14 @@ static void os_signal_handler(int sig_num) typedef void (*sighandler_t)(int sig_num); #endif -static JSValue js_os_signal(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = js_get_thread_state(rt); JSOSSignalHandler *sh; uint32_t sig_num; - JSValue func; + JSValueConst func; sighandler_t handler; if (!is_main_thread(rt)) @@ -2145,8 +2238,8 @@ static JSValue js_os_signal(JSContext *ctx, JSValue this_val, } #if !defined(_WIN32) && !defined(__wasi__) -static JSValue js_os_cputime(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_cputime(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { struct rusage ru; int64_t cputime; @@ -2159,8 +2252,18 @@ static JSValue js_os_cputime(JSContext *ctx, JSValue this_val, } #endif -static JSValue js_os_now(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_exepath(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + char buf[JS__PATH_MAX]; + size_t len = sizeof(buf); + if (js_exepath(buf, &len)) + return JS_UNDEFINED; + return JS_NewStringLen(ctx, buf, len); +} + +static JSValue js_os_now(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return JS_NewInt64(ctx, js__hrtime_ns() / 1000); } @@ -2179,13 +2282,13 @@ static void free_timer(JSRuntime *rt, JSOSTimer *th) // TODO(bnoordhuis) accept string as first arg and eval at timer expiry // TODO(bnoordhuis) retain argv[2..] as args for callback if argc > 2 -static JSValue js_os_setTimeout(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = js_get_thread_state(rt); int64_t delay; - JSValue func; + JSValueConst func; JSOSTimer *th; func = argv[0]; @@ -2222,8 +2325,8 @@ static JSOSTimer *find_timer_by_id(JSThreadState *ts, int timer_id) return NULL; } -static JSValue js_os_clearTimeout(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = js_get_thread_state(rt); @@ -2324,61 +2427,81 @@ static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts, in return 0; } -#if defined(_WIN32) +#ifdef USE_WORKER -static int js_os_poll(JSContext *ctx) +#ifdef _WIN32 + +static int js_waker_init(JSWaker *w) { - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - int min_delay, console_fd; - JSOSRWHandler *rh; - struct list_head *el; + w->handle = CreateEvent(NULL, TRUE, FALSE, NULL); + return w->handle ? 0 : -1; +} - /* XXX: handle signals if useful */ +static void js_waker_signal(JSWaker *w) +{ + SetEvent(w->handle); +} - if (js_os_run_timers(rt, ctx, ts, &min_delay)) +static void js_waker_clear(JSWaker *w) +{ + ResetEvent(w->handle); +} + +static void js_waker_close(JSWaker *w) +{ + CloseHandle(w->handle); + w->handle = INVALID_HANDLE_VALUE; +} + +#else // !_WIN32 + +static int js_waker_init(JSWaker *w) +{ + int fds[2]; + + if (pipe(fds) < 0) return -1; - if (min_delay == 0) - return 0; // expired timer - if (min_delay < 0) - if (list_empty(&ts->os_rw_handlers)) - return -1; /* no more events */ + w->read_fd = fds[0]; + w->write_fd = fds[1]; + return 0; +} - console_fd = -1; - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { - console_fd = rh->fd; +static void js_waker_signal(JSWaker *w) +{ + int ret; + + for(;;) { + ret = write(w->write_fd, "", 1); + if (ret == 1) + break; + if (ret < 0 && (errno != EAGAIN || errno != EINTR)) break; - } } +} - if (console_fd >= 0) { - DWORD ti, ret; - HANDLE handle; - if (min_delay == -1) - ti = INFINITE; - else - ti = min_delay; - handle = (HANDLE)_get_osfhandle(console_fd); - ret = WaitForSingleObject(handle, ti); - if (ret == WAIT_OBJECT_0) { - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) { - return call_handler(ctx, rh->rw_func[0]); - /* must stop because the list may have been modified */ - } - } - } - } else { - Sleep(min_delay); +static void js_waker_clear(JSWaker *w) +{ + uint8_t buf[16]; + int ret; + + for(;;) { + ret = read(w->read_fd, buf, sizeof(buf)); + if (ret >= 0) + break; + if (errno != EAGAIN && errno != EINTR) + break; } - return 0; } -#else -#ifdef USE_WORKER +static void js_waker_close(JSWaker *w) +{ + close(w->read_fd); + close(w->write_fd); + w->read_fd = -1; + w->write_fd = -1; +} + +#endif // _WIN32 static void js_free_message(JSWorkerMessage *msg); @@ -2392,7 +2515,7 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, JSWorkerMessage *msg; JSValue obj, data_obj, func, retval; - pthread_mutex_lock(&ps->mutex); + js_mutex_lock(&ps->mutex); if (!list_empty(&ps->msg_queue)) { el = ps->msg_queue.next; msg = list_entry(el, JSWorkerMessage, link); @@ -2400,19 +2523,11 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, /* remove the message from the queue */ list_del(&msg->link); - if (list_empty(&ps->msg_queue)) { - uint8_t buf[16]; - int ret; - for(;;) { - ret = read(ps->read_fd, buf, sizeof(buf)); - if (ret >= 0) - break; - if (errno != EAGAIN && errno != EINTR) - break; - } - } + // drain read end of pipe + if (list_empty(&ps->msg_queue)) + js_waker_clear(&ps->waker); - pthread_mutex_unlock(&ps->mutex); + js_mutex_unlock(&ps->mutex); data_obj = JS_ReadObject(ctx, msg->data, msg->data_len, JS_READ_OBJ_SAB | JS_READ_OBJ_REFERENCE); @@ -2431,7 +2546,7 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, /* 'func' might be destroyed when calling itself (if it frees the handler), so must take extra care */ func = JS_DupValue(ctx, port->on_message_func); - retval = JS_Call(ctx, func, JS_UNDEFINED, 1, &obj); + retval = JS_Call(ctx, func, JS_UNDEFINED, 1, (JSValueConst *)&obj); JS_FreeValue(ctx, obj); JS_FreeValue(ctx, func); if (JS_IsException(retval)) { @@ -2442,28 +2557,92 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, } ret = 1; } else { - pthread_mutex_unlock(&ps->mutex); + js_mutex_unlock(&ps->mutex); ret = 0; } return ret; } -#else -static int handle_posted_message(JSRuntime *rt, JSContext *ctx, - JSWorkerMessageHandler *port) + +#endif // USE_WORKER + +#if defined(_WIN32) +static int js_os_poll(JSContext *ctx) { + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + int min_delay, count; + JSOSRWHandler *rh; + struct list_head *el; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; // 64 + + /* XXX: handle signals if useful */ + + if (js_os_run_timers(rt, ctx, ts, &min_delay)) + return -1; + if (min_delay == 0) + return 0; // expired timer + if (min_delay < 0) + if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->port_list)) + return -1; /* no more events */ + + count = 0; + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) + handles[count++] = (HANDLE)_get_osfhandle(rh->fd); // stdin + if (count == (int)countof(handles)) + break; + } + + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (JS_IsNull(port->on_message_func)) + continue; + handles[count++] = port->recv_pipe->waker.handle; + if (count == (int)countof(handles)) + break; + } + + if (count > 0) { + DWORD ret, timeout = INFINITE; + if (min_delay != -1) + timeout = min_delay; + ret = WaitForMultipleObjects(count, handles, FALSE, timeout); + if (ret < count) { + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { + return call_handler(ctx, rh->rw_func[0]); + /* must stop because the list may have been modified */ + } + } + + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (!JS_IsNull(port->on_message_func)) { + JSWorkerMessagePipe *ps = port->recv_pipe; + if (ps->waker.handle == handles[ret]) { + if (handle_posted_message(rt, ctx, port)) + goto done; + } + } + } + } + } else { + Sleep(min_delay); + } +done: return 0; } -#endif - +#else // !defined(_WIN32) static int js_os_poll(JSContext *ctx) { JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = js_get_thread_state(rt); - int ret, fd_max, min_delay; - fd_set rfds, wfds; + int r, w, ret, nfds, min_delay; JSOSRWHandler *rh; struct list_head *el; - struct timeval tv, *tvp; + struct pollfd *pfd, *pfds, pfds_local[64]; /* only check signals in the main thread */ if (!ts->recv_pipe && @@ -2489,65 +2668,86 @@ static int js_os_poll(JSContext *ctx) if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->port_list)) return -1; /* no more events */ - tvp = NULL; - if (min_delay >= 0) { - tv.tv_sec = min_delay / 1000; - tv.tv_usec = (min_delay % 1000) * 1000; - tvp = &tv; + nfds = 0; + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + nfds += (!JS_IsNull(rh->rw_func[0]) || !JS_IsNull(rh->rw_func[1])); + } + +#ifdef USE_WORKER + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + nfds += !JS_IsNull(port->on_message_func); + } +#endif // USE_WORKER + + pfd = pfds = pfds_local; + if (nfds > (int)countof(pfds_local)) { + pfd = pfds = js_malloc(ctx, nfds * sizeof(*pfd)); + if (!pfd) + return -1; } - FD_ZERO(&rfds); - FD_ZERO(&wfds); - fd_max = -1; list_for_each(el, &ts->os_rw_handlers) { rh = list_entry(el, JSOSRWHandler, link); - fd_max = max_int(fd_max, rh->fd); - if (!JS_IsNull(rh->rw_func[0])) - FD_SET(rh->fd, &rfds); - if (!JS_IsNull(rh->rw_func[1])) - FD_SET(rh->fd, &wfds); + r = POLLIN * !JS_IsNull(rh->rw_func[0]); + w = POLLOUT * !JS_IsNull(rh->rw_func[1]); + if (r || w) + *pfd++ = (struct pollfd){rh->fd, r|w, 0}; } +#ifdef USE_WORKER list_for_each(el, &ts->port_list) { JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); if (!JS_IsNull(port->on_message_func)) { JSWorkerMessagePipe *ps = port->recv_pipe; - fd_max = max_int(fd_max, ps->read_fd); - FD_SET(ps->read_fd, &rfds); + *pfd++ = (struct pollfd){ps->waker.read_fd, POLLIN, 0}; } } +#endif // USE_WORKER - ret = select(fd_max + 1, &rfds, &wfds, NULL, tvp); - if (ret > 0) { - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (!JS_IsNull(rh->rw_func[0]) && - FD_ISSET(rh->fd, &rfds)) { - return call_handler(ctx, rh->rw_func[0]); + // FIXME(bnoordhuis) the loop below is quadratic in theory but + // linear-ish in practice because we bail out on the first hit, + // i.e., it's probably good enough for now + ret = 0; + nfds = poll(pfds, nfds, min_delay); + for (pfd = pfds; nfds-- > 0; pfd++) { + rh = find_rh(ts, pfd->fd); + if (rh) { + r = (POLLERR|POLLHUP|POLLNVAL|POLLIN) * !JS_IsNull(rh->rw_func[0]); + w = (POLLERR|POLLHUP|POLLNVAL|POLLOUT) * !JS_IsNull(rh->rw_func[1]); + if (r & pfd->revents) { + ret = call_handler(ctx, rh->rw_func[0]); + goto done; /* must stop because the list may have been modified */ } - if (!JS_IsNull(rh->rw_func[1]) && - FD_ISSET(rh->fd, &wfds)) { - return call_handler(ctx, rh->rw_func[1]); + if (w & pfd->revents) { + ret = call_handler(ctx, rh->rw_func[1]); + goto done; /* must stop because the list may have been modified */ } - } - - list_for_each(el, &ts->port_list) { - JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); - if (!JS_IsNull(port->on_message_func)) { - JSWorkerMessagePipe *ps = port->recv_pipe; - if (FD_ISSET(ps->read_fd, &rfds)) { - if (handle_posted_message(rt, ctx, port)) - goto done; + } else { +#ifdef USE_WORKER + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (!JS_IsNull(port->on_message_func)) { + JSWorkerMessagePipe *ps = port->recv_pipe; + if (pfd->fd == ps->waker.read_fd) { + if (handle_posted_message(rt, ctx, port)) + goto done; + } } } +#endif // USE_WORKER } } - done: - return 0; +done: + if (pfds != pfds_local) + js_free(ctx, pfds); + return ret; } -#endif /* !_WIN32 */ +#endif // defined(_WIN32) + static JSValue make_obj_error(JSContext *ctx, JSValue obj, @@ -2574,10 +2774,10 @@ static JSValue make_string_error(JSContext *ctx, } /* return [cwd, errorcode] */ -static JSValue js_os_getcwd(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - char buf[PATH_MAX]; + char buf[JS__PATH_MAX]; int err; if (!getcwd(buf, sizeof(buf))) { @@ -2589,8 +2789,8 @@ static JSValue js_os_getcwd(JSContext *ctx, JSValue this_val, return make_string_error(ctx, buf, err); } -static JSValue js_os_chdir(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_chdir(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *target; int err; @@ -2603,8 +2803,8 @@ static JSValue js_os_chdir(JSContext *ctx, JSValue this_val, return JS_NewInt32(ctx, err); } -static JSValue js_os_mkdir(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_mkdir(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int mode, ret; const char *path; @@ -2629,9 +2829,45 @@ static JSValue js_os_mkdir(JSContext *ctx, JSValue this_val, } /* return [array, errorcode] */ -static JSValue js_os_readdir(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_readdir(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { +#ifdef _WIN32 + const char *path; + JSValue obj; + int err; + uint32_t len; + HANDLE h; + WIN32_FIND_DATAA d; + char s[1024]; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) { + JS_FreeCString(ctx, path); + return JS_EXCEPTION; + } + snprintf(s, sizeof(s), "%s/*", path); + JS_FreeCString(ctx, path); + err = 0; + h = FindFirstFileA(s, &d); + if (h == INVALID_HANDLE_VALUE) + err = GetLastError(); + if (err) + goto done; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewString(ctx, "."), + JS_PROP_C_W_E); + for (len = 1; FindNextFileA(h, &d); len++) { + JS_DefinePropertyValueUint32(ctx, obj, len, + JS_NewString(ctx, d.cFileName), + JS_PROP_C_W_E); + } + FindClose(h); +done: + return make_obj_error(ctx, obj, err); +#else const char *path; DIR *f; struct dirent *d; @@ -2670,6 +2906,7 @@ static JSValue js_os_readdir(JSContext *ctx, JSValue this_val, closedir(f); done: return make_obj_error(ctx, obj, err); +#endif } #if !defined(_WIN32) @@ -2680,8 +2917,8 @@ static int64_t timespec_to_ms(const struct timespec *tv) #endif /* return [obj, errcode] */ -static JSValue js_os_stat(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int is_lstat) +static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int is_lstat) { const char *path; int err, res; @@ -2779,8 +3016,8 @@ static void ms_to_timeval(struct timeval *tv, uint64_t v) } #endif -static JSValue js_os_utimes(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *path; int64_t atime, mtime; @@ -2813,8 +3050,8 @@ static JSValue js_os_utimes(JSContext *ctx, JSValue this_val, } /* sleep(delay_ms) */ -static JSValue js_os_sleep(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int64_t delay; int ret; @@ -2845,7 +3082,7 @@ static JSValue js_os_sleep(JSContext *ctx, JSValue this_val, #if defined(_WIN32) static char *realpath(const char *path, char *buf) { - if (!_fullpath(buf, path, PATH_MAX)) { + if (!_fullpath(buf, path, JS__PATH_MAX)) { errno = ENOENT; return NULL; } else { @@ -2856,11 +3093,11 @@ static char *realpath(const char *path, char *buf) #if !defined(__wasi__) /* return [path, errorcode] */ -static JSValue js_os_realpath(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *path; - char buf[PATH_MAX], *res; + char buf[JS__PATH_MAX], *res; int err; path = JS_ToCString(ctx, argv[0]); @@ -2878,9 +3115,9 @@ static JSValue js_os_realpath(JSContext *ctx, JSValue this_val, } #endif -#if !defined(_WIN32) && !defined(__wasi__) -static JSValue js_os_symlink(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +#if !defined(_WIN32) && !defined(__wasi__) && !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) +static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *target, *linkpath; int err; @@ -2900,11 +3137,11 @@ static JSValue js_os_symlink(JSContext *ctx, JSValue this_val, } /* return [path, errorcode] */ -static JSValue js_os_readlink(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *path; - char buf[PATH_MAX]; + char buf[JS__PATH_MAX]; int err; ssize_t res; @@ -2986,7 +3223,7 @@ static char **build_envp(JSContext *ctx, JSValue obj) static int my_execvpe(const char *filename, char **argv, char **envp) { char *path, *p, *p_next, *p1; - char buf[PATH_MAX]; + char buf[JS__PATH_MAX]; size_t filename_len, path_len; bool eacces_error; @@ -3013,7 +3250,7 @@ static int my_execvpe(const char *filename, char **argv, char **envp) path_len = p1 - p; } /* path too long */ - if ((path_len + 1 + filename_len + 1) > PATH_MAX) + if ((path_len + 1 + filename_len + 1) > JS__PATH_MAX) continue; memcpy(buf, p, path_len); buf[path_len] = '/'; @@ -3046,16 +3283,16 @@ static js_once_t js_os_exec_once = JS_ONCE_INIT; static void js_os_exec_once_init(void) { - js_os_exec_closefrom = dlsym(RTLD_DEFAULT, "closefrom"); + *(void **) (&js_os_exec_closefrom) = dlsym(RTLD_DEFAULT, "closefrom"); } #endif /* exec(args[, options]) -> exitcode */ -static JSValue js_os_exec(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue options, args = argv[0]; + JSValueConst options, args = argv[0]; JSValue val, ret_val; const char **exec_argv, *file = NULL, *str, *cwd = NULL; char **envp = environ; @@ -3065,6 +3302,8 @@ static JSValue js_os_exec(JSContext *ctx, JSValue this_val, static const char *std_name[3] = { "stdin", "stdout", "stderr" }; int std_fds[3]; uint32_t uid = -1, gid = -1; + int ngroups = -1; + gid_t groups[64]; val = JS_GetPropertyStr(ctx, args, "length"); if (JS_IsException(val)) @@ -3168,6 +3407,40 @@ static JSValue js_os_exec(JSContext *ctx, JSValue this_val, if (ret) goto exception; } + + val = JS_GetPropertyStr(ctx, options, "groups"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + int64_t idx, len; + JSValue prop; + uint32_t id; + ngroups = 0; + if (JS_GetLength(ctx, val, &len)) { + JS_FreeValue(ctx, val); + goto exception; + } + for (idx = 0; idx < len; idx++) { + prop = JS_GetPropertyInt64(ctx, val, idx); + if (JS_IsException(prop)) + break; + if (JS_IsUndefined(prop)) + continue; + ret = JS_ToUint32(ctx, &id, prop); + JS_FreeValue(ctx, prop); + if (ret) + break; + if (ngroups == countof(groups)) { + JS_ThrowRangeError(ctx, "too many groups"); + break; + } + groups[ngroups++] = id; + } + JS_FreeValue(ctx, val); + if (idx < len) + goto exception; + } + } #if !defined(EMSCRIPTEN) && !defined(__wasi__) @@ -3203,6 +3476,10 @@ static JSValue js_os_exec(JSContext *ctx, JSValue this_val, if (chdir(cwd) < 0) _exit(127); } + if (ngroups != -1) { + if (setgroups(ngroups, groups) < 0) + _exit(127); + } if (uid != -1) { if (setuid(uid) < 0) _exit(127); @@ -3260,15 +3537,15 @@ static JSValue js_os_exec(JSContext *ctx, JSValue this_val, } /* getpid() -> pid */ -static JSValue js_os_getpid(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_getpid(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return JS_NewInt32(ctx, getpid()); } /* waitpid(pid, block) -> [pid, status] */ -static JSValue js_os_waitpid(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int pid, status, options, ret; JSValue obj; @@ -3295,8 +3572,8 @@ static JSValue js_os_waitpid(JSContext *ctx, JSValue this_val, } /* pipe() -> [read_fd, write_fd] or null if error */ -static JSValue js_os_pipe(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int pipe_fds[2], ret; JSValue obj; @@ -3315,8 +3592,8 @@ static JSValue js_os_pipe(JSContext *ctx, JSValue this_val, } /* kill(pid, sig) */ -static JSValue js_os_kill(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int pid, sig, ret; @@ -3329,8 +3606,8 @@ static JSValue js_os_kill(JSContext *ctx, JSValue this_val, } /* dup(fd) */ -static JSValue js_os_dup(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_dup(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int fd, ret; @@ -3341,8 +3618,8 @@ static JSValue js_os_dup(JSContext *ctx, JSValue this_val, } /* dup2(fd) */ -static JSValue js_os_dup2(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int fd, fd2, ret; @@ -3417,22 +3694,17 @@ static void js_sab_dup(void *opaque, void *ptr) static JSWorkerMessagePipe *js_new_message_pipe(void) { JSWorkerMessagePipe *ps; - int pipe_fds[2]; - - if (pipe(pipe_fds) < 0) - return NULL; ps = malloc(sizeof(*ps)); - if (!ps) { - close(pipe_fds[0]); - close(pipe_fds[1]); + if (!ps) + return NULL; + if (js_waker_init(&ps->waker)) { + free(ps); return NULL; } ps->ref_count = 1; init_list_head(&ps->msg_queue); - pthread_mutex_init(&ps->mutex, NULL); - ps->read_fd = pipe_fds[0]; - ps->write_fd = pipe_fds[1]; + js_mutex_init(&ps->mutex); return ps; } @@ -3470,9 +3742,8 @@ static void js_free_message_pipe(JSWorkerMessagePipe *ps) msg = list_entry(el, JSWorkerMessage, link); js_free_message(msg); } - pthread_mutex_destroy(&ps->mutex); - close(ps->read_fd); - close(ps->write_fd); + js_mutex_destroy(&ps->mutex); + js_waker_close(&ps->waker); free(ps); } } @@ -3487,7 +3758,7 @@ static void js_free_port(JSRuntime *rt, JSWorkerMessageHandler *port) } } -static void js_worker_finalizer(JSRuntime *rt, JSValue val) +static void js_worker_finalizer(JSRuntime *rt, JSValueConst val) { JSThreadState *ts = js_get_thread_state(rt); JSWorkerData *worker = JS_GetOpaque(val, ts->worker_class_id); @@ -3504,7 +3775,7 @@ static JSClassDef js_worker_class = { .finalizer = js_worker_finalizer, }; -static void *worker_func(void *opaque) +static void worker_func(void *opaque) { WorkerFuncArgs *args = opaque; JSRuntime *rt; @@ -3551,10 +3822,9 @@ static void *worker_func(void *opaque) js_std_free_handlers(rt); JS_FreeContext(ctx); JS_FreeRuntime(rt); - return NULL; } -static JSValue js_worker_ctor_internal(JSContext *ctx, JSValue new_target, +static JSValue js_worker_ctor_internal(JSContext *ctx, JSValueConst new_target, JSWorkerMessagePipe *recv_pipe, JSWorkerMessagePipe *send_pipe) { @@ -3588,13 +3858,12 @@ static JSValue js_worker_ctor_internal(JSContext *ctx, JSValue new_target, return JS_EXCEPTION; } -static JSValue js_worker_ctor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv) +static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { JSRuntime *rt = JS_GetRuntime(ctx); WorkerFuncArgs *args = NULL; - pthread_t tid; - pthread_attr_t attr; + js_thread_t thr; JSValue obj = JS_UNDEFINED; int ret; const char *filename = NULL, *basename; @@ -3641,14 +3910,7 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValue new_target, if (JS_IsException(obj)) goto fail; - pthread_attr_init(&attr); - /* no join at the end */ - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - // musl libc gives threads 80 kb stacks, much smaller than - // JS_DEFAULT_STACK_SIZE (1 MB) - pthread_attr_setstacksize(&attr, 2 << 20); // 2 MB, glibc default - ret = pthread_create(&tid, &attr, worker_func, args); - pthread_attr_destroy(&attr); + ret = js_thread_create(&thr, worker_func, args, JS_THREAD_CREATE_DETACHED); if (ret != 0) { JS_ThrowTypeError(ctx, "could not create worker"); goto fail; @@ -3672,8 +3934,8 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValue new_target, return JS_EXCEPTION; } -static JSValue js_worker_postMessage(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = js_get_thread_state(rt); @@ -3723,21 +3985,12 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValue this_val, } ps = worker->send_pipe; - pthread_mutex_lock(&ps->mutex); + js_mutex_lock(&ps->mutex); /* indicate that data is present */ - if (list_empty(&ps->msg_queue)) { - uint8_t ch = '\0'; - int ret; - for(;;) { - ret = write(ps->write_fd, &ch, 1); - if (ret == 1) - break; - if (ret < 0 && (errno != EAGAIN || errno != EINTR)) - break; - } - } + if (list_empty(&ps->msg_queue)) + js_waker_signal(&ps->waker); list_add_tail(&msg->link, &ps->msg_queue); - pthread_mutex_unlock(&ps->mutex); + js_mutex_unlock(&ps->mutex); return JS_UNDEFINED; fail: if (msg) { @@ -3751,8 +4004,8 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValue this_val, } -static JSValue js_worker_set_onmessage(JSContext *ctx, JSValue this_val, - JSValue func) +static JSValue js_worker_set_onmessage(JSContext *ctx, JSValueConst this_val, + JSValueConst func) { JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = js_get_thread_state(rt); @@ -3786,7 +4039,7 @@ static JSValue js_worker_set_onmessage(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_worker_get_onmessage(JSContext *ctx, JSValue this_val) +static JSValue js_worker_get_onmessage(JSContext *ctx, JSValueConst this_val) { JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = js_get_thread_state(rt); @@ -3834,6 +4087,8 @@ void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)) #define OS_PLATFORM "freebsd" #elif defined(__wasi__) #define OS_PLATFORM "wasi" +#elif defined(__GNU__) +#define OS_PLATFORM "hurd" #else #define OS_PLATFORM "unknown" #endif @@ -3887,6 +4142,7 @@ static const JSCFunctionListEntry js_os_funcs[] = { OS_FLAG(SIGTTOU), JS_CFUNC_DEF("cputime", 0, js_os_cputime ), #endif + JS_CFUNC_DEF("exePath", 0, js_os_exepath ), JS_CFUNC_DEF("now", 0, js_os_now ), JS_CFUNC_MAGIC_DEF("setTimeout", 2, js_os_setTimeout, 0 ), JS_CFUNC_MAGIC_DEF("setInterval", 2, js_os_setTimeout, 1 ), @@ -3918,7 +4174,7 @@ static const JSCFunctionListEntry js_os_funcs[] = { #if !defined(__wasi__) JS_CFUNC_DEF("realpath", 1, js_os_realpath ), #endif -#if !defined(_WIN32) && !defined(__wasi__) +#if !defined(_WIN32) && !defined(__wasi__) && !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), JS_CFUNC_DEF("symlink", 2, js_os_symlink ), JS_CFUNC_DEF("readlink", 1, js_os_readlink ), @@ -3984,15 +4240,15 @@ JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name) /**********************************************************/ -static JSValue js_print(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_print(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { #ifdef _WIN32 HANDLE handle; DWORD mode; #endif const char *s; - JSValue v; + JSValueConst v; DynBuf b; int i; @@ -4002,9 +4258,9 @@ static JSValue js_print(JSContext *ctx, JSValue this_val, s = JS_ToCString(ctx, v); if (!s && JS_IsObject(v)) { JS_FreeValue(ctx, JS_GetException(ctx)); - v = JS_ToObjectString(ctx, v); - s = JS_ToCString(ctx, v); - JS_FreeValue(ctx, v); + JSValue t = JS_ToObjectString(ctx, v); + s = JS_ToCString(ctx, t); + JS_FreeValue(ctx, t); } if (s) { dbuf_printf(&b, "%s%s", &" "[!i], s); @@ -4091,6 +4347,7 @@ void js_std_init_handlers(JSRuntime *rt) init_list_head(&ts->os_signal_handlers); init_list_head(&ts->os_timers); init_list_head(&ts->port_list); + init_list_head(&ts->rejected_promise_list); ts->next_timer_id = 1; @@ -4110,6 +4367,14 @@ void js_std_init_handlers(JSRuntime *rt) #endif } +static void free_rp(JSRuntime *rt, JSRejectedPromiseEntry *rp) +{ + list_del(&rp->link); + JS_FreeValueRT(rt, rp->promise); + JS_FreeValueRT(rt, rp->reason); + js_free_rt(rt, rp); +} + void js_std_free_handlers(JSRuntime *rt) { JSThreadState *ts = js_get_thread_state(rt); @@ -4130,6 +4395,11 @@ void js_std_free_handlers(JSRuntime *rt) free_timer(rt, th); } + list_for_each_safe(el, el1, &ts->rejected_promise_list) { + JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); + free_rp(rt, rp); + } + #ifdef USE_WORKER /* XXX: free port_list ? */ js_free_message_pipe(ts->recv_pipe); @@ -4137,7 +4407,7 @@ void js_std_free_handlers(JSRuntime *rt) #endif } -static void js_dump_obj(JSContext *ctx, FILE *f, JSValue val) +static void js_dump_obj(JSContext *ctx, FILE *f, JSValueConst val) { const char *str; @@ -4150,12 +4420,12 @@ static void js_dump_obj(JSContext *ctx, FILE *f, JSValue val) } } -static void js_std_dump_error1(JSContext *ctx, JSValue exception_val) +static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val) { JSValue val; bool is_error; - is_error = JS_IsError(ctx, exception_val); + is_error = JS_IsError(exception_val); js_dump_obj(ctx, stderr, exception_val); if (is_error) { val = JS_GetPropertyStr(ctx, exception_val, "stack"); @@ -4177,14 +4447,62 @@ void js_std_dump_error(JSContext *ctx) JS_FreeValue(ctx, exception_val); } -void js_std_promise_rejection_tracker(JSContext *ctx, JSValue promise, - JSValue reason, +static JSRejectedPromiseEntry *find_rejected_promise(JSContext *ctx, JSThreadState *ts, + JSValueConst promise) +{ + struct list_head *el; + + list_for_each(el, &ts->rejected_promise_list) { + JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); + if (JS_IsSameValue(ctx, rp->promise, promise)) + return rp; + } + return NULL; +} + +void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, + JSValueConst reason, bool is_handled, void *opaque) { - if (!is_handled) { - fprintf(stderr, "Possibly unhandled promise rejection: "); - js_std_dump_error1(ctx, reason); - fflush(stderr); + + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSRejectedPromiseEntry *rp = find_rejected_promise(ctx, ts, promise); + + if (is_handled) { + /* the rejection is handled, so the entry can be removed if present */ + if (rp) + free_rp(rt, rp); + } else { + /* add a new entry if needed */ + if (!rp) { + rp = js_malloc_rt(rt, sizeof(*rp)); + if (rp) { + rp->promise = JS_DupValue(ctx, promise); + rp->reason = JS_DupValue(ctx, reason); + list_add_tail(&rp->link, &ts->rejected_promise_list); + } + } + } +} + +/* check if there are pending promise rejections. It must be done + asynchrously in case a rejected promise is handled later. Currently + we do it once the application is about to sleep. It could be done + more often if needed. */ +static void js_std_promise_rejection_check(JSContext *ctx) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + struct list_head *el; + + if (unlikely(!list_empty(&ts->rejected_promise_list))) { + list_for_each(el, &ts->rejected_promise_list) { + JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); + fprintf(stderr, "Possibly unhandled promise rejection: "); + js_std_dump_error1(ctx, rp->reason); + fflush(stderr); + } exit(1); } } @@ -4208,6 +4526,8 @@ int js_std_loop(JSContext *ctx) } } + js_std_promise_rejection_check(ctx); + if (!ts->can_js_os_poll || js_os_poll(ctx)) break; } @@ -4242,6 +4562,8 @@ JSValue js_std_await(JSContext *ctx, JSValue obj) if (err < 0) { js_std_dump_error(ctx1); } + if (err == 0) + js_std_promise_rejection_check(ctx); if (ts->can_js_os_poll) js_os_poll(ctx); } else { @@ -4265,6 +4587,7 @@ void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, if (js_module_set_import_meta(ctx, obj, false, false) < 0) goto exception; } + JS_FreeValue(ctx, obj); } else { if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { if (JS_ResolveModule(ctx, obj) < 0) { @@ -4287,8 +4610,8 @@ void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, } } -static JSValue js_bjson_read(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_bjson_read(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { uint8_t *buf; uint64_t pos, len; @@ -4311,8 +4634,8 @@ static JSValue js_bjson_read(JSContext *ctx, JSValue this_val, return obj; } -static JSValue js_bjson_write(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_bjson_write(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { size_t len; uint8_t *buf; diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.h old mode 100644 new mode 100755 index 6b49363f..6af5a639 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-libc.h @@ -34,26 +34,40 @@ extern "C" { #endif -JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name); -JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name); -JSModuleDef *js_init_module_bjson(JSContext *ctx, const char *module_name); -void js_std_add_helpers(JSContext *ctx, int argc, char **argv); -int js_std_loop(JSContext *ctx); -JSValue js_std_await(JSContext *ctx, JSValue obj); -void js_std_init_handlers(JSRuntime *rt); -void js_std_free_handlers(JSRuntime *rt); -void js_std_dump_error(JSContext *ctx); -uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename); -int js_module_set_import_meta(JSContext *ctx, JSValue func_val, - bool use_realpath, bool is_main); -JSModuleDef *js_module_loader(JSContext *ctx, - const char *module_name, void *opaque); -void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, - int flags); -void js_std_promise_rejection_tracker(JSContext *ctx, JSValue promise, - JSValue reason, - bool is_handled, void *opaque); -void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); +#if defined(__GNUC__) || defined(__clang__) +#define JS_EXTERN __attribute__((visibility("default"))) +#else +#define JS_EXTERN /* nothing */ +#endif + +JS_EXTERN JSModuleDef *js_init_module_std(JSContext *ctx, + const char *module_name); +JS_EXTERN JSModuleDef *js_init_module_os(JSContext *ctx, + const char *module_name); +JS_EXTERN JSModuleDef *js_init_module_bjson(JSContext *ctx, + const char *module_name); +JS_EXTERN void js_std_add_helpers(JSContext *ctx, int argc, char **argv); +JS_EXTERN int js_std_loop(JSContext *ctx); +JS_EXTERN JSValue js_std_await(JSContext *ctx, JSValue obj); +JS_EXTERN void js_std_init_handlers(JSRuntime *rt); +JS_EXTERN void js_std_free_handlers(JSRuntime *rt); +JS_EXTERN void js_std_dump_error(JSContext *ctx); +JS_EXTERN uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, + const char *filename); +JS_EXTERN int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, + bool use_realpath, bool is_main); +JS_EXTERN JSModuleDef *js_module_loader(JSContext *ctx, + const char *module_name, void *opaque); +JS_EXTERN void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, + size_t buf_len, int flags); +JS_EXTERN void js_std_promise_rejection_tracker(JSContext *ctx, + JSValueConst promise, + JSValueConst reason, + bool is_handled, + void *opaque); +JS_EXTERN void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); + +#undef JS_EXTERN #ifdef __cplusplus } /* extern "C" { */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-opcode.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-opcode.h old mode 100644 new mode 100755 index fb5394c5..bd5be754 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-opcode.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs-opcode.h @@ -264,6 +264,7 @@ DEF( strict_eq, 1, 2, 1, none) DEF( strict_neq, 1, 2, 1, none) DEF(is_undefined_or_null, 1, 1, 1, none) DEF( private_in, 1, 2, 1, none) +DEF(push_bigint_i32, 5, 0, 1, i32) /* must be the last non short and non temporary opcode */ DEF( nop, 1, 0, 0, none) @@ -365,11 +366,6 @@ DEF( is_null, 1, 1, 1, none) DEF(typeof_is_undefined, 1, 1, 1, none) DEF( typeof_is_function, 1, 1, 1, none) -// order matters, see non-IC counterparts -DEF( get_field_ic, 5, 1, 1, none) -DEF( get_field2_ic, 5, 1, 2, none) -DEF( put_field_ic, 5, 2, 0, none) - #undef DEF #undef def #endif /* DEF */ diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs.c old mode 100644 new mode 100755 index fde16ca2..c6815fb3 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs.c @@ -1,8 +1,8 @@ /* * QuickJS Javascript Engine * - * Copyright (c) 2017-2024 Fabrice Bellard - * Copyright (c) 2017-2024 Charlie Gordon + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon * Copyright (c) 2023-2025 Ben Noordhuis * Copyright (c) 2023-2025 Saúl Ibarra Corretgé * @@ -33,10 +33,12 @@ #if !defined(_MSC_VER) #include #if defined(_WIN32) -#include #include #endif #endif +#if defined(_WIN32) +#include +#endif #include #include #include @@ -45,7 +47,7 @@ #include "list.h" #include "quickjs.h" #include "libregexp.h" -#include "libbf.h" +#include "dtoa.h" #if defined(EMSCRIPTEN) || defined(_MSC_VER) #define DIRECT_DISPATCH 0 @@ -63,12 +65,6 @@ #define NO_TM_GMTOFF #endif -#ifdef ANDROID -#include -#define LOG_TAG "QuickJS" -#define printf(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) -#endif - // atomic_store etc. are completely busted in recent versions of tcc; // somehow the compiler forgets to load |ptr| into %rdi when calling // the __atomic_*() helpers in its lib/stdatomic.c and lib/atomic.S @@ -102,6 +98,29 @@ const char* JS_GetVersion(void) { #undef STRINFIGY_ #undef STRINGIFY +static inline JSValueConst *vc(JSValue *vals) +{ + return (JSValueConst *)vals; +} + +static inline JSValue unsafe_unconst(JSValueConst v) +{ +#ifdef JS_CHECK_JSVALUE + return (JSValue)v; +#else + return v; +#endif +} + +static inline JSValueConst safe_const(JSValue v) +{ +#ifdef JS_CHECK_JSVALUE + return (JSValueConst)v; +#else + return v; +#endif +} + enum { /* classid tag */ /* union usage | properties */ JS_CLASS_OBJECT = 1, /* must be first */ @@ -143,6 +162,7 @@ enum { JS_CLASS_WEAKMAP, /* u.map_state */ JS_CLASS_WEAKSET, /* u.map_state */ JS_CLASS_ITERATOR, + JS_CLASS_ITERATOR_CONCAT, /* u.iterator_concat_data */ JS_CLASS_ITERATOR_HELPER, /* u.iterator_helper_data */ JS_CLASS_ITERATOR_WRAP, /* u.iterator_wrap_data */ JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ @@ -163,6 +183,7 @@ enum { JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ JS_CLASS_WEAK_REF, JS_CLASS_FINALIZATION_REGISTRY, + JS_CLASS_DOM_EXCEPTION, JS_CLASS_CALL_SITE, JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ @@ -190,6 +211,9 @@ typedef enum JSErrorEnum { #define JS_MAX_LOCAL_VARS 65535 #define JS_STACK_SIZE_MAX 65534 #define JS_STRING_LEN_MAX ((1 << 30) - 1) +// 1,024 bytes is about the cutoff point where it starts getting +// more profitable to ref slice than to copy +#define JS_STRING_SLICE_LEN_MAX 1024 // in bytes #define __exception __attribute__((warn_unused_result)) @@ -218,6 +242,11 @@ typedef struct JSRuntimeFinalizerState { void *arg; } JSRuntimeFinalizerState; +typedef struct JSValueLink { + struct JSValueLink *next; + JSValueConst value; +} JSValueLink; + struct JSRuntime { JSMallocFunctions mf; JSMallocState malloc_state; @@ -244,8 +273,6 @@ struct JSRuntime { struct list_head tmp_obj_list; /* used during GC */ JSGCPhaseEnum gc_phase : 8; size_t malloc_gc_threshold; - int (*gc_before_callback)(JSRuntime*); /* Callback before the gc event takes place */ - void (*gc_after_callback)(JSRuntime*); /* Callback after the gc event takes place */ #ifdef ENABLE_DUMPS // JS_DUMP_LEAKS struct list_head string_list; /* list of JSString.link */ #endif @@ -257,8 +284,8 @@ struct JSRuntime { JSValue current_exception; /* true if inside an out of memory error, to avoid recursing */ bool in_out_of_memory; - /* and likewise if inside Error.prepareStackTrace() */ - bool in_prepare_stack_trace; + /* true if inside build_backtrace, to avoid recursing */ + bool in_build_stack_trace; /* true if inside JS_FreeRuntime */ bool in_free; @@ -267,6 +294,12 @@ struct JSRuntime { JSInterruptHandler *interrupt_handler; void *interrupt_opaque; + JSPromiseHook *promise_hook; + void *promise_hook_opaque; + // for smuggling the parent promise from js_promise_then + // to js_promise_constructor + JSValueLink *parent_promise; + JSHostPromiseRejectionTracker *host_promise_rejection_tracker; void *host_promise_rejection_tracker_opaque; @@ -289,7 +322,6 @@ struct JSRuntime { int shape_hash_size; int shape_hash_count; /* number of hashed shapes */ JSShape **shape_hash; - bf_context_t bf_ctx; void *user_opaque; void *libc_opaque; JSRuntimeFinalizerState *finalizers; @@ -347,15 +379,7 @@ typedef struct JSVarRef { struct { int __gc_ref_count; /* corresponds to header.ref_count */ uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ - - /* 0 : the JSVarRef is on the stack. header.link is an element - of JSStackFrame.var_ref_list. - 1 : the JSVarRef is detached. header.link has the normal meanning - */ - uint8_t is_detached : 1; - uint8_t is_arg : 1; - uint16_t var_idx; /* index of the corresponding function variable on - the stack */ + uint8_t is_detached; }; }; JSValue *pvalue; /* pointer to the value, either on the stack or @@ -363,23 +387,52 @@ typedef struct JSVarRef { JSValue value; /* used when the variable is no longer on the stack */ } JSVarRef; -// typedef struct JSRefCountHeader { -// int ref_count; -// } JSRefCountHeader; +typedef struct JSRefCountHeader { + int ref_count; +} JSRefCountHeader; + +/* bigint */ +typedef int32_t js_slimb_t; +typedef uint32_t js_limb_t; +typedef int64_t js_sdlimb_t; +typedef uint64_t js_dlimb_t; + +#define JS_LIMB_DIGITS 9 + +/* Must match the size of short_big_int in JSValueUnion */ +#define JS_LIMB_BITS 32 +#define JS_SHORT_BIG_INT_BITS JS_LIMB_BITS +#define JS_BIGINT_MAX_SIZE ((1024 * 1024) / JS_LIMB_BITS) /* in limbs */ +#define JS_SHORT_BIG_INT_MIN INT32_MIN +#define JS_SHORT_BIG_INT_MAX INT32_MAX + -/* the same structure is used for big integers. - Big integers are never infinite or NaNs */ typedef struct JSBigInt { JSRefCountHeader header; /* must come first, 32-bit */ - bf_t num; + uint32_t len; /* number of limbs, >= 1 */ + js_limb_t tab[]; /* two's complement representation, always + normalized so that 'len' is the minimum + possible length >= 1 */ } JSBigInt; +/* this bigint structure can hold a 64 bit integer */ +typedef struct { + js_limb_t big_int_buf[sizeof(JSBigInt) / sizeof(js_limb_t)]; /* for JSBigInt */ + /* must come just after */ + js_limb_t tab[(64 + JS_LIMB_BITS - 1) / JS_LIMB_BITS]; +} JSBigIntBuf; + typedef enum { JS_AUTOINIT_ID_PROTOTYPE, JS_AUTOINIT_ID_MODULE_NS, JS_AUTOINIT_ID_PROP, + JS_AUTOINIT_ID_BYTECODE, } JSAutoInitIDEnum; +enum { + JS_BUILTIN_ARRAY_FROMASYNC = 1, +}; + /* must be large enough to have a negligible runtime cost and small enough to call the interrupt callback often. */ #define JS_INTERRUPT_COUNTER_INIT 10000 @@ -404,8 +457,9 @@ struct JSContext { JSValue error_ctor; JSValue error_back_trace; JSValue error_prepare_stack; - int error_stack_trace_limit; + JSValue error_stack_trace_limit; JSValue iterator_ctor; + JSValue iterator_ctor_getset; JSValue iterator_proto; JSValue async_iterator_proto; JSValue array_proto_values; @@ -418,26 +472,19 @@ struct JSContext { double time_origin; uint64_t random_state; - bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ + /* when the counter reaches zero, JSRutime.interrupt_handler is called */ int interrupt_counter; struct list_head loaded_modules; /* list of JSModuleDef.link */ /* if NULL, RegExp compilation is not supported */ - JSValue (*compile_regexp)(JSContext *ctx, JSValue pattern, - JSValue flags); + JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern, + JSValueConst flags); /* if NULL, eval is not supported */ - JSValue (*eval_internal)(JSContext *ctx, JSValue this_obj, + JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj, const char *input, size_t input_len, - const char *filename, int flags, int scope_idx); - - JSValue regexp_left_ctx; // RegExp.leftContext - JSValue regexp_right_ctx; // RegExp.rightContext - JSValue regexp_last_match_str; - int64_t regexp_last_match_start; - int64_t regexp_last_match_end; - + const char *filename, int line, int flags, int scope_idx); void *user_opaque; }; @@ -463,6 +510,32 @@ typedef struct JSWeakRefRecord { } u; } JSWeakRefRecord; +typedef struct JSMapRecord { + int ref_count; /* used during enumeration to avoid freeing the record */ + bool empty; /* true if the record is deleted */ + struct JSMapState *map; + struct list_head link; + struct list_head hash_link; + JSValue key; + JSValue value; +} JSMapRecord; + +typedef struct JSMapState { + bool is_weak; /* true if WeakSet/WeakMap */ + struct list_head records; /* list of JSMapRecord.link */ + uint32_t record_count; + struct list_head *hash_table; + uint32_t hash_size; /* must be a power of two */ + uint32_t record_count_threshold; /* count at which a hash table + resize is needed */ +} JSMapState; + +enum +{ + JS_TO_STRING_IS_PROPERTY_KEY = 1 << 0, + JS_TO_STRING_NO_SIDE_EFFECTS = 1 << 1, +}; + enum { JS_ATOM_TYPE_STRING = 1, JS_ATOM_TYPE_GLOBAL_SYMBOL, @@ -481,7 +554,12 @@ typedef enum { JS_ATOM_KIND_PRIVATE, } JSAtomKindEnum; -#define JS_ATOM_HASH_MASK ((1 << 30) - 1) +typedef enum { + JS_STRING_KIND_NORMAL, + JS_STRING_KIND_SLICE, +} JSStringKind; + +#define JS_ATOM_HASH_MASK ((1 << 29) - 1) struct JSString { JSRefCountHeader header; /* must come first, 32-bit */ @@ -490,19 +568,46 @@ struct JSString { /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3 XXX: could change encoding to have one more bit in hash */ - uint32_t hash : 30; + uint32_t hash : 29; + uint8_t kind : 1; uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ JSWeakRefRecord *first_weak_ref; #ifdef ENABLE_DUMPS // JS_DUMP_LEAKS struct list_head link; /* string list */ #endif - union { - __extension__ uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */ - __extension__ uint16_t str16[0]; - } u; }; +typedef struct JSStringSlice { + JSString *parent; + uint32_t start; // in bytes, not characters +} JSStringSlice; + +static inline void *strv(JSString *p) +{ + JSStringSlice *slice; + + switch (p->kind) { + case JS_STRING_KIND_NORMAL: + return (void *)&p[1]; + case JS_STRING_KIND_SLICE: + slice = (void *)&p[1]; + return (char *)&slice->parent[1] + slice->start; + } + abort(); + return NULL; +} + +static inline uint8_t *str8(JSString *p) +{ + return strv(p); +} + +static inline uint16_t *str16(JSString *p) +{ + return strv(p); +} + typedef struct JSClosureVar { uint8_t is_local : 1; uint8_t is_arg : 1; @@ -582,94 +687,6 @@ typedef enum JSFunctionKindEnum { JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), } JSFunctionKindEnum; -#define IC_CACHE_ITEM_CAPACITY 4 - -typedef struct JSInlineCacheWatchpoint { - struct list_head link; - struct JSInlineCacheRingSlot *ref; - uint32_t offset; - JSAtom atom; -} JSInlineCacheWatchpoint; - -typedef struct JSInlineCacheRingSlot { - /* SoA for space optimization: 120 bytes */ - JSShape* shape[IC_CACHE_ITEM_CAPACITY]; - uint32_t prop_offset[IC_CACHE_ITEM_CAPACITY]; - JSObject* proto[IC_CACHE_ITEM_CAPACITY]; - JSInlineCacheWatchpoint* watchpoint_ref[IC_CACHE_ITEM_CAPACITY]; - JSAtom atom; - uint8_t index; -} JSInlineCacheRingSlot; - -typedef struct JSInlineCacheHashSlot { - JSAtom atom; - uint32_t index; - struct JSInlineCacheHashSlot *next; -} JSInlineCacheHashSlot; - -typedef struct JSInlineCache { - uint32_t count; - uint32_t capacity; - uint32_t hash_bits; - JSInlineCacheHashSlot **hash; - JSInlineCacheRingSlot *cache; -} JSInlineCache; - -#define INLINE_CACHE_MISS ((uint32_t)-1) // sentinel - -// This is a struct so we don't tie up two argument registers in calls to -// JS_GetPropertyInternal2 and JS_SetPropertyInternal2 in the common case -// where there is no IC and therefore no offset to update. -typedef struct JSInlineCacheUpdate { - JSInlineCache *ic; - uint32_t offset; -} JSInlineCacheUpdate; - -static JSInlineCache *init_ic(JSContext *ctx); -static int rebuild_ic(JSContext *ctx, JSInlineCache *ic); -static int resize_ic_hash(JSContext *ctx, JSInlineCache *ic); -static int free_ic(JSRuntime *rt, JSInlineCache *ic); -static int ic_delete_shape_proto_watchpoints(JSRuntime *rt, JSShape *shape, JSAtom atom, bool force); -static int ic_watchpoint_delete_handler(JSRuntime* rt, JSInlineCacheRingSlot *ref, uint8_t offset, JSAtom atom, JSInlineCacheRingSlot *target); -static int js_shape_delete_watchpoints(JSRuntime *rt, JSShape *shape, JSInlineCacheRingSlot *target); -static JSInlineCacheWatchpoint *js_shape_create_watchpoint(JSRuntime *rt, JSShape *shape, JSInlineCacheRingSlot *ptr, uint8_t offset, JSAtom atom); -static void add_ic_slot(JSContext *ctx, JSInlineCacheUpdate *icu, - JSAtom atom, JSObject *object, uint32_t prop_offset, JSObject* prototype); - -static uint32_t get_ic_prop_offset(const JSInlineCacheUpdate *icu, - JSShape *shape, JSObject **prototype) -{ - uint32_t i, cache_offset = icu->offset; - JSInlineCache *ic = icu->ic; - JSInlineCacheRingSlot *cr; - JSShape *shape_slot; - assert(cache_offset < ic->capacity); - cr = ic->cache + cache_offset; - i = cr->index; - for (;;) { - shape_slot = *(cr->shape + i); - if (likely(shape_slot == shape)) { - cr->index = i; - *prototype = cr->proto[i]; - return cr->prop_offset[i]; - } - - i = (i + 1) % countof(cr->shape); - if (unlikely(i == cr->index)) { - break; - } - } - - *prototype = NULL; - return INLINE_CACHE_MISS; -} - -static force_inline JSAtom get_ic_atom(JSInlineCache *ic, uint32_t cache_offset) -{ - assert(cache_offset < ic->capacity); - return ic->cache[cache_offset].atom; -} - typedef struct JSFunctionBytecode { JSGCObjectHeader header; /* must come first */ uint8_t is_strict_mode : 1; @@ -698,7 +715,6 @@ typedef struct JSFunctionBytecode { JSValue *cpool; /* constant pool (self pointer) */ int cpool_count; int closure_var_count; - JSInlineCache *ic; JSAtom filename; int line_num; int col_num; @@ -933,7 +949,6 @@ struct JSShape { int deleted_prop_count; JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ JSObject *proto; - struct list_head *watchpoint; /* watchpoint for prototype inline cache */ JSShapeProperty prop[]; /* prop_size elements */ }; @@ -973,6 +988,7 @@ struct JSObject { struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */ + struct JSIteratorConcatData *iterator_concat_data; /* JS_CLASS_ITERATOR_CONCAT */ struct JSIteratorHelperData *iterator_helper_data; /* JS_CLASS_ITERATOR_HELPER */ struct JSIteratorWrapData *iterator_wrap_data; /* JS_CLASS_ITERATOR_WRAP */ struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ @@ -1083,91 +1099,93 @@ static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, int atom_type); static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p); static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); -static JSValue js_call_c_function(JSContext *ctx, JSValue func_obj, - JSValue this_obj, - int argc, JSValue *argv, int flags); -static JSValue js_call_bound_function(JSContext *ctx, JSValue func_obj, - JSValue this_obj, - int argc, JSValue *argv, int flags); -static JSValue JS_CallInternal(JSContext *ctx, JSValue func_obj, - JSValue this_obj, JSValue new_target, - int argc, JSValue *argv, int flags); +static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags); +static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags); +static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, JSValueConst new_target, + int argc, JSValueConst *argv, int flags); static JSValue JS_CallConstructorInternal(JSContext *ctx, - JSValue func_obj, - JSValue new_target, - int argc, JSValue *argv, int flags); -static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValue this_obj, - int argc, JSValue *argv); + JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv, int flags); +static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv); static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, - int argc, JSValue *argv); + int argc, JSValueConst *argv); static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, JSValue val, bool is_array_ctor); -static JSValue JS_EvalObject(JSContext *ctx, JSValue this_obj, - JSValue val, int flags, int scope_idx); -JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowInternalError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); - -static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p); +static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, + JSValueConst val, int flags, int scope_idx); +static __maybe_unused void JS_DumpString(JSRuntime *rt, JSString *p); static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); -static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValue val); +static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValueConst val); static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); static __maybe_unused void JS_DumpShapes(JSRuntime *rt); -static JSValue js_function_apply(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic); -static void js_array_finalizer(JSRuntime *rt, JSValue val); -static void js_array_mark(JSRuntime *rt, JSValue val, +static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); +static void js_array_finalizer(JSRuntime *rt, JSValueConst val); +static void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_object_data_finalizer(JSRuntime *rt, JSValue val); -static void js_object_data_mark(JSRuntime *rt, JSValue val, +static void js_object_data_finalizer(JSRuntime *rt, JSValueConst val); +static void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_c_function_finalizer(JSRuntime *rt, JSValue val); -static void js_c_function_mark(JSRuntime *rt, JSValue val, +static void js_c_function_finalizer(JSRuntime *rt, JSValueConst val); +static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val); -static void js_bytecode_function_mark(JSRuntime *rt, JSValue val, +static void js_bytecode_function_finalizer(JSRuntime *rt, JSValueConst val); +static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_bound_function_finalizer(JSRuntime *rt, JSValue val); -static void js_bound_function_mark(JSRuntime *rt, JSValue val, +static void js_bound_function_finalizer(JSRuntime *rt, JSValueConst val); +static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_for_in_iterator_mark(JSRuntime *rt, JSValue val, +static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValueConst val); +static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_regexp_finalizer(JSRuntime *rt, JSValue val); -static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val); -static void js_typed_array_finalizer(JSRuntime *rt, JSValue val); -static void js_typed_array_mark(JSRuntime *rt, JSValue val, +static void js_regexp_finalizer(JSRuntime *rt, JSValueConst val); +static void js_array_buffer_finalizer(JSRuntime *rt, JSValueConst val); +static void js_typed_array_finalizer(JSRuntime *rt, JSValueConst val); +static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_proxy_finalizer(JSRuntime *rt, JSValue val); -static void js_proxy_mark(JSRuntime *rt, JSValue val, +static void js_proxy_finalizer(JSRuntime *rt, JSValueConst val); +static void js_proxy_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_map_finalizer(JSRuntime *rt, JSValue val); -static void js_map_mark(JSRuntime *rt, JSValue val, +static void js_map_finalizer(JSRuntime *rt, JSValueConst val); +static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_map_iterator_mark(JSRuntime *rt, JSValue val, +static void js_map_iterator_finalizer(JSRuntime *rt, JSValueConst val); +static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_array_iterator_mark(JSRuntime *rt, JSValue val, +static void js_array_iterator_finalizer(JSRuntime *rt, JSValueConst val); +static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_iterator_helper_finalizer(JSRuntime *rt, JSValue val); -static void js_iterator_helper_mark(JSRuntime *rt, JSValue val, +static void js_iterator_concat_finalizer(JSRuntime *rt, JSValueConst val); +static void js_iterator_concat_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValue val); -static void js_iterator_wrap_mark(JSRuntime *rt, JSValue val, +static void js_iterator_helper_finalizer(JSRuntime *rt, JSValueConst val); +static void js_iterator_helper_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValueConst val); +static void js_iterator_wrap_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValue val, +static void js_regexp_string_iterator_finalizer(JSRuntime *rt, + JSValueConst val); +static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_generator_finalizer(JSRuntime *rt, JSValue obj); -static void js_generator_mark(JSRuntime *rt, JSValue val, +static void js_generator_finalizer(JSRuntime *rt, JSValueConst val); +static void js_generator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_promise_finalizer(JSRuntime *rt, JSValue val); -static void js_promise_mark(JSRuntime *rt, JSValue val, +static void js_promise_finalizer(JSRuntime *rt, JSValueConst val); +static void js_promise_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val); -static void js_promise_resolve_function_mark(JSRuntime *rt, JSValue val, +static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValueConst val); +static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); #define HINT_STRING 0 @@ -1180,16 +1198,24 @@ static int JS_ToBoolFree(JSContext *ctx, JSValue val); static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val); static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val); static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val); +static JSValue JS_ToPropertyKeyInternal(JSContext *ctx, JSValueConst val, + int flags); static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len); -static JSValue js_compile_regexp(JSContext *ctx, JSValue pattern, - JSValue flags); -static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValue ctor, +static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, + JSValueConst flags); +static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, JSValue pattern, JSValue bc); static void gc_decref(JSRuntime *rt); static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def, JSAtom name); static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int unshift); +static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv); +static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv, int magic); +static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); typedef enum JSStrictEqModeEnum { JS_EQ_STRICT, @@ -1200,97 +1226,90 @@ typedef enum JSStrictEqModeEnum { static bool js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, JSStrictEqModeEnum eq_mode); static bool js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2); -static bool js_same_value(JSContext *ctx, JSValue op1, JSValue op2); -static bool js_same_value_zero(JSContext *ctx, JSValue op1, JSValue op2); +static bool js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2); +static bool js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2); static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val); static JSProperty *add_property(JSContext *ctx, JSObject *p, JSAtom prop, int prop_flags); -static JSValue JS_NewBigInt(JSContext *ctx); -static inline bf_t *JS_GetBigInt(JSValue val) -{ - JSBigInt *p = JS_VALUE_GET_PTR(val); - return &p->num; -} -static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val); -static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val); static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); -static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValue val); -static bf_t *JS_ToBigInt1(JSContext *ctx, bf_t *buf, JSValue val); -static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf); -JSValue JS_ThrowOutOfMemory(JSContext *ctx); +static JSValue JS_ThrowStackOverflow(JSContext *ctx); static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); -static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValue obj); -static int js_proxy_setPrototypeOf(JSContext *ctx, JSValue obj, - JSValue proto_val, bool throw_flag); -static int js_proxy_isExtensible(JSContext *ctx, JSValue obj); -static int js_proxy_preventExtensions(JSContext *ctx, JSValue obj); -static int js_proxy_isArray(JSContext *ctx, JSValue obj); +static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); +static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, + JSValueConst proto_val, bool throw_flag); +static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); +static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj); +static int js_proxy_isArray(JSContext *ctx, JSValueConst obj); static int JS_CreateProperty(JSContext *ctx, JSObject *p, - JSAtom prop, JSValue val, - JSValue getter, JSValue setter, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, int flags); -static int js_string_memcmp(const JSString *p1, const JSString *p2, int len); +static int js_string_memcmp(JSString *p1, JSString *p2, int len); static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref); -static bool is_valid_weakref_target(JSValue val); -static void insert_weakref_record(JSValue target, struct JSWeakRefRecord *wr); +static bool is_valid_weakref_target(JSValueConst val); +static void insert_weakref_record(JSValueConst target, + struct JSWeakRefRecord *wr); static JSValue js_array_buffer_constructor3(JSContext *ctx, - JSValue new_target, + JSValueConst new_target, uint64_t len, uint64_t *max_len, JSClassID class_id, uint8_t *buf, JSFreeArrayBufferDataFunc *free_func, void *opaque, bool alloc_flag); static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr); -static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValue obj); +static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj); static bool array_buffer_is_resizable(const JSArrayBuffer *abuf); static JSValue js_typed_array_constructor(JSContext *ctx, - JSValue this_val, - int argc, JSValue *argv, + JSValueConst this_val, + int argc, JSValueConst *argv, int classid); static JSValue js_typed_array_constructor_ta(JSContext *ctx, - JSValue new_target, - JSValue src_obj, + JSValueConst new_target, + JSValueConst src_obj, int classid, uint32_t len); static bool is_typed_array(JSClassID class_id); static bool typed_array_is_oob(JSObject *p); -static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p); +static uint32_t typed_array_length(JSObject *p); static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx); static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, bool is_arg); -static JSValue js_generator_function_call(JSContext *ctx, JSValue func_obj, - JSValue this_obj, - int argc, JSValue *argv, +static JSValue js_call_generator_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags); -static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val); -static void js_async_function_resolve_mark(JSRuntime *rt, JSValue val, +static void js_async_function_resolve_finalizer(JSRuntime *rt, + JSValueConst val); +static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static JSValue JS_EvalInternal(JSContext *ctx, JSValue this_obj, +static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, const char *input, size_t input_len, - const char *filename, int flags, int scope_idx); + const char *filename, int line, int flags, int scope_idx); static void js_free_module_def(JSContext *ctx, JSModuleDef *m); static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, JS_MarkFunc *mark_func); static JSValue js_import_meta(JSContext *ctx); -static JSValue js_dynamic_import(JSContext *ctx, JSValue specifier); +static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier); static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); static JSValue js_new_promise_capability(JSContext *ctx, JSValue *resolving_funcs, - JSValue ctor); + JSValueConst ctor); static __exception int perform_promise_then(JSContext *ctx, - JSValue promise, - JSValue *resolve_reject, - JSValue *cap_resolving_funcs); -static JSValue js_promise_resolve(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic); + JSValueConst promise, + JSValueConst *resolve_reject, + JSValueConst *cap_resolving_funcs); +static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); -static bool js_string_eq(const JSString *p1, const JSString *p2); -static int js_string_compare(const JSString *p1, const JSString *p2); -static int JS_SetPropertyValue(JSContext *ctx, JSValue this_obj, +static JSValue js_promise_resolve_thenable_job(JSContext *ctx, + int argc, JSValueConst *argv); +static bool js_string_eq(JSString *p1, JSString *p2); +static int js_string_compare(JSString *p1, JSString *p2); +static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags); -static int JS_NumberIsInteger(JSContext *ctx, JSValue val); -static bool JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValue val); +static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val); +static bool JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val); static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val); static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, JSObject *p, JSAtom prop); @@ -1304,25 +1323,27 @@ static int js_shape_prepare_update(JSContext *ctx, JSObject *p, JSShapeProperty **pprs); static int init_shape_hash(JSRuntime *rt); static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, - JSValue obj); + JSValueConst obj); static __exception int js_get_length64(JSContext *ctx, int64_t *pres, - JSValue obj); -static __exception int js_set_length64(JSContext *ctx, JSValue obj, + JSValueConst obj); +static __exception int js_set_length64(JSContext *ctx, JSValueConst obj, int64_t len); static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len); static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, - JSValue array_arg); + JSValueConst array_arg); +static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab); static bool js_get_fast_array(JSContext *ctx, JSValue obj, JSValue **arrpp, uint32_t *countp); +static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len); static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, JSValue sync_iter); -static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val); -static void js_c_function_data_mark(JSRuntime *rt, JSValue val, +static void js_c_function_data_finalizer(JSRuntime *rt, JSValueConst val); +static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static JSValue js_c_function_data_call(JSContext *ctx, JSValue func_obj, - JSValue this_val, - int argc, JSValue *argv, int flags); -static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val); +static JSValue js_call_c_function_data(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_val, + int argc, JSValueConst *argv, int flags); +static JSAtom js_symbol_to_atom(JSContext *ctx, JSValueConst val); static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, JSGCObjectTypeEnum type); static void remove_gc_object(JSGCObjectHeader *h); @@ -1333,14 +1354,15 @@ static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); -static void js_set_uncatchable_error(JSContext *ctx, JSValue val, bool flag); +static void js_set_uncatchable_error(JSContext *ctx, JSValueConst val, + bool flag); static JSValue js_new_callsite(JSContext *ctx, JSCallSiteData *csd); static void js_new_callsite_data(JSContext *ctx, JSCallSiteData *csd, JSStackFrame *sf); static void js_new_callsite_data2(JSContext *ctx, JSCallSiteData *csd, const char *filename, int line_num, int col_num); static void _JS_AddIntrinsicCallSite(JSContext *ctx); -static void JS_SetOpaqueInternal(JSValue obj, void *opaque); +static void JS_SetOpaqueInternal(JSValueConst obj, void *opaque); static const JSClassExoticMethods js_arguments_exotic_methods; static const JSClassExoticMethods js_string_exotic_methods; @@ -1397,8 +1419,6 @@ static JSValue js_int64(int64_t v) return js_float64(v); } -#define JS_NewInt64(ctx, val) js_int64(val) - static JSValue js_number(double d) { if (double_is_int32(d)) @@ -1417,21 +1437,21 @@ static JSValue js_bool(bool v) return JS_MKVAL(JS_TAG_BOOL, (v != 0)); } -static JSValue js_dup(JSValue v) +static JSValue js_dup(JSValueConst v) { if (JS_VALUE_HAS_REF_COUNT(v)) { JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); p->ref_count++; } - return v; + return unsafe_unconst(v); } -JSValue JS_DupValue(JSContext *ctx, JSValue v) +JSValue JS_DupValue(JSContext *ctx, JSValueConst v) { return js_dup(v); } -JSValue JS_DupValueRT(JSRuntime *rt, JSValue v) +JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) { return js_dup(v); } @@ -1451,10 +1471,7 @@ static void js_trigger_gc(JSRuntime *rt, size_t size) printf("GC: size=%zd\n", rt->malloc_state.malloc_size); } #endif - if((rt->gc_before_callback == NULL) || rt->gc_before_callback(rt)){ - JS_RunGC(rt); - if(rt->gc_after_callback != NULL)rt->gc_after_callback(rt); - } + JS_RunGC(rt); rt->malloc_gc_threshold = rt->malloc_state.malloc_size + (rt->malloc_state.malloc_size >> 1); } @@ -1571,13 +1588,6 @@ void *js_mallocz_rt(JSRuntime *rt, size_t size) return js_calloc_rt(rt, 1, size); } -/* called by libbf */ -static void *js_bf_realloc(void *opaque, void *ptr, size_t size) -{ - JSRuntime *rt = opaque; - return js_realloc_rt(rt, ptr, size); -} - /* Throw out of memory in case of error */ void *js_calloc(JSContext *ctx, size_t count, size_t size) { @@ -1710,8 +1720,8 @@ static inline int is_digit(int c) { return c >= '0' && c <= '9'; } -static inline int string_get(const JSString *p, int idx) { - return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; +static inline int string_get(JSString *p, int idx) { + return p->is_wide_char ? str16(p)[idx] : str8(p)[idx]; } typedef struct JSClassShortDef { @@ -1757,9 +1767,10 @@ static JSClassShortDef const js_std_class_def[] = { { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */ { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ - { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */ - { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */ + { JS_ATOM_WeakMap, js_map_finalizer, NULL }, /* JS_CLASS_WEAKMAP */ + { JS_ATOM_WeakSet, js_map_finalizer, NULL }, /* JS_CLASS_WEAKSET */ { JS_ATOM_Iterator, NULL, NULL }, /* JS_CLASS_ITERATOR */ + { JS_ATOM_IteratorConcat, js_iterator_concat_finalizer, js_iterator_concat_mark }, /* JS_CLASS_ITERATOR_CONCAT */ { JS_ATOM_IteratorHelper, js_iterator_helper_finalizer, js_iterator_helper_mark }, /* JS_CLASS_ITERATOR_HELPER */ { JS_ATOM_IteratorWrap, js_iterator_wrap_finalizer, js_iterator_wrap_mark }, /* JS_CLASS_ITERATOR_WRAP */ { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */ @@ -1836,8 +1847,6 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) rt->malloc_state = ms; rt->malloc_gc_threshold = 256 * 1024; - bf_context_init(&rt->bf_ctx, js_bf_realloc, rt); - init_list_head(&rt->context_list); init_list_head(&rt->gc_obj_list); init_list_head(&rt->gc_zero_ref_count_list); @@ -1860,9 +1869,9 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods; rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function; - rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call; + rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_call_c_function_data; rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function; - rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call; + rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_call_generator_function; if (init_shape_hash(rt)) goto fail; @@ -1993,7 +2002,7 @@ void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, /* return 0 if OK, < 0 if exception */ int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, - int argc, JSValue *argv) + int argc, JSValueConst *argv) { JSRuntime *rt = ctx->rt; JSJobEntry *e; @@ -2037,7 +2046,7 @@ int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx) e = list_entry(rt->job_list.next, JSJobEntry, link); list_del(&e->link); ctx = e->ctx; - res = e->job_func(e->ctx, e->argc, e->argv); + res = e->job_func(e->ctx, e->argc, vc(e->argv)); for(i = 0; i < e->argc; i++) JS_FreeValue(ctx, e->argv[i]); if (JS_IsException(res)) @@ -2075,6 +2084,7 @@ static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char str->header.ref_count = 1; str->is_wide_char = is_wide_char; str->len = max_len; + str->kind = JS_STRING_KIND_NORMAL; str->atom_type = 0; str->hash = 0; /* optional but costless */ str->hash_next = 0; /* optional */ @@ -2095,18 +2105,28 @@ static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char) return p; } +static inline void js_free_string0(JSRuntime *rt, JSString *str); + /* same as JS_FreeValueRT() but faster */ static inline void js_free_string(JSRuntime *rt, JSString *str) { - if (--str->header.ref_count <= 0) { - if (str->atom_type) { - JS_FreeAtomStruct(rt, str); - } else { + if (--str->header.ref_count <= 0) + js_free_string0(rt, str); +} + +static inline void js_free_string0(JSRuntime *rt, JSString *str) +{ + if (str->atom_type) { + JS_FreeAtomStruct(rt, str); + } else { #ifdef ENABLE_DUMPS // JS_DUMP_LEAKS - list_del(&str->link); + list_del(&str->link); #endif - js_free_rt(rt, str); + if (str->kind == JS_STRING_KIND_SLICE) { + JSStringSlice *slice = (void *)&str[1]; + js_free_string(rt, slice->parent); // safe, recurses only 1 level } + js_free_rt(rt, str); } } @@ -2174,7 +2194,7 @@ void JS_FreeRuntime(JSRuntime *rt) } #endif -// assert(list_empty(&rt->gc_obj_list)); + assert(list_empty(&rt->gc_obj_list)); /* free the classes */ for(i = 0; i < rt->class_count; i++) { @@ -2185,8 +2205,6 @@ void JS_FreeRuntime(JSRuntime *rt) } js_free_rt(rt, rt->class_array); - bf_context_end(&rt->bf_ctx); - #ifdef ENABLE_DUMPS // JS_DUMP_ATOM_LEAKS /* only the atoms defined in JS_InitAtoms() should be left */ if (check_dump_flag(rt, JS_DUMP_ATOM_LEAKS)) { @@ -2333,23 +2351,17 @@ JSContext *JS_NewContextRaw(JSRuntime *rt) } ctx->rt = rt; list_add_tail(&ctx->link, &rt->context_list); - ctx->bf_ctx = &rt->bf_ctx; for(i = 0; i < rt->class_count; i++) ctx->class_proto[i] = JS_NULL; ctx->array_ctor = JS_NULL; ctx->iterator_ctor = JS_NULL; + ctx->iterator_ctor_getset = JS_NULL; ctx->regexp_ctor = JS_NULL; ctx->promise_ctor = JS_NULL; ctx->error_ctor = JS_NULL; ctx->error_back_trace = JS_UNDEFINED; ctx->error_prepare_stack = JS_UNDEFINED; - ctx->regexp_left_ctx = JS_UNDEFINED; - ctx->regexp_right_ctx = JS_UNDEFINED; - ctx->regexp_last_match_str = JS_UNDEFINED; - ctx->regexp_last_match_start = -1; - ctx->regexp_last_match_end = -1; - - ctx->error_stack_trace_limit = 10; + ctx->error_stack_trace_limit = js_int32(10); init_list_head(&ctx->loaded_modules); JS_AddIntrinsicBasicObjects(ctx); @@ -2375,6 +2387,7 @@ JSContext *JS_NewContext(JSRuntime *rt) JS_AddIntrinsicPromise(ctx); JS_AddIntrinsicBigInt(ctx); JS_AddIntrinsicWeakRef(ctx); + JS_AddIntrinsicDOMException(ctx); JS_AddPerformance(ctx); @@ -2469,19 +2482,18 @@ static void JS_MarkContext(JSRuntime *rt, JSContext *ctx, JS_MarkValue(rt, ctx->error_ctor, mark_func); JS_MarkValue(rt, ctx->error_back_trace, mark_func); JS_MarkValue(rt, ctx->error_prepare_stack, mark_func); + JS_MarkValue(rt, ctx->error_stack_trace_limit, mark_func); for(i = 0; i < rt->class_count; i++) { JS_MarkValue(rt, ctx->class_proto[i], mark_func); } JS_MarkValue(rt, ctx->iterator_ctor, mark_func); + JS_MarkValue(rt, ctx->iterator_ctor_getset, mark_func); JS_MarkValue(rt, ctx->async_iterator_proto, mark_func); JS_MarkValue(rt, ctx->promise_ctor, mark_func); JS_MarkValue(rt, ctx->array_ctor, mark_func); JS_MarkValue(rt, ctx->regexp_ctor, mark_func); JS_MarkValue(rt, ctx->function_ctor, mark_func); JS_MarkValue(rt, ctx->function_proto, mark_func); - JS_MarkValue(rt, ctx->regexp_last_match_str, mark_func); - JS_MarkValue(rt, ctx->regexp_right_ctx, mark_func); - JS_MarkValue(rt, ctx->regexp_left_ctx, mark_func); if (ctx->array_shape) mark_func(rt, &ctx->array_shape->header); @@ -2540,21 +2552,19 @@ void JS_FreeContext(JSContext *ctx) JS_FreeValue(ctx, ctx->error_ctor); JS_FreeValue(ctx, ctx->error_back_trace); JS_FreeValue(ctx, ctx->error_prepare_stack); + JS_FreeValue(ctx, ctx->error_stack_trace_limit); for(i = 0; i < rt->class_count; i++) { JS_FreeValue(ctx, ctx->class_proto[i]); } js_free_rt(rt, ctx->class_proto); JS_FreeValue(ctx, ctx->iterator_ctor); + JS_FreeValue(ctx, ctx->iterator_ctor_getset); JS_FreeValue(ctx, ctx->async_iterator_proto); JS_FreeValue(ctx, ctx->promise_ctor); JS_FreeValue(ctx, ctx->array_ctor); JS_FreeValue(ctx, ctx->regexp_ctor); JS_FreeValue(ctx, ctx->function_ctor); JS_FreeValue(ctx, ctx->function_proto); - - JS_FreeValue(ctx, ctx->regexp_left_ctx); - JS_FreeValue(ctx, ctx->regexp_right_ctx); - JS_FreeValue(ctx, ctx->regexp_last_match_str); js_free_shape_null(ctx->rt, ctx->array_shape); @@ -2634,7 +2644,7 @@ static inline int is_num(int c) } /* return true if the string is a number n with 0 <= n <= 2^32-1 */ -static inline bool is_num_string(uint32_t *pval, const JSString *p) +static inline bool is_num_string(uint32_t *pval, JSString *p) { uint32_t n; uint64_t n64; @@ -2688,17 +2698,16 @@ static inline uint32_t hash_string16(const uint16_t *str, return h; } -static uint32_t hash_string(const JSString *str, uint32_t h) +static uint32_t hash_string(JSString *str, uint32_t h) { if (str->is_wide_char) - h = hash_string16(str->u.str16, str->len, h); + h = hash_string16(str16(str), str->len, h); else - h = hash_string8(str->u.str8, str->len, h); + h = hash_string8(str8(str), str->len, h); return h; } -static __maybe_unused void JS_DumpString(JSRuntime *rt, - const JSString *p) +static __maybe_unused void JS_DumpString(JSRuntime *rt, JSString *p) { int i, c, sep; @@ -2999,10 +3008,11 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) p->header.ref_count = 1; p->is_wide_char = str->is_wide_char; p->len = str->len; + p->kind = JS_STRING_KIND_NORMAL; #ifdef ENABLE_DUMPS // JS_DUMP_LEAKS list_add_tail(&p->link, &rt->string_list); #endif - memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) + + memcpy(str8(p), str8(str), (str->len << str->is_wide_char) + 1 - str->is_wide_char); js_free_string(rt, str); } @@ -3013,6 +3023,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) p->header.ref_count = 1; p->is_wide_char = 1; /* Hack to represent NULL as a JSString */ p->len = 0; + p->kind = JS_STRING_KIND_NORMAL; #ifdef ENABLE_DUMPS // JS_DUMP_LEAKS list_add_tail(&p->link, &rt->string_list); #endif @@ -3057,8 +3068,8 @@ static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, p = js_alloc_string_rt(rt, len, 0); if (!p) return JS_ATOM_NULL; - memcpy(p->u.str8, str, len); - p->u.str8[len] = '\0'; + memcpy(str8(p), str, len); + str8(p)[len] = '\0'; return __JS_NewAtom(rt, p, atom_type); } @@ -3079,7 +3090,7 @@ static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, p->atom_type == JS_ATOM_TYPE_STRING && p->len == len && p->is_wide_char == 0 && - memcmp(p->u.str8, str, len) == 0) { + memcmp(str8(p), str, len) == 0) { if (!__JS_AtomIsConst(i)) p->header.ref_count++; return i; @@ -3244,7 +3255,6 @@ JSValue JS_NewSymbol(JSContext *ctx, const char *description, bool is_global) #define ATOM_GET_STR_BUF_SIZE 64 -/* Should only be used for debug. */ static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, JSAtom atom) { @@ -3259,22 +3269,14 @@ static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, JSAtomStruct *p = rt->atom_array[atom]; *buf = '\0'; if (atom_is_free(p)) { - assert(!atom_is_free(p)); snprintf(buf, buf_size, "", atom); } else if (p != NULL) { JSString *str = p; if (str->is_wide_char) { /* encode surrogates correctly */ - utf8_encode_buf16(buf, buf_size, str->u.str16, str->len); + utf8_encode_buf16(buf, buf_size, str16(str), str->len); } else { - /* special case ASCII strings */ - int i, c = 0; - for(i = 0; i < str->len; i++) { - c |= str->u.str8[i]; - } - if (c < 0x80) - return (const char *)str->u.str8; - utf8_encode_buf8(buf, buf_size, str->u.str8, str->len); + utf8_encode_buf8(buf, buf_size, str8(str), str->len); } } } @@ -3368,7 +3370,7 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) p = p1; len = p->len; if (p->is_wide_char) { - const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len; + const uint16_t *r = str16(p), *r_end = str16(p) + len; if (r >= r_end) return JS_UNDEFINED; c = *r; @@ -3390,7 +3392,7 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) return JS_UNDEFINED; } } else { - const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len; + const uint8_t *r = str8(p), *r_end = str8(p) + len; if (r >= r_end) return JS_UNDEFINED; c = *r; @@ -3510,19 +3512,24 @@ static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) } /* free with JS_FreeCString() */ -const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) +const char *JS_AtomToCStringLen(JSContext *ctx, size_t *plen, JSAtom atom) { JSValue str; const char *cstr; str = JS_AtomToString(ctx, atom); - if (JS_IsException(str)) + if (JS_IsException(str)) { + if (plen) + *plen = 0; return NULL; - cstr = JS_ToCString(ctx, str); + } + cstr = JS_ToCStringLen(ctx, plen, str); JS_FreeValue(ctx, str); return cstr; } +#ifndef QJS_DISABLE_PARSER + /* return a string atom containing name concatenated with str1 */ /* `str1` may be pure ASCII or UTF-8 encoded */ // TODO(chqrlie): use string concatenation instead of UTF-8 conversion @@ -3561,11 +3568,16 @@ static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1) static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) { char buf[16]; - u32toa(buf, n); + size_t len; + + len = u32toa(buf, n); + buf[len] = '\0'; return js_atom_concat_str(ctx, name, buf); } -static inline bool JS_IsEmptyString(JSValue v) +#endif // QJS_DISABLE_PARSER + +static inline bool JS_IsEmptyString(JSValueConst v) { return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0; } @@ -3583,7 +3595,7 @@ JSClassID JS_NewClassID(JSRuntime *rt, JSClassID *pclass_id) return class_id; } -JSClassID JS_GetClassID(JSValue v) +JSClassID JS_GetClassID(JSValueConst v) { JSObject *p; if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) @@ -3598,6 +3610,15 @@ bool JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id) rt->class_array[class_id].class_id != 0); } +JSAtom JS_GetClassName(JSRuntime *rt, JSClassID class_id) +{ + if (JS_IsRegisteredClass(rt, class_id)) { + return JS_DupAtomRT(rt, rt->class_array[class_id].class_id); + } else { + return JS_ATOM_NULL; + } +} + /* create a new object internal class. Return -1 if error, 0 if OK. The finalizer can be NULL if none is needed. */ static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, @@ -3667,6 +3688,12 @@ int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) return ret; } +static inline JSValue js_empty_string(JSRuntime *rt) +{ + JSAtomStruct *p = rt->atom_array[JS_ATOM_empty_string]; + return js_dup(JS_MKPTR(JS_TAG_STRING, p)); +} + // XXX: `buf` contains raw 8-bit data, no UTF-8 decoding is performed // XXX: no special case for len == 0 static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len) @@ -3675,8 +3702,8 @@ static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len) str = js_alloc_string(ctx, len, 0); if (!str) return JS_EXCEPTION; - memcpy(str->u.str8, buf, len); - str->u.str8[len] = '\0'; + memcpy(str8(str), buf, len); + str8(str)[len] = '\0'; return JS_MKPTR(JS_TAG_STRING, str); } @@ -3693,7 +3720,7 @@ static JSValue js_new_string16_len(JSContext *ctx, const uint16_t *buf, int len) str = js_alloc_string(ctx, len, 1); if (!str) return JS_EXCEPTION; - memcpy(str->u.str16, buf, len * 2); + memcpy(str16(str), buf, len * 2); return JS_MKPTR(JS_TAG_STRING, str); } @@ -3710,33 +3737,57 @@ static JSValue js_new_string_char(JSContext *ctx, uint16_t c) static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) { - int len = end - start; + JSStringSlice *slice; + JSString *q; + int len; + + len = end - start; if (start == 0 && end == p->len) { return js_dup(JS_MKPTR(JS_TAG_STRING, p)); } if (len <= 0) { - return JS_AtomToString(ctx, JS_ATOM_empty_string); + return js_empty_string(ctx->rt); + } + if (len > (JS_STRING_SLICE_LEN_MAX >> p->is_wide_char)) { + if (p->kind == JS_STRING_KIND_SLICE) { + slice = (void *)&p[1]; + p = slice->parent; + start += slice->start >> p->is_wide_char; // bytes -> chars + } + // allocate as 16 bit wide string to avoid wastage; + // js_alloc_string allocates 1 byte extra for 8 bit strings; + q = js_alloc_string(ctx, sizeof(*slice)/2, /*is_wide_char*/true); + if (!q) + return JS_EXCEPTION; + q->is_wide_char = p->is_wide_char; + q->kind = JS_STRING_KIND_SLICE; + q->len = len; + slice = (void *)&q[1]; + slice->parent = p; + slice->start = start << p->is_wide_char; // chars -> bytes + p->header.ref_count++; + return JS_MKPTR(JS_TAG_STRING, q); } if (p->is_wide_char) { JSString *str; int i; uint16_t c = 0; for (i = start; i < end; i++) { - c |= p->u.str16[i]; + c |= str16(p)[i]; } if (c > 0xFF) - return js_new_string16_len(ctx, p->u.str16 + start, len); + return js_new_string16_len(ctx, str16(p) + start, len); str = js_alloc_string(ctx, len, 0); if (!str) return JS_EXCEPTION; for (i = 0; i < len; i++) { - str->u.str8[i] = p->u.str16[start + i]; + str8(str)[i] = str16(p)[start + i]; } - str->u.str8[len] = '\0'; + str8(str)[len] = '\0'; return JS_MKPTR(JS_TAG_STRING, str); } else { - return js_new_string8_len(ctx, (const char *)(p->u.str8 + start), len); + return js_new_string8_len(ctx, (const char *)(str8(p) + start), len); } } @@ -3807,7 +3858,7 @@ static no_inline int string_buffer_widen(StringBuffer *s, int size) return string_buffer_set_error(s); size += slack >> 1; for(i = s->len; i-- > 0;) { - str->u.str16[i] = str->u.str8[i]; + str16(str)[i] = str8(str)[i]; } s->is_wide_char = 1; s->size = size; @@ -3849,13 +3900,13 @@ static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c) return -1; } if (s->is_wide_char) { - s->str->u.str16[s->len++] = c; + str16(s->str)[s->len++] = c; } else if (c < 0x100) { - s->str->u.str8[s->len++] = c; + str8(s->str)[s->len++] = c; } else { if (string_buffer_widen(s, s->size)) return -1; - s->str->u.str16[s->len++] = c; + str16(s->str)[s->len++] = c; } return 0; } @@ -3868,9 +3919,9 @@ static int string_buffer_putc8(StringBuffer *s, uint32_t c) return -1; } if (s->is_wide_char) { - s->str->u.str16[s->len++] = c; + str16(s->str)[s->len++] = c; } else { - s->str->u.str8[s->len++] = c; + str8(s->str)[s->len++] = c; } return 0; } @@ -3880,10 +3931,10 @@ static int string_buffer_putc16(StringBuffer *s, uint32_t c) { if (likely(s->len < s->size)) { if (s->is_wide_char) { - s->str->u.str16[s->len++] = c; + str16(s->str)[s->len++] = c; return 0; } else if (c < 0x100) { - s->str->u.str8[s->len++] = c; + str8(s->str)[s->len++] = c; return 0; } } @@ -3902,21 +3953,21 @@ static int string_buffer_putc(StringBuffer *s, uint32_t c) return string_buffer_putc16(s, c); } -static int string_getc(const JSString *p, int *pidx) +static int string_getc(JSString *p, int *pidx) { int idx, c, c1; idx = *pidx; if (p->is_wide_char) { - c = p->u.str16[idx++]; + c = str16(p)[idx++]; if (is_hi_surrogate(c) && idx < p->len) { - c1 = p->u.str16[idx]; + c1 = str16(p)[idx]; if (is_lo_surrogate(c1)) { c = from_surrogate(c, c1); idx++; } } } else { - c = p->u.str8[idx++]; + c = str8(p)[idx++]; } *pidx = idx; return c; @@ -3932,11 +3983,11 @@ static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len) } if (s->is_wide_char) { for (i = 0; i < len; i++) { - s->str->u.str16[s->len + i] = p[i]; + str16(s->str)[s->len + i] = p[i]; } s->len += len; } else { - memcpy(&s->str->u.str8[s->len], p, len); + memcpy(&str8(s->str)[s->len], p, len); s->len += len; } return 0; @@ -3957,11 +4008,11 @@ static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len) return -1; } if (s->is_wide_char) { - memcpy(&s->str->u.str16[s->len], p, len << 1); + memcpy(&str16(s->str)[s->len], p, len << 1); s->len += len; } else { for (i = 0; i < len; i++) { - s->str->u.str8[s->len + i] = p[i]; + str8(s->str)[s->len + i] = p[i]; } s->len += len; } @@ -3974,18 +4025,18 @@ static int string_buffer_puts8(StringBuffer *s, const char *str) return string_buffer_write8(s, (const uint8_t *)str, strlen(str)); } -static int string_buffer_concat(StringBuffer *s, const JSString *p, +static int string_buffer_concat(StringBuffer *s, JSString *p, uint32_t from, uint32_t to) { if (to <= from) return 0; if (p->is_wide_char) - return string_buffer_write16(s, p->u.str16 + from, to - from); + return string_buffer_write16(s, str16(p) + from, to - from); else - return string_buffer_write8(s, p->u.str8 + from, to - from); + return string_buffer_write8(s, str8(p) + from, to - from); } -static int string_buffer_concat_value(StringBuffer *s, JSValue v) +static int string_buffer_concat_value(StringBuffer *s, JSValueConst v) { JSString *p; JSValue v1; @@ -4052,7 +4103,7 @@ static JSValue string_buffer_end(StringBuffer *s) if (s->len == 0) { js_free(s->ctx, str); s->str = NULL; - return JS_AtomToString(s->ctx, JS_ATOM_empty_string); + return js_empty_string(s->ctx->rt); } if (s->len < s->size) { /* smaller size so js_realloc should not fail, but OK if it does */ @@ -4065,7 +4116,7 @@ static JSValue string_buffer_end(StringBuffer *s) s->str = str; } if (!s->is_wide_char) - str->u.str8[s->len] = 0; + str8(str)[s->len] = 0; #ifdef ENABLE_DUMPS // JS_DUMP_LEAKS list_add_tail(&str->link, &s->ctx->rt->string_list); #endif @@ -4083,7 +4134,7 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) int kind; if (buf_len <= 0) { - return JS_AtomToString(ctx, JS_ATOM_empty_string); + return js_empty_string(ctx->rt); } /* Compute string kind and length: 7-bit, 8-bit, 16-bit, 16-bit UTF-16 */ kind = utf8_scan(buf, buf_len, &len); @@ -4095,15 +4146,15 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) str = js_alloc_string(ctx, len, 0); if (!str) return JS_EXCEPTION; - memcpy(str->u.str8, buf, len); - str->u.str8[len] = '\0'; + memcpy(str8(str), buf, len); + str8(str)[len] = '\0'; break; case UTF8_NON_ASCII: /* buf contains non-ASCII code-points, but limited to 8-bit values */ str = js_alloc_string(ctx, len, 0); if (!str) return JS_EXCEPTION; - utf8_decode_buf8(str->u.str8, len + 1, buf, buf_len); + utf8_decode_buf8(str8(str), len + 1, buf, buf_len); break; default: // This causes a potential problem in JS_ThrowError if message is invalid @@ -4112,12 +4163,25 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) str = js_alloc_string(ctx, len, 1); if (!str) return JS_EXCEPTION; - utf8_decode_buf16(str->u.str16, len, buf, buf_len); + utf8_decode_buf16(str16(str), len, buf, buf_len); break; } return JS_MKPTR(JS_TAG_STRING, str); } +JSValue JS_NewTwoByteString(JSContext *ctx, const uint16_t *buf, size_t len) +{ + JSString *str; + + if (!len) + return js_empty_string(ctx->rt); + str = js_alloc_string(ctx, len, 1); + if (!str) + return JS_EXCEPTION; + memcpy(str16(str), buf, len * sizeof(*buf)); + return JS_MKPTR(JS_TAG_STRING, str); +} + static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, JSValue str2, const char *str3) { @@ -4163,7 +4227,8 @@ JSValue JS_NewAtomString(JSContext *ctx, const char *str) /* return (NULL, 0) if exception. */ /* return pointer into a JSString with a live ref_count */ /* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */ -const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValue val1, bool cesu8) +const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, + bool cesu8) { JSValue val; JSString *str, *str_new; @@ -4201,7 +4266,7 @@ const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValue val1, bool ce str = JS_VALUE_GET_STRING(val); len = str->len; if (!str->is_wide_char) { - const uint8_t *src = str->u.str8; + const uint8_t *src = str8(str); int count; /* count the number of non-ASCII characters */ @@ -4214,7 +4279,7 @@ const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValue val1, bool ce for (pos = 0; pos < len; pos++) { count += src[pos] >> 7; } - if (count == 0) { + if (count == 0 && str->kind == JS_STRING_KIND_NORMAL) { if (plen) *plen = len; return (const char *)src; @@ -4222,7 +4287,7 @@ const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValue val1, bool ce str_new = js_alloc_string(ctx, len + count, 0); if (!str_new) goto fail; - q = str_new->u.str8; + q = str8(str_new); for (pos = 0; pos < len; pos++) { c = src[pos]; if (c < 0x80) { @@ -4233,14 +4298,14 @@ const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValue val1, bool ce } } } else { - const uint16_t *src = str->u.str16; + const uint16_t *src = str16(str); /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may produce 4 bytes but use 2 code points. */ str_new = js_alloc_string(ctx, len * 3, 0); if (!str_new) goto fail; - q = str_new->u.str8; + q = str8(str_new); pos = 0; while (pos < len) { c = src[pos++]; @@ -4268,11 +4333,11 @@ const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValue val1, bool ce } *q = '\0'; - str_new->len = q - str_new->u.str8; + str_new->len = q - str8(str_new); JS_FreeValue(ctx, val); if (plen) *plen = str_new->len; - return (const char *)str_new->u.str8; + return (const char *)str8(str_new); fail: if (plen) *plen = 0; @@ -4281,12 +4346,10 @@ const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValue val1, bool ce void JS_FreeCString(JSContext *ctx, const char *ptr) { - JSString *p; if (!ptr) return; /* purposely removing constness */ - p = container_of(ptr, JSString, u); - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, (JSString *)ptr - 1)); } static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len) @@ -4311,32 +4374,32 @@ static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len) return 0; } -static int js_string_memcmp(const JSString *p1, const JSString *p2, int len) +static int js_string_memcmp(JSString *p1, JSString *p2, int len) { int res; if (likely(!p1->is_wide_char)) { if (likely(!p2->is_wide_char)) - res = memcmp(p1->u.str8, p2->u.str8, len); + res = memcmp(str8(p1), str8(p2), len); else - res = -memcmp16_8(p2->u.str16, p1->u.str8, len); + res = -memcmp16_8(str16(p2), str8(p1), len); } else { if (!p2->is_wide_char) - res = memcmp16_8(p1->u.str16, p2->u.str8, len); + res = memcmp16_8(str16(p1), str8(p2), len); else - res = memcmp16(p1->u.str16, p2->u.str16, len); + res = memcmp16(str16(p1), str16(p2), len); } return res; } -static bool js_string_eq(const JSString *p1, const JSString *p2) { +static bool js_string_eq(JSString *p1, JSString *p2) { if (p1->len != p2->len) return false; return js_string_memcmp(p1, p2, p1->len) == 0; } /* return < 0, 0 or > 0 */ -static int js_string_compare(const JSString *p1, const JSString *p2) +static int js_string_compare(JSString *p1, JSString *p2) { int res, len; len = min_int(p1->len, p2->len); @@ -4346,12 +4409,12 @@ static int js_string_compare(const JSString *p1, const JSString *p2) return res; } -static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len) +static void copy_str16(uint16_t *dst, JSString *p, int offset, int len) { if (p->is_wide_char) { - memcpy(dst, p->u.str16 + offset, len * 2); + memcpy(dst, str16(p) + offset, len * 2); } else { - const uint8_t *src1 = p->u.str8 + offset; + const uint8_t *src1 = str8(p) + offset; int i; for(i = 0; i < len; i++) @@ -4359,8 +4422,7 @@ static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len) } } -static JSValue JS_ConcatString1(JSContext *ctx, - const JSString *p1, const JSString *p2) +static JSValue JS_ConcatString1(JSContext *ctx, JSString *p1, JSString *p2) { JSString *p; uint32_t len; @@ -4374,12 +4436,12 @@ static JSValue JS_ConcatString1(JSContext *ctx, if (!p) return JS_EXCEPTION; if (!is_wide_char) { - memcpy(p->u.str8, p1->u.str8, p1->len); - memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len); - p->u.str8[len] = '\0'; + memcpy(str8(p), str8(p1), p1->len); + memcpy(str8(p) + p1->len, str8(p2), p2->len); + str8(p)[len] = '\0'; } else { - copy_str16(p->u.str16, p1, 0, p1->len); - copy_str16(p->u.str16 + p1->len, p2, 0, p2->len); + copy_str16(str16(p), p1, 0, p1->len); + copy_str16(str16(p) + p1->len, p2, 0, p2->len); } return JS_MKPTR(JS_TAG_STRING, p); } @@ -4416,12 +4478,12 @@ static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) && js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) { /* Concatenate in place in available space at the end of p1 */ if (p1->is_wide_char) { - memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); + memcpy(str16(p1) + p1->len, str16(p2), p2->len << 1); p1->len += p2->len; } else { - memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len); + memcpy(str8(p1) + p1->len, str8(p2), p2->len); p1->len += p2->len; - p1->u.str8[p1->len] = '\0'; + str8(p1)[p1->len] = '\0'; } ret_op1: JS_FreeValue(ctx, op2); @@ -4463,7 +4525,7 @@ static inline JSShapeProperty *get_shape_prop(JSShape *sh) static int init_shape_hash(JSRuntime *rt) { - rt->shape_hash_bits = 4; /* 16 shapes */ + rt->shape_hash_bits = 6; /* 64 shapes */ rt->shape_hash_size = 1 << rt->shape_hash_bits; rt->shape_hash_count = 0; rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * @@ -4575,7 +4637,6 @@ static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, sh->hash = shape_initial_hash(proto); sh->is_hashed = true; sh->has_small_array_index = false; - sh->watchpoint = NULL; js_shape_hash_link(ctx->rt, sh); return sh; } @@ -4607,7 +4668,6 @@ static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1) sh->header.ref_count = 1; add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); sh->is_hashed = false; - sh->watchpoint = NULL; if (sh->proto) { js_dup(JS_MKPTR(JS_TAG_OBJECT, sh->proto)); } @@ -4634,7 +4694,6 @@ static void js_free_shape0(JSRuntime *rt, JSShape *sh) if (sh->proto != NULL) { JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); } - js_shape_delete_watchpoints(rt, sh, NULL); pr = get_shape_prop(sh); for(i = 0; i < sh->prop_count; i++) { JS_FreeAtomRT(rt, pr->atom); @@ -5033,7 +5092,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas return JS_MKPTR(JS_TAG_OBJECT, p); } -static JSObject *get_proto_obj(JSValue proto_val) +static JSObject *get_proto_obj(JSValueConst proto_val) { if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) return NULL; @@ -5042,7 +5101,7 @@ static JSObject *get_proto_obj(JSValue proto_val) } /* WARNING: proto must be an object or JS_NULL */ -JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValue proto_val, +JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val, JSClassID class_id) { JSShape *sh; @@ -5060,7 +5119,7 @@ JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValue proto_val, return JS_NewObjectFromShape(ctx, sh, class_id); } -static int JS_SetObjectData(JSContext *ctx, JSValue obj, JSValue val) +static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val) { JSObject *p; @@ -5084,22 +5143,127 @@ static int JS_SetObjectData(JSContext *ctx, JSValue obj, JSValue val) return -1; } -JSValue JS_NewObjectClass(JSContext *ctx, int class_id) +JSValue JS_NewObjectClass(JSContext *ctx, JSClassID class_id) { return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id); } -JSValue JS_NewObjectProto(JSContext *ctx, JSValue proto) +JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto) { return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); } +JSValue JS_NewObjectFrom(JSContext *ctx, int count, const JSAtom *props, + const JSValue *values) +{ + JSShapeProperty *pr; + uint32_t *hash; + JSRuntime *rt; + JSObject *p; + JSShape *sh; + JSValue obj; + JSAtom atom; + intptr_t h; + int i; + + rt = ctx->rt; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + if (count > 0) { + p = JS_VALUE_GET_OBJ(obj); + sh = p->shape; + assert(sh->is_hashed); + assert(sh->header.ref_count == 1); + js_shape_hash_unlink(rt, sh); + if (resize_properties(ctx, &sh, p, count)) { + js_shape_hash_link(rt, sh); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + p->shape = sh; + for (i = 0; i < count; i++) { + atom = props[i]; + pr = &sh->prop[i]; + sh->hash = shape_hash(shape_hash(sh->hash, atom), JS_PROP_C_W_E); + sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); + h = atom & sh->prop_hash_mask; + hash = &prop_hash_end(sh)[-h - 1]; + pr->hash_next = *hash; + *hash = i + 1; + pr->atom = JS_DupAtom(ctx, atom); + pr->flags = JS_PROP_C_W_E; + p->prop[i].u.value = values[i]; + } + js_shape_hash_link(rt, sh); + sh->prop_count = count; + } + return obj; +} + +JSValue JS_NewObjectFromStr(JSContext *ctx, int count, const char **props, + const JSValue *values) +{ + JSAtom atoms_s[16], *atoms = atoms_s; + JSValue ret; + int i; + + i = 0; + ret = JS_EXCEPTION; + if (count < 1) + goto out; + if (count > (int)countof(atoms_s)) { + atoms = js_malloc(ctx, count * sizeof(*atoms)); + if (!atoms) + return JS_EXCEPTION; + } + for (i = 0; i < count; i++) { + atoms[i] = JS_NewAtom(ctx, props[i]); + if (atoms[i] == JS_ATOM_NULL) + goto out; + } + ret = JS_NewObjectFrom(ctx, count, atoms, values); +out: + while (i-- > 0) + JS_FreeAtom(ctx, atoms[i]); + if (atoms != atoms_s) + js_free(ctx, atoms); + return ret; +} + JSValue JS_NewArray(JSContext *ctx) { return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape), JS_CLASS_ARRAY); } +// note: takes ownership of |values|, unlike js_create_array +JSValue JS_NewArrayFrom(JSContext *ctx, int count, const JSValue *values) +{ + JSObject *p; + JSValue obj; + int i; + + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + goto exception; + if (count > 0) { + p = JS_VALUE_GET_OBJ(obj); + if (expand_fast_array(ctx, p, count)) { + JS_FreeValue(ctx, obj); + goto exception; + } + p->u.array.count = count; + p->prop[0].u.value = js_int32(count); + memcpy(p->u.array.u.values, values, count * sizeof(*values)); + } + return obj; +exception: + for (i = 0; i < count; i++) + JS_FreeValue(ctx, values[i]); + return JS_EXCEPTION; +} + JSValue JS_NewObject(JSContext *ctx) { /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */ @@ -5125,7 +5289,7 @@ static bool js_class_has_bytecode(JSClassID class_id) } /* return NULL without exception if not a function or no bytecode */ -static JSFunctionBytecode *JS_GetFunctionBytecode(JSValue val) +static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) { JSObject *p; if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) @@ -5198,10 +5362,10 @@ static int js_method_set_properties(JSContext *ctx, JSValue func_obj, /* Note: at least 'length' arguments will be readable in 'argv' */ /* `name` may be NULL, pure ASCII or UTF-8 encoded */ -static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, - const char *name, - int length, JSCFunctionEnum cproto, int magic, - JSValue proto_val) +JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic, + JSValueConst proto_val) { JSValue func_obj; JSObject *p; @@ -5220,9 +5384,14 @@ static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, cproto == JS_CFUNC_constructor_magic || cproto == JS_CFUNC_constructor_or_func || cproto == JS_CFUNC_constructor_or_func_magic); - if (!name) - name = ""; - name_atom = JS_NewAtom(ctx, name); + name_atom = JS_ATOM_empty_string; + if (name && *name) { + name_atom = JS_NewAtom(ctx, name); + if (name_atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; + } + } js_function_set_properties(ctx, func_obj, name_atom, length); JS_FreeAtom(ctx, name_atom); return func_obj; @@ -5245,7 +5414,7 @@ typedef struct JSCFunctionDataRecord { JSValue data[]; } JSCFunctionDataRecord; -static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val) +static void js_c_function_data_finalizer(JSRuntime *rt, JSValueConst val) { JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); int i; @@ -5258,7 +5427,7 @@ static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val) } } -static void js_c_function_data_mark(JSRuntime *rt, JSValue val, +static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); @@ -5271,33 +5440,53 @@ static void js_c_function_data_mark(JSRuntime *rt, JSValue val, } } -static JSValue js_c_function_data_call(JSContext *ctx, JSValue func_obj, - JSValue this_val, - int argc, JSValue *argv, int flags) +static JSValue js_call_c_function_data(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_val, + int argc, JSValueConst *argv, int flags) { - JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); - JSValue *arg_buf; + JSRuntime *rt = ctx->rt; + JSStackFrame sf_s, *sf = &sf_s, *prev_sf; + JSCFunctionDataRecord *s; + JSValueConst *arg_buf; + JSValue ret; + size_t stack_size; + int arg_count; int i; - /* XXX: could add the function on the stack for debug */ - if (unlikely(argc < s->length)) { - arg_buf = alloca(sizeof(arg_buf[0]) * s->length); + s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); + if (!s) + return JS_EXCEPTION; // can't really happen + arg_buf = argv; + arg_count = s->length; + if (unlikely(argc < arg_count)) { + stack_size = arg_count * sizeof(arg_buf[0]); + if (js_check_stack_overflow(rt, stack_size)) + return JS_ThrowStackOverflow(ctx); + arg_buf = alloca(stack_size); for(i = 0; i < argc; i++) arg_buf[i] = argv[i]; - for(i = argc; i < s->length; i++) + for(i = argc; i < arg_count; i++) arg_buf[i] = JS_UNDEFINED; - } else { - arg_buf = argv; } - - return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data); + prev_sf = rt->current_stack_frame; + sf->prev_frame = prev_sf; + rt->current_stack_frame = sf; + // TODO(bnoordhuis) switch realms like js_call_c_function does + sf->is_strict_mode = false; + sf->cur_func = unsafe_unconst(func_obj); + sf->arg_count = argc; + ret = s->func(ctx, this_val, argc, arg_buf, s->magic, vc(s->data)); + rt->current_stack_frame = sf->prev_frame; + return ret; } -JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, - int length, int magic, int data_len, - JSValue *data) +JSValue JS_NewCFunctionData2(JSContext *ctx, JSCFunctionData *func, + const char *name, + int length, int magic, int data_len, + JSValueConst *data) { JSCFunctionDataRecord *s; + JSAtom name_atom; JSValue func_obj; int i; @@ -5317,11 +5506,26 @@ JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, for(i = 0; i < data_len; i++) s->data[i] = js_dup(data[i]); JS_SetOpaqueInternal(func_obj, s); - js_function_set_properties(ctx, func_obj, - JS_ATOM_empty_string, length); + name_atom = JS_ATOM_empty_string; + if (name && *name) { + name_atom = JS_NewAtom(ctx, name); + if (name_atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; + } + } + js_function_set_properties(ctx, func_obj, name_atom, length); + JS_FreeAtom(ctx, name_atom); return func_obj; } +JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, + int length, int magic, int data_len, + JSValueConst *data) +{ + return JS_NewCFunctionData2(ctx, func, NULL, length, magic, data_len, data); +} + static JSContext *js_autoinit_get_realm(JSProperty *pr) { return (JSContext *)(pr->u.init.realm_and_id & ~3); @@ -5405,36 +5609,6 @@ static force_inline JSShapeProperty *find_own_property(JSProperty **ppr, return NULL; } -static force_inline JSShapeProperty* find_own_property_ic(JSProperty** ppr, JSObject* p, - JSAtom atom, uint32_t* offset) -{ - JSShape* sh; - JSShapeProperty *pr, *prop; - intptr_t h, i; - sh = p->shape; - h = (uintptr_t)atom & sh->prop_hash_mask; - h = prop_hash_end(sh)[-h - 1]; - prop = get_shape_prop(sh); - while (h) { - i = h - 1; - pr = &prop[i]; - if (likely(pr->atom == atom)) { - *ppr = &p->prop[i]; - *offset = i; - /* the compiler should be able to assume that pr != NULL here */ - return pr; - } - h = pr->hash_next; - } - *ppr = NULL; - return NULL; -} - -/* indicate that the object may be part of a function prototype cycle */ -static void set_cycle_flag(JSContext *ctx, JSValue obj) -{ -} - static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) { if (var_ref) { @@ -5451,7 +5625,7 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) } } -static void js_array_finalizer(JSRuntime *rt, JSValue val) +static void js_array_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p = JS_VALUE_GET_OBJ(val); int i; @@ -5462,7 +5636,7 @@ static void js_array_finalizer(JSRuntime *rt, JSValue val) js_free_rt(rt, p->u.array.u.values); } -static void js_array_mark(JSRuntime *rt, JSValue val, +static void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -5473,21 +5647,21 @@ static void js_array_mark(JSRuntime *rt, JSValue val, } } -static void js_object_data_finalizer(JSRuntime *rt, JSValue val) +static void js_object_data_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p = JS_VALUE_GET_OBJ(val); JS_FreeValueRT(rt, p->u.object_data); p->u.object_data = JS_UNDEFINED; } -static void js_object_data_mark(JSRuntime *rt, JSValue val, +static void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); JS_MarkValue(rt, p->u.object_data, mark_func); } -static void js_c_function_finalizer(JSRuntime *rt, JSValue val) +static void js_c_function_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -5495,7 +5669,7 @@ static void js_c_function_finalizer(JSRuntime *rt, JSValue val) JS_FreeContext(p->u.cfunc.realm); } -static void js_c_function_mark(JSRuntime *rt, JSValue val, +static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -5504,7 +5678,7 @@ static void js_c_function_mark(JSRuntime *rt, JSValue val, mark_func(rt, &p->u.cfunc.realm->header); } -static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val) +static void js_bytecode_function_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p1, *p = JS_VALUE_GET_OBJ(val); JSFunctionBytecode *b; @@ -5527,7 +5701,7 @@ static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val) } } -static void js_bytecode_function_mark(JSRuntime *rt, JSValue val, +static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -5554,7 +5728,7 @@ static void js_bytecode_function_mark(JSRuntime *rt, JSValue val, } } -static void js_bound_function_finalizer(JSRuntime *rt, JSValue val) +static void js_bound_function_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSBoundFunction *bf = p->u.bound_function; @@ -5568,7 +5742,7 @@ static void js_bound_function_finalizer(JSRuntime *rt, JSValue val) js_free_rt(rt, bf); } -static void js_bound_function_mark(JSRuntime *rt, JSValue val, +static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -5581,7 +5755,7 @@ static void js_bound_function_mark(JSRuntime *rt, JSValue val, JS_MarkValue(rt, bf->argv[i], mark_func); } -static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val) +static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSForInIterator *it = p->u.for_in_iterator; @@ -5589,7 +5763,7 @@ static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val) js_free_rt(rt, it); } -static void js_for_in_iterator_mark(JSRuntime *rt, JSValue val, +static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -5698,17 +5872,7 @@ static void js_free_value_rt(JSRuntime *rt, JSValue v) switch(tag) { case JS_TAG_STRING: - { - JSString *p = JS_VALUE_GET_STRING(v); - if (p->atom_type) { - JS_FreeAtomStruct(rt, p); - } else { -#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS - list_del(&p->link); -#endif - js_free_rt(rt, p); - } - } + js_free_string0(rt, JS_VALUE_GET_STRING(v)); break; case JS_TAG_OBJECT: case JS_TAG_FUNCTION_BYTECODE: @@ -5728,9 +5892,8 @@ static void js_free_value_rt(JSRuntime *rt, JSValue v) break; case JS_TAG_BIG_INT: { - JSBigInt *bf = JS_VALUE_GET_PTR(v); - bf_delete(&bf->num); - js_free_rt(rt, bf); + JSBigInt *p = JS_VALUE_GET_PTR(v); + js_free_rt(rt, p); } break; case JS_TAG_SYMBOL: @@ -5775,7 +5938,7 @@ static void remove_gc_object(JSGCObjectHeader *h) list_del(&h->link); } -void JS_MarkValue(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) +void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { if (JS_VALUE_HAS_REF_COUNT(val)) { switch(JS_VALUE_GET_TAG(val)) { @@ -5789,6 +5952,22 @@ void JS_MarkValue(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) } } +static void mark_weak_map_value(JSRuntime *rt, JSWeakRefRecord *first_weak_ref, JS_MarkFunc *mark_func) { + JSWeakRefRecord *wr; + JSMapRecord *mr; + JSMapState *s; + + for (wr = first_weak_ref; wr != NULL; wr = wr->next_weak_ref) { + if (wr->kind == JS_WEAK_REF_KIND_MAP) { + mr = wr->u.map_record; + s = mr->map; + assert(s->is_weak); + assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ + JS_MarkValue(rt, mr->value, mark_func); + } + } +} + static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, JS_MarkFunc *mark_func) { @@ -5828,6 +6007,10 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, prs++; } + if (unlikely(p->first_weak_ref)) { + mark_weak_map_value(rt, p->first_weak_ref, mark_func); + } + if (p->class_id != JS_CLASS_OBJECT) { JSClassGCMark *gc_mark; gc_mark = rt->class_array[p->class_id].gc_mark; @@ -5839,8 +6022,6 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: /* the template objects can be part of a cycle */ { - JSShape **shape, *(*shapes)[IC_CACHE_ITEM_CAPACITY]; - JSObject **proto, *(*protos)[IC_CACHE_ITEM_CAPACITY]; JSFunctionBytecode *b = (JSFunctionBytecode *)gp; int i; for(i = 0; i < b->cpool_count; i++) { @@ -5848,18 +6029,6 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, } if (b->realm) mark_func(rt, &b->realm->header); - if (b->ic) { - for (i = 0; i < b->ic->count; i++) { - shapes = &b->ic->cache[i].shape; - for (shape = *shapes; shape != endof(*shapes); shape++) - if (*shape) - mark_func(rt, &(*shape)->header); - protos = &b->ic->cache[i].proto; - for (proto = *protos; proto != endof(*protos); proto++) - if (*proto) - mark_func(rt, &(*proto)->header); - } - } } break; case JS_GC_OBJ_TYPE_VAR_REF: @@ -6034,7 +6203,7 @@ void JS_RunGC(JSRuntime *rt) /* Return false if not an object or if the object has already been freed (zombie objects are visible in finalizers when freeing cycles). */ -bool JS_IsLiveObject(JSRuntime *rt, JSValue obj) +bool JS_IsLiveObject(JSRuntime *rt, JSValueConst obj) { JSObject *p; if (!JS_IsObject(obj)) @@ -6444,10 +6613,10 @@ void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) if (obj_classes[0]) fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none"); for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) { - if (obj_classes[class_id]) { + if (obj_classes[class_id] && class_id < rt->class_count) { char buf[ATOM_GET_STR_BUF_SIZE]; fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id, - JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name)); + JS_AtomGetStrRT(rt, buf, sizeof(buf), rt->class_array[class_id].class_name)); } } if (obj_classes[JS_CLASS_INIT_COUNT]) @@ -6657,7 +6826,7 @@ static int find_line_num(JSContext *ctx, JSFunctionBytecode *b, /* in order to avoid executing arbitrary code during the stack trace generation, we only look at simple 'name' properties containing a string. */ -static const char *get_func_name(JSContext *ctx, JSValue func) +static const char *get_func_name(JSContext *ctx, JSValueConst func) { JSProperty *pr; JSShapeProperty *prs; @@ -6677,13 +6846,13 @@ static const char *get_func_name(JSContext *ctx, JSValue func) } /* Note: it is important that no exception is returned by this function */ -static bool can_add_backtrace(JSValue obj) +static bool can_add_backtrace(JSValueConst obj) { JSObject *p; if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) return false; p = JS_VALUE_GET_OBJ(obj); - if (p->class_id != JS_CLASS_ERROR) + if (p->class_id != JS_CLASS_ERROR && p->class_id != JS_CLASS_DOM_EXCEPTION) return false; if (find_own_property1(p, JS_ATOM_stack)) return false; @@ -6697,9 +6866,9 @@ static bool can_add_backtrace(JSValue obj) /* if filename != NULL, an additional level is added with the filename and line number information (used for parse error). */ -static void build_backtrace(JSContext *ctx, JSValue error_val, JSValue filter_func, - const char *filename, int line_num, int col_num, - int backtrace_flags) +static void build_backtrace(JSContext *ctx, JSValueConst error_val, + JSValueConst filter_func, const char *filename, + int line_num, int col_num, int backtrace_flags) { JSStackFrame *sf, *sf_start; JSValue stack, prepare, saved_exception; @@ -6712,26 +6881,45 @@ static void build_backtrace(JSContext *ctx, JSValue error_val, JSValue filter_fu JSRuntime *rt; JSCallSiteData csd[64]; uint32_t i; + double d; int stack_trace_limit; + rt = ctx->rt; + if (rt->in_build_stack_trace) + return; + rt->in_build_stack_trace = true; + + // Save exception because conversion to double may fail. + saved_exception = JS_GetException(ctx); + + // Extract stack trace limit. + // Ignore error since it sets d to NAN anyway. + // coverity[check_return] + JS_ToFloat64(ctx, &d, ctx->error_stack_trace_limit); + if (isnan(d) || d < 0.0) + stack_trace_limit = 0; + else if (d > INT32_MAX) + stack_trace_limit = INT32_MAX; + else + stack_trace_limit = fabs(d); + + // Restore current exception. + JS_Throw(ctx, saved_exception); saved_exception = JS_UNINITIALIZED; - stack_trace_limit = ctx->error_stack_trace_limit; + stack_trace_limit = min_int(stack_trace_limit, countof(csd)); stack_trace_limit = max_int(stack_trace_limit, 0); - rt = ctx->rt; has_prepare = false; has_filter_func = backtrace_flags & JS_BACKTRACE_FLAG_FILTER_FUNC; i = 0; - if (!rt->in_prepare_stack_trace && !JS_IsNull(ctx->error_ctor)) { + if (!JS_IsNull(ctx->error_ctor)) { prepare = js_dup(ctx->error_prepare_stack); has_prepare = JS_IsFunction(ctx, prepare); - rt->in_prepare_stack_trace = true; } if (has_prepare) { - saved_exception = rt->current_exception; - rt->current_exception = JS_UNINITIALIZED; + saved_exception = JS_GetException(ctx); if (stack_trace_limit == 0) goto done; if (filename) @@ -6844,7 +7032,7 @@ static void build_backtrace(JSContext *ctx, JSValue error_val, JSValue filter_fu JS_FreeValue(ctx, csd[k].func); JS_FreeValue(ctx, csd[k].func_name); } - JSValue args[] = { + JSValueConst args[] = { error_val, stack, }; @@ -6855,8 +7043,7 @@ static void build_backtrace(JSContext *ctx, JSValue error_val, JSValue filter_fu else stack = stack2; JS_FreeValue(ctx, prepare); - JS_FreeValue(ctx, rt->current_exception); - rt->current_exception = saved_exception; + JS_Throw(ctx, saved_exception); } else { if (dbuf_error(&dbuf)) stack = JS_NULL; @@ -6865,7 +7052,6 @@ static void build_backtrace(JSContext *ctx, JSValue error_val, JSValue filter_fu dbuf_free(&dbuf); } - rt->in_prepare_stack_trace = false; if (JS_IsUndefined(ctx->error_back_trace)) ctx->error_back_trace = js_dup(stack); if (has_filter_func || can_add_backtrace(error_val)) { @@ -6874,6 +7060,8 @@ static void build_backtrace(JSContext *ctx, JSValue error_val, JSValue filter_fu } else { JS_FreeValue(ctx, stack); } + + rt->in_build_stack_trace = false; } JSValue JS_NewError(JSContext *ctx) @@ -6885,8 +7073,8 @@ JSValue JS_NewError(JSContext *ctx) return obj; } -static JSValue JS_MakeError(JSContext *ctx, JSErrorEnum error_num, - const char *message, bool add_backtrace) +static JSValue JS_MakeError2(JSContext *ctx, JSErrorEnum error_num, + bool add_backtrace, const char *message) { JSValue obj, msg; @@ -6910,15 +7098,24 @@ static JSValue JS_MakeError(JSContext *ctx, JSErrorEnum error_num, return obj; } -/* fmt and arguments may be pure ASCII or UTF-8 encoded contents */ -static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, - const char *fmt, va_list ap, bool add_backtrace) +static JSValue JS_PRINTF_FORMAT_ATTR(4, 0) +JS_MakeError(JSContext *ctx, JSErrorEnum error_num, bool add_backtrace, + JS_PRINTF_FORMAT const char *fmt, va_list ap) { char buf[256]; - JSValue obj; vsnprintf(buf, sizeof(buf), fmt, ap); - obj = JS_MakeError(ctx, error_num, buf, add_backtrace); + return JS_MakeError2(ctx, error_num, add_backtrace, buf); +} + +/* fmt and arguments may be pure ASCII or UTF-8 encoded contents */ +static JSValue JS_PRINTF_FORMAT_ATTR(4, 0) +JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, bool add_backtrace, + JS_PRINTF_FORMAT const char *fmt, va_list ap) +{ + JSValue obj; + + obj = JS_MakeError(ctx, error_num, add_backtrace, fmt, ap); if (unlikely(JS_IsException(obj))) { /* out of memory: throw JS_NULL to avoid recursing */ obj = JS_NULL; @@ -6926,8 +7123,9 @@ static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, return JS_Throw(ctx, obj); } -static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, - const char *fmt, va_list ap) +static JSValue JS_PRINTF_FORMAT_ATTR(3, 0) +JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, + JS_PRINTF_FORMAT const char *fmt, va_list ap) { JSRuntime *rt = ctx->rt; JSStackFrame *sf; @@ -6937,41 +7135,48 @@ static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, sf = rt->current_stack_frame; add_backtrace = !rt->in_out_of_memory && (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL)); - return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace); -} - -JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowPlainError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_PLAIN_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowSyntaxError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowTypeError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); - va_end(ap); - return val; -} + return JS_ThrowError2(ctx, error_num, add_backtrace, fmt, ap); +} + +#define JS_ERROR_MAP(X) \ + X(Internal, INTERNAL) \ + X(Plain, PLAIN) \ + X(Range, RANGE) \ + X(Reference, REFERENCE) \ + X(Syntax, SYNTAX) \ + X(Type, TYPE) \ + +#define X(lc, uc) \ + JSValue JS_PRINTF_FORMAT_ATTR(2, 3) \ + JS_New##lc##Error(JSContext *ctx, \ + JS_PRINTF_FORMAT const char *fmt, ...) \ + { \ + JSValue val; \ + va_list ap; \ + \ + va_start(ap, fmt); \ + val = JS_MakeError(ctx, JS_##uc##_ERROR, \ + /*add_backtrace*/true, fmt, ap); \ + va_end(ap); \ + return val; \ + } \ + JSValue JS_PRINTF_FORMAT_ATTR(2, 3) \ + JS_Throw##lc##Error(JSContext *ctx, \ + JS_PRINTF_FORMAT const char *fmt, ...) \ + { \ + JSValue val; \ + va_list ap; \ + \ + va_start(ap, fmt); \ + val = JS_ThrowError(ctx, JS_##uc##_ERROR, fmt, ap); \ + va_end(ap); \ + return val; \ + } \ + +JS_ERROR_MAP(X) + +#undef X +#undef JS_ERROR_MAP static int JS_PRINTF_FORMAT_ATTR(3, 4) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, JS_PRINTF_FORMAT const char *fmt, ...) { @@ -6988,26 +7193,26 @@ static int JS_PRINTF_FORMAT_ATTR(3, 4) JS_ThrowTypeErrorOrFalse(JSContext *ctx, } } -/* never use it directly */ -static JSValue JS_PRINTF_FORMAT_ATTR(3, 4) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, JS_PRINTF_FORMAT const char *fmt, ...) +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif // __GNUC__ +static JSValue JS_ThrowTypeErrorAtom(JSContext *ctx, const char *fmt, JSAtom atom) { char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowTypeError(ctx, fmt, - JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); + JS_AtomGetStr(ctx, buf, sizeof(buf), atom); + return JS_ThrowTypeError(ctx, fmt, buf); } -/* never use it directly */ -static JSValue JS_PRINTF_FORMAT_ATTR(3, 4) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, JS_PRINTF_FORMAT const char *fmt, ...) +static JSValue JS_ThrowSyntaxErrorAtom(JSContext *ctx, const char *fmt, JSAtom atom) { char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowSyntaxError(ctx, fmt, - JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); + JS_AtomGetStr(ctx, buf, sizeof(buf), atom); + return JS_ThrowSyntaxError(ctx, fmt, buf); } - -/* %s is replaced by 'atom'. The macro is used so that gcc can check - the format string. */ -#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "") -#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "") +#ifdef __GNUC__ +#pragma GCC diagnostic pop // ignored "-Wformat-nonliteral" +#endif // __GNUC__ static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) { @@ -7020,39 +7225,6 @@ static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) } } -JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowReferenceError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowRangeError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowInternalError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap); - va_end(ap); - return val; -} - JSValue JS_ThrowOutOfMemory(JSContext *ctx) { JSRuntime *rt = ctx->rt; @@ -7069,6 +7241,30 @@ static JSValue JS_ThrowStackOverflow(JSContext *ctx) return JS_ThrowRangeError(ctx, "Maximum call stack size exceeded"); } +static JSValue JS_ThrowTypeErrorNotAConstructor(JSContext *ctx, + JSValueConst func_obj) +{ + JSObject *p; + JSAtom name; + + if (JS_TAG_OBJECT != JS_VALUE_GET_TAG(func_obj)) + goto fini; + p = JS_VALUE_GET_OBJ(func_obj); + if (!js_class_has_bytecode(p->class_id)) + goto fini; + name = p->u.func.function_bytecode->func_name; + if (name == JS_ATOM_NULL) + goto fini; + return JS_ThrowTypeErrorAtom(ctx, "%s is not a constructor", name); +fini: + return JS_ThrowTypeError(ctx, "not a constructor"); +} + +static JSValue JS_ThrowTypeErrorNotAFunction(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not a function"); +} + static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx) { return JS_ThrowTypeError(ctx, "not an object"); @@ -7117,15 +7313,19 @@ static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); } +static void JS_ThrowInterrupted(JSContext *ctx) +{ + JS_ThrowInternalError(ctx, "interrupted"); + JS_SetUncatchableError(ctx, ctx->rt->current_exception); +} + static no_inline __exception int __js_poll_interrupts(JSContext *ctx) { JSRuntime *rt = ctx->rt; ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; if (rt->interrupt_handler) { if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { - /* XXX: should set a specific flag to avoid catching */ - JS_ThrowInternalError(ctx, "interrupted"); - js_set_uncatchable_error(ctx, ctx->rt->current_exception, true); + JS_ThrowInterrupted(ctx); return -1; } } @@ -7142,9 +7342,8 @@ static inline __exception int js_poll_interrupts(JSContext *ctx) } /* return -1 (exception) or true/false */ -static int JS_SetPrototypeInternal(JSContext *ctx, JSValue obj, - JSValue proto_val, - bool throw_flag) +static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj, + JSValueConst proto_val, bool throw_flag) { JSObject *proto, *p, *p1; JSShape *sh; @@ -7213,7 +7412,6 @@ static int JS_SetPrototypeInternal(JSContext *ctx, JSValue obj, if (js_shape_prepare_update(ctx, p, NULL)) return -1; sh = p->shape; - ic_delete_shape_proto_watchpoints(ctx->rt, sh, JS_ATOM_NULL, true); if (sh->proto) JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); sh->proto = proto; @@ -7221,43 +7419,45 @@ static int JS_SetPrototypeInternal(JSContext *ctx, JSValue obj, } /* return -1 (exception) or true/false */ -int JS_SetPrototype(JSContext *ctx, JSValue obj, JSValue proto_val) +int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val) { return JS_SetPrototypeInternal(ctx, obj, proto_val, true); } /* Only works for primitive types, otherwise return JS_NULL. */ -static JSValue JS_GetPrototypePrimitive(JSContext *ctx, JSValue val) +static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val) { + JSValue ret; switch(JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: - val = ctx->class_proto[JS_CLASS_BIG_INT]; + ret = ctx->class_proto[JS_CLASS_BIG_INT]; break; case JS_TAG_INT: case JS_TAG_FLOAT64: - val = ctx->class_proto[JS_CLASS_NUMBER]; + ret = ctx->class_proto[JS_CLASS_NUMBER]; break; case JS_TAG_BOOL: - val = ctx->class_proto[JS_CLASS_BOOLEAN]; + ret = ctx->class_proto[JS_CLASS_BOOLEAN]; break; case JS_TAG_STRING: - val = ctx->class_proto[JS_CLASS_STRING]; + ret = ctx->class_proto[JS_CLASS_STRING]; break; case JS_TAG_SYMBOL: - val = ctx->class_proto[JS_CLASS_SYMBOL]; + ret = ctx->class_proto[JS_CLASS_SYMBOL]; break; case JS_TAG_OBJECT: case JS_TAG_NULL: case JS_TAG_UNDEFINED: default: - val = JS_NULL; + ret = JS_NULL; break; } - return val; + return ret; } /* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */ -JSValue JS_GetPrototype(JSContext *ctx, JSValue obj) +JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj) { JSValue val; if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { @@ -7286,17 +7486,17 @@ static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj) return obj1; } -int JS_GetLength(JSContext *ctx, JSValue obj, int64_t *pres) { +int JS_GetLength(JSContext *ctx, JSValueConst obj, int64_t *pres) { return js_get_length64(ctx, pres, obj); } -int JS_SetLength(JSContext *ctx, JSValue obj, int64_t len) { +int JS_SetLength(JSContext *ctx, JSValueConst obj, int64_t len) { return js_set_length64(ctx, obj, len); } /* return true, false or (-1) in case of exception */ -static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValue val, - JSValue obj) +static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, + JSValueConst obj) { JSValue obj_proto; JSObject *proto; @@ -7369,7 +7569,7 @@ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValue val, } /* return true, false or (-1) in case of exception */ -int JS_IsInstanceOf(JSContext *ctx, JSValue val, JSValue obj) +int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) { JSValue method; @@ -7393,13 +7593,60 @@ int JS_IsInstanceOf(JSContext *ctx, JSValue val, JSValue obj) return JS_OrdinaryIsInstanceOf(ctx, val, obj); } +#include "builtin-array-fromasync.h" + +static JSValue js_bytecode_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, + void *opaque) +{ + switch ((uintptr_t)opaque) { + default: + abort(); + case JS_BUILTIN_ARRAY_FROMASYNC: + { + JSValue obj = JS_ReadObject(ctx, qjsc_builtin_array_fromasync, + sizeof(qjsc_builtin_array_fromasync), + JS_READ_OBJ_BYTECODE); + if (JS_IsException(obj)) + return JS_EXCEPTION; + JSValue fun = JS_EvalFunction(ctx, obj); + if (JS_IsException(fun)) + return JS_EXCEPTION; + assert(JS_IsFunction(ctx, fun)); + JSValue args[] = { + JS_NewCFunction(ctx, js_array_constructor, "Array", 0), + JS_NewCFunctionMagic(ctx, js_error_constructor, "TypeError", 1, + JS_CFUNC_constructor_or_func_magic, + JS_TYPE_ERROR), + JS_AtomToValue(ctx, JS_ATOM_Symbol_asyncIterator), + JS_NewCFunctionMagic(ctx, js_object_defineProperty, + "Object.defineProperty", 3, + JS_CFUNC_generic_magic, 0), + JS_AtomToValue(ctx, JS_ATOM_Symbol_iterator), + }; + JSValue result = JS_Call(ctx, fun, JS_UNDEFINED, + countof(args), vc(args)); + for (size_t i = 0; i < countof(args); i++) + JS_FreeValue(ctx, args[i]); + JS_FreeValue(ctx, fun); + if (JS_SetPrototypeInternal(ctx, result, ctx->function_proto, + /*throw_flag*/true) < 0) { + JS_FreeValue(ctx, result); + return JS_EXCEPTION; + } + return result; + } + } + return JS_UNDEFINED; +} + /* return the value associated to the autoinit property or an exception */ typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); -static JSAutoInitFunc *js_autoinit_func_table[] = { +static JSAutoInitFunc *const js_autoinit_func_table[] = { js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */ js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */ JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ + js_bytecode_autoinit, /* JS_AUTOINIT_ID_BYTECODE */ }; /* warning: 'prs' is reallocated after it */ @@ -7426,17 +7673,15 @@ static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, return 0; } -static JSValue JS_GetPropertyInternal2(JSContext *ctx, JSValue obj, - JSAtom prop, JSValue this_obj, - JSInlineCacheUpdate *icu, - bool throw_ref_error) +static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValueConst this_obj, + bool throw_ref_error) { - JSObject *p, *p1; + JSObject *p; JSProperty *pr; JSShapeProperty *prs; - uint32_t tag, offset, proto_depth; + uint32_t tag; - offset = proto_depth = 0; tag = JS_VALUE_GET_TAG(obj); if (unlikely(tag != JS_TAG_OBJECT)) { switch(tag) { @@ -7472,9 +7717,8 @@ static JSValue JS_GetPropertyInternal2(JSContext *ctx, JSValue obj, p = JS_VALUE_GET_OBJ(obj); } - p1 = p; for(;;) { - prs = find_own_property_ic(&pr, p, prop, &offset); + prs = find_own_property(&pr, p, prop); if (prs) { /* found */ if (unlikely(prs->flags & JS_PROP_TMASK)) { @@ -7499,7 +7743,6 @@ static JSValue JS_GetPropertyInternal2(JSContext *ctx, JSValue obj, continue; } } else { - add_ic_slot(ctx, icu, prop, p1, offset, proto_depth > 0 ? p : NULL); return js_dup(pr->u.value); } } @@ -7560,7 +7803,6 @@ static JSValue JS_GetPropertyInternal2(JSContext *ctx, JSValue obj, } } } - proto_depth++; p = p->shape->proto; if (!p) break; @@ -7572,36 +7814,9 @@ static JSValue JS_GetPropertyInternal2(JSContext *ctx, JSValue obj, } } -static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValue obj, - JSAtom prop, JSValue this_obj, - bool throw_ref_error) -{ - return JS_GetPropertyInternal2(ctx, obj, prop, this_obj, NULL, throw_ref_error); -} - -JSValue JS_GetProperty(JSContext *ctx, JSValue this_obj, JSAtom prop) +JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop) { - return JS_GetPropertyInternal2(ctx, this_obj, prop, this_obj, NULL, false); -} - -static inline JSValue JS_GetPropertyInternalWithIC(JSContext *ctx, JSValue obj, - JSAtom prop, JSValue this_obj, - JSInlineCacheUpdate *icu, - bool throw_ref_error) -{ - uint32_t tag, offset; - JSObject *p, *proto; - tag = JS_VALUE_GET_TAG(obj); - if (unlikely(tag != JS_TAG_OBJECT)) - goto slow_path; - p = JS_VALUE_GET_OBJ(obj); - offset = get_ic_prop_offset(icu, p->shape, &proto); - if (likely(offset != INLINE_CACHE_MISS)) { - if (proto) p = proto; - return js_dup(p->prop[offset].u.value); - } -slow_path: - return JS_GetPropertyInternal2(ctx, obj, prop, this_obj, icu, throw_ref_error); + return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, false); } static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) @@ -7612,7 +7827,7 @@ static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) /* Private fields can be added even on non extensible objects or Proxies */ -static int JS_DefinePrivateField(JSContext *ctx, JSValue obj, +static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, JSValue name, JSValue val) { JSObject *p; @@ -7647,8 +7862,8 @@ static int JS_DefinePrivateField(JSContext *ctx, JSValue obj, return 0; } -static JSValue JS_GetPrivateField(JSContext *ctx, JSValue obj, - JSValue name) +static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name) { JSObject *p; JSShapeProperty *prs; @@ -7670,8 +7885,8 @@ static JSValue JS_GetPrivateField(JSContext *ctx, JSValue obj, return js_dup(pr->u.value); } -static int JS_SetPrivateField(JSContext *ctx, JSValue obj, - JSValue name, JSValue val) +static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name, JSValue val) { JSObject *p; JSShapeProperty *prs; @@ -7702,7 +7917,7 @@ static int JS_SetPrivateField(JSContext *ctx, JSValue obj, /* add a private brand field to 'home_obj' if not already present and if obj is != null add a private brand to it */ -static int JS_AddBrand(JSContext *ctx, JSValue obj, JSValue home_obj) +static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) { JSObject *p, *p1; JSShapeProperty *prs; @@ -7791,8 +8006,7 @@ static int JS_CheckBrand(JSContext *ctx, JSValue obj, JSValue func) return (prs != NULL); } -static uint32_t js_string_obj_get_length(JSContext *ctx, - JSValue obj) +static uint32_t js_string_obj_get_length(JSContext *ctx, JSValueConst obj) { JSObject *p; JSString *p1; @@ -8030,7 +8244,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, } int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, - uint32_t *plen, JSValue obj, int flags) + uint32_t *plen, JSValueConst obj, int flags) { if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { JS_ThrowTypeErrorNotAnObject(ctx); @@ -8122,7 +8336,7 @@ static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, } int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, - JSValue obj, JSAtom prop) + JSValueConst obj, JSAtom prop) { if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { JS_ThrowTypeErrorNotAnObject(ctx); @@ -8138,7 +8352,7 @@ void JS_FreePropertyEnum(JSContext *ctx, JSPropertyEnum *tab, } /* return -1 if exception (Proxy object only) or true/false */ -int JS_IsExtensible(JSContext *ctx, JSValue obj) +int JS_IsExtensible(JSContext *ctx, JSValueConst obj) { JSObject *p; @@ -8152,7 +8366,7 @@ int JS_IsExtensible(JSContext *ctx, JSValue obj) } /* return -1 if exception (Proxy object only) or true/false */ -int JS_PreventExtensions(JSContext *ctx, JSValue obj) +int JS_PreventExtensions(JSContext *ctx, JSValueConst obj) { JSObject *p; @@ -8166,7 +8380,7 @@ int JS_PreventExtensions(JSContext *ctx, JSValue obj) } /* return -1 if exception otherwise true or false */ -int JS_HasProperty(JSContext *ctx, JSValue obj, JSAtom prop) +int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) { JSObject *p; int ret; @@ -8208,14 +8422,15 @@ int JS_HasProperty(JSContext *ctx, JSValue obj, JSAtom prop) } /* val must be a symbol */ -static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val) +static JSAtom js_symbol_to_atom(JSContext *ctx, JSValueConst val) { JSAtomStruct *p = JS_VALUE_GET_PTR(val); return js_get_atom_index(ctx->rt, p); } /* return JS_ATOM_NULL in case of exception */ -JSAtom JS_ValueToAtom(JSContext *ctx, JSValue val) +static JSAtom JS_ValueToAtomInternal(JSContext *ctx, JSValueConst val, + int flags) { JSAtom atom; uint32_t tag; @@ -8229,7 +8444,7 @@ JSAtom JS_ValueToAtom(JSContext *ctx, JSValue val) atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p)); } else { JSValue str; - str = JS_ToPropertyKey(ctx, val); + str = JS_ToPropertyKeyInternal(ctx, val, flags); if (JS_IsException(str)) return JS_ATOM_NULL; if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) { @@ -8241,6 +8456,11 @@ JSAtom JS_ValueToAtom(JSContext *ctx, JSValue val) return atom; } +JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val) +{ + return JS_ValueToAtomInternal(ctx, val, /*flags*/0); +} + static bool js_get_fast_array_element(JSContext *ctx, JSObject *p, uint32_t idx, JSValue *pval) { @@ -8300,7 +8520,7 @@ static bool js_get_fast_array_element(JSContext *ctx, JSObject *p, } } -static JSValue JS_GetPropertyValue(JSContext *ctx, JSValue this_obj, +static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, JSValue prop) { JSAtom atom; @@ -8317,15 +8537,21 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValue this_obj, if (js_get_fast_array_element(ctx, p, idx, &val)) return val; } - } else { - switch(tag) { - case JS_TAG_NULL: - JS_FreeValue(ctx, prop); - return JS_ThrowTypeError(ctx, "cannot read property of null"); - case JS_TAG_UNDEFINED: - JS_FreeValue(ctx, prop); - return JS_ThrowTypeError(ctx, "cannot read property of undefined"); + } else if (unlikely(tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)) { + // per spec: not allowed to call ToPropertyKey before ToObject + // so we must ensure to not invoke JS anything that's observable + // from JS code + atom = JS_ValueToAtomInternal(ctx, prop, JS_TO_STRING_NO_SIDE_EFFECTS); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + if (tag == JS_TAG_NULL) { + JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", atom); + } else { + JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", atom); } + JS_FreeAtom(ctx, atom); + return JS_EXCEPTION; } atom = JS_ValueToAtom(ctx, prop); JS_FreeValue(ctx, prop); @@ -8336,7 +8562,7 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValue this_obj, return ret; } -JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue this_obj, +JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, uint32_t idx) { return JS_GetPropertyInt64(ctx, this_obj, idx); @@ -8347,7 +8573,7 @@ JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue this_obj, true if property exists, stored into *pval, false if property does not exist. *pval set to JS_UNDEFINED. */ -static int JS_TryGetPropertyInt64(JSContext *ctx, JSValue obj, int64_t idx, JSValue *pval) +static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval) { JSValue val; JSAtom prop; @@ -8378,7 +8604,7 @@ static int JS_TryGetPropertyInt64(JSContext *ctx, JSValue obj, int64_t idx, JSVa return present; } -JSValue JS_GetPropertyInt64(JSContext *ctx, JSValue obj, int64_t idx) +JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx) { JSAtom prop; JSValue val; @@ -8400,12 +8626,14 @@ JSValue JS_GetPropertyInt64(JSContext *ctx, JSValue obj, int64_t idx) } /* `prop` may be pure ASCII or UTF-8 encoded */ -JSValue JS_GetPropertyStr(JSContext *ctx, JSValue this_obj, +JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, const char *prop) { JSAtom atom; JSValue ret; atom = JS_NewAtom(ctx, prop); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; ret = JS_GetProperty(ctx, this_obj, atom); JS_FreeAtom(ctx, atom); return ret; @@ -8533,7 +8761,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) pr->flags = 0; pr->atom = JS_ATOM_NULL; pr1->u.value = JS_UNDEFINED; - ic_delete_shape_proto_watchpoints(ctx->rt, sh, atom, false); + /* compact the properties if too many deleted properties */ if (sh->deleted_prop_count >= 8 && sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { @@ -8577,14 +8805,14 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) } static int call_setter(JSContext *ctx, JSObject *setter, - JSValue this_obj, JSValue val, int flags) + JSValueConst this_obj, JSValue val, int flags) { JSValue ret, func; if (likely(setter)) { func = JS_MKPTR(JS_TAG_OBJECT, setter); /* Note: the field could be removed in the setter */ func = js_dup(func); - ret = JS_CallFree(ctx, func, this_obj, 1, &val); + ret = JS_CallFree(ctx, func, this_obj, 1, vc(&val)); JS_FreeValue(ctx, val); if (JS_IsException(ret)) return -1; @@ -8754,16 +8982,14 @@ static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) the new property is not added and an error is raised. 'obj' must be an object when obj != this_obj. */ -static int JS_SetPropertyInternal2(JSContext *ctx, JSValue obj, JSAtom prop, - JSValue val, JSValue this_obj, int flags, - JSInlineCacheUpdate *icu) +static int JS_SetPropertyInternal2(JSContext *ctx, JSValueConst obj, JSAtom prop, + JSValue val, JSValueConst this_obj, int flags) { JSObject *p, *p1; JSShapeProperty *prs; JSProperty *pr; JSPropertyDescriptor desc; int ret; - uint32_t offset = 0; switch(JS_VALUE_GET_TAG(this_obj)) { case JS_TAG_NULL: @@ -8787,12 +9013,11 @@ static int JS_SetPropertyInternal2(JSContext *ctx, JSValue obj, JSAtom prop, } retry: - prs = find_own_property_ic(&pr, p1, prop, &offset); + prs = find_own_property(&pr, p1, prop); if (prs) { if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { /* fast case */ - add_ic_slot(ctx, icu, prop, p, offset, NULL); set_value(ctx, &pr->u.value, val); return true; } else if (prs->flags & JS_PROP_LENGTH) { @@ -8921,6 +9146,8 @@ static int JS_SetPropertyInternal2(JSContext *ctx, JSValue obj, JSAtom prop, goto retry2; } else if (!(prs->flags & JS_PROP_WRITABLE)) { goto read_only_prop; + } else { + break; } } } @@ -8952,13 +9179,10 @@ static int JS_SetPropertyInternal2(JSContext *ctx, JSValue obj, JSAtom prop, } goto generic_create_prop; } else { - ic_delete_shape_proto_watchpoints(ctx->rt, p->shape, prop, false); pr = add_property(ctx, p, prop, JS_PROP_C_W_E); - if (unlikely(!pr)) + if (!pr) goto fail; pr->u.value = val; - /* fast case */ - add_ic_slot(ctx, icu, prop, p, p->shape->prop_count - 1, NULL); return true; } } @@ -9005,41 +9229,19 @@ static int JS_SetPropertyInternal2(JSContext *ctx, JSValue obj, JSAtom prop, return -1; } -static int JS_SetPropertyInternal(JSContext *ctx, JSValue this_obj, - JSAtom prop, JSValue val, int flags) +static int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, JSAtom prop, + JSValue val, int flags) { - return JS_SetPropertyInternal2(ctx, this_obj, prop, val, this_obj, - flags, NULL); + return JS_SetPropertyInternal2(ctx, obj, prop, val, obj, flags); } -int JS_SetProperty(JSContext *ctx, JSValue this_obj, JSAtom prop, JSValue val) +int JS_SetProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue val) { - return JS_SetPropertyInternal2(ctx, this_obj, prop, val, this_obj, JS_PROP_THROW, NULL); -} - -// XXX(bnoordhuis) only used by OP_put_field_ic, maybe inline at call site -static inline int JS_SetPropertyInternalWithIC(JSContext *ctx, JSValue this_obj, - JSAtom prop, JSValue val, int flags, - JSInlineCacheUpdate *icu) { - uint32_t tag, offset; - JSObject *p, *proto; - tag = JS_VALUE_GET_TAG(this_obj); - if (unlikely(tag != JS_TAG_OBJECT)) - goto slow_path; - p = JS_VALUE_GET_OBJ(this_obj); - offset = get_ic_prop_offset(icu, p->shape, &proto); - if (likely(offset != INLINE_CACHE_MISS)) { - if (proto) goto slow_path; - set_value(ctx, &p->prop[offset].u.value, val); - return true; - } -slow_path: - return JS_SetPropertyInternal2(ctx, this_obj, prop, val, this_obj, - flags, icu); + return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW); } /* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ -static int JS_SetPropertyValue(JSContext *ctx, JSValue this_obj, +static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags) { if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && @@ -9184,14 +9386,14 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValue this_obj, } } -int JS_SetPropertyUint32(JSContext *ctx, JSValue this_obj, +int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, uint32_t idx, JSValue val) { return JS_SetPropertyValue(ctx, this_obj, js_uint32(idx), val, JS_PROP_THROW); } -int JS_SetPropertyInt64(JSContext *ctx, JSValue this_obj, +int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, int64_t idx, JSValue val) { JSAtom prop; @@ -9213,12 +9415,16 @@ int JS_SetPropertyInt64(JSContext *ctx, JSValue this_obj, } /* `prop` may be pure ASCII or UTF-8 encoded */ -int JS_SetPropertyStr(JSContext *ctx, JSValue this_obj, +int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, const char *prop, JSValue val) { JSAtom atom; int ret; atom = JS_NewAtom(ctx, prop); + if (atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW); JS_FreeAtom(ctx, atom); return ret; @@ -9236,8 +9442,8 @@ static int get_prop_flags(int flags, int def_flags) } static int JS_CreateProperty(JSContext *ctx, JSObject *p, - JSAtom prop, JSValue val, - JSValue getter, JSValue setter, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, int flags) { JSProperty *pr; @@ -9406,44 +9612,6 @@ static int js_shape_prepare_update(JSContext *ctx, JSObject *p, return 0; } -int js_shape_delete_watchpoints(JSRuntime *rt, JSShape *shape, JSInlineCacheRingSlot *target) { - struct list_head *el, *el1; - if (unlikely(!shape || !shape->watchpoint)) - goto end; - list_for_each_safe(el, el1, shape->watchpoint) { - JSInlineCacheWatchpoint *o = list_entry(el, JSInlineCacheWatchpoint, link); - if (!ic_watchpoint_delete_handler(rt, o->ref, o->offset, o->atom, target)) { - list_del(el); - js_free_rt(rt, o); - break; - } - } - if (!target) { - list_empty(shape->watchpoint); - js_free_rt(rt, shape->watchpoint); - } -end: - return 0; -} - -JSInlineCacheWatchpoint *js_shape_create_watchpoint(JSRuntime *rt, JSShape *shape, JSInlineCacheRingSlot *ptr, uint8_t offset, JSAtom atom) -{ - JSInlineCacheWatchpoint *o; - o = js_malloc_rt(rt, sizeof(*o)); - if (unlikely(!o)) - return NULL; - o->ref = ptr; - o->atom = atom; - o->offset = offset; - if (!shape->watchpoint) { - shape->watchpoint = js_malloc_rt(rt, sizeof(*shape->watchpoint)); - init_list_head(shape->watchpoint); - } - init_list_head(&o->link); - list_add_tail(&o->link, shape->watchpoint); - return o; -} - static int js_update_property_flags(JSContext *ctx, JSObject *p, JSShapeProperty **pprs, int flags) { @@ -9465,9 +9633,9 @@ static int js_update_property_flags(JSContext *ctx, JSObject *p, define_own_property callback. return -1 (exception), false or true. */ -int JS_DefineProperty(JSContext *ctx, JSValue this_obj, - JSAtom prop, JSValue val, - JSValue getter, JSValue setter, int flags) +int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, int flags) { JSObject *p; JSShapeProperty *prs; @@ -9598,8 +9766,7 @@ int JS_DefineProperty(JSContext *ctx, JSValue this_obj, goto not_configurable; } /* update the reference */ - set_value(ctx, pr->u.var_ref->pvalue, - js_dup(val)); + set_value(ctx, pr->u.var_ref->pvalue, js_dup(val)); } /* if writable is set to false, no longer a reference (for mapped arguments) */ @@ -9616,8 +9783,7 @@ int JS_DefineProperty(JSContext *ctx, JSValue this_obj, if (flags & JS_PROP_HAS_VALUE) { /* Note: no JS code is executable because 'val' is guaranted to be a Uint32 */ - res = set_array_length(ctx, p, js_dup(val), - flags); + res = set_array_length(ctx, p, js_dup(val), flags); } else { res = true; } @@ -9712,7 +9878,7 @@ int JS_DefineProperty(JSContext *ctx, JSValue this_obj, } idx = __JS_AtomToUInt32(prop); /* if the typed array is detached, p->u.array.count = 0 */ - if (idx >= typed_array_get_length(ctx, p)) { + if (idx >= p->u.array.count) { typed_array_oob: return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array"); } @@ -9732,7 +9898,7 @@ int JS_DefineProperty(JSContext *ctx, JSValue this_obj, return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags); } -static int JS_DefineAutoInitProperty(JSContext *ctx, JSValue this_obj, +static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSAutoInitIDEnum id, void *opaque, int flags) { @@ -9763,7 +9929,7 @@ static int JS_DefineAutoInitProperty(JSContext *ctx, JSValue this_obj, } /* shortcut to add or redefine a new property value */ -int JS_DefinePropertyValue(JSContext *ctx, JSValue this_obj, +int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue val, int flags) { int ret; @@ -9773,7 +9939,7 @@ int JS_DefinePropertyValue(JSContext *ctx, JSValue this_obj, return ret; } -int JS_DefinePropertyValueValue(JSContext *ctx, JSValue this_obj, +int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags) { JSAtom atom; @@ -9789,14 +9955,14 @@ int JS_DefinePropertyValueValue(JSContext *ctx, JSValue this_obj, return ret; } -int JS_DefinePropertyValueUint32(JSContext *ctx, JSValue this_obj, +int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj, uint32_t idx, JSValue val, int flags) { return JS_DefinePropertyValueValue(ctx, this_obj, js_uint32(idx), val, flags); } -int JS_DefinePropertyValueInt64(JSContext *ctx, JSValue this_obj, +int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj, int64_t idx, JSValue val, int flags) { return JS_DefinePropertyValueValue(ctx, this_obj, js_int64(idx), @@ -9804,19 +9970,23 @@ int JS_DefinePropertyValueInt64(JSContext *ctx, JSValue this_obj, } /* `prop` may be pure ASCII or UTF-8 encoded */ -int JS_DefinePropertyValueStr(JSContext *ctx, JSValue this_obj, +int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, const char *prop, JSValue val, int flags) { JSAtom atom; int ret; atom = JS_NewAtom(ctx, prop); + if (atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); JS_FreeAtom(ctx, atom); return ret; } /* shortcut to add getter & setter */ -int JS_DefinePropertyGetSet(JSContext *ctx, JSValue this_obj, +int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue getter, JSValue setter, int flags) { @@ -9829,7 +9999,7 @@ int JS_DefinePropertyGetSet(JSContext *ctx, JSValue this_obj, return ret; } -static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValue this_obj, +static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj, int64_t idx, JSValue val, int flags) { return JS_DefinePropertyValueValue(ctx, this_obj, js_int64(idx), @@ -10110,10 +10280,33 @@ static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags); } +/* return -1, false or true */ +static int JS_DeleteGlobalVar(JSContext *ctx, JSAtom prop) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int ret; + + /* 9.1.1.4.7 DeleteBinding ( N ) */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) + return false; /* lexical variables cannot be deleted */ + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + if (ret) { + return JS_DeleteProperty(ctx, ctx->global_obj, prop, 0); + } else { + return true; + } +} + /* return -1, false or true. return false if not configurable or invalid object. return -1 in case of exception. flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */ -int JS_DeleteProperty(JSContext *ctx, JSValue obj, JSAtom prop, int flags) +int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags) { JSValue obj1; JSObject *p; @@ -10135,7 +10328,7 @@ int JS_DeleteProperty(JSContext *ctx, JSValue obj, JSAtom prop, int flags) return false; } -int JS_DeletePropertyInt64(JSContext *ctx, JSValue obj, int64_t idx, int flags) +int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags) { JSAtom prop; int res; @@ -10152,7 +10345,7 @@ int JS_DeletePropertyInt64(JSContext *ctx, JSValue obj, int64_t idx, int flags) return res; } -bool JS_IsFunction(JSContext *ctx, JSValue val) +bool JS_IsFunction(JSContext *ctx, JSValueConst val) { JSObject *p; if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) @@ -10168,7 +10361,8 @@ bool JS_IsFunction(JSContext *ctx, JSValue val) } } -bool JS_IsCFunction(JSContext *ctx, JSValue val, JSCFunction *func, int magic) +static bool JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, + int magic) { JSObject *p; if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) @@ -10180,7 +10374,7 @@ bool JS_IsCFunction(JSContext *ctx, JSValue val, JSCFunction *func, int magic) return false; } -bool JS_IsConstructor(JSContext *ctx, JSValue val) +bool JS_IsConstructor(JSContext *ctx, JSValueConst val) { JSObject *p; if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) @@ -10189,7 +10383,7 @@ bool JS_IsConstructor(JSContext *ctx, JSValue val) return p->is_constructor; } -bool JS_SetConstructorBit(JSContext *ctx, JSValue func_obj, bool val) +bool JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, bool val) { JSObject *p; if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) @@ -10199,31 +10393,48 @@ bool JS_SetConstructorBit(JSContext *ctx, JSValue func_obj, bool val) return true; } -bool JS_IsRegExp(JSValue val) +bool JS_IsRegExp(JSValueConst val) { - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return false; - return JS_VALUE_GET_OBJ(val)->class_id == JS_CLASS_REGEXP; + return JS_CLASS_REGEXP == JS_GetClassID(val); } -bool JS_IsMap(JSValue val) +bool JS_IsMap(JSValueConst val) { - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return false; - return JS_VALUE_GET_OBJ(val)->class_id == JS_CLASS_MAP; + return JS_CLASS_MAP == JS_GetClassID(val); } -bool JS_IsError(JSContext *ctx, JSValue val) +bool JS_IsSet(JSValueConst val) { - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return false; - p = JS_VALUE_GET_OBJ(val); - return (p->class_id == JS_CLASS_ERROR); + return JS_CLASS_SET == JS_GetClassID(val); +} + +bool JS_IsWeakRef(JSValueConst val) +{ + return JS_CLASS_WEAK_REF == JS_GetClassID(val); +} + +bool JS_IsWeakSet(JSValueConst val) +{ + return JS_CLASS_WEAKSET == JS_GetClassID(val); +} + +bool JS_IsWeakMap(JSValueConst val) +{ + return JS_CLASS_WEAKMAP == JS_GetClassID(val); +} + +bool JS_IsDataView(JSValueConst val) +{ + return JS_CLASS_DATAVIEW == JS_GetClassID(val); +} + +bool JS_IsError(JSValueConst val) +{ + return JS_CLASS_ERROR == JS_GetClassID(val); } /* used to avoid catching interrupt exceptions */ -bool JS_IsUncatchableError(JSContext *ctx, JSValue val) +bool JS_IsUncatchableError(JSValueConst val) { JSObject *p; if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) @@ -10232,7 +10443,7 @@ bool JS_IsUncatchableError(JSContext *ctx, JSValue val) return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error; } -static void js_set_uncatchable_error(JSContext *ctx, JSValue val, bool flag) +static void js_set_uncatchable_error(JSContext *ctx, JSValueConst val, bool flag) { JSObject *p; if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) @@ -10242,12 +10453,12 @@ static void js_set_uncatchable_error(JSContext *ctx, JSValue val, bool flag) p->is_uncatchable_error = flag; } -void JS_SetUncatchableError(JSContext *ctx, JSValue val) +void JS_SetUncatchableError(JSContext *ctx, JSValueConst val) { js_set_uncatchable_error(ctx, val, true); } -void JS_ClearUncatchableError(JSContext *ctx, JSValue val) +void JS_ClearUncatchableError(JSContext *ctx, JSValueConst val) { js_set_uncatchable_error(ctx, val, false); } @@ -10257,13 +10468,13 @@ void JS_ResetUncatchableError(JSContext *ctx) js_set_uncatchable_error(ctx, ctx->rt->current_exception, false); } -int JS_SetOpaque(JSValue obj, void *opaque) +int JS_SetOpaque(JSValueConst obj, void *opaque) { JSObject *p; if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { p = JS_VALUE_GET_OBJ(obj); // User code can't set the opaque of internal objects. - if (p->class_id >= JS_CLASS_INIT_COUNT || p->class_id == JS_CLASS_OBJECT) { + if (p->class_id >= JS_CLASS_INIT_COUNT) { p->u.opaque = opaque; return 0; } @@ -10273,7 +10484,7 @@ int JS_SetOpaque(JSValue obj, void *opaque) } /* |obj| must be a JSObject of an internal class. */ -static void JS_SetOpaqueInternal(JSValue obj, void *opaque) +static void JS_SetOpaqueInternal(JSValueConst obj, void *opaque) { JSObject *p; assert(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT); @@ -10283,7 +10494,7 @@ static void JS_SetOpaqueInternal(JSValue obj, void *opaque) } /* return NULL if not an object of class class_id */ -void *JS_GetOpaque(JSValue obj, JSClassID class_id) +void *JS_GetOpaque(JSValueConst obj, JSClassID class_id) { JSObject *p; if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) @@ -10294,7 +10505,7 @@ void *JS_GetOpaque(JSValue obj, JSClassID class_id) return p->u.opaque; } -void *JS_GetOpaque2(JSContext *ctx, JSValue obj, JSClassID class_id) +void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id) { void *p = JS_GetOpaque(obj, class_id); if (unlikely(!p)) { @@ -10303,7 +10514,7 @@ void *JS_GetOpaque2(JSContext *ctx, JSValue obj, JSClassID class_id) return p; } -void *JS_GetAnyOpaque(JSValue obj, JSClassID *class_id) +void *JS_GetAnyOpaque(JSValueConst obj, JSClassID *class_id) { JSObject *p; if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { @@ -10348,7 +10559,7 @@ static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) break; } arg = JS_AtomToString(ctx, atom); - ret = JS_CallFree(ctx, method, val, 1, &arg); + ret = JS_CallFree(ctx, method, val, 1, vc(&arg)); JS_FreeValue(ctx, arg); if (JS_IsException(ret)) goto exception; @@ -10389,12 +10600,12 @@ static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) return JS_EXCEPTION; } -static JSValue JS_ToPrimitive(JSContext *ctx, JSValue val, int hint) +static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint) { return JS_ToPrimitiveFree(ctx, js_dup(val), hint); } -void JS_SetIsHTMLDDA(JSContext *ctx, JSValue obj) +void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj) { JSObject *p; if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) @@ -10403,7 +10614,7 @@ void JS_SetIsHTMLDDA(JSContext *ctx, JSValue obj) p->is_HTMLDDA = true; } -static inline bool JS_IsHTMLDDA(JSContext *ctx, JSValue obj) +static inline bool JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj) { JSObject *p; if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) @@ -10430,11 +10641,24 @@ static int JS_ToBoolFree(JSContext *ctx, JSValue val) JS_FreeValue(ctx, val); return ret; } + case JS_TAG_SHORT_BIG_INT: + return JS_VALUE_GET_SHORT_BIG_INT(val) != 0; case JS_TAG_BIG_INT: { JSBigInt *p = JS_VALUE_GET_PTR(val); bool ret; - ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; + int i; + + /* fail safe: we assume it is not necessarily + normalized. Beginning from the MSB ensures that the + test is fast. */ + ret = false; + for(i = p->len - 1; i >= 0; i--) { + if (p->tab[i] != 0) { + ret = true; + break; + } + } JS_FreeValue(ctx, val); return ret; } @@ -10457,7 +10681,7 @@ static int JS_ToBoolFree(JSContext *ctx, JSValue val) } } -int JS_ToBool(JSContext *ctx, JSValue val) +int JS_ToBool(JSContext *ctx, JSValueConst val) { return JS_ToBoolFree(ctx, js_dup(val)); } @@ -10485,7 +10709,7 @@ static int skip_spaces(const char *pc) return p - 1 - p_start; } -static inline int to_digit(int c) +static inline int js_to_digit(int c) { if (c >= '0' && c <= '9') return c - '0'; @@ -10497,200 +10721,1544 @@ static inline int to_digit(int c) return 36; } -/* XXX: remove */ -static double js_strtod(const char *str, int radix, bool is_float) +/* bigint support */ + +#define ADDC(res, carry_out, op1, op2, carry_in) \ +do { \ + js_limb_t __v, __a, __k, __k1; \ + __v = (op1); \ + __a = __v + (op2); \ + __k1 = __a < __v; \ + __k = (carry_in); \ + __a = __a + __k; \ + carry_out = (__a < __k) | __k1; \ + res = __a; \ +} while (0) + +/* a != 0 */ +static inline js_limb_t js_limb_clz(js_limb_t a) { - double d; - int c; + if (!a) + return JS_LIMB_BITS; + return clz32(a); +} + +static js_limb_t js_mp_add(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2, + js_limb_t n, js_limb_t carry) +{ + int i; + for(i = 0;i < n; i++) { + ADDC(res[i], carry, op1[i], op2[i], carry); + } + return carry; +} - if (!is_float || radix != 10) { - const char *p = str; - uint64_t n_max, n; - int int_exp, is_neg; +static js_limb_t js_mp_sub(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2, + int n, js_limb_t carry) +{ + int i; + js_limb_t k, a, v, k1; - is_neg = 0; - if (*p == '-') { - is_neg = 1; - p++; + k = carry; + for(i=0;i v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} + +/* compute 0 - op2. carry = 0 or 1. */ +static js_limb_t js_mp_neg(js_limb_t *res, const js_limb_t *op2, int n) +{ + int i; + js_limb_t v, carry; + + carry = 1; + for(i=0;i> JS_LIMB_BITS; + } + return l; +} + +static js_limb_t js_mp_div1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b, js_limb_t r) +{ + js_slimb_t i; + js_dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; + } + return r; +} + +/* tabr[] += taba[] * b, return the high word. */ +static js_limb_t js_mp_add_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b) +{ + js_limb_t i, l; + js_dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l + tabr[i]; + tabr[i] = t; + l = t >> JS_LIMB_BITS; + } + return l; +} + +/* size of the result : op1_size + op2_size. */ +static void js_mp_mul_basecase(js_limb_t *result, + const js_limb_t *op1, js_limb_t op1_size, + const js_limb_t *op2, js_limb_t op2_size) +{ + int i; + js_limb_t r; + + result[op1_size] = js_mp_mul1(result, op1, op1_size, op2[0], 0); + for(i=1;i> JS_LIMB_BITS); + } + return l; +} + +/* WARNING: d must be >= 2^(JS_LIMB_BITS-1) */ +static inline js_limb_t js_udiv1norm_init(js_limb_t d) +{ + js_limb_t a0, a1; + a1 = -d - 1; + a0 = -1; + return (((js_dlimb_t)a1 << JS_LIMB_BITS) | a0) / d; +} + +/* return the quotient and the remainder in '*pr'of 'a1*2^JS_LIMB_BITS+a0 + / d' with 0 <= a1 < d. */ +static inline js_limb_t js_udiv1norm(js_limb_t *pr, js_limb_t a1, js_limb_t a0, + js_limb_t d, js_limb_t d_inv) +{ + js_limb_t n1m, n_adj, q, r, ah; + js_dlimb_t a; + n1m = ((js_slimb_t)a0 >> (JS_LIMB_BITS - 1)); + n_adj = a0 + (n1m & d); + a = (js_dlimb_t)d_inv * (a1 - n1m) + n_adj; + q = (a >> JS_LIMB_BITS) + a1; + /* compute a - q * r and update q so that the remainder is\ + between 0 and d - 1 */ + a = ((js_dlimb_t)a1 << JS_LIMB_BITS) | a0; + a = a - (js_dlimb_t)q * d - d; + ah = a >> JS_LIMB_BITS; + q += 1 + ah; + r = (js_limb_t)a + (ah & d); + *pr = r; + return q; +} + +#define UDIV1NORM_THRESHOLD 3 + +/* b must be >= 1 << (JS_LIMB_BITS - 1) */ +static js_limb_t js_mp_div1norm(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b, js_limb_t r) +{ + js_slimb_t i; + + if (n >= UDIV1NORM_THRESHOLD) { + js_limb_t b_inv; + b_inv = js_udiv1norm_init(b); + for(i = n - 1; i >= 0; i--) { + tabr[i] = js_udiv1norm(&r, r, taba[i], b, b_inv); + } + } else { + js_dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; } + } + return r; +} - /* skip leading zeros */ - while (*p == '0') - p++; - n = 0; - if (radix == 10) - n_max = ((uint64_t)-1 - 9) / 10; /* most common case */ +/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb + - 1] must be >= 1 << (JS_LIMB_BITS - 1). na - nb must be >= 0. 'taba' + is modified and contains the remainder (nb limbs). tabq[0..na-nb] + contains the quotient with tabq[na - nb] <= 1. */ +static void js_mp_divnorm(js_limb_t *tabq, js_limb_t *taba, js_limb_t na, + const js_limb_t *tabb, js_limb_t nb) +{ + js_limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r; + int i, j; + + b1 = tabb[nb - 1]; + if (nb == 1) { + taba[0] = js_mp_div1norm(tabq, taba, na, b1, 0); + return; + } + n = na - nb; + + if (n >= UDIV1NORM_THRESHOLD) + b1_inv = js_udiv1norm_init(b1); + else + b1_inv = 0; + + /* first iteration: the quotient is only 0 or 1 */ + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[n + j] != tabb[j]) { + if (taba[n + j] < tabb[j]) + q = 0; + break; + } + } + tabq[n] = q; + if (q) { + js_mp_sub(taba + n, taba + n, tabb, nb, 0); + } + + for(i = n - 1; i >= 0; i--) { + if (unlikely(taba[i + nb] >= b1)) { + q = -1; + } else if (b1_inv) { + q = js_udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv); + } else { + js_dlimb_t al; + al = ((js_dlimb_t)taba[i + nb] << JS_LIMB_BITS) | taba[i + nb - 1]; + q = al / b1; + r = al % b1; + } + r = js_mp_sub_mul1(taba + i, tabb, nb, q); + + v = taba[i + nb]; + a = v - r; + c = (a > v); + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = js_mp_add(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == 0) { + break; + } + } + } + } + tabq[i] = q; + } +} + +/* 1 <= shift <= JS_LIMB_BITS - 1 */ +static js_limb_t js_mp_shl(js_limb_t *tabr, const js_limb_t *taba, int n, + int shift) +{ + int i; + js_limb_t l, v; + l = 0; + for(i = 0; i < n; i++) { + v = taba[i]; + tabr[i] = (v << shift) | l; + l = v >> (JS_LIMB_BITS - shift); + } + return l; +} + +/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). + 1 <= shift <= LIMB_BITS - 1 */ +static js_limb_t js_mp_shr(js_limb_t *tab_r, const js_limb_t *tab, int n, + int shift, js_limb_t high) +{ + int i; + js_limb_t l, a; + + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + tab_r[i] = (a >> shift) | (l << (JS_LIMB_BITS - shift)); + l = a; + } + return l & (((js_limb_t)1 << shift) - 1); +} + +static JSBigInt *js_bigint_new(JSContext *ctx, int len) +{ + JSBigInt *r; + if (len > JS_BIGINT_MAX_SIZE) { + JS_ThrowRangeError(ctx, "BigInt is too large to allocate"); + return NULL; + } + r = js_malloc(ctx, sizeof(JSBigInt) + len * sizeof(js_limb_t)); + if (!r) + return NULL; + r->header.ref_count = 1; + r->len = len; + return r; +} + +static JSBigInt *js_bigint_set_si(JSBigIntBuf *buf, js_slimb_t a) +{ + JSBigInt *r = (JSBigInt *)buf->big_int_buf; + r->header.ref_count = 0; /* fail safe */ + r->len = 1; + r->tab[0] = a; + return r; +} + +static JSBigInt *js_bigint_set_si64(JSBigIntBuf *buf, int64_t a) +{ + JSBigInt *r = (JSBigInt *)buf->big_int_buf; + r->header.ref_count = 0; /* fail safe */ + if (a >= INT32_MIN && a <= INT32_MAX) { + r->len = 1; + r->tab[0] = a; + } else { + r->len = 2; + r->tab[0] = a; + r->tab[1] = a >> JS_LIMB_BITS; + } + return r; +} + +/* val must be a short big int */ +static JSBigInt *js_bigint_set_short(JSBigIntBuf *buf, JSValueConst val) +{ + return js_bigint_set_si(buf, JS_VALUE_GET_SHORT_BIG_INT(val)); +} + +static __maybe_unused void js_bigint_dump1(JSContext *ctx, const char *str, + const js_limb_t *tab, int len) +{ + int i; + printf("%s: ", str); + for(i = len - 1; i >= 0; i--) { + printf(" %08x", tab[i]); + } + printf("\n"); +} + +static __maybe_unused void js_bigint_dump(JSContext *ctx, const char *str, + const JSBigInt *p) +{ + js_bigint_dump1(ctx, str, p->tab, p->len); +} + +static JSBigInt *js_bigint_new_si(JSContext *ctx, js_slimb_t a) +{ + JSBigInt *r; + r = js_bigint_new(ctx, 1); + if (!r) + return NULL; + r->tab[0] = a; + return r; +} + +static JSBigInt *js_bigint_new_si64(JSContext *ctx, int64_t a) +{ + if (a >= INT32_MIN && a <= INT32_MAX) { + return js_bigint_new_si(ctx, a); + } else { + JSBigInt *r; + r = js_bigint_new(ctx, 2); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> 32; + return r; + } +} + +static JSBigInt *js_bigint_new_ui64(JSContext *ctx, uint64_t a) +{ + if (a <= INT64_MAX) { + return js_bigint_new_si64(ctx, a); + } else { + JSBigInt *r; + r = js_bigint_new(ctx, (65 + JS_LIMB_BITS - 1) / JS_LIMB_BITS); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> 32; + r->tab[2] = 0; + return r; + } +} + +static JSBigInt *js_bigint_new_di(JSContext *ctx, js_sdlimb_t a) +{ + JSBigInt *r; + if (a == (js_slimb_t)a) { + r = js_bigint_new(ctx, 1); + if (!r) + return NULL; + r->tab[0] = a; + } else { + r = js_bigint_new(ctx, 2); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> JS_LIMB_BITS; + } + return r; +} + +/* Remove redundant high order limbs. Warning: 'a' may be + reallocated. Can never fail. +*/ +static JSBigInt *js_bigint_normalize1(JSContext *ctx, JSBigInt *a, int l) +{ + js_limb_t v; + + assert(a->header.ref_count == 1); + while (l > 1) { + v = a->tab[l - 1]; + if ((v != 0 && v != -1) || + (v & 1) != (a->tab[l - 2] >> (JS_LIMB_BITS - 1))) { + break; + } + l--; + } + if (l != a->len) { + JSBigInt *a1; + /* realloc to reduce the size */ + a->len = l; + a1 = js_realloc(ctx, a, sizeof(JSBigInt) + l * sizeof(js_limb_t)); + if (a1) + a = a1; + } + return a; +} + +static JSBigInt *js_bigint_normalize(JSContext *ctx, JSBigInt *a) +{ + return js_bigint_normalize1(ctx, a, a->len); +} + +/* return 0 or 1 depending on the sign */ +static inline int js_bigint_sign(const JSBigInt *a) +{ + return a->tab[a->len - 1] >> (JS_LIMB_BITS - 1); +} + +static js_slimb_t js_bigint_get_si_sat(const JSBigInt *a) +{ + if (a->len == 1) { + return a->tab[0]; + } else { + if (js_bigint_sign(a)) + return INT32_MIN; else - n_max = ((uint64_t)-1 - (radix - 1)) / radix; - /* XXX: could be more precise */ - int_exp = 0; - while ((c = to_digit(*p)) < radix) { - if (n <= n_max) { - n = n * radix + c; + return INT32_MAX; + } +} + +/* add the op1 limb */ +static JSBigInt *js_bigint_extend(JSContext *ctx, JSBigInt *r, + js_limb_t op1) +{ + int n2 = r->len; + if ((op1 != 0 && op1 != -1) || + (op1 & 1) != r->tab[n2 - 1] >> (JS_LIMB_BITS - 1)) { + JSBigInt *r1; + r1 = js_realloc(ctx, r, + sizeof(JSBigInt) + (n2 + 1) * sizeof(js_limb_t)); + if (!r1) { + js_free(ctx, r); + return NULL; + } + r = r1; + r->len = n2 + 1; + r->tab[n2] = op1; + } else { + /* otherwise still need to normalize the result */ + r = js_bigint_normalize(ctx, r); + } + return r; +} + +/* return NULL in case of error. Compute a + b (b_neg = 0) or a - b + (b_neg = 1) */ +/* XXX: optimize */ +static JSBigInt *js_bigint_add(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, int b_neg) +{ + JSBigInt *r; + int n1, n2, i; + js_limb_t carry, op1, op2, a_sign, b_sign; + + n2 = max_int(a->len, b->len); + n1 = min_int(a->len, b->len); + r = js_bigint_new(ctx, n2); + if (!r) + return NULL; + /* XXX: optimize */ + /* common part */ + carry = b_neg; + for(i = 0; i < n1; i++) { + op1 = a->tab[i]; + op2 = b->tab[i] ^ (-b_neg); + ADDC(r->tab[i], carry, op1, op2, carry); + } + a_sign = -js_bigint_sign(a); + b_sign = (-js_bigint_sign(b)) ^ (-b_neg); + /* part with sign extension of one operand */ + if (a->len > b->len) { + for(i = n1; i < n2; i++) { + op1 = a->tab[i]; + ADDC(r->tab[i], carry, op1, b_sign, carry); + } + } else if (a->len < b->len) { + for(i = n1; i < n2; i++) { + op2 = b->tab[i] ^ (-b_neg); + ADDC(r->tab[i], carry, a_sign, op2, carry); + } + } + + /* part with sign extension for both operands. Extend the result + if necessary */ + return js_bigint_extend(ctx, r, a_sign + b_sign + carry); +} + +/* XXX: optimize */ +static JSBigInt *js_bigint_neg(JSContext *ctx, const JSBigInt *a) +{ + JSBigIntBuf buf; + JSBigInt *b; + b = js_bigint_set_si(&buf, 0); + return js_bigint_add(ctx, b, a, 1); +} + +static JSBigInt *js_bigint_mul(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b) +{ + JSBigInt *r; + + r = js_bigint_new(ctx, a->len + b->len); + if (!r) + return NULL; + js_mp_mul_basecase(r->tab, a->tab, a->len, b->tab, b->len); + /* correct the result if negative operands (no overflow is + possible) */ + if (js_bigint_sign(a)) + js_mp_sub(r->tab + a->len, r->tab + a->len, b->tab, b->len, 0); + if (js_bigint_sign(b)) + js_mp_sub(r->tab + b->len, r->tab + b->len, a->tab, a->len, 0); + return js_bigint_normalize(ctx, r); +} + +/* return the division or the remainder. 'b' must be != 0. return NULL + in case of exception (division by zero or memory error) */ +static JSBigInt *js_bigint_divrem(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, bool is_rem) +{ + JSBigInt *r, *q; + js_limb_t *tabb, h; + int na, nb, a_sign, b_sign, shift; + + if (b->len == 1 && b->tab[0] == 0) { + JS_ThrowRangeError(ctx, "BigInt division by zero"); + return NULL; + } + + a_sign = js_bigint_sign(a); + b_sign = js_bigint_sign(b); + na = a->len; + nb = b->len; + + r = js_bigint_new(ctx, na + 2); + if (!r) + return NULL; + if (a_sign) { + js_mp_neg(r->tab, a->tab, na); + } else { + memcpy(r->tab, a->tab, na * sizeof(a->tab[0])); + } + /* normalize */ + while (na > 1 && r->tab[na - 1] == 0) + na--; + + tabb = js_malloc(ctx, nb * sizeof(tabb[0])); + if (!tabb) { + js_free(ctx, r); + return NULL; + } + if (b_sign) { + js_mp_neg(tabb, b->tab, nb); + } else { + memcpy(tabb, b->tab, nb * sizeof(tabb[0])); + } + /* normalize */ + while (nb > 1 && tabb[nb - 1] == 0) + nb--; + + /* trivial case if 'a' is small */ + if (na < nb) { + js_free(ctx, r); + js_free(ctx, tabb); + if (is_rem) { + /* r = a */ + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0])); + return r; + } else { + /* q = 0 */ + return js_bigint_new_si(ctx, 0); + } + } + + /* normalize 'b' */ + shift = js_limb_clz(tabb[nb - 1]); + if (shift != 0) { + js_mp_shl(tabb, tabb, nb, shift); + h = js_mp_shl(r->tab, r->tab, na, shift); + if (h != 0) + r->tab[na++] = h; + } + + q = js_bigint_new(ctx, na - nb + 2); /* one more limb for the sign */ + if (!q) { + js_free(ctx, r); + js_free(ctx, tabb); + return NULL; + } + + // js_bigint_dump1(ctx, "a", r->tab, na); + // js_bigint_dump1(ctx, "b", tabb, nb); + js_mp_divnorm(q->tab, r->tab, na, tabb, nb); + js_free(ctx, tabb); + + if (is_rem) { + js_free(ctx, q); + if (shift != 0) + js_mp_shr(r->tab, r->tab, nb, shift, 0); + r->tab[nb++] = 0; + if (a_sign) + js_mp_neg(r->tab, r->tab, nb); + r = js_bigint_normalize1(ctx, r, nb); + return r; + } else { + js_free(ctx, r); + q->tab[na - nb + 1] = 0; + if (a_sign ^ b_sign) { + js_mp_neg(q->tab, q->tab, q->len); + } + q = js_bigint_normalize(ctx, q); + return q; + } +} + +/* and, or, xor */ +static JSBigInt *js_bigint_logic(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, OPCodeEnum op) +{ + JSBigInt *r; + js_limb_t b_sign; + int a_len, b_len, i; + + if (a->len < b->len) { + const JSBigInt *tmp; + tmp = a; + a = b; + b = tmp; + } + /* a_len >= b_len */ + a_len = a->len; + b_len = b->len; + b_sign = -js_bigint_sign(b); + + r = js_bigint_new(ctx, a_len); + if (!r) + return NULL; + switch(op) { + case OP_or: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] | b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] | b_sign; + } + break; + case OP_and: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] & b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] & b_sign; + } + break; + case OP_xor: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] ^ b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] ^ b_sign; + } + break; + default: + abort(); + } + return js_bigint_normalize(ctx, r); +} + +static JSBigInt *js_bigint_not(JSContext *ctx, const JSBigInt *a) +{ + JSBigInt *r; + int i; + + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + for(i = 0; i < a->len; i++) { + r->tab[i] = ~a->tab[i]; + } + /* no normalization is needed */ + return r; +} + +static JSBigInt *js_bigint_shl(JSContext *ctx, const JSBigInt *a, + unsigned int shift1) +{ + int d, i, shift; + JSBigInt *r; + js_limb_t l; + + if (a->len == 1 && a->tab[0] == 0) + return js_bigint_new_si(ctx, 0); /* zero case */ + d = shift1 / JS_LIMB_BITS; + shift = shift1 % JS_LIMB_BITS; + r = js_bigint_new(ctx, a->len + d); + if (!r) + return NULL; + for(i = 0; i < d; i++) + r->tab[i] = 0; + if (shift == 0) { + for(i = 0; i < a->len; i++) { + r->tab[i + d] = a->tab[i]; + } + } else { + l = js_mp_shl(r->tab + d, a->tab, a->len, shift); + if (js_bigint_sign(a)) + l |= (js_limb_t)(-1) << shift; + r = js_bigint_extend(ctx, r, l); + } + return r; +} + +static JSBigInt *js_bigint_shr(JSContext *ctx, const JSBigInt *a, + unsigned int shift1) +{ + int d, i, shift, a_sign, n1; + JSBigInt *r; + + d = shift1 / JS_LIMB_BITS; + shift = shift1 % JS_LIMB_BITS; + a_sign = js_bigint_sign(a); + if (d >= a->len) + return js_bigint_new_si(ctx, -a_sign); + n1 = a->len - d; + r = js_bigint_new(ctx, n1); + if (!r) + return NULL; + if (shift == 0) { + for(i = 0; i < n1; i++) { + r->tab[i] = a->tab[i + d]; + } + /* no normalization is needed */ + } else { + js_mp_shr(r->tab, a->tab + d, n1, shift, -a_sign); + r = js_bigint_normalize(ctx, r); + } + return r; +} + +static JSBigInt *js_bigint_pow(JSContext *ctx, const JSBigInt *a, JSBigInt *b) +{ + uint32_t e; + int n_bits, i; + JSBigInt *r, *r1; + + /* b must be >= 0 */ + if (js_bigint_sign(b)) { + JS_ThrowRangeError(ctx, "BigInt negative exponent"); + return NULL; + } + if (b->len == 1 && b->tab[0] == 0) { + /* a^0 = 1 */ + return js_bigint_new_si(ctx, 1); + } else if (a->len == 1) { + js_limb_t v; + bool is_neg; + + v = a->tab[0]; + if (v <= 1) + return js_bigint_new_si(ctx, v); + else if (v == -1) + return js_bigint_new_si(ctx, 1 - 2 * (b->tab[0] & 1)); + is_neg = (js_slimb_t)v < 0; + if (is_neg) + v = -v; + if ((v & (v - 1)) == 0) { + uint64_t e1; + int n; + /* v = 2^n */ + n = JS_LIMB_BITS - 1 - js_limb_clz(v); + if (b->len > 1) + goto overflow; + if (b->tab[0] > INT32_MAX) + goto overflow; + e = b->tab[0]; + e1 = (uint64_t)e * n; + if (e1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS) + goto overflow; + e = e1; + if (is_neg) + is_neg = b->tab[0] & 1; + r = js_bigint_new(ctx, + (e + JS_LIMB_BITS + 1 - is_neg) / JS_LIMB_BITS); + if (!r) + return NULL; + memset(r->tab, 0, sizeof(r->tab[0]) * r->len); + r->tab[e / JS_LIMB_BITS] = + (js_limb_t)(1 - 2 * is_neg) << (e % JS_LIMB_BITS); + return r; + } + } + if (b->len > 1) + goto overflow; + if (b->tab[0] > INT32_MAX) + goto overflow; + e = b->tab[0]; + n_bits = 32 - clz32(e); + + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0])); + for(i = n_bits - 2; i >= 0; i--) { + r1 = js_bigint_mul(ctx, r, r); + if (!r1) + return NULL; + js_free(ctx, r); + r = r1; + if ((e >> i) & 1) { + r1 = js_bigint_mul(ctx, r, a); + if (!r1) + return NULL; + js_free(ctx, r); + r = r1; + } + } + return r; + overflow: + JS_ThrowRangeError(ctx, "BigInt is too large"); + return NULL; +} + +/* return (mant, exp) so that abs(a) ~ mant*2^(exp - (limb_bits - + 1). a must be != 0. */ +static uint64_t js_bigint_get_mant_exp(JSContext *ctx, + int *pexp, const JSBigInt *a) +{ + js_limb_t t[4 - JS_LIMB_BITS / 32], carry, v, low_bits; + int n1, n2, sgn, shift, i, j, e; + uint64_t a1, a0; + + n2 = 4 - JS_LIMB_BITS / 32; + n1 = a->len - n2; + sgn = js_bigint_sign(a); + + /* low_bits != 0 if there are a non zero low bit in abs(a) */ + low_bits = 0; + carry = sgn; + for(i = 0; i < n1; i++) { + v = (a->tab[i] ^ (-sgn)) + carry; + carry = v < carry; + low_bits |= v; + } + /* get the n2 high limbs of abs(a) */ + for(j = 0; j < n2; j++) { + i = j + n1; + if (i < 0) { + v = 0; + } else { + v = (a->tab[i] ^ (-sgn)) + carry; + carry = v < carry; + } + t[j] = v; + } + + a1 = ((uint64_t)t[2] << 32) | t[1]; + a0 = (uint64_t)t[0] << 32; + a0 |= (low_bits != 0); + /* normalize */ + { + shift = clz64(a1); + if (shift != 0) { + a1 = (a1 << shift) | (a0 >> (64 - shift)); + a0 <<= shift; + } + } + a1 |= (a0 != 0); /* keep the bits for the final rounding */ + /* compute the exponent */ + e = a->len * JS_LIMB_BITS - shift - 1; + *pexp = e; + return a1; +} + +/* shift left with round to nearest, ties to even. n >= 1 */ +static uint64_t shr_rndn(uint64_t a, int n) +{ + uint64_t addend = ((a >> n) & 1) + ((1 << (n - 1)) - 1); + return (a + addend) >> n; +} + +/* convert to float64 with round to nearest, ties to even. Return + +/-infinity if too large. */ +static double js_bigint_to_float64(JSContext *ctx, const JSBigInt *a) +{ + int sgn, e; + uint64_t mant; + + if (a->len == 1) { + /* fast case, including zero */ + return (double)(js_slimb_t)a->tab[0]; + } + + sgn = js_bigint_sign(a); + mant = js_bigint_get_mant_exp(ctx, &e, a); + if (e > 1023) { + /* overflow: return infinity */ + mant = 0; + e = 1024; + } else { + mant = (mant >> 1) | (mant & 1); /* avoid overflow in rounding */ + mant = shr_rndn(mant, 10); + /* rounding can cause an overflow */ + if (mant >= ((uint64_t)1 << 53)) { + mant >>= 1; + e++; + } + mant &= (((uint64_t)1 << 52) - 1); + } + return uint64_as_float64(((uint64_t)sgn << 63) | + ((uint64_t)(e + 1023) << 52) | + mant); +} + +/* return (1, NULL) if not an integer, (2, NULL) if NaN or Infinity, + (0, n) if an integer, (0, NULL) in case of memory error */ +static JSBigInt *js_bigint_from_float64(JSContext *ctx, int *pres, double a1) +{ + uint64_t a = float64_as_uint64(a1); + int sgn, e, shift; + uint64_t mant; + JSBigIntBuf buf; + JSBigInt *r; + + sgn = a >> 63; + e = (a >> 52) & ((1 << 11) - 1); + mant = a & (((uint64_t)1 << 52) - 1); + if (e == 2047) { + /* NaN, Infinity */ + *pres = 2; + return NULL; + } + if (e == 0 && mant == 0) { + /* zero */ + *pres = 0; + return js_bigint_new_si(ctx, 0); + } + e -= 1023; + /* 0 < a < 1 : not an integer */ + if (e < 0) + goto not_an_integer; + mant |= (uint64_t)1 << 52; + if (e < 52) { + shift = 52 - e; + /* check that there is no fractional part */ + if (mant & (((uint64_t)1 << shift) - 1)) { + not_an_integer: + *pres = 1; + return NULL; + } + mant >>= shift; + e = 0; + } else { + e -= 52; + } + if (sgn) + mant = -mant; + /* the integer is mant*2^e */ + r = js_bigint_set_si64(&buf, (int64_t)mant); + *pres = 0; + return js_bigint_shl(ctx, r, e); +} + +/* return -1, 0, 1 or (2) (unordered) */ +static int js_bigint_float64_cmp(JSContext *ctx, const JSBigInt *a, + double b) +{ + int b_sign, a_sign, e, f; + uint64_t mant, b1, a_mant; + + b1 = float64_as_uint64(b); + b_sign = b1 >> 63; + e = (b1 >> 52) & ((1 << 11) - 1); + mant = b1 & (((uint64_t)1 << 52) - 1); + a_sign = js_bigint_sign(a); + if (e == 2047) { + if (mant != 0) { + /* NaN */ + return 2; + } else { + /* +/- infinity */ + return 2 * b_sign - 1; + } + } else if (e == 0 && mant == 0) { + /* b = +/-0 */ + if (a->len == 1 && a->tab[0] == 0) + return 0; + else + return 1 - 2 * a_sign; + } else if (a->len == 1 && a->tab[0] == 0) { + /* a = 0, b != 0 */ + return 2 * b_sign - 1; + } else if (a_sign != b_sign) { + return 1 - 2 * a_sign; + } else { + e -= 1023; + /* Note: handling denormals is not necessary because we + compare to integers hence f >= 0 */ + /* compute f so that 2^f <= abs(a) < 2^(f+1) */ + a_mant = js_bigint_get_mant_exp(ctx, &f, a); + if (f != e) { + if (f < e) + return -1; + else + return 1; + } else { + mant = (mant | ((uint64_t)1 << 52)) << 11; /* align to a_mant */ + if (a_mant < mant) + return 2 * a_sign - 1; + else if (a_mant > mant) + return 1 - 2 * a_sign; + else + return 0; + } + } +} + +/* return -1, 0 or 1 */ +static int js_bigint_cmp(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b) +{ + int a_sign, b_sign, res, i; + a_sign = js_bigint_sign(a); + b_sign = js_bigint_sign(b); + if (a_sign != b_sign) { + res = 1 - 2 * a_sign; + } else { + /* we assume the numbers are normalized */ + if (a->len != b->len) { + if (a->len < b->len) + res = 2 * a_sign - 1; + else + res = 1 - 2 * a_sign; + } else { + res = 0; + for(i = a->len -1; i >= 0; i--) { + if (a->tab[i] != b->tab[i]) { + if (a->tab[i] < b->tab[i]) + res = -1; + else + res = 1; + break; + } + } + } + } + return res; +} + +/* contains 10^i */ +static const js_limb_t js_pow_dec[JS_LIMB_DIGITS + 1] = { + 1U, + 10U, + 100U, + 1000U, + 10000U, + 100000U, + 1000000U, + 10000000U, + 100000000U, + 1000000000U, +}; + +/* syntax: [-]digits in base radix. Return NULL if memory error. radix + = 10, 2, 8 or 16. */ +static JSBigInt *js_bigint_from_string(JSContext *ctx, + const char *str, int radix) +{ + const char *p = str; + size_t n_digits1; + int is_neg, n_digits, n_limbs, len, log2_radix, n_bits, i; + JSBigInt *r; + js_limb_t v, c, h; + + is_neg = 0; + if (*p == '-') { + is_neg = 1; + p++; + } + while (*p == '0') + p++; + n_digits1 = strlen(p); + /* the real check for overflox is done js_bigint_new(). Here + we just avoid integer overflow */ + if (n_digits1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS) { + JS_ThrowRangeError(ctx, "BigInt is too large to allocate"); + return NULL; + } + n_digits = n_digits1; + log2_radix = 32 - clz32(radix - 1); /* ceil(log2(radix)) */ + /* compute the maximum number of limbs */ + if (radix == 10) { + n_bits = (n_digits * 27 + 7) / 8; /* >= ceil(n_digits * log2(10)) */ + } else { + n_bits = n_digits * log2_radix; + } + /* we add one extra bit for the sign */ + n_limbs = max_int(1, n_bits / JS_LIMB_BITS + 1); + r = js_bigint_new(ctx, n_limbs); + if (!r) + return NULL; + if (radix == 10) { + int digits_per_limb = JS_LIMB_DIGITS; + len = 1; + r->tab[0] = 0; + for(;;) { + /* XXX: slow */ + v = 0; + for(i = 0; i < digits_per_limb; i++) { + c = js_to_digit(*p); + if (c >= radix) + break; + p++; + v = v * 10 + c; + } + if (i == 0) + break; + if (len == 1 && r->tab[0] == 0) { + r->tab[0] = v; } else { - if (radix == 10) - goto strtod_case; - int_exp++; + h = js_mp_mul1(r->tab, r->tab, len, js_pow_dec[i], v); + if (h != 0) { + r->tab[len++] = h; + } } - p++; } - d = n; - if (int_exp != 0) { - d *= pow(radix, int_exp); + /* add one extra limb to have the correct sign*/ + if ((r->tab[len - 1] >> (JS_LIMB_BITS - 1)) != 0) + r->tab[len++] = 0; + r->len = len; + } else { + unsigned int bit_pos, shift, pos; + + /* power of two base: no multiplication is needed */ + r->len = n_limbs; + memset(r->tab, 0, sizeof(r->tab[0]) * n_limbs); + for(i = 0; i < n_digits; i++) { + c = js_to_digit(p[n_digits - 1 - i]); + assert(c < radix); + bit_pos = i * log2_radix; + shift = bit_pos & (JS_LIMB_BITS - 1); + pos = bit_pos / JS_LIMB_BITS; + r->tab[pos] |= c << shift; + /* if log2_radix does not divide JS_LIMB_BITS, needed an + additional op */ + if (shift + log2_radix > JS_LIMB_BITS) { + r->tab[pos + 1] |= c >> (JS_LIMB_BITS - shift); + } } - if (is_neg) - d = -d; + } + r = js_bigint_normalize(ctx, r); + /* XXX: could do it in place */ + if (is_neg) { + JSBigInt *r1; + r1 = js_bigint_neg(ctx, r); + js_free(ctx, r); + r = r1; + } + return r; +} + +/* 2 <= base <= 36 */ +static char const digits[36] = { + '0','1','2','3','4','5','6','7','8','9', + 'a','b','c','d','e','f','g','h','i','j', + 'k','l','m','n','o','p','q','r','s','t', + 'u','v','w','x','y','z' +}; + +/* special version going backwards */ +/* XXX: use dtoa.c */ +static char *js_u64toa(char *q, int64_t n, unsigned int base) +{ + int digit; + if (base == 10) { + /* division by known base uses multiplication */ + do { + digit = (uint64_t)n % 10; + n = (uint64_t)n / 10; + *--q = '0' + digit; + } while (n != 0); } else { - strtod_case: - d = strtod(str, NULL); + do { + digit = (uint64_t)n % base; + n = (uint64_t)n / base; + *--q = digits[digit]; + } while (n != 0); } - return d; + return q; } -static JSValue js_string_to_bigint(JSContext *ctx, const char *buf, int radix) +/* len >= 1. 2 <= radix <= 36 */ +static char *js_limb_to_a(char *q, js_limb_t n, unsigned int radix, int len) { - bf_t *a; - int ret; - JSValue val; - val = JS_NewBigInt(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigInt(val); - ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ); - if (ret & BF_ST_MEM_ERROR) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); + int digit, i; + + if (radix == 10) { + /* specific case with constant divisor */ + /* XXX: optimize */ + for(i = 0; i < len; i++) { + digit = (js_limb_t)n % 10; + n = (js_limb_t)n / 10; + *--q = digit + '0'; + } + } else { + for(i = 0; i < len; i++) { + digit = (js_limb_t)n % radix; + n = (js_limb_t)n / radix; + *--q = digits[digit]; + } } - return JS_CompactBigInt1(ctx, val); -} - -/* `js_atof(ctx, p, len, pp, radix, flags)` - Convert the string pointed to by `p` to a number value. - Return an exception in case of memory error. - Return `JS_NAN` if invalid syntax. - - `p` points to a null terminated UTF-8 encoded char array, - - `len` the length of the array, - - `pp` if not null receives a pointer to the next character, - - `radix` must be in range 2 to 36, else return `JS_NAN`. - - `flags` is a combination of the flags below. - There is a null byte at `p[len]`, but there might be embedded null - bytes between `p[0]` and `p[len]` which must produce `JS_NAN` if - the `ATOD_NO_TRAILING_CHARS` flag is present. - */ + return q; +} -#define ATOD_TRIM_SPACES (1 << 0) /* trim white space */ -#define ATOD_ACCEPT_EMPTY (1 << 1) /* accept an empty string, value is 0 */ -#define ATOD_ACCEPT_FLOAT (1 << 2) /* parse decimal floating point syntax */ -#define ATOD_ACCEPT_INFINITY (1 << 3) /* parse Infinity as a float point number */ -#define ATOD_ACCEPT_BIN_OCT (1 << 4) /* accept 0o and 0b prefixes */ -#define ATOD_ACCEPT_HEX_PREFIX (1 << 5) /* accept 0x prefix for radix 16 */ -#define ATOD_ACCEPT_UNDERSCORES (1 << 6) /* accept _ between digits as a digit separator */ -#define ATOD_ACCEPT_SUFFIX (1 << 7) /* allow 'n' suffix to produce BigInt */ -#define ATOD_WANT_BIG_INT (1 << 8) /* return type must be BigInt */ -#define ATOD_DECIMAL_AFTER_SIGN (1 << 9) /* only accept decimal number after sign */ -#define ATOD_NO_TRAILING_CHARS (1 << 10) /* do not accept trailing characters */ - -static JSValue js_atof(JSContext *ctx, const char *p, size_t len, - const char **pp, int radix, int flags) -{ - const char *p_start; - const char *end = p + len; - int sep; - bool is_float; - char buf1[64], *buf = buf1; - size_t i, j; - JSValue val = JS_NAN; - double d; - char sign; +#define JS_RADIX_MAX 36 - if (radix < 2 || radix > 36) - goto done; +static const uint8_t js_digits_per_limb_table[JS_RADIX_MAX - 1] = { +32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +}; + +static const js_limb_t js_radix_base_table[JS_RADIX_MAX - 1] = { + 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395, + 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91, + 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021, + 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571, + 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d, + 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51, + 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899, + 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1, + 0x5c13d840, 0x6d91b519, 0x81bf1000, +}; + +static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { + char buf[66]; + int len; + len = i64toa_radix(buf, JS_VALUE_GET_SHORT_BIG_INT(val), radix); + return js_new_string8_len(ctx, buf, len); + } else { + JSBigInt *r, *tmp = NULL; + char *buf, *q, *buf_end; + int is_neg, n_bits, log2_radix, n_digits; + bool is_binary_radix; + JSValue res; + + assert(JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT); + r = JS_VALUE_GET_PTR(val); + if (r->len == 1 && r->tab[0] == 0) { + /* '0' case */ + return js_new_string8_len(ctx, "0", 1); + } + is_binary_radix = ((radix & (radix - 1)) == 0); + is_neg = js_bigint_sign(r); + if (is_neg) { + tmp = js_bigint_neg(ctx, r); + if (!tmp) + return JS_EXCEPTION; + r = tmp; + } else if (!is_binary_radix) { + /* need to modify 'r' */ + tmp = js_bigint_new(ctx, r->len); + if (!tmp) + return JS_EXCEPTION; + memcpy(tmp->tab, r->tab, r->len * sizeof(r->tab[0])); + r = tmp; + } + log2_radix = 31 - clz32(radix); /* floor(log2(radix)) */ + n_bits = r->len * JS_LIMB_BITS - js_limb_clz(r->tab[r->len - 1]); + /* n_digits is exact only if radix is a power of + two. Otherwise it is >= the exact number of digits */ + n_digits = (n_bits + log2_radix - 1) / log2_radix; + /* XXX: could directly build the JSString */ + buf = js_malloc(ctx, n_digits + is_neg + 1); + if (!buf) { + js_free(ctx, tmp); + return JS_EXCEPTION; + } + q = buf + n_digits + is_neg + 1; + *--q = '\0'; + buf_end = q; + if (!is_binary_radix) { + int len; + js_limb_t radix_base, v; + radix_base = js_radix_base_table[radix - 2]; + len = r->len; + for(;;) { + /* remove leading zero limbs */ + while (len > 1 && r->tab[len - 1] == 0) + len--; + if (len == 1 && r->tab[0] < radix_base) { + v = r->tab[0]; + if (v != 0) { + q = js_u64toa(q, v, radix); + } + break; + } else { + v = js_mp_div1(r->tab, r->tab, len, radix_base, 0); + q = js_limb_to_a(q, v, radix, js_digits_per_limb_table[radix - 2]); + } + } + } else { + int i, shift; + unsigned int bit_pos, pos, c; + + /* radix is a power of two */ + for(i = 0; i < n_digits; i++) { + bit_pos = i * log2_radix; + pos = bit_pos / JS_LIMB_BITS; + shift = bit_pos % JS_LIMB_BITS; + c = r->tab[pos] >> shift; + if ((shift + log2_radix) > JS_LIMB_BITS && + (pos + 1) < r->len) { + c |= r->tab[pos + 1] << (JS_LIMB_BITS - shift); + } + c &= (radix - 1); + *--q = digits[c]; + } + } + if (is_neg) + *--q = '-'; + js_free(ctx, tmp); + res = js_new_string8_len(ctx, q, buf_end - q); + js_free(ctx, buf); + return res; + } +} + +/* if possible transform a BigInt to short big and free it, otherwise + return a normal bigint */ +static JSValue JS_CompactBigInt(JSContext *ctx, JSBigInt *p) +{ + JSValue res; + if (p->len == 1) { + res = __JS_NewShortBigInt(ctx, (js_slimb_t)p->tab[0]); + js_free(ctx, p); + return res; + } else { + return JS_MKPTR(JS_TAG_BIG_INT, p); + } +} + +#define ATOD_INT_ONLY (1 << 0) +/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ +#define ATOD_ACCEPT_BIN_OCT (1 << 2) +/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ +#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4) +/* accept _ between digits as a digit separator */ +#define ATOD_ACCEPT_UNDERSCORES (1 << 5) +/* allow a suffix to override the type */ +#define ATOD_ACCEPT_SUFFIX (1 << 6) +/* default type */ +#define ATOD_TYPE_MASK (3 << 7) +#define ATOD_TYPE_FLOAT64 (0 << 7) +#define ATOD_TYPE_BIG_INT (1 << 7) +/* accept -0x1 */ +#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10) + +/* return an exception in case of memory error. Return JS_NAN if + invalid syntax */ +/* XXX: directly use js_atod() */ +static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, + int radix, int flags) +{ + const char *p, *p_start; + int sep, is_neg; + bool is_float, has_legacy_octal; + int atod_type = flags & ATOD_TYPE_MASK; + char buf1[64], *buf; + int i, j, len; + bool buf_allocated = false; + JSValue val; + JSATODTempMem atod_mem; /* optional separator between digits */ sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; - sign = 0; - if (flags & ATOD_TRIM_SPACES) - p += skip_spaces(p); - if (p == end && (flags & ATOD_ACCEPT_EMPTY)) { - if (pp) *pp = p; - if (flags & ATOD_WANT_BIG_INT) - return JS_NewBigInt64(ctx, 0); - else - return js_int32(0); - } - if (*p == '+' || *p == '-') { - sign = *p; + has_legacy_octal = false; + + p = str; + p_start = p; + is_neg = 0; + if (p[0] == '+') { p++; - if (flags & ATOD_DECIMAL_AFTER_SIGN) - flags &= ~(ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT); + p_start++; + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) + goto no_radix_prefix; + } else if (p[0] == '-') { + p++; + p_start++; + is_neg = 1; + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) + goto no_radix_prefix; } if (p[0] == '0') { if ((p[1] == 'x' || p[1] == 'X') && - ((flags & ATOD_ACCEPT_HEX_PREFIX) || radix == 16)) { + (radix == 0 || radix == 16)) { p += 2; radix = 16; - } else if (flags & ATOD_ACCEPT_BIN_OCT) { - if (p[1] == 'o' || p[1] == 'O') { - p += 2; - radix = 8; - } else if (p[1] == 'b' || p[1] == 'B') { - p += 2; - radix = 2; - } + } else if ((p[1] == 'o' || p[1] == 'O') && + radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 8; + } else if ((p[1] == 'b' || p[1] == 'B') && + radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 2; + } else if ((p[1] >= '0' && p[1] <= '9') && + radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) { + int i; + has_legacy_octal = true; + sep = 256; + for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++) + continue; + if (p[i] == '8' || p[i] == '9') + goto no_prefix; + p += 1; + radix = 8; + } else { + goto no_prefix; } - } else { - if (*p == 'I' && (flags & ATOD_ACCEPT_INFINITY) && js__strstart(p, "Infinity", &p)) { - d = INF; - if (sign == '-') + /* there must be a digit after the prefix */ + if (js_to_digit((uint8_t)*p) >= radix) + goto fail; + no_prefix: ; + } else { + no_radix_prefix: + if (!(flags & ATOD_INT_ONLY) && + (atod_type == ATOD_TYPE_FLOAT64) && + js__strstart(p, "Infinity", &p)) { + double d = INF; + if (is_neg) d = -d; val = js_float64(d); goto done; } } + if (radix == 0) + radix = 10; is_float = false; p_start = p; - while (to_digit(*p) < radix) { + while (js_to_digit((uint8_t)*p) < radix + || (*p == sep && (radix != 10 || + p != p_start + 1 || p[-1] != '0') && + js_to_digit((uint8_t)p[1]) < radix)) { p++; - if (*p == sep && to_digit(p[1]) < radix) - p++; } - if ((flags & ATOD_ACCEPT_FLOAT) && radix == 10) { - if (*p == '.' && (p > p_start || to_digit(p[1]) < radix)) { + if (!(flags & ATOD_INT_ONLY) && radix == 10) { + if (*p == '.' && (p > p_start || js_to_digit((uint8_t)p[1]) < radix)) { is_float = true; p++; - while (to_digit(*p) < radix) { + if (*p == sep) + goto fail; + while (js_to_digit((uint8_t)*p) < radix || + (*p == sep && js_to_digit((uint8_t)p[1]) < radix)) p++; - if (*p == sep && to_digit(p[1]) < radix) - p++; - } } if (p > p_start && (*p == 'e' || *p == 'E')) { - i = 1; - if (p[1] == '+' || p[1] == '-') { - i++; - } - if (is_digit(p[i])) { - is_float = true; - p += i + 1; - while (is_digit(*p) || (*p == sep && is_digit(p[1]))) + const char *p1 = p + 1; + is_float = true; + if (*p1 == '+') { + p1++; + } else if (*p1 == '-') { + p1++; + } + if (is_digit((uint8_t)*p1)) { + p = p1 + 1; + while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1]))) p++; } } } if (p == p_start) - goto done; + goto fail; + buf = buf1; + buf_allocated = false; len = p - p_start; if (unlikely((len + 2) > sizeof(buf1))) { buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */ - if (!buf) { - if (pp) *pp = p; - return JS_ThrowOutOfMemory(ctx); - } + if (!buf) + goto mem_error; + buf_allocated = true; } - /* remove the separators and the radix prefix */ + /* remove the separators and the radix prefixes */ j = 0; - if (sign == '-') + if (is_neg) buf[j++] = '-'; for (i = 0; i < len; i++) { if (p_start[i] != '_') @@ -10701,31 +12269,49 @@ static JSValue js_atof(JSContext *ctx, const char *p, size_t len, if (flags & ATOD_ACCEPT_SUFFIX) { if (*p == 'n') { p++; - flags |= ATOD_WANT_BIG_INT; + atod_type = ATOD_TYPE_BIG_INT; } } - if (flags & ATOD_WANT_BIG_INT) { - if (!is_float) - val = js_string_to_bigint(ctx, buf, radix); - } else { - d = js_strtod(buf, radix, is_float); - val = js_number(d); /* return int or float64 */ - } - - done: - if (flags & ATOD_NO_TRAILING_CHARS) { - if (flags & ATOD_TRIM_SPACES) - p += skip_spaces(p); - if (p != end) { - JS_FreeValue(ctx, val); - val = JS_NAN; + switch(atod_type) { + case ATOD_TYPE_FLOAT64: + { + double d; + d = js_atod(buf, NULL, radix, is_float ? 0 : JS_ATOD_INT_ONLY, + &atod_mem); + /* return int or float64 */ + val = js_number(d); + } + break; + case ATOD_TYPE_BIG_INT: + { + JSBigInt *r; + if (has_legacy_octal || is_float) + goto fail; + r = js_bigint_from_string(ctx, buf, radix); + if (!r) { + val = JS_EXCEPTION; + goto done; + } + val = JS_CompactBigInt(ctx, r); } + break; + default: + abort(); } - if (buf != buf1) + +done: + if (buf_allocated) js_free_rt(ctx->rt, buf); - if (pp) *pp = p; + if (pp) + *pp = p; return val; + fail: + val = JS_NAN; + goto done; + mem_error: + val = JS_ThrowOutOfMemory(ctx); + goto done; } typedef enum JSToNumberHintEnum { @@ -10743,6 +12329,7 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { case JS_TAG_BIG_INT: + case JS_TAG_SHORT_BIG_INT: if (flag != TON_FLAG_NUMERIC) { JS_FreeValue(ctx, val); return JS_ThrowTypeError(ctx, "cannot convert BigInt to number"); @@ -10769,18 +12356,28 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, case JS_TAG_STRING: { const char *str; + const char *p; size_t len; - int flags; str = JS_ToCStringLen(ctx, &len, val); JS_FreeValue(ctx, val); if (!str) return JS_EXCEPTION; - flags = ATOD_TRIM_SPACES | ATOD_ACCEPT_EMPTY | - ATOD_ACCEPT_FLOAT | ATOD_ACCEPT_INFINITY | - ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT | - ATOD_DECIMAL_AFTER_SIGN | ATOD_NO_TRAILING_CHARS; - ret = js_atof(ctx, str, len, NULL, 10, flags); + p = str; + p += skip_spaces(p); + if ((p - str) == len) { + ret = JS_NewInt32(ctx, 0); + } else { + int flags = ATOD_ACCEPT_BIN_OCT; + ret = js_atof(ctx, p, &p, 0, flags); + if (!JS_IsException(ret)) { + p += skip_spaces(p); + if ((p - str) != len) { + JS_FreeValue(ctx, ret); + ret = JS_NAN; + } + } + } JS_FreeCString(ctx, str); } break; @@ -10805,7 +12402,7 @@ static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); } -static JSValue JS_ToNumeric(JSContext *ctx, JSValue val) +static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val) { return JS_ToNumericFree(ctx, js_dup(val)); } @@ -10817,10 +12414,8 @@ static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, uint32_t tag; val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = JS_FLOAT64_NAN; - return -1; - } + if (JS_IsException(val)) + goto fail; tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { case JS_TAG_INT: @@ -10829,21 +12424,14 @@ static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, case JS_TAG_FLOAT64: d = JS_VALUE_GET_FLOAT64(val); break; - case JS_TAG_BIG_INT: - { - JSBigInt *p = JS_VALUE_GET_PTR(val); - /* XXX: there can be a double rounding issue with some - primitives (such as JS_ToUint8ClampFree()), but it is - not critical to fix it. */ - bf_get_float64(&p->num, &d, BF_RNDN); - JS_FreeValue(ctx, val); - } - break; default: abort(); } *pres = d; return 0; +fail: + *pres = NAN; + return -1; } static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) @@ -10862,12 +12450,12 @@ static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) } } -int JS_ToFloat64(JSContext *ctx, double *pres, JSValue val) +int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val) { return JS_ToFloat64Free(ctx, pres, js_dup(val)); } -JSValue JS_ToNumber(JSContext *ctx, JSValue val) +JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) { return JS_ToNumberFree(ctx, js_dup(val)); } @@ -10953,13 +12541,13 @@ static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) return 0; } -int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValue val) +static int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val) { return JS_ToInt32SatFree(ctx, pres, js_dup(val)); } -int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValue val, - int min, int max, int min_offset) +static int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val, + int min, int max, int min_offset) { int res = JS_ToInt32SatFree(ctx, pres, js_dup(val)); if (res == 0) { @@ -11016,12 +12604,12 @@ static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) } } -int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValue val) +int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val) { return JS_ToInt64SatFree(ctx, pres, js_dup(val)); } -int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValue val, +int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val, int64_t min, int64_t max, int64_t neg_offset) { int res = JS_ToInt64SatFree(ctx, pres, js_dup(val)); @@ -11090,14 +12678,14 @@ static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) return 0; } -int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValue val) +int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val) { return JS_ToInt64Free(ctx, pres, js_dup(val)); } -int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValue val) +int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val) { - if (JS_IsBigInt(ctx, val)) + if (JS_IsBigInt(val)) return JS_ToBigInt64(ctx, pres, val); else return JS_ToInt64(ctx, pres, val); @@ -11157,7 +12745,7 @@ static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) return 0; } -int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValue val) +int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val) { return JS_ToInt32Free(ctx, pres, js_dup(val)); } @@ -11227,21 +12815,6 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, len = v; } break; - case JS_TAG_BIG_INT: - { - JSBigInt *p = JS_VALUE_GET_PTR(val); - bf_t a; - bool res; - bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD); - bf_init(ctx->bf_ctx, &a); - bf_set_ui(&a, len); - res = bf_cmp_eq(&a, &p->num); - bf_delete(&a); - JS_FreeValue(ctx, val); - if (!res) - goto fail; - } - break; default: if (JS_TAG_IS_FLOAT64(tag)) { double d; @@ -11294,7 +12867,7 @@ static bool is_safe_integer(double d) fabs(d) <= (double)MAX_SAFE_INTEGER; } -int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValue val) +int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) { int64_t v; if (JS_ToInt64Sat(ctx, &v, val)) @@ -11319,7 +12892,7 @@ static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, } /* Note: can return an exception */ -static int JS_NumberIsInteger(JSContext *ctx, JSValue val) +static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val) { double d; if (!JS_IsNumber(val)) @@ -11329,7 +12902,7 @@ static int JS_NumberIsInteger(JSContext *ctx, JSValue val) return isfinite(d) && floor(d) == d; } -static bool JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValue val) +static bool JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) { uint32_t tag; @@ -11347,471 +12920,52 @@ static bool JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValue val) u.d = JS_VALUE_GET_FLOAT64(val); return (u.u64 >> 63); } + case JS_TAG_SHORT_BIG_INT: + return (JS_VALUE_GET_SHORT_BIG_INT(val) < 0); case JS_TAG_BIG_INT: { JSBigInt *p = JS_VALUE_GET_PTR(val); - /* Note: integer zeros are not necessarily positive */ - return p->num.sign && !bf_is_zero(&p->num); + return js_bigint_sign(p); } default: return false; } } -static JSValue js_bigint_to_string1(JSContext *ctx, JSValue val, int radix) -{ - JSValue ret; - bf_t a_s, *a; - char *str; - int saved_sign; - size_t len; - - a = JS_ToBigInt(ctx, &a_s, val); - if (!a) - return JS_EXCEPTION; - saved_sign = a->sign; - if (a->expn == BF_EXP_ZERO) - a->sign = 0; - str = bf_ftoa(&len, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC | - BF_FTOA_JS_QUIRKS); - a->sign = saved_sign; - JS_FreeBigInt(ctx, a, &a_s); - if (!str) - return JS_ThrowOutOfMemory(ctx); - ret = js_new_string8_len(ctx, str, len); - bf_free(ctx->bf_ctx, str); - return ret; -} - -static JSValue js_bigint_to_string(JSContext *ctx, JSValue val) +static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val) { return js_bigint_to_string1(ctx, val, 10); } /*---- floating point number to string conversions ----*/ -/* JavaScript rounding is specified as round to nearest tie away - from zero (RNDNA), but in `printf` the "ties" case is not - specified (in most cases it is RNDN, round to nearest, tie to even), - so we must round manually. We generate 2 extra places and make - an extra call to snprintf if these are exactly '50'. - We set the current rounding mode to FE_DOWNWARD to check if the - last 2 places become '49'. If not, we must round up, which is - performed in place using the string digits. - - Note that we cannot rely on snprintf for rounding up: - the code below fails on macOS for `0.5.toFixed(0)`: gives `0` expected `1` - fesetround(FE_UPWARD); - snprintf(dest, size, "%.*f", n_digits, d); - fesetround(FE_TONEAREST); - */ - -/* `js_fcvt` minimum buffer length: - - up to 21 digits in integral part - - 1 potential decimal point - - up to 102 decimals - - 1 null terminator - */ -#define JS_FCVT_BUF_SIZE (21+1+102+1) - -/* `js_ecvt` minimum buffer length: - - 1 leading digit - - 1 potential decimal point - - up to 102 decimals - - 5 exponent characters (from 'e-324' to 'e+308') - - 1 null terminator - */ -#define JS_ECVT_BUF_SIZE (1+1+102+5+1) - -/* `js_dtoa` minimum buffer length: - - 8 byte prefix - - either JS_FCVT_BUF_SIZE or JS_ECVT_BUF_SIZE - - JS_FCVT_BUF_SIZE is larger than JS_ECVT_BUF_SIZE - */ -#define JS_DTOA_BUF_SIZE (8+JS_FCVT_BUF_SIZE) - -/* `js_ecvt1`: compute the digits and decimal point spot for a double - - `d` is finite, positive or zero - - `n_digits` number of significant digits in range 1..103 - - `buf` receives the printf result - - `buf` has a fixed format: n_digits with a decimal point at offset 1 - and exponent 'e{+/-}xx[x]' at offset n_digits+1 - Return n_digits - Store the position of the decimal point into `*decpt` - */ -static int js_ecvt1(double d, int n_digits, - char dest[minimum_length(JS_ECVT_BUF_SIZE)], - size_t size, int *decpt) -{ - /* d is positive, ensure decimal point is always present */ - snprintf(dest, size, "%#.*e", n_digits - 1, d); - /* dest contents: - 0: first digit - 1: '.' decimal point (locale specific) - 2..n_digits: (n_digits-1) additional digits - n_digits+1: 'e' exponent mark - n_digits+2..: exponent sign, value and null terminator - */ - /* extract the exponent (actually the position of the decimal point) */ - *decpt = 1 + atoi(dest + n_digits + 2); - return n_digits; -} - -/* `js_ecvt`: compute the digits and decimal point spot for a double - with proper javascript rounding. We cannot use `ecvt` for multiple - resasons: portability, because of the number of digits is typically - limited to 17, finally because the default rounding is inadequate. - `d` is finite and positive or zero. - `n_digits` number of significant digits in range 1..101 - or 0 for automatic (only as many digits as necessary) - Return the number of digits produced in `dest`. - Store the position of the decimal point into `*decpt` - */ -static int js_ecvt(double d, int n_digits, - char dest[minimum_length(JS_ECVT_BUF_SIZE)], - size_t size, int *decpt) -{ - if (n_digits == 0) { - /* find the minimum number of digits (XXX: inefficient but simple) */ - // TODO(chqrlie) use direct method from quickjs-printf - unsigned int n_digits_min = 1; - unsigned int n_digits_max = 17; - for (;;) { - n_digits = (n_digits_min + n_digits_max) / 2; - js_ecvt1(d, n_digits, dest, size, decpt); - if (n_digits_min == n_digits_max) - return n_digits; - /* dest contents: - 0: first digit - 1: '.' decimal point (locale specific) - 2..n_digits: (n_digits-1) additional digits - n_digits+1: 'e' exponent mark - n_digits+2..: exponent sign, value and null terminator - */ - if (strtod(dest, NULL) == d) { - unsigned int n0 = n_digits; - /* enough digits */ - /* strip the trailing zeros */ - while (dest[n_digits] == '0') - n_digits--; - if (n_digits == n_digits_min) - return n_digits; - /* done if trailing zeros and not denormal or huge */ - if (n_digits < n0 && d > 3e-308 && d < 8e307) - return n_digits; - n_digits_max = n_digits; - } else { - /* need at least one more digit */ - n_digits_min = n_digits + 1; - } - } - } else { -#if defined(FE_DOWNWARD) && defined(FE_TONEAREST) - int i; - /* generate 2 extra digits: 99% chances to avoid 2 calls */ - js_ecvt1(d, n_digits + 2, dest, size, decpt); - if (dest[n_digits + 1] < '5') - return n_digits; /* truncate the 2 extra digits */ - if (dest[n_digits + 1] == '5' && dest[n_digits + 2] == '0') { - /* close to half-way: try rounding toward 0 */ - fesetround(FE_DOWNWARD); - js_ecvt1(d, n_digits + 2, dest, size, decpt); - fesetround(FE_TONEAREST); - if (dest[n_digits + 1] < '5') - return n_digits; /* truncate the 2 extra digits */ - } - /* round up in the string */ - for(i = n_digits;; i--) { - /* ignore the locale specific decimal point */ - if (is_digit(dest[i])) { - if (dest[i]++ < '9') - break; - dest[i] = '0'; - if (i == 0) { - dest[0] = '1'; - (*decpt)++; - break; - } - } - } - return n_digits; /* truncate the 2 extra digits */ -#else - /* No disambiguation available, eg: __wasi__ targets */ - return js_ecvt1(d, n_digits, dest, size, decpt); -#endif - } -} - -/* `js_fcvt`: convert a floating point value to %f format using RNDNA - `d` is finite and positive or zero. - `n_digits` number of decimal places in range 0..100 - Return the number of characters produced in `dest`. - */ -static size_t js_fcvt(double d, int n_digits, - char dest[minimum_length(JS_FCVT_BUF_SIZE)], size_t size) -{ -#if defined(FE_DOWNWARD) && defined(FE_TONEAREST) - int i, n1; - /* generate 2 extra digits: 99% chances to avoid 2 calls */ - n1 = snprintf(dest, size, "%.*f", n_digits + 2, d) - 2; - if (dest[n1] >= '5') { - if (dest[n1] == '5' && dest[n1 + 1] == '0') { - /* close to half-way: try rounding toward 0 */ - fesetround(FE_DOWNWARD); - n1 = snprintf(dest, size, "%.*f", n_digits + 2, d) - 2; - fesetround(FE_TONEAREST); - } - if (dest[n1] >= '5') { /* number should be rounded up */ - /* d is either exactly half way or greater: round the string manually */ - for (i = n1 - 1;; i--) { - /* ignore the locale specific decimal point */ - if (is_digit(dest[i])) { - if (dest[i]++ < '9') - break; - dest[i] = '0'; - if (i == 0) { - dest[0] = '1'; - dest[n1] = '0'; - dest[n1 - n_digits - 1] = '0'; - dest[n1 - n_digits] = '.'; - n1++; - break; - } - } - } - } - } - /* truncate the extra 2 digits and the decimal point if !n_digits */ - n1 -= !n_digits; - //dest[n1] = '\0'; // optional - return n1; -#else - /* No disambiguation available, eg: __wasi__ targets */ - return snprintf(dest, size, "%.*f", n_digits, d); -#endif -} - -static JSValue js_dtoa_infinite(JSContext *ctx, double d) +static JSValue js_dtoa2(JSContext *ctx, + double d, int radix, int n_digits, int flags) { - // TODO(chqrlie) use atoms for NaN and Infinite? - if (isnan(d)) - return js_new_string8(ctx, "NaN"); - if (d < 0) - return js_new_string8(ctx, "-Infinity"); - else - return js_new_string8(ctx, "Infinity"); -} - -#define JS_DTOA_TOSTRING 0 /* use as many digits as necessary */ -#define JS_DTOA_EXPONENTIAL 1 /* use exponential notation either fixed or variable digits */ -#define JS_DTOA_FIXED 2 /* force fixed number of fractional digits */ -#define JS_DTOA_PRECISION 3 /* use n_digits significant digits (1 <= n_digits <= 101) */ - -/* `js_dtoa`: convert a floating point number to a string - - `mode`: one of the 4 supported formats - - `n_digits`: digit number according to mode - - TOSTRING: 0 only. As many digits as necessary - - EXPONENTIAL: 0 as many decimals as necessary - - 1..101 number of significant digits - - FIXED: 0..100 number of decimal places - - PRECISION: 1..101 number of significant digits - */ -// XXX: should use libbf or quickjs-printf. -static JSValue js_dtoa(JSContext *ctx, double d, int n_digits, int mode) -{ - char buf[JS_DTOA_BUF_SIZE]; - size_t len; - char *start; - int sign, decpt, exp, i, k, n, n_max; - - if (!isfinite(d)) - return js_dtoa_infinite(ctx, d); - - sign = (d < 0); - start = buf + 8; - d = fabs(d); /* also converts -0 to 0 */ - - if (mode != JS_DTOA_EXPONENTIAL && n_digits == 0) { - /* fast path for exact integers in variable format: - clip to MAX_SAFE_INTEGER because to ensure insignificant - digits are generated as 0. - used for JS_DTOA_TOSTRING and JS_DTOA_FIXED without decimals. - */ - if (d <= (double)MAX_SAFE_INTEGER) { - uint64_t u64 = (uint64_t)d; - if (d == u64) { - len = u64toa(start, u64); - goto done; - } - } - } - if (mode == JS_DTOA_FIXED) { - len = js_fcvt(d, n_digits, start, sizeof(buf) - 8); - // TODO(chqrlie) patch the locale specific decimal point - goto done; - } - - n_max = (n_digits > 0) ? n_digits : 21; - /* the number has k digits (1 <= k <= n_max) */ - k = js_ecvt(d, n_digits, start, sizeof(buf) - 8, &decpt); - /* buffer contents: - 0: first digit - 1: '.' decimal point - 2..k: (k-1) additional digits - */ - n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */ - if (mode != JS_DTOA_EXPONENTIAL) { - /* mode is JS_DTOA_PRECISION or JS_DTOA_TOSTRING */ - if (n >= 1 && n <= n_max) { - /* between 1 and n_max digits before the decimal point */ - if (k <= n) { - /* all digits before the point, append zeros */ - start[1] = start[0]; - start++; - for(i = k; i < n; i++) - start[i] = '0'; - len = n; - } else { - /* k > n: move digits before the point */ - for(i = 1; i < n; i++) - start[i] = start[i + 1]; - start[i] = '.'; - len = 1 + k; - } - goto done; - } - if (n >= -5 && n <= 0) { - /* insert -n leading 0 decimals and a '0.' prefix */ - n = -n; - start[1] = start[0]; - start -= n + 1; - start[0] = '0'; - start[1] = '.'; - for(i = 0; i < n; i++) - start[2 + i] = '0'; - len = 2 + k + n; - goto done; - } - } - /* exponential notation */ - exp = n - 1; - /* count the digits and the decimal point if at least one decimal */ - len = k + (k > 1); - start[1] = '.'; /* patch the locale specific decimal point */ - start[len] = 'e'; - start[len + 1] = '+'; - if (exp < 0) { - start[len + 1] = '-'; - exp = -exp; - } - len += 2 + 1 + (exp > 9) + (exp > 99); - for (i = len - 1; exp > 9;) { - int quo = exp / 10; - start[i--] = (char)('0' + exp % 10); - exp = quo; - } - start[i] = (char)('0' + exp); - - done: - start[-1] = '-'; /* prepend the sign if negative */ - return js_new_string8_len(ctx, start - sign, len + sign); -} + char static_buf[128], *buf, *tmp_buf; + int len, len_max; + JSValue res; + JSDTOATempMem dtoa_mem; + len_max = js_dtoa_max_len(d, radix, n_digits, flags); -/* `js_dtoa_radix`: convert a floating point number using a specific base - - `d` must be finite - - `radix` must be in range 2..36 - */ -static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix) -{ - char buf[2200], *ptr, *ptr2, *ptr3; - int sign, digit; - double frac, d0; - int64_t n0; - - if (!isfinite(d)) - return js_dtoa_infinite(ctx, d); - - sign = (d < 0); - d = fabs(d); - d0 = trunc(d); - n0 = 0; - frac = d - d0; - ptr2 = buf + 1100; /* ptr2 points to the end of the string */ - ptr = ptr2; /* ptr points to the beginning of the string */ - if (d0 <= MAX_SAFE_INTEGER) { - int64_t n = n0 = (int64_t)d0; - while (n >= radix) { - digit = n % radix; - n = n / radix; - *--ptr = digits36[digit]; - } - *--ptr = digits36[(size_t)n]; - } else { - /* no decimals */ - while (d0 >= radix) { - digit = fmod(d0, radix); - d0 = trunc(d0 / radix); - if (d0 >= MAX_SAFE_INTEGER) - digit = 0; - *--ptr = digits36[digit]; - } - *--ptr = digits36[(size_t)d0]; - goto done; - } - if (frac != 0) { - double log2_radix = log2(radix); - double prec = 1023 + 51; // handle subnormals - *ptr2++ = '.'; - while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) { - frac *= radix; - digit = trunc(frac); - frac -= digit; - *ptr2++ = digits36[digit]; - n0 = n0 * radix + digit; - prec -= log2_radix; - } - if (frac * radix >= radix / 2) { - /* round up the string representation manually */ - char nine = digits36[radix - 1]; - while (ptr2[-1] == nine) { - /* strip trailing '9' or equivalent digits */ - ptr2--; - } - if (ptr2[-1] == '.') { - /* strip the 'decimal' point */ - ptr2--; - /* increment the integral part */ - for (ptr3 = ptr2;;) { - if (ptr3[-1] != nine) { - ptr3[-1] = (ptr3[-1] == '9') ? 'a' : ptr3[-1] + 1; - break; - } - *--ptr3 = '0'; - if (ptr3 <= ptr) { - /* prepend a '1' if number was all nines */ - *--ptr = '1'; - break; - } - } - } else { - /* increment the last fractional digit */ - ptr2[-1] = (ptr2[-1] == '9') ? 'a' : ptr2[-1] + 1; - } - } else { - /* strip trailing fractional zeros */ - while (ptr2[-1] == '0') - ptr2--; - /* strip the 'decimal' point if last */ - ptr2 -= (ptr2[-1] == '.'); - } + /* longer buffer may be used if radix != 10 */ + if (len_max > sizeof(static_buf) - 1) { + tmp_buf = js_malloc(ctx, len_max + 1); + if (!tmp_buf) + return JS_EXCEPTION; + buf = tmp_buf; + } else { + tmp_buf = NULL; + buf = static_buf; } -done: - ptr[-1] = '-'; - ptr -= sign; - return js_new_string8_len(ctx, ptr, ptr2 - ptr); + len = js_dtoa(buf, d, radix, n_digits, flags, &dtoa_mem); + res = js_new_string8_len(ctx, buf, len); + js_free(ctx, tmp_buf); + return res; } -JSValue JS_ToStringInternal(JSContext *ctx, JSValue val, bool is_ToPropertyKey) +static JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, + int flags) { uint32_t tag; char buf[32]; @@ -11834,12 +12988,14 @@ JSValue JS_ToStringInternal(JSContext *ctx, JSValue val, bool is_ToPropertyKey) case JS_TAG_EXCEPTION: return JS_EXCEPTION; case JS_TAG_OBJECT: - { + if (flags & JS_TO_STRING_NO_SIDE_EFFECTS) { + return js_new_string8(ctx, "{}"); + } else { JSValue val1, ret; val1 = JS_ToPrimitive(ctx, val, HINT_STRING); if (JS_IsException(val1)) return val1; - ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey); + ret = JS_ToStringInternal(ctx, val1, flags); JS_FreeValue(ctx, val1); return ret; } @@ -11847,13 +13003,15 @@ JSValue JS_ToStringInternal(JSContext *ctx, JSValue val, bool is_ToPropertyKey) case JS_TAG_FUNCTION_BYTECODE: return js_new_string8(ctx, "[function bytecode]"); case JS_TAG_SYMBOL: - if (is_ToPropertyKey) { + if (flags & JS_TO_STRING_IS_PROPERTY_KEY) { return js_dup(val); } else { return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); } case JS_TAG_FLOAT64: - return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 0, JS_DTOA_TOSTRING); + return js_dtoa2(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0, + JS_DTOA_FORMAT_FREE); + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: return js_bigint_to_string(ctx, val); case JS_TAG_UNINITIALIZED: @@ -11863,9 +13021,9 @@ JSValue JS_ToStringInternal(JSContext *ctx, JSValue val, bool is_ToPropertyKey) } } -JSValue JS_ToString(JSContext *ctx, JSValue val) +JSValue JS_ToString(JSContext *ctx, JSValueConst val) { - return JS_ToStringInternal(ctx, val, false); + return JS_ToStringInternal(ctx, val, /*flags*/0); } static JSValue JS_ToStringFree(JSContext *ctx, JSValue val) @@ -11883,12 +13041,18 @@ static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val) return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL); } -JSValue JS_ToPropertyKey(JSContext *ctx, JSValue val) +static JSValue JS_ToPropertyKeyInternal(JSContext *ctx, JSValueConst val, + int flags) +{ + return JS_ToStringInternal(ctx, val, flags | JS_TO_STRING_IS_PROPERTY_KEY); +} + +JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val) { - return JS_ToStringInternal(ctx, val, true); + return JS_ToPropertyKeyInternal(ctx, val, /*flags*/0); } -static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValue val) +static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val) { uint32_t tag = JS_VALUE_GET_TAG(val); if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) @@ -11896,7 +13060,7 @@ static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValue val) return JS_ToString(ctx, val); } -static JSValue JS_ToQuotedString(JSContext *ctx, JSValue val1) +static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1) { JSValue val; JSString *p; @@ -12107,7 +13271,7 @@ static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) } } -static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValue val) +static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValueConst val) { uint32_t tag = JS_VALUE_GET_NORM_TAG(val); const char *str; @@ -12139,14 +13303,26 @@ static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValue val) case JS_TAG_FLOAT64: printf("%.14g", JS_VALUE_GET_FLOAT64(val)); break; + case JS_TAG_SHORT_BIG_INT: + printf("%" PRId64 "n", (int64_t)JS_VALUE_GET_SHORT_BIG_INT(val)); + break; case JS_TAG_BIG_INT: { JSBigInt *p = JS_VALUE_GET_PTR(val); - char *str; - str = bf_ftoa(NULL, &p->num, 10, 0, - BF_RNDZ | BF_FTOA_FORMAT_FRAC); - printf("%sn", str); - bf_realloc(&rt->bf_ctx, str, 0); + int sgn, i; + /* In order to avoid allocations we just dump the limbs */ + sgn = js_bigint_sign(p); + if (sgn) + printf("BigInt.asIntN(%d,", p->len * JS_LIMB_BITS); + printf("0x"); + for(i = p->len - 1; i >= 0; i--) { + if (i != p->len - 1) + printf("_"); + printf("%08x", p->tab[i]); + } + printf("n"); + if (sgn) + printf(")"); } break; case JS_TAG_STRING: @@ -12193,8 +13369,17 @@ static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValue val) } } +bool JS_IsArray(JSValueConst val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_ARRAY; + } + return false; +} + /* return -1 if exception (proxy case) or true/false */ -int JS_IsArray(JSContext *ctx, JSValue val) +static int js_is_array(JSContext *ctx, JSValueConst val) { JSObject *p; if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { @@ -12210,92 +13395,49 @@ int JS_IsArray(JSContext *ctx, JSValue val) static double js_math_pow(double a, double b) { + double d; + if (unlikely(!isfinite(b)) && fabs(a) == 1) { /* not compatible with IEEE 754 */ - return JS_FLOAT64_NAN; + d = NAN; } else { - return pow(a, b); + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + d = pow(a, b); + JS_X87_FPCW_RESTORE(fpcw); } + return d; } JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) { - JSValue val; - bf_t *a; - val = JS_NewBigInt(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigInt(val); - if (bf_set_si(a, v)) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); + if (v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX) { + return __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *p; + p = js_bigint_new_si64(ctx, v); + if (!p) + return JS_EXCEPTION; + return JS_MKPTR(JS_TAG_BIG_INT, p); } - return val; } JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) { - JSValue val; - bf_t *a; - val = JS_NewBigInt(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigInt(val); - if (bf_set_ui(a, v)) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); - } - - return val; -} - -/* if the returned bigint is allocated it is equal to - 'buf'. Otherwise it is a pointer to the bigint in 'val'. Return - NULL in case of error. */ -// TODO(bnoordhuis) Merge with JS_ToBigInt() -static bf_t *JS_ToBigInt1(JSContext *ctx, bf_t *buf, JSValue val) -{ - uint32_t tag; - bf_t *r; - JSBigInt *p; - - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - r = buf; - bf_init(ctx->bf_ctx, r); - if (bf_set_si(r, JS_VALUE_GET_INT(val))) - goto fail; - break; - case JS_TAG_FLOAT64: - r = buf; - bf_init(ctx->bf_ctx, r); - if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) { - fail: - bf_delete(r); - return NULL; - } - break; - case JS_TAG_BIG_INT: - p = JS_VALUE_GET_PTR(val); - r = &p->num; - break; - case JS_TAG_UNDEFINED: - default: - r = buf; - bf_init(ctx->bf_ctx, r); - bf_set_nan(r); - break; + if (v <= JS_SHORT_BIG_INT_MAX) { + return __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *p; + p = js_bigint_new_ui64(ctx, v); + if (!p) + return JS_EXCEPTION; + return JS_MKPTR(JS_TAG_BIG_INT, p); } - return r; } /* return NaN if bad bigint literal */ static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) { - const char *str; + const char *str, *p; size_t len; int flags; @@ -12303,11 +13445,21 @@ static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) JS_FreeValue(ctx, val); if (!str) return JS_EXCEPTION; - flags = ATOD_WANT_BIG_INT | - ATOD_TRIM_SPACES | ATOD_ACCEPT_EMPTY | - ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT | - ATOD_DECIMAL_AFTER_SIGN | ATOD_NO_TRAILING_CHARS; - val = js_atof(ctx, str, len, NULL, 10, flags); + p = str; + p += skip_spaces(p); + if ((p - str) == len) { + val = JS_NewBigInt64(ctx, 0); + } else { + flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; + val = js_atof(ctx, p, &p, 0, flags); + p += skip_spaces(p); + if (!JS_IsException(val)) { + if ((p - str) != len) { + JS_FreeValue(ctx, val); + val = JS_NAN; + } + } + } JS_FreeCString(ctx, str); return val; } @@ -12320,216 +13472,82 @@ static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) return val; } -/* if the returned bigint is allocated it is equal to - 'buf'. Otherwise it is a pointer to the bigint in 'val'. */ -static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) +/* JS Numbers are not allowed */ +static JSValue JS_ToBigIntFree(JSContext *ctx, JSValue val) { uint32_t tag; - bf_t *r; - JSBigInt *p; redo: tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + break; case JS_TAG_INT: case JS_TAG_NULL: case JS_TAG_UNDEFINED: case JS_TAG_FLOAT64: goto fail; case JS_TAG_BOOL: - r = buf; - bf_init(ctx->bf_ctx, r); - bf_set_si(r, JS_VALUE_GET_INT(val)); - break; - case JS_TAG_BIG_INT: - p = JS_VALUE_GET_PTR(val); - r = &p->num; + val = __JS_NewShortBigInt(ctx, JS_VALUE_GET_INT(val)); break; case JS_TAG_STRING: val = JS_StringToBigIntErr(ctx, val); if (JS_IsException(val)) - return NULL; + return val; goto redo; case JS_TAG_OBJECT: val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) - return NULL; + return val; goto redo; default: fail: JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "cannot convert to BigInt"); - return NULL; - } - return r; -} - -static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValue val) -{ - return JS_ToBigIntFree(ctx, buf, js_dup(val)); -} - -static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val) -{ - if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) { - return val; - } else { - bf_t a_s, *a, *r; - int ret; - JSValue res; - - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) - return JS_EXCEPTION; - a = JS_ToBigIntFree(ctx, &a_s, val); - if (!a) { - JS_FreeValue(ctx, res); - return JS_EXCEPTION; - } - r = JS_GetBigInt(res); - ret = bf_set(r, a); - JS_FreeBigInt(ctx, a, &a_s); - if (ret) { - JS_FreeValue(ctx, res); - return JS_ThrowOutOfMemory(ctx); - } - return JS_CompactBigInt(ctx, res); + return JS_ThrowTypeError(ctx, "cannot convert to bigint"); } + return val; } -/* free the bf_t allocated by JS_ToBigInt */ -static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf) +static JSValue JS_ToBigInt(JSContext *ctx, JSValueConst val) { - if (a == buf) { - bf_delete(a); - } else { - JSBigInt *p = (JSBigInt *)((uint8_t *)a - offsetof(JSBigInt, num)); - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_INT, p)); - } + return JS_ToBigIntFree(ctx, js_dup(val)); } /* XXX: merge with JS_ToInt64Free with a specific flag */ static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) { - bf_t a_s, *a; + uint64_t res; - a = JS_ToBigIntFree(ctx, &a_s, val); - if (!a) { + val = JS_ToBigIntFree(ctx, val); + if (JS_IsException(val)) { *pres = 0; return -1; } - bf_get_int64(pres, a, BF_GET_INT_MOD); - JS_FreeBigInt(ctx, a, &a_s); + if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { + res = JS_VALUE_GET_SHORT_BIG_INT(val); + } else { + JSBigInt *p = JS_VALUE_GET_PTR(val); + /* return the value mod 2^64 */ + res = p->tab[0]; + if (p->len >= 2) + res |= (uint64_t)p->tab[1] << 32; + JS_FreeValue(ctx, val); + } + *pres = res; return 0; } -int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValue val) +int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) { return JS_ToBigInt64Free(ctx, pres, js_dup(val)); } -int JS_ToBigUint64(JSContext *ctx, uint64_t *pres, JSValue val) +int JS_ToBigUint64(JSContext *ctx, uint64_t *pres, JSValueConst val) { return JS_ToBigInt64Free(ctx, (int64_t *)pres, js_dup(val)); } -static JSValue JS_NewBigInt(JSContext *ctx) -{ - JSBigInt *p; - p = js_malloc(ctx, sizeof(*p)); - if (!p) - return JS_EXCEPTION; - p->header.ref_count = 1; - bf_init(ctx->bf_ctx, &p->num); - return JS_MKPTR(JS_TAG_BIG_INT, p); -} - -static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val) -{ - if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT) - return val; /* fail safe */ - bf_t *a = JS_GetBigInt(val); - if (a->expn == BF_EXP_ZERO && a->sign) { - assert(((JSBigInt*)JS_VALUE_GET_PTR(val))->header.ref_count == 1); - a->sign = 0; - } - return val; -} - -/* Nnormalize the zero representation. Could also be used to convert the bigint - to a short bigint value. The reference count of the value must be - 1. Cannot fail */ -static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val) -{ - return JS_CompactBigInt1(ctx, val); -} - -static JSValue throw_bf_exception(JSContext *ctx, int status) -{ - const char *str; - if (status & BF_ST_MEM_ERROR) - return JS_ThrowOutOfMemory(ctx); - if (status & BF_ST_DIVIDE_ZERO) { - str = "division by zero"; - } else if (status & BF_ST_INVALID_OP) { - str = "invalid operation"; - } else { - str = "integer overflow"; - } - return JS_ThrowRangeError(ctx, "%s", str); -} - -static int js_unary_arith_bigint(JSContext *ctx, - JSValue *pres, OPCodeEnum op, JSValue op1) -{ - bf_t a_s, *r, *a; - int ret, v; - JSValue res; - - if (op == OP_plus) { - JS_ThrowTypeError(ctx, "BigInt argument with unary +"); - JS_FreeValue(ctx, op1); - return -1; - } - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) { - JS_FreeValue(ctx, op1); - return -1; - } - r = JS_GetBigInt(res); - a = JS_ToBigIntFree(ctx, &a_s, op1); // infallible, always a bigint - ret = 0; - switch(op) { - case OP_inc: - case OP_dec: - v = 2 * (op - OP_dec) - 1; - ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ); - break; - case OP_plus: - ret = bf_set(r, a); - break; - case OP_neg: - ret = bf_set(r, a); - bf_neg(r); - break; - case OP_not: - ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ); - bf_neg(r); - break; - default: - abort(); - } - JS_FreeBigInt(ctx, a, &a_s); - if (unlikely(ret)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - res = JS_CompactBigInt(ctx, res); - *pres = res; - return 0; -} - static no_inline __exception int js_unary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) @@ -12537,12 +13555,13 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, JSValue op1; int v; uint32_t tag; + JSBigIntBuf buf1; + JSBigInt *p1; op1 = sp[-1]; /* fast path for float64 */ if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) goto handle_float64; - op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) goto exception; @@ -12574,14 +13593,77 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, sp[-1] = js_int64(v64); } break; + case JS_TAG_SHORT_BIG_INT: + { + int64_t v; + v = JS_VALUE_GET_SHORT_BIG_INT(op1); + switch(op) { + case OP_plus: + JS_ThrowTypeError(ctx, "bigint argument with unary +"); + goto exception; + case OP_inc: + if (v == JS_SHORT_BIG_INT_MAX) + goto bigint_slow_case; + sp[-1] = __JS_NewShortBigInt(ctx, v + 1); + break; + case OP_dec: + if (v == JS_SHORT_BIG_INT_MIN) + goto bigint_slow_case; + sp[-1] = __JS_NewShortBigInt(ctx, v - 1); + break; + case OP_neg: + v = JS_VALUE_GET_SHORT_BIG_INT(op1); + if (v == JS_SHORT_BIG_INT_MIN) { + bigint_slow_case: + p1 = js_bigint_set_short(&buf1, op1); + goto bigint_slow_case1; + } + sp[-1] = __JS_NewShortBigInt(ctx, -v); + break; + default: + abort(); + } + } + break; case JS_TAG_BIG_INT: - if (js_unary_arith_bigint(ctx, sp - 1, op, op1)) - goto exception; + { + JSBigInt *r; + p1 = JS_VALUE_GET_PTR(op1); + bigint_slow_case1: + switch(op) { + case OP_plus: + JS_ThrowTypeError(ctx, "bigint argument with unary +"); + JS_FreeValue(ctx, op1); + goto exception; + case OP_inc: + case OP_dec: + { + JSBigIntBuf buf2; + JSBigInt *p2; + p2 = js_bigint_set_si(&buf2, 2 * (op - OP_dec) - 1); + r = js_bigint_add(ctx, p1, p2, 0); + } + break; + case OP_neg: + r = js_bigint_neg(ctx, p1); + break; + case OP_not: + r = js_bigint_not(ctx, p1); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + if (!r) + goto exception; + sp[-1] = JS_CompactBigInt(ctx, r); + } break; default: handle_float64: { - double d = JS_VALUE_GET_FLOAT64(op1); + double d; + d = JS_VALUE_GET_FLOAT64(op1); switch(op) { case OP_inc: case OP_dec: @@ -12627,12 +13709,19 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) { JSValue op1; - op1 = JS_ToNumericFree(ctx, sp[-1]); + op1 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) goto exception; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { - if (js_unary_arith_bigint(ctx, sp - 1, OP_not, op1)) + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) { + sp[-1] = __JS_NewShortBigInt(ctx, ~JS_VALUE_GET_SHORT_BIG_INT(op1)); + } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { + JSBigInt *r; + r = js_bigint_not(ctx, JS_VALUE_GET_PTR(op1)); + JS_FreeValue(ctx, op1); + if (!r) goto exception; + sp[-1] = JS_CompactBigInt(ctx, r); } else { int32_t v1; if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) @@ -12645,107 +13734,6 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) return -1; } -static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, - JSValue *pres, JSValue op1, JSValue op2) -{ - bf_t a_s, b_s, *r, *a, *b; - int ret; - JSValue res; - - a = JS_ToBigIntFree(ctx, &a_s, op1); - if (!a) { - JS_FreeValue(ctx, op2); - return -1; - } - b = JS_ToBigIntFree(ctx, &b_s, op2); - if (!b) { - JS_FreeBigInt(ctx, a, &a_s); - return -1; - } - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) { - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeBigInt(ctx, b, &b_s); - return -1; - } - r = JS_GetBigInt(res); - ret = 0; - switch(op) { - case OP_add: - ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ); - break; - case OP_sub: - ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ); - break; - case OP_mul: - ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ); - break; - case OP_div: - { - bf_t rem_s, *rem = &rem_s; - bf_init(ctx->bf_ctx, rem); - ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ); - bf_delete(rem); - } - break; - case OP_mod: - ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, - BF_RNDZ) & BF_ST_INVALID_OP; - break; - case OP_pow: - if (b->sign) { - ret = BF_ST_INVALID_OP; - } else { - ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS); - } - break; - - /* logical operations */ - case OP_shl: - case OP_sar: - { - slimb_t v2; -#if LIMB_BITS == 32 - bf_get_int32(&v2, b, 0); - if (v2 == INT32_MIN) - v2 = INT32_MIN + 1; -#else - bf_get_int64(&v2, b, 0); - if (v2 == INT64_MIN) - v2 = INT64_MIN + 1; -#endif - if (op == OP_sar) - v2 = -v2; - ret = bf_set(r, a); - ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ); - if (v2 < 0) { - ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR); - } - } - break; - case OP_and: - ret = bf_logic_and(r, a, b); - break; - case OP_or: - ret = bf_logic_or(r, a, b); - break; - case OP_xor: - ret = bf_logic_xor(r, a, b); - break; - default: - abort(); - } - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeBigInt(ctx, b, &b_s); - if (unlikely(ret)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - *pres = JS_CompactBigInt(ctx, res); - return 0; -} - static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { @@ -12763,7 +13751,50 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s d2 = JS_VALUE_GET_FLOAT64(op2); goto handle_float64; } - + /* fast path for short big int operations */ + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2; + js_sdlimb_t v; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + switch(op) { + case OP_sub: + v = (js_sdlimb_t)v1 - (js_sdlimb_t)v2; + break; + case OP_mul: + v = (js_sdlimb_t)v1 * (js_sdlimb_t)v2; + break; + case OP_div: + if (v2 == 0 || + ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) && + v2 == -1)) { + goto slow_big_int; + } + sp[-2] = __JS_NewShortBigInt(ctx, v1 / v2); + return 0; + case OP_mod: + if (v2 == 0 || + ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) && + v2 == -1)) { + goto slow_big_int; + } + sp[-2] = __JS_NewShortBigInt(ctx, v1 % v2); + return 0; + case OP_pow: + goto slow_big_int; + default: + abort(); + } + if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) { + sp[-2] = __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *r = js_bigint_new_di(ctx, v); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); + } + return 0; + } op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -12794,11 +13825,15 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s } break; case OP_div: - sp[-2] = js_float64((double)v1 / (double)v2); + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + sp[-2] = js_number((double)v1 / (double)v2); + JS_X87_FPCW_RESTORE(fpcw); return 0; case OP_mod: if (v1 < 0 || v2 <= 0) { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); sp[-2] = js_number(fmod(v1, v2)); + JS_X87_FPCW_RESTORE(fpcw); return 0; } else { v = (int64_t)v1 % (int64_t)v2; @@ -12811,9 +13846,47 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s abort(); } sp[-2] = js_int64(v); - } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - if (js_binary_arith_bigint(ctx, op, sp - 2, op1, op2)) + } else if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_BIG_INT) && + (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + slow_big_int: + /* bigint result */ + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + switch(op) { + case OP_add: + r = js_bigint_add(ctx, p1, p2, 0); + break; + case OP_sub: + r = js_bigint_add(ctx, p1, p2, 1); + break; + case OP_mul: + r = js_bigint_mul(ctx, p1, p2); + break; + case OP_div: + r = js_bigint_divrem(ctx, p1, p2, false); + break; + case OP_mod: + r = js_bigint_divrem(ctx, p1, p2, true); + break; + case OP_pow: + r = js_bigint_pow(ctx, p1, p2); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); } else { double dr; /* float64 result */ @@ -12824,6 +13897,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s if (JS_ToFloat64Free(ctx, &d2, op2)) goto exception; handle_float64: + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); switch(op) { case OP_sub: dr = d1 - d2; @@ -12843,6 +13917,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s default: abort(); } + JS_X87_FPCW_RESTORE(fpcw); sp[-2] = js_float64(dr); } return 0; @@ -12870,6 +13945,23 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) sp[-2] = js_float64(d1 + d2); return 0; } + /* fast path for short bigint */ + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2; + js_sdlimb_t v; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + v = (js_sdlimb_t)v1 + (js_sdlimb_t)v2; + if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) { + sp[-2] = __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *r = js_bigint_new_di(ctx, v); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); + } + return 0; + } if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); @@ -12914,9 +14006,25 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) v2 = JS_VALUE_GET_INT(op2); v = (int64_t)v1 + (int64_t)v2; sp[-2] = js_int64(v); - } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - if (js_binary_arith_bigint(ctx, OP_add, sp - 2, op1, op2)) + } else if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + /* bigint result */ + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + r = js_bigint_add(ctx, p1, p2, 0); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); } else { double d1, d2; /* float64 result */ @@ -12926,7 +14034,9 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) } if (JS_ToFloat64Free(ctx, &d2, op2)) goto exception; + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); sp[-2] = js_float64(d1 + d2); + JS_X87_FPCW_RESTORE(fpcw); } return 0; exception: @@ -12948,6 +14058,62 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2, v; + js_sdlimb_t vd; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + /* bigint fast path */ + switch(op) { + case OP_and: + v = v1 & v2; + break; + case OP_or: + v = v1 | v2; + break; + case OP_xor: + v = v1 ^ v2; + break; + case OP_sar: + if (v2 > (JS_LIMB_BITS - 1)) { + goto slow_big_int; + } else if (v2 < 0) { + if (v2 < -(JS_LIMB_BITS - 1)) + goto slow_big_int; + v2 = -v2; + goto bigint_shl; + } + bigint_sar: + v = v1 >> v2; + break; + case OP_shl: + if (v2 > (JS_LIMB_BITS - 1)) { + goto slow_big_int; + } else if (v2 < 0) { + if (v2 < -(JS_LIMB_BITS - 1)) + goto slow_big_int; + v2 = -v2; + goto bigint_sar; + } + bigint_shl: + vd = (js_dlimb_t)v1 << v2; + if (likely(vd >= JS_SHORT_BIG_INT_MIN && + vd <= JS_SHORT_BIG_INT_MAX)) { + v = vd; + } else { + JSBigInt *r = js_bigint_new_di(ctx, vd); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); + return 0; + } + break; + default: + abort(); + } + sp[-2] = __JS_NewShortBigInt(ctx, v); + return 0; + } op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -12961,15 +14127,50 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, tag1 = JS_VALUE_GET_TAG(op1); tag2 = JS_VALUE_GET_TAG(op2); - if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - if (tag1 != tag2) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - JS_ThrowTypeError(ctx, "both operands must be BigInt"); - goto exception; - } else if (js_binary_arith_bigint(ctx, op, sp - 2, op1, op2)) { - goto exception; + if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + slow_big_int: + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + switch(op) { + case OP_and: + case OP_or: + case OP_xor: + r = js_bigint_logic(ctx, p1, p2, op); + break; + case OP_shl: + case OP_sar: + { + js_slimb_t shift; + shift = js_bigint_get_si_sat(p2); + if (shift > INT32_MAX) + shift = INT32_MAX; + else if (shift < -INT32_MAX) + shift = -INT32_MAX; + if (op == OP_sar) + shift = -shift; + if (shift >= 0) + r = js_bigint_shl(ctx, p1, shift); + else + r = js_bigint_shr(ctx, p1, -shift); + } + break; + default: + abort(); } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) + goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); } else { if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { JS_FreeValue(ctx, op2); @@ -13005,49 +14206,96 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, return -1; } +/* op1 must be a bigint or int. */ +static JSBigInt *JS_ToBigIntBuf(JSContext *ctx, JSBigIntBuf *buf1, + JSValue op1) +{ + JSBigInt *p1; + + switch(JS_VALUE_GET_TAG(op1)) { + case JS_TAG_INT: + p1 = js_bigint_set_si(buf1, JS_VALUE_GET_INT(op1)); + break; + case JS_TAG_SHORT_BIG_INT: + p1 = js_bigint_set_short(buf1, op1); + break; + case JS_TAG_BIG_INT: + p1 = JS_VALUE_GET_PTR(op1); + break; + default: + abort(); + } + return p1; +} + +/* op1 and op2 must be numeric types and at least one must be a + bigint. No exception is generated. */ static int js_compare_bigint(JSContext *ctx, OPCodeEnum op, JSValue op1, JSValue op2) { - bf_t a_s, b_s, *a, *b; - int res; + int res, val, tag1, tag2; + JSBigIntBuf buf1, buf2; + JSBigInt *p1, *p2; - a = JS_ToBigInt1(ctx, &a_s, op1); - if (!a) { - JS_FreeValue(ctx, op2); - return -1; - } - b = JS_ToBigInt1(ctx, &b_s, op2); - if (!b) { - if (a == &a_s) - bf_delete(a); + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_INT) && + (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_INT)) { + /* fast path */ + js_slimb_t v1, v2; + if (tag1 == JS_TAG_INT) + v1 = JS_VALUE_GET_INT(op1); + else + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + if (tag2 == JS_TAG_INT) + v2 = JS_VALUE_GET_INT(op2); + else + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + val = (v1 > v2) - (v1 < v2); + } else { + if (tag1 == JS_TAG_FLOAT64) { + p2 = JS_ToBigIntBuf(ctx, &buf2, op2); + val = js_bigint_float64_cmp(ctx, p2, JS_VALUE_GET_FLOAT64(op1)); + if (val == 2) + goto unordered; + val = -val; + } else if (tag2 == JS_TAG_FLOAT64) { + p1 = JS_ToBigIntBuf(ctx, &buf1, op1); + val = js_bigint_float64_cmp(ctx, p1, JS_VALUE_GET_FLOAT64(op2)); + if (val == 2) { + unordered: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return false; + } + } else { + p1 = JS_ToBigIntBuf(ctx, &buf1, op1); + p2 = JS_ToBigIntBuf(ctx, &buf2, op2); + val = js_bigint_cmp(ctx, p1, p2); + } JS_FreeValue(ctx, op1); - return -1; + JS_FreeValue(ctx, op2); } + switch(op) { case OP_lt: - res = bf_cmp_lt(a, b); /* if NaN return false */ + res = val < 0; break; case OP_lte: - res = bf_cmp_le(a, b); /* if NaN return false */ + res = val <= 0; break; case OP_gt: - res = bf_cmp_lt(b, a); /* if NaN return false */ + res = val > 0; break; case OP_gte: - res = bf_cmp_le(b, a); /* if NaN return false */ + res = val >= 0; break; case OP_eq: - res = bf_cmp_eq(a, b); /* if NaN return false */ + res = val == 0; break; default: abort(); } - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); return res; } @@ -13103,16 +14351,20 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, /* fast path for float64/int */ goto float64_compare; } else { - if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || - (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING))) { + if ((((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + tag2 == JS_TAG_STRING) || + ((tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) && + tag1 == JS_TAG_STRING))) { if (tag1 == JS_TAG_STRING) { op1 = JS_StringToBigInt(ctx, op1); - if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT) goto invalid_bigint_string; } if (tag2 == JS_TAG_STRING) { op2 = JS_StringToBigInt(ctx, op2); - if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT) { invalid_bigint_string: JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13136,10 +14388,9 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); - if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT || + tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) { res = js_compare_bigint(ctx, op, op1, op2); - if (res < 0) - goto exception; } else { double d1, d2; @@ -13183,8 +14434,9 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, static bool tag_is_number(uint32_t tag) { - return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || - tag == JS_TAG_FLOAT64); + return (tag == JS_TAG_INT || + tag == JS_TAG_FLOAT64 || + tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT); } static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, @@ -13231,15 +14483,18 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) || (tag2 == JS_TAG_STRING && tag_is_number(tag1))) { - if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT)) { + if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT || + tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) { if (tag1 == JS_TAG_STRING) { op1 = JS_StringToBigInt(ctx, op1); - if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT) goto invalid_bigint_string; } if (tag2 == JS_TAG_STRING) { op2 = JS_StringToBigInt(ctx, op2); - if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT ) { invalid_bigint_string: JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13321,8 +14576,10 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) goto exception; } - if ((JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || - JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) { + if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) { JS_ThrowTypeError(ctx, "BigInt operands are forbidden for >>>"); JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13340,7 +14597,6 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) return -1; } -/* XXX: Should take JSValue arguments */ static bool js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, JSStrictEqModeEnum eq_mode) { @@ -13432,20 +14688,27 @@ static bool js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, res = (d1 == d2); /* if NaN return false and +0 == -0 */ } goto done_no_free; + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: { - bf_t a_s, *a, b_s, *b; - if (tag1 != tag2) { + JSBigIntBuf buf1, buf2; + JSBigInt *p1, *p2; + + if (tag2 != JS_TAG_SHORT_BIG_INT && + tag2 != JS_TAG_BIG_INT) { res = false; break; } - a = JS_ToBigInt1(ctx, &a_s, op1); - b = JS_ToBigInt1(ctx, &b_s, op2); - res = bf_cmp_eq(a, b); - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); + + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + res = (js_bigint_cmp(ctx, p1, p2) == 0); } break; default: @@ -13463,12 +14726,12 @@ static bool js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2) return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); } -static bool js_same_value(JSContext *ctx, JSValue op1, JSValue op2) +static bool js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2) { return js_strict_eq2(ctx, js_dup(op1), js_dup(op2), JS_EQ_SAME_VALUE); } -static bool js_same_value_zero(JSContext *ctx, JSValue op1, JSValue op2) +static bool js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2) { return js_strict_eq2(ctx, js_dup(op1), js_dup(op2), JS_EQ_SAME_VALUE_ZERO); } @@ -13539,7 +14802,7 @@ static __exception int js_operator_private_in(JSContext *ctx, JSValue *sp) } JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); - sp[-2] = JS_NewBool(ctx, ret); + sp[-2] = js_bool(ret); return 0; } @@ -13584,6 +14847,7 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) tag = JS_VALUE_GET_NORM_TAG(op1); switch(tag) { + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: atom = JS_ATOM_bigint; break; @@ -13647,27 +14911,18 @@ static __exception int js_operator_delete(JSContext *ctx, JSValue *sp) return 0; } -static JSValue js_throw_type_error(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) -{ - return JS_ThrowTypeError(ctx, "invalid property access"); -} - -/* XXX: not 100% compatible, but mozilla seems to use a similar - implementation to ensure that caller in non strict mode does not - throw (ES5 compatibility) */ -static JSValue js_function_proto_caller(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); if (!b || b->is_strict_mode || !b->has_prototype) { - return js_throw_type_error(ctx, this_val, 0, NULL); + return JS_ThrowTypeError(ctx, "invalid property access"); } return JS_UNDEFINED; } static JSValue js_function_proto_fileName(JSContext *ctx, - JSValue this_val) + JSValueConst this_val) { JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); if (b) { @@ -13677,7 +14932,7 @@ static JSValue js_function_proto_fileName(JSContext *ctx, } static JSValue js_function_proto_int32(JSContext *ctx, - JSValue this_val, + JSValueConst this_val, int magic) { JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); @@ -13689,9 +14944,10 @@ static JSValue js_function_proto_int32(JSContext *ctx, } static int js_arguments_define_own_property(JSContext *ctx, - JSValue this_obj, - JSAtom prop, JSValue val, - JSValue getter, JSValue setter, int flags) + JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, + JSValueConst setter, int flags) { JSObject *p; uint32_t idx; @@ -13711,7 +14967,7 @@ static const JSClassExoticMethods js_arguments_exotic_methods = { .define_own_property = js_arguments_define_own_property, }; -static JSValue js_build_arguments(JSContext *ctx, int argc, JSValue *argv) +static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv) { JSValue val, *tab; JSProperty *pr; @@ -13763,7 +15019,7 @@ static JSValue js_build_arguments(JSContext *ctx, int argc, JSValue *argv) /* legacy arguments object: add references to the function arguments */ static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, - JSValue *argv, + JSValueConst *argv, JSStackFrame *sf, int arg_count) { JSValue val; @@ -13819,26 +15075,6 @@ static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, return JS_EXCEPTION; } -static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValue *argv) -{ - JSValue val; - int i, ret; - - val = JS_NewArray(ctx); - if (JS_IsException(val)) - return val; - for (i = first; i < argc; i++) { - ret = JS_DefinePropertyValueUint32(ctx, val, i - first, - js_dup(argv[i]), - JS_PROP_C_W_E); - if (ret < 0) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - } - return val; -} - static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) { JSObject *p; @@ -14028,8 +15264,8 @@ static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) return 0; } -static JSValue JS_GetIterator2(JSContext *ctx, JSValue obj, - JSValue method) +static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj, + JSValueConst method) { JSValue enum_obj; @@ -14043,7 +15279,7 @@ static JSValue JS_GetIterator2(JSContext *ctx, JSValue obj, return enum_obj; } -static JSValue JS_GetIterator(JSContext *ctx, JSValue obj, bool is_async) +static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, bool is_async) { JSValue method, ret, sync_iter; @@ -14078,9 +15314,9 @@ static JSValue JS_GetIterator(JSContext *ctx, JSValue obj, bool is_async) } /* return *pdone = 2 if the iterator object is not parsed */ -static JSValue JS_IteratorNext2(JSContext *ctx, JSValue enum_obj, - JSValue method, - int argc, JSValue *argv, int *pdone) +static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj, + JSValueConst method, + int argc, JSValueConst *argv, int *pdone) { JSValue obj; @@ -14091,7 +15327,7 @@ static JSValue JS_IteratorNext2(JSContext *ctx, JSValue enum_obj, if (p->class_id == JS_CLASS_C_FUNCTION && p->u.cfunc.cproto == JS_CFUNC_iterator_next) { JSCFunctionType func; - JSValue args[1]; + JSValueConst args[1]; /* in case the function expects one argument */ if (argc == 0) { @@ -14118,9 +15354,9 @@ static JSValue JS_IteratorNext2(JSContext *ctx, JSValue enum_obj, return JS_EXCEPTION; } -static JSValue JS_IteratorNext(JSContext *ctx, JSValue enum_obj, - JSValue method, - int argc, JSValue *argv, int *pdone) +static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, + JSValueConst method, + int argc, JSValueConst *argv, int *pdone) { JSValue obj, value, done_val; int done; @@ -14128,9 +15364,13 @@ static JSValue JS_IteratorNext(JSContext *ctx, JSValue enum_obj, obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); if (JS_IsException(obj)) goto fail; - if (done != 2) { - *pdone = done; + if (likely(done == 0)) { + *pdone = false; return obj; + } else if (done != 2) { + JS_FreeValue(ctx, obj); + *pdone = true; + return JS_UNDEFINED; } else { done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); if (JS_IsException(done_val)) @@ -14150,7 +15390,7 @@ static JSValue JS_IteratorNext(JSContext *ctx, JSValue enum_obj, } /* return < 0 in case of exception */ -static int JS_IteratorClose(JSContext *ctx, JSValue enum_obj, +static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, bool is_exception_pending) { JSValue method, ret, ex_obj; @@ -14298,12 +15538,12 @@ static JSValue js_create_iterator_result(JSContext *ctx, return obj; } -static JSValue js_array_iterator_next(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, +static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int *pdone, int magic); -static JSValue js_create_array_iterator(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic); +static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); static bool js_is_fast_array(JSContext *ctx, JSValue obj) { @@ -14493,7 +15733,7 @@ static __exception int JS_CopyDataProperties(JSContext *ctx, } /* only valid inside C functions */ -static JSValue JS_GetActiveFunction(JSContext *ctx) +static JSValueConst JS_GetActiveFunction(JSContext *ctx) { return ctx->rt->current_stack_frame->cur_func; } @@ -14503,10 +15743,16 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, { JSVarRef *var_ref; struct list_head *el; + JSValue *pvalue; + + if (is_arg) + pvalue = &sf->arg_buf[var_idx]; + else + pvalue = &sf->var_buf[var_idx]; list_for_each(el, &sf->var_ref_list) { var_ref = list_entry(el, JSVarRef, header.link); - if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) { + if (var_ref->pvalue == pvalue) { var_ref->header.ref_count++; return var_ref; } @@ -14517,13 +15763,8 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, return NULL; var_ref->header.ref_count = 1; var_ref->is_detached = false; - var_ref->is_arg = is_arg; - var_ref->var_idx = var_idx; list_add_tail(&var_ref->header.link, &sf->var_ref_list); - if (is_arg) - var_ref->pvalue = &sf->arg_buf[var_idx]; - else - var_ref->pvalue = &sf->var_buf[var_idx]; + var_ref->pvalue = pvalue; var_ref->value = JS_UNDEFINED; return var_ref; } @@ -14577,8 +15818,6 @@ static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom obj = JS_NewObject(ctx); if (JS_IsException(obj)) return JS_EXCEPTION; - set_cycle_flag(ctx, obj); - set_cycle_flag(ctx, this_val); ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, js_dup(this_val), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); @@ -14728,8 +15967,6 @@ static int js_op_define_class(JSContext *ctx, JSValue *sp, if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype, js_dup(proto), JS_PROP_THROW) < 0) goto fail; - set_cycle_flag(ctx, ctor); - set_cycle_flag(ctx, proto); JS_FreeValue(ctx, parent_proto); JS_FreeValue(ctx, parent_class); @@ -14752,15 +15989,10 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) { struct list_head *el, *el1; JSVarRef *var_ref; - int var_idx; list_for_each_safe(el, el1, &sf->var_ref_list) { var_ref = list_entry(el, JSVarRef, header.link); - var_idx = var_ref->var_idx; - if (var_ref->is_arg) - var_ref->value = js_dup(sf->arg_buf[var_idx]); - else - var_ref->value = js_dup(sf->var_buf[var_idx]); + var_ref->value = js_dup(*var_ref->pvalue); var_ref->pvalue = &var_ref->value; /* the reference is no longer to a local variable */ var_ref->is_detached = true; @@ -14770,13 +16002,15 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int var_idx) { + JSValue *pvalue; struct list_head *el, *el1; JSVarRef *var_ref; + pvalue = &sf->var_buf[var_idx]; list_for_each_safe(el, el1, &sf->var_ref_list) { var_ref = list_entry(el, JSVarRef, header.link); - if (var_idx == var_ref->var_idx && !var_ref->is_arg) { - var_ref->value = js_dup(sf->var_buf[var_idx]); + if (var_ref->pvalue == pvalue) { + var_ref->value = js_dup(*var_ref->pvalue); var_ref->pvalue = &var_ref->value; list_del(&var_ref->header.link); /* the reference is no longer to a local variable */ @@ -14789,16 +16023,16 @@ static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int var_idx) #define JS_CALL_FLAG_COPY_ARGV (1 << 1) #define JS_CALL_FLAG_GENERATOR (1 << 2) -static JSValue js_call_c_function(JSContext *ctx, JSValue func_obj, - JSValue this_obj, - int argc, JSValue *argv, int flags) +static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) { JSRuntime *rt = ctx->rt; JSCFunctionType func; JSObject *p; JSStackFrame sf_s, *sf = &sf_s, *prev_sf; JSValue ret_val; - JSValue *arg_buf; + JSValueConst *arg_buf; int arg_count, i; JSCFunctionEnum cproto; @@ -14816,7 +16050,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValue func_obj, ctx = p->u.cfunc.realm; /* change the current realm */ sf->is_strict_mode = false; - sf->cur_func = func_obj; + sf->cur_func = unsafe_unconst(func_obj); sf->arg_count = argc; arg_buf = argv; @@ -14829,7 +16063,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValue func_obj, arg_buf[i] = JS_UNDEFINED; sf->arg_count = arg_count; } - sf->arg_buf = arg_buf; + sf->arg_buf = (JSValue *)arg_buf; func = p->u.cfunc.c_function; switch(cproto) { @@ -14919,13 +16153,13 @@ static JSValue js_call_c_function(JSContext *ctx, JSValue func_obj, return ret_val; } -static JSValue js_call_bound_function(JSContext *ctx, JSValue func_obj, - JSValue this_obj, - int argc, JSValue *argv, int flags) +static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) { JSObject *p; JSBoundFunction *bf; - JSValue *arg_buf, new_target; + JSValueConst *arg_buf, new_target; int arg_count, i; p = JS_VALUE_GET_OBJ(func_obj); @@ -14961,6 +16195,7 @@ typedef enum { OP_SPECIAL_OBJECT_HOME_OBJECT, OP_SPECIAL_OBJECT_VAR_OBJECT, OP_SPECIAL_OBJECT_IMPORT_META, + OP_SPECIAL_OBJECT_NULL_PROTO, } OPSpecialObjectEnum; #define FUNC_RET_AWAIT 0 @@ -14973,10 +16208,22 @@ static void dump_single_byte_code(JSContext *ctx, const uint8_t *pc, static void print_func_name(JSFunctionBytecode *b); #endif +static bool needs_backtrace(JSValue exc) +{ + JSObject *p; + + if (JS_VALUE_GET_TAG(exc) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(exc); + if (p->class_id != JS_CLASS_ERROR) + return false; + return !find_own_property1(p, JS_ATOM_stack); +} + /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ -static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, - JSValue this_obj, JSValue new_target, - int argc, JSValue *argv, int flags) +static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, + JSValueConst this_obj, JSValueConst new_target, + int argc, JSValueConst *argv, int flags) { JSRuntime *rt = caller_ctx->rt; JSContext *ctx; @@ -14988,7 +16235,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval; JSVarRef **var_refs; size_t alloca_size; - JSInlineCache *ic; #ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_STEP #define DUMP_BYTECODE_OR_DONT(pc) \ @@ -15035,7 +16281,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, pc = sf->cur_pc; sf->prev_frame = rt->current_stack_frame; rt->current_stack_frame = sf; - ic = b->ic; if (s->throw_flag) goto exception; else @@ -15050,7 +16295,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, call_func = rt->class_array[p->class_id].call; if (!call_func) { not_a_function: - return JS_ThrowTypeError(caller_ctx, "not a function"); + return JS_ThrowTypeErrorNotAFunction(caller_ctx); } return call_func(caller_ctx, func_obj, this_obj, argc, argv, flags); @@ -15069,9 +16314,9 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, return JS_ThrowStackOverflow(caller_ctx); sf->is_strict_mode = b->is_strict_mode; - arg_buf = argv; + arg_buf = (JSValue *)argv; sf->arg_count = argc; - sf->cur_func = func_obj; + sf->cur_func = unsafe_unconst(func_obj); init_list_head(&sf->var_ref_list); var_refs = p->u.func.var_refs; @@ -15080,7 +16325,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, int n = min_int(argc, b->arg_count); arg_buf = local_buf; for(i = 0; i < n; i++) - arg_buf[i] = JS_DupValue(caller_ctx, argv[i]); + arg_buf[i] = js_dup(argv[i]); for(; i < b->arg_count; i++) arg_buf[i] = JS_UNDEFINED; sf->arg_count = b->arg_count; @@ -15100,7 +16345,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, sf->prev_frame = rt->current_stack_frame; rt->current_stack_frame = sf; ctx = b->realm; /* set the current realm */ - ic = b->ic; #ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_STEP if (check_dump_flag(ctx->rt, JS_DUMP_BYTECODE_STEP)) @@ -15117,6 +16361,10 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, *sp++ = js_int32(get_u32(pc)); pc += 4; BREAK; + CASE(OP_push_bigint_i32): + *sp++ = __JS_NewShortBigInt(ctx, (int)get_u32(pc)); + pc += 4; + BREAK; CASE(OP_push_const): *sp++ = js_dup(b->cpool[get_u32(pc)]); pc += 4; @@ -15149,7 +16397,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, goto exception; BREAK; CASE(OP_push_empty_string): - *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string); + *sp++ = js_empty_string(rt); BREAK; CASE(OP_get_length): { @@ -15247,6 +16495,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, if (unlikely(JS_IsException(sp[-1]))) goto exception; break; + case OP_SPECIAL_OBJECT_NULL_PROTO: + *sp++ = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OBJECT); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; default: abort(); } @@ -15254,9 +16507,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, BREAK; CASE(OP_rest): { - int first = get_u16(pc); + int i, n, first = get_u16(pc); pc += 2; - *sp++ = js_build_rest(ctx, first, argc, argv); + i = min_int(first, argc); + n = argc - i; + *sp++ = js_create_array(ctx, n, &argv[i]); if (unlikely(JS_IsException(sp[-1]))) goto exception; } @@ -15429,7 +16684,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, call_argv = sp - call_argc; sf->cur_pc = pc; ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, - JS_UNDEFINED, call_argc, call_argv, 0); + JS_UNDEFINED, call_argc, + vc(call_argv), 0); if (unlikely(JS_IsException(ret_val))) goto exception; if (opcode == OP_tail_call) @@ -15447,8 +16703,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, call_argv = sp - call_argc; sf->cur_pc = pc; ret_val = JS_CallConstructorInternal(ctx, call_argv[-2], - call_argv[-1], - call_argc, call_argv, 0); + call_argv[-1], call_argc, + vc(call_argv), 0); if (unlikely(JS_IsException(ret_val))) goto exception; for(i = -2; i < call_argc; i++) @@ -15465,7 +16721,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, call_argv = sp - call_argc; sf->cur_pc = pc; ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2], - JS_UNDEFINED, call_argc, call_argv, 0); + JS_UNDEFINED, call_argc, + vc(call_argv), 0); if (unlikely(JS_IsException(ret_val))) goto exception; if (opcode == OP_tail_call_method) @@ -15478,24 +16735,13 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, BREAK; CASE(OP_array_from): { - int i, ret; - call_argc = get_u16(pc); pc += 2; - ret_val = JS_NewArray(ctx); - if (unlikely(JS_IsException(ret_val))) - goto exception; call_argv = sp - call_argc; - for(i = 0; i < call_argc; i++) { - ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i], - JS_PROP_C_W_E | JS_PROP_THROW); - call_argv[i] = JS_UNDEFINED; - if (ret < 0) { - JS_FreeValue(ctx, ret_val); - goto exception; - } - } + ret_val = JS_NewArrayFrom(ctx, call_argc, call_argv); sp -= call_argc; + if (unlikely(JS_IsException(ret_val))) + goto exception; *sp++ = ret_val; } BREAK; @@ -15507,7 +16753,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, pc += 2; sf->cur_pc = pc; - ret_val = js_function_apply(ctx, sp[-3], 2, &sp[-2], magic); + ret_val = js_function_apply(ctx, sp[-3], 2, vc(&sp[-2]), magic); if (unlikely(JS_IsException(ret_val))) goto exception; JS_FreeValue(ctx, sp[-3]); @@ -15632,7 +16878,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, JS_EVAL_TYPE_DIRECT, scope_idx); } else { ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, - JS_UNDEFINED, call_argc, call_argv, 0); + JS_UNDEFINED, call_argc, + vc(call_argv), 0); } if (unlikely(JS_IsException(ret_val))) goto exception; @@ -15664,8 +16911,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, JS_EVAL_TYPE_DIRECT, scope_idx); } else { - ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, - tab); + ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, vc(tab)); } free_arg_list(ctx, tab, len); if (unlikely(JS_IsException(ret_val))) @@ -16300,7 +17546,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, { JSValue ret; sf->cur_pc = pc; - ret = JS_Call(ctx, sp[-3], sp[-4], 1, (sp - 1)); + ret = JS_Call(ctx, sp[-3], sp[-4], 1, vc(sp - 1)); if (JS_IsException(ret)) goto exception; JS_FreeValue(ctx, sp[-1]); @@ -16329,7 +17575,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, 0, NULL); } else { ret = JS_CallFree(ctx, method, sp[-4], - 1, (sp - 1)); + 1, vc(sp - 1)); } if (JS_IsException(ret)) goto exception; @@ -16361,39 +17607,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, { JSValue val; JSAtom atom; - JSInlineCacheUpdate icu; atom = get_u32(pc); pc += 4; sf->cur_pc = pc; - icu = (JSInlineCacheUpdate){ic, INLINE_CACHE_MISS}; - val = JS_GetPropertyInternal2(ctx, sp[-1], atom, sp[-1], &icu, false); + val = JS_GetPropertyInternal(ctx, sp[-1], atom, sp[-1], false); if (unlikely(JS_IsException(val))) goto exception; - if (icu.offset != INLINE_CACHE_MISS) { - put_u8(pc - 5, OP_get_field_ic); - put_u32(pc - 4, icu.offset); - JS_FreeAtom(ctx, atom); - } - JS_FreeValue(ctx, sp[-1]); - sp[-1] = val; - } - BREAK; - - CASE(OP_get_field_ic): - { - JSValue val; - JSAtom atom; - uint32_t ic_offset; - JSInlineCacheUpdate icu; - ic_offset = get_u32(pc); - atom = get_ic_atom(ic, ic_offset); - pc += 4; - sf->cur_pc = pc; - icu = (JSInlineCacheUpdate){ic, ic_offset}; - val = JS_GetPropertyInternalWithIC(ctx, sp[-1], atom, sp[-1], &icu, false); - if (unlikely(JS_IsException(val))) - goto exception; - assert(icu.offset == ic_offset); JS_FreeValue(ctx, sp[-1]); sp[-1] = val; } @@ -16403,85 +17622,31 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, { JSValue val; JSAtom atom; - JSInlineCacheUpdate icu; atom = get_u32(pc); pc += 4; sf->cur_pc = pc; - icu = (JSInlineCacheUpdate){ic, INLINE_CACHE_MISS}; - val = JS_GetPropertyInternal2(ctx, sp[-1], atom, sp[-1], &icu, false); + val = JS_GetPropertyInternal(ctx, sp[-1], atom, sp[-1], false); if (unlikely(JS_IsException(val))) goto exception; - if (icu.offset != INLINE_CACHE_MISS) { - put_u8(pc - 5, OP_get_field2_ic); - put_u32(pc - 4, icu.offset); - JS_FreeAtom(ctx, atom); - } *sp++ = val; } BREAK; - CASE(OP_get_field2_ic): - { - JSValue val; - JSAtom atom; - uint32_t ic_offset; - JSInlineCacheUpdate icu; - ic_offset = get_u32(pc); - atom = get_ic_atom(ic, ic_offset); - pc += 4; - sf->cur_pc = pc; - icu = (JSInlineCacheUpdate){ic, ic_offset}; - val = JS_GetPropertyInternalWithIC(ctx, sp[-1], atom, sp[-1], &icu, false); - if (unlikely(JS_IsException(val))) - goto exception; - assert(icu.offset == ic_offset); - *sp++ = val; - } - BREAK; - CASE(OP_put_field): { int ret; JSAtom atom; - JSInlineCacheUpdate icu; atom = get_u32(pc); pc += 4; sf->cur_pc = pc; - icu = (JSInlineCacheUpdate){ic, INLINE_CACHE_MISS}; ret = JS_SetPropertyInternal2(ctx, sp[-2], atom, sp[-1], sp[-2], - JS_PROP_THROW_STRICT, &icu); - JS_FreeValue(ctx, sp[-2]); - sp -= 2; - if (unlikely(ret < 0)) - goto exception; - if (icu.offset != INLINE_CACHE_MISS) { - put_u8(pc - 5, OP_put_field_ic); - put_u32(pc - 4, icu.offset); - JS_FreeAtom(ctx, atom); - } - } - BREAK; - - CASE(OP_put_field_ic): - { - int ret; - JSAtom atom; - uint32_t ic_offset; - JSInlineCacheUpdate icu; - ic_offset = get_u32(pc); - atom = get_ic_atom(ic, ic_offset); - pc += 4; - sf->cur_pc = pc; - icu = (JSInlineCacheUpdate){ic, ic_offset}; - ret = JS_SetPropertyInternalWithIC(ctx, sp[-2], atom, sp[-1], - JS_PROP_THROW_STRICT, &icu); + JS_PROP_THROW_STRICT); JS_FreeValue(ctx, sp[-2]); sp -= 2; if (unlikely(ret < 0)) goto exception; - assert(icu.offset == ic_offset); } BREAK; @@ -16718,7 +17883,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, atom = JS_ValueToAtom(ctx, sp[-1]); if (unlikely(atom == JS_ATOM_NULL)) goto exception; - val = JS_GetPropertyInternal2(ctx, sp[-2], atom, sp[-3], NULL, false); + val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], false); JS_FreeAtom(ctx, atom); if (unlikely(JS_IsException(val))) goto exception; @@ -16785,7 +17950,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, ret = JS_SetPropertyInternal2(ctx, sp[-3], atom, sp[-1], sp[-4], - JS_PROP_THROW_STRICT, NULL); + JS_PROP_THROW_STRICT); JS_FreeAtom(ctx, atom); JS_FreeValue(ctx, sp[-4]); JS_FreeValue(ctx, sp[-3]); @@ -16846,8 +18011,10 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, sp[-2] = js_int32(r); sp--; } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) + JS_VALUE_GET_FLOAT64(op2)); + JS_X87_FPCW_RESTORE(fpcw); sp--; } else { add_slow: @@ -16914,8 +18081,10 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, sp[-2] = js_int32(r); sp--; } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) - JS_VALUE_GET_FLOAT64(op2)); + JS_X87_FPCW_RESTORE(fpcw); sp--; } else { goto binary_arith_slow; @@ -16946,7 +18115,9 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, sp[-2] = js_int32(r); sp--; } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); + JS_X87_FPCW_RESTORE(fpcw); mul_fp_res: sp[-2] = js_float64(d); sp--; @@ -17333,7 +18504,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, pc += 4; sf->cur_pc = pc; - ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0); + ret = JS_DeleteGlobalVar(ctx, atom); if (unlikely(ret < 0)) goto exception; *sp++ = js_bool(ret); @@ -17539,9 +18710,13 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, } } exception: - sf->cur_pc = pc; - build_backtrace(ctx, rt->current_exception, JS_UNDEFINED, NULL, 0, 0, 0); - if (!JS_IsUncatchableError(ctx, rt->current_exception)) { + if (needs_backtrace(rt->current_exception) + || JS_IsUndefined(ctx->error_back_trace)) { + sf->cur_pc = pc; + build_backtrace(ctx, rt->current_exception, JS_UNDEFINED, + NULL, 0, 0, 0); + } + if (!JS_IsUncatchableError(rt->current_exception)) { while (sp > stack_buf) { JSValue val = *--sp; JS_FreeValue(ctx, val); @@ -17586,15 +18761,15 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, return ret_val; } -JSValue JS_Call(JSContext *ctx, JSValue func_obj, JSValue this_obj, - int argc, JSValue *argv) +JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv) { return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, argc, argv, JS_CALL_FLAG_COPY_ARGV); } -static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValue this_obj, - int argc, JSValue *argv) +static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv) { JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, argc, argv, JS_CALL_FLAG_COPY_ARGV); @@ -17604,7 +18779,7 @@ static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValue this_obj, /* warning: the refcount of the context is not incremented. Return NULL in case of exception (case of revoked proxy only) */ -static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValue func_obj) +static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj) { JSObject *p; JSContext *realm; @@ -17652,7 +18827,7 @@ static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValue func_obj) return realm; } -static JSValue js_create_from_ctor(JSContext *ctx, JSValue ctor, +static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor, int class_id) { JSValue proto, obj; @@ -17679,9 +18854,10 @@ static JSValue js_create_from_ctor(JSContext *ctx, JSValue ctor, /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ static JSValue JS_CallConstructorInternal(JSContext *ctx, - JSValue func_obj, - JSValue new_target, - int argc, JSValue *argv, int flags) + JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv, + int flags) { JSObject *p; JSFunctionBytecode *b; @@ -17693,13 +18869,13 @@ static JSValue JS_CallConstructorInternal(JSContext *ctx, goto not_a_function; p = JS_VALUE_GET_OBJ(func_obj); if (unlikely(!p->is_constructor)) - return JS_ThrowTypeError(ctx, "not a constructor"); + return JS_ThrowTypeErrorNotAConstructor(ctx, func_obj); if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { JSClassCall *call_func; call_func = ctx->rt->class_array[p->class_id].call; if (!call_func) { not_a_function: - return JS_ThrowTypeError(ctx, "not a function"); + return JS_ThrowTypeErrorNotAFunction(ctx); } return call_func(ctx, func_obj, new_target, argc, argv, flags); @@ -17726,25 +18902,25 @@ static JSValue JS_CallConstructorInternal(JSContext *ctx, } } -JSValue JS_CallConstructor2(JSContext *ctx, JSValue func_obj, - JSValue new_target, - int argc, JSValue *argv) +JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv) { return JS_CallConstructorInternal(ctx, func_obj, new_target, argc, argv, JS_CALL_FLAG_COPY_ARGV); } -JSValue JS_CallConstructor(JSContext *ctx, JSValue func_obj, - int argc, JSValue *argv) +JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, + int argc, JSValueConst *argv) { return JS_CallConstructorInternal(ctx, func_obj, func_obj, argc, argv, JS_CALL_FLAG_COPY_ARGV); } -JSValue JS_Invoke(JSContext *ctx, JSValue this_val, JSAtom atom, - int argc, JSValue *argv) +JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, + int argc, JSValueConst *argv) { JSValue func_obj; func_obj = JS_GetProperty(ctx, this_val, atom); @@ -17754,7 +18930,7 @@ JSValue JS_Invoke(JSContext *ctx, JSValue this_val, JSAtom atom, } static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, - int argc, JSValue *argv) + int argc, JSValueConst *argv) { JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv); JS_FreeValue(ctx, this_val); @@ -17763,8 +18939,9 @@ static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, /* JSAsyncFunctionState (used by generator and async functions) */ static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, - JSValue func_obj, JSValue this_obj, - int argc, JSValue *argv) + JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv) { JSObject *p; JSFunctionBytecode *b; @@ -17847,7 +19024,8 @@ static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) /* the tag does not matter provided it is not an object */ func_obj = JS_MKPTR(JS_TAG_INT, s); return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, - s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR); + s->argc, vc(s->frame.arg_buf), + JS_CALL_FLAG_GENERATOR); } @@ -17874,7 +19052,7 @@ static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s) s->state = JS_GENERATOR_STATE_COMPLETED; } -static void js_generator_finalizer(JSRuntime *rt, JSValue obj) +static void js_generator_finalizer(JSRuntime *rt, JSValueConst obj) { JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR); @@ -17889,7 +19067,7 @@ static void free_generator_stack(JSContext *ctx, JSGeneratorData *s) free_generator_stack_rt(ctx->rt, s); } -static void js_generator_mark(JSRuntime *rt, JSValue val, +static void js_generator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -17905,8 +19083,8 @@ static void js_generator_mark(JSRuntime *rt, JSValue val, #define GEN_MAGIC_RETURN 1 #define GEN_MAGIC_THROW 2 -static JSValue js_generator_next(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, +static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int *pdone, int magic) { JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR); @@ -17992,9 +19170,9 @@ static JSValue js_generator_next(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_generator_function_call(JSContext *ctx, JSValue func_obj, - JSValue this_obj, - int argc, JSValue *argv, +static JSValue js_call_generator_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) { JSValue obj, func_ret; @@ -18052,7 +19230,8 @@ static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s) } } -static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val) +static void js_async_function_resolve_finalizer(JSRuntime *rt, + JSValueConst val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSAsyncFunctionData *s = p->u.async_function_data; @@ -18061,7 +19240,7 @@ static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val) } } -static void js_async_function_resolve_mark(JSRuntime *rt, JSValue val, +static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -18102,15 +19281,16 @@ static bool js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) func_ret = async_func_resume(ctx, &s->func_state); if (JS_IsException(func_ret)) { fail: - if (unlikely(JS_IsUncatchableError(ctx, ctx->rt->current_exception))) { + if (unlikely(JS_IsUncatchableError(ctx->rt->current_exception))) { is_success = false; } else { JSValue error = JS_GetException(ctx); - ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, 1, &error); + ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, + 1, vc(&error)); JS_FreeValue(ctx, error); resolved: if (unlikely(JS_IsException(ret2))) { - if (JS_IsUncatchableError(ctx, ctx->rt->current_exception)) { + if (JS_IsUncatchableError(ctx->rt->current_exception)) { is_success = false; } else { abort(); /* BUG */ @@ -18126,7 +19306,7 @@ static bool js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) if (JS_IsUndefined(func_ret)) { /* function returned */ ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, - 1, &value); + 1, vc(&value)); JS_FreeValue(ctx, value); goto resolved; } else { @@ -18136,7 +19316,7 @@ static bool js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) /* await */ JS_FreeValue(ctx, func_ret); /* not used */ promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, &value, 0); + 1, vc(&value), 0); JS_FreeValue(ctx, value); if (JS_IsException(promise)) goto fail; @@ -18150,8 +19330,8 @@ static bool js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) for(i = 0; i < 2; i++) resolving_funcs1[i] = JS_UNDEFINED; res = perform_promise_then(ctx, promise, - resolving_funcs, - resolving_funcs1); + vc(resolving_funcs), + vc(resolving_funcs1)); JS_FreeValue(ctx, promise); for(i = 0; i < 2; i++) JS_FreeValue(ctx, resolving_funcs[i]); @@ -18163,15 +19343,15 @@ static bool js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) } static JSValue js_async_function_resolve_call(JSContext *ctx, - JSValue func_obj, - JSValue this_obj, - int argc, JSValue *argv, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) { JSObject *p = JS_VALUE_GET_OBJ(func_obj); JSAsyncFunctionData *s = p->u.async_function_data; bool is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; - JSValue arg; + JSValueConst arg; if (argc > 0) arg = argv[0]; @@ -18189,9 +19369,9 @@ static JSValue js_async_function_resolve_call(JSContext *ctx, return JS_UNDEFINED; } -static JSValue js_async_function_call(JSContext *ctx, JSValue func_obj, - JSValue this_obj, - int argc, JSValue *argv, int flags) +static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) { JSValue promise; JSAsyncFunctionData *s; @@ -18274,7 +19454,7 @@ static void js_async_generator_free(JSRuntime *rt, js_free_rt(rt, s); } -static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj) +static void js_async_generator_finalizer(JSRuntime *rt, JSValueConst obj) { JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR); @@ -18283,7 +19463,7 @@ static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj) } } -static void js_async_generator_mark(JSRuntime *rt, JSValue val, +static void js_async_generator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR); @@ -18305,9 +19485,9 @@ static void js_async_generator_mark(JSRuntime *rt, JSValue val, } static JSValue js_async_generator_resolve_function(JSContext *ctx, - JSValue this_obj, - int argc, JSValue *argv, - int magic, JSValue *func_data); + JSValueConst this_obj, + int argc, JSValueConst *argv, + int magic, JSValueConst *func_data); static int js_async_generator_resolve_function_create(JSContext *ctx, JSValue generator, @@ -18319,7 +19499,7 @@ static int js_async_generator_resolve_function_create(JSContext *ctx, for(i = 0; i < 2; i++) { func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1, - i + is_resume_next * 2, 1, &generator); + i + is_resume_next * 2, 1, vc(&generator)); if (JS_IsException(func)) { if (i == 1) JS_FreeValue(ctx, resolving_funcs[0]); @@ -18338,7 +19518,7 @@ static int js_async_generator_await(JSContext *ctx, int i, res; promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, &value, 0); + 1, vc(&value), 0); if (JS_IsException(promise)) goto fail; @@ -18353,8 +19533,8 @@ static int js_async_generator_await(JSContext *ctx, for(i = 0; i < 2; i++) resolving_funcs1[i] = JS_UNDEFINED; res = perform_promise_then(ctx, promise, - resolving_funcs, - resolving_funcs1); + vc(resolving_funcs), + vc(resolving_funcs1)); JS_FreeValue(ctx, promise); for(i = 0; i < 2; i++) JS_FreeValue(ctx, resolving_funcs[i]); @@ -18367,7 +19547,7 @@ static int js_async_generator_await(JSContext *ctx, static void js_async_generator_resolve_or_reject(JSContext *ctx, JSAsyncGeneratorData *s, - JSValue result, + JSValueConst result, int is_reject) { JSAsyncGeneratorRequest *next; @@ -18387,7 +19567,7 @@ static void js_async_generator_resolve_or_reject(JSContext *ctx, static void js_async_generator_resolve(JSContext *ctx, JSAsyncGeneratorData *s, - JSValue value, + JSValueConst value, bool done) { JSValue result; @@ -18399,7 +19579,7 @@ static void js_async_generator_resolve(JSContext *ctx, static void js_async_generator_reject(JSContext *ctx, JSAsyncGeneratorData *s, - JSValue exception) + JSValueConst exception) { js_async_generator_resolve_or_reject(ctx, s, exception, 1); } @@ -18421,13 +19601,13 @@ static int js_async_generator_completed_return(JSContext *ctx, int res; // Can fail looking up JS_ATOM_constructor when is_reject==0. - promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &value, + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&value), /*is_reject*/0); // A poisoned .constructor property is observable and the resulting // exception should be delivered to the catch handler. if (JS_IsException(promise)) { JSValue err = JS_GetException(ctx); - promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &err, + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&err), /*is_reject*/1); JS_FreeValue(ctx, err); if (JS_IsException(promise)) @@ -18443,8 +19623,8 @@ static int js_async_generator_completed_return(JSContext *ctx, resolving_funcs[0] = JS_UNDEFINED; resolving_funcs[1] = JS_UNDEFINED; res = perform_promise_then(ctx, promise, - resolving_funcs1, - resolving_funcs); + vc(resolving_funcs1), + vc(resolving_funcs)); JS_FreeValue(ctx, resolving_funcs1[0]); JS_FreeValue(ctx, resolving_funcs1[1]); JS_FreeValue(ctx, promise); @@ -18554,13 +19734,13 @@ static void js_async_generator_resume_next(JSContext *ctx, } static JSValue js_async_generator_resolve_function(JSContext *ctx, - JSValue this_obj, - int argc, JSValue *argv, - int magic, JSValue *func_data) + JSValueConst this_obj, + int argc, JSValueConst *argv, + int magic, JSValueConst *func_data) { bool is_reject = magic & 1; JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR); - JSValue arg = argv[0]; + JSValueConst arg = argv[0]; /* XXX: what if s == NULL */ @@ -18590,8 +19770,8 @@ static JSValue js_async_generator_resolve_function(JSContext *ctx, } /* magic = GEN_MAGIC_x */ -static JSValue js_async_generator_next(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, +static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR); @@ -18606,7 +19786,7 @@ static JSValue js_async_generator_next(JSContext *ctx, JSValue this_val, JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); err = JS_GetException(ctx); res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, - 1, &err); + 1, vc(&err)); JS_FreeValue(ctx, err); JS_FreeValue(ctx, res2); JS_FreeValue(ctx, resolving_funcs[0]); @@ -18633,9 +19813,10 @@ static JSValue js_async_generator_next(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_async_generator_function_call(JSContext *ctx, JSValue func_obj, - JSValue this_obj, - int argc, JSValue *argv, +static JSValue js_async_generator_function_call(JSContext *ctx, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) { JSValue obj, func_ret; @@ -18855,29 +20036,36 @@ typedef struct JSFunctionDef { struct list_head child_list; /* list of JSFunctionDef.link */ struct list_head link; - bool is_eval; /* true if eval code */ int eval_type; /* only valid if is_eval = true */ - bool is_global_var; /* true if variables are not defined locally: + + /* Pack all boolean flags together as 1-bit fields to reduce struct size + while avoiding padding and compiler deoptimization. */ + bool is_eval : 1; /* true if eval code */ + bool is_global_var : 1; /* true if variables are not defined locally: eval global, eval module or non strict eval */ - bool is_func_expr; /* true if function expression */ - bool has_home_object; /* true if the home object is available */ - bool has_prototype; /* true if a prototype field is necessary */ - bool has_simple_parameter_list; - bool has_parameter_expressions; /* if true, an argument scope is created */ - bool has_use_strict; /* to reject directive in special cases */ - bool has_eval_call; /* true if the function contains a call to eval() */ - bool has_arguments_binding; /* true if the 'arguments' binding is + bool is_func_expr : 1; /* true if function expression */ + bool has_home_object : 1; /* true if the home object is available */ + bool has_prototype : 1; /* true if a prototype field is necessary */ + bool has_simple_parameter_list : 1; + bool has_parameter_expressions : 1; /* if true, an argument scope is created */ + bool has_use_strict : 1; /* to reject directive in special cases */ + bool has_eval_call : 1; /* true if the function contains a call to eval() */ + bool has_arguments_binding : 1; /* true if the 'arguments' binding is available in the function */ - bool has_this_binding; /* true if the 'this' and new.target binding are + bool has_this_binding : 1; /* true if the 'this' and new.target binding are available in the function */ - bool new_target_allowed; /* true if the 'new.target' does not + bool new_target_allowed : 1; /* true if the 'new.target' does not throw a syntax error */ - bool super_call_allowed; /* true if super() is allowed */ - bool super_allowed; /* true if super. or super[] is allowed */ - bool arguments_allowed; /* true if the 'arguments' identifier is allowed */ - bool is_derived_class_constructor; - bool in_function_body; - bool backtrace_barrier; + bool super_call_allowed : 1; /* true if super() is allowed */ + bool super_allowed : 1; /* true if super. or super[] is allowed */ + bool arguments_allowed : 1; /* true if the 'arguments' identifier is allowed */ + bool is_derived_class_constructor : 1; + bool in_function_body : 1; + bool backtrace_barrier : 1; + bool need_home_object : 1; + bool use_short_opcodes : 1; /* true if short opcodes are used in byte_code */ + bool has_await : 1; /* true if await is used (used in module eval) */ + JSFunctionKindEnum func_kind : 8; JSParseFunctionEnum func_type : 7; uint8_t is_strict_mode : 1; @@ -18903,7 +20091,6 @@ typedef struct JSFunctionDef { int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */ int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */ int home_object_var_idx; - bool need_home_object; int scope_level; /* index into fd->scopes if the current lexical scope */ int scope_first; /* index into vd->vars of first lexically scoped variable */ @@ -18919,7 +20106,6 @@ typedef struct JSFunctionDef { DynBuf byte_code; int last_opcode_pos; /* -1 if no last opcode */ - bool use_short_opcodes; /* true if short opcodes are used in byte_code */ LabelSlot *label_slots; int label_size; /* allocated size for label_slots[] */ @@ -18957,8 +20143,6 @@ typedef struct JSFunctionDef { int source_len; JSModuleDef *module; /* != NULL when parsing a module */ - bool has_await; /* true if await is used (used in module eval) */ - JSInlineCache *ic; /* inline cache for field op */ } JSFunctionDef; typedef struct JSToken { @@ -19040,8 +20224,6 @@ static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { opcode_info[(op) >= OP_TEMP_START ? \ (op) + (OP_TEMP_END - OP_TEMP_START) : (op)] -static __exception int next_token(JSParseState *s); - static void free_token(JSParseState *s, JSToken *token) { switch(token->val) { @@ -19138,7 +20320,7 @@ int JS_PRINTF_FORMAT_ATTR(2, 3) js_parse_error(JSParseState *s, JS_PRINTF_FORMAT int backtrace_flags; va_start(ap, fmt); - JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, false); + JS_ThrowError2(ctx, JS_SYNTAX_ERROR, false, fmt, ap); va_end(ap); backtrace_flags = 0; if (s->cur_func && s->cur_func->backtrace_barrier) @@ -19148,6 +20330,10 @@ int JS_PRINTF_FORMAT_ATTR(2, 3) js_parse_error(JSParseState *s, JS_PRINTF_FORMAT return -1; } +#ifndef QJS_DISABLE_PARSER + +static __exception int next_token(JSParseState *s); + static int js_parse_expect(JSParseState *s, int tok) { char buf[ATOM_GET_STR_BUF_SIZE]; @@ -19205,6 +20391,7 @@ static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p_next; uint32_t c; StringBuffer b_s, *b = &b_s; + JSValue str; /* p points to the first byte of the template part */ if (string_buffer_init(s->ctx, b, 32)) @@ -19250,9 +20437,12 @@ static __exception int js_parse_template_part(JSParseState *s, if (string_buffer_putc(b, c)) goto fail; } + str = string_buffer_end(b); + if (JS_IsException(str)) + return -1; s->token.val = TOK_TEMPLATE; s->token.u.str.sep = c; - s->token.u.str.str = string_buffer_end(b); + s->token.u.str.str = str; s->buf_ptr = p; return 0; @@ -19271,6 +20461,7 @@ static __exception int js_parse_string(JSParseState *s, int sep, int ret; uint32_t c; StringBuffer b_s, *b = &b_s; + JSValue str; /* string */ if (string_buffer_init(s->ctx, b, 32)) @@ -19379,9 +20570,12 @@ static __exception int js_parse_string(JSParseState *s, int sep, if (string_buffer_putc(b, c)) goto fail; } + str = string_buffer_end(b); + if (JS_IsException(str)) + return -1; token->val = TOK_STRING; token->u.str.sep = c; - token->u.str.str = string_buffer_end(b); + token->u.str.str = str; *pp = p; return 0; @@ -19409,6 +20603,7 @@ static __exception int js_parse_regexp(JSParseState *s) StringBuffer b_s, *b = &b_s; StringBuffer b2_s, *b2 = &b2_s; uint32_t c; + JSValue body_str, flags_str; p = s->buf_ptr; p++; @@ -19481,9 +20676,17 @@ static __exception int js_parse_regexp(JSParseState *s) p = p_next; } + body_str = string_buffer_end(b); + flags_str = string_buffer_end(b2); + if (JS_IsException(body_str) || + JS_IsException(flags_str)) { + JS_FreeValue(s->ctx, body_str); + JS_FreeValue(s->ctx, flags_str); + return -1; + } s->token.val = TOK_REGEXP; - s->token.u.regexp.body = string_buffer_end(b); - s->token.u.regexp.flags = string_buffer_end(b2); + s->token.u.regexp.body = body_str; + s->token.u.regexp.flags = flags_str; s->buf_ptr = p; return 0; fail: @@ -19492,6 +20695,8 @@ static __exception int js_parse_regexp(JSParseState *s) return -1; } +#endif // QJS_DISABLE_PARSER + static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, char *static_buf) { @@ -19519,6 +20724,8 @@ static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, return 0; } +#ifndef QJS_DISABLE_PARSER + /* convert a TOK_IDENT to a keyword when needed */ static void update_token_ident(JSParseState *s) { @@ -19615,7 +20822,6 @@ static __exception int next_token(JSParseState *s) int c; bool ident_has_escape; JSAtom atom; - int flags, radix; if (js_check_stack_overflow(s->ctx->rt, 1000)) { JS_ThrowStackOverflow(s->ctx); @@ -19810,56 +21016,36 @@ static __exception int next_token(JSParseState *s) break; } if (p[1] >= '0' && p[1] <= '9') { - flags = ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_FLOAT; - radix = 10; goto parse_number; + } else { + goto def_token; } - goto def_token; + break; case '0': - if (is_digit(p[1])) { /* handle legacy octal */ - if (s->cur_func->is_strict_mode) { - js_parse_error(s, "Octal literals are not allowed in strict mode"); - goto fail; - } - /* Legacy octal: no separators, no suffix, no floats, - base 8 unless non octal digits are detected */ - flags = 0; - radix = 8; - while (is_digit(*p)) { - if (*p >= '8' && *p <= '9') - radix = 10; - p++; - } - p = s->token.ptr; - goto parse_number; - } - if (p[1] == '_') { - js_parse_error(s, "Numeric separator can not be used after leading 0"); + /* in strict mode, octal literals are not accepted */ + if (is_digit(p[1]) && (s->cur_func->is_strict_mode)) { + js_parse_error(s, "Octal literals are not allowed in strict mode"); goto fail; } - flags = ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT | - ATOD_ACCEPT_FLOAT | ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX; - radix = 10; goto parse_number; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* number */ + parse_number: { JSValue ret; - const char *p1; - - flags = ATOD_ACCEPT_FLOAT | ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX; - radix = 10; - parse_number: - p1 = (const char *)p; - ret = js_atof(s->ctx, p1, s->buf_end - p, &p1, radix, flags); - p = (const uint8_t *)p1; + const uint8_t *p1; + int flags; + flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL | + ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX; + ret = js_atof(s->ctx, (const char *)p, (const char **)&p, 0, + flags); if (JS_IsException(ret)) goto fail; /* reject `10instanceof Number` */ if (JS_VALUE_IS_NAN(ret) || - lre_js_is_ident_next(utf8_decode(p, &p_next))) { + lre_js_is_ident_next(utf8_decode(p, &p1))) { JS_FreeValue(s->ctx, ret); js_parse_error(s, "invalid number literal"); goto fail; @@ -20093,6 +21279,8 @@ static __exception int next_token(JSParseState *s) return -1; } +#endif // QJS_DISABLE_PARSER + static int json_parse_error(JSParseState *s, const uint8_t *curp, const char *msg) { const uint8_t *p, *line_start; @@ -20393,6 +21581,8 @@ static __exception int json_next_token(JSParseState *s) return -1; } +#ifndef QJS_DISABLE_PARSER + /* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is only set if TOK_IMPORT is returned */ /* XXX: handle all unicode cases */ @@ -20505,7 +21695,7 @@ static void skip_shebang(const uint8_t **pp, const uint8_t *buf_end) } static inline int get_prev_opcode(JSFunctionDef *fd) { - if (fd->last_opcode_pos < 0) + if (fd->last_opcode_pos < 0 || dbuf_error(&fd->byte_code)) return OP_invalid; else return fd->byte_code.buf[fd->last_opcode_pos]; @@ -20566,37 +21756,11 @@ static void emit_op(JSParseState *s, uint8_t val) static void emit_atom(JSParseState *s, JSAtom name) { - emit_u32(s, JS_DupAtom(s->ctx, name)); -} - -static force_inline uint32_t get_index_hash(JSAtom atom, int hash_bits) -{ - return (atom * 0x9e370001) >> (32 - hash_bits); -} - -static void emit_ic(JSParseState *s, JSAtom atom) -{ - uint32_t h; - JSContext *ctx; - JSInlineCache *ic; - JSInlineCacheHashSlot *ch; - - ic = s->cur_func->ic; - ctx = s->ctx; - if (ic->count + 1 >= ic->capacity && resize_ic_hash(ctx, ic)) - return; - h = get_index_hash(atom, ic->hash_bits); - for (ch = ic->hash[h]; ch != NULL; ch = ch->next) - if (ch->atom == atom) - return; - ch = js_malloc(ctx, sizeof(*ch)); - if (unlikely(!ch)) - return; - ch->atom = JS_DupAtom(ctx, atom); - ch->index = 0; - ch->next = ic->hash[h]; - ic->hash[h] = ch; - ic->count += 1; + DynBuf *bc = &s->cur_func->byte_code; + if (dbuf_realloc(bc, bc->size + 4)) + return; /* not enough memory : don't duplicate the atom */ + put_u32(bc->buf + bc->size, JS_DupAtom(s->ctx, name)); + bc->size += 4; } static int update_label(JSFunctionDef *s, int label, int delta) @@ -20610,29 +21774,33 @@ static int update_label(JSFunctionDef *s, int label, int delta) return ls->ref_count; } -static int new_label_fd(JSFunctionDef *fd, int label) +static int new_label_fd(JSFunctionDef *fd) { + int label; LabelSlot *ls; - if (label < 0) { - if (js_resize_array(fd->ctx, (void *)&fd->label_slots, + if (js_resize_array(fd->ctx, (void *)&fd->label_slots, sizeof(fd->label_slots[0]), &fd->label_size, fd->label_count + 1)) - return -1; - label = fd->label_count++; - ls = &fd->label_slots[label]; - ls->ref_count = 0; - ls->pos = -1; - ls->pos2 = -1; - ls->addr = -1; - ls->first_reloc = NULL; - } + return -1; + label = fd->label_count++; + ls = &fd->label_slots[label]; + ls->ref_count = 0; + ls->pos = -1; + ls->pos2 = -1; + ls->addr = -1; + ls->first_reloc = NULL; return label; } static int new_label(JSParseState *s) { - return new_label_fd(s->cur_func, -1); + int label; + label = new_label_fd(s->cur_func); + if (unlikely(label < 0)) { + dbuf_set_error(&s->cur_func->byte_code); + } + return label; } /* don't update the last opcode and don't emit line number info */ @@ -20660,8 +21828,11 @@ static int emit_label(JSParseState *s, int label) static int emit_goto(JSParseState *s, int opcode, int label) { if (js_is_live_code(s)) { - if (label < 0) + if (label < 0) { label = new_label(s); + if (label < 0) + return -1; + } emit_op(s, opcode); emit_u32(s, label); s->cur_func->label_slots[label].ref_count++; @@ -20690,7 +21861,7 @@ static __exception int emit_push_const(JSParseState *s, JSValue val, if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) { JSAtom atom; /* warning: JS_NewAtomStr frees the string value */ - JS_DupValue(s->ctx, val); + js_dup(val); atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val)); if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) { emit_op(s, OP_push_atom_value); @@ -20699,7 +21870,7 @@ static __exception int emit_push_const(JSParseState *s, JSValue val, } } - idx = cpool_add(s, JS_DupValue(s->ctx, val)); + idx = cpool_add(s, js_dup(val)); if (idx < 0) return -1; emit_op(s, OP_push_const); @@ -21371,7 +22542,6 @@ static __exception int js_parse_template(JSParseState *s, int call, int *argc) goto done1; emit_op(s, OP_get_field2); emit_atom(s, JS_ATOM_concat); - emit_ic(s, JS_ATOM_concat); } depth++; } else { @@ -22670,7 +23840,6 @@ static __exception int js_parse_array_literal(JSParseState *s) emit_u32(s, idx); emit_op(s, OP_put_field); emit_atom(s, JS_ATOM_length); - emit_ic(s, JS_ATOM_length); } goto done; } @@ -22709,7 +23878,6 @@ static __exception int js_parse_array_literal(JSParseState *s) emit_op(s, OP_dup1); /* array length - array array length */ emit_op(s, OP_put_field); emit_atom(s, JS_ATOM_length); - emit_ic(s, JS_ATOM_length); } else { emit_op(s, OP_drop); /* array length - array */ } @@ -22799,6 +23967,8 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, switch(opcode) { case OP_scope_get_var: label = new_label(s); + if (label < 0) + return -1; emit_op(s, OP_scope_make_ref); emit_atom(s, name); emit_u32(s, label); @@ -22810,7 +23980,6 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, case OP_get_field: emit_op(s, OP_get_field2); emit_atom(s, name); - emit_ic(s, name); break; case OP_scope_get_private_field: emit_op(s, OP_scope_get_private_field2); @@ -22835,6 +24004,8 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, switch(opcode) { case OP_scope_get_var: label = new_label(s); + if (label < 0) + return -1; emit_op(s, OP_scope_make_ref); emit_atom(s, name); emit_u32(s, label); @@ -22958,7 +24129,6 @@ static void put_lvalue(JSParseState *s, int opcode, int scope, case OP_get_field: emit_op(s, OP_put_field); emit_u32(s, name); /* name has refcount */ - emit_ic(s, name); break; case OP_scope_get_private_field: emit_op(s, OP_scope_put_private_field); @@ -23087,7 +24257,7 @@ static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name) return js_parse_error(s, "Duplicate parameter name not allowed in this context"); } -static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg) +static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, bool is_arg) { JSAtom name; @@ -23111,9 +24281,11 @@ static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg) /* Return -1 if error, 0 if no initializer, 1 if an initializer is present at the top level. */ -static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, - int hasval, int has_ellipsis, - bool allow_initializer, bool export_flag) +static int js_parse_destructuring_element(JSParseState *s, int tok, + bool is_arg, bool hasval, + int has_ellipsis, // tri-state + bool allow_initializer, + bool export_flag) { int label_parse, label_assign, label_done, label_lvalue, depth_lvalue; int start_addr, assign_addr; @@ -23230,7 +24402,6 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, /* get the named property from the source object */ emit_op(s, OP_get_field2); emit_u32(s, prop_name); - emit_ic(s, prop_name); } if (js_parse_destructuring_element(s, tok, is_arg, true, -1, true, export_flag) < 0) return -1; @@ -23320,7 +24491,6 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, /* source -- val */ emit_op(s, OP_get_field); emit_u32(s, prop_name); - emit_ic(s, prop_name); } } else { /* prop_type = PROP_TYPE_VAR, cannot be a computed property */ @@ -23352,7 +24522,6 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, /* source -- source val */ emit_op(s, OP_get_field2); emit_u32(s, prop_name); - emit_ic(s, prop_name); } set_val: if (tok) { @@ -23580,7 +24749,17 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { emit_op(s, OP_push_i32); emit_u32(s, JS_VALUE_GET_INT(val)); + } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { + int64_t v; + v = JS_VALUE_GET_SHORT_BIG_INT(val); + if (v >= INT32_MIN && v <= INT32_MAX) { + emit_op(s, OP_push_bigint_i32); + emit_u32(s, v); + } else { + goto large_number; + } } else { + large_number: if (emit_push_const(s, val, 0) < 0) return -1; } @@ -23729,7 +24908,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) { int skip_bits; if (js_parse_skip_parens_token(s, &skip_bits, false) == '=') { - if (js_parse_destructuring_element(s, 0, 0, false, skip_bits & SKIP_HAS_ELLIPSIS, true, false) < 0) + if (js_parse_destructuring_element(s, 0, false, false, skip_bits & SKIP_HAS_ELLIPSIS, true, false) < 0) return -1; } else { if (s->token.val == '{') { @@ -24143,7 +25322,6 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) } emit_op(s, OP_get_field); emit_atom(s, s->token.u.ident.atom); - emit_ic(s, s->token.u.ident.atom); } } if (next_token(s)) @@ -24729,14 +25907,12 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) emit_op(s, OP_iterator_check_object); emit_op(s, OP_get_field2); emit_atom(s, JS_ATOM_done); - emit_ic(s, JS_ATOM_done); label_next = emit_goto(s, OP_if_true, -1); /* end of loop */ emit_label(s, label_yield); if (is_async) { /* OP_async_yield_star takes the value as parameter */ emit_op(s, OP_get_field); emit_atom(s, JS_ATOM_value); - emit_ic(s, JS_ATOM_value); emit_op(s, OP_async_yield_star); } else { /* OP_yield_star takes (value, done) as parameter */ @@ -24764,12 +25940,10 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) emit_op(s, OP_iterator_check_object); emit_op(s, OP_get_field2); emit_atom(s, JS_ATOM_done); - emit_ic(s, JS_ATOM_done); emit_goto(s, OP_if_false, label_yield); emit_op(s, OP_get_field); emit_atom(s, JS_ATOM_value); - emit_ic(s, JS_ATOM_value); emit_label(s, label_return1); emit_op(s, OP_nip); @@ -24787,7 +25961,6 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) emit_op(s, OP_iterator_check_object); emit_op(s, OP_get_field2); emit_atom(s, JS_ATOM_done); - emit_ic(s, JS_ATOM_done); emit_goto(s, OP_if_false, label_yield); emit_goto(s, OP_goto, label_next); /* close the iterator and throw a type error exception */ @@ -24806,7 +25979,6 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) emit_label(s, label_next); emit_op(s, OP_get_field); emit_atom(s, JS_ATOM_value); - emit_ic(s, JS_ATOM_value); emit_op(s, OP_nip); /* keep the value associated with done = true */ emit_op(s, OP_nip); @@ -25125,7 +26297,6 @@ static void emit_return(JSParseState *s, bool hasval) emit_op(s, OP_swap); emit_op(s, OP_get_field2); emit_atom(s, JS_ATOM_return); - emit_ic(s, JS_ATOM_return); /* stack: iter_obj return_func */ emit_op(s, OP_dup); emit_op(s, OP_is_undefined_or_null); @@ -25288,7 +26459,7 @@ static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok, if ((s->token.val == '[' || s->token.val == '{') && js_parse_skip_parens_token(s, &skip_bits, false) == '=') { emit_op(s, OP_undefined); - if (js_parse_destructuring_element(s, tok, 0, true, skip_bits & SKIP_HAS_ELLIPSIS, true, export_flag) < 0) + if (js_parse_destructuring_element(s, tok, false, true, skip_bits & SKIP_HAS_ELLIPSIS, true, export_flag) < 0) return -1; } else { return js_parse_error(s, "variable name expected"); @@ -25410,7 +26581,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { if (s->token.val == '[' || s->token.val == '{') { - if (js_parse_destructuring_element(s, tok, 0, true, -1, false, false) < 0) + if (js_parse_destructuring_element(s, tok, false, true, -1, false, false) < 0) return -1; has_destructuring = true; } else { @@ -25438,7 +26609,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, int skip_bits; if ((s->token.val == '[' || s->token.val == '{') && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, false)) == TOK_IN || tok1 == TOK_OF)) { - if (js_parse_destructuring_element(s, 0, 0, true, skip_bits & SKIP_HAS_ELLIPSIS, true, false) < 0) + if (js_parse_destructuring_element(s, 0, false, true, skip_bits & SKIP_HAS_ELLIPSIS, true, false) < 0) return -1; } else { int lvalue_label; @@ -25692,7 +26863,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, case TOK_VAR: if (next_token(s)) goto fail; - if (js_parse_var(s, true, tok, false)) + if (js_parse_var(s, PF_IN_ACCEPTED, tok, /*export_flag*/false)) goto fail; if (js_parse_expect_semi(s)) goto fail; @@ -25855,7 +27026,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) { if (next_token(s)) goto fail; - if (js_parse_var(s, false, tok, false)) + if (js_parse_var(s, 0, tok, /*export_flag*/false)) goto fail; } else { if (js_parse_expr2(s, false)) @@ -26117,7 +27288,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { if (s->token.val == '[' || s->token.val == '{') { /* XXX: TOK_LET is not completely correct */ - if (js_parse_destructuring_element(s, TOK_LET, 0, true, -1, true, false) < 0) + if (js_parse_destructuring_element(s, TOK_LET, false, true, -1, true, false) < 0) goto fail; } else { js_parse_error(s, "identifier expected"); @@ -26343,6 +27514,8 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, return -1; } +#endif // QJS_DISABLE_PARSER + /* 'name' is freed */ static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name) { @@ -26428,6 +27601,8 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m) js_free(ctx, m); } +#ifndef QJS_DISABLE_PARSER + static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, JSAtom module_name) { @@ -26452,6 +27627,8 @@ static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, return i; } +#endif // QJS_DISABLE_PARSER + static JSExportEntry *find_export_entry(JSContext *ctx, const JSModuleDef *m, JSAtom export_name) { @@ -26496,6 +27673,8 @@ static JSExportEntry *add_export_entry2(JSContext *ctx, return me; } +#ifndef QJS_DISABLE_PARSER + static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m, JSAtom local_name, JSAtom export_name, JSExportTypeEnum export_type) @@ -26519,6 +27698,8 @@ static int add_star_export_entry(JSContext *ctx, JSModuleDef *m, return 0; } +#endif // QJS_DISABLE_PARSER + /* create a C module */ /* `name_str` may be pure ASCII or UTF-8 encoded */ JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, @@ -26530,6 +27711,8 @@ JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, if (name == JS_ATOM_NULL) return NULL; m = js_new_module_def(ctx, name); + if (!m) + return NULL; m->init_func = func; return m; } @@ -26987,7 +28170,7 @@ static __exception int get_exported_names(JSContext *ctx, } /* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */ -static int js_module_ns_has(JSContext *ctx, JSValue obj, JSAtom atom) +static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom) { return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL); } @@ -27242,7 +28425,7 @@ static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m) if (!var_ref) goto fail; - module_trace(ctx, "local %d: %p\n", i, var_ref); + module_trace(ctx, "local %d: %p\n", i, (void *)var_ref); var_refs[i] = var_ref; } @@ -27453,7 +28636,7 @@ static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m, var_ref->header.ref_count++; var_refs[mi->var_idx] = var_ref; - module_trace(ctx, "local export (var_ref=%p)\n", var_ref); + module_trace(ctx, "local export (var_ref=%p)\n", (void *)var_ref); } } } @@ -27601,13 +28784,14 @@ static JSValue js_import_meta(JSContext *ctx) static JSValue JS_NewModuleValue(JSContext *ctx, JSModuleDef *m) { - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); + return js_dup(JS_MKPTR(JS_TAG_MODULE, m)); } static JSValue js_load_module_rejected(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic, JSValue *func_data) + int argc, JSValueConst *argv, int magic, + JSValueConst *func_data) { - JSValueConst *resolving_funcs = (JSValueConst *)func_data; + JSValueConst *resolving_funcs = func_data; JSValueConst error; JSValue ret; @@ -27616,16 +28800,16 @@ static JSValue js_load_module_rejected(JSContext *ctx, JSValueConst this_val, error = argv[0]; else error = JS_UNDEFINED; - ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, - 1, &error); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, &error); JS_FreeValue(ctx, ret); return JS_UNDEFINED; } static JSValue js_load_module_fulfilled(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic, JSValue *func_data) + int argc, JSValueConst *argv, int magic, + JSValueConst *func_data) { - JSValueConst *resolving_funcs = (JSValueConst *)func_data; + JSValueConst *resolving_funcs = func_data; JSModuleDef *m = JS_VALUE_GET_PTR(func_data[2]); JSValue ret, ns; @@ -27633,11 +28817,10 @@ static JSValue js_load_module_fulfilled(JSContext *ctx, JSValueConst this_val, ns = JS_GetModuleNamespace(ctx, m); if (JS_IsException(ns)) { JSValue err = JS_GetException(ctx); - js_load_module_rejected(ctx, JS_UNDEFINED, 1, (JSValueConst *)&err, 0, func_data); + js_load_module_rejected(ctx, JS_UNDEFINED, 1, vc(&err), 0, func_data); return JS_UNDEFINED; } - ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, - 1, (JSValueConst *)&ns); + ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, 1, vc(&ns)); JS_FreeValue(ctx, ret); JS_FreeValue(ctx, ns); return JS_UNDEFINED; @@ -27667,8 +28850,7 @@ static void JS_LoadModuleInternal(JSContext *ctx, const char *basename, if (JS_IsException(evaluate_promise)) { fail: err = JS_GetException(ctx); - ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, - 1, (JSValueConst *)&err); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, vc(&err)); JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ JS_FreeValue(ctx, err); return; @@ -27681,7 +28863,7 @@ static void JS_LoadModuleInternal(JSContext *ctx, const char *basename, evaluate_resolving_funcs[0] = JS_NewCFunctionData(ctx, js_load_module_fulfilled, 0, 0, 3, func_data); evaluate_resolving_funcs[1] = JS_NewCFunctionData(ctx, js_load_module_rejected, 0, 0, 3, func_data); JS_FreeValue(ctx, func_obj); - ret = js_promise_then(ctx, evaluate_promise, 2, (JSValueConst *)evaluate_resolving_funcs); + ret = js_promise_then(ctx, evaluate_promise, 2, vc(evaluate_resolving_funcs)); JS_FreeValue(ctx, ret); JS_FreeValue(ctx, evaluate_resolving_funcs[0]); JS_FreeValue(ctx, evaluate_resolving_funcs[1]); @@ -27698,19 +28880,18 @@ JSValue JS_LoadModule(JSContext *ctx, const char *basename, promise = JS_NewPromiseCapability(ctx, resolving_funcs); if (JS_IsException(promise)) return JS_EXCEPTION; - JS_LoadModuleInternal(ctx, basename, filename, - (JSValueConst *)resolving_funcs); + JS_LoadModuleInternal(ctx, basename, filename, vc(resolving_funcs)); JS_FreeValue(ctx, resolving_funcs[0]); JS_FreeValue(ctx, resolving_funcs[1]); return promise; } static JSValue js_dynamic_import_job(JSContext *ctx, - int argc, JSValue *argv) + int argc, JSValueConst *argv) { - JSValue *resolving_funcs = argv; - JSValue basename_val = argv[2]; - JSValue specifier = argv[3]; + JSValueConst *resolving_funcs = argv; + JSValueConst basename_val = argv[2]; + JSValueConst specifier = argv[3]; const char *basename = NULL, *filename; JSValue ret, err; @@ -27733,15 +28914,14 @@ static JSValue js_dynamic_import_job(JSContext *ctx, return JS_UNDEFINED; exception: err = JS_GetException(ctx); - ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, - 1, &err); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, vc(&err)); JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ JS_FreeValue(ctx, err); JS_FreeCString(ctx, basename); return JS_UNDEFINED; } -static JSValue js_dynamic_import(JSContext *ctx, JSValue specifier) +static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier) { JSAtom basename; JSValue promise, resolving_funcs[2], basename_val; @@ -27765,11 +28945,11 @@ static JSValue js_dynamic_import(JSContext *ctx, JSValue specifier) args[0] = resolving_funcs[0]; args[1] = resolving_funcs[1]; args[2] = basename_val; - args[3] = specifier; + args[3] = unsafe_unconst(specifier); /* cannot run JS_LoadModuleInternal synchronously because it would cause an unexpected recursion in js_evaluate_module() */ - JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args); + JS_EnqueueJob(ctx, js_dynamic_import_job, 4, vc(args)); JS_FreeValue(ctx, basename_val); JS_FreeValue(ctx, resolving_funcs[0]); @@ -27781,11 +28961,10 @@ static void js_set_module_evaluated(JSContext *ctx, JSModuleDef *m) { m->status = JS_MODULE_STATUS_EVALUATED; if (!JS_IsUndefined(m->promise)) { - JSValue value, ret_val; + JSValue ret_val; assert(m->cycle_root == m); - value = JS_UNDEFINED; - ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED, - 1, (JSValueConst *)&value); + JSValueConst value = JS_UNDEFINED; + ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED, 1, &value); JS_FreeValue(ctx, ret_val); } } @@ -27853,7 +29032,8 @@ static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m, JSValue *pvalue); static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic, JSValue *func_data) + int argc, JSValueConst *argv, int magic, + JSValueConst *func_data) { JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]); JSValueConst error = argv[0]; @@ -27872,14 +29052,14 @@ static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst t assert(module->async_evaluation); module->eval_has_exception = true; - module->eval_exception = JS_DupValue(ctx, error); + module->eval_exception = js_dup(error); module->status = JS_MODULE_STATUS_EVALUATED; for(i = 0; i < module->async_parent_modules_count; i++) { JSModuleDef *m = module->async_parent_modules[i]; JSValue m_obj = JS_NewModuleValue(ctx, m); js_async_module_execution_rejected(ctx, JS_UNDEFINED, 1, &error, 0, - &m_obj); + vc(&m_obj)); JS_FreeValue(ctx, m_obj); } @@ -27894,7 +29074,8 @@ static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst t } static JSValue js_async_module_execution_fulfilled(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic, JSValue *func_data) + int argc, JSValueConst *argv, int magic, + JSValueConst *func_data) { JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]); ExecModuleList exec_list_s, *exec_list = &exec_list_s; @@ -27934,8 +29115,8 @@ static JSValue js_async_module_execution_fulfilled(JSContext *ctx, JSValueConst if (js_execute_sync_module(ctx, m, &error) < 0) { JSValue m_obj = JS_NewModuleValue(ctx, m); js_async_module_execution_rejected(ctx, JS_UNDEFINED, - 1, (JSValueConst *)&error, 0, - &m_obj); + 1, vc(&error), + 0, vc(&m_obj)); JS_FreeValue(ctx, m_obj); JS_FreeValue(ctx, error); } else { @@ -27955,9 +29136,9 @@ static int js_execute_async_module(JSContext *ctx, JSModuleDef *m) if (JS_IsException(promise)) return -1; m_obj = JS_NewModuleValue(ctx, m); - resolve_funcs[0] = JS_NewCFunctionData(ctx, js_async_module_execution_fulfilled, 0, 0, 1, (JSValueConst *)&m_obj); - resolve_funcs[1] = JS_NewCFunctionData(ctx, js_async_module_execution_rejected, 0, 0, 1, (JSValueConst *)&m_obj); - ret_val = js_promise_then(ctx, promise, 2, (JSValueConst *)resolve_funcs); + resolve_funcs[0] = JS_NewCFunctionData(ctx, js_async_module_execution_fulfilled, 0, 0, 1, vc(&m_obj)); + resolve_funcs[1] = JS_NewCFunctionData(ctx, js_async_module_execution_rejected, 0, 0, 1, vc(&m_obj)); + ret_val = js_promise_then(ctx, promise, 2, vc(resolve_funcs)); JS_FreeValue(ctx, ret_val); JS_FreeValue(ctx, m_obj); JS_FreeValue(ctx, resolve_funcs[0]); @@ -28025,7 +29206,7 @@ static int js_inner_module_evaluation(JSContext *ctx, JSModuleDef *m, if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || m->status == JS_MODULE_STATUS_EVALUATED) { if (m->eval_has_exception) { - *pvalue = JS_DupValue(ctx, m->eval_exception); + *pvalue = js_dup(m->eval_exception); return -1; } else { *pvalue = JS_UNDEFINED; @@ -28064,7 +29245,7 @@ static int js_inner_module_evaluation(JSContext *ctx, JSModuleDef *m, assert(m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || m1->status == JS_MODULE_STATUS_EVALUATED); if (m1->eval_has_exception) { - *pvalue = JS_DupValue(ctx, m1->eval_exception); + *pvalue = js_dup(m1->eval_exception); return -1; } } @@ -28131,7 +29312,7 @@ static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) } /* a promise may be created only on the cycle_root of a cycle */ if (!JS_IsUndefined(m->promise)) - return JS_DupValue(ctx, m->promise); + return js_dup(m->promise); m->promise = JS_NewPromiseCapability(ctx, m->resolving_funcs); if (JS_IsException(m->promise)) return JS_EXCEPTION; @@ -28143,7 +29324,7 @@ static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) assert(m1->status == JS_MODULE_STATUS_EVALUATING); m1->status = JS_MODULE_STATUS_EVALUATED; m1->eval_has_exception = true; - m1->eval_exception = JS_DupValue(ctx, result); + m1->eval_exception = js_dup(result); m1->cycle_root = m; /* spec bug: should be present */ stack_top = m1->stack_prev; } @@ -28151,25 +29332,26 @@ static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) assert(m->status == JS_MODULE_STATUS_EVALUATED); assert(m->eval_has_exception); ret_val = JS_Call(ctx, m->resolving_funcs[1], JS_UNDEFINED, - 1, (JSValueConst *)&m->eval_exception); + 1, vc(&m->eval_exception)); JS_FreeValue(ctx, ret_val); } else { assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || m->status == JS_MODULE_STATUS_EVALUATED); assert(!m->eval_has_exception); if (!m->async_evaluation) { - JSValue value; assert(m->status == JS_MODULE_STATUS_EVALUATED); - value = JS_UNDEFINED; + JSValueConst value = JS_UNDEFINED; ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED, - 1, (JSValueConst *)&value); + 1, &value); JS_FreeValue(ctx, ret_val); } assert(stack_top == NULL); } - return JS_DupValue(ctx, m->promise); + return js_dup(m->promise); } +#ifndef QJS_DISABLE_PARSER + static __exception JSAtom js_parse_from_clause(JSParseState *s) { JSAtom module_name; @@ -28193,6 +29375,23 @@ static __exception JSAtom js_parse_from_clause(JSParseState *s) return module_name; } +static bool has_unmatched_surrogate(const uint16_t *s, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) { + if (is_lo_surrogate(s[i])) + return true; + if (!is_hi_surrogate(s[i])) + continue; + if (++i == n) + return true; + if (!is_lo_surrogate(s[i])) + return true; + } + return false; +} + static __exception int js_parse_export(JSParseState *s) { JSContext *ctx = s->ctx; @@ -28225,23 +29424,40 @@ static __exception int js_parse_export(JSParseState *s) switch(tok) { case '{': first_export = m->export_entries_count; + bool has_string_binding = false; while (s->token.val != '}') { - if (!token_is_ident(s->token.val)) { - js_parse_error(s, "identifier expected"); - return -1; + if (token_is_ident(s->token.val)) { + local_name = JS_DupAtom(ctx, s->token.u.ident.atom); + } else if (s->token.val == TOK_STRING) { + local_name = JS_ValueToAtom(ctx, s->token.u.str.str); + if (local_name == JS_ATOM_NULL) + return -1; + has_string_binding = true; + } else { + return js_parse_error(s, "identifier or string expected"); } - local_name = JS_DupAtom(ctx, s->token.u.ident.atom); export_name = JS_ATOM_NULL; if (next_token(s)) goto fail; if (token_is_pseudo_keyword(s, JS_ATOM_as)) { if (next_token(s)) goto fail; - if (!token_is_ident(s->token.val)) { - js_parse_error(s, "identifier expected"); + if (token_is_ident(s->token.val)) { + export_name = JS_DupAtom(ctx, s->token.u.ident.atom); + } else if (s->token.val == TOK_STRING) { + JSString *p = JS_VALUE_GET_STRING(s->token.u.str.str); + if (p->is_wide_char && has_unmatched_surrogate(str16(p), p->len)) { + js_parse_error(s, "illegal export name"); + return -1; + } + export_name = JS_ValueToAtom(ctx, s->token.u.str.str); + if (export_name == JS_ATOM_NULL) { + return -1; + } + } else { + js_parse_error(s, "identifier or string expected"); goto fail; } - export_name = JS_DupAtom(ctx, s->token.u.ident.atom); if (next_token(s)) { fail: JS_FreeAtom(ctx, local_name); @@ -28278,6 +29494,9 @@ static __exception int js_parse_export(JSParseState *s) me->export_type = JS_EXPORT_TYPE_INDIRECT; me->u.req_module_idx = idx; } + } else if (has_string_binding) { + // Without 'from' clause, string literals cannot be used as local binding names + return js_parse_error(s, "string export name only allowed with 'from' clause"); } break; case '*': @@ -28285,11 +29504,16 @@ static __exception int js_parse_export(JSParseState *s) /* export ns from */ if (next_token(s)) return -1; - if (!token_is_ident(s->token.val)) { - js_parse_error(s, "identifier expected"); - return -1; + if (token_is_ident(s->token.val)) { + export_name = JS_DupAtom(ctx, s->token.u.ident.atom); + } else if (s->token.val == TOK_STRING) { + export_name = JS_ValueToAtom(ctx, s->token.u.str.str); + if (export_name == JS_ATOM_NULL) { + return -1; + } + } else { + return js_parse_error(s, "identifier or string expected"); } - export_name = JS_DupAtom(ctx, s->token.u.ident.atom); if (next_token(s)) goto fail1; module_name = js_parse_from_clause(s); @@ -28352,7 +29576,7 @@ static __exception int js_parse_export(JSParseState *s) case TOK_VAR: case TOK_LET: case TOK_CONST: - return js_parse_var(s, true, tok, true); + return js_parse_var(s, PF_IN_ACCEPTED, tok, /*export_flag*/true); default: return js_parse_error(s, "invalid export syntax"); } @@ -28463,11 +29687,15 @@ static __exception int js_parse_import(JSParseState *s) return -1; while (s->token.val != '}') { - if (!token_is_ident(s->token.val)) { - js_parse_error(s, "identifier expected"); - return -1; + if (token_is_ident(s->token.val)) { + import_name = JS_DupAtom(ctx, s->token.u.ident.atom); + } else if (s->token.val == TOK_STRING) { + import_name = JS_ValueToAtom(ctx, s->token.u.str.str); + if (import_name == JS_ATOM_NULL) + return -1; + } else { + return js_parse_error(s, "identifier or string expected expected"); } - import_name = JS_DupAtom(ctx, s->token.u.ident.atom); local_name = JS_ATOM_NULL; if (next_token(s)) goto fail; @@ -28606,10 +29834,11 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx, //fd->pc2line_last_line_num = line_num; //fd->pc2line_last_pc = 0; - fd->ic = init_ic(ctx); return fd; } +#endif // QJS_DISABLE_PARSER + static void free_bytecode_atoms(JSRuntime *rt, const uint8_t *bc_buf, int bc_len, bool use_short_opcodes) @@ -28633,6 +29862,8 @@ static void free_bytecode_atoms(JSRuntime *rt, case OP_FMT_atom_u16: case OP_FMT_atom_label_u8: case OP_FMT_atom_label_u16: + if ((pos + 1 + 4) > bc_len) + break; /* may happen if there is not enough memory when emiting bytecode */ atom = get_u32(bc_buf + pos + 1); JS_FreeAtomRT(rt, atom); break; @@ -28643,6 +29874,8 @@ static void free_bytecode_atoms(JSRuntime *rt, } } +#ifndef QJS_DISABLE_PARSER + static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd) { int i; @@ -28662,10 +29895,6 @@ static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd) js_free(ctx, fd->label_slots); js_free(ctx, fd->source_loc_slots); - /* free ic */ - if (fd->ic) - free_ic(ctx->rt, fd->ic); - for(i = 0; i < fd->cpool_count; i++) { JS_FreeValue(ctx, fd->cpool[i]); } @@ -28709,6 +29938,8 @@ static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd) js_free(ctx, fd); } +#endif // QJS_DISABLE_PARSER + #ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_* static const char *skip_lines(const char *p, int n) { while (p && n-- > 0 && *p) { @@ -29099,6 +30330,7 @@ static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionB int i; char atom_buf[ATOM_GET_STR_BUF_SIZE]; const char *str; + const uint8_t *op; if (b->filename != JS_ATOM_NULL) { str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->filename); @@ -29145,7 +30377,11 @@ static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionB } } printf(" stack_size: %d\n", b->stack_size); - printf(" opcodes:\n"); + printf(" byte_code_len: %d\n", b->byte_code_len); + op = b->byte_code_buf; + for (i = 0; op < &b->byte_code_buf[b->byte_code_len]; i++) + op += short_opcode_info(*op).size; + printf(" opcodes: %d\n", i); dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len, b->vardefs, b->arg_count, b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count, @@ -29160,6 +30396,8 @@ static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionB } #endif +#ifndef QJS_DISABLE_PARSER + static int add_closure_var(JSContext *ctx, JSFunctionDef *s, bool is_local, bool is_arg, int var_idx, JSAtom var_name, @@ -29443,7 +30681,13 @@ static void var_object_test(JSContext *ctx, JSFunctionDef *s, { dbuf_putc(bc, get_with_scope_opcode(op)); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - *plabel_done = new_label_fd(s, *plabel_done); + if (*plabel_done < 0) { + *plabel_done = new_label_fd(s); + if (*plabel_done < 0) { + dbuf_set_error(bc); + return; + } + } dbuf_put_u32(bc, *plabel_done); dbuf_putc(bc, is_with); update_label(s, *plabel_done, 1); @@ -30487,7 +31731,11 @@ static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, Dy evaluating the module so that the exported functions are visible if there are cyclic module references */ if (s->module) { - label_next = new_label_fd(s, -1); + label_next = new_label_fd(s); + if (label_next < 0) { + dbuf_set_error(bc); + return; + } /* if 'this' is true, initialize the global variables and return */ dbuf_putc(bc, OP_push_this); @@ -31653,6 +32901,28 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) push_short_int(&bc_out, val); break; + case OP_push_bigint_i32: + { + /* transform i32(val) neg -> i32(-val) */ + val = get_i32(bc_buf + pos + 1); + if (val != INT32_MIN + && code_match(&cc, pos_next, OP_neg, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + if (code_match(&cc, cc.pos, OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + } else { + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_push_bigint_i32); + dbuf_put_u32(&bc_out, -val); + } + pos_next = cc.pos; + break; + } + } + goto no_change; + case OP_push_const: case OP_fclosure: { @@ -32007,6 +33277,18 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) } goto no_change; + case OP_object: + if (code_match(&cc, pos_next, OP_null, OP_set_proto, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_special_object); + dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NULL_PROTO); + pos_next = cc.pos; + break; + } + goto no_change; + default: no_change: add_pc2line_info(s, bc_out.size, line_num, col_num); @@ -32445,8 +33727,10 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) are used to compile the eval and they must be ordered by scope, so it is necessary to create the closure variables before any other variable lookup is done. */ +#ifndef QJS_DISABLE_PARSER if (fd->has_eval_call) add_eval_variables(ctx, fd); +#endif // QJS_DISABLE_PARSER /* add the module global variables in the closure */ if (fd->module) { @@ -32585,13 +33869,6 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) b->arguments_allowed = fd->arguments_allowed; b->backtrace_barrier = fd->backtrace_barrier; b->realm = JS_DupContext(ctx); - b->ic = fd->ic; - fd->ic = NULL; - rebuild_ic(ctx, b->ic); - if (b->ic->count == 0) { - free_ic(ctx->rt, b->ic); - b->ic = NULL; - } add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); @@ -32612,14 +33889,14 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) return JS_EXCEPTION; } +#endif // QJS_DISABLE_PARSER + static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) { int i; - free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, true); - - if (b->ic) - free_ic(rt, b->ic); + if (b->byte_code_buf) + free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, true); if (b->vardefs) { for(i = 0; i < b->arg_count + b->var_count; i++) { @@ -32649,6 +33926,8 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) } } +#ifndef QJS_DISABLE_PARSER + static __exception int js_parse_directives(JSParseState *s) { char str[20]; @@ -33065,7 +34344,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, emit_op(s, OP_get_arg); emit_u16(s, idx); } - has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, true, -1, true, false); + has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, true, true, -1, true, false); if (has_initializer < 0) goto fail; if (has_initializer) @@ -33460,14 +34739,16 @@ static __exception int js_parse_program(JSParseState *s) return 0; } +#endif // QJS_DISABLE_PARSER + static void js_parse_init(JSContext *ctx, JSParseState *s, const char *input, size_t input_len, - const char *filename) + const char *filename, int line) { memset(s, 0, sizeof(*s)); s->ctx = ctx; s->filename = filename; - s->line_num = 1; + s->line_num = line; s->col_num = 1; s->buf_start = s->buf_ptr = (const uint8_t *)input; s->buf_end = s->buf_ptr + input_len; @@ -33479,7 +34760,7 @@ static void js_parse_init(JSContext *ctx, JSParseState *s, } static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj, - JSValue this_obj, + JSValueConst this_obj, JSVarRef **var_refs, JSStackFrame *sf) { JSValue ret_val; @@ -33515,11 +34796,13 @@ JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj) return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL); } +#ifndef QJS_DISABLE_PARSER + /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ /* `export_name` and `input` may be pure ASCII or UTF-8 encoded */ -static JSValue __JS_EvalInternal(JSContext *ctx, JSValue this_obj, +static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, const char *input, size_t input_len, - const char *filename, int flags, int scope_idx) + const char *filename, int line, int flags, int scope_idx) { JSParseState s1, *s = &s1; int err, eval_type; @@ -33531,7 +34814,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValue this_obj, JSModuleDef *m; bool is_strict_mode; - js_parse_init(ctx, s, input, input_len, filename); + js_parse_init(ctx, s, input, input_len, filename, line); skip_shebang(&s->buf_ptr, s->buf_end); eval_type = flags & JS_EVAL_TYPE_MASK; @@ -33561,7 +34844,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValue this_obj, is_strict_mode = true; } } - fd = js_new_function_def(ctx, NULL, true, false, filename, 1, 1); + fd = js_new_function_def(ctx, NULL, true, false, filename, line, 1); if (!fd) goto fail1; s->cur_func = fd; @@ -33631,20 +34914,28 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValue this_obj, return JS_EXCEPTION; } +#endif // QJS_DISABLE_PARSER + /* the indirection is needed to make 'eval' optional */ -static JSValue JS_EvalInternal(JSContext *ctx, JSValue this_obj, +static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, const char *input, size_t input_len, - const char *filename, int flags, int scope_idx) + const char *filename, int line, int flags, int scope_idx) { + JSRuntime *rt = ctx->rt; + if (unlikely(!ctx->eval_internal)) { return JS_ThrowTypeError(ctx, "eval is not supported"); } - return ctx->eval_internal(ctx, this_obj, input, input_len, filename, + if (!rt->current_stack_frame) { + JS_FreeValueRT(rt, ctx->error_back_trace); + ctx->error_back_trace = JS_UNDEFINED; + } + return ctx->eval_internal(ctx, this_obj, input, input_len, filename, line, flags, scope_idx); } -static JSValue JS_EvalObject(JSContext *ctx, JSValue this_obj, - JSValue val, int flags, int scope_idx) +static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, + JSValueConst val, int flags, int scope_idx) { JSValue ret; const char *str; @@ -33655,21 +34946,46 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValue this_obj, str = JS_ToCStringLen(ctx, &len, val); if (!str) return JS_EXCEPTION; - ret = JS_EvalInternal(ctx, this_obj, str, len, "", flags, scope_idx); + ret = JS_EvalInternal(ctx, this_obj, str, len, "", 1, flags, scope_idx); JS_FreeCString(ctx, str); return ret; } -JSValue JS_EvalThis(JSContext *ctx, JSValue this_obj, +JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, const char *input, size_t input_len, const char *filename, int eval_flags) { + JSEvalOptions options = { + .version = JS_EVAL_OPTIONS_VERSION, + .filename = filename, + .line_num = 1, + .eval_flags = eval_flags + }; + return JS_EvalThis2(ctx, this_obj, input, input_len, &options); +} + +JSValue JS_EvalThis2(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + JSEvalOptions *options) +{ + const char *filename = ""; + int line = 1; + int eval_flags = 0; + if (options) { + if (options->version != JS_EVAL_OPTIONS_VERSION) + return JS_ThrowInternalError(ctx, "bad JSEvalOptions version"); + if (options->filename) + filename = options->filename; + if (options->line_num != 0) + line = options->line_num; + eval_flags = options->eval_flags; + } JSValue ret; assert((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_GLOBAL || (eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE); - ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, + ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, line, eval_flags, -1); return ret; } @@ -33677,11 +34993,21 @@ JSValue JS_EvalThis(JSContext *ctx, JSValue this_obj, JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, const char *filename, int eval_flags) { - return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename, - eval_flags); + JSEvalOptions options = { + .version = JS_EVAL_OPTIONS_VERSION, + .filename = filename, + .line_num = 1, + .eval_flags = eval_flags + }; + return JS_EvalThis2(ctx, ctx->global_obj, input, input_len, &options); +} + +JSValue JS_Eval2(JSContext *ctx, const char *input, size_t input_len, JSEvalOptions *options) +{ + return JS_EvalThis2(ctx, ctx->global_obj, input, input_len, options); } -int JS_ResolveModule(JSContext *ctx, JSValue obj) +int JS_ResolveModule(JSContext *ctx, JSValueConst obj) { if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { JSModuleDef *m = JS_VALUE_GET_PTR(obj); @@ -33826,7 +35152,7 @@ typedef enum BCTagEnum { BC_TAG_SYMBOL, } BCTagEnum; -#define BC_VERSION 19 +#define BC_VERSION 21 typedef struct BCWriterState { JSContext *ctx; @@ -33876,6 +35202,13 @@ static const char * const bc_tag_str[] = { "Set", "Symbol", }; + +static const char *bc_tag_name(uint8_t tag) +{ + if (tag >= countof(bc_tag_str)) + return ""; + return bc_tag_str[tag]; +} #endif static void bc_put_u8(BCWriterState *s, uint8_t v) @@ -34035,11 +35368,6 @@ static void bc_byte_swap(uint8_t *bc_buf, int bc_len) } } -static bool is_ic_op(uint8_t op) -{ - return op >= OP_get_field_ic && op <= OP_put_field_ic; -} - static int JS_WriteFunctionBytecode(BCWriterState *s, const JSFunctionBytecode *b) { @@ -34070,16 +35398,6 @@ static int JS_WriteFunctionBytecode(BCWriterState *s, put_u32(bc_buf + pos + 1, val); break; default: - // IC (inline cache) opcodes should not end up in the serialized - // bytecode; translate them to their non-IC counterparts here - if (is_ic_op(op)) { - val = get_u32(bc_buf + pos + 1); - atom = get_ic_atom(b->ic, val); - if (bc_atom_to_idx(s, &val, atom)) - goto fail; - put_u32(bc_buf + pos + 1, val); - bc_buf[pos] -= (OP_get_field_ic - OP_get_field); - } break; } pos += len; @@ -34103,87 +35421,60 @@ static void JS_WriteString(BCWriterState *s, JSString *p) bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char); if (p->is_wide_char) { for(i = 0; i < p->len; i++) - bc_put_u16(s, p->u.str16[i]); + bc_put_u16(s, str16(p)[i]); } else { - dbuf_put(&s->dbuf, p->u.str8, p->len); + dbuf_put(&s->dbuf, str8(p), p->len); } } -static int JS_WriteBigInt(BCWriterState *s, JSValue obj) +static int JS_WriteBigInt(BCWriterState *s, JSValueConst obj) { - uint32_t tag, tag1; - int64_t e; - JSBigInt *bf = JS_VALUE_GET_PTR(obj); - bf_t *a = &bf->num; - size_t len, i, n1, j; - limb_t v; + JSBigIntBuf buf; + JSBigInt *p; + uint32_t len, i; + js_limb_t v, b; + int shift; - tag = JS_VALUE_GET_TAG(obj); - switch(tag) { - case JS_TAG_BIG_INT: - tag1 = BC_TAG_BIG_INT; - break; - default: - abort(); - } - bc_put_u8(s, tag1); + bc_put_u8(s, BC_TAG_BIG_INT); - /* sign + exponent */ - if (a->expn == BF_EXP_ZERO) - e = 0; - else if (a->expn == BF_EXP_INF) - e = 1; - else if (a->expn == BF_EXP_NAN) - e = 2; - else if (a->expn >= 0) - e = a->expn + 3; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_SHORT_BIG_INT) + p = js_bigint_set_short(&buf, obj); else - e = a->expn; - e = (e * 2) | a->sign; - if (e < INT32_MIN || e > INT32_MAX) { - JS_ThrowRangeError(s->ctx, "maximum BigInt size exceeded"); - return -1; - } - bc_put_sleb128(s, e); - - /* mantissa */ - if (a->len != 0) { - i = 0; - while (i < a->len && a->tab[i] == 0) - i++; - assert(i < a->len); - v = a->tab[i]; - n1 = sizeof(limb_t); - while ((v & 0xff) == 0) { - n1--; - v >>= 8; - } - i++; - len = (a->len - i) * sizeof(limb_t) + n1; - if (len > INT32_MAX) { - JS_ThrowRangeError(s->ctx, "maximum BigInt size exceeded"); - return -1; + p = JS_VALUE_GET_PTR(obj); + if (p->len == 1 && p->tab[0] == 0) { + /* zero case */ + len = 0; + } else { + /* compute the length of the two's complement representation + in bytes */ + len = p->len * (JS_LIMB_BITS / 8); + v = p->tab[p->len - 1]; + shift = JS_LIMB_BITS - 8; + while (shift > 0) { + b = (v >> shift) & 0xff; + if (b != 0x00 && b != 0xff) + break; + if ((b & 1) != ((v >> (shift - 1)) & 1)) + break; + shift -= 8; + len--; } - bc_put_leb128(s, len); - /* always saved in byte based little endian representation */ - for(j = 0; j < n1; j++) { - bc_put_u8(s, v >> (j * 8)); + } + bc_put_leb128(s, len); + if (len > 0) { + for(i = 0; i < (len / (JS_LIMB_BITS / 8)); i++) { + bc_put_u32(s, p->tab[i]); } - for(; i < a->len; i++) { - limb_t v = a->tab[i]; -#if LIMB_BITS == 32 - bc_put_u32(s, v); -#else - bc_put_u64(s, v); -#endif + for(i = 0; i < len % (JS_LIMB_BITS / 8); i++) { + bc_put_u8(s, (p->tab[p->len - 1] >> (i * 8)) & 0xff); } } return 0; } -static int JS_WriteObjectRec(BCWriterState *s, JSValue obj); +static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj); -static int JS_WriteFunctionTag(BCWriterState *s, JSValue obj) +static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) { JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj); uint32_t flags; @@ -34276,7 +35567,7 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValue obj) return -1; } -static int JS_WriteModule(BCWriterState *s, JSValue obj) +static int JS_WriteModule(BCWriterState *s, JSValueConst obj) { JSModuleDef *m = JS_VALUE_GET_PTR(obj); int i; @@ -34326,7 +35617,7 @@ static int JS_WriteModule(BCWriterState *s, JSValue obj) return -1; } -static int JS_WriteArray(BCWriterState *s, JSValue obj) +static int JS_WriteArray(BCWriterState *s, JSValueConst obj) { JSObject *p = JS_VALUE_GET_OBJ(obj); uint32_t i, len; @@ -34369,7 +35660,7 @@ static int JS_WriteArray(BCWriterState *s, JSValue obj) return -1; } -static int JS_WriteObjectTag(BCWriterState *s, JSValue obj) +static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj) { JSObject *p = JS_VALUE_GET_OBJ(obj); uint32_t i, prop_count; @@ -34406,7 +35697,7 @@ static int JS_WriteObjectTag(BCWriterState *s, JSValue obj) return -1; } -static int JS_WriteTypedArray(BCWriterState *s, JSValue obj) +static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj) { JSObject *p = JS_VALUE_GET_OBJ(obj); JSTypedArray *ta = p->u.typed_array; @@ -34420,7 +35711,7 @@ static int JS_WriteTypedArray(BCWriterState *s, JSValue obj) return 0; } -static int JS_WriteArrayBuffer(BCWriterState *s, JSValue obj) +static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj) { JSObject *p = JS_VALUE_GET_OBJ(obj); JSArrayBuffer *abuf = p->u.array_buffer; @@ -34435,7 +35726,7 @@ static int JS_WriteArrayBuffer(BCWriterState *s, JSValue obj) return 0; } -static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValue obj) +static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj) { JSObject *p = JS_VALUE_GET_OBJ(obj); JSArrayBuffer *abuf = p->u.array_buffer; @@ -34460,12 +35751,12 @@ static int JS_WriteRegExp(BCWriterState *s, JSRegExp regexp) JS_WriteString(s, regexp.pattern); if (is_be()) - lre_byte_swap(bc->u.str8, bc->len, /*is_byte_swapped*/false); + lre_byte_swap(str8(bc), bc->len, /*is_byte_swapped*/false); JS_WriteString(s, bc); if (is_be()) - lre_byte_swap(bc->u.str8, bc->len, /*is_byte_swapped*/true); + lre_byte_swap(str8(bc), bc->len, /*is_byte_swapped*/true); return 0; } @@ -34473,7 +35764,7 @@ static int JS_WriteRegExp(BCWriterState *s, JSRegExp regexp) static int JS_WriteMap(BCWriterState *s, struct JSMapState *map_state); static int JS_WriteSet(BCWriterState *s, struct JSMapState *map_state); -static int JS_WriteObjectRec(BCWriterState *s, JSValue obj) +static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) { uint32_t tag; @@ -34598,6 +35889,7 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValue obj) goto fail; } break; + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: if (JS_WriteBigInt(s, obj)) goto fail; @@ -34671,7 +35963,7 @@ static int JS_WriteObjectAtoms(BCWriterState *s) return -1; } -uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValue obj, +uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, int flags, JSSABTab *psab_tab) { BCWriterState ss, *s = &ss; @@ -34719,7 +36011,7 @@ uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValue obj, return NULL; } -uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValue obj, +uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, int flags) { return JS_WriteObject2(ctx, psize, obj, flags, NULL); @@ -34943,6 +36235,10 @@ static JSString *JS_ReadString(BCReaderState *s) return NULL; is_wide_char = len & 1; len >>= 1; + if (len > JS_STRING_LEN_MAX) { + JS_ThrowInternalError(s->ctx, "string too long"); + return NULL; + } p = js_alloc_string(s->ctx, len, is_wide_char); if (!p) { s->error_state = -1; @@ -34954,16 +36250,16 @@ static JSString *JS_ReadString(BCReaderState *s) js_free_string(s->ctx->rt, p); return NULL; } - memcpy(p->u.str8, s->ptr, size); + memcpy(str8(p), s->ptr, size); s->ptr += size; if (is_wide_char) { if (is_be()) { uint32_t i; for (i = 0; i < len; i++) - p->u.str16[i] = bswap16(p->u.str16[i]); + str16(p)[i] = bswap16(str16(p)[i]); } } else { - p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */ + str8(p)[size] = '\0'; /* add the trailing zero for 8 bit strings */ } #ifdef ENABLE_DUMPS // JS_DUMP_READ_OBJECT if (check_dump_flag(s->ctx->rt, JS_DUMP_READ_OBJECT)) { @@ -35019,7 +36315,6 @@ static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b, put_u32(bc_buf + pos + 1, atom); break; default: - assert(!is_ic_op(op)); // should not end up in serialized bytecode break; } #ifdef ENABLE_DUMPS // JS_DUMP_READ_OBJECT @@ -35041,77 +36336,46 @@ static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b, static JSValue JS_ReadBigInt(BCReaderState *s) { - JSValue obj; + JSValue obj = JS_UNDEFINED; + uint32_t len, i, n; + JSBigInt *p; + js_limb_t v; uint8_t v8; - int32_t e; - uint32_t len; - limb_t l, i, n; - limb_t v; - bf_t *a; - obj = JS_NewBigInt(s->ctx); - if (JS_IsException(obj)) + if (bc_get_leb128(s, &len)) goto fail; - - /* sign + exponent */ - if (bc_get_sleb128(s, &e)) + bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len); + if (len == 0) { + /* zero case */ + bc_read_trace(s, "}\n"); + return __JS_NewShortBigInt(s->ctx, 0); + } + p = js_bigint_new(s->ctx, (len - 1) / (JS_LIMB_BITS / 8) + 1); + if (!p) goto fail; - - a = JS_GetBigInt(obj); - a->sign = e & 1; - e >>= 1; - if (e == 0) - a->expn = BF_EXP_ZERO; - else if (e == 1) - a->expn = BF_EXP_INF; - else if (e == 2) - a->expn = BF_EXP_NAN; - else if (e >= 3) - a->expn = e - 3; - else - a->expn = e; - - /* mantissa */ - if (a->expn != BF_EXP_ZERO && - a->expn != BF_EXP_INF && - a->expn != BF_EXP_NAN) { - if (bc_get_leb128(s, &len)) - goto fail; - bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len); - if (len == 0) { - JS_ThrowRangeError(s->ctx, "maximum BigInt size exceeded"); + for(i = 0; i < len / (JS_LIMB_BITS / 8); i++) { + if (bc_get_u32(s, &v)) goto fail; - } - l = (len + sizeof(limb_t) - 1) / sizeof(limb_t); - if (bf_resize(a, l)) { - JS_ThrowOutOfMemory(s->ctx); - goto fail; - } - n = len & (sizeof(limb_t) - 1); - if (n != 0) { - v = 0; - for(i = 0; i < n; i++) { - if (bc_get_u8(s, &v8)) - goto fail; - v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8); - } - a->tab[0] = v; - i = 1; - } else { - i = 0; - } - for(; i < l; i++) { -#if LIMB_BITS == 32 - if (bc_get_u32(s, &v)) - goto fail; -#else - if (bc_get_u64(s, &v)) + p->tab[i] = v; + } + n = len % (JS_LIMB_BITS / 8); + if (n != 0) { + int shift; + v = 0; + for(i = 0; i < n; i++) { + if (bc_get_u8(s, &v8)) goto fail; -#endif - a->tab[i] = v; + v |= (js_limb_t)v8 << (i * 8); } + shift = JS_LIMB_BITS - n * 8; + /* extend the sign */ + if (shift != 0) { + v = (js_slimb_t)(v << shift) >> shift; + } + p->tab[p->len - 1] = v; } - return obj; + bc_read_trace(s, "}\n"); + return JS_CompactBigInt(s->ctx, p); fail: JS_FreeValue(s->ctx, obj); return JS_EXCEPTION; @@ -35404,8 +36668,21 @@ static JSValue JS_ReadModule(BCReaderState *s) goto fail; for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; + JSModuleDef **pm = &rme->module; if (bc_get_atom(s, &rme->module_name)) goto fail; + // Resolves a module either from the cache or by requesting + // it from the module loader. From cache is not ideal because + // the module may not be the one it was a time of serialization + // but directly petitioning the module loader is not correct + // either because then the same module can get loaded twice. + // JS_WriteModule() does not serialize modules transitively + // because that doesn't work for C modules and is also prone + // to loading the same JS module twice. + *pm = js_host_resolve_imported_module_atom(s->ctx, m->module_name, + rme->module_name); + if (!*pm) + goto fail; } } @@ -35572,7 +36849,7 @@ static JSValue JS_ReadTypedArray(BCReaderState *s) JSContext *ctx = s->ctx; JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED; uint8_t array_tag; - JSValue args[3]; + JSValueConst args[3]; uint32_t offset, len, idx; if (bc_get_u8(s, &array_tag)) @@ -35719,7 +36996,7 @@ static JSValue JS_ReadRegExp(BCReaderState *s) } if (is_be()) - lre_byte_swap(bc->u.str8, bc->len, /*is_byte_swapped*/true); + lre_byte_swap(str8(bc), bc->len, /*is_byte_swapped*/true); return js_regexp_constructor_internal(ctx, JS_UNDEFINED, JS_MKPTR(JS_TAG_STRING, pattern), @@ -35788,7 +37065,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) if (bc_get_u8(s, &tag)) return JS_EXCEPTION; - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); + bc_read_trace(s, "%s {\n", bc_tag_name(tag)); switch(tag) { case BC_TAG_NULL: @@ -36029,18 +37306,18 @@ JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, /*******************************************************************/ /* runtime functions & objects */ -static JSValue js_string_constructor(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv); -static JSValue js_boolean_constructor(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv); -static JSValue js_number_constructor(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv); +static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); -static int check_function(JSContext *ctx, JSValue obj) +static int check_function(JSContext *ctx, JSValueConst obj) { if (likely(JS_IsFunction(ctx, obj))) return 0; - JS_ThrowTypeError(ctx, "not a function"); + JS_ThrowTypeErrorNotAFunction(ctx); return -1; } @@ -36064,7 +37341,7 @@ static JSAtom find_atom(JSContext *ctx, const char *name) for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) { JSAtomStruct *p = ctx->rt->atom_array[atom]; JSString *str = p; - if (str->len == len && !memcmp(str->u.str8, name, len)) + if (str->len == len && !memcmp(str8(str), name, len)) return JS_DupAtom(ctx, atom); } abort(); @@ -36098,7 +37375,7 @@ static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, return val; } -static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValue obj, +static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj, JSAtom atom, const JSCFunctionListEntry *e) { @@ -36191,17 +37468,22 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValue obj, return 0; } -void JS_SetPropertyFunctionList(JSContext *ctx, JSValue obj, +int JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, const JSCFunctionListEntry *tab, int len) { - int i; + int i, ret; for (i = 0; i < len; i++) { const JSCFunctionListEntry *e = &tab[i]; JSAtom atom = find_atom(ctx, e->name); - JS_InstantiateFunctionListItem(ctx, obj, atom, e); + if (atom == JS_ATOM_NULL) + return -1; + ret = JS_InstantiateFunctionListItem(ctx, obj, atom, e); JS_FreeAtom(ctx, atom); + if (ret) + return -1; } + return 0; } int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m, @@ -36256,20 +37538,18 @@ int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, /* Note: 'func_obj' is not necessarily a constructor */ static void JS_SetConstructor2(JSContext *ctx, - JSValue func_obj, - JSValue proto, + JSValueConst func_obj, + JSValueConst proto, int proto_flags, int ctor_flags) { JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, js_dup(proto), proto_flags); JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, js_dup(func_obj), ctor_flags); - set_cycle_flag(ctx, func_obj); - set_cycle_flag(ctx, proto); } -void JS_SetConstructor(JSContext *ctx, JSValue func_obj, - JSValue proto) +void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, + JSValueConst proto) { JS_SetConstructor2(ctx, func_obj, proto, 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); @@ -36278,18 +37558,18 @@ void JS_SetConstructor(JSContext *ctx, JSValue func_obj, static void JS_NewGlobalCConstructor2(JSContext *ctx, JSValue func_obj, const char *name, - JSValue proto) + JSValueConst proto) { JS_DefinePropertyValueStr(ctx, ctx->global_obj, name, - js_dup(func_obj), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + js_dup(func_obj), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); JS_SetConstructor(ctx, func_obj, proto); JS_FreeValue(ctx, func_obj); } static JSValue JS_NewGlobalCConstructor(JSContext *ctx, const char *name, - JSCFunction *func, int length, - JSValue proto) + JSCFunction *func, int length, + JSValueConst proto) { JSValue func_obj; func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0); @@ -36298,8 +37578,8 @@ static JSValue JS_NewGlobalCConstructor(JSContext *ctx, const char *name, } static JSValue JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name, - JSCFunction *func, int length, - JSValue proto) + JSCFunction *func, int length, + JSValue proto) { JSValue func_obj; func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0); @@ -36307,14 +37587,14 @@ static JSValue JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name, return func_obj; } -static JSValue js_global_eval(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1); } -static JSValue js_global_isNaN(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { double d; @@ -36323,8 +37603,8 @@ static JSValue js_global_isNaN(JSContext *ctx, JSValue this_val, return js_bool(isnan(d)); } -static JSValue js_global_isFinite(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { bool res; double d; @@ -36335,13 +37615,13 @@ static JSValue js_global_isFinite(JSContext *ctx, JSValue this_val, } static JSValue js_microtask_job(JSContext *ctx, - int argc, JSValue *argv) + int argc, JSValueConst *argv) { return JS_Call(ctx, argv[0], ctx->global_obj, 0, NULL); } -static JSValue js_global_queueMicrotask(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_global_queueMicrotask(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { if (check_function(ctx, argv[0])) return JS_EXCEPTION; @@ -36352,7 +37632,7 @@ static JSValue js_global_queueMicrotask(JSContext *ctx, JSValue this_val, /* Object class */ -JSValue JS_ToObject(JSContext *ctx, JSValue val) +JSValue JS_ToObject(JSContext *ctx, JSValueConst val) { int tag = JS_VALUE_GET_NORM_TAG(val); JSValue obj; @@ -36365,6 +37645,7 @@ JSValue JS_ToObject(JSContext *ctx, JSValue val) case JS_TAG_OBJECT: case JS_TAG_EXCEPTION: return js_dup(val); + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); goto set_value; @@ -36400,7 +37681,7 @@ static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val) } static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, - JSValue desc) + JSValueConst desc) { JSValue val, getter, setter; int present; @@ -36497,8 +37778,8 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, return -1; } -static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValue obj, - JSAtom prop, JSValue desc, +static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValueConst desc, int flags) { JSPropertyDescriptor d; @@ -36514,8 +37795,8 @@ static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValue obj, } static __exception int JS_ObjectDefineProperties(JSContext *ctx, - JSValue obj, - JSValue properties) + JSValueConst obj, + JSValueConst properties) { JSValue props, desc; JSObject *p; @@ -36555,8 +37836,8 @@ static __exception int JS_ObjectDefineProperties(JSContext *ctx, return ret; } -static JSValue js_object_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv) +static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { JSValue ret; if (!JS_IsUndefined(new_target) && @@ -36578,10 +37859,10 @@ static JSValue js_object_constructor(JSContext *ctx, JSValue new_target, return ret; } -static JSValue js_object_create(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_create(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue proto, props; + JSValueConst proto, props; JSValue obj; proto = argv[0]; @@ -36600,12 +37881,10 @@ static JSValue js_object_create(JSContext *ctx, JSValue this_val, return obj; } -static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { - JSValue val; - - val = argv[0]; + JSValueConst val = argv[0]; if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) { /* ES6 feature non compatible with ES5.1: primitive types are accepted */ @@ -36616,21 +37895,20 @@ static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValue this_val, return JS_GetPrototype(ctx, val); } -static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue obj; - obj = argv[0]; + JSValueConst obj = argv[0]; if (JS_SetPrototypeInternal(ctx, obj, argv[1], true) < 0) return JS_EXCEPTION; return js_dup(obj); } /* magic = 1 if called as Reflect.defineProperty */ -static JSValue js_object_defineProperty(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { - JSValue obj, prop, desc; + JSValueConst obj, prop, desc; int ret, flags; JSAtom atom; @@ -36657,24 +37935,22 @@ static JSValue js_object_defineProperty(JSContext *ctx, JSValue this_val, } } -static JSValue js_object_defineProperties(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // defineProperties(obj, properties) - JSValue obj = argv[0]; - + JSValueConst obj = argv[0]; if (JS_ObjectDefineProperties(ctx, obj, argv[1])) return JS_EXCEPTION; - else - return js_dup(obj); + return js_dup(obj); } /* magic = 1 if called as __defineSetter__ */ -static JSValue js_object___defineGetter__(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSValue obj; - JSValue prop, value, get, set; + JSValueConst prop, value, get, set; int ret, flags; JSAtom atom; @@ -36716,10 +37992,10 @@ static JSValue js_object___defineGetter__(JSContext *ctx, JSValue this_val, } } -static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { - JSValue prop; + JSValueConst prop; JSAtom atom; JSValue ret, obj; JSPropertyDescriptor desc; @@ -36783,8 +38059,8 @@ static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValue this_v return JS_EXCEPTION; } -static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { //getOwnPropertyDescriptors(obj) JSValue obj, r; @@ -36806,7 +38082,7 @@ static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValue this_ goto exception; for(i = 0; i < len; i++) { JSValue atomValue, desc; - JSValue args[2]; + JSValueConst args[2]; atomValue = JS_AtomToValue(ctx, props[i].atom); if (JS_IsException(atomValue)) @@ -36834,7 +38110,7 @@ static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValue this_ return JS_EXCEPTION; } -static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValue obj1, +static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1, int flags, int kind) { JSValue obj, r, val, key, value; @@ -36913,25 +38189,25 @@ static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValue obj1, return r; } -static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return JS_GetOwnPropertyNames2(ctx, argv[0], JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY); } -static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return JS_GetOwnPropertyNames2(ctx, argv[0], JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY); } -static JSValue js_object_groupBy(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue cb, res, iter, next, groups, k, v, prop; - JSValue args[2]; + JSValue res, iter, next, groups, k, v, prop; + JSValueConst cb, args[2]; int64_t idx; int done; @@ -36990,7 +38266,7 @@ static JSValue js_object_groupBy(JSContext *ctx, JSValue this_val, } } - res = js_array_push(ctx, prop, 1, &v, /*unshift*/0); + res = js_array_push(ctx, prop, 1, vc(&v), /*unshift*/0); if (JS_IsException(res)) goto exception; // res is an int64 @@ -37017,17 +38293,17 @@ static JSValue js_object_groupBy(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_object_keys(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int kind) +static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int kind) { return JS_GetOwnPropertyNames2(ctx, argv[0], JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind); } -static JSValue js_object_isExtensible(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int reflect) +static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int reflect) { - JSValue obj; + JSValueConst obj; int ret; obj = argv[0]; @@ -37044,10 +38320,10 @@ static JSValue js_object_isExtensible(JSContext *ctx, JSValue this_val, return js_bool(ret); } -static JSValue js_object_preventExtensions(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int reflect) +static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int reflect) { - JSValue obj; + JSValueConst obj; int ret; obj = argv[0]; @@ -37069,8 +38345,8 @@ static JSValue js_object_preventExtensions(JSContext *ctx, JSValue this_val, } } -static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj; JSAtom atom; @@ -37095,8 +38371,8 @@ static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValue this_val, return js_bool(ret); } -static JSValue js_object_hasOwn(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj; JSAtom atom; @@ -37121,14 +38397,14 @@ static JSValue js_object_hasOwn(JSContext *ctx, JSValue this_val, return js_bool(ret); } -static JSValue js_object_valueOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return JS_ToObject(ctx, this_val); } -static JSValue js_object_toString(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj, tag; int is_array; @@ -37143,7 +38419,7 @@ static JSValue js_object_toString(JSContext *ctx, JSValue this_val, obj = JS_ToObject(ctx, this_val); if (JS_IsException(obj)) return obj; - is_array = JS_IsArray(ctx, obj); + is_array = js_is_array(ctx, obj); if (is_array < 0) { JS_FreeValue(ctx, obj); return JS_EXCEPTION; @@ -37182,19 +38458,19 @@ static JSValue js_object_toString(JSContext *ctx, JSValue this_val, return JS_ConcatString3(ctx, "[object ", tag, "]"); } -JSValue JS_ToObjectString(JSContext *ctx, JSValue val) +JSValue JS_ToObjectString(JSContext *ctx, JSValueConst val) { return js_object_toString(ctx, val, 0, NULL); } -static JSValue js_object_toLocaleString(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL); } -static JSValue js_object_assign(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // Object.assign(obj, source1) JSValue obj, s; @@ -37221,10 +38497,10 @@ static JSValue js_object_assign(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_object_seal(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int freeze_flag) +static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int freeze_flag) { - JSValue obj = argv[0]; + JSValueConst obj = argv[0]; JSObject *p; JSTypedArray *ta; JSArrayBuffer *abuf; @@ -37286,10 +38562,10 @@ static JSValue js_object_seal(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_object_isSealed(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int is_frozen) +static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int is_frozen) { - JSValue obj = argv[0]; + JSValueConst obj = argv[0]; JSObject *p; JSPropertyEnum *props; uint32_t len, i; @@ -37332,7 +38608,7 @@ static JSValue js_object_isSealed(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -int JS_SealObject(JSContext *ctx, JSValue obj) +int JS_SealObject(JSContext *ctx, JSValueConst obj) { JSValue value = js_object_seal(ctx, JS_UNDEFINED, 1, &obj, 0); int result = JS_IsException(value) ? -1 : true; @@ -37340,7 +38616,7 @@ int JS_SealObject(JSContext *ctx, JSValue obj) return result; } -int JS_FreezeObject(JSContext *ctx, JSValue obj) +int JS_FreezeObject(JSContext *ctx, JSValueConst obj) { JSValue value = js_object_seal(ctx, JS_UNDEFINED, 1, &obj, 1); int result = JS_IsException(value) ? -1 : true; @@ -37348,11 +38624,11 @@ int JS_FreezeObject(JSContext *ctx, JSValue obj) return result; } -static JSValue js_object_fromEntries(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj, iter, next_method = JS_UNDEFINED; - JSValue iterable; + JSValueConst iterable; int done; /* RequireObjectCoercible() not necessary because it is tested in @@ -37375,10 +38651,8 @@ static JSValue js_object_fromEntries(JSContext *ctx, JSValue this_val, item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); if (JS_IsException(item)) goto fail; - if (done) { - JS_FreeValue(ctx, item); + if (done) break; - } key = JS_UNDEFINED; value = JS_UNDEFINED; @@ -37416,14 +38690,14 @@ static JSValue js_object_fromEntries(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_object_is(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_is(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return js_bool(js_same_value(ctx, argv[0], argv[1])); } -static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValue obj, - JSValue defaultConstructor) +static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj, + JSValueConst defaultConstructor) { JSValue ctor, species; @@ -37445,13 +38719,14 @@ static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValue obj, if (JS_IsUndefined(species) || JS_IsNull(species)) return js_dup(defaultConstructor); if (!JS_IsConstructor(ctx, species)) { + JS_ThrowTypeErrorNotAConstructor(ctx, species); JS_FreeValue(ctx, species); - return JS_ThrowTypeError(ctx, "not a constructor"); + return JS_EXCEPTION; } return species; } -static JSValue js_object_get___proto__(JSContext *ctx, JSValue this_val) +static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val) { JSValue val, ret; @@ -37463,8 +38738,8 @@ static JSValue js_object_get___proto__(JSContext *ctx, JSValue this_val) return ret; } -static JSValue js_object_set___proto__(JSContext *ctx, JSValue this_val, - JSValue proto) +static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val, + JSValueConst proto) { if (JS_IsUndefined(this_val) || JS_IsNull(this_val)) return JS_ThrowTypeErrorNotAnObject(ctx); @@ -37476,11 +38751,11 @@ static JSValue js_object_set___proto__(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj, v1; - JSValue v; + JSValueConst v; int res; v = argv[0]; @@ -37516,8 +38791,8 @@ static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj, res = JS_EXCEPTION; JSAtom prop = JS_ATOM_NULL; @@ -37547,8 +38822,8 @@ static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValue this_val, return res; } -static JSValue js_object___lookupGetter__(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int setter) +static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int setter) { JSValue obj, res = JS_EXCEPTION; JSAtom prop = JS_ATOM_NULL; @@ -37634,15 +38909,15 @@ static const JSCFunctionListEntry js_object_proto_funcs[] = { /* Function class */ -static JSValue js_function_proto(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return JS_UNDEFINED; } /* XXX: add a specific eval mode so that Function("}), ({") is rejected */ -static JSValue js_function_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv, int magic) +static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv, int magic) { JSFunctionKindEnum func_kind = magic; int i, n, ret; @@ -37712,7 +38987,7 @@ static JSValue js_function_constructor(JSContext *ctx, JSValue new_target, } static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, - JSValue obj) + JSValueConst obj) { JSValue len_val; len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); @@ -37724,7 +38999,7 @@ static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, } static __exception int js_get_length64(JSContext *ctx, int64_t *pres, - JSValue obj) + JSValueConst obj) { JSValue len_val; len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); @@ -37735,7 +39010,8 @@ static __exception int js_get_length64(JSContext *ctx, int64_t *pres, return JS_ToLengthFree(ctx, pres, len_val); } -static __exception int js_set_length64(JSContext *ctx, JSValue obj, int64_t len) +static __exception int js_set_length64(JSContext *ctx, JSValueConst obj, + int64_t len) { return JS_SetProperty(ctx, obj, JS_ATOM_length, js_int64(len)); } @@ -37751,7 +39027,7 @@ static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len) /* XXX: should use ValueArray */ static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, - JSValue array_arg) + JSValueConst array_arg) { uint32_t len, i; JSValue *tab, ret; @@ -37796,10 +39072,10 @@ static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, /* magic value: 0 = normal apply, 1 = apply for constructor, 2 = Reflect.apply */ -static JSValue js_function_apply(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { - JSValue this_arg, array_arg; + JSValueConst this_arg, array_arg; uint32_t len; JSValue *tab, ret; @@ -37815,16 +39091,16 @@ static JSValue js_function_apply(JSContext *ctx, JSValue this_val, if (!tab) return JS_EXCEPTION; if (magic & 1) { - ret = JS_CallConstructor2(ctx, this_val, this_arg, len, tab); + ret = JS_CallConstructor2(ctx, this_val, this_arg, len, vc(tab)); } else { - ret = JS_Call(ctx, this_val, this_arg, len, tab); + ret = JS_Call(ctx, this_val, this_arg, len, vc(tab)); } free_arg_list(ctx, tab, len); return ret; } -static JSValue js_function_call(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_function_call(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { if (argc <= 0) { return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL); @@ -37833,8 +39109,8 @@ static JSValue js_function_call(JSContext *ctx, JSValue this_val, } } -static JSValue js_function_bind(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSBoundFunction *bf; JSValue func_obj, name1, len_val; @@ -37905,7 +39181,7 @@ static JSValue js_function_bind(JSContext *ctx, JSValue this_val, goto exception; if (!JS_IsString(name1)) { JS_FreeValue(ctx, name1); - name1 = JS_AtomToString(ctx, JS_ATOM_empty_string); + name1 = js_empty_string(ctx->rt); } name1 = JS_ConcatString3(ctx, "bound ", name1, ""); if (JS_IsException(name1)) @@ -37918,8 +39194,8 @@ static JSValue js_function_bind(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_function_toString(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSObject *p; JSFunctionKindEnum func_kind = JS_FUNC_NORMAL; @@ -37956,13 +39232,13 @@ static JSValue js_function_toString(JSContext *ctx, JSValue this_val, suff = "() {\n [native code]\n}"; name = JS_GetProperty(ctx, this_val, JS_ATOM_name); if (JS_IsUndefined(name)) - name = JS_AtomToString(ctx, JS_ATOM_empty_string); + name = js_empty_string(ctx->rt); return JS_ConcatString3(ctx, pref, name, suff); } } -static JSValue js_function_hasInstance(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int ret; ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val); @@ -37987,7 +39263,7 @@ static const JSCFunctionListEntry js_function_proto_funcs[] = { /* Error class */ -static JSValue iterator_to_array(JSContext *ctx, JSValue items) +static JSValue iterator_to_array(JSContext *ctx, JSValueConst items) { JSValue iter, next_method = JS_UNDEFINED; JSValue v, r = JS_UNDEFINED; @@ -38025,11 +39301,11 @@ static JSValue iterator_to_array(JSContext *ctx, JSValue items) goto done; } -static JSValue js_error_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv, int magic) +static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv, int magic) { JSValue obj, msg, proto, cause; - JSValue message; + JSValueConst message; int present, opts; if (JS_IsUndefined(new_target)) @@ -38101,8 +39377,8 @@ static JSValue js_error_constructor(JSContext *ctx, JSValue new_target, return JS_EXCEPTION; } -static JSValue js_error_toString(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue name, msg; @@ -38118,7 +39394,7 @@ static JSValue js_error_toString(JSContext *ctx, JSValue this_val, msg = JS_GetProperty(ctx, this_val, JS_ATOM_message); if (JS_IsUndefined(msg)) - msg = JS_AtomToString(ctx, JS_ATOM_empty_string); + msg = js_empty_string(ctx->rt); else msg = JS_ToStringFree(ctx, msg); if (JS_IsException(msg)) { @@ -38136,13 +39412,13 @@ static const JSCFunctionListEntry js_error_proto_funcs[] = { JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), }; -static JSValue js_error_isError(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_error_isError(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - return js_bool(JS_IsError(ctx, argv[0])); + return js_bool(JS_IsError(argv[0])); } -static JSValue js_error_get_stackTraceLimit(JSContext *ctx, JSValue this_val) +static JSValue js_error_get_stackTraceLimit(JSContext *ctx, JSValueConst this_val) { JSValue val; @@ -38150,21 +39426,18 @@ static JSValue js_error_get_stackTraceLimit(JSContext *ctx, JSValue this_val) if (JS_IsException(val)) return val; JS_FreeValue(ctx, val); - return js_int32(ctx->error_stack_trace_limit); + return js_dup(ctx->error_stack_trace_limit); } -static JSValue js_error_set_stackTraceLimit(JSContext *ctx, JSValue this_val, JSValue value) +static JSValue js_error_set_stackTraceLimit(JSContext *ctx, JSValueConst this_val, JSValueConst value) { if (JS_IsUndefined(this_val) || JS_IsNull(this_val)) return JS_ThrowTypeErrorNotAnObject(ctx); - double limit = 0; - if (JS_ToFloat64(ctx, &limit, value) < 0) - return JS_EXCEPTION; - ctx->error_stack_trace_limit = isinf(limit) ? INT_MAX : (int) limit; + ctx->error_stack_trace_limit = js_dup(value); return JS_UNDEFINED; } -static JSValue js_error_get_prepareStackTrace(JSContext *ctx, JSValue this_val) +static JSValue js_error_get_prepareStackTrace(JSContext *ctx, JSValueConst this_val) { JSValue val; @@ -38175,7 +39448,7 @@ static JSValue js_error_get_prepareStackTrace(JSContext *ctx, JSValue this_val) return js_dup(ctx->error_prepare_stack); } -static JSValue js_error_set_prepareStackTrace(JSContext *ctx, JSValue this_val, JSValue value) +static JSValue js_error_set_prepareStackTrace(JSContext *ctx, JSValueConst this_val, JSValueConst value) { if (JS_IsUndefined(this_val) || JS_IsNull(this_val)) return JS_ThrowTypeErrorNotAnObject(ctx); @@ -38184,10 +39457,10 @@ static JSValue js_error_set_prepareStackTrace(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_error_capture_stack_trace(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_error_capture_stack_trace(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue v = argv[0]; + JSValueConst v = argv[0]; if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) return JS_ThrowTypeErrorNotAnObject(ctx); build_backtrace(ctx, v, argv[1], NULL, 0, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL|JS_BACKTRACE_FLAG_FILTER_FUNC); @@ -38205,7 +39478,7 @@ static const JSCFunctionListEntry js_error_funcs[] = { /* used by C code. */ static JSValue js_aggregate_error_constructor(JSContext *ctx, - JSValue errors) + JSValueConst errors) { JSValue obj; @@ -38291,8 +39564,8 @@ static int JS_CopySubArray(JSContext *ctx, return -1; } -static JSValue js_array_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv) +static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { JSValue obj; int i; @@ -38318,12 +39591,12 @@ static JSValue js_array_constructor(JSContext *ctx, JSValue new_target, return JS_EXCEPTION; } -static JSValue js_array_from(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_from(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // from(items, mapfn = void 0, this_arg = void 0) - JSValue items = argv[0], mapfn, this_arg; - JSValue args[2]; + JSValueConst items = argv[0], mapfn, this_arg; + JSValueConst args[2]; JSValue stack[2]; JSValue iter, r, v, v2, arrayLike; int64_t k, len; @@ -38431,15 +39704,15 @@ static JSValue js_array_from(JSContext *ctx, JSValue this_val, return r; } -static JSValue js_array_of(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_of(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue obj, args[1]; + JSValue n, obj; int i; if (JS_IsConstructor(ctx, this_val)) { - args[0] = js_int32(argc); - obj = JS_CallConstructor(ctx, this_val, 1, args); + n = js_int32(argc); + obj = JS_CallConstructor(ctx, this_val, 1, vc(&n)); } else { obj = JS_NewArray(ctx); } @@ -38459,31 +39732,30 @@ static JSValue js_array_of(JSContext *ctx, JSValue this_val, return obj; } -static JSValue js_array_isArray(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int ret; - ret = JS_IsArray(ctx, argv[0]); + ret = js_is_array(ctx, argv[0]); if (ret < 0) return JS_EXCEPTION; else return js_bool(ret); } -static JSValue js_get_this(JSContext *ctx, - JSValue this_val) +static JSValue js_get_this(JSContext *ctx, JSValueConst this_val) { return js_dup(this_val); } -static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValue obj, - JSValue len_val) +static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj, + JSValueConst len_val) { JSValue ctor, ret, species; int res; JSContext *realm; - res = JS_IsArray(ctx, obj); + res = js_is_array(ctx, obj); if (res < 0) return JS_EXCEPTION; if (!res) @@ -38529,7 +39801,7 @@ static const JSCFunctionListEntry js_array_funcs[] = { JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), }; -static int JS_isConcatSpreadable(JSContext *ctx, JSValue obj) +static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj) { JSValue val; @@ -38540,11 +39812,11 @@ static int JS_isConcatSpreadable(JSContext *ctx, JSValue obj) return -1; if (!JS_IsUndefined(val)) return JS_ToBoolFree(ctx, val); - return JS_IsArray(ctx, obj); + return js_is_array(ctx, obj); } -static JSValue js_array_at(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_at(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj, ret; int64_t len, idx; @@ -38571,8 +39843,8 @@ static JSValue js_array_at(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_array_with(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_with(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue arr, obj, ret, *arrp, *pval; JSObject *p; @@ -38645,11 +39917,11 @@ static JSValue js_array_with(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_array_concat(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj, arr, val; - JSValue e; + JSValueConst e; int64_t len, k, n; int i, res; @@ -38718,7 +39990,7 @@ static JSValue js_array_concat(JSContext *ctx, JSValue this_val, #define special_filter 4 #define special_TA 8 -static JSObject *get_typed_array(JSContext *ctx, JSValue this_val) +static JSObject *get_typed_array(JSContext *ctx, JSValueConst this_val) { JSObject *p; if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) @@ -38740,7 +40012,7 @@ static JSObject *get_typed_array(JSContext *ctx, JSValue this_val) // Exclusively reading or writing elements with JS_GetProperty, // JS_GetPropertyInt64, JS_SetProperty, etc. is safe because they // perform bounds checks, as does js_get_fast_array_element. -static int js_typed_array_get_length_unsafe(JSContext *ctx, JSValue obj) +static int js_typed_array_get_length_unsafe(JSContext *ctx, JSValueConst obj) { JSObject *p; p = get_typed_array(ctx, obj); @@ -38754,15 +40026,15 @@ static int js_typed_array_get_length_unsafe(JSContext *ctx, JSValue obj) } static JSValue js_typed_array___speciesCreate(JSContext *ctx, - JSValue this_val, - int argc, JSValue *argv); + JSValueConst this_val, + int argc, JSValueConst *argv); -static JSValue js_array_every(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int special) +static JSValue js_array_every(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int special) { JSValue obj, val, index_val, res, ret; - JSValue args[3]; - JSValue func, this_arg; + JSValueConst args[3]; + JSValueConst func, this_arg; int64_t len, k, n; int present; @@ -38891,8 +40163,10 @@ static JSValue js_array_every(JSContext *ctx, JSValue this_val, goto exception; args[0] = ret; res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args); - if (check_exception_free(ctx, res)) + if (check_exception_free(ctx, res)) { + JS_FreeValue(ctx, arr); goto exception; + } JS_FreeValue(ctx, ret); ret = arr; } @@ -38910,12 +40184,12 @@ static JSValue js_array_every(JSContext *ctx, JSValue this_val, #define special_reduce 0 #define special_reduceRight 1 -static JSValue js_array_reduce(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int special) +static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int special) { JSValue obj, val, index_val, acc, acc1; - JSValue args[4]; - JSValue func; + JSValueConst args[4]; + JSValueConst func; int64_t len, k, k1; int present; @@ -38999,8 +40273,8 @@ static JSValue js_array_reduce(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_array_fill(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj; int64_t len, start, end; @@ -39034,8 +40308,8 @@ static JSValue js_array_fill(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_array_includes(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj, val; int64_t len, n; @@ -39082,8 +40356,8 @@ static JSValue js_array_includes(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_array_indexOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj, val; int64_t len, n; @@ -39129,8 +40403,8 @@ static JSValue js_array_indexOf(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_array_lastIndexOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj, val; int64_t len, n; @@ -39183,11 +40457,11 @@ enum { ArrayFindLastIndex, }; -static JSValue js_array_find(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int mode) +static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int mode) { - JSValue func, this_arg; - JSValue args[3]; + JSValueConst func, this_arg; + JSValueConst args[3]; JSValue obj, val, index_val, res; int64_t len, k, end; int dir; @@ -39254,8 +40528,8 @@ static JSValue js_array_find(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_array_toString(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj, method, ret; @@ -39277,8 +40551,8 @@ static JSValue js_array_toString(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_array_join(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int toLocaleString) +static JSValue js_array_join(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int toLocaleString) { JSValue obj, sep = JS_UNDEFINED, el; StringBuffer b_s, *b = &b_s; @@ -39297,7 +40571,7 @@ static JSValue js_array_join(JSContext *ctx, JSValue this_val, goto exception; p = JS_VALUE_GET_STRING(sep); if (p->len == 1 && !p->is_wide_char) - c = p->u.str8[0]; + c = str8(p)[0]; else c = -1; } @@ -39334,8 +40608,8 @@ static JSValue js_array_join(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_array_pop(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int shift) +static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int shift) { JSValue obj, res = JS_UNDEFINED; int64_t len, newLen; @@ -39387,8 +40661,8 @@ static JSValue js_array_pop(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_array_push(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int unshift) +static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int unshift) { JSValue obj; int i; @@ -39423,8 +40697,8 @@ static JSValue js_array_push(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_array_reverse(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj, lval, hval; JSValue *arrp; @@ -39496,8 +40770,8 @@ static JSValue js_array_reverse(JSContext *ctx, JSValue this_val, // leaves holes in sparse arrays intact whereas a.toReversed() replaces them // with undefined, thus in effect creating a dense array. // Does not use Array[@@species], always returns a base Array. -static JSValue js_array_toReversed(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_toReversed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue arr, obj, ret, *arrp, *pval; JSObject *p; @@ -39555,8 +40829,8 @@ static JSValue js_array_toReversed(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_array_slice(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int splice) +static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int splice) { JSValue obj, arr, val, len_val; int64_t len, start, k, final, n, count, del_count, new_len; @@ -39661,8 +40935,8 @@ static JSValue js_array_slice(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_array_toSpliced(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue arr, obj, ret, *arrp, *pval, *last; JSObject *p; @@ -39756,8 +41030,8 @@ static JSValue js_array_toSpliced(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_array_copyWithin(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj; int64_t len, from, to, final, count; @@ -39791,11 +41065,11 @@ static JSValue js_array_copyWithin(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValue target, - JSValue source, int64_t sourceLen, +static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target, + JSValueConst source, int64_t sourceLen, int64_t targetIndex, int depth, - JSValue mapperFunction, - JSValue thisArg) + JSValueConst mapperFunction, + JSValueConst thisArg) { JSValue element; int64_t sourceIndex, elementLen; @@ -39813,15 +41087,17 @@ static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValue target, if (!present) continue; if (!JS_IsUndefined(mapperFunction)) { - JSValue args[3] = { element, js_int64(sourceIndex), source }; - element = JS_Call(ctx, mapperFunction, thisArg, 3, args); - JS_FreeValue(ctx, args[0]); - JS_FreeValue(ctx, args[1]); - if (JS_IsException(element)) + JSValue index = js_int64(sourceIndex); + JSValueConst args[3] = { element, index, source }; + JSValue ret = JS_Call(ctx, mapperFunction, thisArg, 3, args); + JS_FreeValue(ctx, element); + JS_FreeValue(ctx, index); + if (JS_IsException(ret)) return -1; + element = ret; } if (depth > 0) { - is_array = JS_IsArray(ctx, element); + is_array = js_is_array(ctx, element); if (is_array < 0) goto fail; if (is_array) { @@ -39853,11 +41129,11 @@ static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValue target, return -1; } -static JSValue js_array_flatten(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int map) +static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int map) { JSValue obj, arr; - JSValue mapperFunction, thisArg; + JSValueConst mapperFunction, thisArg; int64_t sourceLen; int depthNum; @@ -39909,13 +41185,13 @@ struct array_sort_context { JSContext *ctx; int exception; int has_method; - JSValue method; + JSValueConst method; }; static int js_array_cmp_generic(const void *a, const void *b, void *opaque) { struct array_sort_context *psc = opaque; JSContext *ctx = psc->ctx; - JSValue argv[2]; + JSValueConst argv[2]; JSValue res; ValueSlot *ap = (ValueSlot *)(void *)a; ValueSlot *bp = (ValueSlot *)(void *)b; @@ -39973,8 +41249,8 @@ static int js_array_cmp_generic(const void *a, const void *b, void *opaque) { return 0; } -static JSValue js_array_sort(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { struct array_sort_context asc = { ctx, 0, 0, argv[0] }; JSValue obj = JS_UNDEFINED; @@ -40063,18 +41339,17 @@ static JSValue js_array_sort(JSContext *ctx, JSValue this_val, // leaves holes in sparse arrays intact whereas a.toSorted() replaces them // with undefined, thus in effect creating a dense array. // Does not use Array[@@species], always returns a base Array. -static JSValue js_array_toSorted(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_array_toSorted(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue arr, obj, ret, *arrp, *pval; JSObject *p; int64_t i, len; uint32_t count32; - int ok; - ok = JS_IsUndefined(argv[0]) || JS_IsFunction(ctx, argv[0]); - if (!ok) - return JS_ThrowTypeError(ctx, "not a function"); + if (!JS_IsUndefined(argv[0])) + if (check_function(ctx, argv[0])) + return JS_EXCEPTION; ret = JS_EXCEPTION; arr = JS_UNDEFINED; @@ -40136,7 +41411,7 @@ typedef struct JSArrayIteratorData { uint32_t idx; } JSArrayIteratorData; -static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val) +static void js_array_iterator_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSArrayIteratorData *it = p->u.array_iterator_data; @@ -40146,7 +41421,7 @@ static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val) } } -static void js_array_iterator_mark(JSRuntime *rt, JSValue val, +static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -40156,7 +41431,7 @@ static void js_array_iterator_mark(JSRuntime *rt, JSValue val, } } -static JSValue js_create_array(JSContext *ctx, int len, JSValue *tab) +static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab) { JSValue obj; int i; @@ -40173,8 +41448,8 @@ static JSValue js_create_array(JSContext *ctx, int len, JSValue *tab) return obj; } -static JSValue js_create_array_iterator(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSValue enum_obj, arr; JSArrayIteratorData *it; @@ -40210,8 +41485,8 @@ static JSValue js_create_array_iterator(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_array_iterator_next(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, +static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int *pdone, int magic) { JSArrayIteratorData *it; @@ -40257,7 +41532,7 @@ static JSValue js_array_iterator_next(JSContext *ctx, JSValue this_val, if (it->kind == JS_ITERATOR_KIND_VALUE) { return val; } else { - JSValue args[2]; + JSValueConst args[2]; JSValue num; num = js_uint32(idx); args[0] = num; @@ -40275,7 +41550,7 @@ typedef struct JSIteratorWrapData { JSValue wrapped_next; } JSIteratorWrapData; -static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValue val) +static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSIteratorWrapData *it = p->u.iterator_wrap_data; @@ -40286,7 +41561,7 @@ static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValue val) } } -static void js_iterator_wrap_mark(JSRuntime *rt, JSValue val, +static void js_iterator_wrap_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -40297,8 +41572,8 @@ static void js_iterator_wrap_mark(JSRuntime *rt, JSValue val, } } -static JSValue js_iterator_wrap_next(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, +static JSValue js_iterator_wrap_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int *pdone, int magic) { JSIteratorWrapData *it; @@ -40325,8 +41600,28 @@ static const JSCFunctionListEntry js_iterator_wrap_proto_funcs[] = { JS_ITERATOR_NEXT_DEF("return", 0, js_iterator_wrap_next, GEN_MAGIC_RETURN ), }; -static JSValue js_iterator_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv) +static JSValue js_iterator_constructor_getset(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, + JSValueConst *func_data) +{ + int ret; + + if (argc > 0) { // if setter + if (!JS_IsObject(argv[0])) + return JS_ThrowTypeErrorNotAnObject(ctx); + ret = JS_DefinePropertyValue(ctx, this_val, JS_ATOM_constructor, + js_dup(argv[0]), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (ret < 0) + return JS_EXCEPTION; + } + return js_dup(func_data[0]); +} + +static JSValue js_iterator_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { JSObject *p; @@ -40339,14 +41634,224 @@ static JSValue js_iterator_constructor(JSContext *ctx, JSValue new_target, return js_create_from_ctor(ctx, new_target, JS_CLASS_ITERATOR); } -static JSValue js_iterator_from(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +// note: deliberately doesn't use space-saving bit fields for +// |index|, |count| and |running| because tcc miscompiles them +typedef struct JSIteratorConcatData { + int index, count; // elements (not pairs!) in values[] array + bool running; + JSValue iter, next, values[]; // array of (object, method) pairs +} JSIteratorConcatData; + +static void js_iterator_concat_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSIteratorConcatData *it = p->u.iterator_concat_data; + if (it) { + JS_FreeValueRT(rt, it->iter); + JS_FreeValueRT(rt, it->next); + for (int i = it->index; i < it->count; i++) + JS_FreeValueRT(rt, it->values[i]); + js_free_rt(rt, it); + } +} + +static void js_iterator_concat_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSIteratorConcatData *it = p->u.iterator_concat_data; + if (it) { + JS_MarkValue(rt, it->iter, mark_func); + JS_MarkValue(rt, it->next, mark_func); + for (int i = it->index; i < it->count; i++) + JS_MarkValue(rt, it->values[i], mark_func); + } +} + +static JSValue js_iterator_concat_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue iter, item, next, val, *obj, *meth; + JSIteratorConcatData *it; + JSPropertyDescriptor d; + int done, ret; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ITERATOR_CONCAT); + if (!it) + return JS_EXCEPTION; + if (it->running) + return JS_ThrowTypeError(ctx, "already running"); +next: + if (it->index >= it->count) + return js_create_iterator_result(ctx, JS_UNDEFINED, /*done*/true); + obj = &it->values[it->index + 0]; + meth = &it->values[it->index + 1]; + iter = it->iter; + if (JS_IsUndefined(iter)) { + iter = JS_GetIterator2(ctx, *obj, *meth); + if (JS_IsException(iter)) + return JS_EXCEPTION; + it->iter = iter; + } + next = it->next; + if (JS_IsUndefined(next)) { + next = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next)) + return JS_EXCEPTION; + it->next = next; + } + it->running = true; + item = JS_IteratorNext2(ctx, iter, next, 0, NULL, &done); + it->running = false; + if (JS_IsException(item)) + return JS_EXCEPTION; + if (!done) + return js_create_iterator_result(ctx, item, /*done*/false); + // done==1 means really done, done==2 means "unknown, inspect object" + if (done == 2) { + val = JS_GetProperty(ctx, item, JS_ATOM_done); + if (JS_IsException(val)) { + JS_FreeValue(ctx, item); + return JS_EXCEPTION; + } + done = JS_ToBoolFree(ctx, val); + } + if (done) { + JS_FreeValue(ctx, item); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + it->iter = JS_UNDEFINED; + it->next = JS_UNDEFINED; + JS_FreeValue(ctx, *meth); + JS_FreeValue(ctx, *obj); + it->index += 2; + goto next; + } + // not done, construct { done: false, value: xxx } object + // copy .value verbatim from source object, spec doesn't + // allow dereferencing getters here + d = (JSPropertyDescriptor){ + .value = JS_UNDEFINED, + .getter = JS_UNDEFINED, + .setter = JS_UNDEFINED, + }; + ret = JS_GetOwnProperty(ctx, &d, item, JS_ATOM_value); + JS_FreeValue(ctx, item); + if (ret < 0) + return JS_EXCEPTION; + if (d.flags & JS_PROP_GETSET) { + d.flags |= JS_PROP_HAS_GET | JS_PROP_HAS_SET; + } else { + d.flags |= JS_PROP_HAS_VALUE; + } + item = JS_NewObject(ctx); + if (JS_IsException(item)) + goto fail; + if (JS_DefinePropertyValue(ctx, item, JS_ATOM_done, JS_FALSE, + JS_PROP_C_W_E) < 0) { + goto fail; + } + if (JS_DefineProperty(ctx, item, JS_ATOM_value, d.value, d.getter, + d.setter, d.flags | JS_PROP_C_W_E) < 0) { + fail: + JS_FreeValue(ctx, item); + item = JS_EXCEPTION; + } + js_free_desc(ctx, &d); + return item; +} + +static JSValue js_iterator_concat_return(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSIteratorConcatData *it; + JSValue ret; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ITERATOR_CONCAT); + if (!it) + return JS_EXCEPTION; + if (it->running) + return JS_ThrowTypeError(ctx, "already running"); + ret = JS_UNDEFINED; + if (!JS_IsUndefined(it->iter)) { + ret = JS_GetProperty(ctx, it->iter, JS_ATOM_return); + if (JS_IsException(ret)) + return JS_EXCEPTION; + it->running = true; + ret = JS_CallFree(ctx, ret, it->iter, 0, NULL); + it->running = false; + } + while (it->index < it->count) + JS_FreeValue(ctx, it->values[it->index++]); + JS_FreeValue(ctx, it->iter); + JS_FreeValue(ctx, it->next); + it->iter = JS_UNDEFINED; + it->next = JS_UNDEFINED; + return ret; +} + +// note: |next| and |return| don't use JS_ITERATOR_NEXT_DEF because |next| +// has to return a full { value: xxx, done: xxx } step object - it must +// copy getters and setters from the inner iterator's step object +// slightly inefficient because of the intermediate step object that is +// created but that can't be helped right now +static const JSCFunctionListEntry js_iterator_concat_proto_funcs[] = { + JS_CFUNC_DEF("next", 1, js_iterator_concat_next ), + JS_CFUNC_DEF("return", 1, js_iterator_concat_return ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Iterator Concat", JS_PROP_CONFIGURABLE ), +}; + +static JSValue js_iterator_concat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSIteratorConcatData *it; + JSValue obj, method; + + it = js_malloc(ctx, sizeof(*it) + 2*argc * sizeof(it->values[0])); + if (!it) + return JS_EXCEPTION; + it->running = false; + it->index = 0; + it->count = 0; + it->iter = JS_UNDEFINED; + it->next = JS_UNDEFINED; + for (int i = 0; i < argc; i++) { + JSValueConst obj = argv[i]; + if (!JS_IsObject(obj)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + goto fail; + if (!JS_IsFunction(ctx, method)) { + JS_ThrowTypeErrorNotAFunction(ctx); + JS_FreeValue(ctx, method); + goto fail; + } + it->values[it->count++] = js_dup(obj); + it->values[it->count++] = method; + } + obj = JS_NewObjectClass(ctx, JS_CLASS_ITERATOR_CONCAT); + if (JS_IsException(obj)) + goto fail; + JS_SetOpaqueInternal(obj, it); + return obj; +fail: + for (int i = 0; i < it->count; i++) + JS_FreeValue(ctx, it->values[i]); + js_free(ctx, it); + return JS_EXCEPTION; +} + +static JSValue js_iterator_from(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue obj, method, iter; + JSValue method, iter; JSIteratorWrapData *it; int ret; - obj = argv[0]; + JSValueConst obj = argv[0]; if (JS_IsString(obj)) { method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); if (JS_IsException(method)) @@ -40389,7 +41894,7 @@ static JSValue js_iterator_from(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static int check_iterator(JSContext *ctx, JSValue obj) +static int check_iterator(JSContext *ctx, JSValueConst obj) { if (!JS_IsObject(obj)) { JS_ThrowTypeErrorNotAnObject(ctx); @@ -40409,10 +41914,11 @@ typedef struct JSIteratorHelperData { uint8_t done : 1; } JSIteratorHelperData; -static JSValue js_create_iterator_helper(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_create_iterator_helper(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { - JSValue func, obj, method; + JSValueConst func; + JSValue obj, method; int64_t count; JSIteratorHelperData *it; @@ -40429,33 +41935,31 @@ static JSValue js_create_iterator_helper(JSContext *ctx, JSValue this_val, double dlimit; v = JS_ToNumber(ctx, argv[0]); if (JS_IsException(v)) - return JS_EXCEPTION; + goto fail; // Check for Infinity. if (JS_ToFloat64(ctx, &dlimit, v)) { JS_FreeValue(ctx, v); - return JS_EXCEPTION; + goto fail; } if (isnan(dlimit)) { JS_FreeValue(ctx, v); - goto fail; + goto range_error; } if (!isfinite(dlimit)) { JS_FreeValue(ctx, v); if (dlimit < 0) - goto fail; + goto range_error; else count = MAX_SAFE_INTEGER; } else { v = JS_ToIntegerFree(ctx, v); if (JS_IsException(v)) - return JS_EXCEPTION; + goto fail; if (JS_ToInt64Free(ctx, &count, v)) - return JS_EXCEPTION; - } - if (count < 0) { - fail: - return JS_ThrowRangeError(ctx, "must be positive"); + goto fail; } + if (count < 0) + goto range_error; } break; case JS_ITERATOR_HELPER_KIND_FILTER: @@ -40464,7 +41968,7 @@ static JSValue js_create_iterator_helper(JSContext *ctx, JSValue this_val, { func = argv[0]; if (check_function(ctx, func)) - return JS_EXCEPTION; + goto fail; } break; default: @@ -40474,17 +41978,17 @@ static JSValue js_create_iterator_helper(JSContext *ctx, JSValue this_val, method = JS_GetProperty(ctx, this_val, JS_ATOM_next); if (JS_IsException(method)) - return JS_EXCEPTION; + goto fail; obj = JS_NewObjectClass(ctx, JS_CLASS_ITERATOR_HELPER); if (JS_IsException(obj)) { JS_FreeValue(ctx, method); - return JS_EXCEPTION; + goto fail; } it = js_malloc(ctx, sizeof(*it)); if (!it) { JS_FreeValue(ctx, obj); JS_FreeValue(ctx, method); - return JS_EXCEPTION; + goto fail; } it->kind = magic; it->obj = js_dup(this_val); @@ -40496,20 +42000,27 @@ static JSValue js_create_iterator_helper(JSContext *ctx, JSValue this_val, it->done = 0; JS_SetOpaqueInternal(obj, it); return obj; +range_error: + JS_ThrowRangeError(ctx, "must be positive"); +fail: + JS_IteratorClose(ctx, this_val, true); + return JS_EXCEPTION; } -static JSValue js_iterator_proto_func(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_iterator_proto_func(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSValue item, method, ret, func, index_val, r; - JSValue args[2]; + JSValueConst args[2]; int64_t idx; int done; if (check_iterator(ctx, this_val) < 0) return JS_EXCEPTION; + func = JS_UNDEFINED; + method = JS_UNDEFINED; if (check_function(ctx, argv[0])) - return JS_EXCEPTION; + goto fail; func = js_dup(argv[0]); method = JS_GetProperty(ctx, this_val, JS_ATOM_next); if (JS_IsException(method)) @@ -40648,19 +42159,21 @@ static JSValue js_iterator_proto_func(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_iterator_proto_reduce(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_iterator_proto_reduce(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue item, method, ret, func, index_val, acc; - JSValue args[3]; + JSValueConst args[3]; int64_t idx; int done; if (check_iterator(ctx, this_val) < 0) return JS_EXCEPTION; - if (check_function(ctx, argv[0])) - return JS_EXCEPTION; acc = JS_UNDEFINED; + func = JS_UNDEFINED; + method = JS_UNDEFINED; + if (check_function(ctx, argv[0])) + goto exception; func = js_dup(argv[0]); method = JS_GetProperty(ctx, this_val, JS_ATOM_next); if (JS_IsException(method)) @@ -40710,8 +42223,8 @@ static JSValue js_iterator_proto_reduce(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_iterator_proto_toArray(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_iterator_proto_toArray(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue item, method, result; int64_t idx; @@ -40746,18 +42259,18 @@ static JSValue js_iterator_proto_toArray(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return js_dup(this_val); } -static JSValue js_iterator_proto_get_toStringTag(JSContext *ctx, JSValue this_val) +static JSValue js_iterator_proto_get_toStringTag(JSContext *ctx, JSValueConst this_val) { return JS_AtomToString(ctx, JS_ATOM_Iterator); } -static JSValue js_iterator_proto_set_toStringTag(JSContext *ctx, JSValue this_val, JSValue val) +static JSValue js_iterator_proto_set_toStringTag(JSContext *ctx, JSValueConst this_val, JSValueConst val) { int res; @@ -40778,7 +42291,7 @@ static JSValue js_iterator_proto_set_toStringTag(JSContext *ctx, JSValue this_va return JS_UNDEFINED; } -static void js_iterator_helper_finalizer(JSRuntime *rt, JSValue val) +static void js_iterator_helper_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSIteratorHelperData *it = p->u.iterator_helper_data; @@ -40791,7 +42304,7 @@ static void js_iterator_helper_finalizer(JSRuntime *rt, JSValue val) } } -static void js_iterator_helper_mark(JSRuntime *rt, JSValue val, +static void js_iterator_helper_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -40804,8 +42317,8 @@ static void js_iterator_helper_mark(JSRuntime *rt, JSValue val, } } -static JSValue js_iterator_helper_next(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, +static JSValue js_iterator_helper_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int *pdone, int magic) { JSIteratorHelperData *it; @@ -40863,7 +42376,8 @@ static JSValue js_iterator_helper_next(JSContext *ctx, JSValue this_val, break; case JS_ITERATOR_HELPER_KIND_FILTER: { - JSValue item, method, selected, index_val, args[2]; + JSValue item, method, selected, index_val; + JSValueConst args[2]; if (magic == GEN_MAGIC_NEXT) { method = js_dup(it->next); } else { @@ -40901,7 +42415,8 @@ static JSValue js_iterator_helper_next(JSContext *ctx, JSValue this_val, break; case JS_ITERATOR_HELPER_KIND_FLAT_MAP: { - JSValue item, method, index_val, args[2], iter; + JSValue item, method, index_val, iter; + JSValueConst args[2]; flat_map_again: if (JS_IsUndefined(it->inner)) { if (magic == GEN_MAGIC_NEXT) { @@ -40984,7 +42499,8 @@ static JSValue js_iterator_helper_next(JSContext *ctx, JSValue this_val, break; case JS_ITERATOR_HELPER_KIND_MAP: { - JSValue item, method, index_val, args[2]; + JSValue item, method, index_val; + JSValueConst args[2]; if (magic == GEN_MAGIC_NEXT) { method = js_dup(it->next); } else { @@ -41054,6 +42570,7 @@ static JSValue js_iterator_helper_next(JSContext *ctx, JSValue this_val, } static const JSCFunctionListEntry js_iterator_funcs[] = { + JS_CFUNC_DEF("concat", 0, js_iterator_concat ), JS_CFUNC_DEF("from", 1, js_iterator_from ), }; @@ -41128,8 +42645,8 @@ static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = { /* Number */ -static JSValue js_number_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv) +static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { JSValue val, obj; if (argc == 0) { @@ -41139,11 +42656,16 @@ static JSValue js_number_constructor(JSContext *ctx, JSValue new_target, if (JS_IsException(val)) return val; switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_SHORT_BIG_INT: + val = js_int64(JS_VALUE_GET_SHORT_BIG_INT(val)); + if (JS_IsException(val)) + return val; + break; case JS_TAG_BIG_INT: { JSBigInt *p = JS_VALUE_GET_PTR(val); double d; - bf_get_float64(&p->num, &d, BF_RNDN); + d = js_bigint_to_float64(ctx, p); JS_FreeValue(ctx, val); val = js_float64(d); } @@ -41162,24 +42684,24 @@ static JSValue js_number_constructor(JSContext *ctx, JSValue new_target, } } -static JSValue js_number_isNaN(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { if (!JS_IsNumber(argv[0])) return JS_FALSE; return js_global_isNaN(ctx, this_val, argc, argv); } -static JSValue js_number_isFinite(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { if (!JS_IsNumber(argv[0])) return JS_FALSE; return js_global_isFinite(ctx, this_val, argc, argv); } -static JSValue js_number_isInteger(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int ret; ret = JS_NumberIsInteger(ctx, argv[0]); @@ -41189,8 +42711,8 @@ static JSValue js_number_isInteger(JSContext *ctx, JSValue this_val, return js_bool(ret); } -static JSValue js_number_isSafeInteger(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { double d; if (!JS_IsNumber(argv[0])) @@ -41218,7 +42740,7 @@ static const JSCFunctionListEntry js_number_funcs[] = { JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */ }; -static JSValue js_thisNumberValue(JSContext *ctx, JSValue this_val) +static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val) { if (JS_IsNumber(this_val)) return js_dup(this_val); @@ -41233,13 +42755,13 @@ static JSValue js_thisNumberValue(JSContext *ctx, JSValue this_val) return JS_ThrowTypeError(ctx, "not a number"); } -static JSValue js_number_valueOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return js_thisNumberValue(ctx, this_val); } -static int js_get_radix(JSContext *ctx, JSValue val) +static int js_get_radix(JSContext *ctx, JSValueConst val) { int radix; if (JS_ToInt32Sat(ctx, &radix, val)) @@ -41251,47 +42773,45 @@ static int js_get_radix(JSContext *ctx, JSValue val) return radix; } -static JSValue js_number_toString(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { - char buf[72]; JSValue val; - int base; + int base, flags; double d; val = js_thisNumberValue(ctx, this_val); if (JS_IsException(val)) return val; if (magic || JS_IsUndefined(argv[0])) { - if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { - size_t len = i32toa(buf, JS_VALUE_GET_INT(val)); - return js_new_string8_len(ctx, buf, len); - } base = 10; } else { base = js_get_radix(ctx, argv[0]); - if (base < 0) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } + if (base < 0) + goto fail; } if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { - size_t len = i32toa_radix(buf, JS_VALUE_GET_INT(val), base); - return js_new_string8_len(ctx, buf, len); + char buf1[70]; + int len; + len = i64toa_radix(buf1, JS_VALUE_GET_INT(val), base); + return js_new_string8_len(ctx, buf1, len); } if (JS_ToFloat64Free(ctx, &d, val)) return JS_EXCEPTION; + flags = JS_DTOA_FORMAT_FREE; if (base != 10) - return js_dtoa_radix(ctx, d, base); - - return js_dtoa(ctx, d, 0, JS_DTOA_TOSTRING); + flags |= JS_DTOA_EXP_DISABLED; + return js_dtoa2(ctx, d, base, 0, flags); + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; } -static JSValue js_number_toFixed(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue val; - int f; + int f, flags; double d; val = js_thisNumberValue(ctx, this_val); @@ -41301,23 +42821,21 @@ static JSValue js_number_toFixed(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; if (JS_ToInt32Sat(ctx, &f, argv[0])) return JS_EXCEPTION; - if (f < 0 || f > 100) { - return JS_ThrowRangeError(ctx, "toFixed() digits argument must be between 0 and 100"); - } - if (fabs(d) >= 1e21) { - // use ToString(d) - return js_dtoa(ctx, d, 0, JS_DTOA_TOSTRING); - } else { - return js_dtoa(ctx, d, f, JS_DTOA_FIXED); - } + if (f < 0 || f > 100) + return JS_ThrowRangeError(ctx, "invalid number of digits"); + if (fabs(d) >= 1e21) + flags = JS_DTOA_FORMAT_FREE; + else + flags = JS_DTOA_FORMAT_FRAC; + return js_dtoa2(ctx, d, 10, f, flags); } -static JSValue js_number_toExponential(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue val; + int f, flags; double d; - int f; val = js_thisNumberValue(ctx, this_val); if (JS_IsException(val)) @@ -41326,19 +42844,23 @@ static JSValue js_number_toExponential(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; if (JS_ToInt32Sat(ctx, &f, argv[0])) return JS_EXCEPTION; - if (!isfinite(d)) - return js_dtoa_infinite(ctx, d); - if (!JS_IsUndefined(argv[0])) { - if (f < 0 || f > 100) { - return JS_ThrowRangeError(ctx, "toExponential() argument must be between 0 and 100"); - } - f += 1; /* number of significant digits between 1 and 101 */ + if (!isfinite(d)) { + return JS_ToStringFree(ctx, __JS_NewFloat64(d)); + } + if (JS_IsUndefined(argv[0])) { + flags = JS_DTOA_FORMAT_FREE; + f = 0; + } else { + if (f < 0 || f > 100) + return JS_ThrowRangeError(ctx, "invalid number of digits"); + f++; + flags = JS_DTOA_FORMAT_FIXED; } - return js_dtoa(ctx, d, f, JS_DTOA_EXPONENTIAL); + return js_dtoa2(ctx, d, 10, f, flags | JS_DTOA_EXP_ENABLED); } -static JSValue js_number_toPrecision(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue val; int p; @@ -41350,15 +42872,16 @@ static JSValue js_number_toPrecision(JSContext *ctx, JSValue this_val, if (JS_ToFloat64Free(ctx, &d, val)) return JS_EXCEPTION; if (JS_IsUndefined(argv[0])) - return js_dtoa(ctx, d, 0, JS_DTOA_TOSTRING); + goto to_string; if (JS_ToInt32Sat(ctx, &p, argv[0])) return JS_EXCEPTION; - if (!isfinite(d)) - return js_dtoa_infinite(ctx, d); - if (p < 1 || p > 100) { - return JS_ThrowRangeError(ctx, "toPrecision() argument must be between 1 and 100"); + if (!isfinite(d)) { + to_string: + return JS_ToStringFree(ctx, __JS_NewFloat64(d)); } - return js_dtoa(ctx, d, p, JS_DTOA_PRECISION); + if (p < 1 || p > 100) + return JS_ThrowRangeError(ctx, "invalid number of digits"); + return js_dtoa2(ctx, d, 10, p, JS_DTOA_FORMAT_FIXED); } static const JSCFunctionListEntry js_number_proto_funcs[] = { @@ -41370,51 +42893,51 @@ static const JSCFunctionListEntry js_number_proto_funcs[] = { JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ), }; -static JSValue js_parseInt(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - const char *str; + const char *str, *p; int radix, flags; JSValue ret; - size_t len; - str = JS_ToCStringLen(ctx, &len, argv[0]); + str = JS_ToCString(ctx, argv[0]); if (!str) return JS_EXCEPTION; if (JS_ToInt32(ctx, &radix, argv[1])) { JS_FreeCString(ctx, str); return JS_EXCEPTION; } - flags = ATOD_TRIM_SPACES; - if (radix == 0) { - flags |= ATOD_ACCEPT_HEX_PREFIX; // Only 0x and 0X are supported - radix = 10; + if (radix != 0 && (radix < 2 || radix > 36)) { + ret = JS_NAN; + } else { + p = str; + p += skip_spaces(p); + flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN; + ret = js_atof(ctx, p, NULL, radix, flags); } - ret = js_atof(ctx, str, len, NULL, radix, flags); JS_FreeCString(ctx, str); return ret; } -static JSValue js_parseFloat(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - const char *str; + const char *str, *p; JSValue ret; - int flags; - size_t len; - str = JS_ToCStringLen(ctx, &len, argv[0]); + str = JS_ToCString(ctx, argv[0]); if (!str) return JS_EXCEPTION; - flags = ATOD_TRIM_SPACES | ATOD_ACCEPT_FLOAT | ATOD_ACCEPT_INFINITY; - ret = js_atof(ctx, str, len, NULL, 10, flags); + p = str; + p += skip_spaces(p); + ret = js_atof(ctx, p, NULL, 10, 0); JS_FreeCString(ctx, str); return ret; } /* Boolean */ -static JSValue js_boolean_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv) +static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { JSValue val, obj; val = js_bool(JS_ToBool(ctx, argv[0])); @@ -41428,7 +42951,7 @@ static JSValue js_boolean_constructor(JSContext *ctx, JSValue new_target, } } -static JSValue js_thisBooleanValue(JSContext *ctx, JSValue this_val) +static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val) { if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL) return js_dup(this_val); @@ -41443,8 +42966,8 @@ static JSValue js_thisBooleanValue(JSContext *ctx, JSValue this_val) return JS_ThrowTypeError(ctx, "not a boolean"); } -static JSValue js_boolean_toString(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue val = js_thisBooleanValue(ctx, this_val); if (JS_IsException(val)) @@ -41453,8 +42976,8 @@ static JSValue js_boolean_toString(JSContext *ctx, JSValue this_val, JS_ATOM_true : JS_ATOM_false); } -static JSValue js_boolean_valueOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return js_thisBooleanValue(ctx, this_val); } @@ -41468,7 +42991,7 @@ static const JSCFunctionListEntry js_boolean_proto_funcs[] = { static int js_string_get_own_property(JSContext *ctx, JSPropertyDescriptor *desc, - JSValue obj, JSAtom prop) + JSValueConst obj, JSAtom prop) { JSObject *p; JSString *p1; @@ -41496,10 +43019,10 @@ static int js_string_get_own_property(JSContext *ctx, } static int js_string_define_own_property(JSContext *ctx, - JSValue this_obj, - JSAtom prop, JSValue val, - JSValue getter, - JSValue setter, int flags) + JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, + JSValueConst setter, int flags) { uint32_t idx; JSObject *p; @@ -41536,7 +43059,7 @@ static int js_string_define_own_property(JSContext *ctx, } static int js_string_delete_property(JSContext *ctx, - JSValue obj, JSAtom prop) + JSValueConst obj, JSAtom prop) { uint32_t idx; @@ -41555,12 +43078,12 @@ static const JSClassExoticMethods js_string_exotic_methods = { .delete_property = js_string_delete_property, }; -static JSValue js_string_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv) +static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { JSValue val, obj; if (argc == 0) { - val = JS_AtomToString(ctx, JS_ATOM_empty_string); + val = js_empty_string(ctx->rt); } else { if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) { JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]); @@ -41575,7 +43098,9 @@ static JSValue js_string_constructor(JSContext *ctx, JSValue new_target, JSString *p1 = JS_VALUE_GET_STRING(val); obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING); - if (!JS_IsException(obj)) { + if (JS_IsException(obj)) { + JS_FreeValue(ctx, val); + } else { JS_SetObjectData(ctx, obj, val); JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, js_int32(p1->len), 0); } @@ -41585,7 +43110,7 @@ static JSValue js_string_constructor(JSContext *ctx, JSValue new_target, } } -static JSValue js_thisStringValue(JSContext *ctx, JSValue this_val) +static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val) { if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING) return js_dup(this_val); @@ -41600,8 +43125,8 @@ static JSValue js_thisStringValue(JSContext *ctx, JSValue this_val) return JS_ThrowTypeError(ctx, "not a string"); } -static JSValue js_string_fromCharCode(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int i; StringBuffer b_s, *b = &b_s; @@ -41624,8 +43149,8 @@ static JSValue js_string_fromCharCode(JSContext *ctx, JSValue this_val, return string_buffer_end(b); } -static JSValue js_string_fromCodePoint(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { double d; int i, c; @@ -41673,8 +43198,8 @@ static JSValue js_string_fromCodePoint(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_string_raw(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // raw(temp,...a) JSValue cooked, val, raw; @@ -41714,8 +43239,8 @@ static JSValue js_string_raw(JSContext *ctx, JSValue this_val, } /* only used in test262 */ -JSValue js_string_codePointRange(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { uint32_t start, end, i, n; StringBuffer b_s, *b = &b_s; @@ -41740,8 +43265,8 @@ JSValue js_string_codePointRange(JSContext *ctx, JSValue this_val, return string_buffer_end(b); } -static JSValue js_string_at(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_at(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue val, ret; JSString *p; @@ -41767,8 +43292,8 @@ static JSValue js_string_at(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_string_charCodeAt(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue val, ret; JSString *p; @@ -41792,8 +43317,8 @@ static JSValue js_string_charCodeAt(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_string_charAt(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue val, ret; JSString *p; @@ -41808,7 +43333,7 @@ static JSValue js_string_charAt(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } if (idx < 0 || idx >= p->len) { - ret = JS_AtomToString(ctx, JS_ATOM_empty_string); + ret = js_empty_string(ctx->rt); } else { c = string_get(p, idx); ret = js_new_string_char(ctx, c); @@ -41817,8 +43342,8 @@ static JSValue js_string_charAt(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_string_codePointAt(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue val, ret; JSString *p; @@ -41842,8 +43367,8 @@ static JSValue js_string_codePointAt(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_string_concat(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue r; int i; @@ -41876,13 +43401,13 @@ static int string_indexof_char(JSString *p, int c, int from) int i, len = p->len; if (p->is_wide_char) { for (i = from; i < len; i++) { - if (p->u.str16[i] == c) + if (str16(p)[i] == c) return i; } } else { if ((c & ~0xff) == 0) { for (i = from; i < len; i++) { - if (p->u.str8[i] == (uint8_t)c) + if (str8(p)[i] == (uint8_t)c) return i; } } @@ -41918,8 +43443,8 @@ static int64_t string_advance_index(JSString *p, int64_t index, bool unicode) return index; } -static JSValue js_string_isWellFormed(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_isWellFormed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue str; JSValue ret; @@ -41936,12 +43461,12 @@ static JSValue js_string_isWellFormed(JSContext *ctx, JSValue this_val, goto done; // by definition well-formed for (i = 0, n = p->len; i < n; i++) { - c = p->u.str16[i]; + c = str16(p)[i]; if (!is_surrogate(c)) continue; if (is_lo_surrogate(c) || i + 1 == n) break; - c = p->u.str16[++i]; + c = str16(p)[++i]; if (!is_lo_surrogate(c)) break; } @@ -41954,8 +43479,8 @@ static JSValue js_string_isWellFormed(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_string_toWellFormed(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_toWellFormed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue str; JSValue ret; @@ -41971,30 +43496,30 @@ static JSValue js_string_toWellFormed(JSContext *ctx, JSValue this_val, return str; // by definition well-formed // TODO(bnoordhuis) don't clone when input is well-formed - ret = js_new_string16_len(ctx, p->u.str16, p->len); + ret = js_new_string16_len(ctx, str16(p), p->len); JS_FreeValue(ctx, str); if (JS_IsException(ret)) return JS_EXCEPTION; p = JS_VALUE_GET_STRING(ret); for (i = 0, n = p->len; i < n; i++) { - c = p->u.str16[i]; + c = str16(p)[i]; if (!is_surrogate(c)) continue; if (is_lo_surrogate(c) || i + 1 == n) { - p->u.str16[i] = 0xFFFD; + str16(p)[i] = 0xFFFD; continue; } - c = p->u.str16[++i]; + c = str16(p)[++i]; if (!is_lo_surrogate(c)) - p->u.str16[--i] = 0xFFFD; + str16(p)[--i] = 0xFFFD; } return ret; } -static JSValue js_string_indexOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int lastIndexOf) +static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int lastIndexOf) { JSValue str, v; int i, len, v_len, pos, start, stop, ret, inc; @@ -42059,10 +43584,10 @@ static JSValue js_string_indexOf(JSContext *ctx, JSValue this_val, } /* return < 0 if exception or true/false */ -static int js_is_regexp(JSContext *ctx, JSValue obj); +static int js_is_regexp(JSContext *ctx, JSValueConst obj); -static JSValue js_string_includes(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSValue str, v = JS_UNDEFINED; int i, len, v_len, pos, start, stop, ret; @@ -42125,7 +43650,7 @@ static JSValue js_string_includes(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static int check_regexp_g_flag(JSContext *ctx, JSValue regexp) +static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp) { int ret; JSValue flags; @@ -42154,19 +43679,19 @@ static int check_regexp_g_flag(JSContext *ctx, JSValue regexp) return 0; } -static JSValue js_string_match(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int atom) +static JSValue js_string_match(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int atom) { // match(rx), search(rx), matchAll(rx) // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll - JSValue O = this_val, regexp = argv[0], args[2]; + JSValueConst O = this_val, regexp = argv[0], args[2]; JSValue matcher, S, rx, result, str; int args_len; if (JS_IsUndefined(O) || JS_IsNull(O)) return JS_ThrowTypeError(ctx, "cannot convert to object"); - if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) { + if (JS_IsObject(regexp)) { matcher = JS_GetProperty(ctx, regexp, atom); if (JS_IsException(matcher)) return JS_EXCEPTION; @@ -42199,16 +43724,16 @@ static JSValue js_string_match(JSContext *ctx, JSValue this_val, JS_FreeValue(ctx, S); return JS_EXCEPTION; } - result = JS_InvokeFree(ctx, rx, atom, 1, &S); + result = JS_InvokeFree(ctx, rx, atom, 1, vc(&S)); JS_FreeValue(ctx, S); return result; } -static JSValue js_string___GetSubstitution(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // GetSubstitution(matched, str, position, captures, namedCaptures, rep) - JSValue matched, str, captures, namedCaptures, rep; + JSValueConst matched, str, captures, namedCaptures, rep; JSValue capture, name, s; uint32_t position, len, matched_len, captures_len; int i, j, j0, k, k1; @@ -42312,13 +43837,13 @@ static JSValue js_string___GetSubstitution(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_string_replace(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, +static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int is_replaceAll) { // replace(rx, rep) - JSValue O = this_val, searchValue = argv[0], replaceValue = argv[1]; - JSValue args[6]; + JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1]; + JSValueConst args[6]; JSValue str, search_str, replaceValue_str, repl_str; JSString *sp, *searchp; StringBuffer b_s, *b = &b_s; @@ -42332,7 +43857,7 @@ static JSValue js_string_replace(JSContext *ctx, JSValue this_val, replaceValue_str = JS_UNDEFINED; repl_str = JS_UNDEFINED; - if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) { + if (JS_IsObject(searchValue)) { JSValue replacer; if (is_replaceAll) { if (check_regexp_g_flag(ctx, searchValue) < 0) @@ -42425,12 +43950,12 @@ static JSValue js_string_replace(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_string_split(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_split(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // split(sep, limit) - JSValue O = this_val, separator = argv[0], limit = argv[1]; - JSValue args[2]; + JSValueConst O = this_val, separator = argv[0], limit = argv[1]; + JSValueConst args[2]; JSValue S, A, R, T; uint32_t lim, lengthA; int64_t p, q, s, r, e; @@ -42443,7 +43968,7 @@ static JSValue js_string_split(JSContext *ctx, JSValue this_val, A = JS_UNDEFINED; R = JS_UNDEFINED; - if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) { + if (JS_IsObject(separator)) { JSValue splitter; splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split); if (JS_IsException(splitter)) @@ -42515,8 +44040,8 @@ static JSValue js_string_split(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_string_substring(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue str, ret; int a, b, start, end; @@ -42549,8 +44074,8 @@ static JSValue js_string_substring(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_string_substr(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue str, ret; int a, len, n; @@ -42577,8 +44102,8 @@ static JSValue js_string_substr(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_string_slice(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue str, ret; int len, start, end; @@ -42605,8 +44130,8 @@ static JSValue js_string_slice(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_string_pad(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int padEnd) +static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int padEnd) { JSValue str, v = JS_UNDEFINED; StringBuffer b_s, *b = &b_s; @@ -42638,7 +44163,7 @@ static JSValue js_string_pad(JSContext *ctx, JSValue this_val, } if (n > JS_STRING_LEN_MAX) { JS_ThrowRangeError(ctx, "invalid string length"); - goto fail2; + goto fail3; } if (string_buffer_init(ctx, b, n)) goto fail3; @@ -42676,8 +44201,8 @@ static JSValue js_string_pad(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_string_repeat(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue str; StringBuffer b_s, *b = &b_s; @@ -42720,8 +44245,8 @@ static JSValue js_string_repeat(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_string_trim(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSValue str, ret; int a, b, len; @@ -42756,16 +44281,16 @@ static int string_prevc(JSString *p, int *pidx) return 0; idx--; if (p->is_wide_char) { - c = p->u.str16[idx]; + c = str16(p)[idx]; if (is_lo_surrogate(c) && idx > 0) { - c1 = p->u.str16[idx - 1]; + c1 = str16(p)[idx - 1]; if (is_hi_surrogate(c1)) { c = from_surrogate(c1, c); idx--; } } } else { - c = p->u.str8[idx]; + c = str8(p)[idx]; } *pidx = idx; return c; @@ -42814,8 +44339,8 @@ static int to_utf32_buf(JSContext *ctx, JSString *p, uint32_t **pbuf) return j; } -static JSValue js_string_localeCompare(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int i, n, an, bn, cmp; uint32_t *as, *bs, *ts; @@ -42875,8 +44400,8 @@ static JSValue js_string_localeCompare(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_string_toLowerCase(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int to_lower) +static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int to_lower) { JSValue val; StringBuffer b_s, *b = &b_s; @@ -42943,8 +44468,8 @@ static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len) return JS_EXCEPTION; } -static JSValue js_string_normalize(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *form, *p; size_t form_len; @@ -42956,6 +44481,7 @@ static JSValue js_string_normalize(JSContext *ctx, JSValue this_val, val = JS_ToStringCheckObject(ctx, this_val); if (JS_IsException(val)) return val; + buf = NULL; // appease bogus -Wmaybe-uninitialized warning buf_len = JS_ToUTF32String(ctx, &buf, val); JS_FreeValue(ctx, val); if (buf_len < 0) @@ -43002,16 +44528,16 @@ static JSValue js_string_normalize(JSContext *ctx, JSValue this_val, } /* also used for String.prototype.valueOf */ -static JSValue js_string_toString(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return js_thisStringValue(ctx, this_val); } /* String Iterator */ -static JSValue js_string_iterator_next(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, +static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int *pdone, int magic) { JSArrayIteratorData *it; @@ -43042,7 +44568,7 @@ static JSValue js_string_iterator_next(JSContext *ctx, JSValue this_val, if (c <= 0xffff) { return js_new_string_char(ctx, c); } else { - return js_new_string16_len(ctx, p->u.str16 + start, 2); + return js_new_string16_len(ctx, str16(p) + start, 2); } } @@ -43063,11 +44589,11 @@ enum { magic_string_sup, }; -static JSValue js_string_CreateHTML(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSValue str; - const JSString *p; + JSString *p; StringBuffer b_s, *b = &b_s; static struct { const char *tag, *attr; } const defs[] = { { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL }, @@ -43215,8 +44741,8 @@ static double js_fmax(double a, double b) } } -static JSValue js_math_min_max(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { bool is_max = magic; double r, a; @@ -43306,8 +44832,8 @@ static double js_math_round(double a) return u.d; } -static JSValue js_math_hypot(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { double r, a; int i; @@ -43340,8 +44866,8 @@ static double js_math_fround(double a) return (float)a; } -static JSValue js_math_imul(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { uint32_t a, b, c; int32_t d; @@ -43355,8 +44881,8 @@ static JSValue js_math_imul(JSContext *ctx, JSValue this_val, return js_int32(d); } -static JSValue js_math_clz32(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { uint32_t a, r; @@ -43369,62 +44895,252 @@ static JSValue js_math_clz32(JSContext *ctx, JSValue this_val, return js_int32(r); } -static JSValue js_math_sumPrecise(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +/* we add one extra limb to avoid having to test for overflows during the sum */ +#define SUM_PRECISE_ACC_LEN 34 + +typedef enum { + SUM_PRECISE_STATE_MINUS_ZERO, + SUM_PRECISE_STATE_FINITE, + SUM_PRECISE_STATE_INFINITY, + SUM_PRECISE_STATE_MINUS_INFINITY, /* must be after SUM_PRECISE_STATE_INFINITY */ + SUM_PRECISE_STATE_NAN, /* must be after SUM_PRECISE_STATE_MINUS_INFINITY */ +} SumPreciseStateEnum; + +typedef struct { + uint64_t acc[SUM_PRECISE_ACC_LEN]; + int n_limbs; /* acc is not necessarily normalized */ + SumPreciseStateEnum state; +} SumPreciseState; + +static void sum_precise_init(SumPreciseState *s) +{ + s->state = SUM_PRECISE_STATE_MINUS_ZERO; + s->acc[0] = 0; + s->n_limbs = 1; +} + +#define ADDC64(res, carry_out, op1, op2, carry_in) \ +do { \ + uint64_t __v, __a, __k, __k1; \ + __v = (op1); \ + __a = __v + (op2); \ + __k1 = __a < __v; \ + __k = (carry_in); \ + __a = __a + __k; \ + carry_out = (__a < __k) | __k1; \ + res = __a; \ +} while (0) + +static void sum_precise_add(SumPreciseState *s, double d) +{ + uint64_t a, m, a0, carry, acc_sign, a_sign; + int sgn, e, p, n, i; + unsigned shift; + + a = float64_as_uint64(d); + sgn = a >> 63; + e = (a >> 52) & ((1 << 11) - 1); + m = a & (((uint64_t)1 << 52) - 1); + if (unlikely(e == 2047)) { + if (m == 0) { + /* +/- infinity */ + if (s->state == SUM_PRECISE_STATE_NAN || + (s->state == SUM_PRECISE_STATE_MINUS_INFINITY && !sgn) || + (s->state == SUM_PRECISE_STATE_INFINITY && sgn)) { + s->state = SUM_PRECISE_STATE_NAN; + } else { + s->state = SUM_PRECISE_STATE_INFINITY + sgn; + } + } else { + /* NaN */ + s->state = SUM_PRECISE_STATE_NAN; + } + } else if (e == 0) { + if (likely(m == 0)) { + /* zero */ + if (s->state == SUM_PRECISE_STATE_MINUS_ZERO && !sgn) + s->state = SUM_PRECISE_STATE_FINITE; + } else { + /* subnormal */ + p = 0; + shift = 0; + goto add; + } + } else { + m |= (uint64_t)1 << 52; + shift = e - 1; + p = shift / 64; + /* 'p' is the position of a0 in acc */ + shift %= 64; + add: + if (s->state >= SUM_PRECISE_STATE_INFINITY) + return; + s->state = SUM_PRECISE_STATE_FINITE; + n = s->n_limbs; + + acc_sign = (int64_t)s->acc[n - 1] >> 63; + + /* sign extend acc */ + for(i = n; i <= p; i++) + s->acc[i] = acc_sign; + + carry = sgn; + a_sign = -sgn; + a0 = m << shift; + ADDC64(s->acc[p], carry, s->acc[p], a0 ^ a_sign, carry); + if (shift >= 12) { + p++; + if (p >= n) + s->acc[p] = acc_sign; + a0 = m >> (64 - shift); + ADDC64(s->acc[p], carry, s->acc[p], a0 ^ a_sign, carry); + } + p++; + if (p >= n) { + n = p; + } else { + /* carry */ + for(i = p; i < n; i++) { + /* if 'a' positive: stop condition: carry = 0. + if 'a' negative: stop condition: carry = 1. */ + if (carry == sgn) + goto done; + ADDC64(s->acc[i], carry, s->acc[i], a_sign, carry); + } + } + + /* extend the accumulator if needed */ + a0 = carry + acc_sign + a_sign; + /* -1 <= a0 <= 1 (if both acc and a are negative, carry is set) */ + if (a0 != ((int64_t)s->acc[n - 1] >> 63)) { + s->acc[n++] = a0; + } + done: + s->n_limbs = n; + } +} + +static double sum_precise_get_result(SumPreciseState *s) +{ + int n, shift, e, p, is_neg, i; + uint64_t m, addend, carry; + + if (s->state != SUM_PRECISE_STATE_FINITE) { + switch(s->state) { + default: + case SUM_PRECISE_STATE_MINUS_ZERO: + return -0.0; + case SUM_PRECISE_STATE_INFINITY: + return INFINITY; + case SUM_PRECISE_STATE_MINUS_INFINITY: + return -INFINITY; + case SUM_PRECISE_STATE_NAN: + return NAN; + } + } + + /* extract the sign and absolute value */ + n = s->n_limbs; + is_neg = s->acc[n - 1] >> 63; + if (is_neg) { + /* acc = -acc */ + carry = 1; + for(i = 0; i < n; i++) { + ADDC64(s->acc[i], carry, ~s->acc[i], 0, carry); + } + } + /* normalize */ + while (n > 0 && s->acc[n - 1] == 0) + n--; + /* zero result. The spec tells it is always positive in the finite case */ + if (n == 0) + return 0.0; + /* subnormal case */ + if (n == 1 && s->acc[0] < ((uint64_t)1 << 52)) + return uint64_as_float64(((uint64_t)is_neg << 63) | s->acc[0]); + /* normal case */ + e = n * 64; + p = n - 1; + m = s->acc[p]; + shift = clz64(m); + e = e - shift - 52; + if (shift != 0) { + m <<= shift; + if (p > 0) { + int shift1; + uint64_t nz; + p--; + shift1 = 64 - shift; + nz = s->acc[p] & (((uint64_t)1 << shift1) - 1); + m = m | (s->acc[p] >> shift1) | (nz != 0); + } + } + if ((m & ((1 << 10) - 1)) == 0) { + /* see if the LSB part is non zero for the final rounding */ + while (p > 0) { + p--; + if (s->acc[p] != 0) { + m |= 1; + break; + } + } + } + /* rounding to nearest with ties to even */ + addend = (1 << 10) - 1 + ((m >> 11) & 1); + m = (m + addend) >> 11; + /* handle overflow in the rounding */ + if (m == 0) + e++; + if (unlikely(e >= 2047)) { + /* infinity */ + return uint64_as_float64(((uint64_t)is_neg << 63) | ((uint64_t)2047 << 52)); + } else { + m &= (((uint64_t)1 << 52) - 1); + return uint64_as_float64(((uint64_t)is_neg << 63) | ((uint64_t)e << 52) | m); + } +} + +static JSValue js_math_sumPrecise(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue iter, next, item, ret; - bf_t a, b; + uint32_t tag; int done; double d; - int r; + SumPreciseState s_s, *s = &s_s; - iter = JS_GetIterator(ctx, argv[0], /*async*/false); + iter = JS_GetIterator(ctx, argv[0], /*is_async*/false); if (JS_IsException(iter)) return JS_EXCEPTION; - bf_init(ctx->bf_ctx, &a); - bf_init(ctx->bf_ctx, &b); ret = JS_EXCEPTION; next = JS_GetProperty(ctx, iter, JS_ATOM_next); if (JS_IsException(next)) goto fail; - bf_set_zero(&a, /*is_neg*/true); + sum_precise_init(s); for (;;) { item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); if (JS_IsException(item)) goto fail; if (done) - break; // item == JS_UNDEFINED - switch (JS_VALUE_GET_TAG(item)) { - default: + break; + tag = JS_VALUE_GET_TAG(item); + if (JS_TAG_IS_FLOAT64(tag)) { + d = JS_VALUE_GET_FLOAT64(item); + } else if (tag == JS_TAG_INT) { + d = JS_VALUE_GET_INT(item); + } else { JS_FreeValue(ctx, item); JS_ThrowTypeError(ctx, "not a number"); + JS_IteratorClose(ctx, iter, /*is_exception_pending*/true); goto fail; - case JS_TAG_INT: - d = JS_VALUE_GET_INT(item); - break; - case JS_TAG_FLOAT64: - d = JS_VALUE_GET_FLOAT64(item); - break; } - if (bf_set_float64(&b, d)) - goto oom; - // Infinity + -Infinity results in BF_ST_INVALID_OP, sets |a| to nan - if ((r = bf_add(&a, &a, &b, BF_PREC_INF, BF_RNDN))) - if (r != BF_ST_INVALID_OP) - goto oom; + sum_precise_add(s, d); } - bf_get_float64(&a, &d, BF_RNDN); // return value deliberately ignored - ret = js_float64(d); + ret = js_float64(sum_precise_get_result(s)); fail: - JS_IteratorClose(ctx, iter, JS_IsException(ret)); JS_FreeValue(ctx, iter); JS_FreeValue(ctx, next); - bf_delete(&a); - bf_delete(&b); return ret; -oom: - JS_ThrowOutOfMemory(ctx); - goto fail; } /* xorshift* random number generator by Marsaglia */ @@ -43447,8 +45163,8 @@ static void js_random_init(JSContext *ctx) ctx->random_state = 1; } -static JSValue js_math_random(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_math_random(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSFloat64Union u; uint64_t v; @@ -43601,7 +45317,7 @@ static int getTimezoneOffset(int64_t time) { /* RegExp */ -static void js_regexp_finalizer(JSRuntime *rt, JSValue val) +static void js_regexp_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSRegExp *re = &p->u.regexp; @@ -43610,8 +45326,8 @@ static void js_regexp_finalizer(JSRuntime *rt, JSValue val) } /* create a string containing the RegExp bytecode */ -static JSValue js_compile_regexp(JSContext *ctx, JSValue pattern, - JSValue flags) +static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, + JSValueConst flags) { const char *str; int re_flags, mask; @@ -43688,7 +45404,7 @@ static JSValue js_compile_regexp(JSContext *ctx, JSValue pattern, /* create a RegExp object from a string containing the RegExp bytecode and the source pattern */ -static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValue ctor, +static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, JSValue pattern, JSValue bc) { JSValue obj; @@ -43717,7 +45433,8 @@ static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValue ctor, return obj; } -static JSRegExp *js_get_regexp(JSContext *ctx, JSValue obj, bool throw_error) +static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, + bool throw_error) { if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { JSObject *p = JS_VALUE_GET_OBJ(obj); @@ -43731,7 +45448,7 @@ static JSRegExp *js_get_regexp(JSContext *ctx, JSValue obj, bool throw_error) } /* return < 0 if exception or true/false */ -static int js_is_regexp(JSContext *ctx, JSValue obj) +static int js_is_regexp(JSContext *ctx, JSValueConst obj) { JSValue m; @@ -43745,11 +45462,11 @@ static int js_is_regexp(JSContext *ctx, JSValue obj) return js_get_regexp(ctx, obj, false) != NULL; } -static JSValue js_regexp_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv) +static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { JSValue pattern, flags, bc, val; - JSValue pat, flags1; + JSValueConst pat, flags1; JSRegExp *re; int pat_is_regexp; @@ -43802,7 +45519,7 @@ static JSValue js_regexp_constructor(JSContext *ctx, JSValue new_target, flags = js_dup(flags1); } if (JS_IsUndefined(pattern)) { - pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); + pattern = js_empty_string(ctx->rt); } else { val = pattern; pattern = JS_ToString(ctx, val); @@ -43823,11 +45540,11 @@ static JSValue js_regexp_constructor(JSContext *ctx, JSValue new_target, return JS_EXCEPTION; } -static JSValue js_regexp_compile(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSRegExp *re1, *re; - JSValue pattern1, flags1; + JSValueConst pattern1, flags1; JSValue bc, pattern; re = js_get_regexp(ctx, this_val, true); @@ -43844,7 +45561,7 @@ static JSValue js_regexp_compile(JSContext *ctx, JSValue this_val, } else { bc = JS_UNDEFINED; if (JS_IsUndefined(pattern1)) - pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); + pattern = js_empty_string(ctx->rt); else pattern = JS_ToString(ctx, pattern1); if (JS_IsException(pattern)) @@ -43867,7 +45584,7 @@ static JSValue js_regexp_compile(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_regexp_get_source(JSContext *ctx, JSValue this_val) +static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val) { JSRegExp *re; JSString *p; @@ -43933,7 +45650,7 @@ static JSValue js_regexp_get_source(JSContext *ctx, JSValue this_val) return string_buffer_end(b); } -static JSValue js_regexp_get_flag(JSContext *ctx, JSValue this_val, int mask) +static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask) { JSRegExp *re; int flags; @@ -43949,11 +45666,11 @@ static JSValue js_regexp_get_flag(JSContext *ctx, JSValue this_val, int mask) return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); } - flags = lre_get_flags(re->bytecode->u.str8); + flags = lre_get_flags(str8(re->bytecode)); return js_bool(flags & mask); } -static JSValue js_regexp_get_flags(JSContext *ctx, JSValue this_val) +static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val) { char str[8], *p = str; int res; @@ -44002,15 +45719,15 @@ static JSValue js_regexp_get_flags(JSContext *ctx, JSValue this_val) if (res) *p++ = 'y'; if (p == str) - return JS_AtomToString(ctx, JS_ATOM_empty_string); + return js_empty_string(ctx->rt); return js_new_string8_len(ctx, str, p - str); exception: return JS_EXCEPTION; } -static JSValue js_regexp_toString(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue pattern, flags; StringBuffer b_s, *b = &b_s; @@ -44040,6 +45757,14 @@ bool lre_check_stack_overflow(void *opaque, size_t alloca_size) return js_check_stack_overflow(ctx->rt, alloca_size); } +int lre_check_timeout(void *opaque) +{ + JSContext *ctx = opaque; + JSRuntime *rt = ctx->rt; + return (rt->interrupt_handler && + rt->interrupt_handler(rt, rt->interrupt_opaque)); +} + void *lre_realloc(void *opaque, void *ptr, size_t size) { JSContext *ctx = opaque; @@ -44047,8 +45772,8 @@ void *lre_realloc(void *opaque, void *ptr, size_t size) return js_realloc_rt(ctx->rt, ptr, size); } -static JSValue js_regexp_escape(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_regexp_escape(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { StringBuffer b_s, *b = &b_s; JSString *p; @@ -44060,7 +45785,7 @@ static JSValue js_regexp_escape(JSContext *ctx, JSValue this_val, p = JS_VALUE_GET_STRING(argv[0]); string_buffer_init2(ctx, b, 0, p->is_wide_char); for (i = 0; i < p->len; i++) { - c = p->is_wide_char ? (uint32_t)p->u.str16[i] : (uint32_t)p->u.str8[i]; + c = p->is_wide_char ? (uint32_t)str16(p)[i] : (uint32_t)str8(p)[i]; if (c < 33) { if (c >= 9 && c <= 13) { string_buffer_putc8(b, '\\'); @@ -44094,8 +45819,8 @@ static JSValue js_regexp_escape(JSContext *ctx, JSValue this_val, return string_buffer_end(b); } -static JSValue js_regexp_exec(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSRegExp *re = js_get_regexp(ctx, this_val, true); JSString *str; @@ -44125,7 +45850,7 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValue this_val, if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val)) goto fail; - re_bytecode = re->bytecode->u.str8; + re_bytecode = str8(re->bytecode); re_flags = lre_get_flags(re_bytecode); if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { last_index = 0; @@ -44138,7 +45863,7 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValue this_val, goto fail; } shift = str->is_wide_char; - str_buf = str->u.str8; + str_buf = str8(str); if (last_index > str->len) { rc = 2; } else { @@ -44154,7 +45879,11 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValue this_val, goto fail; } } else { - JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + if (rc == LRE_RET_TIMEOUT) { + JS_ThrowInterrupted(ctx); + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + } goto fail; } } else { @@ -44280,11 +46009,6 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValue this_val, goto fail; } } - - JS_FreeValue(ctx, ctx->regexp_last_match_str); - ctx->regexp_last_match_str = JS_DupValue(ctx, argv[0]); - ctx->regexp_last_match_start = (capture[0] - str_buf) >> shift; - ctx->regexp_last_match_end = (capture[1] - str_buf) >> shift; } ret = obj; obj = JS_UNDEFINED; @@ -44299,7 +46023,7 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValue this_val, } /* delete portions of a string that match a given regex */ -static JSValue JS_RegExpDelete(JSContext *ctx, JSValue this_val, JSValue arg) +static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValue arg) { JSRegExp *re = js_get_regexp(ctx, this_val, true); JSString *str; @@ -44322,7 +46046,7 @@ static JSValue JS_RegExpDelete(JSContext *ctx, JSValue this_val, JSValue arg) if (JS_IsException(str_val)) goto fail; str = JS_VALUE_GET_STRING(str_val); - re_bytecode = re->bytecode->u.str8; + re_bytecode = str8(re->bytecode); re_flags = lre_get_flags(re_bytecode); if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { last_index = 0; @@ -44338,7 +46062,7 @@ static JSValue JS_RegExpDelete(JSContext *ctx, JSValue this_val, JSValue arg) goto fail; } shift = str->is_wide_char; - str_buf = str->u.str8; + str_buf = str8(str); next_src_pos = 0; for (;;) { if (last_index > str->len) @@ -44354,7 +46078,11 @@ static JSValue JS_RegExpDelete(JSContext *ctx, JSValue this_val, JSValue arg) goto fail; } } else { - JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + if (ret == LRE_RET_TIMEOUT) { + JS_ThrowInterrupted(ctx); + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + } goto fail; } break; @@ -44394,7 +46122,7 @@ static JSValue JS_RegExpDelete(JSContext *ctx, JSValue this_val, JSValue arg) return JS_EXCEPTION; } -static JSValue JS_RegExpExec(JSContext *ctx, JSValue r, JSValue s) +static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s) { JSValue method, ret; @@ -44415,8 +46143,8 @@ static JSValue JS_RegExpExec(JSContext *ctx, JSValue r, JSValue s) return js_regexp_exec(ctx, r, 1, &s); } -static JSValue js_regexp_test(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue val; bool ret; @@ -44429,11 +46157,11 @@ static JSValue js_regexp_test(JSContext *ctx, JSValue this_val, return js_bool(ret); } -static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // [Symbol.match](str) - JSValue rx = this_val; + JSValueConst rx = this_val; JSValue A, S, flags, result, matchStr; int global, n, fullUnicode, isEmpty; JSString *p; @@ -44522,7 +46250,8 @@ typedef struct JSRegExpStringIteratorData { int done; } JSRegExpStringIteratorData; -static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val) +static void js_regexp_string_iterator_finalizer(JSRuntime *rt, + JSValueConst val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; @@ -44533,7 +46262,7 @@ static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val) } } -static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValue val, +static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -44545,8 +46274,8 @@ static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValue val, } static JSValue js_regexp_string_iterator_next(JSContext *ctx, - JSValue this_val, - int argc, JSValue *argv, + JSValueConst this_val, + int argc, JSValueConst *argv, int *pdone, int magic) { JSRegExpStringIteratorData *it; @@ -44597,13 +46326,13 @@ static JSValue js_regexp_string_iterator_next(JSContext *ctx, return JS_EXCEPTION; } -static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // [Symbol.matchAll](str) - JSValue R = this_val; + JSValueConst R = this_val; JSValue S, C, flags, matcher, iter; - JSValue args[2]; + JSValueConst args[2]; JSString *strp; int64_t lastIndex; JSRegExpStringIteratorData *it; @@ -44722,7 +46451,7 @@ static int value_buffer_append(ValueBuffer *b, JSValue val) return 0; } -static int js_is_standard_regexp(JSContext *ctx, JSValue rx) +static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx) { JSValue val; int res; @@ -44744,12 +46473,12 @@ static int js_is_standard_regexp(JSContext *ctx, JSValue rx) return res; } -static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // [Symbol.replace](str, rep) - JSValue rx = this_val, rep = argv[1]; - JSValue args[6]; + JSValueConst rx = this_val, rep = argv[1]; + JSValueConst args[6]; JSValue flags, str, rep_val, matched, tab, rep_str, namedCaptures, res; JSString *p, *sp, *rp; StringBuffer b_s, *b = &b_s; @@ -44937,10 +46666,10 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValue this_val, return res; } -static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue rx = this_val; + JSValueConst rx = this_val; JSValue str, previousLastIndex, currentLastIndex, result, index; if (!JS_IsObject(rx)) @@ -44995,12 +46724,12 @@ static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // [Symbol.split](str, limit) - JSValue rx = this_val; - JSValue args[2]; + JSValueConst rx = this_val; + JSValueConst args[2]; JSValue str, ctor, splitter, A, flags, z, sub; JSString *strp; uint32_t lim, size, p, q; @@ -45126,28 +46855,8 @@ static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValue this_val, return A; } -static JSValue js_regexp_get_leftContext(JSContext *ctx, JSValue this_val) { - if (ctx->regexp_last_match_start >= 0) { - JS_FreeValue(ctx, ctx->regexp_left_ctx); - ctx->regexp_left_ctx = js_sub_string(ctx, JS_VALUE_GET_STRING(ctx->regexp_last_match_str), 0, ctx->regexp_last_match_start); - ctx->regexp_last_match_start = -1; // Reset to avoid recomputation - } - return JS_DupValue(ctx, ctx->regexp_left_ctx); -} - -static JSValue js_regexp_get_rightContext(JSContext *ctx, JSValue this_val) { - if (ctx->regexp_last_match_end >= 0) { - JS_FreeValue(ctx, ctx->regexp_right_ctx); - ctx->regexp_right_ctx = js_sub_string(ctx, JS_VALUE_GET_STRING(ctx->regexp_last_match_str), ctx->regexp_last_match_end, JS_VALUE_GET_STRING(ctx->regexp_last_match_str)->len); - ctx->regexp_last_match_end = -1; // Reset to avoid recomputation - } - return JS_DupValue(ctx, ctx->regexp_right_ctx); -} - static const JSCFunctionListEntry js_regexp_funcs[] = { JS_CFUNC_DEF("escape", 1, js_regexp_escape ), - JS_CGETSET_DEF("leftContext", js_regexp_get_leftContext, NULL), - JS_CGETSET_DEF("rightContext", js_regexp_get_rightContext, NULL), JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), }; @@ -45343,7 +47052,7 @@ JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, const char JSParseState s1, *s = &s1; JSValue val = JS_UNDEFINED; - js_parse_init(ctx, s, buf, buf_len, filename); + js_parse_init(ctx, s, buf, buf_len, filename, 1); if (json_next_token(s)) goto fail; val = json_parse_value(s); @@ -45360,11 +47069,11 @@ JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, const char return JS_EXCEPTION; } -static JSValue internalize_json_property(JSContext *ctx, JSValue holder, - JSAtom name, JSValue reviver) +static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder, + JSAtom name, JSValueConst reviver) { JSValue val, new_el, name_val, res; - JSValue args[2]; + JSValueConst args[2]; int ret, is_array; uint32_t i, len = 0; JSAtom prop; @@ -45378,7 +47087,7 @@ static JSValue internalize_json_property(JSContext *ctx, JSValue holder, if (JS_IsException(val)) return val; if (JS_IsObject(val)) { - is_array = JS_IsArray(ctx, val); + is_array = js_is_array(ctx, val); if (is_array < 0) goto fail; if (is_array) { @@ -45429,11 +47138,11 @@ static JSValue internalize_json_property(JSContext *ctx, JSValue holder, return JS_EXCEPTION; } -static JSValue js_json_parse(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj, root; - JSValue reviver; + JSValueConst reviver; const char *str; size_t len; @@ -45464,7 +47173,7 @@ static JSValue js_json_parse(JSContext *ctx, JSValue this_val, } typedef struct JSONStringifyContext { - JSValue replacer_func; + JSValueConst replacer_func; JSValue stack; JSValue property_list; JSValue gap; @@ -45479,12 +47188,13 @@ static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) { } static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, - JSValue holder, JSValue val, JSValue key) + JSValueConst holder, JSValue val, + JSValueConst key) { JSValue v; - JSValue args[2]; + JSValueConst args[2]; - if (JS_IsObject(val) || JS_IsBigInt(ctx, val)) { + if (JS_IsObject(val) || JS_IsBigInt(val)) { JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON); if (JS_IsException(f)) goto exception; @@ -45518,6 +47228,7 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, case JS_TAG_FLOAT64: case JS_TAG_BOOL: case JS_TAG_NULL: + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: case JS_TAG_EXCEPTION: return val; @@ -45533,8 +47244,8 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, } static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, - JSValue holder, JSValue val, - JSValue indent) + JSValueConst holder, JSValue val, + JSValueConst indent) { JSValue indent1, sep, sep1, tab, v, prop; JSObject *p; @@ -45548,6 +47259,11 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, tab = JS_UNDEFINED; prop = JS_UNDEFINED; + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + goto exception; + } + if (JS_IsObject(val)) { p = JS_VALUE_GET_OBJ(val); cl = p->class_id; @@ -45565,7 +47281,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, set_value(ctx, &val, js_dup(p->u.object_data)); goto concat_primitive; } - v = js_array_includes(ctx, jsc->stack, 1, &val); + v = js_array_includes(ctx, jsc->stack, 1, vc(&val)); if (JS_IsException(v)) goto exception; if (JS_ToBoolFree(ctx, v)) { @@ -45586,10 +47302,10 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, sep = js_dup(jsc->empty); sep1 = js_dup(jsc->empty); } - v = js_array_push(ctx, jsc->stack, 1, &val, 0); + v = js_array_push(ctx, jsc->stack, 1, vc(&val), 0); if (check_exception_free(ctx, v)) goto exception; - ret = JS_IsArray(ctx, val); + ret = js_is_array(ctx, val); if (ret < 0) goto exception; if (ret) { @@ -45626,7 +47342,8 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, if (!JS_IsUndefined(jsc->property_list)) tab = js_dup(jsc->property_list); else - tab = js_object_keys(ctx, JS_UNDEFINED, 1, &val, JS_ITERATOR_KIND_KEY); + tab = js_object_keys(ctx, JS_UNDEFINED, 1, vc(&val), + JS_ITERATOR_KIND_KEY); if (JS_IsException(tab)) goto exception; if (js_get_length64(ctx, &len, tab)) @@ -45694,6 +47411,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, case JS_TAG_NULL: concat_value: return string_buffer_concat_value_free(jsc->b, val); + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: JS_ThrowTypeError(ctx, "BigInt are forbidden in JSON.stringify"); goto exception; @@ -45712,8 +47430,8 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, return -1; } -JSValue JS_JSONStringify(JSContext *ctx, JSValue obj, - JSValue replacer, JSValue space0) +JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, + JSValueConst replacer, JSValueConst space0) { StringBuffer b_s; JSONStringifyContext jsc_s, *jsc = &jsc_s; @@ -45726,7 +47444,7 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValue obj, jsc->property_list = JS_UNDEFINED; jsc->gap = JS_UNDEFINED; jsc->b = &b_s; - jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string); + jsc->empty = js_empty_string(ctx->rt); ret = JS_UNDEFINED; wrapper = JS_UNDEFINED; @@ -45737,7 +47455,7 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValue obj, if (JS_IsFunction(ctx, replacer)) { jsc->replacer_func = replacer; } else { - res = JS_IsArray(ctx, replacer); + res = js_is_array(ctx, replacer); if (res < 0) goto exception; if (res) { @@ -45772,7 +47490,7 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValue obj, continue; } present = js_array_includes(ctx, jsc->property_list, - 1, &v); + 1, vc(&v)); if (JS_IsException(present)) { JS_FreeValue(ctx, v); goto exception; @@ -45846,8 +47564,8 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValue obj, return ret; } -static JSValue js_json_stringify(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // stringify(val, replacer, space) return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]); @@ -45871,16 +47589,16 @@ void JS_AddIntrinsicJSON(JSContext *ctx) /* Reflect */ -static JSValue js_reflect_apply(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2); } -static JSValue js_reflect_construct(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue func, array_arg, new_target; + JSValueConst func, array_arg, new_target; JSValue *tab, ret; uint32_t len; @@ -45889,22 +47607,22 @@ static JSValue js_reflect_construct(JSContext *ctx, JSValue this_val, if (argc > 2) { new_target = argv[2]; if (!JS_IsConstructor(ctx, new_target)) - return JS_ThrowTypeError(ctx, "not a constructor"); + return JS_ThrowTypeErrorNotAConstructor(ctx, new_target); } else { new_target = func; } tab = build_arg_list(ctx, &len, array_arg); if (!tab) return JS_EXCEPTION; - ret = JS_CallConstructor2(ctx, func, new_target, len, tab); + ret = JS_CallConstructor2(ctx, func, new_target, len, vc(tab)); free_arg_list(ctx, tab, len); return ret; } -static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue obj; + JSValueConst obj; JSAtom atom; int ret; @@ -45922,10 +47640,10 @@ static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValue this_val, return js_bool(ret); } -static JSValue js_reflect_get(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue obj, prop, receiver; + JSValueConst obj, prop, receiver; JSAtom atom; JSValue ret; @@ -45945,10 +47663,10 @@ static JSValue js_reflect_get(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_reflect_has(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue obj, prop; + JSValueConst obj, prop; JSAtom atom; int ret; @@ -45967,10 +47685,10 @@ static JSValue js_reflect_has(JSContext *ctx, JSValue this_val, return js_bool(ret); } -static JSValue js_reflect_set(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue obj, prop, val, receiver; + JSValueConst obj, prop, val, receiver; int ret; JSAtom atom; @@ -45986,8 +47704,7 @@ static JSValue js_reflect_set(JSContext *ctx, JSValue this_val, atom = JS_ValueToAtom(ctx, prop); if (unlikely(atom == JS_ATOM_NULL)) return JS_EXCEPTION; - ret = JS_SetPropertyInternal2(ctx, obj, atom, js_dup(val), receiver, - 0, NULL); + ret = JS_SetPropertyInternal2(ctx, obj, atom, js_dup(val), receiver, 0); JS_FreeAtom(ctx, atom); if (ret < 0) return JS_EXCEPTION; @@ -45995,8 +47712,8 @@ static JSValue js_reflect_set(JSContext *ctx, JSValue this_val, return js_bool(ret); } -static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { int ret; ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], false); @@ -46006,8 +47723,8 @@ static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValue this_val, return js_bool(ret); } -static JSValue js_reflect_ownKeys(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) return JS_ThrowTypeErrorNotAnObject(ctx); @@ -46039,7 +47756,7 @@ static const JSCFunctionListEntry js_reflect_obj[] = { /* Proxy */ -static void js_proxy_finalizer(JSRuntime *rt, JSValue val) +static void js_proxy_finalizer(JSRuntime *rt, JSValueConst val) { JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); if (s) { @@ -46049,7 +47766,7 @@ static void js_proxy_finalizer(JSRuntime *rt, JSValue val) } } -static void js_proxy_mark(JSRuntime *rt, JSValue val, +static void js_proxy_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); @@ -46065,7 +47782,7 @@ static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx) } static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod, - JSValue obj, JSAtom name) + JSValueConst obj, JSAtom name) { JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); JSValue method; @@ -46090,7 +47807,7 @@ static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod, return s; } -static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValue obj) +static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) { JSProxyData *s; JSValue method, ret, proto1; @@ -46101,7 +47818,7 @@ static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValue obj) return JS_EXCEPTION; if (JS_IsUndefined(method)) return JS_GetPrototype(ctx, s->target); - ret = JS_CallFree(ctx, method, s->handler, 1, &s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, vc(&s->target)); if (JS_IsException(ret)) return ret; if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL && @@ -46131,12 +47848,12 @@ static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValue obj) return ret; } -static int js_proxy_setPrototypeOf(JSContext *ctx, JSValue obj, - JSValue proto_val, bool throw_flag) +static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, + JSValueConst proto_val, bool throw_flag) { JSProxyData *s; JSValue method, ret, proto1; - JSValue args[2]; + JSValueConst args[2]; bool res; int res2; @@ -46176,7 +47893,7 @@ static int js_proxy_setPrototypeOf(JSContext *ctx, JSValue obj, return true; } -static int js_proxy_isExtensible(JSContext *ctx, JSValue obj) +static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj) { JSProxyData *s; JSValue method, ret; @@ -46188,7 +47905,7 @@ static int js_proxy_isExtensible(JSContext *ctx, JSValue obj) return -1; if (JS_IsUndefined(method)) return JS_IsExtensible(ctx, s->target); - ret = JS_CallFree(ctx, method, s->handler, 1, &s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, vc(&s->target)); if (JS_IsException(ret)) return -1; res = JS_ToBoolFree(ctx, ret); @@ -46202,7 +47919,7 @@ static int js_proxy_isExtensible(JSContext *ctx, JSValue obj) return res; } -static int js_proxy_preventExtensions(JSContext *ctx, JSValue obj) +static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj) { JSProxyData *s; JSValue method, ret; @@ -46214,7 +47931,7 @@ static int js_proxy_preventExtensions(JSContext *ctx, JSValue obj) return -1; if (JS_IsUndefined(method)) return JS_PreventExtensions(ctx, s->target); - ret = JS_CallFree(ctx, method, s->handler, 1, &s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, vc(&s->target)); if (JS_IsException(ret)) return -1; res = JS_ToBoolFree(ctx, ret); @@ -46230,13 +47947,13 @@ static int js_proxy_preventExtensions(JSContext *ctx, JSValue obj) return res; } -static int js_proxy_has(JSContext *ctx, JSValue obj, JSAtom atom) +static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom) { JSProxyData *s; JSValue method, ret1, atom_val; int res; JSObject *p; - JSValue args[2]; + JSValueConst args[2]; bool ret, res2; s = get_proxy_method(ctx, &method, obj, JS_ATOM_has); @@ -46274,13 +47991,13 @@ static int js_proxy_has(JSContext *ctx, JSValue obj, JSAtom atom) return ret; } -static JSValue js_proxy_get(JSContext *ctx, JSValue obj, JSAtom atom, - JSValue receiver) +static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst receiver) { JSProxyData *s; JSValue method, ret, atom_val; int res; - JSValue args[3]; + JSValueConst args[3]; JSPropertyDescriptor desc; s = get_proxy_method(ctx, &method, obj, JS_ATOM_get); @@ -46324,22 +48041,21 @@ static JSValue js_proxy_get(JSContext *ctx, JSValue obj, JSAtom atom, return ret; } -static int js_proxy_set(JSContext *ctx, JSValue obj, JSAtom atom, - JSValue value, JSValue receiver, int flags) +static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst value, JSValueConst receiver, int flags) { JSProxyData *s; JSValue method, ret1, atom_val; bool ret; int res; - JSValue args[4]; + JSValueConst args[4]; s = get_proxy_method(ctx, &method, obj, JS_ATOM_set); if (!s) return -1; if (JS_IsUndefined(method)) { return JS_SetPropertyInternal2(ctx, s->target, atom, - js_dup(value), receiver, - flags, NULL); + js_dup(value), receiver, flags); } atom_val = JS_AtomToValue(ctx, atom); if (JS_IsException(atom_val)) { @@ -46383,8 +48099,8 @@ static int js_proxy_set(JSContext *ctx, JSValue obj, JSAtom atom, return ret; } -static JSValue js_create_desc(JSContext *ctx, JSValue val, - JSValue getter, JSValue setter, +static JSValue js_create_desc(JSContext *ctx, JSValueConst val, + JSValueConst getter, JSValueConst setter, int flags) { JSValue ret; @@ -46422,13 +48138,13 @@ static JSValue js_create_desc(JSContext *ctx, JSValue val, } static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc, - JSValue obj, JSAtom prop) + JSValueConst obj, JSAtom prop) { JSProxyData *s; JSValue method, trap_result_obj, prop_val; int res, target_desc_ret, ret; JSObject *p; - JSValue args[2]; + JSValueConst args[2]; JSPropertyDescriptor result_desc, target_desc; s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor); @@ -46518,16 +48234,16 @@ static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc return ret; } -static int js_proxy_define_own_property(JSContext *ctx, JSValue obj, - JSAtom prop, JSValue val, - JSValue getter, JSValue setter, - int flags) +static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, + JSValueConst setter, int flags) { JSProxyData *s; JSValue method, ret1, prop_val, desc_val; int res; JSObject *p; - JSValue args[3]; + JSValueConst args[3]; JSPropertyDescriptor desc; bool ret, setting_not_configurable; @@ -46618,14 +48334,14 @@ static int js_proxy_define_own_property(JSContext *ctx, JSValue obj, return 1; } -static int js_proxy_delete_property(JSContext *ctx, JSValue obj, +static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj, JSAtom atom) { JSProxyData *s; JSValue method, ret, atom_val; int res2, is_extensible; bool res; - JSValue args[2]; + JSValueConst args[2]; s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty); if (!s) @@ -46684,7 +48400,7 @@ static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom) static int js_proxy_get_own_property_names(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen, - JSValue obj) + JSValueConst obj) { JSProxyData *s; JSValue method, prop_array, val; @@ -46702,7 +48418,7 @@ static int js_proxy_get_own_property_names(JSContext *ctx, JS_VALUE_GET_OBJ(s->target), JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK); } - prop_array = JS_CallFree(ctx, method, s->handler, 1, &s->target); + prop_array = JS_CallFree(ctx, method, s->handler, 1, vc(&s->target)); if (JS_IsException(prop_array)) return -1; tab = NULL; @@ -46799,19 +48515,19 @@ static int js_proxy_get_own_property_names(JSContext *ctx, return -1; } -static JSValue js_proxy_call_constructor(JSContext *ctx, JSValue func_obj, - JSValue new_target, - int argc, JSValue *argv) +static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv) { JSProxyData *s; JSValue method, arg_array, ret; - JSValue args[3]; + JSValueConst args[3]; s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct); if (!s) return JS_EXCEPTION; if (!JS_IsConstructor(ctx, s->target)) - return JS_ThrowTypeError(ctx, "not a constructor"); + return JS_ThrowTypeErrorNotAConstructor(ctx, s->target); if (JS_IsUndefined(method)) return JS_CallConstructor2(ctx, s->target, new_target, argc, argv); arg_array = js_create_array(ctx, argc, argv); @@ -46833,13 +48549,13 @@ static JSValue js_proxy_call_constructor(JSContext *ctx, JSValue func_obj, return ret; } -static JSValue js_proxy_call(JSContext *ctx, JSValue func_obj, - JSValue this_obj, - int argc, JSValue *argv, int flags) +static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) { JSProxyData *s; JSValue method, arg_array, ret; - JSValue args[3]; + JSValueConst args[3]; if (flags & JS_CALL_FLAG_CONSTRUCTOR) return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv); @@ -46849,7 +48565,7 @@ static JSValue js_proxy_call(JSContext *ctx, JSValue func_obj, return JS_EXCEPTION; if (!s->is_func) { JS_FreeValue(ctx, method); - return JS_ThrowTypeError(ctx, "not a function"); + return JS_ThrowTypeErrorNotAFunction(ctx); } if (JS_IsUndefined(method)) return JS_Call(ctx, s->target, this_obj, argc, argv); @@ -46868,7 +48584,7 @@ static JSValue js_proxy_call(JSContext *ctx, JSValue func_obj, return ret; } -static int js_proxy_isArray(JSContext *ctx, JSValue obj) +static int js_proxy_isArray(JSContext *ctx, JSValueConst obj) { JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); if (!s) @@ -46883,7 +48599,7 @@ static int js_proxy_isArray(JSContext *ctx, JSValue obj) JS_ThrowTypeErrorRevokedProxy(ctx); return -1; } - return JS_IsArray(ctx, s->target); + return js_is_array(ctx, s->target); } static const JSClassExoticMethods js_proxy_exotic_methods = { @@ -46896,10 +48612,10 @@ static const JSClassExoticMethods js_proxy_exotic_methods = { .set_property = js_proxy_set, }; -static JSValue js_proxy_constructor(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue target, handler; + JSValueConst target, handler; JSValue obj; JSProxyData *s; @@ -46926,29 +48642,29 @@ static JSValue js_proxy_constructor(JSContext *ctx, JSValue this_val, return obj; } -static JSValue js_proxy_revoke(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic, - JSValue *func_data) +static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, + JSValueConst *func_data) { JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY); if (s) { /* We do not free the handler and target in case they are referenced as constants in the C call stack */ s->is_revoked = true; - JS_FreeValue(ctx, func_data[0]); + JS_FreeValue(ctx, unsafe_unconst(func_data[0])); func_data[0] = JS_NULL; } return JS_UNDEFINED; } static JSValue js_proxy_revoke_constructor(JSContext *ctx, - JSValue proxy_obj) + JSValueConst proxy_obj) { return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj); } -static JSValue js_proxy_revocable(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj; @@ -47000,16 +48716,50 @@ void JS_AddIntrinsicProxy(JSContext *ctx) obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); } +bool JS_IsProxy(JSValueConst val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_PROXY; + } + return false; +} + +static JSValue js_get_proxy_field(JSContext *ctx, JSValueConst proxy, + int offset) +{ + if (JS_VALUE_GET_TAG(proxy) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(proxy); + if (p->class_id == JS_CLASS_PROXY) { + JSProxyData *s = JS_GetOpaque(proxy, JS_CLASS_PROXY); + if (s->is_revoked) + return JS_ThrowTypeErrorRevokedProxy(ctx); + return js_dup(*(JSValue *)((char *)s + offset)); + } + } + return JS_ThrowTypeError(ctx, "not a proxy"); +} + +JSValue JS_GetProxyTarget(JSContext *ctx, JSValueConst proxy) +{ + return js_get_proxy_field(ctx, proxy, offsetof(JSProxyData, target)); +} + +JSValue JS_GetProxyHandler(JSContext *ctx, JSValueConst proxy) +{ + return js_get_proxy_field(ctx, proxy, offsetof(JSProxyData, handler)); +} + /* Symbol */ -static JSValue js_symbol_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv) +static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { JSValue str; JSString *p; if (!JS_IsUndefined(new_target)) - return JS_ThrowTypeError(ctx, "not a constructor"); + return JS_ThrowTypeErrorNotAConstructor(ctx, new_target); if (argc == 0 || JS_IsUndefined(argv[0])) { p = NULL; } else { @@ -47021,7 +48771,7 @@ static JSValue js_symbol_constructor(JSContext *ctx, JSValue new_target, return JS_NewSymbolInternal(ctx, p, JS_ATOM_TYPE_SYMBOL); } -static JSValue js_thisSymbolValue(JSContext *ctx, JSValue this_val) +static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val) { if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL) return js_dup(this_val); @@ -47036,26 +48786,26 @@ static JSValue js_thisSymbolValue(JSContext *ctx, JSValue this_val) return JS_ThrowTypeError(ctx, "not a symbol"); } -static JSValue js_symbol_toString(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue val, ret; val = js_thisSymbolValue(ctx, this_val); if (JS_IsException(val)) return val; /* XXX: use JS_ToStringInternal() with a flags */ - ret = js_string_constructor(ctx, JS_UNDEFINED, 1, &val); + ret = js_string_constructor(ctx, JS_UNDEFINED, 1, vc(&val)); JS_FreeValue(ctx, val); return ret; } -static JSValue js_symbol_valueOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return js_thisSymbolValue(ctx, this_val); } -static JSValue js_symbol_get_description(JSContext *ctx, JSValue this_val) +static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val) { JSValue val, ret; JSAtomStruct *p; @@ -47082,8 +48832,8 @@ static const JSCFunctionListEntry js_symbol_proto_funcs[] = { JS_CGETSET_DEF("description", js_symbol_get_description, NULL ), }; -static JSValue js_symbol_for(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue str; @@ -47093,8 +48843,8 @@ static JSValue js_symbol_for(JSContext *ctx, JSValue this_val, return JS_NewSymbolInternal(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL); } -static JSValue js_symbol_keyFor(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSAtomStruct *p; @@ -47113,35 +48863,15 @@ static const JSCFunctionListEntry js_symbol_funcs[] = { /* Set/Map/WeakSet/WeakMap */ -typedef struct JSMapRecord { - int ref_count; /* used during enumeration to avoid freeing the record */ - bool empty; /* true if the record is deleted */ - struct JSMapState *map; - struct list_head link; - struct list_head hash_link; - JSValue key; - JSValue value; -} JSMapRecord; - -typedef struct JSMapState { - bool is_weak; /* true if WeakSet/WeakMap */ - struct list_head records; /* list of JSMapRecord.link */ - uint32_t record_count; - struct list_head *hash_table; - uint32_t hash_size; /* must be a power of two */ - uint32_t record_count_threshold; /* count at which a hash table - resize is needed */ -} JSMapState; - #define MAGIC_SET (1 << 0) #define MAGIC_WEAK (1 << 1) -static JSValue js_map_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv, int magic) +static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv, int magic) { JSMapState *s; JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED; - JSValue arr; + JSValueConst arr; bool is_set, is_weak; is_set = magic & MAGIC_SET; @@ -47188,19 +48918,17 @@ static JSValue js_map_constructor(JSContext *ctx, JSValue new_target, item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); if (JS_IsException(item)) goto fail; - if (done) { - JS_FreeValue(ctx, item); + if (done) break; - } if (is_set) { - ret = JS_Call(ctx, adder, obj, 1, &item); + ret = JS_Call(ctx, adder, obj, 1, vc(&item)); if (JS_IsException(ret)) { JS_FreeValue(ctx, item); goto fail; } } else { JSValue key, value; - JSValue args[2]; + JSValueConst args[2]; key = JS_UNDEFINED; value = JS_UNDEFINED; if (!JS_IsObject(item)) { @@ -47250,21 +48978,26 @@ static JSValue js_map_constructor(JSContext *ctx, JSValue new_target, static JSValue map_normalize_key(JSContext *ctx, JSValue key) { uint32_t tag = JS_VALUE_GET_TAG(key); - /* convert -0.0 to +0.0 */ - if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) { - key = js_int32(0); - } + // convert -0.0 to +0.0 + // not a leak; |key| and return value are not heap-allocated + if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) + return js_int32(0); return key; } +static JSValueConst map_normalize_key_const(JSContext *ctx, JSValueConst key) +{ + return safe_const(map_normalize_key(ctx, unsafe_unconst(key))); +} + /* XXX: better hash ? */ -static uint32_t map_hash_key(JSContext *ctx, JSValue key) +static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) { uint32_t tag = JS_VALUE_GET_NORM_TAG(key); uint32_t h; double d; JSFloat64Union u; - bf_t *a; + JSBigInt *r; switch(tag) { case JS_TAG_BOOL: @@ -47280,15 +49013,18 @@ static uint32_t map_hash_key(JSContext *ctx, JSValue key) case JS_TAG_INT: d = JS_VALUE_GET_INT(key); goto hash_float64; + case JS_TAG_SHORT_BIG_INT: + d = JS_VALUE_GET_SHORT_BIG_INT(key); + goto hash_float64; case JS_TAG_BIG_INT: - a = JS_GetBigInt(key); - h = hash_string8((void *)a->tab, a->len * sizeof(*a->tab), 0); + r = JS_VALUE_GET_PTR(key); + h = hash_string8((void *)r->tab, r->len * sizeof(*r->tab), 0); break; case JS_TAG_FLOAT64: d = JS_VALUE_GET_FLOAT64(key); /* normalize the NaN */ if (isnan(d)) - d = JS_FLOAT64_NAN; + d = NAN; hash_float64: u.d = d; h = (u.u32[0] ^ u.u32[1]) * 3163; @@ -47302,7 +49038,7 @@ static uint32_t map_hash_key(JSContext *ctx, JSValue key) } static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s, - JSValue key) + JSValueConst key) { struct list_head *el; JSMapRecord *mr; @@ -47319,7 +49055,6 @@ static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s, static void map_hash_resize(JSContext *ctx, JSMapState *s) { uint32_t new_hash_size, i, h; - size_t slack; struct list_head *new_hash_table, *el; JSMapRecord *mr; @@ -47328,11 +49063,10 @@ static void map_hash_resize(JSContext *ctx, JSMapState *s) new_hash_size = 4; else new_hash_size = s->hash_size * 2; - new_hash_table = js_realloc2(ctx, s->hash_table, - sizeof(new_hash_table[0]) * new_hash_size, &slack); + new_hash_table = js_realloc(ctx, s->hash_table, + sizeof(new_hash_table[0]) * new_hash_size); if (!new_hash_table) return; - new_hash_size += slack / sizeof(*new_hash_table); for(i = 0; i < new_hash_size; i++) init_list_head(&new_hash_table[i]); @@ -47349,7 +49083,7 @@ static void map_hash_resize(JSContext *ctx, JSMapState *s) s->record_count_threshold = new_hash_size * 2; } -static JSWeakRefRecord **get_first_weak_ref(JSValue key) +static JSWeakRefRecord **get_first_weak_ref(JSValueConst key) { switch (JS_VALUE_GET_TAG(key)) { case JS_TAG_OBJECT: @@ -47371,7 +49105,7 @@ static JSWeakRefRecord **get_first_weak_ref(JSValue key) } static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s, - JSValue key) + JSValueConst key) { uint32_t h; JSMapRecord *mr; @@ -47391,10 +49125,10 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s, wr->kind = JS_WEAK_REF_KIND_MAP; wr->u.map_record = mr; insert_weakref_record(key, wr); + mr->key = unsafe_unconst(key); } else { - js_dup(key); + mr->key = js_dup(key); } - mr->key = key; h = map_hash_key(ctx, key) & (s->hash_size - 1); list_add_tail(&mr->hash_link, &s->hash_table[h]); list_add_tail(&mr->link, &s->records); @@ -47458,18 +49192,18 @@ static void map_decref_record(JSRuntime *rt, JSMapRecord *mr) } } -static JSValue js_map_set(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_map_set(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); JSMapRecord *mr; - JSValue key, value; + JSValueConst key, value; int is_set; if (!s) return JS_EXCEPTION; is_set = (magic & MAGIC_SET); - key = map_normalize_key(ctx, argv[0]); + key = map_normalize_key_const(ctx, argv[0]); if (s->is_weak && !is_valid_weakref_target(key)) return JS_ThrowTypeError(ctx, "invalid value used as %s key", is_set ? "WeakSet" : "WeakMap"); if (is_set) @@ -47488,16 +49222,16 @@ static JSValue js_map_set(JSContext *ctx, JSValue this_val, return js_dup(this_val); } -static JSValue js_map_get(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_map_get(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); JSMapRecord *mr; - JSValue key; + JSValueConst key; if (!s) return JS_EXCEPTION; - key = map_normalize_key(ctx, argv[0]); + key = map_normalize_key_const(ctx, argv[0]); mr = map_find_record(ctx, s, key); if (!mr) return JS_UNDEFINED; @@ -47505,30 +49239,69 @@ static JSValue js_map_get(JSContext *ctx, JSValue this_val, return js_dup(mr->value); } -static JSValue js_map_has(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_map_getOrInsert(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bool computed = magic & 1; + JSClassID class_id = magic >> 1; + JSMapState *s = JS_GetOpaque2(ctx, this_val, class_id); + JSMapRecord *mr; + JSValueConst key; + JSValue value; + + if (!s) + return JS_EXCEPTION; + if (computed && check_function(ctx, argv[1])) + return JS_EXCEPTION; + key = map_normalize_key_const(ctx, argv[0]); + if (s->is_weak && !is_valid_weakref_target(key)) + return JS_ThrowTypeError(ctx, "invalid value used as WeakMap key"); + mr = map_find_record(ctx, s, key); + if (!mr) { + if (computed) { + value = JS_Call(ctx, argv[1], JS_UNDEFINED, 1, &key); + if (JS_IsException(value)) + return JS_EXCEPTION; + mr = map_find_record(ctx, s, key); + if (mr) + map_delete_record(ctx->rt, s, mr); + } else { + value = js_dup(argv[1]); + } + mr = map_add_record(ctx, s, key); + if (!mr) { + JS_FreeValue(ctx, value); + return JS_EXCEPTION; + } + mr->value = value; + } + return js_dup(mr->value); +} + +static JSValue js_map_has(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); JSMapRecord *mr; - JSValue key; + JSValueConst key; if (!s) return JS_EXCEPTION; - key = map_normalize_key(ctx, argv[0]); + key = map_normalize_key_const(ctx, argv[0]); mr = map_find_record(ctx, s, key); return js_bool(mr != NULL); } -static JSValue js_map_delete(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); JSMapRecord *mr; - JSValue key; + JSValueConst key; if (!s) return JS_EXCEPTION; - key = map_normalize_key(ctx, argv[0]); + key = map_normalize_key_const(ctx, argv[0]); mr = map_find_record(ctx, s, key); if (!mr) return JS_FALSE; @@ -47536,8 +49309,8 @@ static JSValue js_map_delete(JSContext *ctx, JSValue this_val, return JS_TRUE; } -static JSValue js_map_clear(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); struct list_head *el, *el1; @@ -47552,7 +49325,7 @@ static JSValue js_map_clear(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_map_get_size(JSContext *ctx, JSValue this_val, int magic) +static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic) { JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); if (!s) @@ -47560,11 +49333,11 @@ static JSValue js_map_get_size(JSContext *ctx, JSValue this_val, int magic) return js_uint32(s->record_count); } -static JSValue js_map_forEach(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - JSValue func, this_arg; + JSValueConst func, this_arg; JSValue ret, args[3]; struct list_head *el; JSMapRecord *mr; @@ -47591,8 +49364,8 @@ static JSValue js_map_forEach(JSContext *ctx, JSValue this_val, args[0] = args[1]; else args[0] = js_dup(mr->value); - args[2] = this_val; - ret = JS_Call(ctx, func, this_arg, 3, args); + args[2] = unsafe_unconst(this_val); + ret = JS_Call(ctx, func, this_arg, 3, vc(args)); JS_FreeValue(ctx, args[0]); if (!magic) JS_FreeValue(ctx, args[1]); @@ -47608,11 +49381,11 @@ static JSValue js_map_forEach(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_map_groupBy(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_map_groupBy(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue cb, res, iter, next, groups, k, v, prop; - JSValue args[2]; + JSValue res, iter, next, groups, k, v, prop; + JSValueConst cb, args[2]; int64_t idx; int done; @@ -47651,7 +49424,7 @@ static JSValue js_map_groupBy(JSContext *ctx, JSValue this_val, if (JS_IsException(k)) goto exception; - prop = js_map_get(ctx, groups, 1, &k, 0); + prop = js_map_get(ctx, groups, 1, vc(&k), 0); if (JS_IsException(prop)) goto exception; @@ -47667,7 +49440,7 @@ static JSValue js_map_groupBy(JSContext *ctx, JSValue this_val, JS_FreeValue(ctx, res); } - res = js_array_push(ctx, prop, 1, &v, /*unshift*/0); + res = js_array_push(ctx, prop, 1, vc(&v), /*unshift*/0); if (JS_IsException(res)) goto exception; // res is an int64 @@ -47694,7 +49467,7 @@ static JSValue js_map_groupBy(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static void js_map_finalizer(JSRuntime *rt, JSValue val) +static void js_map_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p; JSMapState *s; @@ -47722,7 +49495,8 @@ static void js_map_finalizer(JSRuntime *rt, JSValue val) } } -static void js_map_mark(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) +static void js_map_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); JSMapState *s; @@ -47731,10 +49505,10 @@ static void js_map_mark(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) s = p->u.map_state; if (s) { + assert(!s->is_weak); list_for_each(el, &s->records) { mr = list_entry(el, JSMapRecord, link); - if (!s->is_weak) - JS_MarkValue(rt, mr->key, mark_func); + JS_MarkValue(rt, mr->key, mark_func); JS_MarkValue(rt, mr->value, mark_func); } } @@ -47748,7 +49522,7 @@ typedef struct JSMapIteratorData { JSMapRecord *cur_record; } JSMapIteratorData; -static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val) +static void js_map_iterator_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p; JSMapIteratorData *it; @@ -47766,7 +49540,7 @@ static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val) } } -static void js_map_iterator_mark(JSRuntime *rt, JSValue val, +static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -47778,8 +49552,8 @@ static void js_map_iterator_mark(JSRuntime *rt, JSValue val, } } -static JSValue js_create_map_iterator(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSIteratorKindEnum kind; JSMapState *s; @@ -47808,8 +49582,8 @@ static JSValue js_create_map_iterator(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_map_iterator_next(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, +static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int *pdone, int magic) { JSMapIteratorData *it; @@ -47859,7 +49633,7 @@ static JSValue js_map_iterator_next(JSContext *ctx, JSValue this_val, if (it->kind == JS_ITERATOR_KIND_KEY) { return js_dup(mr->key); } else { - JSValue args[2]; + JSValueConst args[2]; args[0] = mr->key; if (magic) args[1] = mr->key; @@ -47897,7 +49671,7 @@ static JSValue js_map_read(BCReaderState *s, int magic) if (JS_IsException(argv[1])) goto fail; } - rv = js_map_set(ctx, obj, countof(argv), argv, magic); + rv = js_map_set(ctx, obj, countof(argv), vc(argv), magic); if (JS_IsException(rv)) goto fail; JS_FreeValue(ctx, rv); @@ -47956,15 +49730,19 @@ static int JS_WriteSet(BCWriterState *s, struct JSMapState *map_state) return js_map_write(s, map_state, MAGIC_SET); } -static int js_setlike_get_size(JSContext *ctx, JSValue setlike, int64_t *pout) +static int js_setlike_get_props(JSContext *ctx, JSValueConst setlike, + uint64_t *psize, JSValue *phas, JSValue *pkeys) { + JSValue has, keys, v; JSMapState *s; - JSValue v; + uint64_t size; double d; + keys = JS_UNDEFINED; + has = JS_UNDEFINED; s = JS_GetOpaque(setlike, JS_CLASS_SET); if (s) { - *pout = s->record_count; + size = s->record_count; } else { v = JS_GetProperty(ctx, setlike, JS_ATOM_size); if (JS_IsException(v)) @@ -47975,89 +49753,73 @@ static int js_setlike_get_size(JSContext *ctx, JSValue setlike, int64_t *pout) } if (JS_ToFloat64Free(ctx, &d, v) < 0) return -1; + if (d < 0) { + JS_ThrowRangeError(ctx, ".size is not a legal size"); + return -1; + } if (isnan(d)) { - JS_ThrowTypeError(ctx, ".size is not a number"); + JS_ThrowTypeError(ctx, ".size is not a legal size"); return -1; } - *pout = d; + if (isinf(d) || d > (double)MAX_SAFE_INTEGER) { + size = UINT64_MAX; + } else { + size = (uint64_t)d; // cast for expository reasons + } } - return 0; -} - -static int js_setlike_get_has(JSContext *ctx, JSValue setlike, JSValue *pout) -{ - JSValue v; - - v = JS_GetProperty(ctx, setlike, JS_ATOM_has); - if (JS_IsException(v)) + has = JS_GetProperty(ctx, setlike, JS_ATOM_has); + if (JS_IsException(has)) return -1; - if (JS_IsUndefined(v)) { - JS_ThrowTypeError(ctx, ".has is undefined"); - return -1; - } - if (!JS_IsFunction(ctx, v)) { + if (!JS_IsFunction(ctx, has)) { JS_ThrowTypeError(ctx, ".has is not a function"); - JS_FreeValue(ctx, v); - return -1; - } - *pout = v; - return 0; -} - -static int js_setlike_get_keys(JSContext *ctx, JSValue setlike, JSValue *pout) -{ - JSValue v; - - v = JS_GetProperty(ctx, setlike, JS_ATOM_keys); - if (JS_IsException(v)) - return -1; - if (JS_IsUndefined(v)) { - JS_ThrowTypeError(ctx, ".keys is undefined"); - return -1; + goto fail; } - if (!JS_IsFunction(ctx, v)) { + keys = JS_GetProperty(ctx, setlike, JS_ATOM_keys); + if (JS_IsException(keys)) + goto fail; + if (!JS_IsFunction(ctx, keys)) { JS_ThrowTypeError(ctx, ".keys is not a function"); - JS_FreeValue(ctx, v); - return -1; + goto fail; } - *pout = v; + *psize = size; + *phas = has; + *pkeys = keys; return 0; +fail: + JS_FreeValue(ctx, has); + JS_FreeValue(ctx, keys); + return -1; } -static JSValue js_set_isDisjointFrom(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_set_isDisjointFrom(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue item, iter, keys, has, next, rv, rval; + JSValue has, item, iter, keys, next, rv, rval; + JSValueConst setlike; int done; bool found; JSMapState *s; - int64_t size; + uint64_t size; int ok; - has = JS_UNDEFINED; + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); + if (!s) + return JS_EXCEPTION; + setlike = argv[0]; + if (js_setlike_get_props(ctx, setlike, &size, &has, &keys) < 0) + return JS_EXCEPTION; iter = JS_UNDEFINED; - keys = JS_UNDEFINED; next = JS_UNDEFINED; rval = JS_EXCEPTION; - s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); - if (!s) - goto exception; - // order matters! - if (js_setlike_get_size(ctx, argv[0], &size) < 0) - goto exception; - if (js_setlike_get_has(ctx, argv[0], &has) < 0) - goto exception; - if (js_setlike_get_keys(ctx, argv[0], &keys) < 0) - goto exception; if (s->record_count > size) { - iter = JS_Call(ctx, keys, argv[0], 0, NULL); + iter = JS_Call(ctx, keys, setlike, 0, NULL); if (JS_IsException(iter)) goto exception; next = JS_GetProperty(ctx, iter, JS_ATOM_next); if (JS_IsException(next)) goto exception; found = false; - do { + for (;;) { item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); if (JS_IsException(item)) goto exception; @@ -48066,7 +49828,12 @@ static JSValue js_set_isDisjointFrom(JSContext *ctx, JSValue this_val, item = map_normalize_key(ctx, item); found = (NULL != map_find_record(ctx, s, item)); JS_FreeValue(ctx, item); - } while (!found); + if (!found) + continue; + if (JS_IteratorClose(ctx, iter, /*is_exception_pending*/false) < 0) + goto exception; + break; + } } else { iter = js_create_map_iterator(ctx, this_val, 0, NULL, MAGIC_SET); if (JS_IsException(iter)) @@ -48078,7 +49845,7 @@ static JSValue js_set_isDisjointFrom(JSContext *ctx, JSValue this_val, goto exception; if (done) // item is JS_UNDEFINED break; - rv = JS_Call(ctx, has, argv[0], 1, &item); + rv = JS_Call(ctx, has, setlike, 1, vc(&item)); JS_FreeValue(ctx, item); ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION if (ok < 0) @@ -48095,30 +49862,25 @@ static JSValue js_set_isDisjointFrom(JSContext *ctx, JSValue this_val, return rval; } -static JSValue js_set_isSubsetOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_set_isSubsetOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue item, iter, keys, has, next, rv, rval; + JSValue has, item, iter, keys, next, rv, rval; + JSValueConst setlike; bool found; JSMapState *s; - int64_t size; + uint64_t size; int done, ok; - has = JS_UNDEFINED; + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); + if (!s) + return JS_EXCEPTION; + setlike = argv[0]; + if (js_setlike_get_props(ctx, setlike, &size, &has, &keys) < 0) + return JS_EXCEPTION; iter = JS_UNDEFINED; - keys = JS_UNDEFINED; next = JS_UNDEFINED; rval = JS_EXCEPTION; - s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); - if (!s) - goto exception; - // order matters! - if (js_setlike_get_size(ctx, argv[0], &size) < 0) - goto exception; - if (js_setlike_get_has(ctx, argv[0], &has) < 0) - goto exception; - if (js_setlike_get_keys(ctx, argv[0], &keys) < 0) - goto exception; found = false; if (s->record_count > size) goto fini; @@ -48132,7 +49894,7 @@ static JSValue js_set_isSubsetOf(JSContext *ctx, JSValue this_val, goto exception; if (done) // item is JS_UNDEFINED break; - rv = JS_Call(ctx, has, argv[0], 1, &item); + rv = JS_Call(ctx, has, setlike, 1, vc(&item)); JS_FreeValue(ctx, item); ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION if (ok < 0) @@ -48149,41 +49911,36 @@ static JSValue js_set_isSubsetOf(JSContext *ctx, JSValue this_val, return rval; } -static JSValue js_set_isSupersetOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_set_isSupersetOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue item, iter, keys, has, next, rval; + JSValue has, item, iter, keys, next, rval; + JSValueConst setlike; int done; bool found; JSMapState *s; - int64_t size; + uint64_t size; - has = JS_UNDEFINED; + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); + if (!s) + return JS_EXCEPTION; + setlike = argv[0]; + if (js_setlike_get_props(ctx, setlike, &size, &has, &keys) < 0) + return JS_EXCEPTION; iter = JS_UNDEFINED; - keys = JS_UNDEFINED; next = JS_UNDEFINED; rval = JS_EXCEPTION; - s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); - if (!s) - goto exception; - // order matters! - if (js_setlike_get_size(ctx, argv[0], &size) < 0) - goto exception; - if (js_setlike_get_has(ctx, argv[0], &has) < 0) - goto exception; - if (js_setlike_get_keys(ctx, argv[0], &keys) < 0) - goto exception; found = false; if (s->record_count < size) goto fini; - iter = JS_Call(ctx, keys, argv[0], 0, NULL); + iter = JS_Call(ctx, keys, setlike, 0, NULL); if (JS_IsException(iter)) goto exception; next = JS_GetProperty(ctx, iter, JS_ATOM_next); if (JS_IsException(next)) goto exception; found = true; - do { + for (;;) { item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); if (JS_IsException(item)) goto exception; @@ -48192,7 +49949,12 @@ static JSValue js_set_isSupersetOf(JSContext *ctx, JSValue this_val, item = map_normalize_key(ctx, item); found = (NULL != map_find_record(ctx, s, item)); JS_FreeValue(ctx, item); - } while (found); + if (found) + continue; + if (JS_IteratorClose(ctx, iter, /*is_exception_pending*/false) < 0) + goto exception; + break; + } fini: rval = found ? JS_TRUE : JS_FALSE; exception: @@ -48203,32 +49965,27 @@ static JSValue js_set_isSupersetOf(JSContext *ctx, JSValue this_val, return rval; } -static JSValue js_set_intersection(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_set_intersection(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue newset, item, iter, keys, has, next, rv; + JSValue has, item, iter, keys, newset, next, rv; + JSValueConst setlike; JSMapState *s, *t; JSMapRecord *mr; - int64_t size; + uint64_t size; int done, ok; - has = JS_UNDEFINED; + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); + if (!s) + return JS_EXCEPTION; + setlike = argv[0]; + if (js_setlike_get_props(ctx, setlike, &size, &has, &keys) < 0) + return JS_EXCEPTION; iter = JS_UNDEFINED; - keys = JS_UNDEFINED; next = JS_UNDEFINED; newset = JS_UNDEFINED; - s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); - if (!s) - goto exception; - // order matters! - if (js_setlike_get_size(ctx, argv[0], &size) < 0) - goto exception; - if (js_setlike_get_has(ctx, argv[0], &has) < 0) - goto exception; - if (js_setlike_get_keys(ctx, argv[0], &keys) < 0) - goto exception; if (s->record_count > size) { - iter = JS_Call(ctx, keys, argv[0], 0, NULL); + iter = JS_Call(ctx, keys, setlike, 0, NULL); if (JS_IsException(iter)) goto exception; next = JS_GetProperty(ctx, iter, JS_ATOM_next); @@ -48270,7 +50027,7 @@ static JSValue js_set_intersection(JSContext *ctx, JSValue this_val, goto exception; if (done) // item is JS_UNDEFINED break; - rv = JS_Call(ctx, has, argv[0], 1, &item); + rv = JS_Call(ctx, has, setlike, 1, vc(&item)); ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION if (ok > 0) { item = map_normalize_key(ctx, item); @@ -48301,33 +50058,28 @@ static JSValue js_set_intersection(JSContext *ctx, JSValue this_val, return newset; } -static JSValue js_set_difference(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_set_difference(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue newset, item, iter, keys, has, next, rv; + JSValue has, item, iter, keys, newset, next, rv; + JSValueConst setlike; JSMapState *s, *t; JSMapRecord *mr; - int64_t size; + uint64_t size; int done; int ok; - has = JS_UNDEFINED; + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); + if (!s) + return JS_EXCEPTION; + setlike = argv[0]; + if (js_setlike_get_props(ctx, setlike, &size, &has, &keys) < 0) + return JS_EXCEPTION; iter = JS_UNDEFINED; - keys = JS_UNDEFINED; next = JS_UNDEFINED; newset = JS_UNDEFINED; - s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); - if (!s) - goto exception; - // order matters! - if (js_setlike_get_size(ctx, argv[0], &size) < 0) - goto exception; - if (js_setlike_get_has(ctx, argv[0], &has) < 0) - goto exception; - if (js_setlike_get_keys(ctx, argv[0], &keys) < 0) - goto exception; if (s->record_count > size) { - iter = JS_Call(ctx, keys, argv[0], 0, NULL); + iter = JS_Call(ctx, keys, setlike, 0, NULL); if (JS_IsException(iter)) goto exception; next = JS_GetProperty(ctx, iter, JS_ATOM_next); @@ -48363,7 +50115,7 @@ static JSValue js_set_difference(JSContext *ctx, JSValue this_val, goto exception; if (done) // item is JS_UNDEFINED break; - rv = JS_Call(ctx, has, argv[0], 1, &item); + rv = JS_Call(ctx, has, setlike, 1, vc(&item)); ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION if (ok == 0) { item = map_normalize_key(ctx, item); @@ -48394,32 +50146,31 @@ static JSValue js_set_difference(JSContext *ctx, JSValue this_val, return newset; } -static JSValue js_set_symmetricDifference(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_set_symmetricDifference(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue newset, item, iter, next, rv; + JSValue has, item, iter, keys, newset, next; + JSValueConst setlike; struct list_head *el; JSMapState *s, *t; JSMapRecord *mr; - int64_t size; + uint64_t size; int done; bool present; s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); if (!s) return JS_EXCEPTION; - // order matters! they're JS-observable side effects - if (js_setlike_get_size(ctx, argv[0], &size) < 0) + setlike = argv[0]; + if (js_setlike_get_props(ctx, setlike, &size, &has, &keys) < 0) return JS_EXCEPTION; - if (js_setlike_get_has(ctx, argv[0], &rv) < 0) - return JS_EXCEPTION; - JS_FreeValue(ctx, rv); + JS_FreeValue(ctx, has); + iter = JS_UNDEFINED; + next = JS_UNDEFINED; newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET); if (JS_IsException(newset)) - return JS_EXCEPTION; + goto exception; t = JS_GetOpaque(newset, JS_CLASS_SET); - iter = JS_UNDEFINED; - next = JS_UNDEFINED; // can't clone this_val using js_map_constructor(), // test262 mandates we don't call the .add method list_for_each(el, &s->records) { @@ -48431,10 +50182,7 @@ static JSValue js_set_symmetricDifference(JSContext *ctx, JSValue this_val, goto exception; mr->value = JS_UNDEFINED; } - iter = JS_GetProperty(ctx, argv[0], JS_ATOM_keys); - if (JS_IsException(iter)) - goto exception; - iter = JS_CallFree(ctx, iter, argv[0], 0, NULL); + iter = JS_Call(ctx, keys, setlike, 0, NULL); if (JS_IsException(iter)) goto exception; next = JS_GetProperty(ctx, iter, JS_ATOM_next); @@ -48477,36 +50225,37 @@ static JSValue js_set_symmetricDifference(JSContext *ctx, JSValue this_val, JS_FreeValue(ctx, newset); newset = JS_EXCEPTION; fini: + JS_FreeValue(ctx, keys); JS_FreeValue(ctx, next); JS_FreeValue(ctx, iter); return newset; } -static JSValue js_set_union(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_set_union(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue newset, item, iter, next, rv; + JSValue has, item, iter, keys, newset, next, rv; + JSValueConst setlike; struct list_head *el; JSMapState *s, *t; JSMapRecord *mr; - int64_t size; + uint64_t size; int done; + iter = JS_UNDEFINED; s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); if (!s) return JS_EXCEPTION; - // order matters! they're JS-observable side effects - if (js_setlike_get_size(ctx, argv[0], &size) < 0) - return JS_EXCEPTION; - if (js_setlike_get_has(ctx, argv[0], &rv) < 0) + setlike = argv[0]; + if (js_setlike_get_props(ctx, setlike, &size, &has, &keys) < 0) return JS_EXCEPTION; - JS_FreeValue(ctx, rv); + JS_FreeValue(ctx, has); + iter = JS_UNDEFINED; + next = JS_UNDEFINED; newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET); if (JS_IsException(newset)) - return JS_EXCEPTION; + goto exception; t = JS_GetOpaque(newset, JS_CLASS_SET); - iter = JS_UNDEFINED; - next = JS_UNDEFINED; list_for_each(el, &s->records) { mr = list_entry(el, JSMapRecord, link); if (mr->empty) @@ -48516,10 +50265,7 @@ static JSValue js_set_union(JSContext *ctx, JSValue this_val, goto exception; mr->value = JS_UNDEFINED; } - iter = JS_GetProperty(ctx, argv[0], JS_ATOM_keys); - if (JS_IsException(iter)) - goto exception; - iter = JS_CallFree(ctx, iter, argv[0], 0, NULL); + iter = JS_Call(ctx, keys, setlike, 0, NULL); if (JS_IsException(iter)) goto exception; next = JS_GetProperty(ctx, iter, JS_ATOM_next); @@ -48531,7 +50277,7 @@ static JSValue js_set_union(JSContext *ctx, JSValue this_val, goto exception; if (done) // item is JS_UNDEFINED break; - rv = js_map_set(ctx, newset, 1, &item, MAGIC_SET); + rv = js_map_set(ctx, newset, 1, vc(&item), MAGIC_SET); JS_FreeValue(ctx, item); if (JS_IsException(rv)) goto exception; @@ -48542,6 +50288,7 @@ static JSValue js_set_union(JSContext *ctx, JSValue this_val, JS_FreeValue(ctx, newset); newset = JS_EXCEPTION; fini: + JS_FreeValue(ctx, keys); JS_FreeValue(ctx, next); JS_FreeValue(ctx, iter); return newset; @@ -48559,6 +50306,10 @@ static const JSCFunctionListEntry js_set_funcs[] = { static const JSCFunctionListEntry js_map_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ), JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ), + JS_CFUNC_MAGIC_DEF("getOrInsert", 2, js_map_getOrInsert, + JS_CLASS_MAP<<1 | /*computed*/false ), + JS_CFUNC_MAGIC_DEF("getOrInsertComputed", 2, js_map_getOrInsert, + JS_CLASS_MAP<<1 | /*computed*/true ), JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ), JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ), JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ), @@ -48605,6 +50356,10 @@ static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = { static const JSCFunctionListEntry js_weak_map_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ), JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("getOrInsert", 2, js_map_getOrInsert, + JS_CLASS_WEAKMAP<<1 | /*computed*/false ), + JS_CFUNC_MAGIC_DEF("getOrInsertComputed", 2, js_map_getOrInsert, + JS_CLASS_WEAKMAP<<1 | /*computed*/true ), JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ), JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ), JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ), @@ -48706,7 +50461,7 @@ typedef struct JSPromiseReactionData { JSValue handler; } JSPromiseReactionData; - JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise) +JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValueConst promise) { JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); if (!s) @@ -48714,15 +50469,15 @@ typedef struct JSPromiseReactionData { return s->promise_state; } -JSValue JS_PromiseResult(JSContext *ctx, JSValue promise) +JSValue JS_PromiseResult(JSContext *ctx, JSValueConst promise) { JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); if (!s) return JS_UNDEFINED; - return JS_DupValue(ctx, s->promise_result); + return js_dup(s->promise_result); } -bool JS_IsPromise(JSValue val) +bool JS_IsPromise(JSValueConst val) { if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) return false; @@ -48730,7 +50485,7 @@ bool JS_IsPromise(JSValue val) } static int js_create_resolving_functions(JSContext *ctx, JSValue *args, - JSValue promise); + JSValueConst promise); static void promise_reaction_data_free(JSRuntime *rt, JSPromiseReactionData *rd) @@ -48752,10 +50507,11 @@ static void promise_reaction_data_free(JSRuntime *rt, #endif static JSValue promise_reaction_job(JSContext *ctx, int argc, - JSValue *argv) + JSValueConst *argv) { - JSValue handler, arg, func; + JSValueConst handler, func; JSValue res, res2; + JSValueConst arg; bool is_reject; assert(argc == 5); @@ -48776,7 +50532,7 @@ static JSValue promise_reaction_job(JSContext *ctx, int argc, } is_reject = JS_IsException(res); if (is_reject) { - if (unlikely(JS_IsUncatchableError(ctx, ctx->rt->current_exception))) + if (unlikely(JS_IsUncatchableError(ctx->rt->current_exception))) return JS_EXCEPTION; res = JS_GetException(ctx); } @@ -48785,8 +50541,7 @@ static JSValue promise_reaction_job(JSContext *ctx, int argc, creating a dummy promise in the 'await' implementation of async functions */ if (!JS_IsUndefined(func)) { - res2 = JS_Call(ctx, func, JS_UNDEFINED, - 1, &res); + res2 = JS_Call(ctx, func, JS_UNDEFINED, 1, vc(&res)); } else { res2 = JS_UNDEFINED; } @@ -48795,6 +50550,12 @@ static JSValue promise_reaction_job(JSContext *ctx, int argc, return res2; } +void JS_SetPromiseHook(JSRuntime *rt, JSPromiseHook promise_hook, void *opaque) +{ + rt->promise_hook = promise_hook; + rt->promise_hook_opaque = opaque; +} + void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTracker *cb, void *opaque) @@ -48803,13 +50564,13 @@ void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, rt->host_promise_rejection_tracker_opaque = opaque; } -static void fulfill_or_reject_promise(JSContext *ctx, JSValue promise, - JSValue value, bool is_reject) +static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise, + JSValueConst value, bool is_reject) { JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); struct list_head *el, *el1; JSPromiseReactionData *rd; - JSValue args[5]; + JSValueConst args[5]; if (!s || s->promise_state != JS_PROMISE_PENDING) return; /* should never happen */ @@ -48818,12 +50579,19 @@ static void fulfill_or_reject_promise(JSContext *ctx, JSValue promise, promise_trace(ctx, "fulfill_or_reject_promise: is_reject=%d\n", is_reject); + if (s->promise_state == JS_PROMISE_FULFILLED) { + JSRuntime *rt = ctx->rt; + if (rt->promise_hook) { + rt->promise_hook(ctx, JS_PROMISE_HOOK_RESOLVE, promise, + JS_UNDEFINED, rt->promise_hook_opaque); + } + } + if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { JSRuntime *rt = ctx->rt; - if (rt->host_promise_rejection_tracker) { + if (rt->host_promise_rejection_tracker) rt->host_promise_rejection_tracker(ctx, promise, value, false, rt->host_promise_rejection_tracker_opaque); - } } list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) { @@ -48845,17 +50613,12 @@ static void fulfill_or_reject_promise(JSContext *ctx, JSValue promise, } } -static void reject_promise(JSContext *ctx, JSValue promise, - JSValue value) -{ - fulfill_or_reject_promise(ctx, promise, value, true); -} - static JSValue js_promise_resolve_thenable_job(JSContext *ctx, - int argc, JSValue *argv) + int argc, JSValueConst *argv) { - JSValue promise, thenable, then; + JSValueConst promise, thenable, then; JSValue args[2], res; + JSRuntime *rt; promise_trace(ctx, "js_promise_resolve_thenable_job\n"); @@ -48865,10 +50628,19 @@ static JSValue js_promise_resolve_thenable_job(JSContext *ctx, then = argv[2]; if (js_create_resolving_functions(ctx, args, promise) < 0) return JS_EXCEPTION; - res = JS_Call(ctx, then, thenable, 2, args); + rt = ctx->rt; + if (rt->promise_hook) { + rt->promise_hook(ctx, JS_PROMISE_HOOK_BEFORE, promise, JS_UNDEFINED, + rt->promise_hook_opaque); + } + res = JS_Call(ctx, then, thenable, 2, vc(args)); + if (rt->promise_hook) { + rt->promise_hook(ctx, JS_PROMISE_HOOK_AFTER, promise, JS_UNDEFINED, + rt->promise_hook_opaque); + } if (JS_IsException(res)) { JSValue error = JS_GetException(ctx); - res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error); + res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, vc(&error)); JS_FreeValue(ctx, error); } JS_FreeValue(ctx, args[0]); @@ -48886,7 +50658,7 @@ static void js_promise_resolve_function_free_resolved(JSRuntime *rt, static int js_create_resolving_functions(JSContext *ctx, JSValue *resolving_funcs, - JSValue promise) + JSValueConst promise) { JSValue obj; @@ -48926,7 +50698,8 @@ static int js_create_resolving_functions(JSContext *ctx, return ret; } -static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val) +static void js_promise_resolve_function_finalizer(JSRuntime *rt, + JSValueConst val) { JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; if (s) { @@ -48936,7 +50709,7 @@ static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val) } } -static void js_promise_resolve_function_mark(JSRuntime *rt, JSValue val, +static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; @@ -48946,14 +50719,15 @@ static void js_promise_resolve_function_mark(JSRuntime *rt, JSValue val, } static JSValue js_promise_resolve_function_call(JSContext *ctx, - JSValue func_obj, - JSValue this_val, - int argc, JSValue *argv, + JSValueConst func_obj, + JSValueConst this_val, + int argc, JSValueConst *argv, int flags) { JSObject *p = JS_VALUE_GET_OBJ(func_obj); JSPromiseFunctionData *s; - JSValue resolution, args[3]; + JSValueConst args[3]; + JSValueConst resolution; JSValue then; bool is_reject; @@ -48984,7 +50758,7 @@ static JSValue js_promise_resolve_function_call(JSContext *ctx, JSValue error; fail_reject: error = JS_GetException(ctx); - reject_promise(ctx, s->promise, error); + fulfill_or_reject_promise(ctx, s->promise, error, true); JS_FreeValue(ctx, error); } else if (!JS_IsFunction(ctx, then)) { JS_FreeValue(ctx, then); @@ -49000,7 +50774,7 @@ static JSValue js_promise_resolve_function_call(JSContext *ctx, return JS_UNDEFINED; } -static void js_promise_finalizer(JSRuntime *rt, JSValue val) +static void js_promise_finalizer(JSRuntime *rt, JSValueConst val) { JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); struct list_head *el, *el1; @@ -49019,7 +50793,7 @@ static void js_promise_finalizer(JSRuntime *rt, JSValue val) js_free_rt(rt, s); } -static void js_promise_mark(JSRuntime *rt, JSValue val, +static void js_promise_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); @@ -49040,12 +50814,13 @@ static void js_promise_mark(JSRuntime *rt, JSValue val, JS_MarkValue(rt, s->promise_result, mark_func); } -static JSValue js_promise_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv) +static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { - JSValue executor; + JSValueConst executor; JSValue obj; JSPromiseData *s; + JSRuntime *rt; JSValue args[2], ret; int i; @@ -49066,11 +50841,19 @@ static JSValue js_promise_constructor(JSContext *ctx, JSValue new_target, JS_SetOpaqueInternal(obj, s); if (js_create_resolving_functions(ctx, args, obj)) goto fail; - ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, args); + rt = ctx->rt; + if (rt->promise_hook) { + JSValueConst parent_promise = JS_UNDEFINED; + if (rt->parent_promise) + parent_promise = rt->parent_promise->value; + rt->promise_hook(ctx, JS_PROMISE_HOOK_INIT, obj, parent_promise, + rt->promise_hook_opaque); + } + ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, vc(args)); if (JS_IsException(ret)) { JSValue ret2, error; error = JS_GetException(ctx); - ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error); + ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, vc(&error)); JS_FreeValue(ctx, error); if (JS_IsException(ret2)) goto fail1; @@ -49089,9 +50872,9 @@ static JSValue js_promise_constructor(JSContext *ctx, JSValue new_target, } static JSValue js_promise_executor(JSContext *ctx, - JSValue this_val, - int argc, JSValue *argv, - int magic, JSValue *func_data) + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValueConst *func_data) { int i; @@ -49105,7 +50888,7 @@ static JSValue js_promise_executor(JSContext *ctx, static JSValue js_promise_executor_new(JSContext *ctx) { - JSValue func_data[2]; + JSValueConst func_data[2]; func_data[0] = JS_UNDEFINED; func_data[1] = JS_UNDEFINED; @@ -49115,7 +50898,7 @@ static JSValue js_promise_executor_new(JSContext *ctx) static JSValue js_new_promise_capability(JSContext *ctx, JSValue *resolving_funcs, - JSValue ctor) + JSValueConst ctor) { JSValue executor, result_promise; JSCFunctionDataRecord *s; @@ -49123,14 +50906,11 @@ static JSValue js_new_promise_capability(JSContext *ctx, executor = js_promise_executor_new(ctx); if (JS_IsException(executor)) - return executor; - + return JS_EXCEPTION; if (JS_IsUndefined(ctor)) { - result_promise = js_promise_constructor(ctx, ctor, 1, - &executor); + result_promise = js_promise_constructor(ctx, ctor, 1, vc(&executor)); } else { - result_promise = JS_CallConstructor(ctx, ctor, 1, - &executor); + result_promise = JS_CallConstructor(ctx, ctor, 1, vc(&executor)); } if (JS_IsException(result_promise)) goto fail; @@ -49154,8 +50934,8 @@ JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs) return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED); } -static JSValue js_promise_resolve(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSValue result_promise, resolving_funcs[2], ret; bool is_reject = magic; @@ -49187,8 +50967,8 @@ static JSValue js_promise_resolve(JSContext *ctx, JSValue this_val, return result_promise; } -static JSValue js_promise_withResolvers(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_promise_withResolvers(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue result_promise, resolving_funcs[2], obj; if (!JS_IsObject(this_val)) @@ -49197,20 +50977,33 @@ static JSValue js_promise_withResolvers(JSContext *ctx, JSValue this_val, if (JS_IsException(result_promise)) return JS_EXCEPTION; obj = JS_NewObject(ctx); - if (JS_IsException(obj)) { - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - JS_FreeValue(ctx, result_promise); - return JS_EXCEPTION; + if (JS_IsException(obj)) + goto exception; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, + JS_PROP_C_W_E) < 0) { + goto exception; + } + result_promise = JS_UNDEFINED; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], + JS_PROP_C_W_E) < 0) { + goto exception; + } + resolving_funcs[0] = JS_UNDEFINED; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], + JS_PROP_C_W_E) < 0) { + goto exception; } - JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E); return obj; +exception: + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, result_promise); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; } -static JSValue js_promise_try(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_promise_try(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue result_promise, resolving_funcs[2], ret, ret2; bool is_reject = 0; @@ -49225,7 +51018,7 @@ static JSValue js_promise_try(JSContext *ctx, JSValue this_val, is_reject = 1; ret = JS_GetException(ctx); } - ret2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, &ret); + ret2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, vc(&ret)); JS_FreeValue(ctx, resolving_funcs[0]); JS_FreeValue(ctx, resolving_funcs[1]); JS_FreeValue(ctx, ret); @@ -49238,7 +51031,7 @@ static JSValue js_promise_try(JSContext *ctx, JSValue this_val, } static __exception int remainingElementsCount_add(JSContext *ctx, - JSValue resolve_element_env, + JSValueConst resolve_element_env, int addend) { JSValue val; @@ -49261,17 +51054,17 @@ static __exception int remainingElementsCount_add(JSContext *ctx, #define PROMISE_MAGIC_any 2 static JSValue js_promise_all_resolve_element(JSContext *ctx, - JSValue this_val, - int argc, JSValue *argv, + JSValueConst this_val, + int argc, JSValueConst *argv, int magic, - JSValue *func_data) + JSValueConst *func_data) { int resolve_type = magic & 3; int is_reject = magic & 4; bool alreadyCalled = JS_ToBool(ctx, func_data[0]); - JSValue values = func_data[2]; - JSValue resolve = func_data[3]; - JSValue resolve_element_env = func_data[4]; + JSValueConst values = func_data[2]; + JSValueConst resolve = func_data[3]; + JSValueConst resolve_element_env = func_data[4]; JSValue ret, obj; int is_zero, index; @@ -49318,7 +51111,7 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx, error = js_aggregate_error_constructor(ctx, values); if (JS_IsException(error)) return JS_EXCEPTION; - ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &error); + ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, vc(&error)); JS_FreeValue(ctx, error); } else { ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &values); @@ -49331,14 +51124,14 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx, } /* magic = 0: Promise.all 1: Promise.allSettled */ -static JSValue js_promise_all(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSValue result_promise, resolving_funcs[2], item, next_promise, ret; JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED; JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element; JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED; - JSValue then_args[2], resolve_element_data[5]; + JSValueConst then_args[2], resolve_element_data[5]; int done, index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any); if (!JS_IsObject(this_val)) @@ -49355,8 +51148,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValue this_val, JSValue error; fail_reject: error = JS_GetException(ctx); - ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, - &error); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, vc(&error)); JS_FreeValue(ctx, error); if (JS_IsException(ret)) goto fail; @@ -49387,7 +51179,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValue this_val, if (done) break; next_promise = JS_Call(ctx, promise_resolve, - this_val, 1, &item); + this_val, 1, vc(&item)); JS_FreeValue(ctx, item); if (JS_IsException(next_promise)) { fail_reject1: @@ -49455,7 +51247,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValue this_val, values = error; } ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED, - 1, &values); + 1, vc(&values)); if (check_exception_free(ctx, ret)) goto fail_reject; } @@ -49475,8 +51267,8 @@ static JSValue js_promise_all(JSContext *ctx, JSValue this_val, goto done; } -static JSValue js_promise_race(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue result_promise, resolving_funcs[2], item, next_promise, ret; JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED; @@ -49497,8 +51289,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValue this_val, JSValue error; fail_reject: error = JS_GetException(ctx); - ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, - &error); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, vc(&error)); JS_FreeValue(ctx, error); if (JS_IsException(ret)) goto fail; @@ -49517,7 +51308,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValue this_val, if (done) break; next_promise = JS_Call(ctx, promise_resolve, - this_val, 1, &item); + this_val, 1, vc(&item)); JS_FreeValue(ctx, item); if (JS_IsException(next_promise)) { fail_reject1: @@ -49525,7 +51316,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValue this_val, goto fail_reject; } ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, - resolving_funcs); + vc(resolving_funcs)); if (check_exception_free(ctx, ret)) goto fail_reject1; } @@ -49545,9 +51336,9 @@ static JSValue js_promise_race(JSContext *ctx, JSValue this_val, } static __exception int perform_promise_then(JSContext *ctx, - JSValue promise, - JSValue *resolve_reject, - JSValue *cap_resolving_funcs) + JSValueConst promise, + JSValueConst *resolve_reject, + JSValueConst *cap_resolving_funcs) { JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); JSPromiseReactionData *rd_array[2], *rd; @@ -49556,7 +51347,7 @@ static __exception int perform_promise_then(JSContext *ctx, rd_array[0] = NULL; rd_array[1] = NULL; for(i = 0; i < 2; i++) { - JSValue handler; + JSValueConst handler; rd = js_mallocz(ctx, sizeof(*rd)); if (!rd) { if (i == 1) @@ -49576,13 +51367,12 @@ static __exception int perform_promise_then(JSContext *ctx, for(i = 0; i < 2; i++) list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]); } else { - JSValue args[5]; + JSValueConst args[5]; if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { JSRuntime *rt = ctx->rt; - if (rt->host_promise_rejection_tracker) { + if (rt->host_promise_rejection_tracker) rt->host_promise_rejection_tracker(ctx, promise, s->promise_result, true, rt->host_promise_rejection_tracker_opaque); - } } i = s->promise_state - JS_PROMISE_FULFILLED; rd = rd_array[i]; @@ -49599,11 +51389,14 @@ static __exception int perform_promise_then(JSContext *ctx, return 0; } -static JSValue js_promise_then(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue ctor, result_promise, resolving_funcs[2]; + bool have_promise_hook; + JSValueLink link; JSPromiseData *s; + JSRuntime *rt; int i, ret; s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE); @@ -49613,12 +51406,20 @@ static JSValue js_promise_then(JSContext *ctx, JSValue this_val, ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); if (JS_IsException(ctor)) return ctor; + rt = ctx->rt; + // always restore, even if js_new_promise_capability callee removes hook + have_promise_hook = (rt->promise_hook != NULL); + if (have_promise_hook) { + link = (JSValueLink){rt->parent_promise, this_val}; + rt->parent_promise = &link; + } result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); + if (have_promise_hook) + rt->parent_promise = link.next; JS_FreeValue(ctx, ctor); if (JS_IsException(result_promise)) return result_promise; - ret = perform_promise_then(ctx, this_val, argv, - resolving_funcs); + ret = perform_promise_then(ctx, this_val, argv, vc(resolving_funcs)); for(i = 0; i < 2; i++) JS_FreeValue(ctx, resolving_funcs[i]); if (ret) { @@ -49628,41 +51429,41 @@ static JSValue js_promise_then(JSContext *ctx, JSValue this_val, return result_promise; } -static JSValue js_promise_catch(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue args[2]; + JSValueConst args[2]; args[0] = JS_UNDEFINED; args[1] = argv[0]; return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args); } -static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, - int magic, JSValue *func_data) +static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValueConst *func_data) { return js_dup(func_data[0]); } -static JSValue js_promise_finally_thrower(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, - int magic, JSValue *func_data) +static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValueConst *func_data) { return JS_Throw(ctx, js_dup(func_data[0])); } -static JSValue js_promise_then_finally_func(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, - int magic, JSValue *func_data) +static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValueConst *func_data) { - JSValue ctor = func_data[0]; - JSValue onFinally = func_data[1]; + JSValueConst ctor = func_data[0]; + JSValueConst onFinally = func_data[1]; JSValue res, promise, ret, then_func; res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL); if (JS_IsException(res)) return res; - promise = js_promise_resolve(ctx, ctor, 1, &res, 0); + promise = js_promise_resolve(ctx, ctor, 1, vc(&res), 0); JS_FreeValue(ctx, res); if (JS_IsException(promise)) return promise; @@ -49677,18 +51478,18 @@ static JSValue js_promise_then_finally_func(JSContext *ctx, JSValue this_val, JS_FreeValue(ctx, promise); return then_func; } - ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, &then_func); + ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, vc(&then_func)); JS_FreeValue(ctx, then_func); return ret; } -static JSValue js_promise_finally(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue onFinally = argv[0]; + JSValueConst onFinally = argv[0]; JSValue ctor, ret; JSValue then_funcs[2]; - JSValue func_data[2]; + JSValueConst func_data[2]; int i; ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); @@ -49711,7 +51512,7 @@ static JSValue js_promise_finally(JSContext *ctx, JSValue this_val, } } JS_FreeValue(ctx, ctor); - ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, then_funcs); + ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, vc(then_funcs)); JS_FreeValue(ctx, then_funcs[0]); JS_FreeValue(ctx, then_funcs[1]); return ret; @@ -49742,9 +51543,9 @@ static const JSCFunctionListEntry js_async_function_proto_funcs[] = { }; static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx, - JSValue this_val, - int argc, JSValue *argv, - int magic, JSValue *func_data) + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValueConst *func_data) { return js_create_iterator_result(ctx, js_dup(argv[0]), JS_ToBool(ctx, func_data[0])); @@ -49753,7 +51554,7 @@ static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx, static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx, bool done) { - JSValue func_data[1]; + JSValueConst func_data[1]; func_data[0] = js_bool(done); return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap, @@ -49773,7 +51574,8 @@ typedef struct JSAsyncFromSyncIteratorData { JSValue next_method; } JSAsyncFromSyncIteratorData; -static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val) +static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, + JSValueConst val) { JSAsyncFromSyncIteratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); @@ -49784,7 +51586,7 @@ static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val) } } -static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValue val, +static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSAsyncFromSyncIteratorData *s = @@ -49821,8 +51623,8 @@ static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, return async_iter; } -static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, +static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { JSValue promise, resolving_funcs[2], value, err, method; @@ -49851,9 +51653,12 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValue this_val if (magic == GEN_MAGIC_RETURN) { err = js_create_iterator_result(ctx, js_dup(argv[0]), true); is_reject = 0; + } else if (JS_IteratorClose(ctx, s->sync_iter, false)) { + err = JS_GetException(ctx); + is_reject = 1; } else { - err = JS_MakeError(ctx, JS_TYPE_ERROR, "throw is not a method", - true); + err = JS_MakeError2(ctx, JS_TYPE_ERROR, /*add_backtrace*/true, + "throw is not a method"); is_reject = 1; } goto done_resolve; @@ -49879,7 +51684,7 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValue this_val is_reject = 1; done_resolve: res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, - 1, &err); + 1, vc(&err)); JS_FreeValue(ctx, err); JS_FreeValue(ctx, res2); JS_FreeValue(ctx, resolving_funcs[0]); @@ -49891,7 +51696,7 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValue this_val int res; value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, &value, 0); + 1, vc(&value), 0); if (JS_IsException(value_wrapper_promise)) { JS_FreeValue(ctx, value); goto reject; @@ -49907,8 +51712,8 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValue this_val resolve_reject[1] = JS_UNDEFINED; res = perform_promise_then(ctx, value_wrapper_promise, - resolve_reject, - resolving_funcs); + vc(resolve_reject), + vc(resolving_funcs)); JS_FreeValue(ctx, resolve_reject[0]); JS_FreeValue(ctx, value_wrapper_promise); JS_FreeValue(ctx, resolving_funcs[0]); @@ -50087,8 +51892,8 @@ static int hex_decode(JSContext *ctx, JSString *p, int k) { return c; } -static JSValue js_global_decodeURI(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int isComponent) +static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int isComponent) { JSValue str; StringBuffer b_s, *b = &b_s; @@ -50198,8 +52003,8 @@ static int encodeURI_hex(StringBuffer *b, int c) { return string_buffer_write8(b, buf, n); } -static JSValue js_global_encodeURI(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, +static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int isComponent) { JSValue str; @@ -50263,8 +52068,8 @@ static JSValue js_global_encodeURI(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_global_escape(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue str; StringBuffer b_s, *b = &b_s; @@ -50289,8 +52094,8 @@ static JSValue js_global_escape(JSContext *ctx, JSValue this_val, return string_buffer_end(b); } -static JSValue js_global_unescape(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue str; StringBuffer b_s, *b = &b_s; @@ -50359,10 +52164,11 @@ static int64_t floor_div_int64(int64_t a, int64_t b) { return (a - (m + (m < 0) * b)) / b; } -static JSValue js_Date_parse(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv); +static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); -static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValue this_val) +static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, + JSValueConst this_val) { if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { JSObject *p = JS_VALUE_GET_OBJ(this_val); @@ -50373,7 +52179,7 @@ static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValue th return -1; } -static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValue this_val, double v) +static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v) { if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { JSObject *p = JS_VALUE_GET_OBJ(this_val); @@ -50422,7 +52228,7 @@ static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; static char const day_names[] = "SunMonTueWedThuFriSat"; -static __exception int get_date_fields(JSContext *ctx, JSValue obj, +static __exception int get_date_fields(JSContext *ctx, JSValueConst obj, double fields[minimum_length(9)], int is_local, int force) { @@ -50490,7 +52296,9 @@ static double set_date_fields(double fields[minimum_length(7)], int is_local) { int yi, mi, i; int64_t days; volatile double temp; /* enforce evaluation order */ + double d = NAN; + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); /* emulate 21.4.1.15 MakeDay ( year, month, date ) */ y = fields[0]; m = fields[1]; @@ -50500,7 +52308,7 @@ static double set_date_fields(double fields[minimum_length(7)], int is_local) { if (mn < 0) mn += 12; if (ym < -271821 || ym > 275760) - return NAN; + goto out; yi = ym; mi = mn; @@ -50532,18 +52340,21 @@ static double set_date_fields(double fields[minimum_length(7)], int is_local) { /* emulate 21.4.1.16 MakeDate ( day, time ) */ tv = (temp = day * 86400000) + time; /* prevent generation of FMA */ if (!isfinite(tv)) - return NAN; + goto out; /* adjust for local time and clip */ if (is_local) { int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv; tv += getTimezoneOffset(ti) * 60000; } - return time_clip(tv); + d = time_clip(tv); +out: + JS_X87_FPCW_RESTORE(fpcw); + return d; } -static JSValue get_date_field(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue get_date_field(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { // get_date_field(obj, n, is_local) double fields[9]; @@ -50563,8 +52374,8 @@ static JSValue get_date_field(JSContext *ctx, JSValue this_val, return js_number(fields[n]); } -static JSValue set_date_field(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue set_date_field(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { // _field(obj, first_field, end_field, args, is_local) double fields[9]; @@ -50607,8 +52418,8 @@ static JSValue set_date_field(JSContext *ctx, JSValue this_val, part: 1=date, 2=time 3=all XXX: should use a variant of strftime(). */ -static JSValue get_date_string(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { // _string(obj, fmt, part) char buf[64]; @@ -50712,7 +52523,7 @@ static JSValue get_date_string(JSContext *ctx, JSValue this_val, } if (!pos) { // XXX: should throw exception? - return JS_AtomToString(ctx, JS_ATOM_empty_string); + return js_empty_string(ctx->rt); } return js_new_string8_len(ctx, buf, pos); } @@ -50722,8 +52533,8 @@ static int64_t date_now(void) { return js__gettimeofday_us() / 1000; } -static JSValue js_date_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv) +static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { // Date(y, mon, d, h, m, s, ms) JSValue rv; @@ -50750,7 +52561,7 @@ static JSValue js_date_constructor(JSContext *ctx, JSValue new_target, } v = JS_ToPrimitive(ctx, argv[0], HINT_NONE); if (JS_IsString(v)) { - dv = js_Date_parse(ctx, JS_UNDEFINED, 1, &v); + dv = js_Date_parse(ctx, JS_UNDEFINED, 1, vc(&v)); JS_FreeValue(ctx, v); if (JS_IsException(dv)) return JS_EXCEPTION; @@ -50790,8 +52601,8 @@ static JSValue js_date_constructor(JSContext *ctx, JSValue new_target, return rv; } -static JSValue js_Date_UTC(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // UTC(y, mon, d, h, m, s, ms) double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; @@ -50859,6 +52670,9 @@ static bool string_get_digits(const uint8_t *sp, int *pp, int *pval, p_start = p; while ((c = sp[p]) >= '0' && c <= '9') { + /* arbitrary limit to 9 digits */ + if (v >= 100000000) + return false; v = v * 10 + c - '0'; p++; if (p - p_start == max_digits) @@ -50902,7 +52716,7 @@ static bool string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, bool stric sgn = sp[p++]; if (sgn == '+' || sgn == '-') { int n = p; - if (!string_get_digits(sp, &p, &hh, 1, 9)) + if (!string_get_digits(sp, &p, &hh, 1, 0)) return false; n = p - n; if (strict && n != 2 && n != 4) @@ -51096,7 +52910,7 @@ static bool js_date_parse_otherstring(const uint8_t *sp, *is_local = false; } else { p++; - if (string_get_digits(sp, &p, &val, 1, 9)) { + if (string_get_digits(sp, &p, &val, 1, 0)) { if (c == '-') { if (val == 0) return false; @@ -51107,7 +52921,7 @@ static bool js_date_parse_otherstring(const uint8_t *sp, } } } else - if (string_get_digits(sp, &p, &val, 1, 9)) { + if (string_get_digits(sp, &p, &val, 1, 0)) { if (string_skip_char(sp, &p, ':')) { /* time part */ fields[3] = val; @@ -51223,8 +53037,8 @@ static bool js_date_parse_otherstring(const uint8_t *sp, return true; } -static JSValue js_Date_parse(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue s, rv; int fields[9]; @@ -51266,25 +53080,25 @@ static JSValue js_Date_parse(JSContext *ctx, JSValue this_val, for(i = 0; i < 7; i++) fields1[i] = fields[i]; d = set_date_fields(fields1, is_local) - fields[8] * 60000; - rv = JS_NewFloat64(ctx, d); + rv = js_float64(d); } } JS_FreeValue(ctx, s); return rv; } -static JSValue js_Date_now(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // now() return js_int64(date_now()); } -static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // Symbol_toPrimitive(hint) - JSValue obj = this_val; + JSValueConst obj = this_val; JSAtom hint = JS_ATOM_NULL; int hint_num; @@ -51312,8 +53126,8 @@ static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValue this_val, return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY); } -static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // getTimezoneOffset() double v; @@ -51327,8 +53141,8 @@ static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValue this_val, return js_int64(getTimezoneOffset((int64_t)trunc(v))); } -static JSValue js_date_getTime(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // getTime() double v; @@ -51338,8 +53152,8 @@ static JSValue js_date_getTime(JSContext *ctx, JSValue this_val, return js_float64(v); } -static JSValue js_date_setTime(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // setTime(v) double v; @@ -51349,12 +53163,12 @@ static JSValue js_date_setTime(JSContext *ctx, JSValue this_val, return JS_SetThisTimeValue(ctx, this_val, time_clip(v)); } -static JSValue js_date_setYear(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // setYear(y) double y; - JSValue args[1]; + JSValue d, ret; if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0])) return JS_EXCEPTION; @@ -51366,12 +53180,14 @@ static JSValue js_date_setYear(JSContext *ctx, JSValue this_val, if (y >= 0 && y < 100) y += 1900; } - args[0] = js_float64(y); - return set_date_field(ctx, this_val, 1, args, 0x011); + d = js_float64(y); + ret = set_date_field(ctx, this_val, 1, vc(&d), 0x011); + JS_FreeValue(ctx, d); + return ret; } -static JSValue js_date_toJSON(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // toJSON(key) JSValue obj, tv, method, rv; @@ -51473,7 +53289,7 @@ JSValue JS_NewDate(JSContext *ctx, double epoch_ms) return obj; } -bool JS_IsDate(JSValue v) +bool JS_IsDate(JSValueConst v) { if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) return false; @@ -51497,7 +53313,9 @@ void JS_AddIntrinsicDate(JSContext *ctx) void JS_AddIntrinsicEval(JSContext *ctx) { +#ifndef QJS_DISABLE_PARSER ctx->eval_internal = __JS_EvalInternal; +#endif // QJS_DISABLE_PARSER } /* BigInt */ @@ -51513,40 +53331,25 @@ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) case JS_TAG_BOOL: val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val)); break; + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: break; case JS_TAG_FLOAT64: { - bf_t *a, a_s; - - a = JS_ToBigInt1(ctx, &a_s, val); - if (!bf_is_finite(a)) { - JS_FreeValue(ctx, val); - val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); - } else { - JSValue val1 = JS_NewBigInt(ctx); - bf_t *r; - int ret; - if (JS_IsException(val1)) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - r = JS_GetBigInt(val1); - ret = bf_set(r, a); - ret |= bf_rint(r, BF_RNDZ); - JS_FreeValue(ctx, val); - if (ret & BF_ST_MEM_ERROR) { - JS_FreeValue(ctx, val1); - val = JS_ThrowOutOfMemory(ctx); - } else if (ret & BF_ST_INEXACT) { - JS_FreeValue(ctx, val1); + double d = JS_VALUE_GET_FLOAT64(val); + JSBigInt *r; + int res; + r = js_bigint_from_float64(ctx, &res, d); + if (!r) { + if (res == 0) { + val = JS_EXCEPTION; + } else if (res == 1) { val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer"); } else { - val = JS_CompactBigInt(ctx, val1); - } + val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); } + } else { + val = JS_CompactBigInt(ctx, r); } - if (a == &a_s) - bf_delete(a); } break; case JS_TAG_STRING: @@ -51567,31 +53370,31 @@ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) } static JSValue js_bigint_constructor(JSContext *ctx, - JSValue new_target, - int argc, JSValue *argv) + JSValueConst new_target, + int argc, JSValueConst *argv) { if (!JS_IsUndefined(new_target)) - return JS_ThrowTypeError(ctx, "not a constructor"); + return JS_ThrowTypeErrorNotAConstructor(ctx, new_target); return JS_ToBigIntCtorFree(ctx, js_dup(argv[0])); } -static JSValue js_thisBigIntValue(JSContext *ctx, JSValue this_val) +static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val) { - if (JS_IsBigInt(ctx, this_val)) + if (JS_IsBigInt(this_val)) return js_dup(this_val); if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { JSObject *p = JS_VALUE_GET_OBJ(this_val); if (p->class_id == JS_CLASS_BIG_INT) { - if (JS_IsBigInt(ctx, p->u.object_data)) + if (JS_IsBigInt(p->u.object_data)) return js_dup(p->u.object_data); } } return JS_ThrowTypeError(ctx, "not a BigInt"); } -static JSValue js_bigint_toString(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue val; int base; @@ -51615,49 +53418,73 @@ static JSValue js_bigint_toString(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_bigint_valueOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return js_thisBigIntValue(ctx, this_val); } static JSValue js_bigint_asUintN(JSContext *ctx, - JSValue this_val, - int argc, JSValue *argv, int asIntN) + JSValueConst this_val, + int argc, JSValueConst *argv, int asIntN) { uint64_t bits; - bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s; - JSValue res; + JSValue res, a; if (JS_ToIndex(ctx, &bits, argv[0])) return JS_EXCEPTION; - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) - return JS_EXCEPTION; - a = JS_ToBigInt(ctx, &a_s, argv[1]); - if (!a) { - JS_FreeValue(ctx, res); + a = JS_ToBigInt(ctx, argv[1]); + if (JS_IsException(a)) return JS_EXCEPTION; + if (bits == 0) { + JS_FreeValue(ctx, a); + res = __JS_NewShortBigInt(ctx, 0); + } else if (JS_VALUE_GET_TAG(a) == JS_TAG_SHORT_BIG_INT) { + /* fast case */ + if (bits >= JS_SHORT_BIG_INT_BITS) { + res = a; + } else { + uint64_t v; + int shift; + shift = 64 - bits; + v = JS_VALUE_GET_SHORT_BIG_INT(a); + v = v << shift; + if (asIntN) + v = (int64_t)v >> shift; + else + v = v >> shift; + res = __JS_NewShortBigInt(ctx, v); + } + } else { + JSBigInt *r, *p = JS_VALUE_GET_PTR(a); + if (bits >= p->len * JS_LIMB_BITS) { + res = a; + } else { + int len, shift, i; + js_limb_t v; + len = (bits + JS_LIMB_BITS - 1) / JS_LIMB_BITS; + r = js_bigint_new(ctx, len); + if (!r) { + JS_FreeValue(ctx, a); + return JS_EXCEPTION; + } + r->len = len; + for(i = 0; i < len - 1; i++) + r->tab[i] = p->tab[i]; + shift = (-bits) & (JS_LIMB_BITS - 1); + /* 0 <= shift <= JS_LIMB_BITS - 1 */ + v = p->tab[len - 1] << shift; + if (asIntN) + v = (js_slimb_t)v >> shift; + else + v = v >> shift; + r->tab[len - 1] = v; + r = js_bigint_normalize(ctx, r); + JS_FreeValue(ctx, a); + res = JS_CompactBigInt(ctx, r); + } } - /* XXX: optimize */ - r = JS_GetBigInt(res); - bf_init(ctx->bf_ctx, mask); - bf_set_ui(mask, 1); - bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); - bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ); - bf_logic_and(r, a, mask); - if (asIntN && bits != 0) { - bf_set_ui(mask, 1); - bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ); - if (bf_cmpu(r, mask) >= 0) { - bf_set_ui(mask, 1); - bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); - bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ); - } - } - bf_delete(mask); - JS_FreeBigInt(ctx, a, &a_s); - return JS_CompactBigInt(ctx, res); + return res; } static const JSCFunctionListEntry js_bigint_funcs[] = { @@ -51699,6 +53526,8 @@ static void JS_AddIntrinsicBasicObjects(JSContext *ctx) int i; ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL); + ctx->global_obj = JS_NewObject(ctx); + ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL); ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0, JS_CFUNC_generic, 0, ctx->class_proto[JS_CLASS_OBJECT]); @@ -51714,7 +53543,7 @@ static void JS_AddIntrinsicBasicObjects(JSContext *ctx) JS_NewAtomString(ctx, native_error_name[i]), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); JS_DefinePropertyValue(ctx, proto, JS_ATOM_message, - JS_AtomToString(ctx, JS_ATOM_empty_string), + js_empty_string(ctx->rt), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); ctx->native_error_proto[i] = proto; } @@ -51746,20 +53575,15 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0); /* add caller and arguments properties to throw a TypeError */ - obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0); JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED, - obj1, ctx->throw_type_error, + ctx->throw_type_error, ctx->throw_type_error, JS_PROP_HAS_GET | JS_PROP_HAS_SET | JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED, - obj1, ctx->throw_type_error, + ctx->throw_type_error, ctx->throw_type_error, JS_PROP_HAS_GET | JS_PROP_HAS_SET | JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); - JS_FreeValue(ctx, obj1); - JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, &ctx->throw_type_error, 1)); - - ctx->global_obj = JS_NewObject(ctx); - ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL); + JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, vc(&ctx->throw_type_error), 1)); /* Object */ obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1, @@ -51807,11 +53631,26 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) countof(js_iterator_proto_funcs)); obj = JS_NewGlobalCConstructor(ctx, "Iterator", js_iterator_constructor, 0, ctx->class_proto[JS_CLASS_ITERATOR]); + // quirk: Iterator.prototype.constructor is an accessor property + // TODO(bnoordhuis) mildly inefficient because JS_NewGlobalCConstructor + // first creates a .constructor value property that we then replace with + // an accessor + ctx->iterator_ctor_getset = JS_NewCFunctionData(ctx, js_iterator_constructor_getset, + 0, 0, 1, vc(&obj)); + JS_DefineProperty(ctx, ctx->class_proto[JS_CLASS_ITERATOR], + JS_ATOM_constructor, JS_UNDEFINED, + ctx->iterator_ctor_getset, ctx->iterator_ctor_getset, + JS_PROP_HAS_GET|JS_PROP_HAS_SET|JS_PROP_WRITABLE|JS_PROP_CONFIGURABLE); ctx->iterator_ctor = js_dup(obj); JS_SetPropertyFunctionList(ctx, obj, js_iterator_funcs, countof(js_iterator_funcs)); + ctx->class_proto[JS_CLASS_ITERATOR_CONCAT] = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ITERATOR]); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ITERATOR_CONCAT], + js_iterator_concat_proto_funcs, + countof(js_iterator_concat_proto_funcs)); + ctx->class_proto[JS_CLASS_ITERATOR_HELPER] = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ITERATOR]); JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ITERATOR_HELPER], js_iterator_helper_proto_funcs, @@ -51832,6 +53671,10 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) ctx->array_ctor = js_dup(obj); JS_SetPropertyFunctionList(ctx, obj, js_array_funcs, countof(js_array_funcs)); + JS_DefineAutoInitProperty(ctx, obj, JS_ATOM_fromAsync, + JS_AUTOINIT_ID_BYTECODE, + (void *)(uintptr_t)JS_BUILTIN_ARRAY_FROMASYNC, + JS_PROP_WRITABLE|JS_PROP_CONFIGURABLE); /* XXX: create auto_initializer */ { @@ -51900,7 +53743,7 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) /* String */ ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_STRING); - JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string)); + JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], js_empty_string(ctx->rt)); obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1, ctx->class_proto[JS_CLASS_STRING]); JS_SetPropertyFunctionList(ctx, obj, js_string_funcs, @@ -51980,7 +53823,7 @@ static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = { }; static JSValue js_array_buffer_constructor3(JSContext *ctx, - JSValue new_target, + JSValueConst new_target, uint64_t len, uint64_t *max_len, JSClassID class_id, uint8_t *buf, @@ -52061,7 +53904,7 @@ static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr) } static JSValue js_array_buffer_constructor2(JSContext *ctx, - JSValue new_target, + JSValueConst new_target, uint64_t len, uint64_t *max_len, JSClassID class_id) { @@ -52071,7 +53914,7 @@ static JSValue js_array_buffer_constructor2(JSContext *ctx, } static JSValue js_array_buffer_constructor1(JSContext *ctx, - JSValue new_target, + JSValueConst new_target, uint64_t len, uint64_t *max_len) { return js_array_buffer_constructor2(ctx, new_target, len, max_len, @@ -52088,7 +53931,7 @@ JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, buf, free_func, opaque, false); } -bool JS_IsArrayBuffer(JSValue obj) { +bool JS_IsArrayBuffer(JSValueConst obj) { return JS_GetClassID(obj) == JS_CLASS_ARRAY_BUFFER; } @@ -52102,8 +53945,8 @@ JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len) true); } -static JSValue js_array_buffer_constructor0(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv, +static JSValue js_array_buffer_constructor0(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv, JSClassID class_id) { uint64_t len, max_len, *pmax_len = NULL; @@ -52137,23 +53980,23 @@ static JSValue js_array_buffer_constructor0(JSContext *ctx, JSValue new_target, class_id); } -static JSValue js_array_buffer_constructor(JSContext *ctx, JSValue new_target, - int argc, JSValue *argv) +static JSValue js_array_buffer_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { return js_array_buffer_constructor0(ctx, new_target, argc, argv, JS_CLASS_ARRAY_BUFFER); } static JSValue js_shared_array_buffer_constructor(JSContext *ctx, - JSValue new_target, - int argc, JSValue *argv) + JSValueConst new_target, + int argc, JSValueConst *argv) { return js_array_buffer_constructor0(ctx, new_target, argc, argv, JS_CLASS_SHARED_ARRAY_BUFFER); } /* also used for SharedArrayBuffer */ -static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val) +static void js_array_buffer_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSArrayBuffer *abuf = p->u.array_buffer; @@ -52188,8 +54031,8 @@ static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val) } static JSValue js_array_buffer_isView(JSContext *ctx, - JSValue this_val, - int argc, JSValue *argv) + JSValueConst this_val, + int argc, JSValueConst *argv) { JSObject *p; @@ -52218,7 +54061,7 @@ static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx) // #sec-get-arraybuffer.prototype.detached static JSValue js_array_buffer_get_detached(JSContext *ctx, - JSValue this_val) + JSValueConst this_val) { JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_BUFFER); if (!abuf) @@ -52229,7 +54072,7 @@ static JSValue js_array_buffer_get_detached(JSContext *ctx, } static JSValue js_array_buffer_get_byteLength(JSContext *ctx, - JSValue this_val, + JSValueConst this_val, int class_id) { JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); @@ -52240,7 +54083,7 @@ static JSValue js_array_buffer_get_byteLength(JSContext *ctx, } static JSValue js_array_buffer_get_maxByteLength(JSContext *ctx, - JSValue this_val, + JSValueConst this_val, int class_id) { JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); @@ -52251,7 +54094,8 @@ static JSValue js_array_buffer_get_maxByteLength(JSContext *ctx, return js_uint32(abuf->byte_length); } -static JSValue js_array_buffer_get_resizable(JSContext *ctx, JSValue this_val, +static JSValue js_array_buffer_get_resizable(JSContext *ctx, + JSValueConst this_val, int class_id) { JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); @@ -52260,7 +54104,7 @@ static JSValue js_array_buffer_get_resizable(JSContext *ctx, JSValue this_val, return js_bool(array_buffer_is_resizable(abuf)); } -void JS_DetachArrayBuffer(JSContext *ctx, JSValue obj) +void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj) { JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER); struct list_head *el; @@ -52288,7 +54132,7 @@ void JS_DetachArrayBuffer(JSContext *ctx, JSValue obj) } /* get an ArrayBuffer or SharedArrayBuffer */ -static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValue obj) +static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj) { JSObject *p; if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) @@ -52305,7 +54149,7 @@ static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValue obj) /* return NULL if exception. WARNING: any JS call can detach the buffer and render the returned pointer invalid */ -uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValue obj) +uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj) { JSArrayBuffer *abuf = js_get_array_buffer(ctx, obj); if (!abuf) @@ -52326,9 +54170,46 @@ static bool array_buffer_is_resizable(const JSArrayBuffer *abuf) return abuf->max_byte_length >= 0; } +static void js_array_buffer_update_typed_arrays(JSArrayBuffer *abuf) +{ + uint32_t size_log2, size_elem; + struct list_head *el; + JSTypedArray *ta; + JSObject *p; + uint8_t *data; + int64_t len; + + len = abuf->byte_length; + data = abuf->data; + list_for_each(el, &abuf->array_list) { + ta = list_entry(el, JSTypedArray, link); + p = ta->obj; + if (p->class_id == JS_CLASS_DATAVIEW) { + if (ta->track_rab && ta->offset < len) + ta->length = len - ta->offset; + continue; + } + p->u.array.count = 0; + p->u.array.u.ptr = NULL; + size_log2 = typed_array_size_log2(p->class_id); + size_elem = 1 << size_log2; + if (ta->track_rab) { + if (len >= (int64_t)ta->offset + size_elem) { + p->u.array.count = (len - ta->offset) >> size_log2; + p->u.array.u.ptr = &data[ta->offset]; + } + } else { + if (len >= (int64_t)ta->offset + ta->length) { + p->u.array.count = ta->length >> size_log2; + p->u.array.u.ptr = &data[ta->offset]; + } + } + } +} + // ES #sec-arraybuffer.prototype.transfer -static JSValue js_array_buffer_transfer(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_array_buffer_transfer(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { bool transfer_to_fixed_length = magic & 1; JSArrayBuffer *abuf; @@ -52378,20 +54259,17 @@ static JSValue js_array_buffer_transfer(JSContext *ctx, JSValue this_val, abuf->data = NULL; abuf->byte_length = 0; abuf->detached = true; + js_array_buffer_update_typed_arrays(abuf); return js_array_buffer_constructor3(ctx, JS_UNDEFINED, new_len, pmax_len, JS_CLASS_ARRAY_BUFFER, bs, abuf->free_func, NULL, false); } -static JSValue js_array_buffer_resize(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int class_id) +static JSValue js_array_buffer_resize(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int class_id) { - uint32_t size_log2, size_elem; - struct list_head *el; JSArrayBuffer *abuf; - JSTypedArray *ta; - JSObject *p; uint8_t *data; int64_t len; @@ -52434,35 +54312,13 @@ static JSValue js_array_buffer_resize(JSContext *ctx, JSValue this_val, abuf->byte_length = len; abuf->data = data; } - data = abuf->data; - // update lengths of all typed arrays backed by this array buffer - list_for_each(el, &abuf->array_list) { - ta = list_entry(el, JSTypedArray, link); - p = ta->obj; - if (p->class_id == JS_CLASS_DATAVIEW) - continue; - p->u.array.count = 0; - p->u.array.u.ptr = NULL; - size_log2 = typed_array_size_log2(p->class_id); - size_elem = 1 << size_log2; - if (ta->track_rab) { - if (len >= (int64_t)ta->offset + size_elem) { - p->u.array.count = (len - ta->offset) >> size_log2; - p->u.array.u.ptr = &data[ta->offset]; - } - } else { - if (len >= (int64_t)ta->offset + ta->length) { - p->u.array.count = ta->length >> size_log2; - p->u.array.u.ptr = &data[ta->offset]; - } - } - } + js_array_buffer_update_typed_arrays(abuf); return JS_UNDEFINED; } static JSValue js_array_buffer_slice(JSContext *ctx, - JSValue this_val, - int argc, JSValue *argv, int class_id) + JSValueConst this_val, + int argc, JSValueConst *argv, int class_id) { JSArrayBuffer *abuf, *new_abuf; int64_t len, start, end, new_len; @@ -52493,7 +54349,7 @@ static JSValue js_array_buffer_slice(JSContext *ctx, } else { JSValue args[1]; args[0] = js_int64(new_len); - new_obj = JS_CallConstructor(ctx, ctor, 1, args); + new_obj = JS_CallConstructor(ctx, ctor, 1, vc(args)); JS_FreeValue(ctx, ctor); JS_FreeValue(ctx, args[0]); } @@ -52515,7 +54371,7 @@ static JSValue js_array_buffer_slice(JSContext *ctx, goto fail; } /* must test again because of side effects */ - if (abuf->detached) { + if (abuf->detached || abuf->byte_length < start + new_len) { JS_ThrowTypeErrorDetachedArrayBuffer(ctx); goto fail; } @@ -52587,14 +54443,14 @@ static bool typed_array_is_oob(JSObject *p) /* WARNING: 'p' must be a typed array. Works even if the array buffer is detached */ -static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p) +static uint32_t typed_array_length(JSObject *p) { JSTypedArray *ta = p->u.typed_array; int size_log2 = typed_array_size_log2(p->class_id); return ta->length >> size_log2; } -static int validate_typed_array(JSContext *ctx, JSValue this_val) +static int validate_typed_array(JSContext *ctx, JSValueConst this_val) { JSObject *p; p = get_typed_array(ctx, this_val); @@ -52607,7 +54463,7 @@ static int validate_typed_array(JSContext *ctx, JSValue this_val) return 0; } -static JSValue js_typed_array_get_length(JSContext *ctx, JSValue this_val) +static JSValue js_typed_array_get_length(JSContext *ctx, JSValueConst this_val) { JSObject *p; p = get_typed_array(ctx, this_val); @@ -52616,7 +54472,7 @@ static JSValue js_typed_array_get_length(JSContext *ctx, JSValue this_val) return js_int32(p->u.array.count); } -static JSValue js_typed_array_get_buffer(JSContext *ctx, JSValue this_val) +static JSValue js_typed_array_get_buffer(JSContext *ctx, JSValueConst this_val) { JSObject *p; JSTypedArray *ta; @@ -52627,7 +54483,7 @@ static JSValue js_typed_array_get_buffer(JSContext *ctx, JSValue this_val) return js_dup(JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); } -static JSValue js_typed_array_get_byteLength(JSContext *ctx, JSValue this_val) +static JSValue js_typed_array_get_byteLength(JSContext *ctx, JSValueConst this_val) { uint32_t size_log2; JSTypedArray *ta; @@ -52645,7 +54501,7 @@ static JSValue js_typed_array_get_byteLength(JSContext *ctx, JSValue this_val) return js_int64((int64_t)p->u.array.count << size_log2); } -static JSValue js_typed_array_get_byteOffset(JSContext *ctx, JSValue this_val) +static JSValue js_typed_array_get_byteOffset(JSContext *ctx, JSValueConst this_val) { JSObject *p; JSTypedArray *ta; @@ -52658,7 +54514,7 @@ static JSValue js_typed_array_get_byteOffset(JSContext *ctx, JSValue this_val) return js_uint32(ta->offset); } -JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValue *argv, +JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv, JSTypedArrayEnum type) { if (type < JS_TYPED_ARRAY_UINT8C || type > JS_TYPED_ARRAY_FLOAT64) @@ -52671,7 +54527,7 @@ JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValue *argv, /* Return the buffer associated to the typed array or an exception if it is not a typed array or if the buffer is detached. pbyte_offset, pbyte_length or pbytes_per_element can be NULL. */ -JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValue obj, +JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, size_t *pbyte_offset, size_t *pbyte_length, size_t *pbytes_per_element) @@ -52696,7 +54552,7 @@ JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValue obj, /* return NULL if exception. WARNING: any JS call can detach the buffer and render the returned pointer invalid */ -uint8_t *JS_GetUint8Array(JSContext *ctx, size_t *psize, JSValue obj) +uint8_t *JS_GetUint8Array(JSContext *ctx, size_t *psize, JSValueConst obj) { JSObject *p; JSTypedArray *ta; @@ -52723,7 +54579,7 @@ uint8_t *JS_GetUint8Array(JSContext *ctx, size_t *psize, JSValue obj) } static JSValue js_typed_array_get_toStringTag(JSContext *ctx, - JSValue this_val) + JSValueConst this_val) { JSObject *p; if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) @@ -52735,9 +54591,9 @@ static JSValue js_typed_array_get_toStringTag(JSContext *ctx, } static JSValue js_typed_array_set_internal(JSContext *ctx, - JSValue dst, - JSValue src, - JSValue off) + JSValueConst dst, + JSValueConst src, + JSValueConst off) { JSObject *p; JSObject *src_p; @@ -52820,8 +54676,8 @@ static JSValue js_typed_array_set_internal(JSContext *ctx, return JS_EXCEPTION; } -static JSValue js_typed_array_at(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_typed_array_at(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSObject *p; int64_t idx, len; @@ -52873,8 +54729,8 @@ static JSValue js_typed_array_at(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_typed_array_with(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_typed_array_with(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue arr, val; JSObject *p; @@ -52916,26 +54772,26 @@ static JSValue js_typed_array_with(JSContext *ctx, JSValue this_val, } static JSValue js_typed_array_set(JSContext *ctx, - JSValue this_val, - int argc, JSValue *argv) + JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue offset = JS_UNDEFINED; + JSValueConst offset = JS_UNDEFINED; if (argc > 1) { offset = argv[1]; } return js_typed_array_set_internal(ctx, this_val, argv[0], offset); } -static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int magic) +static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { if (validate_typed_array(ctx, this_val)) return JS_EXCEPTION; return js_create_array_iterator(ctx, this_val, argc, argv, magic); } -static JSValue js_typed_array_create(JSContext *ctx, JSValue ctor, - int argc, JSValue *argv) +static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor, + int argc, JSValueConst *argv) { JSValue ret; int new_len; @@ -52963,10 +54819,10 @@ static JSValue js_typed_array_create(JSContext *ctx, JSValue ctor, } static JSValue js_typed_array___speciesCreate(JSContext *ctx, - JSValue this_val, - int argc, JSValue *argv) + JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue obj; + JSValueConst obj; JSObject *p; JSValue ctor, ret; int argc1; @@ -52989,12 +54845,12 @@ static JSValue js_typed_array___speciesCreate(JSContext *ctx, return ret; } -static JSValue js_typed_array_from(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { // from(items, mapfn = void 0, this_arg = void 0) - JSValue items = argv[0], mapfn, this_arg; - JSValue args[2]; + JSValueConst items = argv[0], mapfn, this_arg; + JSValueConst args[2]; JSValue stack[2]; JSValue iter, arr, r, v, v2; int64_t k, len; @@ -53046,8 +54902,7 @@ static JSValue js_typed_array_from(JSContext *ctx, JSValue this_val, if (js_get_length64(ctx, &len, arr) < 0) goto exception; v = js_int64(len); - args[0] = v; - r = js_typed_array_create(ctx, this_val, 1, args); + r = js_typed_array_create(ctx, this_val, 1, vc(&v)); JS_FreeValue(ctx, v); if (JS_IsException(r)) goto exception; @@ -53082,15 +54937,14 @@ static JSValue js_typed_array_from(JSContext *ctx, JSValue this_val, return r; } -static JSValue js_typed_array_of(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_typed_array_of(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue obj; - JSValue args[1]; + JSValue v, obj; int i; - args[0] = js_int32(argc); - obj = js_typed_array_create(ctx, this_val, 1, args); + v = js_int32(argc); + obj = js_typed_array_create(ctx, this_val, 1, vc(&v)); if (JS_IsException(obj)) return obj; @@ -53103,8 +54957,8 @@ static JSValue js_typed_array_of(JSContext *ctx, JSValue this_val, return obj; } -static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSObject *p; int len, to, from, final, count, shift, space; @@ -53144,8 +54998,8 @@ static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValue this_val, return js_dup(this_val); } -static JSValue js_typed_array_fill(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSObject *p; int len, k, final, shift; @@ -53237,11 +55091,10 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValue this_val, return js_dup(this_val); } -static JSValue js_typed_array_find(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int mode) +static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int mode) { - JSValue func, this_arg; - JSValue args[3]; + JSValueConst func, this_arg, args[3]; JSValue val, index_val, res; int len, k, end; int dir; @@ -53303,8 +55156,8 @@ static JSValue js_typed_array_find(JSContext *ctx, JSValue this_val, #define special_lastIndexOf 1 #define special_includes -1 -static JSValue js_typed_array_indexOf(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int special) +static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int special) { JSObject *p; int len, tag, is_int, is_bigint, k, stop, inc, res = -1; @@ -53312,7 +55165,6 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValue this_val, double d; float f; uint16_t hf; - bool oob; p = get_typed_array(ctx, this_val); if (!p) @@ -53324,26 +55176,15 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValue this_val, if (len == 0) goto done; - oob = false; if (special == special_lastIndexOf) { k = len - 1; if (argc > 1) { - if (JS_ToFloat64(ctx, &d, argv[1])) + int64_t k1; + if (JS_ToInt64Clamp(ctx, &k1, argv[1], -1, len - 1, len)) goto exception; - if (isnan(d)) { - k = 0; - } else { - if (d >= 0) { - if (d < k) { - k = d; - } - } else { - d += len; - if (d < 0) - goto done; - k = d; - } - } + k = k1; + if (k < 0) + goto done; } stop = -1; inc = -1; @@ -53358,7 +55199,6 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValue this_val, k = 0; } else if (k > len) { k = len; - oob = true; } } stop = len; @@ -53369,8 +55209,10 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValue this_val, exception is raised) */ if (typed_array_is_oob(p) || len > p->u.array.count) { /* "includes" scans all the properties, so "undefined" can match */ - if (special == special_includes && JS_IsUndefined(argv[0]) && len > 0) - res = oob ? -1 : 0; + if (special == special_includes) + if (JS_IsUndefined(argv[0])) + if (k < typed_array_length(p)) + res = 0; goto done; } @@ -53396,19 +55238,33 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValue this_val, v64 = d; is_int = (v64 == d); } - } else - if (tag == JS_TAG_BIG_INT) { - JSBigInt *p1 = JS_VALUE_GET_PTR(argv[0]); + } else if (tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT) { + JSBigIntBuf buf1; + JSBigInt *p1; + int sz = (64 / JS_LIMB_BITS); + if (tag == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, argv[0]); + else + p1 = JS_VALUE_GET_PTR(argv[0]); if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) { - if (bf_get_int64(&v64, &p1->num, 0) != 0) - goto done; + if (p1->len > sz) + goto done; /* does not fit an int64 : cannot be found */ } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) { - if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0) + if (js_bigint_sign(p1)) + goto done; /* v < 0 */ + if (p1->len <= sz) { + /* OK */ + } else if (p1->len == sz + 1 && p1->tab[sz] == 0) { + /* 2^63 <= v <= 2^64-1 */ + } else { goto done; + } } else { goto done; } + if (JS_ToBigInt64(ctx, &v64, argv[0])) + goto exception; d = 0; is_bigint = 1; } else { @@ -53595,8 +55451,8 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_typed_array_join(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int toLocaleString) +static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int toLocaleString) { JSValue sep = JS_UNDEFINED, el; StringBuffer b_s, *b = &b_s; @@ -53619,7 +55475,7 @@ static JSValue js_typed_array_join(JSContext *ctx, JSValue this_val, goto exception; s = JS_VALUE_GET_STRING(sep); if (s->len == 1 && !s->is_wide_char) - c = s->u.str8[0]; + c = str8(s)[0]; else c = -1; // ToString(sep) can detach or resize the arraybuffer as a side effect @@ -53674,8 +55530,8 @@ static JSValue js_typed_array_join(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_typed_array_reverse(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSObject *p; int len; @@ -53737,8 +55593,8 @@ static JSValue js_typed_array_reverse(JSContext *ctx, JSValue this_val, return js_dup(this_val); } -static JSValue js_typed_array_toReversed(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_typed_array_toReversed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue arr, ret; JSObject *p; @@ -53755,10 +55611,10 @@ static JSValue js_typed_array_toReversed(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_typed_array_slice(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue args[2]; + JSValueConst args[2]; JSValue arr, val; JSObject *p, *p1; int n, len, start, final, count, shift, space; @@ -53796,8 +55652,8 @@ static JSValue js_typed_array_slice(JSContext *ctx, JSValue this_val, p1 = get_typed_array(ctx, arr); if (p1 != NULL && p->class_id == p1->class_id && - typed_array_get_length(ctx, p1) >= count && - typed_array_get_length(ctx, p) >= start + count) { + typed_array_length(p1) >= count && + typed_array_length(p) >= start + count) { shift = typed_array_size_log2(p->class_id); memmove(p1->u.array.u.uint8_ptr, p->u.array.u.uint8_ptr + (start << shift), @@ -53823,12 +55679,12 @@ static JSValue js_typed_array_slice(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } -static JSValue js_typed_array_subarray(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSArrayBuffer *abuf; JSTypedArray *ta; - JSValue args[4]; + JSValueConst args[4]; JSValue arr, byteOffset, ta_buffer; JSObject *p; int len, start, final, count, shift, offset; @@ -53864,9 +55720,9 @@ static JSValue js_typed_array_subarray(JSContext *ctx, JSValue this_val, if (JS_IsException(ta_buffer)) goto exception; args[0] = this_val; - args[1] = ta_buffer; - args[2] = js_int32(offset); - args[3] = js_int32(count); + args[1] = safe_const(ta_buffer); + args[2] = safe_const(js_int32(offset)); + args[3] = safe_const(js_int32(count)); // result is length-tracking if source TA is and no explicit count is given if (ta->track_rab && JS_IsUndefined(argv[1])) args[3] = JS_UNDEFINED; @@ -53990,8 +55846,8 @@ static JSValue js_TA_get_float64(JSContext *ctx, const void *a) { struct TA_sort_context { JSContext *ctx; int exception; - JSValue arr; - JSValue cmp; + JSValueConst arr; + JSValueConst cmp; JSValue (*getfun)(JSContext *ctx, const void *a); int elt_size; }; @@ -54019,7 +55875,7 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { a_idx * (size_t)psc->elt_size); argv[1] = psc->getfun(ctx, (char *)p->u.array.u.ptr + b_idx * (size_t)(psc->elt_size)); - res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv); + res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, vc(argv)); if (JS_IsException(res)) { psc->exception = 1; goto done; @@ -54047,8 +55903,8 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { return cmp; } -static JSValue js_typed_array_sort(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSObject *p; int len; @@ -54191,8 +56047,8 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValue this_val, return js_dup(this_val); } -static JSValue js_typed_array_toSorted(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_typed_array_toSorted(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue arr, ret; JSObject *p; @@ -54256,8 +56112,8 @@ static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = { }; static JSValue js_typed_array_base_constructor(JSContext *ctx, - JSValue this_val, - int argc, JSValue *argv) + JSValueConst this_val, + int argc, JSValueConst *argv) { return JS_ThrowTypeError(ctx, "cannot be called"); } @@ -54294,7 +56150,7 @@ static int typed_array_init(JSContext *ctx, JSValue obj, JSValue buffer, static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, - JSValue obj, JSValue method) + JSValueConst obj, JSValueConst method) { JSValue arr, iter, next_method = JS_UNDEFINED, val; int done; @@ -54315,10 +56171,8 @@ static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); if (JS_IsException(val)) goto fail; - if (done) { - JS_FreeValue(ctx, val); + if (done) break; - } if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0) goto fail; k++; @@ -54335,8 +56189,8 @@ static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, } static JSValue js_typed_array_constructor_obj(JSContext *ctx, - JSValue new_target, - JSValue obj, + JSValueConst new_target, + JSValueConst obj, int classid) { JSValue iter, ret, arr = JS_UNDEFINED, val, buffer; @@ -54389,8 +56243,8 @@ static JSValue js_typed_array_constructor_obj(JSContext *ctx, } static JSValue js_typed_array_constructor_ta(JSContext *ctx, - JSValue new_target, - JSValue src_obj, + JSValueConst new_target, + JSValueConst src_obj, int classid, uint32_t len) { JSObject *p, *src_buffer; @@ -54446,8 +56300,8 @@ static JSValue js_typed_array_constructor_ta(JSContext *ctx, } static JSValue js_typed_array_constructor(JSContext *ctx, - JSValue new_target, - int argc, JSValue *argv, + JSValueConst new_target, + int argc, JSValueConst *argv, int classid) { bool track_rab = false; @@ -54517,7 +56371,7 @@ static JSValue js_typed_array_constructor(JSContext *ctx, return obj; } -static void js_typed_array_finalizer(JSRuntime *rt, JSValue val) +static void js_typed_array_finalizer(JSRuntime *rt, JSValueConst val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSTypedArray *ta = p->u.typed_array; @@ -54532,7 +56386,7 @@ static void js_typed_array_finalizer(JSRuntime *rt, JSValue val) } } -static void js_typed_array_mark(JSRuntime *rt, JSValue val, +static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -54543,15 +56397,15 @@ static void js_typed_array_mark(JSRuntime *rt, JSValue val, } static JSValue js_dataview_constructor(JSContext *ctx, - JSValue new_target, - int argc, JSValue *argv) + JSValueConst new_target, + int argc, JSValueConst *argv) { bool recompute_len = false; bool track_rab = false; JSArrayBuffer *abuf; uint64_t offset; uint32_t len; - JSValue buffer; + JSValueConst buffer; JSValue obj; JSTypedArray *ta; JSObject *p; @@ -54635,7 +56489,7 @@ static bool dataview_is_oob(JSObject *p) return (int64_t)ta->offset + ta->length > abuf->byte_length; } -static JSObject *get_dataview(JSContext *ctx, JSValue this_val) +static JSObject *get_dataview(JSContext *ctx, JSValueConst this_val) { JSObject *p; if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) @@ -54649,7 +56503,7 @@ static JSObject *get_dataview(JSContext *ctx, JSValue this_val) return p; } -static JSValue js_dataview_get_buffer(JSContext *ctx, JSValue this_val) +static JSValue js_dataview_get_buffer(JSContext *ctx, JSValueConst this_val) { JSObject *p; JSTypedArray *ta; @@ -54660,7 +56514,7 @@ static JSValue js_dataview_get_buffer(JSContext *ctx, JSValue this_val) return js_dup(JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); } -static JSValue js_dataview_get_byteLength(JSContext *ctx, JSValue this_val) +static JSValue js_dataview_get_byteLength(JSContext *ctx, JSValueConst this_val) { JSArrayBuffer *abuf; JSTypedArray *ta; @@ -54679,7 +56533,7 @@ static JSValue js_dataview_get_byteLength(JSContext *ctx, JSValue this_val) return js_uint32(ta->length); } -static JSValue js_dataview_get_byteOffset(JSContext *ctx, JSValue this_val) +static JSValue js_dataview_get_byteOffset(JSContext *ctx, JSValueConst this_val) { JSTypedArray *ta; JSObject *p; @@ -54694,8 +56548,8 @@ static JSValue js_dataview_get_byteOffset(JSContext *ctx, JSValue this_val) } static JSValue js_dataview_getValue(JSContext *ctx, - JSValue this_obj, - int argc, JSValue *argv, int class_id) + JSValueConst this_obj, + int argc, JSValueConst *argv, int class_id) { JSTypedArray *ta; JSArrayBuffer *abuf; @@ -54807,8 +56661,8 @@ static JSValue js_dataview_getValue(JSContext *ctx, } static JSValue js_dataview_setValue(JSContext *ctx, - JSValue this_obj, - int argc, JSValue *argv, int class_id) + JSValueConst this_obj, + int argc, JSValueConst *argv, int class_id) { JSTypedArray *ta; JSArrayBuffer *abuf; @@ -54818,7 +56672,7 @@ static JSValue js_dataview_setValue(JSContext *ctx, uint64_t v64; uint32_t v; uint64_t pos; - JSValue val; + JSValueConst val; ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); if (!ta) @@ -54832,8 +56686,7 @@ static JSValue js_dataview_setValue(JSContext *ctx, if (class_id <= JS_CLASS_UINT32_ARRAY) { if (JS_ToUint32(ctx, &v, val)) return JS_EXCEPTION; - } else - if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) { + } else if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) { if (JS_ToBigInt64(ctx, (int64_t *)&v64, val)) return JS_EXCEPTION; } else { @@ -54972,7 +56825,7 @@ JSValue JS_NewUint8ArrayCopy(JSContext *ctx, const uint8_t *buf, size_t len) return js_new_uint8array(ctx, buffer); } -int JS_GetTypedArrayType(JSValue obj) +int JS_GetTypedArrayType(JSValueConst obj) { JSClassID class_id = JS_GetClassID(obj); if (is_typed_array(class_id)) @@ -54998,7 +56851,7 @@ typedef enum AtomicsOpEnum { static void *js_atomics_get_ptr(JSContext *ctx, JSArrayBuffer **pabuf, int *psize_log2, JSClassID *pclass_id, - JSValue obj, JSValue idx_val, + JSValueConst obj, JSValueConst idx_val, int is_waitable) { JSObject *p; @@ -55055,8 +56908,8 @@ static void *js_atomics_get_ptr(JSContext *ctx, } static JSValue js_atomics_op(JSContext *ctx, - JSValue this_obj, - int argc, JSValue *argv, int op) + JSValueConst this_obj, + int argc, JSValueConst *argv, int op) { int size_log2; uint64_t v, a, rep_val; @@ -55198,8 +57051,8 @@ static JSValue js_atomics_op(JSContext *ctx, } static JSValue js_atomics_store(JSContext *ctx, - JSValue this_obj, - int argc, JSValue *argv) + JSValueConst this_obj, + int argc, JSValueConst *argv) { int size_log2; void *ptr; @@ -55212,7 +57065,7 @@ static JSValue js_atomics_store(JSContext *ctx, return JS_EXCEPTION; if (size_log2 == 3) { int64_t v64; - ret = JS_ToBigIntValueFree(ctx, js_dup(argv[2])); + ret = JS_ToBigIntFree(ctx, js_dup(argv[2])); if (JS_IsException(ret)) return ret; if (JS_ToBigInt64(ctx, &v64, ret)) { @@ -55252,8 +57105,8 @@ static JSValue js_atomics_store(JSContext *ctx, } static JSValue js_atomics_isLockFree(JSContext *ctx, - JSValue this_obj, - int argc, JSValue *argv) + JSValueConst this_obj, + int argc, JSValueConst *argv) { int v, ret; if (JS_ToInt32Sat(ctx, &v, argv[0])) @@ -55277,13 +57130,13 @@ static struct list_head js_atomics_waiter_list = // no-op: Atomics.pause() is not allowed to block or yield to another // thread, only to hint the CPU that it should back off for a bit; // the amount of work we do here is a good enough substitute -static JSValue js_atomics_pause(JSContext *ctx, JSValue this_obj, - int argc, JSValue *argv) +static JSValue js_atomics_pause(JSContext *ctx, JSValueConst this_obj, + int argc, JSValueConst *argv) { double d; if (argc > 0) { - switch (JS_VALUE_GET_TAG(argv[0])) { + switch (JS_VALUE_GET_NORM_TAG(argv[0])) { case JS_TAG_FLOAT64: // accepted if and only if fraction == 0.0 d = JS_VALUE_GET_FLOAT64(argv[0]); if (isfinite(d)) @@ -55301,8 +57154,8 @@ static JSValue js_atomics_pause(JSContext *ctx, JSValue this_obj, } static JSValue js_atomics_wait(JSContext *ctx, - JSValue this_obj, - int argc, JSValue *argv) + JSValueConst this_obj, + int argc, JSValueConst *argv) { int64_t v; int32_t v32; @@ -55372,8 +57225,8 @@ static JSValue js_atomics_wait(JSContext *ctx, } static JSValue js_atomics_notify(JSContext *ctx, - JSValue this_obj, - int argc, JSValue *argv) + JSValueConst this_obj, + int argc, JSValueConst *argv) { struct list_head *el, *el1, waiter_list; int32_t count, n; @@ -55501,6 +57354,7 @@ void JS_AddIntrinsicTypedArrays(JSContext *ctx) js_typed_array_base_funcs, countof(js_typed_array_base_funcs)); JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto); + JS_SetConstructorBit(ctx, typed_array_base_func, true); /* Used to squelch a -Wcast-function-type warning. */ JSCFunctionType ft = { .generic_magic = js_typed_array_constructor }; @@ -55549,7 +57403,7 @@ static double js__now_ms(void) return js__hrtime_ns() / 1e6; } -static JSValue js_perf_now(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) +static JSValue js_perf_now(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { return js_float64(js__now_ms() - ctx->time_origin); } @@ -55574,7 +57428,7 @@ void JS_AddPerformance(JSContext *ctx) } /* Equality comparisons and sameness */ -int JS_IsEqual(JSContext *ctx, JSValue op1, JSValue op2) +int JS_IsEqual(JSContext *ctx, JSValueConst op1, JSValueConst op2) { JSValue sp[2] = { js_dup(op1), js_dup(op2) }; if (js_eq_slow(ctx, endof(sp), 0)) @@ -55582,17 +57436,17 @@ int JS_IsEqual(JSContext *ctx, JSValue op1, JSValue op2) return JS_VALUE_GET_BOOL(sp[0]); } -bool JS_IsStrictEqual(JSContext *ctx, JSValue op1, JSValue op2) +bool JS_IsStrictEqual(JSContext *ctx, JSValueConst op1, JSValueConst op2) { return js_strict_eq2(ctx, js_dup(op1), js_dup(op2), JS_EQ_STRICT); } -bool JS_IsSameValue(JSContext *ctx, JSValue op1, JSValue op2) +bool JS_IsSameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2) { return js_same_value(ctx, op1, op2); } -bool JS_IsSameValueZero(JSContext *ctx, JSValue op1, JSValue op2) +bool JS_IsSameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2) { return js_same_value_zero(ctx, op1, op2); } @@ -55600,13 +57454,13 @@ bool JS_IsSameValueZero(JSContext *ctx, JSValue op1, JSValue op2) /* WeakRef */ typedef struct JSWeakRefData { - JSValue target; + JSValueConst target; JSValue obj; } JSWeakRefData; static JSWeakRefData js_weakref_sentinel; -static void js_weakref_finalizer(JSRuntime *rt, JSValue val) +static void js_weakref_finalizer(JSRuntime *rt, JSValueConst val) { JSWeakRefData *wrd = JS_GetOpaque(val, JS_CLASS_WEAK_REF); if (!wrd || wrd == &js_weakref_sentinel) @@ -55628,11 +57482,12 @@ static void js_weakref_finalizer(JSRuntime *rt, JSValue val) js_free_rt(rt, wr); } -static JSValue js_weakref_constructor(JSContext *ctx, JSValue new_target, int argc, JSValue *argv) +static JSValue js_weakref_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { if (JS_IsUndefined(new_target)) return JS_ThrowTypeError(ctx, "constructor requires 'new'"); - JSValue arg = argv[0]; + JSValueConst arg = argv[0]; if (!is_valid_weakref_target(arg)) return JS_ThrowTypeError(ctx, "invalid target"); // TODO(saghul): short-circuit if the refcount is 1? @@ -55660,7 +57515,7 @@ static JSValue js_weakref_constructor(JSContext *ctx, JSValue new_target, int ar return obj; } -static JSValue js_weakref_deref(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) +static JSValue js_weakref_deref(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSWeakRefData *wrd = JS_GetOpaque2(ctx, this_val, JS_CLASS_WEAK_REF); if (!wrd) @@ -55681,8 +57536,8 @@ static const JSClassShortDef js_weakref_class_def[] = { typedef struct JSFinRecEntry { struct list_head link; - JSValue obj; - JSValue target; + JSValueConst obj; + JSValueConst target; JSValue held_val; JSValue token; } JSFinRecEntry; @@ -55709,7 +57564,7 @@ static void delete_finrec_weakref(JSRuntime *rt, JSFinRecEntry *fre) js_free_rt(rt, wr); } -static void js_finrec_finalizer(JSRuntime *rt, JSValue val) +static void js_finrec_finalizer(JSRuntime *rt, JSValueConst val) { JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY); if (frd) { @@ -55733,7 +57588,8 @@ static void js_finrec_finalizer(JSRuntime *rt, JSValue val) } } -static void js_finrec_mark(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) +static void js_finrec_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) { JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY); if (frd) { @@ -55747,14 +57603,14 @@ static void js_finrec_mark(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) } } -static JSValue js_finrec_constructor(JSContext *ctx, JSValue new_target, int argc, JSValue *argv) +static JSValue js_finrec_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) { if (JS_IsUndefined(new_target)) return JS_ThrowTypeError(ctx, "constructor requires 'new'"); - JSValue cb = argv[0]; - if (!JS_IsFunction(ctx, cb)) - return JS_ThrowTypeError(ctx, "argument must be a function"); - + JSValueConst cb = argv[0]; + if (check_function(ctx, cb)) + return JS_EXCEPTION; JSValue obj = js_create_from_ctor(ctx, new_target, JS_CLASS_FINALIZATION_REGISTRY); if (JS_IsException(obj)) return JS_EXCEPTION; @@ -55770,16 +57626,17 @@ static JSValue js_finrec_constructor(JSContext *ctx, JSValue new_target, int arg return obj; } -static JSValue js_finrec_register(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) +static JSValue js_finrec_register(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSFinalizationRegistryData *frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY); if (!frd) return JS_EXCEPTION; - JSValue target = argv[0]; - JSValue held_val = argv[1]; + JSValueConst target = argv[0]; + JSValueConst held_val = argv[1]; // The function length needs to return 2, so the 3rd argument won't be initialized. - JSValue token = argc > 2 ? argv[2] : JS_UNDEFINED; + JSValueConst token = argc > 2 ? argv[2] : JS_UNDEFINED; if (!is_valid_weakref_target(target)) return JS_ThrowTypeError(ctx, "invalid target"); @@ -55810,13 +57667,13 @@ static JSValue js_finrec_register(JSContext *ctx, JSValue this_val, int argc, JS return JS_UNDEFINED; } -static JSValue js_finrec_unregister(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) +static JSValue js_finrec_unregister(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSFinalizationRegistryData *frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY); if (!frd) return JS_EXCEPTION; - JSValue token = argv[0]; + JSValueConst token = argv[0]; if (!is_valid_weakref_target(token)) return JS_ThrowTypeError(ctx, "invalid unregister token"); @@ -55847,7 +57704,7 @@ static const JSClassShortDef js_finrec_class_def[] = { { JS_ATOM_FinalizationRegistry, js_finrec_finalizer, js_finrec_mark }, /* JS_CLASS_FINALIZATION_REGISTRY */ }; -static JSValue js_finrec_job(JSContext *ctx, int argc, JSValue *argv) +static JSValue js_finrec_job(JSContext *ctx, int argc, JSValueConst *argv) { return JS_Call(ctx, argv[0], JS_UNDEFINED, 1, &argv[1]); } @@ -55898,6 +57755,7 @@ static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref) assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ list_del(&mr->hash_link); list_del(&mr->link); + s->record_count--; break; case JS_WEAK_REF_KIND_WEAK_REF: wrd = wr->u.weak_ref_data; @@ -55935,7 +57793,7 @@ static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref) * During the GC sweep phase the held object might be collected first. */ if (!rt->in_free && (!JS_IsObject(fre->held_val) || JS_IsLiveObject(rt, fre->held_val))) { - JSValue args[2]; + JSValueConst args[2]; args[0] = frd->cb; args[1] = fre->held_val; JS_EnqueueJob(frd->ctx, js_finrec_job, 2, args); @@ -55954,7 +57812,7 @@ static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref) *first_weak_ref = NULL; /* fail safe */ } -static bool is_valid_weakref_target(JSValue val) +static bool is_valid_weakref_target(JSValueConst val) { switch (JS_VALUE_GET_TAG(val)) { case JS_TAG_OBJECT: @@ -55973,7 +57831,8 @@ static bool is_valid_weakref_target(JSValue val) return true; } -static void insert_weakref_record(JSValue target, struct JSWeakRefRecord *wr) +static void insert_weakref_record(JSValueConst target, + struct JSWeakRefRecord *wr) { JSWeakRefRecord **pwr = get_first_weak_ref(target); /* Add the weak reference */ @@ -55981,209 +57840,9 @@ static void insert_weakref_record(JSValue target, struct JSWeakRefRecord *wr) *pwr = wr; } -/* Poly IC */ - -JSInlineCache *init_ic(JSContext *ctx) -{ - JSInlineCache *ic; - ic = js_malloc(ctx, sizeof(JSInlineCache)); - if (unlikely(!ic)) - goto fail; - ic->count = 0; - ic->hash_bits = 2; - ic->capacity = 1 << ic->hash_bits; - ic->hash = js_mallocz(ctx, sizeof(ic->hash[0]) * ic->capacity); - if (unlikely(!ic->hash)) - goto fail; - ic->cache = NULL; - return ic; -fail: - js_free(ctx, ic); - return NULL; -} - -int rebuild_ic(JSContext *ctx, JSInlineCache *ic) -{ - uint32_t i, count; - JSInlineCacheHashSlot *ch; - if (ic->count == 0) - goto end; - count = 0; - ic->cache = js_mallocz(ctx, sizeof(JSInlineCacheRingSlot) * ic->count); - if (unlikely(!ic->cache)) - goto fail; - for (i = 0; i < ic->capacity; i++) { - for (ch = ic->hash[i]; ch != NULL; ch = ch->next) { - ch->index = count++; - ic->cache[ch->index].atom = JS_DupAtom(ctx, ch->atom); - ic->cache[ch->index].index = 0; - } - } -end: - return 0; -fail: - return -1; -} - -int resize_ic_hash(JSContext *ctx, JSInlineCache *ic) -{ - uint32_t new_capacity, i, h; - JSInlineCacheHashSlot *ch, *ch_next; - JSInlineCacheHashSlot **new_hash; - new_capacity = 1 << (ic->hash_bits + 1); - new_hash = js_mallocz(ctx, sizeof(ic->hash[0]) * new_capacity); - if (unlikely(!new_hash)) - goto fail; - ic->hash_bits += 1; - for (i = 0; i < ic->capacity; i++) { - for (ch = ic->hash[i]; ch != NULL; ch = ch_next) { - h = get_index_hash(ch->atom, ic->hash_bits); - ch_next = ch->next; - ch->next = new_hash[h]; - new_hash[h] = ch; - } - } - js_free(ctx, ic->hash); - ic->hash = new_hash; - ic->capacity = new_capacity; - return 0; -fail: - return -1; -} - -int free_ic(JSRuntime* rt, JSInlineCache *ic) -{ - uint32_t i; - JSInlineCacheHashSlot *ch, *ch_next; - JSShape **shape, *(*shapes)[IC_CACHE_ITEM_CAPACITY]; - JSInlineCacheWatchpoint **ref, *(*refs)[IC_CACHE_ITEM_CAPACITY], *o; - if (ic->cache) { - for (i = 0; i < ic->count; i++) { - shapes = &ic->cache[i].shape; - JS_FreeAtomRT(rt, ic->cache[i].atom); - for (shape = *shapes; shape != endof(*shapes); shape++) - js_free_shape_null(rt, *shape); - refs = &ic->cache[i].watchpoint_ref; - for (ref = *refs; ref != endof(*refs); ref++) { - o = *ref; - if (o) { - ic_watchpoint_delete_handler(rt, o->ref ,o->offset, o->atom, NULL); - list_del(&o->link); - js_free_rt(rt, o); - } - } - } - } - for (i = 0; i < ic->capacity; i++) { - for (ch = ic->hash[i]; ch != NULL; ch = ch_next) { - ch_next = ch->next; - JS_FreeAtomRT(rt, ch->atom); - js_free_rt(rt, ch); - } - } - if (ic->count > 0) - js_free_rt(rt, ic->cache); - js_free_rt(rt, ic->hash); - js_free_rt(rt, ic); - return 0; -} - -static void add_ic_slot(JSContext *ctx, JSInlineCacheUpdate *icu, - JSAtom atom, JSObject *object, uint32_t prop_offset, JSObject* prototype) -{ - uint8_t i; - uint32_t h; - JSInlineCacheHashSlot *ch; - JSInlineCacheRingSlot *cr; - JSInlineCache *ic; - JSShape *sh; - - if (!icu) - return; - ic = icu->ic; - if (!ic) - return; - sh = object->shape; - if (!sh->is_hashed) - return; - if (prototype && !prototype->shape->is_hashed) - return; - cr = NULL; - h = get_index_hash(atom, ic->hash_bits); - for (ch = ic->hash[h]; ch != NULL; ch = ch->next) { - if (ch->atom == atom) { - cr = ic->cache + ch->index; - break; - } - } - assert(cr != NULL); - i = cr->index; - do { - if (sh == cr->shape[i] && prototype == cr->proto[i]) { - cr->prop_offset[i] = prop_offset; - goto end; - } - i = (i + 1) % countof(cr->shape); - if (unlikely(i == cr->index)) { - cr->index = (cr->index + 1) % countof(cr->shape); - break; - } - } while (i != cr->index); - sh = cr->shape[i]; - if (cr->watchpoint_ref[i]) - js_shape_delete_watchpoints(ctx->rt, sh, cr); - cr->prop_offset[i] = prop_offset; - cr->shape[i] = js_dup_shape(object->shape); - js_free_shape_null(ctx->rt, sh); - if (prototype) { - JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, prototype)); - cr->proto[i] = prototype; - cr->watchpoint_ref[i] = js_shape_create_watchpoint(ctx->rt, cr->shape[i], cr, i, JS_DupAtom(ctx, atom)); - } -end: - icu->offset = ch->index; -} - -int ic_watchpoint_delete_handler(JSRuntime* rt, JSInlineCacheRingSlot *ref, uint8_t offset, JSAtom atom, JSInlineCacheRingSlot *target) { - if (target && ref != target) - return 1; - assert(ref->proto[offset] != NULL); - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ref->proto[offset])); - JS_FreeAtomRT(rt, atom); - ref->watchpoint_ref[offset] = NULL; - ref->proto[offset] = NULL; - ref->prop_offset[offset] = 0; - ref->shape[offset] = NULL; - return 0; -} - -int ic_delete_shape_proto_watchpoints(JSRuntime *rt, JSShape *shape, JSAtom atom, bool force) { - struct list_head *el, *el1; - JSObject *p; - JSInlineCacheRingSlot *cr; - JSShape *sh; - p = shape->proto; - while(likely(p)) { - if (p->shape->watchpoint) - list_for_each_safe(el, el1, p->shape->watchpoint) { - JSInlineCacheWatchpoint *o = list_entry(el, JSInlineCacheWatchpoint, link); - if (force || o->atom == atom) { - cr = (JSInlineCacheRingSlot *)o->ref; - sh = cr->shape[o->offset]; - ic_watchpoint_delete_handler(rt, cr, o->offset, o->atom, NULL); - js_free_shape_null(rt, shape); - list_del(el); - js_free_rt(rt, o); - } - } - p = p->shape->proto; - } - return 0; -} - /* CallSite */ -static void js_callsite_finalizer(JSRuntime *rt, JSValue val) +static void js_callsite_finalizer(JSRuntime *rt, JSValueConst val) { JSCallSiteData *csd = JS_GetOpaque(val, JS_CLASS_CALL_SITE); if (csd) { @@ -56194,7 +57853,8 @@ static void js_callsite_finalizer(JSRuntime *rt, JSValue val) } } -static void js_callsite_mark(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) +static void js_callsite_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) { JSCallSiteData *csd = JS_GetOpaque(val, JS_CLASS_CALL_SITE); if (csd) { @@ -56276,7 +57936,7 @@ static void js_new_callsite_data2(JSContext *ctx, JSCallSiteData *csd, const cha } } -static JSValue js_callsite_getfield(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic) +static JSValue js_callsite_getfield(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { JSCallSiteData *csd = JS_GetOpaque2(ctx, this_val, JS_CLASS_CALL_SITE); if (!csd) @@ -56285,21 +57945,21 @@ static JSValue js_callsite_getfield(JSContext *ctx, JSValue this_val, int argc, return js_dup(*field); } -static JSValue js_callsite_isnative(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) +static JSValue js_callsite_isnative(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSCallSiteData *csd = JS_GetOpaque2(ctx, this_val, JS_CLASS_CALL_SITE); if (!csd) return JS_EXCEPTION; - return JS_NewBool(ctx, csd->native); + return js_bool(csd->native); } -static JSValue js_callsite_getnumber(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic) +static JSValue js_callsite_getnumber(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { JSCallSiteData *csd = JS_GetOpaque2(ctx, this_val, JS_CLASS_CALL_SITE); if (!csd) return JS_EXCEPTION; int *field = (void *)((char *)csd + magic); - return JS_NewInt32(ctx, *field); + return js_int32(*field); } static const JSCFunctionListEntry js_callsite_proto_funcs[] = { @@ -56330,8 +57990,231 @@ static void _JS_AddIntrinsicCallSite(JSContext *ctx) countof(js_callsite_proto_funcs)); } +/* DOMException */ +typedef struct JSDOMExceptionData { + JSValue name; + JSValue message; + int code; +} JSDOMExceptionData; + +typedef struct JSDOMExceptionNameDef { + const char * const name; + const char * const code_name; +} JSDOMExceptionNameDef; + +static const JSDOMExceptionNameDef js_dom_exception_names_table[] = { + { "IndexSizeError", "INDEX_SIZE_ERR" }, + { NULL, "DOMSTRING_SIZE_ERR" }, + { "HierarchyRequestError", "HIERARCHY_REQUEST_ERR" }, + { "WrongDocumentError", "WRONG_DOCUMENT_ERR" }, + { "InvalidCharacterError", "INVALID_CHARACTER_ERR" }, + { NULL, "NO_DATA_ALLOWED_ERR" }, + { "NoModificationAllowedError", "NO_MODIFICATION_ALLOWED_ERR" }, + { "NotFoundError", "NOT_FOUND_ERR" }, + { "NotSupportedError", "NOT_SUPPORTED_ERR" }, + { "InUseAttributeError", "INUSE_ATTRIBUTE_ERR" }, + { "InvalidStateError", "INVALID_STATE_ERR" }, + { "SyntaxError", "SYNTAX_ERR" }, + { "InvalidModificationError", "INVALID_MODIFICATION_ERR" }, + { "NamespaceError", "NAMESPACE_ERR" }, + { "InvalidAccessError", "INVALID_ACCESS_ERR" }, + { NULL, "VALIDATION_ERR" }, + { "TypeMismatchError", "TYPE_MISMATCH_ERR" }, + { "SecurityError", "SECURITY_ERR" }, + { "NetworkError", "NETWORK_ERR" }, + { "AbortError", "ABORT_ERR" }, + { "URLMismatchError", "URL_MISMATCH_ERR" }, + { "QuotaExceededError", "QUOTA_EXCEEDED_ERR" }, + { "TimeoutError", "TIMEOUT_ERR" }, + { "InvalidNodeTypeError", "INVALID_NODE_TYPE_ERR" }, + { "DataCloneError", "DATA_CLONE_ERR" } +}; + +static void js_domexception_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSDOMExceptionData *s = JS_GetOpaque(val, JS_CLASS_DOM_EXCEPTION); + if (s) { + JS_FreeValueRT(rt, s->name); + JS_FreeValueRT(rt, s->message); + js_free_rt(rt, s); + } +} + +static void js_domexception_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSDOMExceptionData *s = JS_GetOpaque(val, JS_CLASS_DOM_EXCEPTION); + if (s) { + JS_MarkValue(rt, s->name, mark_func); + JS_MarkValue(rt, s->message, mark_func); + } +} + +static JSValue js_domexception_constructor0(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv, + int backtrace_flags) +{ + JSDOMExceptionData *s; + JSValue obj, message, name; + + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DOM_EXCEPTION); + if (JS_IsException(obj)) + return JS_EXCEPTION; + if (!JS_IsUndefined(argv[0])) + message = JS_ToString(ctx, argv[0]); + else + message = js_empty_string(ctx->rt); + if (JS_IsException(message)) + goto fail1; + if (!JS_IsUndefined(argv[1])) + name = JS_ToString(ctx, argv[1]); + else + name = JS_AtomToString(ctx, JS_ATOM_Error); + if (JS_IsException(name)) + goto fail2; + s = js_malloc(ctx, sizeof(*s)); + if (!s) + goto fail3; + s->name = name; + s->message = message; + s->code = -1; + JS_SetOpaqueInternal(obj, s); + build_backtrace(ctx, obj, JS_UNDEFINED, NULL, 0, 0, backtrace_flags); + return obj; +fail3: + JS_FreeValue(ctx, name); +fail2: + JS_FreeValue(ctx, message); +fail1: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_domexception_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + if (JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "constructor requires 'new'"); + return js_domexception_constructor0(ctx, new_target, argc, argv, + JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL); +} + +static JSValue js_domexception_getfield(JSContext *ctx, JSValueConst this_val, + int magic) +{ + JSDOMExceptionData *s; + JSValue *valp; + + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_DOM_EXCEPTION); + if (!s) + return JS_EXCEPTION; + valp = (void *)((char *)s + magic); + return js_dup(*valp); +} + +static JSValue js_domexception_get_code(JSContext *ctx, JSValueConst this_val) +{ + JSDOMExceptionData *s; + const char *name, *it; + int i; + size_t len; + + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_DOM_EXCEPTION); + if (!s) + return JS_EXCEPTION; + if (s->code == -1) { + name = JS_ToCStringLen(ctx, &len, s->name); + if (!name) + return JS_EXCEPTION; + for (i = 0; i < countof(js_dom_exception_names_table); i++) { + it = js_dom_exception_names_table[i].name; + if (it && !strcmp(it, name) && len == strlen(it)) { + s->code = i; + break; + } + } + s->code++; + JS_FreeCString(ctx, name); + } + return js_int32(s->code); +} + +static const JSCFunctionListEntry js_domexception_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("name", js_domexception_getfield, NULL, + offsetof(JSDOMExceptionData, name) ), + JS_CGETSET_MAGIC_DEF("message", js_domexception_getfield, NULL, + offsetof(JSDOMExceptionData, message) ), + JS_CGETSET_DEF("code", js_domexception_get_code, NULL ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DOMException", JS_PROP_CONFIGURABLE ), +}; + +static const JSClassShortDef js_domexception_class_def[] = { + { JS_ATOM_DOMException, js_domexception_finalizer, js_domexception_mark }, /* JS_CLASS_DOM_EXCEPTION */ +}; + +JSValue JS_PRINTF_FORMAT_ATTR(3, 4) JS_ThrowDOMException(JSContext *ctx, const char *name, JS_PRINTF_FORMAT const char *fmt, ...) +{ + JSValue obj, js_name, js_message; + JSValueConst argv[2]; + va_list ap; + char buf[256]; + + assert(JS_IsRegisteredClass(ctx->rt, JS_CLASS_DOM_EXCEPTION)); + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + js_name = JS_NewString(ctx, name); + if (JS_IsException(js_name)) + return JS_EXCEPTION; + js_message = JS_NewString(ctx, buf); + if (JS_IsException(js_message)) { + JS_FreeValue(ctx, js_name); + return JS_EXCEPTION; + } + argv[0] = js_message; + argv[1] = js_name; + obj = js_domexception_constructor0(ctx, JS_UNDEFINED, 2, argv, 0); + JS_FreeValue(ctx, js_message); + JS_FreeValue(ctx, js_name); + if (JS_IsException(obj)) + return JS_EXCEPTION; + return JS_Throw(ctx, obj); +} + +void JS_AddIntrinsicDOMException(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + int i; + JSAtom name; + JSValue ctor, proto; + + if (!JS_IsRegisteredClass(rt, JS_CLASS_DOM_EXCEPTION)) { + init_class_range(rt, js_domexception_class_def, JS_CLASS_DOM_EXCEPTION, + countof(js_domexception_class_def)); + } + proto = JS_NewObjectClass(ctx, JS_CLASS_ERROR); + JS_SetPropertyFunctionList(ctx, proto, + js_domexception_proto_funcs, + countof(js_domexception_proto_funcs)); + ctor = JS_NewCFunction2(ctx, js_domexception_constructor, "DOMException", 2, + JS_CFUNC_constructor_or_func, 0); + JS_SetConstructor(ctx, ctor, proto); + for (i = 0; i < countof(js_dom_exception_names_table); i++) { + name = JS_NewAtom(ctx, js_dom_exception_names_table[i].code_name); + JS_DefinePropertyValue(ctx, proto, name, js_int32(i + 1), + JS_PROP_ENUMERABLE); + JS_DefinePropertyValue(ctx, ctor, name, js_int32(i + 1), + JS_PROP_ENUMERABLE); + JS_FreeAtom(ctx, name); + } + JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_DOMException, ctor, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + ctx->class_proto[JS_CLASS_DOM_EXCEPTION] = proto; +} + bool JS_DetectModule(const char *input, size_t input_len) { +#ifndef QJS_DISABLE_PARSER JSRuntime *rt; JSContext *ctx; JSValue val; @@ -56346,8 +58229,8 @@ bool JS_DetectModule(const char *input, size_t input_len) JS_FreeRuntime(rt); return false; } - JS_AddIntrinsicRegExp(ctx); // otherwise regexp literals don't parse - val = __JS_EvalInternal(ctx, JS_UNDEFINED, input, input_len, "", + JS_AddIntrinsicRegExpCompiler(ctx); // otherwise regexp literals don't parse + val = __JS_EvalInternal(ctx, JS_UNDEFINED, input, input_len, "", 1, JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY, -1); if (JS_IsException(val)) { const char *msg = JS_ToCString(ctx, rt->current_exception); @@ -56360,6 +58243,9 @@ bool JS_DetectModule(const char *input, size_t input_len) JS_FreeContext(ctx); JS_FreeRuntime(rt); return is_module; +#else + return false; +#endif // QJS_DISABLE_PARSER } uintptr_t js_std_cmd(int cmd, ...) { @@ -56386,6 +58272,13 @@ uintptr_t js_std_cmd(int cmd, ...) { *pv = ctx->error_back_trace; ctx->error_back_trace = JS_UNDEFINED; break; + case 3: // GetStringKind + ctx = va_arg(ap, JSContext *); + pv = va_arg(ap, JSValue *); + rv = -1; + if (JS_IsString(*pv)) + rv = JS_VALUE_GET_STRING(*pv)->kind; + break; default: rv = -1; } @@ -56393,19 +58286,8 @@ uintptr_t js_std_cmd(int cmd, ...) { return rv; } - /* CUSTOM */ -void JS_SetGCBeforeCallback(JSRuntime *rt, int(*fn)(JSRuntime*)) -{ - rt->gc_before_callback = fn; -} - -void JS_SetGCAfterCallback(JSRuntime *rt, void(*fn)(JSRuntime*)) -{ - rt->gc_after_callback = fn; -} - int JS_IsArrayBuffer2(JSContext *ctx, JSValueConst val) { JSObject *p; diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs.h old mode 100644 new mode 100755 index 8b74d74e..c8c97c33 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs.h +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/quickjs.h @@ -37,6 +37,8 @@ extern "C" { #endif +#define QUICKJS_NG 1 + #if defined(__GNUC__) || defined(__clang__) #define js_force_inline inline __attribute__((always_inline)) #define JS_EXTERN __attribute__((visibility("default"))) @@ -100,20 +102,74 @@ enum { JS_TAG_UNINITIALIZED = 4, JS_TAG_CATCH_OFFSET = 5, JS_TAG_EXCEPTION = 6, - JS_TAG_FLOAT64 = 7, + JS_TAG_SHORT_BIG_INT = 7, + JS_TAG_FLOAT64 = 8, /* any larger tag is FLOAT64 if JS_NAN_BOXING */ }; -#define JS_FLOAT64_NAN NAN -#define JSValueConst JSValue /* For backwards compatibility. */ +#if !defined(JS_CHECK_JSVALUE) +#define JSValueConst JSValue +#endif + +// JS_CHECK_JSVALUE build mode does not produce working code but is here to +// help catch reference counting bugs at compile time, by making it harder +// to mix up JSValue and JSValueConst +// +// rules: +// +// - a function with a JSValue parameter takes ownership; +// caller must *not* call JS_FreeValue +// +// - a function with a JSValueConst parameter does not take ownership; +// caller *must* call JS_FreeValue +// +// - a function returning a JSValue transfers ownership to caller; +// caller *must* call JS_FreeValue +// +// - a function returning a JSValueConst does *not* transfer ownership; +// caller must *not* call JS_FreeValue +#if defined(JS_CHECK_JSVALUE) + +typedef struct JSValue *JSValue; +typedef const struct JSValue *JSValueConst; + +#define JS_MKVAL(tag, val) ((JSValue)((tag) | (intptr_t)(val) << 4)) +#define JS_MKPTR(tag, ptr) ((JSValue)((tag) | (intptr_t)(ptr))) +#define JS_VALUE_GET_NORM_TAG(v) ((int)((intptr_t)(v) & 15)) +#define JS_VALUE_GET_TAG(v) ((int)((intptr_t)(v) & 15)) +#define JS_VALUE_GET_SHORT_BIG_INT(v) JS_VALUE_GET_INT(v) +#define JS_VALUE_GET_PTR(v) ((void *)((intptr_t)(v) & ~15)) +#define JS_VALUE_GET_INT(v) ((int)((intptr_t)(v) >> 4)) +#define JS_VALUE_GET_BOOL(v) ((int)((intptr_t)(v) >> 4)) +#define JS_VALUE_GET_FLOAT64(v) ((double)((intptr_t)(v) >> 4)) +#define JS_TAG_IS_FLOAT64(tag) ((int)(tag) == JS_TAG_FLOAT64) +#define JS_NAN JS_MKVAL(JS_TAG_FLOAT64, 0) + +static inline JSValue __JS_NewFloat64(double d) +{ + return JS_MKVAL(JS_TAG_FLOAT64, (int)d); +} + +static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d) +{ + (void)&ctx; + return JS_MKVAL(JS_TAG_SHORT_BIG_INT, d); +} + +static inline bool JS_VALUE_IS_NAN(JSValue v) +{ + (void)&v; + return false; +} -#if defined(JS_NAN_BOXING) && JS_NAN_BOXING +#elif defined(JS_NAN_BOXING) && JS_NAN_BOXING typedef uint64_t JSValue; #define JS_VALUE_GET_TAG(v) (int)((v) >> 32) #define JS_VALUE_GET_INT(v) (int)(v) #define JS_VALUE_GET_BOOL(v) (int)(v) +#define JS_VALUE_GET_SHORT_BIG_INT(v) (int)(v) #define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v) #define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val)) @@ -150,6 +206,12 @@ static inline JSValue __JS_NewFloat64(double d) return v; } +static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d) +{ + (void)&ctx; + return JS_MKVAL(JS_TAG_SHORT_BIG_INT, d); +} + #define JS_TAG_IS_FLOAT64(tag) ((unsigned)((tag) - JS_TAG_FIRST) >= (JS_TAG_FLOAT64 - JS_TAG_FIRST)) /* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ @@ -176,6 +238,7 @@ typedef union JSValueUnion { int32_t int32; double float64; void *ptr; + int32_t short_big_int; } JSValueUnion; typedef struct JSValue { @@ -189,6 +252,7 @@ typedef struct JSValue { #define JS_VALUE_GET_INT(v) ((v).u.int32) #define JS_VALUE_GET_BOOL(v) ((v).u.int32) #define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) +#define JS_VALUE_GET_SHORT_BIG_INT(v) ((v).u.short_big_int) #define JS_VALUE_GET_PTR(v) ((v).u.ptr) /* msvc doesn't understand designated initializers without /std:c++20 */ @@ -210,7 +274,7 @@ static inline JSValue JS_MKVAL(int64_t tag, int32_t int32) static inline JSValue JS_MKNAN(void) { JSValue v; - v.u.float64 = JS_FLOAT64_NAN; + v.u.float64 = NAN; v.tag = JS_TAG_FLOAT64; return v; } @@ -221,7 +285,7 @@ static inline JSValue JS_MKNAN(void) #else #define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag } #define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } -#define JS_NAN (JSValue){ (JSValueUnion){ .float64 = JS_FLOAT64_NAN }, JS_TAG_FLOAT64 } +#define JS_NAN (JSValue){ (JSValueUnion){ .float64 = NAN }, JS_TAG_FLOAT64 } #endif #define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) @@ -234,6 +298,15 @@ static inline JSValue __JS_NewFloat64(double d) return v; } +static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int64_t d) +{ + (void)&ctx; + JSValue v; + v.tag = JS_TAG_SHORT_BIG_INT; + v.u.short_big_int = d; + return v; +} + static inline bool JS_VALUE_IS_NAN(JSValue v) { union { @@ -318,9 +391,9 @@ static inline bool JS_VALUE_IS_NAN(JSValue v) promise. Only allowed with JS_EVAL_TYPE_GLOBAL */ #define JS_EVAL_FLAG_ASYNC (1 << 7) -typedef JSValue JSCFunction(JSContext *ctx, JSValue this_val, int argc, JSValue *argv); -typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic); -typedef JSValue JSCFunctionData(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic, JSValue *func_data); +typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); +typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); +typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValueConst *func_data); typedef struct JSMallocFunctions { void *(*js_calloc)(void *opaque, size_t count, size_t size); @@ -381,9 +454,10 @@ JS_EXTERN void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque); JS_EXTERN int JS_AddRuntimeFinalizer(JSRuntime *rt, JSRuntimeFinalizer *finalizer, void *arg); typedef void JS_MarkFunc(JSRuntime *rt, JSGCObjectHeader *gp); -JS_EXTERN void JS_MarkValue(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func); +JS_EXTERN void JS_MarkValue(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); JS_EXTERN void JS_RunGC(JSRuntime *rt); -JS_EXTERN bool JS_IsLiveObject(JSRuntime *rt, JSValue obj); +JS_EXTERN bool JS_IsLiveObject(JSRuntime *rt, JSValueConst obj); JS_EXTERN JSContext *JS_NewContext(JSRuntime *rt); JS_EXTERN void JS_FreeContext(JSContext *s); @@ -411,17 +485,18 @@ JS_EXTERN void JS_AddIntrinsicPromise(JSContext *ctx); JS_EXTERN void JS_AddIntrinsicBigInt(JSContext *ctx); JS_EXTERN void JS_AddIntrinsicWeakRef(JSContext *ctx); JS_EXTERN void JS_AddPerformance(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicDOMException(JSContext *ctx); /* for equality comparisons and sameness */ -JS_EXTERN int JS_IsEqual(JSContext *ctx, JSValue op1, JSValue op2); -JS_EXTERN bool JS_IsStrictEqual(JSContext *ctx, JSValue op1, JSValue op2); -JS_EXTERN bool JS_IsSameValue(JSContext *ctx, JSValue op1, JSValue op2); +JS_EXTERN int JS_IsEqual(JSContext *ctx, JSValueConst op1, JSValueConst op2); +JS_EXTERN bool JS_IsStrictEqual(JSContext *ctx, JSValueConst op1, JSValueConst op2); +JS_EXTERN bool JS_IsSameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2); /* Similar to same-value equality, but +0 and -0 are considered equal. */ -JS_EXTERN bool JS_IsSameValueZero(JSContext *ctx, JSValue op1, JSValue op2); +JS_EXTERN bool JS_IsSameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2); /* Only used for running 262 tests. TODO(saghul) add build time flag. */ -JS_EXTERN JSValue js_string_codePointRange(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv); +JS_EXTERN JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); JS_EXTERN void *js_calloc_rt(JSRuntime *rt, size_t count, size_t size); JS_EXTERN void *js_malloc_rt(JSRuntime *rt, size_t size); @@ -470,8 +545,12 @@ JS_EXTERN void JS_FreeAtom(JSContext *ctx, JSAtom v); JS_EXTERN void JS_FreeAtomRT(JSRuntime *rt, JSAtom v); JS_EXTERN JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom); JS_EXTERN JSValue JS_AtomToString(JSContext *ctx, JSAtom atom); -JS_EXTERN const char *JS_AtomToCString(JSContext *ctx, JSAtom atom); -JS_EXTERN JSAtom JS_ValueToAtom(JSContext *ctx, JSValue val); +JS_EXTERN const char *JS_AtomToCStringLen(JSContext *ctx, size_t *plen, JSAtom atom); +static inline const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) +{ + return JS_AtomToCStringLen(ctx, NULL, atom); +} +JS_EXTERN JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val); /* object class support */ @@ -492,38 +571,37 @@ typedef struct JSClassExoticMethods { false if the property does not exists, true if it exists. If 1 is returned, the property descriptor 'desc' is filled if != NULL. */ int (*get_own_property)(JSContext *ctx, JSPropertyDescriptor *desc, - JSValue obj, JSAtom prop); + JSValueConst obj, JSAtom prop); /* '*ptab' should hold the '*plen' property keys. Return 0 if OK, -1 if exception. The 'is_enumerable' field is ignored. */ int (*get_own_property_names)(JSContext *ctx, JSPropertyEnum **ptab, - uint32_t *plen, - JSValue obj); + uint32_t *plen, JSValueConst obj); /* return < 0 if exception, or true/false */ - int (*delete_property)(JSContext *ctx, JSValue obj, JSAtom prop); + int (*delete_property)(JSContext *ctx, JSValueConst obj, JSAtom prop); /* return < 0 if exception or true/false */ - int (*define_own_property)(JSContext *ctx, JSValue this_obj, - JSAtom prop, JSValue val, - JSValue getter, JSValue setter, + int (*define_own_property)(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, int flags); /* The following methods can be emulated with the previous ones, so they are usually not needed */ /* return < 0 if exception or true/false */ - int (*has_property)(JSContext *ctx, JSValue obj, JSAtom atom); - JSValue (*get_property)(JSContext *ctx, JSValue obj, JSAtom atom, - JSValue receiver); + int (*has_property)(JSContext *ctx, JSValueConst obj, JSAtom atom); + JSValue (*get_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst receiver); /* return < 0 if exception or true/false */ - int (*set_property)(JSContext *ctx, JSValue obj, JSAtom atom, - JSValue value, JSValue receiver, int flags); + int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst value, JSValueConst receiver, int flags); } JSClassExoticMethods; -typedef void JSClassFinalizer(JSRuntime *rt, JSValue val); -typedef void JSClassGCMark(JSRuntime *rt, JSValue val, +typedef void JSClassFinalizer(JSRuntime *rt, JSValueConst val); +typedef void JSClassGCMark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); #define JS_CALL_FLAG_CONSTRUCTOR (1 << 0) -typedef JSValue JSClassCall(JSContext *ctx, JSValue func_obj, - JSValue this_val, int argc, JSValue *argv, - int flags); +typedef JSValue JSClassCall(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_val, int argc, + JSValueConst *argv, int flags); typedef struct JSClassDef { const char *class_name; /* pure ASCII only! */ @@ -540,12 +618,24 @@ typedef struct JSClassDef { JSClassExoticMethods *exotic; } JSClassDef; +#define JS_EVAL_OPTIONS_VERSION 1 + +typedef struct JSEvalOptions { + int version; + int eval_flags; + const char *filename; + int line_num; + // can add new fields in ABI-compatible manner by incrementing JS_EVAL_OPTIONS_VERSION +} JSEvalOptions; + #define JS_INVALID_CLASS_ID 0 JS_EXTERN JSClassID JS_NewClassID(JSRuntime *rt, JSClassID *pclass_id); /* Returns the class ID if `v` is an object, otherwise returns JS_INVALID_CLASS_ID. */ -JS_EXTERN JSClassID JS_GetClassID(JSValue v); +JS_EXTERN JSClassID JS_GetClassID(JSValueConst v); JS_EXTERN int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def); JS_EXTERN bool JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id); +/* Returns the class name or JS_ATOM_NULL if `id` is not a registered class. Must be freed with JS_FreeAtom. */ +JS_EXTERN JSAtom JS_GetClassName(JSRuntime *rt, JSClassID class_id); /* value handling */ @@ -599,166 +689,207 @@ JS_EXTERN JSValue JS_NewNumber(JSContext *ctx, double d); JS_EXTERN JSValue JS_NewBigInt64(JSContext *ctx, int64_t v); JS_EXTERN JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); -static inline bool JS_IsNumber(JSValue v) +static inline bool JS_IsNumber(JSValueConst v) { int tag = JS_VALUE_GET_TAG(v); return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag); } -static inline bool JS_IsBigInt(JSContext *ctx, JSValue v) +static inline bool JS_IsBigInt(JSValueConst v) { - (void)&ctx; - return JS_VALUE_GET_TAG(v) == JS_TAG_BIG_INT; + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT; } -static inline bool JS_IsBool(JSValue v) +static inline bool JS_IsBool(JSValueConst v) { return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL; } -static inline bool JS_IsNull(JSValue v) +static inline bool JS_IsNull(JSValueConst v) { return JS_VALUE_GET_TAG(v) == JS_TAG_NULL; } -static inline bool JS_IsUndefined(JSValue v) +static inline bool JS_IsUndefined(JSValueConst v) { return JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED; } -static inline bool JS_IsException(JSValue v) +static inline bool JS_IsException(JSValueConst v) { return JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION; } -static inline bool JS_IsUninitialized(JSValue v) +static inline bool JS_IsUninitialized(JSValueConst v) { return JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED; } -static inline bool JS_IsString(JSValue v) +static inline bool JS_IsString(JSValueConst v) { return JS_VALUE_GET_TAG(v) == JS_TAG_STRING; } -static inline bool JS_IsSymbol(JSValue v) +static inline bool JS_IsSymbol(JSValueConst v) { return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL; } -static inline bool JS_IsObject(JSValue v) +static inline bool JS_IsObject(JSValueConst v) { return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT; } +static inline bool JS_IsModule(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_MODULE; +} + JS_EXTERN JSValue JS_Throw(JSContext *ctx, JSValue obj); JS_EXTERN JSValue JS_GetException(JSContext *ctx); JS_EXTERN bool JS_HasException(JSContext *ctx); -JS_EXTERN bool JS_IsError(JSContext *ctx, JSValue val); -JS_EXTERN bool JS_IsUncatchableError(JSContext* ctx, JSValue val); -JS_EXTERN void JS_SetUncatchableError(JSContext *ctx, JSValue val); -JS_EXTERN void JS_ClearUncatchableError(JSContext *ctx, JSValue val); +JS_EXTERN bool JS_IsError(JSValueConst val); +JS_EXTERN bool JS_IsUncatchableError(JSValueConst val); +JS_EXTERN void JS_SetUncatchableError(JSContext *ctx, JSValueConst val); +JS_EXTERN void JS_ClearUncatchableError(JSContext *ctx, JSValueConst val); // Shorthand for: // JSValue exc = JS_GetException(ctx); // JS_ClearUncatchableError(ctx, exc); // JS_Throw(ctx, exc); JS_EXTERN void JS_ResetUncatchableError(JSContext *ctx); JS_EXTERN JSValue JS_NewError(JSContext *ctx); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_NewInternalError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_NewPlainError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_NewRangeError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_NewReferenceError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_NewSyntaxError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_NewTypeError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowInternalError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowPlainError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowRangeError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowReferenceError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowSyntaxError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowTypeError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); -JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowReferenceError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); -JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowRangeError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); -JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowInternalError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(3, 4) JS_ThrowDOMException(JSContext *ctx, const char *name, JS_PRINTF_FORMAT const char *fmt, ...); JS_EXTERN JSValue JS_ThrowOutOfMemory(JSContext *ctx); JS_EXTERN void JS_FreeValue(JSContext *ctx, JSValue v); JS_EXTERN void JS_FreeValueRT(JSRuntime *rt, JSValue v); -JS_EXTERN JSValue JS_DupValue(JSContext *ctx, JSValue v); -JS_EXTERN JSValue JS_DupValueRT(JSRuntime *rt, JSValue v); -JS_EXTERN int JS_ToBool(JSContext *ctx, JSValue val); /* return -1 for JS_EXCEPTION */ -static inline JSValue JS_ToBoolean(JSContext *ctx, JSValue val) +JS_EXTERN JSValue JS_DupValue(JSContext *ctx, JSValueConst v); +JS_EXTERN JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v); +JS_EXTERN int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */ +static inline JSValue JS_ToBoolean(JSContext *ctx, JSValueConst val) { return JS_NewBool(ctx, JS_ToBool(ctx, val)); } -JS_EXTERN JSValue JS_ToNumber(JSContext *ctx, JSValue val); -JS_EXTERN int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValue val); -static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValue val) +JS_EXTERN JSValue JS_ToNumber(JSContext *ctx, JSValueConst val); +JS_EXTERN int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val); +static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val) { return JS_ToInt32(ctx, (int32_t*)pres, val); } -JS_EXTERN int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValue val); -JS_EXTERN int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValue val); -JS_EXTERN int JS_ToFloat64(JSContext *ctx, double *pres, JSValue val); +JS_EXTERN int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val); +JS_EXTERN int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val); +JS_EXTERN int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val); /* return an exception if 'val' is a Number */ -JS_EXTERN int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValue val); -JS_EXTERN int JS_ToBigUint64(JSContext *ctx, uint64_t *pres, JSValue val); +JS_EXTERN int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val); +JS_EXTERN int JS_ToBigUint64(JSContext *ctx, uint64_t *pres, JSValueConst val); /* same as JS_ToInt64() but allow BigInt */ -JS_EXTERN int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValue val); +JS_EXTERN int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val); JS_EXTERN JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1); static inline JSValue JS_NewString(JSContext *ctx, const char *str) { return JS_NewStringLen(ctx, str, strlen(str)); } +// makes a copy of the input; does not check if the input is valid UTF-16, +// that is the responsibility of the caller +JS_EXTERN JSValue JS_NewTwoByteString(JSContext *ctx, const uint16_t *buf, + size_t len); JS_EXTERN JSValue JS_NewAtomString(JSContext *ctx, const char *str); -JS_EXTERN JSValue JS_ToString(JSContext *ctx, JSValue val); -JS_EXTERN JSValue JS_ToPropertyKey(JSContext *ctx, JSValue val); -JS_EXTERN const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValue val1, bool cesu8); -static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValue val1) +JS_EXTERN JSValue JS_ToString(JSContext *ctx, JSValueConst val); +JS_EXTERN JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val); +JS_EXTERN const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, bool cesu8); +static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValueConst val1) { return JS_ToCStringLen2(ctx, plen, val1, 0); } -static inline const char *JS_ToCString(JSContext *ctx, JSValue val1) +static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1) { return JS_ToCStringLen2(ctx, NULL, val1, 0); } JS_EXTERN void JS_FreeCString(JSContext *ctx, const char *ptr); -JS_EXTERN JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValue proto, JSClassID class_id); -JS_EXTERN JSValue JS_NewObjectClass(JSContext *ctx, int class_id); -JS_EXTERN JSValue JS_NewObjectProto(JSContext *ctx, JSValue proto); +JS_EXTERN JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, + JSClassID class_id); +JS_EXTERN JSValue JS_NewObjectClass(JSContext *ctx, JSClassID class_id); +JS_EXTERN JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto); JS_EXTERN JSValue JS_NewObject(JSContext *ctx); -JS_EXTERN JSValue JS_ToObject(JSContext *ctx, JSValue val); -JS_EXTERN JSValue JS_ToObjectString(JSContext *ctx, JSValue val); - -JS_EXTERN bool JS_IsFunction(JSContext* ctx, JSValue val); -JS_EXTERN bool JS_IsConstructor(JSContext* ctx, JSValue val); -JS_EXTERN bool JS_SetConstructorBit(JSContext *ctx, JSValue func_obj, bool val); - -JS_EXTERN bool JS_IsRegExp(JSValue val); -JS_EXTERN bool JS_IsMap(JSValue val); +// takes ownership of the values +JS_EXTERN JSValue JS_NewObjectFrom(JSContext *ctx, int count, + const JSAtom *props, + const JSValue *values); +// takes ownership of the values +JS_EXTERN JSValue JS_NewObjectFromStr(JSContext *ctx, int count, + const char **props, + const JSValue *values); +JS_EXTERN JSValue JS_ToObject(JSContext *ctx, JSValueConst val); +JS_EXTERN JSValue JS_ToObjectString(JSContext *ctx, JSValueConst val); + +JS_EXTERN bool JS_IsFunction(JSContext* ctx, JSValueConst val); +JS_EXTERN bool JS_IsConstructor(JSContext* ctx, JSValueConst val); +JS_EXTERN bool JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, bool val); + +JS_EXTERN bool JS_IsRegExp(JSValueConst val); +JS_EXTERN bool JS_IsMap(JSValueConst val); +JS_EXTERN bool JS_IsSet(JSValueConst val); +JS_EXTERN bool JS_IsWeakRef(JSValueConst val); +JS_EXTERN bool JS_IsWeakSet(JSValueConst val); +JS_EXTERN bool JS_IsWeakMap(JSValueConst val); +JS_EXTERN bool JS_IsDataView(JSValueConst val); JS_EXTERN JSValue JS_NewArray(JSContext *ctx); -JS_EXTERN int JS_IsArray(JSContext *ctx, JSValue val); +// takes ownership of the values +JS_EXTERN JSValue JS_NewArrayFrom(JSContext *ctx, int count, + const JSValue *values); +// reader beware: JS_IsArray used to "punch" through proxies and check +// if the target object is an array but it no longer does; use JS_IsProxy +// and JS_GetProxyTarget instead, and remember that the target itself can +// also be a proxy, ad infinitum +JS_EXTERN bool JS_IsArray(JSValueConst val); + +JS_EXTERN bool JS_IsProxy(JSValueConst val); +JS_EXTERN JSValue JS_GetProxyTarget(JSContext *ctx, JSValueConst proxy); +JS_EXTERN JSValue JS_GetProxyHandler(JSContext *ctx, JSValueConst proxy); JS_EXTERN JSValue JS_NewDate(JSContext *ctx, double epoch_ms); -JS_EXTERN bool JS_IsDate(JSValue v); +JS_EXTERN bool JS_IsDate(JSValueConst v); -JS_EXTERN JSValue JS_GetProperty(JSContext *ctx, JSValue this_obj, JSAtom prop); -JS_EXTERN JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue this_obj, +JS_EXTERN JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop); +JS_EXTERN JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, uint32_t idx); -JS_EXTERN JSValue JS_GetPropertyInt64(JSContext *ctx, JSValue this_obj, +JS_EXTERN JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst this_obj, int64_t idx); -JS_EXTERN JSValue JS_GetPropertyStr(JSContext *ctx, JSValue this_obj, +JS_EXTERN JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, const char *prop); -JS_EXTERN int JS_SetProperty(JSContext *ctx, JSValue this_obj, +JS_EXTERN int JS_SetProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue val); -JS_EXTERN int JS_SetPropertyUint32(JSContext *ctx, JSValue this_obj, +JS_EXTERN int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, uint32_t idx, JSValue val); -JS_EXTERN int JS_SetPropertyInt64(JSContext *ctx, JSValue this_obj, +JS_EXTERN int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, int64_t idx, JSValue val); -JS_EXTERN int JS_SetPropertyStr(JSContext *ctx, JSValue this_obj, +JS_EXTERN int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, const char *prop, JSValue val); -JS_EXTERN int JS_HasProperty(JSContext *ctx, JSValue this_obj, JSAtom prop); -JS_EXTERN int JS_IsExtensible(JSContext *ctx, JSValue obj); -JS_EXTERN int JS_PreventExtensions(JSContext *ctx, JSValue obj); -JS_EXTERN int JS_DeleteProperty(JSContext *ctx, JSValue obj, JSAtom prop, int flags); -JS_EXTERN int JS_SetPrototype(JSContext *ctx, JSValue obj, JSValue proto_val); -JS_EXTERN JSValue JS_GetPrototype(JSContext *ctx, JSValue val); -JS_EXTERN int JS_GetLength(JSContext *ctx, JSValue obj, int64_t *pres); -JS_EXTERN int JS_SetLength(JSContext *ctx, JSValue obj, int64_t len); -JS_EXTERN int JS_SealObject(JSContext *ctx, JSValue obj); -JS_EXTERN int JS_FreezeObject(JSContext *ctx, JSValue obj); +JS_EXTERN int JS_HasProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop); +JS_EXTERN int JS_IsExtensible(JSContext *ctx, JSValueConst obj); +JS_EXTERN int JS_PreventExtensions(JSContext *ctx, JSValueConst obj); +JS_EXTERN int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags); +JS_EXTERN int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val); +JS_EXTERN JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val); +JS_EXTERN int JS_GetLength(JSContext *ctx, JSValueConst obj, int64_t *pres); +JS_EXTERN int JS_SetLength(JSContext *ctx, JSValueConst obj, int64_t len); +JS_EXTERN int JS_SealObject(JSContext *ctx, JSValueConst obj); +JS_EXTERN int JS_FreezeObject(JSContext *ctx, JSValueConst obj); #define JS_GPN_STRING_MASK (1 << 0) #define JS_GPN_SYMBOL_MASK (1 << 1) @@ -769,70 +900,77 @@ JS_EXTERN int JS_FreezeObject(JSContext *ctx, JSValue obj); #define JS_GPN_SET_ENUM (1 << 5) JS_EXTERN int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, - uint32_t *plen, JSValue obj, int flags); + uint32_t *plen, JSValueConst obj, + int flags); JS_EXTERN int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, - JSValue obj, JSAtom prop); + JSValueConst obj, JSAtom prop); JS_EXTERN void JS_FreePropertyEnum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len); -JS_EXTERN JSValue JS_Call(JSContext *ctx, JSValue func_obj, JSValue this_obj, - int argc, JSValue *argv); -JS_EXTERN JSValue JS_Invoke(JSContext *ctx, JSValue this_val, JSAtom atom, - int argc, JSValue *argv); -JS_EXTERN JSValue JS_CallConstructor(JSContext *ctx, JSValue func_obj, - int argc, JSValue *argv); -JS_EXTERN JSValue JS_CallConstructor2(JSContext *ctx, JSValue func_obj, - JSValue new_target, - int argc, JSValue *argv); +JS_EXTERN JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, int argc, JSValueConst *argv); +JS_EXTERN JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, + int argc, JSValueConst *argv); +JS_EXTERN JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, + int argc, JSValueConst *argv); +JS_EXTERN JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv); /* Try to detect if the input is a module. Returns true if parsing the input * as a module produces no syntax errors. It's a naive approach that is not * wholly infallible: non-strict classic scripts may _parse_ okay as a module * but not _execute_ as one (different runtime semantics.) Use with caution. * |input| can be either ASCII or UTF-8 encoded source code. + * Returns false if QuickJS was built with -DQJS_DISABLE_PARSER. */ JS_EXTERN bool JS_DetectModule(const char *input, size_t input_len); /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ JS_EXTERN JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, const char *filename, int eval_flags); -/* same as JS_Eval() but with an explicit 'this_obj' parameter */ -JS_EXTERN JSValue JS_EvalThis(JSContext *ctx, JSValue this_obj, +JS_EXTERN JSValue JS_Eval2(JSContext *ctx, const char *input, size_t input_len, + JSEvalOptions *options); +JS_EXTERN JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, const char *input, size_t input_len, const char *filename, int eval_flags); +JS_EXTERN JSValue JS_EvalThis2(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + JSEvalOptions *options); JS_EXTERN JSValue JS_GetGlobalObject(JSContext *ctx); -JS_EXTERN int JS_IsInstanceOf(JSContext *ctx, JSValue val, JSValue obj); -JS_EXTERN int JS_DefineProperty(JSContext *ctx, JSValue this_obj, - JSAtom prop, JSValue val, - JSValue getter, JSValue setter, int flags); -JS_EXTERN int JS_DefinePropertyValue(JSContext *ctx, JSValue this_obj, +JS_EXTERN int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj); +JS_EXTERN int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags); +JS_EXTERN int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue val, int flags); -JS_EXTERN int JS_DefinePropertyValueUint32(JSContext *ctx, JSValue this_obj, +JS_EXTERN int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj, uint32_t idx, JSValue val, int flags); -JS_EXTERN int JS_DefinePropertyValueStr(JSContext *ctx, JSValue this_obj, +JS_EXTERN int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, const char *prop, JSValue val, int flags); -JS_EXTERN int JS_DefinePropertyGetSet(JSContext *ctx, JSValue this_obj, +JS_EXTERN int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue getter, JSValue setter, int flags); /* Only supported for custom classes, returns 0 on success < 0 otherwise. */ -JS_EXTERN int JS_SetOpaque(JSValue obj, void *opaque); -JS_EXTERN void *JS_GetOpaque(JSValue obj, JSClassID class_id); -JS_EXTERN void *JS_GetOpaque2(JSContext *ctx, JSValue obj, JSClassID class_id); -JS_EXTERN void *JS_GetAnyOpaque(JSValue obj, JSClassID *class_id); +JS_EXTERN int JS_SetOpaque(JSValueConst obj, void *opaque); +JS_EXTERN void *JS_GetOpaque(JSValueConst obj, JSClassID class_id); +JS_EXTERN void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id); +JS_EXTERN void *JS_GetAnyOpaque(JSValueConst obj, JSClassID *class_id); /* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */ JS_EXTERN JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, const char *filename); -JS_EXTERN JSValue JS_JSONStringify(JSContext *ctx, JSValue obj, - JSValue replacer, JSValue space0); +JS_EXTERN JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, + JSValueConst replacer, JSValueConst space0); typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr); JS_EXTERN JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, JSFreeArrayBufferDataFunc *free_func, void *opaque, bool is_shared); JS_EXTERN JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len); -JS_EXTERN void JS_DetachArrayBuffer(JSContext *ctx, JSValue obj); -JS_EXTERN uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValue obj); -JS_EXTERN bool JS_IsArrayBuffer(JSValue obj); -JS_EXTERN uint8_t *JS_GetUint8Array(JSContext *ctx, size_t *psize, JSValue obj); +JS_EXTERN void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj); +JS_EXTERN uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj); +JS_EXTERN bool JS_IsArrayBuffer(JSValueConst obj); +JS_EXTERN uint8_t *JS_GetUint8Array(JSContext *ctx, size_t *psize, JSValueConst obj); typedef enum JSTypedArrayEnum { JS_TYPED_ARRAY_UINT8C = 0, @@ -849,9 +987,9 @@ typedef enum JSTypedArrayEnum { JS_TYPED_ARRAY_FLOAT64, } JSTypedArrayEnum; -JS_EXTERN JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValue *argv, - JSTypedArrayEnum array_type); -JS_EXTERN JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValue obj, +JS_EXTERN JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv, + JSTypedArrayEnum array_type); +JS_EXTERN JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, size_t *pbyte_offset, size_t *pbyte_length, size_t *pbytes_per_element); @@ -859,7 +997,7 @@ JS_EXTERN JSValue JS_NewUint8Array(JSContext *ctx, uint8_t *buf, size_t len, JSFreeArrayBufferDataFunc *free_func, void *opaque, bool is_shared); /* returns -1 if not a typed array otherwise return a JSTypedArrayEnum value */ -JS_EXTERN int JS_GetTypedArrayType(JSValue obj); +JS_EXTERN int JS_GetTypedArrayType(JSValueConst obj); JS_EXTERN JSValue JS_NewUint8ArrayCopy(JSContext *ctx, const uint8_t *buf, size_t len); typedef struct { void *(*sab_alloc)(void *opaque, size_t size); @@ -876,15 +1014,33 @@ typedef enum JSPromiseStateEnum { } JSPromiseStateEnum; JS_EXTERN JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs); -JS_EXTERN JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise); -JS_EXTERN JSValue JS_PromiseResult(JSContext *ctx, JSValue promise); -JS_EXTERN bool JS_IsPromise(JSValue val); +JS_EXTERN JSPromiseStateEnum JS_PromiseState(JSContext *ctx, + JSValueConst promise); +JS_EXTERN JSValue JS_PromiseResult(JSContext *ctx, JSValueConst promise); +JS_EXTERN bool JS_IsPromise(JSValueConst val); JS_EXTERN JSValue JS_NewSymbol(JSContext *ctx, const char *description, bool is_global); +typedef enum JSPromiseHookType { + JS_PROMISE_HOOK_INIT, // emitted when a new promise is created + JS_PROMISE_HOOK_BEFORE, // runs right before promise.then is invoked + JS_PROMISE_HOOK_AFTER, // runs right after promise.then is invoked + JS_PROMISE_HOOK_RESOLVE, // not emitted for rejected promises +} JSPromiseHookType; + +// parent_promise is only passed in when type == JS_PROMISE_HOOK_INIT and +// is then either a promise object or JS_UNDEFINED if the new promise does +// not have a parent promise; only promises created with promise.then have +// a parent promise +typedef void JSPromiseHook(JSContext *ctx, JSPromiseHookType type, + JSValueConst promise, JSValueConst parent_promise, + void *opaque); +JS_EXTERN void JS_SetPromiseHook(JSRuntime *rt, JSPromiseHook promise_hook, + void *opaque); + /* is_handled = true means that the rejection is handled */ -typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValue promise, - JSValue reason, +typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise, + JSValueConst reason, bool is_handled, void *opaque); JS_EXTERN void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTracker *cb, void *opaque); @@ -894,7 +1050,7 @@ JS_EXTERN void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, voi /* if can_block is true, Atomics.wait() can be used */ JS_EXTERN void JS_SetCanBlock(JSRuntime *rt, bool can_block); /* set the [IsHTMLDDA] internal slot */ -JS_EXTERN void JS_SetIsHTMLDDA(JSContext *ctx, JSValue obj); +JS_EXTERN void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj); typedef struct JSModuleDef JSModuleDef; @@ -918,8 +1074,9 @@ JS_EXTERN JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m); /* JS Job support */ -typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValue *argv); -JS_EXTERN int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValue *argv); +typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValueConst *argv); +JS_EXTERN int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, + int argc, JSValueConst *argv); JS_EXTERN bool JS_IsJobPending(JSRuntime *rt); JS_EXTERN int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx); @@ -937,8 +1094,8 @@ typedef struct JSSABTab { #define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references to encode arbitrary object graph */ #define JS_WRITE_OBJ_STRIP_SOURCE (1 << 4) /* do not write source code information */ #define JS_WRITE_OBJ_STRIP_DEBUG (1 << 5) /* do not write debug information */ -JS_EXTERN uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValue obj, int flags); -JS_EXTERN uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValue obj, +JS_EXTERN uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, int flags); +JS_EXTERN uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, int flags, JSSABTab *psab_tab); #define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */ @@ -953,13 +1110,13 @@ JS_EXTERN JSValue JS_ReadObject2(JSContext *ctx, const uint8_t *buf, size_t buf_ JS_EXTERN JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj); /* load the dependencies of the module 'obj'. Useful when JS_ReadObject() returns a module. */ -JS_EXTERN int JS_ResolveModule(JSContext *ctx, JSValue obj); +JS_EXTERN int JS_ResolveModule(JSContext *ctx, JSValueConst obj); /* only exported for os.Worker() */ JS_EXTERN JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels); /* only exported for os.Worker() */ JS_EXTERN JSValue JS_LoadModule(JSContext *ctx, const char *basename, - const char *filename); + const char *filename); /* C function definition */ typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */ @@ -980,44 +1137,52 @@ typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */ typedef union JSCFunctionType { JSCFunction *generic; - JSValue (*generic_magic)(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic); + JSValue (*generic_magic)(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); JSCFunction *constructor; - JSValue (*constructor_magic)(JSContext *ctx, JSValue new_target, int argc, JSValue *argv, int magic); + JSValue (*constructor_magic)(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic); JSCFunction *constructor_or_func; double (*f_f)(double); double (*f_f_f)(double, double); - JSValue (*getter)(JSContext *ctx, JSValue this_val); - JSValue (*setter)(JSContext *ctx, JSValue this_val, JSValue val); - JSValue (*getter_magic)(JSContext *ctx, JSValue this_val, int magic); - JSValue (*setter_magic)(JSContext *ctx, JSValue this_val, JSValue val, int magic); - JSValue (*iterator_next)(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv, int *pdone, int magic); + JSValue (*getter)(JSContext *ctx, JSValueConst this_val); + JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val); + JSValue (*getter_magic)(JSContext *ctx, JSValueConst this_val, int magic); + JSValue (*setter_magic)(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic); + JSValue (*iterator_next)(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int *pdone, int magic); } JSCFunctionType; JS_EXTERN JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, const char *name, int length, JSCFunctionEnum cproto, int magic); +JS_EXTERN JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic, + JSValueConst proto_val); JS_EXTERN JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, int length, int magic, int data_len, - JSValue *data); - -static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name, - int length) + JSValueConst *data); +JS_EXTERN JSValue JS_NewCFunctionData2(JSContext *ctx, JSCFunctionData *func, + const char *name, + int length, int magic, int data_len, + JSValueConst *data); + +static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, + const char *name, int length) { return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0); } static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func, - const char *name, - int length, JSCFunctionEnum cproto, int magic) + const char *name, int length, + JSCFunctionEnum cproto, int magic) { /* Used to squelch a -Wcast-function-type warning. */ JSCFunctionType ft; ft.generic_magic = func; return JS_NewCFunction2(ctx, ft.generic, name, length, cproto, magic); } -JS_EXTERN void JS_SetConstructor(JSContext *ctx, JSValue func_obj, - JSValue proto); +JS_EXTERN void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, + JSValueConst proto); /* C property definition */ @@ -1082,7 +1247,7 @@ typedef struct JSCFunctionListEntry { #define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, { .alias = { from, -1 } } } #define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, { .alias = { from, base } } } -JS_EXTERN void JS_SetPropertyFunctionList(JSContext *ctx, JSValue obj, +JS_EXTERN int JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, const JSCFunctionListEntry *tab, int len); @@ -1102,23 +1267,18 @@ JS_EXTERN int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *exp JS_EXTERN int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, const JSCFunctionListEntry *tab, int len); + /* CUSTOM */ int JS_IsArrayBuffer2(JSContext *ctx, JSValueConst val); JSValue JS_NewString16(JSContext *ctx, const uint16_t *buf, int len); JSValue JS_GetPropertyInt64_2(JSContext *ctx, JSValueConst obj, int64_t idx); JSValue JS_WeakRef_Deref(JSContext *ctx, JSValueConst this_val); -JS_EXTERN void JS_SetGCBeforeCallback(JSRuntime *rt, int(*cb)(JSRuntime*)); -JS_EXTERN void JS_SetGCAfterCallback(JSRuntime *rt, void(*cb)(JSRuntime*)); - -typedef struct JSRefCountHeader { - int ref_count; -} JSRefCountHeader; /* Version */ #define QJS_VERSION_MAJOR 0 -#define QJS_VERSION_MINOR 8 +#define QJS_VERSION_MINOR 11 #define QJS_VERSION_PATCH 0 #define QJS_VERSION_SUFFIX "" diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/repl.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/repl.js old mode 100644 new mode 100755 index d7e302e5..43fc5a04 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/repl.js +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/repl.js @@ -1850,13 +1850,18 @@ import * as bjson from "qjs:bjson"; } function config_file(s) { - return (std.getenv("HOME") || std.getenv("USERPROFILE") || ".") + "/" + s; + var xdg_state_home = std.getenv("XDG_STATE_HOME"); + if (xdg_state_home && xdg_state_home[0] === "/") return xdg_state_home + "/" + s; + var home = std.getenv("HOME"); + if (os.platform === "linux" && home) xdg_state_home = home + "/.local/state"; + if (os.stat(xdg_state_home + "/")[1] === 0) return xdg_state_home + "/" + s; + return (home || std.getenv("USERPROFILE") || ".") + "/." + s; } function save_history() { var s = history.slice(-1000).join('\n').trim(); if (s) { try { - var f = std.open(config_file(".qjs_history"), "w"); + var f = std.open(config_file("qjs_history"), "w"); f.puts(s + '\n'); f.close(); } catch (e) { @@ -1864,7 +1869,7 @@ import * as bjson from "qjs:bjson"; } } function load_history() { - var a = std.loadFile(config_file(".qjs_history")); + var a = std.loadFile(config_file("qjs_history")); if (a) { history = a.trim().split('\n'); history_index = history.length; diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/run-test262.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/run-test262.c old mode 100644 new mode 100755 index 3b29b649..56e233c4 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/run-test262.c +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/run-test262.c @@ -35,12 +35,9 @@ #ifdef _WIN32 #include #include -typedef HANDLE js_thread_t; #else #include -#include #include -typedef pthread_t js_thread_t; #endif #include "cutils.h" @@ -405,13 +402,22 @@ static int add_test_file(const char *filename) static void find_test_files(const char *path); +static bool ispathsep(int c) +{ + return c == '/' || c == '\\'; +} + static void consider_test_file(const char *path, const char *name, int is_dir) { + size_t pathlen; char s[1024]; if (str_equal(name, ".") || str_equal(name, "..")) return; - snprintf(s, sizeof(s), "%s/%s", path, name); + pathlen = strlen(path); + while (pathlen > 0 && ispathsep(path[pathlen-1])) + pathlen--; + snprintf(s, sizeof(s), "%.*s/%s", (int)pathlen, path, name); if (is_dir) find_test_files(s); else @@ -459,12 +465,12 @@ static void enumerate_tests(const char *path) namelist_cmp_indirect); } -static JSValue js_print_262(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_print_262(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); const char *s; - JSValue v; + JSValueConst v; int i; for (i = 0; i < argc; i++) { @@ -473,9 +479,9 @@ static JSValue js_print_262(JSContext *ctx, JSValue this_val, // same logic as js_print in quickjs-libc.c if (local && !s && JS_IsObject(v)) { JS_FreeValue(ctx, JS_GetException(ctx)); - v = JS_ToObjectString(ctx, v); - s = JS_ToCString(ctx, v); - JS_FreeValue(ctx, v); + JSValue t = JS_ToObjectString(ctx, v); + s = JS_ToCString(ctx, t); + JS_FreeValue(ctx, t); } if (!s) return JS_EXCEPTION; @@ -500,15 +506,15 @@ static JSValue js_print_262(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_detachArrayBuffer(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_detachArrayBuffer(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JS_DetachArrayBuffer(ctx, argv[0]); return JS_UNDEFINED; } -static JSValue js_evalScript_262(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_evalScript_262(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { const char *str; size_t len; @@ -521,49 +527,6 @@ static JSValue js_evalScript_262(JSContext *ctx, JSValue this_val, return ret; } -static void start_thread(js_thread_t *thrd, void *(*start)(void *), void *arg) -{ - // musl libc gives threads 80 kb stacks, much smaller than - // JS_DEFAULT_STACK_SIZE (1 MB) - static const unsigned stacksize = 2 << 20; // 2 MB, glibc default -#ifdef _WIN32 - HANDLE h, cp; - - cp = GetCurrentProcess(); - h = (HANDLE)_beginthread((void (*)(void *))(void *)start, stacksize, arg); - if (!h) - fatal(1, "_beginthread error"); - // _endthread() automatically closes the handle but we want to wait on - // it so make a copy. Race-y for very short-lived threads. Can be solved - // by switching to _beginthreadex(CREATE_SUSPENDED) but means changing - // |start| from __cdecl to __stdcall. - if (!DuplicateHandle(cp, h, cp, thrd, 0, false, DUPLICATE_SAME_ACCESS)) - fatal(1, "DuplicateHandle error"); -#else - pthread_attr_t attr; - - if (pthread_attr_init(&attr)) - fatal(1, "pthread_attr_init"); - if (pthread_attr_setstacksize(&attr, stacksize)) - fatal(1, "pthread_attr_setstacksize"); - if (pthread_create(thrd, &attr, start, arg)) - fatal(1, "pthread_create error"); - pthread_attr_destroy(&attr); -#endif -} - -static void join_thread(js_thread_t thrd) -{ -#ifdef _WIN32 - if (WaitForSingleObject(thrd, INFINITE)) - fatal(1, "WaitForSingleObject error"); - CloseHandle(thrd); -#else - if (pthread_join(thrd, NULL)) - fatal(1, "pthread_join error"); -#endif -} - static long cpu_count(void) { #ifdef _WIN32 @@ -612,7 +575,7 @@ typedef struct { static JSValue add_helpers1(JSContext *ctx); static void add_helpers(JSContext *ctx); -static void *agent_start(void *arg) +static void agent_start(void *arg) { ThreadLocalStorage *tls; Test262Agent *agent; @@ -673,7 +636,7 @@ static void *agent_start(void *arg) NULL, NULL, true); args[1] = JS_NewInt32(ctx, agent->broadcast_val); ret_val = JS_Call(ctx, agent->broadcast_func, JS_UNDEFINED, - 2, args); + 2, (JSValueConst *)args); JS_FreeValue(ctx, args[0]); JS_FreeValue(ctx, args[1]); if (JS_IsException(ret_val)) @@ -688,11 +651,10 @@ static void *agent_start(void *arg) JS_FreeContext(ctx); JS_FreeRuntime(rt); - return NULL; } -static JSValue js_agent_start(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_agent_start(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); const char *script; @@ -712,7 +674,7 @@ static JSValue js_agent_start(JSContext *ctx, JSValue this_val, agent->tls = tls; JS_FreeCString(ctx, script); list_add_tail(&agent->link, &tls->agent_list); - start_thread(&agent->tid, agent_start, agent); + js_thread_create(&agent->tid, agent_start, agent, /*flags*/0); return JS_UNDEFINED; } @@ -724,15 +686,15 @@ static void js_agent_free(JSContext *ctx) list_for_each_safe(el, el1, &tls->agent_list) { agent = list_entry(el, Test262Agent, link); - join_thread(agent->tid); + js_thread_join(agent->tid); JS_FreeValue(ctx, agent->broadcast_sab); list_del(&agent->link); free(agent); } } -static JSValue js_agent_leaving(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_agent_leaving(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { Test262Agent *agent = JS_GetContextOpaque(ctx); if (!agent) @@ -753,11 +715,11 @@ static bool is_broadcast_pending(ThreadLocalStorage *tls) return false; } -static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_agent_broadcast(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); - JSValue sab = argv[0]; + JSValueConst sab = argv[0]; struct list_head *el; Test262Agent *agent; uint8_t *buf; @@ -795,8 +757,8 @@ static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_agent_receiveBroadcast(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_agent_receiveBroadcast(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { Test262Agent *agent = JS_GetContextOpaque(ctx); if (!agent) @@ -808,8 +770,8 @@ static JSValue js_agent_receiveBroadcast(JSContext *ctx, JSValue this_val, return JS_UNDEFINED; } -static JSValue js_agent_sleep(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_agent_sleep(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { uint32_t duration; if (JS_ToUint32(ctx, &duration, argv[0])) @@ -833,14 +795,14 @@ static int64_t get_clock_ms(void) #endif } -static JSValue js_agent_monotonicNow(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_agent_monotonicNow(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return JS_NewInt64(ctx, get_clock_ms()); } -static JSValue js_agent_getReport(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_agent_getReport(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); AgentReport *rep; @@ -864,8 +826,8 @@ static JSValue js_agent_getReport(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_agent_report(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_agent_report(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); const char *str; @@ -907,8 +869,8 @@ static JSValue js_new_agent(JSContext *ctx) return agent; } -static JSValue js_createRealm(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_createRealm(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSContext *ctx1; JSValue ret; @@ -922,8 +884,8 @@ static JSValue js_createRealm(JSContext *ctx, JSValue this_val, return ret; } -static JSValue js_IsHTMLDDA(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) +static JSValue js_IsHTMLDDA(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { return JS_NULL; } @@ -931,36 +893,33 @@ static JSValue js_IsHTMLDDA(JSContext *ctx, JSValue this_val, static JSValue add_helpers1(JSContext *ctx) { JSValue global_obj; - JSValue obj262, obj; + JSValue obj262, is_html_dda; global_obj = JS_GetGlobalObject(ctx); JS_SetPropertyStr(ctx, global_obj, "print", JS_NewCFunction(ctx, js_print_262, "print", 1)); + is_html_dda = JS_NewCFunction(ctx, js_IsHTMLDDA, "IsHTMLDDA", 0); + JS_SetIsHTMLDDA(ctx, is_html_dda); +#define N 7 + static const char *props[N] = { + "detachArrayBuffer", "evalScript", "codePointRange", + "agent", "global", "createRealm", "IsHTMLDDA", + }; + JSValue values[N] = { + JS_NewCFunction(ctx, js_detachArrayBuffer, "detachArrayBuffer", 1), + JS_NewCFunction(ctx, js_evalScript_262, "evalScript", 1), + JS_NewCFunction(ctx, js_string_codePointRange, "codePointRange", 2), + js_new_agent(ctx), + JS_DupValue(ctx, global_obj), + JS_NewCFunction(ctx, js_createRealm, "createRealm", 0), + is_html_dda, + }; /* $262 special object used by the tests */ - obj262 = JS_NewObject(ctx); - JS_SetPropertyStr(ctx, obj262, "detachArrayBuffer", - JS_NewCFunction(ctx, js_detachArrayBuffer, - "detachArrayBuffer", 1)); - JS_SetPropertyStr(ctx, obj262, "evalScript", - JS_NewCFunction(ctx, js_evalScript_262, - "evalScript", 1)); - JS_SetPropertyStr(ctx, obj262, "codePointRange", - JS_NewCFunction(ctx, js_string_codePointRange, - "codePointRange", 2)); - JS_SetPropertyStr(ctx, obj262, "agent", js_new_agent(ctx)); - - JS_SetPropertyStr(ctx, obj262, "global", - JS_DupValue(ctx, global_obj)); - JS_SetPropertyStr(ctx, obj262, "createRealm", - JS_NewCFunction(ctx, js_createRealm, - "createRealm", 0)); - obj = JS_NewCFunction(ctx, js_IsHTMLDDA, "IsHTMLDDA", 0); - JS_SetIsHTMLDDA(ctx, obj); - JS_SetPropertyStr(ctx, obj262, "IsHTMLDDA", obj); - + obj262 = JS_NewObjectFromStr(ctx, N, props, values); JS_SetPropertyStr(ctx, global_obj, "$262", JS_DupValue(ctx, obj262)); +#undef N JS_FreeValue(ctx, global_obj); return obj262; @@ -1065,8 +1024,9 @@ void update_exclude_dirs(void) namelist_t *lp = &test_list; namelist_t *ep = &exclude_list; namelist_t *dp = &exclude_dir_list; - char *name; + char *name, *path; int i, j, count; + size_t include, exclude; /* split directpries from exclude_list */ for (count = i = 0; i < ep->count; i++) { @@ -1085,15 +1045,19 @@ void update_exclude_dirs(void) /* filter out excluded directories */ for (count = i = 0; i < lp->count; i++) { name = lp->array[i]; + include = exclude = 0; for (j = 0; j < dp->count; j++) { - if (has_prefix(name, dp->array[j])) { - test_excluded++; - free(name); - name = NULL; - break; - } + path = dp->array[j]; + if (has_prefix(name, path)) + exclude = strlen(path); + if (*path == '!' && has_prefix(name, &path[1])) + include = strlen(&path[1]); } - if (name) { + // most specific include/exclude wins + if (exclude > include) { + test_excluded++; + free(name); + } else { lp->array[count++] = name; } } @@ -1425,8 +1389,8 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len, if (JS_IsException(res_val)) { exception_val = JS_GetException(ctx); - is_error = JS_IsError(ctx, exception_val); - js_print_262(ctx, JS_NULL, 1, &exception_val); + is_error = JS_IsError(exception_val); + js_print_262(ctx, JS_NULL, 1, (JSValueConst *)&exception_val); if (is_error) { JSValue name, stack; const char *stack_str; @@ -1546,7 +1510,7 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len, } } } - if (is_unexpected_error && verbose > 1) { + if (is_unexpected_error && verbose > 1 && JS_IsException(exception_val)) { JSValue val = JS_GetPropertyStr(ctx, exception_val, "stack"); if (!JS_IsException(val) && !JS_IsUndefined(val) && @@ -1736,15 +1700,32 @@ void update_stats(JSRuntime *rt, const char *filename) { js_mutex_unlock(&stats_mutex); } +static JSValue qjs_black_box(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst argv[], int magic) +{ + return JS_NewInt32(ctx, js_std_cmd(magic, ctx, &argv[0])); +} + +static const JSCFunctionListEntry qjs_methods[] = { + JS_CFUNC_MAGIC_DEF("getStringKind", 1, qjs_black_box, /*GetStringKind*/3), +}; + +static const JSCFunctionListEntry qjs_object = + JS_OBJECT_DEF("qjs", qjs_methods, countof(qjs_methods), JS_PROP_C_W_E); + JSContext *JS_NewCustomContext(JSRuntime *rt) { JSContext *ctx; + JSValue obj; ctx = JS_NewContext(rt); if (ctx && local) { js_init_module_std(ctx, "qjs:std"); js_init_module_os(ctx, "qjs:os"); js_init_module_bjson(ctx, "qjs:bjson"); + obj = JS_GetGlobalObject(ctx); + JS_SetPropertyFunctionList(ctx, obj, &qjs_object, 1); + JS_FreeValue(ctx, obj); } return ctx; } @@ -1752,7 +1733,8 @@ JSContext *JS_NewCustomContext(JSRuntime *rt) int run_test_buf(ThreadLocalStorage *tls, const char *filename, char *harness, namelist_t *ip, char *buf, size_t buf_len, const char* error_type, int eval_flags, bool is_negative, - bool is_async, bool can_block, int *msec) + bool is_async, bool can_block, bool track_promise_rejections, + int *msec) { JSRuntime *rt; JSContext *ctx; @@ -1762,6 +1744,7 @@ int run_test_buf(ThreadLocalStorage *tls, const char *filename, char *harness, if (rt == NULL) { fatal(1, "JS_NewRuntime failure"); } + JS_SetDumpFlags(rt, JS_DUMP_LEAKS); JS_SetRuntimeOpaque(rt, tls); js_std_init_handlers(rt); ctx = JS_NewCustomContext(rt); @@ -1776,6 +1759,9 @@ int run_test_buf(ThreadLocalStorage *tls, const char *filename, char *harness, /* loader for ES6 modules */ JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, (void *) filename); + if (track_promise_rejections) + JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, NULL); + add_helpers(ctx); for (i = 0; i < ip->count; i++) { @@ -1822,6 +1808,7 @@ int run_test(ThreadLocalStorage *tls, const char *filename, int *msec) int ret, eval_flags, use_strict, use_nostrict; bool is_negative, is_nostrict, is_onlystrict, is_async, is_module, skip; bool detect_module = true; + bool track_promise_rejections = false; bool can_block; namelist_t include_list = { 0 }, *ip = &include_list; @@ -1880,6 +1867,9 @@ int run_test(ThreadLocalStorage *tls, const char *filename, int *msec) else if (str_equal(option, "qjs:no-detect-module")) { detect_module = false; } + else if (str_equal(option, "qjs:track-promise-rejections")) { + track_promise_rejections = true; + } else if (str_equal(option, "module")) { is_module = true; skip |= skip_module; @@ -1974,12 +1964,13 @@ int run_test(ThreadLocalStorage *tls, const char *filename, int *msec) if (use_nostrict) { ret = run_test_buf(tls, filename, harness, ip, buf, buf_len, error_type, eval_flags, is_negative, is_async, - can_block, msec); + can_block, track_promise_rejections, msec); } if (use_strict) { ret |= run_test_buf(tls, filename, harness, ip, buf, buf_len, error_type, eval_flags | JS_EVAL_FLAG_STRICT, - is_negative, is_async, can_block, msec); + is_negative, is_async, can_block, + track_promise_rejections, msec); } } namelist_free(&include_list); @@ -2007,6 +1998,7 @@ int run_test262_harness_test(ThreadLocalStorage *tls, const char *filename, if (rt == NULL) { fatal(1, "JS_NewRuntime failure"); } + JS_SetDumpFlags(rt, JS_DUMP_LEAKS); JS_SetRuntimeOpaque(rt, tls); ctx = JS_NewContext(rt); if (ctx == NULL) { @@ -2072,7 +2064,7 @@ int run_test262_harness_test(ThreadLocalStorage *tls, const char *filename, clock_t last_clock; -void *show_progress(void *unused) { +void show_progress(void *unused) { int interval = 1000*1000*1000 / 4; // 250 ms js_mutex_lock(&progress_mutex); @@ -2085,7 +2077,6 @@ void *show_progress(void *unused) { fflush(stderr); } js_mutex_unlock(&progress_mutex); - return NULL; } enum { INCLUDE, EXCLUDE, SKIP }; @@ -2101,7 +2092,7 @@ int include_exclude_or_skip(int i) // naming is hard... return INCLUDE; } -void *run_test_dir_list(void *arg) +void run_test_dir_list(void *arg) { ThreadLocalStorage tls_s, *tls = &tls_s; const char *p; @@ -2118,7 +2109,6 @@ void *run_test_dir_list(void *arg) if (verbose > 1 || (slow_test_threshold && msec >= slow_test_threshold)) fprintf(stderr, "%s (%d ms)\n", p, msec); } - return NULL; } void help(void) @@ -2163,6 +2153,7 @@ int main(int argc, char **argv) const char *ignore = ""; bool is_test262_harness = false; bool is_module = false; + bool enable_progress = true; js_std_set_worker_new_context_func(JS_NewCustomContext); @@ -2265,6 +2256,12 @@ int main(int argc, char **argv) update_exclude_dirs(); +#ifndef _WIN32 + if (!isatty(STDOUT_FILENO)) { + enable_progress = false; + } +#endif + if (is_dir_list) { if (optind < argc && !isdigit((unsigned char)argv[optind][0])) { filename = argv[optind++]; @@ -2293,15 +2290,21 @@ int main(int argc, char **argv) } js_cond_init(&progress_cond); js_mutex_init(&progress_mutex); - start_thread(&progress_thread, show_progress, NULL); - for (i = 0; i < nthreads; i++) - start_thread(&threads[i], run_test_dir_list, (void *)(uintptr_t)i); + if (enable_progress) { + js_thread_create(&progress_thread, show_progress, NULL, /*flags*/0); + } + for (i = 0; i < nthreads; i++) { + js_thread_create(&threads[i], run_test_dir_list, + (void *)(uintptr_t)i, /*flags*/0); + } for (i = 0; i < nthreads; i++) - join_thread(threads[i]); + js_thread_join(threads[i]); js_mutex_lock(&progress_mutex); js_cond_signal(&progress_cond); js_mutex_unlock(&progress_mutex); - join_thread(progress_thread); + if (enable_progress) { + js_thread_join(progress_thread); + } js_mutex_destroy(&progress_mutex); js_cond_destroy(&progress_cond); } else { diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/standalone.js b/test-app/runtime/src/main/cpp/napi/quickjs/source/standalone.js old mode 100644 new mode 100755 index 912e524d..59dac958 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/standalone.js +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/standalone.js @@ -48,7 +48,7 @@ export function compileStandalone(inFile, outFile, targetExe) { const bytecode = new Uint8Array(bjson.write(code, JS_WRITE_OBJ_BYTECODE | JS_WRITE_OBJ_REFERENCE | JS_WRITE_OBJ_STRIP_SOURCE)); // Step 2: copy the bytecode to the end of the executable and add a marker. - const exeFileName = targetExe ?? globalThis.argv0; + const exeFileName = targetExe ?? os.exePath() ?? globalThis.argv0; const exe = std.loadFile(exeFileName, { binary: true }); if (!exe) { @@ -80,7 +80,7 @@ export function compileStandalone(inFile, outFile, targetExe) { } export function runStandalone() { - const file = globalThis.argv0; + const file = os.exePath() ?? globalThis.argv0; const exe = std.open(file, 'rb'); if (!exe) { diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/test262-fast.conf b/test-app/runtime/src/main/cpp/napi/quickjs/source/test262-fast.conf old mode 100644 new mode 100755 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/test262.conf b/test-app/runtime/src/main/cpp/napi/quickjs/source/test262.conf old mode 100644 new mode 100755 index 6cde7b8f..130355a9 --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/test262.conf +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/test262.conf @@ -48,10 +48,10 @@ __proto__ __setter__ AggregateError align-detached-buffer-semantics-with-web-reality -arbitrary-module-namespace-names=skip +arbitrary-module-namespace-names array-find-from-last array-grouping -Array.fromAsync=skip +Array.fromAsync Array.prototype.at Array.prototype.flat Array.prototype.flatMap @@ -103,7 +103,6 @@ explicit-resource-management=skip exponentiation export-star-as-namespace-from-module FinalizationRegistry -FinalizationRegistry.prototype.cleanupSome=skip Float16Array Float32Array Float64Array @@ -113,6 +112,7 @@ generators globalThis hashbang host-gc-required=skip +immutable-arraybuffer=skip import-assertions=skip import-attributes=skip import-defer=skip @@ -139,7 +139,7 @@ Intl.RelativeTimeFormat=skip Intl.Segmenter=skip IsHTMLDDA iterator-helpers -iterator-sequencing=skip +iterator-sequencing json-modules=skip json-parse-with-source=skip json-superset @@ -226,6 +226,7 @@ Uint32Array Uint8Array uint8array-base64=skip Uint8ClampedArray +upsert WeakMap WeakRef WeakSet diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests.conf b/test-app/runtime/src/main/cpp/napi/quickjs/source/tests.conf old mode 100644 new mode 100755 index b006e166..7a2afc6f --- a/test-app/runtime/src/main/cpp/napi/quickjs/source/tests.conf +++ b/test-app/runtime/src/main/cpp/napi/quickjs/source/tests.conf @@ -8,3 +8,4 @@ tests/empty.js tests/fixture_cyclic_import.js tests/microbench.js tests/test_worker_module.js +tests/fixture_string_exports.js diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/unicode_gen.c b/test-app/runtime/src/main/cpp/napi/quickjs/source/unicode_gen.c old mode 100644 new mode 100755 diff --git a/test-app/runtime/src/main/cpp/napi/quickjs/source/unicode_gen_def.h b/test-app/runtime/src/main/cpp/napi/quickjs/source/unicode_gen_def.h old mode 100644 new mode 100755 diff --git a/test-app/runtime/src/main/cpp/napi/v8/jsr.cpp b/test-app/runtime/src/main/cpp/napi/v8/jsr.cpp index b1475342..48b382fe 100644 --- a/test-app/runtime/src/main/cpp/napi/v8/jsr.cpp +++ b/test-app/runtime/src/main/cpp/napi/v8/jsr.cpp @@ -7,12 +7,14 @@ #include #include "v8-fast-api-calls.h" #include "NativeScriptAssert.h" +#include "native_api_util.h" using namespace v8; using namespace tns; tns::SimpleAllocator g_allocator; -JSR::JSR(): isolate(nullptr) { + +JSR::JSR() : isolate(nullptr) { v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = &g_allocator; @@ -24,9 +26,10 @@ JSR::JSR(): isolate(nullptr) { } isolate = v8::Isolate::New(create_params); } + std::unique_ptr JSR::platform = nullptr; bool JSR::s_mainThreadInitialized = false; -std::unordered_map JSR::env_to_jsr_cache; +std::unordered_map JSR::env_to_jsr_cache; napi_status js_create_runtime(napi_runtime *runtime) { if (!runtime) return napi_invalid_arg; @@ -35,7 +38,7 @@ napi_status js_create_runtime(napi_runtime *runtime) { return napi_ok; } -napi_status js_set_runtime_flags(const char* flags) { +napi_status js_set_runtime_flags(const char *flags) { V8::V8::SetFlagsFromString(flags); return napi_ok; } @@ -62,9 +65,9 @@ napi_status js_unlock_env(napi_env env) { return napi_ok; } -napi_status js_create_napi_env(napi_env* env, napi_runtime runtime) { +napi_status js_create_napi_env(napi_env *env, napi_runtime runtime) { if (env == nullptr) return napi_invalid_arg; - JSR* jsr = (JSR*) runtime; + JSR *jsr = (JSR *) runtime; // Must enter explictly #ifdef __V8_13__ jsr->isolate->Enter(); @@ -74,6 +77,32 @@ napi_status js_create_napi_env(napi_env* env, napi_runtime runtime) { v8::Local context = v8::Context::New(jsr->isolate); *env = new napi_env__(context, NAPI_VERSION_EXPERIMENTAL); JSR::env_to_jsr_cache.insert(std::make_pair(*env, jsr)); + + Local global = context->Global(); + global->Set(context, + String::NewFromUtf8(jsr->isolate, "__promiseUnhandledEvent").ToLocalChecked(), + Integer::New(jsr->isolate, PromiseRejectEvent::kPromiseRejectWithNoHandler)); + global->Set(context, + String::NewFromUtf8(jsr->isolate, "__promiseHandledEvent").ToLocalChecked(), + Integer::New(jsr->isolate, + PromiseRejectEvent::kPromiseHandlerAddedAfterReject)); + + jsr->isolate->SetPromiseRejectCallback([](PromiseRejectMessage message) { + Local promise = message.GetPromise(); + Isolate *isolate = promise->GetIsolate(); + Local value = message.GetValue(); + Local event = Integer::New(isolate, message.GetEvent()); + v8::HandleScope handle_scope(isolate); + v8::Local context = isolate->GetCurrentContext(); + Local global = context->Global(); + Local callback = global->Get(context, String::NewFromUtf8(isolate, + "onUnhandledPromiseRejectionTracker").ToLocalChecked()).ToLocalChecked(); + if (value.IsEmpty()) + value = Undefined(isolate); + Local args[] = {event, promise, value}; + callback.As()->Call(context, global, 3, args); + }); + // Must exit explictly #ifdef __V8_13__ jsr->isolate->Exit(); @@ -90,11 +119,11 @@ napi_status js_create_napi_env(napi_env* env, napi_runtime runtime) { napi_status js_free_napi_env(napi_env env) { if (env == nullptr) return napi_invalid_arg; env->DeleteMe(); - return napi_ok; + return napi_ok; } napi_status js_free_runtime(napi_runtime runtime) { - JSR* jsr = (JSR*) runtime; + JSR *jsr = (JSR *) runtime; jsr->isolate->Dispose(); delete jsr; return napi_ok; @@ -118,23 +147,26 @@ napi_status js_get_engine_ptr(napi_env env, int64_t *engine_ptr) { return napi_ok; } -napi_status js_adjust_external_memory(napi_env env, int64_t changeInBytes, int64_t* externalMemory) { +napi_status +js_adjust_external_memory(napi_env env, int64_t changeInBytes, int64_t *externalMemory) { *externalMemory = env->isolate->AdjustAmountOfExternalAllocatedMemory(changeInBytes); return napi_ok; } napi_status js_cache_script(napi_env env, const char *source, const char *file) { - v8::Local sourceString = v8::String::NewFromUtf8(env->isolate, source).ToLocalChecked(); + v8::Local sourceString = v8::String::NewFromUtf8(env->isolate, + source).ToLocalChecked(); v8::Local fileString = v8::String::NewFromUtf8(env->isolate, file).ToLocalChecked(); #ifdef __V8_13__ v8::ScriptOrigin origin(fileString); #else - v8::ScriptOrigin origin(env->isolate,fileString); + v8::ScriptOrigin origin(env->isolate, fileString); #endif - v8::Local script = v8::Script::Compile(env->context(),sourceString, &origin).ToLocalChecked(); + v8::Local script = v8::Script::Compile(env->context(), sourceString, + &origin).ToLocalChecked(); Local unboundScript = script->GetUnboundScript(); - ScriptCompiler::CachedData* cachedData = ScriptCompiler::CreateCodeCache(unboundScript); + ScriptCompiler::CachedData *cachedData = ScriptCompiler::CreateCodeCache(unboundScript); int length = cachedData->length; auto cachePath = std::string(file) + ".cache"; @@ -154,7 +186,8 @@ napi_status js_cache_script(napi_env env, const char *source, const char *file) return napi_ok; } -napi_status js_run_cached_script(napi_env env, const char * file, napi_value script, void* cache, napi_value *result) { +napi_status js_run_cached_script(napi_env env, const char *file, napi_value script, void *cache, + napi_value *result) { auto cachePath = std::string(file) + ".cache"; struct stat s_result; if (stat(cachePath.c_str(), &s_result) == 0) { @@ -175,7 +208,8 @@ napi_status js_run_cached_script(napi_env env, const char * file, napi_value scr return napi_cannot_run_js; } - auto * cacheData = new ScriptCompiler::CachedData(reinterpret_cast(data), length, ScriptCompiler::CachedData::BufferOwned); + auto *cacheData = new ScriptCompiler::CachedData(reinterpret_cast(data), length, + ScriptCompiler::CachedData::BufferOwned); std::string filePath = std::string("file://") + file; auto fullRequiredModulePathWithSchema = v8::String::NewFromUtf8(env->isolate, filePath.c_str()); @@ -183,11 +217,11 @@ napi_status js_run_cached_script(napi_env env, const char * file, napi_value scr #ifdef __V8_13__ v8::ScriptOrigin origin(fullRequiredModulePathWithSchema.ToLocalChecked()); #else - v8::ScriptOrigin origin(env->isolate,fullRequiredModulePathWithSchema.ToLocalChecked()); + v8::ScriptOrigin origin(env->isolate, fullRequiredModulePathWithSchema.ToLocalChecked()); #endif v8::Local scriptText; - memcpy(static_cast(&scriptText), &script, sizeof(script)); + memcpy(static_cast(&scriptText), &script, sizeof(script)); TryCatch tc(env->isolate); @@ -206,7 +240,7 @@ napi_status js_run_cached_script(napi_env env, const char * file, napi_value scr return napi_ok; } -napi_status js_get_runtime_version(napi_env env, napi_value* version) { +napi_status js_get_runtime_version(napi_env env, napi_value *version) { napi_create_string_utf8(env, "V8", NAPI_AUTO_LENGTH, version);