├── .clang-format ├── .github └── workflows │ └── test.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── NOTICE ├── README.md ├── package.json ├── src └── js.c └── test └── CMakeLists.txt /.clang-format: -------------------------------------------------------------------------------- 1 | AlignAfterOpenBracket: BlockIndent 2 | AlignConsecutiveMacros: Consecutive 3 | AlignEscapedNewlines: DontAlign 4 | AllowShortIfStatementsOnASingleLine: AllIfsAndElse 5 | AlwaysBreakAfterReturnType: All 6 | BinPackArguments: false 7 | BinPackParameters: false 8 | ContinuationIndentWidth: 2 9 | ColumnLimit: 0 10 | SpaceAfterCStyleCast: true 11 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | jobs: 10 | test: 11 | strategy: 12 | matrix: 13 | include: 14 | - os: ubuntu-22.04 15 | platform: linux 16 | arch: x64 17 | - os: ubuntu-22.04-arm 18 | platform: linux 19 | arch: arm64 20 | - os: ubuntu-22.04 21 | platform: android 22 | arch: x64 23 | - os: ubuntu-22.04 24 | platform: android 25 | arch: ia32 26 | - os: ubuntu-22.04 27 | platform: android 28 | arch: arm64 29 | - os: ubuntu-22.04 30 | platform: android 31 | arch: arm 32 | - os: macos-14 33 | platform: darwin 34 | arch: x64 35 | flags: --sanitize address 36 | - os: macos-14 37 | platform: darwin 38 | arch: arm64 39 | flags: --sanitize address 40 | - os: macos-14 41 | platform: ios 42 | arch: arm64 43 | - os: macos-14 44 | platform: ios 45 | arch: arm64 46 | tags: -simulator 47 | flags: --simulator 48 | - os: macos-14 49 | platform: ios 50 | arch: x64 51 | tags: -simulator 52 | flags: --simulator 53 | - os: windows-2022 54 | platform: win32 55 | arch: x64 56 | - os: windows-2022 57 | platform: win32 58 | arch: arm64 59 | runs-on: ${{ matrix.os }} 60 | name: ${{ matrix.platform }}-${{ matrix.arch }}${{ matrix.tags }} 61 | steps: 62 | - uses: actions/checkout@v4 63 | - uses: actions/setup-node@v4 64 | with: 65 | node-version: lts/* 66 | - run: npm install -g bare-make 67 | - run: npm install 68 | - run: bare-make generate --platform ${{ matrix.platform }} --arch ${{ matrix.arch }} --debug ${{ matrix.flags }} 69 | - run: bare-make build 70 | - run: bare-make test 71 | if: ${{ matrix.platform == 'linux' || matrix.platform == 'darwin' || (matrix.platform == 'win32' && matrix.arch == 'x64') }} 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules/ 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.31) 2 | 3 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) 4 | 5 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 6 | 7 | project(js C) 8 | 9 | add_library(js OBJECT) 10 | 11 | fetch_package("github:holepunchto/libjs" SOURCE_DIR js) 12 | fetch_package("github:holepunchto/libintrusive") 13 | fetch_package("github:quickjs-ng/quickjs@0.10.1") 14 | 15 | set_target_properties( 16 | js 17 | PROPERTIES 18 | C_STANDARD 99 19 | POSITION_INDEPENDENT_CODE ON 20 | ) 21 | 22 | target_sources( 23 | js 24 | PRIVATE 25 | src/js.c 26 | ) 27 | 28 | target_include_directories( 29 | js 30 | INTERFACE 31 | include 32 | PUBLIC 33 | ${js}/include 34 | PRIVATE 35 | $ 36 | $ 37 | $ 38 | $ 39 | ) 40 | 41 | add_library(js_shared SHARED) 42 | 43 | set_target_properties( 44 | js_shared 45 | PROPERTIES 46 | OUTPUT_NAME js 47 | WINDOWS_EXPORT_ALL_SYMBOLS ON 48 | ) 49 | 50 | target_include_directories( 51 | js_shared 52 | INTERFACE 53 | $ 54 | ) 55 | 56 | target_link_libraries( 57 | js_shared 58 | PUBLIC 59 | qjs 60 | uv 61 | utf_shared 62 | PRIVATE 63 | js 64 | PRIVATE 65 | $ 66 | ) 67 | 68 | add_library(js_static STATIC) 69 | 70 | set_target_properties( 71 | js_static 72 | PROPERTIES 73 | OUTPUT_NAME js 74 | PREFIX lib 75 | ) 76 | 77 | target_include_directories( 78 | js_static 79 | INTERFACE 80 | $ 81 | ) 82 | 83 | target_link_libraries( 84 | js_static 85 | PUBLIC 86 | qjs 87 | uv_a 88 | utf_static 89 | PRIVATE 90 | js 91 | PRIVATE 92 | $ 93 | ) 94 | 95 | install(TARGETS js_shared js_static) 96 | 97 | if(PROJECT_IS_TOP_LEVEL) 98 | enable_testing() 99 | 100 | add_subdirectory(test) 101 | endif() 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Holepunch Inc 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libqjs 2 | 3 | ABI compatible replacement for built on QuickJS. Useful for IoT, embedded, or otherwise constrained systems where the requirements of V8 can become problematic. 4 | 5 | ## License 6 | 7 | Apache-2.0 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "libqjs", 4 | "repository": { 5 | "type": "git", 6 | "url": "git+https://github.com/holepunchto/libqjs.git" 7 | }, 8 | "author": "Holepunch", 9 | "license": "Apache-2.0", 10 | "bugs": { 11 | "url": "https://github.com/holepunchto/libqjs/issues" 12 | }, 13 | "homepage": "https://github.com/holepunchto/libqjs#readme", 14 | "devDependencies": { 15 | "cmake-fetch": "^1.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/js.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | typedef struct js_callback_s js_callback_t; 21 | typedef struct js_finalizer_s js_finalizer_t; 22 | typedef struct js_finalizer_list_s js_finalizer_list_t; 23 | typedef struct js_delegate_s js_delegate_t; 24 | typedef struct js_module_resolver_s js_module_resolver_t; 25 | typedef struct js_module_evaluator_s js_module_evaluator_t; 26 | typedef struct js_arraybuffer_header_s js_arraybuffer_header_t; 27 | typedef struct js_promise_rejection_s js_promise_rejection_t; 28 | typedef struct js_threadsafe_queue_s js_threadsafe_queue_t; 29 | typedef struct js_teardown_task_s js_teardown_task_t; 30 | typedef struct js_teardown_queue_s js_teardown_queue_t; 31 | 32 | struct js_deferred_teardown_s { 33 | js_env_t *env; 34 | }; 35 | 36 | struct js_teardown_task_s { 37 | enum { 38 | js_immediate_teardown, 39 | js_deferred_teardown, 40 | } type; 41 | 42 | union { 43 | struct { 44 | js_teardown_cb cb; 45 | } immediate; 46 | 47 | struct { 48 | js_deferred_teardown_t handle; 49 | js_deferred_teardown_cb cb; 50 | } deferred; 51 | }; 52 | 53 | void *data; 54 | intrusive_list_node_t list; 55 | }; 56 | 57 | struct js_teardown_queue_s { 58 | intrusive_list_t tasks; 59 | }; 60 | 61 | struct js_platform_s { 62 | js_platform_options_t options; 63 | uv_loop_t *loop; 64 | }; 65 | 66 | struct js_env_s { 67 | uv_loop_t *loop; 68 | uv_prepare_t prepare; 69 | uv_check_t check; 70 | uv_async_t teardown; 71 | int active_handles; 72 | 73 | js_platform_t *platform; 74 | js_handle_scope_t *scope; 75 | 76 | uint32_t refs; 77 | uint32_t depth; 78 | 79 | JSRuntime *runtime; 80 | JSContext *context; 81 | JSValue bindings; 82 | 83 | int64_t external_memory; 84 | 85 | js_module_resolver_t *resolvers; 86 | js_module_evaluator_t *evaluators; 87 | 88 | bool destroying; 89 | 90 | js_promise_rejection_t *promise_rejections; 91 | 92 | js_teardown_queue_t teardown_queue; 93 | 94 | struct { 95 | JSClassID external; 96 | JSClassID finalizer; 97 | JSClassID type_tag; 98 | JSClassID function; 99 | JSClassID constructor; 100 | JSClassID delegate; 101 | } classes; 102 | 103 | struct { 104 | js_uncaught_exception_cb uncaught_exception; 105 | void *uncaught_exception_data; 106 | 107 | js_unhandled_rejection_cb unhandled_rejection; 108 | void *unhandled_rejection_data; 109 | 110 | js_dynamic_import_cb dynamic_import; 111 | void *dynamic_import_data; 112 | } callbacks; 113 | }; 114 | 115 | struct js_value_s { 116 | JSValue value; 117 | }; 118 | 119 | struct js_handle_scope_s { 120 | js_handle_scope_t *parent; 121 | js_value_t **values; 122 | size_t len; 123 | size_t capacity; 124 | }; 125 | 126 | struct js_escapable_handle_scope_s { 127 | js_handle_scope_t *parent; 128 | }; 129 | 130 | struct js_module_s { 131 | JSContext *context; 132 | JSValue source; 133 | JSValue bytecode; 134 | JSModuleDef *definition; 135 | js_module_meta_cb meta; 136 | void *meta_data; 137 | char *name; 138 | }; 139 | 140 | struct js_module_resolver_s { 141 | js_module_t *module; 142 | js_module_resolve_cb cb; 143 | void *data; 144 | js_module_resolver_t *next; 145 | }; 146 | 147 | struct js_module_evaluator_s { 148 | js_module_t *module; 149 | js_module_evaluate_cb cb; 150 | void *data; 151 | js_module_evaluator_t *next; 152 | }; 153 | 154 | struct js_ref_s { 155 | JSValue value; 156 | JSValue symbol; 157 | uint32_t count; 158 | bool finalized; 159 | }; 160 | 161 | struct js_deferred_s { 162 | JSValue resolve; 163 | JSValue reject; 164 | }; 165 | 166 | struct js_string_view_s { 167 | const char *value; 168 | }; 169 | 170 | struct js_finalizer_s { 171 | void *data; 172 | js_finalize_cb finalize_cb; 173 | void *finalize_hint; 174 | }; 175 | 176 | struct js_finalizer_list_s { 177 | js_finalizer_t finalizer; 178 | js_finalizer_list_t *next; 179 | }; 180 | 181 | struct js_delegate_s { 182 | js_delegate_callbacks_t callbacks; 183 | void *data; 184 | js_finalize_cb finalize_cb; 185 | void *finalize_hint; 186 | }; 187 | 188 | struct js_callback_s { 189 | js_function_cb cb; 190 | void *data; 191 | }; 192 | 193 | struct js_callback_info_s { 194 | js_callback_t *callback; 195 | int argc; 196 | JSValue *argv; 197 | JSValue receiver; 198 | JSValue new_target; 199 | }; 200 | 201 | struct js_arraybuffer_header_s { 202 | atomic_int references; 203 | size_t len; 204 | uint8_t data[]; 205 | }; 206 | 207 | struct js_arraybuffer_backing_store_s { 208 | atomic_int references; 209 | size_t len; 210 | uint8_t *data; 211 | JSValue owner; 212 | }; 213 | 214 | struct js_promise_rejection_s { 215 | JSValue promise; 216 | JSValue reason; 217 | js_promise_rejection_t *next; 218 | }; 219 | 220 | static const uint8_t js_threadsafe_function_idle = 0x0; 221 | static const uint8_t js_threadsafe_function_running = 0x1; 222 | static const uint8_t js_threadsafe_function_pending = 0x2; 223 | 224 | struct js_threadsafe_queue_s { 225 | void **queue; 226 | size_t len; 227 | size_t capacity; 228 | bool closed; 229 | uv_mutex_t lock; 230 | }; 231 | 232 | struct js_threadsafe_function_s { 233 | JSValue function; 234 | js_env_t *env; 235 | uv_async_t async; 236 | js_threadsafe_queue_t queue; 237 | atomic_int state; 238 | atomic_int thread_count; 239 | void *context; 240 | js_finalize_cb finalize_cb; 241 | void *finalize_hint; 242 | js_threadsafe_function_cb cb; 243 | }; 244 | 245 | static const char *js_platform_identifier = "quickjs"; 246 | 247 | int 248 | js_create_platform(uv_loop_t *loop, const js_platform_options_t *options, js_platform_t **result) { 249 | js_platform_t *platform = malloc(sizeof(js_platform_t)); 250 | 251 | platform->loop = loop; 252 | platform->options = options ? *options : (js_platform_options_t) {}; 253 | 254 | *result = platform; 255 | 256 | return 0; 257 | } 258 | 259 | int 260 | js_destroy_platform(js_platform_t *platform) { 261 | free(platform); 262 | 263 | return 0; 264 | } 265 | 266 | int 267 | js_get_platform_identifier(js_platform_t *platform, const char **result) { 268 | *result = js_platform_identifier; 269 | 270 | return 0; 271 | } 272 | 273 | int 274 | js_get_platform_version(js_platform_t *platform, const char **result) { 275 | *result = JS_GetVersion(); 276 | 277 | return 0; 278 | } 279 | 280 | int 281 | js_get_platform_limits(js_platform_t *platform, js_platform_limits_t *result) { 282 | result->arraybuffer_length = INT32_MAX; 283 | result->string_length = 0x3fffffff; 284 | 285 | return 0; 286 | } 287 | 288 | int 289 | js_get_platform_loop(js_platform_t *platform, uv_loop_t **result) { 290 | *result = platform->loop; 291 | 292 | return 0; 293 | } 294 | 295 | static void 296 | js__on_external_finalize(JSRuntime *runtime, JSValue value); 297 | 298 | static JSClassDef js_external_class = { 299 | .class_name = "External", 300 | .finalizer = js__on_external_finalize, 301 | }; 302 | 303 | static void 304 | js__on_finalizer_finalize(JSRuntime *runtime, JSValue value); 305 | 306 | static JSClassDef js_finalizer_class = { 307 | .class_name = "Finalizer", 308 | .finalizer = js__on_finalizer_finalize, 309 | }; 310 | 311 | static void 312 | js__on_type_tag_finalize(JSRuntime *runtime, JSValue value); 313 | 314 | static JSClassDef js_type_tag_class = { 315 | .class_name = "TypeTag", 316 | .finalizer = js__on_type_tag_finalize, 317 | }; 318 | 319 | static void 320 | js__on_function_finalize(JSRuntime *runtime, JSValue value); 321 | 322 | static JSClassDef js_function_class = { 323 | .class_name = "Function", 324 | .finalizer = js__on_function_finalize, 325 | }; 326 | 327 | static void 328 | js__on_constructor_finalize(JSRuntime *runtime, JSValue value); 329 | 330 | static JSClassDef js_constructor_class = { 331 | .class_name = "Constructor", 332 | .finalizer = js__on_constructor_finalize, 333 | }; 334 | 335 | static int 336 | js__on_delegate_get_own_property(JSContext *context, JSPropertyDescriptor *descriptor, JSValueConst object, JSAtom property); 337 | 338 | static int 339 | js__on_delegate_get_own_property_names(JSContext *context, JSPropertyEnum **properties, uint32_t *len, JSValueConst object); 340 | 341 | static int 342 | js__on_delegate_delete_property(JSContext *context, JSValueConst object, JSAtom property); 343 | 344 | static int 345 | js__on_delegate_set_property(JSContext *context, JSValueConst object, JSAtom property, JSValueConst value, JSValueConst receiver, int flags); 346 | 347 | static void 348 | js__on_delegate_finalize(JSRuntime *runtime, JSValue value); 349 | 350 | static JSClassDef js_delegate_class = { 351 | .class_name = "Delegate", 352 | .finalizer = js__on_delegate_finalize, 353 | .exotic = &(JSClassExoticMethods) { 354 | .get_own_property = js__on_delegate_get_own_property, 355 | .get_own_property_names = js__on_delegate_get_own_property_names, 356 | .delete_property = js__on_delegate_delete_property, 357 | .set_property = js__on_delegate_set_property, 358 | }, 359 | }; 360 | 361 | static JSModuleDef * 362 | js__on_resolve_module(JSContext *context, const char *name, void *opaque) { 363 | int err; 364 | 365 | js_env_t *env = (js_env_t *) JS_GetContextOpaque(context); 366 | 367 | js_module_resolver_t *resolver = env->resolvers; 368 | 369 | js_handle_scope_t *scope; 370 | err = js_open_handle_scope(env, &scope); 371 | assert(err == 0); 372 | 373 | js_module_t *module; 374 | 375 | js_value_t specifier = { 376 | .value = JS_NewString(env->context, name), 377 | }; 378 | 379 | js_value_t assertions = { 380 | .value = JS_NULL, 381 | }; 382 | 383 | JSModuleDef *definition = NULL; 384 | 385 | if (resolver) { 386 | module = resolver->cb( 387 | env, 388 | &specifier, 389 | &assertions, 390 | resolver->module, 391 | resolver->data 392 | ); 393 | 394 | if (module == NULL) goto done; 395 | 396 | if (module->definition == NULL) { 397 | err = js_instantiate_module( 398 | env, 399 | module, 400 | resolver->cb, 401 | resolver->data 402 | ); 403 | 404 | if (err < 0) goto done; 405 | } 406 | 407 | definition = module->definition; 408 | } else { 409 | if (env->callbacks.dynamic_import == NULL) { 410 | err = js_throw_error(env, NULL, "Dynamic import() is not supported"); 411 | assert(err == 0); 412 | 413 | goto done; 414 | } 415 | 416 | js_value_t referrer = { 417 | .value = JS_NULL, 418 | }; 419 | 420 | module = env->callbacks.dynamic_import( 421 | env, 422 | &specifier, 423 | &assertions, 424 | &referrer, 425 | env->callbacks.dynamic_import_data 426 | ); 427 | 428 | if (module == NULL) goto done; 429 | 430 | definition = module->definition; 431 | } 432 | 433 | done: 434 | JS_FreeValue(env->context, specifier.value); 435 | 436 | err = js_close_handle_scope(env, scope); 437 | assert(err == 0); 438 | 439 | return definition; 440 | } 441 | 442 | static void 443 | js__on_uncaught_exception(JSContext *context, JSValue error) { 444 | int err; 445 | 446 | js_env_t *env = (js_env_t *) JS_GetContextOpaque(context); 447 | 448 | if (env->callbacks.uncaught_exception) { 449 | js_handle_scope_t *scope; 450 | err = js_open_handle_scope(env, &scope); 451 | assert(err == 0); 452 | 453 | env->callbacks.uncaught_exception( 454 | env, 455 | &(js_value_t) {error}, 456 | env->callbacks.uncaught_exception_data 457 | ); 458 | 459 | err = js_close_handle_scope(env, scope); 460 | assert(err == 0); 461 | 462 | JS_FreeValue(context, error); 463 | } else { 464 | JS_Throw(context, error); 465 | } 466 | } 467 | 468 | static void 469 | js__on_unhandled_rejection(JSContext *context, JSValue promise, JSValue reason) { 470 | int err; 471 | 472 | js_env_t *env = (js_env_t *) JS_GetContextOpaque(context); 473 | 474 | if (env->callbacks.unhandled_rejection) { 475 | js_handle_scope_t *scope; 476 | err = js_open_handle_scope(env, &scope); 477 | assert(err == 0); 478 | 479 | env->callbacks.unhandled_rejection( 480 | env, 481 | &(js_value_t) {reason}, 482 | &(js_value_t) {promise}, 483 | env->callbacks.unhandled_rejection_data 484 | ); 485 | 486 | err = js_close_handle_scope(env, scope); 487 | assert(err == 0); 488 | } 489 | 490 | JS_FreeValue(context, promise); 491 | JS_FreeValue(context, reason); 492 | } 493 | 494 | static void 495 | js__on_promise_rejection(JSContext *context, JSValueConst promise, JSValueConst reason, bool is_handled, void *opaque) { 496 | js_env_t *env = (js_env_t *) JS_GetContextOpaque(context); 497 | 498 | if (env->callbacks.unhandled_rejection == NULL) return; 499 | 500 | if (is_handled) { 501 | js_promise_rejection_t *next = env->promise_rejections; 502 | js_promise_rejection_t *prev = NULL; 503 | 504 | while (next) { 505 | if (JS_VALUE_GET_OBJ(next->promise) == JS_VALUE_GET_OBJ(promise)) { 506 | JS_FreeValue(context, next->promise); 507 | JS_FreeValue(context, next->reason); 508 | 509 | if (prev) prev->next = next->next; 510 | else env->promise_rejections = next->next; 511 | 512 | return free(next); 513 | } 514 | 515 | prev = next; 516 | next = next->next; 517 | } 518 | } else { 519 | js_promise_rejection_t *node = malloc(sizeof(js_promise_rejection_t)); 520 | 521 | node->promise = JS_DupValue(context, promise); 522 | node->reason = JS_DupValue(context, reason); 523 | node->next = env->promise_rejections; 524 | 525 | env->promise_rejections = node; 526 | } 527 | } 528 | 529 | static inline void 530 | js__on_run_microtasks(js_env_t *env) { 531 | int err; 532 | 533 | for (;;) { 534 | JSContext *context; 535 | 536 | err = JS_ExecutePendingJob(env->runtime, &context); 537 | 538 | if (err == 0) break; 539 | 540 | if (err < 0) { 541 | JSValue error = JS_GetException(context); 542 | 543 | js_handle_scope_t *scope; 544 | err = js_open_handle_scope(env, &scope); 545 | assert(err == 0); 546 | 547 | js__on_uncaught_exception(context, error); 548 | 549 | err = js_close_handle_scope(env, scope); 550 | assert(err == 0); 551 | } 552 | } 553 | 554 | js_promise_rejection_t *next = env->promise_rejections; 555 | js_promise_rejection_t *prev = NULL; 556 | 557 | env->promise_rejections = NULL; 558 | 559 | while (next) { 560 | js__on_unhandled_rejection(env->context, next->promise, next->reason); 561 | 562 | prev = next; 563 | next = next->next; 564 | 565 | free(prev); 566 | 567 | js__on_run_microtasks(env); 568 | } 569 | } 570 | 571 | static void 572 | js__on_prepare(uv_prepare_t *handle); 573 | 574 | static inline void 575 | js__on_check_liveness(js_env_t *env) { 576 | int err; 577 | 578 | if (true /* macrotask queue empty */) { 579 | err = uv_prepare_stop(&env->prepare); 580 | } else { 581 | err = uv_prepare_start(&env->prepare, js__on_prepare); 582 | } 583 | 584 | assert(err == 0); 585 | } 586 | 587 | static void 588 | js__on_prepare(uv_prepare_t *handle) { 589 | js_env_t *env = (js_env_t *) handle->data; 590 | 591 | js__on_check_liveness(env); 592 | } 593 | 594 | static void 595 | js__on_check(uv_check_t *handle) { 596 | js_env_t *env = (js_env_t *) handle->data; 597 | 598 | if (uv_loop_alive(env->loop)) return; 599 | 600 | js__on_check_liveness(env); 601 | } 602 | 603 | static void * 604 | js__on_calloc(void *opaque, size_t count, size_t size) { 605 | return calloc(count, size); 606 | } 607 | 608 | static void * 609 | js__on_malloc(void *opaque, size_t size) { 610 | return malloc(size); 611 | } 612 | 613 | static void 614 | js__on_free(void *opaque, void *ptr) { 615 | free(ptr); 616 | } 617 | 618 | static void * 619 | js__on_realloc(void *opaque, void *ptr, size_t size) { 620 | return realloc(ptr, size); 621 | } 622 | 623 | static size_t 624 | js__on_usable_size(const void *ptr) { 625 | return 0; 626 | } 627 | 628 | static void * 629 | js__on_shared_malloc(void *opaque, size_t size) { 630 | js_arraybuffer_header_t *header = malloc(sizeof(js_arraybuffer_header_t) + size); 631 | 632 | header->references = 1; 633 | header->len = size; 634 | 635 | return header->data; 636 | } 637 | 638 | static void 639 | js__on_shared_free(void *opaque, void *ptr) { 640 | js_arraybuffer_header_t *header = (js_arraybuffer_header_t *) ((char *) ptr - sizeof(js_arraybuffer_header_t)); 641 | 642 | if (--header->references == 0) { 643 | free(header); 644 | } 645 | } 646 | 647 | static void 648 | js__on_shared_dup(void *opaque, void *ptr) { 649 | js_arraybuffer_header_t *header = (js_arraybuffer_header_t *) ((char *) ptr - sizeof(js_arraybuffer_header_t)); 650 | 651 | header->references++; 652 | } 653 | 654 | static void 655 | js__on_handle_close(uv_handle_t *handle) { 656 | js_env_t *env = (js_env_t *) handle->data; 657 | 658 | if (--env->active_handles == 0) { 659 | js_module_evaluator_t *evaluator = env->evaluators; 660 | 661 | while (evaluator) { 662 | js_module_evaluator_t *next = evaluator->next; 663 | 664 | free(evaluator); 665 | 666 | evaluator = next; 667 | } 668 | 669 | free(env); 670 | } 671 | } 672 | 673 | static void 674 | js__close_env(js_env_t *env) { 675 | JS_FreeValue(env->context, env->bindings); 676 | JS_FreeContext(env->context); 677 | JS_FreeRuntime(env->runtime); 678 | 679 | uv_close((uv_handle_t *) &env->prepare, js__on_handle_close); 680 | uv_close((uv_handle_t *) &env->check, js__on_handle_close); 681 | uv_close((uv_handle_t *) &env->teardown, js__on_handle_close); 682 | } 683 | 684 | static void 685 | js__on_teardown(uv_async_t *handle) { 686 | js_env_t *env = (js_env_t *) handle->data; 687 | 688 | if (env->refs == 0) js__close_env(env); 689 | } 690 | 691 | int 692 | js_create_env(uv_loop_t *loop, js_platform_t *platform, const js_env_options_t *options, js_env_t **result) { 693 | int err; 694 | 695 | JSRuntime *runtime = JS_NewRuntime2( 696 | &(JSMallocFunctions) { 697 | .js_calloc = js__on_calloc, 698 | .js_malloc = js__on_malloc, 699 | .js_free = js__on_free, 700 | .js_realloc = js__on_realloc, 701 | .js_malloc_usable_size = js__on_usable_size, 702 | }, 703 | NULL 704 | ); 705 | 706 | JS_SetSharedArrayBufferFunctions( 707 | runtime, 708 | &(JSSharedArrayBufferFunctions) { 709 | .sab_alloc = js__on_shared_malloc, 710 | .sab_free = js__on_shared_free, 711 | .sab_dup = js__on_shared_dup, 712 | .sab_opaque = NULL, 713 | } 714 | ); 715 | 716 | JS_SetMaxStackSize(runtime, 0); 717 | JS_SetCanBlock(runtime, false); 718 | JS_SetModuleLoaderFunc(runtime, NULL, js__on_resolve_module, NULL); 719 | JS_SetHostPromiseRejectionTracker(runtime, js__on_promise_rejection, NULL); 720 | 721 | if (options && options->memory_limit) { 722 | JS_SetMemoryLimit(runtime, options->memory_limit); 723 | } else { 724 | uint64_t constrained_memory = uv_get_constrained_memory(); 725 | uint64_t total_memory = uv_get_total_memory(); 726 | 727 | if (constrained_memory > 0 && constrained_memory < total_memory) { 728 | total_memory = constrained_memory; 729 | } 730 | 731 | if (total_memory > 0) { 732 | JS_SetMemoryLimit(runtime, total_memory); 733 | } 734 | } 735 | 736 | js_env_t *env = malloc(sizeof(js_env_t)); 737 | 738 | env->loop = loop; 739 | env->active_handles = 3; 740 | 741 | env->platform = platform; 742 | env->scope = NULL; 743 | 744 | env->refs = 0; 745 | env->depth = 0; 746 | 747 | env->runtime = runtime; 748 | env->context = JS_NewContext(runtime); 749 | env->bindings = JS_NewObject(env->context); 750 | 751 | env->external_memory = 0; 752 | 753 | env->resolvers = NULL; 754 | env->evaluators = NULL; 755 | 756 | env->destroying = false; 757 | 758 | env->promise_rejections = NULL; 759 | 760 | intrusive_list_init(&env->teardown_queue.tasks); 761 | 762 | env->classes.external = 0; 763 | env->classes.finalizer = 0; 764 | env->classes.type_tag = 0; 765 | env->classes.function = 0; 766 | env->classes.constructor = 0; 767 | env->classes.delegate = 0; 768 | 769 | env->callbacks.uncaught_exception = NULL; 770 | env->callbacks.uncaught_exception_data = NULL; 771 | 772 | env->callbacks.unhandled_rejection = NULL; 773 | env->callbacks.unhandled_rejection_data = NULL; 774 | 775 | env->callbacks.dynamic_import = NULL; 776 | env->callbacks.dynamic_import_data = NULL; 777 | 778 | JS_SetRuntimeOpaque(env->runtime, env); 779 | JS_SetContextOpaque(env->context, env); 780 | 781 | JS_NewClassID(runtime, &env->classes.external); 782 | err = JS_NewClass(runtime, env->classes.external, &js_external_class); 783 | assert(err == 0); 784 | 785 | JS_NewClassID(runtime, &env->classes.finalizer); 786 | err = JS_NewClass(runtime, env->classes.finalizer, &js_finalizer_class); 787 | assert(err == 0); 788 | 789 | JS_NewClassID(runtime, &env->classes.type_tag); 790 | err = JS_NewClass(runtime, env->classes.type_tag, &js_type_tag_class); 791 | assert(err == 0); 792 | 793 | JS_NewClassID(runtime, &env->classes.function); 794 | err = JS_NewClass(runtime, env->classes.function, &js_function_class); 795 | assert(err == 0); 796 | 797 | JS_NewClassID(runtime, &env->classes.constructor); 798 | err = JS_NewClass(runtime, env->classes.constructor, &js_constructor_class); 799 | assert(err == 0); 800 | 801 | JS_NewClassID(runtime, &env->classes.delegate); 802 | err = JS_NewClass(runtime, env->classes.delegate, &js_delegate_class); 803 | assert(err == 0); 804 | 805 | err = uv_prepare_init(loop, &env->prepare); 806 | assert(err == 0); 807 | 808 | err = uv_prepare_start(&env->prepare, js__on_prepare); 809 | assert(err == 0); 810 | 811 | env->prepare.data = (void *) env; 812 | 813 | err = uv_check_init(loop, &env->check); 814 | assert(err == 0); 815 | 816 | err = uv_check_start(&env->check, js__on_check); 817 | assert(err == 0); 818 | 819 | env->check.data = (void *) env; 820 | 821 | // The check handle should not on its own keep the loop alive; it's simply 822 | // used for running any outstanding tasks that might cause additional work 823 | // to be queued. 824 | uv_unref((uv_handle_t *) &env->check); 825 | 826 | err = uv_async_init(loop, &env->teardown, js__on_teardown); 827 | assert(err == 0); 828 | 829 | env->teardown.data = (void *) env; 830 | 831 | uv_unref((uv_handle_t *) &env->teardown); 832 | 833 | *result = env; 834 | 835 | return 0; 836 | } 837 | 838 | int 839 | js_destroy_env(js_env_t *env) { 840 | env->destroying = true; 841 | 842 | intrusive_list_for_each(next, &env->teardown_queue.tasks) { 843 | js_teardown_task_t *task = intrusive_entry(next, js_teardown_task_t, list); 844 | 845 | if (task->type == js_deferred_teardown) { 846 | task->deferred.cb(&task->deferred.handle, task->data); 847 | } else { 848 | task->immediate.cb(task->data); 849 | 850 | intrusive_list_remove(&env->teardown_queue.tasks, &task->list); 851 | 852 | free(task); 853 | } 854 | } 855 | 856 | if (env->refs == 0) { 857 | js__close_env(env); 858 | } else { 859 | uv_ref((uv_handle_t *) &env->teardown); 860 | } 861 | 862 | return 0; 863 | } 864 | 865 | int 866 | js_on_uncaught_exception(js_env_t *env, js_uncaught_exception_cb cb, void *data) { 867 | env->callbacks.uncaught_exception = cb; 868 | env->callbacks.uncaught_exception_data = data; 869 | 870 | return 0; 871 | } 872 | 873 | int 874 | js_on_unhandled_rejection(js_env_t *env, js_unhandled_rejection_cb cb, void *data) { 875 | env->callbacks.unhandled_rejection = cb; 876 | env->callbacks.unhandled_rejection_data = data; 877 | 878 | return 0; 879 | } 880 | 881 | int 882 | js_on_dynamic_import(js_env_t *env, js_dynamic_import_cb cb, void *data) { 883 | env->callbacks.dynamic_import = cb; 884 | env->callbacks.dynamic_import_data = data; 885 | 886 | return 0; 887 | } 888 | 889 | int 890 | js_get_env_loop(js_env_t *env, uv_loop_t **result) { 891 | *result = env->loop; 892 | 893 | return 0; 894 | } 895 | 896 | int 897 | js_get_env_platform(js_env_t *env, js_platform_t **result) { 898 | *result = env->platform; 899 | 900 | return 0; 901 | } 902 | 903 | static inline int 904 | js__error(js_env_t *env) { 905 | return JS_HasException(env->context) ? js_pending_exception : js_uncaught_exception; 906 | } 907 | 908 | int 909 | js_open_handle_scope(js_env_t *env, js_handle_scope_t **result) { 910 | // Allow continuing even with a pending exception 911 | 912 | js_handle_scope_t *scope = malloc(sizeof(js_handle_scope_t)); 913 | 914 | scope->parent = env->scope; 915 | scope->values = NULL; 916 | scope->len = 0; 917 | scope->capacity = 0; 918 | 919 | env->scope = scope; 920 | 921 | *result = scope; 922 | 923 | return 0; 924 | } 925 | 926 | int 927 | js_close_handle_scope(js_env_t *env, js_handle_scope_t *scope) { 928 | // Allow continuing even with a pending exception 929 | 930 | for (size_t i = 0; i < scope->len; i++) { 931 | js_value_t *value = scope->values[i]; 932 | 933 | JS_FreeValue(env->context, value->value); 934 | 935 | free(value); 936 | } 937 | 938 | env->scope = scope->parent; 939 | 940 | if (scope->values) free(scope->values); 941 | 942 | free(scope); 943 | 944 | return 0; 945 | } 946 | 947 | int 948 | js_open_escapable_handle_scope(js_env_t *env, js_escapable_handle_scope_t **result) { 949 | return js_open_handle_scope(env, (js_handle_scope_t **) result); 950 | } 951 | 952 | int 953 | js_close_escapable_handle_scope(js_env_t *env, js_escapable_handle_scope_t *scope) { 954 | return js_close_handle_scope(env, (js_handle_scope_t *) scope); 955 | } 956 | 957 | static inline void 958 | js__attach_to_handle_scope(js_env_t *env, js_handle_scope_t *scope, js_value_t *value) { 959 | assert(scope); 960 | 961 | if (scope->len >= scope->capacity) { 962 | if (scope->capacity) scope->capacity *= 2; 963 | else scope->capacity = 4; 964 | 965 | scope->values = realloc(scope->values, scope->capacity * sizeof(js_value_t *)); 966 | } 967 | 968 | scope->values[scope->len++] = value; 969 | } 970 | 971 | int 972 | js_escape_handle(js_env_t *env, js_escapable_handle_scope_t *scope, js_value_t *escapee, js_value_t **result) { 973 | // Allow continuing even with a pending exception 974 | 975 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 976 | 977 | wrapper->value = JS_DupValue(env->context, escapee->value); 978 | 979 | *result = wrapper; 980 | 981 | js__attach_to_handle_scope(env, scope->parent, wrapper); 982 | 983 | return 0; 984 | } 985 | 986 | int 987 | js_create_context(js_env_t *env, js_context_t **result) { 988 | int err; 989 | 990 | err = js_throw_error(env, NULL, "Unsupported operation"); 991 | assert(err == 0); 992 | 993 | return js__error(env); 994 | } 995 | 996 | int 997 | js_destroy_context(js_env_t *env, js_context_t *context) { 998 | int err; 999 | 1000 | err = js_throw_error(env, NULL, "Unsupported operation"); 1001 | assert(err == 0); 1002 | 1003 | return js__error(env); 1004 | } 1005 | 1006 | int 1007 | js_enter_context(js_env_t *env, js_context_t *context) { 1008 | int err; 1009 | 1010 | err = js_throw_error(env, NULL, "Unsupported operation"); 1011 | assert(err == 0); 1012 | 1013 | return js__error(env); 1014 | } 1015 | 1016 | int 1017 | js_exit_context(js_env_t *env, js_context_t *context) { 1018 | int err; 1019 | 1020 | err = js_throw_error(env, NULL, "Unsupported operation"); 1021 | assert(err == 0); 1022 | 1023 | return js__error(env); 1024 | } 1025 | 1026 | int 1027 | js_get_bindings(js_env_t *env, js_value_t **result) { 1028 | if (JS_HasException(env->context)) return js__error(env); 1029 | 1030 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 1031 | 1032 | wrapper->value = JS_DupValue(env->context, env->bindings); 1033 | 1034 | *result = wrapper; 1035 | 1036 | js__attach_to_handle_scope(env, env->scope, wrapper); 1037 | 1038 | return 0; 1039 | } 1040 | 1041 | int 1042 | js_run_script(js_env_t *env, const char *file, size_t len, int offset, js_value_t *source, js_value_t **result) { 1043 | if (JS_HasException(env->context)) return js__error(env); 1044 | 1045 | size_t str_len; 1046 | const char *str = JS_ToCStringLen(env->context, &str_len, source->value); 1047 | 1048 | env->depth++; 1049 | 1050 | if (file == NULL) file = ""; 1051 | 1052 | JSValue value = JS_Eval( 1053 | env->context, 1054 | str, 1055 | str_len, 1056 | file, 1057 | JS_EVAL_TYPE_GLOBAL 1058 | ); 1059 | 1060 | JS_FreeCString(env->context, str); 1061 | 1062 | if (env->depth == 1) js__on_run_microtasks(env); 1063 | 1064 | env->depth--; 1065 | 1066 | if (JS_IsException(value)) { 1067 | if (env->depth == 0) { 1068 | JSValue error = JS_GetException(env->context); 1069 | 1070 | js__on_uncaught_exception(env->context, error); 1071 | } 1072 | 1073 | return js__error(env); 1074 | } 1075 | 1076 | if (result == NULL) JS_FreeValue(env->context, value); 1077 | else { 1078 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 1079 | 1080 | wrapper->value = value; 1081 | 1082 | *result = wrapper; 1083 | 1084 | js__attach_to_handle_scope(env, env->scope, wrapper); 1085 | } 1086 | 1087 | return 0; 1088 | } 1089 | 1090 | int 1091 | js_create_module(js_env_t *env, const char *name, size_t len, int offset, js_value_t *source, js_module_meta_cb cb, void *data, js_module_t **result) { 1092 | if (JS_HasException(env->context)) return js__error(env); 1093 | 1094 | js_module_t *module = malloc(sizeof(js_module_t)); 1095 | 1096 | module->context = env->context; 1097 | module->source = JS_DupValue(env->context, source->value); 1098 | module->bytecode = JS_NULL; 1099 | module->definition = NULL; 1100 | module->meta = cb; 1101 | module->meta_data = data; 1102 | 1103 | if (len == (size_t) -1) { 1104 | module->name = strdup(name); 1105 | } else { 1106 | module->name = malloc(len + 1); 1107 | module->name[len] = '\0'; 1108 | 1109 | memcpy(module->name, name, len); 1110 | } 1111 | 1112 | *result = module; 1113 | 1114 | return 0; 1115 | } 1116 | 1117 | static int 1118 | js__on_evaluate_module(JSContext *context, JSModuleDef *definition) { 1119 | int err; 1120 | 1121 | js_env_t *env = (js_env_t *) JS_GetContextOpaque(context); 1122 | 1123 | js_module_evaluator_t *evaluator = env->evaluators; 1124 | 1125 | while (evaluator && evaluator->module->definition != definition) { 1126 | evaluator = evaluator->next; 1127 | } 1128 | 1129 | js_handle_scope_t *scope; 1130 | err = js_open_handle_scope(env, &scope); 1131 | assert(err == 0); 1132 | 1133 | evaluator->cb(env, evaluator->module, evaluator->data); 1134 | 1135 | err = js_close_handle_scope(env, scope); 1136 | assert(err == 0); 1137 | 1138 | return 0; 1139 | } 1140 | 1141 | int 1142 | js_create_synthetic_module(js_env_t *env, const char *name, size_t len, js_value_t *const export_names[], size_t names_len, js_module_evaluate_cb cb, void *data, js_module_t **result) { 1143 | if (JS_HasException(env->context)) return js__error(env); 1144 | 1145 | js_module_t *module = malloc(sizeof(js_module_t)); 1146 | 1147 | module->context = env->context; 1148 | module->source = JS_NULL; 1149 | module->bytecode = JS_NULL; 1150 | module->definition = JS_NewCModule(env->context, name, js__on_evaluate_module); 1151 | module->meta = NULL; 1152 | module->meta_data = NULL; 1153 | 1154 | if (len == (size_t) -1) { 1155 | module->name = strdup(name); 1156 | } else { 1157 | module->name = malloc(len + 1); 1158 | module->name[len] = '\0'; 1159 | 1160 | memcpy(module->name, name, len); 1161 | } 1162 | 1163 | for (size_t i = 0; i < names_len; i++) { 1164 | const char *str = JS_ToCString(env->context, export_names[i]->value); 1165 | 1166 | JS_AddModuleExport(env->context, module->definition, str); 1167 | 1168 | JS_FreeCString(env->context, str); 1169 | } 1170 | 1171 | js_module_evaluator_t *evaluator = malloc(sizeof(js_module_evaluator_t)); 1172 | 1173 | evaluator->module = module; 1174 | evaluator->cb = cb; 1175 | evaluator->data = data; 1176 | evaluator->next = env->evaluators; 1177 | 1178 | env->evaluators = evaluator; 1179 | 1180 | *result = module; 1181 | 1182 | return 0; 1183 | } 1184 | 1185 | int 1186 | js_delete_module(js_env_t *env, js_module_t *module) { 1187 | // Allow continuing even with a pending exception 1188 | 1189 | JS_FreeValue(env->context, module->source); 1190 | JS_FreeValue(env->context, module->bytecode); 1191 | 1192 | free(module->name); 1193 | free(module); 1194 | 1195 | return 0; 1196 | } 1197 | 1198 | int 1199 | js_get_module_name(js_env_t *env, js_module_t *module, const char **result) { 1200 | // Allow continuing even with a pending exception 1201 | 1202 | *result = module->name; 1203 | 1204 | return 0; 1205 | } 1206 | 1207 | int 1208 | js_get_module_namespace(js_env_t *env, js_module_t *module, js_value_t **result) { 1209 | // Allow continuing even with a pending exception 1210 | 1211 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 1212 | 1213 | wrapper->value = JS_GetModuleNamespace(env->context, module->definition); 1214 | 1215 | *result = wrapper; 1216 | 1217 | js__attach_to_handle_scope(env, env->scope, wrapper); 1218 | 1219 | return 0; 1220 | } 1221 | 1222 | int 1223 | js_set_module_export(js_env_t *env, js_module_t *module, js_value_t *name, js_value_t *value) { 1224 | if (JS_HasException(env->context)) return js__error(env); 1225 | 1226 | int err; 1227 | 1228 | const char *str = JS_ToCString(env->context, name->value); 1229 | 1230 | int success = JS_SetModuleExport(env->context, module->definition, str, JS_DupValue(env->context, value->value)); 1231 | 1232 | JS_FreeCString(env->context, str); 1233 | 1234 | if (success < 0) { 1235 | err = js_throw_error(env, NULL, "Could not set module export"); 1236 | assert(err == 0); 1237 | 1238 | return js__error(env); 1239 | } 1240 | 1241 | return 0; 1242 | } 1243 | 1244 | int 1245 | js_instantiate_module(js_env_t *env, js_module_t *module, js_module_resolve_cb cb, void *data) { 1246 | if (JS_HasException(env->context)) return js__error(env); 1247 | 1248 | if (JS_IsNull(module->source)) return 0; 1249 | 1250 | js_module_resolver_t resolver = { 1251 | .module = module, 1252 | .cb = cb, 1253 | .data = data, 1254 | .next = env->resolvers, 1255 | }; 1256 | 1257 | env->resolvers = &resolver; 1258 | 1259 | size_t str_len; 1260 | const char *str = JS_ToCStringLen(env->context, &str_len, module->source); 1261 | 1262 | env->depth++; 1263 | 1264 | JSValue bytecode = JS_Eval( 1265 | env->context, 1266 | str, 1267 | str_len, 1268 | module->name, 1269 | JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY 1270 | ); 1271 | 1272 | JS_FreeCString(env->context, str); 1273 | 1274 | if (env->depth == 1) js__on_run_microtasks(env); 1275 | 1276 | env->depth--; 1277 | 1278 | if (JS_IsException(bytecode)) { 1279 | if (env->depth == 0) { 1280 | JSValue error = JS_GetException(env->context); 1281 | 1282 | js__on_uncaught_exception(env->context, error); 1283 | } 1284 | 1285 | return js__error(env); 1286 | } 1287 | 1288 | module->bytecode = bytecode; 1289 | 1290 | module->definition = (JSModuleDef *) JS_VALUE_GET_PTR(bytecode); 1291 | 1292 | env->resolvers = resolver.next; 1293 | 1294 | return 0; 1295 | } 1296 | 1297 | int 1298 | js_run_module(js_env_t *env, js_module_t *module, js_value_t **result) { 1299 | if (JS_HasException(env->context)) return js__error(env); 1300 | 1301 | int err; 1302 | 1303 | if (module->meta) { 1304 | JSValue meta = JS_GetImportMeta(env->context, module->definition); 1305 | 1306 | module->meta(env, module, &(js_value_t) {meta}, module->meta_data); 1307 | 1308 | JS_FreeValue(env->context, meta); 1309 | 1310 | if (JS_HasException(env->context)) { 1311 | JSValue error = JS_GetException(env->context); 1312 | 1313 | js_deferred_t *deferred; 1314 | err = js_create_promise(env, &deferred, result); 1315 | if (err < 0) return err; 1316 | 1317 | js_reject_deferred(env, deferred, &(js_value_t) {error}); 1318 | 1319 | JS_FreeValue(env->context, error); 1320 | 1321 | return 0; 1322 | } 1323 | } 1324 | 1325 | env->depth++; 1326 | 1327 | JSValue value = JS_EvalFunction(env->context, module->bytecode); 1328 | 1329 | if (env->depth == 1) js__on_run_microtasks(env); 1330 | 1331 | env->depth--; 1332 | 1333 | module->bytecode = JS_NULL; 1334 | 1335 | if (JS_IsException(value)) { 1336 | JSValue error = JS_GetException(env->context); 1337 | 1338 | js_deferred_t *deferred; 1339 | err = js_create_promise(env, &deferred, result); 1340 | if (err < 0) return err; 1341 | 1342 | js_reject_deferred(env, deferred, &(js_value_t) {error}); 1343 | 1344 | JS_FreeValue(env->context, error); 1345 | 1346 | return 0; 1347 | } 1348 | 1349 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 1350 | 1351 | wrapper->value = value; 1352 | 1353 | *result = wrapper; 1354 | 1355 | js__attach_to_handle_scope(env, env->scope, wrapper); 1356 | 1357 | return 0; 1358 | } 1359 | 1360 | static void 1361 | js__on_reference_finalize(js_env_t *env, void *data, void *finalize_hint) { 1362 | js_ref_t *reference = (js_ref_t *) data; 1363 | 1364 | JS_FreeValue(env->context, reference->symbol); 1365 | 1366 | reference->value = JS_NULL; 1367 | reference->count = 0; 1368 | reference->symbol = JS_NULL; 1369 | reference->finalized = true; 1370 | } 1371 | 1372 | static inline void 1373 | js__set_weak_reference(js_env_t *env, js_ref_t *reference) { 1374 | if (reference->finalized) return; 1375 | 1376 | if (JS_IsObject(reference->value)) { 1377 | int err; 1378 | 1379 | js_finalizer_t *finalizer = malloc(sizeof(js_finalizer_t)); 1380 | 1381 | finalizer->data = reference; 1382 | finalizer->finalize_cb = js__on_reference_finalize; 1383 | finalizer->finalize_hint = NULL; 1384 | 1385 | reference->symbol = JS_NewSymbol(env->context, "__native_reference", false); 1386 | 1387 | JSValue external = JS_NewObjectClass(env->context, env->classes.external); 1388 | 1389 | JS_SetOpaque(external, finalizer); 1390 | 1391 | JSAtom atom = JS_ValueToAtom(env->context, reference->symbol); 1392 | 1393 | err = JS_DefinePropertyValue(env->context, reference->value, atom, external, 0); 1394 | assert(err >= 0); 1395 | 1396 | JS_FreeAtom(env->context, atom); 1397 | 1398 | JS_FreeValue(env->context, reference->value); 1399 | } 1400 | } 1401 | 1402 | static inline void 1403 | js__clear_weak_reference(js_env_t *env, js_ref_t *reference) { 1404 | if (reference->finalized) return; 1405 | 1406 | if (JS_IsObject(reference->value)) { 1407 | int err; 1408 | 1409 | JS_DupValue(env->context, reference->value); 1410 | 1411 | JSAtom atom = JS_ValueToAtom(env->context, reference->symbol); 1412 | 1413 | JSValue external = JS_GetProperty(env->context, reference->value, atom); 1414 | 1415 | js_finalizer_t *finalizer = (js_finalizer_t *) JS_GetOpaque(external, env->classes.external); 1416 | 1417 | JS_FreeValue(env->context, external); 1418 | 1419 | finalizer->finalize_cb = NULL; 1420 | 1421 | err = JS_DeleteProperty(env->context, reference->value, atom, 0); 1422 | assert(err >= 0); 1423 | 1424 | JS_FreeAtom(env->context, atom); 1425 | 1426 | JS_FreeValue(env->context, reference->symbol); 1427 | 1428 | reference->symbol = JS_NULL; 1429 | } 1430 | } 1431 | 1432 | int 1433 | js_create_reference(js_env_t *env, js_value_t *value, uint32_t count, js_ref_t **result) { 1434 | // Allow continuing even with a pending exception 1435 | 1436 | js_ref_t *reference = malloc(sizeof(js_ref_t)); 1437 | 1438 | reference->value = JS_DupValue(env->context, value->value); 1439 | reference->count = count; 1440 | reference->symbol = JS_NULL; 1441 | reference->finalized = false; 1442 | 1443 | if (reference->count == 0) js__set_weak_reference(env, reference); 1444 | 1445 | *result = reference; 1446 | 1447 | return 0; 1448 | } 1449 | 1450 | int 1451 | js_delete_reference(js_env_t *env, js_ref_t *reference) { 1452 | // Allow continuing even with a pending exception 1453 | 1454 | if (reference->count == 0) js__clear_weak_reference(env, reference); 1455 | 1456 | JS_FreeValue(env->context, reference->value); 1457 | 1458 | free(reference); 1459 | 1460 | return 0; 1461 | } 1462 | 1463 | int 1464 | js_reference_ref(js_env_t *env, js_ref_t *reference, uint32_t *result) { 1465 | // Allow continuing even with a pending exception 1466 | 1467 | reference->count++; 1468 | 1469 | if (reference->count == 1) js__clear_weak_reference(env, reference); 1470 | 1471 | if (result) *result = reference->count; 1472 | 1473 | return 0; 1474 | } 1475 | 1476 | int 1477 | js_reference_unref(js_env_t *env, js_ref_t *reference, uint32_t *result) { 1478 | // Allow continuing even with a pending exception 1479 | 1480 | if (reference->count > 0) { 1481 | reference->count--; 1482 | 1483 | if (reference->count == 0) js__set_weak_reference(env, reference); 1484 | } 1485 | 1486 | if (result) *result = reference->count; 1487 | 1488 | return 0; 1489 | } 1490 | 1491 | int 1492 | js_get_reference_value(js_env_t *env, js_ref_t *reference, js_value_t **result) { 1493 | // Allow continuing even with a pending exception 1494 | 1495 | if (reference->finalized) *result = NULL; 1496 | else { 1497 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 1498 | 1499 | wrapper->value = JS_DupValue(env->context, reference->value); 1500 | 1501 | *result = wrapper; 1502 | 1503 | js__attach_to_handle_scope(env, env->scope, wrapper); 1504 | } 1505 | 1506 | return 0; 1507 | } 1508 | 1509 | static void 1510 | js__on_constructor_finalize(JSRuntime *runtime, JSValue value) { 1511 | js_env_t *env = (js_env_t *) JS_GetRuntimeOpaque(runtime); 1512 | 1513 | js_callback_t *callback = (js_callback_t *) JS_GetOpaque(value, env->classes.constructor); 1514 | 1515 | free(callback); 1516 | } 1517 | 1518 | static JSValue 1519 | js__on_constructor_call(JSContext *context, JSValueConst new_target, int argc, JSValueConst *argv, int magic, JSValue *data) { 1520 | int err; 1521 | 1522 | JSValue prototype = JS_GetPropertyStr(context, new_target, "prototype"); 1523 | 1524 | JSValue receiver = JS_NewObjectProto(context, prototype); 1525 | 1526 | JS_FreeValue(context, prototype); 1527 | 1528 | js_env_t *env = (js_env_t *) JS_GetContextOpaque(context); 1529 | 1530 | js_callback_t *callback = (js_callback_t *) JS_GetOpaque(*data, env->classes.constructor); 1531 | 1532 | js_callback_info_t callback_info = { 1533 | .callback = callback, 1534 | .argc = argc, 1535 | .argv = argv, 1536 | .receiver = receiver, 1537 | .new_target = new_target, 1538 | }; 1539 | 1540 | js_handle_scope_t *scope; 1541 | err = js_open_handle_scope(env, &scope); 1542 | assert(err == 0); 1543 | 1544 | js_value_t *result = callback->cb(env, &callback_info); 1545 | 1546 | JSValue value, error; 1547 | 1548 | if (result == NULL) value = JS_UNDEFINED; 1549 | else value = JS_DupValue(env->context, result->value); 1550 | 1551 | if (JS_HasException(env->context)) { 1552 | JS_FreeValue(env->context, value); 1553 | 1554 | value = JS_EXCEPTION; 1555 | } 1556 | 1557 | err = js_close_handle_scope(env, scope); 1558 | assert(err == 0); 1559 | 1560 | return receiver; 1561 | } 1562 | 1563 | int 1564 | js_define_class(js_env_t *env, const char *name, size_t len, js_function_cb constructor, void *data, js_property_descriptor_t const properties[], size_t properties_len, js_value_t **result) { 1565 | if (JS_HasException(env->context)) return js__error(env); 1566 | 1567 | int err; 1568 | 1569 | js_callback_t *callback = malloc(sizeof(js_callback_t)); 1570 | 1571 | callback->cb = constructor; 1572 | callback->data = data; 1573 | 1574 | JSValue external = JS_NewObjectClass(env->context, env->classes.constructor); 1575 | 1576 | JS_SetOpaque(external, callback); 1577 | 1578 | JSValue class = JS_NewCFunctionData(env->context, js__on_constructor_call, 0, 0, 1, &external); 1579 | 1580 | JS_SetConstructorBit(env->context, class, true); 1581 | 1582 | JSValue prototype = JS_NewObject(env->context); 1583 | 1584 | JS_SetConstructor(env->context, class, prototype); 1585 | 1586 | size_t instance_properties_len = 0; 1587 | size_t static_properties_len = 0; 1588 | 1589 | for (size_t i = 0; i < properties_len; i++) { 1590 | const js_property_descriptor_t *property = &properties[i]; 1591 | 1592 | if ((property->attributes & js_static) == 0) { 1593 | instance_properties_len++; 1594 | } else { 1595 | static_properties_len++; 1596 | } 1597 | } 1598 | 1599 | if (instance_properties_len) { 1600 | js_property_descriptor_t *instance_properties = malloc(sizeof(js_property_descriptor_t) * instance_properties_len); 1601 | 1602 | for (size_t i = 0, j = 0; i < properties_len; i++) { 1603 | const js_property_descriptor_t *property = &properties[i]; 1604 | 1605 | if ((property->attributes & js_static) == 0) { 1606 | instance_properties[j++] = *property; 1607 | } 1608 | } 1609 | 1610 | err = js_define_properties(env, &(js_value_t) {prototype}, instance_properties, instance_properties_len); 1611 | assert(err == 0); 1612 | 1613 | free(instance_properties); 1614 | } 1615 | 1616 | if (static_properties_len) { 1617 | js_property_descriptor_t *static_properties = malloc(sizeof(js_property_descriptor_t) * static_properties_len); 1618 | 1619 | for (size_t i = 0, j = 0; i < properties_len; i++) { 1620 | const js_property_descriptor_t *property = &properties[i]; 1621 | 1622 | if ((property->attributes & js_static) != 0) { 1623 | static_properties[j++] = *property; 1624 | } 1625 | } 1626 | 1627 | err = js_define_properties(env, &(js_value_t) {class}, static_properties, static_properties_len); 1628 | assert(err == 0); 1629 | 1630 | free(static_properties); 1631 | } 1632 | 1633 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 1634 | 1635 | wrapper->value = class; 1636 | 1637 | JS_FreeValue(env->context, external); 1638 | JS_FreeValue(env->context, prototype); 1639 | 1640 | *result = wrapper; 1641 | 1642 | js__attach_to_handle_scope(env, env->scope, wrapper); 1643 | 1644 | return 0; 1645 | } 1646 | 1647 | int 1648 | js_define_properties(js_env_t *env, js_value_t *object, js_property_descriptor_t const properties[], size_t properties_len) { 1649 | if (JS_HasException(env->context)) return js__error(env); 1650 | 1651 | int err; 1652 | 1653 | for (size_t i = 0; i < properties_len; i++) { 1654 | const js_property_descriptor_t *property = &properties[i]; 1655 | 1656 | int flags = JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE | JS_PROP_HAS_CONFIGURABLE; 1657 | 1658 | if ((property->attributes & js_writable) != 0 || property->getter || property->setter) { 1659 | flags |= JS_PROP_WRITABLE; 1660 | } 1661 | 1662 | if ((property->attributes & js_enumerable) != 0) { 1663 | flags |= JS_PROP_ENUMERABLE; 1664 | } 1665 | 1666 | if ((property->attributes & js_configurable) != 0) { 1667 | flags |= JS_PROP_CONFIGURABLE; 1668 | } 1669 | 1670 | JSValue value = JS_UNDEFINED, getter = JS_UNDEFINED, setter = JS_UNDEFINED; 1671 | 1672 | if (property->getter || property->setter) { 1673 | if (property->getter) { 1674 | flags |= JS_PROP_HAS_GET; 1675 | 1676 | js_value_t *fn; 1677 | err = js_create_function(env, "fn", -1, property->getter, property->data, &fn); 1678 | assert(err == 0); 1679 | 1680 | getter = fn->value; 1681 | } 1682 | 1683 | if (property->setter) { 1684 | flags |= JS_PROP_HAS_SET; 1685 | 1686 | js_value_t *fn; 1687 | err = js_create_function(env, "fn", -1, property->setter, property->data, &fn); 1688 | assert(err == 0); 1689 | 1690 | setter = fn->value; 1691 | } 1692 | } else if (property->method) { 1693 | flags |= JS_PROP_HAS_VALUE; 1694 | 1695 | js_value_t *fn; 1696 | err = js_create_function(env, "fn", -1, property->method, property->data, &fn); 1697 | assert(err == 0); 1698 | 1699 | value = fn->value; 1700 | } else { 1701 | flags |= JS_PROP_HAS_VALUE; 1702 | 1703 | value = property->value->value; 1704 | } 1705 | 1706 | JSAtom atom = JS_ValueToAtom(env->context, property->name->value); 1707 | 1708 | err = JS_DefineProperty(env->context, object->value, atom, value, getter, setter, flags); 1709 | 1710 | JS_FreeAtom(env->context, atom); 1711 | 1712 | if (err < 0) return js__error(env); 1713 | } 1714 | 1715 | return 0; 1716 | } 1717 | 1718 | int 1719 | js_wrap(js_env_t *env, js_value_t *object, void *data, js_finalize_cb finalize_cb, void *finalize_hint, js_ref_t **result) { 1720 | if (JS_HasException(env->context)) return js__error(env); 1721 | 1722 | int err; 1723 | 1724 | js_finalizer_t *finalizer = malloc(sizeof(js_finalizer_t)); 1725 | 1726 | finalizer->data = data; 1727 | finalizer->finalize_cb = finalize_cb; 1728 | finalizer->finalize_hint = finalize_hint; 1729 | 1730 | JSValue external = JS_NewObjectClass(env->context, env->classes.external); 1731 | 1732 | JS_SetOpaque(external, finalizer); 1733 | 1734 | JSAtom atom = JS_NewAtom(env->context, "__native_external"); 1735 | 1736 | err = JS_DefinePropertyValue(env->context, object->value, atom, external, 0); 1737 | 1738 | if (err < 0) { 1739 | JS_FreeValue(env->context, external); 1740 | 1741 | JS_FreeAtom(env->context, atom); 1742 | 1743 | return js__error(env); 1744 | } 1745 | 1746 | JS_FreeAtom(env->context, atom); 1747 | 1748 | if (result) return js_create_reference(env, object, 0, result); 1749 | 1750 | return 0; 1751 | } 1752 | 1753 | int 1754 | js_unwrap(js_env_t *env, js_value_t *object, void **result) { 1755 | if (JS_HasException(env->context)) return js__error(env); 1756 | 1757 | JSAtom atom = JS_NewAtom(env->context, "__native_external"); 1758 | 1759 | JSValue external = JS_GetProperty(env->context, object->value, atom); 1760 | 1761 | JS_FreeAtom(env->context, atom); 1762 | 1763 | js_finalizer_t *finalizer = (js_finalizer_t *) JS_GetOpaque(external, env->classes.external); 1764 | 1765 | JS_FreeValue(env->context, external); 1766 | 1767 | *result = finalizer->data; 1768 | 1769 | return 0; 1770 | } 1771 | 1772 | int 1773 | js_remove_wrap(js_env_t *env, js_value_t *object, void **result) { 1774 | if (JS_HasException(env->context)) return js__error(env); 1775 | 1776 | int err; 1777 | 1778 | JSAtom atom = JS_NewAtom(env->context, "__native_external"); 1779 | 1780 | JSValue external = JS_GetProperty(env->context, object->value, atom); 1781 | 1782 | js_finalizer_t *finalizer = (js_finalizer_t *) JS_GetOpaque(external, env->classes.external); 1783 | 1784 | JS_FreeValue(env->context, external); 1785 | 1786 | finalizer->finalize_cb = NULL; 1787 | 1788 | if (result) *result = finalizer->data; 1789 | 1790 | err = JS_DeleteProperty(env->context, object->value, atom, 0); 1791 | 1792 | if (err < 0) { 1793 | JS_FreeAtom(env->context, atom); 1794 | 1795 | return js__error(env); 1796 | } 1797 | 1798 | JS_FreeAtom(env->context, atom); 1799 | 1800 | return 0; 1801 | } 1802 | 1803 | static int 1804 | js__on_delegate_get_own_property(JSContext *context, JSPropertyDescriptor *descriptor, JSValueConst object, JSAtom name) { 1805 | js_env_t *env = (js_env_t *) JS_GetContextOpaque(context); 1806 | 1807 | js_delegate_t *delegate = (js_delegate_t *) JS_GetOpaque(object, env->classes.delegate); 1808 | 1809 | if (delegate->callbacks.has) { 1810 | JSValue property = JS_AtomToValue(env->context, name); 1811 | 1812 | bool exists = delegate->callbacks.has(env, &(js_value_t) {property}, delegate->data); 1813 | 1814 | JS_FreeValue(env->context, property); 1815 | 1816 | if (JS_HasException(env->context)) return js__error(env); 1817 | 1818 | if (!exists) return 0; 1819 | } 1820 | 1821 | if (delegate->callbacks.get) { 1822 | JSValue property = JS_AtomToValue(env->context, name); 1823 | 1824 | js_value_t *result = delegate->callbacks.get(env, &(js_value_t) {property}, delegate->data); 1825 | 1826 | JS_FreeValue(env->context, property); 1827 | 1828 | if (JS_HasException(env->context)) return js__error(env); 1829 | 1830 | if (result == NULL) return 0; 1831 | 1832 | if (descriptor) { 1833 | descriptor->flags = JS_PROP_ENUMERABLE; 1834 | descriptor->value = result->value; 1835 | descriptor->getter = JS_UNDEFINED; 1836 | descriptor->setter = JS_UNDEFINED; 1837 | } 1838 | 1839 | return 1; 1840 | } 1841 | 1842 | return 0; 1843 | } 1844 | 1845 | static int 1846 | js__on_delegate_get_own_property_names(JSContext *context, JSPropertyEnum **pproperties, uint32_t *plen, JSValueConst object) { 1847 | int err; 1848 | 1849 | js_env_t *env = (js_env_t *) JS_GetContextOpaque(context); 1850 | 1851 | js_delegate_t *delegate = (js_delegate_t *) JS_GetOpaque(object, env->classes.delegate); 1852 | 1853 | if (delegate->callbacks.own_keys) { 1854 | js_value_t *result = delegate->callbacks.own_keys(env, delegate->data); 1855 | 1856 | if (JS_HasException(env->context)) return js__error(env); 1857 | 1858 | uint32_t len; 1859 | err = js_get_array_length(env, result, &len); 1860 | assert(err == 0); 1861 | 1862 | JSPropertyEnum *properties = js_mallocz(env->context, sizeof(JSPropertyEnum) * len); 1863 | 1864 | for (uint32_t i = 0; i < len; i++) { 1865 | js_value_t *value; 1866 | err = js_get_element(env, result, i, &value); 1867 | assert(err == 0); 1868 | 1869 | properties[i].atom = JS_ValueToAtom(env->context, value->value); 1870 | } 1871 | 1872 | *pproperties = properties; 1873 | *plen = len; 1874 | 1875 | return 0; 1876 | } 1877 | 1878 | *pproperties = NULL; 1879 | *plen = 0; 1880 | 1881 | return 0; 1882 | } 1883 | 1884 | static int 1885 | js__on_delegate_delete_property(JSContext *context, JSValueConst object, JSAtom name) { 1886 | js_env_t *env = (js_env_t *) JS_GetContextOpaque(context); 1887 | 1888 | js_delegate_t *delegate = (js_delegate_t *) JS_GetOpaque(object, env->classes.delegate); 1889 | 1890 | if (delegate->callbacks.delete_property) { 1891 | JSValue property = JS_AtomToValue(env->context, name); 1892 | 1893 | bool success = delegate->callbacks.delete_property(env, &(js_value_t) {property}, delegate->data); 1894 | 1895 | JS_FreeValue(env->context, property); 1896 | 1897 | if (JS_HasException(env->context)) return js__error(env); 1898 | 1899 | return success; 1900 | } 1901 | 1902 | return 0; 1903 | } 1904 | 1905 | static int 1906 | js__on_delegate_set_property(JSContext *context, JSValueConst object, JSAtom name, JSValueConst value, JSValueConst receiver, int flags) { 1907 | js_env_t *env = (js_env_t *) JS_GetContextOpaque(context); 1908 | 1909 | js_delegate_t *delegate = (js_delegate_t *) JS_GetOpaque(object, env->classes.delegate); 1910 | 1911 | if (delegate->callbacks.set) { 1912 | JSValue property = JS_AtomToValue(env->context, name); 1913 | 1914 | bool success = delegate->callbacks.set(env, &(js_value_t) {property}, &(js_value_t) {value}, delegate->data); 1915 | 1916 | JS_FreeValue(env->context, property); 1917 | 1918 | if (JS_HasException(env->context)) return js__error(env); 1919 | 1920 | return success; 1921 | } 1922 | 1923 | return 0; 1924 | } 1925 | 1926 | static void 1927 | js__on_delegate_finalize(JSRuntime *runtime, JSValue value) { 1928 | js_env_t *env = (js_env_t *) JS_GetRuntimeOpaque(runtime); 1929 | 1930 | js_delegate_t *delegate = (js_delegate_t *) JS_GetOpaque(value, env->classes.delegate); 1931 | 1932 | if (delegate->finalize_cb) { 1933 | delegate->finalize_cb(env, delegate->data, delegate->finalize_hint); 1934 | } 1935 | 1936 | free(delegate); 1937 | } 1938 | 1939 | int 1940 | js_create_delegate(js_env_t *env, const js_delegate_callbacks_t *callbacks, void *data, js_finalize_cb finalize_cb, void *finalize_hint, js_value_t **result) { 1941 | // Allow continuing even with a pending exception 1942 | 1943 | js_delegate_t *delegate = malloc(sizeof(js_delegate_t)); 1944 | 1945 | delegate->data = data; 1946 | delegate->finalize_cb = finalize_cb; 1947 | delegate->finalize_hint = finalize_hint; 1948 | 1949 | memcpy(&delegate->callbacks, callbacks, sizeof(js_delegate_callbacks_t)); 1950 | 1951 | JSValue object = JS_NewObjectClass(env->context, env->classes.delegate); 1952 | 1953 | JS_SetOpaque(object, delegate); 1954 | 1955 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 1956 | 1957 | wrapper->value = object; 1958 | 1959 | *result = wrapper; 1960 | 1961 | js__attach_to_handle_scope(env, env->scope, wrapper); 1962 | 1963 | return 0; 1964 | } 1965 | 1966 | static void 1967 | js__on_finalizer_finalize(JSRuntime *runtime, JSValue value) { 1968 | js_env_t *env = (js_env_t *) JS_GetRuntimeOpaque(runtime); 1969 | 1970 | js_finalizer_list_t *next = (js_finalizer_list_t *) JS_GetOpaque(value, env->classes.finalizer); 1971 | js_finalizer_list_t *prev; 1972 | 1973 | while (next) { 1974 | js_finalizer_t *finalizer = &next->finalizer; 1975 | 1976 | if (finalizer->finalize_cb) { 1977 | finalizer->finalize_cb(env, finalizer->data, finalizer->finalize_hint); 1978 | } 1979 | 1980 | prev = next; 1981 | next = next->next; 1982 | 1983 | free(prev); 1984 | } 1985 | } 1986 | 1987 | int 1988 | js_add_finalizer(js_env_t *env, js_value_t *object, void *data, js_finalize_cb finalize_cb, void *finalize_hint, js_ref_t **result) { 1989 | // Allow continuing even with a pending exception 1990 | 1991 | int err; 1992 | 1993 | js_finalizer_list_t *prev = malloc(sizeof(js_finalizer_list_t)); 1994 | 1995 | js_finalizer_t *finalizer = &prev->finalizer; 1996 | 1997 | finalizer->data = data; 1998 | finalizer->finalize_cb = finalize_cb; 1999 | finalizer->finalize_hint = finalize_hint; 2000 | 2001 | JSAtom atom = JS_NewAtom(env->context, "__native_finalizer"); 2002 | 2003 | JSValue external; 2004 | 2005 | if (JS_HasProperty(env->context, object->value, atom) == 1) { 2006 | external = JS_GetProperty(env->context, object->value, atom); 2007 | } else { 2008 | external = JS_NewObjectClass(env->context, env->classes.finalizer); 2009 | 2010 | JS_SetOpaque(external, NULL); 2011 | 2012 | err = JS_DefinePropertyValue(env->context, object->value, atom, external, 0); 2013 | assert(err >= 0); 2014 | 2015 | JS_DupValue(env->context, external); 2016 | } 2017 | 2018 | JS_FreeAtom(env->context, atom); 2019 | 2020 | prev->next = (js_finalizer_list_t *) JS_GetOpaque(external, env->classes.finalizer); 2021 | 2022 | JS_SetOpaque(external, prev); 2023 | 2024 | JS_FreeValue(env->context, external); 2025 | 2026 | if (result) return js_create_reference(env, object, 0, result); 2027 | 2028 | return 0; 2029 | } 2030 | 2031 | static void 2032 | js__on_type_tag_finalize(JSRuntime *runtime, JSValue value) { 2033 | js_env_t *env = (js_env_t *) JS_GetRuntimeOpaque(runtime); 2034 | 2035 | js_type_tag_t *tag = (js_type_tag_t *) JS_GetOpaque(value, env->classes.type_tag); 2036 | 2037 | free(tag); 2038 | } 2039 | 2040 | int 2041 | js_add_type_tag(js_env_t *env, js_value_t *object, const js_type_tag_t *tag) { 2042 | if (JS_HasException(env->context)) return js__error(env); 2043 | 2044 | int err; 2045 | 2046 | JSAtom atom = JS_NewAtom(env->context, "__native_type_tag"); 2047 | 2048 | if (JS_HasProperty(env->context, object->value, atom) == 1) { 2049 | JS_FreeAtom(env->context, atom); 2050 | 2051 | err = js_throw_errorf(env, NULL, "Object is already type tagged"); 2052 | assert(err == 0); 2053 | 2054 | return js__error(env); 2055 | } 2056 | 2057 | js_type_tag_t *existing = malloc(sizeof(js_type_tag_t)); 2058 | 2059 | existing->lower = tag->lower; 2060 | existing->upper = tag->upper; 2061 | 2062 | JSValue external = JS_NewObjectClass(env->context, env->classes.type_tag); 2063 | 2064 | JS_SetOpaque(external, existing); 2065 | 2066 | err = JS_DefinePropertyValue(env->context, object->value, atom, external, 0); 2067 | assert(err >= 0); 2068 | 2069 | JS_FreeAtom(env->context, atom); 2070 | 2071 | return 0; 2072 | } 2073 | 2074 | int 2075 | js_check_type_tag(js_env_t *env, js_value_t *object, const js_type_tag_t *tag, bool *result) { 2076 | if (JS_HasException(env->context)) return js__error(env); 2077 | 2078 | JSAtom atom = JS_NewAtom(env->context, "__native_type_tag"); 2079 | 2080 | *result = false; 2081 | 2082 | if (JS_HasProperty(env->context, object->value, atom) == 1) { 2083 | JSValue external = JS_GetProperty(env->context, object->value, atom); 2084 | 2085 | js_type_tag_t *existing = (js_type_tag_t *) JS_GetOpaque(external, env->classes.type_tag); 2086 | 2087 | JS_FreeValue(env->context, external); 2088 | 2089 | *result = existing->lower == tag->lower && existing->upper == tag->upper; 2090 | } 2091 | 2092 | JS_FreeAtom(env->context, atom); 2093 | 2094 | return 0; 2095 | } 2096 | 2097 | int 2098 | js_create_int32(js_env_t *env, int32_t value, js_value_t **result) { 2099 | // Allow continuing even with a pending exception 2100 | 2101 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2102 | 2103 | wrapper->value = JS_NewInt32(env->context, value); 2104 | 2105 | *result = wrapper; 2106 | 2107 | js__attach_to_handle_scope(env, env->scope, wrapper); 2108 | 2109 | return 0; 2110 | } 2111 | 2112 | int 2113 | js_create_uint32(js_env_t *env, uint32_t value, js_value_t **result) { 2114 | // Allow continuing even with a pending exception 2115 | 2116 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2117 | 2118 | wrapper->value = JS_NewUint32(env->context, value); 2119 | 2120 | *result = wrapper; 2121 | 2122 | js__attach_to_handle_scope(env, env->scope, wrapper); 2123 | 2124 | return 0; 2125 | } 2126 | 2127 | int 2128 | js_create_int64(js_env_t *env, int64_t value, js_value_t **result) { 2129 | // Allow continuing even with a pending exception 2130 | 2131 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2132 | 2133 | wrapper->value = JS_NewInt64(env->context, value); 2134 | 2135 | *result = wrapper; 2136 | 2137 | js__attach_to_handle_scope(env, env->scope, wrapper); 2138 | 2139 | return 0; 2140 | } 2141 | 2142 | int 2143 | js_create_double(js_env_t *env, double value, js_value_t **result) { 2144 | // Allow continuing even with a pending exception 2145 | 2146 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2147 | 2148 | wrapper->value = JS_NewFloat64(env->context, value); 2149 | 2150 | *result = wrapper; 2151 | 2152 | js__attach_to_handle_scope(env, env->scope, wrapper); 2153 | 2154 | return 0; 2155 | } 2156 | 2157 | int 2158 | js_create_bigint_int64(js_env_t *env, int64_t value, js_value_t **result) { 2159 | // Allow continuing even with a pending exception 2160 | 2161 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2162 | 2163 | wrapper->value = JS_NewBigInt64(env->context, value); 2164 | 2165 | *result = wrapper; 2166 | 2167 | js__attach_to_handle_scope(env, env->scope, wrapper); 2168 | 2169 | return 0; 2170 | } 2171 | 2172 | int 2173 | js_create_bigint_uint64(js_env_t *env, uint64_t value, js_value_t **result) { 2174 | // Allow continuing even with a pending exception 2175 | 2176 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2177 | 2178 | wrapper->value = JS_NewBigUint64(env->context, value); 2179 | 2180 | *result = wrapper; 2181 | 2182 | js__attach_to_handle_scope(env, env->scope, wrapper); 2183 | 2184 | return 0; 2185 | } 2186 | 2187 | int 2188 | js_create_string_utf8(js_env_t *env, const utf8_t *str, size_t len, js_value_t **result) { 2189 | if (JS_HasException(env->context)) return js__error(env); 2190 | 2191 | JSValue value; 2192 | 2193 | if (len == (size_t) -1) { 2194 | value = JS_NewString(env->context, (char *) str); 2195 | } else { 2196 | value = JS_NewStringLen(env->context, (char *) str, len); 2197 | } 2198 | 2199 | if (JS_IsException(value)) { 2200 | if (env->depth == 0) { 2201 | JSValue error = JS_GetException(env->context); 2202 | 2203 | js__on_uncaught_exception(env->context, error); 2204 | } 2205 | 2206 | return js__error(env); 2207 | } 2208 | 2209 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2210 | 2211 | wrapper->value = value; 2212 | 2213 | *result = wrapper; 2214 | 2215 | js__attach_to_handle_scope(env, env->scope, wrapper); 2216 | 2217 | return 0; 2218 | } 2219 | 2220 | int 2221 | js_create_string_utf16le(js_env_t *env, const utf16_t *str, size_t len, js_value_t **result) { 2222 | if (JS_HasException(env->context)) return js__error(env); 2223 | 2224 | if (len == (size_t) -1) len = wcslen((wchar_t *) str); 2225 | 2226 | size_t utf8_len = utf8_length_from_utf16le(str, len); 2227 | 2228 | utf8_t *utf8 = malloc(utf8_len); 2229 | 2230 | utf16le_convert_to_utf8(str, len, utf8); 2231 | 2232 | JSValue value = JS_NewStringLen(env->context, (char *) utf8, utf8_len); 2233 | 2234 | free(utf8); 2235 | 2236 | if (JS_IsException(value)) { 2237 | if (env->depth == 0) { 2238 | JSValue error = JS_GetException(env->context); 2239 | 2240 | js__on_uncaught_exception(env->context, error); 2241 | } 2242 | 2243 | return js__error(env); 2244 | } 2245 | 2246 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2247 | 2248 | wrapper->value = value; 2249 | 2250 | *result = wrapper; 2251 | 2252 | js__attach_to_handle_scope(env, env->scope, wrapper); 2253 | 2254 | return 0; 2255 | } 2256 | 2257 | int 2258 | js_create_string_latin1(js_env_t *env, const latin1_t *str, size_t len, js_value_t **result) { 2259 | if (JS_HasException(env->context)) return js__error(env); 2260 | 2261 | if (len == (size_t) -1) len = strlen((char *) str); 2262 | 2263 | size_t utf8_len = utf8_length_from_latin1(str, len); 2264 | 2265 | utf8_t *utf8 = malloc(utf8_len); 2266 | 2267 | latin1_convert_to_utf8(str, len, utf8); 2268 | 2269 | JSValue value = JS_NewStringLen(env->context, (char *) utf8, utf8_len); 2270 | 2271 | free(utf8); 2272 | 2273 | if (JS_IsException(value)) { 2274 | if (env->depth == 0) { 2275 | JSValue error = JS_GetException(env->context); 2276 | 2277 | js__on_uncaught_exception(env->context, error); 2278 | } 2279 | 2280 | return js__error(env); 2281 | } 2282 | 2283 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2284 | 2285 | wrapper->value = value; 2286 | 2287 | *result = wrapper; 2288 | 2289 | js__attach_to_handle_scope(env, env->scope, wrapper); 2290 | 2291 | return 0; 2292 | } 2293 | 2294 | int 2295 | js_create_external_string_utf8(js_env_t *env, utf8_t *str, size_t len, js_finalize_cb finalize_cb, void *finalize_hint, js_value_t **result, bool *copied) { 2296 | int err; 2297 | err = js_create_string_utf8(env, str, len, result); 2298 | if (err < 0) return err; 2299 | 2300 | if (copied) *copied = true; 2301 | 2302 | if (finalize_cb) finalize_cb(env, str, finalize_hint); 2303 | 2304 | return 0; 2305 | } 2306 | 2307 | int 2308 | js_create_external_string_utf16le(js_env_t *env, utf16_t *str, size_t len, js_finalize_cb finalize_cb, void *finalize_hint, js_value_t **result, bool *copied) { 2309 | int err; 2310 | err = js_create_string_utf16le(env, str, len, result); 2311 | if (err < 0) return err; 2312 | 2313 | if (copied) *copied = true; 2314 | 2315 | if (finalize_cb) finalize_cb(env, str, finalize_hint); 2316 | 2317 | return 0; 2318 | } 2319 | 2320 | int 2321 | js_create_external_string_latin1(js_env_t *env, latin1_t *str, size_t len, js_finalize_cb finalize_cb, void *finalize_hint, js_value_t **result, bool *copied) { 2322 | int err; 2323 | err = js_create_string_latin1(env, str, len, result); 2324 | if (err < 0) return err; 2325 | 2326 | if (copied) *copied = true; 2327 | 2328 | if (finalize_cb) finalize_cb(env, str, finalize_hint); 2329 | 2330 | return 0; 2331 | } 2332 | 2333 | int 2334 | js_create_property_key_utf8(js_env_t *env, const utf8_t *str, size_t len, js_value_t **result) { 2335 | return js_create_string_utf8(env, str, len, result); 2336 | } 2337 | 2338 | int 2339 | js_create_property_key_utf16le(js_env_t *env, const utf16_t *str, size_t len, js_value_t **result) { 2340 | return js_create_string_utf16le(env, str, len, result); 2341 | } 2342 | 2343 | int 2344 | js_create_property_key_latin1(js_env_t *env, const latin1_t *str, size_t len, js_value_t **result) { 2345 | return js_create_string_latin1(env, str, len, result); 2346 | } 2347 | 2348 | int 2349 | js_create_symbol(js_env_t *env, js_value_t *description, js_value_t **result) { 2350 | // Allow continuing even with a pending exception 2351 | 2352 | JSValue global = JS_GetGlobalObject(env->context); 2353 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Symbol"); 2354 | 2355 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2356 | 2357 | JSValue arg = description == NULL ? JS_NULL : description->value; 2358 | 2359 | wrapper->value = JS_Call(env->context, constructor, global, 1, &arg); 2360 | 2361 | *result = wrapper; 2362 | 2363 | js__attach_to_handle_scope(env, env->scope, wrapper); 2364 | 2365 | JS_FreeValue(env->context, constructor); 2366 | JS_FreeValue(env->context, global); 2367 | 2368 | return 0; 2369 | } 2370 | 2371 | int 2372 | js_create_object(js_env_t *env, js_value_t **result) { 2373 | // Allow continuing even with a pending exception 2374 | 2375 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2376 | 2377 | wrapper->value = JS_NewObject(env->context); 2378 | 2379 | *result = wrapper; 2380 | 2381 | js__attach_to_handle_scope(env, env->scope, wrapper); 2382 | 2383 | return 0; 2384 | } 2385 | 2386 | static void 2387 | js__on_function_finalize(JSRuntime *runtime, JSValue value) { 2388 | js_env_t *env = (js_env_t *) JS_GetRuntimeOpaque(runtime); 2389 | 2390 | js_callback_t *callback = (js_callback_t *) JS_GetOpaque(value, env->classes.function); 2391 | 2392 | free(callback); 2393 | } 2394 | 2395 | static JSValue 2396 | js__on_function_call(JSContext *context, JSValueConst receiver, int argc, JSValueConst *argv, int magic, JSValue *data) { 2397 | int err; 2398 | 2399 | js_env_t *env = (js_env_t *) JS_GetContextOpaque(context); 2400 | 2401 | js_callback_t *callback = (js_callback_t *) JS_GetOpaque(*data, env->classes.function); 2402 | 2403 | js_callback_info_t callback_info = { 2404 | .callback = callback, 2405 | .argc = argc, 2406 | .argv = argv, 2407 | .receiver = receiver, 2408 | .new_target = JS_NULL, 2409 | }; 2410 | 2411 | js_handle_scope_t *scope; 2412 | err = js_open_handle_scope(env, &scope); 2413 | assert(err == 0); 2414 | 2415 | js_value_t *result = callback->cb(env, &callback_info); 2416 | 2417 | JSValue value; 2418 | 2419 | if (JS_HasException(env->context)) { 2420 | if (result) JS_FreeValue(env->context, result->value); 2421 | value = JS_EXCEPTION; 2422 | } else { 2423 | if (result) value = JS_DupValue(env->context, result->value); 2424 | else value = JS_UNDEFINED; 2425 | } 2426 | 2427 | err = js_close_handle_scope(env, scope); 2428 | assert(err == 0); 2429 | 2430 | return value; 2431 | } 2432 | 2433 | int 2434 | js_create_function(js_env_t *env, const char *name, size_t len, js_function_cb cb, void *data, js_value_t **result) { 2435 | if (JS_HasException(env->context)) return js__error(env); 2436 | 2437 | js_callback_t *callback = malloc(sizeof(js_callback_t)); 2438 | 2439 | callback->cb = cb; 2440 | callback->data = data; 2441 | 2442 | JSValue external = JS_NewObjectClass(env->context, env->classes.function); 2443 | 2444 | JS_SetOpaque(external, callback); 2445 | 2446 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2447 | 2448 | wrapper->value = JS_NewCFunctionData(env->context, js__on_function_call, 0, 0, 1, &external); 2449 | 2450 | JS_FreeValue(env->context, external); 2451 | 2452 | *result = wrapper; 2453 | 2454 | js__attach_to_handle_scope(env, env->scope, wrapper); 2455 | 2456 | return 0; 2457 | } 2458 | 2459 | int 2460 | js_create_function_with_source(js_env_t *env, const char *name, size_t name_len, const char *file, size_t file_len, js_value_t *const args[], size_t args_len, int offset, js_value_t *source, js_value_t **result) { 2461 | if (JS_HasException(env->context)) return js__error(env); 2462 | 2463 | const char *str; 2464 | 2465 | size_t buf_len = 0; 2466 | 2467 | if (name) { 2468 | buf_len += strlen("const "); 2469 | 2470 | if (name_len == (size_t) -1) buf_len += strlen(name); 2471 | else buf_len += name_len; 2472 | 2473 | buf_len += strlen(" = "); 2474 | } 2475 | 2476 | buf_len += strlen("("); 2477 | 2478 | for (int i = 0; i < args_len; i++) { 2479 | if (i != 0) buf_len += strlen(", "); 2480 | 2481 | str = JS_ToCString(env->context, args[i]->value); 2482 | buf_len += strlen(str); 2483 | JS_FreeCString(env->context, str); 2484 | } 2485 | 2486 | buf_len += strlen(") => {\n"); 2487 | 2488 | str = JS_ToCString(env->context, source->value); 2489 | buf_len += strlen(str); 2490 | JS_FreeCString(env->context, str); 2491 | 2492 | buf_len += strlen("}\n"); 2493 | 2494 | if (name) { 2495 | if (name_len == (size_t) -1) buf_len += strlen(name); 2496 | else buf_len += name_len; 2497 | 2498 | buf_len += strlen("\n"); 2499 | } 2500 | 2501 | char *buf = malloc(buf_len + 1 /* NULL */); 2502 | 2503 | buf[0] = '\0'; 2504 | 2505 | if (name) { 2506 | strcat(buf, "const "); 2507 | 2508 | if (name_len == (size_t) -1) strcat(buf, name); 2509 | else strncat(buf, name, name_len); 2510 | 2511 | strcat(buf, " = "); 2512 | } 2513 | 2514 | strcat(buf, "("); 2515 | 2516 | for (int i = 0; i < args_len; i++) { 2517 | if (i != 0) strcat(buf, ", "); 2518 | 2519 | str = JS_ToCString(env->context, args[i]->value); 2520 | strcat(buf, str); 2521 | JS_FreeCString(env->context, str); 2522 | } 2523 | 2524 | strcat(buf, ") => {\n"); 2525 | 2526 | str = JS_ToCString(env->context, source->value); 2527 | strcat(buf, str); 2528 | JS_FreeCString(env->context, str); 2529 | 2530 | strcat(buf, "}\n"); 2531 | 2532 | if (name) { 2533 | if (name_len == (size_t) -1) strcat(buf, name); 2534 | else strncat(buf, name, name_len); 2535 | 2536 | strcat(buf, "\n"); 2537 | } 2538 | 2539 | if (file == NULL) file = ""; 2540 | 2541 | JSValue function = JS_Eval( 2542 | env->context, 2543 | buf, 2544 | buf_len, 2545 | file, 2546 | JS_EVAL_TYPE_GLOBAL 2547 | ); 2548 | 2549 | free(buf); 2550 | 2551 | if (JS_IsException(function)) { 2552 | if (env->depth == 0) { 2553 | JSValue error = JS_GetException(env->context); 2554 | 2555 | js__on_uncaught_exception(env->context, error); 2556 | } 2557 | 2558 | return js__error(env); 2559 | } 2560 | 2561 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2562 | 2563 | wrapper->value = function; 2564 | 2565 | *result = wrapper; 2566 | 2567 | js__attach_to_handle_scope(env, env->scope, wrapper); 2568 | 2569 | return 0; 2570 | } 2571 | 2572 | int 2573 | js_create_typed_function(js_env_t *env, const char *name, size_t len, js_function_cb cb, const js_callback_signature_t *signature, const void *address, void *data, js_value_t **result) { 2574 | return js_create_function(env, name, len, cb, data, result); 2575 | } 2576 | 2577 | int 2578 | js_create_array(js_env_t *env, js_value_t **result) { 2579 | // Allow continuing even with a pending exception 2580 | 2581 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2582 | 2583 | wrapper->value = JS_NewArray(env->context); 2584 | 2585 | *result = wrapper; 2586 | 2587 | js__attach_to_handle_scope(env, env->scope, wrapper); 2588 | 2589 | return 0; 2590 | } 2591 | 2592 | int 2593 | js_create_array_with_length(js_env_t *env, size_t len, js_value_t **result) { 2594 | // Allow continuing even with a pending exception 2595 | 2596 | JSValue global = JS_GetGlobalObject(env->context); 2597 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Array"); 2598 | 2599 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2600 | 2601 | JSValue arg = JS_NewUint32(env->context, len); 2602 | 2603 | wrapper->value = JS_CallConstructor(env->context, constructor, 1, &arg); 2604 | 2605 | *result = wrapper; 2606 | 2607 | js__attach_to_handle_scope(env, env->scope, wrapper); 2608 | 2609 | JS_FreeValue(env->context, arg); 2610 | JS_FreeValue(env->context, constructor); 2611 | JS_FreeValue(env->context, global); 2612 | 2613 | return 0; 2614 | } 2615 | 2616 | static void 2617 | js__on_external_finalize(JSRuntime *runtime, JSValue value) { 2618 | js_env_t *env = (js_env_t *) JS_GetRuntimeOpaque(runtime); 2619 | 2620 | js_finalizer_t *finalizer = (js_finalizer_t *) JS_GetOpaque(value, env->classes.external); 2621 | 2622 | if (finalizer->finalize_cb) { 2623 | finalizer->finalize_cb(env, finalizer->data, finalizer->finalize_hint); 2624 | } 2625 | 2626 | free(finalizer); 2627 | } 2628 | 2629 | int 2630 | js_create_external(js_env_t *env, void *data, js_finalize_cb finalize_cb, void *finalize_hint, js_value_t **result) { 2631 | // Allow continuing even with a pending exception 2632 | 2633 | js_finalizer_t *finalizer = malloc(sizeof(js_finalizer_t)); 2634 | 2635 | finalizer->data = data; 2636 | finalizer->finalize_cb = finalize_cb; 2637 | finalizer->finalize_hint = finalize_hint; 2638 | 2639 | JSValue external = JS_NewObjectClass(env->context, env->classes.external); 2640 | 2641 | JS_SetOpaque(external, finalizer); 2642 | 2643 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2644 | 2645 | wrapper->value = external; 2646 | 2647 | *result = wrapper; 2648 | 2649 | js__attach_to_handle_scope(env, env->scope, wrapper); 2650 | 2651 | return 0; 2652 | } 2653 | 2654 | int 2655 | js_create_date(js_env_t *env, double time, js_value_t **result) { 2656 | // Allow continuing even with a pending exception 2657 | 2658 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2659 | 2660 | wrapper->value = JS_NewDate(env->context, time); 2661 | 2662 | *result = wrapper; 2663 | 2664 | js__attach_to_handle_scope(env, env->scope, wrapper); 2665 | 2666 | return 0; 2667 | } 2668 | 2669 | int 2670 | js_create_error(js_env_t *env, js_value_t *code, js_value_t *message, js_value_t **result) { 2671 | // Allow continuing even with a pending exception 2672 | 2673 | JSValue global = JS_GetGlobalObject(env->context); 2674 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Error"); 2675 | 2676 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2677 | 2678 | JSValue arg = message->value; 2679 | 2680 | JSValue error = JS_CallConstructor(env->context, constructor, 1, &arg); 2681 | 2682 | if (code) { 2683 | JS_SetPropertyStr(env->context, error, "code", JS_DupValue(env->context, code->value)); 2684 | } 2685 | 2686 | wrapper->value = error; 2687 | 2688 | *result = wrapper; 2689 | 2690 | js__attach_to_handle_scope(env, env->scope, wrapper); 2691 | 2692 | JS_FreeValue(env->context, constructor); 2693 | JS_FreeValue(env->context, global); 2694 | 2695 | return 0; 2696 | } 2697 | 2698 | int 2699 | js_create_type_error(js_env_t *env, js_value_t *code, js_value_t *message, js_value_t **result) { 2700 | // Allow continuing even with a pending exception 2701 | 2702 | JSValue global = JS_GetGlobalObject(env->context); 2703 | JSValue constructor = JS_GetPropertyStr(env->context, global, "TypeError"); 2704 | 2705 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2706 | 2707 | JSValue arg = message->value; 2708 | 2709 | JSValue error = JS_CallConstructor(env->context, constructor, 1, &arg); 2710 | 2711 | if (code) { 2712 | JS_SetPropertyStr(env->context, error, "code", JS_DupValue(env->context, code->value)); 2713 | } 2714 | 2715 | wrapper->value = error; 2716 | 2717 | *result = wrapper; 2718 | 2719 | js__attach_to_handle_scope(env, env->scope, wrapper); 2720 | 2721 | JS_FreeValue(env->context, constructor); 2722 | JS_FreeValue(env->context, global); 2723 | 2724 | return 0; 2725 | } 2726 | 2727 | int 2728 | js_create_range_error(js_env_t *env, js_value_t *code, js_value_t *message, js_value_t **result) { 2729 | // Allow continuing even with a pending exception 2730 | 2731 | JSValue global = JS_GetGlobalObject(env->context); 2732 | JSValue constructor = JS_GetPropertyStr(env->context, global, "RangeError"); 2733 | 2734 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2735 | 2736 | JSValue arg = message->value; 2737 | 2738 | JSValue error = JS_CallConstructor(env->context, constructor, 1, &arg); 2739 | 2740 | if (code) { 2741 | JS_SetPropertyStr(env->context, error, "code", JS_DupValue(env->context, code->value)); 2742 | } 2743 | 2744 | wrapper->value = error; 2745 | 2746 | *result = wrapper; 2747 | 2748 | js__attach_to_handle_scope(env, env->scope, wrapper); 2749 | 2750 | JS_FreeValue(env->context, constructor); 2751 | JS_FreeValue(env->context, global); 2752 | 2753 | return 0; 2754 | } 2755 | 2756 | int 2757 | js_create_syntax_error(js_env_t *env, js_value_t *code, js_value_t *message, js_value_t **result) { 2758 | // Allow continuing even with a pending exception 2759 | 2760 | JSValue global = JS_GetGlobalObject(env->context); 2761 | JSValue constructor = JS_GetPropertyStr(env->context, global, "SyntaxError"); 2762 | 2763 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2764 | 2765 | JSValue arg = message->value; 2766 | 2767 | JSValue error = JS_CallConstructor(env->context, constructor, 1, &arg); 2768 | 2769 | if (code) { 2770 | JS_SetPropertyStr(env->context, error, "code", JS_DupValue(env->context, code->value)); 2771 | } 2772 | 2773 | wrapper->value = error; 2774 | 2775 | *result = wrapper; 2776 | 2777 | js__attach_to_handle_scope(env, env->scope, wrapper); 2778 | 2779 | JS_FreeValue(env->context, constructor); 2780 | JS_FreeValue(env->context, global); 2781 | 2782 | return 0; 2783 | } 2784 | 2785 | int 2786 | js_create_promise(js_env_t *env, js_deferred_t **deferred, js_value_t **promise) { 2787 | // Allow continuing even with a pending exception 2788 | 2789 | *deferred = malloc(sizeof(js_deferred_t)); 2790 | 2791 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2792 | 2793 | JSValue functions[2]; 2794 | 2795 | wrapper->value = JS_NewPromiseCapability(env->context, functions); 2796 | 2797 | (*deferred)->resolve = functions[0]; 2798 | (*deferred)->reject = functions[1]; 2799 | 2800 | *promise = wrapper; 2801 | 2802 | js__attach_to_handle_scope(env, env->scope, wrapper); 2803 | 2804 | return 0; 2805 | } 2806 | 2807 | static inline int 2808 | js__conclude_deferred(js_env_t *env, js_deferred_t *deferred, js_value_t *resolution, bool resolved) { 2809 | // Allow continuing even with a pending exception 2810 | 2811 | JSValue global = JS_GetGlobalObject(env->context); 2812 | 2813 | JSValue result = JS_Call(env->context, resolved ? deferred->resolve : deferred->reject, global, 1, &resolution->value); 2814 | 2815 | if (env->depth == 0) js__on_run_microtasks(env); 2816 | 2817 | JS_FreeValue(env->context, global); 2818 | JS_FreeValue(env->context, result); 2819 | JS_FreeValue(env->context, deferred->resolve); 2820 | JS_FreeValue(env->context, deferred->reject); 2821 | 2822 | free(deferred); 2823 | 2824 | return 0; 2825 | } 2826 | 2827 | int 2828 | js_resolve_deferred(js_env_t *env, js_deferred_t *deferred, js_value_t *resolution) { 2829 | return js__conclude_deferred(env, deferred, resolution, true); 2830 | } 2831 | 2832 | int 2833 | js_reject_deferred(js_env_t *env, js_deferred_t *deferred, js_value_t *resolution) { 2834 | return js__conclude_deferred(env, deferred, resolution, false); 2835 | } 2836 | 2837 | int 2838 | js_get_promise_state(js_env_t *env, js_value_t *promise, js_promise_state_t *result) { 2839 | // Allow continuing even with a pending exception 2840 | 2841 | switch (JS_PromiseState(env->context, promise->value)) { 2842 | case JS_PROMISE_PENDING: 2843 | *result = js_promise_pending; 2844 | break; 2845 | case JS_PROMISE_FULFILLED: 2846 | *result = js_promise_fulfilled; 2847 | break; 2848 | case JS_PROMISE_REJECTED: 2849 | *result = js_promise_rejected; 2850 | break; 2851 | } 2852 | 2853 | return 0; 2854 | } 2855 | 2856 | int 2857 | js_get_promise_result(js_env_t *env, js_value_t *promise, js_value_t **result) { 2858 | // Allow continuing even with a pending exception 2859 | 2860 | assert(JS_PromiseState(env->context, promise->value) != JS_PROMISE_PENDING); 2861 | 2862 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2863 | 2864 | wrapper->value = JS_PromiseResult(env->context, promise->value); 2865 | 2866 | *result = wrapper; 2867 | 2868 | js__attach_to_handle_scope(env, env->scope, wrapper); 2869 | 2870 | return 0; 2871 | } 2872 | 2873 | static void 2874 | js__on_arraybuffer_finalize(JSRuntime *runtime, void *opaque, void *ptr) { 2875 | free(ptr); 2876 | } 2877 | 2878 | int 2879 | js_create_arraybuffer(js_env_t *env, size_t len, void **data, js_value_t **result) { 2880 | if (JS_HasException(env->context)) return js__error(env); 2881 | 2882 | int err; 2883 | 2884 | if (len > INT32_MAX) goto err; 2885 | 2886 | uint8_t *bytes = malloc(len); 2887 | 2888 | if (bytes == NULL) goto err; 2889 | 2890 | memset(bytes, 0, len); 2891 | 2892 | if (data) { 2893 | *data = bytes; 2894 | } 2895 | 2896 | JSValue arraybuffer = JS_NewArrayBuffer(env->context, bytes, len, js__on_arraybuffer_finalize, NULL, false); 2897 | 2898 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2899 | 2900 | wrapper->value = arraybuffer; 2901 | 2902 | *result = wrapper; 2903 | 2904 | js__attach_to_handle_scope(env, env->scope, wrapper); 2905 | 2906 | return 0; 2907 | 2908 | err: 2909 | err = js_throw_range_error(env, NULL, "Array buffer allocation failed"); 2910 | assert(err == 0); 2911 | 2912 | return js__error(env); 2913 | } 2914 | 2915 | static void 2916 | js__on_backed_arraybuffer_finalize(JSRuntime *runtime, void *opaque, void *ptr) { 2917 | js_arraybuffer_backing_store_t *backing_store = (js_arraybuffer_backing_store_t *) opaque; 2918 | 2919 | if (--backing_store->references == 0) { 2920 | JS_FreeValueRT(runtime, backing_store->owner); 2921 | 2922 | free(backing_store); 2923 | } 2924 | } 2925 | 2926 | int 2927 | js_create_arraybuffer_with_backing_store(js_env_t *env, js_arraybuffer_backing_store_t *backing_store, void **data, size_t *len, js_value_t **result) { 2928 | if (JS_HasException(env->context)) return js__error(env); 2929 | 2930 | backing_store->references++; 2931 | 2932 | if (data) { 2933 | *data = backing_store->data; 2934 | } 2935 | 2936 | if (len) { 2937 | *len = backing_store->len; 2938 | } 2939 | 2940 | JSValue arraybuffer = JS_NewArrayBuffer(env->context, backing_store->data, backing_store->len, js__on_backed_arraybuffer_finalize, backing_store, false); 2941 | 2942 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2943 | 2944 | wrapper->value = arraybuffer; 2945 | 2946 | *result = wrapper; 2947 | 2948 | js__attach_to_handle_scope(env, env->scope, wrapper); 2949 | 2950 | return 0; 2951 | } 2952 | 2953 | static void 2954 | js__on_unsafe_arraybuffer_finalize(JSRuntime *runtime, void *opaque, void *ptr) { 2955 | free(ptr); 2956 | } 2957 | 2958 | int 2959 | js_create_unsafe_arraybuffer(js_env_t *env, size_t len, void **data, js_value_t **result) { 2960 | if (JS_HasException(env->context)) return js__error(env); 2961 | 2962 | int err; 2963 | 2964 | if (len > INT32_MAX) goto err; 2965 | 2966 | uint8_t *bytes = malloc(len); 2967 | 2968 | if (bytes == NULL) goto err; 2969 | 2970 | if (data) { 2971 | *data = bytes; 2972 | } 2973 | 2974 | JSValue arraybuffer = JS_NewArrayBuffer(env->context, bytes, len, js__on_unsafe_arraybuffer_finalize, NULL, false); 2975 | 2976 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 2977 | 2978 | wrapper->value = arraybuffer; 2979 | 2980 | *result = wrapper; 2981 | 2982 | js__attach_to_handle_scope(env, env->scope, wrapper); 2983 | 2984 | return 0; 2985 | 2986 | err: 2987 | err = js_throw_range_error(env, NULL, "Array buffer allocation failed"); 2988 | assert(err == 0); 2989 | 2990 | return js__error(env); 2991 | } 2992 | 2993 | static void 2994 | js__on_external_arraybuffer_finalize(JSRuntime *runtime, void *opaque, void *ptr) { 2995 | if (ptr == NULL) return; 2996 | 2997 | js_env_t *env = (js_env_t *) JS_GetRuntimeOpaque(runtime); 2998 | 2999 | js_finalizer_t *finalizer = (js_finalizer_t *) opaque; 3000 | 3001 | if (finalizer->finalize_cb) { 3002 | finalizer->finalize_cb(env, finalizer->data, finalizer->finalize_hint); 3003 | } 3004 | 3005 | free(finalizer); 3006 | } 3007 | 3008 | int 3009 | js_create_external_arraybuffer(js_env_t *env, void *data, size_t len, js_finalize_cb finalize_cb, void *finalize_hint, js_value_t **result) { 3010 | if (JS_HasException(env->context)) return js__error(env); 3011 | 3012 | js_finalizer_t *finalizer = malloc(sizeof(js_finalizer_t)); 3013 | 3014 | finalizer->data = data; 3015 | finalizer->finalize_cb = finalize_cb; 3016 | finalizer->finalize_hint = finalize_hint; 3017 | 3018 | JSValue arraybuffer = JS_NewArrayBuffer(env->context, (uint8_t *) data, len, js__on_external_arraybuffer_finalize, (void *) finalizer, false); 3019 | 3020 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 3021 | 3022 | wrapper->value = arraybuffer; 3023 | 3024 | *result = wrapper; 3025 | 3026 | js__attach_to_handle_scope(env, env->scope, wrapper); 3027 | 3028 | return 0; 3029 | } 3030 | 3031 | int 3032 | js_detach_arraybuffer(js_env_t *env, js_value_t *arraybuffer) { 3033 | // Allow continuing even with a pending exception 3034 | 3035 | JS_DetachArrayBuffer(env->context, arraybuffer->value); 3036 | 3037 | return 0; 3038 | } 3039 | 3040 | int 3041 | js_get_arraybuffer_backing_store(js_env_t *env, js_value_t *arraybuffer, js_arraybuffer_backing_store_t **result) { 3042 | // Allow continuing even with a pending exception 3043 | 3044 | js_arraybuffer_backing_store_t *backing_store = malloc(sizeof(js_arraybuffer_backing_store_t)); 3045 | 3046 | backing_store->references = 1; 3047 | 3048 | backing_store->data = JS_GetArrayBuffer(env->context, &backing_store->len, arraybuffer->value); 3049 | 3050 | backing_store->owner = JS_DupValue(env->context, arraybuffer->value); 3051 | 3052 | *result = backing_store; 3053 | 3054 | return 0; 3055 | } 3056 | 3057 | int 3058 | js_create_sharedarraybuffer(js_env_t *env, size_t len, void **data, js_value_t **result) { 3059 | if (JS_HasException(env->context)) return js__error(env); 3060 | 3061 | js_arraybuffer_header_t *header = malloc(sizeof(js_arraybuffer_header_t) + len); 3062 | 3063 | memset(header->data, 0, len); 3064 | 3065 | header->references = 0; 3066 | header->len = len; 3067 | 3068 | if (data) { 3069 | *data = header->data; 3070 | } 3071 | 3072 | JSValue sharedarraybuffer = JS_NewArrayBuffer(env->context, header->data, header->len, NULL, NULL, true); 3073 | 3074 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 3075 | 3076 | wrapper->value = sharedarraybuffer; 3077 | 3078 | *result = wrapper; 3079 | 3080 | js__attach_to_handle_scope(env, env->scope, wrapper); 3081 | 3082 | return 0; 3083 | } 3084 | 3085 | int 3086 | js_create_sharedarraybuffer_with_backing_store(js_env_t *env, js_arraybuffer_backing_store_t *backing_store, void **data, size_t *len, js_value_t **result) { 3087 | if (JS_HasException(env->context)) return js__error(env); 3088 | 3089 | if (data) { 3090 | *data = backing_store->data; 3091 | } 3092 | 3093 | if (len) { 3094 | *len = backing_store->len; 3095 | } 3096 | 3097 | JSValue sharedarraybuffer = JS_NewArrayBuffer(env->context, backing_store->data, backing_store->len, NULL, NULL, true); 3098 | 3099 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 3100 | 3101 | wrapper->value = sharedarraybuffer; 3102 | 3103 | *result = wrapper; 3104 | 3105 | js__attach_to_handle_scope(env, env->scope, wrapper); 3106 | 3107 | return 0; 3108 | } 3109 | 3110 | int 3111 | js_create_unsafe_sharedarraybuffer(js_env_t *env, size_t len, void **data, js_value_t **result) { 3112 | if (JS_HasException(env->context)) return js__error(env); 3113 | 3114 | js_arraybuffer_header_t *header = malloc(sizeof(js_arraybuffer_header_t) + len); 3115 | 3116 | header->references = 0; 3117 | header->len = len; 3118 | 3119 | if (data) { 3120 | *data = header->data; 3121 | } 3122 | 3123 | JSValue sharedarraybuffer = JS_NewArrayBuffer(env->context, header->data, header->len, NULL, NULL, true); 3124 | 3125 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 3126 | 3127 | wrapper->value = sharedarraybuffer; 3128 | 3129 | *result = wrapper; 3130 | 3131 | js__attach_to_handle_scope(env, env->scope, wrapper); 3132 | 3133 | return 0; 3134 | } 3135 | 3136 | int 3137 | js_get_sharedarraybuffer_backing_store(js_env_t *env, js_value_t *sharedarraybuffer, js_arraybuffer_backing_store_t **result) { 3138 | // Allow continuing even with a pending exception 3139 | 3140 | js_arraybuffer_backing_store_t *backing_store = malloc(sizeof(js_arraybuffer_backing_store_t)); 3141 | 3142 | backing_store->references = 1; 3143 | 3144 | backing_store->data = JS_GetArrayBuffer(env->context, &backing_store->len, sharedarraybuffer->value); 3145 | 3146 | backing_store->owner = JS_NULL; 3147 | 3148 | *result = backing_store; 3149 | 3150 | return 0; 3151 | } 3152 | 3153 | int 3154 | js_release_arraybuffer_backing_store(js_env_t *env, js_arraybuffer_backing_store_t *backing_store) { 3155 | // Allow continuing even with a pending exception 3156 | 3157 | if (--backing_store->references == 0) { 3158 | JS_FreeValue(env->context, backing_store->owner); 3159 | 3160 | free(backing_store); 3161 | } 3162 | 3163 | return 0; 3164 | } 3165 | 3166 | int 3167 | js_create_typedarray(js_env_t *env, js_typedarray_type_t type, size_t len, js_value_t *arraybuffer, size_t offset, js_value_t **result) { 3168 | if (JS_HasException(env->context)) return js__error(env); 3169 | 3170 | JSValue global = JS_GetGlobalObject(env->context); 3171 | JSValue constructor; 3172 | 3173 | switch (type) { 3174 | #define V(class, type) \ 3175 | case type: \ 3176 | constructor = JS_GetPropertyStr(env->context, global, class); \ 3177 | break; 3178 | 3179 | V("Int8Array", js_int8array); 3180 | V("Uint8Array", js_uint8array); 3181 | V("Uint8ClampedArray", js_uint8clampedarray); 3182 | V("Int16Array", js_int16array); 3183 | V("Uint16Array", js_uint16array); 3184 | V("Int32Array", js_int32array); 3185 | V("Uint32Array", js_uint32array); 3186 | V("Float16Array", js_float16array); 3187 | V("Float32Array", js_float32array); 3188 | V("Float64Array", js_float64array); 3189 | V("BigInt64Array", js_bigint64array); 3190 | V("BigUint64Array", js_biguint64array); 3191 | #undef V 3192 | } 3193 | 3194 | JSValue argv[3] = {arraybuffer->value, JS_NewInt64(env->context, offset), JS_NewInt64(env->context, len)}; 3195 | 3196 | JSValue typedarray = JS_CallConstructor(env->context, constructor, 3, argv); 3197 | 3198 | JS_FreeValue(env->context, constructor); 3199 | JS_FreeValue(env->context, global); 3200 | 3201 | if (JS_IsException(typedarray)) return js__error(env); 3202 | 3203 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 3204 | 3205 | wrapper->value = typedarray; 3206 | 3207 | *result = wrapper; 3208 | 3209 | js__attach_to_handle_scope(env, env->scope, wrapper); 3210 | 3211 | return 0; 3212 | } 3213 | 3214 | int 3215 | js_create_dataview(js_env_t *env, size_t len, js_value_t *arraybuffer, size_t offset, js_value_t **result) { 3216 | if (JS_HasException(env->context)) return js__error(env); 3217 | 3218 | JSValue global = JS_GetGlobalObject(env->context); 3219 | JSValue constructor = JS_GetPropertyStr(env->context, global, "DataView"); 3220 | 3221 | JSValue argv[3] = {arraybuffer->value, JS_NewInt64(env->context, offset), JS_NewInt64(env->context, len)}; 3222 | 3223 | JSValue typedarray = JS_CallConstructor(env->context, constructor, 3, argv); 3224 | 3225 | JS_FreeValue(env->context, constructor); 3226 | JS_FreeValue(env->context, global); 3227 | 3228 | if (JS_IsException(typedarray)) return js__error(env); 3229 | 3230 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 3231 | 3232 | wrapper->value = typedarray; 3233 | 3234 | *result = wrapper; 3235 | 3236 | js__attach_to_handle_scope(env, env->scope, wrapper); 3237 | 3238 | return 0; 3239 | } 3240 | 3241 | int 3242 | js_coerce_to_boolean(js_env_t *env, js_value_t *value, js_value_t **result) { 3243 | // Allow continuing even with a pending exception 3244 | 3245 | JSValue boolean = JS_ToBoolean(env->context, value->value); 3246 | 3247 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 3248 | 3249 | wrapper->value = boolean; 3250 | 3251 | *result = wrapper; 3252 | 3253 | js__attach_to_handle_scope(env, env->scope, wrapper); 3254 | 3255 | return 0; 3256 | } 3257 | 3258 | int 3259 | js_coerce_to_number(js_env_t *env, js_value_t *value, js_value_t **result) { 3260 | if (JS_HasException(env->context)) return js__error(env); 3261 | 3262 | JSValue number = JS_ToNumber(env->context, value->value); 3263 | 3264 | if (JS_IsException(number)) return js__error(env); 3265 | 3266 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 3267 | 3268 | wrapper->value = number; 3269 | 3270 | *result = wrapper; 3271 | 3272 | js__attach_to_handle_scope(env, env->scope, wrapper); 3273 | 3274 | return 0; 3275 | } 3276 | 3277 | int 3278 | js_coerce_to_string(js_env_t *env, js_value_t *value, js_value_t **result) { 3279 | if (JS_HasException(env->context)) return js__error(env); 3280 | 3281 | JSValue string = JS_ToString(env->context, value->value); 3282 | 3283 | if (JS_IsException(string)) return js__error(env); 3284 | 3285 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 3286 | 3287 | wrapper->value = string; 3288 | 3289 | *result = wrapper; 3290 | 3291 | js__attach_to_handle_scope(env, env->scope, wrapper); 3292 | 3293 | return 0; 3294 | } 3295 | 3296 | int 3297 | js_coerce_to_object(js_env_t *env, js_value_t *value, js_value_t **result) { 3298 | if (JS_HasException(env->context)) return js__error(env); 3299 | 3300 | JSValue object = JS_ToObject(env->context, value->value); 3301 | 3302 | if (JS_IsException(object)) return js__error(env); 3303 | 3304 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 3305 | 3306 | wrapper->value = object; 3307 | 3308 | *result = wrapper; 3309 | 3310 | js__attach_to_handle_scope(env, env->scope, wrapper); 3311 | 3312 | return 0; 3313 | } 3314 | 3315 | int 3316 | js_typeof(js_env_t *env, js_value_t *value, js_value_type_t *result) { 3317 | // Allow continuing even with a pending exception 3318 | 3319 | if (JS_IsNumber(value->value)) { 3320 | *result = js_number; 3321 | } else if (JS_IsBigInt(env->context, value->value)) { 3322 | *result = js_bigint; 3323 | } else if (JS_IsString(value->value)) { 3324 | *result = js_string; 3325 | } else if (JS_IsFunction(env->context, value->value)) { 3326 | *result = js_function; 3327 | } else if (JS_IsObject(value->value)) { 3328 | *result = JS_GetOpaque(value->value, env->classes.external) != NULL 3329 | ? js_external 3330 | : js_object; 3331 | } else if (JS_IsBool(value->value)) { 3332 | *result = js_boolean; 3333 | } else if (JS_IsUndefined(value->value)) { 3334 | *result = js_undefined; 3335 | } else if (JS_IsSymbol(value->value)) { 3336 | *result = js_symbol; 3337 | } else if (JS_IsNull(value->value)) { 3338 | *result = js_null; 3339 | } 3340 | 3341 | return 0; 3342 | } 3343 | 3344 | int 3345 | js_instanceof(js_env_t *env, js_value_t *object, js_value_t *constructor, bool *result) { 3346 | if (JS_HasException(env->context)) return js__error(env); 3347 | 3348 | int success = JS_IsInstanceOf(env->context, object->value, constructor->value); 3349 | 3350 | if (success < 0) return js__error(env); 3351 | 3352 | *result = success; 3353 | 3354 | return 0; 3355 | } 3356 | 3357 | int 3358 | js_is_undefined(js_env_t *env, js_value_t *value, bool *result) { 3359 | // Allow continuing even with a pending exception 3360 | 3361 | *result = JS_IsUndefined(value->value); 3362 | 3363 | return 0; 3364 | } 3365 | 3366 | int 3367 | js_is_null(js_env_t *env, js_value_t *value, bool *result) { 3368 | // Allow continuing even with a pending exception 3369 | 3370 | *result = JS_IsNull(value->value); 3371 | 3372 | return 0; 3373 | } 3374 | 3375 | int 3376 | js_is_boolean(js_env_t *env, js_value_t *value, bool *result) { 3377 | // Allow continuing even with a pending exception 3378 | 3379 | *result = JS_IsBool(value->value); 3380 | 3381 | return 0; 3382 | } 3383 | 3384 | int 3385 | js_is_number(js_env_t *env, js_value_t *value, bool *result) { 3386 | // Allow continuing even with a pending exception 3387 | 3388 | *result = JS_IsNumber(value->value); 3389 | 3390 | return 0; 3391 | } 3392 | 3393 | int 3394 | js_is_int32(js_env_t *env, js_value_t *value, bool *result) { 3395 | // Allow continuing even with a pending exception 3396 | 3397 | if (JS_IsNumber(value->value)) { 3398 | double number; 3399 | 3400 | JS_ToFloat64(env->context, &number, value->value); 3401 | 3402 | double integral; 3403 | 3404 | *result = modf(number, &integral) == 0.0 && integral >= INT32_MIN && integral <= INT32_MAX; 3405 | } else { 3406 | *result = false; 3407 | } 3408 | 3409 | return 0; 3410 | } 3411 | 3412 | int 3413 | js_is_uint32(js_env_t *env, js_value_t *value, bool *result) { 3414 | // Allow continuing even with a pending exception 3415 | 3416 | if (JS_IsNumber(value->value)) { 3417 | double number; 3418 | 3419 | JS_ToFloat64(env->context, &number, value->value); 3420 | 3421 | double integral; 3422 | 3423 | *result = modf(number, &integral) == 0.0 && integral >= 0.0 && integral <= UINT32_MAX; 3424 | } else { 3425 | *result = false; 3426 | } 3427 | 3428 | return 0; 3429 | } 3430 | 3431 | int 3432 | js_is_string(js_env_t *env, js_value_t *value, bool *result) { 3433 | // Allow continuing even with a pending exception 3434 | 3435 | *result = JS_IsString(value->value); 3436 | 3437 | return 0; 3438 | } 3439 | 3440 | int 3441 | js_is_symbol(js_env_t *env, js_value_t *value, bool *result) { 3442 | // Allow continuing even with a pending exception 3443 | 3444 | *result = JS_IsSymbol(value->value); 3445 | 3446 | return 0; 3447 | } 3448 | 3449 | int 3450 | js_is_object(js_env_t *env, js_value_t *value, bool *result) { 3451 | // Allow continuing even with a pending exception 3452 | 3453 | *result = JS_IsObject(value->value); 3454 | 3455 | return 0; 3456 | } 3457 | 3458 | int 3459 | js_is_function(js_env_t *env, js_value_t *value, bool *result) { 3460 | // Allow continuing even with a pending exception 3461 | 3462 | *result = JS_IsFunction(env->context, value->value); 3463 | 3464 | return 0; 3465 | } 3466 | 3467 | int 3468 | js_is_async_function(js_env_t *env, js_value_t *value, bool *result) { 3469 | // Allow continuing even with a pending exception 3470 | 3471 | *result = false; 3472 | 3473 | return 0; 3474 | } 3475 | 3476 | int 3477 | js_is_generator_function(js_env_t *env, js_value_t *value, bool *result) { 3478 | // Allow continuing even with a pending exception 3479 | 3480 | *result = false; 3481 | 3482 | return 0; 3483 | } 3484 | 3485 | int 3486 | js_is_generator(js_env_t *env, js_value_t *value, bool *result) { 3487 | // Allow continuing even with a pending exception 3488 | 3489 | *result = false; 3490 | 3491 | return 0; 3492 | } 3493 | 3494 | int 3495 | js_is_arguments(js_env_t *env, js_value_t *value, bool *result) { 3496 | // Allow continuing even with a pending exception 3497 | 3498 | *result = false; 3499 | 3500 | return 0; 3501 | } 3502 | 3503 | int 3504 | js_is_array(js_env_t *env, js_value_t *value, bool *result) { 3505 | // Allow continuing even with a pending exception 3506 | 3507 | *result = JS_IsArray(value->value); 3508 | 3509 | return 0; 3510 | } 3511 | 3512 | int 3513 | js_is_external(js_env_t *env, js_value_t *value, bool *result) { 3514 | // Allow continuing even with a pending exception 3515 | 3516 | *result = JS_IsObject(value->value) && JS_GetOpaque(value->value, env->classes.external) != NULL; 3517 | 3518 | return 0; 3519 | } 3520 | 3521 | int 3522 | js_is_wrapped(js_env_t *env, js_value_t *value, bool *result) { 3523 | // Allow continuing even with a pending exception 3524 | 3525 | JSAtom atom = JS_NewAtom(env->context, "__native_external"); 3526 | 3527 | *result = JS_IsObject(value->value) && JS_HasProperty(env->context, value->value, atom) == 1; 3528 | 3529 | JS_FreeAtom(env->context, atom); 3530 | 3531 | return 0; 3532 | } 3533 | 3534 | int 3535 | js_is_delegate(js_env_t *env, js_value_t *value, bool *result) { 3536 | // Allow continuing even with a pending exception 3537 | 3538 | *result = JS_IsObject(value->value) && JS_GetOpaque(value->value, env->classes.delegate) != NULL; 3539 | 3540 | return 0; 3541 | } 3542 | 3543 | int 3544 | js_is_bigint(js_env_t *env, js_value_t *value, bool *result) { 3545 | // Allow continuing even with a pending exception 3546 | 3547 | *result = JS_IsBigInt(env->context, value->value); 3548 | 3549 | return 0; 3550 | } 3551 | 3552 | int 3553 | js_is_date(js_env_t *env, js_value_t *value, bool *result) { 3554 | // Allow continuing even with a pending exception 3555 | 3556 | *result = JS_IsDate(value->value); 3557 | 3558 | return 0; 3559 | } 3560 | 3561 | int 3562 | js_is_regexp(js_env_t *env, js_value_t *value, bool *result) { 3563 | // Allow continuing even with a pending exception 3564 | 3565 | *result = JS_IsRegExp(value->value); 3566 | 3567 | return 0; 3568 | } 3569 | 3570 | int 3571 | js_is_error(js_env_t *env, js_value_t *value, bool *result) { 3572 | // Allow continuing even with a pending exception 3573 | 3574 | *result = JS_IsError(env->context, value->value); 3575 | 3576 | return 0; 3577 | } 3578 | 3579 | int 3580 | js_is_promise(js_env_t *env, js_value_t *value, bool *result) { 3581 | // Allow continuing even with a pending exception 3582 | 3583 | *result = JS_IsPromise(value->value); 3584 | 3585 | return 0; 3586 | } 3587 | 3588 | int 3589 | js_is_proxy(js_env_t *env, js_value_t *value, bool *result) { 3590 | // Allow continuing even with a pending exception 3591 | 3592 | *result = JS_IsProxy(value->value); 3593 | 3594 | return 0; 3595 | } 3596 | 3597 | int 3598 | js_is_map(js_env_t *env, js_value_t *value, bool *result) { 3599 | // Allow continuing even with a pending exception 3600 | 3601 | *result = JS_IsMap(value->value); 3602 | 3603 | return 0; 3604 | } 3605 | 3606 | int 3607 | js_is_map_iterator(js_env_t *env, js_value_t *value, bool *result) { 3608 | // Allow continuing even with a pending exception 3609 | 3610 | *result = false; 3611 | 3612 | return 0; 3613 | } 3614 | 3615 | int 3616 | js_is_set(js_env_t *env, js_value_t *value, bool *result) { 3617 | // Allow continuing even with a pending exception 3618 | 3619 | if (JS_IsObject(value->value)) { 3620 | JSValue global = JS_GetGlobalObject(env->context); 3621 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Set"); 3622 | 3623 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3624 | 3625 | JS_FreeValue(env->context, constructor); 3626 | JS_FreeValue(env->context, global); 3627 | } else { 3628 | *result = false; 3629 | } 3630 | 3631 | return 0; 3632 | } 3633 | 3634 | int 3635 | js_is_set_iterator(js_env_t *env, js_value_t *value, bool *result) { 3636 | // Allow continuing even with a pending exception 3637 | 3638 | *result = false; 3639 | 3640 | return 0; 3641 | } 3642 | 3643 | int 3644 | js_is_weak_map(js_env_t *env, js_value_t *value, bool *result) { 3645 | // Allow continuing even with a pending exception 3646 | 3647 | if (JS_IsObject(value->value)) { 3648 | JSValue global = JS_GetGlobalObject(env->context); 3649 | JSValue constructor = JS_GetPropertyStr(env->context, global, "WeakMap"); 3650 | 3651 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3652 | 3653 | JS_FreeValue(env->context, constructor); 3654 | JS_FreeValue(env->context, global); 3655 | } else { 3656 | *result = false; 3657 | } 3658 | 3659 | return 0; 3660 | } 3661 | 3662 | int 3663 | js_is_weak_set(js_env_t *env, js_value_t *value, bool *result) { 3664 | // Allow continuing even with a pending exception 3665 | 3666 | if (JS_IsObject(value->value)) { 3667 | JSValue global = JS_GetGlobalObject(env->context); 3668 | JSValue constructor = JS_GetPropertyStr(env->context, global, "WeakSet"); 3669 | 3670 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3671 | 3672 | JS_FreeValue(env->context, constructor); 3673 | JS_FreeValue(env->context, global); 3674 | } else { 3675 | *result = false; 3676 | } 3677 | 3678 | return 0; 3679 | } 3680 | 3681 | int 3682 | js_is_weak_ref(js_env_t *env, js_value_t *value, bool *result) { 3683 | // Allow continuing even with a pending exception 3684 | 3685 | if (JS_IsObject(value->value)) { 3686 | JSValue global = JS_GetGlobalObject(env->context); 3687 | JSValue constructor = JS_GetPropertyStr(env->context, global, "WeakRef"); 3688 | 3689 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3690 | 3691 | JS_FreeValue(env->context, constructor); 3692 | JS_FreeValue(env->context, global); 3693 | } else { 3694 | *result = false; 3695 | } 3696 | 3697 | return 0; 3698 | } 3699 | 3700 | int 3701 | js_is_arraybuffer(js_env_t *env, js_value_t *value, bool *result) { 3702 | // Allow continuing even with a pending exception 3703 | 3704 | *result = JS_IsArrayBuffer(value->value); 3705 | 3706 | return 0; 3707 | } 3708 | 3709 | int 3710 | js_is_detached_arraybuffer(js_env_t *env, js_value_t *value, bool *result) { 3711 | // Allow continuing even with a pending exception 3712 | 3713 | size_t len; 3714 | 3715 | *result = JS_GetArrayBuffer(env->context, &len, value->value) == NULL; 3716 | 3717 | return 0; 3718 | } 3719 | 3720 | int 3721 | js_is_sharedarraybuffer(js_env_t *env, js_value_t *value, bool *result) { 3722 | // Allow continuing even with a pending exception 3723 | 3724 | if (JS_IsObject(value->value)) { 3725 | JSValue global = JS_GetGlobalObject(env->context); 3726 | JSValue constructor = JS_GetPropertyStr(env->context, global, "SharedArrayBuffer"); 3727 | 3728 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3729 | 3730 | JS_FreeValue(env->context, constructor); 3731 | JS_FreeValue(env->context, global); 3732 | } else { 3733 | *result = false; 3734 | } 3735 | 3736 | return 0; 3737 | } 3738 | 3739 | int 3740 | js_is_typedarray(js_env_t *env, js_value_t *value, bool *result) { 3741 | // Allow continuing even with a pending exception 3742 | 3743 | if (JS_IsObject(value->value)) { 3744 | JSValue global = JS_GetGlobalObject(env->context); 3745 | 3746 | #define V(class) \ 3747 | { \ 3748 | JSValue constructor = JS_GetPropertyStr(env->context, global, class); \ 3749 | \ 3750 | if (JS_IsInstanceOf(env->context, value->value, constructor)) { \ 3751 | *result = true; \ 3752 | \ 3753 | JS_FreeValue(env->context, constructor); \ 3754 | \ 3755 | goto done; \ 3756 | } \ 3757 | \ 3758 | JS_FreeValue(env->context, constructor); \ 3759 | } 3760 | 3761 | V("Int8Array"); 3762 | V("Uint8Array"); 3763 | V("Uint8ClampedArray"); 3764 | V("Int16Array"); 3765 | V("Uint16Array"); 3766 | V("Int32Array"); 3767 | V("Uint32Array"); 3768 | V("Float32Array"); 3769 | V("Float64Array"); 3770 | V("BigInt64Array"); 3771 | V("BigUint64Array"); 3772 | #undef V 3773 | 3774 | *result = false; 3775 | 3776 | done: 3777 | JS_FreeValue(env->context, global); 3778 | } else { 3779 | *result = false; 3780 | } 3781 | 3782 | return 0; 3783 | } 3784 | 3785 | int 3786 | js_is_int8array(js_env_t *env, js_value_t *value, bool *result) { 3787 | // Allow continuing even with a pending exception 3788 | 3789 | if (JS_IsObject(value->value)) { 3790 | JSValue global = JS_GetGlobalObject(env->context); 3791 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Int8Array"); 3792 | 3793 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3794 | 3795 | JS_FreeValue(env->context, constructor); 3796 | JS_FreeValue(env->context, global); 3797 | } else { 3798 | *result = false; 3799 | } 3800 | 3801 | return 0; 3802 | } 3803 | 3804 | int 3805 | js_is_uint8array(js_env_t *env, js_value_t *value, bool *result) { 3806 | // Allow continuing even with a pending exception 3807 | 3808 | if (JS_IsObject(value->value)) { 3809 | JSValue global = JS_GetGlobalObject(env->context); 3810 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Uint8Array"); 3811 | 3812 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3813 | 3814 | JS_FreeValue(env->context, constructor); 3815 | JS_FreeValue(env->context, global); 3816 | } else { 3817 | *result = false; 3818 | } 3819 | 3820 | return 0; 3821 | } 3822 | 3823 | int 3824 | js_is_uint8clampedarray(js_env_t *env, js_value_t *value, bool *result) { 3825 | // Allow continuing even with a pending exception 3826 | 3827 | if (JS_IsObject(value->value)) { 3828 | JSValue global = JS_GetGlobalObject(env->context); 3829 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Uint8ClampedArray"); 3830 | 3831 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3832 | 3833 | JS_FreeValue(env->context, constructor); 3834 | JS_FreeValue(env->context, global); 3835 | } else { 3836 | *result = false; 3837 | } 3838 | 3839 | return 0; 3840 | } 3841 | 3842 | int 3843 | js_is_int16array(js_env_t *env, js_value_t *value, bool *result) { 3844 | // Allow continuing even with a pending exception 3845 | 3846 | if (JS_IsObject(value->value)) { 3847 | JSValue global = JS_GetGlobalObject(env->context); 3848 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Int16Array"); 3849 | 3850 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3851 | 3852 | JS_FreeValue(env->context, constructor); 3853 | JS_FreeValue(env->context, global); 3854 | } else { 3855 | *result = false; 3856 | } 3857 | 3858 | return 0; 3859 | } 3860 | 3861 | int 3862 | js_is_uint16array(js_env_t *env, js_value_t *value, bool *result) { 3863 | // Allow continuing even with a pending exception 3864 | 3865 | if (JS_IsObject(value->value)) { 3866 | JSValue global = JS_GetGlobalObject(env->context); 3867 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Uint16Array"); 3868 | 3869 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3870 | 3871 | JS_FreeValue(env->context, constructor); 3872 | JS_FreeValue(env->context, global); 3873 | } else { 3874 | *result = false; 3875 | } 3876 | 3877 | return 0; 3878 | } 3879 | 3880 | int 3881 | js_is_int32array(js_env_t *env, js_value_t *value, bool *result) { 3882 | // Allow continuing even with a pending exception 3883 | 3884 | if (JS_IsObject(value->value)) { 3885 | JSValue global = JS_GetGlobalObject(env->context); 3886 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Int32Array"); 3887 | 3888 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3889 | 3890 | JS_FreeValue(env->context, constructor); 3891 | JS_FreeValue(env->context, global); 3892 | } else { 3893 | *result = false; 3894 | } 3895 | 3896 | return 0; 3897 | } 3898 | 3899 | int 3900 | js_is_uint32array(js_env_t *env, js_value_t *value, bool *result) { 3901 | // Allow continuing even with a pending exception 3902 | 3903 | if (JS_IsObject(value->value)) { 3904 | JSValue global = JS_GetGlobalObject(env->context); 3905 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Uint32Array"); 3906 | 3907 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3908 | 3909 | JS_FreeValue(env->context, constructor); 3910 | JS_FreeValue(env->context, global); 3911 | } else { 3912 | *result = false; 3913 | } 3914 | 3915 | return 0; 3916 | } 3917 | 3918 | int 3919 | js_is_float16array(js_env_t *env, js_value_t *value, bool *result) { 3920 | *result = false; 3921 | 3922 | return 0; 3923 | } 3924 | 3925 | int 3926 | js_is_float32array(js_env_t *env, js_value_t *value, bool *result) { 3927 | // Allow continuing even with a pending exception 3928 | 3929 | if (JS_IsObject(value->value)) { 3930 | JSValue global = JS_GetGlobalObject(env->context); 3931 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Float32Array"); 3932 | 3933 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3934 | 3935 | JS_FreeValue(env->context, constructor); 3936 | JS_FreeValue(env->context, global); 3937 | } else { 3938 | *result = false; 3939 | } 3940 | 3941 | return 0; 3942 | } 3943 | 3944 | int 3945 | js_is_float64array(js_env_t *env, js_value_t *value, bool *result) { 3946 | // Allow continuing even with a pending exception 3947 | 3948 | if (JS_IsObject(value->value)) { 3949 | JSValue global = JS_GetGlobalObject(env->context); 3950 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Float64Array"); 3951 | 3952 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3953 | 3954 | JS_FreeValue(env->context, constructor); 3955 | JS_FreeValue(env->context, global); 3956 | } else { 3957 | *result = false; 3958 | } 3959 | 3960 | return 0; 3961 | } 3962 | 3963 | int 3964 | js_is_bigint64array(js_env_t *env, js_value_t *value, bool *result) { 3965 | // Allow continuing even with a pending exception 3966 | 3967 | if (JS_IsObject(value->value)) { 3968 | JSValue global = JS_GetGlobalObject(env->context); 3969 | JSValue constructor = JS_GetPropertyStr(env->context, global, "BigInt64Array"); 3970 | 3971 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3972 | 3973 | JS_FreeValue(env->context, constructor); 3974 | JS_FreeValue(env->context, global); 3975 | } else { 3976 | *result = false; 3977 | } 3978 | 3979 | return 0; 3980 | } 3981 | 3982 | int 3983 | js_is_biguint64array(js_env_t *env, js_value_t *value, bool *result) { 3984 | // Allow continuing even with a pending exception 3985 | 3986 | if (JS_IsObject(value->value)) { 3987 | JSValue global = JS_GetGlobalObject(env->context); 3988 | JSValue constructor = JS_GetPropertyStr(env->context, global, "BigUint64Array"); 3989 | 3990 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 3991 | 3992 | JS_FreeValue(env->context, constructor); 3993 | JS_FreeValue(env->context, global); 3994 | } else { 3995 | *result = false; 3996 | } 3997 | 3998 | return 0; 3999 | } 4000 | 4001 | int 4002 | js_is_dataview(js_env_t *env, js_value_t *value, bool *result) { 4003 | // Allow continuing even with a pending exception 4004 | 4005 | if (JS_IsObject(value->value)) { 4006 | JSValue global = JS_GetGlobalObject(env->context); 4007 | JSValue constructor = JS_GetPropertyStr(env->context, global, "DataView"); 4008 | 4009 | *result = JS_IsInstanceOf(env->context, value->value, constructor); 4010 | 4011 | JS_FreeValue(env->context, constructor); 4012 | JS_FreeValue(env->context, global); 4013 | } else { 4014 | *result = false; 4015 | } 4016 | 4017 | return 0; 4018 | } 4019 | 4020 | int 4021 | js_is_module_namespace(js_env_t *env, js_value_t *value, bool *result) { 4022 | // Allow continuing even with a pending exception 4023 | 4024 | *result = false; 4025 | 4026 | return 0; 4027 | } 4028 | 4029 | int 4030 | js_strict_equals(js_env_t *env, js_value_t *a, js_value_t *b, bool *result) { 4031 | // Allow continuing even with a pending exception 4032 | 4033 | *result = JS_IsStrictEqual(env->context, a->value, b->value); 4034 | 4035 | return 0; 4036 | } 4037 | 4038 | int 4039 | js_get_global(js_env_t *env, js_value_t **result) { 4040 | // Allow continuing even with a pending exception 4041 | 4042 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4043 | 4044 | wrapper->value = JS_GetGlobalObject(env->context); 4045 | 4046 | *result = wrapper; 4047 | 4048 | js__attach_to_handle_scope(env, env->scope, wrapper); 4049 | 4050 | return 0; 4051 | } 4052 | 4053 | int 4054 | js_get_undefined(js_env_t *env, js_value_t **result) { 4055 | // Allow continuing even with a pending exception 4056 | 4057 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4058 | 4059 | wrapper->value = JS_UNDEFINED; 4060 | 4061 | *result = wrapper; 4062 | 4063 | js__attach_to_handle_scope(env, env->scope, wrapper); 4064 | 4065 | return 0; 4066 | } 4067 | 4068 | int 4069 | js_get_null(js_env_t *env, js_value_t **result) { 4070 | // Allow continuing even with a pending exception 4071 | 4072 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4073 | 4074 | wrapper->value = JS_NULL; 4075 | 4076 | *result = wrapper; 4077 | 4078 | js__attach_to_handle_scope(env, env->scope, wrapper); 4079 | 4080 | return 0; 4081 | } 4082 | 4083 | int 4084 | js_get_boolean(js_env_t *env, bool value, js_value_t **result) { 4085 | // Allow continuing even with a pending exception 4086 | 4087 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4088 | 4089 | wrapper->value = value ? JS_TRUE : JS_FALSE; 4090 | 4091 | *result = wrapper; 4092 | 4093 | js__attach_to_handle_scope(env, env->scope, wrapper); 4094 | 4095 | return 0; 4096 | } 4097 | 4098 | int 4099 | js_get_value_bool(js_env_t *env, js_value_t *value, bool *result) { 4100 | // Allow continuing even with a pending exception 4101 | 4102 | *result = JS_ToBool(env->context, value->value); 4103 | 4104 | return 0; 4105 | } 4106 | 4107 | int 4108 | js_get_value_int32(js_env_t *env, js_value_t *value, int32_t *result) { 4109 | // Allow continuing even with a pending exception 4110 | 4111 | JS_ToInt32(env->context, result, value->value); 4112 | 4113 | return 0; 4114 | } 4115 | 4116 | int 4117 | js_get_value_uint32(js_env_t *env, js_value_t *value, uint32_t *result) { 4118 | // Allow continuing even with a pending exception 4119 | 4120 | JS_ToUint32(env->context, result, value->value); 4121 | 4122 | return 0; 4123 | } 4124 | 4125 | int 4126 | js_get_value_int64(js_env_t *env, js_value_t *value, int64_t *result) { 4127 | // Allow continuing even with a pending exception 4128 | 4129 | JS_ToInt64(env->context, result, value->value); 4130 | 4131 | return 0; 4132 | } 4133 | 4134 | int 4135 | js_get_value_double(js_env_t *env, js_value_t *value, double *result) { 4136 | // Allow continuing even with a pending exception 4137 | 4138 | JS_ToFloat64(env->context, result, value->value); 4139 | 4140 | return 0; 4141 | } 4142 | 4143 | int 4144 | js_get_value_bigint_int64(js_env_t *env, js_value_t *value, int64_t *result, bool *lossless) { 4145 | // Allow continuing even with a pending exception 4146 | 4147 | JS_ToBigInt64(env->context, result, value->value); 4148 | 4149 | if (lossless) *lossless = true; 4150 | 4151 | return 0; 4152 | } 4153 | 4154 | int 4155 | js_get_value_bigint_uint64(js_env_t *env, js_value_t *value, uint64_t *result, bool *lossless) { 4156 | // Allow continuing even with a pending exception 4157 | 4158 | JS_ToBigInt64(env->context, (int64_t *) result, value->value); 4159 | 4160 | if (lossless) *lossless = true; 4161 | 4162 | return 0; 4163 | } 4164 | 4165 | int 4166 | js_get_value_string_utf8(js_env_t *env, js_value_t *value, utf8_t *str, size_t len, size_t *result) { 4167 | // Allow continuing even with a pending exception 4168 | 4169 | size_t cstr_len; 4170 | const char *cstr = JS_ToCStringLen(env->context, &cstr_len, value->value); 4171 | 4172 | if (str == NULL) { 4173 | *result = cstr_len; 4174 | } else if (len != 0) { 4175 | size_t written = cstr_len < len ? cstr_len : len; 4176 | 4177 | memcpy(str, cstr, written); 4178 | 4179 | if (written < len) str[written] = '\0'; 4180 | 4181 | if (result) *result = written; 4182 | } else if (result) *result = 0; 4183 | 4184 | JS_FreeCString(env->context, cstr); 4185 | 4186 | return 0; 4187 | } 4188 | 4189 | int 4190 | js_get_value_string_utf16le(js_env_t *env, js_value_t *value, utf16_t *str, size_t len, size_t *result) { 4191 | // Allow continuing even with a pending exception 4192 | 4193 | size_t cstr_len; 4194 | const char *cstr = JS_ToCStringLen(env->context, &cstr_len, value->value); 4195 | 4196 | size_t utf16_len = utf16_length_from_utf8((utf8_t *) cstr, cstr_len); 4197 | 4198 | if (str == NULL) { 4199 | *result = utf16_len; 4200 | } else if (len != 0) { 4201 | size_t written = utf16_len < len ? utf16_len : len; 4202 | 4203 | utf8_convert_to_utf16le((utf8_t *) cstr, cstr_len, str); 4204 | 4205 | if (written < len) str[written] = L'\0'; 4206 | 4207 | if (result) *result = written; 4208 | } else if (result) *result = 0; 4209 | 4210 | JS_FreeCString(env->context, cstr); 4211 | 4212 | return 0; 4213 | } 4214 | 4215 | int 4216 | js_get_value_string_latin1(js_env_t *env, js_value_t *value, latin1_t *str, size_t len, size_t *result) { 4217 | // Allow continuing even with a pending exception 4218 | 4219 | size_t cstr_len; 4220 | const char *cstr = JS_ToCStringLen(env->context, &cstr_len, value->value); 4221 | 4222 | size_t latin1_len = latin1_length_from_utf8((utf8_t *) cstr, cstr_len); 4223 | 4224 | if (str == NULL) { 4225 | *result = latin1_len; 4226 | } else if (len != 0) { 4227 | size_t written = latin1_len < len ? latin1_len : len; 4228 | 4229 | utf8_convert_to_latin1((utf8_t *) cstr, cstr_len, str); 4230 | 4231 | if (written < len) str[written] = '\0'; 4232 | 4233 | if (result) *result = written; 4234 | } else if (result) *result = 0; 4235 | 4236 | JS_FreeCString(env->context, cstr); 4237 | 4238 | return 0; 4239 | } 4240 | 4241 | int 4242 | js_get_value_external(js_env_t *env, js_value_t *value, void **result) { 4243 | // Allow continuing even with a pending exception 4244 | 4245 | js_finalizer_t *finalizer = (js_finalizer_t *) JS_GetOpaque(value->value, env->classes.external); 4246 | 4247 | *result = finalizer->data; 4248 | 4249 | return 0; 4250 | } 4251 | 4252 | int 4253 | js_get_value_date(js_env_t *env, js_value_t *value, double *result) { 4254 | // Allow continuing even with a pending exception 4255 | 4256 | JS_ToFloat64(env->context, result, value->value); 4257 | 4258 | return 0; 4259 | } 4260 | 4261 | int 4262 | js_get_array_length(js_env_t *env, js_value_t *array, uint32_t *result) { 4263 | // Allow continuing even with a pending exception 4264 | 4265 | JSValue length = JS_GetPropertyStr(env->context, array->value, "length"); 4266 | 4267 | JS_ToUint32(env->context, result, length); 4268 | 4269 | JS_FreeValue(env->context, length); 4270 | 4271 | return 0; 4272 | } 4273 | 4274 | int 4275 | js_get_array_elements(js_env_t *env, js_value_t *array, js_value_t **elements, size_t len, size_t offset, uint32_t *result) { 4276 | if (JS_HasException(env->context)) return js__error(env); 4277 | 4278 | int err; 4279 | 4280 | uint32_t written = 0; 4281 | 4282 | env->depth++; 4283 | 4284 | uint32_t m; 4285 | err = js_get_array_length(env, array, &m); 4286 | assert(err == 0); 4287 | 4288 | for (uint32_t i = 0, n = len, j = offset; i < n && j < m; i++, j++) { 4289 | JSValue value = JS_GetPropertyUint32(env->context, array->value, j); 4290 | 4291 | if (JS_IsException(value)) { 4292 | if (env->depth == 1) js__on_run_microtasks(env); 4293 | 4294 | env->depth--; 4295 | 4296 | if (env->depth == 0) { 4297 | JSValue error = JS_GetException(env->context); 4298 | 4299 | js__on_uncaught_exception(env->context, error); 4300 | } 4301 | 4302 | return js__error(env); 4303 | } 4304 | 4305 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4306 | 4307 | wrapper->value = value; 4308 | 4309 | elements[i] = wrapper; 4310 | 4311 | js__attach_to_handle_scope(env, env->scope, wrapper); 4312 | 4313 | written++; 4314 | } 4315 | 4316 | if (env->depth == 1) js__on_run_microtasks(env); 4317 | 4318 | env->depth--; 4319 | 4320 | if (result) *result = written; 4321 | 4322 | return 0; 4323 | } 4324 | 4325 | int 4326 | js_set_array_elements(js_env_t *env, js_value_t *array, const js_value_t *elements[], size_t len, size_t offset) { 4327 | if (JS_HasException(env->context)) return js__error(env); 4328 | 4329 | env->depth++; 4330 | 4331 | for (uint32_t i = 0, n = len, j = offset; i < n; i++, j++) { 4332 | int success = JS_SetPropertyUint32(env->context, array->value, j, JS_DupValue(env->context, elements[i]->value)); 4333 | 4334 | if (env->depth == 1) js__on_run_microtasks(env); 4335 | 4336 | env->depth--; 4337 | 4338 | if (success < 0) { 4339 | if (env->depth == 0) { 4340 | JSValue error = JS_GetException(env->context); 4341 | 4342 | js__on_uncaught_exception(env->context, error); 4343 | } 4344 | 4345 | return js__error(env); 4346 | } 4347 | } 4348 | 4349 | if (env->depth == 1) js__on_run_microtasks(env); 4350 | 4351 | env->depth--; 4352 | 4353 | return 0; 4354 | } 4355 | 4356 | int 4357 | js_get_prototype(js_env_t *env, js_value_t *object, js_value_t **result) { 4358 | // Allow continuing even with a pending exception 4359 | 4360 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4361 | 4362 | wrapper->value = JS_GetPrototype(env->context, object->value); 4363 | 4364 | *result = wrapper; 4365 | 4366 | js__attach_to_handle_scope(env, env->scope, wrapper); 4367 | 4368 | return 0; 4369 | } 4370 | 4371 | int 4372 | js_get_property_names(js_env_t *env, js_value_t *object, js_value_t **result) { 4373 | if (JS_HasException(env->context)) return js__error(env); 4374 | 4375 | int err; 4376 | 4377 | JSPropertyEnum *properties; 4378 | uint32_t len; 4379 | 4380 | env->depth++; 4381 | 4382 | err = JS_GetOwnPropertyNames(env->context, &properties, &len, object->value, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK); 4383 | 4384 | if (env->depth == 1) js__on_run_microtasks(env); 4385 | 4386 | env->depth--; 4387 | 4388 | if (err < 0) { 4389 | if (env->depth == 0) { 4390 | JSValue error = JS_GetException(env->context); 4391 | 4392 | js__on_uncaught_exception(env->context, error); 4393 | } 4394 | 4395 | return js__error(env); 4396 | } 4397 | 4398 | JSValue array = JS_NewArray(env->context); 4399 | 4400 | for (uint32_t i = 0; i < len; i++) { 4401 | err = JS_SetPropertyUint32(env->context, object->value, i, JS_AtomToValue(env->context, properties[i].atom)); 4402 | if (err < 0) goto err; 4403 | } 4404 | 4405 | if (result == NULL) JS_FreeValue(env->context, array); 4406 | else { 4407 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4408 | 4409 | wrapper->value = array; 4410 | 4411 | *result = wrapper; 4412 | 4413 | js__attach_to_handle_scope(env, env->scope, wrapper); 4414 | } 4415 | 4416 | return 0; 4417 | 4418 | err: 4419 | for (uint32_t i = 0; i < len; i++) { 4420 | JS_FreeAtom(env->context, properties[i].atom); 4421 | } 4422 | 4423 | free(properties); 4424 | 4425 | return js__error(env); 4426 | } 4427 | 4428 | int 4429 | js_get_property(js_env_t *env, js_value_t *object, js_value_t *key, js_value_t **result) { 4430 | if (JS_HasException(env->context)) return js__error(env); 4431 | 4432 | JSAtom atom = JS_ValueToAtom(env->context, key->value); 4433 | 4434 | env->depth++; 4435 | 4436 | JSValue value = JS_GetProperty(env->context, object->value, atom); 4437 | 4438 | JS_FreeAtom(env->context, atom); 4439 | 4440 | if (env->depth == 1) js__on_run_microtasks(env); 4441 | 4442 | env->depth--; 4443 | 4444 | if (JS_IsException(value)) { 4445 | if (env->depth == 0) { 4446 | JSValue error = JS_GetException(env->context); 4447 | 4448 | js__on_uncaught_exception(env->context, error); 4449 | } 4450 | 4451 | return js__error(env); 4452 | } 4453 | 4454 | if (result == NULL) JS_FreeValue(env->context, value); 4455 | else { 4456 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4457 | 4458 | wrapper->value = value; 4459 | 4460 | *result = wrapper; 4461 | 4462 | js__attach_to_handle_scope(env, env->scope, wrapper); 4463 | } 4464 | 4465 | return 0; 4466 | } 4467 | 4468 | int 4469 | js_has_property(js_env_t *env, js_value_t *object, js_value_t *key, bool *result) { 4470 | if (JS_HasException(env->context)) return js__error(env); 4471 | 4472 | JSAtom atom = JS_ValueToAtom(env->context, key->value); 4473 | 4474 | env->depth++; 4475 | 4476 | int success = JS_HasProperty(env->context, object->value, atom); 4477 | 4478 | JS_FreeAtom(env->context, atom); 4479 | 4480 | if (env->depth == 1) js__on_run_microtasks(env); 4481 | 4482 | env->depth--; 4483 | 4484 | if (success < 0) { 4485 | if (env->depth == 0) { 4486 | JSValue error = JS_GetException(env->context); 4487 | 4488 | js__on_uncaught_exception(env->context, error); 4489 | } 4490 | 4491 | return js__error(env); 4492 | } 4493 | 4494 | if (result) *result = success == 1; 4495 | 4496 | return 0; 4497 | } 4498 | 4499 | int 4500 | js_has_own_property(js_env_t *env, js_value_t *object, js_value_t *key, bool *result) { 4501 | if (JS_HasException(env->context)) return js__error(env); 4502 | 4503 | JSAtom atom = JS_ValueToAtom(env->context, key->value); 4504 | 4505 | env->depth++; 4506 | 4507 | int success = JS_HasProperty(env->context, object->value, atom); 4508 | 4509 | JS_FreeAtom(env->context, atom); 4510 | 4511 | if (env->depth == 1) js__on_run_microtasks(env); 4512 | 4513 | env->depth--; 4514 | 4515 | if (success < 0) { 4516 | if (env->depth == 0) { 4517 | JSValue error = JS_GetException(env->context); 4518 | 4519 | js__on_uncaught_exception(env->context, error); 4520 | } 4521 | 4522 | return js__error(env); 4523 | } 4524 | 4525 | if (result) *result = success == 1; 4526 | 4527 | return 0; 4528 | } 4529 | 4530 | int 4531 | js_set_property(js_env_t *env, js_value_t *object, js_value_t *key, js_value_t *value) { 4532 | if (JS_HasException(env->context)) return js__error(env); 4533 | 4534 | JSAtom atom = JS_ValueToAtom(env->context, key->value); 4535 | 4536 | env->depth++; 4537 | 4538 | int success = JS_SetProperty(env->context, object->value, atom, JS_DupValue(env->context, value->value)); 4539 | 4540 | JS_FreeAtom(env->context, atom); 4541 | 4542 | if (env->depth == 1) js__on_run_microtasks(env); 4543 | 4544 | env->depth--; 4545 | 4546 | if (success < 0) { 4547 | if (env->depth == 0) { 4548 | JSValue error = JS_GetException(env->context); 4549 | 4550 | js__on_uncaught_exception(env->context, error); 4551 | } 4552 | 4553 | return js__error(env); 4554 | } 4555 | 4556 | return 0; 4557 | } 4558 | 4559 | int 4560 | js_delete_property(js_env_t *env, js_value_t *object, js_value_t *key, bool *result) { 4561 | if (JS_HasException(env->context)) return js__error(env); 4562 | 4563 | JSAtom atom = JS_ValueToAtom(env->context, key->value); 4564 | 4565 | env->depth++; 4566 | 4567 | int success = JS_DeleteProperty(env->context, object->value, atom, 0); 4568 | 4569 | JS_FreeAtom(env->context, atom); 4570 | 4571 | if (env->depth == 1) js__on_run_microtasks(env); 4572 | 4573 | env->depth--; 4574 | 4575 | if (success < 0) { 4576 | if (env->depth == 0) { 4577 | JSValue error = JS_GetException(env->context); 4578 | 4579 | js__on_uncaught_exception(env->context, error); 4580 | } 4581 | 4582 | return js__error(env); 4583 | } 4584 | 4585 | if (result) *result = success == 1; 4586 | 4587 | return 0; 4588 | } 4589 | 4590 | int 4591 | js_get_named_property(js_env_t *env, js_value_t *object, const char *name, js_value_t **result) { 4592 | if (JS_HasException(env->context)) return js__error(env); 4593 | 4594 | env->depth++; 4595 | 4596 | JSValue value = JS_GetPropertyStr(env->context, object->value, name); 4597 | 4598 | if (env->depth == 1) js__on_run_microtasks(env); 4599 | 4600 | env->depth--; 4601 | 4602 | if (JS_IsException(value)) { 4603 | if (env->depth == 0) { 4604 | JSValue error = JS_GetException(env->context); 4605 | 4606 | js__on_uncaught_exception(env->context, error); 4607 | } 4608 | 4609 | return js__error(env); 4610 | } 4611 | 4612 | if (result == NULL) JS_FreeValue(env->context, value); 4613 | else { 4614 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4615 | 4616 | wrapper->value = value; 4617 | 4618 | *result = wrapper; 4619 | 4620 | js__attach_to_handle_scope(env, env->scope, wrapper); 4621 | } 4622 | 4623 | return 0; 4624 | } 4625 | 4626 | int 4627 | js_has_named_property(js_env_t *env, js_value_t *object, const char *name, bool *result) { 4628 | if (JS_HasException(env->context)) return js__error(env); 4629 | 4630 | JSAtom atom = JS_NewAtom(env->context, name); 4631 | 4632 | env->depth++; 4633 | 4634 | int success = JS_HasProperty(env->context, object->value, atom); 4635 | 4636 | JS_FreeAtom(env->context, atom); 4637 | 4638 | if (env->depth == 1) js__on_run_microtasks(env); 4639 | 4640 | env->depth--; 4641 | 4642 | if (success < 0) { 4643 | if (env->depth == 0) { 4644 | JSValue error = JS_GetException(env->context); 4645 | 4646 | js__on_uncaught_exception(env->context, error); 4647 | } 4648 | 4649 | return js__error(env); 4650 | } 4651 | 4652 | if (result) *result = success == 1; 4653 | 4654 | return 0; 4655 | } 4656 | 4657 | int 4658 | js_set_named_property(js_env_t *env, js_value_t *object, const char *name, js_value_t *value) { 4659 | if (JS_HasException(env->context)) return js__error(env); 4660 | 4661 | env->depth++; 4662 | 4663 | int success = JS_SetPropertyStr(env->context, object->value, name, JS_DupValue(env->context, value->value)); 4664 | 4665 | if (env->depth == 1) js__on_run_microtasks(env); 4666 | 4667 | env->depth--; 4668 | 4669 | if (success < 0) { 4670 | if (env->depth == 0) { 4671 | JSValue error = JS_GetException(env->context); 4672 | 4673 | js__on_uncaught_exception(env->context, error); 4674 | } 4675 | 4676 | return js__error(env); 4677 | } 4678 | 4679 | return 0; 4680 | } 4681 | 4682 | int 4683 | js_delete_named_property(js_env_t *env, js_value_t *object, const char *name, bool *result) { 4684 | if (JS_HasException(env->context)) return js__error(env); 4685 | 4686 | JSAtom atom = JS_NewAtom(env->context, name); 4687 | 4688 | env->depth++; 4689 | 4690 | int success = JS_DeleteProperty(env->context, object->value, atom, 0); 4691 | 4692 | JS_FreeAtom(env->context, atom); 4693 | 4694 | if (env->depth == 1) js__on_run_microtasks(env); 4695 | 4696 | env->depth--; 4697 | 4698 | if (success < 0) { 4699 | if (env->depth == 0) { 4700 | JSValue error = JS_GetException(env->context); 4701 | 4702 | js__on_uncaught_exception(env->context, error); 4703 | } 4704 | 4705 | return js__error(env); 4706 | } 4707 | 4708 | if (result) *result = success == 1; 4709 | 4710 | return 0; 4711 | } 4712 | 4713 | int 4714 | js_get_element(js_env_t *env, js_value_t *object, uint32_t index, js_value_t **result) { 4715 | if (JS_HasException(env->context)) return js__error(env); 4716 | 4717 | env->depth++; 4718 | 4719 | JSValue value = JS_GetPropertyUint32(env->context, object->value, index); 4720 | 4721 | if (env->depth == 1) js__on_run_microtasks(env); 4722 | 4723 | env->depth--; 4724 | 4725 | if (JS_IsException(value)) { 4726 | if (env->depth == 0) { 4727 | JSValue error = JS_GetException(env->context); 4728 | 4729 | js__on_uncaught_exception(env->context, error); 4730 | } 4731 | 4732 | return js__error(env); 4733 | } 4734 | 4735 | if (result == NULL) JS_FreeValue(env->context, value); 4736 | else { 4737 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4738 | 4739 | wrapper->value = value; 4740 | 4741 | *result = wrapper; 4742 | 4743 | js__attach_to_handle_scope(env, env->scope, wrapper); 4744 | } 4745 | 4746 | return 0; 4747 | } 4748 | 4749 | int 4750 | js_has_element(js_env_t *env, js_value_t *object, uint32_t index, bool *result) { 4751 | if (JS_HasException(env->context)) return js__error(env); 4752 | 4753 | JSAtom atom = JS_NewAtomUInt32(env->context, index); 4754 | 4755 | env->depth++; 4756 | 4757 | int success = JS_HasProperty(env->context, object->value, atom); 4758 | 4759 | JS_FreeAtom(env->context, atom); 4760 | 4761 | if (env->depth == 1) js__on_run_microtasks(env); 4762 | 4763 | env->depth--; 4764 | 4765 | if (success < 0) { 4766 | if (env->depth == 0) { 4767 | JSValue error = JS_GetException(env->context); 4768 | 4769 | js__on_uncaught_exception(env->context, error); 4770 | } 4771 | 4772 | return js__error(env); 4773 | } 4774 | 4775 | if (result) *result = success == 1; 4776 | 4777 | return 0; 4778 | } 4779 | 4780 | int 4781 | js_set_element(js_env_t *env, js_value_t *object, uint32_t index, js_value_t *value) { 4782 | if (JS_HasException(env->context)) return js__error(env); 4783 | 4784 | env->depth++; 4785 | 4786 | int success = JS_SetPropertyUint32(env->context, object->value, index, JS_DupValue(env->context, value->value)); 4787 | 4788 | if (env->depth == 1) js__on_run_microtasks(env); 4789 | 4790 | env->depth--; 4791 | 4792 | if (success < 0) { 4793 | if (env->depth == 0) { 4794 | JSValue error = JS_GetException(env->context); 4795 | 4796 | js__on_uncaught_exception(env->context, error); 4797 | } 4798 | 4799 | return js__error(env); 4800 | } 4801 | 4802 | return 0; 4803 | } 4804 | 4805 | int 4806 | js_delete_element(js_env_t *env, js_value_t *object, uint32_t index, bool *result) { 4807 | if (JS_HasException(env->context)) return js__error(env); 4808 | 4809 | JSAtom atom = JS_NewAtomUInt32(env->context, index); 4810 | 4811 | env->depth++; 4812 | 4813 | int success = JS_DeleteProperty(env->context, object->value, atom, 0); 4814 | 4815 | JS_FreeAtom(env->context, atom); 4816 | 4817 | if (env->depth == 1) js__on_run_microtasks(env); 4818 | 4819 | env->depth--; 4820 | 4821 | if (success < 0) { 4822 | if (env->depth == 0) { 4823 | JSValue error = JS_GetException(env->context); 4824 | 4825 | js__on_uncaught_exception(env->context, error); 4826 | } 4827 | 4828 | return js__error(env); 4829 | } 4830 | 4831 | if (result) *result = success == 1; 4832 | 4833 | return 0; 4834 | } 4835 | 4836 | int 4837 | js_get_string_view(js_env_t *env, js_value_t *string, js_string_encoding_t *encoding, const void **str, size_t *len, js_string_view_t **result) { 4838 | // Allow continuing even with a pending exception 4839 | 4840 | js_string_view_t *view = malloc(sizeof(js_string_view_t)); 4841 | 4842 | view->value = JS_ToCStringLen(env->context, len, string->value); 4843 | 4844 | if (encoding) *encoding = js_utf8; 4845 | 4846 | if (str) *str = view->value; 4847 | 4848 | *result = view; 4849 | 4850 | return 0; 4851 | } 4852 | 4853 | int 4854 | js_release_string_view(js_env_t *env, js_string_view_t *view) { 4855 | // Allow continuing even with a pending exception 4856 | 4857 | JS_FreeCString(env->context, view->value); 4858 | 4859 | free(view); 4860 | 4861 | return 0; 4862 | } 4863 | 4864 | int 4865 | js_get_callback_info(js_env_t *env, const js_callback_info_t *info, size_t *argc, js_value_t *argv[], js_value_t **receiver, void **data) { 4866 | // Allow continuing even with a pending exception 4867 | 4868 | if (argv) { 4869 | size_t i = 0, n = info->argc < *argc ? info->argc : *argc; 4870 | 4871 | for (; i < n; i++) { 4872 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4873 | 4874 | wrapper->value = JS_DupValue(env->context, info->argv[i]); 4875 | 4876 | argv[i] = wrapper; 4877 | 4878 | js__attach_to_handle_scope(env, env->scope, wrapper); 4879 | } 4880 | 4881 | n = *argc; 4882 | 4883 | if (i < n) { 4884 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4885 | 4886 | wrapper->value = JS_UNDEFINED; 4887 | 4888 | js__attach_to_handle_scope(env, env->scope, wrapper); 4889 | 4890 | for (; i < n; i++) { 4891 | argv[i] = wrapper; 4892 | } 4893 | } 4894 | } 4895 | 4896 | if (argc) { 4897 | *argc = info->argc; 4898 | } 4899 | 4900 | if (receiver) { 4901 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4902 | 4903 | wrapper->value = JS_DupValue(env->context, info->receiver); 4904 | 4905 | *receiver = wrapper; 4906 | 4907 | js__attach_to_handle_scope(env, env->scope, wrapper); 4908 | } 4909 | 4910 | if (data) { 4911 | *data = info->callback->data; 4912 | } 4913 | 4914 | return 0; 4915 | } 4916 | 4917 | int 4918 | js_get_typed_callback_info(const js_typed_callback_info_t *info, js_env_t **env, void **data) { 4919 | // Allow continuing even with a pending exception 4920 | 4921 | return 0; 4922 | } 4923 | 4924 | int 4925 | js_get_new_target(js_env_t *env, const js_callback_info_t *info, js_value_t **result) { 4926 | // Allow continuing even with a pending exception 4927 | 4928 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 4929 | 4930 | wrapper->value = JS_DupValue(env->context, info->new_target); 4931 | 4932 | *result = wrapper; 4933 | 4934 | js__attach_to_handle_scope(env, env->scope, wrapper); 4935 | 4936 | return 0; 4937 | } 4938 | 4939 | int 4940 | js_get_arraybuffer_info(js_env_t *env, js_value_t *arraybuffer, void **pdata, size_t *plen) { 4941 | // Allow continuing even with a pending exception 4942 | 4943 | size_t len; 4944 | 4945 | uint8_t *data = JS_GetArrayBuffer(env->context, &len, arraybuffer->value); 4946 | 4947 | if (pdata) { 4948 | *pdata = data; 4949 | } 4950 | 4951 | if (plen) { 4952 | *plen = len; 4953 | } 4954 | 4955 | return 0; 4956 | } 4957 | 4958 | int 4959 | js_get_sharedarraybuffer_info(js_env_t *env, js_value_t *sharedarraybuffer, void **pdata, size_t *plen) { 4960 | // Allow continuing even with a pending exception 4961 | 4962 | size_t len; 4963 | 4964 | uint8_t *data = JS_GetArrayBuffer(env->context, &len, sharedarraybuffer->value); 4965 | 4966 | if (pdata) { 4967 | *pdata = data; 4968 | } 4969 | 4970 | if (plen) { 4971 | *plen = len; 4972 | } 4973 | 4974 | return 0; 4975 | } 4976 | 4977 | int 4978 | js_get_typedarray_info(js_env_t *env, js_value_t *typedarray, js_typedarray_type_t *ptype, void **pdata, size_t *plen, js_value_t **parraybuffer, size_t *poffset) { 4979 | // Allow continuing even with a pending exception 4980 | 4981 | size_t offset, byte_len, bytes_per_element; 4982 | 4983 | JSValue arraybuffer = JS_GetTypedArrayBuffer(env->context, typedarray->value, &offset, &byte_len, &bytes_per_element); 4984 | 4985 | if (ptype) { 4986 | JSValue global = JS_GetGlobalObject(env->context); 4987 | 4988 | #define V(class, type) \ 4989 | { \ 4990 | JSValue constructor = JS_GetPropertyStr(env->context, global, class); \ 4991 | \ 4992 | if (JS_IsInstanceOf(env->context, typedarray->value, constructor)) { \ 4993 | *ptype = type; \ 4994 | \ 4995 | JS_FreeValue(env->context, constructor); \ 4996 | \ 4997 | goto done; \ 4998 | } \ 4999 | \ 5000 | JS_FreeValue(env->context, constructor); \ 5001 | } 5002 | 5003 | V("Int8Array", js_int8array); 5004 | V("Uint8Array", js_uint8array); 5005 | V("Uint8ClampedArray", js_uint8clampedarray); 5006 | V("Int16Array", js_int16array); 5007 | V("Uint16Array", js_uint16array); 5008 | V("Int32Array", js_int32array); 5009 | V("Uint32Array", js_uint32array); 5010 | V("Float32Array", js_float32array); 5011 | V("Float64Array", js_float64array); 5012 | V("BigInt64Array", js_bigint64array); 5013 | V("BigUint64Array", js_biguint64array); 5014 | #undef V 5015 | 5016 | done: 5017 | JS_FreeValue(env->context, global); 5018 | } 5019 | 5020 | if (pdata) { 5021 | size_t size; 5022 | 5023 | *pdata = JS_GetArrayBuffer(env->context, &size, arraybuffer) + offset; 5024 | } 5025 | 5026 | if (plen) { 5027 | *plen = byte_len / bytes_per_element; 5028 | } 5029 | 5030 | if (parraybuffer) { 5031 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 5032 | 5033 | wrapper->value = JS_DupValue(env->context, arraybuffer); 5034 | 5035 | *parraybuffer = wrapper; 5036 | 5037 | js__attach_to_handle_scope(env, env->scope, wrapper); 5038 | } 5039 | 5040 | if (poffset) { 5041 | *poffset = offset; 5042 | } 5043 | 5044 | JS_FreeValue(env->context, arraybuffer); 5045 | 5046 | return 0; 5047 | } 5048 | 5049 | int 5050 | js_get_dataview_info(js_env_t *env, js_value_t *dataview, void **pdata, size_t *plen, js_value_t **parraybuffer, size_t *poffset) { 5051 | // Allow continuing even with a pending exception 5052 | 5053 | size_t offset; 5054 | 5055 | JSValue arraybuffer; 5056 | 5057 | if (pdata || poffset) { 5058 | JSValue value = JS_GetPropertyStr(env->context, dataview->value, "byteOffset"); 5059 | 5060 | JS_ToInt64(env->context, (int64_t *) &offset, value); 5061 | 5062 | JS_FreeValue(env->context, value); 5063 | } 5064 | 5065 | if (pdata || parraybuffer) { 5066 | arraybuffer = JS_GetPropertyStr(env->context, dataview->value, "buffer"); 5067 | } 5068 | 5069 | if (pdata) { 5070 | size_t size; 5071 | 5072 | *pdata = JS_GetArrayBuffer(env->context, &size, arraybuffer) + offset; 5073 | } 5074 | 5075 | if (plen) { 5076 | JSValue value = JS_GetPropertyStr(env->context, dataview->value, "byteLength"); 5077 | 5078 | JS_ToInt64(env->context, (int64_t *) plen, value); 5079 | 5080 | JS_FreeValue(env->context, value); 5081 | } 5082 | 5083 | if (parraybuffer) { 5084 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 5085 | 5086 | wrapper->value = JS_DupValue(env->context, arraybuffer); 5087 | 5088 | *parraybuffer = wrapper; 5089 | 5090 | js__attach_to_handle_scope(env, env->scope, wrapper); 5091 | } 5092 | 5093 | if (poffset) { 5094 | *poffset = offset; 5095 | } 5096 | 5097 | if (pdata || parraybuffer) { 5098 | JS_FreeValue(env->context, arraybuffer); 5099 | } 5100 | 5101 | return 0; 5102 | } 5103 | 5104 | int 5105 | js_call_function(js_env_t *env, js_value_t *recv, js_value_t *function, size_t argc, js_value_t *const argv[], js_value_t **result) { 5106 | if (JS_HasException(env->context)) return js__error(env); 5107 | 5108 | JSValue *args = malloc(argc * sizeof(JSValue)); 5109 | 5110 | for (size_t i = 0; i < argc; i++) { 5111 | args[i] = argv[i]->value; 5112 | } 5113 | 5114 | env->depth++; 5115 | 5116 | JSValue value = JS_Call(env->context, function->value, recv->value, argc, args); 5117 | 5118 | free(args); 5119 | 5120 | if (env->depth == 1) js__on_run_microtasks(env); 5121 | 5122 | env->depth--; 5123 | 5124 | if (JS_IsException(value)) { 5125 | if (env->depth == 0) { 5126 | JSValue error = JS_GetException(env->context); 5127 | 5128 | js__on_uncaught_exception(env->context, error); 5129 | } 5130 | 5131 | return js__error(env); 5132 | } 5133 | 5134 | if (result == NULL) JS_FreeValue(env->context, value); 5135 | else { 5136 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 5137 | 5138 | wrapper->value = value; 5139 | 5140 | *result = wrapper; 5141 | 5142 | js__attach_to_handle_scope(env, env->scope, wrapper); 5143 | } 5144 | 5145 | return 0; 5146 | } 5147 | 5148 | int 5149 | js_call_function_with_checkpoint(js_env_t *env, js_value_t *receiver, js_value_t *function, size_t argc, js_value_t *const argv[], js_value_t **result) { 5150 | if (JS_HasException(env->context)) return js__error(env); 5151 | 5152 | JSValue *args = malloc(argc * sizeof(JSValue)); 5153 | 5154 | for (size_t i = 0; i < argc; i++) { 5155 | args[i] = argv[i]->value; 5156 | } 5157 | 5158 | env->depth++; 5159 | 5160 | JSValue value = JS_Call(env->context, function->value, receiver->value, argc, args); 5161 | 5162 | free(args); 5163 | 5164 | js__on_run_microtasks(env); 5165 | 5166 | env->depth--; 5167 | 5168 | if (JS_IsException(value)) { 5169 | JSValue error = JS_GetException(env->context); 5170 | 5171 | js__on_uncaught_exception(env->context, error); 5172 | 5173 | return js__error(env); 5174 | } 5175 | 5176 | if (result == NULL) JS_FreeValue(env->context, value); 5177 | else { 5178 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 5179 | 5180 | wrapper->value = value; 5181 | 5182 | *result = wrapper; 5183 | 5184 | js__attach_to_handle_scope(env, env->scope, wrapper); 5185 | } 5186 | 5187 | return 0; 5188 | } 5189 | 5190 | int 5191 | js_new_instance(js_env_t *env, js_value_t *constructor, size_t argc, js_value_t *const argv[], js_value_t **result) { 5192 | if (JS_HasException(env->context)) return js__error(env); 5193 | 5194 | JSValue *args = malloc(argc * sizeof(JSValue)); 5195 | 5196 | for (size_t i = 0; i < argc; i++) { 5197 | args[i] = argv[i]->value; 5198 | } 5199 | 5200 | env->depth++; 5201 | 5202 | JSValue value = JS_CallConstructor(env->context, constructor->value, argc, args); 5203 | 5204 | free(args); 5205 | 5206 | env->depth--; 5207 | 5208 | if (JS_IsException(value)) { 5209 | if (env->depth == 0) { 5210 | JSValue error = JS_GetException(env->context); 5211 | 5212 | js__on_uncaught_exception(env->context, error); 5213 | } 5214 | 5215 | return js__error(env); 5216 | } 5217 | 5218 | if (result == NULL) JS_FreeValue(env->context, value); 5219 | else { 5220 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 5221 | 5222 | wrapper->value = value; 5223 | 5224 | *result = wrapper; 5225 | 5226 | js__attach_to_handle_scope(env, env->scope, wrapper); 5227 | } 5228 | 5229 | return 0; 5230 | } 5231 | 5232 | int 5233 | js_create_threadsafe_function(js_env_t *env, js_value_t *function, size_t queue_limit, size_t initial_thread_count, js_finalize_cb finalize_cb, void *finalize_hint, void *context, js_threadsafe_function_cb cb, js_threadsafe_function_t **result) { 5234 | int err; 5235 | 5236 | err = js_throw_error(env, NULL, "Unsupported operation"); 5237 | assert(err == 0); 5238 | 5239 | return js__error(env); 5240 | } 5241 | 5242 | int 5243 | js_get_threadsafe_function_context(js_threadsafe_function_t *function, void **result) { 5244 | return -1; 5245 | } 5246 | 5247 | int 5248 | js_call_threadsafe_function(js_threadsafe_function_t *function, void *data, js_threadsafe_function_call_mode_t mode) { 5249 | return -1; 5250 | } 5251 | 5252 | int 5253 | js_acquire_threadsafe_function(js_threadsafe_function_t *function) { 5254 | return -1; 5255 | } 5256 | 5257 | int 5258 | js_release_threadsafe_function(js_threadsafe_function_t *function, js_threadsafe_function_release_mode_t mode) { 5259 | return -1; 5260 | } 5261 | 5262 | int 5263 | js_ref_threadsafe_function(js_env_t *env, js_threadsafe_function_t *function) { 5264 | int err; 5265 | 5266 | err = js_throw_error(env, NULL, "Unsupported operation"); 5267 | assert(err == 0); 5268 | 5269 | return js__error(env); 5270 | } 5271 | 5272 | int 5273 | js_unref_threadsafe_function(js_env_t *env, js_threadsafe_function_t *function) { 5274 | int err; 5275 | 5276 | err = js_throw_error(env, NULL, "Unsupported operation"); 5277 | assert(err == 0); 5278 | 5279 | return js__error(env); 5280 | } 5281 | 5282 | int 5283 | js_add_teardown_callback(js_env_t *env, js_teardown_cb callback, void *data) { 5284 | if (JS_HasException(env->context)) return js__error(env); 5285 | 5286 | js_teardown_task_t *task = malloc(sizeof(js_teardown_task_t)); 5287 | 5288 | task->type = js_immediate_teardown; 5289 | task->immediate.cb = callback; 5290 | task->data = data; 5291 | 5292 | intrusive_list_prepend(&env->teardown_queue.tasks, &task->list); 5293 | 5294 | return 0; 5295 | } 5296 | 5297 | int 5298 | js_remove_teardown_callback(js_env_t *env, js_teardown_cb callback, void *data) { 5299 | if (JS_HasException(env->context)) return js__error(env); 5300 | 5301 | if (env->destroying) return 0; 5302 | 5303 | intrusive_list_for_each(next, &env->teardown_queue.tasks) { 5304 | js_teardown_task_t *task = intrusive_entry(next, js_teardown_task_t, list); 5305 | 5306 | if (task->type == js_immediate_teardown && task->immediate.cb == callback && task->data == data) { 5307 | intrusive_list_remove(&env->teardown_queue.tasks, &task->list); 5308 | 5309 | free(task); 5310 | 5311 | return 0; 5312 | } 5313 | } 5314 | 5315 | return 0; 5316 | } 5317 | 5318 | int 5319 | js_add_deferred_teardown_callback(js_env_t *env, js_deferred_teardown_cb callback, void *data, js_deferred_teardown_t **result) { 5320 | if (JS_HasException(env->context)) return js__error(env); 5321 | 5322 | js_teardown_task_t *task = malloc(sizeof(js_teardown_task_t)); 5323 | 5324 | task->type = js_deferred_teardown; 5325 | task->deferred.cb = callback; 5326 | task->deferred.handle.env = env; 5327 | task->data = data; 5328 | 5329 | intrusive_list_prepend(&env->teardown_queue.tasks, &task->list); 5330 | 5331 | env->refs++; 5332 | 5333 | if (result) *result = &task->deferred.handle; 5334 | 5335 | return 0; 5336 | } 5337 | 5338 | int 5339 | js_finish_deferred_teardown_callback(js_deferred_teardown_t *handle) { 5340 | // Allow continuing even with a pending exception 5341 | 5342 | int err; 5343 | 5344 | js_env_t *env = handle->env; 5345 | 5346 | intrusive_list_for_each(next, &env->teardown_queue.tasks) { 5347 | js_teardown_task_t *task = intrusive_entry(next, js_teardown_task_t, list); 5348 | 5349 | if (task->type == js_deferred_teardown && &task->deferred.handle == handle) { 5350 | intrusive_list_remove(&env->teardown_queue.tasks, &task->list); 5351 | 5352 | if (--env->refs == 0 && env->destroying) { 5353 | err = uv_async_send(&env->teardown); 5354 | assert(err == 0); 5355 | } 5356 | 5357 | free(task); 5358 | 5359 | return 0; 5360 | } 5361 | } 5362 | 5363 | return -1; 5364 | } 5365 | 5366 | int 5367 | js_throw(js_env_t *env, js_value_t *error) { 5368 | if (JS_HasException(env->context)) return js__error(env); 5369 | 5370 | JS_Throw(env->context, JS_DupValue(env->context, error->value)); 5371 | 5372 | return 0; 5373 | } 5374 | 5375 | int 5376 | js_vformat(char **result, size_t *size, const char *message, va_list args) { 5377 | va_list args_copy; 5378 | va_copy(args_copy, args); 5379 | 5380 | int res = vsnprintf(NULL, 0, message, args_copy); 5381 | 5382 | va_end(args_copy); 5383 | 5384 | if (res < 0) return res; 5385 | 5386 | *size = res + 1 /* NULL */; 5387 | *result = malloc(*size); 5388 | 5389 | va_copy(args_copy, args); 5390 | 5391 | vsnprintf(*result, *size, message, args_copy); 5392 | 5393 | va_end(args_copy); 5394 | 5395 | return 0; 5396 | } 5397 | 5398 | int 5399 | js_throw_error(js_env_t *env, const char *code, const char *message) { 5400 | if (JS_HasException(env->context)) return js__error(env); 5401 | 5402 | JSValue global = JS_GetGlobalObject(env->context); 5403 | JSValue constructor = JS_GetPropertyStr(env->context, global, "Error"); 5404 | 5405 | JSValue arg = JS_NewString(env->context, message); 5406 | 5407 | JSValue error = JS_CallConstructor(env->context, constructor, 1, &arg); 5408 | 5409 | if (code) { 5410 | JS_SetPropertyStr(env->context, error, "code", JS_NewString(env->context, code)); 5411 | } 5412 | 5413 | JS_FreeValue(env->context, arg); 5414 | JS_FreeValue(env->context, constructor); 5415 | JS_FreeValue(env->context, global); 5416 | 5417 | JS_Throw(env->context, error); 5418 | 5419 | return 0; 5420 | } 5421 | 5422 | int 5423 | js_throw_verrorf(js_env_t *env, const char *code, const char *message, va_list args) { 5424 | if (JS_HasException(env->context)) return js__error(env); 5425 | 5426 | size_t len; 5427 | char *formatted; 5428 | js_vformat(&formatted, &len, message, args); 5429 | 5430 | int err = js_throw_error(env, code, formatted); 5431 | 5432 | free(formatted); 5433 | 5434 | return err; 5435 | } 5436 | 5437 | int 5438 | js_throw_errorf(js_env_t *env, const char *code, const char *message, ...) { 5439 | if (JS_HasException(env->context)) return js__error(env); 5440 | 5441 | va_list args; 5442 | va_start(args, message); 5443 | 5444 | int err = js_throw_verrorf(env, code, message, args); 5445 | 5446 | va_end(args); 5447 | 5448 | return err; 5449 | } 5450 | 5451 | int 5452 | js_throw_type_error(js_env_t *env, const char *code, const char *message) { 5453 | if (JS_HasException(env->context)) return js__error(env); 5454 | 5455 | JSValue global = JS_GetGlobalObject(env->context); 5456 | JSValue constructor = JS_GetPropertyStr(env->context, global, "TypeError"); 5457 | 5458 | JSValue arg = JS_NewString(env->context, message); 5459 | 5460 | JSValue error = JS_CallConstructor(env->context, constructor, 1, &arg); 5461 | 5462 | if (code) { 5463 | JS_SetPropertyStr(env->context, error, "code", JS_NewString(env->context, code)); 5464 | } 5465 | 5466 | JS_FreeValue(env->context, arg); 5467 | JS_FreeValue(env->context, constructor); 5468 | JS_FreeValue(env->context, global); 5469 | 5470 | JS_Throw(env->context, error); 5471 | 5472 | return 0; 5473 | } 5474 | 5475 | int 5476 | js_throw_type_verrorf(js_env_t *env, const char *code, const char *message, va_list args) { 5477 | if (JS_HasException(env->context)) return js__error(env); 5478 | 5479 | size_t len; 5480 | char *formatted; 5481 | js_vformat(&formatted, &len, message, args); 5482 | 5483 | int err = js_throw_type_error(env, code, formatted); 5484 | 5485 | free(formatted); 5486 | 5487 | return err; 5488 | } 5489 | 5490 | int 5491 | js_throw_type_errorf(js_env_t *env, const char *code, const char *message, ...) { 5492 | if (JS_HasException(env->context)) return js__error(env); 5493 | 5494 | va_list args; 5495 | va_start(args, message); 5496 | 5497 | int err = js_throw_type_verrorf(env, code, message, args); 5498 | 5499 | va_end(args); 5500 | 5501 | return err; 5502 | } 5503 | 5504 | int 5505 | js_throw_range_error(js_env_t *env, const char *code, const char *message) { 5506 | if (JS_HasException(env->context)) return js__error(env); 5507 | 5508 | JSValue global = JS_GetGlobalObject(env->context); 5509 | JSValue constructor = JS_GetPropertyStr(env->context, global, "RangeError"); 5510 | 5511 | JSValue arg = JS_NewString(env->context, message); 5512 | 5513 | JSValue error = JS_CallConstructor(env->context, constructor, 1, &arg); 5514 | 5515 | if (code) { 5516 | JS_SetPropertyStr(env->context, error, "code", JS_NewString(env->context, code)); 5517 | } 5518 | 5519 | JS_FreeValue(env->context, arg); 5520 | JS_FreeValue(env->context, constructor); 5521 | JS_FreeValue(env->context, global); 5522 | 5523 | JS_Throw(env->context, error); 5524 | 5525 | return 0; 5526 | } 5527 | 5528 | int 5529 | js_throw_range_verrorf(js_env_t *env, const char *code, const char *message, va_list args) { 5530 | if (JS_HasException(env->context)) return js__error(env); 5531 | 5532 | size_t len; 5533 | char *formatted; 5534 | js_vformat(&formatted, &len, message, args); 5535 | 5536 | int err = js_throw_range_error(env, code, formatted); 5537 | 5538 | free(formatted); 5539 | 5540 | return err; 5541 | } 5542 | 5543 | int 5544 | js_throw_range_errorf(js_env_t *env, const char *code, const char *message, ...) { 5545 | if (JS_HasException(env->context)) return js__error(env); 5546 | 5547 | va_list args; 5548 | va_start(args, message); 5549 | 5550 | int err = js_throw_range_verrorf(env, code, message, args); 5551 | 5552 | va_end(args); 5553 | 5554 | return err; 5555 | } 5556 | 5557 | int 5558 | js_throw_syntax_error(js_env_t *env, const char *code, const char *message) { 5559 | if (JS_HasException(env->context)) return js__error(env); 5560 | 5561 | JSValue global = JS_GetGlobalObject(env->context); 5562 | JSValue constructor = JS_GetPropertyStr(env->context, global, "SyntaxError"); 5563 | 5564 | JSValue arg = JS_NewString(env->context, message); 5565 | 5566 | JSValue error = JS_CallConstructor(env->context, constructor, 1, &arg); 5567 | 5568 | if (code) { 5569 | JS_SetPropertyStr(env->context, error, "code", JS_NewString(env->context, code)); 5570 | } 5571 | 5572 | JS_FreeValue(env->context, arg); 5573 | JS_FreeValue(env->context, constructor); 5574 | JS_FreeValue(env->context, global); 5575 | 5576 | JS_Throw(env->context, error); 5577 | 5578 | return 0; 5579 | } 5580 | 5581 | int 5582 | js_throw_syntax_verrorf(js_env_t *env, const char *code, const char *message, va_list args) { 5583 | if (JS_HasException(env->context)) return js__error(env); 5584 | 5585 | size_t len; 5586 | char *formatted; 5587 | js_vformat(&formatted, &len, message, args); 5588 | 5589 | int err = js_throw_syntax_error(env, code, formatted); 5590 | 5591 | free(formatted); 5592 | 5593 | return err; 5594 | } 5595 | 5596 | int 5597 | js_throw_syntax_errorf(js_env_t *env, const char *code, const char *message, ...) { 5598 | if (JS_HasException(env->context)) return js__error(env); 5599 | 5600 | va_list args; 5601 | va_start(args, message); 5602 | 5603 | int err = js_throw_syntax_verrorf(env, code, message, args); 5604 | 5605 | va_end(args); 5606 | 5607 | return err; 5608 | } 5609 | 5610 | int 5611 | js_is_exception_pending(js_env_t *env, bool *result) { 5612 | // Allow continuing even with a pending exception 5613 | 5614 | *result = JS_HasException(env->context); 5615 | 5616 | return 0; 5617 | } 5618 | 5619 | int 5620 | js_get_and_clear_last_exception(js_env_t *env, js_value_t **result) { 5621 | // Allow continuing even with a pending exception 5622 | 5623 | JSValue error = JS_GetException(env->context); 5624 | 5625 | if (JS_IsUninitialized(error)) return js_get_undefined(env, result); 5626 | 5627 | js_value_t *wrapper = malloc(sizeof(js_value_t)); 5628 | 5629 | wrapper->value = error; 5630 | 5631 | *result = wrapper; 5632 | 5633 | js__attach_to_handle_scope(env, env->scope, wrapper); 5634 | 5635 | return 0; 5636 | } 5637 | 5638 | int 5639 | js_fatal_exception(js_env_t *env, js_value_t *error) { 5640 | // Allow continuing even with a pending exception 5641 | 5642 | js__on_uncaught_exception(env->context, JS_DupValue(env->context, error->value)); 5643 | 5644 | return 0; 5645 | } 5646 | 5647 | int 5648 | js_terminate_execution(js_env_t *env) { 5649 | // Allow continuing even with a pending exception 5650 | 5651 | JS_ThrowInternalError(env->context, "terminated"); 5652 | 5653 | JSValue error = JS_GetException(env->context); 5654 | 5655 | JS_SetUncatchableError(env->context, error); 5656 | 5657 | JS_Throw(env->context, error); 5658 | 5659 | return 0; 5660 | } 5661 | 5662 | int 5663 | js_adjust_external_memory(js_env_t *env, int64_t change_in_bytes, int64_t *result) { 5664 | // Allow continuing even with a pending exception 5665 | 5666 | env->external_memory += change_in_bytes; 5667 | 5668 | if (result) *result = env->external_memory; 5669 | 5670 | return 0; 5671 | } 5672 | 5673 | int 5674 | js_request_garbage_collection(js_env_t *env) { 5675 | // Allow continuing even with a pending exception 5676 | 5677 | if (env->platform->options.expose_garbage_collection) { 5678 | JS_RunGC(env->runtime); 5679 | } 5680 | 5681 | return 0; 5682 | } 5683 | 5684 | int 5685 | js_get_heap_statistics(js_env_t *env, js_heap_statistics_t *result) { 5686 | int err; 5687 | 5688 | err = js_throw_error(env, NULL, "Unsupported operation"); 5689 | assert(err == 0); 5690 | 5691 | return js__error(env); 5692 | } 5693 | 5694 | int 5695 | js_create_inspector(js_env_t *env, js_inspector_t **result) { 5696 | int err; 5697 | 5698 | err = js_throw_error(env, NULL, "Unsupported operation"); 5699 | assert(err == 0); 5700 | 5701 | return js__error(env); 5702 | } 5703 | 5704 | int 5705 | js_destroy_inspector(js_env_t *env, js_inspector_t *inspector) { 5706 | int err; 5707 | 5708 | err = js_throw_error(env, NULL, "Unsupported operation"); 5709 | assert(err == 0); 5710 | 5711 | return js__error(env); 5712 | } 5713 | 5714 | int 5715 | js_on_inspector_response(js_env_t *env, js_inspector_t *inspector, js_inspector_message_cb cb, void *data) { 5716 | return 0; 5717 | } 5718 | 5719 | int 5720 | js_on_inspector_paused(js_env_t *env, js_inspector_t *inspector, js_inspector_paused_cb cb, void *data) { 5721 | return 0; 5722 | } 5723 | 5724 | int 5725 | js_connect_inspector(js_env_t *env, js_inspector_t *inspector) { 5726 | int err; 5727 | 5728 | err = js_throw_error(env, NULL, "Unsupported operation"); 5729 | assert(err == 0); 5730 | 5731 | return js__error(env); 5732 | } 5733 | 5734 | int 5735 | js_send_inspector_request(js_env_t *env, js_inspector_t *inspector, js_value_t *message) { 5736 | int err; 5737 | 5738 | err = js_throw_error(env, NULL, "Unsupported operation"); 5739 | assert(err == 0); 5740 | 5741 | return js__error(env); 5742 | } 5743 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | list(APPEND tests 2 | add-finalizer 3 | add-teardown-callback 4 | add-teardown-callback-deferred 5 | add-teardown-callback-deferred-remove 6 | add-teardown-callback-multiple 7 | add-teardown-callback-remove 8 | add-type-tag 9 | add-type-tag-delegate 10 | adjust-external-memory 11 | atomics-wait-timeout 12 | atomics-wait-timeout-notify 13 | call-function 14 | call-function-ignore-return 15 | call-function-in-context 16 | call-function-microtask 17 | call-function-microtask-nested 18 | call-function-throw 19 | call-threadsafe-function-after-release 20 | call-threadsafe-function-with-data 21 | coerce-number-to-string 22 | coerce-object-to-string 23 | coerce-string-to-number 24 | coerce-symbol-to-string 25 | create-arraybuffer 26 | create-arraybuffer-too-large 27 | create-arraybuffer-with-backing-store 28 | create-date 29 | create-date-nan 30 | create-date-overflow 31 | create-delegate 32 | create-delegate-delete-throw 33 | create-delegate-get-throw 34 | create-delegate-get-without-has 35 | create-delegate-has-throw 36 | create-delegate-keys-throw 37 | create-delegate-set-throw 38 | create-env 39 | create-env-multiple 40 | create-external 41 | create-external-arraybuffer 42 | create-external-arraybuffer-with-finalizer 43 | create-external-arraybuffer-with-finalizer-detach 44 | create-external-string-latin1 45 | create-external-string-latin1-with-finalizer 46 | create-external-string-utf8 47 | create-external-string-utf8-with-finalizer 48 | create-external-string-utf16le 49 | create-external-string-utf16le-with-finalizer 50 | create-external-with-finalizer 51 | create-function 52 | create-function-args-extra 53 | create-function-args-fewer 54 | create-function-throw 55 | create-function-throw-indirect 56 | create-function-with-finalizer 57 | create-function-with-source 58 | create-module-import-missing 59 | create-promise-reject 60 | create-promise-resolve 61 | create-reference-array 62 | create-reference-bigint 63 | create-reference-boolean 64 | create-reference-function 65 | create-reference-null 66 | create-reference-number 67 | create-reference-object 68 | create-reference-object-multiple 69 | create-reference-object-weak 70 | create-reference-string 71 | create-reference-symbol 72 | create-reference-undefined 73 | create-sharedarraybuffer 74 | create-sharedarraybuffer-with-backing-store 75 | create-string-utf8 76 | create-string-utf8-with-null 77 | create-threadsafe-function 78 | create-threadsafe-function-with-callback 79 | create-threadsafe-function-with-context 80 | create-threadsafe-function-with-finalizer 81 | create-typed-function 82 | create-typed-function-no-jit 83 | create-typed-function-with-arraybuffer 84 | create-typed-function-with-bigint64 85 | create-typed-function-with-biguint64 86 | create-typed-function-with-dataview 87 | create-typed-function-with-finalizer 88 | create-typed-function-with-int8array 89 | create-typed-function-with-int32 90 | create-typed-function-with-int64 91 | create-typed-function-with-number 92 | create-typed-function-with-pointer 93 | create-typed-function-with-receiver 94 | create-typed-function-with-string-latin1 95 | create-typed-function-with-string-utf16le 96 | create-typed-function-with-uint8array 97 | create-typed-function-with-uint32 98 | create-typed-function-with-uint64 99 | create-uint8array 100 | create-unsafe-arraybuffer 101 | define-class 102 | define-class-with-method 103 | define-class-with-static-method 104 | define-class-with-static-value 105 | define-class-with-symbol-property 106 | define-class-with-value 107 | dynamic-import 108 | dynamic-import-without-handler 109 | fatal-exception 110 | get-arraybuffer-info 111 | get-dataview-info 112 | get-platform-identifier 113 | get-platform-limits 114 | get-platform-version 115 | get-property-missing 116 | get-typedarray-info-uint16array 117 | get-typedarray-info-uint16array-large 118 | get-typedarray-info-uint8array 119 | get-typedarray-info-uint8array-large 120 | get-typedarray-info-uint8array-with-offset 121 | get-value-string-utf8 122 | get-value-string-utf8-length 123 | get-value-string-utf8-no-null 124 | import-meta 125 | import-meta-throw 126 | inspector 127 | inspector-pause 128 | many-large-allocs 129 | many-small-allocs 130 | promise-rejection 131 | promise-rejection-unhandled 132 | promise-rejection-unhandled-reentrant 133 | promise-rejection-unhandled-reentrant-deferred 134 | run-module 135 | run-module-async 136 | run-module-cyclic-import 137 | run-module-double-nested-import 138 | run-module-in-context 139 | run-module-nested-import 140 | run-module-throw 141 | run-script 142 | run-script-ignore-return 143 | run-script-in-context 144 | run-script-throw 145 | set-named-property 146 | terminate-execution 147 | threads 148 | threads-platform-loop 149 | throw-error 150 | throw-error-formatted 151 | typeof-bigint 152 | typeof-boolean 153 | typeof-external 154 | typeof-function 155 | typeof-null 156 | typeof-number 157 | typeof-object 158 | typeof-string 159 | typeof-symbol 160 | typeof-undefined 161 | wasm 162 | wasm-async 163 | wasm-async-io 164 | wasm-async-io-multiple 165 | wrap 166 | wrap-remove-with-finalizer 167 | wrap-with-finalizer 168 | wrap-with-reference 169 | ) 170 | 171 | list(APPEND skipped_tests 172 | # Not supported 173 | atomics-wait-timeout 174 | atomics-wait-timeout-notify 175 | 176 | # Not supported 177 | create-typed-function 178 | create-typed-function-with-arraybuffer 179 | create-typed-function-with-bigint64 180 | create-typed-function-with-biguint64 181 | create-typed-function-with-dataview 182 | create-typed-function-with-finalizer 183 | create-typed-function-with-int8array 184 | create-typed-function-with-int32 185 | create-typed-function-with-int64 186 | create-typed-function-with-number 187 | create-typed-function-with-pointer 188 | create-typed-function-with-receiver 189 | create-typed-function-with-string-latin1 190 | create-typed-function-with-string-utf16le 191 | create-typed-function-with-uint8array 192 | create-typed-function-with-uint32 193 | create-typed-function-with-uint64 194 | 195 | # Not supported 196 | inspector 197 | inspector-pause 198 | 199 | # Not supported 200 | wasm 201 | wasm-async 202 | wasm-async-io 203 | wasm-async-io-multiple 204 | 205 | # Not implemented 206 | call-threadsafe-function-after-release 207 | call-threadsafe-function-with-data 208 | create-threadsafe-function 209 | create-threadsafe-function-with-callback 210 | create-threadsafe-function-with-context 211 | create-threadsafe-function-with-finalizer 212 | 213 | # Not implemented 214 | call-function-in-context 215 | run-module-in-context 216 | run-script-in-context 217 | ) 218 | 219 | foreach(test IN LISTS tests) 220 | add_executable(${test} ${js}/test/${test}.c) 221 | 222 | target_link_libraries( 223 | ${test} 224 | PRIVATE 225 | js_static 226 | ) 227 | 228 | add_test( 229 | NAME ${test} 230 | COMMAND ${test} 231 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 232 | ) 233 | 234 | set_tests_properties( 235 | ${test} 236 | PROPERTIES 237 | TIMEOUT 30 238 | ) 239 | 240 | if(${test} IN_LIST skipped_tests) 241 | set_tests_properties( 242 | ${test} 243 | PROPERTIES 244 | DISABLED ON 245 | ) 246 | endif() 247 | endforeach() 248 | --------------------------------------------------------------------------------