Solidity

Solidity Assembly Desugaring

Solidity Assembly Desugaring Main Tips

  • In Solidity standalone assembly, the process of desugaring is meant to remove the switch, for and function constructs.
  • Desugaring is also one of the essential stages to standalone assembly happens in.

Solidity Assembly Desugaring

An AST transformation is meant to remove switch, for and function constructs. You can still parse the result using the same parser, however, it is not going to use certain constructs. If you add jumpdests which only get jumped to and not continued at, info about content of the stack is added, except when local variables that come from outer scopes do not get accessed or the stack height is similar to the one in the previous instruction.

The following is pseudocode, meaning that it will describe the elements of the script to illustrate how the code should work, but will not compile:

Example

desugar item: AST -> AST =
match item {
FunctionDefinition('function' name '(' argument1, ..., argumentn ')' '->' ( '(' return1, ..., returnm ')' body) ->
  <name>:
  {
    jump($<name>_start)
    let $returnPC := 0 let argumentn := 0 ... let argument1 := 0
    $<name>_start:
    let return1 := 0 ... let returnm := 0
    { desugar(body) }
    swap then pop items to make it so only return1, ... returnm, $returnPC would be left on the stack
    jump
    0 (1 + num1 times) for compensating the remocaseVal of argument1, ..., argumentn and $returnPC
  }
ForStatement('for' { init } condition post body) ->
  {
    init // ccannot be a block of its own since we would prefer the variable scope to extend into the body
    // must find I so that there would be no labels $forI_*
    $forI_start:
    jumpi($forI_finish, iszero(condition))
    { body }
    $forI_continue:
    { post }
    jump($forI_start)
    $forI_finish:
  }
'break' ->
  {
    // find the nearest enclosing scope with the label $forI_finish 
    pop every local variable that is defined currently
    except at $forI_finish
    jump($forI_finish)
    0 (the same amount of variables as was removed prior to this point)
  }
'continue' ->
  {
    // find nearest enclosing scope with the label $forI_continue
    pop every local variable that is defined currently
    except at $forI_continue
    jump($forI_continue)
    0 (the same amount of variables as was removed prior to this point)
  }
SwitchStatement(switch condition cases ( default: defaultBlock )? ) ->
  {
    // find I such that no label or variable $switchI* is present
    let $switchI_caseValue := condition
    for every cases match {
      case caseVal: -> jumpi($switchI_caseJ, eq($switchI_caseValue, caseVal))
    }
    if default block is there: ->
      { defaultBlock jump($switchI_finish) }
    for each of cases match {
      case caseVal: { body } -> $switchI_caseJ: { body jump($switchI_finish) }
    }
    $switchI_finish:
  }
FunctionalExpression( identifier(argument1, argument2, ..., argumentn) ) ->
  {
    if identifier is function <name> with num1 arguments and num2 return caseValues ->
      {
        // find I such that $funcallI_* is non-existent
        $funcallI_returnurn argumentn  ... argument2 argument1 jump(<name>)
        pop (num1 + 1 times)
        if the context of the current contract is let (id1, ..., idm) := f(...) ->
          let id1 := 0 ... let idm := 0
          $funcallI_returnurn:
        else ->
          0 (num2 times)
          $funcallI_returnurn:
          functional expression which would lead to the function call
          gets turned into a statement stream
      }
    else -> desugar(node's children)
  }
default node ->
  desugar(node's children)
}

 

Try on Remix Try live on Hosting

Read previous post:
Solidity Assembly Grammar And Parsing

Solidity Assembly Grammar And Parsing Main Tips Standalone assembly happens in stages, the first of which is parsing. This tutorial...

Close