Skip to content

Commit

Permalink
Merge branch 'feature/compiler' of https://github.com/onflow/cadence
Browse files Browse the repository at this point in the history
…into supun/casting
  • Loading branch information
SupunS committed Feb 14, 2025
2 parents 4fa44fc + 56c8fbd commit b6e8f27
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 3 deletions.
12 changes: 9 additions & 3 deletions bbq/compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -634,9 +634,15 @@ func (c *Compiler[_]) VisitForStatement(_ *ast.ForStatement) (_ struct{}) {
panic(errors.NewUnreachableError())
}

func (c *Compiler[_]) VisitEmitStatement(_ *ast.EmitStatement) (_ struct{}) {
// TODO
panic(errors.NewUnreachableError())
func (c *Compiler[_]) VisitEmitStatement(statement *ast.EmitStatement) (_ struct{}) {
c.compileExpression(statement.InvocationExpression)
eventType := c.ExtendedElaboration.EmitStatementEventType(statement)
typeIndex := c.getOrAddType(eventType)
c.codeGen.Emit(opcode.InstructionEmitEvent{
TypeIndex: typeIndex,
})

return
}

func (c *Compiler[_]) VisitSwitchStatement(statement *ast.SwitchStatement) (_ struct{}) {
Expand Down
41 changes: 41 additions & 0 deletions bbq/compiler/compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -654,3 +654,44 @@ func TestCompileSwitch(t *testing.T) {
program.Constants,
)
}

func TestCompileEmit(t *testing.T) {

t.Parallel()

checker, err := ParseAndCheck(t, `
event Inc(val: Int)
fun test(x: Int) {
emit Inc(val: x)
}
`)
require.NoError(t, err)

compiler := NewInstructionCompiler(checker)
program := compiler.Compile()

require.Len(t, program.Functions, 2)

var testFunction *bbq.Function[opcode.Instruction]
for _, f := range compiler.ExportFunctions() {
if f.Name == "test" {
testFunction = f
}
}
require.NotNil(t, testFunction)

assert.Equal(t,
[]opcode.Instruction{
opcode.InstructionGetLocal{LocalIndex: 0},
opcode.InstructionTransfer{TypeIndex: 0},
opcode.InstructionGetGlobal{GlobalIndex: 1},
opcode.InstructionInvoke{},
opcode.InstructionEmitEvent{TypeIndex: 1},
opcode.InstructionReturn{},
},
testFunction.Code,
)

assert.Empty(t, program.Constants)
}
4 changes: 4 additions & 0 deletions bbq/compiler/extended_elaboration.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ func (e *ExtendedElaboration) CastingExpressionTypes(expression *ast.CastingExpr
return e.elaboration.CastingExpressionTypes(expression)
}

func (e *ExtendedElaboration) EmitStatementEventType(statement *ast.EmitStatement) *sema.CompositeType {
return e.elaboration.EmitStatementEventType(statement)
}

func (e *ExtendedElaboration) ResultVariableType(enclosingBlock ast.Element) (typ sema.Type, exist bool) {
if e.resultVariableTypes != nil {
types, ok := e.resultVariableTypes[enclosingBlock]
Expand Down
32 changes: 32 additions & 0 deletions bbq/opcode/instructions.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions bbq/opcode/instructions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -572,3 +572,16 @@
push:
- name: "result"
type: "bool"

# Other

- name: "emitEvent"
description:
Pops an event off the stack and then emits it.
operands:
- name: "typeIndex"
type: "index"
valueEffects:
pop:
- name: "event"
type: "value"
6 changes: 6 additions & 0 deletions bbq/opcode/opcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,10 @@ const (
_
_
_
_
_
_

// Other
EmitEvent
)
4 changes: 4 additions & 0 deletions bbq/opcode/opcode_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions bbq/vm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ import (
"github.com/onflow/cadence/test_utils/common_utils"
)

// OnEventEmittedFunc is a function that is triggered when an event is emitted by the program.
type OnEventEmittedFunc func(
event *CompositeValue,
eventType *interpreter.CompositeStaticType,
) error

type Config struct {
interpreter.Storage
common.MemoryGauge
Expand All @@ -41,6 +47,9 @@ type Config struct {
MutationDuringCapabilityControllerIteration bool
referencedResourceKindedValues ReferencedResourceKindedValues

// OnEventEmitted is triggered when an event is emitted by the program
OnEventEmitted OnEventEmittedFunc

// TODO: These are temporary. Remove once storing/reading is supported for VM values.
inter *interpreter.Interpreter
TypeLoader func(location common.Location, typeID interpreter.TypeID) sema.CompositeKindedType
Expand Down
38 changes: 38 additions & 0 deletions bbq/vm/test/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3646,6 +3646,44 @@ func TestDefaultFunctionsWithConditions(t *testing.T) {
})
}

func TestCompileEmit(t *testing.T) {

t.Parallel()

var eventEmitted bool

vmConfig := vm.NewConfig(interpreter.NewInMemoryStorage(nil))
vmConfig.OnEventEmitted = func(event *vm.CompositeValue, eventType *interpreter.CompositeStaticType) error {
require.False(t, eventEmitted)
eventEmitted = true

assert.Equal(t,
common.ScriptLocation{0x1}.TypeID(nil, "Inc"),
eventType.ID(),
)

return nil
}

_, err := compileAndInvokeWithOptions(t,
`
event Inc(val: Int)
fun test(x: Int) {
emit Inc(val: x)
}
`,
"test",
CompilerAndVMOptions{
VMConfig: vmConfig,
},
vm.NewIntValue(1),
)
require.NoError(t, err)

require.True(t, eventEmitted)
}

func TestCasting(t *testing.T) {

t.Parallel()
Expand Down
18 changes: 18 additions & 0 deletions bbq/vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,8 @@ func (vm *VM) run() {
opNot(vm)
case opcode.InstructionUnwrap:
opUnwrap(vm)
case opcode.InstructionEmitEvent:
onEmitEvent(vm, ins)
default:
panic(errors.NewUnexpectedError("cannot execute instruction of type %T", ins))
}
Expand All @@ -882,6 +884,22 @@ func (vm *VM) run() {
}
}

func onEmitEvent(vm *VM, ins opcode.InstructionEmitEvent) {
eventValue := vm.pop().(*CompositeValue)

onEventEmitted := vm.config.OnEventEmitted
if onEventEmitted == nil {
return
}

eventType := vm.loadType(ins.TypeIndex).(*interpreter.CompositeStaticType)

err := onEventEmitted(eventValue, eventType)
if err != nil {
panic(err)
}
}

func (vm *VM) initializeConstant(index uint16) (value Value) {
executable := vm.callFrame.executable

Expand Down

0 comments on commit b6e8f27

Please sign in to comment.