Skip to content

Commit

Permalink
Merge pull request #3505 from onflow/bastian/add-type-isrecovered
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolent authored Aug 17, 2024
2 parents 15ca984 + 8e40cb6 commit 4d6993b
Show file tree
Hide file tree
Showing 8 changed files with 300 additions and 103 deletions.
2 changes: 2 additions & 0 deletions runtime/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,8 @@ func (e *interpreterEnvironment) parseAndCheckProgramWithRecovery(
return program, elaboration, err
}

recoveredElaboration.IsRecovered = true

// If recovery succeeded, return the recovered program and elaboration
return recoveredProgram, recoveredElaboration, nil
}
Expand Down
60 changes: 24 additions & 36 deletions runtime/ft_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -941,7 +941,6 @@ func TestRuntimeBrokenFungibleTokenRecovery(t *testing.T) {
}

var events []cadence.Event
var logs []string

signerAccount := contractsAddress

Expand Down Expand Up @@ -970,9 +969,6 @@ func TestRuntimeBrokenFungibleTokenRecovery(t *testing.T) {
OnDecodeArgument: func(b []byte, t cadence.Type) (value cadence.Value, err error) {
return json.Decode(nil, b)
},
OnProgramLog: func(message string) {
logs = append(logs, message)
},
OnRecoverProgram: func(program *ast.Program, location common.Location) (*ast.Program, error) {

// TODO: generalize
Expand Down Expand Up @@ -1153,10 +1149,18 @@ func TestRuntimeBrokenFungibleTokenRecovery(t *testing.T) {
}
execute {
log(ExampleToken.totalSupply)
log(self.vault.balance)
log(ExampleToken.getType())
log(self.vault.getType())
assert(ExampleToken.totalSupply == 4321.00000000)
assert(self.vault.balance == 1234.00000000)
let exampleTokenType = ExampleToken.getType()
assert(exampleTokenType == Type<ExampleToken>())
assert(exampleTokenType.isRecovered)
assert(Type<ExampleToken>().isRecovered)
let vaultType = self.vault.getType()
assert(vaultType == Type<@ExampleToken.Vault>())
assert(vaultType.isRecovered)
assert(Type<@ExampleToken.Vault>().isRecovered)
let exampleVault <- self.vault
let someVault <- exampleVault as! @{FungibleToken.Vault}
Expand All @@ -1180,16 +1184,6 @@ func TestRuntimeBrokenFungibleTokenRecovery(t *testing.T) {
)
require.NoError(t, err)

require.Equal(t,
[]string{
"4321.00000000",
"1234.00000000",
"Type<A.0000000000000001.ExampleToken>()",
"Type<A.0000000000000001.ExampleToken.Vault>()",
},
logs,
)

// Send another transaction that calls a function on the stored vault.
// Function calls on recovered values should panic.

Expand All @@ -1215,8 +1209,6 @@ func TestRuntimeBrokenFungibleTokenRecovery(t *testing.T) {
}
`

logs = nil

err = runtime.ExecuteTransaction(
Script{
Source: []byte(transaction2),
Expand Down Expand Up @@ -1248,10 +1240,18 @@ func TestRuntimeBrokenFungibleTokenRecovery(t *testing.T) {
}
execute {
log(ExampleToken.totalSupply)
log(self.vault.balance)
log(ExampleToken.getType())
log(self.vault.getType())
assert(ExampleToken.totalSupply == 4321.00000000)
assert(self.vault.balance == 1234.00000000)
let exampleTokenType = ExampleToken.getType()
assert(exampleTokenType == Type<ExampleToken>())
assert(exampleTokenType.isRecovered)
assert(Type<ExampleToken>().isRecovered)
let vaultType = self.vault.getType()
assert(vaultType == Type<@ExampleToken.Vault>())
assert(vaultType.isRecovered)
assert(Type<@ExampleToken.Vault>().isRecovered)
let someVault <- self.vault
let exampleVault <- someVault as! @ExampleToken.Vault
Expand All @@ -1261,8 +1261,6 @@ func TestRuntimeBrokenFungibleTokenRecovery(t *testing.T) {
}
`

logs = nil

err = runtime.ExecuteTransaction(
Script{
Source: []byte(transaction3),
Expand All @@ -1274,14 +1272,4 @@ func TestRuntimeBrokenFungibleTokenRecovery(t *testing.T) {
},
)
require.NoError(t, err)

require.Equal(t,
[]string{
"4321.00000000",
"1234.00000000",
"Type<A.0000000000000001.ExampleToken>()",
"Type<A.0000000000000001.ExampleToken.Vault>()",
},
logs,
)
}
18 changes: 18 additions & 0 deletions runtime/interpreter/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,24 @@ func (v TypeValue) GetMember(interpreter *Interpreter, _ LocationRange, name str
return AsBoolValue(result)
},
)

case sema.MetaTypeIsRecoveredFieldName:
staticType := v.Type
if staticType == nil {
return FalseValue
}

location, _, err := common.DecodeTypeID(interpreter, string(staticType.ID()))
if err != nil || location == nil {
return FalseValue
}

elaboration := interpreter.getElaboration(location)
if elaboration == nil {
return FalseValue
}

return AsBoolValue(elaboration.IsRecovered)
}

return nil
Expand Down
3 changes: 3 additions & 0 deletions runtime/repl.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ func NewREPL() (*REPL, error) {
return baseActivation
},
OnEventEmitted: standardLibraryHandler.NewOnEventEmittedHandler(),
ImportLocationHandler: func(inter *interpreter.Interpreter, location common.Location) interpreter.Import {
panic(fmt.Errorf("cannot import %s: Importing programs is not supported yet", location.ID()))
},
}

