[Pharo-project] NativeBoost design and deployment
On 4 January 2013 08:54, Torsten Bergmann <astares@gmx.de> wrote:
When one deploys an image one usually requires ONLY the image - not the source or changes file. An "image locker" code would look like this:
SmalltalkImage checkSourcesFileAvailability: false. SmalltalkImage checkChangesFileAvailability: false
do disable acording warnings when source/changefile is removed.
If one uses NativeBoost in such a deployment scenario, for instance the
NBWin32Shell shellBrowse: 'http://www.google.de'
functionality the internal code (due to missing source) now looks like this
shellExecute: t1 file: t2 parameters: t3 directory: t4 show: t5 <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'> ^ self nbCall: #(#HINSTANCE #ShellExecuteA #(0 #, #LPCTSTR #lpOperation #, #LPCTSTR #lpFile #, #LPCTSTR #lpParameters #, #LPCTSTR #lpDirectory #, #INT #nShowCmd ) ) module: 'Shell32.dll'
Hence the t1 ... t5 parameters.
NativeBoost is in this situation not able to match the FFI parameters and throws an error "Could not find accessor for variable ..."
Try yourself without a changes and source file. This makes NativeBoost not very deployment friendly and unusable in such a "minimal deployment" scenario ...
Any comments?
yes it needs sources (indirectly) to bind method's argument names during code generation. To avoid that, i can imagine that one must modify a compiler to detect if compiled method primitive requires arg names, and store them in method properties. Like that later code generator can use them without need to access the source code.
Thanks T.
-- Best regards, Igor Stasenko
On Fri, Jan 4, 2013 at 12:37 AM, Igor Stasenko <siguctua@gmail.com> wrote:
On 4 January 2013 08:54, Torsten Bergmann <astares@gmx.de> wrote:
When one deploys an image one usually requires ONLY the image - not the source or changes file. An "image locker" code would look like this:
SmalltalkImage checkSourcesFileAvailability: false. SmalltalkImage checkChangesFileAvailability: false
do disable acording warnings when source/changefile is removed.
If one uses NativeBoost in such a deployment scenario, for instance the
NBWin32Shell shellBrowse: 'http://www.google.de'
functionality the internal code (due to missing source) now looks like this
shellExecute: t1 file: t2 parameters: t3 directory: t4 show: t5 <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'> ^ self nbCall: #(#HINSTANCE #ShellExecuteA #(0 #, #LPCTSTR #lpOperation #, #LPCTSTR #lpFile #, #LPCTSTR #lpParameters #, #LPCTSTR #lpDirectory #, #INT #nShowCmd ) ) module: 'Shell32.dll'
Hence the t1 ... t5 parameters.
NativeBoost is in this situation not able to match the FFI parameters and throws an error "Could not find accessor for variable ..."
Try yourself without a changes and source file. This makes NativeBoost not very deployment friendly and unusable in such a "minimal deployment" scenario ...
Any comments?
yes it needs sources (indirectly) to bind method's argument names during code generation. To avoid that, i can imagine that one must modify a compiler to detect if compiled method primitive requires arg names, and store them in method properties. Like that later code generator can use them without need to access the source code.
Indeed. If a pragma were used then the whole thing could look a lot nicer, not be dependent on source, and include support for an error code. e.g. shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd <nbCall: #(#HINSTANCE #ShellExecuteA #(0 #, #LPCTSTR #lpOperation #, #LPCTSTR #lpFile #, #LPCTSTR #lpParameters #, #LPCTSTR #lpDirectory #, #INT #nShowCmd ) ) module: 'Shell32.dll' errorCode: ec> ^self nbCallFailedWith: ec It's pretty trivial to add such pragma compilers to the compiler. There is an example for the FFI. It also frees one to use a nicer syntax, e.g. shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd <nbCall: 'HINSTANCE ShellExecuteA(0, LPCTSTR lpOperation, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd)' module: 'Shell32.dll' errorCode: ec> ^self nbCallFailedWith: ec
Thanks T.
-- Best regards, Igor Stasenko
-- best, Eliot
On 8 January 2013 19:38, Eliot Miranda <eliot.miranda@gmail.com> wrote:
On Fri, Jan 4, 2013 at 12:37 AM, Igor Stasenko <siguctua@gmail.com> wrote:
On 4 January 2013 08:54, Torsten Bergmann <astares@gmx.de> wrote:
When one deploys an image one usually requires ONLY the image - not the source or changes file. An "image locker" code would look like this:
SmalltalkImage checkSourcesFileAvailability: false. SmalltalkImage checkChangesFileAvailability: false
do disable acording warnings when source/changefile is removed.
If one uses NativeBoost in such a deployment scenario, for instance the
NBWin32Shell shellBrowse: 'http://www.google.de'
functionality the internal code (due to missing source) now looks like this
shellExecute: t1 file: t2 parameters: t3 directory: t4 show: t5 <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'> ^ self nbCall: #(#HINSTANCE #ShellExecuteA #(0 #, #LPCTSTR #lpOperation #, #LPCTSTR #lpFile #, #LPCTSTR #lpParameters #, #LPCTSTR #lpDirectory #, #INT #nShowCmd ) ) module: 'Shell32.dll'
Hence the t1 ... t5 parameters.
NativeBoost is in this situation not able to match the FFI parameters and throws an error "Could not find accessor for variable ..."
Try yourself without a changes and source file. This makes NativeBoost not very deployment friendly and unusable in such a "minimal deployment" scenario ...
Any comments?
yes it needs sources (indirectly) to bind method's argument names during code generation. To avoid that, i can imagine that one must modify a compiler to detect if compiled method primitive requires arg names, and store them in method properties. Like that later code generator can use them without need to access the source code.
Indeed. If a pragma were used then the whole thing could look a lot nicer, not be dependent on source, and include support for an error code. e.g.
shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd
<nbCall: #(#HINSTANCE #ShellExecuteA #(0 #, #LPCTSTR #lpOperation #, #LPCTSTR #lpFile #, #LPCTSTR #lpParameters #, #LPCTSTR #lpDirectory #, #INT #nShowCmd ) ) module: 'Shell32.dll' errorCode: ec>
^self nbCallFailedWith: ec
It's pretty trivial to add such pragma compilers to the compiler. There is an example for the FFI. It also frees one to use a nicer syntax, e.g.
shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd <nbCall: 'HINSTANCE ShellExecuteA(0, LPCTSTR lpOperation, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd)' module: 'Shell32.dll' errorCode: ec>
^self nbCallFailedWith: ec
i do not see how encoding function signature in pragma could help with binding argument names. Yes, you need to get some control at compile time to be able to encode method arg names somewhere in method properties for later use.. but not at cost of moving everything into pragma. And 'nice' syntax is actually already there .. nothing prevents you from using strings for function signature, e.g. self nbCall: 'int foo()' equivalent to self nbCall: #(int foo() ) (i don't know why Torsten gave code in such form, that could leave an impression that syntax is horrible ;) And besides, how you suppose to "pragmatize" following: storeDouble: aDouble at: address " This method stores a double floating point value at given memory address. an address can be an instance of NBExternalAddress, or simple ByteArray with at least 8 bytes long, which will hold a 64bit floating-point value" <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin' error: errorCode> ^ self nbCallout options: #( - optCoerceNilToNull + optAllowByteArraysPtr + optAllowExternalAddressPtr ); function: #( void (void * address, double aDouble) ) emit: [:gen | | asm | asm := gen asm. "Here , we expecting that an address value is on top of the stack" asm pop: EDX; "load an address value into EDX register by popping it from a stack" "now copy the floating point value (which is 8-bytes long) to the given address" mov: ESP ptr to: EAX; mov: EAX to: EDX ptr; " store first 32bit part of 64bit double value" mov: ESP ptr + 4 to: EAX; mov: EAX to: EDX ptr + 4. " store second 32bit part of 64bit double value" ] -- Best regards, Igor Stasenko.
The issue is not limited to only pragma as the route, it can the CompiledMethod extension or any. Eventually stripping source/ disconnecting the sources / changes file is an expectation.... Otherwise can there be an easy way of truncated changes / sources file of the NativeBoost part alone read only that can allow the functionality as is. Bar this part of the code I am sure all the rest can be a pragma, so may need to figure that out..how this may fit in compile / parse . ******************* [ :gen | | asm | asm := gen asm. "Here , we expecting that an address value is on top of the stack" asm pop: EDX; "load an address value into EDX register by popping it from a stack" "now copy the floating point value (which is 8-bytes long) to the given address" mov: ESP ptr to: EAX; mov: EAX to: EDX ptr; " store first 32bit part of 64bit double value" mov: ESP ptr + 4 to: EAX; mov: EAX to: EDX ptr + 4. " store second 32bit part of 64bit double value" ...] ******************* On Wed, Jan 9, 2013 at 1:40 PM, Igor Stasenko <siguctua@gmail.com> wrote:
On 8 January 2013 19:38, Eliot Miranda <eliot.miranda@gmail.com> wrote:
On Fri, Jan 4, 2013 at 12:37 AM, Igor Stasenko <siguctua@gmail.com>
wrote:
On 4 January 2013 08:54, Torsten Bergmann <astares@gmx.de> wrote:
When one deploys an image one usually requires ONLY the image - not the source or changes file. An "image locker" code would look like this:
SmalltalkImage checkSourcesFileAvailability: false. SmalltalkImage checkChangesFileAvailability: false
do disable acording warnings when source/changefile is removed.
If one uses NativeBoost in such a deployment scenario, for instance the
NBWin32Shell shellBrowse: 'http://www.google.de'
functionality the internal code (due to missing source) now looks like this
shellExecute: t1 file: t2 parameters: t3 directory: t4 show: t5 <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'> ^ self nbCall: #(#HINSTANCE #ShellExecuteA #(0 #, #LPCTSTR #lpOperation #, #LPCTSTR #lpFile #, #LPCTSTR #lpParameters #, #LPCTSTR #lpDirectory #, #INT #nShowCmd ) ) module: 'Shell32.dll'
Hence the t1 ... t5 parameters.
NativeBoost is in this situation not able to match the FFI parameters and throws an error "Could not find accessor for
variable
..."
Try yourself without a changes and source file. This makes NativeBoost not very deployment friendly and unusable in such a "minimal deployment" scenario ...
Any comments?
yes it needs sources (indirectly) to bind method's argument names during code generation. To avoid that, i can imagine that one must modify a compiler to detect if compiled method primitive requires arg names, and store them in method properties. Like that later code generator can use them without need to access the source code.
Indeed. If a pragma were used then the whole thing could look a lot nicer, not be dependent on source, and include support for an error code. e.g.
shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd
<nbCall: #(#HINSTANCE #ShellExecuteA #(0 #, #LPCTSTR #lpOperation #, #LPCTSTR #lpFile #, #LPCTSTR #lpParameters #, #LPCTSTR #lpDirectory #, #INT #nShowCmd ) ) module: 'Shell32.dll' errorCode: ec>
^self nbCallFailedWith: ec
It's pretty trivial to add such pragma compilers to the compiler. There is an example for the FFI. It also frees one to use a nicer syntax, e.g.
shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd <nbCall: 'HINSTANCE ShellExecuteA(0, LPCTSTR lpOperation, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd)' module: 'Shell32.dll' errorCode: ec>
^self nbCallFailedWith: ec
i do not see how encoding function signature in pragma could help with binding argument names. Yes, you need to get some control at compile time to be able to encode method arg names somewhere in method properties for later use.. but not at cost of moving everything into pragma.
And 'nice' syntax is actually already there .. nothing prevents you from using strings for function signature, e.g.
self nbCall: 'int foo()' equivalent to self nbCall: #(int foo() )
(i don't know why Torsten gave code in such form, that could leave an impression that syntax is horrible ;)
And besides, how you suppose to "pragmatize" following:
storeDouble: aDouble at: address " This method stores a double floating point value at given memory address. an address can be an instance of NBExternalAddress, or simple ByteArray with at least 8 bytes long, which will hold a 64bit floating-point value"
<primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin' error: errorCode>
^ self nbCallout options: #( - optCoerceNilToNull + optAllowByteArraysPtr + optAllowExternalAddressPtr ); function: #( void (void * address, double aDouble) ) emit: [:gen | | asm | asm := gen asm.
"Here , we expecting that an address value is on top of the stack" asm pop: EDX; "load an address value into EDX register by popping it from a stack"
"now copy the floating point value (which is 8-bytes long) to the given address" mov: ESP ptr to: EAX; mov: EAX to: EDX ptr; " store first 32bit part of 64bit double value"
mov: ESP ptr + 4 to: EAX; mov: EAX to: EDX ptr + 4. " store second 32bit part of 64bit double value"
]
-- Best regards, Igor Stasenko.
On 9 January 2013 09:39, S Krish <krishnamachari.sudhakar@gmail.com> wrote:
The issue is not limited to only pragma as the route, it can the CompiledMethod extension or any. Eventually stripping source/ disconnecting the sources / changes file is an expectation....
Otherwise can there be an easy way of truncated changes / sources file of the NativeBoost part alone read only that can allow the functionality as is.
Bar this part of the code I am sure all the rest can be a pragma, so may need to figure that out..how this may fit in compile / parse .
Potentially you could move things to pragmas, but at cost of increased complexity and losing flexibility. Putting things into pragmas will heavily dictate how you should organize your code, and will lead to canonization of interface, which will be very hard to change later (if you need that). Original FFI implementation is a good example of that, where the syntax of <cdecl: ..> are not compatible with common pragma syntax. And the problem is , that once choice was done (no matter how good or bad it is), now it is really hard to change it. But then imagine, how you could turn this to pragmas: ^ self nbCall: self signature module: self module options: self options or this: ^ self nbCallout function: #( int foo()) address: [ self getMyFunctionAddress ] The only thing you need right now is to capture arg names, so indeed, we need a pragma to indicate an intent, something like: <captureArgNames> ^ self nbCall: self signature module: self module options: self options But as i said before, i don't want to hack compiler right now, i postpone changes for Opal. Then you will be able to write something like that: shellExecute: lpOperation file: y parameters: z directory: w show: v <nativeCall> ^ self nbCall: .... module: 'Shell32.dll' the rest will be handled under the hood (including things like working without source code). -- Best regards, Igor Stasenko.
On 9 January 2013 09:10, Igor Stasenko <siguctua@gmail.com> wrote:
On 8 January 2013 19:38, Eliot Miranda <eliot.miranda@gmail.com> wrote:
On Fri, Jan 4, 2013 at 12:37 AM, Igor Stasenko <siguctua@gmail.com> wrote:
On 4 January 2013 08:54, Torsten Bergmann <astares@gmx.de> wrote:
When one deploys an image one usually requires ONLY the image - not the source or changes file. An "image locker" code would look like this:
SmalltalkImage checkSourcesFileAvailability: false. SmalltalkImage checkChangesFileAvailability: false
do disable acording warnings when source/changefile is removed.
If one uses NativeBoost in such a deployment scenario, for instance the
NBWin32Shell shellBrowse: 'http://www.google.de'
functionality the internal code (due to missing source) now looks like this
shellExecute: t1 file: t2 parameters: t3 directory: t4 show: t5 <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'> ^ self nbCall: #(#HINSTANCE #ShellExecuteA #(0 #, #LPCTSTR #lpOperation #, #LPCTSTR #lpFile #, #LPCTSTR #lpParameters #, #LPCTSTR #lpDirectory #, #INT #nShowCmd ) ) module: 'Shell32.dll'
Hence the t1 ... t5 parameters.
NativeBoost is in this situation not able to match the FFI parameters and throws an error "Could not find accessor for variable ..."
Try yourself without a changes and source file. This makes NativeBoost not very deployment friendly and unusable in such a "minimal deployment" scenario ...
Any comments?
yes it needs sources (indirectly) to bind method's argument names during code generation. To avoid that, i can imagine that one must modify a compiler to detect if compiled method primitive requires arg names, and store them in method properties. Like that later code generator can use them without need to access the source code.
Indeed. If a pragma were used then the whole thing could look a lot nicer, not be dependent on source, and include support for an error code. e.g.
shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd
<nbCall: #(#HINSTANCE #ShellExecuteA #(0 #, #LPCTSTR #lpOperation #, #LPCTSTR #lpFile #, #LPCTSTR #lpParameters #, #LPCTSTR #lpDirectory #, #INT #nShowCmd ) ) module: 'Shell32.dll' errorCode: ec>
^self nbCallFailedWith: ec
It's pretty trivial to add such pragma compilers to the compiler. There is an example for the FFI. It also frees one to use a nicer syntax, e.g.
shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd <nbCall: 'HINSTANCE ShellExecuteA(0, LPCTSTR lpOperation, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd)' module: 'Shell32.dll' errorCode: ec>
^self nbCallFailedWith: ec
i do not see how encoding function signature in pragma could help with binding argument names. Yes, you need to get some control at compile time to be able to encode method arg names somewhere in method properties for later use.. but not at cost of moving everything into pragma.
And 'nice' syntax is actually already there .. nothing prevents you from using strings for function signature, e.g.
self nbCall: 'int foo()' equivalent to self nbCall: #(int foo() )
(i don't know why Torsten gave code in such form, that could leave an impression that syntax is horrible ;)
ah, yes.. it is because he shows decompiled method source. The original source code looks much better: shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd <primitive: #primitiveNativeCall module: #NativeBoostPlugin> ^ self nbCall: #( HINSTANCE ShellExecuteA( 0, LPCTSTR lpOperation, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd)) module: 'Shell32.dll'
And besides, how you suppose to "pragmatize" following:
storeDouble: aDouble at: address " This method stores a double floating point value at given memory address. an address can be an instance of NBExternalAddress, or simple ByteArray with at least 8 bytes long, which will hold a 64bit floating-point value"
<primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin' error: errorCode>
^ self nbCallout options: #( - optCoerceNilToNull + optAllowByteArraysPtr + optAllowExternalAddressPtr ); function: #( void (void * address, double aDouble) ) emit: [:gen | | asm | asm := gen asm.
"Here , we expecting that an address value is on top of the stack" asm pop: EDX; "load an address value into EDX register by popping it from a stack"
"now copy the floating point value (which is 8-bytes long) to the given address" mov: ESP ptr to: EAX; mov: EAX to: EDX ptr; " store first 32bit part of 64bit double value"
mov: ESP ptr + 4 to: EAX; mov: EAX to: EDX ptr + 4. " store second 32bit part of 64bit double value"
]
-- Best regards, Igor Stasenko.
-- Best regards, Igor Stasenko.
Just my 2 cents on this: I don't think pragma is a good idea. It breaks the nice everything is a message ... plus the reason given by Igor. I think pragma is just a way to work around some other *limitations* we have. We should better work on those. If not for the problem at hand maybe we can use the compiled method directly, instead of keeping names around Cheers, Ciprian On Jan 9, 2013 10:25 AM, "Igor Stasenko" <siguctua@gmail.com> wrote:
On 9 January 2013 09:10, Igor Stasenko <siguctua@gmail.com> wrote:
On 8 January 2013 19:38, Eliot Miranda <eliot.miranda@gmail.com> wrote:
On Fri, Jan 4, 2013 at 12:37 AM, Igor Stasenko <siguctua@gmail.com>
wrote:
On 4 January 2013 08:54, Torsten Bergmann <astares@gmx.de> wrote:
When one deploys an image one usually requires ONLY the image - not the source or changes file. An "image locker" code would look like this:
SmalltalkImage checkSourcesFileAvailability: false. SmalltalkImage checkChangesFileAvailability: false
do disable acording warnings when source/changefile is removed.
If one uses NativeBoost in such a deployment scenario, for instance the
NBWin32Shell shellBrowse: 'http://www.google.de'
functionality the internal code (due to missing source) now looks like this
shellExecute: t1 file: t2 parameters: t3 directory: t4 show: t5 <primitive: 'primitiveNativeCall' module:
'NativeBoostPlugin'>
^ self nbCall: #(#HINSTANCE #ShellExecuteA #(0 #, #LPCTSTR #lpOperation #, #LPCTSTR #lpFile #, #LPCTSTR #lpParameters #,
#LPCTSTR
#lpDirectory #, #INT #nShowCmd ) ) module: 'Shell32.dll'
Hence the t1 ... t5 parameters.
NativeBoost is in this situation not able to match the FFI parameters and throws an error "Could not find accessor for variable ..."
Try yourself without a changes and source file. This makes NativeBoost not very deployment friendly and unusable in such a "minimal deployment" scenario ...
Any comments?
yes it needs sources (indirectly) to bind method's argument names during code generation. To avoid that, i can imagine that one must modify a compiler to detect if compiled method primitive requires arg names, and store them in method properties. Like that later code generator can use them without need to access the source code.
Indeed. If a pragma were used then the whole thing could look a lot nicer, not be dependent on source, and include support for an error code. e.g.
shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd
<nbCall: #(#HINSTANCE #ShellExecuteA #(0 #, #LPCTSTR #lpOperation #, #LPCTSTR #lpFile #, #LPCTSTR #lpParameters #, #LPCTSTR #lpDirectory #, #INT #nShowCmd ) ) module: 'Shell32.dll' errorCode: ec>
^self nbCallFailedWith: ec
It's pretty trivial to add such pragma compilers to the compiler. There is an example for the FFI. It also frees one to use a nicer syntax, e.g.
shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd <nbCall: 'HINSTANCE ShellExecuteA(0, LPCTSTR lpOperation, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd)' module: 'Shell32.dll' errorCode: ec>
^self nbCallFailedWith: ec
i do not see how encoding function signature in pragma could help with binding argument names. Yes, you need to get some control at compile time to be able to encode method arg names somewhere in method properties for later use.. but not at cost of moving everything into pragma.
And 'nice' syntax is actually already there .. nothing prevents you from using strings for function signature, e.g.
self nbCall: 'int foo()' equivalent to self nbCall: #(int foo() )
(i don't know why Torsten gave code in such form, that could leave an impression that syntax is horrible ;)
ah, yes.. it is because he shows decompiled method source. The original source code looks much better:
shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd <primitive: #primitiveNativeCall module: #NativeBoostPlugin>
^ self nbCall: #( HINSTANCE ShellExecuteA( 0, LPCTSTR lpOperation, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd)) module: 'Shell32.dll'
And besides, how you suppose to "pragmatize" following:
storeDouble: aDouble at: address " This method stores a double floating point value at given
memory address.
an address can be an instance of NBExternalAddress, or simple ByteArray with at least 8 bytes long, which will hold a
64bit
floating-point value"
<primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin' error: errorCode>
^ self nbCallout options: #( - optCoerceNilToNull + optAllowByteArraysPtr + optAllowExternalAddressPtr ); function: #( void (void * address, double aDouble) ) emit: [:gen | | asm | asm := gen asm.
"Here , we expecting that an address value is on top of the stack" asm pop: EDX; "load an address value into EDX register by popping it from a stack"
"now copy the floating point value (which is 8-bytes long) to the given address" mov: ESP ptr to: EAX; mov: EAX to: EDX ptr; " store first 32bit part of 64bit double value"
mov: ESP ptr + 4 to: EAX; mov: EAX to: EDX ptr + 4. " store second 32bit part of 64bit double value"
]
-- Best regards, Igor Stasenko.
-- Best regards, Igor Stasenko.
On Wed, Jan 9, 2013 at 12:10 AM, Igor Stasenko <siguctua@gmail.com> wrote:
On 8 January 2013 19:38, Eliot Miranda <eliot.miranda@gmail.com> wrote:
On Fri, Jan 4, 2013 at 12:37 AM, Igor Stasenko <siguctua@gmail.com>
wrote:
On 4 January 2013 08:54, Torsten Bergmann <astares@gmx.de> wrote:
When one deploys an image one usually requires ONLY the image - not the source or changes file. An "image locker" code would look like this:
SmalltalkImage checkSourcesFileAvailability: false. SmalltalkImage checkChangesFileAvailability: false
do disable acording warnings when source/changefile is removed.
If one uses NativeBoost in such a deployment scenario, for instance the
NBWin32Shell shellBrowse: 'http://www.google.de'
functionality the internal code (due to missing source) now looks like this
shellExecute: t1 file: t2 parameters: t3 directory: t4 show: t5 <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'> ^ self nbCall: #(#HINSTANCE #ShellExecuteA #(0 #, #LPCTSTR #lpOperation #, #LPCTSTR #lpFile #, #LPCTSTR #lpParameters #, #LPCTSTR #lpDirectory #, #INT #nShowCmd ) ) module: 'Shell32.dll'
Hence the t1 ... t5 parameters.
NativeBoost is in this situation not able to match the FFI parameters and throws an error "Could not find accessor for
variable
..."
Try yourself without a changes and source file. This makes NativeBoost not very deployment friendly and unusable in such a "minimal deployment" scenario ...
Any comments?
yes it needs sources (indirectly) to bind method's argument names during code generation. To avoid that, i can imagine that one must modify a compiler to detect if compiled method primitive requires arg names, and store them in method properties. Like that later code generator can use them without need to access the source code.
Indeed. If a pragma were used then the whole thing could look a lot nicer, not be dependent on source, and include support for an error code. e.g.
shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd
<nbCall: #(#HINSTANCE #ShellExecuteA #(0 #, #LPCTSTR #lpOperation #, #LPCTSTR #lpFile #, #LPCTSTR #lpParameters #, #LPCTSTR #lpDirectory #, #INT #nShowCmd ) ) module: 'Shell32.dll' errorCode: ec>
^self nbCallFailedWith: ec
It's pretty trivial to add such pragma compilers to the compiler. There is an example for the FFI. It also frees one to use a nicer syntax, e.g.
shellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd <nbCall: 'HINSTANCE ShellExecuteA(0, LPCTSTR lpOperation, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd)' module: 'Shell32.dll' errorCode: ec>
^self nbCallFailedWith: ec
i do not see how encoding function signature in pragma could help with binding argument names.
Because your pragma processing code can add additional state, i.e. the map form input argument order to call argument order.
Yes, you need to get some control at compile time to be able to encode method arg names somewhere in method properties for later use.. but not at cost of moving everything into pragma.
What's this "cost"? The call info has to live somewhere.
And 'nice' syntax is actually already there .. nothing prevents you from using strings for function signature, e.g.
self nbCall: 'int foo()' equivalent to self nbCall: #(int foo() )
(i don't know why Torsten gave code in such form, that could leave an impression that syntax is horrible ;)
And besides, how you suppose to "pragmatize" following:
storeDouble: aDouble at: address " This method stores a double floating point value at given memory address. an address can be an instance of NBExternalAddress, or simple ByteArray with at least 8 bytes long, which will hold a 64bit floating-point value"
<primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin' error: errorCode>
^ self nbCallout options: #( - optCoerceNilToNull + optAllowByteArraysPtr + optAllowExternalAddressPtr ); function: #( void (void * address, double aDouble) ) emit: [:gen | | asm | asm := gen asm.
"Here , we expecting that an address value is on top of the stack" asm pop: EDX; "load an address value into EDX register by popping it from a stack"
"now copy the floating point value (which is 8-bytes long) to the given address" mov: ESP ptr to: EAX; mov: EAX to: EDX ptr; " store first 32bit part of 64bit double value"
mov: ESP ptr + 4 to: EAX; mov: EAX to: EDX ptr + 4. " store second 32bit part of 64bit double value"
]
Just wrap the assembler up in a string. But this low level shouldn't be necessary. If you went the compile-time route then you'd analyse the call according to the platform's ABI and figure out the assembler automatically. This is the architecture I was hoping yopu'd implement. If the compilation step is left until the first call then the signatire becomes platform-independent. For a clunkier approach see my callback implementation where you build up a library of transformers indexed by signature. The library contains different versions for each platform. If you applied this simple idea to the above you'd have some pragma somewhere which said "on x86 with signature #( void (void * address, double aDouble) ) output this marshalling code: #(pop: EDX "load an...") etc. And then the call site would just supply the signature. You'd get to share the marshalling code between functions having the same signatures and you'd have a level of platform-independence. -- best, Eliot
On 9 January 2013 20:58, Eliot Miranda <eliot.miranda@gmail.com> wrote:
Just wrap the assembler up in a string. But this low level shouldn't be necessary. If you went the compile-time route then you'd analyse the call according to the platform's ABI and figure out the assembler automatically. This is the architecture I was hoping yopu'd implement. If the compilation step is left until the first call then the signatire becomes platform-independent.
Assembler is absolutely necessary, because it is a developer who can decide to use it, not me. I just giving an opportunity. This is a primary objective of NativeBoost project. NativeBoost includes FFI implementation as integral part of it. But that doesn't means that i should narrow it down only for this purpose. If i want to implement a primitive using brute-force assembler, it is logical to put it in the source of corresponding method, not somewhere else. Also, since this is executable code, and does not requires separate parser/compiler, you are free to extensively practice a code-reuse, as in any smalltalk code you write (as well as make it platform independent, btw). And so, users are free to mix assembler with own code, and there is little or no constraints and canonical forms, to which everyone should adhere. It is just plain smalltalk.
For a clunkier approach see my callback implementation where you build up a library of transformers indexed by signature. The library contains different versions for each platform. If you applied this simple idea to the above you'd have some pragma somewhere which said "on x86 with signature #( void (void * address, double aDouble) ) output this marshalling code: #(pop: EDX "load an...") etc. And then the call site would just supply the signature. You'd get to share the marshalling code between functions having the same signatures and you'd have a level of platform-independence.
Wait.. Let us separate concerns. Platform-independence is orthogonal to use (or not) use of pragmas in code. I do not see why i need to focus on platform independence right here right now. Does our beloved VM runs anywhere else than on 32-bit x86 CPUs? Once there will be other options, trust me, i will be first who will concentrate on making things properly separated. But when it is not , why i do even need to think/do something about it? x64 using completely different call convention(s), not saying that object format will require some changes, and as result VM interface(s) as well.. So, why i should constrain myself and introduce some "platform independece" when i have no real thing at hand, so i don't even have a clue whether my "platform-independent" solution will will fit well with future VM/platform or not? Aside of it, right now, you can debug the code generation and see everything from starting point up to the end, using debugger just by stepping into method which uses NB call. You can see what happens, and see how your function signature turned into real bytes which then fed to CPU. So, users can LEARN and improve it. Now if you split things , or even worse, move all logic at compile time, you will make things opaque enough for debugging it and understanding how it works. Yes, this is good, if intent is to prevent a "regular" developer to even think that he can directly use assembler. But my intent is actually straightly opposite. My intent is to expose users to it. I am perfectly fine with those who prefer to keep playing with plastic toys without risk of hurting themselves. But plastic world(s) has own limits, and so, if you want to reach outside, then there is no magic, you have to stop playing in sandbox with plastic toys, but instead grow up and take some risk and responsibility. And so, my philosophy (and NB as well) is to help people breaking out and combine best of two worlds. Because, at the end, which code is more secure/safe: - a well debugged and tested C code - or a well debugged and tested smalltalk code ? -- Best regards, Igor Stasenko.
@Igor +1 On Jan 10, 2013 12:14 AM, "Igor Stasenko" <siguctua@gmail.com> wrote:
On 9 January 2013 20:58, Eliot Miranda <eliot.miranda@gmail.com> wrote:
Just wrap the assembler up in a string. But this low level shouldn't be necessary. If you went the compile-time route then you'd analyse the
call
according to the platform's ABI and figure out the assembler automatically. This is the architecture I was hoping yopu'd implement. If the compilation step is left until the first call then the signatire becomes platform-independent.
Assembler is absolutely necessary, because it is a developer who can decide to use it, not me. I just giving an opportunity. This is a primary objective of NativeBoost project. NativeBoost includes FFI implementation as integral part of it. But that doesn't means that i should narrow it down only for this purpose.
If i want to implement a primitive using brute-force assembler, it is logical to put it in the source of corresponding method, not somewhere else. Also, since this is executable code, and does not requires separate parser/compiler, you are free to extensively practice a code-reuse, as in any smalltalk code you write (as well as make it platform independent, btw). And so, users are free to mix assembler with own code, and there is little or no constraints and canonical forms, to which everyone should adhere. It is just plain smalltalk.
For a clunkier approach see my callback implementation where you build up a library of transformers indexed by signature. The library contains different versions for each platform. If you applied this simple idea to the above you'd have some pragma somewhere which said "on x86 with signature #( void (void * address, double aDouble) ) output this marshalling code: #(pop: EDX "load an...") etc. And then the call site would just supply the signature. You'd get to share the marshalling code between functions having the same signatures and you'd have a level of platform-independence.
Wait.. Let us separate concerns. Platform-independence is orthogonal to use (or not) use of pragmas in code.
I do not see why i need to focus on platform independence right here right now. Does our beloved VM runs anywhere else than on 32-bit x86 CPUs? Once there will be other options, trust me, i will be first who will concentrate on making things properly separated. But when it is not , why i do even need to think/do something about it? x64 using completely different call convention(s), not saying that object format will require some changes, and as result VM interface(s) as well.. So, why i should constrain myself and introduce some "platform independece" when i have no real thing at hand, so i don't even have a clue whether my "platform-independent" solution will will fit well with future VM/platform or not?
Aside of it, right now, you can debug the code generation and see everything from starting point up to the end, using debugger just by stepping into method which uses NB call. You can see what happens, and see how your function signature turned into real bytes which then fed to CPU. So, users can LEARN and improve it. Now if you split things , or even worse, move all logic at compile time, you will make things opaque enough for debugging it and understanding how it works. Yes, this is good, if intent is to prevent a "regular" developer to even think that he can directly use assembler. But my intent is actually straightly opposite.
My intent is to expose users to it. I am perfectly fine with those who prefer to keep playing with plastic toys without risk of hurting themselves. But plastic world(s) has own limits, and so, if you want to reach outside, then there is no magic, you have to stop playing in sandbox with plastic toys, but instead grow up and take some risk and responsibility. And so, my philosophy (and NB as well) is to help people breaking out and combine best of two worlds.
Because, at the end, which code is more secure/safe: - a well debugged and tested C code - or a well debugged and tested smalltalk code ?
-- Best regards, Igor Stasenko.
participants (4)
-
Ciprian Teodorov -
Eliot Miranda -
Igor Stasenko -
S Krish