///|
pub enum FuzzOutputMode {
Text
Jsonl
} derive(Eq, Debug)
///|
pub struct FuzzCliArgs {
suite : String
profile : String
seed : UInt64
seed_i64 : Int64
output_mode : FuzzOutputMode
seed_count : Int32
shard_index : Int32
shard_count : Int32
report_json_path : String?
out_dir : String?
} derive(Eq, Debug)
///|
pub struct FuzzRecipe {
name : String
suite : String
profile : String
seed_i64 : Int64
output_mode : FuzzOutputMode
seed_count : Int32
shard_count : Int32
} derive(Eq, Debug)
///|
pub struct FuzzInterestingnessHashLayer {
name : String
digest : String
description : String
} derive(Eq, Debug)
///|
pub struct FuzzInterestingnessHashSchema {
schema_id : String
layers : Array[FuzzInterestingnessHashLayer]
} derive(Eq, Debug)
///|
pub struct FuzzInterestingnessCaseKey {
raw_hash : String
decoded_shape_hash : String
feature_fact_hash : String
normalized_hash : String
predicate_hash : String
reduced_artifact_hash : String
} derive(Eq, Debug)
///|
pub struct FuzzCorpusHashMetadata {
raw_artifact_hash : String
reduced_artifact_hash : String
predicate_hash : String
feature_fact_hash : String
interestingness_label : String
} derive(Eq, Debug)
///|
pub struct FuzzCorpusPromotionMetadata {
id : String
state : String
source : String
input : String
created_at : String
generator : String
features : Array[String]
expectation : String
classification : String
replay : String
artifacts : Array[String]
owner_or_task : String
notes : String
raw_artifact_hash : String
reduced_artifact_hash : String
normalized_shape_hash : String
predicate_hash : String
feature_fact_hash : String
interestingness_label : String
} derive(Eq, Debug)
///|
pub struct FuzzCorpusReplayCommandMetadata {
case_id : String
suite : String
profile : String
seed : UInt64
seed_index : Int32
strategy_or_artifact : String
expected_outcome : String
classification : String
command : String
} derive(Eq, Debug)
///|
pub struct FuzzCorpusReplayAllPlan {
planned_count : Int32
skipped_count : Int32
malformed_count : Int32
commands : Array[FuzzCorpusReplayCommandMetadata]
} derive(Eq, Debug)
///|
pub struct FuzzCorpusCaseIndexEntry {
hash_kind : String
hash : String
case_ids : Array[String]
seeds : Array[UInt64]
profiles : Array[String]
artifact_paths : Array[String]
} derive(Eq, Debug)
///|
pub struct FuzzCorpusCaseIndex {
entries : Array[FuzzCorpusCaseIndexEntry]
} derive(Eq, Debug)
///|
pub struct FuzzCorpusDedupDryRunDecision {
action : String
hash_kind : String
hash : String
case_ids : Array[String]
artifact_paths : Array[String]
reason : String
} derive(Eq, Debug)
///|
pub struct FuzzCorpusDedupDryRunPlan {
duplicate_count : Int32
compress_count : Int32
keep_count : Int32
decisions : Array[FuzzCorpusDedupDryRunDecision]
} derive(Eq, Debug)
///|
pub fn FuzzInterestingnessCaseKey::new(
raw_hash : String,
decoded_shape_hash : String,
feature_fact_hash : String,
normalized_hash : String,
predicate_hash : String,
reduced_artifact_hash : String,
) -> FuzzInterestingnessCaseKey {
{
raw_hash,
decoded_shape_hash,
feature_fact_hash,
normalized_hash,
predicate_hash,
reduced_artifact_hash,
}
}
///|
fn fuzz_fnv1a64_hex_digit(value : UInt64) -> Char {
let nibble = value.to_int()
if nibble < 10 {
(48 + nibble).unsafe_to_char()
} else {
(87 + nibble).unsafe_to_char()
}
}
///|
fn fuzz_fnv1a64_hex(hash : UInt64) -> String {
let out = StringBuilder::new()
let mut shift = 60
while shift >= 0 {
out.write_char(fuzz_fnv1a64_hex_digit((hash >> shift) & 0xFUL))
shift -= 4
}
out.to_string()
}
///|
pub fn fuzz_corpus_hash_bytes(bytes : Bytes) -> String {
let mut hash = 1469598103934665603UL
let prime = 1099511628211UL
let mut i = 0
while i < bytes.length() {
hash = (hash ^ bytes[i].to_uint64()) * prime
i += 1
}
"fnv1a64-\{fuzz_fnv1a64_hex(hash)}"
}
///|
pub fn fuzz_corpus_hash_text(text : String) -> String {
fuzz_corpus_hash_bytes(fuzz_string_to_bytes(text))
}
///|
fn fuzz_join_sorted_feature_facts(feature_facts : Array[String]) -> String {
let sorted : Array[String] = []
for fact in feature_facts {
sorted.push(fact)
}
sorted.sort()
let out = StringBuilder::new()
for i in 0.. 0 {
out.write_char('\n')
}
out.write_string(sorted[i])
}
out.to_string()
}
///|
pub fn build_fuzz_corpus_hash_metadata(
raw_artifact : Bytes,
reduced_artifact~ : Bytes? = None,
predicate : String,
feature_facts : Array[String],
interestingness_label : String,
) -> FuzzCorpusHashMetadata {
let reduced_bytes = match reduced_artifact {
Some(bytes) => bytes
None => raw_artifact
}
FuzzCorpusHashMetadata::new(
fuzz_corpus_hash_bytes(raw_artifact),
fuzz_corpus_hash_bytes(reduced_bytes),
fuzz_corpus_hash_text(predicate),
fuzz_corpus_hash_text(fuzz_join_sorted_feature_facts(feature_facts)),
interestingness_label,
)
}
///|
pub fn FuzzCorpusPromotionMetadata::new(
id : String,
state : String,
source : String,
input : String,
created_at : String,
generator : String,
features : Array[String],
expectation : String,
classification : String,
replay : String,
artifacts : Array[String],
owner_or_task : String,
notes : String,
raw_artifact_hash~ : String = "",
reduced_artifact_hash~ : String = "",
normalized_shape_hash~ : String = "",
predicate_hash~ : String = "",
feature_fact_hash~ : String = "",
interestingness_label~ : String = "unclassified",
) -> FuzzCorpusPromotionMetadata {
{
id,
state,
source,
input,
created_at,
generator,
features,
expectation,
classification,
replay,
artifacts,
owner_or_task,
notes,
raw_artifact_hash,
reduced_artifact_hash,
normalized_shape_hash,
predicate_hash,
feature_fact_hash,
interestingness_label,
}
}
///|
pub fn FuzzCorpusHashMetadata::new(
raw_artifact_hash : String,
reduced_artifact_hash : String,
predicate_hash : String,
feature_fact_hash : String,
interestingness_label : String,
) -> FuzzCorpusHashMetadata {
{
raw_artifact_hash,
reduced_artifact_hash,
predicate_hash,
feature_fact_hash,
interestingness_label,
}
}
///|
pub fn FuzzCorpusReplayCommandMetadata::new(
case_id : String,
suite : String,
profile : String,
seed : UInt64,
seed_index : Int32,
strategy_or_artifact : String,
expected_outcome : String,
classification : String,
command : String,
) -> FuzzCorpusReplayCommandMetadata {
{
case_id,
suite,
profile,
seed,
seed_index,
strategy_or_artifact,
expected_outcome,
classification,
command,
}
}
///|
pub fn FuzzCorpusCaseIndexEntry::new(
hash_kind : String,
hash : String,
case_ids : Array[String],
seeds : Array[UInt64],
profiles : Array[String],
artifact_paths : Array[String],
) -> FuzzCorpusCaseIndexEntry {
{ hash_kind, hash, case_ids, seeds, profiles, artifact_paths, }
}
///|
pub fn FuzzCorpusCaseIndex::new(
entries : Array[FuzzCorpusCaseIndexEntry],
) -> FuzzCorpusCaseIndex {
{ entries, }
}
///|
pub fn FuzzCorpusDedupDryRunDecision::new(
action : String,
hash_kind : String,
hash : String,
case_ids : Array[String],
artifact_paths : Array[String],
reason : String,
) -> FuzzCorpusDedupDryRunDecision {
{ action, hash_kind, hash, case_ids, artifact_paths, reason, }
}
///|
pub fn FuzzCorpusDedupDryRunPlan::new(
duplicate_count : Int32,
compress_count : Int32,
keep_count : Int32,
decisions : Array[FuzzCorpusDedupDryRunDecision],
) -> FuzzCorpusDedupDryRunPlan {
{ duplicate_count, compress_count, keep_count, decisions, }
}
///|
pub fn fuzz_interestingness_hash_schema() -> FuzzInterestingnessHashSchema {
{
schema_id: "starshine.fuzz-interestingness.v1",
layers: [
{
name: "raw",
digest: "fnv1a64-hex",
description: "Hash of original specimen bytes before decoding, normalization, or reduction.",
},
{
name: "decoded-shape",
digest: "fnv1a64-hex",
description: "Hash of decoded module/script shape: section and instruction families without preserving incidental payload bytes.",
},
{
name: "feature-fact",
digest: "fnv1a64-hex",
description: "Hash of sorted feature facts such as GC, exception, memory64, SIMD, string, import, export, and proposal markers.",
},
{
name: "normalized",
digest: "fnv1a64-hex",
description: "Hash of canonical normalized comparison text or bytes after accepted representation-only cleanup.",
},
{
name: "predicate",
digest: "fnv1a64-hex",
description: "Hash of the oracle outcome, interestingness predicate, failure class, pass name, and relevant tool verdicts.",
},
{
name: "reduced-artifact",
digest: "fnv1a64-hex",
description: "Hash of the minimized or otherwise reduced artifact that still satisfies the predicate.",
},
],
}
}
///|
pub fn FuzzRecipe::new(
name : String,
suite : String,
profile : String,
seed_i64 : Int64,
output_mode~ : FuzzOutputMode = FuzzOutputMode::Text,
seed_count~ : Int32 = 1,
shard_count~ : Int32 = 1,
) -> FuzzRecipe {
{ name, suite, profile, seed_i64, output_mode, seed_count, shard_count, }
}
///|
pub struct FuzzShardWorkItem {
name : String
suite : String
profile : String
seed_i64 : Int64
output_mode : FuzzOutputMode
seed_count : Int32
shard_index : Int32
shard_count : Int32
out_dir : String?
} derive(Eq, Debug)
///|
pub fn build_fuzz_shard_queue(
recipe : FuzzRecipe,
out_dir_root : String?,
) -> Result[Array[FuzzShardWorkItem], String] {
if recipe.seed_count <= 0 {
return Err("seed-count must be greater than zero")
}
if recipe.shard_count <= 0 {
return Err("shard-count must be greater than zero")
}
let out : Array[FuzzShardWorkItem] = []
let total_label = fuzz_zero_pad_decimal(recipe.shard_count, 3)
let mut shard_index = 0
while shard_index < recipe.shard_count {
let shard_label = fuzz_zero_pad_decimal(shard_index, 3)
let shard_name = "\{recipe.name}-shard-\{shard_label}-of-\{total_label}"
let shard_out_dir = match out_dir_root {
Some(root) => Some("\{root}/shard-\{shard_label}-of-\{total_label}")
None => None
}
out.push({
name: shard_name,
suite: recipe.suite,
profile: recipe.profile,
seed_i64: recipe.seed_i64,
output_mode: recipe.output_mode,
seed_count: recipe.seed_count,
shard_index,
shard_count: recipe.shard_count,
out_dir: shard_out_dir,
})
shard_index += 1
}
Ok(out)
}
///|
fn fuzz_output_manifest_is_complete(manifest_json : String) -> Bool {
manifest_json.contains("starshine.fuzz-output.v1") &&
manifest_json.contains("result.json") &&
manifest_json.contains("cases.jsonl")
}
///|
pub fn build_fuzz_resume_shard_queue(
recipe : FuzzRecipe,
out_dir_root : String?,
completed_manifests : Array[String],
) -> Result[Array[FuzzShardWorkItem], String] {
let full_queue = match build_fuzz_shard_queue(recipe, out_dir_root) {
Ok(v) => v
Err(e) => return Err(e)
}
let out : Array[FuzzShardWorkItem] = []
let mut i = 0
while i < full_queue.length() {
let completed = i < completed_manifests.length() &&
fuzz_output_manifest_is_complete(completed_manifests[i])
if !completed {
out.push(full_queue[i])
}
i += 1
}
Ok(out)
}
///|
pub fn fuzz_recipe_schema_version() -> String {
"starshine.fuzz.recipe.v1"
}
///|
pub fn fuzz_standard_recipe_text_by_name(name : String) -> String? {
let schema_line = "schema=\{fuzz_recipe_schema_version()}\n"
match fuzz_ascii_lower(name) {
"smoke" =>
Some(
schema_line +
"name=smoke\nsuite=validate-valid\nprofile=smoke\nseed=0x5eed\nseed-count=1\nshard-count=1\noutput=text\n",
)
"default-smoke" =>
Some(
schema_line +
"name=default-smoke\nsuite=all\nprofile=smoke\nseed=0x5eed\nseed-count=1\nshard-count=1\noutput=text\n",
)
"ci" =>
Some(
schema_line +
"name=ci\nsuite=cmd-harness\nprofile=ci+gen-valid=natural-small+passes=common-clusters\nseed=0x5eed\nseed-count=4\nshard-count=1\noutput=jsonl\n",
)
"default-ci" =>
Some(
schema_line +
"name=default-ci\nsuite=all\nprofile=ci\nseed=0x5eed\nseed-count=4\nshard-count=1\noutput=jsonl\n",
)
"nightly" =>
Some(
schema_line +
"name=nightly\nsuite=validate-valid-metamorphic\nprofile=stress\nseed=0x1eed\nseed-count=32\nshard-count=8\noutput=jsonl\n",
)
"optimizer-ci" =>
Some(
schema_line +
"name=optimizer-ci\nsuite=cmd-harness\nprofile=ci+gen-valid=pass-fuzz-stress+passes=random-short+determinism+codec-idempotence\nseed=0x5eed\nseed-count=8\nshard-count=4\noutput=jsonl\n",
)
"optimizer-idempotence-ci" =>
Some(
schema_line +
"name=optimizer-idempotence-ci\nsuite=cmd-harness\nprofile=ci+gen-valid=pass-fuzz-stress+passes=fixed-point-cleanup+opt-idempotence+codec-idempotence\nseed=0x5eed\nseed-count=8\nshard-count=4\noutput=jsonl\n",
)
"optimizer-stress" =>
Some(
schema_line +
"name=optimizer-stress\nsuite=cmd-harness\nprofile=stress+gen-valid=random-all-profiles+passes=random-mixed+determinism+codec-idempotence\nseed=0x5eed\nseed-count=64\nshard-count=8\noutput=jsonl\n",
)
"optimizer-idempotence-stress" =>
Some(
schema_line +
"name=optimizer-idempotence-stress\nsuite=cmd-harness\nprofile=stress+gen-valid=random-all-profiles+passes=fixed-point-cleanup+opt-idempotence+codec-idempotence\nseed=0x5eed\nseed-count=64\nshard-count=8\noutput=jsonl\n",
)
"pass-signoff" =>
Some(
schema_line +
"name=pass-signoff\nsuite=cmd-harness\nprofile=ci+gen-valid=binaryen-oracle-portable+passes=each-pass+check-idempotence\nseed=0x5eed\nseed-count=8\nshard-count=4\noutput=jsonl\n",
)
"validator-stress" =>
Some(
schema_line +
"name=validator-stress\nsuite=validate-valid\nprofile=stress\nseed=0x10d69\nseed-count=16\nshard-count=4\noutput=jsonl\n",
)
"parser-stress" =>
Some(
schema_line +
"name=parser-stress\nsuite=wast-roundtrip\nprofile=stress+parser-stress+scripts\nseed=0x5eed\nseed-count=16\nshard-count=4\noutput=jsonl\n",
)
"binaryen-oracle" =>
Some(
schema_line +
"name=binaryen-oracle\nsuite=gen-valid-wat-validate-roundtrip\nprofile=binaryen-oracle-portable\nseed=0x5eed\nseed-count=8\nshard-count=4\noutput=jsonl\n",
)
"text-differential-smoke" =>
Some(
schema_line +
"name=text-differential-smoke\nsuite=text-differential\nprofile=smoke\nseed=0x1045a7\nseed-count=1\nshard-count=1\noutput=jsonl\n",
)
_ => None
}
}
///|
pub fn fuzz_standard_recipe_names() -> Array[String] {
[
"smoke", "default-smoke", "ci", "default-ci", "nightly", "optimizer-ci", "optimizer-idempotence-ci",
"optimizer-stress", "optimizer-idempotence-stress", "pass-signoff", "validator-stress",
"parser-stress", "binaryen-oracle", "text-differential-smoke",
]
}
///|
pub fn fuzz_standard_recipe_by_name(name : String) -> FuzzRecipe? {
match fuzz_standard_recipe_text_by_name(name) {
Some(text) =>
match parse_fuzz_recipe_text(text) {
Ok(recipe) => Some(recipe)
Err(_) => None
}
None => None
}
}
///|
pub struct EmitGenValidBatchArgs {
count : Int32
seed : UInt64
out_dir : String
profile : String
required_features : Array[ValidateValidFeatureFloor]
excluded_features : Array[ValidateValidFeatureKey]
max_attempts : Int32
manifest_path : String?
metamorphic_transform_ids : Array[String]
} derive(Eq, Debug)
///|
pub fn EmitGenValidBatchArgs::new(
count : Int32,
seed : UInt64,
out_dir : String,
profile~ : String = "binaryen-oracle-portable",
required_features~ : Array[ValidateValidFeatureFloor] = [],
excluded_features~ : Array[ValidateValidFeatureKey] = [],
max_attempts~ : Int32 = count,
manifest_path~ : String? = None,
metamorphic_transform_ids~ : Array[String] = [],
) -> EmitGenValidBatchArgs {
{
count,
seed,
out_dir,
profile,
required_features,
excluded_features,
max_attempts,
manifest_path,
metamorphic_transform_ids,
}
}
///|
pub struct EmitInvalidReproArgs {
suite : String
strategy_id : String
seed : UInt64
attempt : Int32
profile : String
out_dir : String
} derive(Eq, Debug)
///|
pub fn EmitInvalidReproArgs::new(
suite : String,
strategy_id : String,
seed : UInt64,
attempt : Int32,
out_dir : String,
profile~ : String = "smoke",
) -> EmitInvalidReproArgs {
{ suite, strategy_id, seed, attempt, profile, out_dir, }
}
///|
pub enum FuzzCliCommand {
Run(FuzzCliArgs)
Help
ListSuites
ListProfiles
ListRecipes
EmitGenValidBatch(EmitGenValidBatchArgs)
EmitInvalidRepro(EmitInvalidReproArgs)
ReplayCorpus(String)
ResolveRandomPassProfile(String, UInt64)
GoldenSeedSmoke
} derive(Eq, Debug)
///|
pub struct FuzzRunOutputIO {
ensure_dir : (String) -> Result[Unit, String]
write_file : (String, Bytes) -> Result[Unit, String]
}
///|
pub fn FuzzRunOutputIO::new(
ensure_dir~ : (String) -> Result[Unit, String] = fn(path) {
Err("ensure_dir not configured: \{path}")
},
write_file~ : (String, Bytes) -> Result[Unit, String] = fn(path, _) {
Err("write_file not configured: \{path}")
},
) -> FuzzRunOutputIO {
{ ensure_dir, write_file, }
}
///|
pub struct FuzzSuiteResult {
suite : String
profile : String
seed : Int64
attempts : Int32
elapsed_ms : UInt64
seed_index : Int32
seed_count : Int32
shard_index : Int32
shard_count : Int32
total_run_budget_ms : UInt64?
total_run_budget_exceeded : Bool
run_budget_classification : String
details_json : String?
} derive(Eq, Debug)
///|
pub struct FuzzGoldenSeedSmokeCase {
suite : String
profile : String
seed : Int64
seed_index : Int32
attempts : Int32
details_required : Bool
details_json : String?
catalog_entry_id : String?
expected_counter_count : Int32
} derive(Eq, Debug)
///|
pub fn FuzzGoldenSeedSmokeCase::new(
suite : String,
profile : String,
seed : Int64,
seed_index : Int32,
attempts : Int32,
details_required : Bool,
details_json~ : String? = None,
catalog_entry_id~ : String? = None,
expected_counter_count~ : Int32 = 0,
) -> FuzzGoldenSeedSmokeCase {
{
suite,
profile,
seed,
seed_index,
attempts,
details_required,
details_json,
catalog_entry_id,
expected_counter_count,
}
}
///|
pub fn FuzzGoldenSeedSmokeCase::details_json_contains(
self : FuzzGoldenSeedSmokeCase,
needle : String,
) -> Bool {
match self.details_json {
Some(details) => details.contains(needle)
None => false
}
}
///|
pub struct FuzzGoldenSeedSmokeReport {
schema_id : String
recipe_name : String
seed : Int64
cases : Array[FuzzGoldenSeedSmokeCase]
} derive(Eq, Debug)
///|
pub fn FuzzGoldenSeedSmokeReport::new(
schema_id : String,
recipe_name : String,
seed : Int64,
cases : Array[FuzzGoldenSeedSmokeCase],
) -> FuzzGoldenSeedSmokeReport {
{ schema_id, recipe_name, seed, cases, }
}
///|
pub struct FuzzReplayCommand {
suite : String
profile : String
seed : Int64
seed_index : Int32
} derive(Eq, Debug)
///|
fn fuzz_ascii_lower(input : String) -> String {
let out = StringBuilder::new()
for c in input {
if c >= 'A' && c <= 'Z' {
out.write_char((c.to_int() + 32).unsafe_to_char())
} else {
out.write_char(c)
}
}
out.to_string()
}
///|
pub fn fuzz_active_suite_names() -> Array[String] {
[
"wast-roundtrip", "wat-roundtrip", "wast-validate-roundtrip", "gen-valid-wat-validate-roundtrip",
"valid-multi-module-linking", "validate-valid", "validate-valid-metamorphic",
"validate-invalid-ast", "validate-invalid-binary", "validate-invalid-text", "validate-invalid-text-dynamic",
"validate-invalid-spec-seed", "binary-roundtrip", "cmd-harness",
]
}
///|
pub fn fuzz_reserved_suite_names() -> Array[String] {
[]
}
///|
pub fn fuzz_suite_inventory_lines() -> Array[String] {
let lines : Array[String] = []
for suite in fuzz_active_suite_names() {
lines.push("active\t\{suite}")
}
for suite in fuzz_reserved_suite_names() {
lines.push("reserved\t\{suite}")
}
lines
}
///|
fn fuzz_profile_names() -> Array[String] {
["smoke", "ci", "stress"]
}
///|
fn fuzz_join(values : Array[String], sep : String) -> String {
let out = StringBuilder::new()
let mut i = 0
while i < values.length() {
if i > 0 {
out.write_string(sep)
}
out.write_string(values[i])
i += 1
}
out.to_string()
}
///|
pub fn fuzz_usage_text() -> String {
let active_suites = fuzz_join(fuzz_active_suite_names(), ", ")
let reserved = fuzz_reserved_suite_names()
let reserved_suites = if reserved.is_empty() {
"(none)"
} else {
fuzz_join(reserved, ", ")
}
let profiles = fuzz_join(fuzz_profile_names(), ", ")
"usage: moon run src/fuzz -- [suite] [profile] [seed] [--recipe ] [--suite ] [--profile ] [--seed |--seed=] [--seed-count ] [--shard-index --shard-count ] [--report-json ] [--output text|jsonl|--jsonl]\ncommands: --help|-h | --list-suites | --list-profiles | --list-recipes | --golden-seed-smoke | --resolve-random-pass-profile --seed | --emit-gen-valid-batch --count --seed --out-dir [--manifest ] (portable coverage-forced batch) | --emit-invalid-repro --suite --strategy --seed --attempt --out-dir [--profile ] | --replay-corpus \nactive suites: \{active_suites}\nreserved suite ids: \{reserved_suites}\nprofiles: \{profiles}\nopt-in recipes: text-differential-smoke"
}
///|
pub struct WastValidateRoundtripFuzzStats {
profile : String
seed : UInt64
attempts : Int32
printed : Int32
print_failures : Int32
parsed : Int32
parse_failures : Int32
lowered : Int32
lower_failures : Int32
validated : Int32
validation_failures : Int32
} derive(Eq, Debug)
///|
pub struct GenValidWatValidateRoundtripFuzzStats {
profile : String
seed : UInt64
attempts : Int32
generated : Int32
printed : Int32
print_failures : Int32
parsed : Int32
parse_failures : Int32
lowered : Int32
lower_failures : Int32
validated : Int32
validation_failures : Int32
} derive(Eq, Debug)
///|
pub struct TextDifferentialFuzzStats {
profile : String
seed : UInt64
attempts : Int32
local_parse_ok : Int32
local_print_ok : Int32
local_reparse_lower_ok : Int32
local_lower_mismatch : Int32
adapter_unavailable : Int32
adapter_unavailable_classifications : Int32
accepted_classifications : Int32
external_adapters : Array[String]
} derive(Eq, Debug)
///|
pub struct ValidMultiModuleWastCase {
name : String
text : String
feature_facts : Array[String]
} derive(Eq, Debug)
///|
pub enum MultiModuleWastClassification {
ValidLinking
Unlinkable
DuplicateModuleName
DuplicateRegisterName
ParseFailure
SingleModuleValidationFailure
LinkFailure
LinkSkipped
} derive(Eq, Debug)
///|
pub struct ValidMultiModuleLinkingFuzzStats {
profile : String
seed : UInt64
attempts : Int32
generated : Int32
parsed_scripts : Int32
parse_failures : Int32
single_module_validated : Int32
single_module_validation_failures : Int32
link_passed : Int32
link_failed : Int32
link_skipped : Int32
} derive(Eq, Debug)
///|
fn gen_valid_multi_module_wast_case_text(suffix : String) -> String {
let provider_name = "provider_" + suffix
let consumer_name = "consumer_" + suffix
"(module $" +
provider_name +
"\n" +
" (func $f (export \"f\") (result i32) i32.const 7)\n" +
" (memory (export \"mem\") 1)\n" +
" (table (export \"tab\") 1 funcref)\n" +
" (global (export \"g\") i32 (i32.const 9)))\n" +
"(register \"provider\" $" +
provider_name +
")\n" +
"(module $" +
consumer_name +
"\n" +
" (import \"provider\" \"f\" (func $f (result i32)))\n" +
" (import \"provider\" \"mem\" (memory 1))\n" +
" (import \"provider\" \"tab\" (table 1 funcref))\n" +
" (import \"provider\" \"g\" (global i32))\n" +
" (func (export \"use-func\") (result i32) call $f)\n" +
" (func (export \"use-global\") (result i32) global.get 0))\n" +
"(register \"consumer\" $" +
consumer_name +
")\n"
}
///|
pub fn gen_valid_multi_module_wast_case(
seed : UInt64,
) -> ValidMultiModuleWastCase {
let suffix = fuzz_zero_pad_decimal((seed % 100000UL).to_int(), 5)
{
name: "valid-multi-module-linking-\{suffix}",
text: gen_valid_multi_module_wast_case_text(suffix),
feature_facts: [
"multi-module", "valid-linking", "register", "import:func", "import:memory",
"import:table", "import:global", "export:func", "export:memory", "export:table",
"export:global",
],
}
}
///|
pub fn gen_valid_multi_module_wast_cases(
seed : UInt64,
count : Int32,
) -> Array[ValidMultiModuleWastCase] {
let cases : Array[ValidMultiModuleWastCase] = []
if count <= 0 {
return cases
}
for i = 0; i < count; i = i + 1 {
cases.push(gen_valid_multi_module_wast_case(seed + i.to_uint64()))
}
cases
}
///|
fn gen_unlinkable_multi_module_provider_prefix(suffix : String) -> String {
let provider_name = "unlinkable_provider_" + suffix
"(module $" +
provider_name +
"\n" +
" (func (export \"f\") (result i32) i32.const 7)\n" +
" (memory (export \"mem\") 1)\n" +
" (table (export \"tab\") 1 funcref)\n" +
" (global (export \"g\") i32 (i32.const 9))\n" +
" (tag (export \"tag\") (param i32)))\n" +
"(register \"provider\" $" +
provider_name +
")\n"
}
///|
fn gen_unlinkable_multi_module_wast_case_body(
variant : Int32,
suffix : String,
) -> (String, String, Array[String]) {
let prefix = gen_unlinkable_multi_module_provider_prefix(suffix)
match variant {
0 =>
(
"missing-func",
prefix +
"(assert_unlinkable (module (import \"provider\" \"missing_f\" (func))) \"unknown import\")\n",
["import:func", "missing-import:func"],
)
1 =>
(
"missing-memory",
prefix +
"(assert_unlinkable (module (import \"provider\" \"missing_mem\" (memory 1))) \"unknown import\")\n",
["import:memory", "missing-import:memory"],
)
2 =>
(
"missing-table",
prefix +
"(assert_unlinkable (module (import \"provider\" \"missing_tab\" (table 1 funcref))) \"unknown import\")\n",
["import:table", "missing-import:table"],
)
3 =>
(
"missing-global",
prefix +
"(assert_unlinkable (module (import \"provider\" \"missing_g\" (global i32))) \"unknown import\")\n",
["import:global", "missing-import:global"],
)
4 =>
(
"missing-tag",
prefix +
"(assert_unlinkable (module (tag (import \"provider\" \"missing_tag\") (param i32))) \"unknown import\")\n",
["import:tag", "missing-import:tag"],
)
5 =>
(
"mismatch-func",
prefix +
"(assert_unlinkable (module (import \"provider\" \"f\" (func (param i32)))) \"incompatible import type\")\n",
["import:func", "type-mismatch:func"],
)
6 =>
(
"mismatch-memory",
prefix +
"(assert_unlinkable (module (import \"provider\" \"mem\" (memory 2 5))) \"incompatible import type\")\n",
["import:memory", "type-mismatch:memory"],
)
7 =>
(
"mismatch-table",
prefix +
"(assert_unlinkable (module (import \"provider\" \"tab\" (table 11 funcref))) \"incompatible import type\")\n",
["import:table", "type-mismatch:table"],
)
8 =>
(
"mismatch-global",
prefix +
"(assert_unlinkable (module (import \"provider\" \"g\" (global f64))) \"incompatible import type\")\n",
["import:global", "type-mismatch:global"],
)
_ =>
(
"mismatch-tag",
prefix +
"(assert_unlinkable (module (tag (import \"provider\" \"tag\") (param f32))) \"incompatible import type\")\n",
["import:tag", "type-mismatch:tag"],
)
}
}
///|
pub fn gen_unlinkable_multi_module_wast_case(
seed : UInt64,
) -> ValidMultiModuleWastCase {
let variant = (seed % 10UL).to_int()
let suffix = fuzz_zero_pad_decimal((seed % 100000UL).to_int(), 5)
let (variant_name, text, variant_facts) = gen_unlinkable_multi_module_wast_case_body(
variant, suffix,
)
let facts = [
"multi-module", "unlinkable", "assert_unlinkable", "valid-before-link", variant_name,
]
facts.append(variant_facts)
{
name: "unlinkable-multi-module-\{suffix}-\{variant_name}",
text,
feature_facts: facts,
}
}
///|
pub fn gen_unlinkable_multi_module_wast_cases(
seed : UInt64,
count : Int32,
) -> Array[ValidMultiModuleWastCase] {
let cases : Array[ValidMultiModuleWastCase] = []
if count <= 0 {
return cases
}
for i = 0; i < count; i = i + 1 {
cases.push(gen_unlinkable_multi_module_wast_case(seed + i.to_uint64()))
}
cases
}
///|
fn gen_duplicate_module_name_wast_case(
suffix : String,
) -> ValidMultiModuleWastCase {
let module_name = "dup_module_" + suffix
{
name: "duplicate-module-name-multi-module-\{suffix}",
text: "(module $" +
module_name +
" (func))\n" +
"(module $" +
module_name +
" (func))\n",
feature_facts: ["multi-module", "duplicate-module-name"],
}
}
///|
fn gen_duplicate_register_name_wast_case(
suffix : String,
) -> ValidMultiModuleWastCase {
let first_module_name = "dup_register_a_" + suffix
let second_module_name = "dup_register_b_" + suffix
{
name: "duplicate-register-name-multi-module-\{suffix}",
text: "(module $" +
first_module_name +
" (func))\n" +
"(register \"dupe\" $" +
first_module_name +
")\n" +
"(module $" +
second_module_name +
" (func))\n" +
"(register \"dupe\" $" +
second_module_name +
")\n",
feature_facts: ["multi-module", "duplicate-register-name", "register"],
}
}
///|
pub fn gen_duplicate_multi_module_wast_case(
seed : UInt64,
) -> ValidMultiModuleWastCase {
let suffix = fuzz_zero_pad_decimal((seed % 100000UL).to_int(), 5)
if seed % 2UL == 1UL {
gen_duplicate_module_name_wast_case(suffix)
} else {
gen_duplicate_register_name_wast_case(suffix)
}
}
///|
fn fuzz_string_array_contains(items : Array[String], needle : String) -> Bool {
for item in items {
if item == needle {
return true
}
}
false
}
///|
fn classify_multi_module_note_module_id(
seen : Array[String],
id : String?,
) -> Bool {
match id {
Some(name) => {
if fuzz_string_array_contains(seen, name) {
return true
}
seen.push(name)
false
}
None => false
}
}
///|
pub fn classify_multi_module_wast_case(
case : ValidMultiModuleWastCase,
) -> MultiModuleWastClassification {
let script = match wast_to_script(case.text, filename=case.name + ".wast") {
Ok(v) => v
Err(_) => return ParseFailure
}
let seen_module_ids : Array[String] = []
let seen_register_names : Array[String] = []
let mut saw_unlinkable = false
for cmd in script.commands {
match cmd {
WastCommand::Module(m) =>
if classify_multi_module_note_module_id(seen_module_ids, m.id) {
return DuplicateModuleName
}
WastCommand::ModuleBinary(id, _) =>
if classify_multi_module_note_module_id(seen_module_ids, id) {
return DuplicateModuleName
}
WastCommand::ModuleQuote(id, _) =>
if classify_multi_module_note_module_id(seen_module_ids, id) {
return DuplicateModuleName
}
WastCommand::Register(name, _) => {
if fuzz_string_array_contains(seen_register_names, name) {
return DuplicateRegisterName
}
seen_register_names.push(name)
}
WastCommand::AssertUnlinkable(_, _) => saw_unlinkable = true
_ => ()
}
}
if saw_unlinkable {
return Unlinkable
}
match @wast.run_wast_spec_file(case.name + ".wast", case.text).status {
@wast.WastSpecFileStatus::Passed => ValidLinking
@wast.WastSpecFileStatus::Skipped(_) => LinkSkipped
@wast.WastSpecFileStatus::Failed(msg) =>
if msg.contains("validation failed:") ||
msg.contains("module lower failed:") {
SingleModuleValidationFailure
} else {
LinkFailure
}
}
}
///|
fn valid_multi_module_linking_attempts(
profile : String,
) -> Result[Int32, String] {
let normalized = fuzz_ascii_lower(profile.trim().to_owned())
match normalized {
"smoke" => Ok(4)
"ci" => Ok(16)
"stress" => Ok(64)
_ =>
Err(
"unknown valid-multi-module-linking fuzz profile '\{profile}'; expected smoke|ci|stress",
)
}
}
///|
pub fn run_valid_multi_module_linking_fuzz(
profile : String,
seed : UInt64,
) -> Result[ValidMultiModuleLinkingFuzzStats, String] {
let normalized = fuzz_ascii_lower(profile.trim().to_owned())
let attempts = match valid_multi_module_linking_attempts(profile) {
Ok(v) => v
Err(e) => return Err(e)
}
let cases = gen_valid_multi_module_wast_cases(seed, attempts)
let mut generated = 0
let mut parsed_scripts = 0
let mut parse_failures = 0
let mut single_module_validated = 0
let mut single_module_validation_failures = 0
let mut link_passed = 0
let mut link_failed = 0
let mut link_skipped = 0
let mut last_error = ""
for case in cases {
generated += 1
let script = match wast_to_script(case.text, filename=case.name + ".wast") {
Ok(v) => {
parsed_scripts += 1
v
}
Err(e) => {
parse_failures += 1
last_error = e
continue
}
}
for cmd in script.commands {
match cmd {
WastCommand::Module(m) =>
match wast_ast_to_binary_module(m) {
Ok(binary_module) =>
match validate_module(binary_module) {
Ok(_) => single_module_validated += 1
Err(e) => {
single_module_validation_failures += 1
last_error = validation_error_message(e)
}
}
Err(e) => {
single_module_validation_failures += 1
last_error = e
}
}
_ => ()
}
}
let report = @wast.run_wast_spec_file(case.name + ".wast", case.text)
match report.status {
@wast.WastSpecFileStatus::Passed => link_passed += 1
@wast.WastSpecFileStatus::Failed(e) => {
link_failed += 1
last_error = e
}
@wast.WastSpecFileStatus::Skipped(e) => {
link_skipped += 1
last_error = e
}
}
}
if generated != attempts {
return Err("valid multi-module linking generated count mismatch")
}
if parse_failures > 0 ||
single_module_validation_failures > 0 ||
link_failed > 0 ||
link_skipped > 0 ||
link_passed == 0 {
return Err(
"valid multi-module linking did not fully pass: profile=\{normalized} seed=\{seed} generated=\{generated} parsed=\{parsed_scripts} single_module_validated=\{single_module_validated} single_module_validation_failures=\{single_module_validation_failures} link_passed=\{link_passed} link_failed=\{link_failed} link_skipped=\{link_skipped} last_error=\{last_error}",
)
}
Ok(ValidMultiModuleLinkingFuzzStats::{
profile: normalized,
seed,
attempts,
generated,
parsed_scripts,
parse_failures,
single_module_validated,
single_module_validation_failures,
link_passed,
link_failed,
link_skipped,
})
}
///|
fn gen_valid_wat_roundtrip_config() -> GenValidConfig {
GenValidConfig::wat_text_roundtrip_default()
}
///|
pub fn run_gen_valid_wat_validate_roundtrip_fuzz(
profile : String,
seed : UInt64,
) -> Result[GenValidWatValidateRoundtripFuzzStats, String] {
let normalized = fuzz_ascii_lower(profile.trim().to_owned())
let attempts = match normalized {
"smoke" => 32
"ci" => 128
"stress" => 512
_ =>
return Err(
"unknown gen-valid-wat-validate fuzz profile '\{profile}'; expected smoke|ci|stress",
)
}
let config = gen_valid_wat_roundtrip_config()
let state = @splitmix.new(seed~)
let mut generated = 0
let mut printed = 0
let mut print_failures = 0
let mut parsed = 0
let mut parse_failures = 0
let mut lowered = 0
let mut lower_failures = 0
let mut validated = 0
let mut validation_failures = 0
let mut last_print_failure = ""
let mut last_parse_failure = ""
for _ in 0.. {
printed += 1
v
}
Err(e) => {
print_failures += 1
last_print_failure = e
continue
}
}
let parsed_module = match
wast_to_module(text, filename="gen-valid-wat-roundtrip.wat") {
Ok(v) => {
parsed += 1
v
}
Err(e) => {
parse_failures += 1
last_parse_failure = e
continue
}
}
let lowered_module = match wast_ast_to_binary_module(parsed_module) {
Ok(v) => {
lowered += 1
v
}
Err(_) => {
lower_failures += 1
continue
}
}
match validate_module(lowered_module) {
Ok(_) => validated += 1
Err(_) => validation_failures += 1
}
}
if generated != attempts {
return Err("gen-valid wat roundtrip generated count mismatch")
}
if printed <= 0 || parsed <= 0 || lowered <= 0 || validated <= 0 {
return Err(
"gen-valid wat roundtrip made no validated progress: profile=\{normalized} seed=\{seed} generated=\{generated} printed=\{printed} parsed=\{parsed} lowered=\{lowered} validated=\{validated} last_print_failure=\{last_print_failure} last_parse_failure=\{last_parse_failure}",
)
}
Ok(GenValidWatValidateRoundtripFuzzStats::{
profile: normalized,
seed,
attempts,
generated,
printed,
print_failures,
parsed,
parse_failures,
lowered,
lower_failures,
validated,
validation_failures,
})
}
///|
fn text_differential_case(seed : UInt64, attempt : Int32) -> String {
let value = ((seed + attempt.to_uint64()) % 1000UL).to_int()
"(module (func (export \"answer\") (result i32) i32.const \{value}))"
}
///|
fn text_differential_attempt_locally(source : String) -> Result[Bool, String] {
let parsed = match wast_to_module(source, filename="text-differential.wat") {
Ok(v) => v
Err(e) => return Err("parse failed: \{e}")
}
let lowered = match wast_ast_to_binary_module(parsed) {
Ok(v) => v
Err(e) => return Err("lower failed: \{e}")
}
let lowered_bytes = match encode_module(lowered) {
Ok(v) => v
Err(e) => return Err("encode failed: \{e}")
}
let printed = match module_to_wast(parsed) {
Ok(v) => v
Err(e) => return Err("print failed: \{e}")
}
let reparsed = match
wast_to_module(printed, filename="text-differential.printed.wat") {
Ok(v) => v
Err(e) => return Err("reparse failed: \{e}")
}
let relowered = match wast_ast_to_binary_module(reparsed) {
Ok(v) => v
Err(e) => return Err("reparse lower failed: \{e}")
}
let relowered_bytes = match encode_module(relowered) {
Ok(v) => v
Err(e) => return Err("reparse encode failed: \{e}")
}
Ok(lowered_bytes == relowered_bytes)
}
///|
pub fn run_text_differential_fuzz(
profile : String,
seed : UInt64,
) -> Result[TextDifferentialFuzzStats, String] {
let normalized = fuzz_ascii_lower(profile.trim().to_owned())
let attempts = match normalized {
"smoke" => 1
"ci" => 8
"stress" => 32
_ =>
return Err(
"unknown text-differential fuzz profile '\{profile}'; expected smoke|ci|stress",
)
}
let external_adapters = ["wabt-unavailable", "wasm-tools-unavailable"]
let mut local_parse_ok = 0
let mut local_print_ok = 0
let mut local_reparse_lower_ok = 0
let mut local_lower_mismatch = 0
let mut adapter_unavailable = 0
let mut adapter_unavailable_classifications = 0
let accepted_classifications = 0
let mut last_local_error = ""
for attempt in 0.. {
local_parse_ok += 1
local_print_ok += 1
if bytes_equal {
local_reparse_lower_ok += 1
} else {
local_lower_mismatch += 1
}
}
Err(e) => {
last_local_error = e
continue
}
}
adapter_unavailable += external_adapters.length()
adapter_unavailable_classifications += 1
}
if local_reparse_lower_ok == 0 {
return Err(
"text differential made no local parse/print/lower progress: profile=\{normalized} seed=\{seed} last_local_error=\{last_local_error}",
)
}
Ok(TextDifferentialFuzzStats::{
profile: normalized,
seed,
attempts,
local_parse_ok,
local_print_ok,
local_reparse_lower_ok,
local_lower_mismatch,
adapter_unavailable,
adapter_unavailable_classifications,
accepted_classifications,
external_adapters,
})
}
///|
pub fn run_wast_validate_roundtrip_fuzz(
profile : String,
seed : UInt64,
) -> Result[WastValidateRoundtripFuzzStats, String] {
let normalized = fuzz_ascii_lower(profile.trim().to_owned())
let attempts = match normalized {
"smoke" => 64
"ci" => 256
"stress" => 1024
_ =>
return Err(
"unknown wast-validate fuzz profile '\{profile}'; expected smoke|ci|stress",
)
}
let state = @splitmix.new(seed~)
let mut printed = 0
let mut print_failures = 0
let mut parsed = 0
let mut parse_failures = 0
let mut lowered = 0
let mut lower_failures = 0
let mut validated = 0
let mut validation_failures = 0
let mut iteration = 0
while iteration < attempts {
iteration += 1
let wmod : @wast.Module = if iteration == 1 {
match wast_to_module("(module)", filename="wast-validate-seed.wat") {
Ok(v) => v
Err(e) => abort("unexpected fixed WAST seed parse failure: \{e}")
}
} else {
Arbitrary::arbitrary(5, state)
}
let text = match module_to_wast(wmod) {
Ok(v) => {
printed += 1
v
}
Err(_) => {
print_failures += 1
continue
}
}
let parsed_module = match wast_to_module(text) {
Ok(v) => {
parsed += 1
v
}
Err(_) => {
parse_failures += 1
continue
}
}
let lowered_module = match wast_ast_to_binary_module(parsed_module) {
Ok(v) => {
lowered += 1
v
}
Err(_) => {
lower_failures += 1
continue
}
}
match validate_module(lowered_module) {
Ok(_) => validated += 1
Err(_) => validation_failures += 1
}
}
if lowered <= 0 {
return Err(
"wast validate roundtrip lowered no modules: profile=\{normalized} seed=\{seed} attempts=\{attempts}",
)
}
if validated <= 0 {
return Err(
"wast validate roundtrip validated no modules: profile=\{normalized} seed=\{seed} lowered=\{lowered}",
)
}
Ok(WastValidateRoundtripFuzzStats::{
profile: normalized,
seed,
attempts,
printed,
print_failures,
parsed,
parse_failures,
lowered,
lower_failures,
validated,
validation_failures,
})
}
///|
fn validate_valid_text_companion_requirements(
normalized : String,
) -> (Int32, Int32) {
match normalized {
"smoke" => (0, 0)
"ci" => (20, 2)
"stress" => (80, 8)
_ => abort("unreachable validate-valid text companion profile")
}
}
///|
fn run_validate_valid_suite_detailed(
profile : String,
seed : UInt64,
) -> Result[(Int32, String?), String] {
let run_config = match validate_valid_run_config(profile) {
Ok(config) => config
Err(e) => return Err(e)
}
let normalized = run_config.profile
let (min_text_attempts, min_text_stable) = validate_valid_text_companion_requirements(
normalized,
)
let direct_stats = match run_validate_valid_fuzz(normalized, seed) {
Ok(stats) => stats
Err(e) => return Err(e)
}
let attempts = direct_stats.attempts
let validated = direct_stats.validated
let mut roundtrip = 0
let mut stable = 0
if min_text_attempts > 0 {
let text_seed = seed ^ 0x574154554c
match run_wat_roundtrip_fuzz(normalized, text_seed) {
Ok(stats) => {
roundtrip = stats.successes
stable = stats.stable
}
Err(e) =>
return Err(
"validate-valid fuzz text companion failed: profile=\{normalized} seed=\{text_seed} error=\{e}",
)
}
}
if validated != attempts {
return Err(
"validate-valid fuzz failed: profile=\{normalized} seed=\{seed} direct validate count mismatch: \{validated}/\{attempts}",
)
}
if roundtrip < min_text_attempts {
return Err(
"validate-valid fuzz produced too few text roundtrips: profile=\{normalized} seed=\{seed} roundtrip=\{roundtrip} min=\{min_text_attempts}",
)
}
if stable < min_text_stable {
return Err(
"validate-valid fuzz produced too few stable text roundtrips: profile=\{normalized} seed=\{seed} stable=\{stable} min=\{min_text_stable}",
)
}
Ok((attempts, Some(format_validate_valid_details_json(direct_stats))))
}
///|
fn fuzz_seed_digit(c : UInt16, base : Int32) -> Int32? {
let digit = if c >= '0' && c <= '9' {
c.to_int() - 0x30
} else if base == 16 && c >= 'a' && c <= 'f' {
c.to_int() - 0x61 + 10
} else if base == 16 && c >= 'A' && c <= 'F' {
c.to_int() - 0x41 + 10
} else {
return None
}
if digit >= base {
return None
}
Some(digit)
}
///|
fn fuzz_parse_seed(raw : String) -> Result[Int64, String] {
let text = raw.trim().to_owned()
if text.length() == 0 {
return Err("seed must not be empty")
}
let mut negative = false
let mut i = 0
if text[0] == '+' {
i = 1
} else if text[0] == '-' {
negative = true
i = 1
}
if i >= text.length() {
return Err("invalid seed '\{raw}'")
}
let mut base = 10
if i + 1 < text.length() &&
text[i] == '0' &&
(text[i + 1] == 'x' || text[i + 1] == 'X') {
base = 16
i += 2
}
let mut result : UInt64 = 0UL
let max_abs = if negative {
0x8000000000000000UL
} else {
0x7FFFFFFFFFFFFFFFUL
}
let base_u = base.to_uint64()
let mut saw_digit = false
while i < text.length() {
let c = text[i]
if c == '_' {
i += 1
continue
}
let digit = match fuzz_seed_digit(c, base) {
Some(v) => v
None => return Err("invalid seed '\{raw}'")
}
saw_digit = true
let digit_u = digit.to_uint64()
if result > (max_abs - digit_u) / base_u {
return Err("seed '\{raw}' is out of range for int64")
}
result = result * base_u + digit_u
i += 1
}
if !saw_digit {
return Err("invalid seed '\{raw}'")
}
if negative {
if result == 0x8000000000000000UL {
Ok(result.reinterpret_as_int64())
} else {
Ok(-result.reinterpret_as_int64())
}
} else {
Ok(result.reinterpret_as_int64())
}
}
///|
fn fuzz_parse_output_mode(raw : String) -> Result[FuzzOutputMode, String] {
let normalized = fuzz_ascii_lower(raw.trim().to_owned())
match normalized {
"text" => Ok(FuzzOutputMode::Text)
"jsonl" => Ok(FuzzOutputMode::Jsonl)
_ => Err("unknown output mode '\{raw}'; expected text|jsonl")
}
}
///|
fn fuzz_generate_seed_i64() -> Int64 {
trace_now_us().reinterpret_as_int64()
}
///|
fn fuzz_has_seed_equals_flag(arg : String) -> Bool {
arg.length() >= 7 &&
arg[0] == '-' &&
arg[1] == '-' &&
arg[2] == 's' &&
arg[3] == 'e' &&
arg[4] == 'e' &&
arg[5] == 'd' &&
arg[6] == '='
}
///|
fn fuzz_has_output_equals_flag(arg : String) -> Bool {
arg.length() >= 9 &&
arg[0] == '-' &&
arg[1] == '-' &&
arg[2] == 'o' &&
arg[3] == 'u' &&
arg[4] == 't' &&
arg[5] == 'p' &&
arg[6] == 'u' &&
arg[7] == 't' &&
arg[8] == '='
}
///|
fn fuzz_has_prefix_text(arg : String, prefix : String) -> Bool {
if arg.length() < prefix.length() {
return false
}
let mut i = 0
while i < prefix.length() {
if arg[i] != prefix[i] {
return false
}
i += 1
}
true
}
///|
fn fuzz_tail_string(input : String, start : Int32) -> String {
let out = StringBuilder::new()
let mut i = start
while i < input.length() {
out.write_char(input[i].unsafe_to_char())
i += 1
}
out.to_string()
}
///|
fn fuzz_parse_seed_or_abort(raw : String) -> Int64 {
match fuzz_parse_seed(raw) {
Ok(v) => v
Err(e) => abort(e)
}
}
///|
fn fuzz_parse_output_or_abort(raw : String) -> FuzzOutputMode {
match fuzz_parse_output_mode(raw) {
Ok(v) => v
Err(e) => abort(e)
}
}
///|
fn fuzz_parse_uint64(raw : String) -> Result[UInt64, String] {
let text = raw.trim().to_owned()
if text.length() == 0 {
return Err("seed must not be empty")
}
let negative = text[0] == '-'
let mut i = if negative || text[0] == '+' { 1 } else { 0 }
let mut base = 10
if i + 1 < text.length() &&
text[i] == '0' &&
(text[i + 1] == 'x' || text[i + 1] == 'X') {
base = 16
i += 2
}
let base_u = base.to_uint64()
let mut value = 0UL
let mut saw_digit = false
while i < text.length() {
if text[i] == '_' {
i += 1
continue
}
let digit = match fuzz_seed_digit(text[i], base) {
Some(v) => v.to_uint64()
None => return Err("invalid seed '\{raw}'")
}
if value > (0xffffffffffffffffUL - digit) / base_u {
return Err("seed '\{raw}' is out of range for uint64")
}
value = value * base_u + digit
saw_digit = true
i += 1
}
if !saw_digit {
return Err("invalid seed '\{raw}'")
}
if negative && value != 0UL {
return Err("seed must be non-negative")
}
Ok(value)
}
///|
fn fuzz_parse_uint64_or_abort(raw : String) -> UInt64 {
match fuzz_parse_uint64(raw) {
Ok(v) => v
Err(e) => abort(e)
}
}
///|
fn fuzz_parse_non_negative_decimal_or_abort(
label : String,
raw : String,
) -> Int32 {
let text = raw.trim().to_owned()
if text.length() == 0 {
abort("\{label} must not be empty")
}
let mut value = 0
for c in text {
if c < '0' || c > '9' {
abort("invalid \{label} '\{raw}'")
}
let digit = c.to_int() - '0'.to_int()
value = value * 10 + digit
}
value
}
///|
fn fuzz_parse_feature_floor_or_abort(raw : String) -> ValidateValidFeatureFloor {
let parts : Array[String] = []
for part in raw.split(":") {
parts.push(part.to_owned())
}
if parts.length() > 2 {
abort(
"invalid --require-feature '\{raw}'; expected