inter, err := interpreter.NewInterpreter(
Expand Down
2 changes: 2 additions & 0 deletions runtime/sema/elaboration.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ type Elaboration struct {
TransactionTypes []*TransactionType
semanticAccesses map[ast.Access]Access
isChecking bool
// IsRecovered is true if the program was recovered (see runtime.Interface.RecoverProgram)
IsRecovered bool
}

func NewElaboration(gauge common.MemoryGauge) *Elaboration {
Expand Down
38 changes: 26 additions & 12 deletions runtime/sema/meta_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,6 @@

package sema

const MetaTypeIdentifierFieldName = "identifier"

const metaTypeIdentifierFieldDocString = `
The fully-qualified identifier of the type
`

const MetaTypeIsSubtypeFunctionName = "isSubtype"

const metaTypeIsSubtypeFunctionDocString = `
Returns true if this type is a subtype of the given type at run-time
`

const MetaTypeName = "Type"

// MetaType represents the type of a type.
Expand All @@ -49,6 +37,12 @@ var MetaType = &SimpleType{

var MetaTypeAnnotation = NewTypeAnnotation(MetaType)

const MetaTypeIdentifierFieldName = "identifier"

const metaTypeIdentifierFieldDocString = `
The fully-qualified identifier of the type
`

var MetaTypeIsSubtypeFunctionType = NewSimpleFunctionType(
FunctionPurityView,
[]Parameter{
Expand All @@ -61,6 +55,20 @@ var MetaTypeIsSubtypeFunctionType = NewSimpleFunctionType(
BoolTypeAnnotation,
)

const MetaTypeIsSubtypeFunctionName = "isSubtype"

const metaTypeIsSubtypeFunctionDocString = `
Returns true if this type is a subtype of the given type at run-time
`

const MetaTypeIsRecoveredFieldName = "isRecovered"

var MetaTypeIsRecoveredFieldType = BoolType

const metaTypeIsRecoveredFieldDocString = `
The type was defined through a recovered program
`

func init() {
MetaType.Members = func(t *SimpleType) map[string]MemberResolver {
return MembersAsResolvers([]*Member{
Expand All @@ -76,6 +84,12 @@ func init() {
MetaTypeIsSubtypeFunctionType,
metaTypeIsSubtypeFunctionDocString,
),
NewUnmeteredPublicConstantFieldMember(
t,
MetaTypeIsRecoveredFieldName,
MetaTypeIsRecoveredFieldType,
metaTypeIsRecoveredFieldDocString,
),
})
}
}
Loading

0 comments on commit 4d6993b

Please sign in to comment.