-
Notifications
You must be signed in to change notification settings - Fork 0
/
GravityClass.swift
113 lines (93 loc) · 4.52 KB
/
GravityClass.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/**
* Copyright © 2022 Dustin Collins (Strega's Gate)
* All Rights Reserved.
* Licensed under MIT License
*
* http://stregasgate.com
*/
import GravityC
/// A gravity script class definition.
public class GravityClass: GravityValueEmitter {
internal let gravity: Gravity
internal let gravityClass: UnsafeMutablePointer<gravity_class_t>
internal init(name: String, superClass: GravityClass?, gravity: Gravity) {
self.gravity = gravity
self.gravitySuperClass = superClass
self.gravityClass = name.withCString { name in
return gravity_class_new_pair(gravity.vm, name, superClass?.gravityClass, 0, 0)
}
}
public var gValue: gravity_value_t {
return gravity_value_from_object(gravityClass)
}
/// The super class definition, if any.
public let gravitySuperClass: GravityClass?
/**
Add a new variable to the Gravity object.
- parameter name: The name of the var as written in a gravity script.
*/
public func addInstanceVariable(named name: String) {
name.withCString { name in
gravity.setGrabageCollectionEnabled(false)
let index = gravity_class_add_ivar(gravityClass, name)
let function = gravity_function_new_special(nil, nil, UInt16(index), nil, nil)
let closure = gravity_closure_new(gravity.vm, function)
gravity_class_bind(gravityClass, name, gravity_value_from_object(closure))
gravity.setGrabageCollectionEnabled(true)
}
}
/**
Add a new function to the Gravity object.
- parameter name: The name of the function as written in a gravity script.
- parameter function: A swift function or closure to be called when this func is called in the gravity script.
*/
public func addFunction(named name: String, function: @escaping GravitySwiftInstanceFunction) {
var name = name
name.withCString { cName in
let gFunc = gravity_function_new_bridged(gravity.vm, cName, &name)
let gClosure = gravity_closure_new(gravity.vm, gFunc)
let userData = GravityCFuncBridgedUserData(functionName: name, gravityClass: self)
gFunc!.pointee.xdata = gravity.retainedUserDataPointer(from: userData)
var gValue = gravity_value_t()
gValue.p = unsafeBitCast(gClosure, to: UnsafeMutablePointer<gravity_object_t>.self)
gValue.isa = gravity_class_closure
gravity_class_bind(gravityClass, name, gValue)
}
var funcDatabase = Gravity.cBridgedFunctionMap[gravity.vm] ?? [:]
funcDatabase[name] = function
Gravity.cBridgedFunctionMap[gravity.vm] = funcDatabase
}
public func createInstance() -> GravityInstance {
return GravityInstance(gravityClass: self, gravity: gravity)
}
}
/// A function called from a gravity instance
public typealias GravitySwiftInstanceFunction = (_ gravity: Gravity, _ sender: GravityInstance, _ args: [GravityValue]) -> GravityValue
extension Gravity {
internal static var cBridgedFunctionMap: [OpaquePointer:[String:GravitySwiftInstanceFunction]] = [:]
@inline(__always) internal func cleanupCBridgedFunctions() {
Gravity.cBridgedFunctionMap.removeValue(forKey: vm)
}
}
internal class GravityCFuncBridgedUserData {
let functionName: String
let gravityClass: GravityClass
init(functionName: String, gravityClass: GravityClass) {
self.functionName = functionName
self.gravityClass = gravityClass
}
}
internal func gravityCFuncBridged(vm: OpaquePointer!, xdata: UnsafeMutableRawPointer?, ctx: gravity_value_t, args: UnsafeMutablePointer<gravity_value_t>!, nargs: Int16, rindex: UInt32) -> Bool {
// We use userData to store the Swift functions retrieval key
guard let userData = unsafeBitCast(xdata, to: Optional<GravityCFuncBridgedUserData>.self) else {return true}
// This should never fail
guard let swiftFunction = Gravity.cBridgedFunctionMap[vm]?[userData.functionName] else {fatalError()}
// An unmanged Gravity instance
let gravity = Gravity(vm: vm)
// Convert args to GravityValue(s)
var args = UnsafeBufferPointer(start: args, count: Int(nargs)).map({GravityValue(gValue: $0)})
// The first arg is always the sender
let sender = GravityInstance(value: args.removeFirst(), gravity: gravity)
let result = swiftFunction(gravity, sender, args)
return _gravityHandleCFuncReturn(vm: vm, returnValue: result, returnSlot: rindex)
}