Skip to content

Commit

Permalink
Merge tag '1.1.2' into develop
Browse files Browse the repository at this point in the history
Fix memory leaks
  • Loading branch information
ctreffs committed Jan 23, 2020
2 parents 5922f5b + f42759e commit 5db5bfb
Show file tree
Hide file tree
Showing 8 changed files with 883 additions and 244 deletions.
85 changes: 15 additions & 70 deletions Sources/AutoWrapper/ArgT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ extension ArgType: Equatable { }
extension ArgType: Hashable { }

public struct ArgsT: Decodable {
public let escapedName: String
public let name: String
public let type: DataType
public let ret: String?
Expand All @@ -60,8 +61,18 @@ public struct ArgsT: Decodable {

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Keys.self)
self.name = try container.decode(String.self, forKey: .name).swiftEscaped
let rawName = try container.decode(String.self, forKey: .name)
self.type = try container.decode(DataType.self, forKey: .type)

self.name = rawName
let escapedName = rawName.swiftEscaped
switch escapedName {
case "...":
self.escapedName = "arguments"
default:
self.escapedName = escapedName
}

self.ret = try container.decodeIfPresent(String.self, forKey: .ret)
self.signature = try container.decodeIfPresent(String.self, forKey: .signature)
}
Expand All @@ -70,79 +81,13 @@ public struct ArgsT: Decodable {
type.isValid && name != "..."
}

public var argName: String {
switch name {
case "...":
return "arguments"
default:
return name
}
}

public var toSwift: String {
switch self.type.type {
case let .custom(name) where name.hasSuffix("Callback") && argName.contains("callback"):
return "_ \(argName): @escaping \(self.type.toString(.argSwift))"
default:
return "_ \(argName): \(self.type.toString(.argSwift))"
}
}

// swiftlint:disable:next cyclomatic_complexity
public func wrapCArg(_ arg: String) -> String {
switch self.type.meta {
case .primitive:
return arg

case .array where self.type.type == .char:
return "\(arg).map { $0.cStrPtr() }"
case .array:
return "&\(arg)"
case let .arrayFixedSize(count) where self.type.isConst == false:
if type.type.isNumber && count < 5 {
// SIMD
return "withUnsafeMutablePointer(to: &\(arg)) { $0.withMemoryRebound(to: \(self.type.toString(.argSwift, wrapped: false)).self, capacity: \(count)) { $0 } }"
} else {
return "UnsafeMutableBufferPointer<\(self.type.toString(.argSwift, wrapped: false))>(start: &\(arg).0, count: \(count)).baseAddress!"
}

case let .arrayFixedSize(count):
return "UnsafeBufferPointer<\(self.type.toString(.argSwift, wrapped: false))>(start: &\(arg).0, count: \(count)).baseAddress!"
case .reference:
return "&\(arg)"
case .pointer where self.type.isConst == false && self.type.type == .void:
return arg

case .pointer where self.type.type != .char && self.type.isConst == false:
return "\(arg)"
case .pointer:
return arg

case .unknown:
return arg

case .exception:
return arg
}
}

public var toC: String {
var out: String = argName
switch type.type {
case .char where type.isConst == true && type.meta == .pointer:
// const char*
out.append("?.cStrPtr()")

case .char where type.isConst == false && type.meta == .pointer:
// char*
out.append("?.cMutableStrPtr()")

case .va_list:
out = "withVaList(\(out), { $0 })"
case let .custom(name) where name.hasSuffix("Callback") && escapedName.contains("callback"):
return "_ \(escapedName): @escaping \(self.type.toString(.argSwift))"
default:
break
return "_ \(escapedName): \(self.type.toString(.argSwift))"
}
return wrapCArg(out)
}
}

Expand Down
9 changes: 3 additions & 6 deletions Sources/AutoWrapper/Definitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ public struct FunctionDef: Decodable {
def.map { $0.toSwift }.joined(separator: ", ")
}

public func encode(c def: [ArgsT]) -> String {
def.map { $0.toC }.joined(separator: ",")
}

