Eliot Miranda uploaded a new version of FFI-Kernel to project FFI:
http://source.squeak.org/FFI/FFI-Kernel.terf-eem.187.mcz
==================== Summary ====================
Name: FFI-Kernel.terf-eem.187
Author: eem
Time: 2 August 2021, 11:15:30.36735 am
UUID: 54737d19-448d-4377-859b-be958a51dec6
Ancestors: FFI-Kernel-eem.186
Performance:
- cache an ExternalStructure class's externalType in a class inst var.
- directly allocate an ExternalStructure[Type] using self byteSize rather than deferring to the externalType.
Minor: avoid variable shadowing warnings in compileStructureSpec:withAccessors:, compileTypeAliasSpec:withAccessors:, & maybeCompileAccessor:withSelector:.
=============== Diff against FFI-Kernel-eem.186 ===============
Item was changed:
----- Method: ExternalArrayType>>contentType (in category 'external data') -----
contentType "^ <ExternalType>"
+ "We are an array of things. Our content type is encoded in the compiledSpec's headerWord. The super implementation of #typeName can figure that out."
+ self flag: #contentVsContainer. "mt: For n-dimensional containers, we might have to adapt this."
+ ^ contentType ifNil: [contentType := ExternalType typeNamed: super typeName ]!
- ^ contentType!
Item was changed:
ExternalObject subclass: #ExternalStructure
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: 'ExternalTypePool FFIConstants'
category: 'FFI-Kernel'!
ExternalStructure class
+ instanceVariableNames: 'compiledSpec byteAlignment externalType'!
- 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 externalType'!
- instanceVariableNames: 'compiledSpec byteAlignment'!
Item was changed:
----- Method: ExternalStructure class>>allocate (in category 'instance creation') -----
allocate
+ ^ self fromHandle: (ByteArray new: self byteSize)!
-
- ^self externalType allocate!
Item was changed:
----- Method: ExternalStructure class>>compileStructureSpec:withAccessors: (in category 'field definition - support') -----
compileStructureSpec: specArray withAccessors: aSymbol
"Compile a type specification for the FFI calls.
Return the newly compiled spec.
Eventually generate the field accessors according to following rules:
- aSymbol = #always always generate the accessors
- aSymbol = #never never generate the accessors
- aSymbol = #generated only generate the auto-generated accessors
- aSymbol = #absent only generate the absent accessors"
| newByteAlignment byteOffset typeSpec newCompiledSpec |
byteOffset := 0.
newByteAlignment := self minStructureAlignment.
typeSpec := WriteStream on: (WordArray new: 10).
typeSpec nextPut: FFIFlagStructure.
+ specArray do:
+ [:spec | | fieldName fieldTypeName fieldType typeSize fieldAlignment |
- specArray do: [:spec |
- | fieldName fieldTypeName externalType typeSize fieldAlignment |
fieldName := spec first.
fieldTypeName := spec second.
+ fieldType := (ExternalType typeNamed: fieldTypeName) ifNil: [self errorTypeNotFound: spec second].
+ typeSize := fieldType byteSize.
+ fieldAlignment := (fieldType byteAlignment max: self minFieldAlignment) min: self maxFieldAlignment.
- externalType := (ExternalType typeNamed: fieldTypeName)
- ifNil: [self errorTypeNotFound: spec second].
- typeSize := externalType byteSize.
- fieldAlignment := (externalType byteAlignment
- max: self minFieldAlignment)
- min: self maxFieldAlignment.
byteOffset := byteOffset alignedTo: fieldAlignment.
newByteAlignment := newByteAlignment max: fieldAlignment.
+ spec size > 2 ifTrue: "extra size"
+ [spec third < typeSize
- spec size > 2 ifTrue: ["extra size"
- spec third < typeSize
ifTrue: [^ self error: 'Explicit type size is less than expected'].
+ typeSize := spec third].
+ (fieldName notNil and: [self shouldGenerate: fieldName policy: aSymbol]) ifTrue:
+ [self generateStructureFieldAccessorsFor: fieldName startingAt: byteOffset + 1 type: fieldType].
+ typeSpec nextPutAll: (fieldType embeddedSpecWithSize: typeSize).
+ byteOffset := byteOffset + typeSize].
- typeSize := spec third.
- ].
- (fieldName notNil and: [self shouldGenerate: fieldName policy: aSymbol]) ifTrue: [
- self generateStructureFieldAccessorsFor: fieldName startingAt: byteOffset + 1 type: externalType.
- ].
- typeSpec nextPutAll: (externalType embeddedSpecWithSize: typeSize).
- byteOffset := byteOffset + typeSize.
- ].
newByteAlignment := newByteAlignment min: self maxStructureAlignment.
byteOffset := byteOffset alignedTo: newByteAlignment.
newCompiledSpec := typeSpec contents.
newCompiledSpec at: 1 put: (byteOffset bitOr: FFIFlagStructure).
self
setCompiledSpec: newCompiledSpec
+ byteAlignment: newByteAlignment!
- byteAlignment: newByteAlignment.!
Item was changed:
----- Method: ExternalStructure class>>compileTypeAliasSpec:withAccessors: (in category 'field definition - support') -----
compileTypeAliasSpec: spec withAccessors: aSymbol
"Define all the fields in the receiver.
Return the newly compiled spec."
+ | fieldName fieldTypeName fieldType |
- | fieldName fieldTypeName externalType |
fieldName := spec first.
fieldTypeName := spec second.
+ fieldType := (ExternalType typeNamed: fieldTypeName) ifNil: [self errorTypeNotFound: spec second].
+ (fieldName notNil and: [self shouldGenerate: fieldName policy: aSymbol]) ifTrue:
+ [self generateTypeAliasAccessorsFor: fieldName type: fieldType].
+ fieldType isPointerType
+ ifTrue: "Special case. Typedef for a pointer type, e.g., typedef char* LPSTR in Win32 API. Mark it as both structure and pointer. Note that #isPointerType will only answer true for the in-image pointer type, not the non-pointer alias for the pointer."
+ [self
- externalType := (ExternalType typeNamed: fieldTypeName)
- ifNil: [self errorTypeNotFound: spec second].
- (fieldName notNil and:[self shouldGenerate: fieldName policy: aSymbol]) ifTrue:[
- self generateTypeAliasAccessorsFor: fieldName type: externalType].
- externalType isPointerType
- ifTrue: ["Special case. Typedef for a pointer type, e.g., typedef char* LPSTR in Win32 API. Mark it as both structure and pointer. Note that #isPointerType will only answer true for the in-image pointer type, not the non-pointer alias for the pointer."
- self
setCompiledSpec: (WordArray with: ExternalType pointerAliasSpec)
byteAlignment: ExternalType pointerAliasAlignment]
+ ifFalse: "Usual case. Typedef for another struct type or atomic type. Just re-use compiled spec and extras from the aliased type."
+ [self
- ifFalse: ["Usual case. Typedef for another struct type or atomic type. Just re-use compiled spec and extras from the aliased type."
- self
flag: #isTypeAlias;
+ setCompiledSpec: fieldType compiledSpec
+ byteAlignment: fieldType byteAlignment]!
- setCompiledSpec: externalType compiledSpec
- byteAlignment: externalType byteAlignment].!
Item was changed:
----- Method: ExternalStructure class>>externalType (in category 'external type') -----
externalType
"Return an external type describing the receiver as a structure"
+ ^externalType ifNil: [externalType := ExternalType structTypeNamed: self name]!
- ^ExternalType structTypeNamed: self name!
Item was changed:
----- Method: ExternalStructure class>>maybeCompileAccessor:withSelector: (in category 'field definition - support') -----
maybeCompileAccessor: aString withSelector: selector
"Only compile if category or source changed."
+ | theCategory ref |
+ theCategory := #'*autogenerated - accessing'.
- | category ref |
- category := #'*autogenerated - accessing'.
((ref := MethodReference class: self selector: selector) isValid
+ and: [ref sourceString = aString]) ifTrue:
+ [ref category ~= theCategory ifTrue:
+ [self organization classify: selector under: theCategory].
+ ^self].
+ self compile: aString classified: theCategory!
- and: [ref category = category]
- and: [ref sourceString = aString])
- ifTrue: [^ self].
- self compile: aString classified: category.!
Item was added:
+ ----- Method: ExternalStructureType>>allocate (in category 'external data') -----
+ allocate
+ ^ referentClass fromHandle: (ByteArray new: self byteSize)!