@@ -37,6 +37,51 @@ def multifill' (vars : List Identifier) : Except Yul.Exception (State × List Li
3737 | .ok (s, rets) => .ok (s.multifill vars rets)
3838 | .error e => .error e
3939
40+ def firstDuplicate? : List Identifier → Option Identifier
41+ | [] => .none
42+ | var :: vars =>
43+ if vars.contains var then .some var else firstDuplicate? vars
44+
45+ def firstDeclared? (s : State) (vars : List Identifier) : Option Identifier :=
46+ vars.find? (fun var => (s.lookup? var).isSome)
47+
48+ def firstUndeclared? (s : State) (vars : List Identifier) : Option Identifier :=
49+ vars.find? (fun var => (s.lookup? var).isNone)
50+
51+ def checkDeclaration (s : State) (vars : List Identifier) : Except Yul.Exception Unit :=
52+ match firstDuplicate? vars with
53+ | .some var => .error (.DuplicateDeclaration var)
54+ | .none =>
55+ match firstDeclared? s vars with
56+ | .some var => .error (.DuplicateDeclaration var)
57+ | .none => .ok ()
58+
59+ def checkAssignment (s : State) (vars : List Identifier) : Except Yul.Exception Unit :=
60+ match firstDuplicate? vars with
61+ | .some _ => .error .InvalidArguments
62+ | .none =>
63+ match firstUndeclared? s vars with
64+ | .some var => .error (.UnknownIdentifier var)
65+ | .none => .ok ()
66+
67+ def restoreRevertedContractCallState (s₀ s₂ : State) (outOffset outSize : Literal) :
68+ Except Yul.Exception (State × List Literal) :=
69+ match s₀ with
70+ | .OutOfFuel => .error .OutOfFuel
71+ | .Checkpoint j => .ok (.Checkpoint j, [⟨0 ⟩])
72+ | .Ok sharedState₀ varstore =>
73+ let returnData := s₂.toMachineState.H_return
74+ let memory₃ :=
75+ returnData.copySlice 0 s₀.toMachineState.memory outOffset.toNat
76+ (min outSize.toNat returnData.size)
77+ let sharedState₃ :=
78+ { sharedState₀ with
79+ memory := memory₃
80+ returnData := returnData
81+ H_return := ByteArray.empty
82+ }
83+ .ok (.Ok sharedState₃ varstore, [⟨0 ⟩])
84+
4085def setStatic (s : State) (p : Bool) : State :=
4186 match s with
4287 | .OutOfFuel => .OutOfFuel
@@ -143,6 +188,8 @@ def primCall (fuel : ℕ) (s₀ : State) (prim : Operation .Yul) (args : List Li
143188 H_return := ByteArray.empty
144189 }
145190 .ok (.Ok sharedState₃ varstore, [⟨1 ⟩])
191+ | .error (.Revert s₂) =>
192+ restoreRevertedContractCallState s₀ s₂ outOffset outSize
146193 | .error e => .error e
147194 | .ok (s₂, _) =>
148195
@@ -235,6 +282,8 @@ def primCall (fuel : ℕ) (s₀ : State) (prim : Operation .Yul) (args : List Li
235282 executionEnv := executionEnv₃
236283 }
237284 .ok (setStatic (.Ok sharedState₃ varstore) s₀.executionEnv.perm, [⟨1 ⟩])
285+ | .error (.Revert s₂) =>
286+ restoreRevertedContractCallState s₀ s₂ outOffset outSize
238287 | .error e => .error e
239288 | .ok (s₂, _) =>
240289
@@ -320,6 +369,8 @@ def primCall (fuel : ℕ) (s₀ : State) (prim : Operation .Yul) (args : List Li
320369 }
321370 .ok (.Ok sharedState₃ varstore, [⟨1 ⟩])
322371
372+ | .error (.Revert s₂) =>
373+ restoreRevertedContractCallState s₀ s₂ outOffset outSize
323374 | .error e => .error e
324375 | .ok (s₂, _) =>
325376 let memory₃ := s₂.toMachineState.H_return.copySlice 0 s₀.toMachineState.memory outOffset.toNat (min outSize.toNat s₂.toMachineState.H_return.size)
@@ -394,6 +445,8 @@ def primCall (fuel : ℕ) (s₀ : State) (prim : Operation .Yul) (args : List Li
394445 executionEnv := executionEnv₃
395446 }
396447 .ok (.Ok sharedState₃ varstore, [⟨1 ⟩])
448+ | .error (.Revert s₂) =>
449+ restoreRevertedContractCallState s₀ s₂ outOffset outSize
397450 | .error e => .error e
398451 | .ok (s₂, _) =>
399452 let memory₃ := s₂.toMachineState.H_return.copySlice 0 s₀.toMachineState.memory outOffset.toNat (min outSize.toNat s₂.toMachineState.H_return.size)
@@ -462,7 +515,7 @@ def primCall (fuel : ℕ) (s₀ : State) (prim : Operation .Yul) (args : List Li
462515 match fOpt with
463516 | .none => .error (.MissingContractFunction (yulFunctionNameOption.getD ".none" ))
464517 | .some f =>
465- let s₁ := 👌 s.initcall f.params args
518+ let s₁ := 👌 s.initcall f.params f.rets args
466519 match exec fuel' (.Block f.body) codeOverride s₁ with
467520 | .error e => .error e
468521 | .ok s₂ =>
@@ -479,7 +532,7 @@ def primCall (fuel : ℕ) (s₀ : State) (prim : Operation .Yul) (args : List Li
479532 | 0 => .error .OutOfFuel
480533 | .succ fuel' =>
481534 let f := FunctionDefinition.Def [] [] [s.executionEnv.code.dispatcher]
482- let s₁ := 👌 s.initcall f.params []
535+ let s₁ := 👌 s.initcall f.params f.rets []
483536 match exec fuel' (.Block f.body) codeOverride s₁ with
484537 | .error e => .error e
485538 | .ok s₂ =>
@@ -512,30 +565,47 @@ def primCall (fuel : ℕ) (s₀ : State) (prim : Operation .Yul) (args : List Li
512565 | .succ fuel' => multifill' vars (call fuel' args yulFunctionName codeOverride s)
513566 | .error e => .error e
514567
568+ def evalValues (fuel : Nat) (expr : Expr) (codeOverride : Option YulContract) (s : State) : Except Yul.Exception (State × List Literal) :=
569+ match fuel with
570+ | 0 => .error .OutOfFuel
571+ | .succ fuel' =>
572+ match expr with
573+ | .Call (Sum.inl prim) args =>
574+ match reverse' (evalArgs fuel' args.reverse codeOverride s) with
575+ | .ok (s, args) => primCall fuel' s prim args
576+ | .error e => .error e
577+ | .Call (Sum.inr yulFunctionName) args =>
578+ match reverse' (evalArgs fuel' args.reverse codeOverride s) with
579+ | .ok (s, args) => call fuel' args yulFunctionName codeOverride s
580+ | .error e => .error e
581+ | .Var id =>
582+ match s.lookup? id with
583+ | .some val => .ok (s, [val])
584+ | .none => .error (.UnknownIdentifier id)
585+ | .Lit val => .ok (s, [val])
586+
515587 /--
516588 `eval` evaluates an expression.
517589
518590 - calls evaluated here are assumed to have coarity 1
519591 -/
520592 def eval (fuel : Nat) (expr : Expr) (codeOverride : Option YulContract) (s : State) : Except Yul.Exception (State × Literal) :=
593+ head' (evalValues fuel expr codeOverride s)
594+
595+ def execSeq (fuel : Nat) (stmts : List Stmt) (codeOverride : Option YulContract) (s : State) : Except Yul.Exception State :=
521596 match fuel with
522597 | 0 => .error .OutOfFuel
523598 | .succ fuel' =>
524- match expr with
525-
526- -- We hit these two cases (`PrimCall` and `Call`) when evaluating:
527- --
528- -- 1. f() (expression statements)
529- -- 2. g(f()) (calls in function arguments)
530- -- 3. if f() {...} (if conditions)
531- -- 4. for {...} f() ... (for conditions)
532- -- 5. switch f() ... (switch conditions)
533-
534- | .Call (Sum.inl prim) args => evalPrimCall fuel' prim (reverse' (evalArgs fuel' args.reverse codeOverride s))
535- | .Call (Sum.inr yulFunctionName) args =>
536- evalCall fuel' yulFunctionName codeOverride (reverse' (evalArgs fuel' args.reverse codeOverride s))
537- | .Var id => .ok (s, s[id]!)
538- | .Lit val => .ok (s, val)
599+ match stmts with
600+ | [] => .ok s
601+ | stmt :: stmts =>
602+ match exec fuel' stmt codeOverride s with
603+ | .error e => .error e
604+ | .ok s₁ =>
605+ match s₁ with
606+ | .Ok _ _ => execSeq fuel' stmts codeOverride s₁
607+ | .OutOfFuel => .ok s₁
608+ | .Checkpoint _ => .ok s₁
539609
540610 /--
541611 `exec` executs a single statement.
@@ -545,24 +615,23 @@ def primCall (fuel : ℕ) (s₀ : State) (prim : Operation .Yul) (args : List Li
545615 | 0 => .error .OutOfFuel
546616 | .succ fuel' =>
547617 match stmt with
548- | .Block [] => .ok s
549- | .Block (stmt :: stmts) =>
550- let s₁ := exec fuel' stmt codeOverride s
551- match s₁ with
552- | .error e => .error e
553- | .ok s₁ => exec fuel' (.Block stmts) codeOverride s₁
618+ | .Block stmts =>
619+ match execSeq fuel' stmts codeOverride s with
620+ | .error e => .error e
621+ | .ok s₁ => .ok (s₁.restrictStoreTo s.store)
554622
555623 | .Let vars exprOption =>
624+ match checkDeclaration s vars with
625+ | .error e => .error e
626+ | .ok () =>
556627 match exprOption with
557- | .none => .ok (List.foldr (λ var s ↦ s.insert var ⟨0 ⟩) s vars)
558- | .some expr =>
559- match expr with
560- | .Call (Sum.inl prim) args =>
561- execPrimCall fuel' prim vars (reverse' (evalArgs fuel' args.reverse codeOverride s))
562- | .Call (Sum.inr yulFunctionName) args =>
563- execCall fuel' yulFunctionName vars codeOverride (reverse' (evalArgs fuel' args.reverse codeOverride s))
564- | .Var identifier => .ok (s.insert vars.head! s[identifier]!) -- It should be safe to call head! here if the Yul code is parsed correctly.
565- | .Lit literal => .ok (s.insert vars.head! literal) -- It should be safe to call head! here if the Yul code is parsed correctly.
628+ | .none => .ok (s.zeroFill vars)
629+ | .some expr => multifill' vars (evalValues fuel' expr codeOverride s)
630+
631+ | .Assign vars expr =>
632+ match checkAssignment s vars with
633+ | .error e => .error e
634+ | .ok () => multifill' vars (evalValues fuel' expr codeOverride s)
566635
567636 | .If cond body =>
568637 match eval fuel' cond codeOverride s with
@@ -640,8 +709,10 @@ def execTopLevel (fuel : Nat) (stmt : Stmt) (s : State) : State :=
640709 | .error (.MissingContract _) => default
641710 | .error (.MissingContractFunction _) => default -- We do not model fallback functions
642711 | .error .InvalidExpression => default
712+ | .error (.UnknownIdentifier _) => default
713+ | .error (.DuplicateDeclaration _) => default
643714 | .error .YulEXTCODESIZENotImplemented => default
644- | .error .Revert => s
715+ | .error ( .Revert _) => s
645716 | .error (.YulHalt s _) => s
646717 | .ok s => s
647718
0 commit comments