public var encodedFuncname: String {
guard let range = ov_cimguiname.range(of: funcname) else {
assertionFailure("Original name should contain funcname")
Expand Down Expand Up @@ -112,9 +108,10 @@ public struct FunctionDef: Decodable {
}

public var toSwift: String {
"""
// \t\(innerReturn)\(wrapCCall("\(self.ov_cimguiname)(\(encode(c: self.argsT)))"))
return """
\(funcDefs) \(encodedFuncname)(\(encode(swift: self.argsT))) -> \(returnType.toString(.ret)) {
\t\(innerReturn)\(wrapCCall("\(self.ov_cimguiname)(\(encode(c: self.argsT)))"))
\(FunctionBodyRenderer.render(ov_cimguiname, argsT, returnType))
}
"""
}
Expand Down
152 changes: 152 additions & 0 deletions Sources/AutoWrapper/FunctionBodyRenderer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//
// FunctionBodyRenderer.swift
//
//
// Created by Christian Treffs on 23.01.20.
//

struct FunctionBodyRenderer {
static func render(_ callName: String, _ args: [ArgsT], _ returnType: DataType) -> String {
// determine if somethings needs to be pre- or appended depending on return type
let prependCall: String
let appendCall: String
switch (returnType.meta, returnType.type) {
case (.pointer, .char):
prependCall = "String(cString: "
appendCall = ")"
default:
prependCall = ""
appendCall = ""
}

// insert return keyword if neccessary
let returnKeyword: String
switch returnType.type {
case .void where returnType.isConst == true:
returnKeyword = ""
default:
returnKeyword = "return "
}

var preCallLines: [String] = []
var callArguments: [String] = []
var postCallLines: [String] = []

// parse arguments and construct lines
for arg in args {
for parsed in parse(cArg: arg) {
switch parsed {
case let .preLine(line):
preCallLines.append(line)

case let .line(line):
callArguments.append(line)

case let .postLine(line):
postCallLines.insert(line, at: 0)
}
}
}

// create call signature
let callSignature = "\(returnKeyword)\(prependCall)\(callName)(" + callArguments.joined(separator: ",") + ")\(appendCall)"

// render output
assert(preCallLines.count == postCallLines.count)

let functionBody: String
if preCallLines.isEmpty {
functionBody = "\t" + callSignature
} else {
var begin: String = ""
var end: String = ""

let maxIndentation: Int = preCallLines.count
for (index, (pre, post)) in zip(preCallLines, postCallLines.reversed()).enumerated() {
begin.append(String(repeating: "\t", count: index + 1) + pre + "\n")
end.append("\n" + String(repeating: "\t", count: maxIndentation - index) + post)
}

functionBody = begin + String(repeating: "\t", count: maxIndentation + 1) + callSignature + end
}
return functionBody
}

enum ParsedArg {
case preLine(String)
case line(String)
case postLine(String)
}

static func parse(cArg arg: ArgsT) -> [ParsedArg] {
switch arg.type.meta {
case .primitive where arg.type.type == .va_list:
return [
.preLine("withVaList(\(arg.escapedName)) { varArgsPtr in"),
.line("varArgsPtr"),
.postLine("}")
]

case .primitive:
return [.line(arg.escapedName)]

case .array where arg.type.type == .char:
return [
.preLine("withArrayOfCStringsBasePointer(\(arg.escapedName)) { \(arg.name)Ptr in"),
.line("\(arg.name)Ptr"),
.postLine("}")
]

case .array, .reference:
return [.line("&\(arg.escapedName)")]

case let .arrayFixedSize(count) where arg.type.isConst == false:
if arg.type.type.isNumber && count < 5 {
// SIMD vector
return [
.preLine("withUnsafeMutablePointer(to: &\(arg.escapedName)) { \(arg.name)MutPtr in"),
.preLine("\(arg.name)MutPtr.withMemoryRebound(to: \(arg.type.toString(.argSwift, wrapped: false)).self, capacity: \(count)) { \(arg.name)Ptr in"),
.line("\(arg.name)Ptr"),
.postLine("}"),
.postLine("}")
]
} else {
return [.line("UnsafeMutableBufferPointer<\(arg.type.toString(.argSwift, wrapped: false))>(start: &\(arg.escapedName).0, count: \(count)).baseAddress!")]
}

case let .arrayFixedSize(count):
return [.line("UnsafeMutableBufferPointer<\(arg.type.toString(.argSwift, wrapped: false))>(start: &\(arg.escapedName).0, count: \(count)).baseAddress!")]

case .pointer where arg.type.isConst == false && arg.type.type == .void:
return [.line(arg.escapedName)]

case .pointer where arg.type.type != .char && arg.type.isConst == false:
return [.line(arg.escapedName)]

case .pointer where arg.type.type == .char && arg.type.isConst == true:
// const char*
return [
.preLine("\(arg.escapedName)!.withCString { \(arg.name)Ptr in"),
.line("\(arg.name)Ptr"),
.postLine("}")
]

case .pointer where arg.type.type == .char && arg.type.isConst == false:
// char*
return [
.preLine("\(arg.escapedName)!.withCString { \(arg.name)Ptr in"),
.line("UnsafeMutablePointer(mutating: \(arg.name)Ptr)"),
.postLine("}")
]

case .pointer:
return [.line(arg.escapedName)]

case .unknown:
return [.line(arg.escapedName)]

case .exception:
return [.line(arg.escapedName)]
}
}
}
8 changes: 4 additions & 4 deletions Sources/Demos/Metal/Renderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ extension Renderer: MTKViewDelegate {
// 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.

// Create a window called "Hello, world!" and append into it.
ImGuiBegin("Hello, world!", &show_demo_window, 0)
ImGuiBegin("Begin", &show_demo_window, 0)

// Display some text (you can use a format strings too)
ImGuiTextV("This is some useful text.")
Expand All @@ -92,11 +92,11 @@ extension Renderer: MTKViewDelegate {
ImGuiCheckbox("Demo Window", &show_demo_window)
ImGuiCheckbox("Another Window", &show_another_window)

ImGuiSliderFloat("Float Slider", &f, 0.0, 1.0, nil, 1) // Edit 1 float using a slider from 0.0f to 1.0f
ImGuiSliderFloat("Float Slider", &f, 0.0, 1.0, "", 1) // Edit 1 float using a slider from 0.0f to 1.0f

ImGuiColorEdit3("clear color", &clear_color, 0) // Edit 3 floats representing a color

if ImGuiButton("Button", ImVec2(x: 0,y: 0)) { // Buttons return true when clicked (most widgets return true when edited/activated)
if ImGuiButton("Button", ImVec2(x: 100,y: 20)) { // Buttons return true when clicked (most widgets return true when edited/activated)
counter += 1
}

Expand All @@ -120,7 +120,7 @@ extension Renderer: MTKViewDelegate {
ImGuiBegin("Another Window", &show_another_window, 0) // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)

ImGuiTextV("Hello from another window!")
if ImGuiButton("Close Me", ImVec2(x: 0, y: 0)) {
if ImGuiButton("Close Me", ImVec2(x: 100, y: 20)) {
show_another_window = false
}
ImGuiEnd()
Expand Down
5 changes: 3 additions & 2 deletions Sources/Demos/Metal/imgui_impl_metal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import CImGui
import ImGui
import Foundation
import AppKit
import Metal
import MetalKit
Expand All @@ -22,7 +21,9 @@ func ImGui_ImplMetal_Init(_ device: MTLDevice) -> Bool {
let io = ImGuiGetIO()!


io.pointee.BackendRendererName = "imgui_impl_metal".cStrPtr()
"imgui_impl_metal".withCString {
io.pointee.BackendRendererName = $0
}

// We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
io.pointee.BackendFlags |= Int32(ImGuiBackendFlags_RendererHasVtxOffset.rawValue)
Expand Down
4 changes: 3 additions & 1 deletion Sources/Demos/Metal/imgui_impl_osx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ func ImGui_ImplOSX_Init() {
//io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
//io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
//io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy)
io.pointee.BackendPlatformName = "imgui_metal_osx".cStrPtr()
"imgui_metal_osx".withCString {
io.pointee.BackendPlatformName = $0
}

// Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array.
let offset_for_function_keys: Int32 = 256 - 0xF700
Expand Down
Loading

0 comments on commit 5db5bfb

Please sign in to comment.