Skip to content
14 changes: 13 additions & 1 deletion bbq/compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1619,7 +1619,19 @@ func (c *Compiler[_, _]) VisitEmitStatement(statement *ast.EmitStatement) (_ str
invocationExpression := statement.InvocationExpression
arguments := invocationExpression.Arguments
invocationTypes := c.DesugaredElaboration.InvocationExpressionTypes(invocationExpression)
c.compileArguments(arguments, invocationTypes)

// Instead of compiling arguments as usual (via compileArguments),
// only convert the arguments and don't transfer them.

for index, argument := range arguments {
c.compileExpression(argument.Expression)

parameterType := invocationTypes.ParameterTypes[index]
parameterTypeIndex := c.getOrAddType(parameterType)
c.emit(opcode.InstructionConvert{
Type: parameterTypeIndex,
})
}

argCount := len(arguments)
if argCount >= math.MaxUint16 {
Expand Down
2 changes: 1 addition & 1 deletion bbq/compiler/compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1336,7 +1336,7 @@ func TestCompileEmit(t *testing.T) {
opcode.InstructionStatement{},
// x
opcode.InstructionGetLocal{Local: xIndex},
opcode.InstructionTransferAndConvert{Type: 1},
opcode.InstructionConvert{Type: 1},
// emit
opcode.InstructionEmitEvent{
Type: 2,
Expand Down
22 changes: 21 additions & 1 deletion bbq/vm/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ type Context struct {
// This cache-alike is maintained per execution.
// TODO: Re-use the conversions from the compiler.
// TODO: Maybe extend/share this between executions.
semaTypeCache map[sema.TypeID]sema.Type
semaTypeCache map[sema.TypeID]sema.Type
semaAccessCache map[interpreter.Authorization]sema.Access

// linkedGlobalsCache is a local cache-alike that is being used to hold already linked imports.
linkedGlobalsCache map[common.Location]LinkedGlobals
Expand Down Expand Up @@ -531,3 +532,22 @@ func (c *Context) GetEntitlementMapType(
func (c *Context) LocationRange() interpreter.LocationRange {
return c.getLocationRange()
}

func (c *Context) SemaAccessFromStaticAuthorization(auth interpreter.Authorization) (sema.Access, error) {
semaAccess, ok := c.semaAccessCache[auth]
if ok {
return semaAccess, nil
}

semaAccess, err := interpreter.ConvertStaticAuthorizationToSemaAccess(auth, c)
if err != nil {
return nil, err
}

if c.semaAccessCache == nil {
c.semaAccessCache = make(map[interpreter.Authorization]sema.Access)
}
c.semaAccessCache[auth] = semaAccess

return semaAccess, nil
}
38 changes: 21 additions & 17 deletions interpreter/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,93 +417,97 @@ type NoOpReferenceCreationContext struct{}

var _ interpreter.ReferenceCreationContext = NoOpReferenceCreationContext{}

func (n NoOpReferenceCreationContext) ClearReferencedResourceKindedValues(_ atree.ValueID) {
func (NoOpReferenceCreationContext) ClearReferencedResourceKindedValues(_ atree.ValueID) {
// NO-OP
}

func (n NoOpReferenceCreationContext) ReferencedResourceKindedValues(_ atree.ValueID) map[*interpreter.EphemeralReferenceValue]struct{} {
func (NoOpReferenceCreationContext) ReferencedResourceKindedValues(_ atree.ValueID) map[*interpreter.EphemeralReferenceValue]struct{} {
// NO-OP
return nil
}

func (n NoOpReferenceCreationContext) MaybeTrackReferencedResourceKindedValue(_ *interpreter.EphemeralReferenceValue) {
func (NoOpReferenceCreationContext) MaybeTrackReferencedResourceKindedValue(_ *interpreter.EphemeralReferenceValue) {
// NO-OP
}

func (n NoOpReferenceCreationContext) MeterMemory(_ common.MemoryUsage) error {
func (NoOpReferenceCreationContext) MeterMemory(_ common.MemoryUsage) error {
// NO-OP
return nil
}

func (n NoOpReferenceCreationContext) MeterComputation(_ common.ComputationUsage) error {
func (NoOpReferenceCreationContext) MeterComputation(_ common.ComputationUsage) error {
// NO-OP
return nil
}

func (n NoOpReferenceCreationContext) ReadStored(_ common.Address, _ common.StorageDomain, _ interpreter.StorageMapKey) interpreter.Value {
func (NoOpReferenceCreationContext) ReadStored(_ common.Address, _ common.StorageDomain, _ interpreter.StorageMapKey) interpreter.Value {
// NO-OP
return nil
}

func (n NoOpReferenceCreationContext) GetEntitlementType(_ interpreter.TypeID) (*sema.EntitlementType, error) {
func (NoOpReferenceCreationContext) GetEntitlementType(_ interpreter.TypeID) (*sema.EntitlementType, error) {
// NO-OP
return nil, nil
}

func (n NoOpReferenceCreationContext) GetEntitlementMapType(_ interpreter.TypeID) (*sema.EntitlementMapType, error) {
func (NoOpReferenceCreationContext) GetEntitlementMapType(_ interpreter.TypeID) (*sema.EntitlementMapType, error) {
// NO-OP
return nil, nil
}

func (n NoOpReferenceCreationContext) GetInterfaceType(_ common.Location, _ string, _ interpreter.TypeID) (*sema.InterfaceType, error) {
func (NoOpReferenceCreationContext) GetInterfaceType(_ common.Location, _ string, _ interpreter.TypeID) (*sema.InterfaceType, error) {
// NO-OP
return nil, nil
}

func (n NoOpReferenceCreationContext) GetCompositeType(_ common.Location, _ string, _ interpreter.TypeID) (*sema.CompositeType, error) {
func (NoOpReferenceCreationContext) GetCompositeType(_ common.Location, _ string, _ interpreter.TypeID) (*sema.CompositeType, error) {
// NO-OP
return nil, nil
}

func (n NoOpReferenceCreationContext) IsTypeInfoRecovered(_ common.Location) bool {
func (NoOpReferenceCreationContext) IsTypeInfoRecovered(_ common.Location) bool {
// NO-OP
return false
}

func (n NoOpReferenceCreationContext) SemaTypeFromStaticType(_ interpreter.StaticType) sema.Type {
func (NoOpReferenceCreationContext) SemaTypeFromStaticType(_ interpreter.StaticType) sema.Type {
// NO-OP
return nil
}

func (NoOpReferenceCreationContext) SemaAccessFromStaticAuthorization(interpreter.Authorization) (sema.Access, error) {
panic(errors.NewUnreachableError())
}

type NoOpFunctionCreationContext struct {
//Just to make the compiler happy
interpreter.ResourceDestructionContext
}

var _ interpreter.FunctionCreationContext = NoOpFunctionCreationContext{}

func (n NoOpFunctionCreationContext) ClearReferencedResourceKindedValues(_ atree.ValueID) {
func (NoOpFunctionCreationContext) ClearReferencedResourceKindedValues(_ atree.ValueID) {
// NO-OP
}

func (n NoOpFunctionCreationContext) ReferencedResourceKindedValues(
func (NoOpFunctionCreationContext) ReferencedResourceKindedValues(
_ atree.ValueID,
) map[*interpreter.EphemeralReferenceValue]struct{} {
// NO-OP
return nil
}

func (n NoOpFunctionCreationContext) CheckInvalidatedResourceOrResourceReference(
func (NoOpFunctionCreationContext) CheckInvalidatedResourceOrResourceReference(
_ interpreter.Value,
) {
// NO-OP
}

func (n NoOpFunctionCreationContext) MaybeTrackReferencedResourceKindedValue(_ *interpreter.EphemeralReferenceValue) {
func (NoOpFunctionCreationContext) MaybeTrackReferencedResourceKindedValue(_ *interpreter.EphemeralReferenceValue) {
// NO-OP
}

func (n NoOpFunctionCreationContext) MeterMemory(_ common.MemoryUsage) error {
func (NoOpFunctionCreationContext) MeterMemory(_ common.MemoryUsage) error {
// NO-OP
return nil
}
Expand Down
53 changes: 29 additions & 24 deletions interpreter/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type TypeConverter interface {
common.MemoryGauge
StaticTypeConversionHandler
SemaTypeFromStaticType(staticType StaticType) sema.Type
SemaAccessFromStaticAuthorization(auth Authorization) (sema.Access, error)
}

var _ TypeConverter = &Interpreter{}
Expand Down Expand Up @@ -538,98 +539,102 @@ type NoOpStringContext struct {

var _ ValueStringContext = NoOpStringContext{}

func (ctx NoOpStringContext) MeterMemory(_ common.MemoryUsage) error {
func (NoOpStringContext) MeterMemory(_ common.MemoryUsage) error {
return nil
}

func (ctx NoOpStringContext) MeterComputation(_ common.ComputationUsage) error {
func (NoOpStringContext) MeterComputation(_ common.ComputationUsage) error {
return nil
}

func (ctx NoOpStringContext) WithContainerMutationPrevention(_ atree.ValueID, f func()) {
func (NoOpStringContext) WithContainerMutationPrevention(_ atree.ValueID, f func()) {
f()
}

func (ctx NoOpStringContext) ValidateContainerMutation(_ atree.ValueID) {
func (NoOpStringContext) ValidateContainerMutation(_ atree.ValueID) {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) EnforceNotResourceDestruction(_ atree.ValueID) {
func (NoOpStringContext) EnforceNotResourceDestruction(_ atree.ValueID) {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) ReadStored(_ common.Address, _ common.StorageDomain, _ StorageMapKey) Value {
func (NoOpStringContext) ReadStored(_ common.Address, _ common.StorageDomain, _ StorageMapKey) Value {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) WriteStored(_ common.Address, _ common.StorageDomain, _ StorageMapKey, _ Value) (existed bool) {
func (NoOpStringContext) WriteStored(_ common.Address, _ common.StorageDomain, _ StorageMapKey, _ Value) (existed bool) {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) Storage() Storage {
func (NoOpStringContext) Storage() Storage {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) MaybeValidateAtreeValue(_ atree.Value) {
func (NoOpStringContext) MaybeValidateAtreeValue(_ atree.Value) {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) MaybeValidateAtreeStorage() {
func (NoOpStringContext) MaybeValidateAtreeStorage() {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) MaybeTrackReferencedResourceKindedValue(_ *EphemeralReferenceValue) {
func (NoOpStringContext) MaybeTrackReferencedResourceKindedValue(_ *EphemeralReferenceValue) {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) ClearReferencedResourceKindedValues(_ atree.ValueID) {
func (NoOpStringContext) ClearReferencedResourceKindedValues(_ atree.ValueID) {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) ReferencedResourceKindedValues(_ atree.ValueID) map[*EphemeralReferenceValue]struct{} {
func (NoOpStringContext) ReferencedResourceKindedValues(_ atree.ValueID) map[*EphemeralReferenceValue]struct{} {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) OnResourceOwnerChange(_ *CompositeValue, _ common.Address, _ common.Address) {
func (NoOpStringContext) OnResourceOwnerChange(_ *CompositeValue, _ common.Address, _ common.Address) {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) RecordStorageMutation() {
func (NoOpStringContext) RecordStorageMutation() {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) StorageMutatedDuringIteration() bool {
func (NoOpStringContext) StorageMutatedDuringIteration() bool {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) InStorageIteration() bool {
func (NoOpStringContext) InStorageIteration() bool {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) SetInStorageIteration(_ bool) {
func (NoOpStringContext) SetInStorageIteration(_ bool) {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) GetEntitlementType(_ TypeID) (*sema.EntitlementType, error) {
func (NoOpStringContext) GetEntitlementType(_ TypeID) (*sema.EntitlementType, error) {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) GetEntitlementMapType(_ TypeID) (*sema.EntitlementMapType, error) {
func (NoOpStringContext) GetEntitlementMapType(_ TypeID) (*sema.EntitlementMapType, error) {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) GetInterfaceType(_ common.Location, _ string, _ TypeID) (*sema.InterfaceType, error) {
func (NoOpStringContext) GetInterfaceType(_ common.Location, _ string, _ TypeID) (*sema.InterfaceType, error) {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) GetCompositeType(_ common.Location, _ string, _ TypeID) (*sema.CompositeType, error) {
func (NoOpStringContext) GetCompositeType(_ common.Location, _ string, _ TypeID) (*sema.CompositeType, error) {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) IsTypeInfoRecovered(_ common.Location) bool {
func (NoOpStringContext) IsTypeInfoRecovered(_ common.Location) bool {
panic(errors.NewUnreachableError())
}

func (ctx NoOpStringContext) SemaTypeFromStaticType(_ StaticType) sema.Type {
func (NoOpStringContext) SemaTypeFromStaticType(_ StaticType) sema.Type {
panic(errors.NewUnreachableError())
}

func (NoOpStringContext) SemaAccessFromStaticAuthorization(Authorization) (sema.Access, error) {
panic(errors.NewUnreachableError())
}
15 changes: 4 additions & 11 deletions interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5187,17 +5187,6 @@ func (interpreter *Interpreter) GetEntitlementMapType(typeID common.TypeID) (*se
return ty, nil
}

func MustConvertStaticAuthorizationToSemaAccess(
handler StaticAuthorizationConversionHandler,
auth Authorization,
) sema.Access {
access, err := ConvertStaticAuthorizationToSemaAccess(auth, handler)
if err != nil {
panic(err)
}
return access
}

func (interpreter *Interpreter) getElaboration(location common.Location) *sema.Elaboration {

// Ensure the program for this location is loaded,
Expand Down Expand Up @@ -6400,6 +6389,10 @@ func (interpreter *Interpreter) MaybeUpdateStorageReferenceMemberReceiver(
return member
}

func (interpreter *Interpreter) SemaAccessFromStaticAuthorization(auth Authorization) (sema.Access, error) {
return ConvertStaticAuthorizationToSemaAccess(auth, interpreter)
}

func StorageReference(
context ValueStaticTypeContext,
storageReference *StorageReferenceValue,
Expand Down
2 changes: 1 addition & 1 deletion interpreter/interpreter_statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ func (interpreter *Interpreter) VisitEmitStatement(statement *ast.EmitStatement)
argumentType := argumentTypes[i]
parameterType := parameterTypes[i]

eventFields[i] = TransferAndConvert(
eventFields[i] = ConvertAndBoxWithValidation(
interpreter,
value,
argumentType,
Expand Down
38 changes: 38 additions & 0 deletions interpreter/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7340,6 +7340,44 @@ func TestInterpretEmitEvent(t *testing.T) {
)
}

func BenchmarkEmit(b *testing.B) {

inter, err := parseCheckAndPrepareWithOptions(
b,
`
event Foo(ints: [Int])

fun test() {
var i = 0
while i < 1000 {
emit Foo(ints: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
i = i + 1
}
}
`,
ParseCheckAndInterpretOptions{
InterpreterConfig: &interpreter.Config{
OnEventEmitted: func(
_ interpreter.ValueExportContext,
_ *sema.CompositeType,
_ []interpreter.Value,
) error {
return nil
},
},
},
)
require.NoError(b, err)

b.ReportAllocs()
b.ResetTimer()

for i := 0; i < b.N; i++ {
_, err := inter.Invoke("test")
require.NoError(b, err)
}
}

func TestInterpretReferenceEventParameter(t *testing.T) {

t.Parallel()
Expand Down
Loading
Loading