Skip to content

Commit

Permalink
Merge pull request #211 from franklinsch/send-function
Browse files Browse the repository at this point in the history
Implement 'send' function to send Wei to other Ethereum accounts
  • Loading branch information
franklinsch authored May 15, 2018
2 parents eec79e1 + 1d7f383 commit def24eb
Show file tree
Hide file tree
Showing 21 changed files with 299 additions and 69 deletions.
5 changes: 5 additions & 0 deletions Sources/AST/AST.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ public struct StructDeclaration: SourceEntity {
}

private var shouldInitializerBeSynthesized: Bool {
// Don't synthesize an initializer for the special stdlib Flint$Global struct.
guard identifier.name != Environment.globalFunctionStructName else {
return false
}

let containsInitializer = members.contains { member in
if case .initializerDeclaration(_) = member { return true }
return false
Expand Down
2 changes: 1 addition & 1 deletion Sources/AST/ASTPassContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ extension ASTPassContext {

/// Whether we are visiting a node in a function declaration or initializer.
public var inFunctionOrInitializer: Bool {
return functionDeclarationContext != nil || functionDeclarationContext != nil
return functionDeclarationContext != nil || initializerDeclarationContext != nil
}

/// Whether we are visiting a property's default assignment.
Expand Down
27 changes: 27 additions & 0 deletions Sources/AST/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ public struct Environment {
/// A list of the names of the structs which have been declared in the program.
var declaredStructs = [Identifier]()

/// The name of the stdlib struct which contains all global functions.
public static let globalFunctionStructName = "Flint$Global"

/// The prefix for Flint runtime functions.
public static var runtimeFunctionPrefix = "flint$"

/// Whether the given function call is a runtime function.
public static func isRuntimeFunctionCall(_ functionCall: FunctionCall) -> Bool {
return functionCall.identifier.name.starts(with: runtimeFunctionPrefix)
}

public init() {}

/// Add a contract declaration to the environment.
Expand Down Expand Up @@ -346,6 +357,7 @@ public struct Environment {
public enum FunctionCallMatchResult {
case matchedFunction(FunctionInformation)
case matchedInitializer(InitializerInformation)
case matchedGlobalFunction(FunctionInformation)
case failure(candidates: [FunctionInformation])
}

Expand Down Expand Up @@ -396,6 +408,21 @@ public struct Environment {
}
}

// Check if it's a global function.

if let functions = types[Environment.globalFunctionStructName]?.functions[functionCall.identifier.name] {
for candidate in functions {

guard candidate.parameterTypes == argumentTypes,
areCallerCapabilitiesCompatible(source: callerCapabilities, target: candidate.callerCapabilities) else {
candidates.append(candidate)
continue
}

match = .matchedGlobalFunction(candidate)
}
}

return match ?? .failure(candidates: candidates)
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/AST/SourceLocation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ public struct SourceLocation: Equatable {
public var column: Int
public var length: Int
public var file: URL
public var isFromStdlib: Bool

public init(line: Int, column: Int, length: Int, file: URL) {
public init(line: Int, column: Int, length: Int, file: URL, isFromStdlib: Bool = false) {
self.line = line
self.column = column
self.length = length
self.file = file
self.isFromStdlib = isFromStdlib
}

public static func spanning<S1: SourceEntity, S2: SourceEntity>(_ lowerBoundEntity: S1, to upperBoundEntity: S2) -> SourceLocation {
Expand Down
2 changes: 0 additions & 2 deletions Sources/IRGen/Function/IULIAContractInitializer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ struct IULIAContractInitializer {

let body = IULIAFunctionBody(functionDeclaration: initializerDeclaration.asFunctionDeclaration, typeIdentifier: typeIdentifier, capabilityBinding: capabilityBinding, callerCapabilities: callerCapabilities, environment: environment, isContractFunction: isContractFunction).rendered()

// TODO: Remove IULIARuntimeFunctionDeclaration.store once constructor code and function code is unified.

return """
init()
function init() {
Expand Down
66 changes: 53 additions & 13 deletions Sources/IRGen/IULIAPreprocessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public struct IULIAPreprocessor: ASTPass {
public func process(functionDeclaration: FunctionDeclaration, passContext: ASTPassContext) -> ASTPassResult<FunctionDeclaration> {
var functionDeclaration = functionDeclaration

// Bind the implicit Wei value of the transaction to a variable.
if functionDeclaration.isPayable, let payableParameterIdentifier = functionDeclaration.firstPayableValueParameter?.identifier {
let weiType = Identifier(identifierToken: Token(kind: .identifier("Wei"), sourceLocation: payableParameterIdentifier.sourceLocation))
let variableDeclaration = VariableDeclaration(declarationToken: nil, identifier: payableParameterIdentifier, type: Type(identifier: weiType))
Expand All @@ -92,12 +93,13 @@ public struct IULIAPreprocessor: ASTPass {
if let structDeclarationContext = passContext.structDeclarationContext {
let parameters = functionDeclaration.parameters.map { $0.type.rawType }
let name = Mangler.mangleFunctionName(functionDeclaration.identifier.name, parameterTypes: parameters, enclosingType: passContext.enclosingTypeIdentifier!.name)

// For struct functions, add `flintSelf` to the beginning of the parameters list.
let parameter = constructParameter(name: "flintSelf", type: .inoutType(.userDefinedType(structDeclarationContext.structIdentifier.name)), sourceLocation: functionDeclaration.sourceLocation)
functionDeclaration.parameters.insert(parameter, at: 0)

functionDeclaration.identifier = Identifier(identifierToken: Token(kind: .identifier(name), sourceLocation: functionDeclaration.identifier.sourceLocation))

if Environment.globalFunctionStructName != passContext.enclosingTypeIdentifier?.name {
// For struct functions, add `flintSelf` to the beginning of the parameters list.
let parameter = constructParameter(name: "flintSelf", type: .inoutType(.userDefinedType(structDeclarationContext.structIdentifier.name)), sourceLocation: functionDeclaration.sourceLocation)
functionDeclaration.parameters.insert(parameter, at: 0)
}
}

// Add an isMem parameter for each struct parameter.
Expand Down Expand Up @@ -232,6 +234,14 @@ public struct IULIAPreprocessor: ASTPass {
var receiverTrail = passContext.functionCallReceiverTrail ?? []
let enclosingType = passContext.enclosingTypeIdentifier!.name
let callerCapabilities = passContext.contractBehaviorDeclarationContext?.callerCapabilities ?? []
let isGlobalFunctionCall = self.isGlobalFunctionCall(functionCall, in: passContext)

let scopeContext = passContext.scopeContext!

guard !Environment.isRuntimeFunctionCall(functionCall) else {
// Don't mangle runtime functions.
return ASTPassResult(element: functionCall, diagnostics: [], passContext: passContext)
}

if receiverTrail.isEmpty {
receiverTrail = [.self(Token(kind: .self, sourceLocation: functionCall.sourceLocation))]
Expand All @@ -248,25 +258,31 @@ public struct IULIAPreprocessor: ASTPass {
let mangledName = mangledFunctionName(for: initializerWithoutReceiver, in: passContext)
functionCall.identifier = Identifier(identifierToken: Token(kind: .identifier(mangledName), sourceLocation: functionCall.sourceLocation))
} else {
let scopeContext = passContext.scopeContext!

// Get the result type of the call.
let type = passContext.environment!.type(of: receiverTrail.last!, enclosingType: enclosingType, callerCapabilities: callerCapabilities, scopeContext: scopeContext)
let declarationEnclosingType: RawTypeIdentifier

if isGlobalFunctionCall {
declarationEnclosingType = Environment.globalFunctionStructName
} else {
declarationEnclosingType = passContext.environment!.type(of: receiverTrail.last!, enclosingType: enclosingType, callerCapabilities: callerCapabilities, scopeContext: scopeContext).name
}

// If it returns a dynamic type, pass the receiver as the first parameter.
if passContext.environment!.isStructDeclared(type.name) {
if passContext.environment!.isStructDeclared(declarationEnclosingType) {
let mangledName = mangledFunctionName(for: functionCall, in: passContext)
let receiver = constructExpression(from: receiverTrail)
let inoutExpression = InoutExpression(ampersandToken: Token(kind: .punctuation(.ampersand), sourceLocation: receiver.sourceLocation), expression: receiver)
functionCall.arguments.insert(.inoutExpression(inoutExpression), at: 0)

if !isGlobalFunctionCall {
let receiver = constructExpression(from: receiverTrail)
let inoutExpression = InoutExpression(ampersandToken: Token(kind: .punctuation(.ampersand), sourceLocation: receiver.sourceLocation), expression: receiver)
functionCall.arguments.insert(.inoutExpression(inoutExpression), at: 0)
}

// Replace the name of a function call by its mangled name.
functionCall.identifier = Identifier(identifierToken: Token(kind: .identifier(mangledName), sourceLocation: functionCall.sourceLocation))
}
}

let scopeContext = passContext.scopeContext!

// For each non-implicit dynamic type, add an isMem parameter.
var offset = 0
for (index, argument) in functionCall.arguments.enumerated() {
Expand Down Expand Up @@ -304,6 +320,11 @@ public struct IULIAPreprocessor: ASTPass {
}

func mangledFunctionName(for functionCall: FunctionCall, in passContext: ASTPassContext) -> String {
// Don't mangle runtime functions.
guard !Environment.isRuntimeFunctionCall(functionCall) else {
return functionCall.identifier.name
}

let environment = passContext.environment!

let enclosingType: String = functionCall.identifier.enclosingType ?? passContext.enclosingTypeIdentifier!.name
Expand All @@ -319,11 +340,30 @@ public struct IULIAPreprocessor: ASTPass {
let declaration = initializerInformation.declaration
let parameterTypes = declaration.parameters.map { $0.type.rawType }
return Mangler.mangleInitializerName(functionCall.identifier.name, parameterTypes: parameterTypes)
case .matchedGlobalFunction(let functionInformation):
let parameterTypes = functionInformation.declaration.parameters.map { $0.type.rawType }
return Mangler.mangleFunctionName(functionCall.identifier.name, parameterTypes: parameterTypes, enclosingType: Environment.globalFunctionStructName)
case .failure(_):
fatalError("Unable to find declaration of \(functionCall)")
}
}

func isGlobalFunctionCall(_ functionCall: FunctionCall, in passContext: ASTPassContext) -> Bool {
let enclosingType = passContext.enclosingTypeIdentifier!.name
let callerCapabilities = passContext.contractBehaviorDeclarationContext?.callerCapabilities ?? []
let scopeContext = passContext.scopeContext!
let environment = passContext.environment!

let match = environment.matchFunctionCall(functionCall, enclosingType: enclosingType, callerCapabilities: callerCapabilities, scopeContext: scopeContext)

// Mangle global function
if case .matchedGlobalFunction(_) = match {
return true
}

return false
}

public func process(arrayLiteral: ArrayLiteral, passContext: ASTPassContext) -> ASTPassResult<ArrayLiteral> {
return ASTPassResult(element: arrayLiteral, diagnostics: [], passContext: passContext)
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/IRGen/Runtime/IULIAFunctionSelector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct IULIAFunctionSelector {
let cases = renderCases()

return """
switch selector()
switch \(IULIARuntimeFunction.selector())
\(cases)
default {
revert(0, 0)
Expand Down
Loading

0 comments on commit def24eb

Please sign in to comment.