diff --git a/docs/reports/primitive-coercion-fast-path.json b/docs/reports/primitive-coercion-fast-path.json new file mode 100644 index 00000000..ed3fade6 --- /dev/null +++ b/docs/reports/primitive-coercion-fast-path.json @@ -0,0 +1,1489 @@ +{ + "schema_version": 1, + "date": "2026-09-09", + "issue": "https://github.com/pocket-stack/quickjs-oxide/issues/8", + "pr": "https://github.com/Eric-Song-Nop/quickjs-oxide/pull/3", + "parent_pr": "https://github.com/Eric-Song-Nop/quickjs-oxide/pull/2", + "environment": { + "inherited_build_environment": { + "RUSTFLAGS": null, + "CARGO_ENCODED_RUSTFLAGS": null, + "RUSTUP_TOOLCHAIN": null, + "CARGO_PROFILE_RELEASE_LTO": null, + "CARGO_PROFILE_RELEASE_CODEGEN_UNITS": null + }, + "host": "eric-83am", + "kernel": "Linux eric-83am 6.18.44-1-lts #1 SMP PREEMPT_DYNAMIC Tue, 11 Aug 2026 21:10:19 +0000 x86_64 GNU/Linux", + "rustc_release": "rustc 1.94.1 (e408947bf 2026-03-25)\nbinary: rustc\ncommit-hash: e408947bfd200af42db322daf0fadfe7e26d3bd1\ncommit-date: 2026-03-25\nhost: x86_64-unknown-linux-gnu\nrelease: 1.94.1\nLLVM version: 21.1.8\n", + "rustc_tests": "rustc 1.88.0 (6b00bc388 2025-06-23)\nbinary: rustc\ncommit-hash: 6b00bc3880198600130e1cf62b8f8a93494488cc\ncommit-date: 2025-06-23\nhost: x86_64-unknown-linux-gnu\nrelease: 1.88.0\nLLVM version: 20.1.5\n", + "cpu": "Architecture: x86_64\nCPU op-mode(s): 32-bit, 64-bit\nAddress sizes: 48 bits physical, 48 bits virtual\nByte Order: Little Endian\nCPU(s): 16\nOn-line CPU(s) list: 0-15\nVendor ID: AuthenticAMD\nModel name: AMD Ryzen 7 7840HS with Radeon 780M Graphics\nCPU family: 25\nModel: 116\nThread(s) per core: 2\nCore(s) per socket: 8\nSocket(s): 1\nStepping: 1\nMicrocode version: 0xa704108\nFrequency boost: enabled\nCPU(s) scaling MHz: 51%\nCPU max MHz: 5137.9038\nCPU min MHz: 419.4210\nBogoMIPS: 7588.59\nFlags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl xtopology nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpuid_fault cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca flush_l1d amd_lbr_pmc_freeze\nVirtualization: AMD-V\nL1d cache: 256 KiB (8 instances)\nL1i cache: 256 KiB (8 instances)\nL2 cache: 8 MiB (8 instances)\nL3 cache: 16 MiB (1 instance)\nNUMA node(s): 1\nNUMA node0 CPU(s): 0-15\nVulnerability Gather data sampling: Not affected\nVulnerability Ghostwrite: Not affected\nVulnerability Indirect target selection: Not affected\nVulnerability Itlb multihit: Not affected\nVulnerability L1tf: Not affected\nVulnerability Mds: Not affected\nVulnerability Meltdown: Not affected\nVulnerability Mmio stale data: Not affected\nVulnerability Old microcode: Not affected\nVulnerability Reg file data sampling: Not affected\nVulnerability Retbleed: Not affected\nVulnerability Spec rstack overflow: Mitigation; Safe RET\nVulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl\nVulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization\nVulnerability Spectre v2: Mitigation; Enhanced / Automatic IBRS; IBPB conditional; STIBP always-on; PBRSB-eIBRS Not affected; BHI Not affected\nVulnerability Srbds: Not affected\nVulnerability Tsa: Mitigation; Clear CPU buffers\nVulnerability Tsx async abort: Not affected\nVulnerability Vmscape: Mitigation; IBPB before exit to userspace\n", + "product_commit": "6e59179e042b7ad93659136b9ba1af99f8aa9cdb", + "git_status": "", + "release_command": "CARGO_PROFILE_RELEASE_DEBUG=1 CARGO_PROFILE_RELEASE_STRIP=none CARGO_TARGET_DIR=target/cpu-profile QUICKJS_OXIDE_BUILD_COMMIT=$(git rev-parse HEAD) cargo build --locked --release -p quickjs-oxide-cli --no-default-features --features profiling", + "diagnostics": "profiling feature compiled, runtime diagnostics inactive; no instrumentation patch; no forced frame pointers", + "execution": "Herdr eric-83am --session default, PocketLab w6:p3; builds/tests/benchmarks serialized" + }, + "methodology": { + "timer": "time.perf_counter_ns around each complete subprocess", + "timeout_seconds": 90, + "initial": "5 repetitions per version/workload; before-first on even repetitions, after-first on odd", + "confirmation": "10 repetitions per version for fixed/original Richards; after-first on even repetitions, before-first on odd; executed after all validation, same exact binaries", + "confirmation_reason": "Initial Richards medians were slightly slower with overlapping ranges; independent confirmation retained alongside original samples.", + "external_workload_revision": "ahaoboy/js-engine-benchmark@2034d98fc8c5f8044e186267593f5d5ea5232caf", + "limitations": [ + "CPU frequency and system isolation were not controlled.", + "Whole-process timings include initialization, compilation, execution and teardown.", + "Property-read workload includes numeric loop and accumulation work.", + "Runtime diagnostics were inactive; no new CPU sampling or production counters.", + "No compilation-phase or memory-overhead probe in this round; no new persistent runtime data structure.", + "Initial and confirmation samples are both retained; do not infer whole-engine speedup from a particular workload." + ] + }, + "initial": { + "before_commit": "eb48e7163423b69379a2e98a8d6c7aff812b1fbf", + "after_commit": "6e59179e042b7ad93659136b9ba1af99f8aa9cdb", + "binaries": { + "before": { + "qjs_sha256": "89a34a5a752df07af4d108649769497e955ca798026729ea764203c80f2ba730", + "qjs_bytes": 35338936 + }, + "after": { + "qjs_sha256": "a96de056a0a0ac0e7b3f631727bd2f322903bb74c892ae87630fdb60ad76264b", + "qjs_bytes": 35314888 + } + }, + "workloads": { + "empty_fixed": { + "path": "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/empty_loop-fixed.js", + "sha256": "712718c7d9f15dde0f3be565c47ddc4ab1b79b9aade5ba031c2e17fb7ac44e6b" + }, + "prop_fixed": { + "path": "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/prop_read-fixed.js", + "sha256": "661603fba02fbf5437c927517107863bb9e27a183028cf64f93bed95d30488e0" + }, + "richards_fixed": { + "path": "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js", + "sha256": "cf295e5d11938844ff3b8dbe354fae237f08e7f3f3e72a86bc4d6766fb3a27d5" + }, + "richards": { + "path": "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js", + "sha256": "3a6a75ed44921a21ccb5d651c4fa40e1168cc398497f5783f70f520b5880f816" + } + }, + "summary": { + "empty_fixed": { + "before": { + "median_ms": 1967.351968, + "samples_ms": [ + 1985.769096, + 1967.351968, + 1934.720655, + 1978.16724, + 1923.922927 + ], + "min_ms": 1923.922927, + "max_ms": 1985.769096 + }, + "after": { + "median_ms": 1547.222232, + "samples_ms": [ + 1500.396974, + 1577.076359, + 1542.474021, + 1547.222232, + 1723.615519 + ], + "min_ms": 1500.396974, + "max_ms": 1723.615519 + }, + "wall_reduction_percent": 21.355087591525457 + }, + "prop_fixed": { + "before": { + "median_ms": 7041.107819, + "samples_ms": [ + 7057.373386, + 7041.107819, + 7081.772489, + 7003.246285, + 6926.039795 + ], + "min_ms": 6926.039795, + "max_ms": 7081.772489 + }, + "after": { + "median_ms": 6522.870517, + "samples_ms": [ + 6446.743066, + 6549.437951, + 6522.870517, + 6495.734853, + 6726.171088 + ], + "min_ms": 6446.743066, + "max_ms": 6726.171088 + }, + "wall_reduction_percent": 7.36016711179408 + }, + "richards_fixed": { + "before": { + "median_ms": 1496.602138, + "samples_ms": [ + 1496.602138, + 1496.261833, + 1501.644331, + 1494.998003, + 1517.356895 + ], + "min_ms": 1494.998003, + "max_ms": 1517.356895 + }, + "after": { + "median_ms": 1506.158625, + "samples_ms": [ + 1504.762115, + 1506.158625, + 1508.046493, + 1492.278198, + 1511.040782 + ], + "min_ms": 1492.278198, + "max_ms": 1511.040782 + }, + "wall_reduction_percent": -0.6385455932042872 + }, + "richards": { + "before": { + "median_ms": 6243.92081, + "samples_ms": [ + 6307.535494, + 6271.022499, + 6243.92081, + 6231.14528, + 6174.643063 + ], + "min_ms": 6174.643063, + "max_ms": 6307.535494, + "scores": [ + 23.6, + 23.7, + 23.8, + 23.8, + 24.1 + ], + "median_score": 23.8 + }, + "after": { + "median_ms": 6295.843161, + "samples_ms": [ + 6295.843161, + 6299.1972, + 6256.949475, + 6304.143842, + 6233.638592 + ], + "min_ms": 6233.638592, + "max_ms": 6304.143842, + "scores": [ + 23.6, + 23.6, + 23.8, + 23.5, + 23.9 + ], + "median_score": 23.6 + }, + "wall_reduction_percent": -0.8315664560774705 + } + } + }, + "confirmation": { + "before_commit": "eb48e7163423b69379a2e98a8d6c7aff812b1fbf", + "after_commit": "6e59179e042b7ad93659136b9ba1af99f8aa9cdb", + "binaries": { + "before": { + "qjs_sha256": "89a34a5a752df07af4d108649769497e955ca798026729ea764203c80f2ba730", + "qjs_bytes": 35338936 + }, + "after": { + "qjs_sha256": "a96de056a0a0ac0e7b3f631727bd2f322903bb74c892ae87630fdb60ad76264b", + "qjs_bytes": 35314888 + } + }, + "workloads": { + "richards_fixed": { + "path": "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js", + "sha256": "cf295e5d11938844ff3b8dbe354fae237f08e7f3f3e72a86bc4d6766fb3a27d5" + }, + "richards": { + "path": "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js", + "sha256": "3a6a75ed44921a21ccb5d651c4fa40e1168cc398497f5783f70f520b5880f816" + } + }, + "summary": { + "richards_fixed": { + "before": { + "median_ms": 1498.445614, + "samples_ms": [ + 1498.098496, + 1498.792732, + 1505.548557, + 1518.298232, + 1486.069639, + 1489.871181, + 1497.581292, + 1499.02299, + 1508.100661, + 1497.494648 + ], + "min_ms": 1486.069639, + "max_ms": 1518.298232 + }, + "after": { + "median_ms": 1494.5227705, + "samples_ms": [ + 1494.261573, + 1490.902898, + 1515.161802, + 1515.133829, + 1489.628912, + 1490.588149, + 1504.4835, + 1489.391616, + 1498.782923, + 1494.783968 + ], + "min_ms": 1489.391616, + "max_ms": 1515.161802 + }, + "wall_reduction_percent": 0.26179418614521444 + }, + "richards": { + "before": { + "median_ms": 6224.2805315, + "samples_ms": [ + 6200.000149, + 6282.892704, + 6261.228444, + 6228.875665, + 6175.497722, + 6208.653149, + 6242.819163, + 6431.02904, + 6209.494384, + 6219.685398 + ], + "min_ms": 6175.497722, + "max_ms": 6431.02904, + "scores": [ + 24.0, + 23.7, + 23.8, + 23.9, + 24.1, + 24.0, + 23.9, + 23.0, + 23.9, + 23.9 + ], + "median_score": 23.9 + }, + "after": { + "median_ms": 6230.8594125, + "samples_ms": [ + 6299.446736, + 6218.511026, + 6279.391132, + 6261.645388, + 6276.428231, + 6202.764773, + 6208.859202, + 6213.240376, + 6174.803974, + 6243.207799 + ], + "min_ms": 6174.803974, + "max_ms": 6299.446736, + "scores": [ + 23.6, + 23.9, + 23.7, + 23.7, + 23.8, + 24.0, + 23.9, + 23.9, + 24.1, + 23.8 + ], + "median_score": 23.85 + }, + "wall_reduction_percent": -0.10569705151792785 + } + } + }, + "samples": { + "benchmark": [ + { + "case": "empty_fixed", + "repetition": 0, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/empty_loop-fixed.js" + ], + "wall_ns": 1985769096, + "exit_code": 0, + "stdout": "10000000\n", + "stderr": "" + }, + { + "case": "empty_fixed", + "repetition": 0, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/empty_loop-fixed.js" + ], + "wall_ns": 1500396974, + "exit_code": 0, + "stdout": "10000000\n", + "stderr": "" + }, + { + "case": "empty_fixed", + "repetition": 1, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/empty_loop-fixed.js" + ], + "wall_ns": 1577076359, + "exit_code": 0, + "stdout": "10000000\n", + "stderr": "" + }, + { + "case": "empty_fixed", + "repetition": 1, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/empty_loop-fixed.js" + ], + "wall_ns": 1967351968, + "exit_code": 0, + "stdout": "10000000\n", + "stderr": "" + }, + { + "case": "empty_fixed", + "repetition": 2, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/empty_loop-fixed.js" + ], + "wall_ns": 1934720655, + "exit_code": 0, + "stdout": "10000000\n", + "stderr": "" + }, + { + "case": "empty_fixed", + "repetition": 2, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/empty_loop-fixed.js" + ], + "wall_ns": 1542474021, + "exit_code": 0, + "stdout": "10000000\n", + "stderr": "" + }, + { + "case": "empty_fixed", + "repetition": 3, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/empty_loop-fixed.js" + ], + "wall_ns": 1547222232, + "exit_code": 0, + "stdout": "10000000\n", + "stderr": "" + }, + { + "case": "empty_fixed", + "repetition": 3, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/empty_loop-fixed.js" + ], + "wall_ns": 1978167240, + "exit_code": 0, + "stdout": "10000000\n", + "stderr": "" + }, + { + "case": "empty_fixed", + "repetition": 4, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/empty_loop-fixed.js" + ], + "wall_ns": 1923922927, + "exit_code": 0, + "stdout": "10000000\n", + "stderr": "" + }, + { + "case": "empty_fixed", + "repetition": 4, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/empty_loop-fixed.js" + ], + "wall_ns": 1723615519, + "exit_code": 0, + "stdout": "10000000\n", + "stderr": "" + }, + { + "case": "prop_fixed", + "repetition": 0, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/prop_read-fixed.js" + ], + "wall_ns": 7057373386, + "exit_code": 0, + "stdout": "20000000\n50000000\n", + "stderr": "" + }, + { + "case": "prop_fixed", + "repetition": 0, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/prop_read-fixed.js" + ], + "wall_ns": 6446743066, + "exit_code": 0, + "stdout": "20000000\n50000000\n", + "stderr": "" + }, + { + "case": "prop_fixed", + "repetition": 1, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/prop_read-fixed.js" + ], + "wall_ns": 6549437951, + "exit_code": 0, + "stdout": "20000000\n50000000\n", + "stderr": "" + }, + { + "case": "prop_fixed", + "repetition": 1, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/prop_read-fixed.js" + ], + "wall_ns": 7041107819, + "exit_code": 0, + "stdout": "20000000\n50000000\n", + "stderr": "" + }, + { + "case": "prop_fixed", + "repetition": 2, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/prop_read-fixed.js" + ], + "wall_ns": 7081772489, + "exit_code": 0, + "stdout": "20000000\n50000000\n", + "stderr": "" + }, + { + "case": "prop_fixed", + "repetition": 2, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/prop_read-fixed.js" + ], + "wall_ns": 6522870517, + "exit_code": 0, + "stdout": "20000000\n50000000\n", + "stderr": "" + }, + { + "case": "prop_fixed", + "repetition": 3, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/prop_read-fixed.js" + ], + "wall_ns": 6495734853, + "exit_code": 0, + "stdout": "20000000\n50000000\n", + "stderr": "" + }, + { + "case": "prop_fixed", + "repetition": 3, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/prop_read-fixed.js" + ], + "wall_ns": 7003246285, + "exit_code": 0, + "stdout": "20000000\n50000000\n", + "stderr": "" + }, + { + "case": "prop_fixed", + "repetition": 4, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/prop_read-fixed.js" + ], + "wall_ns": 6926039795, + "exit_code": 0, + "stdout": "20000000\n50000000\n", + "stderr": "" + }, + { + "case": "prop_fixed", + "repetition": 4, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/cpu-investigation/frame-pointers/prop_read-fixed.js" + ], + "wall_ns": 6726171088, + "exit_code": 0, + "stdout": "20000000\n50000000\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 0, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1496602138, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 0, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1504762115, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 1, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1506158625, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 1, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1496261833, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 2, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1501644331, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 2, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1508046493, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 3, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1492278198, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 3, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1494998003, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 4, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1517356895, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 4, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1511040782, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards", + "repetition": 0, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6307535494, + "exit_code": 0, + "stdout": "Richards: 23.6\n----\nScore: 23.6\n", + "stderr": "", + "harness_score": 23.6 + }, + { + "case": "richards", + "repetition": 0, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6295843161, + "exit_code": 0, + "stdout": "Richards: 23.6\n----\nScore: 23.6\n", + "stderr": "", + "harness_score": 23.6 + }, + { + "case": "richards", + "repetition": 1, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6299197200, + "exit_code": 0, + "stdout": "Richards: 23.6\n----\nScore: 23.6\n", + "stderr": "", + "harness_score": 23.6 + }, + { + "case": "richards", + "repetition": 1, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6271022499, + "exit_code": 0, + "stdout": "Richards: 23.7\n----\nScore: 23.7\n", + "stderr": "", + "harness_score": 23.7 + }, + { + "case": "richards", + "repetition": 2, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6243920810, + "exit_code": 0, + "stdout": "Richards: 23.8\n----\nScore: 23.8\n", + "stderr": "", + "harness_score": 23.8 + }, + { + "case": "richards", + "repetition": 2, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6256949475, + "exit_code": 0, + "stdout": "Richards: 23.8\n----\nScore: 23.8\n", + "stderr": "", + "harness_score": 23.8 + }, + { + "case": "richards", + "repetition": 3, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6304143842, + "exit_code": 0, + "stdout": "Richards: 23.5\n----\nScore: 23.5\n", + "stderr": "", + "harness_score": 23.5 + }, + { + "case": "richards", + "repetition": 3, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6231145280, + "exit_code": 0, + "stdout": "Richards: 23.8\n----\nScore: 23.8\n", + "stderr": "", + "harness_score": 23.8 + }, + { + "case": "richards", + "repetition": 4, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6174643063, + "exit_code": 0, + "stdout": "Richards: 24.1\n----\nScore: 24.1\n", + "stderr": "", + "harness_score": 24.1 + }, + { + "case": "richards", + "repetition": 4, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6233638592, + "exit_code": 0, + "stdout": "Richards: 23.9\n----\nScore: 23.9\n", + "stderr": "", + "harness_score": 23.9 + } + ], + "confirmation": [ + { + "case": "richards_fixed", + "repetition": 0, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1494261573, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 0, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1498098496, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 1, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1498792732, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 1, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1490902898, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 2, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1515161802, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 2, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1505548557, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 3, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1518298232, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 3, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1515133829, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 4, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1489628912, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 4, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1486069639, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 5, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1489871181, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 5, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1490588149, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 6, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1504483500, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 6, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1497581292, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 7, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1499022990, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 7, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1489391616, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 8, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1498782923, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 8, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1508100661, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 9, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1497494648, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards_fixed", + "repetition": 9, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards-fixed-investigation.js" + ], + "wall_ns": 1494783968, + "exit_code": 0, + "stdout": "richards-fixed:10\n", + "stderr": "" + }, + { + "case": "richards", + "repetition": 0, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6299446736, + "exit_code": 0, + "stdout": "Richards: 23.6\n----\nScore: 23.6\n", + "stderr": "", + "harness_score": 23.6 + }, + { + "case": "richards", + "repetition": 0, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6200000149, + "exit_code": 0, + "stdout": "Richards: 24.0\n----\nScore: 24.0\n", + "stderr": "", + "harness_score": 24.0 + }, + { + "case": "richards", + "repetition": 1, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6282892704, + "exit_code": 0, + "stdout": "Richards: 23.7\n----\nScore: 23.7\n", + "stderr": "", + "harness_score": 23.7 + }, + { + "case": "richards", + "repetition": 1, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6218511026, + "exit_code": 0, + "stdout": "Richards: 23.9\n----\nScore: 23.9\n", + "stderr": "", + "harness_score": 23.9 + }, + { + "case": "richards", + "repetition": 2, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6279391132, + "exit_code": 0, + "stdout": "Richards: 23.7\n----\nScore: 23.7\n", + "stderr": "", + "harness_score": 23.7 + }, + { + "case": "richards", + "repetition": 2, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6261228444, + "exit_code": 0, + "stdout": "Richards: 23.8\n----\nScore: 23.8\n", + "stderr": "", + "harness_score": 23.8 + }, + { + "case": "richards", + "repetition": 3, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6228875665, + "exit_code": 0, + "stdout": "Richards: 23.9\n----\nScore: 23.9\n", + "stderr": "", + "harness_score": 23.9 + }, + { + "case": "richards", + "repetition": 3, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6261645388, + "exit_code": 0, + "stdout": "Richards: 23.7\n----\nScore: 23.7\n", + "stderr": "", + "harness_score": 23.7 + }, + { + "case": "richards", + "repetition": 4, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6276428231, + "exit_code": 0, + "stdout": "Richards: 23.8\n----\nScore: 23.8\n", + "stderr": "", + "harness_score": 23.8 + }, + { + "case": "richards", + "repetition": 4, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6175497722, + "exit_code": 0, + "stdout": "Richards: 24.1\n----\nScore: 24.1\n", + "stderr": "", + "harness_score": 24.1 + }, + { + "case": "richards", + "repetition": 5, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6208653149, + "exit_code": 0, + "stdout": "Richards: 24.0\n----\nScore: 24.0\n", + "stderr": "", + "harness_score": 24.0 + }, + { + "case": "richards", + "repetition": 5, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6202764773, + "exit_code": 0, + "stdout": "Richards: 24.0\n----\nScore: 24.0\n", + "stderr": "", + "harness_score": 24.0 + }, + { + "case": "richards", + "repetition": 6, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6208859202, + "exit_code": 0, + "stdout": "Richards: 23.9\n----\nScore: 23.9\n", + "stderr": "", + "harness_score": 23.9 + }, + { + "case": "richards", + "repetition": 6, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6242819163, + "exit_code": 0, + "stdout": "Richards: 23.9\n----\nScore: 23.9\n", + "stderr": "", + "harness_score": 23.9 + }, + { + "case": "richards", + "repetition": 7, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6431029040, + "exit_code": 0, + "stdout": "Richards: 23.0\n----\nScore: 23.0\n", + "stderr": "", + "harness_score": 23.0 + }, + { + "case": "richards", + "repetition": 7, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6213240376, + "exit_code": 0, + "stdout": "Richards: 23.9\n----\nScore: 23.9\n", + "stderr": "", + "harness_score": 23.9 + }, + { + "case": "richards", + "repetition": 8, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6174803974, + "exit_code": 0, + "stdout": "Richards: 24.1\n----\nScore: 24.1\n", + "stderr": "", + "harness_score": 24.1 + }, + { + "case": "richards", + "repetition": 8, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6209494384, + "exit_code": 0, + "stdout": "Richards: 23.9\n----\nScore: 23.9\n", + "stderr": "", + "harness_score": 23.9 + }, + { + "case": "richards", + "repetition": 9, + "version": "before", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/before/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6219685398, + "exit_code": 0, + "stdout": "Richards: 23.9\n----\nScore: 23.9\n", + "stderr": "", + "harness_score": 23.9 + }, + { + "case": "richards", + "repetition": 9, + "version": "after", + "command": [ + "/home/eric/Documents/Sources/PocketLab/quickjs-oxide/target/primitive-coercion-validation/after/qjs", + "/home/eric/Documents/Sources/PocketLab/js-engine-benchmark/dist/quickjs-oxide/richards.js" + ], + "wall_ns": 6243207799, + "exit_code": 0, + "stdout": "Richards: 23.8\n----\nScore: 23.8\n", + "stderr": "", + "harness_score": 23.8 + } + ] + }, + "validation": { + "workspace_tests": { + "passed": 3002, + "failed": 0, + "ignored": 1, + "test_binaries": 13, + "command": "cargo +1.88.0 test --locked --workspace --all-targets --features profiling" + }, + "checks": { + "fmt.log": { + "status": "passed", + "log_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "clippy-profiling.log": { + "status": "passed", + "log_sha256": "f7ac4debb43be54389c78eaeee5951a503490c954a0dfa84d5647882b97c2ce2" + }, + "clippy-default.log": { + "status": "passed", + "log_sha256": "ab90e341436f8dd621ed38766229229b30390d48e080b0a9f3b905f427591539" + }, + "clippy-test262-host.log": { + "status": "passed", + "log_sha256": "51747060ddc48ea799a993c3c46c6ffc52dde83e3f63d7cefa080caef860eeec" + }, + "source-layout.log": { + "status": "passed", + "log_sha256": "001bb04eb88178751a406964d769ced3c879bae715715a83d1f98fcc8baf49f0" + }, + "rust-only.log": { + "status": "passed", + "log_sha256": "4d1e74ca127d1cd134d16e965d96bc586a23ff20a4124152e7a96426a1a95d3a" + }, + "binary-boundary.log": { + "status": "passed", + "log_sha256": "7abf1bc6d9854823ce4bd47d926a9def883f8c90af675ba9ac6d30e5d93d31aa" + } + }, + "isolation_canaries_rejected": 690, + "test262": { + "before_file_sha256": "29301a4afe0d1d18ac9cedff60bf90f1fff6b4d95d0ea233bdbbc2899259b400", + "after_file_sha256": "c54126fb398bff12bf58b03d2bb51743343b4ff650bc0af212074eb4d1a0b5d1", + "outcome_count": 102037, + "changed_outcomes": 0, + "changed_rows": [], + "outcome_rows_sha256": { + "before": "fa99d3349bb4b61f30ba57d7c7f275df64f19691edefc50b96b53887339fa8c3", + "after": "fa99d3349bb4b61f30ba57d7c7f275df64f19691edefc50b96b53887339fa8c3" + }, + "counts": { + "before": { + "pass": 79982, + "skipped-feature": 11775, + "fail-parse": 7, + "unsupported-negative-provenance": 2562, + "unsupported-module": 121, + "skipped-config-exclude": 6700, + "fail-runtime": 43, + "unsupported-feature": 847 + }, + "after": { + "pass": 79982, + "skipped-feature": 11775, + "fail-parse": 7, + "unsupported-negative-provenance": 2562, + "unsupported-module": 121, + "skipped-config-exclude": 6700, + "fail-runtime": 43, + "unsupported-feature": 847 + } + }, + "before_header": "# quickjs-oxide Test262 outcome vector v5 engine_semantics_sha256=1bfa4ffbd2b3081315865d7f17c22d319ef21f4dc245f325489a52535852cd72", + "after_header": "# quickjs-oxide Test262 outcome vector v5 engine_semantics_sha256=6ae5bc651775950fd9d1784ea036b5f55f83e0fc4d5cb084333318f3cf537c0e", + "gate_exit_codes": { + "before": 1, + "after": 1 + }, + "frozen_gate": "Both full-file receipt gates fail on the changed source-fingerprint header. No baseline was updated." + } + }, + "archive": { + "file": "quickjs-oxide-primitive-coercion-pocketlab-2026-09-09.tar.gz", + "bytes": 22471956, + "sha256": "d7c8d752afe2b97c78726985cfae61a9f2f9fda7cf0d26940436d56f96ae5b2e", + "manifest_members_verified": 199, + "external_benchmark_sources_included": false + } +} diff --git a/docs/reports/primitive-coercion-fast-path.md b/docs/reports/primitive-coercion-fast-path.md new file mode 100644 index 00000000..8ddc7dcd --- /dev/null +++ b/docs/reports/primitive-coercion-fast-path.md @@ -0,0 +1,84 @@ +# Primitive operand preparation — 2026-09-09 + +VM 数值准备现在通过一个共享 `ToPrimitive` 入口:原始值保持表示直接返回,对象及原 hint 交给已有 host。基于静态 Atom 优化 [PR #2](https://github.com/Eric-Song-Nop/quickjs-oxide/pull/2),关联 [issue #8](https://github.com/pocket-stack/quickjs-oxide/issues/8) 与 stacked [PR #3](https://github.com/Eric-Song-Nop/quickjs-oxide/pull/3)。 + +VM numeric preparation now uses one shared `ToPrimitive` entry point: primitives retain their representation and return directly, while objects and the original hint use the existing host. This stacks on static Atom reuse; arithmetic implementations remain shared. + +## Initial measurements / 首轮测量 + +同机同 release 参数,每版本/负载五个独立进程,前后交替顺序。正数表示耗时下降,负数表示变慢。所有 40 次运行输出检查通过。 + +Five independent processes per version/workload, alternating order, identical release settings. Positive reductions mean less time; negative reductions mean slower. All 40 outputs passed validation. + +| Workload / 负载 | Before median ms / 修改前 | After median ms / 修改后 | Time reduction / 耗时下降 | +| --- | ---: | ---: | ---: | +| Integer loop ×10 million / 整数空循环一千万次 | 1967.352 | 1547.222 | +21.36% | +| Property reads ×20 million / 属性读取负载两千万次 | 7041.108 | 6522.871 | +7.36% | +| Fixed Richards ×10 / 固定 Richards 十次 | 1496.602 | 1506.159 | -0.64% | +| Original Richards harness / 原始 Richards harness | 6243.921 | 6295.843 | -0.83% | + +Sample ranges / 样本范围: + +- Integer loop ×10 million / 整数空循环一千万次: 1923.923–1985.769 ms → 1500.397–1723.616 ms. +- Property reads ×20 million / 属性读取负载两千万次: 6926.040–7081.772 ms → 6446.743–6726.171 ms. +- Fixed Richards ×10 / 固定 Richards 十次: 1494.998–1517.357 ms → 1492.278–1511.041 ms. +- Original Richards harness / 原始 Richards harness: 6174.643–6307.535 ms → 6233.639–6304.144 ms. + +整数循环与属性读取负载的前后范围不重叠。属性读取负载也包含循环及累加运算,不能把其收益全部归因于属性查找。首轮 Richards 中位耗时略升且范围重叠,因此另外安排十轮定向复测;没有删除或替换首轮样本。 + +Integer-loop and property-workload ranges do not overlap. The property workload includes loop and accumulation work, so its gain is not an isolated property-lookup improvement. Initial Richards medians were slightly slower with overlapping ranges, motivating a separate ten-pair confirmation; the original samples were not removed or replaced. + +## Richards confirmation / Richards 定向复测 + +所有验证完成后,使用哈希相同的两份二进制,不重新构建。每版本/负载十次独立进程,交替顺序且首轮先运行修改后版本。另 40 次输出检查全部通过。 + +After validation, reuse the exact same binaries without rebuilding. Ten processes per version/workload, alternating order starting with the after version. All 40 additional outputs passed. + +| Workload / 负载 | Before median ms / 修改前 | After median ms / 修改后 | Time reduction / 耗时下降 | +| --- | ---: | ---: | ---: | +| Fixed Richards ×10 / 固定 Richards 十次 | 1498.446 | 1494.523 | +0.26% | +| Original Richards harness / 原始 Richards harness | 6224.281 | 6230.859 | -0.11% | + +- Fixed Richards ×10 / 固定 Richards 十次: 1486.070–1518.298 ms → 1489.392–1515.162 ms. +- Original Richards harness / 原始 Richards harness: 6175.498–6431.029 ms → 6174.804–6299.447 ms. + +Original harness median scores / 原始 harness 中位分数:Initial / 首轮: **23.80 → 23.60**;Confirmation / 复测: **23.90 → 23.85**. Scores are separate from whole-process times. / 分数与完整进程耗时是不同指标。 + +本轮没有建立稳定的 Richards 加速结论;报告保留小幅回退的可能性,不将整数循环收益推广到整个引擎。 + +This round does not establish a stable Richards speedup; a small regression remains possible. Integer-loop gains are not a whole-engine speedup claim. + +## Implementation / 实现 + +- `src/engine/vm/numeric.rs`: shared inline `to_primitive` returns all primitive kinds unchanged and delegates only objects; existing `to_numeric` uses it. / 共享入口按原始值与对象的语义边界分流,已有数值准备复用它。 +- `src/engine/vm/numeric_execution.rs`: replace eight direct host calls across numeric preparation and unary/add/comparison paths in total (one in `numeric.rs`, seven here). Arithmetic branches, hints, conversion order and exception handling remain intact. / 共替换八处调用(此文件七处),不复制计算逻辑。 +- Preserve `Int/Float` tags for unary/update operations rather than routing all operations through `NumericValue::Number(f64)`. Runtime retains its general ToPrimitive implementation for other callers. / 不把所有操作强制统一成 f64,避免改变现有标签行为;Runtime 转换仍服务其他调用者。 +- Detached-host call tracking is test-only. Production adds no counter, cache or persistent allocation. / host 调用记录只用于测试,生产代码不增加计数器、缓存或持久分配。 + +## Validation / 验证 + +- **3002 Rust tests passed**, 0 failed, one existing ignored, across 13 binaries (Rust 1.88, workspace/all-targets/profiling). / 3002 项通过,零失败,一项原有忽略。 +- New regressions cover all primitive kinds, integral Float tags, negative zero and NaN bits, host bypass, object hint/order/abrupt completion, ordinary conversion fallback, numeric boundaries, strings and BigInt. Existing numeric tag/boundary tests also pass. / 新增测试覆盖原始值表示、host 绕过、对象可观察行为及混合类型语义。 +- Formatting, source layout (419 Rust files / 65 READMEs), Rust-only and strict production Clippy pass for profiling/default/test262-host. / 格式、布局、Rust-only 与三种配置的生产目标 Clippy 通过。 +- Binary-object production boundary passed; **all 690 isolation canaries rejected**. / 字节码生产边界检查通过,全部隔离 canary 按预期拒绝。 +- Full Test262 ran separately on both revisions: **102037 outcome rows identical**, zero changed outcomes/new failures. Both retain 79982 passes, 7 parse failures, 43 runtime failures, 3530 unsupported and 18475 skipped. Only the first fingerprint header line differs. Both frozen full-file gates exit 1; no baseline was updated. / 前后完整结果逐行一致;冻结文件校验仍因源码指纹头变化失败,未更新基线。 + +## Reproduction and evidence / 复现与证据 + +Before / 修改前: `eb48e7163423b69379a2e98a8d6c7aff812b1fbf`. After / 修改后: `6e59179e042b7ad93659136b9ba1af99f8aa9cdb`. Later documentation commits do not change measured runtime code. / 后续文档提交不改变所测运行时代码。 + +Heavy work ran serially through Herdr on eric-83am / PocketLab (AMD Ryzen 7 7840HS, Linux 6.18.44-1-lts). Release Rust 1.94.1, profiling compiled but inactive, no forced frame pointers or diagnostic patch. / 重型工作均在远端串行执行,诊断关闭。 + +```sh +CARGO_PROFILE_RELEASE_DEBUG=1 CARGO_PROFILE_RELEASE_STRIP=none \ +CARGO_TARGET_DIR=target/cpu-profile \ +QUICKJS_OXIDE_BUILD_COMMIT=$(git rev-parse HEAD) \ +cargo build --locked --release -p quickjs-oxide-cli \ + --no-default-features --features profiling +``` + +Workloads reused unchanged from `ahaoboy/js-engine-benchmark@2034d98fc8c5f8044e186267593f5d5ea5232caf`. Python `time.perf_counter_ns()` measures complete subprocesses with a 90-second timeout. CPU frequency and system isolation are not controlled. No new CPU sampling, phase timing or memory probe was performed. / 使用未改动的固定工作负载,完整进程计时;未锁定频率或完全隔离系统,本轮不重采 CPU 栈或阶段/内存探针。 + +[Machine-readable evidence / 机器可读证据](primitive-coercion-fast-path.json) contains all 80 samples, output validation data, commands, hashes, test receipts and environment metadata. Outcome-row SHA-256: `fa99d3349bb4b61f30ba57d7c7f275df64f19691edefc50b96b53887339fa8c3`. + +Raw archive / 原始包: `quickjs-oxide-primitive-coercion-pocketlab-2026-09-09.tar.gz`, **22471956 bytes**, SHA-256 **`d7c8d752afe2b97c78726985cfae61a9f2f9fda7cf0d26940436d56f96ae5b2e`**. All 199 manifest members verified remotely; downloaded archive hash matches. Includes both exact executables, all measurements, validation logs, complete Test262 vectors, scripts and source diff. Retained in local/remote workspace `target/`, not attached to GitHub; external benchmark source is excluded. / 所有清单成员逐一验证,本地下载哈希一致;原始包保留于工作区,不作为 GitHub 附件,不包含外部 benchmark 源码。 diff --git a/src/engine/vm/README.md b/src/engine/vm/README.md index 212e33b5..d7518d23 100644 --- a/src/engine/vm/README.md +++ b/src/engine/vm/README.md @@ -23,7 +23,8 @@ - [host_bridge.rs](host_bridge.rs):Bytecode VM adapter and per-frame binding state.。 - [mod.rs](mod.rs):模块入口、共享接口与子模块声明。 - [native_stack.rs](native_stack.rs):Deterministic native-call stack budgeting.。 -- [numeric.rs](numeric.rs):numeric 的类型和操作实现。 +- [numeric.rs](numeric.rs):共享原始值/数值转换入口、数值类型及比较辅助函数。 +- [numeric_coercion_tests.rs](numeric_coercion_tests.rs):原始值直通及对象转换顺序回归测试。 - [numeric_execution.rs](numeric_execution.rs):数值指令执行。 - [protocol.rs](protocol.rs):执行请求、宿主协议和 VM 入口。 - [tests.rs](tests.rs):模块回归测试。 diff --git a/src/engine/vm/detached.rs b/src/engine/vm/detached.rs index c41a5010..8235d77f 100644 --- a/src/engine/vm/detached.rs +++ b/src/engine/vm/detached.rs @@ -115,6 +115,7 @@ pub(in crate::engine::vm) struct DetachedHost<'a, T> { pub(in crate::engine::vm) box_primitive_results: VecDeque>, #[cfg(test)] pub(in crate::engine::vm) box_primitive_inputs: Vec, + pub(in crate::engine::vm) to_primitive_inputs: Vec<(Value, ToPrimitiveHint)>, #[cfg(test)] pub(in crate::engine::vm) call_results: VecDeque>, #[cfg(test)] @@ -217,6 +218,7 @@ impl<'a, T> DetachedHost<'a, T> { box_primitive_results: VecDeque::new(), #[cfg(test)] box_primitive_inputs: Vec::new(), + to_primitive_inputs: Vec::new(), #[cfg(test)] call_results: VecDeque::new(), #[cfg(test)] @@ -419,7 +421,8 @@ impl> VmHost for DetachedHost<'_, T> { )) } - fn to_primitive(&mut self, value: Value, _hint: ToPrimitiveHint) -> Result { + fn to_primitive(&mut self, value: Value, hint: ToPrimitiveHint) -> Result { + self.to_primitive_inputs.push((value.clone(), hint)); if matches!(value, Value::Object(_)) { Err(Error::internal( "detached VM cannot execute object ToPrimitive", diff --git a/src/engine/vm/mod.rs b/src/engine/vm/mod.rs index 273b6680..dac7d8fb 100644 --- a/src/engine/vm/mod.rs +++ b/src/engine/vm/mod.rs @@ -17,6 +17,8 @@ use num_traits::FromPrimitive; #[cfg(test)] use std::collections::VecDeque; +#[cfg(test)] +mod numeric_coercion_tests; #[cfg(test)] mod tests; diff --git a/src/engine/vm/numeric.rs b/src/engine/vm/numeric.rs index 4fc0dc4a..be0c440a 100644 --- a/src/engine/vm/numeric.rs +++ b/src/engine/vm/numeric.rs @@ -80,11 +80,25 @@ pub(in crate::engine::vm) fn abstract_equal( } } +/// Apply ToPrimitive at the VM boundary. Primitive operands keep their exact +/// representation and need no host services; only objects can execute user code. +#[inline] +pub(in crate::engine::vm) fn to_primitive( + host: &mut impl VmHost, + value: Value, + hint: ToPrimitiveHint, +) -> Result { + match value { + Value::Object(_) => host.to_primitive(value, hint), + primitive => Ok(Completion::Return(primitive)), + } +} + pub(in crate::engine::vm) fn to_numeric( host: &mut impl VmHost, value: Value, ) -> Result, Error> { - match host.to_primitive(value, ToPrimitiveHint::Number)? { + match to_primitive(host, value, ToPrimitiveHint::Number)? { Completion::Return(value) => Ok(OperationOutcome::Value(to_numeric_primitive(value)?)), Completion::Throw(value) => Ok(OperationOutcome::Throw(value)), } diff --git a/src/engine/vm/numeric_coercion_tests.rs b/src/engine/vm/numeric_coercion_tests.rs new file mode 100644 index 00000000..7d135ce1 --- /dev/null +++ b/src/engine/vm/numeric_coercion_tests.rs @@ -0,0 +1,154 @@ +use super::*; +use crate::engine::api::Runtime; + +#[test] +fn primitive_preparation_bypasses_host_and_preserves_representation() { + let runtime = Runtime::new(); + let function = DetachedBytecode:: { + code: vec![], + constants: vec![], + local_count: 0, + max_stack: 0, + }; + let mut host = DetachedHost::new(&function); + let values = [ + Value::Undefined, + Value::Null, + Value::Bool(true), + Value::Int(i32::MIN), + Value::Float(42.0), + Value::Float(-0.0), + Value::Float(f64::from_bits(0x7ff8_0000_0000_0042)), + Value::Float(f64::INFINITY), + Value::BigInt(JsBigInt::from(42_i32)), + Value::String(JsString::try_from_utf16(vec![0xd800]).unwrap()), + Value::Symbol(runtime.new_symbol(None).unwrap()), + ]; + for hint in [ + ToPrimitiveHint::Default, + ToPrimitiveHint::Number, + ToPrimitiveHint::String, + ] { + for input in &values { + let Completion::Return(output) = to_primitive(&mut host, input.clone(), hint).unwrap() + else { + panic!("primitive conversion threw"); + }; + match (input, output) { + (Value::Float(expected), Value::Float(actual)) => { + assert_eq!(actual.to_bits(), expected.to_bits()); + } + (expected, actual) => assert_eq!(&actual, expected), + } + } + } + assert!(host.to_primitive_inputs.is_empty()); + + let object = Value::Object(runtime.new_object(None).unwrap()); + for hint in [ToPrimitiveHint::Default, ToPrimitiveHint::Number] { + assert!(to_primitive(&mut host, object.clone(), hint).is_err()); + assert_eq!( + host.to_primitive_inputs.last(), + Some(&(object.clone(), hint)) + ); + } + assert_eq!(host.to_primitive_inputs.len(), 2); +} + +#[test] +fn numeric_instructions_use_primitive_preparation_without_host_calls() { + for (instruction, operands) in [ + (Instruction::Neg, vec![Value::Int(2)]), + (Instruction::Plus, vec![Value::Float(2.0)]), + (Instruction::Inc, vec![Value::Int(i32::MAX)]), + (Instruction::Dec, vec![Value::Float(-0.0)]), + (Instruction::PostInc, vec![Value::Int(2)]), + (Instruction::PostDec, vec![Value::Int(2)]), + (Instruction::BitNot, vec![Value::Int(2)]), + (Instruction::Add, vec![Value::Int(2), Value::Float(3.0)]), + (Instruction::Sub, vec![Value::Int(2), Value::Int(3)]), + (Instruction::Shr, vec![Value::Int(2), Value::Int(1)]), + (Instruction::Lt, vec![Value::Int(2), Value::Int(3)]), + ] { + let mut code: Vec<_> = (0..operands.len()) + .map(|index| Instruction::PushConst(u32::try_from(index).unwrap())) + .collect(); + code.extend([instruction, Instruction::Return]); + let function = DetachedBytecode { + code, + constants: operands, + local_count: 0, + max_stack: 2, + }; + let mut host = DetachedHost::new(&function); + assert!(matches!( + VmActivation::new(2).run(&function.code, &mut host).unwrap(), + VmExit::Complete(Completion::Return(_)) + )); + assert!(host.to_primitive_inputs.is_empty()); + } +} + +#[test] +fn numeric_object_conversion_preserves_hints_order_and_abrupt_completion() { + let runtime = Runtime::new(); + let mut context = runtime.new_context(); + assert_eq!(context.eval(r#" + (() => { + function check(expression, hint, expected) { + let events = []; + const left = { [Symbol.toPrimitive](h) { events.push('L:' + h); return 6; } }; + const right = { [Symbol.toPrimitive](h) { events.push('R:' + h); return 2; } }; + if (expression(left, right) !== expected || events.join(',') !== 'L:' + hint + ',R:' + hint) + throw new Error('conversion order or hint'); + events = []; + const marker = {}; + const throws = { [Symbol.toPrimitive](h) { events.push('L:' + h); throw marker; } }; + try { expression(throws, right); throw new Error('missing throw'); } + catch (e) { if (e !== marker) throw e; } + if (events.join(',') !== 'L:' + hint) throw new Error('right converted after left throw'); + } + check((a, b) => a + b, 'default', 8); + check((a, b) => a - b, 'number', 4); + check((a, b) => a >>> b, 'number', 1); + check((a, b) => a < b, 'number', false); + check((a, b) => a > b, 'number', true); + check((a, b) => a <= b, 'number', false); + check((a, b) => a >= b, 'number', true); + let hints = []; + const object = { [Symbol.toPrimitive](h) { hints.push(h); return '6'; } }; + if (+object !== 6 || -object !== -6 || ~object !== -7) return false; + let value = object; + if (value++ !== 6 || value !== 7) return false; + value = object; + if (--value !== 5 || hints.join(',') !== 'number,number,number,number,number') return false; + let ordinary = []; + if (({ valueOf() { ordinary.push('valueOf'); return {}; }, + toString() { ordinary.push('toString'); return '4'; } }) - 1 !== 3) return false; + return ordinary.join(',') === 'valueOf,toString'; + })() + "#).unwrap(), Value::Bool(true)); +} + +#[test] +fn numeric_preparation_keeps_string_bigint_and_number_semantics() { + let runtime = Runtime::new(); + let mut context = runtime.new_context(); + assert_eq!(context.eval(r#" + (() => { + if ('1' + 2 !== '12' || '10' < '2' !== true || 1n + 2n !== 3n) return false; + if ('6' - true !== 5 || null * 3 !== 0 || !Number.isNaN(undefined - 1)) return false; + if (2 + 0.5 !== 2.5 || !Object.is(-0 * 1, -0) || !Object.is(-0 + -0, -0)) return false; + if (NaN < 1 || NaN >= 1 || Infinity + 1 !== Infinity) return false; + let value = '6'; + if (value++ !== 6 || value !== 7) return false; + value = 2147483647; + if (++value !== 2147483648) return false; + for (const operation of [() => 1n + 2, () => +1n, () => Symbol() - 1, () => 1n >>> 1n]) { + try { operation(); return false; } + catch (e) { if (!(e instanceof TypeError)) throw e; } + } + return true; + })() + "#).unwrap(), Value::Bool(true)); +} diff --git a/src/engine/vm/numeric_execution.rs b/src/engine/vm/numeric_execution.rs index e71e1d88..4d3e250a 100644 --- a/src/engine/vm/numeric_execution.rs +++ b/src/engine/vm/numeric_execution.rs @@ -5,7 +5,7 @@ impl VmActivation { &mut self, host: &mut impl VmHost, ) -> Result, Error> { - let operand = match host.to_primitive(self.pop()?, ToPrimitiveHint::Number)? { + let operand = match to_primitive(host, self.pop()?, ToPrimitiveHint::Number)? { Completion::Return(value) => value, Completion::Throw(value) => return Ok(OperationOutcome::Throw(value)), }; @@ -25,7 +25,7 @@ impl VmActivation { &mut self, host: &mut impl VmHost, ) -> Result, Error> { - let operand = match host.to_primitive(self.pop()?, ToPrimitiveHint::Number)? { + let operand = match to_primitive(host, self.pop()?, ToPrimitiveHint::Number)? { Completion::Return(value) => value, Completion::Throw(value) => return Ok(OperationOutcome::Throw(value)), }; @@ -50,7 +50,7 @@ impl VmActivation { increment: bool, postfix: bool, ) -> Result, Error> { - let operand = match host.to_primitive(self.pop()?, ToPrimitiveHint::Number)? { + let operand = match to_primitive(host, self.pop()?, ToPrimitiveHint::Number)? { Completion::Return(value) => value, Completion::Throw(value) => return Ok(OperationOutcome::Throw(value)), }; @@ -186,11 +186,11 @@ impl VmActivation { operation: impl FnOnce(std::cmp::Ordering) -> bool, ) -> Result, Error> { let (left, right) = self.pop_pair()?; - let left = match host.to_primitive(left, ToPrimitiveHint::Number)? { + let left = match to_primitive(host, left, ToPrimitiveHint::Number)? { Completion::Return(value) => value, Completion::Throw(value) => return Ok(OperationOutcome::Throw(value)), }; - let right = match host.to_primitive(right, ToPrimitiveHint::Number)? { + let right = match to_primitive(host, right, ToPrimitiveHint::Number)? { Completion::Return(value) => value, Completion::Throw(value) => return Ok(OperationOutcome::Throw(value)), }; @@ -234,11 +234,11 @@ impl VmActivation { host: &mut impl VmHost, ) -> Result, Error> { let (left, right) = self.pop_pair()?; - let left = match host.to_primitive(left, ToPrimitiveHint::Default)? { + let left = match to_primitive(host, left, ToPrimitiveHint::Default)? { Completion::Return(value) => value, Completion::Throw(value) => return Ok(OperationOutcome::Throw(value)), }; - let right = match host.to_primitive(right, ToPrimitiveHint::Default)? { + let right = match to_primitive(host, right, ToPrimitiveHint::Default)? { Completion::Return(value) => value, Completion::Throw(value) => return Ok(OperationOutcome::Throw(value)), };