[squeak-dev] FFI: FFI-Kernel-mt.201.mcz

commits at source.squeak.org commits at source.squeak.org
Thu Aug 12 14:59:02 UTC 2021

Marcel Taeumel uploaded a new version of FFI-Kernel to project FFI:

==================== Summary ====================

Name: FFI-Kernel-mt.201
Author: mt
Time: 12 August 2021, 4:59:01.488801 pm
UUID: 31d99f8f-471c-4b4d-ae81-67d2d93588c1
Ancestors: FFI-Kernel-mt.200

Speed up struct-field accessor generation/compilation further. Make logging opt-in via preference for debugging or learning purposes only.

=============== Diff against FFI-Kernel-mt.200 ===============

Item was changed:
  ExternalObject subclass: #ExternalStructure
  	instanceVariableNames: ''
+ 	classVariableNames: 'LogAccessorSourceCode'
- 	classVariableNames: ''
  	poolDictionaries: 'ExternalTypePool 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>
  	- the required alignment for the structure
  	- 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 added:
+ ----- Method: ExternalStructure class>>logAccessorSourceCode (in category 'preferences') -----
+ logAccessorSourceCode
+ 	<preference: 'Log source code for generated accessors'
+ 		categoryList: #('FFI Kernel')
+ 		description: 'When true, keep the sources generated for struct-field accessors in the method trailer. While this might increase the image file notably, no sources file will be accessed for performance reasons. When false, only the decompiled sources will be available for those accessors.'
+ 		type: #Boolean>
+ 	^ LogAccessorSourceCode ifNil: [false]!

Item was added:
+ ----- Method: ExternalStructure class>>logAccessorSourceCode: (in category 'preferences') -----
+ logAccessorSourceCode: aBoolean
+ 	LogAccessorSourceCode = aBoolean ifTrue: [^ self].
+ 	LogAccessorSourceCode := aBoolean.
+ 	ExternalStructure defineAllFields.!

Item was changed:
  ----- Method: ExternalStructure class>>maybeCompileAccessor:withSelector: (in category 'field definition - support') -----
  maybeCompileAccessor: newSourceString withSelector: selector
+ 	"When logging is enabled, only compile if category or source changed."
- 	"Only compile if category or source changed. Use generic author initials during compilation."
+ 	| log newCategory oldMethod newMethod |
+ 	log := self logAccessorSourceCode.
- 	| newCategory existingReference method |
  	newCategory := #'*autogenerated - accessing'.
+ 	(log and: [self includesSelector: selector]) ifTrue: [
+ 		oldMethod := self compiledMethodAt: selector.
+ 		((self organization categoryOfElement: selector) = newCategory
+ 			and: [oldMethod trailer sourceCode = newSourceString])
+ 				ifTrue: [^ self]].
- 	((existingReference := MethodReference class: self selector: selector) isValid
- 		and: [existingReference category = newCategory]
- 		and: [existingReference sourceString = newSourceString])
- 			ifTrue: [^ self].
  	"Compile silently. No changeStamp. No access to sources file. Keep source code in method trailer."
  	self compileSilently: newSourceString classified: newCategory.
+ 	log ifTrue: [
+ 		newMethod := self compiledMethodAt: selector.
+ 		newMethod becomeForward: (newMethod copyWithSourceCode: newSourceString)].!
- 	method := self compiledMethodAt: selector.
- 	method becomeForward: (method copyWithSourceCode: newSourceString).!

Item was changed:
  ----- Method: GenericIntegerReadWriteSend class>>useSpecificIntegerPrimitives: (in category 'preferences') -----
  useSpecificIntegerPrimitives: aBoolean
  	UseSpecificIntegerPrimitives = aBoolean ifTrue: [^ self].
  	aBoolean ~= self canUseSpecificIntegerPrimitives
  		ifTrue: [self notify: 'FFI plugin does not support specific integer primitives. Proceed to update atomic sends anyway. Fallback code will redirect to generic integer primitive. Performance might be affected.'].
  	UseSpecificIntegerPrimitives := aBoolean.
  	Cursor wait showWhile: [
  		ExternalType initializeAtomicSends.
+ 		ExternalStructure defineAllFields].!
- 		MessageTally spyOn: [ExternalStructure defineAllFields]].!

More information about the Squeak-dev mailing list