Compare commits

..

81 Commits

Author SHA1 Message Date
Nicholas Dudfield
7694602d10 Close F0 nominal classifier proof gap 2026-08-25 12:01:08 +07:00
Nicholas Dudfield
f3e9d2b6a7 Advance JSHooks provider to F0 2026-08-25 11:44:57 +07:00
Nicholas Dudfield
ff7de7207a ci(hooks): skip Docker builds on jshookz again
Restore the pre-2b493ed skip: do not run Build using Docker on jshookz
pushes/PRs, and skip the build job when the PR head is jshookz.
2026-08-24 16:32:09 +07:00
Nicholas Dudfield
1b3722c012 style: x-format-changed since origin/dev
clang-format 18 on HookArtifact/JSHooks tests (CI clang-format job),
ruff on conanfile.py, cmake-format on CMakeLists.txt.
2026-08-24 16:24:28 +07:00
Nicholas Dudfield
e2f87c0b03 ci(hooks): run JSHookz e2e on jshookz push only
Drop the pull_request trigger for now. Push and PR used different refs,
so concurrency did not cancel the duplicate self-hosted Env job.
2026-08-24 14:46:53 +07:00
Nicholas Dudfield
6d6f011b5b fix(hooks): fail closed on sealed Receipt-A lock tables
Generator now rejects declaration/surface hashes, the 22 typed exports,
the 13 typed imports including module, and the four object limits unless
they match the immutable Receipt-A expectation. Coordinated JSON and
JSON+CMake mutations go red in the Python suite, which is also a CMake
target for the focused consumer gate.
2026-08-24 13:34:31 +07:00
Nicholas Dudfield
8e7a7f48f9 ci(hooks): feed JSHookz e2e from xahaud-pinned
Point JSHOOKZ_REF at the dedicated 78de1c4 convenience branch. Tracked
manifest/native locks remain the authority; no expected-commit check.
2026-08-24 13:27:14 +07:00
Nicholas Dudfield
07962562f6 test(hooks): project typed exports and fail-closed lock mutations
Project all 22 typed QuickJS provider export rows (kind/name/params/results
plus the memory row) and wasm_stack_bytes=131072 from the sealed 78de1c4
lock. Cross-compare JSON/CMake/native identities in the generator and add
durable mutation reds for provider bytes/size/SHA, manifest SHA, runtime
profile, declaration/surface, bytecode/native ABI, import/export
signatures, memory shape, and each serialized-object limit.
2026-08-24 13:13:02 +07:00
Nicholas Dudfield
50615166b1 chore(hooks): pin QuickJS provider 78de1c4 (Receipt B)
Consume the sealed lock at 1101461 bytes / SHA-256 40f9ac02… / runtime
60950ca8… / bytecode ABI 75ea54f3…. Project the four serialized-object
limits, 22 exports, memory min 6 / max 512, and javascript-surface
hashes. Native STObject/STArray fixture covers Amount.issue.currency,
repeated identity, and canonical JSON/bytes. Fuel ratchets step with
the larger provider; guest ABI is unchanged.
2026-08-24 12:54:02 +07:00
Nicholas Dudfield
f14e9236d2 fix(hooks): bill current-profile invocation fuel without incomplete type
SetHook cannot dereference the opaque QuickJS runtime handle. Use
currentQuickJSRuntimeProfile().invocationFuel for the CREATE fee
ceiling. Keep this out of the Receipt B provider pin.
2026-08-24 12:38:23 +07:00
Nicholas Dudfield
2b493ed881 fix(hooks): harden QuickJS consumer before next provider pin
Empty embed is fatal at daemon start. QuickJS CREATE bills the
invocation-fuel ceiling. TestEnv keeps the log rewriter on a shared
rewrite table. Wasmtime i32 results are kind-checked. Drop unused
pre-GuestMemory HOOK_SETUP/DECLARE/DEFINE copies from Macro.h. Docker
no longer skips jshookz; e2e also runs on pull_request.
2026-08-24 12:24:44 +07:00
Nicholas Dudfield
b9d0434e96 chore(hooks): pin provider after xahau-types and CFUNC rewrite
Seal lock to 6d05e793 (941714). Bytecode ABI unchanged. Four
HookInstructionCount ratchets step by one with the new wasm.
2026-08-18 12:34:39 +07:00
Nicholas Dudfield
3d5844e0ed ci(hooks): install pinned Wizer 10 in JSHookz e2e
Copy the provider workflow's wizer-v10.0.0 linux tarball install.
Without it, build provider dies after cmake with "wizer not found".
Also clang-format 18.1.8 the TestEnv / JSHooks files CI rejected.
2026-08-17 20:20:33 +07:00
Nicholas Dudfield
ad85d170c6 ci(hooks): check out jshookz main instead of a SHA
The sealed lock is the pin. CI builds whatever is on jshookz main
and fails if it does not reproduce the checked-in manifests. The
snapshot importer no longer rewrites JSHOOKZ_REF.
2026-08-17 20:10:08 +07:00
Nicholas Dudfield
32baf652ee chore(hooks): pin Wizered QuickJS provider snapshot
Seal the runtime-profile lock to jshookz 5d00e66 (wasm 6cbe9383,
941701, memory min 6). Bytecode ABI is unchanged. Default provider
build Wizers from jshookz_provider.unwizered.wasm.
2026-08-17 20:06:49 +07:00
Nicholas Dudfield
7bf8cd75e5 build(hooks): project provider values at build time
Replace configure-time header/embed generation with a static
QuickJSProviderProfile.h and one generated QuickJSProviderValues.cpp.
CMake only EXISTS-checks the three lock files. A refresh target plus
fingerprint stamp sees wasm appear or change without reconfigure.

The snapshot importer accepts python/jshookz or packages/jshookz.
2026-08-17 19:17:26 +07:00
Nicholas Dudfield
e910177838 chore(hooks): update JSHookz provider snapshot
Pin the sealed runtime-profile lock to local product 220bfdf
(wasm 9a0ff771, 852299). Bytecode ABI is unchanged; runtime
profile is now f606dec6. The e2e workflow finds python/jshookz
after the producer flatten. The wasm stays gitignored.

CI JSHOOKZ_REF is unpublished until that product commit is on
origin.
2026-08-17 17:33:57 +07:00
Nicholas Dudfield
d1cd4b22f0 fix(logs): give TestEnv a null-check rewriter
setTransform is installed in the TestEnv constructor and cleared
in the destructor. The empty path is a null check; SuiteJournal
writes the original string when nothing is installed.
2026-08-17 15:49:42 +07:00
Nicholas Dudfield
277bad787d test(hooks): add TestEnv, HooksTrace, and external hook-test wiring
TestEnv names accounts, prefixes phases, and honors XAHAU_TEST_LOG
(TESTENV_LOGGING alias). Logs::applyTransform copies the rewriter
under the existing mutex so suite journals can rewrite r-addresses
without racing setTransform. Hook host traces move off the View
journal onto a HooksTrace partition.

HOOKS_TEST_DIR / HOOKS_SOURCE_DIR pull producer-side *_test.cpp
into the rippled test binary. Coverage is not wired.
2026-08-17 15:14:01 +07:00
Nicholas Dudfield
dce17e2fa6 test(hooks): move JSHooks fixtures onto current v1 guest
Refresh the sealed provider pin to local product a48fb8a (wasm
95c40c7b, 852299). Guest sources now use rollback.onFail, total
hook.account(), STBlob.fromHex, CallbackInfo, and
//@jshookz-allow-malformed for undeployable modules. Regenerated
the bytecode header and fuel ratchets from a local JSHooks run
(17 cases, 343 tests). BEAST_EXPECT_EQ prints both sides.

CI JSHOOKZ_REF is that unpublished SHA; do not push until it is
on origin.
2026-08-17 15:13:15 +07:00
Nicholas Dudfield
e6403f1596 docs(hooks): launch contract names the daemon wiring as built 2026-08-15 18:46:50 +07:00
Nicholas Dudfield
ee0b677c2e chore(hooks): match embed generator file mode to its sibling script 2026-08-15 18:44:29 +07:00
Nicholas Dudfield
a4a43578de test(hooks): cost seam rejects failed validation; assert every stage ran
measureQuickJSSessionCostForTests now treats a validate call that did
not return callable flags (1 or 3) as an error instead of sampling it,
and the measuring case asserts each stage's minimum wall time is
non-zero, so a skipped stage or a rejecting session turns the case red
without pinning any number (grok, b6b57e3ec review).
2026-08-15 18:38:41 +07:00
Nicholas Dudfield
b6b57e3ec3 test(hooks): measure per-session quickjs provider cost
ENABLE_TESTS seam measureQuickJSSessionCostForTests runs N cold sessions
exactly as validation builds them (create → initialize → arm invocation
fuel → validate) and reports wall time per stage (sum and min) plus the
initialization and invocation fuel consumed; ProviderSession gains
initializationFuelConsumed beside invocationFuelConsumed. JSHooks_test
logs the figures (issue 0001 step 1: measure before changing provider
bytes) and asserts only the shape.

on this mac, release, live producer bundle: ~200us per cold session
(create 34us, initialize 157us, validate 6us), init fuel 1800041 of
5000000, invocation fuel 49832 of 50000000.
2026-08-15 18:12:45 +07:00
Nicholas Dudfield
41400f1cc6 build(hooks): never let a stale empty provider embed survive a bundle change
a stub written by a bundle-less configure is newer than a wasm copied in
afterwards, so ninja could skip the embed and the daemon would warn and
stay up. when the wasm is present, configure now drops an existing stub
(recognised by its provenance line) so the custom command must run; the
absent case is a configure-time warning instead of a status line. found by
grok's review of 77a1c2d65; verified by writing a stub, reconfiguring, and
watching the embed regenerate with the real bytes.
2026-08-15 18:09:32 +07:00
Nicholas Dudfield
77a1c2d65d feat(hooks): embed sealed quickjs provider and register it at daemon startup
cmake/EmbedQuickJSProvider.py projects the bundle's provider wasm into a
generated translation unit (one escaped string literal; ~0.7s to compile)
defining hook::embeddedQuickJSProvider(), re-verifying sha-256 and size
against the manifest at build time; a bundle without the binary embeds an
empty span. QuickJSProvider.cmake wires the custom command (or the absent
stub) and RippledCore adds the generated source to rippled.

the daemon path in Main.cpp launches the background registration before
Application::setup, awaits it after setup and before start, and treats a
refused launch or a failed registration as fatal, per the registry
contract: a node that cannot register its sealed provider must not stay
up. an empty embed logs a warning and continues. jtx::Env does not pass
through Main, so unit tests keep an unregistered current profile.

standalone probe on this machine: registered 1031425 bytes, awaited
135 ms after launch, before "Application starting".
2026-08-15 17:53:17 +07:00
Nicholas Dudfield
e4f4b28e0f fix(hooks): harden background provider registration per grok review
one static RegistryState holds mutex, runtimes and pending together so
teardown no longer depends on which accessor a process touches first
(error()-first then launch destroyed the registry before the pending
join). the pending entry is reserved under the lock with a promise-backed
future and the compile thread is started outside it and detached, so no
path can wait on the compile while holding the registry mutex and nothing
joins at exit; the worker catches and retains exceptions instead of
settling with an empty error.

launch now returns the refusal reason: mismatched profile against a
registered or launched identity, or a terminally failed earlier launch
(its retained error); same-profile relaunch stays a no-op.
quickJSRuntimeRegistrationError becomes awaitQuickJSRuntimeRegistration,
which is nullopt only when the identity is registered with this profile
and otherwise says why not (retained failure, different profile, or
never launched). header states the contract: launch returns before any
find/await, await before shutdown, failure is fatal at startup.

first tests for the lane on distinct runtimeProfile identities: launch
with concurrent finds agreeing on one handle, same/mismatched relaunch,
and a corrupted launch retaining its error and refusing relaunch.
2026-08-15 17:39:19 +07:00
Nicholas Dudfield
9870e5df67 feat(hooks): background quickjs provider compilation at startup
launchQuickJSRuntimeRegistration compiles/registers a provider on a
startup thread (std::async; the shared state joins on teardown so a
half-registered compile cannot be abandoned). findQuickJSRuntime waits
for a pending registration of the same identity before answering, so
background compilation is invisible to resolution except as startup
latency; failed identities retain their error via
quickJSRuntimeRegistrationError. in-memory only: no serialization, no
cache, nothing consensus-visible — the input is still the sha-verified
provider bytes (ai-docs issue 0001, ruled simplest step)
2026-08-15 17:13:25 +07:00
Nicholas Dudfield
dfc2a8ed83 chore(hooks): update JSHookz provider snapshot 2026-08-14 15:52:10 +07:00
Nicholas Dudfield
9de7ba734d chore(hooks): automate QuickJS provider snapshots 2026-08-14 15:50:45 +07:00
Nicholas Dudfield
359c3e7990 ci(hooks): update QuickJS policy test filter 2026-08-13 21:18:47 +07:00
Nicholas Dudfield
650b2d6e1c refactor(hooks): remove duplicate QuickJS catalogue 2026-08-13 20:18:52 +07:00
Nicholas Dudfield
94c61cc626 refactor(hooks): remove unused import categories 2026-08-13 19:58:10 +07:00
Nicholas Dudfield
b7881302e3 refactor(hooks): share host bodies with QuickJS 2026-08-13 19:46:03 +07:00
Nicholas Dudfield
4e87ccd924 chore(hooks): mark generated integration artifacts 2026-08-13 16:19:50 +07:00
Nicholas Dudfield
df86bebd5f test(hooks): focus proof on the QuickJS boundary 2026-08-13 16:19:36 +07:00
Nicholas Dudfield
670e77d4c0 refactor(hooks): simplify QuickJS guest memory 2026-08-13 16:18:53 +07:00
Nicholas Dudfield
ba54d7fd77 refactor(hooks): restore legacy WasmEdge execution 2026-08-13 15:50:18 +07:00
Nicholas Dudfield
78929910ee refactor(hooks): freeze QuickJS runtime policy 2026-08-13 14:50:05 +07:00
Nicholas Dudfield
3436c241e2 refactor(hooks): restore legacy host wrappers to applyHook 2026-08-13 10:42:31 +07:00
Nicholas Dudfield
398f6f01d7 ci: wait for both coverage reports 2026-08-13 09:11:11 +07:00
Nicholas Dudfield
aeea7a8251 ci(hooks): cover the shared host seam 2026-08-13 07:54:44 +07:00
Nicholas Dudfield
33510213d3 fix(hooks): reject null provider allocations 2026-08-13 07:54:38 +07:00
Nicholas Dudfield
bb136e616a fix(hooks): make admission and teardown deterministic 2026-08-13 07:31:19 +07:00
Nicholas Dudfield
e4ccc443c1 fix(ci): preserve Git checkout metadata 2026-08-13 07:13:35 +07:00
Nicholas Dudfield
0b38795617 ci(hooks): reuse Xahau build caches 2026-08-13 07:07:36 +07:00
Nicholas Dudfield
f3ed814dcf test(ci): report provider-backed coverage 2026-08-12 19:06:50 +07:00
Nicholas Dudfield
7c66f94a90 test(ci): exercise QuickJS provider in coverage 2026-08-12 18:40:48 +07:00
Nicholas Dudfield
240f33d398 fix(ci): refresh Wasmtime recipe 2026-08-12 18:21:24 +07:00
Nicholas Dudfield
a868a46848 fix(build): normalize Wasmtime package compiler 2026-08-12 18:14:01 +07:00
Nicholas Dudfield
b86112bca4 ci(hooks): defer HBB release packaging 2026-08-12 17:59:18 +07:00
Nicholas Dudfield
92832aac10 fix(ci): install server build dependencies 2026-08-12 17:51:39 +07:00
Nicholas Dudfield
8e7def3739 fix(hooks): keep hook macros in app layer 2026-08-12 17:51:32 +07:00
Nicholas Dudfield
552d1cf1e0 style(hooks): apply clang-format 2026-08-12 17:32:23 +07:00
Nicholas Dudfield
f35d8591d8 ci(hooks): prove jshookz integration 2026-08-12 17:31:24 +07:00
Nicholas Dudfield
124235d92e build(hooks): consume jshookz provider bundle 2026-08-12 16:42:23 +07:00
Nicholas Dudfield
56a456a7ff test(hooks): verify dual-runtime catalogue execution 2026-08-12 11:15:35 +07:00
Nicholas Dudfield
70814197f5 feat(hooks): expose raw Hook catalogue through Wasmtime 2026-08-12 11:13:45 +07:00
Nicholas Dudfield
d5334234fc build(hooks): pin Wasmtime provider dependencies 2026-08-12 11:13:23 +07:00
Nicholas Dudfield
d406e336a4 Factor shared Hook host operation seam 2026-08-11 16:10:11 +07:00
Nicholas Dudfield
4c58f4223c Route Hook installation through canonical artifacts 2026-08-11 15:30:22 +07:00
Nicholas Dudfield
2d48d21cc6 Refactor QuickJS Hook provider execution boundary 2026-08-11 13:01:46 +07:00
Nicholas Dudfield
7dd63aaac9 Add QuickJS Hook transaction integration 2026-08-11 12:24:41 +07:00
Richard Holland
bb244ef772 put release builds into a candidate folder to prevent auto-update scripts running before smoke tests (#761) 2026-06-21 12:12:43 +10:00
Richard Holland
639ea34377 Fixhookmap (#756) 2026-06-16 17:06:25 +10:00
tequ
089c0dc3fe Fix ammLPHolds logic to include escrowed cases (#757) 2026-06-16 15:26:29 +10:00
tequ
78167e09c0 change sfHookOn of ltHookDefinition to soeOptional for HookOnV2 (#755) 2026-06-16 13:58:53 +10:00
tequ
607a7fdf98 Change AMM to Supported::no (#758) 2026-06-16 13:56:58 +10:00
tequ
c55420bcd8 Fix duplicate and incorrect fields in server_definitions (#753) 2026-05-27 07:58:13 +10:00
tequ
cb91b4e88e Restore pre-reserved tem codes (#754) 2026-05-27 07:57:55 +10:00
tequ
90333b6fd0 Fix HookAPI Expected and Refactor Enum classes (#729) 2026-05-26 11:02:17 +10:00
tequ
7f9a9364b0 fix: Ensures canonical order for PriceDataSeries upon PriceOracle creation (#5485) (#744) 2026-05-25 11:34:24 +10:00
tequ
706d31f01d Update .git-blame-ignore-revs (#746) 2026-05-25 11:28:56 +10:00
tequ
083e9e4315 Generate hook extern declarations with C linkage (#750) 2026-05-25 11:27:21 +10:00
tequ
b9e0c56def Guard depth 32 (#653) 2026-05-25 11:10:49 +10:00
tequ
663ed4edb8 Named Hook (#718) 2026-05-19 12:00:25 +10:00
tequ
8422758d4d chore: Improve codecov coverage reporting (#4977) (#745) 2026-05-18 12:21:47 +10:00
tequ
586c78e812 fix: Replace badCurrency() checks with isBadCurrency() for improved clarity (#742) 2026-05-18 11:57:25 +10:00
Niq Dudfield
9b50b68d39 perf(ledger): optimize catalogue loading memory usage and performance (#548) 2026-05-06 17:29:44 +10:00
tequ
5e8d26f67a refactor: Calculate numFeatures automatically (#5324) (#739)
Co-authored-by: Ed Hennis <ed@ripple.com>
2026-04-30 18:17:50 +10:00
tequ
a6186d7855 IOURewardClaim (#500) 2026-04-30 15:27:51 +10:00
248 changed files with 29365 additions and 131184 deletions

View File

@@ -1,6 +1,40 @@
codecov:
require_ci_to_pass: true
# This integration branch uploads the normal suite and JSHookz E2E coverage.
notify:
after_n_builds: 2
comment:
behavior: default
layout: reach,diff,flags,tree,reach
show_carryforward_flags: false
coverage:
range: "60..80"
precision: 1
round: nearest
status:
project:
default:
target: 60%
threshold: 2%
patch:
default:
target: auto
threshold: 2%
changes: false
github_checks:
annotations: true
parsers:
cobertura:
partials_as_hits: true
handle_missing_conditions : true
slack_app: false
ignore:
- "src/test/"
- "include/xrpl/beast/test/"
- "include/xrpl/beast/unit_test/"

View File

@@ -1,8 +1,25 @@
# This feature requires Git >= 2.24
# To use it by default in git blame:
# git config blame.ignoreRevsFile .git-blame-ignore-revs
# Format first-party source according to .clang-format
50760c693510894ca368e90369b0cc2dabfd07f3
e2384885f5f630c8f0ffe4bf21a169b433a16858
241b9ddde9e11beb7480600fd5ed90e1ef109b21
760f16f56835663d9286bd29294d074de26a7ba6
0eebe6a5f4246fced516d52b83ec4e7f47373edd
# Reintroduce Clang-Format & Levelization
da1d20d6d5d862716125d60899b80fab5302954a
# Consolidate external libraries
da1d20d6d5d862716125d60899b80fab5302954a
# Rename .hpp to .h
0345a2645d0f5ad900f4fbbcaff96040d3a887fc
# Format formerly .hpp files
5a227dc719016e10045e17c9396ad401118044f1
# Rewrite includes
e61880699997398f5a746e6c4034edc7632661f5
# Move CMake directory (#4997)
e47b1c1b3b97c3f6d11858ee02f463596e29e7f0
# Rearrange sources (#4997)
bfafa2bb39e562901736d656806bd700c3699a2f
# Rewrite includes (#4997)
e61880699997398f5a746e6c4034edc7632661f5
# Recompute loops (#4997)
d25b5dcd568bb96c18e347d55fac10fe901a1bfb
# Reformat code with clang-format-18
02749feea88ce61c1f7eeb2d61a57d8ecf07ab11

7
.gitattributes vendored
View File

@@ -12,5 +12,8 @@ LICENSE binary
*.props text eol=crlf
*.filters text eol=crlf
# Mark it as generated and folded out by default
src/test/app/SetJSHook_wasm.h linguist-generated=true
# JSHookz projections checked for freshness by the integration workflow.
external/quickjs-provider/jshookz_provider.manifest.cmake linguist-generated=true
external/quickjs-provider/jshookz_provider.manifest.json linguist-generated=true
external/quickjs-provider/jshookz_provider.native-abi.json linguist-generated=true
src/test/app/JSHooks_test_hooks.h linguist-generated=true

View File

@@ -145,6 +145,9 @@ runs:
# Export wasmedge if not already exported
conan list wasmedge/0.11.2@xahaud/stable 2>/dev/null | (grep -q "not found" && exit 1 || exit 0) || \
conan export external/wasmedge --version 0.11.2 --user xahaud --channel stable
# Keep the cached recipe in sync with this checkout; package binaries remain cached.
conan export external/wasmtime --version 47.0.3 --user xahaud --channel stable
- name: Install dependencies
shell: bash
env:
@@ -159,4 +162,6 @@ runs:
--output-folder . \
--build missing \
--settings build_type=${{ inputs.configuration }} \
--options '&:xrpld=True' \
--options '&:tests=True' \
..

View File

@@ -33,6 +33,7 @@ jobs:
fetch-depth: 2 # Only get the last 2 commits, to avoid fetching all history
build:
if: github.head_ref != 'jshookz'
runs-on: [self-hosted, xahaud-build]
needs: [checkout]
defaults:

View File

@@ -82,6 +82,9 @@ jobs:
command -v clang-format || (echo "Error: clang-format not found" && exit 1)
echo "All tools verified successfully"
- name: Verify independent Wasmtime host catalogue fixture
run: src/test/app/jshooks/raw-catalogue/generate.py --check
# Execute build script to regenerate xahau.h
- name: Run build_xahau_h.sh
run: |

View File

@@ -20,7 +20,7 @@ jobs:
sudo apt-get update
sudo apt-get install clang-format-${CLANG_VERSION}
- name: Format first-party sources
run: find include src -type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' -o -name '*.ipp' \) -not -path "src/quickjs/*" -exec clang-format-${CLANG_VERSION} -i {} +
run: find include src -type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' -o -name '*.ipp' \) -exec clang-format-${CLANG_VERSION} -i {} +
- name: Check for differences
id: assert
run: |

236
.github/workflows/jshookz-e2e.yml vendored Normal file
View File

@@ -0,0 +1,236 @@
# End-to-end proof for the external jshookz toolchain and real Xahau Env.
name: JSHookz end to end
on:
push:
branches: [jshookz]
# PRs targeting jshookz/dev currently duplicate the branch-push e2e
# (different github.ref, so concurrency does not cancel). Re-enable later.
workflow_dispatch:
permissions:
contents: read
id-token: write
concurrency:
group: jshookz-e2e-${{ github.ref }}
cancel-in-progress: true
env:
JSHOOKZ_REF: xahaud-pinned
HOOKZ_REF: 846f5f0f80861985dceb41ad6f02ff914f8a0c3d
XAHAUD_SCRIPTS_REF: e41a712c68a613d95a8911cbf89711db2d95aa3b
WASI_SDK_VERSION: "32"
WASI_SDK_SHA256: 55fc523ebfbc98f69d1034fcfcb83d1ff5610cd9ab7eceef6cd097a30ba4ef93
BINARYEN_VERSION: "128"
BINARYEN_SHA256: 4ce79586d1c4762502eebe9a1db071fa5e446ef8897f2f766eb1cce5ec6dee9e
WIZER_VERSION: "10.0.0"
WIZER_SHA256: 1e9dfaa2f6c436ca7acfdbb82c166f5b8a0062068f12916a554e82c97ca8d371
WASI_SDK_PATH: ${{ github.workspace }}/.tools/wasi-sdk
HOOKZ_WASI_SDK: ${{ github.workspace }}/.tools/wasi-sdk
BINARYEN_HOME: ${{ github.workspace }}/.tools/binaryen
JSHOOKZ_PROVIDER_BUNDLE: ${{ github.workspace }}/.deps/jshookz/build/xahau-provider
XAHAU_QJS_PROVIDER_WASM: ${{ github.workspace }}/.deps/jshookz/build/xahau-provider/jshookz_provider.wasm
XAHAU_REQUIRE_QJS_PROVIDER_TESTS: "1"
CC: gcc-13
CXX: g++-13
CCACHE_DIR: /github/home/.ccache-cache
CMAKE_BUILD_PARALLEL_LEVEL: "2"
jobs:
env-proof:
runs-on: [self-hosted, generic, 20.04]
timeout-minutes: 120
container:
image: ubuntu:24.04
volumes:
- /home/runner/.conan-cache:/.conan-cache
- /home/runner/.ccache-cache:/github/home/.ccache-cache
defaults:
run:
shell: bash
steps:
- name: Install checkout prerequisite
run: |
apt-get update
apt-get install -y git
- name: Check out Xahau integration
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Check out public jshookz
uses: actions/checkout@v4
with:
repository: sublimator/jshookz
ref: ${{ env.JSHOOKZ_REF }}
path: .deps/jshookz
persist-credentials: false
- name: Check out public hookz
uses: actions/checkout@v4
with:
repository: sublimator/hookz
ref: ${{ env.HOOKZ_REF }}
path: .deps/hookz
persist-credentials: false
- name: Check out public xahaud-scripts
uses: actions/checkout@v4
with:
repository: sublimator/xahaud-scripts
ref: ${{ env.XAHAUD_SCRIPTS_REF }}
path: .deps/xahaud-scripts
persist-credentials: false
- uses: astral-sh/setup-uv@v7
- uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install native and TypeScript tools
run: |
apt-get install -y \
build-essential ccache clang-format-18 cmake curl g++-13 gcc-13 \
libsqlite3-dev ninja-build pkg-config xz-utils
ln -sf /usr/bin/clang-format-18 /usr/local/bin/clang-format
ln -sf /usr/bin/gcov-13 /usr/local/bin/gcov
npm install --global typescript@6.0.3
uv python install 3.13
uv tool install --python 3.13 conan==2.21.0
uv tool install --python 3.13 'gcovr>=7,<9'
- name: Install wasi-sdk and Binaryen
run: |
mkdir -p .tools
curl --fail --location --retry 3 \
--output /tmp/wasi-sdk.tar.gz \
"https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux.tar.gz"
echo "$WASI_SDK_SHA256 /tmp/wasi-sdk.tar.gz" | sha256sum --check
tar -xzf /tmp/wasi-sdk.tar.gz -C .tools
mv ".tools/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux" "$WASI_SDK_PATH"
curl --fail --location --retry 3 \
--output /tmp/binaryen.tar.gz \
"https://github.com/WebAssembly/binaryen/releases/download/version_${BINARYEN_VERSION}/binaryen-version_${BINARYEN_VERSION}-x86_64-linux.tar.gz"
echo "$BINARYEN_SHA256 /tmp/binaryen.tar.gz" | sha256sum --check
tar -xzf /tmp/binaryen.tar.gz -C .tools
mv ".tools/binaryen-version_${BINARYEN_VERSION}" "$BINARYEN_HOME"
echo "$BINARYEN_HOME/bin" >> "$GITHUB_PATH"
- name: Install Wizer
run: |
curl --fail --location --retry 3 \
--output /tmp/wizer.tar.xz \
"https://github.com/bytecodealliance/wizer/releases/download/v${WIZER_VERSION}/wizer-v${WIZER_VERSION}-x86_64-linux.tar.xz"
echo "$WIZER_SHA256 /tmp/wizer.tar.xz" | sha256sum --check
tar -xJf /tmp/wizer.tar.xz -C .tools
echo "$GITHUB_WORKSPACE/.tools/wizer-v${WIZER_VERSION}-x86_64-linux" >> "$GITHUB_PATH"
- name: Install the Hook toolchain
run: |
if [ -f .deps/jshookz/python/jshookz/pyproject.toml ]; then
jshookz_project=.deps/jshookz/python/jshookz
else
jshookz_project=.deps/jshookz/packages/jshookz
fi
uv sync --project "$jshookz_project" \
--locked --group dev --python 3.13
uv tool install --python 3.13 --force --from .deps/hookz hookz
uv tool install --python 3.13 --force \
--from .deps/xahaud-scripts xahaud-scripts
jshookz_cmd="$jshookz_project/.venv/bin/jshookz"
echo "JSHOOKZ_CMD=$jshookz_cmd" >> "$GITHUB_ENV"
echo "JSHOOKZ_HOOK_COMPILER=$jshookz_cmd compile-hook" >> "$GITHUB_ENV"
"$jshookz_cmd" info
hookz --version
x-run-tests --help >/dev/null
- name: Build and verify the sealed provider
run: |
"$JSHOOKZ_CMD" build provider
test -s "$XAHAU_QJS_PROVIDER_WASM"
cmp external/quickjs-provider/jshookz_provider.manifest.json \
"$JSHOOKZ_PROVIDER_BUNDLE/jshookz_provider.manifest.json"
cmp external/quickjs-provider/jshookz_provider.manifest.cmake \
"$JSHOOKZ_PROVIDER_BUNDLE/jshookz_provider.manifest.cmake"
cmp external/quickjs-provider/jshookz_provider.native-abi.json \
"$JSHOOKZ_PROVIDER_BUNDLE/jshookz_provider.native-abi.json"
cp "$XAHAU_QJS_PROVIDER_WASM" \
external/quickjs-provider/jshookz_provider.wasm
- name: Prove generator-lock mutation reds
run: python3 cmake/test_generate_quickjs_provider_bundle.py
- name: Configure Conan for Xahau
run: |
mkdir -p \
/.conan-cache/conan2 \
/.conan-cache/conan2_download \
/.conan-cache/conan2_sources
mkdir -p "$HOME/.conan2"
printf '%s\n' \
'core.cache:storage_path=/.conan-cache/conan2' \
'core.download:download_cache=/.conan-cache/conan2_download' \
'core.sources:download_cache=/.conan-cache/conan2_sources' \
> "$HOME/.conan2/global.conf"
mkdir -p "$HOME/.conan2/profiles"
cat > "$HOME/.conan2/profiles/default" <<'EOF'
[settings]
arch=x86_64
build_type=Debug
compiler=gcc
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.version=13
os=Linux
[buildenv]
CC=/usr/bin/gcc-13
CXX=/usr/bin/g++-13
[conf]
tools.build:compiler_executables={"c": "/usr/bin/gcc-13", "cpp": "/usr/bin/g++-13"}
[options]
&:xrpld=True
&:tests=True
date/*:use_system_tz_db=True
EOF
conan remote add --force xrplf https://conan.ripplex.io
conan profile show
conan export external/snappy --version 1.1.10 \
--user xahaud --channel stable
conan export external/soci --version 4.0.3 \
--user xahaud --channel stable
conan export external/wasmedge --version 0.11.2 \
--user xahaud --channel stable
conan export external/wasmtime --version 47.0.3 \
--user xahaud --channel stable
- name: Build Xahau and run the authoritative Env suite
run: |
x-run-tests \
--compile-hooks src/test/app/JSHooks_test.cpp \
--build-type Coverage \
--build-dir build-jshookz-e2e \
--reconfigure-build \
--ccache \
-j 2 \
-- \
ripple.app.JSHooks,ripple.app.QuickJSHostPolicy,ripple.app.HookGuestMemory,ripple.app.HookAPI,ripple.app.HookWasmtimeHostCatalogue,ripple.app.HookArtifact
- name: Upload JSHookz coverage
uses: codecov/codecov-action@v5
with:
files: build-jshookz-e2e/coverage/coverage.json
fail_ci_if_error: true
disable_search: true
verbose: true
plugins: noop
use_oidc: true
- name: Verify the checked-in fixture header is current
run: git diff --exit-code -- src/test/app/JSHooks_test_hooks.h

2
.gitignore vendored
View File

@@ -124,8 +124,6 @@ pkg
CMakeUserPresets.json
bld.rippled/
# binary
qjsc
generated
.vscode

View File

@@ -35,7 +35,7 @@ See [System Requirements](https://xrpl.org/system-requirements.html).
Building rippled generally requires git, Python, Conan, CMake, and a C++ compiler. Some guidance on setting up such a [C++ development environment can be found here](./docs/build/environment.md).
- [Python 3.7](https://www.python.org/downloads/)
- [Python 3.7](https://www.python.org/downloads/) (configure also uses it to project the QuickJS provider)
- [Conan 2.x](https://conan.io/downloads)
- [CMake 3.16](https://cmake.org/download/)

View File

@@ -84,6 +84,7 @@ test.nodestore > xrpl.basics
test.nodestore > xrpld.core
test.nodestore > xrpld.nodestore
test.nodestore > xrpld.unity
test.nodestore > xrpl.protocol
test.overlay > test.jtx
test.overlay > test.toplevel
test.overlay > test.unit_test
@@ -118,6 +119,7 @@ test.rpc > xrpld.core
test.rpc > xrpld.net
test.rpc > xrpld.overlay
test.rpc > xrpld.rpc
test.rpc > xrpld.shamap
test.rpc > xrpl.hook
test.rpc > xrpl.json
test.rpc > xrpl.protocol
@@ -150,7 +152,6 @@ xrpl.resource > xrpl.protocol
xrpl.server > xrpl.basics
xrpl.server > xrpl.json
xrpl.server > xrpl.protocol
xrpld.app > quickjs.toplevel
xrpld.app > test.unit_test
xrpld.app > xrpl.basics
xrpld.app > xrpld.conditions

View File

@@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.16)
cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -18,41 +18,52 @@ if(POLICY CMP0144)
cmake_policy(SET CMP0144 NEW)
endif()
project (xrpl)
project(xrpl)
set(Boost_NO_BOOST_CMAKE ON)
# make GIT_COMMIT_HASH define available to all sources
find_package(Git)
if(Git_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gch)
if(gch)
set(GIT_COMMIT_HASH "${gch}")
message(STATUS gch: ${GIT_COMMIT_HASH})
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git
rev-parse HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE gch)
if(gch)
set(GIT_COMMIT_HASH "${gch}")
message(STATUS gch: ${GIT_COMMIT_HASH})
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
endif()
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse --abbrev-ref HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gb)
if(gb)
set(GIT_BRANCH "${gb}")
message(STATUS gb: ${GIT_BRANCH})
add_definitions(-DGIT_BRANCH="${GIT_BRANCH}")
endif()
endif() #git
execute_process(
COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git
rev-parse --abbrev-ref HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE gb)
if(gb)
set(GIT_BRANCH "${gb}")
message(STATUS gb: ${GIT_BRANCH})
add_definitions(-DGIT_BRANCH="${GIT_BRANCH}")
endif()
endif() # git
# make SOURCE_ROOT_PATH define available for logging
set(SOURCE_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/")
add_definitions(-DSOURCE_ROOT_PATH="${SOURCE_ROOT_PATH}")
# BEAST_ENHANCED_LOGGING - adds file:line numbers and formatting to logs
# Automatically enabled for Debug builds via generator expression
# Can be explicitly controlled with -DBEAST_ENHANCED_LOGGING=ON/OFF
option(BEAST_ENHANCED_LOGGING "Include file and line numbers in log messages (auto: Debug=ON, Release=OFF)" OFF)
# Automatically enabled for Debug builds via generator expression Can be
# explicitly controlled with -DBEAST_ENHANCED_LOGGING=ON/OFF
option(
BEAST_ENHANCED_LOGGING
"Include file and line numbers in log messages (auto: Debug=ON, Release=OFF)"
OFF)
message(STATUS "BEAST_ENHANCED_LOGGING option: ${BEAST_ENHANCED_LOGGING}")
if(thread_safety_analysis)
add_compile_options(-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -DRIPPLE_ENABLE_THREAD_SAFETY_ANNOTATIONS)
add_compile_options(
-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS
-DRIPPLE_ENABLE_THREAD_SAFETY_ANNOTATIONS)
add_compile_options("-stdlib=libc++")
add_link_options("-stdlib=libc++")
endif()
@@ -62,40 +73,48 @@ option(USE_CONAN "Use Conan package manager for dependencies" OFF)
if(CMAKE_TOOLCHAIN_FILE)
# Check if the toolchain file path contains "conan_toolchain"
if(CMAKE_TOOLCHAIN_FILE MATCHES "conan_toolchain")
set(USE_CONAN ON CACHE BOOL "Using Conan detected from toolchain file" FORCE)
set(USE_CONAN
ON
CACHE BOOL "Using Conan detected from toolchain file" FORCE)
message(STATUS "Conan toolchain detected: ${CMAKE_TOOLCHAIN_FILE}")
message(STATUS "Building with Conan dependencies")
endif()
endif()
include (CheckCXXCompilerFlag)
include (FetchContent)
include (ExternalProject)
include (CMakeFuncs) # must come *after* ExternalProject b/c it overrides one function in EP
if (target)
message (FATAL_ERROR "The target option has been removed - use native cmake options to control build")
endif ()
include(CheckCXXCompilerFlag)
include(FetchContent)
include(ExternalProject)
include(CMakeFuncs) # must come *after* ExternalProject b/c it overrides one
# function in EP
if(target)
message(
FATAL_ERROR
"The target option has been removed - use native cmake options to control build"
)
endif()
include(RippledSanity)
include(RippledVersion)
include(RippledSettings)
# this check has to remain in the top-level cmake
# because of the early return statement
if (packages_only)
if (NOT TARGET rpm)
message (FATAL_ERROR "packages_only requested, but targets were not created - is docker installed?")
# this check has to remain in the top-level cmake because of the early return
# statement
if(packages_only)
if(NOT TARGET rpm)
message(
FATAL_ERROR
"packages_only requested, but targets were not created - is docker installed?"
)
endif()
return ()
endif ()
return()
endif()
include(RippledCompiler)
include(RippledInterface)
include(deps/Boost)
find_package(OpenSSL 1.1.1 REQUIRED)
set_target_properties(OpenSSL::SSL PROPERTIES
INTERFACE_COMPILE_DEFINITIONS OPENSSL_NO_SSL2
)
set_target_properties(OpenSSL::SSL PROPERTIES INTERFACE_COMPILE_DEFINITIONS
OPENSSL_NO_SSL2)
set(SECP256K1_INSTALL TRUE)
add_subdirectory(external/secp256k1)
add_library(secp256k1::secp256k1 ALIAS secp256k1)
@@ -103,9 +122,9 @@ add_subdirectory(external/ed25519-donna)
add_subdirectory(external/antithesis-sdk)
find_package(gRPC REQUIRED)
find_package(lz4 REQUIRED)
# Target names with :: are not allowed in a generator expression.
# We need to pull the include directories and imported location properties
# from separate targets.
# Target names with :: are not allowed in a generator expression. We need to
# pull the include directories and imported location properties from separate
# targets.
find_package(LibArchive REQUIRED)
find_package(SOCI REQUIRED)
find_package(SQLite3 REQUIRED)
@@ -113,9 +132,9 @@ find_package(SQLite3 REQUIRED)
option(rocksdb "Enable RocksDB" ON)
if(rocksdb)
find_package(RocksDB REQUIRED)
set_target_properties(RocksDB::rocksdb PROPERTIES
INTERFACE_COMPILE_DEFINITIONS RIPPLE_ROCKSDB_AVAILABLE=1
)
set_target_properties(
RocksDB::rocksdb PROPERTIES INTERFACE_COMPILE_DEFINITIONS
RIPPLE_ROCKSDB_AVAILABLE=1)
target_link_libraries(ripple_libs INTERFACE RocksDB::rocksdb)
endif()
@@ -124,8 +143,12 @@ find_package(date REQUIRED)
find_package(xxHash REQUIRED)
find_package(magic_enum REQUIRED)
include(deps/WasmEdge)
if(TARGET nudb::core)
include(deps/WasmEdge)
if(xrpld)
include(deps/Wasmtime)
include(QuickJSProvider)
endif()
if(TARGET nudb::core)
set(nudb nudb::core)
elseif(TARGET NuDB::nudb)
set(nudb NuDB::nudb)
@@ -134,18 +157,16 @@ else()
endif()
target_link_libraries(ripple_libs INTERFACE ${nudb})
target_link_libraries(ripple_libs INTERFACE
ed25519::ed25519
LibArchive::LibArchive
lz4::lz4
OpenSSL::Crypto
OpenSSL::SSL
secp256k1::secp256k1
soci::soci
SQLite::SQLite3
)
include(deps/quickjs)
target_link_libraries(
ripple_libs
INTERFACE ed25519::ed25519
LibArchive::LibArchive
lz4::lz4
OpenSSL::Crypto
OpenSSL::SSL
secp256k1::secp256k1
soci::soci
SQLite::SQLite3)
if(coverage)
include(RippledCov)

148
bin/update-jshookz-snapshot Executable file
View File

@@ -0,0 +1,148 @@
#!/usr/bin/env python3
"""Build and import one committed jshookz provider snapshot into Xahau."""
from __future__ import annotations
import argparse
import json
import os
import shlex
import shutil
import subprocess
from pathlib import Path
SNAPSHOT_FILES = (
"jshookz_provider.manifest.cmake",
"jshookz_provider.manifest.json",
"jshookz_provider.native-abi.json",
"jshookz_provider.wasm",
)
API_SNAPSHOT_FILES = {
"python/jshookz/src/jshookz/types/api-artifacts.json": "api-artifacts.json",
"python/jshookz/src/jshookz/types/hooks-api.d.ts": "hooks-api.d.ts",
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts":
"xahau-quickjs-v1.d.ts",
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.surface.json":
"xahau-quickjs-v1.surface.json",
}
def run(*args: str, cwd: Path, capture: bool = False) -> str:
completed = subprocess.run(
args,
cwd=cwd,
check=True,
text=True,
stdout=subprocess.PIPE if capture else None,
)
return completed.stdout.strip() if capture else ""
def main() -> int:
parser = argparse.ArgumentParser(
description=(
"Build a committed jshookz checkout and copy its sealed "
"provider lock into Xahau. CI tracks jshookz main; the lock "
"is what must match."
)
)
parser.add_argument("jshookz_checkout", type=Path)
parser.add_argument(
"--no-build",
action="store_true",
help="import an already-built build/xahau-provider bundle",
)
args = parser.parse_args()
xahau_root = Path(__file__).resolve().parents[1]
jshookz_root = args.jshookz_checkout.expanduser().resolve()
cli = next(
(
candidate
for candidate in (
jshookz_root / "python/jshookz/.venv/bin/jshookz",
jshookz_root / "packages/jshookz/.venv/bin/jshookz",
)
if candidate.is_file()
),
None,
)
if cli is None:
parser.error(
"missing jshookz CLI under python/jshookz/.venv or "
"packages/jshookz/.venv"
)
bundle = jshookz_root / "build/xahau-provider"
destination = xahau_root / "external/quickjs-provider"
dirty = run(
"git",
"status",
"--porcelain",
"--untracked-files=no",
cwd=jshookz_root,
capture=True,
)
if dirty:
parser.error(
"jshookz checkout has uncommitted tracked changes; commit them "
"before creating a pinned snapshot"
)
commit = run("git", "rev-parse", "HEAD", cwd=jshookz_root, capture=True)
if not args.no_build:
run(str(cli), "build", "provider", cwd=jshookz_root)
missing = [name for name in SNAPSHOT_FILES if not (bundle / name).is_file()]
missing.extend(
source for source in API_SNAPSHOT_FILES if not (jshookz_root / source).is_file()
)
if missing:
parser.error(f"incomplete provider bundle: {', '.join(missing)}")
for name in SNAPSHOT_FILES:
shutil.copy2(bundle / name, destination / name)
for source, name in API_SNAPSHOT_FILES.items():
shutil.copy2(jshookz_root / source, destination / name)
manifest = json.loads((bundle / "jshookz_provider.manifest.json").read_text())
wasi_sdk = os.environ.get("HOOKZ_WASI_SDK")
if not wasi_sdk:
try:
wasi_sdk = str(
Path(
run(
"mise",
"where",
"wasi-sdk",
cwd=jshookz_root,
capture=True,
)
)
/ "wasi-sdk"
)
except (FileNotFoundError, subprocess.CalledProcessError):
wasi_sdk = "/path/to/wasi-sdk"
print(f"jshookz commit: {commit}")
print(f"provider sha256: {manifest['provider']['sha256']}")
print(f"bytecode ABI: {manifest['bytecode_abi_id']}")
print(f"runtime profile: {manifest['runtime_profile_id']}")
print("snapshot imported; regenerate fixtures and run the focused suite with:")
print(
" env "
f"JSHOOKZ_HOOK_COMPILER={shlex.quote(f'{cli} compile-hook')} "
f"HOOKZ_WASI_SDK={shlex.quote(wasi_sdk)} "
f"XAHAU_QJS_PROVIDER_WASM={shlex.quote(str(bundle / 'jshookz_provider.wasm'))} "
"XAHAU_REQUIRE_QJS_PROVIDER_TESTS=1 "
"x-run-tests --compile-hooks src/test/app/JSHooks_test.cpp "
"--build-dir build --ccache -- "
"ripple.app.JSHooks,ripple.app.QuickJSHostPolicy,"
"ripple.app.HookGuestMemory,ripple.app.HookAPI"
)
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -95,8 +95,16 @@ if [[ "$4" == "" ]]; then
echo "Non GH, local building, no Action runner magic"
else
# GH Action, runner
cp /io/release-build/xahaud /data/builds/$(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4
cp /io/release-build/release.info /data/builds/$(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4.releaseinfo
if [[ "$(git rev-parse --abbrev-ref HEAD)" == "release" ]]; then
echo "building on the release branch... placing it in builds/candidate"
mkdir /data/builds/candidate
cp /io/release-build/xahaud /data/builds/candidate/$(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4
cp /io/release-build/release.info /data/builds/candidate/$(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4.releaseinfo
else
echo "building non-release branch, placing it in builds root"
cp /io/release-build/xahaud /data/builds/$(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4
cp /io/release-build/release.info /data/builds/$(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4.releaseinfo
fi
echo "Published build to: http://build.xahau.tech/"
echo $(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4
fi

View File

@@ -12,17 +12,16 @@ echo "-- GITHUB_REPOSITORY: $1"
echo "-- GITHUB_SHA: $2"
echo "-- GITHUB_RUN_NUMBER: $4"
umask 0000;
umask 0000
####
cd /io;
mkdir -p src/certs;
curl --silent -k https://raw.githubusercontent.com/RichardAH/rippled-release-builder/main/ca-bundle/certbundle.h -o src/certs/certbundle.h;
if [ "`grep certbundle.h src/xrpld/net/detail/RegisterSSLCerts.cpp | wc -l`" -eq "0" ]
then
cp src/xrpld/net/detail/RegisterSSLCerts.cpp src/xrpld/net/detail/RegisterSSLCerts.cpp.old
perl -i -pe "s/^{/{
cd /io
mkdir -p src/certs
curl --silent -k https://raw.githubusercontent.com/RichardAH/rippled-release-builder/main/ca-bundle/certbundle.h -o src/certs/certbundle.h
if [ "$(grep certbundle.h src/xrpld/net/detail/RegisterSSLCerts.cpp | wc -l)" -eq "0" ]; then
cp src/xrpld/net/detail/RegisterSSLCerts.cpp src/xrpld/net/detail/RegisterSSLCerts.cpp.old
perl -i -pe "s/^{/{
#ifdef EMBEDDED_CA_BUNDLE
BIO *cbio = BIO_new_mem_buf(ca_bundle.data(), ca_bundle.size());
X509_STORE *cts = SSL_CTX_get_cert_store(ctx.native_handle());
@@ -68,15 +67,14 @@ fi
source /opt/rh/gcc-toolset-11/enable
export PATH=/usr/local/bin:$PATH
export CC='/usr/lib64/ccache/gcc' &&
export CXX='/usr/lib64/ccache/g++' &&
echo "-- Build Rippled --" &&
pwd &&
export CXX='/usr/lib64/ccache/g++' &&
echo "-- Build Rippled --" &&
pwd &&
echo "MOVING TO [ build-core.sh ]"
echo "MOVING TO [ build-core.sh ]";
printenv > .env.temp;
cat .env.temp | grep '=' | sed s/\\\(^[^=]\\+=\\\)/\\1\\\"/g|sed s/\$/\\\"/g > .env;
rm .env.temp;
printenv >.env.temp
cat .env.temp | grep '=' | sed s/\\\(^[^=]\\+=\\\)/\\1\\\"/g | sed s/\$/\\\"/g >.env
rm .env.temp
echo "Persisting ENV:"
cat .env

View File

@@ -62,8 +62,7 @@
"3C43D9A973AA4443EF3FC38E42DD306160FBFFDAB901CD8BAA15D09F2597EB87",
"0285B7E5E08E1A8E4C15636F0591D87F73CB6A7B6452A932AD72BBC8E5D1CBE3",
"6E739F4F8B07BED29FC9FF440DA3C301CD14A180DF45819F658FEC2F7DE31427",
"36799EA497B1369B170805C078AEFE6188345F9B3E324C21E9CA3FF574E3C3D6",
"DD4F86291F142A20761B32B4D0CE4291F86CA33F0B46F0D04171482FBA52E536"
"36799EA497B1369B170805C078AEFE6188345F9B3E324C21E9CA3FF574E3C3D6"
],
"Flags": 0,
"LedgerEntryType": "Amendments",

View File

@@ -1,4 +1,4 @@
# standalone: ./xahaud -a --ledgerfile cfg/genesis.json --conf cfg/xahaud-standalone.cfg
# standalone: ./xahaud -a --ledgerfile config/genesis.json --conf config/xahaud-standalone.cfg
[server]
port_rpc_admin_local
port_ws_public
@@ -154,4 +154,3 @@ D686F2538F410C9D0D856788E98E3579595DAF7B38D38887F81ECAC934B06040 HooksUpdate1
EDB4EE4C524E16BDD91D9A529332DED08DCAAA51CC6DC897ACFA1A0ED131C5B6 fix240819
8063140E9260799D6716756B891CEC3E7006C4E4F277AB84670663A88F94B9C4 fixPageCap
88693F108C3CD8A967F3F4253A32DEF5E35F9406ACD2A11B88B11D90865763A9 fix240911
DD4F86291F142A20761B32B4D0CE4291F86CA33F0B46F0D04171482FBA52E536 JsHooks

View File

@@ -95,6 +95,9 @@
# - replace both functions setup_target_for_coverage_gcovr_* with a single setup_target_for_coverage_gcovr
# - add support for all gcovr output formats
#
# 2024-04-03, Bronek Kozicki
# - add support for output formats: jacoco, clover, lcov
#
# USAGE:
#
# 1. Copy this file into your cmake modules path.
@@ -256,10 +259,10 @@ endif()
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# FORMAT "cobertura" # Output format, one of:
# # xml cobertura sonarqube json-summary
# # json-details coveralls csv txt
# # html-single html-nested html-details
# # (xml is an alias to cobertura;
# # xml cobertura sonarqube jacoco clover
# # json-summary json-details coveralls csv
# # txt html-single html-nested html-details
# # lcov (xml is an alias to cobertura;
# # if no format is set, defaults to xml)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
@@ -308,6 +311,8 @@ function(setup_target_for_coverage_gcovr)
set(GCOVR_OUTPUT_FILE ${Coverage_NAME}.txt)
elseif(Coverage_FORMAT STREQUAL "csv")
set(GCOVR_OUTPUT_FILE ${Coverage_NAME}.csv)
elseif(Coverage_FORMAT STREQUAL "lcov")
set(GCOVR_OUTPUT_FILE ${Coverage_NAME}.lcov)
else()
set(GCOVR_OUTPUT_FILE ${Coverage_NAME}.xml)
endif()
@@ -320,6 +325,14 @@ function(setup_target_for_coverage_gcovr)
set(Coverage_FORMAT cobertura) # overwrite xml
elseif(Coverage_FORMAT STREQUAL "sonarqube")
list(APPEND GCOVR_ADDITIONAL_ARGS --sonarqube "${GCOVR_OUTPUT_FILE}" )
elseif(Coverage_FORMAT STREQUAL "jacoco")
list(APPEND GCOVR_ADDITIONAL_ARGS --jacoco "${GCOVR_OUTPUT_FILE}" )
list(APPEND GCOVR_ADDITIONAL_ARGS --jacoco-pretty )
elseif(Coverage_FORMAT STREQUAL "clover")
list(APPEND GCOVR_ADDITIONAL_ARGS --clover "${GCOVR_OUTPUT_FILE}" )
list(APPEND GCOVR_ADDITIONAL_ARGS --clover-pretty )
elseif(Coverage_FORMAT STREQUAL "lcov")
list(APPEND GCOVR_ADDITIONAL_ARGS --lcov "${GCOVR_OUTPUT_FILE}" )
elseif(Coverage_FORMAT STREQUAL "json-summary")
list(APPEND GCOVR_ADDITIONAL_ARGS --json-summary "${GCOVR_OUTPUT_FILE}" )
list(APPEND GCOVR_ADDITIONAL_ARGS --json-summary-pretty)

View File

@@ -0,0 +1,928 @@
#!/usr/bin/env python3
"""Project the sealed QuickJS bundle into one C++ translation unit."""
from __future__ import annotations
import argparse
import hashlib
import json
import re
import sys
from pathlib import Path
from typing import Any
CHUNK = 64
CMAKE_SET = re.compile(r'^set\(XAHAU_QUICKJS_([A-Z0-9_]+) "([^"]*)"\)\s*$')
WASM_VALTYPE = {"i32", "i64"}
WASM_BYTE = {"i32": "0x7f", "i64": "0x7e"}
WASM_STACK_BYTES = 131072
WASM_PAGE_BYTES = 65536
PROVIDER_MEMORY_MINIMUM_PAGES = 7
PROVIDER_MEMORY_MAXIMUM_PAGES = 512
PROVIDER_MEMORY_MAX_BYTES = PROVIDER_MEMORY_MAXIMUM_PAGES * WASM_PAGE_BYTES
SEALED_MANIFEST_SHA256 = (
"1673b27d21063957cece9095cceb7e2dbfa53b6f49a5b7f824a486d7d90b2391"
)
SEALED_PROVIDER_SHA256 = (
"f60ab333acb547e50d7d087960839bd11833cb133d496af98e0b72a1bb3b84df"
)
SEALED_PROVIDER_SIZE = 1116638
SEALED_NATIVE_ABI_SHA256 = (
"328ec938dcad875f3bdf25b8d779dd77f3571589818ca9271cb98c64c7018f99"
)
SEALED_BYTECODE_ABI_ID = (
"75ea54f357d397c4b33899e495bb385dad975a43b1c4a7a2cead30474327d33e"
)
SEALED_RUNTIME_PROFILE_ID = (
"29acf12e170436e5ccadfbc118d02a60909f86a88262106652b2f71994b0ea25"
)
SEALED_WASMTIME_VERSION = "47.0.3"
SEALED_HOOK_API_VERSION = 1
SEALED_HOST_ADAPTER_POLICY = "xahau-raw-hook-host-v1"
SEALED_BROAD_DECLARATION_SHA256 = (
"649079b831bc68eff0f440644649a505addafb08a3f5174e4031c8de6aedffa0"
)
SEALED_EXACT_V1_DECLARATION_SHA256 = (
"20881232f0602e35de0fcbb8b5a3c1511606392749a0f79daebb386204f4f4da"
)
SEALED_SURFACE_SHA256 = (
"96a9218bf38abbf1bc9f2839408700d03572e0b065d625263f70477bc32597ac"
)
SEALED_API_ARTIFACT_MANIFEST_SHA256 = (
"95babf622bf5edff76fdcbd9746bad8f1f8b275a3d4bacdc320ff4deae543eba"
)
API_ARTIFACT_SCHEMA = "jshookz.api-artifacts.v1"
SEALED_API_ARTIFACTS = {
"python/jshookz/src/jshookz/types/hooks-api.d.ts": (
"broad_declaration",
"hooks-api.d.ts",
SEALED_BROAD_DECLARATION_SHA256,
),
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts": (
"exact_v1_declaration",
"xahau-quickjs-v1.d.ts",
SEALED_EXACT_V1_DECLARATION_SHA256,
),
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.surface.json": (
"selected_surface",
"xahau-quickjs-v1.surface.json",
SEALED_SURFACE_SHA256,
),
}
SEALED_LIMITS = {
"host_work_base_per_call": 1,
"host_work_budget": 1000000,
"host_work_meter": "base-plus-addressed-byte-v1",
"host_work_per_addressed_byte": 1,
"quickjs_heap_bytes": 16777216,
"quickjs_stack_bytes": 65536,
"serialized_object_max_bytes": 1048576,
"serialized_object_max_fields": 32768,
"serialized_object_max_scopes": 32769,
"serialized_object_max_depth": 10,
"wasmtime_fuel_per_initialization": 5000000,
"wasmtime_fuel_per_invocation": 50000000,
}
SEALED_HOST_WORK_ADDRESSED_LENGTH_INDICES = {
"accept": [1],
"emit": [1, 3],
"hook_account": [1],
"ledger_last_hash": [1],
"prepare": [1, 3],
"rollback": [1],
"state": [1, 3],
"state_set": [1, 3],
"trace": [1, 3],
}
SEALED_ENGINE_CONFIGURATION = {
"consume_fuel": True,
"cranelift_nan_canonicalization": True,
"wasi": False,
"wasm_memory64": False,
"wasm_multi_memory": False,
"wasm_relaxed_simd": False,
"wasm_tail_call": False,
"wasm_threads": False,
}
IMPORT_KEYS = frozenset({"module", "name", "params", "results"})
FUNCTION_EXPORT_KEYS = frozenset({"kind", "name", "params", "results"})
MEMORY_EXPORT_KEYS = frozenset(
{"kind", "name", "minimum_pages", "maximum_pages", "memory64", "shared"}
)
SURFACE_KEYS = frozenset(
{"declaration", "declaration_sha256", "manifest", "schema", "sha256"}
)
NESTED_SURFACE_KEYS = frozenset({"declaration", "manifest", "schema"})
def _import_row(name: str, params: list[str], results: list[str]) -> dict[str, Any]:
return {"module": "env", "name": name, "params": params, "results": results}
def _function_export(name: str, params: list[str], results: list[str]) -> dict[str, Any]:
return {"kind": "function", "name": name, "params": params, "results": results}
SEALED_IMPORTS = [
_import_row("accept", ["i32", "i32", "i64"], ["i64"]),
_import_row("emit", ["i32", "i32", "i32", "i32"], ["i64"]),
_import_row("etxn_reserve", ["i32"], ["i64"]),
_import_row("hook_account", ["i32", "i32"], ["i64"]),
_import_row("ledger_last_hash", ["i32", "i32"], ["i64"]),
_import_row("ledger_last_time", [], ["i64"]),
_import_row("ledger_seq", [], ["i64"]),
_import_row("otxn_type", [], ["i64"]),
_import_row("prepare", ["i32", "i32", "i32", "i32"], ["i64"]),
_import_row("rollback", ["i32", "i32", "i64"], ["i64"]),
_import_row("state", ["i32", "i32", "i32", "i32"], ["i64"]),
_import_row("state_set", ["i32", "i32", "i32", "i32"], ["i64"]),
_import_row("trace", ["i32", "i32", "i32", "i32", "i32"], ["i64"]),
]
SEALED_EXPORTS = [
_function_export("_initialize", [], []),
_function_export("free", ["i32"], []),
_function_export("malloc", ["i32"], ["i32"]),
{
"kind": "memory",
"maximum_pages": 512,
"memory64": False,
"minimum_pages": 7,
"name": "memory",
"shared": False,
},
_function_export("qjs_cbak", ["i32", "i32", "i32"], ["i32"]),
_function_export("qjs_compile", ["i32", "i32"], ["i32"]),
_function_export("qjs_compile_module", ["i32", "i32"], ["i32"]),
_function_export("qjs_destroy", [], []),
_function_export("qjs_enable_coverage", ["i32"], []),
_function_export("qjs_eval", ["i32", "i32"], ["i32"]),
_function_export("qjs_eval_bytecode", ["i32", "i32"], ["i32"]),
_function_export("qjs_eval_module", ["i32", "i32"], ["i32"]),
_function_export("qjs_get_bytecode_len", [], ["i32"]),
_function_export("qjs_get_bytecode_ptr", [], ["i32"]),
_function_export("qjs_get_result_len", [], ["i32"]),
_function_export("qjs_get_result_ptr", [], ["i32"]),
_function_export("qjs_hook", ["i32", "i32", "i32"], ["i32"]),
_function_export("qjs_init", [], []),
_function_export("qjs_set_max_stack_size", ["i32"], []),
_function_export("qjs_set_memory_limit", ["i32"], []),
_function_export("qjs_set_seed", ["i32"], []),
_function_export("qjs_validate_hook_module", ["i32", "i32"], ["i32"]),
]
class LockError(ValueError):
"""A sealed QuickJS lock identity failed a fail-closed check."""
def quote(value: str) -> str:
if not isinstance(value, str) or any(c in value for c in "\n\r"):
raise LockError(f"invalid QuickJS policy string: {value!r}")
return json.dumps(value)
def parse_cmake(path: Path) -> dict[str, str]:
values: dict[str, str] = {}
for line in path.read_text().splitlines():
match = CMAKE_SET.match(line)
if match:
values[match.group(1)] = match.group(2)
return values
def hex_bytes(value: str) -> str:
if len(value) != 64 or any(c not in "0123456789abcdefABCDEF" for c in value):
raise LockError(f"QuickJS identity must be 32 hexadecimal bytes: {value!r}")
return ", ".join(f"0x{value[i:i + 2]}" for i in range(0, 64, 2))
def require(cmake: dict[str, str], key: str) -> str:
if key not in cmake:
raise LockError(f"QuickJS cmake projection missing {key}")
return cmake[key]
def require_int(cmake: dict[str, str], key: str) -> int:
return int(require(cmake, key))
def require_hex(value: object, label: str) -> str:
text = str(value).lower()
if len(text) != 64 or any(c not in "0123456789abcdef" for c in text):
raise LockError(f"{label} is not a SHA-256 hex digest: {value!r}")
return text
def require_sealed(actual: object, expected: object, label: str) -> None:
if actual != expected:
raise LockError(
f"{label} disagrees with the sealed F0 table: "
f"{actual!r} != {expected!r}"
)
def validate_api_artifacts(bundle: Path) -> dict[str, str]:
manifest_path = bundle / "api-artifacts.json"
manifest = json.loads(manifest_path.read_text())
if not isinstance(manifest, dict) or set(manifest) != {"artifacts", "schema"}:
raise LockError("API artifact manifest has extra or missing keys")
if manifest.get("schema") != API_ARTIFACT_SCHEMA:
raise LockError("API artifact manifest schema disagrees with the sealed F0 table")
artifacts = manifest.get("artifacts")
if not isinstance(artifacts, dict) or set(artifacts) != set(SEALED_API_ARTIFACTS):
raise LockError("API artifact manifest file set disagrees with the sealed F0 table")
identities: dict[str, str] = {}
for source_path, artifact in SEALED_API_ARTIFACTS.items():
identity_name, local_name, sealed_digest = artifact
actual_digest = hashlib.sha256((bundle / local_name).read_bytes()).hexdigest()
declared_digest = require_hex(
artifacts.get(source_path), f"API artifact {source_path} SHA-256"
)
if actual_digest != declared_digest:
raise LockError(
f"API artifact {source_path} disagrees with its manifest digest"
)
require_sealed(actual_digest, sealed_digest, f"API artifact {source_path}")
identities[identity_name] = actual_digest
actual_manifest = hashlib.sha256(manifest_path.read_bytes()).hexdigest()
require_sealed(
actual_manifest,
SEALED_API_ARTIFACT_MANIFEST_SHA256,
"API artifact manifest SHA-256",
)
identities["api_artifact_manifest"] = actual_manifest
return identities
def cpp_bool(value: object) -> str:
if value is True:
return "true"
if value is False:
return "false"
raise LockError(f"expected JSON boolean, got {value!r}")
def join_types(values: object, label: str) -> str:
if not isinstance(values, list) or not all(isinstance(item, str) for item in values):
raise LockError(f"{label} is not a list of wasm value types: {values!r}")
for item in values:
if item not in WASM_VALTYPE:
raise LockError(f"{label} has unsupported wasm value type {item!r}")
return ",".join(values)
def wasm_signature(params: object, results: object, label: str) -> list[str]:
join_types(params, f"{label} params")
join_types(results, f"{label} results")
assert isinstance(params, list) and isinstance(results, list)
return [WASM_BYTE[item] for item in results] + [WASM_BYTE[item] for item in params]
def embed_body(data: bytes | None) -> str:
if data is None:
return (
"std::span<std::uint8_t const>\n"
"embeddedQuickJSProvider()\n"
"{\n"
" return {};\n"
"}\n"
)
lines = []
for offset in range(0, len(data), CHUNK):
chunk = data[offset : offset + CHUNK]
lines.append(' "' + "".join(f"\\x{b:02x}" for b in chunk) + '"')
return (
"namespace {\n"
"\n"
"constexpr char const sealedProvider[] =\n"
+ "\n".join(lines)
+ ";\n"
"\n"
"static_assert(\n"
f" sizeof(sealedProvider) - 1 == {len(data)},\n"
' "embedded provider size disagrees with its manifest");\n'
"\n"
"} // namespace\n"
"\n"
"std::span<std::uint8_t const>\n"
"embeddedQuickJSProvider()\n"
"{\n"
" return {\n"
" reinterpret_cast<std::uint8_t const*>(sealedProvider),\n"
" sizeof(sealedProvider) - 1};\n"
"}\n"
)
def format_export_row(item: dict[str, Any]) -> str:
kind = item["kind"]
name = item["name"]
if kind == "memory":
fields = (
quote(kind),
quote(name),
quote(""),
quote(""),
f"{int(item['minimum_pages'])}U",
f"{int(item['maximum_pages'])}U",
cpp_bool(item["memory64"]),
cpp_bool(item["shared"]),
)
elif kind == "function":
fields = (
quote(kind),
quote(name),
quote(join_types(item["params"], f"export {name} params")),
quote(join_types(item["results"], f"export {name} results")),
"0U",
"0U",
"false",
"false",
)
else:
raise LockError(f"unsupported provider export kind: {kind!r}")
return "{" + ", ".join(fields) + "}"
def require_typed_exports(exports: object, label: str) -> list[dict[str, Any]]:
if not isinstance(exports, list) or not all(isinstance(item, dict) for item in exports):
raise LockError(f"{label} must be a list of typed export rows")
names: list[str] = []
memory_rows: list[dict[str, Any]] = []
for item in exports:
kind = item.get("kind")
name = item.get("name")
if not isinstance(kind, str) or not isinstance(name, str) or not name:
raise LockError(f"{label} row is missing kind/name: {item!r}")
if kind == "function":
if set(item) != FUNCTION_EXPORT_KEYS:
raise LockError(f"{label} function {name} has extra or missing keys")
join_types(item.get("params"), f"{label} {name} params")
join_types(item.get("results"), f"{label} {name} results")
elif kind == "memory":
if set(item) != MEMORY_EXPORT_KEYS:
raise LockError(f"{label} memory row has extra or missing keys")
for key in ("minimum_pages", "maximum_pages"):
if not isinstance(item.get(key), int):
raise LockError(f"{label} memory row missing integer {key}")
if item.get("memory64") is not False or item.get("shared") is not False:
raise LockError(
f"{label} memory row is not memory64=false / shared=false"
)
memory_rows.append(item)
else:
raise LockError(f"{label} has unsupported export kind {kind!r}")
names.append(name)
if len(names) != len(set(names)):
raise LockError(f"{label} contains duplicate export names")
if names != sorted(names):
raise LockError(f"{label} must be normalized by export name")
if len(memory_rows) != 1 or memory_rows[0].get("name") != "memory":
raise LockError(f"{label} must export exactly one memory named memory")
return exports
def require_typed_imports(imports: object, label: str) -> list[dict[str, Any]]:
if not isinstance(imports, list) or not all(isinstance(item, dict) for item in imports):
raise LockError(f"{label} must be a list of typed import rows")
names: list[str] = []
for item in imports:
module = item.get("module")
name = item.get("name")
if not isinstance(module, str) or not isinstance(name, str) or not name:
raise LockError(f"{label} row is missing module/name: {item!r}")
if set(item) != IMPORT_KEYS:
raise LockError(f"{label} {name} has extra or missing keys")
wasm_signature(item.get("params"), item.get("results"), f"{label} {name}")
names.append(name)
if len(names) != len(set(names)):
raise LockError(f"{label} contains duplicate import names")
return imports
def require_build(build: object, label: str) -> dict[str, Any]:
if not isinstance(build, dict):
raise LockError(f"{label} is missing build metadata")
stack = build.get("wasm_stack_bytes")
memory = build.get("wasm_memory_max_bytes")
if stack != WASM_STACK_BYTES:
raise LockError(
f"{label} wasm_stack_bytes is {stack!r}, expected {WASM_STACK_BYTES}"
)
if memory != PROVIDER_MEMORY_MAX_BYTES:
raise LockError(
f"{label} wasm_memory_max_bytes is {memory!r}, "
f"expected {PROVIDER_MEMORY_MAX_BYTES}"
)
return build
def cross_compare_int(cmake: dict[str, str], key: str, actual: object, label: str) -> int:
expected = require_int(cmake, key)
if actual != expected:
raise LockError(
f"{label} disagrees between JSON ({actual!r}) and CMake {key} ({expected!r})"
)
return expected
def cross_compare_str(cmake: dict[str, str], key: str, actual: object, label: str) -> str:
expected = require(cmake, key)
if str(actual) != expected:
raise LockError(
f"{label} disagrees between JSON ({actual!r}) and CMake {key} ({expected!r})"
)
return expected
def validate_lock(
profile: dict[str, Any],
native: dict[str, Any],
cmake: dict[str, str],
wasmtime_version: str,
api_identities: dict[str, str],
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], dict[str, Any], str, str, int]:
schema = require(cmake, "MANIFEST_SCHEMA")
if schema != "xahau.quickjs.runtime-profile-lock.v1" or profile.get("schema") != schema:
raise LockError(f"unsupported QuickJS provider manifest schema: {schema}")
expected_sha = require_hex(require(cmake, "PROVIDER_SHA256"), "CMake provider SHA-256")
json_sha = require_hex(profile.get("provider", {}).get("sha256"), "JSON provider SHA-256")
if json_sha != expected_sha:
raise LockError("provider sha256 disagrees between JSON lock and cmake")
require_sealed(expected_sha, SEALED_PROVIDER_SHA256, "provider SHA-256")
expected_size = require_int(cmake, "PROVIDER_SIZE")
json_size = profile.get("provider", {}).get("size")
if json_size != expected_size:
raise LockError("provider size disagrees between JSON lock and cmake")
require_sealed(expected_size, SEALED_PROVIDER_SIZE, "provider size")
bytecode = require_hex(require(cmake, "BYTECODE_ABI_ID"), "CMake bytecode ABI")
if require_hex(profile.get("bytecode_abi_id"), "JSON bytecode ABI") != bytecode:
raise LockError("bytecode ABI disagrees between JSON lock and cmake")
require_sealed(bytecode, SEALED_BYTECODE_ABI_ID, "bytecode ABI")
runtime_profile = require_hex(
require(cmake, "RUNTIME_PROFILE_ID"), "CMake runtime-profile ID"
)
if require_hex(profile.get("runtime_profile_id"), "JSON runtime-profile ID") != runtime_profile:
raise LockError("runtime-profile ID disagrees between JSON lock and cmake")
require_sealed(
runtime_profile, SEALED_RUNTIME_PROFILE_ID, "runtime-profile ID"
)
pinned_wasmtime = require(cmake, "WASMTIME_VERSION")
engine = profile.get("source", {}).get("engine", {})
engine_version = engine.get("version")
if wasmtime_version != pinned_wasmtime or engine_version != pinned_wasmtime:
raise LockError(
f"QuickJS provider requires Wasmtime {pinned_wasmtime}, "
f"but CMake resolved {wasmtime_version} and JSON has {engine_version!r}"
)
require_sealed(pinned_wasmtime, SEALED_WASMTIME_VERSION, "Wasmtime version")
require_sealed(
engine.get("configuration"),
SEALED_ENGINE_CONFIGURATION,
"Wasmtime engine configuration",
)
limits = profile.get("source", {}).get("limits")
if not isinstance(limits, dict):
raise LockError("JSON runtime-profile source is missing limits")
cross_compare_int(
cmake, "SERIALIZED_OBJECT_MAX_BYTES", limits.get("serialized_object_max_bytes"),
"serialized_object_max_bytes",
)
cross_compare_int(
cmake, "SERIALIZED_OBJECT_MAX_FIELDS", limits.get("serialized_object_max_fields"),
"serialized_object_max_fields",
)
cross_compare_int(
cmake, "SERIALIZED_OBJECT_MAX_SCOPES", limits.get("serialized_object_max_scopes"),
"serialized_object_max_scopes",
)
cross_compare_int(
cmake, "SERIALIZED_OBJECT_MAX_DEPTH", limits.get("serialized_object_max_depth"),
"serialized_object_max_depth",
)
for key, expected in SEALED_LIMITS.items():
require_sealed(limits.get(key), expected, key)
require_sealed(
limits.get("host_work_addressed_length_indices"),
SEALED_HOST_WORK_ADDRESSED_LENGTH_INDICES,
"host_work_addressed_length_indices",
)
cross_compare_int(cmake, "HEAP_BYTES", limits.get("quickjs_heap_bytes"), "quickjs_heap_bytes")
cross_compare_int(cmake, "STACK_BYTES", limits.get("quickjs_stack_bytes"), "quickjs_stack_bytes")
cross_compare_int(
cmake, "INITIALIZATION_FUEL", limits.get("wasmtime_fuel_per_initialization"),
"initialization fuel",
)
cross_compare_int(
cmake, "INVOCATION_FUEL", limits.get("wasmtime_fuel_per_invocation"),
"invocation fuel",
)
cross_compare_int(cmake, "HOST_WORK_BUDGET", limits.get("host_work_budget"), "host_work_budget")
cross_compare_int(
cmake, "HOST_WORK_BASE_PER_CALL", limits.get("host_work_base_per_call"),
"host_work_base_per_call",
)
cross_compare_int(
cmake, "HOST_WORK_PER_ADDRESSED_BYTE", limits.get("host_work_per_addressed_byte"),
"host_work_per_addressed_byte",
)
cross_compare_str(cmake, "HOST_WORK_METER", limits.get("host_work_meter"), "host_work_meter")
host_adapter_policy = cross_compare_str(
cmake, "HOST_ADAPTER_POLICY",
profile.get("source", {}).get("execution", {}).get("host_adapter_policy"),
"host_adapter_policy",
)
require_sealed(
host_adapter_policy, SEALED_HOST_ADAPTER_POLICY, "host_adapter_policy"
)
hook_api_version = cross_compare_int(
cmake, "HOOK_API_VERSION",
profile.get("source", {}).get("artifact", {}).get("hook_api_version"),
"hook_api_version",
)
require_sealed(hook_api_version, SEALED_HOOK_API_VERSION, "hook_api_version")
provider = profile.get("provider")
source_provider = profile.get("source", {}).get("provider")
if not isinstance(provider, dict) or not isinstance(source_provider, dict):
raise LockError("JSON lock is missing provider or source.provider")
require_build(provider.get("build"), "provider.build")
require_build(source_provider.get("build"), "source.provider.build")
require_sealed(
source_provider.get("forbidden_import_modules"),
["wasi_snapshot_preview1"],
"forbidden provider import modules",
)
expected_import_count = require_int(cmake, "PROVIDER_IMPORT_COUNT")
provider_imports = require_typed_imports(provider.get("imports"), "provider.imports")
source_imports = require_typed_imports(
source_provider.get("imports"), "source.provider.imports"
)
native_imports = native.get("selected")
if not isinstance(native_imports, list):
raise LockError("native ABI snapshot is missing selected imports")
if provider_imports != source_imports:
raise LockError("provider import signatures disagree between JSON copies")
if provider_imports != SEALED_IMPORTS:
raise LockError(
"provider import signatures disagree with the sealed F0 table"
)
if (
len(provider_imports) != expected_import_count
or len(native_imports) != expected_import_count
):
raise LockError("QuickJS provider/native ABI import counts differ")
provider_names = [item["name"] for item in provider_imports]
native_names = [item["name"] for item in native_imports]
if len(set(provider_names)) != expected_import_count or set(provider_names) != set(
native_names
):
raise LockError("QuickJS provider/native ABI import sets differ")
native_by_name = {item["name"]: item for item in native_imports}
for item in provider_imports:
native_item = native_by_name[item["name"]]
expected_sig = wasm_signature(
item["params"], item["results"], f"import {item['name']}"
)
if native_item.get("wasm_signature") != expected_sig:
raise LockError(
f"import {item['name']} wasm signature disagrees between JSON and native ABI"
)
expected_export_count = require_int(cmake, "PROVIDER_EXPORT_COUNT")
provider_exports = require_typed_exports(provider.get("exports"), "provider.exports")
allowed_exports = require_typed_exports(
source_provider.get("allowed_exports"), "source.provider.allowed_exports"
)
if len(provider_exports) != expected_export_count:
raise LockError("QuickJS provider export count disagrees with its CMake lock")
if provider_exports != allowed_exports:
raise LockError("provider export signatures disagree between JSON copies")
memory = next(item for item in provider_exports if item["kind"] == "memory")
if (
int(memory["minimum_pages"]) != PROVIDER_MEMORY_MINIMUM_PAGES
or int(memory["maximum_pages"]) != PROVIDER_MEMORY_MAXIMUM_PAGES
or memory["memory64"] is not False
or memory["shared"] is not False
):
raise LockError(
"QuickJS provider memory shape is not min 7 / max 512 / "
"memory64=false / shared=false"
)
if provider_exports != SEALED_EXPORTS:
raise LockError(
"provider export signatures disagree with the sealed F0 table"
)
surface = profile.get("javascript_surface")
nested_surface = profile.get("source", {}).get("javascript_surface")
if not isinstance(surface, dict) or not isinstance(nested_surface, dict):
raise LockError("JSON lock is missing javascript_surface")
if set(surface) != SURFACE_KEYS or set(nested_surface) != NESTED_SURFACE_KEYS:
raise LockError("javascript_surface has extra or missing keys")
declaration_sha = require_hex(
surface.get("declaration_sha256"), "javascript surface declaration SHA-256"
)
surface_sha = require_hex(surface.get("sha256"), "javascript surface SHA-256")
require_sealed(
declaration_sha,
api_identities["exact_v1_declaration"],
"exact-v1 declaration SHA-256",
)
require_sealed(
surface_sha, api_identities["selected_surface"], "selected surface SHA-256"
)
for key in ("declaration", "manifest", "schema"):
if surface.get(key) != nested_surface.get(key):
raise LockError(f"javascript_surface.{key} disagrees between JSON copies")
return (
provider_imports,
provider_exports,
native_imports,
declaration_sha,
surface_sha,
expected_size,
)
def render_source(
*,
provenance: str,
cmake: dict[str, str],
native: dict[str, Any],
provider_imports: list[dict[str, Any]],
provider_exports: list[dict[str, Any]],
native_imports: list[dict[str, Any]],
declaration_sha: str,
surface_sha: str,
broad_declaration_sha: str,
api_artifact_manifest_sha: str,
manifest_sha: str,
expected_sha: str,
expected_size: int,
wasm_bytes: bytes | None,
actual_native: str,
) -> str:
provider_names = [item["name"] for item in provider_imports]
provider_rows = ",\n ".join(
"{"
+ ", ".join(
(
quote(item["module"]),
quote(item["name"]),
quote(join_types(item["params"], f"import {item['name']} params")),
quote(join_types(item["results"], f"import {item['name']} results")),
)
)
+ "}"
for item in provider_imports
)
native_rows = ",\n ".join(
"{"
+ ", ".join(
(
quote(item["name"]),
quote(item["return_type"]),
quote(",".join(item["param_types"])),
quote(item["amendment"] or ""),
)
)
+ "}"
for item in native_imports
)
export_rows = ",\n ".join(format_export_row(item) for item in provider_exports)
source = native["source"]
memory = next(item for item in provider_exports if item["kind"] == "memory")
return f"""// Generated by cmake/GenerateQuickJSProviderBundle.py; do not edit.
// Source: {provenance}
#include <xrpld/app/hook/QuickJSHookRuntime.h>
#include <xrpld/app/hook/detail/QuickJSProviderProfile.h>
#include <array>
#include <cstdint>
#include <span>
#include <string_view>
namespace hook::artifact::generated {{
std::array<std::uint8_t, 32> const providerSHA256 = {{
{hex_bytes(expected_sha)}}};
std::array<std::uint8_t, 32> const bytecodeABI = {{
{hex_bytes(require(cmake, "BYTECODE_ABI_ID"))}}};
std::array<std::uint8_t, 32> const runtimeProfile = {{
{hex_bytes(require(cmake, "RUNTIME_PROFILE_ID"))}}};
std::string_view const providerManifestSHA256 = {quote(manifest_sha)};
std::size_t const providerSize = {expected_size};
std::uint16_t const hookApiVersion = {require(cmake, "HOOK_API_VERSION")};
std::uint64_t const initializationFuel = {require(cmake, "INITIALIZATION_FUEL")}ULL;
std::uint64_t const invocationFuel = {require(cmake, "INVOCATION_FUEL")}ULL;
std::string_view const hostWorkMeter = {quote(require(cmake, "HOST_WORK_METER"))};
std::uint64_t const hostWorkBudget = {require(cmake, "HOST_WORK_BUDGET")}ULL;
std::uint64_t const hostWorkBasePerCall = {require(cmake, "HOST_WORK_BASE_PER_CALL")}ULL;
std::uint64_t const hostWorkPerAddressedByte =
{require(cmake, "HOST_WORK_PER_ADDRESSED_BYTE")}ULL;
std::string_view const hostAdapterPolicy = {quote(require(cmake, "HOST_ADAPTER_POLICY"))};
std::uint32_t const heapBytes = {require(cmake, "HEAP_BYTES")}U;
std::uint32_t const stackBytes = {require(cmake, "STACK_BYTES")}U;
std::uint32_t const wasmStackBytes = {WASM_STACK_BYTES}U;
std::uint32_t const serializedObjectMaxBytes =
{require(cmake, "SERIALIZED_OBJECT_MAX_BYTES")}U;
std::uint32_t const serializedObjectMaxFields =
{require(cmake, "SERIALIZED_OBJECT_MAX_FIELDS")}U;
std::uint32_t const serializedObjectMaxScopes =
{require(cmake, "SERIALIZED_OBJECT_MAX_SCOPES")}U;
std::uint32_t const serializedObjectMaxDepth =
{require(cmake, "SERIALIZED_OBJECT_MAX_DEPTH")}U;
std::uint32_t const providerMemoryMinimumPages = {int(memory["minimum_pages"])}U;
std::uint32_t const providerMemoryMaximumPages = {int(memory["maximum_pages"])}U;
bool const providerMemory64 = false;
bool const providerMemoryShared = false;
std::string_view const javascriptBroadDeclarationSHA256 =
{quote(broad_declaration_sha)};
std::string_view const javascriptExactV1DeclarationSHA256 =
{quote(declaration_sha)};
std::string_view const javascriptSurfaceSHA256 = {quote(surface_sha)};
std::string_view const javascriptAPIArtifactManifestSHA256 =
{quote(api_artifact_manifest_sha)};
namespace {{
std::string_view const providerImportNames[] = {{
{", ".join(quote(name) for name in provider_names)}}};
std::string_view const providerExportNames[] = {{
{", ".join(quote(item["name"]) for item in provider_exports)}}};
ProviderImportSignature const providerImportSignatureData[] = {{
{provider_rows}}};
ProviderExportSignature const providerExportSignatureData[] = {{
{export_rows}}};
NativeImportSignature const nativeImportSignatureData[] = {{
{native_rows}}};
}} // namespace
std::span<std::string_view const> const providerImports{{providerImportNames}};
std::span<std::string_view const> const providerExports{{providerExportNames}};
std::span<ProviderImportSignature const> const providerImportSignatures{{
providerImportSignatureData}};
std::span<ProviderExportSignature const> const providerExportSignatures{{
providerExportSignatureData}};
std::span<NativeImportSignature const> const nativeImportSignatures{{
nativeImportSignatureData}};
std::string_view const nativeABISourceRepository = {quote(source["repository"])};
std::string_view const nativeABISourceCommit = {quote(source["commit"])};
std::string_view const nativeABISourcePath = {quote(source["path"])};
std::string_view const nativeABISHA256 = {quote(actual_native)};
std::size_t const nativeABICatalogueCount = {source["macro_function_count"]};
}} // namespace hook::artifact::generated
namespace hook {{
{embed_body(wasm_bytes)}
}} // namespace hook
"""
def project_bundle(bundle: Path, wasmtime_version: str, output: Path) -> int:
profile_path = bundle / "jshookz_provider.manifest.json"
cmake_path = bundle / "jshookz_provider.manifest.cmake"
native_path = bundle / "jshookz_provider.native-abi.json"
api_manifest_path = bundle / "api-artifacts.json"
api_paths = tuple(
bundle / local_name
for _, local_name, _ in SEALED_API_ARTIFACTS.values()
)
for path in (profile_path, cmake_path, native_path, api_manifest_path, *api_paths):
if not path.is_file():
print(f"missing sealed QuickJS bundle file: {path}", file=sys.stderr)
return 1
cmake = parse_cmake(cmake_path)
actual_manifest = hashlib.sha256(profile_path.read_bytes()).hexdigest()
if actual_manifest != require(cmake, "MANIFEST_SHA256"):
print(
"QuickJS provider JSON manifest does not match its CMake projection",
file=sys.stderr,
)
return 1
actual_native = hashlib.sha256(native_path.read_bytes()).hexdigest()
if actual_native != require(cmake, "NATIVE_ABI_SHA256"):
print(
"QuickJS native ABI snapshot does not match its sealed digest",
file=sys.stderr,
)
return 1
try:
profile = json.loads(profile_path.read_text())
native = json.loads(native_path.read_text())
api_identities = validate_api_artifacts(bundle)
(
provider_imports,
provider_exports,
native_imports,
declaration_sha,
surface_sha,
expected_size,
) = validate_lock(
profile, native, cmake, wasmtime_version, api_identities
)
require_sealed(
actual_manifest, SEALED_MANIFEST_SHA256, "provider manifest SHA-256"
)
require_sealed(
actual_native, SEALED_NATIVE_ABI_SHA256, "native ABI SHA-256"
)
except LockError as error:
print(str(error), file=sys.stderr)
return 1
wasm_name = require(cmake, "PROVIDER_FILE")
wasm_path = bundle / wasm_name
expected_sha = require(cmake, "PROVIDER_SHA256").lower()
fingerprint = hashlib.sha256()
for path in (
profile_path,
cmake_path,
native_path,
api_manifest_path,
*api_paths,
Path(__file__),
):
fingerprint.update(path.read_bytes())
fingerprint.update(wasmtime_version.encode())
wasm_bytes: bytes | None = None
if wasm_path.is_file():
wasm_bytes = wasm_path.read_bytes()
digest = hashlib.sha256(wasm_bytes).hexdigest()
if digest != expected_sha or len(wasm_bytes) != expected_size:
print(
"QuickJS provider WASM does not match its generated manifest",
file=sys.stderr,
)
return 1
fingerprint.update(b"wasm")
fingerprint.update(bytes.fromhex(digest))
provenance = f"{wasm_path} sha256={digest} size={len(wasm_bytes)}"
else:
fingerprint.update(b"absent")
provenance = "no provider binary was present in the bundle directory"
stamp_path = output.with_name(output.name + ".stamp")
stamp = fingerprint.hexdigest()
if output.is_file() and stamp_path.is_file() and stamp_path.read_text() == stamp:
return 0
if wasm_bytes is None:
print(
f"No QuickJS provider WASM at {wasm_path}; nothing is embedded "
"and the daemon will refuse to start",
file=sys.stderr,
)
text = render_source(
provenance=provenance,
cmake=cmake,
native=native,
provider_imports=provider_imports,
provider_exports=provider_exports,
native_imports=native_imports,
declaration_sha=declaration_sha,
surface_sha=surface_sha,
broad_declaration_sha=api_identities["broad_declaration"],
api_artifact_manifest_sha=api_identities["api_artifact_manifest"],
manifest_sha=actual_manifest,
expected_sha=expected_sha,
expected_size=expected_size,
wasm_bytes=wasm_bytes,
actual_native=actual_native,
)
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(text)
stamp_path.write_text(stamp)
return 0
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--bundle-dir", type=Path, required=True)
parser.add_argument("--wasmtime-version", required=True)
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args()
try:
return project_bundle(args.bundle_dir, args.wasmtime_version, args.output)
except LockError as error:
print(str(error), file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,90 @@
set(XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR
"${CMAKE_CURRENT_SOURCE_DIR}/external/quickjs-provider"
CACHE PATH
"Directory containing the sealed QuickJS provider WASM and generated manifests")
get_filename_component(XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}"
ABSOLUTE
BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(XAHAU_QUICKJS_PROVIDER_MANIFEST
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/jshookz_provider.manifest.cmake")
set(XAHAU_QUICKJS_PROVIDER_PROFILE_LOCK
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/jshookz_provider.manifest.json")
set(XAHAU_QUICKJS_NATIVE_ABI
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/jshookz_provider.native-abi.json")
set(XAHAU_QUICKJS_API_ARTIFACT_MANIFEST
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/api-artifacts.json")
set(XAHAU_QUICKJS_BROAD_DECLARATION
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/hooks-api.d.ts")
set(XAHAU_QUICKJS_EXACT_V1_DECLARATION
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/xahau-quickjs-v1.d.ts")
set(XAHAU_QUICKJS_SELECTED_SURFACE
"${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}/xahau-quickjs-v1.surface.json")
foreach(_bundle_file
"${XAHAU_QUICKJS_PROVIDER_MANIFEST}"
"${XAHAU_QUICKJS_PROVIDER_PROFILE_LOCK}"
"${XAHAU_QUICKJS_NATIVE_ABI}"
"${XAHAU_QUICKJS_API_ARTIFACT_MANIFEST}"
"${XAHAU_QUICKJS_BROAD_DECLARATION}"
"${XAHAU_QUICKJS_EXACT_V1_DECLARATION}"
"${XAHAU_QUICKJS_SELECTED_SURFACE}")
if(NOT EXISTS "${_bundle_file}")
message(FATAL_ERROR
"Missing sealed QuickJS provider bundle file: ${_bundle_file}")
endif()
endforeach()
if(NOT DEFINED wasmtime_VERSION_STRING OR wasmtime_VERSION_STRING STREQUAL "")
message(FATAL_ERROR
"QuickJS provider requires an exact Wasmtime version, but the resolved "
"package did not publish one")
endif()
find_program(XAHAU_QUICKJS_PYTHON NAMES python3 python)
if(NOT XAHAU_QUICKJS_PYTHON)
message(FATAL_ERROR
"Python is required to project the sealed QuickJS provider bundle")
endif()
set(XAHAU_QUICKJS_GENERATED_INCLUDE_DIR
"${CMAKE_CURRENT_BINARY_DIR}/generated")
set(XAHAU_QUICKJS_PROVIDER_VALUES_SOURCE
"${XAHAU_QUICKJS_GENERATED_INCLUDE_DIR}/xrpld/app/hook/detail/QuickJSProviderValues.cpp")
set(XAHAU_QUICKJS_BUNDLE_SCRIPT
"${CMAKE_CURRENT_LIST_DIR}/GenerateQuickJSProviderBundle.py")
# The gitignored wasm is not a CMake depend (ninja cannot name a missing
# file). Refresh on every rippled build; the script no-ops when inputs
# including wasm existence/digest are unchanged.
add_custom_target(xahau_quickjs_refresh_provider
COMMAND
"${XAHAU_QUICKJS_PYTHON}"
"${XAHAU_QUICKJS_BUNDLE_SCRIPT}"
--bundle-dir "${XAHAU_QUICKJS_PROVIDER_BUNDLE_DIR}"
--wasmtime-version "${wasmtime_VERSION_STRING}"
--output "${XAHAU_QUICKJS_PROVIDER_VALUES_SOURCE}"
BYPRODUCTS "${XAHAU_QUICKJS_PROVIDER_VALUES_SOURCE}"
DEPENDS
"${XAHAU_QUICKJS_PROVIDER_PROFILE_LOCK}"
"${XAHAU_QUICKJS_PROVIDER_MANIFEST}"
"${XAHAU_QUICKJS_NATIVE_ABI}"
"${XAHAU_QUICKJS_API_ARTIFACT_MANIFEST}"
"${XAHAU_QUICKJS_BROAD_DECLARATION}"
"${XAHAU_QUICKJS_EXACT_V1_DECLARATION}"
"${XAHAU_QUICKJS_SELECTED_SURFACE}"
"${XAHAU_QUICKJS_BUNDLE_SCRIPT}"
COMMENT "Projecting sealed QuickJS provider values"
VERBATIM)
message(STATUS
"QuickJS provider values: ${XAHAU_QUICKJS_PROVIDER_VALUES_SOURCE}")
add_custom_target(xahau_quickjs_lock_mutations
COMMAND
"${XAHAU_QUICKJS_PYTHON}"
"${CMAKE_CURRENT_LIST_DIR}/test_generate_quickjs_provider_bundle.py"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Proving sealed QuickJS lock mutation reds"
VERBATIM)

View File

@@ -58,7 +58,6 @@ target_link_libraries(xrpl.imports.main
OpenSSL::Crypto
Ripple::boost
wasmedge::wasmedge
NIH::quickjs
Ripple::opts
Ripple::syslibs
absl::random_random
@@ -175,18 +174,106 @@ if(xrpld)
"${CMAKE_CURRENT_SOURCE_DIR}/src/xrpld/*.cpp"
)
target_sources(rippled PRIVATE ${sources})
add_dependencies(rippled xahau_quickjs_refresh_provider)
target_sources(rippled PRIVATE "${XAHAU_QUICKJS_PROVIDER_VALUES_SOURCE}")
if(tests)
file(GLOB_RECURSE sources CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/test/*.cpp"
)
if(HOOKS_TEST_ONLY OR DEFINED ENV{HOOKS_TEST_ONLY})
list(FILTER sources EXCLUDE REGEX "_test\\.cpp$")
message(STATUS "HOOKS_TEST_ONLY: excluded *_test.cpp from src/test/")
endif()
target_sources(rippled PRIVATE ${sources})
# External Env tests live next to the producer. Point at a directory of
# *_test.cpp files (typically jshookz/env-tests). hookz build-test-hooks
# must be on PATH. Coverage is not wired here.
if(NOT HOOKS_TEST_DIR AND DEFINED ENV{HOOKS_TEST_DIR})
set(HOOKS_TEST_DIR $ENV{HOOKS_TEST_DIR})
endif()
if(NOT HOOKS_SOURCE_DIR AND DEFINED ENV{HOOKS_SOURCE_DIR})
set(HOOKS_SOURCE_DIR $ENV{HOOKS_SOURCE_DIR})
endif()
if(HOOKS_TEST_DIR AND EXISTS "${HOOKS_TEST_DIR}")
file(GLOB EXTERNAL_HOOK_TESTS CONFIGURE_DEPENDS
"${HOOKS_TEST_DIR}/*_test.cpp"
)
if(EXTERNAL_HOOK_TESTS)
set(_hooks_extra_args "")
set(_hooks_source_deps "")
if(HOOKS_SOURCE_DIR)
foreach(_dir ${HOOKS_SOURCE_DIR})
list(APPEND _hooks_extra_args "--hooks-c-dir" "${_dir}")
string(REGEX REPLACE "^[^=]+=" "" _hook_dir "${_dir}")
if(EXISTS "${_hook_dir}")
file(GLOB_RECURSE _hook_dir_deps CONFIGURE_DEPENDS
"${_hook_dir}/*.c"
"${_hook_dir}/*.h"
"${_hook_dir}/*.ts"
"${_hook_dir}/*.js"
)
if(HOOKS_TEST_DIR)
list(FILTER _hook_dir_deps EXCLUDE REGEX "^${HOOKS_TEST_DIR}/")
endif()
list(APPEND _hooks_source_deps ${_hook_dir_deps})
endif()
endforeach()
list(REMOVE_DUPLICATES _hooks_source_deps)
endif()
set(_hooks_always_run OFF)
if(HOOKS_FORCE_RECOMPILE OR DEFINED ENV{HOOKS_FORCE_RECOMPILE})
list(APPEND _hooks_extra_args "--force-write" "--no-cache")
set(_hooks_always_run ON)
message(STATUS "Hook force recompile enabled (cache bypassed)")
endif()
foreach(_test_file ${EXTERNAL_HOOK_TESTS})
get_filename_component(_stem ${_test_file} NAME_WE)
set(_hooks_header "${HOOKS_TEST_DIR}/${_stem}_hooks.h")
if(_hooks_always_run)
add_custom_target(compile_hooks_${_stem} ALL
COMMAND hookz build-test-hooks "${_test_file}" ${_hooks_extra_args}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Compiling hooks for ${_stem} (forced)"
VERBATIM
)
list(APPEND EXTERNAL_HOOK_TARGETS compile_hooks_${_stem})
else()
add_custom_command(
OUTPUT "${_hooks_header}"
COMMAND hookz build-test-hooks "${_test_file}" ${_hooks_extra_args}
DEPENDS "${_test_file}" ${_hooks_source_deps}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Compiling hooks for ${_stem}"
VERBATIM
)
list(APPEND EXTERNAL_HOOK_HEADERS "${_hooks_header}")
endif()
endforeach()
if(_hooks_always_run)
foreach(_tgt ${EXTERNAL_HOOK_TARGETS})
add_dependencies(rippled ${_tgt})
endforeach()
else()
add_custom_target(compile_external_hooks DEPENDS ${EXTERNAL_HOOK_HEADERS})
add_dependencies(rippled compile_external_hooks)
endif()
target_sources(rippled PRIVATE ${EXTERNAL_HOOK_TESTS})
set_property(
SOURCE ${EXTERNAL_HOOK_TESTS}
APPEND PROPERTY INCLUDE_DIRECTORIES "${HOOKS_TEST_DIR}"
)
message(STATUS "Including external hook tests from: ${HOOKS_TEST_DIR}")
endif()
endif()
endif()
target_link_libraries(rippled
Ripple::boost
Ripple::opts
Ripple::libs
wasmtime::wasmtime
xrpl.libxrpl
# Workaround for a Conan 1.x bug that prevents static linking of libstdc++
# when a dependency (snappy) modifies system_libs. See the comment in

View File

@@ -6,7 +6,6 @@ include(create_symbolic_link)
install (
TARGETS
quickjs
common
opts
ripple_syslibs

View File

@@ -0,0 +1 @@
find_package(wasmtime REQUIRED)

View File

@@ -1,52 +0,0 @@
# QuickJS source files
set(QUICKJS_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/libutf.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/qsort_r.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/tutf8e.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/vector.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/char-utils.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/buffer-utils.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/utils.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/ringbuffer.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/libregexp.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/quickjs.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/libregexp.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/libunicode.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/cutils.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/quickjs-libc.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/libbf.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/quickjs-textcode.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/quickjs-internal.c
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/debug.c
)
# QuickJS include directories
set(QUICKJS_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs)
# RHTODO: Fix this to be dynamic
set(QUICKJS_VERSION "2023-01-01")
# QuickJS compile definitions
set(QUICKJS_COMPILE_DEFINITIONS
_GNU_SOURCE
CONFIG_VERSION="${QUICKJS_VERSION}"
CONFIG_BIGNUM
)
# Create static library
add_library(quickjs STATIC ${QUICKJS_SOURCES})
target_include_directories(quickjs
PUBLIC
$<BUILD_INTERFACE:${QUICKJS_INCLUDE_DIRS}>
$<INSTALL_INTERFACE:include/quickjs>
)
target_compile_definitions(quickjs PUBLIC ${QUICKJS_COMPILE_DEFINITIONS})
add_library(NIH::quickjs ALIAS quickjs)
target_link_libraries(ripple_libs INTERFACE NIH::quickjs)
install(
FILES
${CMAKE_CURRENT_SOURCE_DIR}/src/quickjs/*.h
DESTINATION include/quickjs
)

View File

@@ -0,0 +1,756 @@
#!/usr/bin/env python3
"""Fail-closed generator/checker mutation reds for the sealed QuickJS lock."""
from __future__ import annotations
import hashlib
import json
import re
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SCRIPT = Path(__file__).with_name("GenerateQuickJSProviderBundle.py")
BUNDLE = ROOT / "external" / "quickjs-provider"
WASM_VERSION = "47.0.3"
CMAKE_SET = re.compile(r'^set\(XAHAU_QUICKJS_([A-Z0-9_]+) "([^"]*)"\)\s*$')
PIN = {
"provider_sha256": "f60ab333acb547e50d7d087960839bd11833cb133d496af98e0b72a1bb3b84df",
"provider_size": "1116638",
"manifest_sha256": "1673b27d21063957cece9095cceb7e2dbfa53b6f49a5b7f824a486d7d90b2391",
"runtime_profile_id": "29acf12e170436e5ccadfbc118d02a60909f86a88262106652b2f71994b0ea25",
"broad_declaration_sha256": "649079b831bc68eff0f440644649a505addafb08a3f5174e4031c8de6aedffa0",
"exact_v1_declaration_sha256": "20881232f0602e35de0fcbb8b5a3c1511606392749a0f79daebb386204f4f4da",
"surface_sha256": "96a9218bf38abbf1bc9f2839408700d03572e0b065d625263f70477bc32597ac",
"api_artifact_manifest_sha256": "95babf622bf5edff76fdcbd9746bad8f1f8b275a3d4bacdc320ff4deae543eba",
"bytecode_abi": "75ea54f357d397c4b33899e495bb385dad975a43b1c4a7a2cead30474327d33e",
"native_abi": "328ec938dcad875f3bdf25b8d779dd77f3571589818ca9271cb98c64c7018f99",
"wasm_stack_bytes": "131072",
}
RECEIPT_B = {
"provider_sha256": "40f9ac0203afa9296196c627bc94e669ad789322ddc1e4ab58ba0cf6d32a5e21",
"provider_size": "1101461",
"runtime_profile_id": "60950ca8b6fe4dd2a35559367051998bc04f75e8aa41934dd4caad0de5a1c3ba",
"declaration_sha256": "56b4b2974b8a63a550721abd60350e392990762e76666f050b1a7c810e584957",
"surface_sha256": "b112346b95da74d04930ba86bd597e56a428e41e11581804ccc49907ae206eb0",
}
TYPED_IMPORT_ROWS = (
'{"env", "accept", "i32,i32,i64", "i64"}',
'{"env", "emit", "i32,i32,i32,i32", "i64"}',
'{"env", "etxn_reserve", "i32", "i64"}',
'{"env", "hook_account", "i32,i32", "i64"}',
'{"env", "ledger_last_hash", "i32,i32", "i64"}',
'{"env", "ledger_last_time", "", "i64"}',
'{"env", "ledger_seq", "", "i64"}',
'{"env", "otxn_type", "", "i64"}',
'{"env", "prepare", "i32,i32,i32,i32", "i64"}',
'{"env", "rollback", "i32,i32,i64", "i64"}',
'{"env", "state", "i32,i32,i32,i32", "i64"}',
'{"env", "state_set", "i32,i32,i32,i32", "i64"}',
'{"env", "trace", "i32,i32,i32,i32,i32", "i64"}',
)
TYPED_EXPORT_ROWS = (
'{"function", "_initialize", "", "", 0U, 0U, false, false}',
'{"function", "free", "i32", "", 0U, 0U, false, false}',
'{"function", "malloc", "i32", "i32", 0U, 0U, false, false}',
'{"memory", "memory", "", "", 7U, 512U, false, false}',
'{"function", "qjs_cbak", "i32,i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_compile", "i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_compile_module", "i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_destroy", "", "", 0U, 0U, false, false}',
'{"function", "qjs_enable_coverage", "i32", "", 0U, 0U, false, false}',
'{"function", "qjs_eval", "i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_eval_bytecode", "i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_eval_module", "i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_get_bytecode_len", "", "i32", 0U, 0U, false, false}',
'{"function", "qjs_get_bytecode_ptr", "", "i32", 0U, 0U, false, false}',
'{"function", "qjs_get_result_len", "", "i32", 0U, 0U, false, false}',
'{"function", "qjs_get_result_ptr", "", "i32", 0U, 0U, false, false}',
'{"function", "qjs_hook", "i32,i32,i32", "i32", 0U, 0U, false, false}',
'{"function", "qjs_init", "", "", 0U, 0U, false, false}',
'{"function", "qjs_set_max_stack_size", "i32", "", 0U, 0U, false, false}',
'{"function", "qjs_set_memory_limit", "i32", "", 0U, 0U, false, false}',
'{"function", "qjs_set_seed", "i32", "", 0U, 0U, false, false}',
'{"function", "qjs_validate_hook_module", "i32,i32", "i32", 0U, 0U, false, false}',
)
def json_path(bundle: Path) -> Path:
return bundle / "jshookz_provider.manifest.json"
def cmake_path(bundle: Path) -> Path:
return bundle / "jshookz_provider.manifest.cmake"
def native_path(bundle: Path) -> Path:
return bundle / "jshookz_provider.native-abi.json"
def wasm_path(bundle: Path) -> Path:
return bundle / "jshookz_provider.wasm"
def api_manifest_path(bundle: Path) -> Path:
return bundle / "api-artifacts.json"
def broad_declaration_path(bundle: Path) -> Path:
return bundle / "hooks-api.d.ts"
def exact_v1_declaration_path(bundle: Path) -> Path:
return bundle / "xahau-quickjs-v1.d.ts"
def surface_path(bundle: Path) -> Path:
return bundle / "xahau-quickjs-v1.surface.json"
def load_json(bundle: Path) -> dict:
return json.loads(json_path(bundle).read_text())
def write_json(bundle: Path, data: dict) -> None:
json_path(bundle).write_text(json.dumps(data, indent=2) + "\n")
def set_cmake(bundle: Path, key: str, value: str) -> None:
path = cmake_path(bundle)
lines = []
replaced = False
for line in path.read_text().splitlines():
match = CMAKE_SET.match(line)
if match and match.group(1) == key:
lines.append(f'set(XAHAU_QUICKJS_{key} "{value}")')
replaced = True
else:
lines.append(line)
if not replaced:
raise AssertionError(f"CMake lock is missing {key}")
path.write_text("\n".join(lines) + "\n")
def rehash_manifest(bundle: Path) -> None:
digest = hashlib.sha256(json_path(bundle).read_bytes()).hexdigest()
set_cmake(bundle, "MANIFEST_SHA256", digest)
def rehash_native(bundle: Path) -> None:
digest = hashlib.sha256(native_path(bundle).read_bytes()).hexdigest()
set_cmake(bundle, "NATIVE_ABI_SHA256", digest)
def load_api_manifest(bundle: Path) -> dict:
return json.loads(api_manifest_path(bundle).read_text())
def write_api_manifest(bundle: Path, data: dict) -> None:
api_manifest_path(bundle).write_text(json.dumps(data, indent=2) + "\n")
def clone_bundle(root: Path) -> Path:
dest = root / "bundle"
shutil.copytree(BUNDLE, dest, ignore=shutil.ignore_patterns("README.md"))
return dest
def generate(
bundle: Path, work: Path, wasmtime_version: str = WASM_VERSION
) -> tuple[int, str, str]:
output = work / "QuickJSProviderValues.cpp"
completed = subprocess.run(
[
sys.executable,
str(SCRIPT),
"--bundle-dir",
str(bundle),
"--wasmtime-version",
wasmtime_version,
"--output",
str(output),
],
check=False,
capture_output=True,
text=True,
)
cpp = output.read_text() if output.is_file() else ""
return completed.returncode, completed.stderr, cpp
def hex_bytes(value: str) -> str:
return ", ".join(f"0x{value[i:i + 2]}" for i in range(0, 64, 2))
def pin_holds(returncode: int, cpp: str) -> bool:
if returncode != 0 or not cpp:
return False
required = [
hex_bytes(PIN["provider_sha256"]),
f"std::size_t const providerSize = {PIN['provider_size']};",
f'"{PIN["manifest_sha256"]}"',
hex_bytes(PIN["runtime_profile_id"]),
f'"{PIN["broad_declaration_sha256"]}"',
f'"{PIN["exact_v1_declaration_sha256"]}"',
f'"{PIN["surface_sha256"]}"',
f'"{PIN["api_artifact_manifest_sha256"]}"',
hex_bytes(PIN["bytecode_abi"]),
f'"{PIN["native_abi"]}"',
f"std::uint32_t const wasmStackBytes = {PIN['wasm_stack_bytes']}U;",
"std::uint32_t const serializedObjectMaxBytes =\n 1048576U;",
"std::uint32_t const serializedObjectMaxFields =\n 32768U;",
"std::uint32_t const serializedObjectMaxScopes =\n 32769U;",
"std::uint32_t const serializedObjectMaxDepth =\n 10U;",
"std::uint32_t const providerMemoryMinimumPages = 7U;",
"std::uint32_t const providerMemoryMaximumPages = 512U;",
"constexpr char const sealedProvider[] =",
f"sizeof(sealedProvider) - 1 == {PIN['provider_size']}",
]
if any(item not in cpp for item in required):
return False
return all(row in cpp for row in (*TYPED_IMPORT_ROWS, *TYPED_EXPORT_ROWS))
class GenerateQuickJSProviderBundleTest(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
missing = [
path.name
for path in (
json_path(BUNDLE),
cmake_path(BUNDLE),
native_path(BUNDLE),
wasm_path(BUNDLE),
api_manifest_path(BUNDLE),
broad_declaration_path(BUNDLE),
exact_v1_declaration_path(BUNDLE),
surface_path(BUNDLE),
SCRIPT,
)
if not path.is_file()
]
if missing:
raise AssertionError(
"sealed QuickJS bundle is incomplete: " + ", ".join(missing)
)
def mutate(
self, mutator, wasmtime_version: str = WASM_VERSION
) -> tuple[int, str, str]:
with tempfile.TemporaryDirectory() as tmp:
work = Path(tmp)
bundle = clone_bundle(work)
mutator(bundle)
return generate(bundle, work / "out", wasmtime_version)
def assert_generator_red(
self, mutator, fragment: str, wasmtime_version: str = WASM_VERSION
) -> None:
returncode, stderr, cpp = self.mutate(mutator, wasmtime_version)
self.assertNotEqual(returncode, 0, stderr)
self.assertIn(fragment, stderr)
self.assertFalse(pin_holds(returncode, cpp), stderr)
def test_unmutated_lock_projects_pin_and_typed_exports(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
work = Path(tmp)
returncode, stderr, cpp = generate(clone_bundle(work), work / "out")
self.assertEqual(returncode, 0, stderr)
self.assertTrue(pin_holds(returncode, cpp), stderr)
self.assertEqual(cpp.count("ProviderExportSignature const"), 2)
def test_provider_bytes_mutation(self) -> None:
def mutate(bundle: Path) -> None:
path = wasm_path(bundle)
data = bytearray(path.read_bytes())
data[-1] ^= 0xFF
path.write_bytes(data)
self.assert_generator_red(mutate, "WASM does not match")
def test_provider_size_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "PROVIDER_SIZE", "1116637")
self.assert_generator_red(mutate, "provider size disagrees")
def test_coordinated_provider_size_mutation(self) -> None:
def mutate(bundle: Path) -> None:
value = 1116637
data = load_json(bundle)
data["provider"]["size"] = value
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "PROVIDER_SIZE", str(value))
self.assert_generator_red(mutate, "provider size disagrees with the sealed F0")
def test_provider_sha_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(
bundle,
"PROVIDER_SHA256",
"00" + PIN["provider_sha256"][2:],
)
self.assert_generator_red(mutate, "provider sha256 disagrees")
def test_coordinated_provider_sha_mutation(self) -> None:
def mutate(bundle: Path) -> None:
value = "00" + PIN["provider_sha256"][2:]
data = load_json(bundle)
data["provider"]["sha256"] = value
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "PROVIDER_SHA256", value)
self.assert_generator_red(mutate, "provider SHA-256 disagrees with the sealed F0")
def test_manifest_sha_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(
bundle,
"MANIFEST_SHA256",
"00" + PIN["manifest_sha256"][2:],
)
self.assert_generator_red(mutate, "JSON manifest does not match")
def test_coordinated_manifest_identity_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["diagnostic_note"] = "not F0"
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "provider manifest SHA-256")
def test_runtime_profile_id_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(
bundle,
"RUNTIME_PROFILE_ID",
"00" + PIN["runtime_profile_id"][2:],
)
self.assert_generator_red(mutate, "runtime-profile ID disagrees")
def test_coordinated_runtime_profile_id_mutation(self) -> None:
def mutate(bundle: Path) -> None:
value = "00" + PIN["runtime_profile_id"][2:]
data = load_json(bundle)
data["runtime_profile_id"] = value
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "RUNTIME_PROFILE_ID", value)
self.assert_generator_red(mutate, "runtime-profile ID disagrees with the sealed F0")
def _coordinated_api_artifact_mutation(
self, source_path: str, local_path, fragment: str
) -> None:
def mutate(bundle: Path) -> None:
path = local_path(bundle)
path.write_bytes(path.read_bytes() + b"\n")
manifest = load_api_manifest(bundle)
manifest["artifacts"][source_path] = hashlib.sha256(
path.read_bytes()
).hexdigest()
write_api_manifest(bundle, manifest)
self.assert_generator_red(mutate, fragment)
def test_broad_declaration_identity_mutation(self) -> None:
self._coordinated_api_artifact_mutation(
"python/jshookz/src/jshookz/types/hooks-api.d.ts",
broad_declaration_path,
"hooks-api.d.ts disagrees with the sealed F0 table",
)
def test_exact_v1_declaration_identity_mutation(self) -> None:
self._coordinated_api_artifact_mutation(
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts",
exact_v1_declaration_path,
"xahau-quickjs-v1.d.ts disagrees with the sealed F0 table",
)
def test_selected_surface_identity_mutation(self) -> None:
self._coordinated_api_artifact_mutation(
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.surface.json",
surface_path,
"xahau-quickjs-v1.surface.json disagrees with the sealed F0 table",
)
def test_api_artifact_manifest_identity_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_api_manifest(bundle)
api_manifest_path(bundle).write_text(json.dumps(data, indent=4) + "\n")
self.assert_generator_red(mutate, "API artifact manifest SHA-256")
def test_declaration_sha_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["javascript_surface"]["declaration_sha256"] = (
"00" + PIN["exact_v1_declaration_sha256"][2:]
)
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "declaration SHA-256 disagrees")
def test_surface_sha_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["javascript_surface"]["sha256"] = "00" + PIN["surface_sha256"][2:]
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "surface SHA-256 disagrees")
def test_bytecode_abi_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "BYTECODE_ABI_ID", "00" + PIN["bytecode_abi"][2:])
self.assert_generator_red(mutate, "bytecode ABI disagrees")
def test_coordinated_bytecode_abi_mutation(self) -> None:
def mutate(bundle: Path) -> None:
value = "00" + PIN["bytecode_abi"][2:]
data = load_json(bundle)
data["bytecode_abi_id"] = value
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "BYTECODE_ABI_ID", value)
self.assert_generator_red(mutate, "bytecode ABI disagrees with the sealed F0")
def test_native_abi_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "NATIVE_ABI_SHA256", "00" + PIN["native_abi"][2:])
self.assert_generator_red(mutate, "native ABI snapshot does not match")
def test_coordinated_native_abi_identity_mutation(self) -> None:
def mutate(bundle: Path) -> None:
path = native_path(bundle)
path.write_bytes(path.read_bytes() + b"\n")
rehash_native(bundle)
self.assert_generator_red(mutate, "native ABI SHA-256")
def test_import_signature_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["provider"]["imports"][0]["params"].append("i32")
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "import signatures disagree")
def test_export_signature_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
for item in data["provider"]["exports"]:
if item["name"] == "qjs_hook":
item["params"].append("i32")
break
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "export signatures disagree")
def test_coordinated_export_signature_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
for collection in (
data["provider"]["exports"],
data["source"]["provider"]["allowed_exports"],
):
for item in collection:
if item["name"] == "qjs_hook":
item["params"].append("i32")
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "sealed F0 table")
def test_coordinated_wasi_import_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
for collection in (
data["provider"]["imports"],
data["source"]["provider"]["imports"],
):
for item in collection:
if item["name"] == "accept":
item["module"] = "wasi_snapshot_preview1"
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "sealed F0 table")
def test_coordinated_import_signature_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
for collection in (
data["provider"]["imports"],
data["source"]["provider"]["imports"],
):
for item in collection:
if item["name"] == "accept":
item["params"].append("i32")
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "sealed F0 table")
def test_export_extra_key_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
for collection in (
data["provider"]["exports"],
data["source"]["provider"]["allowed_exports"],
):
for item in collection:
if item["name"] == "qjs_init":
item["note"] = "extra"
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "extra or missing keys")
def _memory_shape_mutation(self, key: str, value) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
for collection in (
data["provider"]["exports"],
data["source"]["provider"]["allowed_exports"],
):
for item in collection:
if item["kind"] == "memory":
item[key] = value
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "memory")
def test_memory_minimum_shape_mutation(self) -> None:
self._memory_shape_mutation("minimum_pages", 6)
def test_memory_maximum_shape_mutation(self) -> None:
self._memory_shape_mutation("maximum_pages", 511)
def test_memory64_shape_mutation(self) -> None:
self._memory_shape_mutation("memory64", True)
def test_shared_memory_shape_mutation(self) -> None:
self._memory_shape_mutation("shared", True)
def test_serialized_object_max_bytes_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "SERIALIZED_OBJECT_MAX_BYTES", "1048577")
self.assert_generator_red(mutate, "serialized_object_max_bytes")
def test_serialized_object_max_fields_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "SERIALIZED_OBJECT_MAX_FIELDS", "32769")
self.assert_generator_red(mutate, "serialized_object_max_fields")
def test_serialized_object_max_scopes_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "SERIALIZED_OBJECT_MAX_SCOPES", "32770")
self.assert_generator_red(mutate, "serialized_object_max_scopes")
def test_serialized_object_max_depth_mutation(self) -> None:
def mutate(bundle: Path) -> None:
set_cmake(bundle, "SERIALIZED_OBJECT_MAX_DEPTH", "11")
self.assert_generator_red(mutate, "serialized_object_max_depth")
def _coordinated_limit(self, json_key: str, cmake_key: str, value: int) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["limits"][json_key] = value
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, cmake_key, str(value))
self.assert_generator_red(mutate, "sealed F0 table")
def test_coordinated_serialized_object_max_bytes_mutation(self) -> None:
self._coordinated_limit(
"serialized_object_max_bytes", "SERIALIZED_OBJECT_MAX_BYTES", 1048578
)
def test_coordinated_serialized_object_max_fields_mutation(self) -> None:
self._coordinated_limit(
"serialized_object_max_fields", "SERIALIZED_OBJECT_MAX_FIELDS", 1
)
def test_coordinated_serialized_object_max_scopes_mutation(self) -> None:
self._coordinated_limit(
"serialized_object_max_scopes", "SERIALIZED_OBJECT_MAX_SCOPES", 1
)
def test_coordinated_serialized_object_max_depth_mutation(self) -> None:
self._coordinated_limit(
"serialized_object_max_depth", "SERIALIZED_OBJECT_MAX_DEPTH", 1
)
def test_coordinated_heap_limit_mutation(self) -> None:
self._coordinated_limit("quickjs_heap_bytes", "HEAP_BYTES", 16777215)
def test_coordinated_stack_limit_mutation(self) -> None:
self._coordinated_limit("quickjs_stack_bytes", "STACK_BYTES", 65535)
def test_coordinated_initialization_fuel_mutation(self) -> None:
self._coordinated_limit(
"wasmtime_fuel_per_initialization", "INITIALIZATION_FUEL", 4999999
)
def test_coordinated_invocation_fuel_mutation(self) -> None:
self._coordinated_limit(
"wasmtime_fuel_per_invocation", "INVOCATION_FUEL", 49999999
)
def test_coordinated_host_work_budget_mutation(self) -> None:
self._coordinated_limit("host_work_budget", "HOST_WORK_BUDGET", 999999)
def test_coordinated_host_work_base_mutation(self) -> None:
self._coordinated_limit(
"host_work_base_per_call", "HOST_WORK_BASE_PER_CALL", 2
)
def test_coordinated_host_work_per_byte_mutation(self) -> None:
self._coordinated_limit(
"host_work_per_addressed_byte", "HOST_WORK_PER_ADDRESSED_BYTE", 2
)
def test_coordinated_host_work_meter_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["limits"]["host_work_meter"] = "other-meter"
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "HOST_WORK_METER", "other-meter")
self.assert_generator_red(mutate, "host_work_meter")
def test_host_work_address_mapping_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["limits"]["host_work_addressed_length_indices"][
"accept"
] = [0]
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "host_work_addressed_length_indices")
def test_coordinated_host_adapter_policy_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["execution"]["host_adapter_policy"] = "other-policy"
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "HOST_ADAPTER_POLICY", "other-policy")
self.assert_generator_red(mutate, "host_adapter_policy")
def test_coordinated_hook_api_version_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["artifact"]["hook_api_version"] = 2
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "HOOK_API_VERSION", "2")
self.assert_generator_red(mutate, "hook_api_version")
def test_wasmtime_engine_configuration_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["engine"]["configuration"]["wasi"] = True
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "Wasmtime engine configuration")
def test_coordinated_wasmtime_version_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["source"]["engine"]["version"] = "48.0.0"
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "WASMTIME_VERSION", "48.0.0")
self.assert_generator_red(
mutate, "Wasmtime version disagrees with the sealed F0", "48.0.0"
)
def test_provider_memory_max_bytes_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["provider"]["build"]["wasm_memory_max_bytes"] = 33554431
data["source"]["provider"]["build"][
"wasm_memory_max_bytes"
] = 33554431
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "wasm_memory_max_bytes")
def test_wasm_stack_bytes_mutation(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["provider"]["build"]["wasm_stack_bytes"] = 65536
data["source"]["provider"]["build"]["wasm_stack_bytes"] = 65536
write_json(bundle, data)
rehash_manifest(bundle)
self.assert_generator_red(mutate, "wasm_stack_bytes")
def test_stale_receipt_b_lock_control(self) -> None:
def mutate(bundle: Path) -> None:
data = load_json(bundle)
data["provider"]["sha256"] = RECEIPT_B["provider_sha256"]
data["provider"]["size"] = int(RECEIPT_B["provider_size"])
data["runtime_profile_id"] = RECEIPT_B["runtime_profile_id"]
data["javascript_surface"]["declaration_sha256"] = RECEIPT_B[
"declaration_sha256"
]
data["javascript_surface"]["sha256"] = RECEIPT_B["surface_sha256"]
for collection in (
data["provider"]["exports"],
data["source"]["provider"]["allowed_exports"],
):
for item in collection:
if item["kind"] == "memory":
item["minimum_pages"] = 6
write_json(bundle, data)
rehash_manifest(bundle)
set_cmake(bundle, "PROVIDER_SHA256", RECEIPT_B["provider_sha256"])
set_cmake(bundle, "PROVIDER_SIZE", RECEIPT_B["provider_size"])
set_cmake(
bundle, "RUNTIME_PROFILE_ID", RECEIPT_B["runtime_profile_id"]
)
self.assert_generator_red(
mutate, "provider SHA-256 disagrees with the sealed F0"
)
if __name__ == "__main__":
unittest.main()

View File

@@ -2,158 +2,162 @@ from conan import ConanFile
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
import re
class Xrpl(ConanFile):
name = 'xrpl'
license = 'ISC'
author = 'John Freeman <jfreeman@ripple.com>'
url = 'https://github.com/xrplf/rippled'
description = 'The XRP Ledger'
settings = 'os', 'compiler', 'build_type', 'arch'
class Xrpl(ConanFile):
name = "xrpl"
license = "ISC"
author = "John Freeman <jfreeman@ripple.com>"
url = "https://github.com/xrplf/rippled"
description = "The XRP Ledger"
settings = "os", "compiler", "build_type", "arch"
options = {
'assertions': [True, False],
'coverage': [True, False],
'fPIC': [True, False],
'jemalloc': [True, False],
'rocksdb': [True, False],
'shared': [True, False],
'static': [True, False],
'tests': [True, False],
'unity': [True, False],
'xrpld': [True, False],
'with_wasmedge': [True, False],
'tool_requires_b2': [True, False],
"assertions": [True, False],
"coverage": [True, False],
"fPIC": [True, False],
"jemalloc": [True, False],
"rocksdb": [True, False],
"shared": [True, False],
"static": [True, False],
"tests": [True, False],
"unity": [True, False],
"xrpld": [True, False],
"with_wasmedge": [True, False],
"tool_requires_b2": [True, False],
}
requires = [
'date/3.0.3',
'grpc/1.50.1',
'libarchive/3.7.6',
'magic_enum/0.9.5',
'nudb/2.0.8',
'openssl/3.6.0',
'soci/4.0.3@xahaud/stable',
'xxhash/0.8.2',
'zlib/1.3.1',
"date/3.0.3",
"grpc/1.50.1",
"libarchive/3.7.6",
"magic_enum/0.9.5",
"nudb/2.0.8",
"openssl/3.6.0",
"soci/4.0.3@xahaud/stable",
"xxhash/0.8.2",
"zlib/1.3.1",
]
tool_requires = [
'protobuf/3.21.12',
"protobuf/3.21.12",
]
default_options = {
'assertions': False,
'coverage': False,
'fPIC': True,
'jemalloc': False,
'rocksdb': True,
'shared': False,
'static': True,
'tests': False,
'unity': False,
'xrpld': False,
'with_wasmedge': True,
'tool_requires_b2': False,
'date/*:header_only': False,
'grpc/*:shared': False,
'grpc/*:secure': True,
'libarchive/*:shared': False,
'libarchive/*:with_acl': False,
'libarchive/*:with_bzip2': False,
'libarchive/*:with_cng': False,
'libarchive/*:with_expat': False,
'libarchive/*:with_iconv': False,
'libarchive/*:with_libxml2': False,
'libarchive/*:with_lz4': True,
'libarchive/*:with_lzma': False,
'libarchive/*:with_lzo': False,
'libarchive/*:with_nettle': False,
'libarchive/*:with_openssl': False,
'libarchive/*:with_pcreposix': False,
'libarchive/*:with_xattr': False,
'libarchive/*:with_zlib': False,
'lz4/*:shared': False,
'openssl/*:shared': False,
'protobuf/*:shared': False,
'protobuf/*:with_zlib': True,
'rocksdb/*:enable_sse': False,
'rocksdb/*:lite': False,
'rocksdb/*:shared': False,
'rocksdb/*:use_rtti': True,
'rocksdb/*:with_jemalloc': False,
'rocksdb/*:with_lz4': True,
'rocksdb/*:with_snappy': True,
'snappy/*:shared': False,
'soci/*:shared': False,
'soci/*:with_sqlite3': True,
'soci/*:with_boost': True,
'xxhash/*:shared': False,
"assertions": False,
"coverage": False,
"fPIC": True,
"jemalloc": False,
"rocksdb": True,
"shared": False,
"static": True,
"tests": False,
"unity": False,
"xrpld": False,
"with_wasmedge": True,
"tool_requires_b2": False,
"date/*:header_only": False,
"grpc/*:shared": False,
"grpc/*:secure": True,
"libarchive/*:shared": False,
"libarchive/*:with_acl": False,
"libarchive/*:with_bzip2": False,
"libarchive/*:with_cng": False,
"libarchive/*:with_expat": False,
"libarchive/*:with_iconv": False,
"libarchive/*:with_libxml2": False,
"libarchive/*:with_lz4": True,
"libarchive/*:with_lzma": False,
"libarchive/*:with_lzo": False,
"libarchive/*:with_nettle": False,
"libarchive/*:with_openssl": False,
"libarchive/*:with_pcreposix": False,
"libarchive/*:with_xattr": False,
"libarchive/*:with_zlib": False,
"lz4/*:shared": False,
"openssl/*:shared": False,
"protobuf/*:shared": False,
"protobuf/*:with_zlib": True,
"rocksdb/*:enable_sse": False,
"rocksdb/*:lite": False,
"rocksdb/*:shared": False,
"rocksdb/*:use_rtti": True,
"rocksdb/*:with_jemalloc": False,
"rocksdb/*:with_lz4": True,
"rocksdb/*:with_snappy": True,
"snappy/*:shared": False,
"soci/*:shared": False,
"soci/*:with_sqlite3": True,
"soci/*:with_boost": True,
"wasmtime/*:shared": False,
"xxhash/*:shared": False,
}
def set_version(self):
path = f'{self.recipe_folder}/src/libxrpl/protocol/BuildInfo.cpp'
regex = r'versionString\s?=\s?\"(.*)\"'
with open(path, 'r') as file:
path = f"{self.recipe_folder}/src/libxrpl/protocol/BuildInfo.cpp"
regex = r"versionString\s?=\s?\"(.*)\""
with open(path, "r") as file:
matches = (re.search(regex, line) for line in file)
match = next(m for m in matches if m)
self.version = match.group(1)
def build_requirements(self):
# These provide build tools (protoc, grpc plugins) that run during build
self.tool_requires('grpc/1.50.1')
self.tool_requires("grpc/1.50.1")
# Explicitly require b2 (e.g. for building from source for glibc compatibility)
if self.options.tool_requires_b2:
self.tool_requires('b2/5.3.2')
self.tool_requires("b2/5.3.2")
def configure(self):
if self.settings.compiler == 'apple-clang':
self.options['boost/*'].visibility = 'global'
if self.settings.compiler == "apple-clang":
self.options["boost/*"].visibility = "global"
def requirements(self):
# Force sqlite3 version to avoid conflicts with soci
self.requires('sqlite3/3.47.0', override=True)
self.requires("sqlite3/3.47.0", override=True)
# Force our custom snappy build for all dependencies
self.requires('snappy/1.1.10@xahaud/stable', override=True)
self.requires("snappy/1.1.10@xahaud/stable", override=True)
# Force boost version for all dependencies to avoid conflicts
self.requires('boost/1.86.0', override=True)
self.requires('lz4/1.10.0', force=True)
self.requires("boost/1.86.0", override=True)
self.requires("lz4/1.10.0", force=True)
if self.options.with_wasmedge:
self.requires('wasmedge/0.11.2@xahaud/stable')
self.requires("wasmedge/0.11.2@xahaud/stable")
if self.options.xrpld:
self.requires("wasmtime/47.0.3@xahaud/stable")
if self.options.jemalloc:
self.requires('jemalloc/5.3.0')
self.requires("jemalloc/5.3.0")
if self.options.rocksdb:
self.requires('rocksdb/6.29.5')
self.requires("rocksdb/6.29.5")
exports_sources = (
'CMakeLists.txt',
'bin/getRippledInfo',
'cfg/*',
'cmake/*',
'external/*',
'include/*',
'src/*',
"CMakeLists.txt",
"bin/getRippledInfo",
"cfg/*",
"cmake/*",
"external/*",
"include/*",
"src/*",
)
def layout(self):
cmake_layout(self)
# Fix this setting to follow the default introduced in Conan 1.48
# to align with our build instructions.
self.folders.generators = 'build/generators'
self.folders.generators = "build/generators"
generators = "CMakeDeps"
generators = 'CMakeDeps'
def generate(self):
tc = CMakeToolchain(self)
tc.variables['tests'] = self.options.tests
tc.variables['assert'] = self.options.assertions
tc.variables['coverage'] = self.options.coverage
tc.variables['jemalloc'] = self.options.jemalloc
tc.variables['rocksdb'] = self.options.rocksdb
tc.variables['BUILD_SHARED_LIBS'] = self.options.shared
tc.variables['static'] = self.options.static
tc.variables['unity'] = self.options.unity
tc.variables['xrpld'] = self.options.xrpld
tc.variables["tests"] = self.options.tests
tc.variables["assert"] = self.options.assertions
tc.variables["coverage"] = self.options.coverage
tc.variables["jemalloc"] = self.options.jemalloc
tc.variables["rocksdb"] = self.options.rocksdb
tc.variables["BUILD_SHARED_LIBS"] = self.options.shared
tc.variables["static"] = self.options.static
tc.variables["unity"] = self.options.unity
tc.variables["xrpld"] = self.options.xrpld
tc.generate()
def build(self):
@@ -168,29 +172,29 @@ class Xrpl(ConanFile):
cmake.install()
def package_info(self):
libxrpl = self.cpp_info.components['libxrpl']
libxrpl = self.cpp_info.components["libxrpl"]
libxrpl.libs = [
'xrpl',
'xrpl.libpb',
'ed25519',
'secp256k1',
"xrpl",
"xrpl.libpb",
"ed25519",
"secp256k1",
]
# TODO: Fix the protobufs to include each other relative to
# `include/`, not `include/ripple/proto/`.
libxrpl.includedirs = ['include', 'include/ripple/proto']
libxrpl.includedirs = ["include", "include/ripple/proto"]
libxrpl.requires = [
'boost::boost',
'date::date',
'grpc::grpc++',
'libarchive::libarchive',
'lz4::lz4',
'nudb::nudb',
'openssl::crypto',
'protobuf::libprotobuf',
'soci::soci',
'sqlite3::sqlite',
'xxhash::xxhash',
'zlib::zlib',
"boost::boost",
"date::date",
"grpc::grpc++",
"libarchive::libarchive",
"lz4::lz4",
"nudb::nudb",
"openssl::crypto",
"protobuf::libprotobuf",
"soci::soci",
"sqlite3::sqlite",
"xxhash::xxhash",
"zlib::zlib",
]
if self.options.rocksdb:
libxrpl.requires.append('rocksdb::librocksdb')
libxrpl.requires.append("rocksdb::librocksdb")

48
external/quickjs-provider/README.md vendored Normal file
View File

@@ -0,0 +1,48 @@
# QuickJS provider manifest
These files are generated or sealed together by a jshookz source checkout:
```sh
# Current layout (cpp/provider, python/jshookz). Older checkouts used
# packages/jshookz; bin/update-jshookz-snapshot accepts either CLI path.
uv sync --project python/jshookz --locked --group dev
python/jshookz/.venv/bin/jshookz build provider
```
Update this Xahau branch from a clean, committed jshookz checkout with one
command:
```sh
bin/update-jshookz-snapshot /path/to/jshookz
```
That builds and imports the sealed lock files plus the broad/exact-v1
declarations, selected-surface manifest, and API-artifact manifest. CI checks out jshookz
`main` and fails if `build provider` does not reproduce that lock. The
importer then prints a self-contained `x-run-tests` command that
regenerates the embedded Hook fixtures and verifies the cut.
`jshookz_provider.manifest.json` is the verified runtime-profile lock.
`jshookz_provider.manifest.cmake` is its minimal CMake projection. Xahau checks
the JSON hash before generating the C++ profile constants, and the runtime
checks any supplied provider WASM against the projected size and SHA-256.
`api-artifacts.json` closes the three tracked API artifacts; Xahau verifies its
own digest and every named artifact before projecting their identities. These
files add no parallel CMake lock-value fields.
The provider binary is not vendored in this integration slice. Tests inject
the exact built artifact. Configure needs python3 to project these lock files.
A daemon whose embed is empty will not start.
For a local producer-to-consumer build, point Xahau straight at the emitted
bundle instead of copying it:
```sh
cmake -S . -B build \
-DXAHAU_QUICKJS_PROVIDER_BUNDLE_DIR=/path/to/jshookz/build/xahau-provider
```
A build-time generator writes `QuickJSProviderValues.cpp` from those
locks and, if present, the gitignored wasm. Configure only checks that
the three lock files exist. Changing the bundle rebuilds that one
translation unit; hash, Wasmtime, and optional-wasm checks run then.

View File

@@ -0,0 +1,8 @@
{
"artifacts": {
"python/jshookz/src/jshookz/types/hooks-api.d.ts": "649079b831bc68eff0f440644649a505addafb08a3f5174e4031c8de6aedffa0",
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts": "20881232f0602e35de0fcbb8b5a3c1511606392749a0f79daebb386204f4f4da",
"python/jshookz/src/jshookz/types/xahau-quickjs-v1.surface.json": "96a9218bf38abbf1bc9f2839408700d03572e0b065d625263f70477bc32597ac"
},
"schema": "jshookz.api-artifacts.v1"
}

3209
external/quickjs-provider/hooks-api.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
# Generated by jshookz build provider; do not edit.
set(XAHAU_QUICKJS_MANIFEST_SCHEMA "xahau.quickjs.runtime-profile-lock.v1")
set(XAHAU_QUICKJS_MANIFEST_SHA256 "1673b27d21063957cece9095cceb7e2dbfa53b6f49a5b7f824a486d7d90b2391")
set(XAHAU_QUICKJS_PROVIDER_FILE "jshookz_provider.wasm")
set(XAHAU_QUICKJS_PROVIDER_SHA256 "f60ab333acb547e50d7d087960839bd11833cb133d496af98e0b72a1bb3b84df")
set(XAHAU_QUICKJS_PROVIDER_SIZE "1116638")
set(XAHAU_QUICKJS_PROVIDER_IMPORT_COUNT "13")
set(XAHAU_QUICKJS_PROVIDER_EXPORT_COUNT "22")
set(XAHAU_QUICKJS_NATIVE_ABI_FILE "jshookz_provider.native-abi.json")
set(XAHAU_QUICKJS_NATIVE_ABI_SHA256 "328ec938dcad875f3bdf25b8d779dd77f3571589818ca9271cb98c64c7018f99")
set(XAHAU_QUICKJS_BYTECODE_ABI_ID "75ea54f357d397c4b33899e495bb385dad975a43b1c4a7a2cead30474327d33e")
set(XAHAU_QUICKJS_RUNTIME_PROFILE_ID "29acf12e170436e5ccadfbc118d02a60909f86a88262106652b2f71994b0ea25")
set(XAHAU_QUICKJS_HOOK_API_VERSION "1")
set(XAHAU_QUICKJS_WASMTIME_VERSION "47.0.3")
set(XAHAU_QUICKJS_INITIALIZATION_FUEL "5000000")
set(XAHAU_QUICKJS_INVOCATION_FUEL "50000000")
set(XAHAU_QUICKJS_HOST_WORK_METER "base-plus-addressed-byte-v1")
set(XAHAU_QUICKJS_HOST_WORK_BUDGET "1000000")
set(XAHAU_QUICKJS_HOST_WORK_BASE_PER_CALL "1")
set(XAHAU_QUICKJS_HOST_WORK_PER_ADDRESSED_BYTE "1")
set(XAHAU_QUICKJS_HOST_ADAPTER_POLICY "xahau-raw-hook-host-v1")
set(XAHAU_QUICKJS_HEAP_BYTES "16777216")
set(XAHAU_QUICKJS_STACK_BYTES "65536")
set(XAHAU_QUICKJS_SERIALIZED_OBJECT_MAX_BYTES "1048576")
set(XAHAU_QUICKJS_SERIALIZED_OBJECT_MAX_FIELDS "32768")
set(XAHAU_QUICKJS_SERIALIZED_OBJECT_MAX_SCOPES "32769")
set(XAHAU_QUICKJS_SERIALIZED_OBJECT_MAX_DEPTH "10")

View File

@@ -0,0 +1,854 @@
{
"bytecode_abi_id": "75ea54f357d397c4b33899e495bb385dad975a43b1c4a7a2cead30474327d33e",
"javascript_surface": {
"declaration": "python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts",
"declaration_sha256": "20881232f0602e35de0fcbb8b5a3c1511606392749a0f79daebb386204f4f4da",
"manifest": "python/jshookz/src/jshookz/types/xahau-quickjs-v1.surface.json",
"schema": "jshookz.javascript-surface.v1",
"sha256": "96a9218bf38abbf1bc9f2839408700d03572e0b065d625263f70477bc32597ac"
},
"provider": {
"build": {
"wasm_memory_max_bytes": 33554432,
"wasm_stack_bytes": 131072
},
"exports": [
{
"kind": "function",
"name": "_initialize",
"params": [],
"results": []
},
{
"kind": "function",
"name": "free",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "malloc",
"params": [
"i32"
],
"results": [
"i32"
]
},
{
"kind": "memory",
"maximum_pages": 512,
"memory64": false,
"minimum_pages": 7,
"name": "memory",
"shared": false
},
{
"kind": "function",
"name": "qjs_cbak",
"params": [
"i32",
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_compile",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_compile_module",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_destroy",
"params": [],
"results": []
},
{
"kind": "function",
"name": "qjs_enable_coverage",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_eval",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_eval_bytecode",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_eval_module",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_bytecode_len",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_bytecode_ptr",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_result_len",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_result_ptr",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_hook",
"params": [
"i32",
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_init",
"params": [],
"results": []
},
{
"kind": "function",
"name": "qjs_set_max_stack_size",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_set_memory_limit",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_set_seed",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_validate_hook_module",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
}
],
"imports": [
{
"module": "env",
"name": "accept",
"params": [
"i32",
"i32",
"i64"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "emit",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "etxn_reserve",
"params": [
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "hook_account",
"params": [
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "ledger_last_hash",
"params": [
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "ledger_last_time",
"params": [],
"results": [
"i64"
]
},
{
"module": "env",
"name": "ledger_seq",
"params": [],
"results": [
"i64"
]
},
{
"module": "env",
"name": "otxn_type",
"params": [],
"results": [
"i64"
]
},
{
"module": "env",
"name": "prepare",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "rollback",
"params": [
"i32",
"i32",
"i64"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "state",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "state_set",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "trace",
"params": [
"i32",
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
}
],
"sha256": "f60ab333acb547e50d7d087960839bd11833cb133d496af98e0b72a1bb3b84df",
"size": 1116638
},
"runtime_profile_id": "29acf12e170436e5ccadfbc118d02a60909f86a88262106652b2f71994b0ea25",
"schema": "xahau.quickjs.runtime-profile-lock.v1",
"source": {
"artifact": {
"envelope_version": 1,
"hook_api_version": 1,
"kind": "quickjs-bytecode"
},
"bytecode_abi": {
"entry_exports": {
"callback": "optional-callable",
"main": "required-callable"
},
"quickjs_bytecode_version": 5,
"quickjs_version": "2025-09-13",
"read_flags": [
"JS_READ_OBJ_BYTECODE"
],
"root": "es-module",
"write_flags": [
"JS_WRITE_OBJ_BYTECODE"
]
},
"engine": {
"configuration": {
"consume_fuel": true,
"cranelift_nan_canonicalization": true,
"wasi": false,
"wasm_memory64": false,
"wasm_multi_memory": false,
"wasm_relaxed_simd": false,
"wasm_tail_call": false,
"wasm_threads": false
},
"implementation": "wasmtime-native-c-api",
"module_lifecycle": "compile-once-fresh-store-and-instance-per-invocation",
"version": "47.0.3"
},
"execution": {
"host_adapter_policy": "xahau-raw-hook-host-v1",
"host_work_exhaustion": "uncatchable-wasmtime-trap-to-wasm-error",
"initialization": "finite-cold-qjs-init-budget-before-invocation-fuel-reset",
"ordinary_host_status": "returned-as-i64-never-trapped",
"pending_jobs_after_entry": "not-drained",
"result_failure_policy": "rollback.onFail-preserves-coded-failure;rollback.requirePresent-requires-success-and-non-nullish-value;rollback.requireTruthy-requires-success-and-truthy-value;accept.unlessPresent-accepts-on-failure-or-nullish-value;accept.unlessTruthy-accepts-on-failure-or-falsy-value;guards-use-explicit-message-and-optional-code-default-zero",
"sfHookInstructionCount": "initial-wasmtime-invocation-fuel-minus-remaining-fuel",
"terminals": "record-hook-result-and-uncatchable-wasmtime-trap"
},
"javascript_surface": {
"declaration": "python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts",
"manifest": "python/jshookz/src/jshookz/types/xahau-quickjs-v1.surface.json",
"schema": "jshookz.javascript-surface.v1"
},
"limits": {
"host_work_addressed_length_indices": {
"accept": [
1
],
"emit": [
1,
3
],
"hook_account": [
1
],
"ledger_last_hash": [
1
],
"prepare": [
1,
3
],
"rollback": [
1
],
"state": [
1,
3
],
"state_set": [
1,
3
],
"trace": [
1,
3
]
},
"host_work_base_per_call": 1,
"host_work_budget": 1000000,
"host_work_meter": "base-plus-addressed-byte-v1",
"host_work_per_addressed_byte": 1,
"quickjs_heap_bytes": 16777216,
"quickjs_stack_bytes": 65536,
"serialized_object_max_bytes": 1048576,
"serialized_object_max_depth": 10,
"serialized_object_max_fields": 32768,
"serialized_object_max_scopes": 32769,
"wasmtime_fuel_per_initialization": 5000000,
"wasmtime_fuel_per_invocation": 50000000
},
"name": "xahau-quickjs-v1",
"provider": {
"allowed_exports": [
{
"kind": "function",
"name": "_initialize",
"params": [],
"results": []
},
{
"kind": "function",
"name": "free",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "malloc",
"params": [
"i32"
],
"results": [
"i32"
]
},
{
"kind": "memory",
"maximum_pages": 512,
"memory64": false,
"minimum_pages": 7,
"name": "memory",
"shared": false
},
{
"kind": "function",
"name": "qjs_cbak",
"params": [
"i32",
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_compile",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_compile_module",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_destroy",
"params": [],
"results": []
},
{
"kind": "function",
"name": "qjs_enable_coverage",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_eval",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_eval_bytecode",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_eval_module",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_bytecode_len",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_bytecode_ptr",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_result_len",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_get_result_ptr",
"params": [],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_hook",
"params": [
"i32",
"i32",
"i32"
],
"results": [
"i32"
]
},
{
"kind": "function",
"name": "qjs_init",
"params": [],
"results": []
},
{
"kind": "function",
"name": "qjs_set_max_stack_size",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_set_memory_limit",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_set_seed",
"params": [
"i32"
],
"results": []
},
{
"kind": "function",
"name": "qjs_validate_hook_module",
"params": [
"i32",
"i32"
],
"results": [
"i32"
]
}
],
"build": {
"c_flags": [
"-fwrapv",
"-funsigned-char",
"-mllvm",
"-wasm-enable-sjlj"
],
"cxx_flags": [
"-fno-exceptions",
"-fno-rtti",
"-mllvm",
"-wasm-enable-sjlj"
],
"optimization": "O2",
"target": "wasm32-wasi",
"wasi_sdk": "32",
"wasm_memory_max_bytes": 33554432,
"wasm_stack_bytes": 131072
},
"forbidden_import_modules": [
"wasi_snapshot_preview1"
],
"imports": [
{
"module": "env",
"name": "accept",
"params": [
"i32",
"i32",
"i64"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "emit",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "etxn_reserve",
"params": [
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "hook_account",
"params": [
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "ledger_last_hash",
"params": [
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "ledger_last_time",
"params": [],
"results": [
"i64"
]
},
{
"module": "env",
"name": "ledger_seq",
"params": [],
"results": [
"i64"
]
},
{
"module": "env",
"name": "otxn_type",
"params": [],
"results": [
"i64"
]
},
{
"module": "env",
"name": "prepare",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "rollback",
"params": [
"i32",
"i32",
"i64"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "state",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "state_set",
"params": [
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
},
{
"module": "env",
"name": "trace",
"params": [
"i32",
"i32",
"i32",
"i32",
"i32"
],
"results": [
"i64"
]
}
],
"javascript": {
"atomics": false,
"date_now": "previous-ledger-close-ripple-epoch-to-unix-milliseconds",
"dynamic_eval": "enabled-and-metered",
"finalization_registry": false,
"math_random": "absent-use-ledger-random-api",
"number": "enabled",
"promise": "required-for-module-evaluation-no-post-entry-job-drain",
"shared_array_buffer": false,
"timezone": "UTC",
"weak_ref": false
}
},
"schema": "xahau.quickjs.runtime-profile-source.v1"
}
}

View File

@@ -0,0 +1 @@
{"selected":[{"amendment":null,"name":"accept","param_types":["uint32_t","uint32_t","int64_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7e"]},{"amendment":null,"name":"rollback","param_types":["uint32_t","uint32_t","int64_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7e"]},{"amendment":null,"name":"ledger_seq","param_types":[],"return_type":"int64_t","wasm_signature":["0x7e"]},{"amendment":null,"name":"ledger_last_time","param_types":[],"return_type":"int64_t","wasm_signature":["0x7e"]},{"amendment":null,"name":"ledger_last_hash","param_types":["uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f"]},{"amendment":null,"name":"otxn_type","param_types":[],"return_type":"int64_t","wasm_signature":["0x7e"]},{"amendment":null,"name":"hook_account","param_types":["uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f"]},{"amendment":null,"name":"trace","param_types":["uint32_t","uint32_t","uint32_t","uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7f","0x7f","0x7f"]},{"amendment":null,"name":"state","param_types":["uint32_t","uint32_t","uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7f","0x7f"]},{"amendment":null,"name":"state_set","param_types":["uint32_t","uint32_t","uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7f","0x7f"]},{"amendment":"featureHooksUpdate2","name":"prepare","param_types":["uint32_t","uint32_t","uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7f","0x7f"]},{"amendment":null,"name":"etxn_reserve","param_types":["uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f"]},{"amendment":null,"name":"emit","param_types":["uint32_t","uint32_t","uint32_t","uint32_t"],"return_type":"int64_t","wasm_signature":["0x7e","0x7f","0x7f","0x7f","0x7f"]}],"source":{"commit":"bb244ef7729503a0317bcff0f8fdaa93ca5cb7d2","macro_function_count":75,"path":"include/xrpl/hook/hook_api.macro","repository":"https://github.com/Xahau/xahaud"}}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,581 @@
{
"byte_policies": {
"AccountID.from": {
"0": "bytesLike"
},
"AccountID.fromHex": {
"0": "hexString"
},
"Hash256.from": {
"0": "bytesLike"
},
"Hash256.fromHex": {
"0": "hexString"
},
"STBlob.equals": {
"0": "bytesLikeOrSTBlob"
},
"STBlob.from": {
"0": "bytesLike"
},
"STBlob.fromHex": {
"0": "hexString"
},
"accept": {
"0": "lifecycleMessage"
},
"emit.prepare": {
"0": "bytesLikeOrSTBlob"
},
"emit.tx": {
"0": "bytesLikeOrSTBlob"
},
"rollback": {
"0": "lifecycleMessage"
},
"state.get": {
"0": "stateKeyLike"
},
"state.set": {
"0": "stateKeyLike",
"1": "stateValueLike"
}
},
"declaration": {
"path": "python/jshookz/src/jshookz/types/xahau-quickjs-v1.d.ts",
"sha256": "20881232f0602e35de0fcbb8b5a3c1511606392749a0f79daebb386204f4f4da"
},
"globals": {
"AccountID": "object",
"Amount": "object",
"Currency": "object",
"Field": "object",
"Hash": "object",
"Hash128": "object",
"Hash160": "object",
"Hash192": "object",
"Hash256": "object",
"IOUAmount": "object",
"Issue": "object",
"MPTAmount": "object",
"NativeAmount": "object",
"Path": "object",
"PathHop": "object",
"PathSet": "object",
"Result": "object",
"STArray": "object",
"STBlob": "object",
"STObject": "object",
"SerializedField": "object",
"UInt": "object",
"UInt16": "object",
"UInt32": "object",
"UInt64": "object",
"UInt8": "object",
"Vector256": "object",
"VoidResult": "object",
"XChainBridge": "object",
"XFLDecimal": "object",
"accept": "function",
"emit": "object",
"hook": "object",
"ledger": "object",
"otxn": "object",
"rollback": "function",
"state": "object",
"trace": "function",
"util": "object"
},
"namespaces": {
"accept": {
"unlessPresent": "function",
"unlessTruthy": "function",
"when": "function"
},
"emit": {
"prepare": "function",
"reserve": "function",
"tx": "function"
},
"hook": {
"account": "function"
},
"ledger": {
"lastHash": "value",
"lastTime": "value",
"sequence": "value"
},
"otxn": {
"type": "function"
},
"rollback": {
"onAllFail": "function",
"onAnyFail": "function",
"onFail": "function",
"requirePresent": "function",
"requireTruthy": "function",
"when": "function"
},
"state": {
"get": "function",
"set": "function"
},
"util": {
"decodeObject": "function",
"safeDecodeObject": "function",
"validateObject": "function"
}
},
"prototypes": {
"AccountID": {
"members": {
"equals": "function",
"isZero": "function",
"toBytes": "function",
"toHex": "function"
},
"own": [
"equals",
"isZero",
"toBytes",
"toHex"
],
"probe": "AccountID.from(new Uint8Array(20))"
},
"Amount": {
"members": {
"asIOU": "function",
"asMPT": "function",
"asNative": "function",
"byteLength": "value",
"compare": "function",
"currency": "value",
"drops": "value",
"equals": "function",
"isIOU": "function",
"isMPT": "function",
"isNative": "function",
"issue": "value",
"issuer": "value",
"kind": "value",
"mptIssuanceId": "value",
"toBytes": "function",
"toString": "function",
"toXFL": "function",
"value": "value"
},
"own": [
"asIOU",
"asMPT",
"asNative",
"byteLength",
"compare",
"currency",
"drops",
"equals",
"isIOU",
"isMPT",
"isNative",
"issue",
"issuer",
"kind",
"mptIssuanceId",
"toBytes",
"toString",
"toXFL",
"value"
],
"probe": "util.decodeObject(STBlob.fromHex('61D4838D7EA4C680000000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8')).Amount"
},
"Currency": {
"members": {
"byteLength": "value",
"equals": "function",
"isNative": "value",
"toBytes": "function",
"toHex": "function",
"toString": "function"
},
"own": [
"byteLength",
"equals",
"isNative",
"toBytes",
"toHex",
"toString"
],
"probe": "util.decodeObject(new Uint8Array()).withField('BaseAsset', new Uint8Array(20)).BaseAsset"
},
"Hash128": {
"members": {
"byteLength": "value",
"compare": "function",
"equals": "function",
"isZero": "function",
"toBytes": "function",
"toHex": "function"
},
"own": [
"byteLength",
"compare",
"equals",
"isZero",
"toBytes",
"toHex"
],
"probe": "util.decodeObject(new Uint8Array()).withField('EmailHash', new Uint8Array(16)).EmailHash"
},
"Hash160": {
"members": {
"byteLength": "value",
"compare": "function",
"equals": "function",
"isZero": "function",
"toBytes": "function",
"toHex": "function"
},
"own": [
"byteLength",
"compare",
"equals",
"isZero",
"toBytes",
"toHex"
],
"probe": "util.decodeObject(new Uint8Array()).withField('TakerPaysCurrency', new Uint8Array(20)).TakerPaysCurrency"
},
"Hash192": {
"members": {
"byteLength": "value",
"compare": "function",
"equals": "function",
"isZero": "function",
"toBytes": "function",
"toHex": "function"
},
"own": [
"byteLength",
"compare",
"equals",
"isZero",
"toBytes",
"toHex"
],
"probe": "util.decodeObject(new Uint8Array()).withField('MPTokenIssuanceID', new Uint8Array(24)).MPTokenIssuanceID"
},
"Hash256": {
"members": {
"byteLength": "value",
"compare": "function",
"equals": "function",
"isZero": "function",
"toBytes": "function",
"toHex": "function"
},
"own": [
"byteLength",
"compare",
"equals",
"isZero",
"toBytes",
"toHex"
],
"probe": "Hash256.from(new Uint8Array(32))"
},
"Issue": {
"members": {
"currency": "value",
"equals": "function",
"issuer": "value",
"kind": "value",
"mptIssuanceId": "value",
"toBytes": "function"
},
"own": [
"currency",
"equals",
"issuer",
"kind",
"mptIssuanceId",
"toBytes"
],
"probe": "util.decodeObject(new Uint8Array()).withField('LockingChainIssue', new Uint8Array(20)).LockingChainIssue"
},
"Path": {
"members": {
"[Symbol.iterator]": "function",
"at": "function",
"length": "value"
},
"own": [
"[Symbol.iterator]",
"at",
"length"
],
"probe": "util.decodeObject(STBlob.fromHex('011201B5F762798A53D543A014CAF8B297CFF8F2F937E800')).Paths.at(0)"
},
"PathHop": {
"members": {
"account": "value",
"currency": "value",
"issuer": "value"
},
"own": [
"account",
"currency",
"issuer"
],
"probe": "util.decodeObject(STBlob.fromHex('011201B5F762798A53D543A014CAF8B297CFF8F2F937E800')).Paths.at(0).at(0)"
},
"PathSet": {
"members": {
"[Symbol.iterator]": "function",
"at": "function",
"length": "value",
"toBytes": "function"
},
"own": [
"[Symbol.iterator]",
"at",
"length",
"toBytes"
],
"probe": "util.decodeObject(STBlob.fromHex('011201B5F762798A53D543A014CAF8B297CFF8F2F937E800')).Paths"
},
"Result": {
"frozen": true,
"members": {
"okMapOr": "function",
"okOr": "function",
"okOrHandle": "function"
},
"own": [
"okMapOr",
"okOr",
"okOrHandle"
],
"probe": "UInt64.from(0n)"
},
"STArray": {
"exotic": {
"array": false,
"enumerable": [
"0"
],
"extensible": false,
"own": [
"0",
"length"
]
},
"members": {
"[Symbol.iterator]": "function",
"at": "function",
"length": "value",
"toJSON": "function"
},
"own": [
"[Symbol.iterator]",
"at",
"toJSON"
],
"probe": "util.decodeObject(new Uint8Array([0xF9, 0xEA, 0x22, 0, 0, 0, 1, 0xE1, 0xF1])).Memos"
},
"STBlob": {
"members": {
"byteAt": "function",
"byteLength": "value",
"equals": "function",
"toBytes": "function",
"toHex": "function"
},
"own": [
"byteAt",
"byteLength",
"equals",
"toBytes",
"toHex"
],
"probe": "STBlob.from(new Uint8Array(0))"
},
"STObject": {
"exotic": {
"array": false,
"enumerable": [
"Flags"
],
"extensible": false,
"own": [
"Flags"
]
},
"members": {
"fieldBytes": "function",
"get": "function",
"has": "function",
"toBytes": "function",
"toJSON": "function",
"withField": "function",
"withoutField": "function"
},
"own": [
"fieldBytes",
"get",
"has",
"toBytes",
"toJSON",
"withField",
"withoutField"
],
"probe": "util.decodeObject(new Uint8Array([0x22, 0, 0, 0, 9]))"
},
"UInt": {
"members": {
"add": "function",
"bits": "value",
"byteLength": "value",
"compare": "function",
"equals": "function",
"isZero": "function",
"saturatingAdd": "function",
"saturatingSubtract": "function",
"subtract": "function",
"toBigInt": "function",
"toNumber": "function",
"toString": "function",
"wrappingAdd": "function",
"wrappingMultiply": "function",
"wrappingSubtract": "function"
},
"own": [
"add",
"bits",
"byteLength",
"compare",
"equals",
"isZero",
"saturatingAdd",
"saturatingSubtract",
"subtract",
"toBigInt",
"toNumber",
"toString",
"wrappingAdd",
"wrappingMultiply",
"wrappingSubtract"
],
"probe": "UInt8.zero"
},
"Vector256": {
"exotic": {
"array": false,
"enumerable": [
"0"
],
"extensible": false,
"own": [
"0",
"length"
]
},
"members": {
"[Symbol.iterator]": "function",
"at": "function",
"length": "value",
"toBytes": "function"
},
"own": [
"[Symbol.iterator]",
"at",
"toBytes"
],
"probe": "util.decodeObject(STBlob.fromHex('011320000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F')).Indexes"
},
"VoidResult": {
"frozen": true,
"members": {
"moot": "function",
"okMapOr": "function",
"okOr": "function",
"okOrHandle": "function"
},
"own": [
"moot"
],
"probe": "state.set('surface-probe', new Uint8Array(0))"
},
"XChainBridge": {
"members": {
"IssuingChainDoor": "value",
"IssuingChainIssue": "value",
"LockingChainDoor": "value",
"LockingChainIssue": "value",
"equals": "function",
"toBytes": "function"
},
"own": [
"IssuingChainDoor",
"IssuingChainIssue",
"LockingChainDoor",
"LockingChainIssue",
"equals",
"toBytes"
],
"probe": "util.decodeObject(STBlob.fromHex('011914B5F762798A53D543A014CAF8B297CFF8F2F937E8000000000000000000000000000000000000000014B5F762798A53D543A014CAF8B297CFF8F2F937E80000000000000000000000000000000000000000')).XChainBridge"
},
"XFLDecimal": {
"members": {
"isNegative": "function",
"isZero": "function"
},
"own": [
"isNegative",
"isZero"
],
"probe": "util.decodeObject(STBlob.fromHex('61D4838D7EA4C680000000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8')).Amount.toXFL()"
}
},
"schema": "jshookz.javascript-surface.v1",
"statics": {
"AccountID": {
"from": "function",
"fromHex": "function",
"one": "value",
"zero": "value"
},
"Hash256": {
"from": "function",
"fromHex": "function",
"zero": "value"
},
"STBlob": {
"from": "function",
"fromHex": "function"
},
"UInt16": {
"from": "function",
"max": "value",
"mulDiv": "function",
"zero": "value"
},
"UInt32": {
"from": "function",
"max": "value",
"mulDiv": "function",
"zero": "value"
},
"UInt64": {
"from": "function",
"max": "value",
"mulDiv": "function",
"mulDivXfl": "function",
"zero": "value"
},
"UInt8": {
"from": "function",
"max": "value",
"mulDiv": "function",
"zero": "value"
}
}
}

27
external/wasmtime/conandata.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
sources:
47.0.3:
Linux:
armv8:
sha256: c1ef99bb4ef0151282f339e22a67f8229d9ab336f9a17387e65f140b79c45be1
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-aarch64-linux-c-api.tar.xz
x86_64:
sha256: aaa3621f2a3d8393696702897f8f78a1cc504437d500701496d560125aefd732
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-x86_64-linux-c-api.tar.xz
Macos:
armv8:
sha256: 1854c8f03a764c89afe77fa88d9092ab89a368e527cd27a12959b1d91152324e
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-aarch64-macos-c-api.tar.xz
x86_64:
sha256: 627622087b77b92c163e826ec6ebf834a70d78735828043edf7ace263f8a9e62
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-x86_64-macos-c-api.tar.xz
MinGW:
x86_64:
sha256: 355a0c7b49b92bb011e74a4047cc1186271aeb8c8cfcc4360248caaf3c4d5f38
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-x86_64-mingw-c-api.zip
Windows:
armv8:
sha256: 57107b14b28ac2152e903501fe1722007b90fc11ccd63ad6d62ed574c5dd90db
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-aarch64-windows-c-api.zip
x86_64:
sha256: 952dfbc864295eccc89caa92bc303686017ca4ea205a1f21878809f374bb77c5
url: https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-x86_64-windows-c-api.zip

162
external/wasmtime/conanfile.py vendored Normal file
View File

@@ -0,0 +1,162 @@
import os
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.files import copy, get
from conan.tools.microsoft import is_msvc
required_conan_version = ">=2.0"
class WasmtimeConan(ConanFile):
name = "wasmtime"
description = "Standalone WebAssembly runtime using Cranelift"
license = "Apache-2.0 WITH LLVM-exception"
url = "https://github.com/Xahau/xahaud"
homepage = "https://github.com/bytecodealliance/wasmtime"
topics = ("webassembly", "wasm")
package_type = "library"
settings = "os", "arch", "compiler", "build_type"
options = {"shared": [True, False]}
default_options = {"shared": False}
no_copy_source = True
@property
def _sources_os_key(self):
if is_msvc(self):
return "Windows"
if self.settings.os == "Windows" and self.settings.compiler == "gcc":
return "MinGW"
return str(self.settings.os)
def configure(self):
self.settings.rm_safe("compiler.libcxx")
self.settings.rm_safe("compiler.cppstd")
def package_id(self):
# Official release archives contain a prebuilt C ABI. Compiler patch
# versions do not change the packaged bytes.
if self.info.settings.compiler == "clang":
self.info.settings.compiler = "gcc"
del self.info.settings.compiler.version
def validate(self):
try:
self.conan_data["sources"][self.version][self._sources_os_key][
str(self.settings.arch)
]
except KeyError as error:
raise ConanInvalidConfiguration(
"No official Wasmtime C-API archive for "
f"{self.settings.os}/{self.settings.arch}"
) from error
def build(self):
get(
self,
**self.conan_data["sources"][self.version][self._sources_os_key][
str(self.settings.arch)
],
destination=self.build_folder,
strip_root=True,
)
def package(self):
copy(
self,
"*.h",
dst=os.path.join(self.package_folder, "include"),
src=os.path.join(self.build_folder, "include"),
)
copy(
self,
"*.hh",
dst=os.path.join(self.package_folder, "include"),
src=os.path.join(self.build_folder, "include"),
)
source_lib = os.path.join(self.build_folder, "lib")
package_lib = os.path.join(self.package_folder, "lib")
package_bin = os.path.join(self.package_folder, "bin")
if self.options.shared:
copy(
self,
"wasmtime.dll.lib",
dst=package_lib,
src=source_lib,
keep_path=False,
)
copy(
self,
"wasmtime.dll",
dst=package_bin,
src=source_lib,
keep_path=False,
)
copy(
self,
"libwasmtime.dll.a",
dst=package_lib,
src=source_lib,
keep_path=False,
)
copy(
self,
"libwasmtime.so*",
dst=package_lib,
src=source_lib,
keep_path=False,
)
copy(
self,
"libwasmtime.dylib",
dst=package_lib,
src=source_lib,
keep_path=False,
)
else:
copy(
self,
"wasmtime.lib",
dst=package_lib,
src=source_lib,
keep_path=False,
)
copy(
self,
"libwasmtime.a",
dst=package_lib,
src=source_lib,
keep_path=False,
)
copy(
self,
"LICENSE",
dst=os.path.join(self.package_folder, "licenses"),
src=self.build_folder,
)
def package_info(self):
if self.options.shared:
self.cpp_info.libs = [
"wasmtime.dll" if self.settings.os == "Windows" else "wasmtime"
]
else:
if self.settings.os == "Windows":
self.cpp_info.defines = ["WASM_API_EXTERN=", "WASI_API_EXTERN="]
self.cpp_info.libs = ["wasmtime"]
if self.settings.os == "Windows":
self.cpp_info.system_libs = [
"ws2_32",
"bcrypt",
"advapi32",
"userenv",
"ntdll",
"shell32",
"ole32",
]
elif self.settings.os in ("Linux", "FreeBSD"):
self.cpp_info.system_libs = ["pthread", "dl", "m", "rt"]

View File

@@ -2,6 +2,9 @@
// Generated using generate_extern.sh
#include <stdint.h>
#ifndef HOOK_EXTERN
#ifdef __cplusplus
extern "C" {
#endif
extern int32_t __attribute__((noduplicate))
_g(uint32_t guard_id, uint32_t maxiter);
@@ -336,5 +339,8 @@ prepare(
uint32_t read_ptr,
uint32_t read_len);
#ifdef __cplusplus
}
#endif
#define HOOK_EXTERN
#endif // HOOK_EXTERN

View File

@@ -9,7 +9,7 @@ ENUM_FILE="$SCRIPT_DIR/../include/xrpl/hook/Enum.h"
echo '// For documentation please see: https://xrpl-hooks.readme.io/reference/'
echo '// Generated using generate_error.sh'
echo '#ifndef HOOK_ERROR_CODES'
sed -n '/enum hook_return_code/,/};/p' "$ENUM_FILE" |
sed -n '/enum class hook_return_code/,/};/p' "$ENUM_FILE" |
awk '
function ltrim(s) { sub(/^[[:space:]]+/, "", s); return s }
function rtrim(s) { sub(/[[:space:]]+$/, "", s); return s }
@@ -31,7 +31,7 @@ sed -n '/enum hook_return_code/,/};/p' "$ENUM_FILE" |
{
line = $0
if (line ~ /enum[[:space:]]+hook_return_code/)
if (line ~ /enum[[:space:]]+class[[:space:]]+hook_return_code/)
next
if (line ~ /^[[:space:]]*\{/)
next

View File

@@ -11,6 +11,9 @@ APPLY_HOOK="$SCRIPT_DIR/../include/xrpl/hook/hook_api.macro"
echo '// Generated using generate_extern.sh'
echo '#include <stdint.h>'
echo '#ifndef HOOK_EXTERN'
echo '#ifdef __cplusplus'
echo 'extern "C" {'
echo '#endif'
echo
awk '
function trim(s) {
@@ -27,8 +30,8 @@ APPLY_HOOK="$SCRIPT_DIR/../include/xrpl/hook/hook_api.macro"
next;
}
# Look for comment lines that start with // int64_t or // int32_t
if (line ~ /^[[:space:]]*\/\/[[:space:]]*(int64_t|int32_t)[[:space:]]+[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*\(/) {
# Look for comment lines that start with // and contain function signature
if (line ~ /^[[:space:]]*\/\/[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]+[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*\(/) {
# Remove leading // and trim
sub(/^[[:space:]]*\/\/[[:space:]]*/, "", line);
line = trim(line);
@@ -46,6 +49,9 @@ APPLY_HOOK="$SCRIPT_DIR/../include/xrpl/hook/hook_api.macro"
}
' "$APPLY_HOOK"
echo '#ifdef __cplusplus'
echo '}'
echo '#endif'
echo '#define HOOK_EXTERN'
echo '#endif // HOOK_EXTERN'
} | (

View File

@@ -189,6 +189,7 @@
#define sfSignatureReward ((6U << 16U) + 29U)
#define sfMinAccountCreateAmount ((6U << 16U) + 30U)
#define sfLPTokenBalance ((6U << 16U) + 31U)
#define sfTrustLineRewardAccumulator ((6U << 16U) + 99U)
#define sfPublicKey ((7U << 16U) + 1U)
#define sfMessageKey ((7U << 16U) + 2U)
#define sfSigningPubKey ((7U << 16U) + 3U)
@@ -220,6 +221,7 @@
#define sfProvider ((7U << 16U) + 30U)
#define sfMPTokenMetadata ((7U << 16U) + 31U)
#define sfCredentialType ((7U << 16U) + 32U)
#define sfHookName ((7U << 16U) + 97U)
#define sfRemarkValue ((7U << 16U) + 98U)
#define sfRemarkName ((7U << 16U) + 99U)
#define sfAccount ((8U << 16U) + 1U)
@@ -255,6 +257,7 @@
#define sfIssuingChainIssue ((24U << 16U) + 2U)
#define sfAsset ((24U << 16U) + 3U)
#define sfAsset2 ((24U << 16U) + 4U)
#define sfClaimCurrency ((24U << 16U) + 5U)
#define sfXChainBridge ((25U << 16U) + 1U)
#define sfTransactionMetaData ((14U << 16U) + 2U)
#define sfCreatedNode ((14U << 16U) + 3U)
@@ -292,6 +295,8 @@
#define sfActiveValidator ((14U << 16U) + 95U)
#define sfGenesisMint ((14U << 16U) + 96U)
#define sfRemark ((14U << 16U) + 97U)
#define sfHighReward ((14U << 16U) + 98U)
#define sfLowReward ((14U << 16U) + 99U)
#define sfSigners ((15U << 16U) + 3U)
#define sfSignerEntries ((15U << 16U) + 4U)
#define sfTemplate ((15U << 16U) + 5U)

View File

@@ -25,6 +25,7 @@
#include <boost/beast/core/string.hpp>
#include <boost/filesystem.hpp>
#include <fstream>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
@@ -165,6 +166,8 @@ private:
beast::severities::Severity thresh_;
File file_;
bool silent_ = false;
using Transform = std::function<std::string(std::string const&)>;
Transform transform_;
public:
Logs(beast::severities::Severity level);
@@ -203,6 +206,28 @@ public:
std::string const& text,
bool console);
/** Rewrite test log lines. Not synchronized.
*
* Test-only. Installed once for the life of Logs. Empty is a null check.
*/
void
setTransform(Transform fn)
{
transform_ = std::move(fn);
}
bool
hasTransform() const
{
return static_cast<bool>(transform_);
}
std::string
applyTransform(std::string const& text) const
{
return transform_ ? transform_(text) : text;
}
std::string
rotate();

View File

@@ -16,6 +16,7 @@
#define featureHooksUpdate1 "1"
#define featureHooksUpdate2 "1"
#define fix20250131 "1"
#define fixGuardDepth32 "1"
namespace hook_api {
struct Rules
{
@@ -264,11 +265,8 @@ enum hook_log_code : uint16_t {
SECTIONS_OUT_OF_SEQUENCE =
85, // the wasm contained sections out of sequence
CUSTOM_SECTION_DISALLOWED =
86, // the wasm contained a custom section (id=0)
INTERNAL_ERROR = 87, // an internal error described by the log text
JS_TEST_FAILURE = 88, // smoke test of js bytecode failed
JS_FEE_MISSING = 89,
JS_FEE_OUT_OF_RANGE = 90,
86, // the wasm contained a custom section (id=0)
INTERNAL_ERROR = 87, // an internal error described by the log text
// RH NOTE: only HookSet msgs got log codes, possibly all Hook log lines
// should get a code?
};
@@ -322,7 +320,7 @@ namespace compare_mode {
enum compare_mode : uint32_t { EQUAL = 1, LESS = 2, GREATER = 4 };
}
enum hook_return_code : int64_t {
enum class hook_return_code : int64_t {
SUCCESS =
0, // return codes > 0 are reserved for hook apis to return "success"
OUT_OF_BOUNDS =
@@ -389,20 +387,11 @@ enum hook_return_code : int64_t {
TOO_MANY_NAMESPACES = -45
};
enum ExitType : uint8_t {
enum class ExitType : uint8_t {
UNSET = 0,
WASM_ERROR = 1,
ROLLBACK = 2,
ACCEPT = 3,
JSVM_ERROR = 4,
LEDGER_ERROR =
5, // if the ledger contained for example a nonsense hookapi number
INSTRUCTION_LIMIT_REACHED = 6,
};
enum CodeType : uint8_t {
WASM = 0,
JS = 1,
};
const uint16_t max_state_modifications = 256;
@@ -436,10 +425,6 @@ getImportWhitelist(Rules const& rules)
if (AMENDMENT == uint256{} || rules.enabled(AMENDMENT)) \
whitelist[#FUNCTION_NAME] = { \
RETURN_TYPE, HOOK_WRAP_PARAMS PARAMS_TUPLE};
#define JSHOOK_API_DEFINITION( \
RETURN_TYPE, FUNCTION_NAME, PARAMS_TUPLE, AMENDMENT) \
{ \
}
#include "hook_api.macro"
@@ -459,6 +444,7 @@ getImportWhitelist(Rules const& rules)
enum GuardRulesVersion : uint64_t {
GuardRuleFix20250131 = 0x00000001,
GuardRuleDepth32 = 0x00000002,
};
inline uint64_t
@@ -467,6 +453,8 @@ getGuardRulesVersion(Rules const& rules)
uint64_t version = 0;
if (rules.enabled(fix20250131))
version |= GuardRuleFix20250131;
if (rules.enabled(fixGuardDepth32))
version |= GuardRuleDepth32;
return version;
}

View File

@@ -204,9 +204,13 @@ struct WasmBlkInf
}
// compute worst case execution time
inline uint64_t
compute_wce(const WasmBlkInf* blk, int level, bool* recursion_limit_reached)
compute_wce(
const WasmBlkInf* blk,
int level,
int max_level,
bool* recursion_limit_reached)
{
if (level > 16)
if (level > max_level)
{
*recursion_limit_reached = true;
return 0;
@@ -233,8 +237,8 @@ compute_wce(const WasmBlkInf* blk, int level, bool* recursion_limit_reached)
if (blk->children.size() > 0)
for (auto const& child : blk->children)
worst_case_execution +=
compute_wce(child, level + 1, recursion_limit_reached);
worst_case_execution += compute_wce(
child, level + 1, max_level, recursion_limit_reached);
if (parent == 0 ||
parent->iteration_bound ==
@@ -788,12 +792,17 @@ check_guard(
}
bool recursion_limit_reached = false;
uint64_t wce = compute_wce(&(*root), 0, &recursion_limit_reached);
int max_level = 16;
if (rulesVersion & hook_api::GuardRuleDepth32)
max_level = 32;
uint64_t wce =
compute_wce(&(*root), 0, max_level, &recursion_limit_reached);
if (recursion_limit_reached)
{
GUARDLOG(hook::log::NESTING_LIMIT)
<< "GuardCheck "
<< "Maximum allowable depth of blocks reached (16 levels). Flatten "
<< "Maximum allowable depth of blocks reached (" << max_level
<< " levels). Flatten "
"your loops and conditions!.\n";
return {};
}

View File

@@ -0,0 +1,102 @@
//------------------------------------------------------------------------------
/*
Canonical Hook artifact classification and QuickJS deployment envelope.
Legacy C Hooks remain raw WebAssembly. QuickJS bytecode is never deployed
raw: it is carried in a fixed-width, big-endian XQJS envelope whose exact
bytes participate in HookHash.
*/
//==============================================================================
#ifndef XRPL_HOOK_HOOKARTIFACT_H_INCLUDED
#define XRPL_HOOK_HOOKARTIFACT_H_INCLUDED
#include <xrpl/basics/Expected.h>
#include <xrpl/basics/Slice.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <string_view>
namespace hook::artifact {
inline constexpr std::size_t identitySize = 32;
inline constexpr std::size_t quickJSHeaderSize = 80;
inline constexpr std::size_t maxCreateCodeSize = 65'535;
inline constexpr std::array<std::uint8_t, 4> quickJSMagic = {
'X',
'Q',
'J',
'S'};
inline constexpr std::uint8_t quickJSEnvelopeVersion = 1;
inline constexpr std::uint8_t quickJSBytecodeKind = 1;
using Identity = std::array<std::uint8_t, identitySize>;
enum class Kind : std::uint8_t {
legacyWasm,
quickJSBytecode,
};
enum class Error : std::uint8_t {
tooLarge,
tooShort,
unknownMagic,
truncatedQuickJSHeader,
unsupportedEnvelopeVersion,
unsupportedArtifactKind,
nonCanonicalHeaderSize,
nonZeroReserved,
emptyPayload,
lengthMismatch,
zeroBytecodeABI,
zeroRuntimeProfile,
};
struct View
{
Kind kind;
std::uint16_t hookApiVersion;
Identity bytecodeABI;
Identity runtimeProfile;
ripple::Slice payload;
};
/** Parse either a legacy raw-Wasm Hook or a canonical QuickJS artifact.
The returned slices retain no ownership and must not outlive `code`.
*/
ripple::Expected<View, Error>
parse(ripple::Slice code) noexcept;
std::string_view
toString(Error error) noexcept;
extern Identity const quickJSBytecodeABI;
extern Identity const quickJSRuntimeProfile;
extern Identity const quickJSProviderSHA256;
extern std::size_t const quickJSProviderSize;
extern std::uint16_t const quickJSHookApiVersion;
extern std::uint64_t const quickJSInitializationFuel;
extern std::uint64_t const quickJSInvocationFuel;
extern std::string_view const quickJSHostWorkMeter;
extern std::uint64_t const quickJSHostWorkBudget;
extern std::uint64_t const quickJSHostWorkBasePerCall;
extern std::uint64_t const quickJSHostWorkPerAddressedByte;
extern std::string_view const quickJSHostAdapterPolicy;
extern std::uint32_t const quickJSHeapBytes;
extern std::uint32_t const quickJSStackBytes;
extern std::uint32_t const quickJSSerializedObjectMaxBytes;
extern std::uint32_t const quickJSSerializedObjectMaxFields;
extern std::uint32_t const quickJSSerializedObjectMaxScopes;
extern std::uint32_t const quickJSSerializedObjectMaxDepth;
extern std::uint32_t const quickJSProviderMemoryMinimumPages;
extern std::uint32_t const quickJSProviderMemoryMaximumPages;
extern std::uint32_t const quickJSProviderWasmStackBytes;
bool
isCurrentQuickJS(View const& artifact) noexcept;
} // namespace hook::artifact
#endif // XRPL_HOOK_HOOKARTIFACT_H_INCLUDED

View File

@@ -67,7 +67,6 @@
#define SEP_int32_t LPAREN int32_t COMMA
#define SEP_uint64_t LPAREN uint64_t COMMA
#define SEP_int64_t LPAREN int64_t COMMA
#define SEP_JSValue LPAREN JSValue COMMA
#define VAL_uint32_t WasmEdge_ValueGetI32(in[_stack++])
#define VAL_int32_t WasmEdge_ValueGetI32(in[_stack++])
@@ -90,246 +89,16 @@
#define WASM_VAL_TYPE(T, b) CAT2(TYP_, T)
#define HALF_COUNT(...) \
HALF_COUNT_IMPL( \
__VA_ARGS__, \
16, \
16, \
15, \
15, \
14, \
14, \
13, \
13, \
12, \
12, \
11, \
11, \
10, \
10, \
9, \
9, \
8, \
8, \
7, \
7, \
6, \
6, \
5, \
5, \
4, \
4, \
3, \
3, \
2, \
2, \
1, \
1, \
0, \
0)
#define UNSIGNED_TYPE(T) std::make_unsigned_t<T>
#define HALF_COUNT_IMPL( \
_1, \
_2, \
_3, \
_4, \
_5, \
_6, \
_7, \
_8, \
_9, \
_10, \
_11, \
_12, \
_13, \
_14, \
_15, \
_16, \
_17, \
_18, \
_19, \
_20, \
_21, \
_22, \
_23, \
_24, \
_25, \
_26, \
_27, \
_28, \
_29, \
_30, \
_31, \
_32, \
N, \
...) \
N
#define DECLARE_WASM_FUNCTION(R, F, ...) \
R F(hook::HookContext& hookCtx, \
WasmEdge_CallingFrameContext const& frameCtx __VA_OPT__( \
COMMA __VA_ARGS__)); \
extern WasmEdge_Result WasmFunction##F( \
void* data_ptr, \
const WasmEdge_CallingFrameContext* frameCtx, \
const WasmEdge_Value* in, \
WasmEdge_Value* out); \
extern WasmEdge_ValType WasmFunctionParams##F[]; \
extern WasmEdge_ValType WasmFunctionResult##F[]; \
extern WasmEdge_FunctionTypeContext* WasmFunctionType##F; \
extern WasmEdge_String WasmFunctionName##F;
#define DECLARE_JS_FUNCTION(R, F, ...) \
extern JSValue JSFunction##F( \
JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); \
const int JSFunctionParamCount##F = __VA_OPT__(HALF_COUNT(__VA_ARGS__) +) 0;
#define DEFINE_WASM_FUNCTION(R, F, ...) \
WasmEdge_Result hook_api::WasmFunction##F( \
void* data_ptr, \
const WasmEdge_CallingFrameContext* frameCtx, \
const WasmEdge_Value* in, \
WasmEdge_Value* out) \
{ \
__VA_OPT__(int _stack = 0;) \
__VA_OPT__(FOR_VARS(VAR_ASSIGN, 2, __VA_ARGS__);) \
hook::HookContext* hookCtx = \
reinterpret_cast<hook::HookContext*>(data_ptr); \
R return_code = hook_api::F( \
*hookCtx, \
*const_cast<WasmEdge_CallingFrameContext*>(frameCtx) \
__VA_OPT__(COMMA STRIP_TYPES(__VA_ARGS__))); \
if (return_code == RC_ROLLBACK || return_code == RC_ACCEPT) \
return WasmEdge_Result_Terminate; \
out[0] = RET_ASSIGN(R, return_code); \
return WasmEdge_Result_Success; \
}; \
WasmEdge_ValType hook_api::WasmFunctionParams##F[] = { \
__VA_OPT__(FOR_VARS(WASM_VAL_TYPE, 0, __VA_ARGS__))}; \
WasmEdge_ValType hook_api::WasmFunctionResult##F[1] = { \
WASM_VAL_TYPE(R, dummy)}; \
WasmEdge_FunctionTypeContext* hook_api::WasmFunctionType##F = \
WasmEdge_FunctionTypeCreate( \
WasmFunctionParams##F, \
VA_NARGS(NULL __VA_OPT__(, __VA_ARGS__)), \
WasmFunctionResult##F, \
1); \
WasmEdge_String hook_api::WasmFunctionName##F = \
WasmEdge_StringCreateByCString(#F); \
R hook_api::F( \
hook::HookContext& hookCtx, \
WasmEdge_CallingFrameContext const& frameCtx __VA_OPT__( \
COMMA __VA_ARGS__))
#define VAR_JSASSIGN(T, V) \
if (_stack >= argc) \
returnJS(INVALID_ARGUMENT); \
T& V = argv[_stack++]
#define FORWARD_JS_FUNCTION_CALL(F, ac, av) \
hook_api::JSFunction##F(ctx, this_val, ac, av)
#define DEFINE_JS_FUNCTION(R, F, ...) \
JSValue hook_api::JSFunction##F( \
JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) \
{ \
__VA_OPT__(int _stack = 0;) \
__VA_OPT__(FOR_VARS(VAR_JSASSIGN, 2, __VA_ARGS__);)
#define JS_HOOK_SETUP() \
JSRuntime* rt = JS_GetRuntime(ctx); \
hook::HookContext& hookCtx = \
*reinterpret_cast<hook::HookContext*>(JS_GetRuntimeOpaque(rt)); \
[[maybe_unused]] ApplyContext& applyCtx = hookCtx.applyCtx; \
[[maybe_unused]] auto& view = applyCtx.view(); \
[[maybe_unused]] auto j = applyCtx.app.journal("View"); \
[[maybe_unused]] auto& api = hookCtx.api(); \
try \
{
#define JS_HOOK_TEARDOWN() \
} \
catch (const std::exception& e) \
{ \
JLOG(hookCtx.applyCtx.app.journal("View").error()) \
<< "HookError[" << HC_ACC() << "]: (JS) " << __func__ \
<< " threw uncaught exception, what=" << e.what(); \
return JS_NewInt64(ctx, INTERNAL_ERROR); \
} \
}
#define WASM_HOOK_SETUP() \
try \
{ \
[[maybe_unused]] ApplyContext& applyCtx = hookCtx.applyCtx; \
[[maybe_unused]] auto& view = applyCtx.view(); \
[[maybe_unused]] auto j = applyCtx.app.journal("View"); \
[[maybe_unused]] WasmEdge_MemoryInstanceContext* memoryCtx = \
WasmEdge_CallingFrameGetMemoryInstance(&frameCtx, 0); \
[[maybe_unused]] unsigned char* memory = \
WasmEdge_MemoryInstanceGetPointer(memoryCtx, 0, 0); \
[[maybe_unused]] const uint64_t memory_length = \
WasmEdge_MemoryInstanceGetPageSize(memoryCtx) * \
WasmEdge_kPageSize; \
[[maybe_unused]] auto& api = hookCtx.api(); \
if (!memoryCtx || !memory || !memory_length) \
return INTERNAL_ERROR;
#define WASM_HOOK_TEARDOWN() \
} \
catch (const std::exception& e) \
{ \
JLOG(hookCtx.applyCtx.app.journal("View").error()) \
<< "HookError[" << HC_ACC() << "]: (WASM) " << __func__ \
<< " threw uncaught exception, what=" << e.what(); \
return INTERNAL_ERROR; \
}
#define WRITE_WASM_MEMORY( \
bytes_written, \
guest_dst_ptr, \
guest_dst_len, \
host_src_ptr, \
host_src_len, \
host_memory_ptr, \
guest_memory_length) \
{ \
int64_t bytes_to_write = std::min( \
static_cast<int64_t>(host_src_len), \
static_cast<int64_t>(guest_dst_len)); \
if (guest_dst_ptr + bytes_to_write > guest_memory_length) \
{ \
JLOG(j.warn()) << "HookError[" << HC_ACC() << "]: " << __func__ \
<< " tried to retreive blob of " << host_src_len \
<< " bytes past end of wasm memory"; \
return OUT_OF_BOUNDS; \
} \
if (!WasmEdge_ResultOK(WasmEdge_MemoryInstanceSetData( \
memoryCtx, \
reinterpret_cast<const uint8_t*>(host_src_ptr), \
guest_dst_ptr, \
bytes_to_write))) \
return INTERNAL_ERROR; \
bytes_written += bytes_to_write; \
}
#define WRITE_WASM_MEMORY_AND_RETURN( \
guest_dst_ptr, \
guest_dst_len, \
host_src_ptr, \
host_src_len, \
host_memory_ptr, \
guest_memory_length) \
{ \
int64_t bytes_written = 0; \
WRITE_WASM_MEMORY( \
bytes_written, \
guest_dst_ptr, \
guest_dst_len, \
host_src_ptr, \
host_src_len, \
host_memory_ptr, \
guest_memory_length); \
return bytes_written; \
#define HOOK_TEARDOWN() \
} \
catch (const std::exception& e) \
{ \
JLOG(hookCtx.applyCtx.app.journal("View").error()) \
<< "HookError[" << HC_ACC() << "]: " << __func__ \
<< " threw uncaught exception, what=" << e.what(); \
return INTERNAL_ERROR; \
}
// ptr = pointer inside the wasm memory space
@@ -375,28 +144,6 @@
: RC_ROLLBACK); \
}
#define HOOK_EXIT_JS(error_msg, error_code, exit_type) \
{ \
int64_t val = 0; \
if (JS_IsNumber(error_code)) \
JS_ToInt64(ctx, &val, error_code); \
hookCtx.result.exitCode = val; \
hookCtx.result.exitType = exit_type; \
if (JS_IsString(error_msg)) \
{ \
size_t len; \
const char* cstr = JS_ToCStringLen(ctx, &len, error_msg); \
if (len > 256) \
len = 256; \
hookCtx.result.exitReason = std::string(cstr, len); \
JS_FreeCString(ctx, cstr); \
} \
return JS_Exit( \
ctx, \
exit_type == hook_api::ExitType::ACCEPT ? "HookExit Accept" \
: "HookExit Rollback"); \
}
#define WRITE_WASM_MEMORY_OR_RETURN_AS_INT64( \
write_ptr_in, write_len_in, data_ptr_in, data_len_in, is_account_in) \
{ \
@@ -411,7 +158,7 @@
data_ptr < (data_ptr_in)) \
return INTERNAL_ERROR; \
if (data_len == 0) \
return 0; \
return 0ULL; \
if ((write_ptr_in) == 0) \
return data_as_int64(data_ptr, data_len); \
if (data_len > (write_len_in)) \

View File

@@ -7,559 +7,311 @@ HOOK_API_DEFINITION(
HOOK_API_DEFINITION(
int64_t, accept, (uint32_t, uint32_t, int64_t),
uint256{})
// function accept(error_msg: string, error_code: number): number
JSHOOK_API_DEFINITION(
JSValue, accept, (JSValue, JSValue),
uint256{})
// int64_t rollback(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
HOOK_API_DEFINITION(
int64_t, rollback, (uint32_t, uint32_t, int64_t),
uint256{})
// function rollback(error_msg: string, error_code: number): number
JSHOOK_API_DEFINITION(
JSValue, rollback, (JSValue, JSValue),
uint256{})
// int64_t util_raddr(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len);
HOOK_API_DEFINITION(
int64_t, util_raddr, (uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function util_raddr(acc_id:string): string
JSHOOK_API_DEFINITION(
JSValue, util_raddr, (JSValue),
uint256{})
// int64_t util_accid(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len);
HOOK_API_DEFINITION(
int64_t, util_accid, (uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function util_accid(acc_id:string): string
JSHOOK_API_DEFINITION(
JSValue, util_accid, (JSValue),
uint256{})
// int64_t util_verify(uint32_t dread_ptr, uint32_t dread_len, uint32_t sread_ptr, uint32_t sread_len, uint32_t kread_ptr, uint32_t kread_len);
HOOK_API_DEFINITION(
int64_t, util_verify, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function util_verify(data:string, sig: string, pubkey: string): number
JSHOOK_API_DEFINITION(
JSValue, util_verify, (JSValue, JSValue, JSValue),
uint256{})
// int64_t util_sha512h(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len);
HOOK_API_DEFINITION(
int64_t, util_sha512h, (uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function util_sha512h(data: string): number
JSHOOK_API_DEFINITION(
JSValue, util_sha512h, (JSValue),
uint256{})
// int64_t util_keylet(uint32_t write_ptr, uint32_t write_len, uint32_t keylet_type, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f);
HOOK_API_DEFINITION(
int64_t, util_keylet, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function util_keylet(keylet_type: number, keylet_data: string): number
JSHOOK_API_DEFINITION(
JSValue, util_keylet, (JSValue, JSValue),
uint256{})
// int64_t sto_validate(uint32_t tread_ptr, uint32_t tread_len);
HOOK_API_DEFINITION(
int64_t, sto_validate, (uint32_t, uint32_t),
uint256{})
// function sto_validate(sto: string): number
JSHOOK_API_DEFINITION(
JSValue, sto_validate, (JSValue),
uint256{})
// int64_t sto_subfield(uint32_t read_ptr, uint32_t read_len, uint32_t field_id);
HOOK_API_DEFINITION(
int64_t, sto_subfield, (uint32_t, uint32_t, uint32_t),
uint256{})
// function sto_subfield(sto: string, field_id: number): number
JSHOOK_API_DEFINITION(
JSValue, sto_subfield, (JSValue, JSValue),
uint256{})
// int64_t sto_subarray(uint32_t read_ptr, uint32_t read_len, uint32_t array_id);
HOOK_API_DEFINITION(
int64_t, sto_subarray, (uint32_t, uint32_t, uint32_t),
uint256{})
// function sto_subarray(sto: string, array_id: number): number
JSHOOK_API_DEFINITION(
JSValue, sto_subarray, (JSValue, JSValue),
uint256{})
// int64_t sto_emplace(uint32_t write_ptr, uint32_t write_len, uint32_t sread_ptr, uint32_t sread_len, uint32_t fread_ptr, uint32_t fread_len, uint32_t field_id);
HOOK_API_DEFINITION(
int64_t, sto_emplace, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function sto_emplace(sto: string, field_bytes: number, field_id: number): number
JSHOOK_API_DEFINITION(
JSValue, sto_emplace, (JSValue, JSValue, JSValue),
uint256{})
// int64_t sto_erase(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len, uint32_t field_id);
HOOK_API_DEFINITION(
int64_t, sto_erase, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function sto_erase(sto: string, field_id: number): number
JSHOOK_API_DEFINITION(
JSValue, sto_erase, (JSValue, JSValue),
uint256{})
// int64_t etxn_burden();
HOOK_API_DEFINITION(
int64_t, etxn_burden, (),
uint256{})
// function etxn_burden(): number
JSHOOK_API_DEFINITION(
JSValue, etxn_burden, (),
uint256{})
// int64_t etxn_details(uint32_t write_ptr, uint32_t write_len);
HOOK_API_DEFINITION(
int64_t, etxn_details, (uint32_t, uint32_t),
uint256{})
// function etxn_details(): number
JSHOOK_API_DEFINITION(
JSValue, etxn_details, (),
uint256{})
// int64_t etxn_fee_base(uint32_t read_ptr, uint32_t read_len);
HOOK_API_DEFINITION(
int64_t, etxn_fee_base, (uint32_t, uint32_t),
uint256{})
// function etxn_fee_base(txblob: string): number
JSHOOK_API_DEFINITION(
JSValue, etxn_fee_base, (JSValue),
uint256{})
// int64_t etxn_reserve(uint32_t count);
HOOK_API_DEFINITION(
int64_t, etxn_reserve, (uint32_t),
uint256{})
// function etxn_reserve(count: number): number
JSHOOK_API_DEFINITION(
JSValue, etxn_reserve, (JSValue),
uint256{})
// int64_t etxn_generation();
HOOK_API_DEFINITION(
int64_t, etxn_generation, (),
uint256{})
// function etxn_generation(): number
JSHOOK_API_DEFINITION(
JSValue, etxn_generation, (),
uint256{})
// int64_t etxn_nonce(uint32_t write_ptr, uint32_t write_len);
HOOK_API_DEFINITION(
int64_t, etxn_nonce, (uint32_t, uint32_t),
uint256{})
// function etxn_nonce(): number
JSHOOK_API_DEFINITION(
JSValue, etxn_nonce, (),
uint256{})
// int64_t emit(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len);
HOOK_API_DEFINITION(
int64_t, emit, (uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function emit(txn: string): number
JSHOOK_API_DEFINITION(
JSValue, emit, (JSValue),
uint256{})
// int64_t float_set(int32_t exponent, int64_t mantissa);
HOOK_API_DEFINITION(
int64_t, float_set, (int32_t, int64_t),
uint256{})
// function float_set(e: number, m: number): number
JSHOOK_API_DEFINITION(
JSValue, float_set, (JSValue, JSValue),
uint256{})
// int64_t float_multiply(int64_t float1, int64_t float2);
HOOK_API_DEFINITION(
int64_t, float_multiply, (int64_t, int64_t),
uint256{})
// function float_multiply(f1: number, f2: number): number
JSHOOK_API_DEFINITION(
JSValue, float_multiply, (JSValue, JSValue),
uint256{})
// int64_t float_mulratio(int64_t float1, uint32_t round_up, uint32_t numerator, uint32_t denominator);
HOOK_API_DEFINITION(
int64_t, float_mulratio, (int64_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function float_mulratio(f1: number, round_up: number, numerator: number, denominator: number): number
JSHOOK_API_DEFINITION(
JSValue, float_mulratio, (JSValue, JSValue, JSValue, JSValue),
uint256{})
// int64_t float_negate(int64_t float1);
HOOK_API_DEFINITION(
int64_t, float_negate, (int64_t),
uint256{})
// function float_negate(f1: number): number
JSHOOK_API_DEFINITION(
JSValue, float_negate, (JSValue),
uint256{})
// int64_t float_compare(int64_t float1, int64_t float2, uint32_t mode);
HOOK_API_DEFINITION(
int64_t, float_compare, (int64_t, int64_t, uint32_t),
uint256{})
// function float_compare(f1: number, f2: number, mode: number): number
JSHOOK_API_DEFINITION(
JSValue, float_compare, (JSValue, JSValue, JSValue),
uint256{})
// int64_t float_sum(int64_t float1, int64_t float2);
HOOK_API_DEFINITION(
int64_t, float_sum, (int64_t, int64_t),
uint256{})
// function float_sum(f1: number, f2: number): number
JSHOOK_API_DEFINITION(
JSValue, float_sum, (JSValue, JSValue),
uint256{})
// int64_t float_sto(uint32_t write_ptr, uint32_t write_len, uint32_t cread_ptr, uint32_t cread_len, uint32_t iread_ptr, uint32_t iread_len, int64_t float1, uint32_t field_code);
HOOK_API_DEFINITION(
int64_t, float_sto, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, int64_t, uint32_t),
uint256{})
// function float_sto(cur: string, isu: string, float1: number, field_code: number): number
JSHOOK_API_DEFINITION(
JSValue, float_sto, (JSValue, JSValue, JSValue, JSValue),
uint256{})
// int64_t float_sto_set(uint32_t read_ptr, uint32_t read_len);
HOOK_API_DEFINITION(
int64_t, float_sto_set, (uint32_t, uint32_t),
uint256{})
// function float_sto_set(buf: string): number
JSHOOK_API_DEFINITION(
JSValue, float_sto_set, (JSValue),
uint256{})
// int64_t float_invert(int64_t float1);
HOOK_API_DEFINITION(
int64_t, float_invert, (int64_t),
uint256{})
// function float_invert(f1: number): number
JSHOOK_API_DEFINITION(
JSValue, float_invert, (JSValue),
uint256{})
// int64_t float_divide(int64_t float1, int64_t float2);
HOOK_API_DEFINITION(
int64_t, float_divide, (int64_t, int64_t),
uint256{})
// function float_divide(f1: number, f2: number): number
JSHOOK_API_DEFINITION(
JSValue, float_divide, (JSValue, JSValue),
uint256{})
// int64_t float_one();
HOOK_API_DEFINITION(
int64_t, float_one, (),
uint256{})
// function float_one(): number
JSHOOK_API_DEFINITION(
JSValue, float_one, (),
uint256{})
// int64_t float_mantissa(int64_t float1);
HOOK_API_DEFINITION(
int64_t, float_mantissa, (int64_t),
uint256{})
// function float_mantissa(f1: number): number
JSHOOK_API_DEFINITION(
JSValue, float_mantissa, (JSValue),
uint256{})
// int64_t float_sign(int64_t float1);
HOOK_API_DEFINITION(
int64_t, float_sign, (int64_t),
uint256{})
// function float_sign(f1: number): number
JSHOOK_API_DEFINITION(
JSValue, float_sign, (JSValue),
uint256{})
// int64_t float_int(int64_t float1, uint32_t decimal_places, uint32_t abs);
HOOK_API_DEFINITION(
int64_t, float_int, (int64_t, uint32_t, uint32_t),
uint256{})
// function float_int(f1: number, decimal_places: number, abs: number): number
JSHOOK_API_DEFINITION(
JSValue, float_int, (JSValue, JSValue, JSValue),
uint256{})
// int64_t float_log(int64_t float1);
HOOK_API_DEFINITION(
int64_t, float_log, (int64_t),
uint256{})
// function float_log(f1: number): number
JSHOOK_API_DEFINITION(
JSValue, float_log, (JSValue),
uint256{})
// int64_t float_root(int64_t float1, uint32_t n);
HOOK_API_DEFINITION(
int64_t, float_root, (int64_t, uint32_t),
uint256{})
// function float_root(f1: number, n: number): number
JSHOOK_API_DEFINITION(
JSValue, float_root, (JSValue, JSValue),
uint256{})
// int64_t fee_base();
HOOK_API_DEFINITION(
int64_t, fee_base, (),
uint256{})
// function fee_base(): number
JSHOOK_API_DEFINITION(
JSValue, fee_base, (),
uint256{})
// int64_t ledger_seq();
HOOK_API_DEFINITION(
int64_t, ledger_seq, (),
uint256{})
// function ledger_seq(): number
JSHOOK_API_DEFINITION(
JSValue, ledger_seq, (),
uint256{})
// int64_t ledger_last_time();
HOOK_API_DEFINITION(
int64_t, ledger_last_time, (),
uint256{})
// function ledger_last_time(): number
JSHOOK_API_DEFINITION(
JSValue, ledger_last_time, (),
uint256{})
// int64_t ledger_last_hash(uint32_t write_ptr, uint32_t write_len);
HOOK_API_DEFINITION(
int64_t, ledger_last_hash, (uint32_t, uint32_t),
uint256{})
// function ledger_last_hash(): number
JSHOOK_API_DEFINITION(
JSValue, ledger_last_hash, (),
uint256{})
// int64_t ledger_nonce(uint32_t write_ptr, uint32_t write_len);
HOOK_API_DEFINITION(
int64_t, ledger_nonce, (uint32_t, uint32_t),
uint256{})
// function ledger_nonce(): number
JSHOOK_API_DEFINITION(
JSValue, ledger_nonce, (),
uint256{})
// int64_t ledger_keylet(uint32_t write_ptr, uint32_t write_len, uint32_t lread_ptr, uint32_t lread_len, uint32_t hread_ptr, uint32_t hread_len);
HOOK_API_DEFINITION(
int64_t, ledger_keylet, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function ledger_keylet(low: string, high: string): number
JSHOOK_API_DEFINITION(
JSValue, ledger_keylet, (JSValue, JSValue),
uint256{})
// int64_t hook_account(uint32_t write_ptr, uint32_t write_len);
HOOK_API_DEFINITION(
int64_t, hook_account, (uint32_t, uint32_t),
uint256{})
// function hook_account(): number
JSHOOK_API_DEFINITION(
JSValue, hook_account, (),
uint256{})
// int64_t hook_hash(uint32_t write_ptr, uint32_t write_len, int32_t hook_no);
HOOK_API_DEFINITION(
int64_t, hook_hash, (uint32_t, uint32_t, int32_t),
uint256{})
// function hook_hash(hook_no: number): number
JSHOOK_API_DEFINITION(
JSValue, hook_hash, (JSValue),
uint256{})
// int64_t hook_param_set(uint32_t read_ptr, uint32_t read_len, uint32_t kread_ptr, uint32_t kread_len, uint32_t hread_ptr, uint32_t hread_len);
HOOK_API_DEFINITION(
int64_t, hook_param_set, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function hook_param_set(value: string, key: string, hash: string): number
JSHOOK_API_DEFINITION(
JSValue, hook_param_set, (JSValue, JSValue, JSValue),
uint256{})
// int64_t hook_param(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len);
HOOK_API_DEFINITION(
int64_t, hook_param, (uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function hook_param(key: string): number
JSHOOK_API_DEFINITION(
JSValue, hook_param, (JSValue),
uint256{})
// int64_t hook_again();
HOOK_API_DEFINITION(
int64_t, hook_again, (),
uint256{})
// function hook_again(): number
JSHOOK_API_DEFINITION(
JSValue, hook_again, (),
uint256{})
// int64_t hook_skip(uint32_t read_ptr, uint32_t read_len, uint32_t flags);
HOOK_API_DEFINITION(
int64_t, hook_skip, (uint32_t, uint32_t, uint32_t),
uint256{})
// function hook_skip(hhash: string, flags: number): number
JSHOOK_API_DEFINITION(
JSValue, hook_skip, (JSValue, JSValue),
uint256{})
// int64_t hook_pos();
HOOK_API_DEFINITION(
int64_t, hook_pos, (),
uint256{})
// function hook_pos(): number
JSHOOK_API_DEFINITION(
JSValue, hook_pos, (),
uint256{})
// int64_t slot(uint32_t write_ptr, uint32_t write_len, uint32_t slot);
HOOK_API_DEFINITION(
int64_t, slot, (uint32_t, uint32_t, uint32_t),
uint256{})
// function slot(slot_no: number): number
JSHOOK_API_DEFINITION(
JSValue, slot, (JSValue),
uint256{})
// int64_t slot_clear(uint32_t slot);
HOOK_API_DEFINITION(
int64_t, slot_clear, (uint32_t),
uint256{})
// function slot_clear(slot_no: number): number
JSHOOK_API_DEFINITION(
JSValue, slot_clear, (JSValue),
uint256{})
// int64_t slot_count(uint32_t slot);
HOOK_API_DEFINITION(
int64_t, slot_count, (uint32_t),
uint256{})
// function slot_count(slot_no: number): number
JSHOOK_API_DEFINITION(
JSValue, slot_count, (JSValue),
uint256{})
// int64_t slot_set(uint32_t read_ptr, uint32_t read_len, uint32_t slot);
HOOK_API_DEFINITION(
int64_t, slot_set, (uint32_t, uint32_t, uint32_t),
uint256{})
// function slot_set(kl: string, slot_no: number): number
JSHOOK_API_DEFINITION(
JSValue, slot_set, (JSValue, JSValue),
uint256{})
// int64_t slot_size(uint32_t slot);
HOOK_API_DEFINITION(
int64_t, slot_size, (uint32_t),
uint256{})
// function slot_size(slot_no: number): number
JSHOOK_API_DEFINITION(
JSValue, slot_size, (JSValue),
uint256{})
// int64_t slot_subarray(uint32_t parent_slot, uint32_t array_id, uint32_t new_slot);
HOOK_API_DEFINITION(
int64_t, slot_subarray, (uint32_t, uint32_t, uint32_t),
uint256{})
// function slot_subarray(parent_slot_no: number, array_id: number, new_slot: number): number
JSHOOK_API_DEFINITION(
JSValue, slot_subarray, (JSValue, JSValue, JSValue),
uint256{})
// int64_t slot_subfield(uint32_t parent_slot, uint32_t field_id, uint32_t new_slot);
HOOK_API_DEFINITION(
int64_t, slot_subfield, (uint32_t, uint32_t, uint32_t),
uint256{})
// function slot_subfield(parent_slot_no: number, field_id: number, new_slot: number): number
JSHOOK_API_DEFINITION(
JSValue, slot_subfield, (JSValue, JSValue, JSValue),
uint256{})
// int64_t slot_type(uint32_t slot_no, uint32_t flags);
HOOK_API_DEFINITION(
int64_t, slot_type, (uint32_t, uint32_t),
uint256{})
// function slot_type(slot_no: number, flags: number): number
JSHOOK_API_DEFINITION(
JSValue, slot_type, (JSValue, JSValue),
uint256{})
// int64_t slot_float(uint32_t slot_no);
HOOK_API_DEFINITION(
int64_t, slot_float, (uint32_t),
uint256{})
// function slot_float(slot_no: number): number
JSHOOK_API_DEFINITION(
JSValue, slot_float, (JSValue),
uint256{})
// int64_t state_set(uint32_t read_ptr, uint32_t read_len, uint32_t kread_ptr, uint32_t kread_len);
HOOK_API_DEFINITION(
int64_t, state_set, (uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function state_set(val: string, key: string): number
JSHOOK_API_DEFINITION(
JSValue, state_set, (JSValue, JSValue),
uint256{})
// int64_t state_foreign_set(uint32_t read_ptr, uint32_t read_len, uint32_t kread_ptr, uint32_t kread_len, uint32_t nread_ptr, uint32_t nread_len, uint32_t aread_ptr, uint32_t aread_len);
HOOK_API_DEFINITION(
int64_t, state_foreign_set, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function state_foreign_set(val: string, key: string, ns: string, accid: string): number
JSHOOK_API_DEFINITION(
JSValue, state_foreign_set, (JSValue, JSValue, JSValue, JSValue),
uint256{})
// int64_t state(uint32_t write_ptr, uint32_t write_len, uint32_t kread_ptr, uint32_t kread_len);
HOOK_API_DEFINITION(
int64_t, state, (uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function state(key: string): number
JSHOOK_API_DEFINITION(
JSValue, state, (JSValue),
uint256{})
// int64_t state_foreign(uint32_t write_ptr, uint32_t write_len, uint32_t kread_ptr, uint32_t kread_len, uint32_t nread_ptr, uint32_t nread_len, uint32_t aread_ptr, uint32_t aread_len);
HOOK_API_DEFINITION(
int64_t, state_foreign, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function state_foreign(key: string, ns: string, accid: string): number
JSHOOK_API_DEFINITION(
JSValue, state_foreign, (JSValue, JSValue, JSValue),
uint256{})
// int64_t trace(uint32_t mread_ptr, uint32_t mread_len, uint32_t dread_ptr, uint32_t dread_len, uint32_t as_hex);
HOOK_API_DEFINITION(
int64_t, trace, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function trace(msg: string, data: string, as_hex: number): number
JSHOOK_API_DEFINITION(
JSValue, trace, (JSValue, JSValue, JSValue),
uint256{})
// int64_t trace_num(uint32_t read_ptr, uint32_t read_len, int64_t number);
HOOK_API_DEFINITION(
@@ -575,108 +327,48 @@ HOOK_API_DEFINITION(
HOOK_API_DEFINITION(
int64_t, otxn_burden, (),
uint256{})
// function otxn_burden(): number
JSHOOK_API_DEFINITION(
JSValue, otxn_burden, (),
uint256{})
// int64_t otxn_field(uint32_t write_ptr, uint32_t write_len, uint32_t field_id);
HOOK_API_DEFINITION(
int64_t, otxn_field, (uint32_t, uint32_t, uint32_t),
uint256{})
// function otxn_field(field_id: number): number
JSHOOK_API_DEFINITION(
JSValue, otxn_field, (JSValue),
uint256{})
// int64_t otxn_generation();
HOOK_API_DEFINITION(
int64_t, otxn_generation, (),
uint256{})
// function otxn_generation(): number
JSHOOK_API_DEFINITION(
JSValue, otxn_generation, (),
uint256{})
// int64_t otxn_id(uint32_t write_ptr, uint32_t write_len, uint32_t flags);
HOOK_API_DEFINITION(
int64_t, otxn_id, (uint32_t, uint32_t, uint32_t),
uint256{})
// function otxn_id(flags: number): number
JSHOOK_API_DEFINITION(
JSValue, otxn_id, (JSValue),
uint256{})
// int64_t otxn_type();
HOOK_API_DEFINITION(
int64_t, otxn_type, (),
uint256{})
// function otxn_type(): number
JSHOOK_API_DEFINITION(
JSValue, otxn_type, (),
uint256{})
// int64_t otxn_slot(uint32_t slot_no);
HOOK_API_DEFINITION(
int64_t, otxn_slot, (uint32_t),
uint256{})
// function otxn_slot(slot_no: number): number
JSHOOK_API_DEFINITION(
JSValue, otxn_slot, (JSValue),
uint256{})
// int64_t otxn_param(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len);
HOOK_API_DEFINITION(
int64_t, otxn_param, (uint32_t, uint32_t, uint32_t, uint32_t),
uint256{})
// function otxn_param(param_key: string): number
JSHOOK_API_DEFINITION(
JSValue, otxn_param, (JSValue),
uint256{})
// int64_t meta_slot(uint32_t slot_no);
HOOK_API_DEFINITION(
int64_t, meta_slot, (uint32_t),
uint256{})
// function meta_slot(slot_no: number): number
JSHOOK_API_DEFINITION(
JSValue, meta_slot, (JSValue),
uint256{})
// int64_t xpop_slot(uint32_t slot_no_tx, uint32_t slot_no_meta);
HOOK_API_DEFINITION(
int64_t, xpop_slot, (uint32_t, uint32_t),
featureHooksUpdate1)
// function xpop_slot(slot_no_tx: number, slot_no_meta: number): number
JSHOOK_API_DEFINITION(
JSValue, xpop_slot, (JSValue, JSValue),
uint256{})
// int64_t prepare(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len);
HOOK_API_DEFINITION(
int64_t, prepare, (uint32_t, uint32_t, uint32_t, uint32_t),
featureHooksUpdate2)
// function prepare(tmpl: string): number
JSHOOK_API_DEFINITION(
JSValue, prepare, (JSValue),
uint256{})
// function otxn_json(): number
JSHOOK_API_DEFINITION(
JSValue, otxn_json, (),
uint256{})
// function slot_json(slot_no: number): number
JSHOOK_API_DEFINITION(
JSValue, slot_json, (JSValue),
uint256{})
// function sto_to_json(sto_in: string): number
JSHOOK_API_DEFINITION(
JSValue, sto_to_json, (JSValue),
uint256{})
// function sto_from_json(json_in: string): number
JSHOOK_API_DEFINITION(
JSValue, sto_from_json, (JSValue),
uint256{})

View File

@@ -33,35 +33,39 @@
*
* Steps required to add new features to the code:
*
* 1) In this file, increment `numFeatures` and add a uint256 declaration
* for the feature at the bottom
* 2) Add a uint256 definition for the feature to the corresponding source
* file (Feature.cpp). Use `registerFeature` to create the feature with
* the feature's name, `Supported::no`, and `VoteBehavior::DefaultNo`. This
* should be the only place the feature's name appears in code as a string.
* 3) Use the uint256 as the parameter to `view.rules.enabled()` to
* control flow into new code that this feature limits.
* 4) If the feature development is COMPLETE, and the feature is ready to be
* SUPPORTED, change the `registerFeature` parameter to Supported::yes.
* 5) When the feature is ready to be ENABLED, change the `registerFeature`
* parameter to `VoteBehavior::DefaultYes`.
* In general, any newly supported amendments (`Supported::yes`) should have
* a `VoteBehavior::DefaultNo` for at least one full release cycle. High
* priority bug fixes can be an exception to this rule of thumb.
* 1) Add the appropriate XRPL_FEATURE or XRPL_FIX macro definition for the
* feature to features.macro with the feature's name, `Supported::no`, and
* `VoteBehavior::DefaultNo`.
*
* 2) Use the generated variable name as the parameter to `view.rules.enabled()`
* to control flow into new code that this feature limits. (featureName or
* fixName)
*
* 3) If the feature development is COMPLETE, and the feature is ready to be
* SUPPORTED, change the macro parameter in features.macro to Supported::yes.
*
* 4) In general, any newly supported amendments (`Supported::yes`) should have
* a `VoteBehavior::DefaultNo` indefinitely so that external governance can
* make the decision on when to activate it. High priority bug fixes can be
* an exception to this rule. In such cases, ensure the fix has been
* clearly communicated to the community using appropriate channels,
* then change the macro parameter in features.macro to
* `VoteBehavior::DefaultYes`. The communication process is beyond
* the scope of these instructions.
*
*
* When a feature has been enabled for several years, the conditional code
* may be removed, and the feature "retired". To retire a feature:
* 1) Remove the uint256 declaration from this file.
* 2) MOVE the uint256 definition in Feature.cpp to the "retired features"
* section at the end of the file.
* 3) CHANGE the name of the variable to start with "retired".
* 4) CHANGE the parameters of the `registerFeature` call to `Supported::yes`
* and `VoteBehavior::DefaultNo`.
*
* 1) MOVE the macro definition in features.macro to the "retired features"
* section at the end of the file, and change the macro to XRPL_RETIRE.
*
* The feature must remain registered and supported indefinitely because it
* still exists in the ledger, but there is no need to vote for it because
* there's nothing to vote for. If it is removed completely from the code, any
* instances running that code will get amendment blocked. Removing the
* feature from the ledger is beyond the scope of these instructions.
* may exist in the Amendments object on ledger. There is no need to vote
* for it because there's nothing to vote for. If the feature definition is
* removed completely from the code, any instances running that code will get
* amendment blocked. Removing the feature from the ledger is beyond the scope
* of these instructions.
*
*/
@@ -76,11 +80,32 @@ allAmendments();
namespace detail {
#pragma push_macro("XRPL_FEATURE")
#undef XRPL_FEATURE
#pragma push_macro("XRPL_FIX")
#undef XRPL_FIX
#pragma push_macro("XRPL_RETIRE")
#undef XRPL_RETIRE
#define XRPL_FEATURE(name, supported, vote) +1
#define XRPL_FIX(name, supported, vote) +1
#define XRPL_RETIRE(name) +1
// This value SHOULD be equal to the number of amendments registered in
// Feature.cpp. Because it's only used to reserve storage, and determine how
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
// the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 116;
static constexpr std::size_t numFeatures =
(0 +
#include <xrpl/protocol/detail/features.macro>
);
#undef XRPL_RETIRE
#pragma pop_macro("XRPL_RETIRE")
#undef XRPL_FIX
#pragma pop_macro("XRPL_FIX")
#undef XRPL_FEATURE
#pragma pop_macro("XRPL_FEATURE")
/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated
@@ -320,12 +345,17 @@ foreachFeature(FeatureBitset bs, F&& f)
#undef XRPL_FEATURE
#pragma push_macro("XRPL_FIX")
#undef XRPL_FIX
#pragma push_macro("XRPL_RETIRE")
#undef XRPL_RETIRE
#define XRPL_FEATURE(name, supported, vote) extern uint256 const feature##name;
#define XRPL_FIX(name, supported, vote) extern uint256 const fix##name;
#define XRPL_RETIRE(name)
#include <xrpl/protocol/detail/features.macro>
#undef XRPL_RETIRE
#pragma pop_macro("XRPL_RETIRE")
#undef XRPL_FIX
#pragma pop_macro("XRPL_FIX")
#undef XRPL_FEATURE

View File

@@ -137,6 +137,7 @@ enum TEMcodes : TERUnderlyingType {
temXCHAIN_BRIDGE_NONDOOR_OWNER,
temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT,
temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT,
temXCHAIN_TOO_MANY_ATTESTATIONS, // RESERVED - not used
temHOOK_DATA_TOO_LARGE,
temEMPTY_DID,
@@ -362,7 +363,6 @@ enum TECcodes : TERUnderlyingType {
tecARRAY_TOO_LARGE = 197,
tecLOCKED = 198,
tecBAD_CREDENTIALS = 199,
tecINVALID_HOOK_API_VERSION = 200,
tecLAST_POSSIBLE_ENTRY = 255,
};

View File

@@ -23,6 +23,9 @@
#if !defined(XRPL_FIX)
#error "undefined macro: XRPL_FIX"
#endif
#if !defined(XRPL_RETIRE)
#error "undefined macro: XRPL_RETIRE"
#endif
// clang-format off
@@ -31,7 +34,11 @@
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.
XRPL_FEATURE(JSHooks, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(JSHooks, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (HookMap, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FIX (GuardDepth32, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(NamedHooks, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(IOURewardClaim, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (IOULockedBalanceInvariant, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (ImportIssuer, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(HookAPISerializedType240, Supported::yes, VoteBehavior::DefaultNo)
@@ -55,18 +62,18 @@ XRPL_FIX (FillOrKill, Supported::yes, VoteBehavior::DefaultYe
XRPL_FEATURE(DID, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (DisallowIncomingV1, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(XChainBridge, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(AMM, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(AMM, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (ReducedOffersV1, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(HooksUpdate2, Supported::yes, VoteBehavior::DefaultNo);
XRPL_FEATURE(HookOnV2, Supported::yes, VoteBehavior::DefaultNo);
XRPL_FIX (HookAPI20251128, Supported::yes, VoteBehavior::DefaultYes);
XRPL_FIX (CronStacking, Supported::yes, VoteBehavior::DefaultYes);
XRPL_FEATURE(ExtendedHookState, Supported::yes, VoteBehavior::DefaultNo);
XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultYes);
XRPL_FEATURE(Cron, Supported::yes, VoteBehavior::DefaultNo);
XRPL_FEATURE(IOUIssuerWeakTSH, Supported::yes, VoteBehavior::DefaultNo);
XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo);
XRPL_FIX (ProvisionalDoubleThreading, Supported::yes, VoteBehavior::DefaultYes);
XRPL_FEATURE(HooksUpdate2, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(HookOnV2, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (HookAPI20251128, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FIX (CronStacking, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(ExtendedHookState, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(Cron, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(IOUIssuerWeakTSH, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (ProvisionalDoubleThreading, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(Clawback, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (RewardClaimFlags, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(HookCanEmit, Supported::yes, VoteBehavior::DefaultNo)
@@ -150,4 +157,24 @@ XRPL_FIX (NFTokenDirV1, Supported::yes, VoteBehavior::Obsolete)
XRPL_FEATURE(NonFungibleTokensV1, Supported::yes, VoteBehavior::Obsolete)
XRPL_FEATURE(CryptoConditionsSuite, Supported::yes, VoteBehavior::Obsolete)
// The following amendments have been active for at least two years. Their
// pre-amendment code has been removed and the identifiers are deprecated.
// All known amendments and amendments that may appear in a validated
// ledger must be registered either here or above with the "active" amendments
XRPL_RETIRE(MultiSign)
XRPL_RETIRE(TrustSetAuth)
XRPL_RETIRE(FeeEscalation)
XRPL_RETIRE(PayChan)
XRPL_RETIRE(CryptoConditions)
XRPL_RETIRE(TickSize)
XRPL_RETIRE(fix1368)
XRPL_RETIRE(Escrow)
XRPL_RETIRE(fix1373)
XRPL_RETIRE(EnforceInvariants)
XRPL_RETIRE(SortedDirectories)
XRPL_RETIRE(fix1201)
XRPL_RETIRE(fix1512)
XRPL_RETIRE(fix1523)
XRPL_RETIRE(fix1528)
// clang-format on

View File

@@ -93,7 +93,7 @@ LEDGER_ENTRY(ltCHECK, 0x0043, Check, check, ({
*/
LEDGER_ENTRY(ltHOOK_DEFINITION, 'D', HookDefinition, hook_definition, ({
{sfHookHash, soeREQUIRED},
{sfHookOn, soeREQUIRED},
{sfHookOn, soeOPTIONAL},
{sfHookOnIncoming, soeOPTIONAL},
{sfHookOnOutgoing, soeOPTIONAL},
{sfHookCanEmit, soeOPTIONAL},
@@ -396,6 +396,8 @@ LEDGER_ENTRY(ltRIPPLE_STATE, 0x0072, RippleState, state, ({
{sfHighQualityOut, soeOPTIONAL},
{sfLockedBalance, soeOPTIONAL},
{sfLockCount, soeOPTIONAL},
{sfHighReward, soeOPTIONAL},
{sfLowReward, soeOPTIONAL},
}))
/** The ledger object which lists the network's fee settings.

View File

@@ -257,6 +257,7 @@ TYPED_SFIELD(sfPrice, AMOUNT, 28)
TYPED_SFIELD(sfSignatureReward, AMOUNT, 29)
TYPED_SFIELD(sfMinAccountCreateAmount, AMOUNT, 30)
TYPED_SFIELD(sfLPTokenBalance, AMOUNT, 31)
TYPED_SFIELD(sfTrustLineRewardAccumulator,AMOUNT, 99)
// variable length (common)
TYPED_SFIELD(sfPublicKey, VL, 1)
@@ -292,6 +293,7 @@ TYPED_SFIELD(sfAssetClass, VL, 29)
TYPED_SFIELD(sfProvider, VL, 30)
TYPED_SFIELD(sfMPTokenMetadata, VL, 31)
TYPED_SFIELD(sfCredentialType, VL, 32)
TYPED_SFIELD(sfHookName, VL, 97)
TYPED_SFIELD(sfRemarkValue, VL, 98)
TYPED_SFIELD(sfRemarkName, VL, 99)
@@ -340,6 +342,7 @@ TYPED_SFIELD(sfLockingChainIssue, ISSUE, 1)
TYPED_SFIELD(sfIssuingChainIssue, ISSUE, 2)
TYPED_SFIELD(sfAsset, ISSUE, 3)
TYPED_SFIELD(sfAsset2, ISSUE, 4)
TYPED_SFIELD(sfClaimCurrency, ISSUE, 5)
// bridge
TYPED_SFIELD(sfXChainBridge, XCHAIN_BRIDGE, 1)
@@ -386,6 +389,8 @@ UNTYPED_SFIELD(sfImportVLKey, OBJECT, 94)
UNTYPED_SFIELD(sfActiveValidator, OBJECT, 95)
UNTYPED_SFIELD(sfGenesisMint, OBJECT, 96)
UNTYPED_SFIELD(sfRemark, OBJECT, 97)
UNTYPED_SFIELD(sfHighReward, OBJECT, 98)
UNTYPED_SFIELD(sfLowReward, OBJECT, 99)
// array of objects (common)
// ARRAY/1 is reserved for end of array

View File

@@ -550,6 +550,7 @@ TRANSACTION(ttIMPORT, 97, Import, ({
* from a specified hook */
TRANSACTION(ttCLAIM_REWARD, 98, ClaimReward, ({
{sfIssuer, soeOPTIONAL},
{sfClaimCurrency, soeOPTIONAL},
}))
/** This transaction invokes a hook, providing arbitrary data. Essentially as a 0 drop payment. **/

View File

@@ -76,6 +76,7 @@ JSS(Holder); // field.
JSS(HookApiVersion); // field
JSS(HookCanEmit); // field
JSS(HookHash); // field
JSS(HookName); // field
JSS(HookNamespace); // field
JSS(HookOn); // field
JSS(HookOnIncoming); // field

View File

@@ -11,11 +11,11 @@ echo "START BUILDING (HOST)"
echo "Cleaning previously built binary"
rm -f release-build/xahaud
BUILD_CORES=$(echo "scale=0 ; `nproc` / 1.337" | bc)
BUILD_CORES=$(echo "scale=0 ; $(nproc) / 1.337" | bc)
if [[ "$GITHUB_REPOSITORY" == "" ]]; then
#Default
BUILD_CORES=${BUILD_CORES:-8}
BUILD_CORES=${BUILD_CORES:-8}
fi
# Ensure still works outside of GH Actions by setting these to /dev/null
@@ -31,21 +31,19 @@ echo "-- GITHUB_SHA: $GITHUB_SHA"
echo "-- GITHUB_RUN_NUMBER: $GITHUB_RUN_NUMBER"
echo "-- CONTAINER_NAME: $CONTAINER_NAME"
which docker 2> /dev/null 2> /dev/null
if [ "$?" -eq "1" ]
then
which docker 2>/dev/null 2>/dev/null
if [ "$?" -eq "1" ]; then
echo 'Docker not found. Install it first.'
exit 1
fi
stat .git 2> /dev/null 2> /dev/null
if [ "$?" -eq "1" ]
then
stat .git 2>/dev/null 2>/dev/null
if [ "$?" -eq "1" ]; then
echo 'Run this inside the source directory. (.git dir not found).'
exit 1
fi
STATIC_CONTAINER=$(docker ps -a | grep $CONTAINER_NAME |wc -l)
STATIC_CONTAINER=$(docker ps -a | grep $CONTAINER_NAME | wc -l)
CACHE_VOLUME_NAME="xahau-release-builder-cache"
@@ -57,13 +55,14 @@ if false; then
docker stop $CONTAINER_NAME
else
echo "No static container, build on temp container"
rm -rf release-build;
mkdir -p release-build;
rm -rf release-build
mkdir -p release-build
docker volume create $CACHE_VOLUME_NAME
# Create inline Dockerfile with environment setup for build-full.sh
DOCKERFILE_CONTENT=$(cat <<'DOCKERFILE_EOF'
DOCKERFILE_CONTENT=$(
cat <<'DOCKERFILE_EOF'
FROM ghcr.io/phusion/holy-build-box:4.0.1-amd64
ARG BUILD_CORES=8
@@ -218,7 +217,7 @@ RUN /hbb_exe/activate-exec bash -c "ccache -M 100G && \
ln -s ../../bin/ccache /usr/lib64/ccache/c++"
DOCKERFILE_EOF
)
)
# Build custom Docker image
IMAGE_NAME="xahaud-builder:latest"
@@ -228,14 +227,14 @@ DOCKERFILE_EOF
if [[ "$GITHUB_REPOSITORY" == "" ]]; then
# Non GH, local building
echo "Non-GH runner, local building, temp container"
docker run -i --user 0:$(id -g) --rm -v /data/builds:/data/builds -v `pwd`:/io -v "$CACHE_VOLUME_NAME":/cache --network host "$IMAGE_NAME" /hbb_exe/activate-exec bash -c "source /opt/rh/gcc-toolset-11/enable && bash -x /io/build-full.sh '$GITHUB_REPOSITORY' '$GITHUB_SHA' '$BUILD_CORES' '$GITHUB_RUN_NUMBER'"
docker run -i --user 0:$(id -g) --rm -v /data/builds:/data/builds -v $(pwd):/io -v "$CACHE_VOLUME_NAME":/cache --network host "$IMAGE_NAME" /hbb_exe/activate-exec bash -c "source /opt/rh/gcc-toolset-11/enable && bash -x /io/build-full.sh '$GITHUB_REPOSITORY' '$GITHUB_SHA' '$BUILD_CORES' '$GITHUB_RUN_NUMBER'"
else
# GH Action, runner
echo "GH Action, runner, clean & re-create create persistent container"
docker rm -f $CONTAINER_NAME
echo "echo 'Stopping container: $CONTAINER_NAME'" >> "$JOB_CLEANUP_SCRIPT"
echo "docker stop --time=15 \"$CONTAINER_NAME\" || echo 'Failed to stop container or container not running'" >> "$JOB_CLEANUP_SCRIPT"
docker run -di --user 0:$(id -g) --name $CONTAINER_NAME -v /data/builds:/data/builds -v `pwd`:/io -v "$CACHE_VOLUME_NAME":/cache --network host "$IMAGE_NAME" /hbb_exe/activate-exec bash
echo "echo 'Stopping container: $CONTAINER_NAME'" >>"$JOB_CLEANUP_SCRIPT"
echo "docker stop --time=15 \"$CONTAINER_NAME\" || echo 'Failed to stop container or container not running'" >>"$JOB_CLEANUP_SCRIPT"
docker run -di --user 0:$(id -g) --name $CONTAINER_NAME -v /data/builds:/data/builds -v $(pwd):/io -v "$CACHE_VOLUME_NAME":/cache --network host "$IMAGE_NAME" /hbb_exe/activate-exec bash
docker exec -i $CONTAINER_NAME /hbb_exe/activate-exec bash -c "source /opt/rh/gcc-toolset-11/enable && bash -x /io/build-full.sh '$GITHUB_REPOSITORY' '$GITHUB_SHA' '$BUILD_CORES' '$GITHUB_RUN_NUMBER'"
docker stop $CONTAINER_NAME
fi

View File

@@ -66,7 +66,7 @@ invalidAMMAsset(
Issue const& issue,
std::optional<std::pair<Issue, Issue>> const& pair)
{
if (badCurrency() == issue.currency)
if (isBadCurrency(issue.currency))
return temBAD_CURRENCY;
if (isXRP(issue) && issue.account.isNonZero())
return temBAD_ISSUER;

View File

@@ -250,12 +250,9 @@ FeatureCollections::registerFeature(
Feature const* i = getByName(name);
if (!i)
{
// If this check fails, and you just added a feature, increase the
// numFeatures value in Feature.h
check(
features.size() < detail::numFeatures,
"More features defined than allocated. Adjust numFeatures in "
"Feature.h.");
"More features defined than allocated.");
auto const f = sha512Half(Slice(name.data(), name.size()));
@@ -424,45 +421,26 @@ featureToName(uint256 const& f)
#undef XRPL_FEATURE
#pragma push_macro("XRPL_FIX")
#undef XRPL_FIX
#pragma push_macro("XRPL_RETIRE")
#undef XRPL_RETIRE
#define XRPL_FEATURE(name, supported, vote) \
uint256 const feature##name = registerFeature(#name, supported, vote);
#define XRPL_FIX(name, supported, vote) \
uint256 const fix##name = registerFeature("fix" #name, supported, vote);
#define XRPL_RETIRE(name) \
[[deprecated("The referenced amendment has been retired"), maybe_unused]] \
uint256 const retired##name = retireFeature(#name);
#include <xrpl/protocol/detail/features.macro>
#undef XRPL_RETIRE
#pragma pop_macro("XRPL_RETIRE")
#undef XRPL_FIX
#pragma pop_macro("XRPL_FIX")
#undef XRPL_FEATURE
#pragma pop_macro("XRPL_FEATURE")
// clang-format off
// The following amendments have been active for at least two years. Their
// pre-amendment code has been removed and the identifiers are deprecated.
// All known amendments and amendments that may appear in a validated
// ledger must be registered either here or above with the "active" amendments
[[deprecated("The referenced amendment has been retired"), maybe_unused]]
uint256 const
retiredMultiSign = retireFeature("MultiSign"),
retiredTrustSetAuth = retireFeature("TrustSetAuth"),
retiredFeeEscalation = retireFeature("FeeEscalation"),
retiredPayChan = retireFeature("PayChan"),
retiredCryptoConditions = retireFeature("CryptoConditions"),
retiredTickSize = retireFeature("TickSize"),
retiredFix1368 = retireFeature("fix1368"),
retiredEscrow = retireFeature("Escrow"),
retiredFix1373 = retireFeature("fix1373"),
retiredEnforceInvariants = retireFeature("EnforceInvariants"),
retiredSortedDirectories = retireFeature("SortedDirectories"),
retiredFix1201 = retireFeature("fix1201"),
retiredFix1512 = retireFeature("fix1512"),
retiredFix1523 = retireFeature("fix1523"),
retiredFix1528 = retireFeature("fix1528");
// clang-format on
// All of the features should now be registered, since variables in a cpp file
// are initialized from top to bottom.
//

View File

@@ -99,7 +99,7 @@ InnerObjectFormats::InnerObjectFormats()
{sfHookOnOutgoing, soeOPTIONAL},
{sfHookCanEmit, soeOPTIONAL},
{sfHookApiVersion, soeOPTIONAL},
{sfFee, soeOPTIONAL},
{sfHookName, soeOPTIONAL},
{sfFlags, soeOPTIONAL}});
add(sfHookGrant.jsonName,
@@ -254,6 +254,24 @@ InnerObjectFormats::InnerObjectFormats()
{sfIssuer, soeREQUIRED},
{sfCredentialType, soeREQUIRED},
});
add(sfHighReward.jsonName,
sfHighReward.getCode(),
{
{sfRewardLgrFirst, soeREQUIRED},
{sfRewardLgrLast, soeREQUIRED},
{sfRewardTime, soeREQUIRED},
{sfTrustLineRewardAccumulator, soeREQUIRED},
});
add(sfLowReward.jsonName,
sfLowReward.getCode(),
{
{sfRewardLgrFirst, soeREQUIRED},
{sfRewardLgrLast, soeREQUIRED},
{sfRewardTime, soeREQUIRED},
{sfTrustLineRewardAccumulator, soeREQUIRED},
});
}
InnerObjectFormats const&

View File

@@ -111,7 +111,7 @@ issueFromJson(Json::Value const& v)
}
auto const currency = to_currency(curStr.asString());
if (currency == badCurrency() || currency == noCurrency())
if (isBadCurrency(currency) || currency == noCurrency())
{
Throw<Json::error>("issueFromJson currency must be a valid currency");
}

View File

@@ -103,7 +103,7 @@ currencyFromJson(SField const& name, Json::Value const& v)
}
auto const currency = to_currency(v.asString());
if (currency == badCurrency() || currency == noCurrency())
if (isBadCurrency(currency) || currency == noCurrency())
{
Throw<std::runtime_error>(
"currencyFromJson currency must be a valid currency");

View File

@@ -124,7 +124,6 @@ transResults()
MAKE_ERROR(tecARRAY_TOO_LARGE, "Array is too large."),
MAKE_ERROR(tecLOCKED, "Fund is locked."),
MAKE_ERROR(tecBAD_CREDENTIALS, "Bad credentials."),
MAKE_ERROR(tecINVALID_HOOK_API_VERSION, "Invalid field for current hook API version."),
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),

View File

@@ -48,6 +48,7 @@ TxFormats::TxFormats()
{sfFirstLedgerSequence, soeOPTIONAL},
{sfNetworkID, soeOPTIONAL},
{sfHookParameters, soeOPTIONAL},
{sfHookName, soeOPTIONAL},
};
#pragma push_macro("UNWRAP")

View File

@@ -1,820 +0,0 @@
#include "defines.h"
#include "char-utils.h"
#include "buffer-utils.h"
#include "utils.h"
#ifdef _WIN32
#include <windows.h>
#elif defined(HAVE_TERMIOS_H)
#include <termios.h>
#include <sys/ioctl.h>
#include <unistd.h>
#endif
#include "debug.h"
/**
* \addtogroup buffer-utils
* @{
*/
size_t
ansi_length(const char* str, size_t len) {
size_t i, n = 0, p;
for(i = 0; i < len;) {
if(str[i] == 0x1b && (p = ansi_skip(&str[i], len - i)) > 0) {
i += p;
continue;
}
n++;
i++;
}
return n;
}
size_t
ansi_skip(const char* str, size_t len) {
size_t pos = 0;
if(str[pos] == 0x1b) {
if(++pos < len && str[pos] == '[') {
while(++pos < len)
if(is_alphanumeric_char(str[pos]))
break;
if(++pos < len && str[pos] == '~')
++pos;
return pos;
}
}
return 0;
}
size_t
ansi_truncate(const char* str, size_t len, size_t limit) {
size_t i, n = 0, p;
for(i = 0; i < len;) {
if((p = ansi_skip(&str[i], len - i)) > 0) {
i += p;
continue;
}
n += is_escape_char(str[i]) ? 2 : 1;
i++;
if(n > limit)
break;
}
return i;
}
int64_t
array_search(void* a, size_t m, size_t elsz, void* needle) {
char* ptr = a;
int64_t n, ret;
n = m / elsz;
for(ret = 0; ret < n; ret++) {
if(!memcmp(ptr, needle, elsz))
return ret;
ptr += elsz;
}
return -1;
}
char*
str_escape(const char* s) {
DynBuf dbuf;
dbuf_init2(&dbuf, 0, 0);
dbuf_put_escaped(&dbuf, s, strlen(s));
dbuf_0(&dbuf);
return (char*)dbuf.buf;
}
char*
byte_escape(const void* s, size_t n) {
DynBuf dbuf;
dbuf_init2(&dbuf, 0, 0);
dbuf_put_escaped(&dbuf, s, n);
dbuf_0(&dbuf);
return (char*)dbuf.buf;
}
size_t
byte_findb(const void* haystack, size_t hlen, const void* what, size_t wlen) {
size_t i, last;
const char* s = (const char*)haystack;
if(hlen < wlen)
return hlen;
last = hlen - wlen;
for(i = 0; i <= last; i++) {
if(byte_equal(s, wlen, what))
return i;
s++;
}
return hlen;
}
size_t
byte_finds(const void* haystack, size_t hlen, const char* what) {
return byte_findb(haystack, hlen, what, strlen(what));
}
size_t
byte_equal(const void* s, size_t n, const void* t) {
return memcmp(s, t, n) == 0;
}
void
byte_copy(void* out, size_t len, const void* in) {
char* s = (char*)out;
const char* t = (const char*)in;
size_t i;
for(i = 0; i < len; ++i)
s[i] = t[i];
}
void
byte_copyr(void* out, size_t len, const void* in) {
char* s = (char*)out + len;
const char* t = (const char*)in;
const char* u = t + len;
for(;;) {
if(t >= u)
break;
--u;
--s;
*s = *u;
}
}
size_t
byte_rchrs(const char* in, size_t len, const char needles[], size_t nn) {
const char *s = in, *end = in + len, *found = 0;
size_t i;
for(; s < end; s++) {
for(i = 0; i < nn; ++i) {
if(*s == needles[i])
found = s;
}
}
return (size_t)((found ? found : s) - in);
}
char*
dbuf_at_n(const DynBuf* db, size_t i, size_t* n, char sep) {
size_t p, l = 0;
for(p = 0; p < db->size; ++p) {
if(l == i) {
*n = byte_chr((const char*)&db->buf[p], db->size - p, sep);
return (char*)&db->buf[p];
}
if(db->buf[p] == sep)
++l;
}
*n = 0;
return 0;
}
const char*
dbuf_last_line(DynBuf* db, size_t* len) {
size_t i;
if((i = byte_rchr(db->buf, db->size, '\n')) < db->size)
i++;
else
i = 0;
if(len)
*len = db->size - i;
return (const char*)&db->buf[i];
}
int
dbuf_prepend(DynBuf* s, const uint8_t* data, size_t len) {
int ret;
if(!(ret = dbuf_reserve_start(s, len)))
memcpy(s->buf, data, len);
return 0;
}
void
dbuf_put_colorstr(DynBuf* db, const char* str, const char* color, int with_color) {
if(with_color)
dbuf_putstr(db, color);
dbuf_putstr(db, str);
if(with_color)
dbuf_putstr(db, COLOR_NONE);
}
void
dbuf_put_escaped_pred(DynBuf* db, const char* str, size_t len, int (*pred)(int)) {
size_t i = 0, j;
char c;
while(i < len) {
if((j = predicate_find(&str[i], len - i, pred))) {
dbuf_append(db, (const uint8_t*)&str[i], j);
i += j;
}
if(i == len)
break;
dbuf_putc(db, '\\');
if(str[i] == 0x1b) {
dbuf_append(db, (const uint8_t*)"x1b", 3);
} else {
int r = pred(str[i]);
dbuf_putc(db, (r > 1 && r <= 127) ? r : (c = escape_char_letter(str[i])) ? c : str[i]);
if(r == 'u' || r == 'x')
dbuf_printf(db, r == 'u' ? "%04x" : "%02x", str[i]);
}
i++;
}
}
const uint8_t escape_url_tab[256] = {
'%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%',
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '%',
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
'%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%', '%',
};
const uint8_t escape_noquote_tab[256] = {
'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 0x62, 0x74, 0x6e, 0x76, 0x66, 0x72, 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x',
'x', 'x', 'x', 'x', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'x', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'u', 'u', 'u', 'u', 'u',
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u',
};
const uint8_t escape_singlequote_tab[256] = {
'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 0x62, 0x74, 0x6e, 0x76, 0x66, 0x72, 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x',
'x', 'x', 'x', 'x', 0, 0, 0, 0, 0, 0, 0, 0x27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'x', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'u', 'u', 'u', 'u', 'u',
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u',
};
const uint8_t escape_doublequote_tab[256] = {
'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 0x62, 0x74, 0x6e, 0x76, 0x66, 0x72, 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x',
'x', 'x', 'x', 'x', 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'x', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'u', 'u', 'u', 'u', 'u',
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u',
};
const uint8_t escape_backquote_tab[256] = {
'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 0x62, 0x74, 0, 0x76, 0x66, 0, 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x',
'x', 'x', 'x', 'x', 0, 0, 0, 0, 0x24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, 0x60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'x', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'u', 'u', 'u', 'u', 'u',
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u',
};
void
dbuf_put_escaped_table(DynBuf* db, const char* str, size_t len, const uint8_t table[256]) {
size_t i = 0, clen;
int32_t c;
const uint8_t *pos, *end, *next;
for(pos = (const uint8_t*)str, end = pos + len; pos < end; pos = next) {
uint8_t r, ch;
if((c = unicode_from_utf8(pos, end - pos, &next)) < 0)
break;
clen = next - pos;
ch = c;
r = (clen >= 2 || c > 0xff) ? 'u' : table[c];
if(r == 'u' && clen > 1 && (c & 0xffffff00) == 0) {
r = 'x';
// ch = c >> 8;
}
if(r == '%') {
static const char hexdigits[] = "0123456789ABCDEF";
dbuf_putc(db, '%');
dbuf_putc(db, hexdigits[c >> 4]);
dbuf_putc(db, hexdigits[c & 0xf]);
} else if(c == 0x1b) {
dbuf_putstr(db, "\\x1b");
} else if(r == 'u') {
dbuf_printf(db, c > 0xffff ? "\\u{%X}" : "\\u%04x", c);
} else if(r == 'x') {
dbuf_printf(db, "\\x%02x", ch);
} else if(r) {
dbuf_putc(db, '\\');
dbuf_putc(db, (r > 1 && r <= 127) ? r : (c = escape_char_letter(ch)) ? c : ch);
} else {
dbuf_put(db, pos, next - pos);
}
i++;
}
}
void
dbuf_put_unescaped_pred(DynBuf* db, const char* str, size_t len, int (*pred)(const char*, size_t*)) {
size_t i = 0, j;
// char c;
while(i < len) {
int r = 0;
if((j = byte_chr(&str[i], len - i, '\\'))) {
dbuf_append(db, (const uint8_t*)&str[i], j);
i += j;
}
if(i == len)
break;
size_t n = 1;
if(pred) {
r = pred(&str[i + 1], &n);
if(!r && n == 1)
dbuf_putc(db, '\\');
}
if(r >= 0)
dbuf_putc(db, /*n > 1 ||*/ r ? /*(r > 1 && r < 256) ?*/ r : str[i]);
i += n;
}
}
static inline int
hexdigit(char c) {
if(c >= '0' && c <= '9')
return c - '0';
if(c >= 'a' && c <= 'f')
c -= 32;
if(c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
void
dbuf_put_unescaped_table(DynBuf* db, const char* str, size_t len, const uint8_t table[256]) {
size_t i = 0, j;
char escape_char = table == escape_url_tab ? '%' : '\\';
while(i < len) {
if((j = byte_chr(&str[i], len - i, escape_char))) {
dbuf_append(db, (const uint8_t*)&str[i], j);
i += j;
}
if(i == len)
break;
if(escape_char == '%') {
int hi = hexdigit(str[i + 1]), lo = hexdigit(str[i + 2]);
uint8_t c = (hi << 4) | (lo & 0xf);
dbuf_putc(db, c);
i += 2;
} else {
++i;
uint8_t c;
switch(str[i]) {
case 'b': c = '\b'; break;
case 't': c = '\t'; break;
case 'n': c = '\n'; break;
case 'v': c = '\v'; break;
case 'f': c = '\f'; break;
case 'r': c = '\r'; break;
default: c = str[i]; break;
}
uint8_t r = table[c];
if(!(r && r != 'x' && r != 'u')) {
dbuf_putc(db, '\\');
dbuf_putc(db, c);
} else {
dbuf_putc(db, str[i] == r ? c : r);
}
}
++i;
}
}
void
dbuf_put_escaped(DynBuf* db, const char* str, size_t len) {
return dbuf_put_escaped_table(db, str, len, escape_noquote_tab);
}
void
dbuf_put_value(DynBuf* db, JSContext* ctx, JSValueConst value) {
const char* str;
size_t len;
str = JS_ToCStringLen(ctx, &len, value);
dbuf_append(db, str, len);
JS_FreeCString(ctx, str);
}
void
dbuf_put_uint32(DynBuf* db, uint32_t num) {
char buf[FMT_ULONG];
dbuf_put(db, (const uint8_t*)buf, fmt_ulong(buf, num));
}
void
dbuf_put_atom(DynBuf* db, JSContext* ctx, JSAtom atom) {
const char* str;
str = JS_AtomToCString(ctx, atom);
dbuf_putstr(db, str);
JS_FreeCString(ctx, str);
}
int
dbuf_reserve_start(DynBuf* s, size_t len) {
if(unlikely((s->size + len) > s->allocated_size)) {
if(dbuf_realloc(s, s->size + len))
return -1;
}
if(s->size > 0)
memcpy(s->buf + len, s->buf, s->size);
s->size += len;
return 0;
}
uint8_t*
dbuf_reserve(DynBuf* s, size_t len) {
if(unlikely((s->size + len) > s->allocated_size))
if(dbuf_realloc(s, s->size + len))
return 0;
return &s->buf[s->size];
}
size_t
dbuf_token_pop(DynBuf* db, char delim) {
size_t n, p, len;
len = db->size;
for(n = db->size; n > 0;) {
if((p = byte_rchr(db->buf, n, delim)) == n) {
db->size = 0;
break;
}
if(p > 0 && db->buf[p - 1] == '\\') {
n = p - 1;
continue;
}
db->size = p;
break;
}
return len - db->size;
}
size_t
dbuf_token_push(DynBuf* db, const char* str, size_t len, char delim) {
size_t pos;
if(db->size)
dbuf_putc(db, delim);
pos = db->size;
dbuf_put_escaped_pred(db, str, len, is_dot_char);
return db->size - pos;
}
JSValue
dbuf_tostring_free(DynBuf* s, JSContext* ctx) {
JSValue r;
r = JS_NewStringLen(ctx, s->buf ? (const char*)s->buf : "", s->buf ? s->size : 0);
dbuf_free(s);
return r;
}
ssize_t
dbuf_load(DynBuf* s, const char* filename) {
FILE* fp;
size_t nbytes = 0;
if((fp = fopen(filename, "rb"))) {
char buf[4096];
size_t r;
while(!feof(fp)) {
if((r = fread(buf, 1, sizeof(buf), fp)) == 0) {
fclose(fp);
return -1;
}
dbuf_put(s, (uint8_t const*)buf, r);
nbytes += r;
}
fclose(fp);
}
return nbytes;
}
int
dbuf_vprintf(DynBuf* s, const char* fmt, va_list ap) {
s->size += vsnprintf((char*)(s->buf + s->size), s->allocated_size - s->size, fmt, ap);
return 0;
}
InputBuffer
js_input_buffer(JSContext* ctx, JSValueConst value) {
InputBuffer ret = {{{0, 0}}, 0, &input_buffer_free_default, JS_UNDEFINED, {0, INT64_MAX}};
if(js_is_typedarray(ctx, value)) {
ret.value = offset_typedarray(&ret.range, value, ctx);
} else if(js_is_arraybuffer(ctx, value) || js_is_sharedarraybuffer(ctx, value)) {
ret.value = JS_DupValue(ctx, value);
}
if(js_is_arraybuffer(ctx, ret.value) || js_is_sharedarraybuffer(ctx, ret.value)) {
block_arraybuffer(&ret.block, ret.value, ctx);
} else {
JS_ThrowTypeError(ctx, "Invalid type (%s) for input buffer", js_value_typestr(ctx, ret.value));
JS_FreeValue(ctx, ret.value);
ret.value = JS_EXCEPTION;
}
return ret;
}
#undef free
InputBuffer
js_input_chars(JSContext* ctx, JSValueConst value) {
InputBuffer ret = {{{0, 0}}, 0, &input_buffer_free_default, JS_UNDEFINED, OFFSET_INIT()};
if(JS_IsString(value)) {
ret.data = (uint8_t*)JS_ToCStringLen(ctx, &ret.size, value);
ret.value = JS_DupValue(ctx, value);
ret.free = &input_buffer_free_default;
} else {
ret = js_input_buffer(ctx, value);
}
return ret;
}
InputBuffer
js_input_args(JSContext* ctx, int argc, JSValueConst argv[]) {
InputBuffer input = js_input_chars(ctx, argv[0]);
if(argc > 1)
js_offset_length(ctx, input.size, argc - 1, argv + 1, &input.range);
return input;
}
InputBuffer
js_output_args(JSContext* ctx, int argc, JSValueConst argv[]) {
InputBuffer output = js_input_buffer(ctx, argv[0]);
if(argc > 1)
js_offset_length(ctx, output.size, argc - 1, argv + 1, &output.range);
return output;
}
BOOL
input_buffer_valid(const InputBuffer* in) {
return !JS_IsException(in->value);
}
InputBuffer
input_buffer_clone(const InputBuffer* in, JSContext* ctx) {
InputBuffer ret = js_input_buffer(ctx, in->value);
ret.pos = in->pos;
ret.size = in->size;
ret.free = in->free;
return ret;
}
void
input_buffer_dump(const InputBuffer* in, DynBuf* db) {
dbuf_printf(db, "(InputBuffer){ .data = %p, .size = %lu, .pos = %lu, .free = %p }", in->data, (unsigned long)in->size, (unsigned long)in->pos, in->free);
}
void
input_buffer_free(InputBuffer* in, JSContext* ctx) {
if(in->data) {
in->free(ctx, (const char*)in->data, in->value);
in->data = 0;
in->size = 0;
in->pos = 0;
in->value = JS_UNDEFINED;
}
}
const uint8_t*
input_buffer_peek(InputBuffer* in, size_t* lenp) {
input_buffer_peekc(in, lenp);
return input_buffer_data(in) + in->pos;
}
const uint8_t*
input_buffer_get(InputBuffer* in, size_t* lenp) {
const uint8_t* ret = input_buffer_peek(in, lenp);
in->pos += *lenp;
return ret;
}
const char*
input_buffer_currentline(InputBuffer* in, size_t* len) {
size_t i;
if((i = byte_rchr(input_buffer_data(in), in->pos, '\n')) < in->pos)
i++;
if(len)
*len = in->pos - i;
return (const char*)&input_buffer_data(in)[i];
}
size_t
input_buffer_column(InputBuffer* in, size_t* len) {
size_t i;
if((i = byte_rchr(input_buffer_data(in), in->pos, '\n')) < in->pos)
i++;
return in->pos - i;
}
int
js_offset_length(JSContext* ctx, int64_t size, int argc, JSValueConst argv[], OffsetLength* off_len_p) {
int ret = 0;
int64_t off = 0, len = size;
if(argc >= 1 && JS_IsNumber(argv[0])) {
if(!JS_ToInt64(ctx, &off, argv[0]))
ret = 1;
if(argc >= 2 && JS_IsNumber(argv[1]))
if(!JS_ToInt64(ctx, &len, argv[1]))
ret = 2;
if(size && off != size)
off = ((off % size) + size) % size;
if(len >= 0)
len = MIN_NUM(len, size - off);
else
len = size - off;
if(off_len_p) {
off_len_p->offset = off;
off_len_p->length = len;
}
}
return ret;
}
int
js_index_range(JSContext* ctx, int64_t size, int argc, JSValueConst argv[], IndexRange* idx_rng_p) {
int ret = 0;
int64_t start = 0, end = size;
if(argc >= 1 && JS_IsNumber(argv[0])) {
if(!JS_ToInt64(ctx, &start, argv[0]))
ret = 1;
if(argc >= 2 && JS_IsNumber(argv[1]))
if(!JS_ToInt64(ctx, &end, argv[1]))
ret = 2;
if(size > 0) {
start = ((start % size) + size) % size;
end = ((end % size) + size) % size;
}
if(end > size)
end = size;
if(idx_rng_p) {
idx_rng_p->start = start;
idx_rng_p->end = end;
}
}
return ret;
}
int
screen_size(int size[2]) {
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
size[0] = csbi.srWindow.Right - csbi.srWindow.Left + 1;
size[1] = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
return 0;
#elif defined(HAVE_TERMIOS_H)
{
struct winsize w = {.ws_col = -1, .ws_row = -1};
if(isatty(STDIN_FILENO))
ioctl(STDIN_FILENO, TIOCGWINSZ, &w);
else if(isatty(STDOUT_FILENO))
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
else if(isatty(STDERR_FILENO))
ioctl(STDERR_FILENO, TIOCGWINSZ, &w);
size[0] = w.ws_col;
size[1] = w.ws_row;
return 0;
}
#else
size[0] = 80;
size[1] = 25;
return 0;
#endif
return -1;
}
#undef js_realloc_rt
void
js_dbuf_allocator(JSContext* ctx, DynBuf* s) {
dbuf_init2(s, JS_GetRuntime(ctx), (DynBufReallocFunc*)js_realloc_rt);
}
inline int
input_buffer_peekc(InputBuffer* in, size_t* lenp) {
const uint8_t *pos, *end, *next;
int cp;
pos = input_buffer_data(in) + in->pos;
end = input_buffer_data(in) + input_buffer_length(in);
cp = unicode_from_utf8(pos, end - pos, &next);
*lenp = next - pos;
return cp;
}
inline int
input_buffer_putc(InputBuffer* in, unsigned int c, JSContext* ctx) {
int len;
if(in->pos + UTF8_CHAR_LEN_MAX > in->size)
if(block_realloc(&in->block, in->pos + UTF8_CHAR_LEN_MAX, ctx))
return -1;
len = unicode_to_utf8(&in->data[in->pos], c);
in->pos += len;
return len;
}
size_t
dbuf_bitflags(DynBuf* db, uint32_t bits, const char* const names[]) {
size_t i, n = 0;
for(i = 0; i < sizeof(bits) * 8; i++) {
if(bits & (1 << i)) {
size_t len = strlen(names[i]);
if(n) {
n++;
dbuf_putstr(db, "|");
}
dbuf_append(db, names[i], len);
n += len;
}
}
return n;
}
/**
* @}
*/

View File

@@ -1,348 +0,0 @@
#ifndef BUFFER_UTILS_H
#define BUFFER_UTILS_H
#include <quickjs.h>
#include <cutils.h>
#include <stdarg.h>
#include "char-utils.h"
/**
* \defgroup buffer-utils buffer-utils: Buffer Utilities
* @{
*/
int64_t array_search(void* a, size_t m, size_t elsz, void* needle);
#define array_contains(a, m, elsz, needle) (array_search((a), (m), (elsz), (needle)) != -1)
size_t ansi_length(const char*, size_t);
size_t ansi_skip(const char*, size_t);
size_t ansi_truncate(const char*, size_t, size_t limit);
int64_t array_search(void*, size_t, size_t elsz, void* needle);
char* str_escape(const char*);
char* byte_escape(const void*, size_t);
size_t byte_findb(const void*, size_t, const void* what, size_t wlen);
size_t byte_finds(const void*, size_t, const char* what);
size_t byte_equal(const void* s, size_t n, const void* t);
void byte_copy(void* out, size_t len, const void* in);
void byte_copyr(void* out, size_t len, const void* in);
size_t byte_rchrs(const char* in, size_t len, const char needles[], size_t nn);
#define DBUF_INIT_0() \
(DynBuf) { 0, 0, 0, 0, 0, 0 }
#define DBUF_INIT_CTX(ctx) \
(DynBuf) { 0, 0, 0, 0, (DynBufReallocFunc*)js_realloc_rt, JS_GetRuntime(ctx) }
extern const uint8_t escape_url_tab[256], escape_noquote_tab[256], escape_singlequote_tab[256], escape_doublequote_tab[256], escape_backquote_tab[256];
char* dbuf_at_n(const DynBuf*, size_t, size_t* n, char sep);
const char* dbuf_last_line(DynBuf*, size_t*);
int dbuf_prepend(DynBuf*, const uint8_t*, size_t len);
void dbuf_put_colorstr(DynBuf*, const char*, const char* color, int with_color);
void dbuf_put_escaped_pred(DynBuf*, const char*, size_t len, int (*pred)(int));
void dbuf_put_escaped_table(DynBuf*, const char*, size_t len, const uint8_t table[256]);
void dbuf_put_unescaped_table(DynBuf* db, const char* str, size_t len, const uint8_t table[256]);
void dbuf_put_unescaped_pred(DynBuf*, const char*, size_t len, int (*pred)());
void dbuf_put_escaped(DynBuf*, const char*, size_t len);
void dbuf_put_value(DynBuf*, JSContext*, JSValue value);
void dbuf_put_uint32(DynBuf* db, uint32_t num);
void dbuf_put_atom(DynBuf* db, JSContext* ctx, JSAtom atom);
int dbuf_reserve_start(DynBuf*, size_t);
uint8_t* dbuf_reserve(DynBuf*, size_t);
size_t dbuf_token_pop(DynBuf*, char);
size_t dbuf_token_push(DynBuf*, const char*, size_t len, char delim);
JSValue dbuf_tostring_free(DynBuf*, JSContext*);
ssize_t dbuf_load(DynBuf*, const char*);
int dbuf_vprintf(DynBuf*, const char*, va_list);
int screen_size(int size[2]);
static inline int
dbuf_putm(DynBuf* db, ...) {
int r = 0;
va_list a;
const char* s;
va_start(a, db);
while((s = va_arg(a, char*)))
if(dbuf_putstr(db, s))
return -1;
va_end(a);
return r;
}
#define dbuf_append(d, x, n) dbuf_put((d), (const uint8_t*)(x), (n))
static inline size_t
dbuf_count(DynBuf* db, int ch) {
return byte_count(db->buf, db->size, ch);
}
static inline void
dbuf_0(DynBuf* db) {
dbuf_putc(db, '\0');
db->size--;
}
static inline void
dbuf_zero(DynBuf* db) {
dbuf_realloc(db, 0);
db->size = 0;
}
size_t dbuf_bitflags(DynBuf* db, uint32_t bits, const char* const names[]);
#define js_dbuf_init(ctx, buf) dbuf_init2((buf), (ctx), (realloc_func*)&utils_js_realloc)
#define js_dbuf_init_rt(rt, buf) dbuf_init2((buf), (rt), (realloc_func*)&utils_js_realloc_rt)
void js_dbuf_allocator(JSContext* ctx, DynBuf* s);
typedef struct {
uint8_t* base;
size_t size;
} MemoryBlock;
static inline void
block_init(MemoryBlock* mb) {
mb->base = 0;
mb->size = 0;
}
/* clang-format off */
static inline void* block_data(const MemoryBlock* mb) { return mb->base; }
static inline size_t block_length(const MemoryBlock* mb) { return mb->size; }
static inline void* block_begin(const MemoryBlock* mb) { return mb->base; }
static inline void* block_end(const MemoryBlock* mb) { return mb->base + mb->size; }
/* clang-format on */
static inline BOOL
block_arraybuffer(MemoryBlock* mb, JSValueConst ab, JSContext* ctx) {
mb->base = JS_GetArrayBuffer(ctx, &mb->size, ab);
return mb->base != 0;
}
typedef struct {
uint8_t *start, *end;
} PointerRange;
static inline void
range_init(PointerRange* pr) {
pr->end = pr->start = 0;
}
static inline PointerRange
range_from(const MemoryBlock* mb) {
return (PointerRange){mb->base, mb->base + mb->size};
}
typedef struct {
int64_t start, end;
} IndexRange;
typedef struct {
int64_t offset, length;
} OffsetLength;
#define OFFSET_INIT() \
(OffsetLength) { 0, INT64_MAX }
static inline void
offset_init(OffsetLength* ol) {
ol->offset = 0;
ol->length = INT64_MAX;
}
static inline BOOL
offset_is_default(const OffsetLength* ol) {
return ol->offset == 0 && ol->length == INT64_MAX;
}
static inline void*
offset_data(const OffsetLength* ol, const void* x) {
return (uint8_t*)x + ol->offset;
}
static inline size_t
offset_size(const OffsetLength* ol, size_t n) {
if(ol->length == -1)
return (signed long)n - ol->offset;
return MIN_NUM(ol->length, (signed long)n - ol->offset);
}
static inline MemoryBlock
offset_block(const OffsetLength* ol, const void* x, size_t n) {
return (MemoryBlock){offset_data(ol, x), offset_size(ol, n)};
}
static inline PointerRange
offset_range(const OffsetLength* ol, const void* x, size_t n) {
MemoryBlock mb = offset_block(ol, x, n);
return range_from(&mb);
}
static inline OffsetLength
offset_slice(const OffsetLength ol, int64_t start, int64_t end) {
if(start < 0)
start = ol.length + (start % ol.length);
else if(start > ol.length)
start = ol.length;
if(end < 0)
end = ol.length + (end % ol.length);
else if(end > ol.length)
end = ol.length;
return (OffsetLength){start, end - start};
}
static inline OffsetLength
offset_offset(const OffsetLength* ol, const OffsetLength* by) {
OffsetLength ret;
ret.offset = ol->offset + by->offset;
ret.length = MIN_NUM(by->length, ol->length - by->offset);
return ret;
}
static inline OffsetLength
offset_from_indexrange(const IndexRange* ir) {
OffsetLength ret;
ret.offset = ir->start;
ret.length = ir->end - ir->start;
return ret;
}
static inline JSValue
offset_typedarray(OffsetLength* ol, JSValueConst array, JSContext* ctx) {
JSValue ret;
size_t offset, length;
ret = JS_GetTypedArrayBuffer(ctx, array, &offset, &length, NULL);
if(!JS_IsException(ret)) {
ol->offset = offset;
ol->length = length;
}
return ret;
}
static inline IndexRange
indexrange_from_offset(const OffsetLength* ol) {
IndexRange ret;
ret.start = ol->offset;
ret.end = ol->offset + ol->length;
return ret;
}
static inline MemoryBlock
block_range(const MemoryBlock* mb, const OffsetLength* range) {
MemoryBlock ret;
ret.base = mb->base + range->offset;
ret.size = MIN_NUM((size_t)range->length, mb->size - range->offset);
return ret;
}
static inline int
block_realloc(MemoryBlock* mb, size_t new_size, JSContext* ctx) {
if((mb->base = js_realloc(ctx, mb->base, new_size))) {
mb->size = new_size;
return 0;
}
return -1;
}
typedef struct InputBuffer {
union {
MemoryBlock block;
struct {
uint8_t* data;
size_t size;
};
};
size_t pos;
void (*free)(JSContext*, const char*, JSValue);
JSValue value;
OffsetLength range;
} InputBuffer;
static inline void
input_buffer_free_default(JSContext* ctx, const char* str, JSValue val) {
if(JS_IsString(val))
JS_FreeCString(ctx, str);
if(!JS_IsUndefined(val))
JS_FreeValue(ctx, val);
}
InputBuffer js_input_buffer(JSContext* ctx, JSValueConst value);
InputBuffer js_input_chars(JSContext* ctx, JSValueConst value);
InputBuffer js_input_args(JSContext* ctx, int argc, JSValueConst argv[]);
InputBuffer js_output_args(JSContext* ctx, int argc, JSValueConst argv[]);
InputBuffer input_buffer_clone(const InputBuffer* in, JSContext* ctx);
BOOL input_buffer_valid(const InputBuffer* in);
void input_buffer_dump(const InputBuffer* in, DynBuf* db);
void input_buffer_free(InputBuffer* in, JSContext* ctx);
static inline uint8_t*
input_buffer_data(const InputBuffer* in) {
return offset_data(&in->range, in->data);
}
static inline size_t
input_buffer_length(const InputBuffer* in) {
return offset_size(&in->range, in->size);
}
static inline MemoryBlock
input_buffer_block(InputBuffer* in) {
return (MemoryBlock){input_buffer_data(in), input_buffer_length(in)};
}
static inline MemoryBlock*
input_buffer_blockptr(InputBuffer* in) {
return &in->block;
}
const uint8_t* input_buffer_get(InputBuffer* in, size_t* lenp);
const uint8_t* input_buffer_peek(InputBuffer* in, size_t* lenp);
const char* input_buffer_currentline(InputBuffer*, size_t* len);
size_t input_buffer_column(InputBuffer*, size_t* len);
int input_buffer_peekc(InputBuffer* in, size_t* lenp);
int input_buffer_putc(InputBuffer*, unsigned int, JSContext*);
static inline int
input_buffer_getc(InputBuffer* in) {
size_t n;
int ret;
ret = input_buffer_peekc(in, &n);
in->pos += n;
return ret;
}
static inline void*
input_buffer_begin(const InputBuffer* in) {
return input_buffer_data(in);
}
static inline void*
input_buffer_end(const InputBuffer* in) {
return input_buffer_data(in) + input_buffer_length(in);
}
static inline BOOL
input_buffer_eof(const InputBuffer* in) {
return in->pos == input_buffer_length(in);
}
static inline size_t
input_buffer_remain(const InputBuffer* in) {
return input_buffer_length(in) - in->pos;
}
int js_offset_length(JSContext*, int64_t size, int argc, JSValueConst argv[], OffsetLength* off_len_p);
int js_index_range(JSContext*, int64_t size, int argc, JSValueConst argv[], IndexRange* idx_rng_p);
/**
* @}
*/
#endif /* defined(BUFFER_UTILS) */

View File

@@ -1,578 +0,0 @@
#include "char-utils.h"
#include "libutf.h"
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MSYS__)
#include <winnls.h>
#include <windows.h>
#include <wchar.h>
#endif
/**
* \addtogroup char-utils
* @{
*/
size_t
token_length(const char* str, size_t len, char delim) {
const char *s, *e;
size_t pos;
for(s = str, e = s + len; s < e; s += pos + 1) {
pos = byte_chr(s, e - s, delim);
if(s + pos == e)
break;
if(pos == 0 || s[pos - 1] != '\\') {
s += pos;
break;
}
}
return s - str;
}
size_t
fmt_ulong(char* dest, uint32_t i) {
uint32_t len, tmp, len2;
for(len = 1, tmp = i; tmp > 9; ++len)
tmp /= 10;
if(dest)
for(tmp = i, dest += len, len2 = len + 1; --len2; tmp /= 10)
*--dest = (char)((tmp % 10) + '0');
return len;
}
size_t
fmt_longlong(char* dest, int64_t i) {
if(i < 0) {
if(dest)
*dest++ = '-';
return fmt_ulonglong(dest, (uint64_t)-i) + 1;
}
return fmt_ulonglong(dest, (uint64_t)i);
}
size_t
fmt_ulonglong(char* dest, uint64_t i) {
size_t len;
uint64_t tmp, len2;
for(len = 1, tmp = i; tmp > 9ll; ++len)
tmp /= 10ll;
if(dest)
for(tmp = i, dest += len, len2 = len + 1; --len2; tmp /= 10ll)
*--dest = (tmp % 10ll) + '0';
return len;
}
#define tohex(c) (char)((c) >= 10 ? (c)-10 + 'a' : (c) + '0')
size_t
fmt_xlonglong(char* dest, uint64_t i) {
uint64_t len, tmp;
for(len = 1, tmp = i; tmp > 15ll; ++len)
tmp >>= 4ll;
if(dest)
for(tmp = i, dest += len;;) {
*--dest = tohex(tmp & 15ll);
if(!(tmp >>= 4ll))
break;
}
return len;
}
size_t
fmt_xlonglong0(char* dest, uint64_t num, size_t n) {
size_t i = 0, len;
if((len = fmt_xlonglong(NULL, num)) < n) {
len = n - len;
while(i < len)
dest[i++] = '0';
}
i += fmt_xlonglong(&dest[i], num);
return i;
}
size_t
fmt_8long(char* dest, uint32_t i) {
uint32_t len, tmp;
/* first count the number of bytes needed */
for(len = 1, tmp = i; tmp > 7; ++len)
tmp >>= 3;
if(dest)
for(tmp = i, dest += len;;) {
*--dest = (char)((tmp & 7) + '0');
if(!(tmp >>= 3))
break;
}
return len;
}
#define tohex(c) (char)((c) >= 10 ? (c)-10 + 'a' : (c) + '0')
size_t
fmt_xlong(char* dest, uint32_t i) {
uint32_t len, tmp;
/* first count the number of bytes needed */
for(len = 1, tmp = i; tmp > 15; ++len)
tmp >>= 4;
if(dest)
for(tmp = i, dest += len;;) {
*--dest = tohex(tmp & 15);
if(!(tmp >>= 4))
break;
}
return len;
}
size_t
fmt_xlong0(char* dest, uint32_t num, size_t n) {
size_t i = 0, len;
if((len = fmt_xlong(NULL, num)) < n) {
len = n - len;
while(i < len)
dest[i++] = '0';
}
i += fmt_xlong(&dest[i], num);
return i;
}
size_t
scan_ushort(const char* src, uint16_t* dest) {
const char* cur;
uint16_t l;
for(cur = src, l = 0; *cur >= '0' && *cur <= '9'; ++cur) {
uint32_t tmp = l * 10ul + *cur - '0';
if((uint16_t)tmp != tmp)
break;
l = tmp;
}
if(cur > src)
*dest = l;
return (size_t)(cur - src);
}
size_t
scan_uint(const char* src, uint32_t* dest) {
uint64_t u64;
size_t r = scan_ulonglong(src, &u64);
*dest = u64;
return r;
}
size_t
scan_int(const char* src, int32_t* dest) {
int64_t i64;
size_t r = scan_longlong(src, &i64);
*dest = i64;
return r;
}
#ifndef MAXLONG
#define MAXLONG (((uint32_t)-1) >> 1)
#endif
size_t
scan_longlong(const char* src, int64_t* dest) {
size_t i, o;
uint64_t l;
char c = src[0];
unsigned int neg = c == '-';
o = c == '-' || c == '+';
if((i = scan_ulonglong(src + o, &l))) {
if(i > 0ll && l > MAXLONG + neg) {
l /= 10ll;
--i;
}
if(i + o)
*dest = (int64_t)(c == '-' ? -l : l);
return i + o;
}
return 0;
}
size_t
scan_ulonglong(const char* src, uint64_t* dest) {
const char* tmp = src;
uint64_t l = 0;
unsigned char c;
while((c = (unsigned char)(*tmp - '0')) < 10) {
uint64_t n;
n = l << 3ll;
if((n >> 3ll) != l)
break;
if(n + (l << 1ll) < n)
break;
n += l << 1ll;
if(n + c < n)
break;
l = n + c;
++tmp;
}
if(tmp - src)
*dest = l;
return (size_t)(tmp - src);
}
size_t
scan_xlonglong(const char* src, uint64_t* dest) {
const char* tmp = src;
int64_t l = 0;
unsigned char c;
while((c = scan_fromhex(*tmp)) < 16) {
l = (l << 4) + c;
++tmp;
}
*dest = l;
return tmp - src;
}
size_t
scan_8longn(const char* src, size_t n, uint32_t* dest) {
const char* tmp = src;
uint32_t l = 0;
unsigned char c;
while(n-- > 0 && (c = (unsigned char)(*tmp - '0')) < 8) {
if(l >> (sizeof(l) * 8 - 3))
break;
l = l * 8 + c;
++tmp;
}
*dest = l;
return (size_t)(tmp - src);
}
size_t
scan_whitenskip(const char* s, size_t limit) {
const char *t, *u;
for(t = s, u = t + limit; t < u; ++t)
if(!is_whitespace_char(*t))
break;
return (size_t)(t - s);
}
size_t
scan_nonwhitenskip(const char* s, size_t limit) {
const char *t, *u;
for(t = s, u = t + limit; t < u; ++t)
if(is_whitespace_char(*t))
break;
return (size_t)(t - s);
}
size_t
scan_line(const char* s, size_t limit) {
const char *t, *u;
for(t = s, u = s + limit; t < u; ++t)
if(*t == '\n' || *t == '\r')
break;
return (size_t)(t - s);
}
size_t
scan_lineskip(const char* s, size_t limit) {
const char *t, *u;
for(t = s, u = s + limit; t < u; ++t)
if(*t == '\n') {
++t;
break;
}
return (size_t)(t - s);
}
size_t
scan_lineskip_escaped(const char* s, size_t limit) {
const char *t, *u;
for(t = s, u = s + limit; t < u; ++t) {
if(*t == '\\') {
++t;
continue;
}
if(*t == '\n') {
++t;
break;
}
}
return (size_t)(t - s);
}
size_t
scan_eolskip(const char* s, size_t limit) {
size_t n = 0;
if(n + 1 < limit && s[0] == '\r' && s[1] == '\n')
n += 2;
else if(n < limit && s[0] == '\n')
n += 1;
return n;
}
size_t
utf8_strlen(const void* in, size_t len) {
const uint8_t *pos, *end, *next;
size_t i = 0;
for(pos = (const uint8_t*)in, end = pos + len; pos < end; pos = next, ++i)
unicode_from_utf8(pos, end - pos, &next);
return i;
}
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MSYS__)
wchar_t*
utf8_towcs(const char* s) {
int len = (int)strlen(s);
int n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0);
wchar_t* ret;
if((ret = (wchar_t*)malloc((n + 1) * sizeof(wchar_t)))) {
MultiByteToWideChar(CP_UTF8, 0, s, len, ret, n);
ret[n] = L'\0';
}
return ret;
}
char*
utf8_fromwcs(const wchar_t* wstr) {
int len = (int)wcslen(wstr);
int n = WideCharToMultiByte(CP_UTF8, 0, wstr, len, NULL, 0, NULL, NULL);
char* ret;
if((ret = malloc((n + 1)))) {
WideCharToMultiByte(CP_UTF8, 0, wstr, len, ret, n, NULL, NULL);
ret[n] = '\0';
}
return ret;
}
#endif
BOOL
utf16_multiword(const void* in) {
const uint16_t* p16 = in;
LibutfC16Type type = libutf_c16_type(p16[0]);
return !((LIBUTF_UTF16_NOT_SURROGATE == type) || (LIBUTF_UTF16_SURROGATE_HIGH != type || LIBUTF_UTF16_SURROGATE_LOW != libutf_c16_type(p16[1])));
}
int
case_lowerc(int c) {
if(c >= 'A' && c <= 'Z')
c += 'a' - 'A';
return c;
}
int
case_starts(const char* a, const char* b) {
const char *s, *t;
for(s = a, t = b;; ++s, ++t) {
unsigned char x, y;
if(!*t)
return 1;
x = case_lowerc(*s);
y = case_lowerc(*t);
if(x != y)
break;
if(!x)
break;
}
return 0;
}
int
case_diffb(const void* S, size_t len, const void* T) {
unsigned char x, y;
const char *s, *t;
for(s = (const char*)S, t = (const char*)T; len > 0;) {
--len;
x = case_lowerc(*s);
y = case_lowerc(*t);
++s;
++t;
if(x != y)
return ((int)(unsigned int)x) - ((int)(unsigned int)y);
}
return 0;
}
size_t
case_findb(const void* haystack, size_t hlen, const void* what, size_t wlen) {
size_t i, last;
const char* s = haystack;
if(hlen < wlen)
return hlen;
last = hlen - wlen;
for(i = 0; i <= last; i++, s++)
if(!case_diffb(s, wlen, what))
return i;
return hlen;
}
size_t
case_finds(const void* haystack, const char* what) {
return case_findb(haystack, strlen(haystack), what, strlen(what));
}
ssize_t
write_file(const char* file, const void* buf, size_t len) {
FILE* f;
ssize_t ret = -1;
if((f = fopen(file, "w+")))
switch(fwrite(buf, len, 1, f)) {
case 1: {
ret = len;
break;
}
}
fflush(f);
ret = ftell(f);
fclose(f);
return ret;
}
ssize_t
puts_file(const char* file, const char* s) {
return write_file(file, s, strlen(s));
}
size_t
u64toa(char* x, uint64_t num, int base) {
size_t len = 0;
uint64_t n = num;
do {
n /= base;
len++;
x++;
} while(n != 0);
*x-- = '\0';
do {
char c = num % base;
num /= base;
if(c >= 10)
c += 'a' - '0' - 10;
*x-- = c + '0';
} while(num != 0);
return len;
}
size_t
i64toa(char* x, int64_t num, int base) {
size_t pos = 0, len;
if(num < 0) {
x[pos++] = '-';
num = -num;
}
len = u64toa(&x[pos], num, base);
return pos + len;
}
size_t
str_findb(const char* s1, const char* x, size_t n) {
const char* b;
size_t i, j, len = strlen(s1);
if(len >= n) {
size_t end = len - n + 1;
for(i = 0; i < end; i++) {
b = &s1[i];
for(j = 0; x[j] == b[j];)
if(++j == n)
return i;
}
}
return len;
}
size_t
str_find(const void* s, const void* what) {
return str_findb(s, what, strlen(what));
}
/**
* @}
*/

View File

@@ -1,441 +0,0 @@
#ifndef CHAR_UTILS_H
#define CHAR_UTILS_H
#include <cutils.h>
#include <string.h>
#include "debug.h"
/**
* \defgroup char-utils char-utils: Character Utilities
* @{
*/
#define is_control_char(c) ((c) == '\a' || (c) == '\b' || (c) == '\t' || (c) == '\n' || (c) == '\v' || (c) == '\f' || (c) == '\r')
#define is_alphanumeric_char(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
#define is_digit_char(c) ((c) >= '0' && (c) <= '9')
#define is_print_char(c) ((c) >= ' ' && (c) <= '\x7f')
#define is_newline_char(c) ((c) == '\n')
#define is_identifier_char(c) (is_alphanumeric_char(c) || is_digit_char(c) || (c) == '$' || (c) == '_')
#define is_whitespace_char(c) ((c) == ' ' || (c) == '\t' || (c) == '\v' || (c) == '\n' || (c) == '\r')
#define str_equal(s, t) (!strcmp((s), (t)))
static inline int
escape_char_pred(int c) {
static const unsigned char table[256] = {
'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 0x62, 0x74, 0x6e, 0x76, 0x66, 0x72, 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x',
'x', 'x', 'x', 0, 0, 0, 0, 0, 0, 0, 0x27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0x5c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'x', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
return table[(unsigned char)c];
}
static inline int
unescape_char_pred(int c) {
switch(c) {
case 'b': return 8;
case 'f': return 12;
case 'n': return 10;
case 'r': return 13;
case 't': return 9;
case 'v': return 11;
case '\'': return 39;
case '\\': return 92;
}
return 0;
}
static inline int
is_escape_char(int c) {
return is_control_char(c) || c == '\\' || c == '\'' || c == 0x1b || c == 0;
}
static inline int
is_backslash_char(int c) {
return c == '\\';
}
//#define is_dot_char(c) ((c) == '.')0
//#define is_backslash_char(c) ((c) == '\\')
static inline int
is_dot_char(int c) {
return c == '.';
}
static inline int
is_identifier(const char* str) {
if(!((*str >= 'A' && *str <= 'Z') || (*str >= 'a' && *str <= 'z') || *str == '$' || *str == '_'))
return 0;
while(*++str)
if(!is_identifier_char(*str))
return 0;
return 1;
}
static inline int
is_integer(const char* str) {
if(*str == '-')
++str;
if(!(*str >= '1' && *str <= '9') && !(*str == '0' && str[1] == '\0'))
return 0;
while(*++str)
if(!is_digit_char(*str))
return 0;
return 1;
}
static inline size_t
byte_count(const void* s, size_t n, char c) {
const uint8_t* t;
uint8_t ch = (uint8_t)c;
size_t count;
for(t = (uint8_t*)s, count = 0; n; ++t, --n)
if(*t == ch)
++count;
return count;
}
static inline size_t
byte_chr(const void* str, size_t len, char c) {
const char* s = memchr(str, c, len);
if(s)
return s - (const char*)str;
return len;
}
static inline size_t
byte_rchr(const void* haystack, size_t len, char needle) {
const char *s, *t;
for(s = (const char*)haystack, t = s + len;;) {
--t;
if(s > t)
break;
if(*t == needle)
return (size_t)(t - s);
}
return len;
}
/*size_t
byte_rchr(const void* str, size_t len, char c) {
const char* s = memrchr(str, c, len);
if(s)
return s - (const char*)str;
return len;
}*/
static inline size_t
byte_chrs(const void* str, size_t len, const char needle[], size_t nl) {
const char *s, *t;
for(s = str, t = s + len; s != t; s++)
if(byte_chr(needle, nl, *s) < nl)
break;
return s - (const char*)str;
}
static inline int
byte_diff(const void* a, size_t len, const void* b) {
size_t i;
for(i = 0; i < len; ++i) {
int r = ((unsigned char*)a)[i] - ((unsigned char*)b)[i];
if(r)
return r;
}
return 0;
}
static inline int
byte_diff2(const char* a, size_t alen, const char* b, size_t blen) {
if(alen < blen)
return -b[alen];
if(blen < alen)
return a[blen];
return byte_diff(a, alen, b);
}
static inline size_t
str_chr(const char* in, char needle) {
const char *t, c = needle;
for(t = in; *t; ++t)
if(*t == c)
break;
return (size_t)(t - in);
}
static inline size_t
str_chrs(const char* in, const char needles[], size_t nn) {
const char* t;
size_t i;
for(t = in; *t; ++t)
for(i = 0; i < nn; i++)
if(*t == needles[i])
return (size_t)(t - in);
return (size_t)(t - in);
}
static inline size_t
str_rchr(const char* s, char needle) {
const char *in, *found = 0;
for(in = s; *in; ++in)
if(*in == needle)
found = in;
return (size_t)((found ? found : in) - s);
}
static inline size_t
str_rchrs(const char* in, const char needles[], size_t nn) {
const char *s, *found = 0;
size_t i;
for(s = in; *s; ++s)
for(i = 0; i < nn; ++i)
if(*s == needles[i])
found = s;
return (size_t)((found ? found : s) - in);
}
static inline int
str_endb(const char* a, const char* x, size_t n) {
size_t alen = strlen(a);
a += alen - n;
return alen >= n && !memcmp(a, x, n);
}
/* str_ends returns 1 if the b is a suffix of a, 0 otherwise */
static inline int
str_ends(const char* a, const char* b) {
return str_endb(a, b, strlen(b));
}
static inline int
str_startb(const char* a, const char* x, size_t len) {
size_t i;
for(i = 0;; i++) {
if(i == len)
return 1;
if(a[i] != x[i])
break;
}
return 0;
}
static inline int
str_start(const char* a, const char* b) {
return str_startb(a, b, strlen(b));
}
#define str_contains(s, needle) (!!strchr((s), (needle)))
char* str_escape(const char*);
static inline size_t
str_count(const char* s, char c) {
size_t i, count = 0;
for(i = 0; s[i]; i++)
if(s[i] == c)
++count;
return count;
}
static inline size_t
str_copy(char* out, const char* in) {
char* s;
for(s = out; (*s = *in); ++s)
++in;
return (size_t)(s - out);
}
static inline size_t
str_copyn(char* out, const char* in, size_t n) {
char* s;
for(s = out; n-- && (*s = *in); ++s)
++in;
*s = '\0';
return (size_t)(s - out);
}
static inline char*
str_ndup(const char* s, size_t n) {
char* r = malloc(n + 1);
if(r == NULL)
return NULL;
memcpy(r, s, n);
r[n] = '\0';
return r;
}
size_t str_findb(const char*, const char*, size_t);
size_t str_find(const void*, const void*);
static inline size_t
predicate_find(const char* str, size_t len, int (*pred)(int32_t)) {
size_t pos;
for(pos = 0; pos < len; pos++)
if(pred(str[pos]))
break;
return pos;
}
static inline size_t
lookup_find(const char* str, size_t len, const char table[256]) {
size_t pos;
for(pos = 0; pos < len; pos++)
if(table[(unsigned char)str[pos]])
break;
return pos;
}
static inline char
escape_char_letter(char c) {
switch(c) {
case '\0': return '0';
case '\a': return 'a';
case '\b': return 'b';
case '\t': return 't';
case '\n': return 'n';
case '\v': return 'v';
case '\f': return 'f';
case '\r': return 'r';
case '\\': return '\\';
case '\'': return '\'';
}
return 0;
}
#define FMT_LONG 41 /* enough space to hold -2^127 in decimal, plus \0 */
#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */
#define FMT_8LONG 44 /* enough space to hold 2^128 - 1 in octal, plus \0 */
#define FMT_XLONG 33 /* enough space to hold 2^128 - 1 in hexadecimal, plus \0 */
size_t token_length(const char*, size_t, char delim);
size_t fmt_ulong(char*, uint32_t);
size_t scan_ushort(const char*, uint16_t*);
size_t fmt_longlong(char*, int64_t);
size_t fmt_ulonglong(char*, uint64_t);
size_t fmt_xlonglong(char*, uint64_t);
size_t fmt_xlonglong0(char*, uint64_t, size_t);
size_t fmt_8long(char* dest, uint32_t i);
size_t fmt_xlong(char* dest, uint32_t num);
size_t fmt_xlong0(char* dest, uint32_t num, size_t n);
size_t scan_longlong(const char*, int64_t*);
size_t scan_int(const char*, int32_t*);
size_t scan_uint(const char*, uint32_t*);
size_t scan_ulonglong(const char*, uint64_t*);
size_t scan_xlonglong(const char*, uint64_t*);
size_t scan_8longn(const char*, size_t, uint32_t* dest);
size_t scan_whitenskip(const char*, size_t);
size_t scan_nonwhitenskip(const char*, size_t);
size_t scan_line(const char*, size_t);
size_t scan_lineskip(const char*, size_t);
size_t scan_lineskip_escaped(const char*, size_t);
size_t scan_eolskip(const char*, size_t);
size_t utf8_strlen(const void*, size_t);
wchar_t* utf8_towcs(const char*);
char* utf8_fromwcs(const wchar_t*);
BOOL utf16_multiword(const void*);
int case_lowerc(int);
int case_starts(const char*, const char*);
int case_diffb(const void*, size_t, const void* T);
size_t case_findb(const void*, size_t, const void* what, size_t wlen);
size_t case_finds(const void*, const char*);
static inline int
scan_fromhex(unsigned char c) {
c -= '0';
if(c <= 9)
return c;
c &= ~0x20;
c -= 'A' - '0';
if(c < 6)
return c + 10;
return -1;
}
static inline size_t
scan_8long(const char* src, uint32_t* dest) {
return scan_8longn(src, (size_t)-1, dest);
}
static inline size_t
utf8_charlen(const char* in, size_t len) {
const uint8_t* next = (const void*)in;
int r = unicode_from_utf8((const uint8_t*)in, len, &next);
return r == -1 ? 0 : next - (const uint8_t*)in;
}
static inline int
utf8_charcode(const char* in, size_t len) {
const uint8_t* next = (const void*)in;
int r = unicode_from_utf8((const uint8_t*)in, len, &next);
return next > in ? r : -1;
}
BOOL utf16_multiword(const void*);
ssize_t write_file(const char* file, const void* buf, size_t len);
ssize_t puts_file(const char* file, const char* s);
size_t u64toa(char*, uint64_t num, int base);
size_t i64toa(char*, int64_t num, int base);
/**
* @}
*/
#endif /* defined(CHAR_UTILS_H) */

View File

@@ -1,631 +0,0 @@
/*
* C utilities
*
* Copyright (c) 2017 Fabrice Bellard
* Copyright (c) 2018 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "cutils.h"
void pstrcpy(char *buf, int buf_size, const char *str)
{
int c;
char *q = buf;
if (buf_size <= 0)
return;
for(;;) {
c = *str++;
if (c == 0 || q >= buf + buf_size - 1)
break;
*q++ = c;
}
*q = '\0';
}
/* strcat and truncate. */
char *pstrcat(char *buf, int buf_size, const char *s)
{
int len;
len = strlen(buf);
if (len < buf_size)
pstrcpy(buf + len, buf_size - len, s);
return buf;
}
int strstart(const char *str, const char *val, const char **ptr)
{
const char *p, *q;
p = str;
q = val;
while (*q != '\0') {
if (*p != *q)
return 0;
p++;
q++;
}
if (ptr)
*ptr = p;
return 1;
}
int has_suffix(const char *str, const char *suffix)
{
size_t len = strlen(str);
size_t slen = strlen(suffix);
return (len >= slen && !memcmp(str + len - slen, suffix, slen));
}
/* Dynamic buffer package */
static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size)
{
return realloc(ptr, size);
}
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func)
{
memset(s, 0, sizeof(*s));
if (!realloc_func)
realloc_func = dbuf_default_realloc;
s->opaque = opaque;
s->realloc_func = realloc_func;
}
void dbuf_init(DynBuf *s)
{
dbuf_init2(s, NULL, NULL);
}
/* return < 0 if error */
int dbuf_realloc(DynBuf *s, size_t new_size)
{
size_t size;
uint8_t *new_buf;
if (new_size > s->allocated_size) {
if (s->error)
return -1;
size = s->allocated_size * 3 / 2;
if (size > new_size)
new_size = size;
new_buf = s->realloc_func(s->opaque, s->buf, new_size);
if (!new_buf) {
s->error = TRUE;
return -1;
}
s->buf = new_buf;
s->allocated_size = new_size;
}
return 0;
}
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len)
{
size_t end;
end = offset + len;
if (dbuf_realloc(s, end))
return -1;
memcpy(s->buf + offset, data, len);
if (end > s->size)
s->size = end;
return 0;
}
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len)
{
if (unlikely((s->size + len) > s->allocated_size)) {
if (dbuf_realloc(s, s->size + len))
return -1;
}
memcpy_no_ub(s->buf + s->size, data, len);
s->size += len;
return 0;
}
int dbuf_put_self(DynBuf *s, size_t offset, size_t len)
{
if (unlikely((s->size + len) > s->allocated_size)) {
if (dbuf_realloc(s, s->size + len))
return -1;
}
memcpy(s->buf + s->size, s->buf + offset, len);
s->size += len;
return 0;
}
int dbuf_putc(DynBuf *s, uint8_t c)
{
return dbuf_put(s, &c, 1);
}
int dbuf_putstr(DynBuf *s, const char *str)
{
return dbuf_put(s, (const uint8_t *)str, strlen(str));
}
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
const char *fmt, ...)
{
va_list ap;
char buf[128];
int len;
va_start(ap, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (len < sizeof(buf)) {
/* fast case */
return dbuf_put(s, (uint8_t *)buf, len);
} else {
if (dbuf_realloc(s, s->size + len + 1))
return -1;
va_start(ap, fmt);
vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size,
fmt, ap);
va_end(ap);
s->size += len;
}
return 0;
}
void dbuf_free(DynBuf *s)
{
/* we test s->buf as a fail safe to avoid crashing if dbuf_free()
is called twice */
if (s->buf) {
s->realloc_func(s->opaque, s->buf, 0);
}
memset(s, 0, sizeof(*s));
}
/* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes
are output. */
int unicode_to_utf8(uint8_t *buf, unsigned int c)
{
uint8_t *q = buf;
if (c < 0x80) {
*q++ = c;
} else {
if (c < 0x800) {
*q++ = (c >> 6) | 0xc0;
} else {
if (c < 0x10000) {
*q++ = (c >> 12) | 0xe0;
} else {
if (c < 0x00200000) {
*q++ = (c >> 18) | 0xf0;
} else {
if (c < 0x04000000) {
*q++ = (c >> 24) | 0xf8;
} else if (c < 0x80000000) {
*q++ = (c >> 30) | 0xfc;
*q++ = ((c >> 24) & 0x3f) | 0x80;
} else {
return 0;
}
*q++ = ((c >> 18) & 0x3f) | 0x80;
}
*q++ = ((c >> 12) & 0x3f) | 0x80;
}
*q++ = ((c >> 6) & 0x3f) | 0x80;
}
*q++ = (c & 0x3f) | 0x80;
}
return q - buf;
}
static const unsigned int utf8_min_code[5] = {
0x80, 0x800, 0x10000, 0x00200000, 0x04000000,
};
static const unsigned char utf8_first_code_mask[5] = {
0x1f, 0xf, 0x7, 0x3, 0x1,
};
/* return -1 if error. *pp is not updated in this case. max_len must
be >= 1. The maximum length for a UTF8 byte sequence is 6 bytes. */
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp)
{
int l, c, b, i;
c = *p++;
if (c < 0x80) {
*pp = p;
return c;
}
switch(c) {
case 0xc0: case 0xc1: case 0xc2: case 0xc3:
case 0xc4: case 0xc5: case 0xc6: case 0xc7:
case 0xc8: case 0xc9: case 0xca: case 0xcb:
case 0xcc: case 0xcd: case 0xce: case 0xcf:
case 0xd0: case 0xd1: case 0xd2: case 0xd3:
case 0xd4: case 0xd5: case 0xd6: case 0xd7:
case 0xd8: case 0xd9: case 0xda: case 0xdb:
case 0xdc: case 0xdd: case 0xde: case 0xdf:
l = 1;
break;
case 0xe0: case 0xe1: case 0xe2: case 0xe3:
case 0xe4: case 0xe5: case 0xe6: case 0xe7:
case 0xe8: case 0xe9: case 0xea: case 0xeb:
case 0xec: case 0xed: case 0xee: case 0xef:
l = 2;
break;
case 0xf0: case 0xf1: case 0xf2: case 0xf3:
case 0xf4: case 0xf5: case 0xf6: case 0xf7:
l = 3;
break;
case 0xf8: case 0xf9: case 0xfa: case 0xfb:
l = 4;
break;
case 0xfc: case 0xfd:
l = 5;
break;
default:
return -1;
}
/* check that we have enough characters */
if (l > (max_len - 1))
return -1;
c &= utf8_first_code_mask[l - 1];
for(i = 0; i < l; i++) {
b = *p++;
if (b < 0x80 || b >= 0xc0)
return -1;
c = (c << 6) | (b & 0x3f);
}
if (c < utf8_min_code[l - 1])
return -1;
*pp = p;
return c;
}
#if 0
#if defined(EMSCRIPTEN) || defined(__ANDROID__)
static void *rqsort_arg;
static int (*rqsort_cmp)(const void *, const void *, void *);
static int rqsort_cmp2(const void *p1, const void *p2)
{
return rqsort_cmp(p1, p2, rqsort_arg);
}
/* not reentrant, but not needed with emscripten */
void rqsort(void *base, size_t nmemb, size_t size,
int (*cmp)(const void *, const void *, void *),
void *arg)
{
rqsort_arg = arg;
rqsort_cmp = cmp;
qsort(base, nmemb, size, rqsort_cmp2);
}
#endif
#else
typedef void (*exchange_f)(void *a, void *b, size_t size);
typedef int (*cmp_f)(const void *, const void *, void *opaque);
static void exchange_bytes(void *a, void *b, size_t size) {
uint8_t *ap = (uint8_t *)a;
uint8_t *bp = (uint8_t *)b;
while (size-- != 0) {
uint8_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_byte(void *a, void *b, size_t size) {
uint8_t *ap = (uint8_t *)a;
uint8_t *bp = (uint8_t *)b;
uint8_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int16s(void *a, void *b, size_t size) {
uint16_t *ap = (uint16_t *)a;
uint16_t *bp = (uint16_t *)b;
for (size /= sizeof(uint16_t); size-- != 0;) {
uint16_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_int16(void *a, void *b, size_t size) {
uint16_t *ap = (uint16_t *)a;
uint16_t *bp = (uint16_t *)b;
uint16_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int32s(void *a, void *b, size_t size) {
uint32_t *ap = (uint32_t *)a;
uint32_t *bp = (uint32_t *)b;
for (size /= sizeof(uint32_t); size-- != 0;) {
uint32_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_int32(void *a, void *b, size_t size) {
uint32_t *ap = (uint32_t *)a;
uint32_t *bp = (uint32_t *)b;
uint32_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int64s(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
for (size /= sizeof(uint64_t); size-- != 0;) {
uint64_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_int64(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
uint64_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int128s(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) {
uint64_t t = ap[0];
uint64_t u = ap[1];
ap[0] = bp[0];
ap[1] = bp[1];
bp[0] = t;
bp[1] = u;
}
}
static void exchange_one_int128(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
uint64_t t = ap[0];
uint64_t u = ap[1];
ap[0] = bp[0];
ap[1] = bp[1];
bp[0] = t;
bp[1] = u;
}
static inline exchange_f exchange_func(const void *base, size_t size) {
switch (((uintptr_t)base | (uintptr_t)size) & 15) {
case 0:
if (size == sizeof(uint64_t) * 2)
return exchange_one_int128;
else
return exchange_int128s;
case 8:
if (size == sizeof(uint64_t))
return exchange_one_int64;
else
return exchange_int64s;
case 4:
case 12:
if (size == sizeof(uint32_t))
return exchange_one_int32;
else
return exchange_int32s;
case 2:
case 6:
case 10:
case 14:
if (size == sizeof(uint16_t))
return exchange_one_int16;
else
return exchange_int16s;
default:
if (size == 1)
return exchange_one_byte;
else
return exchange_bytes;
}
}
static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)
{
uint8_t *basep = (uint8_t *)base;
size_t i, n, c, r;
exchange_f swap = exchange_func(base, size);
if (nmemb > 1) {
i = (nmemb / 2) * size;
n = nmemb * size;
while (i > 0) {
i -= size;
for (r = i; (c = r * 2 + size) < n; r = c) {
if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0)
c += size;
if (cmp(basep + r, basep + c, opaque) > 0)
break;
swap(basep + r, basep + c, size);
}
}
for (i = n - size; i > 0; i -= size) {
swap(basep, basep + i, size);
for (r = 0; (c = r * 2 + size) < i; r = c) {
if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0)
c += size;
if (cmp(basep + r, basep + c, opaque) > 0)
break;
swap(basep + r, basep + c, size);
}
}
}
}
static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque)
{
return cmp(a, b, opaque) < 0 ?
(cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) :
(cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c ));
}
/* pointer based version with local stack and insertion sort threshhold */
void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)
{
struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack;
uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m;
size_t m4, i, lt, gt, span, span2;
int c, depth;
exchange_f swap = exchange_func(base, size);
exchange_f swap_block = exchange_func(base, size | 128);
if (nmemb < 2 || size <= 0)
return;
sp->base = (uint8_t *)base;
sp->count = nmemb;
sp->depth = 0;
sp++;
while (sp > stack) {
sp--;
ptr = sp->base;
nmemb = sp->count;
depth = sp->depth;
while (nmemb > 6) {
if (++depth > 50) {
/* depth check to ensure worst case logarithmic time */
heapsortx(ptr, nmemb, size, cmp, opaque);
nmemb = 0;
break;
}
/* select median of 3 from 1/4, 1/2, 3/4 positions */
/* should use median of 5 or 9? */
m4 = (nmemb >> 2) * size;
m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque);
swap(ptr, m, size); /* move the pivot to the start or the array */
i = lt = 1;
pi = plt = ptr + size;
gt = nmemb;
pj = pgt = top = ptr + nmemb * size;
for (;;) {
while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) {
if (c == 0) {
swap(plt, pi, size);
lt++;
plt += size;
}
i++;
pi += size;
}
while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) {
if (c == 0) {
gt--;
pgt -= size;
swap(pgt, pj, size);
}
}
if (pi >= pj)
break;
swap(pi, pj, size);
i++;
pi += size;
}
/* array has 4 parts:
* from 0 to lt excluded: elements identical to pivot
* from lt to pi excluded: elements smaller than pivot
* from pi to gt excluded: elements greater than pivot
* from gt to n excluded: elements identical to pivot
*/
/* move elements identical to pivot in the middle of the array: */
/* swap values in ranges [0..lt[ and [i-lt..i[
swapping the smallest span between lt and i-lt is sufficient
*/
span = plt - ptr;
span2 = pi - plt;
lt = i - lt;
if (span > span2)
span = span2;
swap_block(ptr, pi - span, span);
/* swap values in ranges [gt..top[ and [i..top-(top-gt)[
swapping the smallest span between top-gt and gt-i is sufficient
*/
span = top - pgt;
span2 = pgt - pi;
pgt = top - span2;
gt = nmemb - (gt - i);
if (span > span2)
span = span2;
swap_block(pi, top - span, span);
/* now array has 3 parts:
* from 0 to lt excluded: elements smaller than pivot
* from lt to gt excluded: elements identical to pivot
* from gt to n excluded: elements greater than pivot
*/
/* stack the larger segment and keep processing the smaller one
to minimize stack use for pathological distributions */
if (lt > nmemb - gt) {
sp->base = ptr;
sp->count = lt;
sp->depth = depth;
sp++;
ptr = pgt;
nmemb -= gt;
} else {
sp->base = pgt;
sp->count = nmemb - gt;
sp->depth = depth;
sp++;
nmemb = lt;
}
}
/* Use insertion sort for small fragments */
for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) {
for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size)
swap(pj, pj - size, size);
}
}
}
#endif

View File

@@ -1,341 +0,0 @@
/*
* C utilities
*
* Copyright (c) 2017 Fabrice Bellard
* Copyright (c) 2018 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef CUTILS_H
#define CUTILS_H
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define force_inline inline __attribute__((always_inline))
#define no_inline __attribute__((noinline))
#define __maybe_unused __attribute__((unused))
#define xglue(x, y) x ## y
#define glue(x, y) xglue(x, y)
#define stringify(s) tostring(s)
#define tostring(s) #s
#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif
#ifndef countof
#define countof(x) (sizeof(x) / sizeof((x)[0]))
#endif
#ifndef container_of
/* return the pointer of type 'type *' containing 'ptr' as field 'member' */
#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member)))
#endif
typedef int BOOL;
#ifndef FALSE
enum {
FALSE = 0,
TRUE = 1,
};
#endif
void pstrcpy(char *buf, int buf_size, const char *str);
char *pstrcat(char *buf, int buf_size, const char *s);
int strstart(const char *str, const char *val, const char **ptr);
int has_suffix(const char *str, const char *suffix);
/* Prevent UB when n == 0 and (src == NULL or dest == NULL) */
static inline void memcpy_no_ub(void *dest, const void *src, size_t n) {
if (n)
memcpy(dest, src, n);
}
static inline int max_int(int a, int b)
{
if (a > b)
return a;
else
return b;
}
static inline int min_int(int a, int b)
{
if (a < b)
return a;
else
return b;
}
static inline uint32_t max_uint32(uint32_t a, uint32_t b)
{
if (a > b)
return a;
else
return b;
}
static inline uint32_t min_uint32(uint32_t a, uint32_t b)
{
if (a < b)
return a;
else
return b;
}
static inline int64_t max_int64(int64_t a, int64_t b)
{
if (a > b)
return a;
else
return b;
}
static inline int64_t min_int64(int64_t a, int64_t b)
{
if (a < b)
return a;
else
return b;
}
/* WARNING: undefined if a = 0 */
static inline int clz32(unsigned int a)
{
return __builtin_clz(a);
}
/* WARNING: undefined if a = 0 */
static inline int clz64(uint64_t a)
{
return __builtin_clzll(a);
}
/* WARNING: undefined if a = 0 */
static inline int ctz32(unsigned int a)
{
return __builtin_ctz(a);
}
/* WARNING: undefined if a = 0 */
static inline int ctz64(uint64_t a)
{
return __builtin_ctzll(a);
}
struct __attribute__((packed)) packed_u64 {
uint64_t v;
};
struct __attribute__((packed)) packed_u32 {
uint32_t v;
};
struct __attribute__((packed)) packed_u16 {
uint16_t v;
};
static inline uint64_t get_u64(const uint8_t *tab)
{
return ((const struct packed_u64 *)tab)->v;
}
static inline int64_t get_i64(const uint8_t *tab)
{
return (int64_t)((const struct packed_u64 *)tab)->v;
}
static inline void put_u64(uint8_t *tab, uint64_t val)
{
((struct packed_u64 *)tab)->v = val;
}
static inline uint32_t get_u32(const uint8_t *tab)
{
return ((const struct packed_u32 *)tab)->v;
}
static inline int32_t get_i32(const uint8_t *tab)
{
return (int32_t)((const struct packed_u32 *)tab)->v;
}
static inline void put_u32(uint8_t *tab, uint32_t val)
{
((struct packed_u32 *)tab)->v = val;
}
static inline uint32_t get_u16(const uint8_t *tab)
{
return ((const struct packed_u16 *)tab)->v;
}
static inline int32_t get_i16(const uint8_t *tab)
{
return (int16_t)((const struct packed_u16 *)tab)->v;
}
static inline void put_u16(uint8_t *tab, uint16_t val)
{
((struct packed_u16 *)tab)->v = val;
}
static inline uint32_t get_u8(const uint8_t *tab)
{
return *tab;
}
static inline int32_t get_i8(const uint8_t *tab)
{
return (int8_t)*tab;
}
static inline void put_u8(uint8_t *tab, uint8_t val)
{
*tab = val;
}
#ifndef bswap16
static inline uint16_t bswap16(uint16_t x)
{
return (x >> 8) | (x << 8);
}
#endif
#ifndef bswap32
static inline uint32_t bswap32(uint32_t v)
{
return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |
((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);
}
#endif
#ifndef bswap64
static inline uint64_t bswap64(uint64_t v)
{
return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) |
((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) |
((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) |
((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) |
((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) |
((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) |
((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) |
((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8));
}
#endif
/* XXX: should take an extra argument to pass slack information to the caller */
typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size);
typedef struct DynBuf {
uint8_t *buf;
size_t size;
size_t allocated_size;
BOOL error; /* true if a memory allocation error occurred */
DynBufReallocFunc *realloc_func;
void *opaque; /* for realloc_func */
} DynBuf;
void dbuf_init(DynBuf *s);
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func);
int dbuf_realloc(DynBuf *s, size_t new_size);
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len);
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len);
int dbuf_put_self(DynBuf *s, size_t offset, size_t len);
int dbuf_putc(DynBuf *s, uint8_t c);
int dbuf_putstr(DynBuf *s, const char *str);
static inline int dbuf_put_u16(DynBuf *s, uint16_t val)
{
return dbuf_put(s, (uint8_t *)&val, 2);
}
static inline int dbuf_put_u32(DynBuf *s, uint32_t val)
{
return dbuf_put(s, (uint8_t *)&val, 4);
}
static inline int dbuf_put_u64(DynBuf *s, uint64_t val)
{
return dbuf_put(s, (uint8_t *)&val, 8);
}
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
const char *fmt, ...);
void dbuf_free(DynBuf *s);
static inline BOOL dbuf_error(DynBuf *s) {
return s->error;
}
static inline void dbuf_set_error(DynBuf *s)
{
s->error = TRUE;
}
#define UTF8_CHAR_LEN_MAX 6
int unicode_to_utf8(uint8_t *buf, unsigned int c);
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp);
static inline BOOL is_surrogate(uint32_t c)
{
return (c >> 11) == (0xD800 >> 11); // 0xD800-0xDFFF
}
static inline BOOL is_hi_surrogate(uint32_t c)
{
return (c >> 10) == (0xD800 >> 10); // 0xD800-0xDBFF
}
static inline BOOL is_lo_surrogate(uint32_t c)
{
return (c >> 10) == (0xDC00 >> 10); // 0xDC00-0xDFFF
}
static inline uint32_t get_hi_surrogate(uint32_t c)
{
return (c >> 10) - (0x10000 >> 10) + 0xD800;
}
static inline uint32_t get_lo_surrogate(uint32_t c)
{
return (c & 0x3FF) | 0xDC00;
}
static inline uint32_t from_surrogate(uint32_t hi, uint32_t lo)
{
return 0x10000 + 0x400 * (hi - 0xD800) + (lo - 0xDC00);
}
static inline int from_hex(int c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else
return -1;
}
void rqsort(void *base, size_t nmemb, size_t size,
int (*cmp)(const void *, const void *, void *),
void *arg);
#endif /* CUTILS_H */

View File

@@ -1,483 +0,0 @@
#define _IN_DEBUG_C 1
#include "debug.h"
#include <quickjs.h>
#include <list.h>
#include <cutils.h>
#include <assert.h>
#include "defines.h"
#include <string.h>
#include <stdlib.h>
/**
* \addtogroup debug
* @{
*/
struct alloc_block {
struct list_head link;
const char* file;
int line;
size_t size;
};
#define ALLOC_BLOCK_SIZE sizeof(struct alloc_block)
#define ALLOC_BLOCK(p) (((struct alloc_block*)(p)) - 1)
#define ALLOC_PTR struct alloc_block*
#undef malloc
#undef calloc
#undef realloc
#undef strdup
#undef free
#undef js_malloc
#undef js_mallocz
#undef js_realloc
#undef js_strdup
#undef js_strndup
#undef js_free
#undef js_malloc_usable_size
#undef js_malloc_rt
#undef js_mallocz_rt
#undef js_realloc_rt
#undef js_free_rt
#undef js_malloc_usable_size_rt
thread_local struct list_head alloc_block_list = {0, 0};
static inline void
add_to_list(struct list_head* el, struct list_head* head) {
if(alloc_block_list.prev == 0 && alloc_block_list.next == 0)
init_list_head(&alloc_block_list);
list_add_tail(el, head);
}
int64_t
check_pointer(void* p) {
ALLOC_PTR ptr = ALLOC_BLOCK(p);
struct list_head* link;
int64_t ret = 0;
list_for_each(link, &alloc_block_list) {
if(link == &ptr->link)
return ret;
ret++;
}
// assert(0);
return -1;
}
void*
debug_malloc(size_t n, const char* file, int line) {
ALLOC_PTR ptr;
if((ptr = malloc(n + ALLOC_BLOCK_SIZE))) {
ptr->file = file;
ptr->line = line;
ptr->size = n;
add_to_list(&ptr->link, &alloc_block_list);
return &ptr[1];
}
return 0;
}
void*
debug_calloc(size_t m, size_t n, const char* file, int line) {
ALLOC_PTR ptr;
m *= n;
n = 1;
if((ptr = calloc(m + ALLOC_BLOCK_SIZE, 1))) {
ptr->file = file;
ptr->line = line;
ptr->size = n;
add_to_list(&ptr->link, &alloc_block_list);
return &ptr[1];
}
return 0;
}
void*
debug_realloc(void* p, size_t n, const char* file, int line) {
ALLOC_PTR ptr;
if(p) {
check_pointer(p);
ptr = ALLOC_BLOCK(p);
list_del(&ptr->link);
if(n == 0) {
free(ptr);
return 0;
}
ptr = realloc(ptr, n + ALLOC_BLOCK_SIZE);
} else {
ptr = malloc(n + ALLOC_BLOCK_SIZE);
}
if(ptr) {
ptr->file = file;
ptr->line = line;
ptr->size = n;
add_to_list(&ptr->link, &alloc_block_list);
return &ptr[1];
}
return 0;
}
void*
debug_strdup(const char* s, const char* file, int line) {
ALLOC_PTR ptr;
size_t len = strlen(s);
if((ptr = malloc(len + 1 + ALLOC_BLOCK_SIZE))) {
ptr->file = file;
ptr->line = line;
ptr->size = len + 1;
add_to_list(&ptr->link, &alloc_block_list);
memcpy(&ptr[1], s, len + 1);
return &ptr[1];
}
return 0;
}
void
debug_free(void* p, const char* file, int line) {
ALLOC_PTR ptr = ALLOC_BLOCK(p);
list_del(&ptr->link);
memset(ptr, 0xff, ALLOC_BLOCK_SIZE);
free(ptr);
}
void*
debug_js_malloc(JSContext* ctx, size_t n, const char* file, int line) {
ALLOC_PTR ptr;
if((ptr = js_malloc(ctx, n + ALLOC_BLOCK_SIZE))) {
ptr->file = file;
ptr->line = line;
ptr->size = n;
add_to_list(&ptr->link, &alloc_block_list);
return &ptr[1];
}
return 0;
}
void*
debug_js_mallocz(JSContext* ctx, size_t n, const char* file, int line) {
ALLOC_PTR ptr;
if((ptr = js_mallocz(ctx, n + ALLOC_BLOCK_SIZE))) {
ptr->file = file;
ptr->line = line;
ptr->size = n;
add_to_list(&ptr->link, &alloc_block_list);
return &ptr[1];
}
return 0;
}
void*
debug_js_realloc(JSContext* ctx, void* p, size_t n, const char* file, int line) {
ALLOC_PTR ptr;
if(p) {
check_pointer(p);
ptr = ALLOC_BLOCK(p);
list_del(&ptr->link);
if(n == 0) {
js_free(ctx, ptr);
return 0;
}
ptr = js_realloc(ctx, ptr, n + ALLOC_BLOCK_SIZE);
} else {
ptr = js_malloc(ctx, n + ALLOC_BLOCK_SIZE);
}
if(ptr) {
ptr->file = file;
ptr->line = line;
ptr->size = n;
add_to_list(&ptr->link, &alloc_block_list);
return &ptr[1];
}
return 0;
}
void*
debug_js_realloc2(JSContext* ctx, void* p, size_t size, size_t* pslack, const char* file, int line) {
void* ptr;
if((ptr = debug_js_realloc(ctx, p, size, file, line))) {
if(pslack) {
size_t new_size = debug_js_malloc_usable_size(ctx, ptr, file, line);
*pslack = (new_size > size) ? new_size - size : 0;
}
}
return ptr;
}
void*
debug_js_strdup(JSContext* ctx, const char* s, const char* file, int line) {
ALLOC_PTR ptr;
size_t len = strlen(s);
if((ptr = js_malloc(ctx, len + 1 + ALLOC_BLOCK_SIZE))) {
char* p = (char*)&ptr[1];
ptr->file = file;
ptr->line = line;
ptr->size = len + 1;
add_to_list(&ptr->link, &alloc_block_list);
memcpy(p, s, len + 1);
return p;
}
return 0;
}
void*
debug_js_strndup(JSContext* ctx, const char* s, size_t len, const char* file, int line) {
ALLOC_PTR ptr;
if((ptr = js_malloc(ctx, len + 1 + ALLOC_BLOCK_SIZE))) {
char* p = (char*)&ptr[1];
ptr->file = file;
ptr->line = line;
ptr->size = len + 1;
add_to_list(&ptr->link, &alloc_block_list);
memcpy(p, s, len);
p[len] = '\0';
return p;
}
return 0;
}
size_t
debug_js_malloc_usable_size(JSContext* ctx, const void* p, const char* file, int line) {
ALLOC_PTR ptr = ALLOC_BLOCK(p);
return js_malloc_usable_size(ctx, ptr) - ALLOC_BLOCK_SIZE;
}
void
debug_js_free(JSContext* ctx, void* p, const char* file, int line) {
ALLOC_PTR ptr;
check_pointer(p);
ptr = ALLOC_BLOCK(p);
list_del(&ptr->link);
memset(ptr, 0xff, ALLOC_BLOCK_SIZE);
js_free(ctx, ptr);
}
void*
debug_js_malloc_rt(JSRuntime* rt, size_t n, const char* file, int line) {
ALLOC_PTR ptr;
if((ptr = js_malloc_rt(rt, n + ALLOC_BLOCK_SIZE))) {
ptr->file = file;
ptr->line = line;
ptr->size = n;
add_to_list(&ptr->link, &alloc_block_list);
return &ptr[1];
}
return 0;
}
void*
debug_js_mallocz_rt(JSRuntime* rt, size_t n, const char* file, int line) {
ALLOC_PTR ptr;
if((ptr = js_mallocz_rt(rt, n + ALLOC_BLOCK_SIZE))) {
ptr->file = file;
ptr->line = line;
ptr->size = n;
add_to_list(&ptr->link, &alloc_block_list);
return &ptr[1];
}
return 0;
}
void*
debug_js_realloc_rt(JSRuntime* rt, void* p, size_t n, const char* file, int line) {
ALLOC_PTR ptr;
if(p) {
check_pointer(p);
ptr = ALLOC_BLOCK(p);
list_del(&ptr->link);
if(n == 0) {
js_free_rt(rt, ptr);
return 0;
}
ptr = js_realloc_rt(rt, ptr, n + ALLOC_BLOCK_SIZE);
} else {
ptr = js_malloc_rt(rt, n + ALLOC_BLOCK_SIZE);
}
if(ptr) {
ptr->file = file;
ptr->line = line;
ptr->size = n;
add_to_list(&ptr->link, &alloc_block_list);
return &ptr[1];
}
return 0;
}
size_t
debug_js_malloc_usable_size_rt(JSRuntime* rt, const void* p, const char* file, int line) {
ALLOC_PTR ptr = ALLOC_BLOCK(p);
return js_malloc_usable_size_rt(rt, ptr) - ALLOC_BLOCK_SIZE;
}
void
debug_js_free_rt(JSRuntime* rt, void* p, const char* file, int line) {
ALLOC_PTR ptr;
check_pointer(p);
ptr = ALLOC_BLOCK(p);
// printf("debug_js_free_rt %p\n", p);
list_del(&ptr->link);
memset(ptr, 0xff, ALLOC_BLOCK_SIZE);
js_free_rt(rt, ptr);
}
#undef malloc
#undef calloc
#undef realloc
#undef strdup
#undef free
#undef js_malloc
#undef js_mallocz
#undef js_realloc
#undef js_strdup
#undef js_strndup
#undef js_malloc_usable_size
#undef js_free
#undef js_malloc_rt
#undef js_mallocz_rt
#undef js_realloc_rt
#undef js_malloc_usable_size_rt
#undef js_free_rt
void*
orig_malloc(size_t size) {
return malloc(size);
}
void*
orig_calloc(size_t nelem, size_t elemsz) {
return calloc(nelem, elemsz);
}
void*
orig_realloc(void* ptr, size_t size) {
return realloc(ptr, size);
}
void*
orig_strdup(const char* str) {
return strdup(str);
}
void
orig_free(void* ptr) {
free(ptr);
}
void*
orig_js_malloc(JSContext* ctx, size_t size) {
return js_malloc(ctx, size);
}
void*
orig_js_mallocz(JSContext* ctx, size_t size) {
return js_mallocz(ctx, size);
}
void*
orig_js_realloc(JSContext* ctx, void* p, size_t size) {
return js_realloc(ctx, p, size);
}
void*
orig_js_strdup(JSContext* ctx, const char* str) {
return js_strdup(ctx, str);
}
void*
orig_js_strndup(JSContext* ctx, const char* str, size_t size) {
return js_strndup(ctx, str, size);
}
size_t
orig_js_malloc_usable_size(JSContext* ctx, const void* p) {
return js_malloc_usable_size(ctx, p);
}
void
orig_js_free(JSContext* ctx, void* p) {
return js_free(ctx, p);
}
void*
orig_js_malloc_rt(JSRuntime* rt, size_t size) {
return js_malloc_rt(rt, size);
}
void*
orig_js_mallocz_rt(JSRuntime* rt, size_t size) {
return js_mallocz_rt(rt, size);
}
void*
orig_js_realloc_rt(JSRuntime* rt, void* p, size_t size) {
return js_realloc_rt(rt, p, size);
}
size_t
orig_js_malloc_usable_size_rt(JSRuntime* rt, const void* p) {
return js_malloc_usable_size_rt(rt, p);
}
void
orig_js_free_rt(JSRuntime* rt, void* p) {
return js_free_rt(rt, p);
}
/**
* @}
*/

View File

@@ -1,165 +0,0 @@
#ifndef DEBUG_H
#define DEBUG_H
#include <quickjs.h>
#include <cutils.h>
#include "defines.h"
#ifndef QUICKJS_H
#error "quickjs.h not included"
#endif
/**
* \defgroup debug debug: Debugging helpers
* @{
*/
extern thread_local struct list_head alloc_block_list;
int64_t check_pointer(void*);
void* debug_malloc(size_t, const char*, int);
void* debug_calloc(size_t, size_t, const char*, int line);
void* debug_realloc(void*, size_t, const char*, int line);
void* debug_strdup(const char*, const char*, int);
void debug_free(void*, const char*, int);
void* debug_js_malloc(JSContext*, size_t, const char*, int line);
void* debug_js_mallocz(JSContext*, size_t, const char*, int line);
void* debug_js_realloc(JSContext*, void*, size_t, const char* file, int line);
void* debug_js_realloc2(JSContext*, void*, size_t, size_t* pslack, const char* file, int line);
void* debug_js_strdup(JSContext*, const char*, const char*, int line);
void* debug_js_strndup(JSContext*, const char*, size_t, const char* file, int line);
size_t debug_js_malloc_usable_size(JSContext*, const void*, const char*, int line);
void debug_js_free(JSContext*, void*, const char*, int line);
void* debug_js_malloc_rt(JSRuntime*, size_t, const char*, int line);
void* debug_js_mallocz_rt(JSRuntime*, size_t, const char*, int line);
void* debug_js_realloc_rt(JSRuntime*, void*, size_t, const char* file, int line);
size_t debug_js_malloc_usable_size_rt(JSRuntime*, const void*, const char*, int line);
void debug_js_free_rt(JSRuntime*, void*, const char*, int line);
#if !defined(_IN_DEBUG_C)
#if defined(DEBUG_ALLOC)
#define malloc(size) debug_malloc(size, __FILE__, __LINE__)
#define calloc(nelem, size) debug_calloc(nelem, size, __FILE__, __LINE__)
#define realloc(ptr, size) debug_realloc(ptr, size, __FILE__, __LINE__)
#define strdup(str) debug_strdup(str, __FILE__, __LINE__)
#define free(ptr) debug_free(ptr, __FILE__, __LINE__)
#define js_malloc(ctx, size) debug_js_malloc(ctx, size, __FILE__, __LINE__)
#define js_mallocz(ctx, size) debug_js_mallocz(ctx, size, __FILE__, __LINE__)
#define js_realloc(ctx, ptr, size) debug_js_realloc(ctx, ptr, size, __FILE__, __LINE__)
#define js_strdup(ctx, str) debug_js_strdup(ctx, str, __FILE__, __LINE__)
#define js_strndup(ctx, str, len) debug_js_strndup(ctx, str, len, __FILE__, __LINE__)
#define js_free(ctx, ptr) debug_js_free(ctx, ptr, __FILE__, __LINE__)
#define js_malloc_usable_size(ctx, ptr) debug_js_malloc_usable_size(ctx, ptr, __FILE__, __LINE__)
#define js_malloc_rt(rt, size) debug_js_malloc_rt(rt, size, __FILE__, __LINE__)
#define js_mallocz_rt(rt, size) debug_js_mallocz_rt(rt, size, __FILE__, __LINE__)
#define js_realloc_rt(rt, ptr, size) debug_js_realloc_rt(rt, ptr, size, __FILE__, __LINE__)
#define js_malloc_usable_size_rt(rt, ptr) debug_js_malloc_usable_size_rt(rt, ptr, __FILE__, __LINE__)
#define js_free_rt(rt, ptr) debug_js_free_rt(rt, ptr, __FILE__, __LINE__)
#endif
#ifdef DEBUG_ALLOC
#define realloc_helper(name) \
void* name(void* ptr, size_t size) { \
if(ptr == 0) \
return debug_malloc(size, __FILE__, __LINE__); \
if(size == 0) \
return debug_free(ptr, __FILE__, __LINE__); \
return debug_realloc(ptr, size, __FILE__, __LINE__); \
}
#define realloc2_helper(name) \
void* name(void* opaque, void* ptr, size_t size) { \
if(ptr == 0) \
return debug_malloc(size, __FILE__, __LINE__); \
if(size == 0) { \
debug_free(ptr, __FILE__, __LINE__); \
return 0; \
} \
return debug_realloc(ptr, size, __FILE__, __LINE__); \
}
#define js_realloc_helper(name) \
void* name(JSContext* ctx, void* ptr, size_t size) { \
if(ptr == 0) \
return debug_js_malloc(ctx, size, __FILE__, __LINE__); \
if(size == 0) { \
debug_js_free(ctx, ptr, __FILE__, __LINE__); \
return 0; \
} \
return debug_js_realloc(ctx, ptr, size, __FILE__, __LINE__); \
}
#define js_realloc_rt_helper(name) \
void* name(JSRuntime* rt, void* ptr, size_t size) { \
if(ptr == 0) \
return debug_js_malloc_rt(rt, size, __FILE__, __LINE__); \
if(size == 0) { \
debug_js_free_rt(rt, ptr, __FILE__, __LINE__); \
return 0; \
} \
return debug_js_realloc_rt(rt, ptr, size, __FILE__, __LINE__); \
}
#else
#define realloc_helper(name) \
void* name(void* ptr, size_t size) { \
if(ptr == 0) \
return malloc(size); \
if(size == 0) { \
free(ptr); \
return 0; \
} \
return realloc(ptr, size); \
}
#define realloc2_helper(name) \
void* name(void* opaque, void* ptr, size_t size) { \
if(ptr == 0) \
return malloc(size); \
if(size == 0) { \
free(ptr); \
return 0; \
} \
return realloc(ptr, size); \
}
#define js_realloc_helper(name) \
void* name(JSContext* ctx, void* ptr, size_t size) { \
if(ptr == 0) \
return orig_js_malloc(ctx, size); \
if(size == 0) { \
orig_js_free(ctx, ptr); \
return 0; \
} \
return orig_js_realloc(ctx, ptr, size); \
}
#define js_realloc_rt_helper(name) \
void* name(JSRuntime* rt, void* ptr, size_t size) { \
if(ptr == 0) \
return orig_js_malloc_rt(rt, size); \
if(size == 0) { \
orig_js_free_rt(rt, ptr); \
return 0; \
} \
return orig_js_realloc_rt(rt, ptr, size); \
}
#endif
#endif
void* orig_malloc(size_t);
void* orig_calloc(size_t, size_t);
void* orig_realloc(void*, size_t);
void* orig_strdup(const char*);
void orig_free(void*);
void* orig_js_malloc(JSContext*, size_t);
void* orig_js_mallocz(JSContext*, size_t);
void* orig_js_realloc(JSContext*, void*, size_t);
void* orig_js_strdup(JSContext*, const char*);
void* orig_js_strndup(JSContext*, const char*, size_t);
size_t orig_js_malloc_usable_size(JSContext*, const void*);
void orig_js_free(JSContext*, void*);
void* orig_js_malloc_rt(JSRuntime*, size_t);
void* orig_js_mallocz_rt(JSRuntime*, size_t);
void* orig_js_realloc_rt(JSRuntime*, void*, size_t);
size_t orig_js_malloc_usable_size_rt(JSRuntime*, const void*);
void orig_js_free_rt(JSRuntime*, void*);
/**
* @}
*/
#endif /* defined(DEBUG_H) */

View File

@@ -1,181 +0,0 @@
#ifndef DEFINES_H
#define DEFINES_H
/**
* \defgroup defines defines: Preprocessor definitions
* @{
*/
#ifdef _WIN32
#include <io.h>
#define FD_TO_SOCKET(fd) ((SOCKET)_get_osfhandle((fd)))
#define SOCKET_TO_FD(fh) (_open_osfhandle((intptr_t)(fh), O_RDWR | O_BINARY))
#else
#define FD_TO_SOCKET(fd) (fd)
#define SOCKET_TO_FD(fh) (fh)
#endif
#ifndef offsetof
#define offsetof(type, field) ((size_t) & ((type*)0)->field)
#endif
#ifndef inrange
#define inrange(value, min, max) ((value) >= (min) && (value) <= (max))
#endif
#define trim_dotslash(str) (!strncmp((str), "./", 2) ? (str) + 2 : (str))
#ifndef thread_local
#ifdef _Thread_local
#define thread_local _Thread_local
#elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
#define thread_local __thread
#elif defined(_WIN32)
#define thread_local __declspec(thread)
#else
#error No TLS implementation found.
#endif
#endif
#if defined(__GNUC__) || defined(__clang__)
#define PACK __attribute__((packed))
#define ENDPACK
#else
#define PACK #pragma pack(push, 1)
#define ENDPACK #pragma pack(pop)
#endif
#define JS_CGETSET_ENUMERABLE_DEF(prop_name, fgetter, fsetter, magic_num) \
{ \
.name = prop_name, .prop_flags = JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE, .def_type = JS_DEF_CGETSET_MAGIC, .magic = magic_num, .u = { \
.getset = {.get = {.getter_magic = fgetter}, .set = {.setter_magic = fsetter}} \
} \
}
#define JS_CGETSET_MAGIC_FLAGS_DEF(prop_name, fgetter, fsetter, magic_num, flags) \
{ \
.name = prop_name, .prop_flags = flags, .def_type = JS_DEF_CGETSET_MAGIC, .magic = magic_num, .u = { \
.getset = {.get = {.getter_magic = fgetter}, .set = {.setter_magic = fsetter}} \
} \
}
#define JS_CFUNC_DEF_FLAGS(prop_name, length, func1, flags) \
{ \
.name = prop_name, .prop_flags = flags, .def_type = JS_DEF_CFUNC, .magic = 0, .u = {.func = {length, JS_CFUNC_generic, {.generic = func1}} } \
}
#define JS_CONSTANT_FLAGS(name, flags) JS_PROP_INT32_DEF(#name, name, (flags))
#define JS_CONSTANT(name) JS_PROP_INT32_DEF(#name, name, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)
#define JS_CONSTANT_NONENUMERABLE(name) JS_PROP_INT32_DEF(#name, name, JS_PROP_CONFIGURABLE)
#ifdef JS_SHARED_LIBRARY
#if defined(_WIN32) || defined(__MINGW32__)
#define VISIBLE __declspec(dllexport)
#define HIDDEN
#else
#define VISIBLE __attribute__((visibility("default")))
#define HIDDEN __attribute__((visibility("hidden")))
#endif
#else
#define VISIBLE
#define HIDDEN
#endif
#ifndef MAX_NUM
#define MAX_NUM(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN_NUM
#define MIN_NUM(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef ABS_NUM
#define ABS_NUM(n) ((n) < 0 ? -(n) : (n))
#endif
#ifndef MOD_NUM
#define MOD_NUM(n, divisor) ((((n) % (divisor)) + (divisor)) % (divisor))
#endif
#ifndef SIGN_NUM
#define SIGN_NUM(n) ((n) < 0)
#endif
#define COLOR_BLACK "\x1b[0;30m"
#define COLOR_RED "\x1b[0;31m"
#define COLOR_GREEN "\x1b[0;32m"
#define COLOR_BROWN "\x1b[0;33m"
#define COLOR_BLUE "\x1b[0;34m"
#define COLOR_PURPLE "\x1b[0;35m"
#define COLOR_MARINE "\x1b[0;36m"
#define COLOR_LIGHTGRAY "\x1b[0;37m"
#define COLOR_GRAY "\x1b[1;30m"
#define COLOR_NONE "\x1b[0m"
#define COLOR_LIGHTRED "\x1b[1;31m"
#define COLOR_LIGHTGREEN "\x1b[1;32m"
#define COLOR_YELLOW "\x1b[1;33m"
#define COLOR_LIGHTBLUE "\x1b[1;34m"
#define COLOR_MAGENTA "\x1b[1;35m"
#define COLOR_CYAN "\x1b[1;36m"
#define COLOR_WHITE "\x1b[1;37m"
#define BGCOLOR_RED "\x1b[48;5;124m"
#define BGCOLOR_BLUE "\x1b[48;5;20m"
#define BGCOLOR_YELLOW "\x1b[48;5;214m"
#define BGCOLOR_GREEN "\x1b[48;5;28m"
#define BGCOLOR_PINK "\x1b[48;5;165m"
#define JS_VALUE_FREE(ctx, value) \
do { \
JS_FreeValue((ctx), (value)); \
(value) = JS_UNDEFINED; \
} while(0);
#define JS_VALUE_FREE_RT(ctx, value) \
do { \
JS_FreeValueRT((ctx), (value)); \
(value) = JS_UNDEFINED; \
} while(0);
#if 0
#define js_object_tmpmark_set(value) \
do { \
((uint8_t*)JS_VALUE_GET_OBJ((value)))[5] |= 0x40; \
} while(0);
#define js_object_tmpmark_clear(value) \
do { \
((uint8_t*)JS_VALUE_GET_OBJ((value)))[5] &= ~0x40; \
} while(0);
#define js_object_tmpmark_isset(value) (((uint8_t*)JS_VALUE_GET_OBJ((value)))[5] & 0x40)
#else
#define js_object_tmpmark_set(value) \
do { \
JS_VALUE_GET_OBJ((value))->tmp_mark |= 0x40; \
} while(0);
#define js_object_tmpmark_clear(value) \
do { \
JS_VALUE_GET_OBJ((value))->tmp_mark &= ~0x40; \
} while(0);
#define js_object_tmpmark_isset(value) (JS_VALUE_GET_OBJ((value))->tmp_mark & 0x40)
#endif
#define js_runtime_exception_set(rt, value) \
do { \
*(JSValue*)((uint8_t*)(rt) + 216) = value; \
} while(0);
#define js_runtime_exception_get(rt) (*(JSValue*)((uint8_t*)(rt) + 216))
#define js_runtime_exception_clear(rt) \
do { \
if(!JS_IsNull(js_runtime_exception_get(rt))) \
JS_FreeValueRT((rt), js_runtime_exception_get(rt)); \
js_runtime_exception_set(rt, JS_NULL); \
} while(0)
#define JS_ATOM_TAG_INT (1U << 31)
#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1)
#define JS_ATOM_ISINT(i) ((JSAtom)((i)&JS_ATOM_TAG_INT))
#define JS_ATOM_FROMINT(i) ((JSAtom)((i)&JS_ATOM_MAX_INT) | JS_ATOM_TAG_INT)
#define JS_ATOM_TOINT(i) (unsigned int)(((JSAtom)(i) & (~(JS_ATOM_TAG_INT))))
/**
* @}
*/
#endif /* defined(DEFINES_H) */

View File

@@ -1,45 +0,0 @@
/* File generated automatically by the QuickJS compiler. */
#include "quickjs-libc.h"
const uint32_t qjsc_hello_size = 87;
const uint8_t qjsc_hello[87] = {
0x43, 0x04, 0x0e, 0x63, 0x6f, 0x6e, 0x73, 0x6f,
0x6c, 0x65, 0x06, 0x6c, 0x6f, 0x67, 0x16, 0x48,
0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72,
0x6c, 0x64, 0x22, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x73, 0x2f, 0x68, 0x65, 0x6c, 0x6c,
0x6f, 0x2e, 0x6a, 0x73, 0x0c, 0x00, 0x06, 0x00,
0xa2, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00,
0x14, 0x01, 0xa4, 0x01, 0x00, 0x00, 0x00, 0x38,
0xe3, 0x00, 0x00, 0x00, 0x42, 0xe4, 0x00, 0x00,
0x00, 0x04, 0xe5, 0x00, 0x00, 0x00, 0x24, 0x01,
0x00, 0xcf, 0x28, 0xcc, 0x03, 0x01, 0x00,
};
static JSContext *JS_NewCustomContext(JSRuntime *rt)
{
JSContext *ctx = JS_NewContextRaw(rt);
if (!ctx)
return NULL;
JS_AddIntrinsicBaseObjects(ctx);
return ctx;
}
int main(int argc, char **argv)
{
JSRuntime *rt;
JSContext *ctx;
rt = JS_NewRuntime();
js_std_set_worker_new_context_func(JS_NewCustomContext);
js_std_init_handlers(rt);
ctx = JS_NewCustomContext(rt);
js_std_add_helpers(ctx, argc, argv);
js_std_eval_binary(ctx, qjsc_hello, qjsc_hello_size, 0);
js_std_loop(ctx);
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return 0;
}

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_1_H
#define TUTF8E_ISO_8859_1_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_1_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_1, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_1_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_1, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_1_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_1, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_1_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_1, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_10_H
#define TUTF8E_ISO_8859_10_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_10_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_10, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_10_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_10, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_10_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_10, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_10_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_10, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_11_H
#define TUTF8E_ISO_8859_11_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_11_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_11, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_11_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_11, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_11_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_11, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_11_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_11, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_13_H
#define TUTF8E_ISO_8859_13_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_13_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_13, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_13_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_13, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_13_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_13, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_13_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_13, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_14_H
#define TUTF8E_ISO_8859_14_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_14_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_14, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_14_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_14, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_14_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_14, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_14_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_14, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_15_H
#define TUTF8E_ISO_8859_15_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_15_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_15, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_15_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_15, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_15_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_15, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_15_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_15, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_16_H
#define TUTF8E_ISO_8859_16_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_16_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_16, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_16_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_16, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_16_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_16, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_16_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_16, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_2_H
#define TUTF8E_ISO_8859_2_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_2_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_2, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_2_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_2, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_2_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_2, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_2_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_2, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_3_H
#define TUTF8E_ISO_8859_3_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_3_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_3, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_3_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_3, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_3_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_3, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_3_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_3, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_4_H
#define TUTF8E_ISO_8859_4_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_4_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_4, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_4_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_4, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_4_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_4, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_4_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_4, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_5_H
#define TUTF8E_ISO_8859_5_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_5_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_5, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_5_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_5, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_5_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_5, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_5_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_5, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_6_H
#define TUTF8E_ISO_8859_6_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_6_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_6, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_6_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_6, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_6_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_6, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_6_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_6, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_7_H
#define TUTF8E_ISO_8859_7_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_7_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_7, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_7_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_7, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_7_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_7, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_7_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_7, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_8_H
#define TUTF8E_ISO_8859_8_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_8_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_8, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_8_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_8, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_8_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_8, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_8_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_8, input, input_length, invalid, output, output_length);
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef TUTF8E_ISO_8859_9_H
#define TUTF8E_ISO_8859_9_H
#include <tutf8e.h>
static inline int tutf8e_iso_8859_9_string_length(const char *input, size_t *input_length, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_length(tutf8e_encoder_iso_8859_9, input, input_length, invalid, output_length);
}
static inline int tutf8e_iso_8859_9_string_encode(const char *input, char *output, const char *invalid, size_t *output_length)
{
return tutf8e_encoder_string_encode(tutf8e_encoder_iso_8859_9, input, invalid, output, output_length);
}
static inline int tutf8e_iso_8859_9_buffer_length(const char *i, size_t input_length, const char *invalid, size_t *length)
{
return tutf8e_encoder_buffer_length(tutf8e_encoder_iso_8859_9, input, input_length, invalid, length);
}
static inline int tutf8e_iso_8859_9_buffer_encode(const char *i, size_t input_length, const char *invalid, char *output, size_t *output_length)
{
return tutf8e_encoder_buffer_encode(tutf8e_encoder_iso_8859_9, input, input_length, invalid, output, output_length);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,535 +0,0 @@
/*
* Tiny arbitrary precision floating point library
*
* Copyright (c) 2017-2021 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBBF_H
#define LIBBF_H
#include <stddef.h>
#include <stdint.h>
#if defined(__SIZEOF_INT128__) && (INTPTR_MAX >= INT64_MAX)
#define LIMB_LOG2_BITS 6
#else
#define LIMB_LOG2_BITS 5
#endif
#define LIMB_BITS (1 << LIMB_LOG2_BITS)
#if LIMB_BITS == 64
typedef __int128 int128_t;
typedef unsigned __int128 uint128_t;
typedef int64_t slimb_t;
typedef uint64_t limb_t;
typedef uint128_t dlimb_t;
#define BF_RAW_EXP_MIN INT64_MIN
#define BF_RAW_EXP_MAX INT64_MAX
#define LIMB_DIGITS 19
#define BF_DEC_BASE UINT64_C(10000000000000000000)
#else
typedef int32_t slimb_t;
typedef uint32_t limb_t;
typedef uint64_t dlimb_t;
#define BF_RAW_EXP_MIN INT32_MIN
#define BF_RAW_EXP_MAX INT32_MAX
#define LIMB_DIGITS 9
#define BF_DEC_BASE 1000000000U
#endif
/* in bits */
/* minimum number of bits for the exponent */
#define BF_EXP_BITS_MIN 3
/* maximum number of bits for the exponent */
#define BF_EXP_BITS_MAX (LIMB_BITS - 3)
/* extended range for exponent, used internally */
#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1)
/* minimum possible precision */
#define BF_PREC_MIN 2
/* minimum possible precision */
#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2)
/* some operations support infinite precision */
#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */
#if LIMB_BITS == 64
#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197))
#else
#define BF_CHKSUM_MOD 975620677U
#endif
#define BF_EXP_ZERO BF_RAW_EXP_MIN
#define BF_EXP_INF (BF_RAW_EXP_MAX - 1)
#define BF_EXP_NAN BF_RAW_EXP_MAX
/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0,
+/-infinity is represented with expn = BF_EXP_INF and len = 0,
NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored)
*/
typedef struct {
struct bf_context_t *ctx;
int sign;
slimb_t expn;
limb_t len;
limb_t *tab;
} bf_t;
typedef struct {
/* must be kept identical to bf_t */
struct bf_context_t *ctx;
int sign;
slimb_t expn;
limb_t len;
limb_t *tab;
} bfdec_t;
typedef enum {
BF_RNDN, /* round to nearest, ties to even */
BF_RNDZ, /* round to zero */
BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */
BF_RNDU, /* round to +inf */
BF_RNDNA, /* round to nearest, ties away from zero */
BF_RNDA, /* round away from zero */
BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU,
inexact flag is always set) */
} bf_rnd_t;
/* allow subnormal numbers. Only available if the number of exponent
bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */
#define BF_FLAG_SUBNORMAL (1 << 3)
/* 'prec' is the precision after the radix point instead of the whole
mantissa. Can only be used with bf_round() and
bfdec_[add|sub|mul|div|sqrt|round](). */
#define BF_FLAG_RADPNT_PREC (1 << 4)
#define BF_RND_MASK 0x7
#define BF_EXP_BITS_SHIFT 5
#define BF_EXP_BITS_MASK 0x3f
/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */
#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)
/* contains the rounding mode and number of exponents bits */
typedef uint32_t bf_flags_t;
typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size);
typedef struct {
bf_t val;
limb_t prec;
} BFConstCache;
typedef struct bf_context_t {
void *realloc_opaque;
bf_realloc_func_t *realloc_func;
BFConstCache log2_cache;
BFConstCache pi_cache;
struct BFNTTState *ntt_state;
} bf_context_t;
static inline int bf_get_exp_bits(bf_flags_t flags)
{
int e;
e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK;
if (e == BF_EXP_BITS_MASK)
return BF_EXP_BITS_MAX + 1;
else
return BF_EXP_BITS_MAX - e;
}
static inline bf_flags_t bf_set_exp_bits(int n)
{
return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT;
}
/* returned status */
#define BF_ST_INVALID_OP (1 << 0)
#define BF_ST_DIVIDE_ZERO (1 << 1)
#define BF_ST_OVERFLOW (1 << 2)
#define BF_ST_UNDERFLOW (1 << 3)
#define BF_ST_INEXACT (1 << 4)
/* indicate that a memory allocation error occured. NaN is returned */
#define BF_ST_MEM_ERROR (1 << 5)
#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */
static inline slimb_t bf_max(slimb_t a, slimb_t b)
{
if (a > b)
return a;
else
return b;
}
static inline slimb_t bf_min(slimb_t a, slimb_t b)
{
if (a < b)
return a;
else
return b;
}
void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func,
void *realloc_opaque);
void bf_context_end(bf_context_t *s);
/* free memory allocated for the bf cache data */
void bf_clear_cache(bf_context_t *s);
static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size)
{
return s->realloc_func(s->realloc_opaque, ptr, size);
}
/* 'size' must be != 0 */
static inline void *bf_malloc(bf_context_t *s, size_t size)
{
return bf_realloc(s, NULL, size);
}
static inline void bf_free(bf_context_t *s, void *ptr)
{
/* must test ptr otherwise equivalent to malloc(0) */
if (ptr)
bf_realloc(s, ptr, 0);
}
void bf_init(bf_context_t *s, bf_t *r);
static inline void bf_delete(bf_t *r)
{
bf_context_t *s = r->ctx;
/* we accept to delete a zeroed bf_t structure */
if (s && r->tab) {
bf_realloc(s, r->tab, 0);
}
}
static inline void bf_neg(bf_t *r)
{
r->sign ^= 1;
}
static inline int bf_is_finite(const bf_t *a)
{
return (a->expn < BF_EXP_INF);
}
static inline int bf_is_nan(const bf_t *a)
{
return (a->expn == BF_EXP_NAN);
}
static inline int bf_is_zero(const bf_t *a)
{
return (a->expn == BF_EXP_ZERO);
}
static inline void bf_memcpy(bf_t *r, const bf_t *a)
{
*r = *a;
}
int bf_set_ui(bf_t *r, uint64_t a);
int bf_set_si(bf_t *r, int64_t a);
void bf_set_nan(bf_t *r);
void bf_set_zero(bf_t *r, int is_neg);
void bf_set_inf(bf_t *r, int is_neg);
int bf_set(bf_t *r, const bf_t *a);
void bf_move(bf_t *r, bf_t *a);
int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode);
int bf_set_float64(bf_t *a, double d);
int bf_cmpu(const bf_t *a, const bf_t *b);
int bf_cmp_full(const bf_t *a, const bf_t *b);
int bf_cmp(const bf_t *a, const bf_t *b);
static inline int bf_cmp_eq(const bf_t *a, const bf_t *b)
{
return bf_cmp(a, b) == 0;
}
static inline int bf_cmp_le(const bf_t *a, const bf_t *b)
{
return bf_cmp(a, b) <= 0;
}
static inline int bf_cmp_lt(const bf_t *a, const bf_t *b)
{
return bf_cmp(a, b) < 0;
}
int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags);
int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags);
int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec,
bf_flags_t flags);
int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags);
int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
#define BF_DIVREM_EUCLIDIAN BF_RNDF
int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b,
limb_t prec, bf_flags_t flags, int rnd_mode);
int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
bf_flags_t flags, int rnd_mode);
int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
bf_flags_t flags, int rnd_mode);
/* round to integer with infinite precision */
int bf_rint(bf_t *r, int rnd_mode);
int bf_round(bf_t *r, limb_t prec, bf_flags_t flags);
int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a);
int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
slimb_t bf_get_exp_min(const bf_t *a);
int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b);
int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b);
int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b);
/* additional flags for bf_atof */
/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */
#define BF_ATOF_NO_HEX (1 << 16)
/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */
#define BF_ATOF_BIN_OCT (1 << 17)
/* Do not parse NaN or Inf */
#define BF_ATOF_NO_NAN_INF (1 << 18)
/* return the exponent separately */
#define BF_ATOF_EXPONENT (1 << 19)
int bf_atof(bf_t *a, const char *str, const char **pnext, int radix,
limb_t prec, bf_flags_t flags);
/* this version accepts prec = BF_PREC_INF and returns the radix
exponent */
int bf_atof2(bf_t *r, slimb_t *pexponent,
const char *str, const char **pnext, int radix,
limb_t prec, bf_flags_t flags);
int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
slimb_t expn, limb_t prec, bf_flags_t flags);
/* Conversion of floating point number to string. Return a null
terminated string or NULL if memory error. *plen contains its
length if plen != NULL. The exponent letter is "e" for base 10,
"p" for bases 2, 8, 16 with a binary exponent and "@" for the other
bases. */
#define BF_FTOA_FORMAT_MASK (3 << 16)
/* fixed format: prec significant digits rounded with (flags &
BF_RND_MASK). Exponential notation is used if too many zeros are
needed.*/
#define BF_FTOA_FORMAT_FIXED (0 << 16)
/* fractional format: prec digits after the decimal point rounded with
(flags & BF_RND_MASK) */
#define BF_FTOA_FORMAT_FRAC (1 << 16)
/* free format:
For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum
number of digits to represent 'a'. The precision and the rounding
mode are ignored.
For the non binary radices with bf_ftoa(): use as many digits as
necessary so that bf_atof() return the same number when using
precision 'prec', rounding to nearest and the subnormal
configuration of 'flags'. The result is meaningful only if 'a' is
already rounded to 'prec' bits. If the subnormal flag is set, the
exponent in 'flags' must also be set to the desired exponent range.
*/
#define BF_FTOA_FORMAT_FREE (2 << 16)
/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits
(takes more computation time). Identical to BF_FTOA_FORMAT_FREE for
binary radices with bf_ftoa() and for bfdec_ftoa(). */
#define BF_FTOA_FORMAT_FREE_MIN (3 << 16)
/* force exponential notation for fixed or free format */
#define BF_FTOA_FORCE_EXP (1 << 20)
/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for
base 2 if non zero value */
#define BF_FTOA_ADD_PREFIX (1 << 21)
/* return "Infinity" instead of "Inf" and add a "+" for positive
exponents */
#define BF_FTOA_JS_QUIRKS (1 << 22)
char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec,
bf_flags_t flags);
/* modulo 2^n instead of saturation. NaN and infinity return 0 */
#define BF_GET_INT_MOD (1 << 0)
int bf_get_int32(int *pres, const bf_t *a, int flags);
int bf_get_int64(int64_t *pres, const bf_t *a, int flags);
int bf_get_uint64(uint64_t *pres, const bf_t *a);
/* the following functions are exported for testing only. */
void mp_print_str(const char *str, const limb_t *tab, limb_t n);
void bf_print_str(const char *str, const bf_t *a);
int bf_resize(bf_t *r, limb_t len);
int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len);
int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags);
int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k);
slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv,
int is_ceil1);
int mp_mul(bf_context_t *s, limb_t *result,
const limb_t *op1, limb_t op1_size,
const limb_t *op2, limb_t op2_size);
limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2,
limb_t n, limb_t carry);
limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n);
int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n);
int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n);
limb_t bf_isqrt(limb_t a);
/* transcendental functions */
int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags);
int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags);
int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */
int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags);
int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x,
limb_t prec, bf_flags_t flags);
int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
/* decimal floating point */
static inline void bfdec_init(bf_context_t *s, bfdec_t *r)
{
bf_init(s, (bf_t *)r);
}
static inline void bfdec_delete(bfdec_t *r)
{
bf_delete((bf_t *)r);
}
static inline void bfdec_neg(bfdec_t *r)
{
r->sign ^= 1;
}
static inline int bfdec_is_finite(const bfdec_t *a)
{
return (a->expn < BF_EXP_INF);
}
static inline int bfdec_is_nan(const bfdec_t *a)
{
return (a->expn == BF_EXP_NAN);
}
static inline int bfdec_is_zero(const bfdec_t *a)
{
return (a->expn == BF_EXP_ZERO);
}
static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a)
{
bf_memcpy((bf_t *)r, (const bf_t *)a);
}
int bfdec_set_ui(bfdec_t *r, uint64_t a);
int bfdec_set_si(bfdec_t *r, int64_t a);
static inline void bfdec_set_nan(bfdec_t *r)
{
bf_set_nan((bf_t *)r);
}
static inline void bfdec_set_zero(bfdec_t *r, int is_neg)
{
bf_set_zero((bf_t *)r, is_neg);
}
static inline void bfdec_set_inf(bfdec_t *r, int is_neg)
{
bf_set_inf((bf_t *)r, is_neg);
}
static inline int bfdec_set(bfdec_t *r, const bfdec_t *a)
{
return bf_set((bf_t *)r, (bf_t *)a);
}
static inline void bfdec_move(bfdec_t *r, bfdec_t *a)
{
bf_move((bf_t *)r, (bf_t *)a);
}
static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b)
{
return bf_cmpu((const bf_t *)a, (const bf_t *)b);
}
static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b)
{
return bf_cmp_full((const bf_t *)a, (const bf_t *)b);
}
static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b)
{
return bf_cmp((const bf_t *)a, (const bf_t *)b);
}
static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b)
{
return bfdec_cmp(a, b) == 0;
}
static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b)
{
return bfdec_cmp(a, b) <= 0;
}
static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b)
{
return bfdec_cmp(a, b) < 0;
}
int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
bf_flags_t flags);
int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
bf_flags_t flags);
int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
limb_t prec, bf_flags_t flags, int rnd_mode);
int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags, int rnd_mode);
int bfdec_rint(bfdec_t *r, int rnd_mode);
int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags);
int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags);
int bfdec_get_int32(int *pres, const bfdec_t *a);
int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b);
char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags);
int bfdec_atof(bfdec_t *r, const char *str, const char **pnext,
limb_t prec, bf_flags_t flags);
/* the following functions are exported for testing only. */
extern const limb_t mp_pow_dec[LIMB_DIGITS + 1];
void bfdec_print_str(const char *str, const bfdec_t *a);
static inline int bfdec_resize(bfdec_t *r, limb_t len)
{
return bf_resize((bf_t *)r, len);
}
int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags);
#endif /* LIBBF_H */

View File

@@ -1,57 +0,0 @@
/*
* Regular Expression Engine
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifdef DEF
DEF(invalid, 1) /* never used */
DEF(char, 3)
DEF(char32, 5)
DEF(dot, 1)
DEF(any, 1) /* same as dot but match any character including line terminator */
DEF(line_start, 1)
DEF(line_end, 1)
DEF(goto, 5)
DEF(split_goto_first, 5)
DEF(split_next_first, 5)
DEF(match, 1)
DEF(save_start, 2) /* save start position */
DEF(save_end, 2) /* save end position, must come after saved_start */
DEF(save_reset, 3) /* reset save positions */
DEF(loop, 5) /* decrement the top the stack and goto if != 0 */
DEF(push_i32, 5) /* push integer on the stack */
DEF(drop, 1)
DEF(word_boundary, 1)
DEF(not_word_boundary, 1)
DEF(back_reference, 2)
DEF(backward_back_reference, 2) /* must come after back_reference */
DEF(range, 3) /* variable length */
DEF(range32, 3) /* variable length */
DEF(lookahead, 5)
DEF(negative_lookahead, 5)
DEF(push_char_pos, 1) /* push the character position on the stack */
DEF(check_advance, 1) /* pop one stack element and check that it is different from the character position */
DEF(prev, 1) /* go to the previous char */
DEF(simple_greedy_quant, 17)
#endif /* DEF */

File diff suppressed because it is too large Load Diff

View File

@@ -1,92 +0,0 @@
/*
* Regular Expression Engine
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBREGEXP_H
#define LIBREGEXP_H
#include <stddef.h>
#include "libunicode.h"
#define LRE_BOOL int /* for documentation purposes */
#define LRE_FLAG_GLOBAL (1 << 0)
#define LRE_FLAG_IGNORECASE (1 << 1)
#define LRE_FLAG_MULTILINE (1 << 2)
#define LRE_FLAG_DOTALL (1 << 3)
#define LRE_FLAG_UNICODE (1 << 4)
#define LRE_FLAG_STICKY (1 << 5)
#define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */
#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */
uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
const char *buf, size_t buf_len, int re_flags,
void *opaque);
int lre_get_capture_count(const uint8_t *bc_buf);
int lre_get_flags(const uint8_t *bc_buf);
const char *lre_get_groupnames(const uint8_t *bc_buf);
int lre_exec(uint8_t **capture,
const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
int cbuf_type, void *opaque);
int lre_parse_escape(const uint8_t **pp, int allow_utf16);
LRE_BOOL lre_is_space(int c);
/* must be provided by the user */
LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size);
void *lre_realloc(void *opaque, void *ptr, size_t size);
/* JS identifier test */
extern uint32_t const lre_id_start_table_ascii[4];
extern uint32_t const lre_id_continue_table_ascii[4];
static inline int lre_js_is_ident_first(int c)
{
if ((uint32_t)c < 128) {
return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1;
} else {
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_start(c);
#else
return !lre_is_space(c);
#endif
}
}
static inline int lre_js_is_ident_next(int c)
{
if ((uint32_t)c < 128) {
return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1;
} else {
/* ZWNJ and ZWJ are accepted in identifiers */
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_continue(c) || c == 0x200C || c == 0x200D;
#else
return !lre_is_space(c) || c == 0x200C || c == 0x200D;
#endif
}
}
#undef LRE_BOOL
#endif /* LIBREGEXP_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,127 +0,0 @@
/*
* Unicode utilities
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBUNICODE_H
#define LIBUNICODE_H
#include <inttypes.h>
#define LRE_BOOL int /* for documentation purposes */
/* define it to include all the unicode tables (40KB larger) */
#define CONFIG_ALL_UNICODE
#define LRE_CC_RES_LEN_MAX 3
typedef enum {
UNICODE_NFC,
UNICODE_NFD,
UNICODE_NFKC,
UNICODE_NFKD,
} UnicodeNormalizationEnum;
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
int lre_canonicalize(uint32_t c, LRE_BOOL is_unicode);
LRE_BOOL lre_is_cased(uint32_t c);
LRE_BOOL lre_is_case_ignorable(uint32_t c);
/* char ranges */
typedef struct {
int len; /* in points, always even */
int size;
uint32_t *points; /* points sorted by increasing value */
void *mem_opaque;
void *(*realloc_func)(void *opaque, void *ptr, size_t size);
} CharRange;
typedef enum {
CR_OP_UNION,
CR_OP_INTER,
CR_OP_XOR,
} CharRangeOpEnum;
void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
void cr_free(CharRange *cr);
int cr_realloc(CharRange *cr, int size);
int cr_copy(CharRange *cr, const CharRange *cr1);
static inline int cr_add_point(CharRange *cr, uint32_t v)
{
if (cr->len >= cr->size) {
if (cr_realloc(cr, cr->len + 1))
return -1;
}
cr->points[cr->len++] = v;
return 0;
}
static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2)
{
if ((cr->len + 2) > cr->size) {
if (cr_realloc(cr, cr->len + 2))
return -1;
}
cr->points[cr->len++] = c1;
cr->points[cr->len++] = c2;
return 0;
}
int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len);
static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2)
{
uint32_t b_pt[2];
b_pt[0] = c1;
b_pt[1] = c2 + 1;
return cr_union1(cr, b_pt, 2);
}
int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
const uint32_t *b_pt, int b_len, int op);
int cr_invert(CharRange *cr);
int cr_regexp_canonicalize(CharRange *cr, LRE_BOOL is_unicode);
#ifdef CONFIG_ALL_UNICODE
LRE_BOOL lre_is_id_start(uint32_t c);
LRE_BOOL lre_is_id_continue(uint32_t c);
int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
UnicodeNormalizationEnum n_type,
void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
/* Unicode character range functions */
int unicode_script(CharRange *cr,
const char *script_name, LRE_BOOL is_ext);
int unicode_general_category(CharRange *cr, const char *gc_name);
int unicode_prop(CharRange *cr, const char *prop_name);
#endif /* CONFIG_ALL_UNICODE */
#undef LRE_BOOL
#endif /* LIBUNICODE_H */

View File

@@ -1,153 +0,0 @@
#include <libutf.h>
#include <assert.h>
#include <stdlib.h>
LibutfC16Type libutf_c16_type(uint_least16_t c16) {
if (0xD800 != (0xF800 & c16)) {
return LIBUTF_UTF16_NOT_SURROGATE;
}
return (c16 & 0x0400) ? LIBUTF_UTF16_SURROGATE_LOW : LIBUTF_UTF16_SURROGATE_HIGH;
}
LibutfC8Type libutf_c8_type(char c) {
static const LibutfC8Type lookup_table[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4,-2,-2,-2,-2,-2,-2,-2,-2,
};
return lookup_table[(unsigned char) c];
}
bool libutf_c32_to_c8(uint_least32_t c32, int *length, char c8[4]) {
if (!libutf_c32_is_valid(c32)) {
*length = 3;
c8[0] = (char) 0xEF;
c8[1] = (char) 0xBF;
c8[2] = (char) 0xBD;
return false;
}
if (c32 <= 0x007F) {
c8[0] = c32;
*length = 1;
return true;
} else if (c32 <= 0x07FF) {
c8[0] = (char) (0xC0 | (c32 >> 6));
c8[1] = (char) (0x80 | (c32 & 0x3F));
*length = 2;
return true;
} else if (c32 <= 0xFFFF) {
c8[0] = (char) (0xE0 | (c32 >> 12));
c8[1] = (char) (0x80 | ((c32 >> 6) & 0x3F));
c8[2] = (char) (0x80 | (c32 & 0x3F));
*length = 3;
return true;
}
assert(c32 <= 0x10FFFF);
c8[0] = (char) (0xF0 | (c32 >> 18));
c8[1] = (char) (0x80 | ((c32 >> 12) & 0x3F));
c8[2] = (char) (0x80 | ((c32 >> 6) & 0x3F));
c8[3] = (char) (0x80 | (c32 & 0x3F));
*length = 4;
return true;
}
bool libutf_c8_to_c32(const char *c8, uint_least32_t *c32) {
if (!c8 || !c32) {
return false;
}
unsigned char c = *c8;
*c32 = 0xFFFD;
LibutfC8Type type = libutf_c8_type(c);
if (type < 0) {
return false;
}
int len = type;
uint_least32_t result;
switch (len) {
case 1:
*c32 = c;
return true;
case 2:
result = c & 0x1F;
break;
case 3:
result = c & 0x0F;
break;
case 4:
result = c & 0x07;
break;
default:
abort();
}
for (int i = 1; i < len; ++i) {
c = *++c8;
if ((0xC0 & c) != 0x80) {
return false;
}
result = (result << 6) + (c & 0x3F);
}
if (!libutf_c32_is_valid(result)) {
return false;
}
char c8seq[4];
int n;
bool ok = libutf_c32_to_c8(result, &n, c8seq);
assert(ok);
if (n < (int) len) {
return false;
}
*c32 = result;
return true;
}
bool libutf_c32_to_c16(uint_least32_t c32, int *length, uint_least16_t c16[2]) {
if (!libutf_c32_is_valid(c32)) {
c16[0] = 0xFFFD;
c16[1] = 0;
*length = 1;
return false;
}
if (c32 < 0x10000) {
c16[0] = c32;
c16[1] = 0;
*length = 1;
} else {
c32 -= 0x10000;
c16[0] = 0xD800 | (c32 >> 10);
c16[1] = 0xDC00 | (c32 & 0x3FF);
*length = 2;
}
return true;
}
bool libutf_c16_to_c32(uint_least16_t c16[2], uint_least32_t *c32) {
LibutfC16Type type = libutf_c16_type(c16[0]);
if (LIBUTF_UTF16_NOT_SURROGATE == type) {
*c32 = c16[0];
return true;
}
if (LIBUTF_UTF16_SURROGATE_HIGH != type || LIBUTF_UTF16_SURROGATE_LOW != libutf_c16_type(c16[1])) {
*c32 = 0xFFFD;
return false;
}
*c32 = 0x10000 + ((c16[0] & 0x3FF) << 10) + (c16[1] & 0x3ff);
return true;
}
bool libutf_c32_is_valid(uint_least32_t c32) {
return c32 < 0xD800 || (0xDFFF < c32 && c32 < 0x110000);
}

View File

@@ -1,109 +0,0 @@
/* LIBrary of UTF-(8,16,32) helper utility functions.
*
* Terminology:
* code point = number of a Unicode character
* code unit
* = 1 byte for UTF-8
* = 2 bytes for UTF-16
* = 4 bytes for UTF-32
* sequence = sequence of code units encoding SINGLE Unicode character
*
* Unicode has code points 0x000000..0x10FFFF except for the codes 0xD800..0xDFFF which are
* reserved for UTF-16 surrogate pairs.
*
* UTF-32 is the simplest Unicode encoding. It encodes Unicode code points as is. Thus we may use
* UTF-32 as a synonym for Unicode code point.
* + O(1) indexing
* - the text is 2-4 times bigger than in other encodings
*
* UTF-16 is variable width encoding. Each Unicode code point is represented as either single
* code unit (two-byte number) or two code units which are called surrogate pairs.
* - ASCII-only texts will be twice as big as in ASCII
* - O(n) indexing
*
* UTF-8 is a variable width encoding. Each Unicode code point is represented as a sequence
* of up to 4 bytes. The first byte determines the length of the sequence.
* + is a superset of ASCII
* + ASCII-only texts will have the same size
* - O(n) indexing
* - single Unicode code point can have different representations in
* UTF-8, only the shortest one is considered valid
*/
#ifndef LIBUTF_H
#define LIBUTF_H
#include <stdbool.h>
#include <stdint.h>
// UTF-8 code unit type
typedef enum {
LIBUTF_UTF8_OVERLONG = -2,
LIBUTF_UTF8_TRAILING = -1,
LIBUTF_UTF8_ONE_BYTE = 1,
LIBUTF_UTF8_TWO_BYTE = 2,
LIBUTF_UTF8_THREE_BYTE = 3,
LIBUTF_UTF8_FOUR_BYTE = 4,
} LibutfC8Type;
// UTF-16 code unit type
typedef enum {
LIBUTF_UTF16_SURROGATE_LOW = -1,
LIBUTF_UTF16_NOT_SURROGATE = 1,
LIBUTF_UTF16_SURROGATE_HIGH = 2,
} LibutfC16Type;
/* Determine type of UTF-16 code unit. type = length of UTF-16 sequence starting with the specified code unit
* or negative value if sequence cannot start with this code unit. */
LibutfC16Type libutf_c16_type(uint_least16_t c16);
/* Determine type of UTF-8 sequence based on the first byte. type = length of UTF-8 sequence starting
* with this byte or negative value if the byte cannot be first. UTF-8 may be up to 4 bytes long.
* Common idiom for using this function:
* >>> LibutfC8Type type = libutf_c8_type(c);
* >>> if (type < 0) {
* >>> return ERROR_CODE;
* >>> }
* >>> int length = type;
* */
LibutfC8Type libutf_c8_type(char c);
/* Convert Unicode code point into UTF-8 sequence. If c32 is not a valid
* Unicode code point c8 will be filled with UTF-8 representation of special
* replacement character U+FFFD, *length will be set to its length and false will be returned.
* c32 -- UTF-32 Unicode code point
* length -- where to put length of the UTF-8 sequence.
* c8 -- where to put UTF-8 sequence. Make sure string has enough space.
* result -- true if c32 is a valid Unicode code point or false otherwise. */
bool libutf_c32_to_c8(uint_least32_t c32, int *length, char c8[4]);
/* Convert UTF-8 sequence into a UTF-32 Unicode code point. If c8 does not
* point to a valid UTF-8 sequence c32 will be filled with special replacement
* character U+FFFD and false will be returned.
* c8 -- pointer to UTF-8 sequence.
* c32 -- where to save UTF-32 Unicode code point.
* result -- true if c8 points to a valid UTF-8 sequence or false otherwise. */
bool libutf_c8_to_c32(const char *c8, uint_least32_t *c32);
/* Convert UTF-32 Unicode code point into UTF-16 sequence. If c32 is not a valid
* Unicode code point c16 will be filled with UTF-16 representation of special
* replacement character U+FFFD and false will be returned.
* c32 -- UTF-32 Unicode code point.
* length -- were to put length of UTF-16 sequence.
* c16 -- where to put UTF-16 sequence (c16[0] -- high surrogate, c16[1] -- low surrogate)
* result -- true if c32 is a valid Unicode code point or false otherwise. */
bool libutf_c32_to_c16(uint_least32_t c32, int *length, uint_least16_t c16[2]);
/* Construct UTF-32 Unicode code point from UTF-16 surrogate pair. high
* must be high surrogate and low must be low surrogate otherwise *c32 will
* be filled with special replacement character U+FFFD and false will be returned.
* c16 -- where to put UTF-16 sequence (c32[0] -- high surrogate, c32[1] -- low surrogate)
* result -- true if c16 points to a valid UTF-16 sequence (single not surrogate
* or valid surrogate pair) or false otherwise. */
bool libutf_c16_to_c32(uint_least16_t c16[2], uint_least32_t *c32);
/* Check whether given value is a valid Unicode code point i.e. below 0x110000 and is not
* a UTF-16 surrogate. */
bool libutf_c32_is_valid(uint_least32_t c32);
#endif

View File

@@ -1,99 +0,0 @@
/*
* Linux klist like system
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIST_H
#define LIST_H
#ifndef NULL
#include <stddef.h>
#endif
struct list_head {
struct list_head *prev;
struct list_head *next;
};
#define LIST_HEAD_INIT(el) { &(el), &(el) }
/* return the pointer of type 'type *' containing 'el' as field 'member' */
#define list_entry(el, type, member) container_of(el, type, member)
static inline void init_list_head(struct list_head *head)
{
head->prev = head;
head->next = head;
}
/* insert 'el' between 'prev' and 'next' */
static inline void __list_add(struct list_head *el,
struct list_head *prev, struct list_head *next)
{
prev->next = el;
el->prev = prev;
el->next = next;
next->prev = el;
}
/* add 'el' at the head of the list 'head' (= after element head) */
static inline void list_add(struct list_head *el, struct list_head *head)
{
__list_add(el, head, head->next);
}
/* add 'el' at the end of the list 'head' (= before element head) */
static inline void list_add_tail(struct list_head *el, struct list_head *head)
{
__list_add(el, head->prev, head);
}
static inline void list_del(struct list_head *el)
{
struct list_head *prev, *next;
prev = el->prev;
next = el->next;
prev->next = next;
next->prev = prev;
el->prev = NULL; /* fail safe */
el->next = NULL; /* fail safe */
}
static inline int list_empty(struct list_head *el)
{
return el->next == el;
}
#define list_for_each(el, head) \
for(el = (head)->next; el != (head); el = el->next)
#define list_for_each_safe(el, el1, head) \
for(el = (head)->next, el1 = el->next; el != (head); \
el = el1, el1 = el->next)
#define list_for_each_prev(el, head) \
for(el = (head)->prev; el != (head); el = el->prev)
#define list_for_each_prev_safe(el, el1, head) \
for(el = (head)->prev, el1 = el->prev; el != (head); \
el = el1, el1 = el->prev)
#endif /* LIST_H */

View File

@@ -1,552 +0,0 @@
/*
* QuickJS stand alone interpreter
*
* Copyright (c) 2017-2021 Fabrice Bellard
* Copyright (c) 2017-2021 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#if defined(__APPLE__)
#include <malloc/malloc.h>
#elif defined(__linux__)
#include <malloc.h>
#elif defined(__FreeBSD__)
#include <malloc_np.h>
#endif
#include "cutils.h"
#include "quickjs-libc.h"
#ifdef CONFIG_BIGNUM
static int bignum_ext;
#endif
static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
const char *filename, int eval_flags)
{
JSValue val;
int ret;
if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) {
/* for the modules, we compile then run to be able to set
import.meta */
val = JS_Eval(ctx, buf, buf_len, filename,
eval_flags | JS_EVAL_FLAG_COMPILE_ONLY);
if (!JS_IsException(val)) {
js_module_set_import_meta(ctx, val, TRUE, TRUE);
val = JS_EvalFunction(ctx, val);
}
val = js_std_await(ctx, val);
} else {
val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
}
if (JS_IsException(val)) {
js_std_dump_error(ctx);
ret = -1;
} else {
ret = 0;
}
JS_FreeValue(ctx, val);
return ret;
}
static int eval_file(JSContext *ctx, const char *filename, int module)
{
uint8_t *buf;
int ret, eval_flags;
size_t buf_len;
buf = js_load_file(ctx, &buf_len, filename);
if (!buf) {
perror(filename);
exit(1);
}
if (module < 0) {
module = (has_suffix(filename, ".mjs") ||
JS_DetectModule((const char *)buf, buf_len));
}
if (module)
eval_flags = JS_EVAL_TYPE_MODULE;
else
eval_flags = JS_EVAL_TYPE_GLOBAL;
ret = eval_buf(ctx, buf, buf_len, filename, eval_flags);
js_free(ctx, buf);
return ret;
}
/* also used to initialize the worker context */
static JSContext *JS_NewCustomContext(JSRuntime *rt)
{
JSContext *ctx;
ctx = JS_NewContext(rt);
if (!ctx)
return NULL;
#ifdef CONFIG_BIGNUM
if (bignum_ext) {
JS_AddIntrinsicBigFloat(ctx);
JS_AddIntrinsicBigDecimal(ctx);
JS_AddIntrinsicOperators(ctx);
JS_EnableBignumExt(ctx, TRUE);
}
#endif
/* system modules */
js_init_module_std(ctx, "std");
js_init_module_os(ctx, "os");
return ctx;
}
#if defined(__APPLE__)
#define MALLOC_OVERHEAD 0
#else
#define MALLOC_OVERHEAD 8
#endif
struct trace_malloc_data {
uint8_t *base;
};
static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr,
struct trace_malloc_data *dp)
{
return ptr - dp->base;
}
/* default memory allocation functions with memory limitation */
static size_t js_trace_malloc_usable_size(const void *ptr)
{
#if defined(__APPLE__)
return malloc_size(ptr);
#elif defined(_WIN32)
return _msize((void *)ptr);
#elif defined(EMSCRIPTEN)
return 0;
#elif defined(__linux__)
return malloc_usable_size((void *)ptr);
#else
/* change this to `return 0;` if compilation fails */
return malloc_usable_size((void *)ptr);
#endif
}
static void
#ifdef _WIN32
/* mingw printf is used */
__attribute__((format(gnu_printf, 2, 3)))
#else
__attribute__((format(printf, 2, 3)))
#endif
js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...)
{
va_list ap;
int c;
va_start(ap, fmt);
while ((c = *fmt++) != '\0') {
if (c == '%') {
/* only handle %p and %zd */
if (*fmt == 'p') {
uint8_t *ptr = va_arg(ap, void *);
if (ptr == NULL) {
printf("NULL");
} else {
printf("H%+06lld.%zd",
js_trace_malloc_ptr_offset(ptr, s->opaque),
js_trace_malloc_usable_size(ptr));
}
fmt++;
continue;
}
if (fmt[0] == 'z' && fmt[1] == 'd') {
size_t sz = va_arg(ap, size_t);
printf("%zd", sz);
fmt += 2;
continue;
}
}
putc(c, stdout);
}
va_end(ap);
}
static void js_trace_malloc_init(struct trace_malloc_data *s)
{
free(s->base = malloc(8));
}
static void *js_trace_malloc(JSMallocState *s, size_t size)
{
void *ptr;
/* Do not allocate zero bytes: behavior is platform dependent */
assert(size != 0);
if (unlikely(s->malloc_size + size > s->malloc_limit))
return NULL;
ptr = malloc(size);
js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr);
if (ptr) {
s->malloc_count++;
s->malloc_size += js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
}
return ptr;
}
static void js_trace_free(JSMallocState *s, void *ptr)
{
if (!ptr)
return;
js_trace_malloc_printf(s, "F %p\n", ptr);
s->malloc_count--;
s->malloc_size -= js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
free(ptr);
}
static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size)
{
size_t old_size;
if (!ptr) {
if (size == 0)
return NULL;
return js_trace_malloc(s, size);
}
old_size = js_trace_malloc_usable_size(ptr);
if (size == 0) {
js_trace_malloc_printf(s, "R %zd %p\n", size, ptr);
s->malloc_count--;
s->malloc_size -= old_size + MALLOC_OVERHEAD;
free(ptr);
return NULL;
}
if (s->malloc_size + size - old_size > s->malloc_limit)
return NULL;
js_trace_malloc_printf(s, "R %zd %p", size, ptr);
ptr = realloc(ptr, size);
js_trace_malloc_printf(s, " -> %p\n", ptr);
if (ptr) {
s->malloc_size += js_trace_malloc_usable_size(ptr) - old_size;
}
return ptr;
}
static const JSMallocFunctions trace_mf = {
js_trace_malloc,
js_trace_free,
js_trace_realloc,
js_trace_malloc_usable_size,
};
#define PROG_NAME "qjs"
void help(void)
{
printf("QuickJS lite\n"
"usage: " PROG_NAME " [options] [file [args]]\n"
"-h --help list options\n"
"-e --eval EXPR evaluate EXPR\n"
"-i --interactive go to interactive mode\n"
"-m --module load as ES6 module (default=autodetect)\n"
" --script load as ES6 script (default=autodetect)\n"
"-I --include file include an additional file\n"
" --std make 'std' and 'os' available to the loaded script\n"
#ifdef CONFIG_BIGNUM
" --bignum enable the bignum extensions (BigFloat, BigDecimal)\n"
" --qjscalc load the QJSCalc runtime (default if invoked as qjscalc)\n"
#endif
"-T --trace trace memory allocation\n"
"-d --dump dump the memory usage stats\n"
" --memory-limit n limit the memory usage to 'n' bytes\n"
" --stack-size n limit the stack size to 'n' bytes\n"
" --unhandled-rejection dump unhandled promise rejections\n"
"-q --quit just instantiate the interpreter and quit\n");
exit(1);
}
int main(int argc, char **argv)
{
JSRuntime *rt;
JSContext *ctx;
struct trace_malloc_data trace_data = { NULL };
int optind;
char *expr = NULL;
int interactive = 0;
int dump_memory = 0;
int trace_memory = 0;
int empty_run = 0;
int module = -1;
int load_std = 0;
int dump_unhandled_promise_rejection = 0;
size_t memory_limit = 0;
char *include_list[32];
int i, include_count = 0;
#ifdef CONFIG_BIGNUM
int load_jscalc;
#endif
size_t stack_size = 0;
#ifdef CONFIG_BIGNUM
/* load jscalc runtime if invoked as 'qjscalc' */
{
const char *p, *exename;
exename = argv[0];
p = strrchr(exename, '/');
if (p)
exename = p + 1;
load_jscalc = !strcmp(exename, "qjscalc");
}
#endif
/* cannot use getopt because we want to pass the command line to
the script */
optind = 1;
while (optind < argc && *argv[optind] == '-') {
char *arg = argv[optind] + 1;
const char *longopt = "";
/* a single - is not an option, it also stops argument scanning */
if (!*arg)
break;
optind++;
if (*arg == '-') {
longopt = arg + 1;
arg += strlen(arg);
/* -- stops argument scanning */
if (!*longopt)
break;
}
for (; *arg || *longopt; longopt = "") {
char opt = *arg;
if (opt)
arg++;
if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) {
help();
continue;
}
if (opt == 'e' || !strcmp(longopt, "eval")) {
if (*arg) {
expr = arg;
break;
}
if (optind < argc) {
expr = argv[optind++];
break;
}
fprintf(stderr, "qjs: missing expression for -e\n");
exit(2);
}
if (opt == 'I' || !strcmp(longopt, "include")) {
if (optind >= argc) {
fprintf(stderr, "expecting filename");
exit(1);
}
if (include_count >= countof(include_list)) {
fprintf(stderr, "too many included files");
exit(1);
}
include_list[include_count++] = argv[optind++];
continue;
}
if (opt == 'i' || !strcmp(longopt, "interactive")) {
interactive++;
continue;
}
if (opt == 'm' || !strcmp(longopt, "module")) {
module = 1;
continue;
}
if (!strcmp(longopt, "script")) {
module = 0;
continue;
}
if (opt == 'd' || !strcmp(longopt, "dump")) {
dump_memory++;
continue;
}
if (opt == 'T' || !strcmp(longopt, "trace")) {
trace_memory++;
continue;
}
if (!strcmp(longopt, "std")) {
load_std = 1;
continue;
}
if (!strcmp(longopt, "unhandled-rejection")) {
dump_unhandled_promise_rejection = 1;
continue;
}
#ifdef CONFIG_BIGNUM
if (!strcmp(longopt, "bignum")) {
bignum_ext = 1;
continue;
}
if (!strcmp(longopt, "qjscalc")) {
load_jscalc = 1;
continue;
}
#endif
if (opt == 'q' || !strcmp(longopt, "quit")) {
empty_run++;
continue;
}
if (!strcmp(longopt, "memory-limit")) {
if (optind >= argc) {
fprintf(stderr, "expecting memory limit");
exit(1);
}
memory_limit = (size_t)strtod(argv[optind++], NULL);
continue;
}
if (!strcmp(longopt, "stack-size")) {
if (optind >= argc) {
fprintf(stderr, "expecting stack size");
exit(1);
}
stack_size = (size_t)strtod(argv[optind++], NULL);
continue;
}
if (opt) {
fprintf(stderr, "qjs: unknown option '-%c'\n", opt);
} else {
fprintf(stderr, "qjs: unknown option '--%s'\n", longopt);
}
help();
}
}
#ifdef CONFIG_BIGNUM
if (load_jscalc)
bignum_ext = 1;
#endif
if (trace_memory) {
js_trace_malloc_init(&trace_data);
rt = JS_NewRuntime2(&trace_mf, &trace_data);
} else {
rt = JS_NewRuntime();
}
if (!rt) {
fprintf(stderr, "qjs: cannot allocate JS runtime\n");
exit(2);
}
if (memory_limit != 0)
JS_SetMemoryLimit(rt, memory_limit);
if (stack_size != 0)
JS_SetMaxStackSize(rt, stack_size);
js_std_set_worker_new_context_func(JS_NewCustomContext);
js_std_init_handlers(rt);
ctx = JS_NewCustomContext(rt);
if (!ctx) {
fprintf(stderr, "qjs: cannot allocate JS context\n");
exit(2);
}
/* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
if (dump_unhandled_promise_rejection) {
JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker,
NULL);
}
if (!empty_run) {
js_std_add_helpers(ctx, argc - optind, argv + optind);
/* make 'std' and 'os' visible to non module code */
if (load_std) {
const char *str = "import * as std from 'std';\n"
"import * as os from 'os';\n"
"globalThis.std = std;\n"
"globalThis.os = os;\n";
eval_buf(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE);
}
for(i = 0; i < include_count; i++) {
if (eval_file(ctx, include_list[i], module))
goto fail;
}
if (expr) {
if (eval_buf(ctx, expr, strlen(expr), "<cmdline>", 0))
goto fail;
} else
if (optind >= argc) {
/* interactive mode */
interactive = 1;
} else {
const char *filename;
filename = argv[optind];
if (eval_file(ctx, filename, module))
goto fail;
}
js_std_loop(ctx);
}
if (dump_memory) {
JSMemoryUsage stats;
JS_ComputeMemoryUsage(rt, &stats);
JS_DumpMemoryUsage(stdout, &stats, rt);
}
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
if (empty_run && dump_memory) {
clock_t t[5];
double best[5];
int i, j;
for (i = 0; i < 100; i++) {
t[0] = clock();
rt = JS_NewRuntime();
t[1] = clock();
ctx = JS_NewContext(rt);
t[2] = clock();
JS_FreeContext(ctx);
t[3] = clock();
JS_FreeRuntime(rt);
t[4] = clock();
for (j = 4; j > 0; j--) {
double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC;
if (i == 0 || best[j] > ms)
best[j] = ms;
}
}
printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n",
best[1] + best[2] + best[3] + best[4],
best[1], best[2], best[3], best[4]);
}
return 0;
fail:
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return 1;
}

View File

@@ -1,761 +0,0 @@
/*
* QuickJS command line compiler
*
* Copyright (c) 2018-2021 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#if !defined(_WIN32)
#include <sys/wait.h>
#endif
#include "cutils.h"
#include "quickjs-libc.h"
typedef struct {
char *name;
char *short_name;
int flags;
} namelist_entry_t;
typedef struct namelist_t {
namelist_entry_t *array;
int count;
int size;
} namelist_t;
typedef struct {
const char *option_name;
const char *init_name;
} FeatureEntry;
static namelist_t cname_list;
static namelist_t cmodule_list;
static namelist_t init_module_list;
static uint64_t feature_bitmap;
static FILE *outfile;
static BOOL byte_swap;
static BOOL dynamic_export;
static const char *c_ident_prefix = "qjsc_";
#define FE_ALL (-1)
static const FeatureEntry feature_list[] = {
{ "date", "Date" },
{ "eval", "Eval" },
{ "string-normalize", "StringNormalize" },
{ "regexp", "RegExp" },
{ "json", "JSON" },
{ "proxy", "Proxy" },
{ "map", "MapSet" },
{ "typedarray", "TypedArrays" },
{ "promise", "Promise" },
#define FE_MODULE_LOADER 9
{ "module-loader", NULL },
{ "bigint", "BigInt" },
};
void namelist_add(namelist_t *lp, const char *name, const char *short_name,
int flags)
{
namelist_entry_t *e;
if (lp->count == lp->size) {
size_t newsize = lp->size + (lp->size >> 1) + 4;
namelist_entry_t *a =
realloc(lp->array, sizeof(lp->array[0]) * newsize);
/* XXX: check for realloc failure */
lp->array = a;
lp->size = newsize;
}
e = &lp->array[lp->count++];
e->name = strdup(name);
if (short_name)
e->short_name = strdup(short_name);
else
e->short_name = NULL;
e->flags = flags;
}
void namelist_free(namelist_t *lp)
{
while (lp->count > 0) {
namelist_entry_t *e = &lp->array[--lp->count];
free(e->name);
free(e->short_name);
}
free(lp->array);
lp->array = NULL;
lp->size = 0;
}
namelist_entry_t *namelist_find(namelist_t *lp, const char *name)
{
int i;
for(i = 0; i < lp->count; i++) {
namelist_entry_t *e = &lp->array[i];
if (!strcmp(e->name, name))
return e;
}
return NULL;
}
static void get_c_name(char *buf, size_t buf_size, const char *file)
{
const char *p, *r;
size_t len, i;
int c;
char *q;
p = strrchr(file, '/');
if (!p)
p = file;
else
p++;
r = strrchr(p, '.');
if (!r)
len = strlen(p);
else
len = r - p;
pstrcpy(buf, buf_size, c_ident_prefix);
q = buf + strlen(buf);
for(i = 0; i < len; i++) {
c = p[i];
if (!((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z'))) {
c = '_';
}
if ((q - buf) < buf_size - 1)
*q++ = c;
}
*q = '\0';
}
static void dump_hex(FILE *f, const uint8_t *buf, size_t len)
{
size_t i, col;
col = 0;
for(i = 0; i < len; i++) {
fprintf(f, " 0x%02x,", buf[i]);
if (++col == 8) {
fprintf(f, "\n");
col = 0;
}
}
if (col != 0)
fprintf(f, "\n");
}
static void output_object_code(JSContext *ctx,
FILE *fo, JSValueConst obj, const char *c_name,
BOOL load_only)
{
uint8_t *out_buf;
size_t out_buf_len;
int flags;
flags = JS_WRITE_OBJ_BYTECODE;
if (byte_swap)
flags |= JS_WRITE_OBJ_BSWAP;
out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags);
if (!out_buf) {
js_std_dump_error(ctx);
exit(1);
}
namelist_add(&cname_list, c_name, NULL, load_only);
fprintf(fo, "const uint32_t %s_size = %u;\n\n",
c_name, (unsigned int)out_buf_len);
fprintf(fo, "const uint8_t %s[%u] = {\n",
c_name, (unsigned int)out_buf_len);
dump_hex(fo, out_buf, out_buf_len);
fprintf(fo, "};\n\n");
js_free(ctx, out_buf);
}
static int js_module_dummy_init(JSContext *ctx, JSModuleDef *m)
{
/* should never be called when compiling JS code */
abort();
}
static void find_unique_cname(char *cname, size_t cname_size)
{
char cname1[1024];
int suffix_num;
size_t len, max_len;
assert(cname_size >= 32);
/* find a C name not matching an existing module C name by
adding a numeric suffix */
len = strlen(cname);
max_len = cname_size - 16;
if (len > max_len)
cname[max_len] = '\0';
suffix_num = 1;
for(;;) {
snprintf(cname1, sizeof(cname1), "%s_%d", cname, suffix_num);
if (!namelist_find(&cname_list, cname1))
break;
suffix_num++;
}
pstrcpy(cname, cname_size, cname1);
}
JSModuleDef *jsc_module_loader(JSContext *ctx,
const char *module_name, void *opaque)
{
JSModuleDef *m;
namelist_entry_t *e;
/* check if it is a declared C or system module */
e = namelist_find(&cmodule_list, module_name);
if (e) {
/* add in the static init module list */
namelist_add(&init_module_list, e->name, e->short_name, 0);
/* create a dummy module */
m = JS_NewCModule(ctx, module_name, js_module_dummy_init);
} else if (has_suffix(module_name, ".so")) {
fprintf(stderr, "Warning: binary module '%s' will be dynamically loaded\n", module_name);
/* create a dummy module */
m = JS_NewCModule(ctx, module_name, js_module_dummy_init);
/* the resulting executable will export its symbols for the
dynamic library */
dynamic_export = TRUE;
} else {
size_t buf_len;
uint8_t *buf;
JSValue func_val;
char cname[1024];
buf = js_load_file(ctx, &buf_len, module_name);
if (!buf) {
JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
module_name);
return NULL;
}
/* compile the module */
func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
js_free(ctx, buf);
if (JS_IsException(func_val))
return NULL;
get_c_name(cname, sizeof(cname), module_name);
if (namelist_find(&cname_list, cname)) {
find_unique_cname(cname, sizeof(cname));
}
output_object_code(ctx, outfile, func_val, cname, TRUE);
/* the module is already referenced, so we must free it */
m = JS_VALUE_GET_PTR(func_val);
JS_FreeValue(ctx, func_val);
}
return m;
}
static void compile_file(JSContext *ctx, FILE *fo,
const char *filename,
const char *c_name1,
int module)
{
uint8_t *buf;
char c_name[1024];
int eval_flags;
JSValue obj;
size_t buf_len;
buf = js_load_file(ctx, &buf_len, filename);
if (!buf) {
fprintf(stderr, "Could not load '%s'\n", filename);
exit(1);
}
eval_flags = JS_EVAL_FLAG_COMPILE_ONLY;
if (module < 0) {
module = (has_suffix(filename, ".mjs") ||
JS_DetectModule((const char *)buf, buf_len));
}
if (module)
eval_flags |= JS_EVAL_TYPE_MODULE;
else
eval_flags |= JS_EVAL_TYPE_GLOBAL;
obj = JS_Eval(ctx, (const char *)buf, buf_len, filename, eval_flags);
if (JS_IsException(obj)) {
js_std_dump_error(ctx);
exit(1);
}
js_free(ctx, buf);
if (c_name1) {
pstrcpy(c_name, sizeof(c_name), c_name1);
} else {
get_c_name(c_name, sizeof(c_name), filename);
}
output_object_code(ctx, fo, obj, c_name, FALSE);
JS_FreeValue(ctx, obj);
}
static const char main_c_template1[] =
"int main(int argc, char **argv)\n"
"{\n"
" JSRuntime *rt;\n"
" JSContext *ctx;\n"
" rt = JS_NewRuntime();\n"
" js_std_set_worker_new_context_func(JS_NewCustomContext);\n"
" js_std_init_handlers(rt);\n"
;
static const char main_c_template2[] =
" js_std_loop(ctx);\n"
" js_std_free_handlers(rt);\n"
" JS_FreeContext(ctx);\n"
" JS_FreeRuntime(rt);\n"
" return 0;\n"
"}\n";
#define PROG_NAME "qjsc"
void help(void)
{
printf("QuickJS Compiler version " CONFIG_VERSION "\n"
"usage: " PROG_NAME " [options] [files]\n"
"\n"
"options are:\n"
"-c only output bytecode to a C file\n"
"-e output main() and bytecode to a C file (default = executable output)\n"
"-o output set the output filename\n"
"-N cname set the C name of the generated data\n"
"-m compile as Javascript module (default=autodetect)\n"
"-D module_name compile a dynamically loaded module or worker\n"
"-M module_name[,cname] add initialization code for an external C module\n"
"-x byte swapped output\n"
"-p prefix set the prefix of the generated C names\n"
"-S n set the maximum stack size to 'n' bytes (default=%d)\n",
JS_DEFAULT_STACK_SIZE);
#ifdef CONFIG_LTO
{
int i;
printf("-flto use link time optimization\n");
printf("-fbignum enable bignum extensions\n");
printf("-fno-[");
for(i = 0; i < countof(feature_list); i++) {
if (i != 0)
printf("|");
printf("%s", feature_list[i].option_name);
}
printf("]\n"
" disable selected language features (smaller code size)\n");
}
#endif
exit(1);
}
#if defined(CONFIG_CC) && !defined(_WIN32)
int exec_cmd(char **argv)
{
int pid, status, ret;
pid = fork();
if (pid == 0) {
execvp(argv[0], argv);
exit(1);
}
for(;;) {
ret = waitpid(pid, &status, 0);
if (ret == pid && WIFEXITED(status))
break;
}
return WEXITSTATUS(status);
}
static int output_executable(const char *out_filename, const char *cfilename,
BOOL use_lto, BOOL verbose, const char *exename)
{
const char *argv[64];
const char **arg, *bn_suffix, *lto_suffix;
char libjsname[1024];
char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p;
int ret;
/* get the directory of the executable */
pstrcpy(exe_dir, sizeof(exe_dir), exename);
p = strrchr(exe_dir, '/');
if (p) {
*p = '\0';
} else {
pstrcpy(exe_dir, sizeof(exe_dir), ".");
}
/* if 'quickjs.h' is present at the same path as the executable, we
use it as include and lib directory */
snprintf(buf, sizeof(buf), "%s/quickjs.h", exe_dir);
if (access(buf, R_OK) == 0) {
pstrcpy(inc_dir, sizeof(inc_dir), exe_dir);
pstrcpy(lib_dir, sizeof(lib_dir), exe_dir);
} else {
snprintf(inc_dir, sizeof(inc_dir), "%s/include/quickjs", CONFIG_PREFIX);
snprintf(lib_dir, sizeof(lib_dir), "%s/lib/quickjs", CONFIG_PREFIX);
}
lto_suffix = "";
bn_suffix = "";
arg = argv;
*arg++ = CONFIG_CC;
*arg++ = "-O2";
#ifdef CONFIG_LTO
if (use_lto) {
*arg++ = "-flto";
lto_suffix = ".lto";
}
#endif
/* XXX: use the executable path to find the includes files and
libraries */
*arg++ = "-D";
*arg++ = "_GNU_SOURCE";
*arg++ = "-I";
*arg++ = inc_dir;
*arg++ = "-o";
*arg++ = out_filename;
if (dynamic_export)
*arg++ = "-rdynamic";
*arg++ = cfilename;
snprintf(libjsname, sizeof(libjsname), "%s/libquickjs%s%s.a",
lib_dir, bn_suffix, lto_suffix);
*arg++ = libjsname;
*arg++ = "-lm";
*arg++ = "-ldl";
*arg++ = "-lpthread";
*arg = NULL;
if (verbose) {
for(arg = argv; *arg != NULL; arg++)
printf("%s ", *arg);
printf("\n");
}
ret = exec_cmd((char **)argv);
unlink(cfilename);
return ret;
}
#else
static int output_executable(const char *out_filename, const char *cfilename,
BOOL use_lto, BOOL verbose, const char *exename)
{
fprintf(stderr, "Executable output is not supported for this target\n");
exit(1);
return 0;
}
#endif
typedef enum {
OUTPUT_C,
OUTPUT_C_MAIN,
OUTPUT_EXECUTABLE,
} OutputTypeEnum;
int main(int argc, char **argv)
{
int c, i, verbose;
const char *out_filename, *cname;
char cfilename[1024];
FILE *fo;
JSRuntime *rt;
JSContext *ctx;
BOOL use_lto;
int module;
OutputTypeEnum output_type;
size_t stack_size;
#ifdef CONFIG_BIGNUM
BOOL bignum_ext = FALSE;
#endif
namelist_t dynamic_module_list;
out_filename = NULL;
output_type = OUTPUT_EXECUTABLE;
cname = NULL;
feature_bitmap = FE_ALL;
module = -1;
byte_swap = FALSE;
verbose = 0;
use_lto = FALSE;
stack_size = 0;
memset(&dynamic_module_list, 0, sizeof(dynamic_module_list));
/* add system modules */
namelist_add(&cmodule_list, "std", "std", 0);
namelist_add(&cmodule_list, "os", "os", 0);
for(;;) {
c = getopt(argc, argv, "ho:cN:f:mxevM:p:S:D:");
if (c == -1)
break;
switch(c) {
case 'h':
help();
case 'o':
out_filename = optarg;
break;
case 'c':
output_type = OUTPUT_C;
break;
case 'e':
output_type = OUTPUT_C_MAIN;
break;
case 'N':
cname = optarg;
break;
case 'f':
{
const char *p;
p = optarg;
if (!strcmp(optarg, "lto")) {
use_lto = TRUE;
} else if (strstart(p, "no-", &p)) {
use_lto = TRUE;
for(i = 0; i < countof(feature_list); i++) {
if (!strcmp(p, feature_list[i].option_name)) {
feature_bitmap &= ~((uint64_t)1 << i);
break;
}
}
if (i == countof(feature_list))
goto bad_feature;
} else
#ifdef CONFIG_BIGNUM
if (!strcmp(optarg, "bignum")) {
bignum_ext = TRUE;
} else
#endif
{
bad_feature:
fprintf(stderr, "unsupported feature: %s\n", optarg);
exit(1);
}
}
break;
case 'm':
module = 1;
break;
case 'M':
{
char *p;
char path[1024];
char cname[1024];
pstrcpy(path, sizeof(path), optarg);
p = strchr(path, ',');
if (p) {
*p = '\0';
pstrcpy(cname, sizeof(cname), p + 1);
} else {
get_c_name(cname, sizeof(cname), path);
}
namelist_add(&cmodule_list, path, cname, 0);
}
break;
case 'D':
namelist_add(&dynamic_module_list, optarg, NULL, 0);
break;
case 'x':
byte_swap = TRUE;
break;
case 'v':
verbose++;
break;
case 'p':
c_ident_prefix = optarg;
break;
case 'S':
stack_size = (size_t)strtod(optarg, NULL);
break;
default:
break;
}
}
if (optind >= argc)
help();
if (!out_filename) {
if (output_type == OUTPUT_EXECUTABLE) {
out_filename = "a.out";
} else {
out_filename = "out.c";
}
}
if (output_type == OUTPUT_EXECUTABLE) {
#if defined(_WIN32) || defined(__ANDROID__)
/* XXX: find a /tmp directory ? */
snprintf(cfilename, sizeof(cfilename), "out%d.c", getpid());
#else
snprintf(cfilename, sizeof(cfilename), "/tmp/out%d.c", getpid());
#endif
} else {
pstrcpy(cfilename, sizeof(cfilename), out_filename);
}
fo = fopen(cfilename, "w");
if (!fo) {
perror(cfilename);
exit(1);
}
outfile = fo;
rt = JS_NewRuntime();
ctx = JS_NewContext(rt);
#ifdef CONFIG_BIGNUM
if (bignum_ext) {
JS_AddIntrinsicBigFloat(ctx);
JS_AddIntrinsicBigDecimal(ctx);
JS_AddIntrinsicOperators(ctx);
JS_EnableBignumExt(ctx, TRUE);
}
#endif
/* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL);
fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n"
"\n"
);
if (output_type != OUTPUT_C) {
fprintf(fo, "#include \"quickjs-libc.h\"\n"
"\n"
);
} else {
fprintf(fo, "#include <inttypes.h>\n"
"\n"
);
}
for(i = optind; i < argc; i++) {
const char *filename = argv[i];
compile_file(ctx, fo, filename, cname, module);
cname = NULL;
}
for(i = 0; i < dynamic_module_list.count; i++) {
if (!jsc_module_loader(ctx, dynamic_module_list.array[i].name, NULL)) {
fprintf(stderr, "Could not load dynamic module '%s'\n",
dynamic_module_list.array[i].name);
exit(1);
}
}
if (output_type != OUTPUT_C) {
fprintf(fo,
"static JSContext *JS_NewCustomContext(JSRuntime *rt)\n"
"{\n"
" JSContext *ctx = JS_NewContextRaw(rt);\n"
" if (!ctx)\n"
" return NULL;\n");
/* add the basic objects */
fprintf(fo, " JS_AddIntrinsicBaseObjects(ctx);\n");
for(i = 0; i < countof(feature_list); i++) {
if ((feature_bitmap & ((uint64_t)1 << i)) &&
feature_list[i].init_name) {
fprintf(fo, " JS_AddIntrinsic%s(ctx);\n",
feature_list[i].init_name);
}
}
#ifdef CONFIG_BIGNUM
if (bignum_ext) {
fprintf(fo,
" JS_AddIntrinsicBigFloat(ctx);\n"
" JS_AddIntrinsicBigDecimal(ctx);\n"
" JS_AddIntrinsicOperators(ctx);\n"
" JS_EnableBignumExt(ctx, 1);\n");
}
#endif
/* add the precompiled modules (XXX: could modify the module
loader instead) */
for(i = 0; i < init_module_list.count; i++) {
namelist_entry_t *e = &init_module_list.array[i];
/* initialize the static C modules */
fprintf(fo,
" {\n"
" extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n"
" js_init_module_%s(ctx, \"%s\");\n"
" }\n",
e->short_name, e->short_name, e->name);
}
for(i = 0; i < cname_list.count; i++) {
namelist_entry_t *e = &cname_list.array[i];
if (e->flags) {
fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 1);\n",
e->name, e->name);
}
}
fprintf(fo,
" return ctx;\n"
"}\n\n");
fputs(main_c_template1, fo);
if (stack_size != 0) {
fprintf(fo, " JS_SetMaxStackSize(rt, %u);\n",
(unsigned int)stack_size);
}
/* add the module loader if necessary */
if (feature_bitmap & (1 << FE_MODULE_LOADER)) {
fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n");
}
fprintf(fo,
" ctx = JS_NewCustomContext(rt);\n"
" js_std_add_helpers(ctx, argc, argv);\n");
for(i = 0; i < cname_list.count; i++) {
namelist_entry_t *e = &cname_list.array[i];
if (!e->flags) {
fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 0);\n",
e->name, e->name);
}
}
fputs(main_c_template2, fo);
}
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
fclose(fo);
if (output_type == OUTPUT_EXECUTABLE) {
return output_executable(out_filename, cfilename, use_lto, verbose,
argv[0]);
}
namelist_free(&cname_list);
namelist_free(&cmodule_list);
namelist_free(&init_module_list);
return 0;
}

View File

@@ -1,87 +0,0 @@
#include <sys/types.h>
#include <stdlib.h>
static void
exch(char* base, size_t size, size_t a, size_t b) {
char* x = base + a * size;
char* y = base + b * size;
while(size) {
char z = *x;
*x = *y;
*y = z;
--size;
++x;
++y;
}
}
#define RAND
/* Quicksort with 3-way partitioning, ala Sedgewick */
/* Blame him for the scary variable names */
/* http://www.cs.princeton.edu/~rs/talks/QuicksortIsOptimal.pdf */
static void
quicksort(char* base, size_t size, ssize_t l, ssize_t r, int (*compar)(const void*, const void*, void*), void* ptr) {
ssize_t i = l - 1, j = r, p = l - 1, q = r, k;
char* v = base + r * size;
if(r <= l)
return;
#ifdef RAND
/*
We chose the rightmost element in the array to be sorted as pivot,
which is OK if the data is random, but which is horrible if the
data is already sorted. Try to improve by exchanging it with a
random other pivot.
*/
exch(base, size, l + (rand() % (r - l)), r);
#elif defined MID
/*
We chose the rightmost element in the array to be sorted as pivot,
which is OK if the data is random, but which is horrible if the
data is already sorted. Try to improve by chosing the middle
element instead.
*/
exch(base, size, l + (r - l) / 2, r);
#endif
for(;;) {
while(++i != r && compar(base + i * size, v, ptr) < 0)
;
while(compar(v, base + (--j) * size, ptr) < 0)
if(j == l)
break;
if(i >= j)
break;
exch(base, size, i, j);
if(compar(base + i * size, v, ptr) == 0)
exch(base, size, ++p, i);
if(compar(v, base + j * size, ptr) == 0)
exch(base, size, j, --q);
}
exch(base, size, i, r);
j = i - 1;
++i;
for(k = l; k < p; k++, j--)
exch(base, size, k, j);
for(k = r - 1; k > q; k--, i++)
exch(base, size, i, k);
quicksort(base, size, l, j, compar, ptr);
quicksort(base, size, i, r, compar, ptr);
}
void
quicksort_r(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*, void*), void* ptr) {
/* check for integer overflows */
if(nmemb >= (((size_t)-1) >> 1) || size >= (((size_t)-1) >> 1))
return;
#if 0
if (sizeof(size_t) < sizeof(unsigned long long)) {
if ((unsigned long long)size * nmemb > (size_t)-1) return;
} else {
if (size*nmemb/nmemb != size) return;
}
#endif
if(nmemb > 1)
quicksort(base, size, 0, nmemb - 1, compar, ptr);
}

Some files were not shown because too many files have changed in this diff Show More