feat(native): TIsExpr, TAsExpr, TSupportsExpr + ClassSymName
Implement `X is TFoo` (class type test via _IsInstance), `X as TFoo` (checked downcast with _Raise_InvalidCast on failure), and `Supports(Obj, IFoo)` (interface query via _ImplementsInterface). Add ClassSymName helper that resolves a class/interface name to its assembly symbol with unit prefix (e.g. Exception → SysUtils_Exception), mirroring the QBE backend's ClassSymName. Fix existing as-cast and except-handler typeinfo references to use ClassSymName instead of bare NativeMangle(TypeName).
This commit is contained in:
parent
54b19e6fee
commit
43d9d702f0
|
|
@ -160,6 +160,10 @@ type
|
|||
AGenericInstances: TObjectList;
|
||||
AGenericRecordInstances: TObjectList);
|
||||
|
||||
{ Resolve a class/interface name to its assembly symbol form, adding the
|
||||
unit prefix when the type is defined in a non-system unit. Mirrors the
|
||||
QBE backend's ClassSymName logic. }
|
||||
function ClassSymName(const AClassName: string): string;
|
||||
{ True when AName is a slot in the current function frame. }
|
||||
function IsLocal(const AName: string): Boolean;
|
||||
{ The AT&T operand addressing AName: "-N(%rbp)" for a frame local,
|
||||
|
|
@ -796,6 +800,38 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function TX86_64Backend.ClassSymName(const AClassName: string): string;
|
||||
var
|
||||
Sym: TSymbol;
|
||||
Owner: string;
|
||||
I: Integer;
|
||||
Ch: string;
|
||||
begin
|
||||
Result := '';
|
||||
if FSymTable <> nil then
|
||||
begin
|
||||
Sym := FSymTable.Lookup(AClassName);
|
||||
if Sym <> nil then
|
||||
begin
|
||||
Owner := Sym.OwningUnit;
|
||||
if (Owner <> '') and
|
||||
not SameText(Owner, 'System') and
|
||||
not ((Length(Owner) >= 4) and SameText(Copy(Owner, 0, 4), 'rtl.')) and
|
||||
not ((Length(Owner) >= 7) and SameText(Copy(Owner, 0, 7), 'blaise_')) then
|
||||
begin
|
||||
for I := 0 to Length(Owner) - 1 do
|
||||
begin
|
||||
Ch := Copy(Owner, I, 1);
|
||||
if Ch = '.' then Result := Result + '_'
|
||||
else Result := Result + Ch;
|
||||
end;
|
||||
Result := Result + '_';
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
Result := Result + NativeMangle(AClassName);
|
||||
end;
|
||||
|
||||
function FindMethodInClassDef(AClassDef: TClassTypeDef;
|
||||
const AName: string): TMethodDecl;
|
||||
var
|
||||
|
|
@ -1729,7 +1765,7 @@ begin
|
|||
Self.EmitExprToEax(AE.Obj); { obj -> %rax }
|
||||
Self.Emit(#9'pushq %rax'); { keep obj on the stack across calls }
|
||||
Self.Emit(#9'movq %rax, %rdi');
|
||||
Self.Emit(Format(#9'leaq typeinfo_%s(%%rip), %%rsi', [NativeMangle(AE.TypeName)]));
|
||||
Self.Emit(Format(#9'leaq typeinfo_%s(%%rip), %%rsi', [Self.ClassSymName(AE.TypeName)]));
|
||||
Self.Emit(#9'callq _GetItab'); { itab -> %rax }
|
||||
Self.Emit(#9'pushq %rax'); { keep itab; stack now (itab, obj) }
|
||||
LFail := Self.NewLabel('as_fail');
|
||||
|
|
@ -3709,6 +3745,59 @@ begin
|
|||
Exit;
|
||||
end;
|
||||
|
||||
{ X is TFoo — class type test via _IsInstance / _ImplementsInterface.
|
||||
Result: 0/1 in %eax. }
|
||||
if AExpr is TIsExpr then
|
||||
begin
|
||||
Self.EmitExprToEax(TIsExpr(AExpr).Obj);
|
||||
Self.Emit(#9'movq %rax, %rdi');
|
||||
if (TIsExpr(AExpr).ResolvedTargetType <> nil) and
|
||||
(TIsExpr(AExpr).ResolvedTargetType.Kind = tyInterface) then
|
||||
begin
|
||||
Self.Emit(Format(#9'leaq typeinfo_%s(%%rip), %%rsi',
|
||||
[Self.ClassSymName(TIsExpr(AExpr).TypeName)]));
|
||||
Self.Emit(#9'callq _ImplementsInterface');
|
||||
end
|
||||
else
|
||||
begin
|
||||
Self.Emit(Format(#9'leaq typeinfo_%s(%%rip), %%rsi',
|
||||
[Self.ClassSymName(TIsExpr(AExpr).TypeName)]));
|
||||
Self.Emit(#9'callq _IsInstance');
|
||||
end;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
{ X as TFoo — checked downcast. Calls _IsInstance; raises EInvalidCast on
|
||||
failure; result = original object pointer (class-to-class). }
|
||||
if AExpr is TAsExpr then
|
||||
begin
|
||||
ScEndLbl := Self.NewLabel('as_ok');
|
||||
Self.EmitExprToEax(TAsExpr(AExpr).Obj);
|
||||
Self.Emit(#9'pushq %rax');
|
||||
Self.Emit(#9'movq %rax, %rdi');
|
||||
Self.Emit(Format(#9'leaq typeinfo_%s(%%rip), %%rsi',
|
||||
[Self.ClassSymName(TAsExpr(AExpr).TypeName)]));
|
||||
Self.Emit(#9'callq _IsInstance');
|
||||
Self.Emit(#9'testl %eax, %eax');
|
||||
Self.Emit(#9'jnz ' + ScEndLbl);
|
||||
Self.Emit(#9'callq _Raise_InvalidCast');
|
||||
Self.Emit(ScEndLbl + ':');
|
||||
Self.Emit(#9'popq %rax');
|
||||
Exit;
|
||||
end;
|
||||
|
||||
{ Supports(Obj, IFoo) — interface query via _ImplementsInterface.
|
||||
Result: 0/1 in %eax. }
|
||||
if AExpr is TSupportsExpr then
|
||||
begin
|
||||
Self.EmitExprToEax(TSupportsExpr(AExpr).Obj);
|
||||
Self.Emit(#9'movq %rax, %rdi');
|
||||
Self.Emit(Format(#9'leaq typeinfo_%s(%%rip), %%rsi',
|
||||
[Self.ClassSymName(TSupportsExpr(AExpr).IntfTypeName)]));
|
||||
Self.Emit(#9'callq _ImplementsInterface');
|
||||
Exit;
|
||||
end;
|
||||
|
||||
raise ENativeCodeGenError.Create(
|
||||
'native backend: unsupported expression form ' + AExpr.ClassName);
|
||||
end;
|
||||
|
|
@ -3867,7 +3956,7 @@ begin
|
|||
Self.Emit(#9'pushq %rax'); { save obj for push below }
|
||||
Self.Emit(#9'movq %rax, %rdi');
|
||||
Self.Emit(Format(#9'leaq typeinfo_%s(%%rip), %%rsi',
|
||||
[NativeMangle(TAsExpr(AArg).TypeName)]));
|
||||
[Self.ClassSymName(TAsExpr(AArg).TypeName)]));
|
||||
Self.Emit(#9'callq _GetItab');
|
||||
Self.Emit(#9'pushq %rax'); { itab on top; obj below }
|
||||
{ Stack top: itab, below: obj — but we need obj first (lower slot).
|
||||
|
|
@ -5049,7 +5138,7 @@ begin
|
|||
|
||||
{ _IsInstance(obj, typeinfo): returns non-zero if obj is an instance of the type. }
|
||||
Self.Emit(#9'movq %r15, %rdi');
|
||||
Self.Emit(#9'leaq typeinfo_' + H.TypeName + '(%rip), %rsi');
|
||||
Self.Emit(#9'leaq typeinfo_' + Self.ClassSymName(H.TypeName) + '(%rip), %rsi');
|
||||
Self.Emit(#9'callq _IsInstance');
|
||||
Self.Emit(#9'testl %eax, %eax');
|
||||
Self.Emit(#9'jnz ' + LblBody);
|
||||
|
|
|
|||
|
|
@ -230,6 +230,9 @@ type
|
|||
procedure TestRun_Native_ShortCircuit_OrSkipsRhs;
|
||||
procedure TestRun_Native_ShortCircuit_AndNilGuard;
|
||||
procedure TestRun_Native_ProceduralParam;
|
||||
procedure TestRun_Native_IsExpr_Class;
|
||||
procedure TestRun_Native_AsExpr_Class;
|
||||
procedure TestRun_Native_SupportsExpr;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
|
@ -3725,6 +3728,60 @@ begin
|
|||
'42' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2ENativeTests.TestRun_Native_IsExpr_Class;
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnBoth(
|
||||
'program P;'
|
||||
+ 'type'
|
||||
+ ' TBase = class public N: Integer; end;'
|
||||
+ ' TChild = class(TBase) public S: string; end;'
|
||||
+ 'var Obj: TBase;'
|
||||
+ 'begin'
|
||||
+ ' Obj := TChild.Create();'
|
||||
+ ' if Obj is TChild then WriteLn(''child-yes'') else WriteLn(''child-no'');'
|
||||
+ ' if Obj is TBase then WriteLn(''base-yes'') else WriteLn(''base-no'');'
|
||||
+ ' Obj := TBase.Create();'
|
||||
+ ' if Obj is TChild then WriteLn(''child-yes'') else WriteLn(''child-no'')'
|
||||
+ 'end.',
|
||||
'child-yes' + LE + 'base-yes' + LE + 'child-no' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2ENativeTests.TestRun_Native_AsExpr_Class;
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnBoth(
|
||||
'program P;'
|
||||
+ 'type'
|
||||
+ ' TBase = class public N: Integer; end;'
|
||||
+ ' TChild = class(TBase) public S: string; end;'
|
||||
+ 'var B: TBase; C: TChild;'
|
||||
+ 'begin'
|
||||
+ ' B := TChild.Create();'
|
||||
+ ' TChild(B).S := ''hello'';'
|
||||
+ ' C := TChild(B as TChild);'
|
||||
+ ' WriteLn(C.S)'
|
||||
+ 'end.',
|
||||
'hello' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2ENativeTests.TestRun_Native_SupportsExpr;
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnBoth(
|
||||
'program P;'
|
||||
+ 'type'
|
||||
+ ' IGreet = interface procedure Greet; end;'
|
||||
+ ' TFoo = class(IGreet) public procedure Greet; end;'
|
||||
+ 'procedure TFoo.Greet; begin WriteLn(''hi'') end;'
|
||||
+ 'var Obj: TFoo;'
|
||||
+ 'begin'
|
||||
+ ' Obj := TFoo.Create();'
|
||||
+ ' if Supports(Obj, IGreet) then WriteLn(''supports'') else WriteLn(''no'')'
|
||||
+ 'end.',
|
||||
'supports' + LE, 0);
|
||||
end;
|
||||
|
||||
initialization
|
||||
RegisterTest(TE2ENativeTests);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue