Skip to content

Commit

Permalink
Merge pull request #357 from DJRHails/caller_functions
Browse files Browse the repository at this point in the history
Caller functions
  • Loading branch information
SusanEisenbach authored Sep 7, 2018
2 parents 177163a + ecdeeab commit b3c69dc
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 103 deletions.
2 changes: 2 additions & 0 deletions Sources/AST/ASTDumper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ public class ASTDumper {
writeLine("Any")
case .errorType:
writeLine("Flint error type \(rawType.name)")
case .functionType(_):
writeLine("function type \(rawType.name)")
}
}

Expand Down
1 change: 1 addition & 0 deletions Sources/AST/Environment/Environment+Memory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ extension Environment {
case .inoutType(_): fatalError()
case .any: return 0
case .errorType: return 0
case .functionType(_): return 0

case .stdlibType(let type):
return types[type.rawValue]!.properties.reduce(0) { acc, element in
Expand Down
4 changes: 4 additions & 0 deletions Sources/AST/Environment/Environment+Type.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ extension Environment {
return type
}

if let function = types[enclosingType]?.functions[property]?.first! {
return .functionType(parameters: function.parameterTypes, result: function.resultType)
}

guard let scopeContext = scopeContext, let type = scopeContext.type(for: property) else { return .errorType }
return type
}
Expand Down
14 changes: 13 additions & 1 deletion Sources/AST/Environment/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public struct Environment {

/// The list of properties declared in a type which can be used as caller capabilities.
func declaredCallerCapabilities(enclosingType: RawTypeIdentifier) -> [String] {
return types[enclosingType]!.properties.compactMap { key, value in
let properties: [String] = types[enclosingType]!.properties.compactMap { key, value in
switch value.rawType {
case .basicType(.address): return key
case .fixedSizeArrayType(.basicType(.address), _): return key
Expand All @@ -75,6 +75,18 @@ public struct Environment {
default: return nil
}
}
let functions: [String] = types[enclosingType]!.functions.compactMap { name, functions in
for function in functions {
if function.resultType == .basicType(.address), function.parameterTypes == [] {
return name
}
if function.resultType == .basicType(.bool), function.parameterTypes == [.basicType(.address)] {
return name
}
}
return nil
}
return properties + functions
}

public func getStateValue(_ state: Identifier, in contract: RawTypeIdentifier) -> Expression {
Expand Down
2 changes: 1 addition & 1 deletion Sources/AST/TopLevelModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public struct TopLevelModule: ASTNode {
let keyParameter = Parameter(identifier: keyIdentifier, type: Type(inferredType: key, identifier: identifier), implicitToken: nil)
let subExpression = SubscriptExpression(baseExpression: .identifier(identifier), indexExpression: .identifier(keyIdentifier), closeSquareBracketToken: Token(kind: .punctuation(.closeSquareBracket), sourceLocation: sourceLocation))
return ([keyParameter], .subscriptExpression(subExpression), Type(inferredType: value, identifier: identifier))
case .stdlibType(_), .rangeType(_), .userDefinedType(_), .inoutType(_), .any, .errorType:
case .stdlibType(_), .rangeType(_), .userDefinedType(_), .inoutType(_), .functionType(_), .any, .errorType:
return nil
}
}
Expand Down
3 changes: 3 additions & 0 deletions Sources/AST/Type.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public indirect enum RawType: Equatable {
case dictionaryType(key: RawType, value: RawType)
case userDefinedType(RawTypeIdentifier)
case inoutType(RawType)
case functionType(parameters: [RawType], result: RawType)
case any
case errorType

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

Expand All @@ -59,6 +61,7 @@ public indirect enum RawType: Equatable {
case .dictionaryType(let key, let value): return key.isBuiltInType && value.isBuiltInType
case .inoutType(let element): return element.isBuiltInType
case .userDefinedType(_): return false
case .functionType(_): return false
}
}

Expand Down
134 changes: 78 additions & 56 deletions Sources/IRGen/Runtime/IRCallerCapabilityChecks.swift
Original file line number Diff line number Diff line change
@@ -1,57 +1,79 @@
//
// IRCallerCapabilityChecks.swift
// IRGen
//
// Created by Hails, Daniel R on 11/07/2018.
//

import AST

/// Checks whether the caller of a function has appropriate caller capabilities.
struct IRCallerCapabilityChecks {
static let postfix: String = "CallerCheck"
static let varName: String = "_flint" + postfix

var variableName: String
var callerCapabilities: [CallerCapability]
let revert: Bool

init(callerCapabilities: [CallerCapability], revert: Bool = true, variableName: String = varName) {
self.variableName = variableName
self.callerCapabilities = callerCapabilities
self.revert = revert
}

func rendered(enclosingType: RawTypeIdentifier, environment: Environment) -> String {
let checks = callerCapabilities.compactMap { callerCapability -> String? in
guard !callerCapability.isAny else { return nil }

let type = environment.type(of: callerCapability.identifier.name, enclosingType: enclosingType)
let offset = environment.propertyOffset(for: callerCapability.name, enclosingType: enclosingType)!

switch type {
case .fixedSizeArrayType(_, let size):
return (0..<size).map { index in
let check = IRRuntimeFunction.isValidCallerCapability(address: "sload(add(\(offset), \(index)))")
return "\(variableName) := add(\(variableName), \(check)"
}.joined(separator: "\n")
case .arrayType(_):
let check = IRRuntimeFunction.isCallerCapabilityInArray(arrayOffset: offset)
return "\(variableName) := add(\(variableName), \(check))"
default:
let check = IRRuntimeFunction.isValidCallerCapability(address: "sload(\(offset)))")
return "\(variableName) := add(\(variableName), \(check)"
}
}
let revertString = revert ? "if eq(\(variableName), 0) { revert(0, 0) }" : ""
if !checks.isEmpty {
return """
let \(variableName) := 0
\(checks.joined(separator: "\n"))
\(revertString)
"""
}

return ""
}
}
// IRCallerCapabilityChecks.swift
// IRGen
//
// Created by Hails, Daniel R on 11/07/2018.
//

import AST

/// Checks whether the caller of a function has appropriate caller capabilities.
struct IRCallerCapabilityChecks {
static let postfix: String = "CallerCheck"
static let varName: String = "_flint" + postfix

var variableName: String
var callerCapabilities: [CallerCapability]
let revert: Bool

init(callerCapabilities: [CallerCapability], revert: Bool = true, variableName: String = varName) {
self.variableName = variableName
self.callerCapabilities = callerCapabilities
self.revert = revert
}

func rendered(enclosingType: RawTypeIdentifier, environment: Environment) -> String {
let checks = callerCapabilities.compactMap { callerCapability -> String? in
guard !callerCapability.isAny else { return nil }

let type = environment.type(of: callerCapability.identifier.name, enclosingType: enclosingType)
let offset = environment.propertyOffset(for: callerCapability.name, enclosingType: enclosingType)
let functionContext = FunctionContext(environment: environment, scopeContext: ScopeContext(), enclosingTypeName: enclosingType, isInStructFunction: false)

switch type {
case .functionType(parameters: [], result: .basicType(.address)):
var identifier = callerCapability.identifier
let name = Mangler.mangleFunctionName(identifier.name, parameterTypes: [], enclosingType: enclosingType)
identifier.identifierToken.kind = .identifier(name)
let functionCall = IRFunctionCall(functionCall: FunctionCall(identifier: identifier, arguments: [], closeBracketToken: .init(kind: .punctuation(.closeBracket), sourceLocation: .DUMMY), isAttempted: false))
let check = "eq(caller(), \(functionCall.rendered(functionContext: functionContext)))"
return "\(variableName) := add(\(variableName), \(check))"
case .functionType(parameters: [.basicType(.address)], result: .basicType(.bool)):
var identifier = callerCapability.identifier
let name = Mangler.mangleFunctionName(identifier.name, parameterTypes: [.basicType(.address)], enclosingType: enclosingType)
identifier.identifierToken.kind = .identifier(name)
let functionCall = IRFunctionCall(functionCall: FunctionCall(identifier: identifier, arguments: [FunctionArgument(.rawAssembly("caller()", resultType: .basicType(.address)))], closeBracketToken: .init(kind: .punctuation(.closeBracket), sourceLocation: .DUMMY), isAttempted: false))
let check = "\(functionCall.rendered(functionContext: functionContext))"
return "\(variableName) := add(\(variableName), \(check))"
case .fixedSizeArrayType(_, let size):
return (0..<size).map { index in
let check = IRRuntimeFunction.isValidCallerCapability(address: "sload(add(\(offset!), \(index)))")
return "\(variableName) := add(\(variableName), \(check)"
}.joined(separator: "\n")
case .arrayType(_):
let check = IRRuntimeFunction.isCallerCapabilityInArray(arrayOffset: offset!)
return "\(variableName) := add(\(variableName), \(check))"
case .basicType(.address):
let check = IRRuntimeFunction.isValidCallerCapability(address: "sload(\(offset!)))")
return "\(variableName) := add(\(variableName), \(check)"
case .basicType(_), .stdlibType(_), .rangeType(_), .dictionaryType(_), .userDefinedType(_),
.inoutType(_), .functionType(_), .any, .errorType:
return ""
}




}
let revertString = revert ? "if eq(\(variableName), 0) { revert(0, 0) }" : ""
if !checks.isEmpty {
return """
let \(variableName) := 0
\(checks.joined(separator: "\n"))
\(revertString)
"""
}

return ""
}
}
80 changes: 40 additions & 40 deletions Sources/IRGen/Runtime/IRWrapperFunction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,46 @@
import AST

struct IRWrapperFunction {
static let prefixHard = "flintAttemptCallWrapperHard$"
static let prefixSoft = "flintAttemptCallWrapperSoft$"
let function: IRFunction
static let prefixHard = "flintAttemptCallWrapperHard$"
static let prefixSoft = "flintAttemptCallWrapperSoft$"
let function: IRFunction

func rendered(enclosingType: RawTypeIdentifier) -> String {
func rendered(enclosingType: RawTypeIdentifier) -> String {
return rendered(enclosingType: enclosingType, hard: true) + "\n" + rendered(enclosingType: enclosingType, hard: false)
}

func rendered(enclosingType: RawTypeIdentifier, hard: Bool) -> String {
let callerCheck = IRCallerCapabilityChecks.init(callerCapabilities: function.callerCapabilities, revert: false)
let callerCode = callerCheck.rendered(enclosingType: enclosingType, environment: function.environment)
let functionCall = function.signature(withReturn: false)

let invalidCall = hard ? "revert(0, 0)" : "\(IRFunction.returnVariableName) := 0"

var validCall = hard ? "\(IRFunction.returnVariableName) := \(functionCall)" : "\(functionCall)\n \(IRFunction.returnVariableName) := 1"
var returnSignature = "-> \(IRFunction.returnVariableName) "
if hard, function.functionDeclaration.isVoid {
validCall = functionCall
returnSignature = ""
}
if !hard, !function.functionDeclaration.isVoid {
validCall = "\(IRFunction.returnVariableName) := \(functionCall)\n \(IRFunction.returnVariableName) := 1"
}

return """
function \(signature(hard))\(returnSignature){
\(callerCode.indented(by: 2))
switch \(callerCheck.variableName)
case 0 {
\(invalidCall)
}
default {
\(validCall)
}
}
"""
}

func signature(_ hard: Bool) -> String {
return "\(hard ? IRWrapperFunction.prefixHard : IRWrapperFunction.prefixSoft)\(function.signature(withReturn: false))"
}
}

func rendered(enclosingType: RawTypeIdentifier, hard: Bool) -> String {
let callerCheck = IRCallerCapabilityChecks.init(callerCapabilities: function.callerCapabilities, revert: false)
let callerCode = callerCheck.rendered(enclosingType: enclosingType, environment: function.environment)
let functionCall = function.signature(withReturn: false)

let invalidCall = hard ? "revert(0, 0)" : "\(IRFunction.returnVariableName) := 0"

var validCall = hard ? "\(IRFunction.returnVariableName) := \(functionCall)" : "\(functionCall)\n \(IRFunction.returnVariableName) := 1"
var returnSignature = "-> \(IRFunction.returnVariableName) "
if hard, function.functionDeclaration.isVoid {
validCall = functionCall
returnSignature = ""
}
if !hard, !function.functionDeclaration.isVoid {
validCall = "\(IRFunction.returnVariableName) := \(functionCall)\n \(IRFunction.returnVariableName) := 1"
}

return """
function \(signature(hard))\(returnSignature){
\(callerCode.indented(by: 2))
switch \(callerCheck.variableName)
case 0 {
\(invalidCall)
}
default {
\(validCall)
}
}
"""
}

func signature(_ hard: Bool) -> String {
return "\(hard ? IRWrapperFunction.prefixHard : IRWrapperFunction.prefixSoft)\(function.signature(withReturn: false))"
}
}
20 changes: 20 additions & 0 deletions Tests/BehaviorTests/tests/caller/caller.flint
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ Caller :: (any) {
owners[lastIndex] = caller
lastIndex += 1
}

public func lastOwner() -> Address {
return owners[lastIndex - 1]
}

public func isPrimaryOwner(addr: Address) -> Bool {
return addr == owner
}
}

Caller :: (owners) {
Expand All @@ -35,6 +43,18 @@ Caller :: (owner) {
}
}

Caller :: (lastOwner) {
public func lastCall() -> Bool {
return true
}
}

Caller :: (isPrimaryOwner) {
public func primaryCall() -> Bool {
return true
}
}

Caller :: (any) {
public func ifYouCan() -> Bool {
try! restrictedFunc()
Expand Down
25 changes: 23 additions & 2 deletions Tests/SemanticTests/caller_capability.flint
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,30 @@ Test :: caller <- (any) {
public init() {
self.owners[1] = caller
}

public func isOwner(addr: Address) -> Bool {
var found: Bool = false
for let owner: Address in owners {
if owner == addr {
found = true
}
}
return found
}

public func getPrimaryOwner() -> Address {
return owners[1]
}
}

Test :: (owners) {
func bar() {
}
func bar() {}
}

Test :: (isOwner) {
func foo() {}
}

Test :: (getPrimaryOwner) {
func zoo() {}
}
Loading

0 comments on commit b3c69dc

Please sign in to comment.