Add Seed Parsing Support and FuzzIL Operations for JS Features
When the user asks to add support for a specific JavaScript feature in the FuzzILTool Compiler, follow this exact step-by-step checklist. Do not skip steps unless explicitly instructed.
CRITICAL RULES
- IMPORTANT: NEVER modify any code without asking the user to review the diff first. You are explicitly in an interactive request-review mode.
- CONVENTION:
.protofiles define a protobuf format validation (similar to an XML Schema).[name].xxx.protobufdefines a binary file containing the raw data that can be validated against the schema (similar to an XML file that validates against an XSD schema). - TESTING ENV: All standard
swift testexecutions MUST have the dynamic prefixFUZZILLI_TEST_SHELL={fuzzbuild}/d8. Ask the user if they have a fuzzing-enabled V8 build. If so, ask them to input the path to set the{fuzzbuild}variable.If they don't have one, ask them to provide their local V8 directory path, and instruct them to run the following in that directory to create one (and then set
{fuzzbuild}toout/fuzzbuildwithin that path):gn gen out/fuzzbuild --args='is_debug=false dcheck_always_on=true v8_static_library=true v8_enable_verify_heap=true v8_enable_partition_alloc=false v8_fuzzilli=true sanitizer_coverage_flags="trace-pc-guard" target_cpu="x64" v8_symbol_level=2 use_remoteexec=true' && autoninja -C out/fuzzbuild
Step 1: Feature Extraction
- Find a (set of) test262 cases failing due to the missing JS feature. Ask the user if they already have some.
- Write a minimal but complete compiler test inside
{Fuzzilli_Root}/Tests/FuzzilliTests/CompilerTests/[feature_name].js. - Create a baseline passing equivalent test that has the smallest possible diff WITHOUT the feature at
{Fuzzilli_Root}/Tests/FuzzilliTests/CompilerTests/[feature_name_diff].js. - Follow TDD: Run
FUZZILLI_TEST_SHELL={fuzzbuild}/d8 swift test --filter CompilerTests. - USER VERIFICATION: Give the user the exact command to run the tests from
{Fuzzilli_Root}and ask them explicitly if they agree with the baseline failure of[feature_name].jsand the success of[feature_name_diff].js.
Step 2: AST Diffing
Run the following node.js snippet to parse both files via babel and strip out noisy
start/end/locidentifiers. Save this snippet locally as/tmp/snippet.js:const Parser = require("@babel/parser"); const fs = require('fs'); const file = process.argv[2]; let ast = Parser.parse(fs.readFileSync(file, 'utf8'), { plugins: ["explicitResourceManagement", "v8intrinsic"] }); // Custom replacer to clean AST output and remove noisy layout elements function replacer(key, value) { if (key === 'start' || key === 'end' || key === 'loc' || key === 'extra' || key === 'comments' || key === 'errors') { return undefined; } return value; } console.log(JSON.stringify(ast, replacer, 2));Run it using the parser's local Babel installation:
cd {Fuzzilli_Root}/Sources/Fuzzilli/Compiler/Parser NODE_PATH=./node_modules node /tmp/snippet.js {Fuzzilli_Root}/Tests/FuzzilliTests/CompilerTests/[feature_name].js > /tmp/[feature_name].babel.json NODE_PATH=./node_modules node /tmp/snippet.js {Fuzzilli_Root}/Tests/FuzzilliTests/CompilerTests/[feature_name_diff].js > /tmp/[feature_name_diff].babel.jsonUSER VERIFICATION: Tell the user the absolute paths of
/tmp/[feature_name].babel.jsonand/tmp/[feature_name_diff].babel.json. Explain that these are the clean, diffable Babel representations and ask them to diff the clean copies locally in VS Code or their CLI.
Step 3: Parser Updates
Review and choose the appropriate
ast.protorepresentation for the nodes identified in the JSON AST diff.Edit
parser.jsto implement the translation logic from[name].babel.json --> [name].ast.protobuf.Generate the Fuzzilli-specific binary protobuf definitions of the feature and diff files using
parser.js:# Execute parser.js logic to output binary protocol buffers node parser.js {Fuzzilli_Root}/Tests/FuzzilliTests/CompilerTests/[feature_name].js /tmp/out.[feature_name].ast.protobuf node parser.js {Fuzzilli_Root}/Tests/FuzzilliTests/CompilerTests/[feature_name_diff].js /tmp/out.[feature_name_diff].ast.protobufUSER VERIFICATION: Give the user the node commands executed above. Provide the
protoccommand line string so they can deserialize the output to human-readable format (protoc --decode=...). Ask them to evaluate the diff of the protobuf output, and verify they agree with the proposed modifications toparser.jsandast.proto.
Step 4: Compiler and Operations
Identify and choose the correct representation in
opcodes.swift(this will generateprogram.proto) andoperations.proto.Modify
compiler.swiftto handle the translations from/tmp/out.[feature_name].ast.protobufinto/tmp/out.[feature_name].program.protobuf(FuzzIL).- Note: Determine if
Instruction.swift(specifically the "analyze" function) also requires modification for this feature.
- Note: Determine if
Regenerate the core protocol buffers and Swift files using the python generator scripts:
cd {Fuzzilli_Root}/Sources/Fuzzilli/Protobuf/ python3 gen_programproto.py protoc --swift_opt=Visibility=Public --swift_out=. program.proto operations.proto sync.proto ast.protoUSER VERIFICATION: Tell the user how to run:
# 1. Compile the JS files directly to `.fzil` (FuzzILTool will write .fzil next to the source files)
swift run FuzzILTool --compile Tests/FuzzilliTests/CompilerTests/[feature_name].js`
swift run FuzzILTool --compile Tests/FuzzilliTests/CompilerTests/[feature_name_diff].js`
# 2. Decode the generated .fzil files to text protobuf with protoc
protoc --decode=fuzzilli.protobuf.Program -I=Sources/Fuzzilli/Protobuf Sources/Fuzzilli/Protobuf/program.proto < Tests/FuzzilliTests/CompilerTests/[feature_name].fzil > /tmp/[feature_name].program.txt
protoc --decode=fuzzilli.protobuf.Program -I=Sources/Fuzzilli/Protobuf Sources/Fuzzilli/Protobuf/program.proto < Tests/FuzzilliTests/CompilerTests/[feature_name_diff].fzil > /tmp/[feature_name_diff].program.txt
# 3. Diff the decoded FuzzIL protobufs to verify the node structures!
diff -u /tmp/[feature_name_diff].program.txt /tmp/[feature_name].program.txt
Step 5: Lifters (FuzzIL -> "FuzzIL Textual Representation" and FuzzIL -> JS)
- Edit
FuzzILLifter.swiftto handle/tmp/out.[feature_name].program.protobufto/tmp/out.[feature_name].fzil.text. - Edit
JavaScriptLifter.swiftto handle/tmp/out.[feature_name].program.protobufto/tmp/out.[feature_name].relifted.js. - USER VERIFICATION: Give the user the command lines to run the lifter commands
swift run FuzzILTool --LiftToFuzzILandswift run FuzzILTool --Lift[Corpus]ToJS. Have them run them, then ask them to diff/tmp/out.[feature_name].human.fzilwith/tmp/out.[feature_name_diff].human.fzil, and/tmp/out.[feature_name].relifted.jswith/tmp/out.[feature_name_diff].relifted.js.
Step 6: Testing & Verification
- Verify that both
[feature_name].jsand[feature_name_diff].jstests now pass within the FuzzIL pipeline. - Give the exact swift test command for the user to execute themselves securely.
- Add additional deep test coverage in
LifterTests.swift(updateProgramBuilder.swifthelpers as needed) and ensure all tests pass cleanly.
Step 7: Final Touches
Execute the Fuzzilli formatting and standard rebuild pipeline:
cd {Fuzzilli_Root} swift format . --recursive --in-place --parallel find . -type f \( -name "*.proto" -o -name "*.js" \) -exec sed -i 's/[ \t]*$//' {} + Tools/presubmit.py --regenerate-proto --format swift build FUZZILLI_TEST_SHELL={fuzzbuild}/d8 swift testRun
git diff origin/mainandswift format lint --recursive .and make sure no wranings or errors from swift lint falls in the range of the current modifications.Ask the user to have a 10 mins minimum run of FuzzilliCLI with the FuzzIL of
swift run FuzzILTool --compile {Fuzzilli_Root}/Tests/FuzzilliTests/CompilerTests/[feature_name].jsas a seed, eventually playing with the generators weight. Ask them to compare the stats (correctness/coverage/...) against an equivalent run inorigin/main