fpc/utils/pas2jni/def.pas
yury 252f9ef153 * pas2jni: Support for class references.
- Fixed inclusion of unneeded pointer types.

git-svn-id: trunk@35779 -
2017-04-11 18:34:58 +00:00

785 lines
17 KiB
ObjectPascal

{
pas2jni - JNI bridge generator for Pascal.
Copyright (c) 2013 by Yury Sidorov.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************}
unit def;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, contnrs;
type
TDefType = (dtNone, dtUnit, dtClass, dtProc, dtField, dtProp, dtParam, dtVar,
dtType, dtConst, dtProcType, dtEnum, dtSet, dtPointer, dtArray,
dtJniObject, dtJniEnv, dtClassRef);
TDefClass = class of TDef;
{ TDef }
TDef = class
private
FAliasName: string;
FRefCnt: integer;
FItems: TObjectList;
FInSetUsed: boolean;
procedure CheckItems;
function GetAliasName: string;
function GetCount: integer;
function GetIsUsed: boolean;
function GetItem(Index: Integer): TDef;
procedure SetItem(Index: Integer; const AValue: TDef);
protected
procedure SetIsUsed(const AValue: boolean); virtual;
function ResolveDef(d: TDef; ExpectedClass: TDefClass = nil): TDef;
procedure AddRef;
procedure DecRef;
procedure SetExtUsed(ExtDef: TDef; AUsed: boolean; var HasRef: boolean);
function ShouldUseChild(d: TDef): boolean; virtual;
public
DefType: TDefType;
DefId: integer;
SymId: integer;
Name: string;
Parent: TDef;
Tag: integer;
IsPrivate: boolean;
constructor Create; virtual; overload;
constructor Create(AParent: TDef; AType: TDefType); virtual; overload;
destructor Destroy; override;
function Add(ADef: TDef): integer;
function Insert(Index: integer; ADef: TDef): integer;
function FindDef(ADefId: integer; Recursive: boolean = True): TDef;
procedure ResolveDefs; virtual;
procedure SetNotUsed;
function GetRefDef: TDef; virtual;
function GetRefDef2: TDef; virtual;
property Items[Index: Integer]: TDef read GetItem write SetItem; default;
property Count: integer read GetCount;
property IsUsed: boolean read GetIsUsed write SetIsUsed;
property RefCnt: integer read FRefCnt;
property AliasName: string read GetAliasName write FAliasName;
end;
TClassType = (ctClass, ctInterface, ctObject, ctRecord);
{ TClassDef }
TClassDef = class(TDef)
private
FHasClassRef: boolean;
protected
procedure SetIsUsed(const AValue: boolean); override;
function ShouldUseChild(d: TDef): boolean; override;
public
CType: TClassType;
AncestorClass: TClassDef;
HasAbstractMethods: boolean;
HasReplacedItems: boolean;
ImplementsReplacedItems: boolean;
Size: integer;
IID: string;
procedure ResolveDefs; override;
function GetRefDef: TDef; override;
end;
TBasicType = (btVoid, btByte, btShortInt, btWord, btSmallInt, btLongWord, btLongInt, btInt64,
btSingle, btDouble, btString, btWideString, btBoolean, btChar, btWideChar, btEnum,
btGuid);
{ TTypeDef }
TTypeDef = class(TDef)
protected
procedure SetIsUsed(const AValue: boolean); override;
public
BasicType: TBasicType;
end;
{ TPointerDef }
TPointerDef = class(TDef)
private
FHasPtrRef: boolean;
protected
procedure SetIsUsed(const AValue: boolean); override;
public
PtrType: TDef;
procedure ResolveDefs; override;
function IsObjPtr: boolean;
function GetRefDef: TDef; override;
end;
{ TReplDef }
TReplDef = class(TDef)
protected
procedure SetIsUsed(const AValue: boolean); override;
public
IsReplaced: boolean;
IsReplImpl: boolean;
ReplacedItem: TReplDef;
function CanReplaced: boolean; virtual;
function IsReplacedBy(d: TReplDef): boolean; virtual;
procedure CheckReplaced;
end;
TVarOption = (voRead, voWrite, voConst, voVar, voOut);
TVarOptions = set of TVarOption;
{ TVarDef }
TVarDef = class(TReplDef)
private
FHasTypeRef: boolean;
protected
procedure SetIsUsed(const AValue: boolean); override;
public
VarOpt: TVarOptions;
VarType: TDef;
constructor Create; override;
procedure ResolveDefs; override;
function IsReplacedBy(d: TReplDef): boolean; override;
function CanReplaced: boolean; override;
function GetRefDef: TDef; override;
end;
TProcType = (ptProcedure, ptFunction, ptConstructor, ptDestructor);
TProcOption = (poOverride, poOverload, poMethodPtr, poPrivate, poProtected, poClassMethod);
TProcOptions = set of TProcOption;
{ TProcDef }
TProcDef = class(TReplDef)
private
FHasRetTypeRef: boolean;
protected
procedure SetIsUsed(const AValue: boolean); override;
function ShouldUseChild(d: TDef): boolean; override;
public
ProcType: TProcType;
ReturnType: TDef;
ProcOpt: TProcOptions;
procedure ResolveDefs; override;
function IsReplacedBy(d: TReplDef): boolean; override;
function CanReplaced: boolean; override;
function GetRefDef: TDef; override;
end;
TUnitDef = class(TDef)
public
OS: string;
CPU: string;
IntfCRC: string;
PPUVer: integer;
UsedUnits: array of TUnitDef;
Processed: boolean;
IsUnitUsed: boolean;
end;
TConstDef = class(TVarDef)
public
Value: string;
end;
{ TSetDef }
TSetDef = class(TDef)
private
FHasElTypeRef: boolean;
protected
procedure SetIsUsed(const AValue: boolean); override;
public
Size: integer;
Base: integer;
ElMax: integer;
ElType: TTypeDef;
function GetRefDef: TDef; override;
end;
{ TArrayDef }
TArrayDef = class(TDef)
private
FHasElTypeRef: boolean;
FHasRTypeRef: boolean;
protected
procedure SetIsUsed(const AValue: boolean); override;
public
ElType: TDef;
RangeType: TDef;
RangeLow, RangeHigh: integer;
function GetRefDef: TDef; override;
function GetRefDef2: TDef; override;
end;
{ TClassRefDef }
TClassRefDef = class(TDef)
private
FHasClassRef: boolean;
protected
procedure SetIsUsed(const AValue: boolean); override;
public
ClassRef: TDef;
procedure ResolveDefs; override;
function GetRefDef: TDef; override;
end;
const
ReplDefs = [dtField, dtProp, dtProc];
var
OnCanUseDef: function (def, refdef: TDef): boolean;
implementation
function IsSameType(t1, t2: TDef): boolean;
begin
Result:=t1 = t2;
if Result then
exit;
if (t1 = nil) or (t2 = nil) or (t1.DefType <> t2.DefType) then
exit;
if t1.DefType <> dtType then
exit;
Result:=TTypeDef(t1).BasicType = TTypeDef(t2).BasicType;
end;
{ TClassRefDef }
procedure TClassRefDef.SetIsUsed(const AValue: boolean);
begin
inherited SetIsUsed(AValue);
SetExtUsed(ClassRef, AValue, FHasClassRef);
end;
procedure TClassRefDef.ResolveDefs;
begin
inherited ResolveDefs;
ClassRef:=ResolveDef(ClassRef);
end;
function TClassRefDef.GetRefDef: TDef;
begin
Result:=ClassRef;
end;
{ TArrayDef }
procedure TArrayDef.SetIsUsed(const AValue: boolean);
begin
inherited SetIsUsed(AValue);
SetExtUsed(ElType, AValue, FHasElTypeRef);
SetExtUsed(RangeType, AValue, FHasRTypeRef);
end;
function TArrayDef.GetRefDef: TDef;
begin
Result:=ElType;
end;
function TArrayDef.GetRefDef2: TDef;
begin
Result:=RangeType;
end;
{ TPointerDef }
procedure TPointerDef.SetIsUsed(const AValue: boolean);
begin
if IsObjPtr then begin
inherited SetIsUsed(AValue);
SetExtUsed(PtrType, AValue, FHasPtrRef);
end
else
if AValue then
AddRef
else
DecRef;
end;
procedure TPointerDef.ResolveDefs;
begin
inherited ResolveDefs;
PtrType:=ResolveDef(PtrType);
end;
function TPointerDef.IsObjPtr: boolean;
begin
Result:=(PtrType <> nil) and (PtrType.DefType in [dtClass]);
end;
function TPointerDef.GetRefDef: TDef;
begin
Result:=PtrType;
end;
{ TReplDef }
procedure TReplDef.SetIsUsed(const AValue: boolean);
var
i: integer;
begin
i:=RefCnt;
inherited SetIsUsed(AValue);
if (i = 0) and (RefCnt > 0) then
CheckReplaced;
end;
function TReplDef.CanReplaced: boolean;
begin
Result:=not (IsPrivate or (Parent = nil) or (Parent.DefType <> dtClass));
end;
function TReplDef.IsReplacedBy(d: TReplDef): boolean;
begin
Result:=d.CanReplaced and (CompareText(Name, d.Name) = 0);
end;
procedure TReplDef.CheckReplaced;
function _Scan(cls: TClassDef): boolean;
var
i: integer;
d: TReplDef;
c: TClassDef;
begin
Result:=False;
c:=cls.AncestorClass;
if c = nil then
exit;
for i:=0 to c.Count - 1 do begin
d:=TReplDef(c[i]);
if (d.DefType in ReplDefs) and IsReplacedBy(d) then begin
d.IsReplaced:=True;
ReplacedItem:=d;
Result:=True;
break;
end;
end;
if not Result then
Result:=_Scan(c);
if Result then begin
cls.ImplementsReplacedItems:=True;
c.HasReplacedItems:=True;
end;
end;
begin
if not CanReplaced then
exit;
if _Scan(TClassDef(Parent)) then
IsReplImpl:=True;
end;
{ TSetDef }
procedure TSetDef.SetIsUsed(const AValue: boolean);
begin
inherited SetIsUsed(AValue);
SetExtUsed(ElType, AValue, FHasElTypeRef);
end;
function TSetDef.GetRefDef: TDef;
begin
Result:=ElType;
end;
{ TTypeDef }
procedure TTypeDef.SetIsUsed(const AValue: boolean);
begin
if BasicType in [btEnum] then
inherited SetIsUsed(AValue)
else
if AValue then
AddRef
else
DecRef;
end;
{ TProcDef }
procedure TProcDef.SetIsUsed(const AValue: boolean);
var
i: integer;
begin
if IsPrivate then
exit;
if AValue and (RefCnt = 0) then begin
for i:=0 to Count - 1 do
if (Items[i].DefType = dtParam) and (TVarDef(Items[i]).VarType = nil) then
exit; // If procedure has unsupported parameters, don't use it
end;
inherited SetIsUsed(AValue);
if ReturnType <> Parent then
SetExtUsed(ReturnType, AValue, FHasRetTypeRef);
end;
function TProcDef.ShouldUseChild(d: TDef): boolean;
begin
Result:=d.DefType in [dtParam];
end;
procedure TProcDef.ResolveDefs;
begin
inherited ResolveDefs;
ReturnType:=ResolveDef(ReturnType);
end;
function TProcDef.IsReplacedBy(d: TReplDef): boolean;
var
i: integer;
p: TProcDef;
begin
Result:=False;
if d.DefType <> dtProc then
exit;
p:=TProcDef(d);
if (Count = p.Count) and inherited IsReplacedBy(p) then begin
// Check parameter types
for i:=0 to Count - 1 do
if not IsSameType(TVarDef(Items[i]).VarType, TVarDef(p.Items[i]).VarType) then
exit;
Result:=True;
end;
end;
function TProcDef.CanReplaced: boolean;
begin
Result:=inherited CanReplaced and (ProcType = ptFunction);
end;
function TProcDef.GetRefDef: TDef;
begin
Result:=ReturnType;
end;
{ TClassDef }
procedure TClassDef.SetIsUsed(const AValue: boolean);
begin
inherited SetIsUsed(AValue);
SetExtUsed(AncestorClass, AValue, FHasClassRef);
end;
function TClassDef.ShouldUseChild(d: TDef): boolean;
begin
Result:=d.DefType in [dtProc, dtField, dtProp];
end;
procedure TClassDef.ResolveDefs;
begin
inherited ResolveDefs;
AncestorClass:=TClassDef(ResolveDef(AncestorClass, TClassDef));
end;
function TClassDef.GetRefDef: TDef;
begin
Result:=AncestorClass;
end;
{ TVarDef }
procedure TVarDef.SetIsUsed(const AValue: boolean);
var
ptr, d: TDef;
begin
if IsPrivate then
exit;
inherited SetIsUsed(AValue);
// Detect circular pointers
if (VarType <> nil) and (VarType.DefType = dtPointer) and (VarType.RefCnt > 0) then begin
ptr:=TPointerDef(VarType).PtrType;
if ptr <> nil then begin
d:=Self;
while d <> nil do begin
if d = ptr then
exit;
d:=d.Parent;;
end;
end;
end;
SetExtUsed(VarType, AValue, FHasTypeRef);
end;
procedure TVarDef.ResolveDefs;
begin
inherited ResolveDefs;
VarType:=ResolveDef(VarType);
end;
function TVarDef.IsReplacedBy(d: TReplDef): boolean;
begin
Result:=(d.DefType in [dtProp, dtField]) and not IsSameType(VarType, TVarDef(d).VarType) and inherited IsReplacedBy(d);
end;
function TVarDef.CanReplaced: boolean;
begin
Result:=(voRead in VarOpt) and inherited CanReplaced;
end;
function TVarDef.GetRefDef: TDef;
begin
Result:=VarType;
end;
constructor TVarDef.Create;
begin
inherited Create;
VarOpt:=[voRead, voWrite];
end;
{ TDef }
procedure TDef.CheckItems;
begin
if FItems = nil then
FItems:=TObjectList.Create(True);
end;
function TDef.GetAliasName: string;
begin
if FAliasName <> '' then
Result:=FAliasName
else
Result:=Name;
end;
function TDef.GetCount: integer;
begin
if FItems = nil then
Result:=0
else begin
CheckItems;
Result:=FItems.Count;
end;
end;
function TDef.GetIsUsed: boolean;
begin
Result:=FRefCnt > 0;
end;
function TDef.GetItem(Index: Integer): TDef;
begin
CheckItems;
Result:=TDef(FItems[Index]);
end;
procedure TDef.SetIsUsed(const AValue: boolean);
var
i: integer;
f: boolean;
d: TDef;
begin
if FInSetUsed or (DefType = dtNone) or IsPrivate then
exit;
if AValue then begin
if Assigned(OnCanUseDef) and not OnCanUseDef(Self, Parent) then
exit;
AddRef;
f:=FRefCnt = 1;
end
else begin
if FRefCnt = 0 then
exit;
DecRef;
f:=FRefCnt = 0;
end;
if f then begin
// Update used mark of children only once
FInSetUsed:=True;
try
for i:=0 to Count - 1 do begin
d:=Items[i];
if ShouldUseChild(d) then
d.IsUsed:=AValue;
end;
finally
FInSetUsed:=False;
end;
// Update parent's used mark
if (Parent <> nil) and (Parent.DefType = dtUnit) then
if AValue then
Parent.AddRef
else
Parent.DecRef;
end;
end;
function TDef.ResolveDef(d: TDef; ExpectedClass: TDefClass): TDef;
begin
if (d = nil) or (d.DefType <> dtNone) then
Result:=d
else begin
Result:=d.Parent.FindDef(d.DefId);
if (ExpectedClass <> nil) and (Result <> nil) then
if not (Result is ExpectedClass) then
raise Exception.CreateFmt('Unexpected class. Expected: %s, got: %s', [ExpectedClass.ClassName, Result.ClassName]);
end;
end;
procedure TDef.AddRef;
begin
Inc(FRefCnt);
end;
procedure TDef.DecRef;
begin
if FRefCnt > 0 then
Dec(FRefCnt);
end;
procedure TDef.SetExtUsed(ExtDef: TDef; AUsed: boolean; var HasRef: boolean);
var
OldRefCnt: integer;
begin
if ExtDef = nil then
exit;
if AUsed then begin
if HasRef then
exit;
if Assigned(OnCanUseDef) and not OnCanUseDef(ExtDef, Self) then
exit;
OldRefCnt:=ExtDef.RefCnt;
ExtDef.IsUsed:=True;
HasRef:=OldRefCnt <> ExtDef.RefCnt;
end
else
if HasRef and not IsUsed then begin
ExtDef.IsUsed:=False;
HasRef:=False;
end;
end;
function TDef.ShouldUseChild(d: TDef): boolean;
begin
Result:=True;
end;
procedure TDef.SetItem(Index: Integer; const AValue: TDef);
begin
CheckItems;
FItems[Index]:=AValue;
end;
constructor TDef.Create;
begin
DefId:=-1;
DefType:=dtNone;
end;
constructor TDef.Create(AParent: TDef; AType: TDefType);
begin
Create;
if AParent <> nil then
AParent.Add(Self);
DefType:=AType;
end;
destructor TDef.Destroy;
begin
FreeAndNil(FItems);
if (Parent <> nil) and (Parent.FItems <> nil) then begin
Parent.FItems.OwnsObjects:=False;
try
Parent.FItems.Remove(Self);
finally
Parent.FItems.OwnsObjects:=True;
end;
end;
inherited Destroy;
end;
function TDef.Add(ADef: TDef): integer;
begin
Result:=Insert(Count, ADef);
end;
function TDef.Insert(Index: integer; ADef: TDef): integer;
begin
CheckItems;
Result:=Index;
FItems.Insert(Result, ADef);
ADef.Parent:=Self;
end;
function TDef.FindDef(ADefId: integer; Recursive: boolean): TDef;
function _Find(d: TDef): TDef;
var
i: integer;
begin
Result:=nil;
for i:=0 to d.Count - 1 do
with d[i] do begin
if (DefType <> dtNone) and (DefId = ADefId) then begin
Result:=d[i];
break;
end;
if Recursive and (Count > 0) then begin
Result:=_Find(d[i]);
if Result <> nil then
break;
end;
end;
end;
begin
Result:=_Find(Self);
end;
procedure TDef.ResolveDefs;
var
i: integer;
begin
for i:=0 to Count - 1 do
Items[i].ResolveDefs;
end;
procedure TDef.SetNotUsed;
begin
if FRefCnt = 0 then
exit;
FRefCnt:=1;
IsUsed:=False;
end;
function TDef.GetRefDef: TDef;
begin
Result:=nil;
end;
function TDef.GetRefDef2: TDef;
begin
Result:=nil;
end;
end.