Skip to content
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

fix(gnovm): use OpCall to run deferred statements #2597

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/gno.land/r/demo/tests/tests.gno
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ func CurrentRealmPath() string {
return std.CurrentRealm().PkgPath()
}

func PrintCurrentRealmPath() {
println(CurrentRealmPath())
}

var initOrigCaller = std.GetOrigCaller()

func InitOrigCaller() std.Address {
Expand Down
41 changes: 36 additions & 5 deletions gnovm/pkg/gnolang/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,49 @@ func (fr *Frame) PopDefer() (res Defer, ok bool) {
// Defer

type Defer struct {
Func *FuncValue // function value
GoFunc *NativeValue // go function value
Args []TypedValue // arguments
Source *DeferStmt // source
Parent *Block
Func *FuncValue // function value
GoFunc *NativeValue // go function value
Receiver TypedValue // for methods
Args []TypedValue // arguments
Source *DeferStmt // source
Parent *Block

// PanicScope is set to the value of the Machine's PanicScope when the
// defer is created. The PanicScope of the Machine is incremented each time
// a panic occurs and is decremented each time a panic is recovered.
PanicScope uint
}

//----------------------------------------
// Exception

// Exception represents a panic that originates from a gno program.
type Exception struct {
// Value is the value passed to panic.
Value TypedValue
// Frames is a snapshot of the Machine frames at the time panic() is called.
// It is used to determine whether recover() can be called (it may only be
// called directly by a referred function) and to determine whether the
// current execution of a deferred function is happening in the context of
// a panic.
Frames []*Frame

Stacktrace Stacktrace
}

func (e Exception) Sprint(m *Machine) string {
return e.Value.Sprint(m)
}

// UnhandledPanicError represents an error thrown when a panic is not handled in the realm.
type UnhandledPanicError struct {
Descriptor string // Description of the unhandled panic.
}

func (e UnhandledPanicError) Error() string {
return e.Descriptor
}

type StacktraceCall struct {
Stmt Stmt
Frame *Frame
Expand Down
88 changes: 28 additions & 60 deletions gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io"
"os"
"reflect"
"slices"
"strings"
"sync"
"testing"
Expand All @@ -18,30 +19,6 @@ import (
"github.com/gnolang/overflow"
)

// Exception represents a panic that originates from a gno program.
type Exception struct {
// Value is the value passed to panic.
Value TypedValue
// Frame is used to reference the frame a panic occurred in so that recover() knows if the
// currently executing deferred function is able to recover from the panic.
Frame *Frame

Stacktrace Stacktrace
}

func (e Exception) Sprint(m *Machine) string {
return e.Value.Sprint(m)
}

// UnhandledPanicError represents an error thrown when a panic is not handled in the realm.
type UnhandledPanicError struct {
Descriptor string // Description of the unhandled panic.
}

func (e UnhandledPanicError) Error() string {
return e.Descriptor
}

//----------------------------------------
// Machine

Expand Down Expand Up @@ -72,13 +49,6 @@ type Machine struct {
Store Store
Context interface{}
GasMeter store.GasMeter
// PanicScope is incremented each time a panic occurs and is reset to
// zero when it is recovered.
PanicScope uint
// DeferPanicScope is set to the value of the defer's panic scope before
// it is executed. It is reset to zero after the defer functions in the current
// scope have finished executing.
DeferPanicScope uint
}

// NewMachine initializes a new gno virtual machine, acting as a shorthand
Expand Down Expand Up @@ -984,30 +954,29 @@ type Op uint8
const (

/* Control operators */
OpInvalid Op = 0x00 // invalid
OpHalt Op = 0x01 // halt (e.g. last statement)
OpNoop Op = 0x02 // no-op
OpExec Op = 0x03 // exec next statement
OpPrecall Op = 0x04 // sets X (func) to frame
OpCall Op = 0x05 // call(Frame.Func, [...])
OpCallNativeBody Op = 0x06 // call body is native
OpReturn Op = 0x07 // return ...
OpReturnFromBlock Op = 0x08 // return results (after defers)
OpReturnToBlock Op = 0x09 // copy results to block (before defer)
OpDefer Op = 0x0A // defer call(X, [...])
OpCallDeferNativeBody Op = 0x0B // call body is native
OpGo Op = 0x0C // go call(X, [...])
OpSelect Op = 0x0D // exec next select case
OpSwitchClause Op = 0x0E // exec next switch clause
OpSwitchClauseCase Op = 0x0F // exec next switch clause case
OpTypeSwitch Op = 0x10 // exec type switch clauses (all)
OpIfCond Op = 0x11 // eval cond
OpPopValue Op = 0x12 // pop X
OpPopResults Op = 0x13 // pop n call results
OpPopBlock Op = 0x14 // pop block NOTE breaks certain invariants.
OpPopFrameAndReset Op = 0x15 // pop frame and reset.
OpPanic1 Op = 0x16 // pop exception and pop call frames.
OpPanic2 Op = 0x17 // pop call frames.
OpInvalid Op = 0x00 // invalid
OpHalt Op = 0x01 // halt (e.g. last statement)
OpNoop Op = 0x02 // no-op
OpExec Op = 0x03 // exec next statement
OpPrecall Op = 0x04 // sets X (func) to frame
OpCall Op = 0x05 // call(Frame.Func, [...])
OpCallNativeBody Op = 0x06 // call body is native
OpReturn Op = 0x07 // return ...
OpReturnFromBlock Op = 0x08 // return results (after defers)
OpReturnToBlock Op = 0x09 // copy results to block (before defer)
OpDefer Op = 0x0A // defer call(X, [...])
OpGo Op = 0x0B // go call(X, [...])
OpSelect Op = 0x0C // exec next select case
OpSwitchClause Op = 0x0D // exec next switch clause
OpSwitchClauseCase Op = 0x0E // exec next switch clause case
OpTypeSwitch Op = 0x0F // exec type switch clauses (all)
OpIfCond Op = 0x10 // eval cond
OpPopValue Op = 0x11 // pop X
OpPopResults Op = 0x12 // pop n call results
OpPopBlock Op = 0x13 // pop block NOTE breaks certain invariants.
OpPopFrameAndReset Op = 0x14 // pop frame and reset.
OpPanic1 Op = 0x15 // pop exception and pop call frames.
OpPanic2 Op = 0x16 // pop call frames.

/* Unary & binary operators */
OpUpos Op = 0x20 // + (unary)
Expand Down Expand Up @@ -1295,9 +1264,6 @@ func (m *Machine) Run() {
case OpPanic2:
m.incrCPU(OpCPUPanic2)
m.doOpPanic2()
case OpCallDeferNativeBody:
m.incrCPU(OpCPUCallDeferNativeBody)
m.doOpCallDeferNativeBody()
case OpGo:
m.incrCPU(OpCPUGo)
panic("not yet implemented")
Expand Down Expand Up @@ -2140,16 +2106,18 @@ func (m *Machine) CheckEmpty() error {
}

func (m *Machine) Panic(ex TypedValue) {
// Skip the last frame, as it's the one of the panic() (or native)
// function call.
panicFrames := slices.Clone(m.Frames[:len(m.Frames)-1])
m.Exceptions = append(
m.Exceptions,
Exception{
Value: ex,
Frame: m.MustLastCallFrame(1),
Frames: panicFrames,
Stacktrace: m.Stacktrace(),
},
)

m.PanicScope++
m.PopUntilLastCallFrame()
m.PushOp(OpPanic2)
m.PushOp(OpReturnCallDefers)
Expand Down
Loading
Loading