Closed Alekcvp closed 1 year ago
the https://github.com/remobjects/pascalscript/commit/f68967d58fe4efe0fd293fcc4a2c9bdd27af5f80 commit should fix some x64 issues.
if it doesn't work in your case, can you create and attach a simple testcase that reproduces this issue, pls?
In my particular case, I fixed it by adding the following changes to InvokeCall.inc:
case res.atype.basetype of
{ add result types here }
btString: tbtstring(res.dta^) := tbtstring(Invoke(Address, Args, SysCalConv, TypeInfo(String),
{$IFDEF WIN64} _Self = nil {$ELSE} False {$ENDIF},IsConstr).AsString);
{$IFNDEF PS_NOWIDESTRING}
btUnicodeString: tbtunicodestring(res.dta^) := Invoke(Address, Args, SysCalConv, TypeInfo(String),
{$IFDEF WIN64} _Self = nil {$ELSE} False {$ENDIF}, IsConstr).AsString;
btWideString: tbtWideString(res.dta^) := Invoke(Address, Args, SysCalConv, TypeInfo(String),
{$IFDEF WIN64} _Self = nil {$ELSE} False {$ENDIF}, IsConstr).AsString;
But I can not call it a suitable solution in general due to insufficient experience.
Simple testcase to reproduce the error:
program PSTestCase;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, uPSCompiler, uPSRuntime, uPSUtils;
procedure Print(const Text: string);
begin
WriteLn(Text);
end;
function Test(Value: Int64): string;
begin
Result := IntToStr(Value); // --> x64 AV here and 'Value' has a wrong value
end;
function PSOnUses(Sender: TPSPascalCompiler; const Name: tbtString): Boolean;
begin
Sender.AddDelphiFunction('function Test(Value: Int64): string;');
Sender.AddDelphiFunction('procedure Print(const Text: string);');
Result := True;
end;
var
C: TPSPascalCompiler;
E: TPSExec;
Data: AnsiString;
begin
C := TPSPascalCompiler.Create;
E := TPSExec.Create;
try
C.OnUses := PSOnUses;
C.Compile('begin Print(Test(832794832)); end.');
C.GetOutput(Data);
E.RegisterDelphiFunction(@Print, 'Print', cdRegister);
E.RegisterDelphiFunction(@Test, 'Test', cdRegister);
E.LoadData(Data);
E.RunScript;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
E.Free;
C.Free;
ReadLn;
end.
use this InvokeCall.inc
:
function TPSExec.InnerfuseCall(_Self, Address: Pointer; CallingConv: TPSCallingConvention; Params: TPSList; res: PPSVariantIFC): Boolean;
var SysCalConv : TCallConv;
Args: TArray<TValue>;
Arg : TValue;
i : Integer;
fvar: PPSVariantIFC;
IsConstr : Boolean;
IsStatic : Boolean;
ctx: TRTTIContext;
RttiType : TRttiType;
ResValue : TValue;
begin
Result := False;
IsStatic := _Self = nil;
case CallingConv of
cdRegister : SysCalConv := ccReg;
cdPascal : SysCalConv := ccPascal;
cdCdecl : SysCalConv := ccCdecl;
cdStdCall : SysCalConv := ccStdCall;
cdSafeCall : SysCalConv := ccSafeCall;
else
SysCalConv := ccReg;//to prevent warning "W1036 Variable might not have been initialized"
end;
if not IsStatic then begin
{$IFDEF CPUX86}
if CallingConv <> cdPascal then
{$ENDIF CPUX86}
Args := Args + [TValue.From<Pointer>( _Self )];
end;
for I := 0 to Params.Count - 1 do
begin
if Params[i] = nil
then Exit;
fvar := Params[i];
if fvar.varparam then
begin { var param }
case fvar.aType.BaseType of
btArray, btVariant, btSet, btStaticArray, btRecord, btInterface, btClass, {$IFNDEF PS_NOWIDESTRING} btWideString, btWideChar, {$ENDIF}
btU8, btS8, btU16, btS16, btU32, btS32, btSingle, btDouble, btExtended, btString, btPChar, btChar, btCurrency,
btUnicodeString
{$IFNDEF PS_NOINT64}, bts64{$ENDIF}:
Arg := TValue.From<Pointer>( Pointer(fvar.dta) );
else
begin
Exit;
end;
end;
end
else
begin { not a var param }
case fvar.aType.BaseType of
{ add normal params here }
{$IFNDEF PS_NOWIDESTRING}
btWidestring,
btUnicodestring,
{$ENDIF}
btString: Arg := TValue.From<String>(pstring(fvar.dta)^);
btU8, btS8: Arg := TValue.From<Byte>(pbyte(fvar.dta)^);
btU16, BtS16: Arg := TValue.From<Word>(pword(fvar.dta)^);
btU32, btS32: Arg := TValue.From<Cardinal>(pCardinal(fvar.dta)^);
{$IFNDEF PS_NOINT64}bts64:{$ENDIF} Arg := TValue.From<Int64>(pint64(fvar.dta)^);
btSingle: Arg := TValue.From<Single>(PSingle(fvar.dta)^);
btDouble: Arg := TValue.From<Double>(PDouble(fvar.dta)^);
btExtended: Arg := TValue.From<Extended>(PExtended(fvar.dta)^);
btPChar: Arg := TValue.From<PChar>(ppchar(fvar.dta)^);
btChar: Arg := TValue.From<Char>(pchar(fvar.dta)^);
btClass: Arg := TValue.From<TObject>(TObject(fvar.dta^));
btRecord: Arg := TValue.From<Pointer>(fvar.dta);
btStaticArray: Arg := TValue.From<Pointer>(fvar.dta);
btVariant:
Arg := TValue.From(Variant(fvar.dta^));
btArray:
begin
if Copy(fvar.aType.ExportName, 1, 10) = '!OPENARRAY' then
begin //openarray
//in case of openarray we should provide TWO params: first is pointer to array,
Args := Args + [TValue.From<Pointer>(Pointer(fvar.Dta^))];
//2nd - integer with arraylength - 1 (high)
Arg := TValue.From<Integer>(PSDynArrayGetLength(Pointer(fvar.Dta^), fvar.aType)-1);// = High of OpenArray
end
else //dynarray = just push pointer
Arg := TValue.From<Pointer>(fvar.dta);
end;
btSet:
begin
case TPSTypeRec_Set(fvar.aType).aByteSize of
1: Arg := TValue.From<Byte>(pbyte(fvar.dta)^);
2: Arg := TValue.From<Word>(pWord(fvar.dta)^);
3,
4: Arg := TValue.From<Cardinal>(pCardinal(fvar.dta)^);
else
Arg := TValue.From<Pointer>(fvar.dta);
end;
end;
else
// writeln(stderr, 'Parameter type not implemented!');
Exit;
end; { case }
end;
Args := Args + [Arg];
end;
if not IsStatic then begin
{$IFDEF CPUX86}
if CallingConv <> cdPascal then
Args := Args + [TValue.From<Pointer>( _Self )];
{$ENDIF CPUX86}
end;
IsConstr := (Integer(CallingConv) and 64) <> 0;
if not assigned(res) then
begin
Invoke(Address,Args,SysCalConv,nil,IsStatic, IsConstr); { ignore return }
end
else begin
case res.atype.basetype of
{ add result types here }
btString: tbtstring(res.dta^) := tbtstring(Invoke(Address,Args,SysCalConv,TypeInfo(String),IsStatic, IsConstr).AsString)
;
{$IFNDEF PS_NOWIDESTRING}
btUnicodeString: tbtunicodestring(res.dta^) := Invoke(Address,Args,SysCalConv,TypeInfo(String),IsStatic, IsConstr).AsString;
btWideString: tbtWideString(res.dta^) := Invoke(Address,Args,SysCalConv,TypeInfo(String),IsStatic, IsConstr).AsString;
{$ENDIF}
btU8, btS8: pbyte(res.dta)^ := Byte(Invoke(Address,Args,SysCalConv,TypeInfo(Byte),IsStatic, IsConstr).AsInteger);
btU16, btS16: pword(res.dta)^ := word(Invoke(Address,Args,SysCalConv,TypeInfo(Word),IsStatic, IsConstr).AsInteger);
btU32, btS32: pCardinal(res.dta)^ := Cardinal(Invoke(Address,Args,SysCalConv,TypeInfo(Cardinal),IsStatic, IsConstr).AsInteger);
{$IFNDEF PS_NOINT64}bts64:{$ENDIF} pInt64(res.dta)^ := Int64(Invoke(Address,Args,SysCalConv,TypeInfo(Int64),IsStatic, IsConstr).AsInt64);
btSingle: psingle(res.dta)^ := Double(Invoke(Address,Args,SysCalConv,TypeInfo(Single),IsStatic, IsConstr).AsExtended);
btDouble: pdouble(res.dta)^ := Double(Invoke(Address,Args,SysCalConv,TypeInfo(Double),IsStatic, IsConstr).AsExtended);
btExtended: pextended(res.dta)^ := Extended(Invoke(Address,Args,SysCalConv,TypeInfo(Extended),IsStatic, IsConstr).AsExtended);
{$IFDEF FPC}
btPChar: ppchar(res.dta)^ := pchar(Invoke(Address,Args,SysCalConv,TypeInfo(PChar),IsStatic, IsConstr).AsOrdinal);
btChar: pchar(res.dta)^ := Char(Invoke(Address,Args,SysCalConv,TypeInfo(Char),IsStatic, IsConstr).AsChar);
{$ELSE}
btPChar: ppchar(res.dta)^ := pchar(Invoke(Address,Args,SysCalConv,TypeInfo(PChar),IsStatic, IsConstr).AsType<PChar>());
btChar: pchar(res.dta)^ := Char(Invoke(Address,Args,SysCalConv,TypeInfo(Char),IsStatic, IsConstr).AsType<Char>());
{$ENDIF}
btSet:
begin
case TPSTypeRec_Set(res.aType).aByteSize of
1: byte(res.Dta^) := Byte(Invoke(Address,Args,SysCalConv,TypeInfo(Byte),IsStatic, IsConstr).AsInteger);
2: word(res.Dta^) := word(Invoke(Address,Args,SysCalConv,TypeInfo(Word),IsStatic, IsConstr).AsInteger);
3,
4: Longint(res.Dta^) := Cardinal(Invoke(Address,Args,SysCalConv,TypeInfo(Cardinal),IsStatic, IsConstr).AsInteger);
{$IFNDEF FPC}
else
begin
for RttiType in ctx.GetTypes do
if (RttiType.Name.ToUpper.EndsWith(String(res.aType.FExportName))) and (RttiType.TypeKind = tkSet)
and (RttiType.TypeSize = TPSTypeRec_Set(res.aType).aByteSize) then
begin
Invoke(Address,Args,SysCalConv,RttiType.Handle,IsStatic, IsConstr).ExtractRawData(res.dta);
Break;
end;
end;
{$ENDIF}
end;
end;
btClass:
begin
{$IFNDEF FPC}for RttiType in ctx.GetTypes do
if (RttiType.Name.ToUpper.EndsWith(String(res.aType.FExportName))) and (RttiType.TypeKind = tkClass) then{$ENDIF}
begin
TObject(res.dta^) := Invoke(Address,Args,SysCalConv,{$IFDEF FPC}TypeInfo(TObject){$ELSE}RttiType.Handle{$ENDIF},IsStatic, IsConstr).AsObject;
{$IFNDEF FPC}Break;{$ENDIF}
end;
end;
{$IFNDEF FPC}
btStaticArray:
begin
for RttiType in ctx.GetTypes do
if (RttiType.Name.ToUpper.EndsWith(String(res.aType.FExportName))) and (RttiType.TypeKind = tkArray) then
begin
CopyArrayContents(res.dta, Invoke(Address,Args,SysCalConv,RttiType.Handle,IsStatic, IsConstr).GetReferenceToRawData, TPSTypeRec_StaticArray(res.aType).Size, TPSTypeRec_StaticArray(res.aType).ArrayType);
Break;
end;
end;
btRecord:
begin
for RttiType in ctx.GetTypes do
if (RttiType.Name.ToUpper.EndsWith(String(res.aType.FExportName))) and (RttiType.TypeKind = tkRecord) then
begin
CopyArrayContents(res.dta, (Invoke(Address,Args,SysCalConv,RttiType.Handle,IsStatic, IsConstr).GetReferenceToRawData), 1, res.aType);
Break;
end;
end;
btArray: //need to check with open arrays
begin
for RttiType in ctx.GetTypes do
if (RttiType.Name.ToUpper.EndsWith(String(res.aType.FExportName))) and (RttiType.TypeKind = tkDynArray) then
begin
ResValue := Invoke(Address,Args,SysCalConv,RttiType.Handle,IsStatic, IsConstr);
if ResValue.GetArrayLength > 0 then
CopyArrayContents(res.dta, ResValue.GetReferenceToRawData, 1, res.aType)
else
res.dta := nil;
Break;
end;
end;
btVariant:
begin
PVariant(res.dta)^ := Invoke(Address, Args, SysCalConv, TypeInfo(Variant), IsStatic, IsConstr).AsVariant;
end;
{$ENDIF}
else
// writeln(stderr, 'Result type not implemented!');
Exit;
end; { case }
end; //assigned(res)
Result := True;
end;
I have imported delphi function:
s.AddDelphiFunction('function PackSize(Value: Int64): string;');
FPSExec.RegisterDelphiFunction(@PackSize, 'PackSize', cdRegister);
In x64 mode, when calling it, Delphi passes a reference to the Result in the RCX, and the Value in the RDX. Pascal Script calls it with Value in RCX and Result (? Pointer to nil) in RDX.
x86 mode working perfectly.