Function test naked stdcall alias "test"( Byval s As String ) As ZString Ptr
Asm
mov edx,DWORD PTR [esp+4]
mov eax,DWORD PTR [edx]
ret 4
End Asm
End Function
Reading the assembly listing, the decoration of the function test looks incorrect :
The expected symbol decoration is _test@4 as the function takes only one parameter. The listing shows _test@12
Source code compiled with FreeBASIC Version 1.07.1
fxm wrote:The value of 12 corresponds to the size of the string descriptor.
IMHO the compiler should issue a warning like "are you sure you want to pass a string descriptor, i.e. by value?". ALL Windows APIs expect strings in form of a pointer, not a 12-byte FreeBasic string descriptor. Remember the "B" in "Basic" ;-)
In fact, passing a string by value is a recent ability, but also a special case (compared to the passing of other variables by value), looking at the way it was implemented:
- When a string must be passed by value, in fact a copy of the original string is passed, but always by its descriptor (in the same way as for a string passed by reference), so that a pointer is always passed in both cases (Byref or Byval).
@admin: maybe this case of procedure name decoration (string passed by value) must be revised.
I would rather be in favor of changing the function name decoration (suffix @N) because in the 2 cases (Byref or Byval), a pointer (to the string descriptor) is always passed by value (for a 32-bit compilation: N should always be equal to 4).
Proof demonstrating that a string is always passed by the value of a pointer to its descriptor:
Function test1 Naked Stdcall Alias "test1" ( Byref s As String ) As Zstring Ptr
Asm
mov edx,DWORD PTR [esp+4]
mov eax,DWORD PTR [edx]
ret 4
End Asm
End Function
Function test2 Naked Stdcall Alias "test2" ( Byval s As String ) As Zstring Ptr
Asm
mov edx,DWORD PTR [esp+4]
mov eax,DWORD PTR [edx]
ret 4
End Asm
End Function
Dim As String s = "A string is always passed by a pointer value (to its descriptor)"
Dim As Function (Byval p As Any Ptr) As Zstring Ptr p1 = Cptr(Any Ptr, @test1)
Dim As Function (Byval p As Any Ptr) As Zstring Ptr p2 = Cptr(Any Ptr, @test2)
Print "Print:"
Print " s :", s
Print " *test1(s):", *test1(s)
Print " *test2(s):", *test2(s)
Print " *p1(@s) :", *p1(@s) '' in fact a pointer is passed by value
Print " *p2(@s) :", *p2(@s) '' in fact a pointer is passed by value
Sleep
Print:
s : A string is always passed by a pointer value (to its descriptor)
*test1(s): A string is always passed by a pointer value (to its descriptor)
*test2(s): A string is always passed by a pointer value (to its descriptor)
*p1(@s) : A string is always passed by a pointer value (to its descriptor)
*p2(@s) : A string is always passed by a pointer value (to its descriptor)
fxm wrote:I would rather be in favor of changing the function name decoration (suffix @N) because in the 2 cases (Byref or Byval), a pointer (to the string descriptor) is always passed by value (for a 32-bit compilation: N should always be equal to 4).
I don't agree, because a FB_String is a Type (aka: UDT or struct) with it's size referenced in the decoration.
(needed for indexing a 'typed' Ptr as opposed to Any Ptr, which can't be indexed)
The preferred solution whould be: ByVal As ZString Ptr (conversion implicitly done by FBC).
Which will give @4 or @8, depending on the compiler's bitness.
But presently (for 32-bit compilation):
- Passing a string by reference => @4 (see above)
- Passing a string by value => @12 (see above)
That is illogical because when a string is passed by value, in fact a copy of the original string is passed by reference!
In both cases, a string is always passed by reference, either the original string, or a copy of this string.
(In both cases under the hood, a pointer to the descriptor is passed by value, either to the descriptor of the original string, or to the descriptor of a copy of this string)
sub dothis(byval s as string)
s+=" zzz"
end sub
sub dothis2(byref s as string)
static as string g:g=s
g+=" zzz"
end sub
dim as long lim=1000000
dim as double t
dim as string x="HELLO"
'warmup
for n as long=1 to lim
dothis(x)
dothis2(x)
next n
t=timer
for n as long=1 to lim
dothis(x)
next
print timer-t,x
t=timer
for n as long=1 to lim
dothis2(x)
next
print timer-t,x
sleep
Function test naked stdcall alias "test"( Byval s As String ) As ZString Ptr
Asm
mov edx,DWORD PTR [esp+4]
mov eax,DWORD PTR [edx]
ret 4
End Asm
End Function
Function test2 naked stdcall alias "test2"( Byref s As String ) As ZString Ptr
Asm
mov edx,DWORD PTR [esp+4]
mov eax,DWORD PTR [edx]
ret 4
End Asm
End Function
declare function decorated cdecl alias "test@12"( Byval As String ) As ZString Ptr
declare function decorated2 cdecl alias "test2@4"( Byref As String ) As ZString Ptr
print *decorated2(!"Hello\n")
print *decorated("Press any key to end . . .")
print
sleep
sub dothis(byval s as string)
s+=" zzz"
end sub
sub dothis2(byref s as string)
s+=" zzz"
end sub
dim as long lim=1000000
dim as double t
dim as string x="HELLO"
'warmup
for n as long=1 to lim
dothis(x)
dim as string x2 = x
dothis2(x2)
next n
t=timer
for n as long=1 to lim
dothis(x)
next
print timer-t,x
t=timer
for n as long=1 to lim
dim as string x2 = x
dothis2(x2)
next
print timer-t,x
sleep