feat(jshooks): add cmake custom command to compile test hooks

- replace shell script with more peformant python script
- master header includes build/jshooks/generated/test.hook.$hash.inc files
  - looks for // @hook comment in hooks and places next to include
  - less annoying when running git diff without explicit ignore
  - first looks in usual location
- cmake locates or builds qjsc
  - searches for qjsc in previous place or honours QJSC_BINARY file
  - if not found builds from https://github.com/RichardAH/quickjslite
This commit is contained in:
Nicholas Dudfield
2025-07-31 16:35:26 +07:00
parent a6c4e39235
commit b0bde9f387
5 changed files with 681 additions and 18339 deletions

View File

@@ -330,6 +330,11 @@ if (tests)
src/ripple/beast/unit_test/detail/const_container.hpp
DESTINATION include/ripple/beast/unit_test/detail)
endif () #tests
#[===================================================================[
JS Hooks integration
#]===================================================================]
include(Builds/CMake/jshooks/JSHooks.cmake)
#[===================================================================[
rippled executable
#]===================================================================]
@@ -1067,6 +1072,11 @@ if (tests)
src/test/unit_test/multi_runner.cpp)
endif () #tests
# Add JS hooks support to rippled when tests are enabled
if (tests)
target_add_jshooks(rippled)
endif () #tests
target_link_libraries (rippled
Ripple::boost
Ripple::opts

View File

@@ -0,0 +1,102 @@
# JSHooks.cmake Generate JS hook headers for tests
# Find Python3
find_package(Python3 REQUIRED COMPONENTS Interpreter)
# Set paths
set(JSHOOK_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/test/app)
set(JSHOOK_BUILD_DIR ${CMAKE_BINARY_DIR}/jshooks/generated)
set(JSHOOK_CACHE_DIR ${CMAKE_BINARY_DIR}/jshooks/cache)
set(JSHOOK_SCRIPT_DIR ${CMAKE_CURRENT_LIST_DIR})
# Create directories
file(MAKE_DIRECTORY ${JSHOOK_BUILD_DIR})
file(MAKE_DIRECTORY ${JSHOOK_CACHE_DIR})
# Download and build qjsc if not provided
if(NOT DEFINED QJSC_BINARY OR QJSC_BINARY STREQUAL "")
# Check if qjsc exists locally first
set(LOCAL_QJSC_PATH ${JSHOOK_SOURCE_DIR}/qjsc)
if(EXISTS ${LOCAL_QJSC_PATH})
set(QJSC_BINARY ${LOCAL_QJSC_PATH})
else()
# Use FetchContent to download and build quickjslite
include(FetchContent)
FetchContent_Declare(
quickjslite
GIT_REPOSITORY https://github.com/RichardAH/quickjslite.git
GIT_TAG 863bf70645dcf21e960af2f1540f40029d09498e
)
FetchContent_MakeAvailable(quickjslite)
# Build qjsc using make
set(QUICKJS_SOURCE_DIR ${quickjslite_SOURCE_DIR})
set(QUICKJS_BINARY_DIR ${quickjslite_BINARY_DIR})
set(QJSC_BINARY ${QUICKJS_SOURCE_DIR}/qjsc)
# Custom target to build qjsc
include(ProcessorCount)
ProcessorCount(N)
if(NOT N EQUAL 0)
set(MAKE_JOBS -j${N})
else()
set(MAKE_JOBS "")
endif()
add_custom_command(
OUTPUT ${QJSC_BINARY}
COMMAND ${CMAKE_COMMAND} -E chdir ${QUICKJS_SOURCE_DIR} make ${MAKE_JOBS} qjsc
DEPENDS ${QUICKJS_SOURCE_DIR}/Makefile
COMMENT "Building qjsc from quickjslite (using ${N} jobs)..."
VERBATIM
)
# Create a target for qjsc dependency
add_custom_target(build_qjsc DEPENDS ${QJSC_BINARY})
endif()
endif()
# Set up dependencies for qjsc
if(TARGET build_qjsc)
set(QJSC_DEPENDENCY build_qjsc)
else()
set(QJSC_DEPENDENCY "")
endif()
# Custom command to generate headers
add_custom_command(
OUTPUT ${JSHOOK_SOURCE_DIR}/SetJSHook_wasm.h ${JSHOOK_BUILD_DIR}/.timestamp
COMMAND
${CMAKE_COMMAND} -E env QJSC_BINARY=${QJSC_BINARY} ${Python3_EXECUTABLE}
${JSHOOK_SCRIPT_DIR}/build_test_jshooks.py --source
${JSHOOK_SOURCE_DIR}/SetJSHook_test.cpp --master
${JSHOOK_SOURCE_DIR}/SetJSHook_wasm.h --output-dir ${JSHOOK_BUILD_DIR}
--cache-dir ${JSHOOK_CACHE_DIR} --log-level info
COMMAND ${CMAKE_COMMAND} -E touch ${JSHOOK_BUILD_DIR}/.timestamp
DEPENDS ${JSHOOK_SOURCE_DIR}/SetJSHook_test.cpp
${JSHOOK_SCRIPT_DIR}/build_test_jshooks.py
${QJSC_BINARY}
WORKING_DIRECTORY ${JSHOOK_SOURCE_DIR}
COMMENT "Generating JS hook headers..."
VERBATIM)
# Custom target
add_custom_target(generate_js_hooks ALL
DEPENDS ${JSHOOK_SOURCE_DIR}/SetJSHook_wasm.h)
# Add qjsc dependency if needed
if(QJSC_DEPENDENCY)
add_dependencies(generate_js_hooks ${QJSC_DEPENDENCY})
endif()
# Function to add JS hook support to a target
function(target_add_jshooks TARGET)
target_include_directories(${TARGET} PRIVATE ${CMAKE_BINARY_DIR}/jshooks)
add_dependencies(${TARGET} generate_js_hooks)
endfunction()
# Export variables for use by consumers
set(JSHOOK_GENERATED_DIR ${JSHOOK_BUILD_DIR})
set(JSHOOK_MASTER_HEADER ${JSHOOK_SOURCE_DIR}/SetJSHook_wasm.h)

View File

@@ -0,0 +1,380 @@
#!/usr/bin/env python3
import argparse
import hashlib
import logging
import os
import re
import subprocess
import sys
from datetime import datetime
from pathlib import Path
def get_qjsc_hash(qjsc_path):
"""Generate a hash of the qjsc binary to include in the cache key."""
try:
with open(qjsc_path, "rb") as f:
return hashlib.sha256(f.read()).hexdigest()[
:16
] # Use first 16 chars of hash for brevity
except Exception as e:
logging.warning(f"Could not hash qjsc binary: {e}")
return "unknown"
def find_qjsc_binary():
"""Find qjsc binary in expected locations."""
# Check environment variable first
if "QJSC_BINARY" in os.environ:
qjsc_path = os.environ["QJSC_BINARY"]
if os.path.exists(qjsc_path) and os.access(qjsc_path, os.X_OK):
return qjsc_path
else:
logging.error(f"QJSC_BINARY points to invalid path: {qjsc_path}")
sys.exit(1)
# Check current directory
qjsc_path = "./qjsc"
if os.path.exists(qjsc_path) and os.access(qjsc_path, os.X_OK):
return qjsc_path
# Not found
logging.error(
"qjsc not found in current directory.\n"
"This requires the custom quickjslite build from:\n"
"https://github.com/RichardAH/quickjslite\n"
"Build it and place in src/test/app/ or set QJSC_BINARY environment variable"
)
sys.exit(1)
def convert_js_to_carray(js_file, js_content, qjsc_path):
"""
Convert a JavaScript file to a C array using qjsc.
Extracts just the hex bytes from the qjsc output.
"""
try:
# Run qjsc to compile the JavaScript file to C code
result = subprocess.run(
[qjsc_path, "-c", "-o", "/dev/stdout", js_file],
capture_output=True,
check=True,
)
# Check if we have any output
if not result.stdout:
logging.error(f"Error: qjsc produced no output for {js_file}")
sys.exit(1)
# Convert to text and extract just the hex values
output_text = result.stdout.decode("utf-8", errors="replace")
# Extract hexadecimal values from the array definition
# Looking for patterns like 0x43, 0x0c, etc.
hex_values = re.findall(r"0x[0-9A-Fa-f]{2}", output_text)
# Format them as 0xXXU for the C array
c_array = ", ".join([f"{hex_val}U" for hex_val in hex_values])
return c_array
except subprocess.CalledProcessError as e:
logging.error(f"Error executing qjsc: {e}, content: {js_content}")
if e.stderr:
logging.error(f"stderr: {e.stderr.decode('utf-8', errors='replace')}")
sys.exit(1)
def extract_hook_description(js_content, start_line=None):
"""Extract description from // @hook comment at start of JS content."""
lines = js_content.split("\n")
for line in lines:
line = line.strip()
if line.startswith("// @hook "):
return line[9:] # Remove '// @hook ' prefix
elif line and not line.startswith("//"):
break # Stop at first non-comment line
# If no @hook comment found, return line number info
if start_line is not None:
return f"MISSING @hook (line {start_line})"
return None
def generate_content_hash(content):
"""Generate 8-character hash for content."""
return hashlib.sha256(content.encode("utf-8")).hexdigest()[:8]
def main():
parser = argparse.ArgumentParser(
description="Generate JS hook headers with individual .inc files"
)
parser.add_argument(
"--source",
default="SetJSHook_test.cpp",
help="Source file containing JS hooks",
)
parser.add_argument(
"--master",
default="SetJSHook_wasm.h",
help="Master header file to generate",
)
parser.add_argument(
"--output-dir",
required=True,
help="Directory for generated .inc files",
)
parser.add_argument(
"--cache-dir",
help="Directory for compilation cache (optional)",
)
parser.add_argument(
"--xahaud-root",
help="Xahaud root directory (if not specified, will try to auto-detect)",
)
parser.add_argument(
"--log-level",
default="error",
choices=["debug", "info", "warning", "error", "critical"],
help="Set logging level (default: error)",
)
args = parser.parse_args()
# Configure logging
numeric_level = getattr(logging, args.log_level.upper(), None)
logging.basicConfig(
level=numeric_level, format="[jshooks] %(levelname)s: %(message)s"
)
# Set working directory if xahaud_root is provided
if args.xahaud_root:
working_dir = os.path.join(args.xahaud_root, "src/test/app")
if os.path.exists(working_dir):
os.chdir(working_dir)
logging.info(f"Changed working directory to: {working_dir}")
else:
logging.error(f"Working directory does not exist: {working_dir}")
sys.exit(1)
# Find qjsc binary
qjsc_path = find_qjsc_binary()
logging.info(f"Using qjsc: {qjsc_path}")
# Create output directories
Path(args.output_dir).mkdir(parents=True, exist_ok=True)
if args.cache_dir:
Path(args.cache_dir).mkdir(parents=True, exist_ok=True)
# Get hash of qjsc binary for cache key
qjsc_hash = get_qjsc_hash(qjsc_path)
# Make paths relative for logging
relative_source = f"src/test/app/{os.path.basename(args.source)}"
relative_master = f"src/test/app/{os.path.basename(args.master)}"
logging.info(f"Processing {relative_source} to generate {relative_master}...")
# Check if input file exists
if not os.path.exists(args.source):
logging.error(f"Error: Input file '{args.source}' not found.")
sys.exit(1)
# Read input file content
with open(args.source, "r", encoding="utf-8") as f:
content = f.read()
content_with_form_feeds = content.replace("\n", "\f")
# Get hooks using regex that matches the original bash script's grep command
pattern = r'R"\[test\.hook\](.*?)\[test\.hook\]"'
hook_matches = list(re.finditer(pattern, content_with_form_feeds, re.DOTALL))
if not hook_matches:
logging.warning("Warning: No test hooks found in the input file.")
# Create empty master header
with open(args.master, "w") as f:
f.write(
f"""// Generated by build_test_jshooks.py - DO COMMIT THIS FILE
// Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
//
// This file provides a registry of all JS test hooks.
// Individual compiled hooks are in build/jshooks/generated/
#ifndef SETHOOK_JSWASM_INCLUDED
#define SETHOOK_JSWASM_INCLUDED
#include <map>
#include <stdint.h>
#include <string>
#include <vector>
namespace ripple {{
namespace test {{
// Map of all JS test hooks - populated at compile time
std::map<std::string, std::vector<uint8_t>> jswasm = {{
// No hooks found
}};
}} // namespace test
}} // namespace ripple
#endif
"""
)
sys.exit(0)
# Process the matches and calculate line numbers
processed_matches = []
hook_line_ranges = []
for match in hook_matches:
# Calculate start and end line numbers
start_line = content_with_form_feeds[: match.start()].count("\f") + 1
end_line = content_with_form_feeds[: match.end()].count("\f") + 1
hook_line_ranges.append((start_line, end_line))
# Extract the content and process it
raw_content = match.group(1)
# Remove the opening tag
processed = re.sub(r"^\(", "", raw_content)
# Remove the closing tag and any trailing whitespace/form feeds
processed = re.sub(r"\)[\f \t]*$", "/*end*/", processed)
processed_matches.append(processed)
# Generate individual .inc files and collect include statements
include_statements = []
counter = 0
for hook_content in processed_matches:
logging.debug(f"Processing hook {counter}...")
# Clean content and convert back to newlines
clean_content = (
hook_content[:-7] if hook_content.endswith("/*end*/") else hook_content
)
clean_content = clean_content.replace("\f", "\n")
# Check if this is a WebAssembly module
wat_count = len(re.findall(r"\(module", hook_content))
if wat_count > 0:
logging.error(f"Error: WebAssembly text format detected in hook {counter}")
sys.exit(1)
# Generate hash for this hook
content_hash = generate_content_hash(clean_content)
inc_filename = f"test.hook.{content_hash}.inc"
inc_path = os.path.join(args.output_dir, inc_filename)
# Extract description
start_line, end_line = hook_line_ranges[counter]
description = extract_hook_description(clean_content, start_line)
comment = f" // {description}" if description else ""
# Add to include statements
include_statements.append(f' #include "generated/{inc_filename}"{comment}')
# Check cache
cache_file = None
if args.cache_dir:
cache_content_hash = hashlib.sha256(
f"{qjsc_hash}:{clean_content}".encode("utf-8")
).hexdigest()
cache_file = os.path.join(
args.cache_dir, f"hook-{cache_content_hash}.c_array"
)
# Get or generate C array
if args.cache_dir and cache_file and os.path.exists(cache_file):
logging.debug(f"Using cached version for hook {counter}")
with open(cache_file, "r") as cache:
c_array = cache.read()
else:
logging.debug(f"Compiling hook {counter}...")
# Generate JS file
js_content = clean_content + "\n"
js_file = os.path.join(args.output_dir, f"temp-{counter}-gen.js")
with open(js_file, "w", encoding="utf-8") as f:
f.write(js_content)
try:
c_array = convert_js_to_carray(js_file, js_content, qjsc_path)
# Cache the result
if args.cache_dir and cache_file:
with open(cache_file, "w") as cache:
cache.write(c_array)
# Clean up temp file
os.remove(js_file)
except Exception as e:
logging.error(f"Compilation error for hook {counter}: {e}")
sys.exit(1)
# Generate .inc file with actual line range
start_line, end_line = hook_line_ranges[counter]
# Make source path relative to repo root
relative_source = f"src/test/app/{os.path.basename(args.source)}"
with open(inc_path, "w") as f:
f.write(
f"""// Auto-generated - DO NOT EDIT
// Source: {relative_source}:{start_line} ({start_line}:{end_line})
// Hash: {content_hash}
{{R"[test.hook]({clean_content})[test.hook]", {{
{c_array}
}}}},
"""
)
counter += 1
# Generate master header
with open(args.master, "w") as f:
f.write(
f"""// Generated by build_test_jshooks.py - DO COMMIT THIS FILE
// Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
//
// This file provides a registry of all JS test hooks.
// Individual compiled hooks are in build/jshooks/generated/
#ifndef SETHOOK_JSWASM_INCLUDED
#define SETHOOK_JSWASM_INCLUDED
#include <map>
#include <stdint.h>
#include <string>
#include <vector>
namespace ripple {{
namespace test {{
// Map of all JS test hooks - populated at compile time
std::map<std::string, std::vector<uint8_t>> jswasm = {{
{chr(10).join(include_statements)}
}};
}} // namespace test
}} // namespace ripple
#endif
"""
)
# Format the output file using clang-format
logging.info("Formatting master header...")
try:
subprocess.run(["clang-format", "-i", args.master], check=True)
logging.info(f"Successfully generated {relative_master} with {counter} hooks")
except subprocess.CalledProcessError:
logging.warning(
"Warning: clang-format failed, output might not be properly formatted"
)
except FileNotFoundError:
logging.warning("Warning: clang-format not found, output will not be formatted")
if __name__ == "__main__":
main()

View File

@@ -1561,6 +1561,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_compare
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -1656,6 +1657,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_divide
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -1949,6 +1951,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_int
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -2051,6 +2054,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_invert
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -2114,6 +2118,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_log
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -2160,6 +2165,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_mantissa
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -2329,6 +2335,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_mulratio
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -2465,6 +2472,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_multiply
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -2860,6 +2868,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook emit
const INVALID_ARGUMENT = -7
const PREREQUISITE_NOT_MET = -9
const EMISSION_FAILURE = -11
@@ -3163,6 +3172,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook etxn_details
const TOO_SMALL = -4
const OUT_OF_BOUNDS = -1
const PREREQUISITE_NOT_MET = -9
@@ -3207,6 +3217,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook etxn_fee_base
const TOO_SMALL = -4
const OUT_OF_BOUNDS = -1
const PREREQUISITE_NOT_MET = -9
@@ -3252,6 +3263,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook etxn_nonce
const TOO_SMALL = -4
const OUT_OF_BOUNDS = -1
const TOO_MANY_NONCES = -12
@@ -3301,6 +3313,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook etxn_reserve
const TOO_BIG = -3
const TOO_SMALL = -4
const ALREADY_SET = -8
@@ -3344,6 +3357,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook fee_base
const ASSERT = (x) => {
if (!x) rollback(x.toString(), 0)
}
@@ -3377,6 +3391,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook otxn_field
const INVALID_FIELD = -17
const sfAccount = 0x80001
@@ -3427,6 +3442,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook ledger_keylet
const ASSERT = (x) => {
if (!x) rollback(x.toString(), 0)
}
@@ -3509,6 +3525,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_negate
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -3561,6 +3578,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_one
var Hook = (arg) => {
const f = float_one()
f === 6089866696204910592n ? accept('', 2) : rollback('', 1)
@@ -3592,6 +3610,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_root
var ASSERT = (x) => {
if (!x) {
rollback('ASSERT.error', 0)
@@ -3636,6 +3655,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_set
var ASSERT = (x) => {
if (!x) {
rollback('ASSERT.error', 0)
@@ -3702,6 +3722,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_sign
var ASSERT = (x) => {
if (!x) {
rollback('ASSERT.error', 0)
@@ -3867,6 +3888,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_sto
const INVALID_FLOAT = -10024
const INVALID_ARGUMENT = -7
const ASSERT = (x) => {
@@ -4038,6 +4060,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_sto_set
const NOT_AN_OBJECT = -23
const ASSERT = (x) => {
if (!x) rollback(x.toString(), 1)
@@ -4148,6 +4171,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook float_sum
const float_exponent = (f) => Number(((f >> 54n) & 0xffn) - 97n)
const ASSERT_EQUAL = (x, y) => {
const px = x
@@ -4353,6 +4377,7 @@ public:
TestHook hook = jswasm[
R"[test.hook](
// @hook hook_account
const ASSERT = (x, code) => {
if (!x) {
rollback(x.toString(), code)
@@ -4488,6 +4513,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook hook_again
const PREREQUISITE_NOT_MET = -9
const ALREADY_SET = -8
@@ -4557,6 +4583,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook hook_hash
const ASSERT = (x, code) => {
if (!x) {
rollback(x.toString(), code)
@@ -4619,6 +4646,7 @@ public:
}
TestHook hook2 = jswasm[R"[test.hook](
// @hook hook_hash 2
const ASSERT = (x, code) => {
if (!x) {
rollback(x.toString(), code)
@@ -4731,6 +4759,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook hook_param
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -4845,6 +4874,7 @@ public:
env.fund(XRP(10000), bob);
TestHook checker_wasm = jswasm[R"[test.hook](
// @hook hook_param_set
const ASSERT = (x, code) => {
if (!x) {
rollback(x.toString(), code)
@@ -4897,6 +4927,7 @@ public:
)[test.hook]"];
TestHook setter_wasm = jswasm[R"[test.hook](
// @hook hook_param_set 2
const ASSERT = (x, code) => {
if (!x) {
rollback(x.toString(), code)
@@ -5021,6 +5052,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook hook_pos
const Hook = (arg) => {
return accept('', hook_pos())
}
@@ -5073,6 +5105,7 @@ public:
env.fund(XRP(10000), bob);
TestHook skip_wasm = jswasm[R"[test.hook](
// @hook hook_skip
const ASSERT = (x, code) => {
if (!x) {
rollback(x.toString(), code)
@@ -5118,6 +5151,7 @@ public:
)[test.hook]"];
TestHook pos_wasm = jswasm[R"[test.hook](
// @hook hook_skip 2
const Hook = (arg) => {
return accept('', 255)
}
@@ -5175,6 +5209,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook ledger_last_hash
const ASSERT = (x, code) => {
if (!x) {
rollback(x.toString(), code)
@@ -5252,6 +5287,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook ledger_last_time
const Hook = (arg) => {
return accept('', ledger_last_time())
}
@@ -5313,6 +5349,7 @@ public:
env.fund(XRP(10000), alice);
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook ledger_nonce
const ASSERT = (x, code) => {
if (!x) {
rollback(x.toString(), code)
@@ -5415,6 +5452,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook ledger_seq
const Hook = (arg) => {
return accept('', ledger_seq())
}
@@ -5468,6 +5506,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook meta_slot
const PREREQUISITE_NOT_MET = -9
const ASSERT = (x) => {
if (!x) rollback(x.toString(), 0)
@@ -5544,6 +5583,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook xpop_slot
const ttIMPORT = 97
const DOESNT_EXIST = -5
const NO_FREE_SLOTS = -6
@@ -5637,6 +5677,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook otxn_id
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -5685,6 +5726,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook otxn_slot
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -5739,6 +5781,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook otxn_type
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -5793,6 +5836,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook otxn_param
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -5911,6 +5955,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook otxn_json
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -5963,6 +6008,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook slot
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -6023,6 +6069,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook slot_clear
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -6065,6 +6112,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook slot_count
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -6110,6 +6158,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook slot_float
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -6157,6 +6206,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook slot_set
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -6242,6 +6292,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook slot_size
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -6293,6 +6344,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook slot_subarray
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -6398,6 +6450,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook slot_subfield
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -6471,6 +6524,7 @@ public:
env(trust(alice, bob["USD"](600)));
TestHook hook = jswasm[R"[test.hook](
// @hook slot_type
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -6546,6 +6600,7 @@ public:
env(trust(alice, bob["USD"](600)));
TestHook hook = jswasm[R"[test.hook](
// @hook slot_json
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -6620,6 +6675,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook state
const ASSERT = (x, code) => {
if (!x) {
trace('error', 0, false)
@@ -6667,6 +6723,7 @@ public:
// objects
{
TestHook hook = jswasm[R"[test.hook](
// @hook state 2
const ASSERT = (x, code) => {
if (!x) {
trace('error', 0, false)
@@ -6722,6 +6779,7 @@ public:
{
TestHook hook = jswasm[R"[test.hook](
// @hook state_foreign
var ASSERT = (x, code) => {
if (!x) {
trace('error', 0, false)
@@ -6756,6 +6814,7 @@ public:
// set a second hook on bob that will read the state objects from alice
{
TestHook hook = jswasm[R"[test.hook](
// @hook state_foreign 2
var ASSERT = (x, code) => {
if (!x) {
trace('error', 0, false)
@@ -6832,6 +6891,7 @@ public:
env.fund(XRP(2600), eve);
TestHook grantee_wasm = jswasm[R"[test.hook](
// @hook state_foreign_set
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -6911,6 +6971,7 @@ public:
// this is the grantor
TestHook grantor_wasm = jswasm[R"[test.hook](
// @hook state_foreign_set 2
var ASSERT = (x, code) => {
if (!x) {
trace('error', 0, false)
@@ -7157,6 +7218,7 @@ public:
// check reserve exhaustion
TestHook exhaustion_wasm = jswasm[R"[test.hook](
// @hook state_foreign_set 3
var ASSERT = (x, code) => {
if (!x) {
trace('error', 0, false)
@@ -7311,6 +7373,7 @@ public:
// alice
{
TestHook hook = jswasm[R"[test.hook](
// @hook state_set
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -7457,6 +7520,7 @@ public:
// existing state
{
TestHook hook = jswasm[R"[test.hook](
// @hook state_set 2
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -7477,6 +7541,7 @@ public:
)[test.hook]"];
TestHook hook2 = jswasm[R"[test.hook](
// @hook state_set 3
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -7512,6 +7577,7 @@ public:
// taken because bob's hooks will execute first if bob's is the
// otxn. therefore we will flip to a payment from alice to bob here
TestHook hook3 = jswasm[R"[test.hook](
// @hook state_set 4
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -7595,6 +7661,7 @@ public:
// strong side is rolled back
{
TestHook hook = jswasm[R"[test.hook](
// @hook state_set 5
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -7683,6 +7750,7 @@ public:
// check reserve exhaustion
TestHook exhaustion_wasm = jswasm[R"[test.hook](
// @hook state_set 6
var ASSERT = (x, line) => {
if (!x) {
trace('line', line, false)
@@ -7809,6 +7877,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook trace
const ASSERT = (x, code) => {
if (!x) {
rollback(x.toString(), code)
@@ -7837,6 +7906,7 @@ public:
void
test_util_accid(FeatureBitset features)
{
testcase("Test util_accid");
using namespace jtx;
Env env{*this, features};
@@ -7846,6 +7916,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook util_accid
const ASSERT = (x, code) => {
if (!x) {
rollback(x.toString(), code)
@@ -8405,6 +8476,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook util_keylet
const ASSERT = (x, code) => {
if (!x) {
trace('error', 0, false)
@@ -8782,6 +8854,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook util_raddr
const ASSERT = (x, code) => {
if (!x) {
rollback(x.toString(), code)
@@ -9705,6 +9778,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook util_sha512h
const ASSERT = (x, code) => {
if (!x) {
trace('error', 0, false)
@@ -10321,6 +10395,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook util_verify
const ASSERT = (x, code) => {
if (!x) {
trace('error', 0, false)
@@ -10400,6 +10475,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook sto_emplace
var ASSERT = (x) => {
if (!x) {
rollback(x.toString(), 0)
@@ -10518,6 +10594,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook sto_erase
const ASSERT = (x) => {
if (!x) rollback(x.toString(), 0)
}
@@ -10608,6 +10685,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook sto_subarray
const DOESNT_EXIST = -5
const INVALID_ARGUMENT = -7
@@ -10669,6 +10747,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook sto_subfield
const DOESNT_EXIST = -5
const INVALID_ARGUMENT = -7
const ASSERT = (x) => {
@@ -10741,6 +10820,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook sto_validate
const INVALID_ARGUMENT = -7
const ASSERT = (x) => {
if (!x) rollback(x.toString(), 0)
@@ -10808,6 +10888,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook sto_to_json
const INVALID_ARGUMENT = -7
const ASSERT = (x) => {
if (!x) rollback(x.toString(), 0)
@@ -10893,6 +10974,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook sto_from_json
const INVALID_ARGUMENT = -7
const ASSERT = (x) => {
if (!x) rollback(x.toString(), 0)
@@ -10974,6 +11056,7 @@ public:
env.fund(XRP(10000), bob);
TestHook hook = jswasm[R"[test.hook](
// @hook JS Date
const ASSERT = (x) => {
if (!x) rollback(x.toString(), -1)
}
@@ -11149,6 +11232,7 @@ private:
TestHook accept_wasm = // WASM: 0
jswasm[
R"[test.hook](
// @hook accept_wasm shared fixture
const Hook = (arg) => {
return accept("0", 0);
}
@@ -11159,6 +11243,7 @@ private:
TestHook rollback_wasm = // WASM: 1
jswasm[
R"[test.hook](
// @hook rollback_wasm shared fixture
const Hook = (arg) => {
return rollback("0", 0);
}
@@ -11169,6 +11254,7 @@ private:
TestHook illegalfunc_wasm = // WASM: 3
jswasm[
R"[test.hook](
// @hook illegalfunc_wasm shared fixture
const Hook = (arg) => {
console.log("HERE");
return accept(ret, 0);
@@ -11178,6 +11264,7 @@ private:
TestHook long_wasm = // WASM: 4
jswasm[
R"[test.hook](
// @hook long_wasm shared fixture
const M_REPEAT_10 = (X) => X.repeat(10);
const M_REPEAT_100 = (X) => M_REPEAT_10(X).repeat(10);
const M_REPEAT_1000 = (X) => M_REPEAT_100(X).repeat(10);
@@ -11190,6 +11277,7 @@ private:
TestHook makestate_wasm = // WASM: 5
jswasm[
R"[test.hook](
// @hook makestate_wasm shared fixture
const Hook = (arg) => {
const test_key = "0000000000000000000000000000000000000000000000006b657900";
const test_value = "76616C756500";
@@ -11203,6 +11291,7 @@ private:
TestHook accept2_wasm = // WASM: 6
jswasm[
R"[test.hook](
// @hook accept2_wasm shared fixture
const Hook = (arg) => {
return accept("0", 2);
}

File diff suppressed because it is too large Load Diff