Eliot Miranda uploaded a new version of Sound to project The Trunk:
http://source.squeak.org/trunk/Sound-eem.80.mcz
==================== Summary ====================
Name: Sound-eem.80
Author: eem
Time: 25 May 2021, 4:07:32.508103 pm
UUID: c65df365-47b5-4670-8ec6-411aac823b15
Ancestors: Sound-eem.79
Fix FFT>>plot:in: for an all-zero collection.
=============== Diff against Sound-eem.79 ===============
Item was changed:
----- Method: FFT>>plot:in: (in category 'testing') -----
plot: samples in: rect
"Throw-away code just to check out a couple of examples"
+ | dx pen min max x divisor offset |
- | dx pen min max x |
Display fillWhite: rect; border: (rect expandBy: 2) width: 2.
min := 1.0e30. max := -1.0e30.
samples do:
[:v |
min := min min: v.
max := max max: v].
+ max = min
+ ifTrue: [divisor := rect height asFloat.
+ offset := rect height / 2.0]
+ ifFalse: [divisor := max - min.
+ offset := 0.0].
pen := Pen new. pen up.
x := rect left.
dx := rect width asFloat / samples size.
samples do:
[:v | | y |
+ y := (max - v) / divisor * rect height + offset.
+ pen goto: x asInteger @ (rect top + y asInteger); down.
- y := (max-v) / (max-min) * rect height asFloat.
- pen goto: x asInteger @ (rect top + y asInteger).
- pen down.
x := x + dx].
+ max printString displayOn: Display at: (x + 2) @ (rect top - 9).
+ min printString displayOn: Display at: (x + 2) @ (rect bottom - 9)!
- max printString displayOn: Display at: (x+2) @ (rect top-9).
- min printString displayOn: Display at: (x+2) @ (rect bottom - 9)!
Marcel Taeumel uploaded a new version of FFI-Callbacks to project FFI:
http://source.squeak.org/FFI/FFI-Callbacks-mt.18.mcz
==================== Summary ====================
Name: FFI-Callbacks-mt.18
Author: mt
Time: 23 May 2021, 4:03:13.688158 pm
UUID: fcb16553-c424-d646-b58f-dc47277ed808
Ancestors: FFI-Callbacks-mt.17
Use #abiSend: for callback init.
=============== Diff against FFI-Callbacks-mt.17 ===============
Item was changed:
----- Method: FFICallback>>init__ccall (in category 'initialization - thunk prepare') -----
init__ccall
"Initialize the receiver with a __ccall thunk."
+ FFIPlatformDescription current abiSend: #'init_ccall' to: self.!
- FFICallback methodsDo: [:method |
- (method selector beginsWith: 'init__ccall') ifTrue: [
- (method hasPragma: #init) ifTrue: [
- (method pragmaAt: #abi:)
- ifNotNil: [:pragma | (pragma argumentAt: 1) = abi
- ifTrue: [^ self executeMethod: method]]]]].
-
- self error: 'Could not initialize thunk for current ABI: ', abi.!
Item was changed:
----- Method: FFICallback>>init__ccall_ARM32 (in category 'initialization - thunk prepare') -----
init__ccall_ARM32
+ <abi: #ARM32> <init_ccall>
- <abi: #ARM32> <init>
"Initialize the receiver with a __ccall thunk. The thunk calls thunkEntry in the IA32ABI plugin,
whose source is in platforms/Cross/plugins/IA32ABI/arm32abicc.c. thunkEntry is the entry point
for Callbacks. The objective of the thunk is to call thunkEntry with all arguments to the call of the
thunk (registers and stack) as well as a pointer to the thunk itself. thunkEntry is as follows:
long long
thunkEntry(long r0, long r1, long r2, long r3,
double d0, double d1, double d2, double d3,
double d4, double d5, double d6, double d7,
void *thunkpPlus16, sqIntptr_t *stackp)
thunkEntry then collects the various arguments (thunk, integer register arguments, floating-point
register arguments, stack pointer) in a VMCallbackContext and invokes the callback via invokeCallbackContext:."
"0x0 <thunk+0>: mov r12, sp ; 0xe1a0c00d
0x4 <thunk+4>: sub sp, sp, #16 ; 0xe24dd010
0x8 <thunk+8>: str pc, [sp, #0] ; 0xe58df000 N.B. passes thunk+16; thunkEntry compensates
0xc <thunk+12>: str r12, [sp,#4] ; 0xe58dc004
0x10 <thunk+16>: str lr, [sp, #12] ; 0xe58de00c
0x14 <thunk+20>: ldr r12, [pc, #8] ; 0xe59fc008
0x18 <thunk+24>: blx r12 ; 0xe12fff3c
0x1c <thunk+28>: add sp, sp, #12 ; 0xe28dd00c
0x20 <thunk+32>: ldr pc, [sp], #4!! ; 0xe49df004 ; pop {pc}
0x24 <thunk+36>: .word thunkEntry"
self flag: #hidden. "mt: How is the thunk's handle stored to lookup this instance upon callback later?"
thunk getHandle
unsignedLongAt: 1 put: 16re1a0c00d;
unsignedLongAt: 5 put: 16re24dd010;
unsignedLongAt: 9 put: 16re58df000;
unsignedLongAt: 13 put: 16re58dc004;
unsignedLongAt: 17 put: 16re58de00c;
unsignedLongAt: 21 put: 16re59fc008;
unsignedLongAt: 25 put: 16re12fff3c;
unsignedLongAt: 29 put: 16re28dd00c;
unsignedLongAt: 33 put: 16re49df004;
pointerAt: 37 put: self thunkEntryAddress length: 4.!
Item was changed:
----- Method: FFICallback>>init__ccall_ARM64 (in category 'initialization - thunk prepare') -----
init__ccall_ARM64
+ <abi: #ARM64> <init_ccall>
- <abi: #ARM64> <init>
"Initialize the receiver with a __ccall thunk. The thunk calls thunkEntry in the Alien/IA32ABI plugin,
whose source is in platforms/Cross/plugins/IA32ABI/arm64abicc.c. thunkEntry is the entry point
for Callbacks. The objective of the thunk is to call thunkEntry with all arguments to the call of the
thunk (registers and stack) as well as a pointer to the thunk itself. thunkEntry is as follows:
long long
thunkEntry(long x0, long x1, long x2, long x3,
long x4, long x5, long x6, long x7,
double d0, double d1, double d2, double d3,
double d4, double d5, double d6, double d7,
void *thunkpPlus16, sqIntptr_t *stackp)
thunkEntry then collects the various arguments (thunk, integer register arguments, floating-point
register arguments, stack pointer) in a VMCallbackContext and invokes the callback via invokeCallbackContext:."
self shouldBeImplemented
"self newCCall"!
Item was changed:
----- Method: FFICallback>>init__ccall_IA32 (in category 'initialization - thunk prepare') -----
init__ccall_IA32
+ <abi: #IA32> <init_ccall>
- <abi: #IA32> <init>
"Initialize the receiver with a __ccall thunk. The thunk calls thunkEntry in the IA32ABI plugin,
whose source is in platforms/Cross/plugins/IA32ABI/x64win64abicc.c. thunkEntry is the entry point
for Callbacks. The objective of the thunk is to call thunkEntry with all arguments to the call of the
thunk (registers and stack) as well as a pointer to the thunk itself. thunkEntry is as follows:
long
thunkEntry(void *thunkp, sqIntptr_t *stackp)
thunkEntry then collects the various arguments (thunk, stack pointer) in a VMCallbackContext and
invokes the callback via invokeCallbackContext:."
"thunk: push %esp 0x54 0xa1905454
thunk+01: push %esp 0x54
thunk+02: nop 0x90
thunk+03: mov $thunkEntry,%eax 0xb8 0x00 0x00 0x00 0x00 0x00000000 - entry
thunk+08: nop 0x90 0x68909090
thunk+09: nop 0x90
thunk+10: nop 0x90
thunk+11: push $thunk 0x68 0x00 0x00 0x00 0x00 0x00000000 - thunk
thunk+16: call *%eax 0xff 0xd0 0xc483d0ff
thunk+18: add $0xC,%esp 0x83 0xc4 0x0C 0x9090c30C
thunk+21: ret 0xc3
thunk+22: nop 0x90
thunk+23: nop 0x90"
thunk getHandle
unsignedLongAt: 1 put: 16rB8905454;
pointerAt: 5 put: self thunkEntryAddress length: 4;
unsignedLongAt: 9 put: 16r68909090;
pointerAt: 13 put: thunk getHandle length: 4;
unsignedLongAt: 17 put: 16rC483D0FF;
unsignedLongAt: 21 put: 16r9090C30C!
Item was changed:
----- Method: FFICallback>>init__ccall_X64 (in category 'initialization - thunk prepare') -----
init__ccall_X64
+ <abi: #X64> <init_ccall>
- <abi: #X64> <init>
"Initialize the receiver with a __ccall thunk. The thunk calls thunkEntry in the IA32ABI plugin,
whose source is in platforms/Cross/plugins/IA32ABI/x64sysvabicc.c. thunkEntry is the entry point
for Callbacks. The objective of the thunk is to call thunkEntry with all arguments to the call of the
thunk (registers and stack) as well as a pointer to the thunk itself. thunkEntry is as follows:
long
thunkEntry(long a0, long a1, long a2, long a3, long a4, long a5,
double d0, double d1, double d2, double d3,
double d4, double d5, double d6, double d7,
void *thunkp, sqIntptr_t *stackp)
thunkEntry then collects the various arguments (thunk, integer register arguments, floating-point
register arguments, stack pointer) in a VMCallbackContext and invokes the callback via invokeCallbackContext:."
"thunk+0x0: pushq %rsp 54
thunk+0x1: pushq %rsp 54
thunk+0x4: movabsq $thunk, %rax 48 b8 b0..b7 eight bytes of thunk address a.k.a. handle
thunk+0xc: pushq %rax 50
thunk+0xd: movabsq $thunkEntry, %rax 48 b8 b0..b7 eight bytes of the thunkEntry address
thunk+0x17: callq *%rax ff d0
thunk+0x19: addq $0x18, %rsp 48 83 c4 18
thunk+0x1d: retq c3
thunk+0x1e: nop 90
thunk+0x1f: nop 90"
thunk getHandle
unsignedLongAt: 1 put: 16rb8485454;
pointerAt: 5 put: thunk getHandle length: 8;
unsignedLongAt: 13 put: 16r00b84850; "00 is the first byte of the 64-bit constant the movabsq/0x48 opcode moves"
pointerAt: 16 put: self thunkEntryAddress length: 8;
unsignedByteAt: 24 put: 16rff;
unsignedLongAt: 25 put: 16rc48348d0;
unsignedLongAt: 29 put: 16r9090c318.!
Item was changed:
----- Method: FFICallback>>init__ccall_X64Win64 (in category 'initialization - thunk prepare') -----
init__ccall_X64Win64
+ <abi: #X64Win64> <init_ccall>
- <abi: #X64Win64> <init>
"Initialize the receiver with a __ccall thunk. The thunk calls thunkEntry in the IA32ABI plugin,
whose source is in platforms/Cross/plugins/IA32ABI/x64win64abicc.c. thunkEntry is the entry point
for Callbacks. The objective of the thunk is to call thunkEntry with all arguments to the call of the
thunk (registers and stack) as well as a pointer to the thunk itself. thunkEntry is as follows:
long long
thunkEntry(long long rcx, long long rdx,
long long r8, long long r9,
void *thunkp, sqIntptr_t *stackp)
thunkEntry then collects the various arguments (thunk, integer register arguments, stack pointer)
in a VMCallbackContext and invokes the callback via invokeCallbackContext:."
"thunk+0x0: pushq %rsp 54
thunk+0x1: pushq %rsp 54
thunk+0x4: movabsq $thunk, %rax 48 b8 b0..b7 eight bytes of thunk address a.k.a. addressField
thunk+0xc: pushq %rax 50
thunk+0xd: subq $0x20, %rsp 48 83 c4 e0 (this is addq -20 since the immediate is signed extended)
thunk+0x11: movabsq $thunkEntry, %rax 48 b8 b0..b7 eight bytes of the thunkEntry address
thunk+0x1b: callq *%rax ff d0
thunk+0x1d: addq $0x38, %rsp 48 83 c4 38
thunk+0x21: retq c3
thunk+0x22: nop 90
thunk+0x23: nop 90"
thunk getHandle
unsignedLongAt: 1 put: 16rb8485454;
pointerAt: 5 put: thunk getHandle length: 8;
unsignedLongAt: 13 put: 16rc4834850;
unsignedLongAt: 17 put: 16r00b848e0; "00 is the first byte of the 64-bit constant the movabsq/0x48 opcode moves"
pointerAt: 20 put: self thunkEntryAddress length: 8;
unsignedByteAt: 28 put: 16rff;
unsignedLongAt: 29 put: 16rc48348d0;
unsignedLongAt: 33 put: 16r9090c338.!
Item was changed:
----- Method: FFICallback>>init__stdcall: (in category 'initialization - thunk prepare') -----
init__stdcall: numBytes
"Initialize the receiver with a __stdcall thunk with numBytes argument bytes."
+ FFIPlatformDescription current abiSend: #'init_stdcall' to: self with: numBytes.!
- FFICallback methodsDo: [:method |
- (method selector beginsWith: 'init__stdcall') ifTrue: [
- (method hasPragma: #init) ifTrue: [
- (method pragmaAt: #abi:)
- ifNotNil: [:pragma | (pragma argumentAt: 1) = abi
- ifTrue: [^ self with: numBytes executeMethod: method]]]]].
-
- self error: 'Could not initialize thunk for current ABI: ', abi.!
Item was changed:
----- Method: FFICallback>>init__stdcall_IA32: (in category 'initialization - thunk prepare') -----
init__stdcall_IA32: numBytes
+ <abi: #IA32> <init_stdcall>
- <abi: #IA32> <init>
"Initialize the receiver with a __stdcall thunk with numBytes argument bytes. (See #init__ccall_IA32 for more info)"
"thunk: push %esp 0x54 0xa1905454
thunk+01: push %esp 0x54
thunk+02: nop 0x90
thunk+03: mov $thunkEntry,%eax 0xb8 0x00 0x00 0x00 0x00 0x00000000 - entry
thunk+08: nop 0x90 0x68909090
thunk+09: nop 0x90
thunk+10: nop 0x90
thunk+11: push $thunk 0x68 0x00 0x00 0x00 0x00 0x00000000 - thunk
thunk+16: call *%eax 0xff 0xd0 0xc483d0ff
thunk+18: add $0xC,%esp 0x83 0xc4 0x0C 0xBYTSc20C
thunk+21: ret $bytes 0xc2 0xBY 0xTS"
thunk getHandle
unsignedLongAt: 1 put: 16rB8905454;
pointerAt: 5 put: self thunkEntryAddress length: 4;
unsignedLongAt: 9 put: 16r68909090;
pointerAt: 13 put: thunk getHandle length: 4;
unsignedLongAt: 17 put: 16rC483D0FF;
unsignedShortAt: 21 put: 16rC20C;
unsignedShortAt: 23 put: numBytes.!
Marcel Taeumel uploaded a new version of FFI-Kernel to project FFI:
http://source.squeak.org/FFI/FFI-Kernel-mt.168.mcz
==================== Summary ====================
Name: FFI-Kernel-mt.168
Author: mt
Time: 23 May 2021, 3:46:48.099158 pm
UUID: 3db8d87e-8c71-7946-840a-b09c28b36af3
Ancestors: FFI-Kernel-mt.167
Minor clean-up in raw-bits array to clarify the use of FFIAtomicReadWriteSend for FloatArray and (Signed|Unsigned)IntegerArray.
=============== Diff against FFI-Kernel-mt.167 ===============
Item was added:
+ ----- Method: FloatArray>>doubleAt: (in category '*FFI-Kernel-accessing') -----
+ doubleAt: byteOffset
+ "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:."
+
+ ^ self atByteOffset: byteOffset!
Item was added:
+ ----- Method: FloatArray>>doubleAt:put: (in category '*FFI-Kernel-accessing') -----
+ doubleAt: byteOffset put: value
+ "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:put:."
+
+ ^ self atByteOffset: byteOffset put: value!
Item was added:
+ ----- Method: FloatArray>>floatAt: (in category '*FFI-Kernel-accessing') -----
+ floatAt: byteOffset
+ "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:."
+
+ ^ self atByteOffset: byteOffset!
Item was added:
+ ----- Method: FloatArray>>floatAt:put: (in category '*FFI-Kernel-accessing') -----
+ floatAt: byteOffset put: value
+ "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:put:."
+
+ ^ self atByteOffset: byteOffset put: value!
Item was removed:
- ----- Method: RawBitsArray>>doubleAt: (in category '*FFI-Kernel-accessing') -----
- doubleAt: byteOffset
- "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:."
-
- ^ self atByteOffset: byteOffset!
Item was removed:
- ----- Method: RawBitsArray>>doubleAt:put: (in category '*FFI-Kernel-accessing') -----
- doubleAt: byteOffset put: value
- "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:put:."
-
- ^ self atByteOffset: byteOffset put: value!
Item was removed:
- ----- Method: RawBitsArray>>floatAt: (in category '*FFI-Kernel-accessing') -----
- floatAt: byteOffset
- "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:."
-
- ^ self atByteOffset: byteOffset!
Item was removed:
- ----- Method: RawBitsArray>>floatAt:put: (in category '*FFI-Kernel-accessing') -----
- floatAt: byteOffset put: value
- "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:put:."
-
- ^ self atByteOffset: byteOffset put: value!
Item was removed:
- ----- Method: RawBitsArray>>integerAt:put:size:signed: (in category '*FFI-Kernel-accessing') -----
- integerAt: byteOffset put: value size: nBytes signed: aBoolean
- "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:put:."
-
- ^ self atByteOffset: byteOffset put: value!
Item was removed:
- ----- Method: RawBitsArray>>integerAt:size:signed: (in category '*FFI-Kernel-accessing') -----
- integerAt: byteOffset size: nBytes signed: aBoolean
- "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:."
-
- ^ self atByteOffset: byteOffset!
Item was added:
+ ----- Method: SignedIntegerArray>>integerAt:put:size:signed: (in category '*FFI-Kernel-accessing') -----
+ integerAt: byteOffset put: value size: nBytes signed: aBoolean
+ "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:put:."
+
+ ^ self atByteOffset: byteOffset put: value!
Item was added:
+ ----- Method: SignedIntegerArray>>integerAt:size:signed: (in category '*FFI-Kernel-accessing') -----
+ integerAt: byteOffset size: nBytes signed: aBoolean
+ "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:."
+
+ ^ self atByteOffset: byteOffset!
Item was added:
+ ----- Method: UnsignedIntegerArray>>integerAt:put:size:signed: (in category '*FFI-Kernel-accessing') -----
+ integerAt: byteOffset put: value size: nBytes signed: aBoolean
+ "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:put:."
+
+ ^ self atByteOffset: byteOffset put: value!
Item was added:
+ ----- Method: UnsignedIntegerArray>>integerAt:size:signed: (in category '*FFI-Kernel-accessing') -----
+ integerAt: byteOffset size: nBytes signed: aBoolean
+ "Backstop for compatibility with handle-based access. Raw-bits arrays are their own handle. See #getHandle and ExternalType >> #handle:at:."
+
+ ^ self atByteOffset: byteOffset!
Marcel Taeumel uploaded a new version of FFI-Kernel to project FFI:
http://source.squeak.org/FFI/FFI-Kernel-mt.167.mcz
==================== Summary ====================
Name: FFI-Kernel-mt.167
Author: mt
Time: 23 May 2021, 3:45:09.673158 pm
UUID: 6e67015a-9732-e741-aefe-49f7cf42ef82
Ancestors: FFI-Kernel-mt.166
Complements FFI-Pools-mt.27. New preference for using a type pool for about 2x faster access to struct fields.
=============== Diff against FFI-Kernel-mt.166 ===============
Item was changed:
----- Method: ByteArray>>structAt:length: (in category '*FFI-Kernel') -----
structAt: byteOffset length: length
"Return a structure of the given length starting at the indicated byte offset."
| value startByteOffset |
+ self flag: #todo. "mt: Better name #unsignedBytesAt:length:?"
value := ByteArray new: length.
startByteOffset := byteOffset - 1.
1 to: length do: [:valueByteOffset |
value
unsignedByteAt: valueByteOffset
put: (self unsignedByteAt: startByteOffset + valueByteOffset)].
^ value!
Item was changed:
----- Method: ByteArray>>structAt:put:length: (in category '*FFI-Kernel') -----
structAt: byteOffset put: value length: length
"Store a structure of the given length starting at the indicated byte offset."
| startByteOffset |
+ self flag: #todo. "mt: Better name #unsignedBytesAt:put:length:?"
startByteOffset := byteOffset - 1.
1 to: length do: [:valueByteOffset |
self
unsignedByteAt: startByteOffset + valueByteOffset
put: (value unsignedByteAt:valueByteOffset)].
^ value!
Item was changed:
----- Method: ExternalArrayType>>readAlias (in category 'external structure') -----
readAlias
^ '^ {1} fromHandle: handle{2}'
format: {
(referentClass ifNil: [ExternalData]) name.
referentClass ifNotNil: [''] ifNil: [
+ ' type: ', self storeStringForField]}!
- ' type: ', self storeString]}!
Item was changed:
----- Method: ExternalArrayType>>readFieldAt: (in category 'external structure') -----
readFieldAt: byteOffset
^ '^ ExternalData fromHandle: (handle structAt: {1} length: {2}) type: {3}'
format: {
byteOffset.
self byteSize.
+ self storeStringForField}!
- self storeString}!
Item was changed:
----- Method: ExternalPointerType>>readFieldAt: (in category 'external structure') -----
readFieldAt: byteOffset
"
ExternalStructure defineAllFields.
"
^ '^ {1} fromHandle: (handle pointerAt: {2} length: {3}){4}'
format: {
(referentClass ifNil: [ExternalData]) name.
byteOffset.
self byteSize.
referentClass ifNotNil: [''] ifNil: [
+ ' type: ', self asNonPointerType "content type" storeStringForField]}!
- ' type: ', self asNonPointerType "content type" storeString]}!
Item was changed:
ExternalObject subclass: #ExternalStructure
instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: 'ExternalTypePool FFIConstants'
- classVariableNames: 'PreviousPlatform'
- poolDictionaries: 'FFIConstants'
category: 'FFI-Kernel'!
ExternalStructure class
instanceVariableNames: 'compiledSpec byteAlignment'!
!ExternalStructure commentStamp: 'eem 6/26/2019 15:26' prior: 0!
An ExternalStructure is for representing external data that is
- either a structure composed of different fields (a struct of C language)
- or an alias for another type (like a typedef of C language)
It reserves enough bytes of data for representing all the fields.
The data is stored into the handle instance variable which can be of two different types:
- ExternalAddress
If the handle is an external address then the object described does not reside in the Smalltalk object memory.
- ByteArray
If the handle is a byte array then the object described resides in Smalltalk memory.
Instance Variables (class side)
byteAlignment: <Integer>
compiledSpec: <WordArray>
byteAlignment
- the required alignment for the structure
compiledSpec
- the bit-field definiton of the structure in the ExternalType encoding understood by the VM's FFI call marshalling machinery.
A specific structure is defined by subclassing ExternalStructure and specifying its #fields via a class side method.
For example if we define a subclass:
ExternalStructure subclass: #StructExample
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'garbage'.
Then declare the fields like this:
StructExample class compile: 'fields ^#( (name ''char*'') (color ''ulong'') )' classified: 'garbage'.
It means that this type is composed of two different fields:
- a string (accessed thru the field #name)
- and an unsigned 32bit integer (accessed thru the field #color).
It represents the following C type:
struct StructExample {char *name; uint32_t color; };
The accessors for those fields can be generated automatically like this:
StructExample defineFields.
As can be verified in a Browser:
StructExample browse.
We see that name and color fields are stored sequentially in different zones of data.
The total size of the structure can be verified with:
StructExample byteSize = (Smalltalk wordSize + 4).
An ExternalStructure can also be used for defining an alias.
The fields definition must contain only 2 elements: an eventual accessor (or nil) and the type.
For example, We can define a machine dependent 'unsigned long' like this:
ExternalStructure subclass: #UnsignedLong
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'garbage'.
Then set the fields like this:
UnsignedLong class compile: 'fields ^(Smalltalk wordSize=4 or: [Smalltalk platformName=''Win64''])
ifTrue: [#(nil ''ulong'')] ifFalse: [#(nil ''ulonglong'')]' classified: 'garbage'.
And verify the size on current platform:
UnsignedLong byteSize.
Then, the class names 'UnsignedLong' and 'StructExamples' acts as a type specification.
They can be used for composing other types, and for defining prototype of external functions:
LibraryExample>>initMyStruct: aStructExample name: name color: anInteger
<cdecl: void 'init_my_struct'( StructExample * char * UnsignedLong )>
self externalCallFailed
!
ExternalStructure class
instanceVariableNames: 'compiledSpec byteAlignment'!
Item was changed:
Object subclass: #ExternalType
instanceVariableNames: 'compiledSpec referentClass referencedType byteAlignment'
+ classVariableNames: 'ArrayClasses ArrayTypes AtomicSends AtomicTypeNames AtomicTypes ExtraTypeChecks StructTypes UseArrayClasses UseTypePool'
- classVariableNames: 'ArrayClasses ArrayTypes AtomicSends AtomicTypeNames AtomicTypes ExtraTypeChecks StructTypes UseArrayClasses'
poolDictionaries: 'FFIConstants'
category: 'FFI-Kernel'!
!ExternalType commentStamp: 'mt 6/5/2020 18:25' prior: 0!
An external type represents the type of external objects.
Instance variables:
compiledSpec <WordArray> Compiled specification of the external type
referentClass <Behavior | nil> Class type of argument required
referencedType <ExternalType> Associated (non)pointer type with the receiver
byteAlignment <Integer | nil> The desired alignment for a field of the external type within a structure. If nil it has yet to be computed.
Compiled Spec:
The compiled spec defines the type in terms which are understood by the VM. Each word is defined as:
bits 0...15 - byte size of the entity
bit 16 - structure flag (FFIFlagStructure)
This flag is set if the following words define a structure
bit 17 - pointer flag (FFIFlagPointer)
This flag is set if the entity represents a pointer to another object
bit 18 - atomic flag (FFIFlagAtomic)
This flag is set if the entity represents an atomic type.
If the flag is set the atomic type bits are valid.
bits 19...23 - unused
bits 24...27 - atomic type (FFITypeVoid ... FFITypeDoubleFloat)
bits 28...31 - unused
Note that all combinations of the flags FFIFlagPointer, FFIFlagAtomic, and FFIFlagStructure are invalid, EXCEPT from the following:
FFIFlagPointer + FFIFlagAtomic:
This defines a pointer to an atomic type (e.g., 'char*', 'int*').
The actual atomic type is represented in the atomic type bits.
FFIFlagPointer + FFIFlagStructure:
This defines a structure which is a typedef of a pointer type as in
typedef void* VoidPointer;
typedef Pixmap* PixmapPtr;
It requires a byte size of four or eight (e.g. a 32-bit or 64-bit pointer) to work correctly.
[Note: Other combinations may be allowed in the future]
!
Item was changed:
----- Method: ExternalType class>>resetAllAtomicTypes (in category 'housekeeping') -----
resetAllAtomicTypes
"Warning: This call is only required if you change the initialization for AtomicTypes."
AtomicTypes := nil.
AtomicSends := nil.
+ ArrayClasses := nil.
+
StructTypes := nil.
ArrayTypes := nil.
- ArrayClasses := nil.
self initializeDefaultTypes.
self initializeArrayClasses.
self resetAllStructureTypes.!
Item was changed:
----- Method: ExternalType class>>resetAllStructureTypes (in category 'housekeeping') -----
resetAllStructureTypes
"Warning: This call is only required if you change the container for StructTypes!! Note that (2) and (3) can be swapped but that puts unnecessary pressure on the GC."
StructTypes := nil.
ArrayTypes := nil.
"1) Initialize the container for structure types."
self initializeStructureTypes.
"2) Recompile all FFI calls to create and persist structure types."
self recompileAllLibraryFunctions.
"3) Update all structure types' spec and alignment."
+ ExternalTypePool reset.
+ ExternalStructure defineAllFields.
+ ExternalTypePool cleanUp.
- ExternalStructure compileAllFields.
!
Item was changed:
----- Method: ExternalType class>>useArrayClasses (in category 'preferences') -----
useArrayClasses
+ <preference: 'Use array classes (i.e. RawBitsArray)'
- <preference: 'Use Array Classes (i.e. RawBitsArray)'
categoryList: #('FFI Kernel')
description: 'When true, type-based allocation in (local) object memory will use array classes instead of a ByteArray wrapped in ExternalData. Does not apply to external allocation.'
type: #Boolean>
^UseArrayClasses ifNil:[true]!
Item was added:
+ ----- Method: ExternalType class>>useTypePool (in category 'preferences') -----
+ useTypePool
+ <preference: 'Use type pool in structure fields'
+ categoryList: #('FFI Kernel')
+ description: 'When true, fill a pool of external types to be used in struct-field accessors. See ExternalTypePool.'
+ type: #Boolean>
+ ^UseTypePool ifNil: [true]!
Item was added:
+ ----- Method: ExternalType class>>useTypePool: (in category 'preferences') -----
+ useTypePool: aBoolean
+
+ UseTypePool = aBoolean ifTrue: [^ self].
+
+ UseTypePool := aBoolean.
+
+ Cursor wait showWhile: [
+ "Either fill or clean out the type pool."
+ ExternalTypePool reset.
+ ExternalStructure defineAllFields.
+ ExternalTypePool cleanUp].!
Item was added:
+ ----- Method: ExternalType>>storeStringForField (in category 'external structure') -----
+ storeStringForField
+ "Answers the code snippet to be used to make use of the receiver during field access in an external structure."
+
+ ^ self class useTypePool
+ ifTrue: [ExternalTypePool assuredPoolVarNameFor: self]
+ ifFalse: [self storeString]!
Item was changed:
(PackageInfo named: 'FFI-Kernel') postscript: 'Smalltalk removeFromStartUpList: ExternalAddress.
Smalltalk removeFromStartUpList: ExternalObject.
"Introduce FFIAtomicReadWriteSend. All types need to be reset and all fields need to be re-defined."
+ ExternalType resetAllTypes.'!
- ExternalType resetAllTypes.
- ExternalStructure defineAllFields.
- '!