```
├── .github/
├── workflows/
├── release.yml
├── .gitignore
├── .gitmodules
├── LICENSE
├── LICENSE.nodejs
├── LICENSE.v8
├── README.md
├── assets/
├── banner.png
├── boxy.svg
├── hakostress.gif
├── bridge/
├── CMakeLists.txt
├── build.h
├── cmake/
├── GetGitVersion.cmake
├── ParseWasiVersion.cmake
├── version.h.in
├── wasi_version.h.in
├── hako.c
├── hako.h
├── embedders/
├── ts/
├── .gitignore
├── README.md
├── biome.json
├── build.ts
├── package-lock.json
├── package.json
├── patches/
├── uwasi+1.4.0.patch
├── src/
├── etc/
├── errors.ts
├── ffi.ts
```
## /.github/workflows/release.yml
```yml path="/.github/workflows/release.yml"
name: Publish to NPM
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
deployments: write
steps:
- name: checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-node@v4
with:
node-version: '22.14.0'
registry-url: 'https://registry.npmjs.org'
- name: setup bun
uses: oven-sh/setup-bun@v2
with:
bun-version: '1.2.9'
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@802fa1a2c4e212495c05bf94dba2704a92a472be
with:
cmake-version: '4.0.x'
- name: envsetup
run: chmod +x tools/envsetup.sh && tools/envsetup.sh
- name: patch
run: chmod +x tools/patch.sh && tools/patch.sh
# the TypeScript embedder is up first
- name: install dependencies (TS)
working-directory: embedders/ts
run: |
# npm has a bug, nothing we can do.
rm -rf package-lock.json
npm i
# just until uwasi deploys a new version
npm run patch:deps
- name: generate builds (TS)
working-directory: embedders/ts
run: npm run generate:builds
- name: generate version (TS)
working-directory: embedders/ts
run: npm run generate:version
- name: lint (TS)
working-directory: embedders/ts
run: |
source ${{ github.workspace }}/tools/third-party/env.sh
npm run lint
- name: test (TS)
working-directory: embedders/ts
run: npm run test
- name: build (TS)
working-directory: embedders/ts
run: npm run build
# we will publish in a bit, let's build and publish REPL
- name: install dependencies (REPL)
working-directory: examples/repl
run: bun install
- name: generate build (REPL)
working-directory: examples/repl
run: bun run build
- name: publish REPL to cloudflare pages
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.PAGE_PUBLISH_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: hakorepl
directory: dist
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
branch: production
workingDirectory: examples/repl
wranglerVersion: '3'
- name: Publish package to NPM
working-directory: embedders/ts
run: npm publish --provenance --access public --tag latest
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```
## /.gitignore
```gitignore path="/.gitignore"
.DS_Store
bridge/build
bridge/.cache
bridge/version.h
bridge/wasi_version.h
dist
node_modules
*.wasm
.vscode
bun.lock
hako.sln
*.g.ts
obj
bin
tools/third-party
```
## /.gitmodules
```gitmodules path="/.gitmodules"
[submodule "primjs"]
path = primjs
url = https://github.com/lynx-family/primjs
```
## /LICENSE
``` path="/LICENSE"
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
## /LICENSE.nodejs
```nodejs path="/LICENSE.nodejs"
The MIT License (MIT)
Copyright (c) 2017 [Node.js API collaborators](https://github.com/nodejs/node-addon-api#collaborators)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
```
## /LICENSE.v8
```v8 path="/LICENSE.v8"
Copyright 2006-2011, the V8 project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
```
## /README.md
Hako (ha-ko) or 箱 means “box” in Japanese.
[](https://www.apache.org/licenses/LICENSE-2.0.txt)
Hako is a embeddable, lightweight, secure, high-performance JavaScript engine. It is a fork of PrimJS; Hako has full support for ES2019 and later ESNext features, and offers superior performance and a better development experience when compared to QuickJS.
## What makes it secure?
Hako compiles down to WebAssembly, a memory-safe, sandboxed execution environment. This means even though Hako is written in C/C++, programs it is embedded in have an extra layer of security from memory corruption attacks. Hako also has a built-in sandboxing mechanism in the form of VMContext which allows you to restrict the capabilities of JavaScript code.
A number of APIs are exposed to limit the amount of memory and 'gas' a script can consume before it is terminated, and development follows an opinionated 'fail-fast' design for bailing out of potentially unstable code that could consume or block I/O. Combining all of these, you can run hundreds of VMs in parallel on a single machine.

## What makes it embeddable?
Hako does not use Emscripten to compile down to WebAssembly; so long as your language of choice has a WebAssembly runtime, you can embed Hako in it by implementing the necessary imports. You can see an example of embedding Hako in Go [here](https://gist.github.com/andrewmd5/197efb527ef40131c34ca12fd6d0a61e).
It is also incredibly tiny. The release build is ~800KB.
## What makes it fast?
Hako is a fork of [PrimJS](https://github.com/lynx-family/primjs), which in sythentic benchmarks shows performance gains of 28% over QuickJS. Compiling to WebAssembly has no noticeable impact on performance as the amazing JIT compilers of JavaScriptCore/Bun, V8/NodeJS, and Wasmtime allow code to run at near-native speeds. You can enable profiling in Hako to see how your code is performing.
Here's a simple string concatenation benchmark comparing Hako to QuickJS and QuickJS-NG:
```javascript
const t1 = Date.now()
let str = '';
for (let i = 0; i < 1000_000; i++) {
str += 'a';
}
const t2 = Date.now()
console.log(t2 - t1);
// Results:
// quickjs-ng: 6854ms
// quickjs: 112ms
// hako: 92ms
```
As you can see, Hako outperforms both QuickJS and QuickJS-NG in this common operation, with QuickJS-NG being particularly slow at string concatenation.
## Notice
This project is still in early access - documentation is a work in progress, and the API/ABI should not be considered stable. If you wish to contribute, please do so by opening an issue or a pull request for further discussion. Your feedback is greatly appreciated.
You can find the intitial reference implementation of Hako in TypeScript [here](./embedders/ts/README.md).
For further reading and to get a sense of the roadmap see the initial releasse blog post [here](https://andrews.substack.com/p/hako).
## /assets/banner.png
Binary file available at https://raw.githubusercontent.com/andrewmd5/hako/refs/heads/main/assets/banner.png
## /assets/boxy.svg
```svg path="/assets/boxy.svg"
```
## /assets/hakostress.gif
Binary file available at https://raw.githubusercontent.com/andrewmd5/hako/refs/heads/main/assets/hakostress.gif
## /bridge/CMakeLists.txt
cmake_minimum_required(VERSION 4.0.0)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(HAVE_FLAG_SEARCH_PATHS_FIRST 0)
# Get WASI SDK path from environment - require it to be set
if(DEFINED ENV{WASI_SDK_PATH})
set(WASI_SDK_PATH $ENV{WASI_SDK_PATH})
else()
message(FATAL_ERROR "WASI SDK not found. Please set WASI_SDK_PATH environment variable.")
endif()
if(DEFINED ENV{PRIMJS_DIR})
set(PRIMJS_DIR $ENV{PRIMJS_DIR})
else()
message(FATAL_ERROR "PrimJS dir not found. Please set PRIMJS_DIR environment variable.")
endif()
message(STATUS "Using WASI SDK from: ${WASI_SDK_PATH}")
message(STATUS "Using PrimJS from: ${PRIMJS_DIR}")
# Set WASI tools
set(CMAKE_C_COMPILER "${WASI_SDK_PATH}/bin/clang")
set(CMAKE_CXX_COMPILER "${WASI_SDK_PATH}/bin/clang++")
set(CMAKE_AR "${WASI_SDK_PATH}/bin/llvm-ar")
set(CMAKE_RANLIB "${WASI_SDK_PATH}/bin/llvm-ranlib")
set(CMAKE_STRIP "${WASI_SDK_PATH}/bin/llvm-strip")
# Set WASI sysroot and target
set(CMAKE_SYSROOT "${WASI_SDK_PATH}/share/wasi-sysroot")
set(CMAKE_C_COMPILER_TARGET "wasm32-wasi-threads")
set(CMAKE_CXX_COMPILER_TARGET "wasm32-wasi-threads")
set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
set(CMAKE_C_COMPILER_WORKS "1")
set(CMAKE_CXX_COMPILER_WORKS "1")
project("hako")
set(CMAKE_C_LINK_FLAGS "")
set(CMAKE_CXX_LINK_FLAGS "")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# Include the ParseWasiVersion module
include(ParseWasiVersion)
# Parse the WASI SDK VERSION file
parse_wasi_version(${WASI_SDK_PATH})
# Create a wasi_version.h file with the parsed values
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/wasi_version.h.in
${CMAKE_CURRENT_SOURCE_DIR}/wasi_version.h
)
include(GetGitVersion)
get_git_version(GIT_VERSION)
set(HAKO_VERSION ${GIT_VERSION})
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.h.in
${CMAKE_CURRENT_SOURCE_DIR}/version.h
)
# Basic settings
enable_language(C CXX ASM)
set(CMAKE_CXX_STANDARD 17)
# Build type
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()
message(STATUS "Building in ${CMAKE_BUILD_TYPE} mode")
message(STATUS "Source directory: ${CMAKE_CURRENT_SOURCE_DIR}")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
# Debug mode - add debug info, disable optimizations
set(OPTIMIZATION_FLAGS "-g -O0")
else()
# Release mode - use full optimization
set(OPTIMIZATION_FLAGS "-O3")
set(WASM_OPT_FLAG "--wasm-opt")
endif()
# Configuration options
option(ENABLE_QUICKJS_DEBUGGER "Enable quickjs debugger" OFF)
option(ENABLE_HAKO_PROFILER "Enable the Hako profiler" OFF)
option(ENABLE_LEPUSNG "Enable LepusNG" ON)
option(ENABLE_PRIMJS_SNAPSHOT "Enable primjs snapshot" OFF)
option(ENABLE_COMPATIBLE_MM "Enable compatible memory" OFF)
option(DISABLE_NANBOX "Disable nanbox" OFF)
option(ENABLE_CODECACHE "Enable code cache" OFF)
option(CACHE_PROFILE "Enable cache profile" OFF)
option(ENABLE_MEM "Enable memory detection" OFF)
option(ENABLE_ATOMICS "Enable Atomics" ON)
option(FORCE_GC "Enable force gc" OFF)
option(ENABLE_ASAN "Enable address sanitizer" OFF)
option(ENABLE_BIGNUM "Enable bignum" OFF)
option(WASM_INITIAL_MEMORY "Initial memory size in bytes" "25165824") # 24MB default
option(WASM_MAX_MEMORY "Maximum memory size in bytes" "268435456") # 256MB default
option(WASM_STACK_SIZE "Stack size in bytes" "8388608") # 8MB default
option(WASM_OUTPUT_NAME "Output name for the WASM file" "hako.wasm")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(ENABLE_MEM ON)
endif()
# Required WASI emulation options
add_compile_options(
-Wno-cast-function-type-mismatch
-D_WASI_EMULATED_MMAN
-D_WASI_EMULATED_SIGNAL
-D_WASI_EMULATED_PROCESS_CLOCKS
)
# Simplify for WASI
if(NOT DEFINED LYNX_SIMPLIFY)
add_definitions(-DLYNX_SIMPLIFY -DENABLE_BUILTIN_SERIALIZE)
endif()
# Compiler flags
set(CMAKE_COMMON_FLAGS
"${OPTIMIZATION_FLAGS} -fPIC -ffunction-sections -fdata-sections \
-fno-short-enums -fno-strict-aliasing -Wall -Wextra -Wno-unused-parameter \
-Wno-unused-function -faddrsig -Wno-c99-designator -Wno-unknown-warning-option \
-Wno-sign-compare -Wno-unused-but-set-variable -pthread -matomics -msimd128 -mmultivalue -mmutable-globals -mtail-call -msign-ext -mbulk-memory -mnontrapping-fptoint -mextended-const")
if(ENABLE_ASAN)
add_definitions(-DHAKO_SANITIZE_LEAK)
set(CMAKE_COMMON_FLAGS
"${CMAKE_COMMON_FLAGS} -fno-omit-frame-pointer")
else()
set(CMAKE_COMMON_FLAGS
"${CMAKE_COMMON_FLAGS} -fomit-frame-pointer -fno-sanitize=safe-stack")
endif()
set(CMAKE_C_FLAGS "${CMAKE_COMMON_FLAGS} ${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_COMMON_FLAGS} ${CMAKE_CXX_FLAGS} -std=c++17")
set(CMAKE_SHARED_LINKER_FLAGS
"${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--build-id=sha1 -O2")
# Common definitions
add_definitions(-DEMSCRIPTEN)
add_definitions(-D__WASI_SDK__)
add_definitions(-DOS_WASI=1)
if(${ENABLE_LEPUSNG} AND ${ENABLE_BIGNUM})
message(FATAL_ERROR "ENABLE_LEPUSNG and ENABLE_BIGNUM cannot be both enabled.")
endif()
if(${ENABLE_BIGNUM})
add_definitions(-DCONFIG_BIGNUM)
endif()
if(${ENABLE_ATOMICS})
add_definitions(-DENABLE_ATOMICS -DCONFIG_ATOMICS)
endif()
if(${ENABLE_HAKO_PROFILER})
add_definitions(-DENABLE_HAKO_PROFILER)
endif()
# Feature definitions
if(${ENABLE_MEM})
add_definitions(-DDEBUG_MEMORY)
add_definitions(-DDUMP_QJS_VALUE)
add_definitions(-DDUMP_LEAKS)
endif()
if(${FORCE_GC})
add_definitions(-DFORCE_GC_AT_MALLOC)
endif()
if(${ENABLE_LEPUSNG})
add_definitions(-DENABLE_LEPUSNG)
endif()
if(${DISABLE_NANBOX})
add_definitions(-DDISABLE_NANBOX=1)
else()
add_definitions(-DDISABLE_NANBOX=0)
endif()
# primjs snapshot version
if(${ENABLE_PRIMJS_SNAPSHOT})
add_definitions(-DENABLE_PRIMJS_SNAPSHOT)
if(${ENABLE_COMPATIBLE_MM})
add_definitions(-DENABLE_COMPATIBLE_MM)
endif()
if(${ENABLE_QUICKJS_DEBUGGER})
set(primjs_embedded_sources
${PRIMJS_DIR}/src/interpreter/primjs/wasi/embedded-inspector.S)
else()
set(primjs_embedded_sources
${PRIMJS_DIR}/src/interpreter/primjs/wasi/embedded.S)
endif()
endif()
# List all QuickJS sources
set(quickjs_sources
${PRIMJS_DIR}/src/basic/log/logging.cc
${PRIMJS_DIR}/src/gc/allocator.cc
${PRIMJS_DIR}/src/gc/collector.cc
${PRIMJS_DIR}/src/gc/global-handles.cc
${PRIMJS_DIR}/src/gc/qjsvaluevalue-space.cc
${PRIMJS_DIR}/src/gc/sweeper.cc
${PRIMJS_DIR}/src/gc/thread_pool.cc
${PRIMJS_DIR}/src/gc/collector.cc
${PRIMJS_DIR}/src/interpreter/quickjs/source/cutils.cc
${PRIMJS_DIR}/src/interpreter/quickjs/source/libregexp.cc
${PRIMJS_DIR}/src/interpreter/quickjs/source/libunicode.cc
${PRIMJS_DIR}/src/interpreter/quickjs/source/primjs_monitor.cc
${PRIMJS_DIR}/src/interpreter/quickjs/source/quickjs_gc.cc
${PRIMJS_DIR}/src/interpreter/quickjs/source/quickjs_queue.cc
${PRIMJS_DIR}/src/interpreter/quickjs/source/quickjs_version.cc
${PRIMJS_DIR}/src/interpreter/quickjs/source/quickjs-libc.cc
${PRIMJS_DIR}/src/interpreter/quickjs/source/quickjs.cc)
# Add BigNum support if enabled
if(ENABLE_BIGNUM)
set(quickjs_sources
${quickjs_sources}
${PRIMJS_DIR}/src/interpreter/quickjs/source/libbf.cc)
endif()
# Add debugger sources if enabled
if(${ENABLE_QUICKJS_DEBUGGER})
add_definitions(-DENABLE_QUICKJS_DEBUGGER)
set(quickjs_debugger_sources
${PRIMJS_DIR}/src/inspector/cpuprofiler/cpu_profiler.cc
${PRIMJS_DIR}/src/inspector/cpuprofiler/profile_generator.cc
${PRIMJS_DIR}/src/inspector/cpuprofiler/profile_tree.cc
${PRIMJS_DIR}/src/inspector/cpuprofiler/profiler_sampling.cc
${PRIMJS_DIR}/src/inspector/cpuprofiler/tracing_cpu_profiler.cc
${PRIMJS_DIR}/src/inspector/debugger/debugger_breakpoint.cc
${PRIMJS_DIR}/src/inspector/debugger/debugger_callframe.cc
${PRIMJS_DIR}/src/inspector/debugger/debugger_properties.cc
${PRIMJS_DIR}/src/inspector/debugger/debugger_queue.cc
${PRIMJS_DIR}/src/inspector/debugger/debugger.cc
${PRIMJS_DIR}/src/inspector/heapprofiler/edge.cc
${PRIMJS_DIR}/src/inspector/heapprofiler/entry.cc
${PRIMJS_DIR}/src/inspector/heapprofiler/gen.cc
${PRIMJS_DIR}/src/inspector/heapprofiler/heapexplorer.cc
${PRIMJS_DIR}/src/inspector/heapprofiler/heapprofiler.cc
${PRIMJS_DIR}/src/inspector/heapprofiler/serialize.cc
${PRIMJS_DIR}/src/inspector/heapprofiler/snapshot.cc
${PRIMJS_DIR}/src/inspector/runtime/runtime.cc
${PRIMJS_DIR}/src/inspector/protocols.cc
${PRIMJS_DIR}/src/inspector/string_tools.cc)
# Add debugger sources to QuickJS sources
set(quickjs_sources ${quickjs_sources} ${quickjs_debugger_sources})
endif()
# Add embedded sources if defined
if(DEFINED primjs_embedded_sources)
set(quickjs_sources ${quickjs_sources} ${primjs_embedded_sources})
endif()
# Include directories
include_directories(
${PRIMJS_DIR}/src
${PRIMJS_DIR}/src/interpreter
${PRIMJS_DIR}/src/interpreter/quickjs/include
${PRIMJS_DIR}/src/interpreter/quickjs/source
${PRIMJS_DIR}/src/napi
${PRIMJS_DIR}/src/napi/common
${PRIMJS_DIR}/src/napi/env
${PRIMJS_DIR}/src/napi/internal
${PRIMJS_DIR}/src/napi/quickjs
${PRIMJS_DIR}/src/napi/v8)
# Build the QuickJS static library
add_library(quickjs STATIC ${quickjs_sources})
set_target_properties(quickjs PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
OUTPUT_NAME "quick")
# Add WASI-specific link options to the QuickJS library
target_link_options(quickjs PRIVATE
"-Wl,--allow-undefined -Wl,wasi-emulated-signal -Wl,wasi-emulated-process-clocks -Wl,--shared-memory")
# Build the hako WASM module
set(hako_source
${CMAKE_CURRENT_SOURCE_DIR}/hako.c)
add_executable(hako_reactor ${hako_source})
set_target_properties(hako_reactor PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
OUTPUT_NAME ${WASM_OUTPUT_NAME}
)
target_link_libraries(hako_reactor PRIVATE quickjs)
target_include_directories(hako_reactor PRIVATE
${PRIMJS_DIR}/src
${PRIMJS_DIR}/src/interpreter
${PRIMJS_DIR}/src/interpreter/quickjs/include
${PRIMJS_DIR}/src/interpreter/quickjs/source
${PRIMJS_DIR}/src/napi
${PRIMJS_DIR}/src/napi/common
${PRIMJS_DIR}/src/napi/env
${PRIMJS_DIR}/src/napi/internal
${PRIMJS_DIR}/src/napi/quickjs
${PRIMJS_DIR}/src/napi/v8
)
# WASM-specific link options
target_link_options(hako_reactor PRIVATE
-mexec-model=reactor
-Wl,--import-memory,--export-memory
-Wl,--no-entry
-Wl,--export=malloc
-Wl,--export=free
-Wl,--export=__heap_base
-Wl,--export=__data_end
-Wl,--allow-undefined
-Wl,-z,stack-size=${WASM_STACK_SIZE}
-Wl,--initial-memory=${WASM_INITIAL_MEMORY}
-Wl,--max-memory=${WASM_MAX_MEMORY}
${WASM_OPT_FLAG}
)
message(STATUS "Configuration summary:")
message(STATUS " WASI SDK path: ${WASI_SDK_PATH}")
message(STATUS " PrimJS directory: ${PRIMJS_DIR}")
message(STATUS " Output WASM: ${WASM_OUTPUT_NAME}")
message(STATUS " Initial memory: ${WASM_INITIAL_MEMORY} bytes")
message(STATUS " Maximum memory: ${WASM_MAX_MEMORY} bytes")
message(STATUS " Stack size: ${WASM_STACK_SIZE} bytes")
message(STATUS " Bignum support: ${ENABLE_BIGNUM}")
message(STATUS " LepusNG support: ${ENABLE_LEPUSNG}")
message(STATUS " Debugger support: ${ENABLE_QUICKJS_DEBUGGER}")
if(DEFINED WASI_VERSION_PARSED)
message(STATUS " WASI SDK version: ${WASI_VERSION}")
if(DEFINED WASI_WASI_LIBC)
message(STATUS " WASI-libc version: ${WASI_WASI_LIBC}")
endif()
if(DEFINED WASI_LLVM)
message(STATUS " LLVM version: ${WASI_LLVM}")
endif()
if(DEFINED WASI_LLVM_VERSION)
message(STATUS " LLVM detailed version: ${WASI_LLVM_VERSION}")
endif()
if(DEFINED WASI_CONFIG)
message(STATUS " WASI config: ${WASI_CONFIG}")
endif()
endif()
## /bridge/build.h
```h path="/bridge/build.h"
#ifndef BUILD_H
#define BUILD_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Build flags indicating which features are enabled in the runtime
*/
typedef enum HAKO_BuildFlag {
HAKO_BuildFlag_Debug = 1 << 0, /* Debug build */
HAKO_BuildFlag_Sanitizer = 1 << 1, /* Address sanitizer enabled */
HAKO_BuildFlag_Bignum = 1 << 2, /* BigNum support enabled */
HAKO_BuildFlag_LepusNG = 1 << 3, /* LepusNG enabled */
HAKO_BuildFlag_Debugger = 1 << 4, /* QuickJS debugger enabled */
HAKO_BuildFlag_Snapshot = 1 << 5, /* PrimJS snapshot enabled */
HAKO_BuildFlag_CompatibleMM = 1 << 6, /* Compatible memory management */
HAKO_BuildFlag_Nanbox = 1 << 7, /* NaN boxing enabled */
HAKO_BuildFlag_CodeCache = 1 << 8, /* Code cache enabled */
HAKO_BuildFlag_CacheProfile = 1 << 9, /* Cache profiling enabled */
HAKO_BuildFlag_MemDetection = 1 << 10, /* Memory leak detection enabled */
HAKO_BuildFlag_Atomics = 1 << 11, /* Atomics support enabled */
HAKO_BuildFlag_ForceGC = 1 << 12, /* Force GC at allocation enabled */
HAKO_BuildFlag_LynxSimplify = 1 << 13, /* Lynx simplification enabled */
HAKO_BuildFlag_BuiltinSerialize = 1 << 14, /* Builtin serialization enabled */
HAKO_BuildFlag_HakoProfiler = 1 << 15, /* Hako profiler enabled */
} HAKO_BuildFlag;
/* Build flags as individual compile-time constants */
#if defined(DEBUG) || defined(_DEBUG)
#define HAKO_HAS_DEBUG 1
#else
#define HAKO_HAS_DEBUG 0
#endif
#ifdef __SANITIZE_ADDRESS__
#define HAKO_HAS_SANITIZER 1
#else
#define HAKO_HAS_SANITIZER 0
#endif
#ifdef CONFIG_BIGNUM
#define HAKO_HAS_BIGNUM 1
#else
#define HAKO_HAS_BIGNUM 0
#endif
#ifdef ENABLE_LEPUSNG
#define HAKO_HAS_LEPUSNG 1
#else
#define HAKO_HAS_LEPUSNG 0
#endif
#ifdef ENABLE_QUICKJS_DEBUGGER
#define HAKO_HAS_DEBUGGER 1
#else
#define HAKO_HAS_DEBUGGER 0
#endif
#ifdef ENABLE_PRIMJS_SNAPSHOT
#define HAKO_HAS_SNAPSHOT 1
#else
#define HAKO_HAS_SNAPSHOT 0
#endif
#ifdef ENABLE_COMPATIBLE_MM
#define HAKO_HAS_COMPATIBLE_MM 1
#else
#define HAKO_HAS_COMPATIBLE_MM 0
#endif
#if defined(DISABLE_NANBOX) && DISABLE_NANBOX == 0
#define HAKO_HAS_NANBOX 1
#else
#define HAKO_HAS_NANBOX 0
#endif
#ifdef ENABLE_CODECACHE
#define HAKO_HAS_CODECACHE 1
#else
#define HAKO_HAS_CODECACHE 0
#endif
#ifdef CACHE_PROFILE
#define HAKO_HAS_CACHE_PROFILE 1
#else
#define HAKO_HAS_CACHE_PROFILE 0
#endif
#ifdef DEBUG_MEMORY
#define HAKO_HAS_MEM_DETECTION 1
#else
#define HAKO_HAS_MEM_DETECTION 0
#endif
#if defined(CONFIG_ATOMICS) || defined(ENABLE_ATOMICS)
#define HAKO_HAS_ATOMICS 1
#else
#define HAKO_HAS_ATOMICS 0
#endif
#ifdef FORCE_GC_AT_MALLOC
#define HAKO_HAS_FORCE_GC 1
#else
#define HAKO_HAS_FORCE_GC 0
#endif
#ifdef LYNX_SIMPLIFY
#define HAKO_HAS_LYNX_SIMPLIFY 1
#else
#define HAKO_HAS_LYNX_SIMPLIFY 0
#endif
#ifdef ENABLE_BUILTIN_SERIALIZE
#define HAKO_HAS_BUILTIN_SERIALIZE 1
#else
#define HAKO_HAS_BUILTIN_SERIALIZE 0
#endif
#ifdef ENABLE_HAKO_PROFILER
#define HAKO_HAS_HAKO_PROFILER 1
#else
#define HAKO_HAS_HAKO_PROFILER 0
#endif
/* Define the build flags value as a true compile-time constant */
#define HAKO_BUILD_FLAGS_VALUE \
((HAKO_HAS_DEBUG ? HAKO_BuildFlag_Debug : 0) | \
(HAKO_HAS_SANITIZER ? HAKO_BuildFlag_Sanitizer : 0) | \
(HAKO_HAS_BIGNUM ? HAKO_BuildFlag_Bignum : 0) | \
(HAKO_HAS_LEPUSNG ? HAKO_BuildFlag_LepusNG : 0) | \
(HAKO_HAS_DEBUGGER ? HAKO_BuildFlag_Debugger : 0) | \
(HAKO_HAS_SNAPSHOT ? HAKO_BuildFlag_Snapshot : 0) | \
(HAKO_HAS_COMPATIBLE_MM ? HAKO_BuildFlag_CompatibleMM : 0) | \
(HAKO_HAS_NANBOX ? HAKO_BuildFlag_Nanbox : 0) | \
(HAKO_HAS_CODECACHE ? HAKO_BuildFlag_CodeCache : 0) | \
(HAKO_HAS_CACHE_PROFILE ? HAKO_BuildFlag_CacheProfile : 0) | \
(HAKO_HAS_MEM_DETECTION ? HAKO_BuildFlag_MemDetection : 0) | \
(HAKO_HAS_ATOMICS ? HAKO_BuildFlag_Atomics : 0) | \
(HAKO_HAS_FORCE_GC ? HAKO_BuildFlag_ForceGC : 0) | \
(HAKO_HAS_LYNX_SIMPLIFY ? HAKO_BuildFlag_LynxSimplify : 0) | \
(HAKO_HAS_BUILTIN_SERIALIZE ? HAKO_BuildFlag_BuiltinSerialize : 0) | \
(HAKO_HAS_HAKO_PROFILER ? HAKO_BuildFlag_HakoProfiler : 0))
/* Helper macro to check if a build flag is enabled at compile time */
#define HAKO_IS_ENABLED(flag) ((HAKO_BUILD_FLAGS_VALUE & (flag)) != 0)
/**
* @brief Structure containing build information
*/
typedef struct HakoBuildInfo {
const char *version; /* Git version */
HAKO_BuildFlag flags; /* Feature flags bitmap */
const char *build_date; /* Build date */
const char *wasi_sdk_version;
const char *wasi_libc; /* WASI-libc commit hash */
const char *llvm; /* LLVM commit hash */
const char *llvm_version; /* LLVM version */
const char *config; /* Configuration hash */
} HakoBuildInfo;
#ifdef __cplusplus
}
#endif
#endif /* BUILD_H */
```
## /bridge/cmake/GetGitVersion.cmake
```cmake path="/bridge/cmake/GetGitVersion.cmake"
# GetGitVersion.cmake
# Returns a version string from Git tags
#
# This function inspects the git tags for the project and returns a string
# into a CMake variable
#
# get_git_version()
#
# - Example
#
# include(GetGitVersion)
# get_git_version(GIT_VERSION)
find_package(Git)
if(__get_git_version)
return()
endif()
set(__get_git_version INCLUDED)
function(get_git_version var)
if(GIT_EXECUTABLE)
# First try the exact tag - this works better in CI environments
execute_process(
COMMAND ${GIT_EXECUTABLE} describe --exact-match --tags
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE status_exact
OUTPUT_VARIABLE GIT_VERSION_EXACT
ERROR_VARIABLE error_exact
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT ${status_exact})
# We found an exact tag match
set(GIT_VERSION ${GIT_VERSION_EXACT})
message(STATUS "Git exact tag match: ${GIT_VERSION}")
else()
# Try with the pattern matching approach
execute_process(
COMMAND ${GIT_EXECUTABLE} describe --match "v[0-9]*.[0-9]*.[0-9]*" --abbrev=8
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE status
OUTPUT_VARIABLE GIT_VERSION
ERROR_VARIABLE error_output
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(${status})
message(STATUS "Git version detection failed with: ${error_output}")
# Fallback to tag listing to see what's available
execute_process(
COMMAND ${GIT_EXECUTABLE} tag -l
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE available_tags
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
message(STATUS "Available tags: ${available_tags}")
set(GIT_VERSION "0.0.0")
else()
string(REGEX REPLACE "-[0-9]+-g" "-" GIT_VERSION ${GIT_VERSION})
endif()
endif()
else()
set(GIT_VERSION "0.0.0")
endif()
if(GIT_VERSION MATCHES "^v")
string(SUBSTRING ${GIT_VERSION} 1 -1 GIT_VERSION)
endif()
message(STATUS "Git Version: ${GIT_VERSION}")
set(${var} ${GIT_VERSION} PARENT_SCOPE)
endfunction()
```
## /bridge/cmake/ParseWasiVersion.cmake
```cmake path="/bridge/cmake/ParseWasiVersion.cmake"
# ParseWasiVersion.cmake
# Function to parse the WASI SDK VERSION file into CMake variables
#
# This function reads the VERSION file from the WASI SDK directory and parses
# its contents into CMake variables that can be used in the build system
# and in generated source files.
if(__parse_wasi_version)
return()
endif()
set(__parse_wasi_version INCLUDED)
function(parse_wasi_version WASI_SDK_PATH)
set(VERSION_FILE "${WASI_SDK_PATH}/VERSION")
# Check if VERSION file exists
if(NOT EXISTS "${VERSION_FILE}")
message(WARNING "WASI SDK VERSION file not found at ${VERSION_FILE}")
# Set default values
set(WASI_VERSION "unknown" PARENT_SCOPE)
set(WASI_WASI_LIBC "unknown" PARENT_SCOPE)
set(WASI_LLVM "unknown" PARENT_SCOPE)
set(WASI_LLVM_VERSION "unknown" PARENT_SCOPE)
set(WASI_CONFIG "unknown" PARENT_SCOPE)
return()
endif()
# Read the VERSION file content
file(READ "${VERSION_FILE}" VERSION_CONTENT)
# Extract the WASI SDK version (first line)
if(VERSION_CONTENT MATCHES "^([^\n]*)")
set(WASI_VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE)
message(STATUS "WASI SDK version: ${CMAKE_MATCH_1}")
else()
set(WASI_VERSION "unknown" PARENT_SCOPE)
endif()
# Extract WASI-libc commit hash
if(VERSION_CONTENT MATCHES "wasi-libc:[ \t]*([^\n]*)")
set(WASI_WASI_LIBC "${CMAKE_MATCH_1}" PARENT_SCOPE)
message(STATUS "WASI-libc commit: ${CMAKE_MATCH_1}")
else()
set(WASI_WASI_LIBC "unknown" PARENT_SCOPE)
endif()
# Extract LLVM commit hash
if(VERSION_CONTENT MATCHES "llvm:[ \t]*([^\n]*)")
set(WASI_LLVM "${CMAKE_MATCH_1}" PARENT_SCOPE)
message(STATUS "LLVM commit: ${CMAKE_MATCH_1}")
else()
set(WASI_LLVM "unknown" PARENT_SCOPE)
endif()
# Extract LLVM version
if(VERSION_CONTENT MATCHES "llvm-version:[ \t]*([^\n]*)")
set(WASI_LLVM_VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE)
message(STATUS "LLVM version: ${CMAKE_MATCH_1}")
else()
set(WASI_LLVM_VERSION "unknown" PARENT_SCOPE)
endif()
# Extract configuration hash
if(VERSION_CONTENT MATCHES "config:[ \t]*([^\n]*)")
set(WASI_CONFIG "${CMAKE_MATCH_1}" PARENT_SCOPE)
message(STATUS "Config hash: ${CMAKE_MATCH_1}")
else()
set(WASI_CONFIG "unknown" PARENT_SCOPE)
endif()
endfunction()
```
## /bridge/cmake/version.h.in
```in path="/bridge/cmake/version.h.in"
#ifndef HAKO_VERSION_H_
#define HAKO_VERSION_H_
#define HAKO_VERSION "@HAKO_VERSION@"
#endif /* HAKO_VERSION_H_ */
```
## /bridge/cmake/wasi_version.h.in
```in path="/bridge/cmake/wasi_version.h.in"
/**
* @file wasi_version.h
* @brief WASI SDK version information parsed from VERSION file
* @note This file is auto-generated. Do not edit directly.
*/
#ifndef WASI_VERSION_H
#define WASI_VERSION_H
#ifdef __cplusplus
extern "C"
{
#endif
/**
* WASI SDK version information
*/
#define WASI_VERSION "@WASI_VERSION@"
/**
* WASI-libc commit hash
*/
#define WASI_WASI_LIBC "@WASI_WASI_LIBC@"
/**
* LLVM commit hash
*/
#define WASI_LLVM "@WASI_LLVM@"
/**
* LLVM version
*/
#define WASI_LLVM_VERSION "@WASI_LLVM_VERSION@"
/**
* Configuration hash
*/
#define WASI_CONFIG "@WASI_CONFIG@"
#ifdef __cplusplus
}
#endif
#endif /* WASI_VERSION_H */
```
## /bridge/hako.c
```c path="/bridge/hako.c"
#include
#include
#include
#include
#ifdef HAKO_SANITIZE_LEAK
#include
#endif
#include "cutils.h"
#include "hako.h"
#include "quickjs-libc.h"
#include "version.h"
#include "wasi_version.h"
#define PKG "quickjs-wasi: "
#define LOG_LEN 500
#define NUM_THREADS 10
#include
/**
* Define attribute for exporting functions to WebAssembly
*/
#if defined(__WASI__) || defined(__wasi__)
#define WASM_EXPORT(func) __attribute__((export_name(#func))) func
#else
#define WASM_EXPORT(func) func
#endif
typedef struct hako_RuntimeData
{
bool debug_log;
} hako_RuntimeData;
__attribute__((import_module("hako"),
import_name("call_function"))) extern LEPUSValue *
host_call_function(LEPUSContext *ctx, LEPUSValueConst *this_ptr, int argc,
LEPUSValueConst *argv, uint32_t magic_func_id);
__attribute__((import_module("hako"),
import_name("interrupt_handler"))) extern int
host_interrupt_handler(LEPUSRuntime *rt, LEPUSContext *ctx, void *opaque);
__attribute__((import_module("hako"),
import_name("load_module_source"))) extern char *
host_load_module_source(LEPUSRuntime *rt, LEPUSContext *ctx,
CString *module_name);
__attribute__((import_module("hako"),
import_name("normalize_module"))) extern char *
host_normalize_module(LEPUSRuntime *rt, LEPUSContext *ctx,
CString *module_base_name, CString *module_name);
__attribute__((import_module("hako"),
import_name("profile_function_start"))) extern void
host_profile_function_start(LEPUSContext *ctx, CString *event, JSVoid *opaque);
__attribute__((import_module("hako"),
import_name("profile_function_end"))) extern void
host_profile_function_end(LEPUSContext *ctx, CString *event, JSVoid *opaque);
static const char *HAKO_BAD_FREE_MSG =
"+---------------------------------------------------------+\n"
"| FATAL ERROR #1 |\n"
"+---------------------------------------------------------+\n"
"| Attempted to free constant JavaScript primitive: |\n"
"| Address: %p |\n"
"| |\n"
"| Cannot free undefined/null/true/false as these are |\n"
"| static values. Doing so would cause undefined behavior |\n"
"| and probable memory corruption. |\n"
"| |\n"
"| Fix: Check value ownership before attempting to free. |\n"
"+---------------------------------------------------------+\n";
static HakoBuildInfo build_info = {.version = HAKO_VERSION,
.flags = HAKO_BUILD_FLAGS_VALUE,
.build_date = __DATE__ " " __TIME__,
.wasi_sdk_version = WASI_VERSION,
.wasi_libc = WASI_WASI_LIBC,
.llvm = WASI_LLVM,
.llvm_version = WASI_LLVM_VERSION,
.config = WASI_CONFIG};
hako_RuntimeData *hako_get_runtime_data(LEPUSRuntime *rt)
{
hako_RuntimeData *data = malloc(sizeof(hako_RuntimeData));
data->debug_log = false;
return data;
}
hako_RuntimeData *hako_get_context_rt_data(LEPUSContext *ctx)
{
return hako_get_runtime_data(LEPUS_GetRuntime(ctx));
}
void hako_log(char *msg)
{
fputs(PKG, stderr);
fputs(msg, stderr);
fputs("\n", stderr);
}
void hako_dump(LEPUSContext *ctx, LEPUSValueConst value)
{
CString *str = LEPUS_ToCString(ctx, value);
if (!str)
{
return;
}
fputs(str, stderr);
LEPUS_FreeCString(ctx, str);
putchar('\n');
}
#define MAX_EVENT_BUFFER_SIZE 1024
static char event_buffer[MAX_EVENT_BUFFER_SIZE];
static int hako_atom_to_str(LEPUSContext *ctx, JSAtom atom, const char **out_str, const char *default_value)
{
// Use provided default_value if available, otherwise use ""
const char *anonymous_str = default_value ? default_value : "";
if (atom == 0 /*JS_ATOM_NULL*/)
{
*out_str = anonymous_str;
return 0; // Static string, no need to free
}
const char *atom_str = LEPUS_AtomToCString(ctx, atom);
if (atom_str[0])
{
*out_str = atom_str;
return 1; // Dynamic string, needs to be freed
}
*out_str = anonymous_str;
return 0; // Static string, no need to free
}
static void hako_profile_function_start(LEPUSContext *ctx, JSAtom func, JSAtom filename, void *opaque)
{
__wasi_errno_t err;
__wasi_timestamp_t current_time;
// Get the current time using WASI
err = __wasi_clock_time_get(__WASI_CLOCKID_MONOTONIC, 0, ¤t_time);
const char *func_str;
int need_free_func = hako_atom_to_str(ctx, func, &func_str, NULL);
const char *filename_str;
int need_free_filename = hako_atom_to_str(ctx, filename, &filename_str, "file://hako.c");
// Use the shared buffer for formatting the event
int written = snprintf(event_buffer, MAX_EVENT_BUFFER_SIZE,
"{\"name\": \"%s\",\"cat\": \"js\",\"ph\": \"B\",\"ts\": %llu,\"pid\": 1,\"tid\": 1,\"args\": {\"file\": \"%s\"}}",
func_str, current_time / 1000, filename_str);
host_profile_function_start(ctx, event_buffer, opaque);
// Clean up dynamic strings
if (need_free_func)
{
LEPUS_FreeCString(ctx, func_str);
}
if (need_free_filename)
{
LEPUS_FreeCString(ctx, filename_str);
}
}
static void hako_profile_function_end(LEPUSContext *ctx, JSAtom func, JSAtom filename, void *opaque)
{
__wasi_errno_t err;
__wasi_timestamp_t current_time;
// Get the current time using WASI
err = __wasi_clock_time_get(__WASI_CLOCKID_MONOTONIC, 0, ¤t_time);
const char *func_str;
int need_free_func = hako_atom_to_str(ctx, func, &func_str, NULL);
const char *filename_str;
int need_free_filename = hako_atom_to_str(ctx, filename, &filename_str, "file://hako.c");
// Use the shared buffer for formatting the event
int written = snprintf(event_buffer, MAX_EVENT_BUFFER_SIZE,
"{\"name\": \"%s\",\"cat\": \"js\",\"ph\": \"E\",\"ts\": %llu,\"pid\": 1,\"tid\": 1,\"args\": {\"file\": \"%s\"}}",
func_str, current_time / 1000, filename_str);
host_profile_function_end(ctx, event_buffer, opaque);
// Clean up dynamic strings
if (need_free_func)
{
LEPUS_FreeCString(ctx, func_str);
}
if (need_free_filename)
{
LEPUS_FreeCString(ctx, filename_str);
}
}
static struct LEPUSModuleDef *hako_compile_module(LEPUSContext *ctx, CString *module_name,
BorrowedHeapChar *module_body)
{
// Use explicit flags for module compilation
int eval_flags = LEPUS_EVAL_TYPE_MODULE | LEPUS_EVAL_FLAG_COMPILE_ONLY |
LEPUS_EVAL_FLAG_STRICT;
LEPUSValue func_val = LEPUS_Eval(ctx, module_body, strlen(module_body),
module_name, eval_flags);
if (LEPUS_IsException(func_val))
{
return NULL;
}
// Ensure the result is a module
if (!LEPUS_VALUE_IS_MODULE(func_val))
{
LEPUS_ThrowTypeError(ctx, "Module '%s' code compiled to non-module object",
module_name);
LEPUS_FreeValue(ctx, func_val);
return NULL;
}
struct LEPUSModuleDef *module = LEPUS_VALUE_GET_PTR(func_val);
LEPUS_FreeValue(ctx, func_val);
return module;
}
static LEPUSModuleDef *hako_load_module(LEPUSContext *ctx, CString *module_name,
void *_unused)
{
LEPUSRuntime *rt = LEPUS_GetRuntime(ctx);
char *module_source = host_load_module_source(rt, ctx, module_name);
if (module_source == NULL)
{
LEPUS_ThrowTypeError(
ctx,
"Module not found: '%s'. Please check that the module name is correct "
"and the module is available in your environment.",
module_name);
return NULL;
}
LEPUSModuleDef *module = hako_compile_module(ctx, module_name, module_source);
free(module_source);
return module;
}
static char *hako_normalize_module(LEPUSContext *ctx, CString *module_base_name,
CString *module_name, void *_unused)
{
LEPUSRuntime *rt = LEPUS_GetRuntime(ctx);
char *normalized_module_name =
host_normalize_module(rt, ctx, module_base_name, module_name);
char *js_module_name = lepus_strdup(ctx, normalized_module_name, 1);
free(normalized_module_name);
return js_module_name;
}
static LEPUSValue *jsvalue_to_heap(LEPUSValueConst value)
{
LEPUSValue *result = malloc(sizeof(LEPUSValue));
if (result)
{
*result = value;
}
return result;
}
LEPUSValue *WASM_EXPORT(HAKO_Throw)(LEPUSContext *ctx, LEPUSValueConst *error)
{
LEPUSValue copy = LEPUS_DupValue(ctx, *error);
return jsvalue_to_heap(LEPUS_Throw(ctx, copy));
}
LEPUSValue *WASM_EXPORT(HAKO_NewError)(LEPUSContext *ctx)
{
return jsvalue_to_heap(LEPUS_NewError(ctx));
}
/**
* Limits.
*/
/**
* Memory limit. Set to -1 to disable.
*/
void WASM_EXPORT(HAKO_RuntimeSetMemoryLimit)(LEPUSRuntime *rt, size_t limit)
{
LEPUS_SetMemoryLimit(rt, limit);
}
/**
* Memory diagnostics
*/
LEPUSValue *WASM_EXPORT(HAKO_RuntimeComputeMemoryUsage)(LEPUSRuntime *rt,
LEPUSContext *ctx)
{
#if LYNX_SIMPLIFY
LEPUSMemoryUsage s;
LEPUS_ComputeMemoryUsage(rt, &s);
LEPUSValue result = LEPUS_NewObject(ctx);
LEPUS_SetPropertyStr(ctx, result, "malloc_limit",
LEPUS_NewInt64(ctx, s.malloc_limit));
LEPUS_SetPropertyStr(ctx, result, "memory_used_size",
LEPUS_NewInt64(ctx, s.memory_used_size));
LEPUS_SetPropertyStr(ctx, result, "malloc_count",
LEPUS_NewInt64(ctx, s.malloc_count));
LEPUS_SetPropertyStr(ctx, result, "memory_used_count",
LEPUS_NewInt64(ctx, s.memory_used_count));
LEPUS_SetPropertyStr(ctx, result, "atom_count",
LEPUS_NewInt64(ctx, s.atom_count));
LEPUS_SetPropertyStr(ctx, result, "atom_size",
LEPUS_NewInt64(ctx, s.atom_size));
LEPUS_SetPropertyStr(ctx, result, "str_count",
LEPUS_NewInt64(ctx, s.str_count));
LEPUS_SetPropertyStr(ctx, result, "str_size",
LEPUS_NewInt64(ctx, s.str_size));
LEPUS_SetPropertyStr(ctx, result, "obj_count",
LEPUS_NewInt64(ctx, s.obj_count));
LEPUS_SetPropertyStr(ctx, result, "obj_size",
LEPUS_NewInt64(ctx, s.obj_size));
LEPUS_SetPropertyStr(ctx, result, "prop_count",
LEPUS_NewInt64(ctx, s.prop_count));
LEPUS_SetPropertyStr(ctx, result, "prop_size",
LEPUS_NewInt64(ctx, s.prop_size));
LEPUS_SetPropertyStr(ctx, result, "shape_count",
LEPUS_NewInt64(ctx, s.shape_count));
LEPUS_SetPropertyStr(ctx, result, "shape_size",
LEPUS_NewInt64(ctx, s.shape_size));
LEPUS_SetPropertyStr(ctx, result, "lepus_func_count",
LEPUS_NewInt64(ctx, s.lepus_func_count));
LEPUS_SetPropertyStr(ctx, result, "lepus_func_size",
LEPUS_NewInt64(ctx, s.lepus_func_size));
LEPUS_SetPropertyStr(ctx, result, "lepus_func_code_size",
LEPUS_NewInt64(ctx, s.lepus_func_code_size));
LEPUS_SetPropertyStr(ctx, result, "lepus_func_pc2line_count",
LEPUS_NewInt64(ctx, s.lepus_func_pc2line_count));
LEPUS_SetPropertyStr(ctx, result, "lepus_func_pc2line_size",
LEPUS_NewInt64(ctx, s.lepus_func_pc2line_size));
LEPUS_SetPropertyStr(ctx, result, "c_func_count",
LEPUS_NewInt64(ctx, s.c_func_count));
LEPUS_SetPropertyStr(ctx, result, "array_count",
LEPUS_NewInt64(ctx, s.array_count));
LEPUS_SetPropertyStr(ctx, result, "fast_array_count",
LEPUS_NewInt64(ctx, s.fast_array_count));
LEPUS_SetPropertyStr(ctx, result, "fast_array_elements",
LEPUS_NewInt64(ctx, s.fast_array_elements));
LEPUS_SetPropertyStr(ctx, result, "binary_object_count",
LEPUS_NewInt64(ctx, s.binary_object_count));
LEPUS_SetPropertyStr(ctx, result, "binary_object_size",
LEPUS_NewInt64(ctx, s.binary_object_size));
return jsvalue_to_heap(result);
#else
LEPUSValue result = LEPUS_NewObject(ctx);
return jsvalue_to_heap(result);
#endif
}
OwnedHeapChar *WASM_EXPORT(HAKO_RuntimeDumpMemoryUsage)(LEPUSRuntime *rt)
{
#if LYNX_SIMPLIFY
char *result = malloc(sizeof(char) * 1024);
FILE *memfile = fmemopen(result, 1024, "w");
LEPUSMemoryUsage s;
LEPUS_ComputeMemoryUsage(rt, &s);
LEPUS_DumpMemoryUsage(memfile, &s, rt);
fclose(memfile);
return result;
#else
char *result = malloc(sizeof(char) * 1024);
snprintf(result, 1024,
"Memory usage unavailable - LYNX_SIMPLIFY not defined");
return result;
#endif
}
int WASM_EXPORT(HAKO_RecoverableLeakCheck)()
{
#ifdef HAKO_SANITIZE_LEAK
return __lsan_do_recoverable_leak_check();
#else
return 0;
#endif
}
LEPUS_BOOL WASM_EXPORT(HAKO_BuildIsSanitizeLeak)()
{
#ifdef HAKO_SANITIZE_LEAK
return 1;
#else
return 0;
#endif
}
void WASM_EXPORT(HAKO_RuntimeJSThrow)(LEPUSContext *ctx, CString *message)
{
LEPUS_ThrowReferenceError(ctx, "%s", message);
}
void WASM_EXPORT(HAKO_ContextSetMaxStackSize)(LEPUSContext *ctx,
size_t stack_size)
{
LEPUS_SetMaxStackSize(ctx, stack_size);
}
/**
* Constant pointers. Because we always use LEPUSValue* from the host Javascript
* environment, we need helper functions to return pointers to these constants.
*/
LEPUSValueConst HAKO_Undefined = LEPUS_UNDEFINED;
LEPUSValueConst *WASM_EXPORT(HAKO_GetUndefined)() { return &HAKO_Undefined; }
LEPUSValueConst HAKO_Null = LEPUS_NULL;
LEPUSValueConst *WASM_EXPORT(HAKO_GetNull)() { return &HAKO_Null; }
LEPUSValueConst HAKO_False = LEPUS_FALSE;
LEPUSValueConst *WASM_EXPORT(HAKO_GetFalse)() { return &HAKO_False; }
LEPUSValueConst HAKO_True = LEPUS_TRUE;
LEPUSValueConst *WASM_EXPORT(HAKO_GetTrue)() { return &HAKO_True; }
/**
* Standard FFI functions
*/
void WASM_EXPORT(HAKO_EnableProfileCalls)(LEPUSRuntime *rt, uint32_t sampling, JSVoid *opaque)
{
#ifdef ENABLE_HAKO_PROFILER
JS_EnableProfileCalls(rt, hako_profile_function_start, hako_profile_function_end, sampling, opaque);
#endif
}
LEPUSRuntime *WASM_EXPORT(HAKO_NewRuntime)()
{
LEPUSRuntime *rt = LEPUS_NewRuntimeWithMode(0);
if (rt == NULL)
{
return NULL;
}
#ifdef ENABLE_COMPATIBLE_MM
#ifdef ENABLE_LEPUSNG
LEPUS_SetRuntimeInfo(rt, "Lynx_LepusNG");
#else
LEPUS_SetRuntimeInfo(rt, "Lynx_JS");
#endif
#else
#ifdef ENABLE_LEPUSNG
LEPUS_SetRuntimeInfo(rt, "Lynx_LepusNG_RC");
#else
LEPUS_SetRuntimeInfo(rt, "Lynx_JS_RC");
#endif
#endif
return rt;
}
void WASM_EXPORT(HAKO_FreeRuntime)(LEPUSRuntime *rt)
{
LEPUS_FreeRuntime(rt);
}
void WASM_EXPORT(HAKO_SetStripInfo)(LEPUSRuntime *rt, int flags)
{
LEPUS_SetStripInfo(rt, flags);
}
int WASM_EXPORT(HAKO_GetStripInfo)(LEPUSRuntime *rt)
{
return LEPUS_GetStripInfo(rt);
}
LEPUSContext *WASM_EXPORT(HAKO_NewContext)(LEPUSRuntime *rt,
HAKO_Intrinsic intrinsics)
{
if (intrinsics == 0)
{
return LEPUS_NewContext(rt);
}
LEPUSContext *ctx = LEPUS_NewContextRaw(rt);
if (ctx == NULL)
{
return NULL;
}
if (intrinsics & HAKO_Intrinsic_BaseObjects)
{
LEPUS_AddIntrinsicBaseObjects(ctx);
}
if (intrinsics & HAKO_Intrinsic_Date)
{
LEPUS_AddIntrinsicDate(ctx);
}
if (intrinsics & HAKO_Intrinsic_Eval)
{
LEPUS_AddIntrinsicEval(ctx);
}
if (intrinsics & HAKO_Intrinsic_StringNormalize)
{
LEPUS_AddIntrinsicStringNormalize(ctx);
}
if (intrinsics & HAKO_Intrinsic_RegExp)
{
LEPUS_AddIntrinsicRegExp(ctx);
}
if (intrinsics & HAKO_Intrinsic_RegExpCompiler)
{
LEPUS_AddIntrinsicRegExpCompiler(ctx);
}
if (intrinsics & HAKO_Intrinsic_JSON)
{
LEPUS_AddIntrinsicJSON(ctx);
}
if (intrinsics & HAKO_Intrinsic_Proxy)
{
LEPUS_AddIntrinsicProxy(ctx);
}
if (intrinsics & HAKO_Intrinsic_MapSet)
{
LEPUS_AddIntrinsicMapSet(ctx);
}
if (intrinsics & HAKO_Intrinsic_TypedArrays)
{
LEPUS_AddIntrinsicTypedArrays(ctx);
}
if (intrinsics & HAKO_Intrinsic_Promise)
{
LEPUS_AddIntrinsicPromise(ctx);
}
return ctx;
}
void WASM_EXPORT(HAKO_SetContextData)(LEPUSContext *ctx, JSVoid *data)
{
LEPUS_SetContextOpaque(ctx, data);
}
JSVoid *WASM_EXPORT(HAKO_GetContextData)(LEPUSContext *ctx)
{
return LEPUS_GetContextOpaque(ctx);
}
void WASM_EXPORT(HAKO_SetNoStrictMode)(LEPUSContext *ctx)
{
LEPUS_SetNoStrictMode(ctx);
}
void WASM_EXPORT(HAKO_SetVirtualStackSize)(LEPUSContext *ctx, uint32_t size)
{
LEPUS_SetVirtualStackSize(ctx, size);
}
void WASM_EXPORT(HAKO_FreeContext)(LEPUSContext *ctx)
{
LEPUS_FreeContext(ctx);
}
void WASM_EXPORT(HAKO_FreeValuePointer)(LEPUSContext *ctx,
LEPUSValue *value)
{
if (value == &HAKO_Undefined || value == &HAKO_Null || value == &HAKO_True ||
value == &HAKO_False)
{
fprintf(stderr, HAKO_BAD_FREE_MSG, (void *)value);
__builtin_trap();
}
LEPUS_FreeValue(ctx, *value);
free(value);
}
void WASM_EXPORT(HAKO_FreeValuePointerRuntime)(LEPUSRuntime *rt,
LEPUSValue *value)
{
if (value == &HAKO_Undefined || value == &HAKO_Null || value == &HAKO_True ||
value == &HAKO_False)
{
fprintf(stderr, HAKO_BAD_FREE_MSG, (void *)value);
__builtin_trap();
}
LEPUS_FreeValueRT(rt, *value);
free(value);
}
void WASM_EXPORT(HAKO_FreeVoidPointer)(LEPUSContext *ctx, JSVoid *ptr)
{
lepus_free(ctx, ptr);
}
void WASM_EXPORT(HAKO_FreeCString)(LEPUSContext *ctx, JSBorrowedChar *str)
{
LEPUS_FreeCString(ctx, str);
}
LEPUSValue *WASM_EXPORT(HAKO_DupValuePointer)(LEPUSContext *ctx,
LEPUSValueConst *val)
{
return jsvalue_to_heap(LEPUS_DupValue(ctx, *val));
}
LEPUSValue *WASM_EXPORT(HAKO_NewObject)(LEPUSContext *ctx)
{
return jsvalue_to_heap(LEPUS_NewObject(ctx));
}
LEPUSValue *WASM_EXPORT(HAKO_NewObjectProto)(LEPUSContext *ctx,
LEPUSValueConst *proto)
{
return jsvalue_to_heap(LEPUS_NewObjectProto(ctx, *proto));
}
LEPUSValue *WASM_EXPORT(HAKO_NewArray)(LEPUSContext *ctx)
{
return jsvalue_to_heap(LEPUS_NewArray(ctx));
}
void hako_free_buffer(LEPUSRuntime *unused_rt, void *unused_opaque,
void *ptr)
{
free(ptr);
}
LEPUSValue *WASM_EXPORT(HAKO_NewArrayBuffer)(LEPUSContext *ctx, JSVoid *buffer,
size_t length)
{
return jsvalue_to_heap(LEPUS_NewArrayBuffer(ctx, (uint8_t *)buffer, length,
hako_free_buffer, NULL, false));
}
LEPUSValue *WASM_EXPORT(HAKO_NewFloat64)(LEPUSContext *ctx, double num)
{
return jsvalue_to_heap(LEPUS_NewFloat64(ctx, num));
}
double WASM_EXPORT(HAKO_GetFloat64)(LEPUSContext *ctx, LEPUSValueConst *value)
{
double result = NAN;
LEPUS_ToFloat64(ctx, &result, *value);
return result;
}
LEPUSValue *WASM_EXPORT(HAKO_NewString)(LEPUSContext *ctx,
BorrowedHeapChar *string)
{
return jsvalue_to_heap(LEPUS_NewString(ctx, string));
}
JSBorrowedChar *WASM_EXPORT(HAKO_ToCString)(LEPUSContext *ctx,
LEPUSValueConst *value)
{
return LEPUS_ToCString(ctx, *value);
}
JSVoid *WASM_EXPORT(HAKO_CopyArrayBuffer)(LEPUSContext *ctx,
LEPUSValueConst *data,
size_t *out_length)
{
size_t length;
uint8_t *buffer = LEPUS_GetArrayBuffer(ctx, &length, *data);
if (!buffer)
return 0;
uint8_t *result = malloc(length);
if (!result)
return result;
memcpy(result, buffer, length);
if (out_length)
*out_length = length;
return result;
}
LEPUSValue hako_get_symbol_key(LEPUSContext *ctx, LEPUSValueConst *value)
{
LEPUSValue global = LEPUS_GetGlobalObject(ctx);
LEPUSValue Symbol = LEPUS_GetPropertyStr(ctx, global, "Symbol");
LEPUS_FreeValue(ctx, global);
LEPUSValue Symbol_keyFor = LEPUS_GetPropertyStr(ctx, Symbol, "keyFor");
LEPUSValue key = LEPUS_Call(ctx, Symbol_keyFor, Symbol, 1, value);
LEPUS_FreeValue(ctx, Symbol_keyFor);
LEPUS_FreeValue(ctx, Symbol);
return key;
}
LEPUSValue hako_resolve_func_data(LEPUSContext *ctx, LEPUSValueConst this_val,
int argc, LEPUSValueConst *argv, int magic,
LEPUSValue *func_data)
{
return LEPUS_DupValue(ctx, func_data[0]);
}
LEPUSValue *WASM_EXPORT(HAKO_Eval)(LEPUSContext *ctx, BorrowedHeapChar *js_code,
size_t js_code_length, BorrowedHeapChar *filename,
EvalDetectModule detectModule,
EvalFlags evalFlags)
{
// Only detect module if detection is enabled and module type isn't already
// specified
if (detectModule && (evalFlags & LEPUS_EVAL_TYPE_MODULE) == 0)
{
bool isModule = LEPUS_DetectModule(js_code, js_code_length);
if (isModule)
{
evalFlags |= LEPUS_EVAL_TYPE_MODULE;
}
}
LEPUSModuleDef *module = NULL;
LEPUSValue eval_result;
bool is_module = (evalFlags & LEPUS_EVAL_TYPE_MODULE) != 0;
// Compile and evaluate module code specially
if (is_module && (evalFlags & LEPUS_EVAL_FLAG_COMPILE_ONLY) == 0)
{
LEPUSValue func_obj = LEPUS_Eval(ctx, js_code, js_code_length, filename,
evalFlags | LEPUS_EVAL_FLAG_COMPILE_ONLY);
if (LEPUS_IsException(func_obj))
{
return jsvalue_to_heap(func_obj);
}
if (!LEPUS_VALUE_IS_MODULE(func_obj))
{
LEPUS_FreeValue(ctx, func_obj);
return jsvalue_to_heap(LEPUS_ThrowTypeError(
ctx, "Module code compiled to non-module object"));
}
module = LEPUS_VALUE_GET_PTR(func_obj);
if (module == NULL)
{
LEPUS_FreeValue(ctx, func_obj);
return jsvalue_to_heap(
LEPUS_ThrowTypeError(ctx, "Module compiled to null"));
}
eval_result = LEPUS_EvalFunction(ctx, func_obj, LEPUS_UNDEFINED);
}
else
{
// Regular evaluation for non-module code or compile-only
eval_result = LEPUS_Eval(ctx, js_code, js_code_length, filename, evalFlags);
}
// If we got an exception or not a promise, return it directly
if (LEPUS_IsException(eval_result) || !LEPUS_IsPromise(eval_result))
{
// For non-promise modules, return the module namespace
if (is_module && !LEPUS_IsPromise(eval_result) &&
!LEPUS_IsException(eval_result))
{
LEPUSValue module_namespace = LEPUS_GetModuleNamespace(ctx, module);
LEPUS_FreeValue(ctx, eval_result);
return jsvalue_to_heap(module_namespace);
}
// For everything else, return the eval result directly
return jsvalue_to_heap(eval_result);
}
// At this point, we know we're dealing with a promise
LEPUSPromiseStateEnum state = LEPUS_PromiseState(ctx, eval_result);
// Handle promise based on its state
if (state == LEPUS_PROMISE_FULFILLED || state == -1)
{
// For fulfilled promises with modules, return the namespace
if (is_module)
{
LEPUSValue module_namespace = LEPUS_GetModuleNamespace(ctx, module);
LEPUS_FreeValue(ctx, eval_result);
return jsvalue_to_heap(module_namespace);
}
else
{
// For non-modules, get the promise result
LEPUSValue result = LEPUS_PromiseResult(ctx, eval_result);
LEPUS_FreeValue(ctx, eval_result);
return jsvalue_to_heap(result);
}
}
else if (state == LEPUS_PROMISE_REJECTED)
{
// For rejected promises, throw the rejection reason
LEPUSValue reason = LEPUS_PromiseResult(ctx, eval_result);
LEPUS_Throw(ctx, reason);
LEPUS_FreeValue(ctx, reason);
LEPUS_FreeValue(ctx, eval_result);
return jsvalue_to_heap(LEPUS_EXCEPTION);
}
else if (state == LEPUS_PROMISE_PENDING)
{
// For pending promises, handle differently based on whether it's a module
if (is_module)
{
LEPUSValue module_namespace = LEPUS_GetModuleNamespace(ctx, module);
if (LEPUS_IsException(module_namespace))
{
LEPUS_FreeValue(ctx, eval_result);
return jsvalue_to_heap(module_namespace);
}
LEPUSValue then_resolve_module_namespace = LEPUS_NewCFunctionData(
ctx, &hako_resolve_func_data, 0, 0, 1, &module_namespace);
LEPUS_FreeValue(ctx, module_namespace);
if (LEPUS_IsException(then_resolve_module_namespace))
{
LEPUS_FreeValue(ctx, eval_result);
return jsvalue_to_heap(then_resolve_module_namespace);
}
LEPUSAtom then_atom = LEPUS_NewAtom(ctx, "then");
LEPUSValueConst then_args[1] = {then_resolve_module_namespace};
LEPUSValue new_promise =
LEPUS_Invoke(ctx, eval_result, then_atom, 1, then_args);
LEPUS_FreeAtom(ctx, then_atom);
LEPUS_FreeValue(ctx, then_resolve_module_namespace);
LEPUS_FreeValue(ctx, eval_result);
return jsvalue_to_heap(new_promise);
}
else
{
// For non-modules, return the promise directly
return jsvalue_to_heap(eval_result);
}
}
else
{
// Unknown promise state, return as is
return jsvalue_to_heap(eval_result);
}
}
LEPUSValue *WASM_EXPORT(HAKO_NewSymbol)(LEPUSContext *ctx,
BorrowedHeapChar *description,
int isGlobal)
{
LEPUSValue global = LEPUS_GetGlobalObject(ctx);
LEPUSValue Symbol = LEPUS_GetPropertyStr(ctx, global, "Symbol");
LEPUS_FreeValue(ctx, global);
LEPUSValue descriptionValue = LEPUS_NewString(ctx, description);
LEPUSValue symbol;
if (isGlobal != 0)
{
LEPUSValue Symbol_for = LEPUS_GetPropertyStr(ctx, Symbol, "for");
symbol = LEPUS_Call(ctx, Symbol_for, Symbol, 1, &descriptionValue);
LEPUS_FreeValue(ctx, descriptionValue);
LEPUS_FreeValue(ctx, Symbol_for);
LEPUS_FreeValue(ctx, Symbol);
return jsvalue_to_heap(symbol);
}
symbol = LEPUS_Call(ctx, Symbol, LEPUS_UNDEFINED, 1, &descriptionValue);
LEPUS_FreeValue(ctx, descriptionValue);
LEPUS_FreeValue(ctx, Symbol);
return jsvalue_to_heap(symbol);
}
JSBorrowedChar *
WASM_EXPORT(HAKO_GetSymbolDescriptionOrKey)(LEPUSContext *ctx,
LEPUSValueConst *value)
{
JSBorrowedChar *result;
LEPUSValue key = hako_get_symbol_key(ctx, value);
if (!LEPUS_IsUndefined(key))
{
result = LEPUS_ToCString(ctx, key);
LEPUS_FreeValue(ctx, key);
return result;
}
LEPUSValue description = LEPUS_GetPropertyStr(ctx, *value, "description");
result = LEPUS_ToCString(ctx, description);
LEPUS_FreeValue(ctx, description);
return result;
}
LEPUS_BOOL WASM_EXPORT(HAKO_IsGlobalSymbol)(LEPUSContext *ctx,
LEPUSValueConst *value)
{
LEPUSValue key = hako_get_symbol_key(ctx, value);
int undefined = LEPUS_IsUndefined(key);
LEPUS_FreeValue(ctx, key);
if (undefined)
{
return 0;
}
else
{
return 1;
}
}
LEPUS_BOOL WASM_EXPORT(HAKO_IsJobPending)(LEPUSRuntime *rt)
{
return LEPUS_IsJobPending(rt);
}
LEPUSValue *WASM_EXPORT(HAKO_ExecutePendingJob)(LEPUSRuntime *rt,
int maxJobsToExecute,
LEPUSContext **lastJobContext)
{
LEPUSContext *pctx;
int status = 1;
int executed = 0;
while (executed != maxJobsToExecute && status == 1)
{
status = LEPUS_ExecutePendingJob(rt, &pctx);
if (status == -1)
{
*lastJobContext = pctx;
return jsvalue_to_heap(LEPUS_GetException(pctx));
}
else if (status == 1)
{
*lastJobContext = pctx;
executed++;
}
}
return jsvalue_to_heap(LEPUS_NewFloat64(pctx, executed));
}
LEPUSValue *WASM_EXPORT(HAKO_GetProp)(LEPUSContext *ctx,
LEPUSValueConst *this_val,
LEPUSValueConst *prop_name)
{
LEPUSAtom prop_atom = LEPUS_ValueToAtom(ctx, *prop_name);
LEPUSValue prop_val = LEPUS_GetProperty(ctx, *this_val, prop_atom);
LEPUS_FreeAtom(ctx, prop_atom);
if (LEPUS_IsException(prop_val))
{
return NULL;
}
return jsvalue_to_heap(prop_val);
}
LEPUSValue *WASM_EXPORT(HAKO_GetPropNumber)(LEPUSContext *ctx,
LEPUSValueConst *this_val,
int prop_name)
{
LEPUSValue prop_val =
LEPUS_GetPropertyUint32(ctx, *this_val, (uint32_t)prop_name);
if (LEPUS_IsException(prop_val))
{
return NULL;
}
return jsvalue_to_heap(prop_val);
}
LEPUS_BOOL WASM_EXPORT(HAKO_SetProp)(LEPUSContext *ctx,
LEPUSValueConst *this_val,
LEPUSValueConst *prop_name,
LEPUSValueConst *prop_value)
{
LEPUSAtom prop_atom = LEPUS_ValueToAtom(ctx, *prop_name);
LEPUSValue extra_prop_value = LEPUS_DupValue(ctx, *prop_value);
int result = LEPUS_SetProperty(ctx, *this_val, prop_atom, extra_prop_value);
LEPUS_FreeAtom(ctx, prop_atom);
return result;
}
LEPUS_BOOL WASM_EXPORT(HAKO_DefineProp)(
LEPUSContext *ctx, LEPUSValueConst *this_val, LEPUSValueConst *prop_name,
LEPUSValueConst *prop_value, LEPUSValueConst *get, LEPUSValueConst *set,
LEPUS_BOOL configurable, LEPUS_BOOL enumerable, LEPUS_BOOL has_value)
{
LEPUSAtom prop_atom = LEPUS_ValueToAtom(ctx, *prop_name);
int flags = 0;
if (configurable)
{
flags = flags | LEPUS_PROP_CONFIGURABLE;
if (has_value)
{
flags = flags | LEPUS_PROP_HAS_CONFIGURABLE;
}
}
if (enumerable)
{
flags = flags | LEPUS_PROP_ENUMERABLE;
if (has_value)
{
flags = flags | LEPUS_PROP_HAS_ENUMERABLE;
}
}
if (!LEPUS_IsUndefined(*get))
{
flags = flags | LEPUS_PROP_HAS_GET;
}
if (!LEPUS_IsUndefined(*set))
{
flags = flags | LEPUS_PROP_HAS_SET;
}
if (has_value)
{
flags = flags | LEPUS_PROP_HAS_VALUE;
}
int result = LEPUS_DefineProperty(ctx, *this_val, prop_atom, *prop_value,
*get, *set, flags);
LEPUS_FreeAtom(ctx, prop_atom);
return result;
}
static inline bool __JS_AtomIsTaggedInt(LEPUSAtom v)
{
return (v & LEPUS_ATOM_TAG_INT) != 0;
}
static inline uint32_t __JS_AtomToUInt32(LEPUSAtom atom)
{
return atom & ~LEPUS_ATOM_TAG_INT;
}
LEPUSValue *WASM_EXPORT(HAKO_GetOwnPropertyNames)(LEPUSContext *ctx,
LEPUSValue ***out_ptrs,
uint32_t *out_len,
LEPUSValueConst *obj,
int flags)
{
if (out_ptrs == NULL || out_len == NULL)
{
return jsvalue_to_heap(LEPUS_ThrowTypeError(ctx, "Invalid arguments"));
}
if (LEPUS_IsObject(*obj) == false)
{
return jsvalue_to_heap(LEPUS_ThrowTypeError(ctx, "not an object"));
}
LEPUSPropertyEnum *tab = NULL;
uint32_t total_props = 0;
uint32_t out_props = 0;
bool hako_standard_compliant_number =
(flags & HAKO_STANDARD_COMPLIANT_NUMBER) != 0;
bool hako_include_string = (flags & LEPUS_GPN_STRING_MASK) != 0;
bool hako_include_number =
hako_standard_compliant_number ? 0 : (flags & HAKO_GPN_NUMBER_MASK) != 0;
if (hako_include_number)
{
flags = flags | LEPUS_GPN_STRING_MASK;
}
int status = 0;
status = LEPUS_GetOwnPropertyNames(ctx, &tab, &total_props, *obj, flags);
if (status < 0)
{
if (tab != NULL)
{
lepus_free(ctx, tab);
}
return jsvalue_to_heap(LEPUS_GetException(ctx));
}
*out_ptrs = malloc(sizeof(LEPUSValue) * *out_len);
for (int i = 0; i < total_props; i++)
{
LEPUSAtom atom = tab[i].atom;
if (__JS_AtomIsTaggedInt(atom))
{
if (hako_include_number)
{
uint32_t v = __JS_AtomToUInt32(atom);
(*out_ptrs)[out_props++] = jsvalue_to_heap(LEPUS_NewInt32(ctx, v));
}
else if (hako_include_string && hako_standard_compliant_number)
{
(*out_ptrs)[out_props++] =
jsvalue_to_heap(LEPUS_AtomToValue(ctx, tab[i].atom));
}
LEPUS_FreeAtom(ctx, atom);
continue;
}
LEPUSValue atom_value = LEPUS_AtomToValue(ctx, atom);
LEPUS_FreeAtom(ctx, atom);
if (LEPUS_IsString(atom_value))
{
if (hako_include_string)
{
(*out_ptrs)[out_props++] = jsvalue_to_heap(atom_value);
}
else
{
LEPUS_FreeValue(ctx, atom_value);
}
}
else
{
(*out_ptrs)[out_props++] = jsvalue_to_heap(atom_value);
}
}
lepus_free(ctx, tab);
*out_len = out_props;
return NULL;
}
LEPUSValue *WASM_EXPORT(HAKO_Call)(LEPUSContext *ctx, LEPUSValueConst *func_obj,
LEPUSValueConst *this_obj, int argc,
LEPUSValueConst **argv_ptrs)
{
LEPUSValueConst argv[argc];
int i;
for (i = 0; i < argc; i++)
{
argv[i] = *(argv_ptrs[i]);
}
return jsvalue_to_heap(LEPUS_Call(ctx, *func_obj, *this_obj, argc, argv));
}
LEPUSValue *WASM_EXPORT(HAKO_GetLastError)(LEPUSContext *ctx,
LEPUSValue *maybe_exception)
{
// If maybe_exception is provided
if (maybe_exception != NULL)
{
// Only if it's an exception, return the result of GetException
if (LEPUS_IsException(*maybe_exception))
{
return jsvalue_to_heap(LEPUS_GetException(ctx));
}
// If it's provided but not an exception, just return NULL
return NULL;
}
// If maybe_exception is NULL, check if there's an exception in context
LEPUSValue exception = LEPUS_GetException(ctx);
if (!LEPUS_IsNull(exception))
{
return jsvalue_to_heap(exception);
}
return NULL;
}
/**
* Enhanced dump function with JSON serialization and property enumeration
*/
JSBorrowedChar *WASM_EXPORT(HAKO_Dump)(LEPUSContext *ctx,
LEPUSValueConst *obj)
{
LEPUSValue error_obj = LEPUS_UNDEFINED;
LEPUSValue json_value = LEPUS_UNDEFINED;
JSBorrowedChar *result = NULL;
// Special handling for Error objects
if (LEPUS_IsError(ctx, *obj))
{
// Create a plain object to hold error properties
error_obj = LEPUS_NewObject(ctx);
LEPUSValue current_error = LEPUS_DupValue(ctx, *obj);
LEPUSValue current_obj = error_obj;
LEPUSValue next_obj;
int depth = 0;
while (depth < 3)
{
// Get message property
LEPUSValue message = LEPUS_GetPropertyStr(ctx, current_error, "message");
if (!LEPUS_IsException(message) && !LEPUS_IsUndefined(message))
{
// Set directly - LEPUS_SetPropertyStr will handle reference counting
LEPUS_SetPropertyStr(ctx, current_obj, "message", message);
// Don't free message here - SetPropertyStr either increases the ref
// count or takes ownership
}
else
{
// Only free if we didn't set the property
LEPUS_FreeValue(ctx, message);
}
// Get name property
LEPUSValue name = LEPUS_GetPropertyStr(ctx, current_error, "name");
if (!LEPUS_IsException(name) && !LEPUS_IsUndefined(name))
{
LEPUS_SetPropertyStr(ctx, current_obj, "name", name);
// Don't free name here
}
else
{
LEPUS_FreeValue(ctx, name);
}
// Get stack property
LEPUSValue stack = LEPUS_GetPropertyStr(ctx, current_error, "stack");
if (!LEPUS_IsException(stack) && !LEPUS_IsUndefined(stack))
{
LEPUS_SetPropertyStr(ctx, current_obj, "stack", stack);
// Don't free stack here
}
else
{
LEPUS_FreeValue(ctx, stack);
}
// Check for cause
LEPUSValue cause = LEPUS_GetPropertyStr(ctx, current_error, "cause");
if (!LEPUS_IsException(cause) && !LEPUS_IsUndefined(cause) &&
!LEPUS_IsNull(cause) && LEPUS_IsError(ctx, cause) &&
depth < 2) // Check depth before going deeper
{
// Create a new object for the cause
next_obj = LEPUS_NewObject(ctx);
// Link current object to the cause
LEPUS_SetPropertyStr(ctx, current_obj, "cause", next_obj);
// Move to next iteration
current_obj = next_obj;
LEPUS_FreeValue(ctx, current_error);
current_error = cause; // Take ownership, don't free
depth++;
}
else
{
// Handle non-error cause or max depth reached
if (!LEPUS_IsException(cause) && !LEPUS_IsUndefined(cause) &&
!LEPUS_IsNull(cause))
{
LEPUS_SetPropertyStr(ctx, current_obj, "cause", cause);
// Don't free cause here
}
else
{
LEPUS_FreeValue(ctx, cause);
}
LEPUS_FreeValue(ctx, current_error);
break;
}
}
// Use LEPUS_ToJSON to create JSON string
json_value = LEPUS_ToJSON(ctx, error_obj, 2); // Indent with 2 spaces
LEPUS_FreeValue(ctx, error_obj);
if (!LEPUS_IsException(json_value))
{
// Convert to C string
result = LEPUS_ToCString(ctx, json_value);
LEPUS_FreeValue(ctx, json_value);
return result;
}
else
{
LEPUS_FreeValue(ctx, json_value);
}
}
else
{
// For non-error objects, try LEPUS_ToJSON directly
json_value = LEPUS_ToJSON(ctx, *obj, 2); // Indent with 2 spaces
if (!LEPUS_IsException(json_value))
{
// Convert to C string
result = LEPUS_ToCString(ctx, json_value);
LEPUS_FreeValue(ctx, json_value);
return result;
}
else
{
LEPUS_FreeValue(ctx, json_value);
}
}
// If JSON serialization fails, use a static buffer
static char error_buffer[128];
snprintf(error_buffer, sizeof(error_buffer),
"{\"error\":\"Failed to serialize object\"}");
return error_buffer;
}
LEPUSValue *
WASM_EXPORT(HAKO_GetModuleNamespace)(LEPUSContext *ctx,
LEPUSValueConst *module_func_obj)
{
if (!LEPUS_VALUE_IS_MODULE(*module_func_obj))
{
return jsvalue_to_heap(LEPUS_ThrowTypeError(ctx, "Not a module"));
}
struct LEPUSModuleDef *module = LEPUS_VALUE_GET_PTR(*module_func_obj);
return jsvalue_to_heap(LEPUS_GetModuleNamespace(ctx, module));
}
OwnedHeapChar *WASM_EXPORT(HAKO_Typeof)(LEPUSContext *ctx,
LEPUSValueConst *value)
{
CString *result = "unknown";
if (LEPUS_IsUndefined(*value))
{
result = "undefined";
}
else if (LEPUS_IsNull(*value))
{
result = "null";
}
else if (LEPUS_IsNumber(*value))
{
result = "number";
}
#ifdef CONFIG_BIGNUM
else if (LEPUS_IsBigInt(*value))
{
result = "bigint";
}
else if (LEPUS_IsBigFloat(*value))
{
result = "bigfloat";
}
#endif
else if (LEPUS_IsFunction(ctx, *value))
{
result = "function";
}
else if (LEPUS_IsBool(*value))
{
result = "boolean";
}
else if (LEPUS_IsNull(*value))
{
result = "object";
}
else if (LEPUS_IsUninitialized(*value))
{
result = "undefined";
}
else if (LEPUS_IsString(*value))
{
result = "string";
}
else if (LEPUS_IsSymbol(*value))
{
result = "symbol";
}
else if (LEPUS_IsObject(*value))
{
result = "object";
}
char *out = strdup(result);
return out;
}
LEPUSAtom HAKO_AtomLength = 0;
int WASM_EXPORT(HAKO_GetLength)(LEPUSContext *ctx, uint32_t *out_len,
LEPUSValueConst *value)
{
LEPUSValue len_val;
int result;
if (!LEPUS_IsObject(*value))
{
return -1;
}
if (HAKO_AtomLength == 0)
{
HAKO_AtomLength = LEPUS_NewAtom(ctx, "length");
}
len_val = LEPUS_GetProperty(ctx, *value, HAKO_AtomLength);
if (LEPUS_IsException(len_val))
{
return -1;
}
result = LEPUS_ToUint32(ctx, out_len, len_val);
LEPUS_FreeValue(ctx, len_val);
return result;
}
LEPUS_BOOL WASM_EXPORT(HAKO_IsEqual)(LEPUSContext *ctx, LEPUSValueConst *a,
LEPUSValueConst *b, IsEqualOp op)
{
switch (op)
{
case HAKO_EqualOp_SameValue:
return LEPUS_SameValue(ctx, *a, *b);
case HAKO_EqualOp_SameValueZero:
return LEPUS_SameValueZero(ctx, *a, *b);
default:
case HAKO_EqualOp_StrictEq:
return LEPUS_StrictEq(ctx, *a, *b);
}
}
LEPUSValue *WASM_EXPORT(HAKO_GetGlobalObject)(LEPUSContext *ctx)
{
return jsvalue_to_heap(LEPUS_GetGlobalObject(ctx));
}
LEPUSValue *
WASM_EXPORT(HAKO_NewPromiseCapability)(LEPUSContext *ctx,
LEPUSValue **resolve_funcs_out)
{
LEPUSValue resolve_funcs[2];
LEPUSValue promise = LEPUS_NewPromiseCapability(ctx, resolve_funcs);
resolve_funcs_out[0] = jsvalue_to_heap(resolve_funcs[0]);
resolve_funcs_out[1] = jsvalue_to_heap(resolve_funcs[1]);
return jsvalue_to_heap(promise);
}
LEPUS_BOOL WASM_EXPORT(HAKO_IsPromise)(LEPUSContext *ctx,
LEPUSValueConst *promise)
{
return LEPUS_IsPromise(*promise);
}
LEPUSPromiseStateEnum WASM_EXPORT(HAKO_PromiseState)(LEPUSContext *ctx,
LEPUSValueConst *promise)
{
return LEPUS_PromiseState(ctx, *promise);
}
LEPUSValue *WASM_EXPORT(HAKO_PromiseResult)(LEPUSContext *ctx,
LEPUSValueConst *promise)
{
return jsvalue_to_heap(LEPUS_PromiseResult(ctx, *promise));
}
LEPUS_BOOL WASM_EXPORT(HAKO_BuildIsDebug)()
{
#ifdef HAKO_DEBUG_MODE
return 1;
#else
return 0;
#endif
}
CString *WASM_EXPORT(HAKO_GetVersion)() { return HAKO_VERSION; }
uint64_t WASM_EXPORT(HAKO_GetPrimjsVersion)()
{
return LEPUS_GetPrimjsVersion();
}
// Module loading helpers
// C -> Host Callbacks
LEPUSValue *hako_host_call_function(LEPUSContext *ctx,
LEPUSValueConst *this_ptr, int argc,
LEPUSValueConst *argv,
uint32_t magic_func_id)
{
return host_call_function(ctx, this_ptr, argc, argv, magic_func_id);
}
// Function: PrimJS -> C
LEPUSValue hako_call_function(LEPUSContext *ctx, LEPUSValueConst this_val,
int argc, LEPUSValueConst *argv, int magic)
{
LEPUSValue *result_ptr =
hako_host_call_function(ctx, &this_val, argc, argv, magic);
if (result_ptr == NULL)
{
return LEPUS_UNDEFINED;
}
LEPUSValue result = *result_ptr;
if (result_ptr == &HAKO_Undefined || result_ptr == &HAKO_Null ||
result_ptr == &HAKO_True || result_ptr == &HAKO_False)
{
return result;
}
free(result_ptr);
return result;
}
LEPUSValue *WASM_EXPORT(HAKO_NewFunction)(LEPUSContext *ctx, uint32_t func_id,
CString *name)
{
LEPUSValue func_obj =
LEPUS_NewCFunctionMagic(ctx, hako_call_function, name, 0,
LEPUS_CFUNC_constructor_or_func_magic, func_id);
return jsvalue_to_heap(func_obj);
}
LEPUSValueConst *
WASM_EXPORT(HAKO_ArgvGetJSValueConstPointer)(LEPUSValueConst *argv, int index)
{
return &argv[index];
}
void WASM_EXPORT(HAKO_RuntimeEnableInterruptHandler)(LEPUSRuntime *rt, JSVoid *opaque)
{
LEPUS_SetInterruptHandler(rt, host_interrupt_handler, opaque);
}
void WASM_EXPORT(HAKO_RuntimeDisableInterruptHandler)(LEPUSRuntime *rt)
{
LEPUS_SetInterruptHandler(rt, NULL, NULL);
}
void WASM_EXPORT(HAKO_RuntimeEnableModuleLoader)(LEPUSRuntime *rt,
LEPUS_BOOL use_custom_normalize)
{
LEPUSModuleNormalizeFunc *module_normalize = NULL;
if (use_custom_normalize)
{
module_normalize = hako_normalize_module;
}
LEPUS_SetModuleLoaderFunc(rt, module_normalize, hako_load_module, NULL);
}
void WASM_EXPORT(HAKO_RuntimeDisableModuleLoader)(LEPUSRuntime *rt)
{
LEPUS_SetModuleLoaderFunc(rt, NULL, NULL, NULL);
}
LEPUSValue *WASM_EXPORT(HAKO_bjson_encode)(LEPUSContext *ctx,
LEPUSValueConst *val)
{
size_t length;
uint8_t *buffer = LEPUS_WriteObject(ctx, &length, *val, 0);
if (!buffer)
return jsvalue_to_heap(LEPUS_EXCEPTION);
LEPUSValue array = LEPUS_NewArrayBufferCopy(ctx, buffer, length);
lepus_free(ctx, buffer);
return jsvalue_to_heap(array);
}
LEPUSValue *WASM_EXPORT(HAKO_bjson_decode)(LEPUSContext *ctx,
LEPUSValueConst *data)
{
size_t length;
uint8_t *buffer = LEPUS_GetArrayBuffer(ctx, &length, *data);
if (!buffer)
return jsvalue_to_heap(LEPUS_EXCEPTION);
LEPUSValue value = LEPUS_ReadObject(ctx, buffer, length, 0);
return jsvalue_to_heap(value);
}
LEPUS_BOOL WASM_EXPORT(HAKO_IsArray)(LEPUSContext *ctx, LEPUSValueConst *val)
{
return LEPUS_IsArray(ctx, *val);
}
LEPUS_BOOL WASM_EXPORT(HAKO_IsTypedArray)(LEPUSContext *ctx,
LEPUSValueConst *val)
{
return LEPUS_IsTypedArray(ctx, *val);
}
// there is a super weird bug here where if any string literal contains the
// class name (e.g. "Uint8Array") it will be corrupted in memory.
HAKO_TypedArrayType WASM_EXPORT(HAKO_GetTypedArrayType)(LEPUSContext *ctx,
LEPUSValueConst *val)
{
LEPUSTypedArrayType type = LEPUS_GetTypedArrayType(ctx, *val);
switch (type)
{
case LEPUS_TYPED_UINT8_ARRAY:
return HAKO_TYPED_UINT8_ARRAY;
case LEPUS_TYPED_UINT8C_ARRAY:
return HAKO_TYPED_UINT8C_ARRAY;
case LEPUS_TYPED_INT8_ARRAY:
return HAKO_TYPED_INT8_ARRAY;
case LEPUS_TYPED_UINT16_ARRAY:
return HAKO_TYPED_UINT16_ARRAY;
case LEPUS_TYPED_INT16_ARRAY:
return HAKO_TYPED_INT16_ARRAY;
case LEPUS_TYPED_UINT32_ARRAY:
return HAKO_TYPED_UINT32_ARRAY;
case LEPUS_TYPED_INT32_ARRAY:
return HAKO_TYPED_INT32_ARRAY;
case LEPUS_TYPED_FLOAT32_ARRAY:
return HAKO_TYPED_FLOAT32_ARRAY;
case LEPUS_TYPED_FLOAT64_ARRAY:
return HAKO_TYPED_FLOAT64_ARRAY;
default:
return HAKO_TYPED_UNKNOWN;
}
}
JSVoid *WASM_EXPORT(HAKO_CopyTypedArrayBuffer)(LEPUSContext *ctx,
LEPUSValueConst *val,
size_t *out_length)
{
if (LEPUS_GetTypedArrayType(ctx, *val) != LEPUS_TYPED_UINT8_ARRAY)
{
LEPUS_ThrowTypeError(ctx, "Not a Uint8Array");
return NULL;
}
size_t byte_offset, byte_length, bytes_per_element;
LEPUSValue buffer = LEPUS_GetTypedArrayBuffer(
ctx, *val, &byte_offset, &byte_length, &bytes_per_element);
if (LEPUS_IsException(buffer))
return NULL;
// Now that we have the buffer, get the actual bytes
size_t buffer_length;
uint8_t *buffer_data = LEPUS_GetArrayBuffer(ctx, &buffer_length, buffer);
if (!buffer_data)
{
LEPUS_FreeValue(ctx, buffer); // Free the buffer value we got
return NULL;
}
// Allocate memory for the result
uint8_t *result = malloc(byte_length);
if (!result)
{
LEPUS_FreeValue(ctx, buffer);
return NULL;
}
// Copy the relevant portion of the buffer
memcpy(result, buffer_data + byte_offset, byte_length);
// Set the output length if requested
if (out_length)
*out_length = byte_length;
// Free the buffer value we obtained
LEPUS_FreeValue(ctx, buffer);
return result;
}
LEPUS_BOOL WASM_EXPORT(HAKO_IsArrayBuffer)(LEPUSValueConst *val)
{
return LEPUS_IsArrayBuffer(*val);
}
LEPUSValue *WASM_EXPORT(HAKO_ToJson)(LEPUSContext *ctx, LEPUSValueConst *val,
int indent)
{
if (LEPUS_IsUndefined(*val))
{
return jsvalue_to_heap(LEPUS_NewString(ctx, "undefined"));
}
if (LEPUS_IsNull(*val))
{
return jsvalue_to_heap(LEPUS_NewString(ctx, "null"));
}
LEPUSValue result = LEPUS_ToJSON(ctx, *val, indent);
if (LEPUS_IsException(result))
{
return jsvalue_to_heap(result);
}
return jsvalue_to_heap(result);
}
LEPUS_BOOL WASM_EXPORT(HAKO_IsError)(LEPUSContext *ctx, LEPUSValueConst *val)
{
return LEPUS_IsError(ctx, *val);
}
LEPUS_BOOL WASM_EXPORT(HAKO_IsException)(LEPUSValueConst *val)
{
return LEPUS_IsException(*val);
}
LEPUSValue *WASM_EXPORT(HAKO_GetException)(LEPUSContext *ctx)
{
return jsvalue_to_heap(LEPUS_GetException(ctx));
}
void WASM_EXPORT(SetGCThreshold)(LEPUSRuntime *rt, int64_t threshold)
{
LEPUS_SetGCThreshold(rt, threshold);
}
LEPUSValue *WASM_EXPORT(HAKO_NewBigInt)(LEPUSContext *ctx, int32_t low,
int32_t high)
{
#ifdef CONFIG_BIGNUM
int64_t combined = ((int64_t)high << 32) | ((uint32_t)low);
return jsvalue_to_heap(LEPUS_NewBigInt64(ctx, combined));
#else
return jsvalue_to_heap(LEPUS_ThrowTypeError(ctx, "BigInt not supported"));
#endif
}
LEPUSValue *WASM_EXPORT(HAKO_NewBigUInt)(LEPUSContext *ctx, uint32_t low,
uint32_t high)
{
#ifdef CONFIG_BIGNUM
uint64_t combined = ((uint64_t)high << 32) | low;
return jsvalue_to_heap(LEPUS_NewBigUint64(ctx, combined));
#else
return jsvalue_to_heap(LEPUS_ThrowTypeError(ctx, "BigInt not supported"));
#endif
}
LEPUS_BOOL WASM_EXPORT(HAKO_IsGCMode)(LEPUSContext *ctx)
{
return LEPUS_IsGCMode(ctx);
}
LEPUSValue *WASM_EXPORT(HAKO_NewDate)(LEPUSContext *ctx, double time)
{
return jsvalue_to_heap(LEPUS_NewDate(ctx, time));
}
LEPUSClassID WASM_EXPORT(HAKO_GetClassID)(LEPUSContext *ctx,
LEPUSValueConst *val)
{
return LEPUS_GetClassID(ctx, *val);
}
LEPUS_BOOL WASM_EXPORT(HAKO_IsInstanceOf)(LEPUSContext *ctx,
LEPUSValueConst *val,
LEPUSValueConst *obj)
{
return LEPUS_IsInstanceOf(ctx, *val, *obj);
}
HakoBuildInfo *WASM_EXPORT(HAKO_BuildInfo)()
{
// Return pointer to the existing static structure
return &build_info;
}
void *thread_entry_point(void *ctx)
{
int id = (int)ctx;
printf(" in thread %d\n", id);
return 0;
}
```
## /bridge/hako.h
```h path="/bridge/hako.h"
#ifndef HAKO_H
#define HAKO_H
#ifdef __cplusplus
extern "C"
{
#endif
#include
#include
#include
#include "quickjs.h"
#include "build.h"
#define BorrowedHeapChar const char
#define OwnedHeapChar char
#define JSBorrowedChar const char
#define JSVoid void
#define CString const char
#define EvalFlags int
#define EvalDetectModule int
#define HAKO_GPN_NUMBER_MASK (1 << 6)
#define HAKO_STANDARD_COMPLIANT_NUMBER (1 << 7)
#define LEPUS_ATOM_TAG_INT (1U << 31)
typedef enum HAKO_Intrinsic
{
HAKO_Intrinsic_BaseObjects = 1 << 0,
HAKO_Intrinsic_Date = 1 << 1,
HAKO_Intrinsic_Eval = 1 << 2,
HAKO_Intrinsic_StringNormalize = 1 << 3,
HAKO_Intrinsic_RegExp = 1 << 4,
HAKO_Intrinsic_RegExpCompiler = 1 << 5,
HAKO_Intrinsic_JSON = 1 << 6,
HAKO_Intrinsic_Proxy = 1 << 7,
HAKO_Intrinsic_MapSet = 1 << 8,
HAKO_Intrinsic_TypedArrays = 1 << 9,
HAKO_Intrinsic_Promise = 1 << 10,
HAKO_Intrinsic_BigInt = 1 << 11,
HAKO_Intrinsic_BigFloat = 1 << 12,
HAKO_Intrinsic_BigDecimal = 1 << 13,
HAKO_Intrinsic_OperatorOverloading = 1 << 14,
HAKO_Intrinsic_BignumExt = 1 << 15
} HAKO_Intrinsic;
typedef enum
{
HAKO_TYPED_UNKNOWN = 0,
HAKO_TYPED_UINT8_ARRAY = 1,
HAKO_TYPED_UINT8C_ARRAY = 2,
HAKO_TYPED_INT8_ARRAY = 3,
HAKO_TYPED_UINT16_ARRAY = 4,
HAKO_TYPED_INT16_ARRAY = 5,
HAKO_TYPED_UINT32_ARRAY = 6,
HAKO_TYPED_INT32_ARRAY = 7,
HAKO_TYPED_FLOAT32_ARRAY = 8,
HAKO_TYPED_FLOAT64_ARRAY = 9
} HAKO_TypedArrayType;
typedef enum IsEqualOp
{
HAKO_EqualOp_StrictEq = 0,
HAKO_EqualOp_SameValue = 1,
HAKO_EqualOp_SameValueZero = 2
} IsEqualOp;
/**
* @brief Creates a new Hako runtime
* @category Runtime Management
*
* @return LEPUSRuntime* - Pointer to the newly created runtime
* @tsreturn JSRuntimePointer
*/
LEPUSRuntime *HAKO_NewRuntime();
/**
* @brief Frees a Hako runtime and associated resources
* @category Runtime Management
*
* @param rt Runtime to free
* @tsparam rt JSRuntimePointer
*/
void HAKO_FreeRuntime(LEPUSRuntime *rt);
/**
* @brief Configure which debug info is stripped from the compiled code
* @category Runtime Management
*
* @param rt Runtime to configure
* @param flags Flags to configure stripping behavior
* @tsparam rt JSRuntimePointer
* @tsparam flags number
*/
void HAKO_SetStripInfo(LEPUSRuntime *rt, int flags);
/**
* @brief Get the current debug info stripping configuration
* @category Runtime Management
*
* @param rt Runtime to query
* @return int - Current stripping flags
* @tsparam rt JSRuntimePointer
* @tsreturn number
*/
int HAKO_GetStripInfo(LEPUSRuntime *rt);
/**
* @brief Sets memory limit for the runtime
* @category Runtime Management
*
* @param rt Runtime to set the limit for
* @param limit Memory limit in bytes, or -1 to disable limit
* @tsparam rt JSRuntimePointer
* @tsparam limit number
*/
void HAKO_RuntimeSetMemoryLimit(LEPUSRuntime *rt, size_t limit);
/**
* @brief Computes memory usage statistics for the runtime
* @category Memory
*
* @param rt Runtime to compute statistics for
* @param ctx Context to use for creating the result object
* @return LEPUSValue* - Object containing memory usage statistics
* @tsparam rt JSRuntimePointer
* @tsparam ctx JSContextPointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_RuntimeComputeMemoryUsage(LEPUSRuntime *rt, LEPUSContext *ctx);
/**
* @brief Dumps memory usage statistics as a string
* @category Memory
*
* @param rt Runtime to dump statistics for
* @return OwnedHeapChar* - String containing memory usage information
* @tsparam rt JSRuntimePointer
* @tsreturn CString
*/
OwnedHeapChar *HAKO_RuntimeDumpMemoryUsage(LEPUSRuntime *rt);
/**
* @brief Checks if there are pending promise jobs in the runtime
* @category Promise
*
* @param rt Runtime to check
* @return LEPUS_BOOL - True if jobs are pending, false otherwise
* @tsparam rt JSRuntimePointer
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_IsJobPending(LEPUSRuntime *rt);
/**
* @brief Executes pending promise jobs in the runtime
* @category Promise
*
* @param rt Runtime to execute jobs in
* @param maxJobsToExecute Maximum number of jobs to execute
* @param lastJobContext Pointer to store the context of the last executed job
* @return LEPUSValue* - Number of executed jobs or an exception
* @tsparam rt JSRuntimePointer
* @tsparam maxJobsToExecute number
* @tsparam lastJobContext number
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_ExecutePendingJob(LEPUSRuntime *rt, int maxJobsToExecute, LEPUSContext **lastJobContext);
/**
* @brief Enables interrupt handler for the runtime
* @category Interrupt Handling
*
* @param rt Runtime to enable interrupt handler for
* @param opaque Pointer to user-defined data
* @tsparam rt JSRuntimePointer
* @tsparam opaque number
*/
void HAKO_RuntimeEnableInterruptHandler(LEPUSRuntime *rt, JSVoid *opaque);
/**
* @brief Disables interrupt handler for the runtime
* @category Interrupt Handling
*
* @param rt Runtime to disable interrupt handler for
* @tsparam rt JSRuntimePointer
*/
void HAKO_RuntimeDisableInterruptHandler(LEPUSRuntime *rt);
/**
* @brief Enables module loader for the runtime
* @category Module Loading
*
* @param rt Runtime to enable module loader for
* @param use_custom_normalize Whether to use custom module name normalization
* @tsparam rt JSRuntimePointer
* @tsparam use_custom_normalize number
*/
void HAKO_RuntimeEnableModuleLoader(LEPUSRuntime *rt, LEPUS_BOOL use_custom_normalize);
/**
* @brief Disables module loader for the runtime
* @category Module Loading
*
* @param rt Runtime to disable module loader for
* @tsparam rt JSRuntimePointer
*/
void HAKO_RuntimeDisableModuleLoader(LEPUSRuntime *rt);
/**
* @brief Throws a JavaScript reference error with a message
* @category Error Handling
*
* @param ctx Context to throw the error in
* @param message Error message
* @tsparam ctx JSContextPointer
* @tsparam message CString
*/
void HAKO_RuntimeJSThrow(LEPUSContext *ctx, CString *message);
/**
* @brief Creates a new JavaScript context
* @category Context Management
*
* @param rt Runtime to create the context in
* @param intrinsics HAKO_Intrinsic flags to enable
* @return LEPUSContext* - Newly created context
* @tsparam rt JSRuntimePointer
* @tsparam intrinsics number
* @tsreturn JSContextPointer
*/
LEPUSContext *HAKO_NewContext(LEPUSRuntime *rt, HAKO_Intrinsic intrinsics);
/**
* @brief sets opaque data for the context. you are responsible for freeing the data.
* @category Context Management
*
* @param ctx Context to set the data for
* @param data Pointer to the data
* @tsparam ctx JSContextPointer
* @tsparam data number
*/
void HAKO_SetContextData(LEPUSContext *ctx, JSVoid *data);
/**
* @brief Gets opaque data for the context
* @category Context Management
*
* @param ctx Context to get the data from
* @return JSVoid* - Pointer to the data
* @tsparam ctx JSContextPointer
* @tsreturn number
*/
JSVoid *HAKO_GetContextData(LEPUSContext *ctx);
/**
* @brief If no_lepus_strict_mode is set to true, these conditions will handle, differently: if the object is null or undefined, read properties will return null if the object is null or undefined, write properties will not throw exception.
* @category Context Management
*
* @param ctx Context to set to no strict mode
* @tsparam ctx JSContextPointer
*/
void HAKO_SetNoStrictMode(LEPUSContext *ctx);
/**
* @brief Sets the virtual stack size for a context
* @category Context Management
*
* @param ctx Context to set the stack size for
* @param size Stack size in bytes
* @tsparam ctx JSContextPointer
* @tsparam size number
*/
void HAKO_SetVirtualStackSize(LEPUSContext *ctx, uint32_t size);
/**
* @brief Frees a JavaScript context
* @category Context Management
*
* @param ctx Context to free
* @tsparam ctx JSContextPointer
*/
void HAKO_FreeContext(LEPUSContext *ctx);
/**
* @brief Sets the maximum stack size for a context
* @category Context Management
*
* @param ctx Context to configure
* @param stack_size Maximum stack size in bytes
* @tsparam ctx JSContextPointer
* @tsparam stack_size number
*/
void HAKO_ContextSetMaxStackSize(LEPUSContext *ctx, size_t stack_size);
/**
* @brief Gets a pointer to the undefined value
* @category Constants
*
* @return LEPUSValueConst* - Pointer to the undefined value
* @tsreturn JSValueConstPointer
*/
LEPUSValueConst *HAKO_GetUndefined();
/**
* @brief Gets a pointer to the null value
* @category Constants
*
* @return LEPUSValueConst* - Pointer to the null value
* @tsreturn JSValueConstPointer
*/
LEPUSValueConst *HAKO_GetNull();
/**
* @brief Gets a pointer to the false value
* @category Constants
*
* @return LEPUSValueConst* - Pointer to the false value
* @tsreturn JSValueConstPointer
*/
LEPUSValueConst *HAKO_GetFalse();
/**
* @brief Gets a pointer to the true value
* @category Constants
*
* @return LEPUSValueConst* - Pointer to the true value
* @tsreturn JSValueConstPointer
*/
LEPUSValueConst *HAKO_GetTrue();
/**
* @brief Duplicates a JavaScript value pointer
* @category Value Management
*
* @param ctx Context to use
* @param val Value to duplicate
* @return LEPUSValue* - Pointer to the duplicated value
* @tsparam ctx JSContextPointer
* @tsparam val JSValueConstPointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_DupValuePointer(LEPUSContext *ctx, LEPUSValueConst *val);
/**
* @brief Frees a JavaScript value pointer
* @category Value Management
*
* @param ctx Context the value belongs to
* @param value Value pointer to free
* @tsparam ctx JSContextPointer
* @tsparam value JSValuePointer
*/
void HAKO_FreeValuePointer(LEPUSContext *ctx, LEPUSValue *value);
/**
* @brief Frees a JavaScript value pointer using a runtime
* @category Value Management
*
* @param rt Runtime the value belongs to
* @param value Value pointer to free
* @tsparam rt JSRuntimePointer
* @tsparam value JSValuePointer
*/
void HAKO_FreeValuePointerRuntime(LEPUSRuntime *rt, LEPUSValue *value);
/**
* @brief Frees a void pointer managed by a context
* @category Value Management
*
* @param ctx Context that allocated the pointer
* @param ptr Pointer to free
* @tsparam ctx JSContextPointer
* @tsparam ptr number
*/
void HAKO_FreeVoidPointer(LEPUSContext *ctx, JSVoid *ptr);
/**
* @brief Frees a C string managed by a context
* @category Value Management
*
* @param ctx Context that allocated the string
* @param str String to free
* @tsparam ctx JSContextPointer
* @tsparam str CString
*/
void HAKO_FreeCString(LEPUSContext *ctx, JSBorrowedChar *str);
/**
* @brief Throws a JavaScript error
* @category Error Handling
*
* @param ctx Context to throw in
* @param error Error to throw
* @return LEPUSValue* - LEPUS_EXCEPTION
* @tsparam ctx JSContextPointer
* @tsparam error JSValueConstPointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_Throw(LEPUSContext *ctx, LEPUSValueConst *error);
/**
* @brief Creates a new Error object
* @category Value Creation
*
* @param ctx Context to create in
* @return LEPUSValue* - New Error object
* @tsparam ctx JSContextPointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_NewError(LEPUSContext *ctx);
/**
* @brief Resolves the the last exception from a context, and returns its Error. Cannot be called twice.
* @category Error Handling
*
* @param ctx Context to resolve in
* @param maybe_exception Value that might be an exception
* @return LEPUSValue* - Error object or NULL if not an exception
* @tsparam ctx JSContextPointer
* @tsparam maybe_exception JSValuePointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_GetLastError(LEPUSContext *ctx, LEPUSValue *maybe_exception);
/**
* @brief Creates a new empty object
* @category Value Creation
*
* @param ctx Context to create in
* @return LEPUSValue* - New object
* @tsparam ctx JSContextPointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_NewObject(LEPUSContext *ctx);
/**
* @brief Creates a new object with specified prototype
* @category Value Creation
*
* @param ctx Context to create in
* @param proto Prototype object
* @return LEPUSValue* - New object
* @tsparam ctx JSContextPointer
* @tsparam proto JSValueConstPointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_NewObjectProto(LEPUSContext *ctx, LEPUSValueConst *proto);
/**
* @brief Creates a new array
* @category Value Creation
*
* @param ctx Context to create in
* @return LEPUSValue* - New array
* @tsparam ctx JSContextPointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_NewArray(LEPUSContext *ctx);
/**
* @brief Creates a new array buffer using existing memory
* @category Value Creation
*
* @param ctx Context to create in
* @param buffer Buffer to use
* @param length Buffer length in bytes
* @return LEPUSValue* - New ArrayBuffer
* @tsparam ctx JSContextPointer
* @tsparam buffer number
* @tsparam length number
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_NewArrayBuffer(LEPUSContext *ctx, JSVoid *buffer, size_t length);
/**
* @brief Gets a property value by name
* @category Value Operations
*
* @param ctx Context to use
* @param this_val Object to get property from
* @param prop_name Property name
* @return LEPUSValue* - Property value
* @tsparam ctx JSContextPointer
* @tsparam this_val JSValueConstPointer
* @tsparam prop_name JSValueConstPointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_GetProp(LEPUSContext *ctx, LEPUSValueConst *this_val, LEPUSValueConst *prop_name);
/**
* @brief Gets a property value by numeric index
* @category Value Operations
*
* @param ctx Context to use
* @param this_val Object to get property from
* @param prop_name Property index
* @return LEPUSValue* - Property value
* @tsparam ctx JSContextPointer
* @tsparam this_val JSValueConstPointer
* @tsparam prop_name number
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_GetPropNumber(LEPUSContext *ctx, LEPUSValueConst *this_val, int prop_name);
/**
* @brief Sets a property value
* @category Value Operations
*
* @param ctx Context to use
* @param this_val Object to set property on
* @param prop_name Property name
* @param prop_value Property value
* @return LEPUS_BOOL - True if successful, false otherwise, -1 if exception
* @tsparam ctx JSContextPointer
* @tsparam this_val JSValueConstPointer
* @tsparam prop_name JSValueConstPointer
* @tsparam prop_value JSValueConstPointer
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_SetProp(LEPUSContext *ctx, LEPUSValueConst *this_val, LEPUSValueConst *prop_name, LEPUSValueConst *prop_value);
/**
* @brief Defines a property with custom attributes
* @category Value Operations
*
* @param ctx Context to use
* @param this_val Object to define property on
* @param prop_name Property name
* @param prop_value Property value
* @param get Getter function or undefined
* @param set Setter function or undefined
* @param configurable Whether property is configurable
* @param enumerable Whether property is enumerable
* @param has_value Whether property has a value
* @return LEPUS_BOOL - True if successful, false otherwise, -1 if exception
* @tsparam ctx JSContextPointer
* @tsparam this_val JSValueConstPointer
* @tsparam prop_name JSValueConstPointer
* @tsparam prop_value JSValueConstPointer
* @tsparam get JSValueConstPointer
* @tsparam set JSValueConstPointer
* @tsparam configurable LEPUS_BOOL
* @tsparam enumerable LEPUS_BOOL
* @tsparam has_value LEPUS_BOOL
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_DefineProp(LEPUSContext *ctx, LEPUSValueConst *this_val, LEPUSValueConst *prop_name, LEPUSValueConst *prop_value, LEPUSValueConst *get, LEPUSValueConst *set, LEPUS_BOOL configurable, LEPUS_BOOL enumerable, LEPUS_BOOL has_value);
/**
* @brief Gets all own property names of an object
* @category Value Operations
*
* @param ctx Context to use
* @param out_ptrs Pointer to array to store property names
* @param out_len Pointer to store length of property names array
* @param obj Object to get property names from
* @param flags Property name flags
* @return LEPUSValue* - Exception if error occurred, NULL otherwise
* @tsparam ctx JSContextPointer
* @tsparam out_ptrs number
* @tsparam out_len number
* @tsparam obj JSValueConstPointer
* @tsparam flags number
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_GetOwnPropertyNames(LEPUSContext *ctx, LEPUSValue ***out_ptrs, uint32_t *out_len, LEPUSValueConst *obj, int flags);
/**
* @brief Gets the global object
* @category Value Operations
*
* @param ctx Context to get global object from
* @return LEPUSValue* - Global object
* @tsparam ctx JSContextPointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_GetGlobalObject(LEPUSContext *ctx);
/**
* @brief Gets the length of an object (array length or string length)
* @category Value Operations
*
* @param ctx Context to use
* @param out_len Pointer to store length
* @param value Object to get length from
* @return int - 0 on success, negative on error
* @tsparam ctx JSContextPointer
* @tsparam out_len number
* @tsparam value JSValueConstPointer
* @tsreturn number
*/
int HAKO_GetLength(LEPUSContext *ctx, uint32_t *out_len, LEPUSValueConst *value);
/**
* @brief Creates a new floating point number
* @category Value Creation
*
* @param ctx Context to create in
* @param num Number value
* @return LEPUSValue* - New number
* @tsparam ctx JSContextPointer
* @tsparam num number
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_NewFloat64(LEPUSContext *ctx, double num);
/**
* @brief Creates a new BigInt number
* @category Value Creation
*
* @param ctx Context to create in
* @param low Low 32 bits of the number
* @param high High 32 bits of the number
* @return LEPUSValue* - New BigInt
* @tsparam ctx JSContextPointer
* @tsparam low number
* @tsparam high number
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_NewBigInt(LEPUSContext *ctx, int32_t low, int32_t high);
/**
* @brief Creates a new BigUInt number
* @category Value Creation
*
* @param ctx Context to create in
* @param low Low 32 bits of the number
* @param high High 32 bits of the number
* @return LEPUSValue* - New BigUInt
* @tsparam ctx JSContextPointer
* @tsparam low number
* @tsparam high number
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_NewBigUInt(LEPUSContext *ctx, uint32_t low, uint32_t high);
/**
* @brief Sets the garbage collection threshold
* @category Memory Management
*
* @param ctx Context to set the threshold for
* @param threshold Threshold in bytes
* @tsparam ctx JSContextPointer
* @tsparam threshold number
*/
void HAKO_SetGCThreshold(LEPUSContext *ctx, int64_t threshold);
/**
* @brief Checks if the context is in garbage collection mode
* @category Memory Management
*
* @param ctx Context to check
* @return LEPUS_BOOL - True if in GC mode, false otherwise
* @tsparam ctx JSContextPointer
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_IsGCMode(LEPUSContext *ctx);
/**
* @brief Gets the floating point value of a number
* @category Value Operations
*
* @param ctx Context to use
* @param value Value to convert
* @return double - Number value
* @tsparam ctx JSContextPointer
* @tsparam value JSValueConstPointer
* @tsreturn number
*/
double HAKO_GetFloat64(LEPUSContext *ctx, LEPUSValueConst *value);
/**
* @brief Creates a new string
* @category Value Creation
*
* @param ctx Context to create in
* @param string String content
* @return LEPUSValue* - New string
* @tsparam ctx JSContextPointer
* @tsparam string CString
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_NewString(LEPUSContext *ctx, BorrowedHeapChar *string);
/**
* @brief Gets the C string representation of a value
* @category Value Operations
*
* @param ctx Context to use
* @param value Value to convert
* @return JSBorrowedChar* - String representation
* @tsparam ctx JSContextPointer
* @tsparam value JSValueConstPointer
* @tsreturn CString
*/
JSBorrowedChar *HAKO_ToCString(LEPUSContext *ctx, LEPUSValueConst *value);
/**
* @brief Creates a new symbol
* @category Value Creation
*
* @param ctx Context to create in
* @param description Symbol description
* @param isGlobal Whether to create a global symbol
* @return LEPUSValue* - New symbol
* @tsparam ctx JSContextPointer
* @tsparam description CString
* @tsparam isGlobal number
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_NewSymbol(LEPUSContext *ctx, BorrowedHeapChar *description, int isGlobal);
/**
* @brief Gets the description or key of a symbol
* @category Value Operations
*
* @param ctx Context to use
* @param value Symbol to get description from
* @return JSBorrowedChar* - Symbol description or key
* @tsparam ctx JSContextPointer
* @tsparam value JSValueConstPointer
* @tsreturn CString
*/
JSBorrowedChar *HAKO_GetSymbolDescriptionOrKey(LEPUSContext *ctx, LEPUSValueConst *value);
/**
* @brief Checks if a symbol is global
* @category Value Operations
*
* @param ctx Context to use
* @param value Symbol to check
* @return LEPUS_BOOL - True if symbol is global, false otherwise
* @tsparam ctx JSContextPointer
* @tsparam value JSValueConstPointer
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_IsGlobalSymbol(LEPUSContext *ctx, LEPUSValueConst *value);
/**
* @brief Gets the type of a value as a string
* @category Value Operations
*
* @param ctx Context to use
* @param value Value to get type of
* @return OwnedHeapChar* - Type name
* @tsparam ctx JSContextPointer
* @tsparam value JSValueConstPointer
* @tsreturn OwnedHeapChar
*/
OwnedHeapChar *HAKO_Typeof(LEPUSContext *ctx, LEPUSValueConst *value);
/**
* @brief Checks if a value is an array
* @category Value Operations
*
* @param ctx Context to use
* @param value Value to check
* @return LEPUS_BOOL - True if value is an array, false otherwise (-1 if error)
* @tsparam ctx JSContextPointer
* @tsparam value JSValueConstPointer
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_IsArray(LEPUSContext *ctx, LEPUSValueConst *value);
/**
* @brief Checks if a value is a typed array
* @category Value Operations
*
* @param ctx Context to use
* @param value Value to check
* @return LEPUS_BOOL - True if value is a typed array, false otherwise (-1 if error)
* @tsparam ctx JSContextPointer
* @tsparam value JSValueConstPointer
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_IsTypedArray(LEPUSContext *ctx, LEPUSValueConst *value);
/**
* @brief Gets the type of a typed array
* @category Value Operations
*
* @param ctx Context to use
* @param value Typed array to get type of
* @return HAKO_TypedArrayType - Type id
* @tsparam ctx JSContextPointer
* @tsparam value JSValueConstPointer
* @tsreturn number
*/
HAKO_TypedArrayType HAKO_GetTypedArrayType(LEPUSContext *ctx, LEPUSValueConst *value);
/**
* @brief Checks if a value is an ArrayBuffer
* @category Value Operations
*
* @param value Value to check
* @return LEPUS_BOOL - True if value is an ArrayBuffer, false otherwise (-1 if error)
* @tsparam value JSValueConstPointer
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_IsArrayBuffer(LEPUSValueConst *value);
/**
* @brief Checks if two values are equal according to the specified operation
* @category Value Operations
*
* @param ctx Context to use
* @param a First value
* @param b Second value
* @param op Equal operation type
* @return LEPUS_BOOL - True if values are equal, false otherwise
* @tsparam ctx JSContextPointer
* @tsparam a JSValueConstPointer
* @tsparam b JSValueConstPointer
* @tsparam op number
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_IsEqual(LEPUSContext *ctx, LEPUSValueConst *a, LEPUSValueConst *b, IsEqualOp op);
/**
* @brief Copy the buffer from a guest ArrayBuffer
* @category Value Operations
*
* @param ctx Context to use
* @param data ArrayBuffer to get buffer from
* @param out_len Pointer to store length of the buffer
* @return JSVoid* - Buffer pointer
* @tsparam ctx JSContextPointer
* @tsparam data JSValueConstPointer
* @tsparam out_len number
* @tsreturn number
*/
JSVoid *HAKO_CopyArrayBuffer(LEPUSContext *ctx, LEPUSValueConst *data, size_t *out_len);
/**
* @brief Copy the buffer pointer from a TypedArray
* @category Value Operations
*
* @param ctx Context to use
* @param data TypedArray to get buffer from
* @param out_len Pointer to store length of the buffer
* @return JSVoid* - Buffer pointer
* @tsparam ctx JSContextPointer
* @tsparam data JSValueConstPointer
* @tsparam out_len number
* @tsreturn number
*/
JSVoid *HAKO_CopyTypedArrayBuffer(LEPUSContext *ctx, LEPUSValueConst *data, size_t *out_len);
/**
* @brief Creates a new function with a host function ID
* @category Value Creation
*
* @param ctx Context to create in
* @param func_id Function ID to call on the host
* @param name Function name
* @return LEPUSValue* - New function
* @tsparam ctx JSContextPointer
* @tsparam func_id number
* @tsparam name CString
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_NewFunction(LEPUSContext *ctx, uint32_t func_id, CString *name);
/**
* @brief Calls a function
* @category Value Operations
*
* @param ctx Context to use
* @param func_obj Function to call
* @param this_obj This value
* @param argc Number of arguments
* @param argv_ptrs Array of argument pointers
* @return LEPUSValue* - Function result
* @tsparam ctx JSContextPointer
* @tsparam func_obj JSValueConstPointer
* @tsparam this_obj JSValueConstPointer
* @tsparam argc number
* @tsparam argv_ptrs number
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_Call(LEPUSContext *ctx, LEPUSValueConst *func_obj, LEPUSValueConst *this_obj, int argc, LEPUSValueConst **argv_ptrs);
/**
* @brief Gets a JavaScript value from an argv array
* @category Value Operations
*
* @param argv Array of values
* @param index Index to get
* @return LEPUSValueConst* - Value at index
* @tsparam argv number
* @tsparam index number
* @tsreturn JSValueConstPointer
*/
LEPUSValueConst *HAKO_ArgvGetJSValueConstPointer(LEPUSValueConst *argv, int index);
/**
* @brief Evaluates JavaScript code
* @category Eval
*
* @param ctx Context to evaluate in
* @param js_code Code to evaluate
* @param js_code_length Code length
* @param filename Filename for error reporting
* @param detectModule Whether to auto-detect module code
* @param evalFlags Evaluation flags
* @return LEPUSValue* - Evaluation result
* @tsparam ctx JSContextPointer
* @tsparam js_code CString
* @tsparam js_code_length number
* @tsparam filename CString
* @tsparam detectModule number
* @tsparam evalFlags number
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_Eval(LEPUSContext *ctx, BorrowedHeapChar *js_code, size_t js_code_length, BorrowedHeapChar *filename, EvalDetectModule detectModule, EvalFlags evalFlags);
/**
* @brief Creates a new promise capability
* @category Promise
*
* @param ctx Context to create in
* @param resolve_funcs_out Array to store resolve and reject functions
* @return LEPUSValue* - New promise
* @tsparam ctx JSContextPointer
* @tsparam resolve_funcs_out number
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_NewPromiseCapability(LEPUSContext *ctx, LEPUSValue **resolve_funcs_out);
/**
* @brief Checks if a value is a promise
* @category Promise
*
* @param ctx Context to use
* @param promise Value to check
* @return LEPUS_BOOL - True if value is a promise, false otherwise
* @tsparam ctx JSContextPointer
* @tsparam promise JSValueConstPointer
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_IsPromise(LEPUSContext *ctx, LEPUSValueConst *promise);
/**
* @brief Gets the state of a promise
* @category Promise
*
* @param ctx Context to use
* @param promise Promise to get state from
* @return LEPUSPromiseStateEnum - Promise state
* @tsparam ctx JSContextPointer
* @tsparam promise JSValueConstPointer
* @tsreturn number
*/
LEPUSPromiseStateEnum HAKO_PromiseState(LEPUSContext *ctx, LEPUSValueConst *promise);
/**
* @brief Gets the result value of a promise
* @category Promise
*
* @param ctx Context to use
* @param promise Promise to get result from
* @return LEPUSValue* - Promise result
* @tsparam ctx JSContextPointer
* @tsparam promise JSValueConstPointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_PromiseResult(LEPUSContext *ctx, LEPUSValueConst *promise);
/**
* @brief Gets the namespace object of a module
* @category Module Loading
*
* @param ctx Context to use
* @param module_func_obj Module function object
* @return LEPUSValue* - Module namespace
* @tsparam ctx JSContextPointer
* @tsparam module_func_obj JSValueConstPointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_GetModuleNamespace(LEPUSContext *ctx, LEPUSValueConst *module_func_obj);
/**
* @brief Dumps a value to a JSON string
* @category Value Operations
*
* @param ctx Context to use
* @param obj Value to dump
* @return JSBorrowedChar* - JSON string representation
* @tsparam ctx JSContextPointer
* @tsparam obj JSValueConstPointer
* @tsreturn CString
*/
JSBorrowedChar *HAKO_Dump(LEPUSContext *ctx, LEPUSValueConst *obj);
/**
* @brief Checks if the build is a debug build
* @category Debug & Info
*
* @return LEPUS_BOOL - True if debug build, false otherwise
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_BuildIsDebug();
/**
* @brief Checks if the build has leak sanitizer enabled
* @category Debug & Info
*
* @return LEPUS_BOOL - True if leak sanitizer is enabled, false otherwise
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_BuildIsSanitizeLeak();
/**
* @brief Performs a recoverable leak check
* @category Debug & Info
*
* @return int - Result of leak check
* @tsreturn number
*/
int HAKO_RecoverableLeakCheck();
/**
* @brief Gets the version string
* @category Debug & Info
*
* @return CString* - Version string
* @tsreturn CString
*/
CString *HAKO_GetVersion();
/**
* @brief Gets the PrimJS version number
* @category Debug & Info
*
* @return uint64_t - PrimJS version
* @tsreturn bigint
*/
uint64_t HAKO_GetPrimjsVersion();
/**
* @brief Encodes a value to binary JSON format
* @category Binary JSON
*
* @param ctx Context to use
* @param val Value to encode
* @return LEPUSValue* - ArrayBuffer containing encoded data
* @tsparam ctx JSContextPointer
* @tsparam val JSValueConstPointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_bjson_encode(LEPUSContext *ctx, LEPUSValueConst *val);
/**
* @brief Decodes a value from binary JSON format
* @category Binary JSON
*
* @param ctx Context to use
* @param data ArrayBuffer containing encoded data
* @return LEPUSValue* - Decoded value
* @tsparam ctx JSContextPointer
* @tsparam data JSValueConstPointer
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_bjson_decode(LEPUSContext *ctx, LEPUSValueConst *data);
/**
* @brief Covnerts a value to JSON format
* @category Value Operations
*
* @param ctx Context to use
* @param val Value to stringify
* @param indent Indentation level
* @return LEPUSValue* - JSON string representation
* @tsparam ctx JSContextPointer
* @tsparam val JSValueConstPointer
* @tsparam indent number
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_ToJson(LEPUSContext *ctx, LEPUSValueConst *val, int indent);
/**
* @brief Checks if a value is an Error
* @category Error Handling
*
* @param ctx Context to use
* @param val Value to check
* @return LEPUS_BOOL - 1 if value is an error, 0 otherwise
* @tsparam ctx JSContextPointer
* @tsparam val JSValueConstPointer
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_IsError(LEPUSContext *ctx, LEPUSValueConst *val);
/**
* @brief Checks if a value is an exception
* @category Error Handling
*
* @param val Value to check
* @return LEPUS_BOOL - 1 if value is an exception, 0 otherwise
* @tsparam val JSValueConstPointer
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_IsException(LEPUSValueConst *val);
/**
* @brief Creates a new date object
* @category Value Creation
*
* @param ctx Context to create in
* @param time Time value
* @return LEPUSValue* - New date object
* @tsparam ctx JSContextPointer
* @tsparam time number
* @tsreturn JSValuePointer
*/
LEPUSValue *HAKO_NewDate(LEPUSContext *ctx, double time);
/**
* @brief Gets the class ID of a value
* @category Value Operations
*
* @param ctx Context to use
* @param val Value to get class ID from
* @return LEPUSClassID - Class ID of the value (0 if not a class)
* @tsparam ctx JSContextPointer
* @tsparam val JSValueConstPointer
* @tsreturn number
*/
LEPUSClassID HAKO_GetClassID(LEPUSContext *ctx, LEPUSValueConst *val);
/**
* @brief Checks if a value is an instance of a class
* @category Value Operations
*
* @param ctx Context to use
* @param val Value to check
* @param obj Class object to check against
* @return LEPUS_BOOL TRUE, FALSE or (-1) in case of exception
* @tsparam ctx JSContextPointer
* @tsparam val JSValueConstPointer
* @tsparam obj JSValueConstPointer
* @tsreturn LEPUS_BOOL
*/
LEPUS_BOOL HAKO_IsInstanceOf(LEPUSContext *ctx, LEPUSValueConst *val, LEPUSValueConst *obj);
/**
* @brief Gets the build information
* @category Debug & Info
*
* @return HakoBuildInfo* - Pointer to build information
* @tsreturn number
*/
HakoBuildInfo *HAKO_BuildInfo();
/**
* @brief Enables profiling of function calls
* @category Debug & Info
*
* @param rt Runtime to enable profiling for
* @param sampling Sampling rate - If sampling == 0, it's interpreted as "no sampling" which means we log 1/1 calls.
* @param opaque Opaque data to pass to the callback
* @tsparam rt JSRuntimePointer
* @tsparam sampling number
* @tsparam opaque number
* @tsreturn void
*/
void HAKO_EnableProfileCalls(LEPUSRuntime *rt, uint32_t sampling, JSVoid *opaque);
#ifdef HAKO_DEBUG_MODE
#define HAKO_LOG(msg) hako_log(msg)
#else
#define HAKO_LOG(msg) ((void)0)
#endif
#ifdef __cplusplus
}
#endif
#endif /* HAKO_H */
```
## /embedders/ts/.gitignore
```gitignore path="/embedders/ts/.gitignore"
# Dependencies
node_modules
.pnp
.pnp.js
# Build output
dist
build
*.tsbuildinfo
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Test coverage
coverage
# Editor directories and files
.vscode/*
!.vscode/extensions.json
!.vscode/settings.json
.idea
.DS_Store
```
## /embedders/ts/README.md
Hako (ha-ko) or 箱 means “box” in Japanese.
[](https://www.apache.org/licenses/LICENSE-2.0.txt)
Hako is a embeddable, lightweight, secure, high-performance JavaScript engine. It is a fork of PrimJS; Hako has full support for ES2019 and later ESNext features, and offers superior performance and a better development experience when compared to QuickJS.