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

Allow built-in composite types to be storable #2881

Closed
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
10 changes: 10 additions & 0 deletions runtime/cmd/decode-state-values/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,16 @@ func (i interpreterStorage) CheckHealth() error {
panic("unexpected CheckHealth call")
}

func (i interpreterStorage) RootInterpreter() *interpreter.Interpreter {
panic("unexpected RootInterpreter call")

}

func (i interpreterStorage) SetRootInterpreter(_ *interpreter.Interpreter) {
panic("unexpected SetRootInterpreter call")

}

// load

func load() {
Expand Down
32 changes: 20 additions & 12 deletions runtime/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,10 @@ func (e *interpreterEnvironment) getCode(location common.Location) (code []byte,
func (e *interpreterEnvironment) newInterpreter(
location common.Location,
program *interpreter.Program,
) (*interpreter.Interpreter, error) {
) (
inter *interpreter.Interpreter,
err error,
) {

sharedState := e.runtimeInterface.GetInterpreterSharedState()
if sharedState != nil {
Expand All @@ -633,25 +636,30 @@ func (e *interpreterEnvironment) newInterpreter(
// Even though suboptimal, this ensures that no writes "leak" from one top-level entry call to another
// (when interpreter shared state is reused).

return interpreter.NewInterpreterWithSharedState(
inter, err = interpreter.NewInterpreterWithSharedState(
program,
location,
sharedState,
)
}
} else {

inter, err := interpreter.NewInterpreter(
program,
location,
e.InterpreterConfig,
)
if err != nil {
return nil, err
inter, err = interpreter.NewInterpreter(
program,
location,
e.InterpreterConfig,
)
if err != nil {
return nil, err
}

e.runtimeInterface.SetInterpreterSharedState(inter.SharedState)
}

e.runtimeInterface.SetInterpreterSharedState(inter.SharedState)
if inter != nil {
inter.Storage().SetRootInterpreter(inter)
}

return inter, nil
return
}

func (e *interpreterEnvironment) newOnStatementHandler() interpreter.OnStatementFunc {
Expand Down
2 changes: 1 addition & 1 deletion runtime/interpreter/deepcopyremove_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,6 @@ func TestValueDeepCopyAndDeepRemove(t *testing.T) {
require.Equal(t, 1, count)
}

func newUnmeteredInMemoryStorage() InMemoryStorage {
func newUnmeteredInMemoryStorage() *InMemoryStorage {
return NewInMemoryStorage(nil)
}
20 changes: 20 additions & 0 deletions runtime/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ type Storage interface {
atree.SlabStorage
GetStorageMap(address common.Address, domain string, createIfNotExists bool) *StorageMap
CheckHealth() error
RootInterpreter() *Interpreter
SetRootInterpreter(inter *Interpreter)
}

type ReferencedResourceKindedValues map[atree.StorageID]map[ReferenceTrackedResourceKindedValue]struct{}
Expand Down Expand Up @@ -312,6 +314,11 @@ func NewInterpreterWithSharedState(

interpreter.activations.PushNewWithParent(baseActivation)

storage := interpreter.Storage()
if storage != nil && storage.RootInterpreter() == nil {
storage.SetRootInterpreter(interpreter)
}

return interpreter, nil
}

Expand Down Expand Up @@ -5574,3 +5581,16 @@ func (interpreter *Interpreter) withResourceDestruction(

f()
}

// GetInterpreter returns the interpreter for the given location.
// The program code might need to be loaded.
func (interpreter *Interpreter) GetInterpreter(location common.Location) *Interpreter {

// NOTE: standard library values have no location

if location == nil || interpreter.Location == location {
return interpreter
}

return interpreter.EnsureLoaded(location)
}
23 changes: 16 additions & 7 deletions runtime/interpreter/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,14 @@ func (k StorageKey) IsLess(o StorageKey) bool {
// InMemoryStorage
type InMemoryStorage struct {
*atree.BasicSlabStorage
StorageMaps map[StorageKey]*StorageMap
memoryGauge common.MemoryGauge
StorageMaps map[StorageKey]*StorageMap
memoryGauge common.MemoryGauge
rootInterpreter *Interpreter
}

var _ Storage = InMemoryStorage{}
var _ Storage = &InMemoryStorage{}

func NewInMemoryStorage(memoryGauge common.MemoryGauge) InMemoryStorage {
func NewInMemoryStorage(memoryGauge common.MemoryGauge) *InMemoryStorage {
decodeStorable := func(decoder *cbor.StreamDecoder, storableSlabStorageID atree.StorageID) (atree.Storable, error) {
return DecodeStorable(decoder, storableSlabStorageID, memoryGauge)
}
Expand All @@ -136,14 +137,22 @@ func NewInMemoryStorage(memoryGauge common.MemoryGauge) InMemoryStorage {
decodeTypeInfo,
)

return InMemoryStorage{
return &InMemoryStorage{
BasicSlabStorage: slabStorage,
StorageMaps: make(map[StorageKey]*StorageMap),
memoryGauge: memoryGauge,
}
}

func (i InMemoryStorage) GetStorageMap(
func (i *InMemoryStorage) RootInterpreter() *Interpreter {
return i.rootInterpreter
}

func (i *InMemoryStorage) SetRootInterpreter(inter *Interpreter) {
i.rootInterpreter = inter
}

func (i *InMemoryStorage) GetStorageMap(
address common.Address,
domain string,
createIfNotExists bool,
Expand All @@ -159,7 +168,7 @@ func (i InMemoryStorage) GetStorageMap(
return storageMap
}

func (i InMemoryStorage) CheckHealth() error {
func (i *InMemoryStorage) CheckHealth() error {
_, err := atree.CheckStorageHealth(i, -1)
return err
}
Expand Down
44 changes: 21 additions & 23 deletions runtime/interpreter/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -16502,7 +16502,7 @@ func (v *CompositeValue) Destroy(interpreter *Interpreter, locationRange Locatio
attachment.Destroy(interpreter, locationRange)
})

interpreter = v.getInterpreter(interpreter)
interpreter = interpreter.GetInterpreter(v.Location)

// if composite was deserialized, dynamically link in the destructor
if v.Destructor == nil {
Expand Down Expand Up @@ -16616,7 +16616,7 @@ func (v *CompositeValue) GetMember(interpreter *Interpreter, locationRange Locat
}
}

interpreter = v.getInterpreter(interpreter)
interpreter = interpreter.GetInterpreter(v.Location)

// Dynamically link in the computed fields, injected fields, and functions

Expand All @@ -16643,20 +16643,6 @@ func (v *CompositeValue) checkInvalidatedResourceUse(locationRange LocationRange
}
}

func (v *CompositeValue) getInterpreter(interpreter *Interpreter) *Interpreter {

// Get the correct interpreter. The program code might need to be loaded.
// NOTE: standard library values have no location

location := v.Location

if location == nil || interpreter.Location == location {
return interpreter
}

return interpreter.EnsureLoaded(v.Location)
}

func (v *CompositeValue) GetComputedFields(interpreter *Interpreter) map[string]ComputedField {
if v.computedFields == nil {
v.computedFields = interpreter.GetCompositeValueComputedFields(v)
Expand Down Expand Up @@ -17128,7 +17114,7 @@ func (v *CompositeValue) ConformsToStaticType(
return true
}

func (v *CompositeValue) IsStorable() bool {
func (v *CompositeValue) IsStorable(interpreter *Interpreter) bool {

// Only structures, resources, enums, and contracts can be stored.
// Contracts are not directly storable by programs,
Expand All @@ -17138,23 +17124,35 @@ func (v *CompositeValue) IsStorable() bool {
case common.CompositeKindStructure,
common.CompositeKindResource,
common.CompositeKindEnum,
common.CompositeKindAttachment,
common.CompositeKindContract:
break
common.CompositeKindAttachment:
if v.Location == nil {
return interpreter.MustSemaTypeOfValue(v).IsStorable(map[*sema.Member]bool{})
} else {
return true
}
case common.CompositeKindContract:
return v.Location != nil
default:
return false
}

// Composite value's of native/built-in types are not storable for now
return v.Location != nil
}

func (v *CompositeValue) Storable(
storage atree.SlabStorage,
address atree.Address,
maxInlineSize uint64,
) (atree.Storable, error) {
if !v.IsStorable() {

interpreterStorage, ok := storage.(Storage)
if !ok {
panic(errors.NewUnreachableError())
}

interpreter := interpreterStorage.RootInterpreter().
GetInterpreter(v.Location)

if !v.IsStorable(interpreter) {
return NonStorable{Value: v}, nil
}

Expand Down
Loading