Skip to content

Commit b3c69dc

Browse files
Merge pull request #357 from DJRHails/caller_functions
Caller functions
2 parents 177163a + ecdeeab commit b3c69dc

File tree

11 files changed

+204
-103
lines changed

11 files changed

+204
-103
lines changed

Sources/AST/ASTDumper.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ public class ASTDumper {
310310
writeLine("Any")
311311
case .errorType:
312312
writeLine("Flint error type \(rawType.name)")
313+
case .functionType(_):
314+
writeLine("function type \(rawType.name)")
313315
}
314316
}
315317

Sources/AST/Environment/Environment+Memory.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ extension Environment {
1818
case .inoutType(_): fatalError()
1919
case .any: return 0
2020
case .errorType: return 0
21+
case .functionType(_): return 0
2122

2223
case .stdlibType(let type):
2324
return types[type.rawValue]!.properties.reduce(0) { acc, element in

Sources/AST/Environment/Environment+Type.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ extension Environment {
1313
return type
1414
}
1515

16+
if let function = types[enclosingType]?.functions[property]?.first! {
17+
return .functionType(parameters: function.parameterTypes, result: function.resultType)
18+
}
19+
1620
guard let scopeContext = scopeContext, let type = scopeContext.type(for: property) else { return .errorType }
1721
return type
1822
}

Sources/AST/Environment/Environment.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public struct Environment {
6666

6767
/// The list of properties declared in a type which can be used as caller capabilities.
6868
func declaredCallerCapabilities(enclosingType: RawTypeIdentifier) -> [String] {
69-
return types[enclosingType]!.properties.compactMap { key, value in
69+
let properties: [String] = types[enclosingType]!.properties.compactMap { key, value in
7070
switch value.rawType {
7171
case .basicType(.address): return key
7272
case .fixedSizeArrayType(.basicType(.address), _): return key
@@ -75,6 +75,18 @@ public struct Environment {
7575
default: return nil
7676
}
7777
}
78+
let functions: [String] = types[enclosingType]!.functions.compactMap { name, functions in
79+
for function in functions {
80+
if function.resultType == .basicType(.address), function.parameterTypes == [] {
81+
return name
82+
}
83+
if function.resultType == .basicType(.bool), function.parameterTypes == [.basicType(.address)] {
84+
return name
85+
}
86+
}
87+
return nil
88+
}
89+
return properties + functions
7890
}
7991

8092
public func getStateValue(_ state: Identifier, in contract: RawTypeIdentifier) -> Expression {

Sources/AST/TopLevelModule.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public struct TopLevelModule: ASTNode {
111111
let keyParameter = Parameter(identifier: keyIdentifier, type: Type(inferredType: key, identifier: identifier), implicitToken: nil)
112112
let subExpression = SubscriptExpression(baseExpression: .identifier(identifier), indexExpression: .identifier(keyIdentifier), closeSquareBracketToken: Token(kind: .punctuation(.closeSquareBracket), sourceLocation: sourceLocation))
113113
return ([keyParameter], .subscriptExpression(subExpression), Type(inferredType: value, identifier: identifier))
114-
case .stdlibType(_), .rangeType(_), .userDefinedType(_), .inoutType(_), .any, .errorType:
114+
case .stdlibType(_), .rangeType(_), .userDefinedType(_), .inoutType(_), .functionType(_), .any, .errorType:
115115
return nil
116116
}
117117
}

Sources/AST/Type.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public indirect enum RawType: Equatable {
1919
case dictionaryType(key: RawType, value: RawType)
2020
case userDefinedType(RawTypeIdentifier)
2121
case inoutType(RawType)
22+
case functionType(parameters: [RawType], result: RawType)
2223
case any
2324
case errorType
2425

@@ -47,6 +48,7 @@ public indirect enum RawType: Equatable {
4748
case .inoutType(let rawType): return "$inout\(rawType.name)"
4849
case .any: return "Any"
4950
case .errorType: return "Flint$ErrorType"
51+
case .functionType(let parameters, let result): return "(\(parameters.map{ $0.name }.joined(separator: ", ")) -> \(result)"
5052
}
5153
}
5254

@@ -59,6 +61,7 @@ public indirect enum RawType: Equatable {
5961
case .dictionaryType(let key, let value): return key.isBuiltInType && value.isBuiltInType
6062
case .inoutType(let element): return element.isBuiltInType
6163
case .userDefinedType(_): return false
64+
case .functionType(_): return false
6265
}
6366
}
6467

Lines changed: 78 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,79 @@
11
//
2-
// IRCallerCapabilityChecks.swift
3-
// IRGen
4-
//
5-
// Created by Hails, Daniel R on 11/07/2018.
6-
//
7-
8-
import AST
9-
10-
/// Checks whether the caller of a function has appropriate caller capabilities.
11-
struct IRCallerCapabilityChecks {
12-
static let postfix: String = "CallerCheck"
13-
static let varName: String = "_flint" + postfix
14-
15-
var variableName: String
16-
var callerCapabilities: [CallerCapability]
17-
let revert: Bool
18-
19-
init(callerCapabilities: [CallerCapability], revert: Bool = true, variableName: String = varName) {
20-
self.variableName = variableName
21-
self.callerCapabilities = callerCapabilities
22-
self.revert = revert
23-
}
24-
25-
func rendered(enclosingType: RawTypeIdentifier, environment: Environment) -> String {
26-
let checks = callerCapabilities.compactMap { callerCapability -> String? in
27-
guard !callerCapability.isAny else { return nil }
28-
29-
let type = environment.type(of: callerCapability.identifier.name, enclosingType: enclosingType)
30-
let offset = environment.propertyOffset(for: callerCapability.name, enclosingType: enclosingType)!
31-
32-
switch type {
33-
case .fixedSizeArrayType(_, let size):
34-
return (0..<size).map { index in
35-
let check = IRRuntimeFunction.isValidCallerCapability(address: "sload(add(\(offset), \(index)))")
36-
return "\(variableName) := add(\(variableName), \(check)"
37-
}.joined(separator: "\n")
38-
case .arrayType(_):
39-
let check = IRRuntimeFunction.isCallerCapabilityInArray(arrayOffset: offset)
40-
return "\(variableName) := add(\(variableName), \(check))"
41-
default:
42-
let check = IRRuntimeFunction.isValidCallerCapability(address: "sload(\(offset)))")
43-
return "\(variableName) := add(\(variableName), \(check)"
44-
}
45-
}
46-
let revertString = revert ? "if eq(\(variableName), 0) { revert(0, 0) }" : ""
47-
if !checks.isEmpty {
48-
return """
49-
let \(variableName) := 0
50-
\(checks.joined(separator: "\n"))
51-
\(revertString)
52-
"""
53-
}
54-
55-
return ""
56-
}
57-
}
2+
// IRCallerCapabilityChecks.swift
3+
// IRGen
4+
//
5+
// Created by Hails, Daniel R on 11/07/2018.
6+
//
7+
8+
import AST
9+
10+
/// Checks whether the caller of a function has appropriate caller capabilities.
11+
struct IRCallerCapabilityChecks {
12+
static let postfix: String = "CallerCheck"
13+
static let varName: String = "_flint" + postfix
14+
15+
var variableName: String
16+
var callerCapabilities: [CallerCapability]
17+
let revert: Bool
18+
19+
init(callerCapabilities: [CallerCapability], revert: Bool = true, variableName: String = varName) {
20+
self.variableName = variableName
21+
self.callerCapabilities = callerCapabilities
22+
self.revert = revert
23+
}
24+
25+
func rendered(enclosingType: RawTypeIdentifier, environment: Environment) -> String {
26+
let checks = callerCapabilities.compactMap { callerCapability -> String? in
27+
guard !callerCapability.isAny else { return nil }
28+
29+
let type = environment.type(of: callerCapability.identifier.name, enclosingType: enclosingType)
30+
let offset = environment.propertyOffset(for: callerCapability.name, enclosingType: enclosingType)
31+
let functionContext = FunctionContext(environment: environment, scopeContext: ScopeContext(), enclosingTypeName: enclosingType, isInStructFunction: false)
32+
33+
switch type {
34+
case .functionType(parameters: [], result: .basicType(.address)):
35+
var identifier = callerCapability.identifier
36+
let name = Mangler.mangleFunctionName(identifier.name, parameterTypes: [], enclosingType: enclosingType)
37+
identifier.identifierToken.kind = .identifier(name)
38+
let functionCall = IRFunctionCall(functionCall: FunctionCall(identifier: identifier, arguments: [], closeBracketToken: .init(kind: .punctuation(.closeBracket), sourceLocation: .DUMMY), isAttempted: false))
39+
let check = "eq(caller(), \(functionCall.rendered(functionContext: functionContext)))"
40+
return "\(variableName) := add(\(variableName), \(check))"
41+
case .functionType(parameters: [.basicType(.address)], result: .basicType(.bool)):
42+
var identifier = callerCapability.identifier
43+
let name = Mangler.mangleFunctionName(identifier.name, parameterTypes: [.basicType(.address)], enclosingType: enclosingType)
44+
identifier.identifierToken.kind = .identifier(name)
45+
let functionCall = IRFunctionCall(functionCall: FunctionCall(identifier: identifier, arguments: [FunctionArgument(.rawAssembly("caller()", resultType: .basicType(.address)))], closeBracketToken: .init(kind: .punctuation(.closeBracket), sourceLocation: .DUMMY), isAttempted: false))
46+
let check = "\(functionCall.rendered(functionContext: functionContext))"
47+
return "\(variableName) := add(\(variableName), \(check))"
48+
case .fixedSizeArrayType(_, let size):
49+
return (0..<size).map { index in
50+
let check = IRRuntimeFunction.isValidCallerCapability(address: "sload(add(\(offset!), \(index)))")
51+
return "\(variableName) := add(\(variableName), \(check)"
52+
}.joined(separator: "\n")
53+
case .arrayType(_):
54+
let check = IRRuntimeFunction.isCallerCapabilityInArray(arrayOffset: offset!)
55+
return "\(variableName) := add(\(variableName), \(check))"
56+
case .basicType(.address):
57+
let check = IRRuntimeFunction.isValidCallerCapability(address: "sload(\(offset!)))")
58+
return "\(variableName) := add(\(variableName), \(check)"
59+
case .basicType(_), .stdlibType(_), .rangeType(_), .dictionaryType(_), .userDefinedType(_),
60+
.inoutType(_), .functionType(_), .any, .errorType:
61+
return ""
62+
}
63+
64+
65+
66+
67+
}
68+
let revertString = revert ? "if eq(\(variableName), 0) { revert(0, 0) }" : ""
69+
if !checks.isEmpty {
70+
return """
71+
let \(variableName) := 0
72+
\(checks.joined(separator: "\n"))
73+
\(revertString)
74+
"""
75+
}
76+
77+
return ""
78+
}
79+
}

Sources/IRGen/Runtime/IRWrapperFunction.swift

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,46 +8,46 @@
88
import AST
99

1010
struct IRWrapperFunction {
11-
static let prefixHard = "flintAttemptCallWrapperHard$"
12-
static let prefixSoft = "flintAttemptCallWrapperSoft$"
13-
let function: IRFunction
11+
static let prefixHard = "flintAttemptCallWrapperHard$"
12+
static let prefixSoft = "flintAttemptCallWrapperSoft$"
13+
let function: IRFunction
1414

15-
func rendered(enclosingType: RawTypeIdentifier) -> String {
15+
func rendered(enclosingType: RawTypeIdentifier) -> String {
1616
return rendered(enclosingType: enclosingType, hard: true) + "\n" + rendered(enclosingType: enclosingType, hard: false)
17-
}
18-
19-
func rendered(enclosingType: RawTypeIdentifier, hard: Bool) -> String {
20-
let callerCheck = IRCallerCapabilityChecks.init(callerCapabilities: function.callerCapabilities, revert: false)
21-
let callerCode = callerCheck.rendered(enclosingType: enclosingType, environment: function.environment)
22-
let functionCall = function.signature(withReturn: false)
23-
24-
let invalidCall = hard ? "revert(0, 0)" : "\(IRFunction.returnVariableName) := 0"
25-
26-
var validCall = hard ? "\(IRFunction.returnVariableName) := \(functionCall)" : "\(functionCall)\n \(IRFunction.returnVariableName) := 1"
27-
var returnSignature = "-> \(IRFunction.returnVariableName) "
28-
if hard, function.functionDeclaration.isVoid {
29-
validCall = functionCall
30-
returnSignature = ""
31-
}
32-
if !hard, !function.functionDeclaration.isVoid {
33-
validCall = "\(IRFunction.returnVariableName) := \(functionCall)\n \(IRFunction.returnVariableName) := 1"
34-
}
35-
36-
return """
37-
function \(signature(hard))\(returnSignature){
38-
\(callerCode.indented(by: 2))
39-
switch \(callerCheck.variableName)
40-
case 0 {
41-
\(invalidCall)
42-
}
43-
default {
44-
\(validCall)
45-
}
46-
}
47-
"""
48-
}
49-
50-
func signature(_ hard: Bool) -> String {
51-
return "\(hard ? IRWrapperFunction.prefixHard : IRWrapperFunction.prefixSoft)\(function.signature(withReturn: false))"
52-
}
17+
}
18+
19+
func rendered(enclosingType: RawTypeIdentifier, hard: Bool) -> String {
20+
let callerCheck = IRCallerCapabilityChecks.init(callerCapabilities: function.callerCapabilities, revert: false)
21+
let callerCode = callerCheck.rendered(enclosingType: enclosingType, environment: function.environment)
22+
let functionCall = function.signature(withReturn: false)
23+
24+
let invalidCall = hard ? "revert(0, 0)" : "\(IRFunction.returnVariableName) := 0"
25+
26+
var validCall = hard ? "\(IRFunction.returnVariableName) := \(functionCall)" : "\(functionCall)\n \(IRFunction.returnVariableName) := 1"
27+
var returnSignature = "-> \(IRFunction.returnVariableName) "
28+
if hard, function.functionDeclaration.isVoid {
29+
validCall = functionCall
30+
returnSignature = ""
31+
}
32+
if !hard, !function.functionDeclaration.isVoid {
33+
validCall = "\(IRFunction.returnVariableName) := \(functionCall)\n \(IRFunction.returnVariableName) := 1"
34+
}
35+
36+
return """
37+
function \(signature(hard))\(returnSignature){
38+
\(callerCode.indented(by: 2))
39+
switch \(callerCheck.variableName)
40+
case 0 {
41+
\(invalidCall)
42+
}
43+
default {
44+
\(validCall)
45+
}
46+
}
47+
"""
48+
}
49+
50+
func signature(_ hard: Bool) -> String {
51+
return "\(hard ? IRWrapperFunction.prefixHard : IRWrapperFunction.prefixSoft)\(function.signature(withReturn: false))"
52+
}
5353
}

Tests/BehaviorTests/tests/caller/caller.flint

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ Caller :: (any) {
1212
owners[lastIndex] = caller
1313
lastIndex += 1
1414
}
15+
16+
public func lastOwner() -> Address {
17+
return owners[lastIndex - 1]
18+
}
19+
20+
public func isPrimaryOwner(addr: Address) -> Bool {
21+
return addr == owner
22+
}
1523
}
1624

1725
Caller :: (owners) {
@@ -35,6 +43,18 @@ Caller :: (owner) {
3543
}
3644
}
3745

46+
Caller :: (lastOwner) {
47+
public func lastCall() -> Bool {
48+
return true
49+
}
50+
}
51+
52+
Caller :: (isPrimaryOwner) {
53+
public func primaryCall() -> Bool {
54+
return true
55+
}
56+
}
57+
3858
Caller :: (any) {
3959
public func ifYouCan() -> Bool {
4060
try! restrictedFunc()

Tests/SemanticTests/caller_capability.flint

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,30 @@ Test :: caller <- (any) {
88
public init() {
99
self.owners[1] = caller
1010
}
11+
12+
public func isOwner(addr: Address) -> Bool {
13+
var found: Bool = false
14+
for let owner: Address in owners {
15+
if owner == addr {
16+
found = true
17+
}
18+
}
19+
return found
20+
}
21+
22+
public func getPrimaryOwner() -> Address {
23+
return owners[1]
24+
}
1125
}
1226

1327
Test :: (owners) {
14-
func bar() {
15-
}
28+
func bar() {}
29+
}
30+
31+
Test :: (isOwner) {
32+
func foo() {}
33+
}
34+
35+
Test :: (getPrimaryOwner) {
36+
func zoo() {}
1637
}

0 commit comments

Comments
 (0)