Skip to content

Feature/nestedBreak #513

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

Yi2255
Copy link
Contributor

@Yi2255 Yi2255 commented Apr 30, 2025

Hi Saelo,
This PR implements NestedBreak as discussed in #479. I’ve refined the implementation based on a clearer understanding of JavaScript’s LabelStatement constraints and Fuzzilli’s context architecture.

Below is the definition of LabelStatement in ECMA:

LabeledStatement : 
 Identifier : Statement

And here is the full list of Statement types:


Statement ::= 
  BlockStatement
  | BreakStatement
  | ContinueStatement
  | DebuggerStatement
  | DoWhileStatement
  | EmptyStatement
  | ExpressionStatement
  | ForStatement
  | ForInStatement
  | ForOfStatement
  | FunctionDeclaration
  | IfStatement
  | LabeledStatement
  | ReturnStatement
  | SwitchStatement
  | ThrowStatement
  | TryStatement
  | VariableDeclaration
  | WhileStatement
  | WithStatement
  | ClassDeclaration
  | ImportDeclaration
  | ExportDeclaration

In fact, not all of these statements can be used in combination with a labelStatement. Below is a summary of those that cannot be used:

1. DeclarationStatement

Labels can only be applied to statements, not declarations. There is a legacy grammar that allows function declarations to be labeled in non-strict code:

L: function F() {}

Therefore, all declaration statements can be ignored. This is merely a historical quirk and has no practical significance.

2. DebuggerStatement

Only one scenario exists: label: debugger, which is also not worth supporting.

3. ExpressionStatement

ExpressionStatement :
[lookahead ≠ { '{' , 'function' , 'class' , 'let' , 'async function' }] Expression ;

A LabelStatement must be followed by exactly one Statement, so in practice the only valid form here is something like label: break label;, which we can also ignore.

4. ReturnStatement

A return statement causes a function to cease execution and, in most cases, returns a value to the caller. If Expression is omitted, the return value is undefined. Otherwise, the return value is the result of evaluating Expression. A return statement may not actually return a value to the caller depending on surrounding context. For example, in a try block, a return statement’s Completion Record may be replaced during evaluation of the finally block.

Because return can only appear within function bodies, it cannot be used with a label.

After filtering out the above cases, here are the valid statements that can be used with a label, which roughly match your earlier examples:


1. IfStatement
2. TryStatement
3. WithStatement
4. BlockStatement
5. SwitchStatement
6. loopFamily
   - DoWhileStatement
   - ForStatement
   - ForInStatement
   - ForOfStatement
   - WhileStatement

It’s clear that all valid statement types are recursive structures, which means our previously designed loopLabelStack can be reused.

Here are some discussions on implementation details:

  1. Fuzzilli does not abstract base grammar categories like Statement or Expression, so there’s no way to manipulate all statements in a uniform way.

  2. Given the list of valid Statements above, the break operation cannot be implemented in the way you advised (i.e., support the nested break in every JS context). A label must anchor the entry point of a code block; otherwise, a break cannot determine its target.

  3. Fuzzilli’s context system is designed in a top-down manner, which means there is no unified solution for handling the same operation appearing in different contexts. For example, the already implemented switchBreak and loopBreak are both just break; in JavaScript, but due to the underlying architecture based on isSubSet(of: originContext) in ProgramBuilder.swift, they must be implemented separately.

//ProgramBuilder.swift
availableGenerators = fuzzer.codeGenerators.filter({ $0.requiredContext.isSubset(of: origContext) })

Therefore, I’ve chosen to follow the existing design approach and implement a dedicated xxxNestedBreak operation for each context, rather than introducing a generic nestedBreak.

  1. I’ve additionally added three missing contexts: ifBlock, tryBlock, and codeBlock. Then, I implemented six new context-specific nested break operations:
    • switchNestedBreak
    • blockNestedBreak
    • ifNestedBreak
    • withNestedBreak
    • tryNestedBreak
    • loopNestedBreak

I also reorganized the supporting label stack to enable reuse across different Statements and contexts. The main implementation process is as follows:

  1. Implement separately switchNestedBreakblockNestedBreakifNestedBreakwithNestedBreaktryNestedBreakloopNestedBreak six JSOperations;
  2. Use LabelStack to trace the nested code block of these six code blocks. The element in LabelStack is LabelPin, which has two fields: beginPos and hasLabel. beginPos is the index of the starting point of the js code block, and hasLabel indicates whether beginPos has generated a label (with a small probability of generating N lines of break or continue in the same block). Because there is no need to mark every code entry with a label Identifier, which will expand the code size, it is only inserted when breakNested or continueNested appears. Therefore, the core operation of LabelStack is insertLabel, which has three operations: ①Insert label Identifier string into js code; ②mark that a label has been inserted here; ③Move the beginPos of all LabelPins forward by the given size offset.;
  3. In addition, it is necessary to restrict the context of ifNestedBreak, blockNestedBreak, and tryNestedBreak when implementing them in jsoperation. Therefore, three new contexts have been added to the context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant