/
FFICalloutMethodBuilder.class.st
234 lines (191 loc) · 5.97 KB
/
FFICalloutMethodBuilder.class.st
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
"
I build FFI method bytecodes for a call.
"
Class {
#name : #FFICalloutMethodBuilder,
#superclass : #Object,
#instVars : [
'calloutAPI',
'requestor',
'sender',
'signature',
'functionResolutionStrategies',
'library'
],
#category : #'UnifiedFFI-Callouts'
}
{ #category : #'instance creation' }
FFICalloutMethodBuilder class >> calloutAPI: aCalloutAPI [
^ self basicNew
initializeCalloutAPI: aCalloutAPI;
yourself
]
{ #category : #accessing }
FFICalloutMethodBuilder >> addFunctionResolveStrategy: aStrategy [
functionResolutionStrategies add: aStrategy
]
{ #category : #private }
FFICalloutMethodBuilder >> argumentNames [
^ self method argumentNames
]
{ #category : #building }
FFICalloutMethodBuilder >> build: aBlock [
aBlock value: self.
^ self generate
]
{ #category : #accessing }
FFICalloutMethodBuilder >> callType [
self callingConvention = #cdecl ifTrue: [ ^ 0 ].
self callingConvention = #stdcall ifTrue: [ ^ 1 ].
self error: 'Invalid call convention!'
]
{ #category : #accessing }
FFICalloutMethodBuilder >> callingConvention [
^ self calloutAPI callingConvention
]
{ #category : #accessing }
FFICalloutMethodBuilder >> calloutAPI [
^ calloutAPI
]
{ #category : #'private factory' }
FFICalloutMethodBuilder >> createFFICallout [
^ FFICallout new
sender: self sender;
options: self calloutAPI options;
yourself
]
{ #category : #'private factory' }
FFICalloutMethodBuilder >> createFFICalloutLiteralFromSpec: functionSpec [
| externalFunction |
externalFunction := ExternalLibraryFunction
name: functionSpec functionName
module: self libraryName
callType: self callType
returnType: functionSpec returnType externalTypeWithArity
argumentTypes: (functionSpec arguments collect: [:each | each externalTypeWithArity]).
functionResolutionStrategies
detect: [ :each | each isApplicableFor: self requestor ]
ifFound: [ :each | each resolve: externalFunction ].
^ externalFunction
]
{ #category : #'as yet unclassified' }
FFICalloutMethodBuilder >> extractLibrary [
"Obtain the library to use"
| ffiLibrary |
ffiLibrary := library ifNil: [sender receiver ffiLibrary].
ffiLibrary := ffiLibrary isClass
ifFalse: [ ffiLibrary asFFILibrary ]
ifTrue: [ ffiLibrary uniqueInstance ].
^ ffiLibrary
]
{ #category : #private }
FFICalloutMethodBuilder >> generate [
^ self generateMethodFromSpec: (self parseSignature: self signature)
]
{ #category : #private }
FFICalloutMethodBuilder >> generateFFICallout: builder spec: functionSpec ffiLibrary: ffiLibrary [
"Builds a method call"
"save ffi call as literal"
builder pushLiteral: (self createFFICalloutLiteralFromSpec: functionSpec).
"iterate arguments in order (in the function) to create the function call"
functionSpec arguments do: [ :each | each emitArgument: builder context: sender inCallout: self requestor ].
"create the array"
builder pushConsArray: functionSpec arguments size.
"send call and store into result"
builder send: #invokeWithArguments:.
functionSpec arguments do: [ :each | each emitReturnArgument: builder context: sender ].
"convert in case return type needs it. And return reseult"
^ functionSpec returnType
emitReturn: builder
resultTempVar: #result
context: sender
inCallout: self requestor
]
{ #category : #private }
FFICalloutMethodBuilder >> generateMethodFromSpec: functionSpec [
| ir ffiLibrary properties |
functionSpec resolveUsing: self requestor.
ffiLibrary := self extractLibrary.
ir := IRBuilder buildIR: [ :builder | | r |
"Copy the properties of the old method"
sender methodProperties
ifNotNil: [ properties := sender methodProperties copy.
properties method: nil.
builder properties: properties ].
builder
numArgs: self argumentNames size;
addTemps: (self argumentNames copyWith: #result).
ffiLibrary preMethodBuildContext: sender builder: builder spec: functionSpec.
r := self generateFFICallout: builder spec: functionSpec ffiLibrary: ffiLibrary.
ffiLibrary postMethodBuildContext: sender builder: builder spec: functionSpec.
r].
^ ir generate: self methodTrailer
]
{ #category : #initialization }
FFICalloutMethodBuilder >> initialize [
super initialize.
functionResolutionStrategies := FFIFunctionResolutionStrategy allSubclasses collect: [:aClass | aClass new]
]
{ #category : #initialization }
FFICalloutMethodBuilder >> initializeCalloutAPI: aCalloutAPI [
calloutAPI := aCalloutAPI.
self initialize
]
{ #category : #accessing }
FFICalloutMethodBuilder >> library [
^ library
]
{ #category : #accessing }
FFICalloutMethodBuilder >> library: aLibrary [
"A module can be a string with a path to the library or a reference to a class who is child
of FFILibrary.
Example: 'libc' or LibC"
library := aLibrary
]
{ #category : #accessing }
FFICalloutMethodBuilder >> libraryName [
"Answer the module name, who can come from a string with the path to the module or a
reference to a class who is child of FFILibrary"
self library ifNil: [ ^ nil ].
^ self library asFFILibrary libraryName
]
{ #category : #accessing }
FFICalloutMethodBuilder >> method [
^ self sender compiledCode method
]
{ #category : #accessing }
FFICalloutMethodBuilder >> methodTrailer [
^ self method trailer
]
{ #category : #private }
FFICalloutMethodBuilder >> parseSignature: aSignature [
^ FFIFunctionParser new
requestor: self requestor;
parseNamedFunction: aSignature
]
{ #category : #accessing }
FFICalloutMethodBuilder >> requestor [
^ requestor ifNil: [ requestor := self createFFICallout ]
]
{ #category : #accessing }
FFICalloutMethodBuilder >> requestor: anObject [
"The object who made the request for FFI.
By default, NBFFICallout (check #requestor)"
requestor := anObject
]
{ #category : #accessing }
FFICalloutMethodBuilder >> sender [
^ sender
]
{ #category : #accessing }
FFICalloutMethodBuilder >> sender: anObject [
sender := anObject
]
{ #category : #accessing }
FFICalloutMethodBuilder >> signature [
^ signature
]
{ #category : #accessing }
FFICalloutMethodBuilder >> signature: anObject [
signature := anObject
]