mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 08:58:10 +02:00
2668 lines
86 KiB
ObjectPascal
2668 lines
86 KiB
ObjectPascal
(*
|
|
This file is distributed under the Lesser GNU General Public License
|
|
(see the file COPYING.LGPL) with the following modification:
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent modules,
|
|
and to copy and distribute the resulting executable under terms of your choice,
|
|
provided that you also meet, for each linked independent module, the terms
|
|
and conditions of the license of that module. An independent module is a
|
|
module which is not derived from or based on this library. If you modify this
|
|
library, you may extend this exception to your version of the library, but
|
|
you are not obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version.
|
|
|
|
If you didn't receive a copy of the file COPYING.LGPL, contact:
|
|
Free Software Foundation, Inc.,
|
|
675 Mass Ave
|
|
Cambridge, MA 02139
|
|
USA
|
|
*)
|
|
unit JitTypes;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$ModeSwitch advancedrecords}
|
|
{$ModeSwitch typehelpers}
|
|
{.$Inline off}
|
|
{..$DEFINE JIT_REFCNT_DEBUG}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF JIT_REFCNT_DEBUG}
|
|
LazLogger,
|
|
{$ENDIF}
|
|
Classes, SysUtils, TypInfo, fgl, JitHelper, JitRttiWriter;
|
|
|
|
type
|
|
|
|
{ JitTypeParserException }
|
|
|
|
JitTypeParserException = class(Exception)
|
|
private
|
|
FErrorPos: Integer;
|
|
FErrorToken: String;
|
|
public
|
|
constructor Create(APos: Integer; const AToken, msg: string);
|
|
property ErrorPos: Integer read FErrorPos;
|
|
property ErrorToken: String read FErrorToken;
|
|
end;
|
|
JitTypeParserExceptionTypeNotFound = class(JitTypeParserException)
|
|
end;
|
|
JitTypeParserExceptionSyntaxError = class(JitTypeParserException)
|
|
end;
|
|
|
|
(* TRefCountedJitReference
|
|
By default, when TJitType and TJitCreator are destroyed, they will free the
|
|
TypeInfo and JitClass that they provided.
|
|
In order to be able to use this data past the creators life time, one can
|
|
obtain a LockReference from the creator.
|
|
The TJitType and TJitCreator can still be destroyed, but the TypeInfo and
|
|
JitClass will be kept, until all locks have been released.
|
|
*)
|
|
|
|
{ TRefCountedJitReference }
|
|
|
|
TRefCountedJitReference = class
|
|
strict private
|
|
FRefCount: Integer;
|
|
|
|
protected
|
|
procedure DoRefCountZero; virtual;
|
|
procedure DoBeforeDecRefCount; virtual;
|
|
|
|
procedure IncRefCount; inline;
|
|
procedure DecRefCount; inline;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
|
|
procedure ReleaseLock; inline;
|
|
property RefCount: Integer read FRefCount;
|
|
end;
|
|
|
|
{ TRefCountedJitNestedReference }
|
|
|
|
TRefCountedJitNestedReference = class(TRefCountedJitReference)
|
|
protected type
|
|
{ TRefCountedJitReferenceList }
|
|
|
|
TRefCountedJitReferenceList = class(specialize TFPGList<TRefCountedJitReference>)
|
|
public
|
|
procedure ClearList;
|
|
end;
|
|
strict private
|
|
(* The list can contain duplicates / This way we know when this list has more that one ref to the same object *)
|
|
FNestedReferrences: TRefCountedJitReferenceList;
|
|
private class var
|
|
FLastTag: QWord;
|
|
private
|
|
FInGetCircularRefCount,
|
|
FOnlyHasCircularNestedRefs: ByteBool; // All refs start with a none circular ref
|
|
FTag: QWord;
|
|
function HasExternalCircularRefCount(ARefObj: TRefCountedJitReference;
|
|
ATag: QWord; AnHasExternRefInCallPath: Boolean): Boolean;
|
|
function GetCircularRefCount(ARefObj: TRefCountedJitReference; ATag: QWord;
|
|
AnHasExternRefInCallPath: Boolean; out AnFoundExternRef: Boolean): integer;
|
|
protected
|
|
procedure DoRefCountZero; override;
|
|
procedure DoBeforeDecRefCount; override;
|
|
property NestedReferrences: TRefCountedJitReferenceList read FNestedReferrences;
|
|
|
|
function NestedCount: integer; virtual;
|
|
function GetNested(AnIndex: integer): TRefCountedJitReference; virtual;
|
|
property Nested[AnIndex: integer]: TRefCountedJitReference read GetNested;
|
|
public
|
|
procedure AddToList(AReference: TRefCountedJitReference); overload;
|
|
// RemoveFromList: will remove ONE entry of the given instance
|
|
procedure RemoveFromList(AReference: TRefCountedJitReference);
|
|
procedure ClearList; virtual;
|
|
|
|
public
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ TFreeNotifyingObject }
|
|
|
|
TFreeNotifyingObject = class
|
|
private type
|
|
|
|
{ TMethodComp }
|
|
|
|
TMethodComp = record
|
|
m: TMethod;
|
|
class operator = (a, b: TMethodComp): Boolean;
|
|
end;
|
|
TNotifyList = specialize TFPGList<TMethodComp>;
|
|
private
|
|
FFreeNotificationList: TNotifyList;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure AddFreeNotification(ANotification: TNotifyEvent);
|
|
procedure RemoveFreeNotification(ANotification: TNotifyEvent);
|
|
end;
|
|
|
|
{ TReferenceAbleJitClass }
|
|
|
|
TReferenceAbleJitClass = class(TFreeNotifyingObject)
|
|
protected
|
|
function GetLockReferenceInc: TRefCountedJitReference; virtual; // forwarders must overwrite this method, to avoid double inc
|
|
function GetLockReferenceObj: TRefCountedJitReference; virtual;
|
|
public
|
|
(* LockReference
|
|
- returns nil, if no ref needed
|
|
- returned referrence will have RefCount already increased
|
|
*)
|
|
property LockReference: TRefCountedJitReference read GetLockReferenceInc;
|
|
end;
|
|
|
|
{ TJitClassCreatorBase }
|
|
|
|
TJitClassCreatorBase = class(TReferenceAbleJitClass)
|
|
protected
|
|
FClassUnit: String;
|
|
function GetTypeInfo: PTypeInfo; virtual; abstract;
|
|
function GetJitClass: TClass; virtual; abstract;
|
|
public
|
|
property TypeInfo: PTypeInfo read GetTypeInfo;
|
|
property ClassUnit: String read FClassUnit; // write SetClassUnit;
|
|
end;
|
|
|
|
TJitTypeLibrary = class;
|
|
|
|
{ TJitType }
|
|
|
|
TJitType = class(TReferenceAbleJitClass)
|
|
strict private
|
|
FTypeLibrary: TJitTypeLibrary;
|
|
private
|
|
FOwnedByLibrary: Boolean;
|
|
FTypeName: String;
|
|
FUnitName: String;
|
|
procedure DoTypeLibFreed(Sender: TObject);
|
|
procedure SetTypeLibrary(AValue: TJitTypeLibrary);
|
|
|
|
protected
|
|
function GetTypeInfo: PTypeInfo; virtual;
|
|
function IsConstTypeInfo: Boolean; virtual; // TypeInfo must not be freed
|
|
function GetResolvedTypeName: String;
|
|
property TypeLibrary: TJitTypeLibrary read FTypeLibrary write SetTypeLibrary;
|
|
public
|
|
constructor Create(ATypeName: String; ATypeLibrary: TJitTypeLibrary = nil);
|
|
constructor Create(ATypeName, AUnitName: String; ATypeLibrary: TJitTypeLibrary = nil);
|
|
destructor Destroy; override;
|
|
property TypeName: String read FTypeName;
|
|
property ResolvedTypeName: String read GetResolvedTypeName; // un-aliased
|
|
property TypeInfo: PTypeInfo read GetTypeInfo;
|
|
property UnitName: String read FUnitName write FUnitName; // ignored by alias
|
|
property OwnedByLibrary: Boolean read FOwnedByLibrary;
|
|
end;
|
|
|
|
{ TJitDeclarationParser }
|
|
|
|
TJitParserTkKind = (
|
|
ptNone, ptError,
|
|
ptIdent, ptNum, ptChar, ptEmptyString,
|
|
ptRoundOpen, ptRoundClose, ptSquareOpen, ptSquareClose, // brackens
|
|
ptColon, ptComma, ptSemicolon, ptEqual, ptSymbol,
|
|
ptDot, ptDotDot,
|
|
kwSet, kwArray, kwOf, kwClass, kwObject, kwString, kwRecord, kwFunction, kwProcedure,
|
|
kwProperty, kwPublished, kwPublic, kwProtected, kwPrivate,
|
|
kwEnd,
|
|
kwVar, kwConst, kwOut, kwConstRef,
|
|
ptEOT);
|
|
TJitParserTkKinds = set of TJitParserTkKind;
|
|
|
|
PJitDeclarationParser = ^TJitDeclarationParser;
|
|
TCheckSectionEndProc = function(AParser: PJitDeclarationParser): boolean of object;
|
|
|
|
TJitDeclarationParser = record
|
|
private type
|
|
TJitDeclarationParserData = record
|
|
FCurToken: PChar;
|
|
FCurTokenLen: Integer;
|
|
FCurTokenKind: TJitParserTkKind;
|
|
end;
|
|
TParseContext = (pcNone, pcFuncParamStart, pcOfExpexted);
|
|
private
|
|
FStartPos: PChar;
|
|
FCheckSectionEndProc: TCheckSectionEndProc;
|
|
FSavePoint: TJitDeclarationParserData;
|
|
DAT: TJitDeclarationParserData;
|
|
procedure SkipWhiteSpace; inline;
|
|
public
|
|
constructor Create(ATokenStart: PChar);
|
|
function CurPos: Integer;
|
|
function IsBeforeEOT: Boolean; // Next will be IsBeforeEOT
|
|
function IsBeforeEotOrSectionEnd: Boolean; // at IsBeforeEOT, ";" or "end"
|
|
|
|
function Next(AContext: TParseContext = pcNone): TJitParserTkKind;
|
|
function CurrentToken: String;
|
|
function CurrentTokenAsNum(out IsQWord: Boolean): Int64;
|
|
function CurrentTokenRaw: String; inline; // with escaping & for idents
|
|
function NextToken: String; inline;
|
|
|
|
function PeekKind(AContext: TParseContext = pcNone): TJitParserTkKind; inline;
|
|
function PeekTokenRaw: String;
|
|
// GetQualifiedIdent: Expands CurrentToken to full ident
|
|
procedure GetQualifiedIdent(out APath, AnIdent: String); // for unit.type
|
|
procedure GetQualifiedIdent(out APath, AnIdent1, AnIdent2: String); // for unit.enumtype.member
|
|
procedure SavePosition;
|
|
procedure RestorePosition;
|
|
property CurrentKind: TJitParserTkKind read DAT.FCurTokenKind;
|
|
property CheckSectionEndProc: TCheckSectionEndProc read FCheckSectionEndProc write FCheckSectionEndProc;
|
|
end;
|
|
|
|
TJitTypeInfoParseFlag = (pfAllowProcName, pfAlwaysAsMethod);
|
|
TJitTypeInfoParseFlags = set of TJitTypeInfoParseFlag;
|
|
|
|
{ TJitTypeInfo }
|
|
|
|
TJitTypeInfo = class(TJitType)
|
|
private type
|
|
{ TRefCountedTypeInfo }
|
|
TRefCountedTypeInfo = class(TRefCountedJitNestedReference)
|
|
private
|
|
FTypeInfo: PTypeInfo;
|
|
|
|
function IndexOf(ATypeInfo: PTypeInfo): integer;
|
|
procedure AddToList(ATypeInfo: TJitType); overload;
|
|
// RemoveFromList: will remove ONE entry of this
|
|
procedure RemoveFromList(ATypeInfo: PTypeInfo);
|
|
procedure SetTypeInfo(ATypeInfo: PTypeInfo);
|
|
protected
|
|
procedure DoRefCountZero; override;
|
|
property TypeInfo: PTypeInfo read FTypeInfo;
|
|
public
|
|
constructor Create(ATypeInfo: PTypeInfo);
|
|
end;
|
|
|
|
private
|
|
FIsConstTypeInfo: Boolean;
|
|
FTypeInfo: PTypeInfo; // use Set[Const]FTypeInfo for write access => so Ref is updated too
|
|
FRefCountedTypeInfo: TRefCountedTypeInfo; // created on request => use RefCountedTypeInfo()
|
|
FDeclaration: String;
|
|
FParseFlags: TJitTypeInfoParseFlags;
|
|
FIsInParseFromDeclaration: Boolean;
|
|
|
|
procedure SetFTypeInfo(AValue: PTypeInfo);
|
|
procedure SetConstFTypeInfo(AValue: PTypeInfo);
|
|
function RefCountedTypeInfo: TRefCountedTypeInfo; inline;
|
|
protected
|
|
procedure ParseFromDeclaration(AParser: PJitDeclarationParser = nil);
|
|
function GetTypeInfo: PTypeInfo; override;
|
|
function GetLockReferenceObj: TRefCountedJitReference; override;
|
|
function IsConstTypeInfo: Boolean; override;
|
|
public
|
|
constructor Create(ATypeName, ADeclaration, AUnitName: String;
|
|
ATypeLibrary: TJitTypeLibrary = nil; AParseFlags: TJitTypeInfoParseFlags = []);
|
|
constructor Create(ATypeName, ADeclaration: String;
|
|
ATypeLibrary: TJitTypeLibrary = nil; AParseFlags: TJitTypeInfoParseFlags = []);
|
|
constructor Create(ATypeName: String; ATypeInfo: PTypeInfo; ATypeLibrary: TJitTypeLibrary = nil);
|
|
destructor Destroy; override;
|
|
|
|
property Declaration: String read FDeclaration;
|
|
|
|
end;
|
|
|
|
TJitTypeClassBase = class abstract (TJitType)
|
|
protected
|
|
function GetJitClass: TClass; virtual; abstract;
|
|
public
|
|
property JitClass: TClass read GetJitClass;
|
|
end;
|
|
|
|
{ TJitTypeClass }
|
|
|
|
TJitTypeClass = class(TJitTypeClassBase)
|
|
private
|
|
FClass: TClass;
|
|
|
|
protected
|
|
function GetTypeInfo: PTypeInfo; override;
|
|
function GetJitClass: TClass; override;
|
|
public
|
|
// UnitName defaults to AClass.UnitName
|
|
constructor Create(ATypeName: String; AClass: TClass; ATypeLibrary: TJitTypeLibrary = nil);
|
|
constructor Create(ATypeName, AUnitName: String; AClass: TClass; ATypeLibrary: TJitTypeLibrary = nil);
|
|
end;
|
|
|
|
{ TJitTypeJitClass }
|
|
|
|
TJitTypeJitClass = class(TJitTypeClassBase)
|
|
private
|
|
FJitClassCreator: TJitClassCreatorBase;
|
|
FOwnJitCreator: Boolean;
|
|
procedure DoCreatorFreed(Sender: TObject);
|
|
|
|
protected
|
|
function GetTypeInfo: PTypeInfo; override;
|
|
function GetJitClass: TClass; override;
|
|
function GetLockReferenceInc: TRefCountedJitReference; override;
|
|
function IsConstTypeInfo: Boolean; override;
|
|
public
|
|
// UnitName defaults to AJitClassCreator.ClassUnit
|
|
constructor Create(ATypeName: String; AJitClassCreator: TJitClassCreatorBase;
|
|
ATypeLibrary: TJitTypeLibrary = nil; ATakeOwnerShip: Boolean = False);
|
|
constructor Create(ATypeName, AUnitName: String; AJitClassCreator: TJitClassCreatorBase;
|
|
ATypeLibrary: TJitTypeLibrary = nil; ATakeOwnerShip: Boolean = False);
|
|
destructor Destroy; override;
|
|
|
|
property JitClassCreator: TJitClassCreatorBase read FJitClassCreator;
|
|
property OwnJitCreator: Boolean read FOwnJitCreator write FOwnJitCreator;
|
|
end;
|
|
|
|
{ TJitTypeAlias }
|
|
|
|
TJitTypeAlias = class(TJitType)
|
|
private
|
|
FRealType: TJitType;
|
|
FRealTypeName: String;
|
|
FInGetRealType: Boolean;
|
|
|
|
procedure DoRealTypeFreed(Sender: TObject);
|
|
function GetRealType: TJitType;
|
|
protected
|
|
function GetTypeInfo: PTypeInfo; override;
|
|
function GetLockReferenceInc: TRefCountedJitReference; override;
|
|
function IsConstTypeInfo: Boolean; override;
|
|
function GetResolvedTypeName: String;
|
|
public
|
|
constructor Create(ATypeName, ARealTypeName: String; ATypeLibrary: TJitTypeLibrary = nil);
|
|
constructor Create(ATypeName: String; ARealType: TJitType; ATypeLibrary: TJitTypeLibrary = nil);
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
|
|
TTypeSearchOption = (
|
|
tsoOnlyUnit // Search only in the given unit. Otherwise search the unit first, then all others
|
|
);
|
|
TTypeSearchOptions = set of TTypeSearchOption;
|
|
|
|
{ TJitTypeLibrary }
|
|
|
|
TJitTypeLibrary = class(TFreeNotifyingObject)
|
|
private type
|
|
TTypeMap = specialize TFPGMap<String, TJitType>;
|
|
procedure DoTypeFreed(Sender: TObject);
|
|
private
|
|
FAllowDuplicates: Boolean;
|
|
FTypeMap: TTypeMap;
|
|
function GetTypes(AName: String): TJitType;
|
|
procedure SetAllowDuplicates(AValue: Boolean);
|
|
protected
|
|
function IndexOf(ATypeName, AnUnitName: String): Integer; overload;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
|
|
function AddType(AType: TJitType; ATakeOwnerShip: Boolean = True): TJitType;
|
|
(* AddType:
|
|
AddType may create an alias, if the Declaration is a single existing identifier.
|
|
If a new TJitTypeInfo should be created regardless => use AForceNewJitTypeInfo
|
|
*)
|
|
function AddType(ATypeName, ADeclaration: String; AForceNewJitTypeInfo: Boolean = False): TJitType; overload;
|
|
function AddType(ATypeName, ADeclaration, AUnitName: String; AForceNewJitTypeInfo: Boolean = False): TJitType; overload;
|
|
(* AddClass:
|
|
AUnitName defaults to AClass.UnitName.
|
|
It can be overriden, this should be done with an empty name to make the type match any unit in IndexOf
|
|
*)
|
|
function AddClass(ATypeName: String; AClass: TClass): TJitType; overload;
|
|
function AddClass(ATypeName, AUnitName: String; AClass: TClass): TJitType; overload;
|
|
function AddJitClass(ATypeName: String; AJitClassCreator: TJitClassCreatorBase; ATakeCreatorOwnerShip: Boolean = False): TJitType; overload;
|
|
function AddAlias(ATypeName, ARealTypeName: String): TJitType; overload;
|
|
procedure Remove(ATypeName: String; AnUnitName: String = '');
|
|
procedure Clear;
|
|
|
|
function FindType(const AName: String; AnUnitName: String = ''; ASearchOptions: TTypeSearchOptions = []): TJitType;
|
|
|
|
function FindTypeForEnumElem(const AnEnumElem: String; AnUnitName: String = ''; ASearchOptions: TTypeSearchOptions = []): TJitType;
|
|
function FindTypeForEnumElem(const AnEnumElem: String; out AnEnumVal: Integer; AnUnitName: String = ''; ASearchOptions: TTypeSearchOptions = []): TJitType;
|
|
|
|
property Types[AName: String]: TJitType read GetTypes; default;
|
|
property AllowDuplicates: Boolean read FAllowDuplicates write SetAllowDuplicates;
|
|
end;
|
|
|
|
function TypeInfoByName(ATypeName: String): PTypeInfo;
|
|
function TrimDeclaration(ADecl: String): String;
|
|
|
|
implementation
|
|
|
|
function TypeInfoByName(ATypeName: String): PTypeInfo;
|
|
begin
|
|
case lowercase(ATypeName) of
|
|
'byte': Result := TypeInfo(Byte);
|
|
'word': Result := TypeInfo(Word);
|
|
'longword': Result := TypeInfo(LongWord);
|
|
'qword': Result := TypeInfo(QWord);
|
|
'shortint': Result := TypeInfo(ShortInt);
|
|
'smallint': Result := TypeInfo(SmallInt);
|
|
'longint': Result := TypeInfo(LongInt);
|
|
'int64': Result := TypeInfo(Int64);
|
|
'single': Result := TypeInfo(Single);
|
|
'double': Result := TypeInfo(Double);
|
|
'real': Result := TypeInfo(Real);
|
|
'extended': Result := TypeInfo(Extended);
|
|
'boolean': Result := TypeInfo(Boolean);
|
|
'bytebool': Result := TypeInfo(ByteBool);
|
|
'wordbool': Result := TypeInfo(WordBool);
|
|
'longbool': Result := TypeInfo(LongBool);
|
|
'qwordbool': Result := TypeInfo(QWordBool);
|
|
'ansistring': Result := TypeInfo(AnsiString);
|
|
'unicodestring': Result := TypeInfo(UnicodeString);
|
|
'widestring': Result := TypeInfo(WideString);
|
|
'shortstring': Result := TypeInfo(shortstring);
|
|
'char': Result := TypeInfo(Char);
|
|
'widechar': Result := TypeInfo(WideChar);
|
|
'pointer': Result := TypeInfo(pointer);
|
|
else
|
|
Result := nil;
|
|
end;
|
|
end;
|
|
|
|
function TrimDeclaration(ADecl: String): String;
|
|
Const WhiteSpace = [#0..' ', ';'];
|
|
var Ofs, Len: integer;
|
|
begin
|
|
len := Length(ADecl);
|
|
while (Len>0) and (ADecl[Len] in WhiteSpace) do
|
|
dec(Len);
|
|
Ofs := 1;
|
|
while (Ofs<=Len) and (ADecl[Ofs] in WhiteSpace) do
|
|
Inc(Ofs);
|
|
result := Copy(ADecl, Ofs, 1 + Len - Ofs);
|
|
end;
|
|
|
|
{ JitTypeParserException }
|
|
|
|
constructor JitTypeParserException.Create(APos: Integer; const AToken,
|
|
msg: string);
|
|
begin
|
|
inherited Create(msg);
|
|
FErrorPos := APos;
|
|
FErrorToken := AToken;
|
|
end;
|
|
|
|
{ TRefCountedJitNestedReference.TRefCountedJitReferenceList }
|
|
|
|
procedure TRefCountedJitNestedReference.TRefCountedJitReferenceList.ClearList;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to Count - 1 do
|
|
Items[i].ReleaseLock;
|
|
Clear;
|
|
end;
|
|
|
|
{ TFreeNotifyingObject.TMethodComp }
|
|
|
|
class operator TFreeNotifyingObject.TMethodComp. = (a, b: TMethodComp): Boolean;
|
|
begin
|
|
result := (a.m.Code = b.m.Code) and
|
|
(a.m.Data = b.m.Data);
|
|
end;
|
|
|
|
{ TRefCountedJitReference }
|
|
|
|
procedure TRefCountedJitReference.DoRefCountZero;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TRefCountedJitReference.DoBeforeDecRefCount;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
constructor TRefCountedJitReference.Create;
|
|
begin
|
|
FRefCount := 1;
|
|
end;
|
|
|
|
destructor TRefCountedJitReference.Destroy;
|
|
begin
|
|
{$IFDEF JIT_REFCNT_DEBUG}
|
|
debuglnEnter(['> TRefCountedJitReference.Destroy ',dbgs(self),' ' ]); try
|
|
{$ENDIF}
|
|
if FRefCount > 0 then
|
|
raise exception.Create('destroy while referrenced');
|
|
|
|
DoRefCountZero;
|
|
inherited Destroy;
|
|
{$IFDEF JIT_REFCNT_DEBUG}
|
|
finally debuglnExit(['< TRefCountedJitReference.Destroy ',dbgs(self) ]); end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TRefCountedJitReference.IncRefCount;
|
|
begin
|
|
inc(FRefCount);
|
|
end;
|
|
|
|
procedure TRefCountedJitReference.DecRefCount;
|
|
begin
|
|
DoBeforeDecRefCount;
|
|
dec(FRefCount);
|
|
if FRefCount = 0 then
|
|
Destroy;
|
|
end;
|
|
|
|
procedure TRefCountedJitReference.ReleaseLock;
|
|
begin
|
|
DecRefCount;
|
|
end;
|
|
|
|
{ TRefCountedJitNestedReference }
|
|
|
|
function TRefCountedJitNestedReference.HasExternalCircularRefCount(
|
|
ARefObj: TRefCountedJitReference; ATag: QWord; AnHasExternRefInCallPath: Boolean
|
|
): Boolean;
|
|
var
|
|
i: Integer;
|
|
n: TRefCountedJitReference;
|
|
begin
|
|
{$IFDEF JIT_REFCNT_DEBUG}
|
|
debuglnEnter(['> HasExternalCircularRefCount ',dbgs(self),' ', RefCount ]); try
|
|
{$ENDIF}
|
|
// Once the caller itself has no external refs (i.e. has only circular ones),
|
|
// the search can stop, as soon as any ONE circle with external has been found
|
|
Result := not FOnlyHasCircularNestedRefs;
|
|
if FInGetCircularRefCount or (NestedCount = 0) then
|
|
exit;
|
|
|
|
FInGetCircularRefCount := True;
|
|
try
|
|
for i := 0 to NestedCount - 1 do begin
|
|
n := Nested[i];
|
|
if (n is TRefCountedJitNestedReference) and
|
|
(TRefCountedJitNestedReference(n).FTag <> ATag) // not yet visited
|
|
then begin
|
|
if n = ARefObj then begin
|
|
Result := AnHasExternRefInCallPath;
|
|
end
|
|
else
|
|
if (TRefCountedJitNestedReference(n).FTag <> ATag) then // not yet visited
|
|
begin
|
|
TRefCountedJitNestedReference(n).FTag := ATag;
|
|
Result := TRefCountedJitNestedReference(n).HasExternalCircularRefCount(ARefObj, ATag,
|
|
AnHasExternRefInCallPath or not FOnlyHasCircularNestedRefs);
|
|
end;
|
|
if Result then
|
|
exit;
|
|
end;
|
|
end;
|
|
finally
|
|
FInGetCircularRefCount := False;
|
|
end;
|
|
{$IFDEF JIT_REFCNT_DEBUG}
|
|
finally debuglnExit(['< TRefCountedJitNestedReference.HasExternalCircularRefCount ', Result ]); end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TRefCountedJitNestedReference.GetCircularRefCount(
|
|
ARefObj: TRefCountedJitReference; ATag: QWord;
|
|
AnHasExternRefInCallPath: Boolean; out AnFoundExternRef: Boolean): integer;
|
|
var
|
|
i: Integer;
|
|
n: TRefCountedJitReference;
|
|
ExtFound: Boolean;
|
|
begin
|
|
{$IFDEF JIT_REFCNT_DEBUG}
|
|
debuglnEnter(['> GetCircularRefCount ',dbgs(Self) , ' //', RefCount, ' Only:', FOnlyHasCircularNestedRefs, ' nestcnt: ', NestedCount, ' ext in path: ', AnHasExternRefInCallPath]); try
|
|
{$ENDIF}
|
|
Result := 0;
|
|
AnHasExternRefInCallPath := AnHasExternRefInCallPath or not FOnlyHasCircularNestedRefs;
|
|
AnFoundExternRef := AnHasExternRefInCallPath;
|
|
|
|
if FInGetCircularRefCount or (NestedCount = 0) then
|
|
exit;
|
|
|
|
FInGetCircularRefCount := True;
|
|
try
|
|
for i := 0 to NestedCount - 1 do begin
|
|
n := Nested[i];
|
|
if (n is TRefCountedJitNestedReference) then begin
|
|
if n = ARefObj then begin
|
|
inc(Result);
|
|
if AnHasExternRefInCallPath then
|
|
AnFoundExternRef := True;
|
|
end
|
|
else
|
|
if (TRefCountedJitNestedReference(n).FTag <> ATag) then // not yet visited
|
|
begin
|
|
TRefCountedJitNestedReference(n).FTag := ATag;
|
|
Result := Result + TRefCountedJitNestedReference(n).GetCircularRefCount(ARefObj,
|
|
ATag,
|
|
AnHasExternRefInCallPath,
|
|
ExtFound
|
|
);
|
|
AnFoundExternRef := AnFoundExternRef or ExtFound;
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
FInGetCircularRefCount := False;
|
|
end;
|
|
{$IFDEF JIT_REFCNT_DEBUG}
|
|
finally debuglnExit(['< GetCircularRefCount ', Result, ' with ext: ', AnFoundExternRef ]); end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TRefCountedJitNestedReference.DoRefCountZero;
|
|
begin
|
|
inherited DoRefCountZero;
|
|
ClearList;
|
|
end;
|
|
|
|
procedure TRefCountedJitNestedReference.DoBeforeDecRefCount;
|
|
var
|
|
ExtFound: Boolean;
|
|
begin
|
|
{$IFDEF JIT_REFCNT_DEBUG}
|
|
debuglnEnter(['> DoBeforeDecRefCount ',dbgs(self),' refc ', RefCount, ' Only:', FOnlyHasCircularNestedRefs, ' nestcnt: ', NestedCount , ' SKIP ',FInGetCircularRefCount]); try
|
|
{$ENDIF}
|
|
if (RefCount = 1) or FInGetCircularRefCount then
|
|
exit;
|
|
{$PUSH}{$R-}
|
|
inc(FLastTag);
|
|
if FLastTag = 0 then
|
|
inc(FLastTag);
|
|
{$POP}
|
|
if FOnlyHasCircularNestedRefs then begin
|
|
ExtFound := HasExternalCircularRefCount(Self, FLastTag, False);
|
|
{$IFDEF JIT_REFCNT_DEBUG}
|
|
DebugLn(['has ext: ', ExtFound]);
|
|
{$ENDIF}
|
|
end
|
|
else
|
|
begin
|
|
FOnlyHasCircularNestedRefs := True; // pretend
|
|
FOnlyHasCircularNestedRefs := RefCount - 1 - GetCircularRefCount(Self, FLastTag, False, ExtFound) = 0;
|
|
{$IFDEF JIT_REFCNT_DEBUG}
|
|
DebugLn(['Only : ', FOnlyHasCircularNestedRefs, ' has ext: ', ExtFound]);
|
|
{$ENDIF}
|
|
end;
|
|
if not ExtFound then begin
|
|
// release all refs
|
|
FInGetCircularRefCount := True;
|
|
ClearList;
|
|
end;
|
|
{$IFDEF JIT_REFCNT_DEBUG}
|
|
finally debuglnExit(['< DoBeforeDecRefCount ', RefCount ]); end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TRefCountedJitNestedReference.NestedCount: integer;
|
|
begin
|
|
if FNestedReferrences = nil then
|
|
Result := 0
|
|
else
|
|
Result := FNestedReferrences.Count;
|
|
end;
|
|
|
|
function TRefCountedJitNestedReference.GetNested(AnIndex: integer
|
|
): TRefCountedJitReference;
|
|
begin
|
|
Result := FNestedReferrences[AnIndex];
|
|
end;
|
|
|
|
procedure TRefCountedJitNestedReference.AddToList(
|
|
AReference: TRefCountedJitReference);
|
|
begin
|
|
if AReference = nil then
|
|
exit;
|
|
if FNestedReferrences = nil then
|
|
FNestedReferrences := TRefCountedJitReferenceList.Create;
|
|
FNestedReferrences.Add(AReference);
|
|
end;
|
|
|
|
procedure TRefCountedJitNestedReference.RemoveFromList(
|
|
AReference: TRefCountedJitReference);
|
|
begin
|
|
if (FNestedReferrences = nil) or (AReference = nil) then
|
|
exit;
|
|
FNestedReferrences.Remove(AReference);
|
|
end;
|
|
|
|
procedure TRefCountedJitNestedReference.ClearList;
|
|
begin
|
|
if FNestedReferrences = nil then
|
|
exit;
|
|
FNestedReferrences.ClearList;
|
|
end;
|
|
|
|
destructor TRefCountedJitNestedReference.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
ClearList;
|
|
FNestedReferrences.Free;
|
|
end;
|
|
|
|
{ TFreeNotifyingObject }
|
|
|
|
destructor TFreeNotifyingObject.Destroy;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if FFreeNotificationList <> nil then begin
|
|
i := FFreeNotificationList.Count - 1;
|
|
while i >= 0 do begin
|
|
TNotifyEvent(FFreeNotificationList[i])(Self);
|
|
dec(i);
|
|
if i >= FFreeNotificationList.Count then
|
|
i := FFreeNotificationList.Count - 1;
|
|
end;
|
|
end;
|
|
inherited Destroy;
|
|
FreeAndNil(FFreeNotificationList);
|
|
end;
|
|
|
|
procedure TFreeNotifyingObject.AddFreeNotification(ANotification: TNotifyEvent);
|
|
begin
|
|
if FFreeNotificationList = nil then
|
|
FFreeNotificationList := TNotifyList.Create;
|
|
FFreeNotificationList.Add(TMethodComp(ANotification));
|
|
end;
|
|
|
|
procedure TFreeNotifyingObject.RemoveFreeNotification(ANotification: TNotifyEvent);
|
|
begin
|
|
if FFreeNotificationList = nil then
|
|
exit;
|
|
FFreeNotificationList.Remove(TMethodComp(ANotification));
|
|
end;
|
|
|
|
{ TReferenceAbleJitClass }
|
|
|
|
function TReferenceAbleJitClass.GetLockReferenceInc: TRefCountedJitReference;
|
|
begin
|
|
Result := GetLockReferenceObj;
|
|
if Result <> nil then
|
|
Result.IncRefCount;
|
|
end;
|
|
|
|
function TReferenceAbleJitClass.GetLockReferenceObj: TRefCountedJitReference;
|
|
begin
|
|
Result := nil;
|
|
end;
|
|
|
|
{ TJitType }
|
|
|
|
function TJitType.GetResolvedTypeName: String;
|
|
begin
|
|
Result := TypeName;
|
|
end;
|
|
|
|
procedure TJitType.DoTypeLibFreed(Sender: TObject);
|
|
begin
|
|
FTypeLibrary := Nil;
|
|
end;
|
|
|
|
procedure TJitType.SetTypeLibrary(AValue: TJitTypeLibrary);
|
|
begin
|
|
if FTypeLibrary = AValue then Exit;
|
|
|
|
if FTypeLibrary <> nil then
|
|
FTypeLibrary.RemoveFreeNotification(@DoTypeLibFreed);
|
|
|
|
FTypeLibrary := AValue;
|
|
|
|
if FTypeLibrary <> nil then
|
|
FTypeLibrary.AddFreeNotification(@DoTypeLibFreed);
|
|
end;
|
|
|
|
function TJitType.GetTypeInfo: PTypeInfo;
|
|
begin
|
|
Result := nil;
|
|
end;
|
|
|
|
function TJitType.IsConstTypeInfo: Boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
constructor TJitType.Create(ATypeName: String; ATypeLibrary: TJitTypeLibrary);
|
|
begin
|
|
FTypeName := ATypeName;
|
|
FTypeLibrary := ATypeLibrary;
|
|
if FTypeLibrary <> nil then
|
|
FTypeLibrary.AddFreeNotification(@DoTypeLibFreed);
|
|
inherited Create;
|
|
end;
|
|
|
|
constructor TJitType.Create(ATypeName, AUnitName: String;
|
|
ATypeLibrary: TJitTypeLibrary);
|
|
begin
|
|
Create(ATypeName, ATypeLibrary);
|
|
FUnitName := AUnitName;
|
|
end;
|
|
|
|
destructor TJitType.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
if FTypeLibrary <> nil then
|
|
FTypeLibrary.RemoveFreeNotification(@DoTypeLibFreed);
|
|
end;
|
|
|
|
{ TJitDeclarationParser }
|
|
|
|
procedure TJitDeclarationParser.SkipWhiteSpace;
|
|
begin
|
|
while DAT.FCurToken^ in [' ', #9, #10, #13] do
|
|
inc(DAT.FCurToken);
|
|
end;
|
|
|
|
constructor TJitDeclarationParser.Create(ATokenStart: PChar);
|
|
begin
|
|
FStartPos := ATokenStart;
|
|
DAT.FCurToken := ATokenStart;
|
|
DAT.FCurTokenLen := 0;
|
|
FCheckSectionEndProc := nil;
|
|
SavePosition;
|
|
Next;
|
|
end;
|
|
|
|
function TJitDeclarationParser.CurPos: Integer;
|
|
begin
|
|
Result := DAT.FCurToken - FStartPos;
|
|
end;
|
|
|
|
function TJitDeclarationParser.IsBeforeEOT: Boolean;
|
|
var
|
|
t: PChar;
|
|
begin
|
|
t := DAT.FCurToken;
|
|
inc(DAT.FCurToken, DAT.FCurTokenLen);
|
|
SkipWhiteSpace;
|
|
Result := (DAT.FCurToken^ = #0);
|
|
DAT.FCurToken := t;
|
|
end;
|
|
|
|
function TJitDeclarationParser.IsBeforeEotOrSectionEnd: Boolean;
|
|
var
|
|
t: PChar;
|
|
begin
|
|
t := DAT.FCurToken;
|
|
inc(DAT.FCurToken, DAT.FCurTokenLen);
|
|
SkipWhiteSpace;
|
|
Result := (DAT.FCurToken^ = #0) or
|
|
(DAT.FCurToken^ = ';') or
|
|
( (DAT.FCurToken[0] in ['e', 'E']) and
|
|
(DAT.FCurToken[1] in ['n', 'N']) and
|
|
(DAT.FCurToken[2] in ['d', 'D']) and
|
|
(DAT.FCurToken[3] in [#0, ';', ' ', #9, #10, #13])
|
|
);
|
|
DAT.FCurToken := t;
|
|
if (not Result) and Assigned(FCheckSectionEndProc) then
|
|
Result := FCheckSectionEndProc(@Self);
|
|
end;
|
|
|
|
function TJitDeclarationParser.Next(AContext: TParseContext): TJitParserTkKind;
|
|
var NxtTok: PChar;
|
|
procedure SetResult(ATokenKind: TJitParserTkKind; ALen: Integer = 1);
|
|
begin
|
|
DAT.FCurTokenKind := ATokenKind;
|
|
Inc(NxtTok, ALen);
|
|
end;
|
|
procedure SetResultForSet(ATokenKind: TJitParserTkKind; const cs: TSysCharset; ASkip: Integer = 0);
|
|
begin
|
|
DAT.FCurTokenKind := ATokenKind;
|
|
NxtTok := NxtTok + ASkip;
|
|
while (NxtTok^ in cs) do
|
|
Inc(NxtTok);
|
|
end;
|
|
begin
|
|
inc(DAT.FCurToken, DAT.FCurTokenLen);
|
|
DAT.FCurTokenLen := 0;
|
|
SkipWhiteSpace;
|
|
if IsBeforeEOT then begin
|
|
DAT.FCurTokenLen := 0;
|
|
DAT.FCurTokenKind := ptEOT;
|
|
Result := ptEOT;
|
|
exit;
|
|
end;
|
|
|
|
DAT.FCurTokenKind := ptError;
|
|
NxtTok := DAT.FCurToken;
|
|
case DAT.FCurToken^ of
|
|
'a'..'z', 'A'..'Z', '_': begin
|
|
SetResultForSet(ptIdent, ['a'..'z', 'A'..'Z', '_', '0'..'9']);
|
|
case NxtTok - DAT.FCurToken of
|
|
2: if (AContext = pcOfExpexted) and
|
|
(strlicomp('of', DAT.FCurToken, 2) = 0) then DAT.FCurTokenKind := kwOf;
|
|
3: case DAT.FCurToken^ of
|
|
's', 'S': if strlicomp('set', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwSet;
|
|
'v', 'V': if strlicomp('var', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwVar;
|
|
'e', 'E': if strlicomp('end', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwEnd;
|
|
end;
|
|
5: case DAT.FCurToken^ of
|
|
'c', 'C': if strlicomp('const', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwConst
|
|
else
|
|
if strlicomp('class', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwClass;
|
|
'a', 'A': if strlicomp('array', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwArray;
|
|
end;
|
|
6: case DAT.FCurToken^ of
|
|
'r', 'R': if strlicomp('record', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwRecord;
|
|
's', 'S': if strlicomp('string', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwString;
|
|
'p', 'P': if strlicomp('public', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwPublic;
|
|
'o', 'O': if strlicomp('object', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwObject;
|
|
end;
|
|
7: case DAT.FCurToken^ of
|
|
'p', 'P': if strlicomp('private', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwPrivate;
|
|
end;
|
|
8: case DAT.FCurToken^ of
|
|
'c', 'C': if (AContext = pcFuncParamStart) and
|
|
(strlicomp('constref', DAT.FCurToken, 3) = 0) then DAT.FCurTokenKind := kwConstRef; // not a keyword / needs contexct
|
|
'p', 'P': if strlicomp('property', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwProperty;
|
|
'f', 'F': if strlicomp('function', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwFunction;
|
|
end;
|
|
9: case DAT.FCurToken[3] of
|
|
'c', 'C': if strlicomp('procedure', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwProcedure;
|
|
't', 'T': if strlicomp('protected', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwProcedure;
|
|
'l', 'L': if strlicomp('published', DAT.FCurToken, 3) = 0 then DAT.FCurTokenKind := kwPublished;
|
|
end;
|
|
end;
|
|
end;
|
|
'0'..'9': SetResultForSet(ptNum, ['0'..'9']);
|
|
'$': SetResultForSet(ptNum, ['0'..'9', 'a'..'f', 'A'..'F'], 1);
|
|
'&': if DAT.FCurToken[1] in ['0'..'7'] then
|
|
SetResultForSet(ptNum, ['0'..'7'], 1)
|
|
else begin
|
|
SetResultForSet(ptIdent, ['a'..'z', 'A'..'Z', '_', '0'..'9'], 1); // escaped ident
|
|
end;
|
|
'%': SetResultForSet(ptNum, ['0'..'1'], 1);
|
|
'-','+': case DAT.FCurToken[1] of
|
|
'0'..'9': SetResultForSet(ptNum, ['0'..'9'], 1);
|
|
'$': SetResultForSet(ptNum, ['0'..'9', 'a'..'f', 'A'..'F'], 2);
|
|
'&': SetResultForSet(ptNum, ['0'..'7'], 2);
|
|
'%': SetResultForSet(ptNum, ['0'..'1'], 2);
|
|
//else // error / return empty
|
|
end;
|
|
'.': if DAT.FCurToken[1] = '.' then
|
|
SetResult(ptDotDot, 2)
|
|
else
|
|
SetResult(ptDot);
|
|
'#': case DAT.FCurToken[1] of
|
|
'0'..'9': SetResultForSet(ptChar, ['0'..'9'], 1);
|
|
'$': SetResultForSet(ptChar, ['0'..'9', 'a'..'f', 'A'..'F'], 2);
|
|
'&': SetResultForSet(ptChar, ['0'..'7'], 2);
|
|
'%': SetResultForSet(ptChar, ['0'..'1'], 2);
|
|
//else // error / return empty
|
|
end;
|
|
'''': if (DAT.FCurToken[1] = '''') then
|
|
SetResult(ptEmptyString, 2)
|
|
else
|
|
if (DAT.FCurToken[1] <> #0) and (DAT.FCurToken[2] = '''') then
|
|
SetResult(ptChar, 3);
|
|
'(': SetResult(ptRoundOpen);
|
|
')': SetResult(ptRoundClose);
|
|
'[': SetResult(ptSquareOpen);
|
|
']': SetResult(ptSquareClose);
|
|
',': SetResult(ptComma);
|
|
':': SetResult(ptColon);
|
|
';': SetResult(ptSemicolon);
|
|
'=': SetResult(ptEqual);
|
|
//else // error / return empty // only chars => subset of char
|
|
else begin
|
|
// error
|
|
DAT.FCurTokenKind := ptSymbol;
|
|
inc(NxtTok);
|
|
end;
|
|
end;
|
|
DAT.FCurTokenLen := NxtTok - DAT.FCurToken;
|
|
Result := DAT.FCurTokenKind;
|
|
end;
|
|
|
|
function TJitDeclarationParser.CurrentToken: String;
|
|
var
|
|
t: PChar;
|
|
l: Integer;
|
|
begin
|
|
case DAT.FCurTokenKind of
|
|
ptNone, ptError, ptEOT: Result := '';
|
|
ptEmptyString: Result := '''';
|
|
ptChar: case DAT.FCurToken^ of
|
|
'#': begin
|
|
SetLength(Result, DAT.FCurTokenLen - 1);
|
|
if DAT.FCurTokenLen > 1 then
|
|
move((DAT.FCurToken+1)^, Result[1], DAT.FCurTokenLen - 1);
|
|
Result := char(StrToInt(Result));
|
|
end;
|
|
'''': Result := (DAT.FCurToken + 1)^;
|
|
else raise JitTypeParserExceptionSyntaxError.Create(CurPos, '', 'Internal parser error');
|
|
end;
|
|
else begin
|
|
t := DAT.FCurToken;
|
|
l := DAT.FCurTokenLen;
|
|
if (DAT.FCurTokenKind = ptIdent) and (t^ = '&') then begin
|
|
inc(t);
|
|
dec(l);
|
|
end;
|
|
SetLength(Result, DAT.FCurTokenLen);
|
|
if DAT.FCurTokenLen > 0 then
|
|
move(DAT.FCurToken^, Result[1], DAT.FCurTokenLen);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TJitDeclarationParser.CurrentTokenAsNum(out IsQWord: Boolean): Int64;
|
|
var
|
|
s: String;
|
|
begin
|
|
if DAT.FCurTokenKind <> ptNum then
|
|
raise JitTypeParserExceptionSyntaxError.Create(CurPos, CurrentToken, 'Number expected');
|
|
|
|
IsQWord := False;
|
|
s := CurrentToken;
|
|
if not TryStrToInt64(s, Result) then
|
|
if TryStrToQWord(s, QWord(Result)) then
|
|
IsQWord := True
|
|
else
|
|
raise JitTypeParserExceptionSyntaxError.Create(CurPos, CurrentToken, 'Number expected');
|
|
end;
|
|
|
|
function TJitDeclarationParser.CurrentTokenRaw: String;
|
|
begin
|
|
case DAT.FCurTokenKind of
|
|
ptNone, ptError, ptEOT: Result := '';
|
|
ptEmptyString: Result := '''';
|
|
else begin
|
|
SetLength(Result, DAT.FCurTokenLen);
|
|
if DAT.FCurTokenLen > 0 then
|
|
move(DAT.FCurToken^, Result[1], DAT.FCurTokenLen);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TJitDeclarationParser.NextToken: String;
|
|
begin
|
|
Next;
|
|
Result := CurrentTokenRaw;
|
|
end;
|
|
|
|
function TJitDeclarationParser.PeekTokenRaw: String;
|
|
var
|
|
t: TJitDeclarationParserData;
|
|
begin
|
|
t := DAT;
|
|
Result := NextToken;
|
|
DAT := t;
|
|
end;
|
|
|
|
function TJitDeclarationParser.PeekKind(AContext: TParseContext
|
|
): TJitParserTkKind;
|
|
var
|
|
t: TJitDeclarationParserData;
|
|
begin
|
|
t := DAT;
|
|
Result := Next(AContext);
|
|
DAT := t;
|
|
end;
|
|
|
|
procedure TJitDeclarationParser.GetQualifiedIdent(out APath, AnIdent: String);
|
|
var
|
|
ct: PChar;
|
|
begin
|
|
APath := '';
|
|
AnIdent := '';
|
|
if CurrentKind <> ptIdent then
|
|
raise JitTypeParserExceptionSyntaxError.Create(CurPos, CurrentToken, 'Identifier expected');
|
|
|
|
ct := DAT.FCurToken;
|
|
AnIdent := CurrentToken;
|
|
while ((DAT.FCurToken + DAT.FCurTokenLen)^ = '.') and
|
|
( ((DAT.FCurToken + DAT.FCurTokenLen + 1)^ in ['a'..'z', 'A'..'Z', '_']) or
|
|
((DAT.FCurToken + DAT.FCurTokenLen + 1)^ = '&') and
|
|
((DAT.FCurToken + DAT.FCurTokenLen + 2)^ in ['a'..'z', 'A'..'Z', '_'])
|
|
)
|
|
do begin
|
|
if APath <> '' then
|
|
APath := APath + '.';
|
|
APath := APath + AnIdent;
|
|
Next;
|
|
Next;
|
|
AnIdent := CurrentToken;
|
|
end;
|
|
|
|
DAT.FCurTokenLen := DAT.FCurToken - ct + DAT.FCurTokenLen;
|
|
DAT.FCurToken := ct;
|
|
end;
|
|
|
|
procedure TJitDeclarationParser.GetQualifiedIdent(out APath, AnIdent1,
|
|
AnIdent2: String);
|
|
var
|
|
ct: PChar;
|
|
begin
|
|
APath := '';
|
|
AnIdent1 := '';
|
|
AnIdent2 := '';
|
|
if CurrentKind <> ptIdent then
|
|
raise JitTypeParserExceptionSyntaxError.Create(CurPos, CurrentToken, 'Identifier expected');
|
|
|
|
ct := DAT.FCurToken;
|
|
AnIdent2 := CurrentToken;
|
|
while ((DAT.FCurToken + DAT.FCurTokenLen)^ = '.') and
|
|
( ((DAT.FCurToken + DAT.FCurTokenLen + 1)^ in ['a'..'z', 'A'..'Z', '_']) or
|
|
((DAT.FCurToken + DAT.FCurTokenLen + 1)^ = '&') and
|
|
((DAT.FCurToken + DAT.FCurTokenLen + 2)^ in ['a'..'z', 'A'..'Z', '_'])
|
|
)
|
|
do begin
|
|
if (APath <> '') and (AnIdent1 <> '') then
|
|
APath := APath + '.';
|
|
APath := APath + AnIdent1;
|
|
AnIdent1 := AnIdent2;
|
|
Next;
|
|
Next;
|
|
AnIdent2 := CurrentToken;
|
|
end;
|
|
|
|
DAT.FCurTokenLen := DAT.FCurToken - ct + DAT.FCurTokenLen;
|
|
DAT.FCurToken := ct;
|
|
end;
|
|
|
|
procedure TJitDeclarationParser.SavePosition;
|
|
begin
|
|
FSavePoint := DAT;
|
|
end;
|
|
|
|
procedure TJitDeclarationParser.RestorePosition;
|
|
begin
|
|
DAT := FSavePoint;
|
|
end;
|
|
|
|
{ TJitTypeInfo.TRefCountedTypeInfo }
|
|
|
|
function TJitTypeInfo.TRefCountedTypeInfo.IndexOf(ATypeInfo: PTypeInfo): integer;
|
|
begin
|
|
if NestedReferrences = nil then
|
|
exit;
|
|
Result := NestedReferrences.Count - 1;
|
|
while Result >= 0 do begin
|
|
if (NestedReferrences[Result] is TRefCountedTypeInfo) and
|
|
(TRefCountedTypeInfo(NestedReferrences[Result]).TypeInfo = ATypeInfo)
|
|
then
|
|
exit;
|
|
dec(Result);
|
|
end;
|
|
end;
|
|
|
|
procedure TJitTypeInfo.TRefCountedTypeInfo.AddToList(ATypeInfo: TJitType);
|
|
begin
|
|
if ATypeInfo <> nil then
|
|
AddToList(ATypeInfo.LockReference);
|
|
end;
|
|
|
|
procedure TJitTypeInfo.TRefCountedTypeInfo.RemoveFromList(ATypeInfo: PTypeInfo);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if NestedReferrences = nil then
|
|
exit;
|
|
i := IndexOf(ATypeInfo);
|
|
if i < 0 then
|
|
exit;
|
|
NestedReferrences[i].ReleaseLock;
|
|
NestedReferrences.Delete(i);
|
|
end;
|
|
|
|
procedure TJitTypeInfo.TRefCountedTypeInfo.SetTypeInfo(ATypeInfo: PTypeInfo);
|
|
begin
|
|
if (RefCount > 1) and (FTypeInfo <> nil) then
|
|
raise Exception.Create('set TypeInfo while referrenced');
|
|
|
|
if (FTypeInfo <> nil) then begin
|
|
ClearList;
|
|
Freemem(FTypeInfo);
|
|
end;
|
|
|
|
FTypeInfo := ATypeInfo;
|
|
end;
|
|
|
|
procedure TJitTypeInfo.TRefCountedTypeInfo.DoRefCountZero;
|
|
begin
|
|
inherited DoRefCountZero;
|
|
if FTypeInfo <> nil then begin
|
|
Freemem(FTypeInfo);
|
|
FTypeInfo := nil;
|
|
end;
|
|
end;
|
|
|
|
constructor TJitTypeInfo.TRefCountedTypeInfo.Create(ATypeInfo: PTypeInfo);
|
|
begin
|
|
inherited Create;
|
|
FTypeInfo := ATypeInfo;
|
|
end;
|
|
|
|
{ TJitTypeInfo }
|
|
|
|
function TJitTypeInfo.GetTypeInfo: PTypeInfo;
|
|
begin
|
|
if FTypeInfo = nil then
|
|
ParseFromDeclaration;
|
|
Result := FTypeInfo;
|
|
end;
|
|
|
|
function TJitTypeInfo.GetLockReferenceObj: TRefCountedJitReference;
|
|
begin
|
|
Result := RefCountedTypeInfo;
|
|
end;
|
|
|
|
function TJitTypeInfo.IsConstTypeInfo: Boolean;
|
|
begin
|
|
Result := FIsConstTypeInfo;
|
|
end;
|
|
|
|
procedure TJitTypeInfo.SetFTypeInfo(AValue: PTypeInfo);
|
|
begin
|
|
if FTypeInfo = AValue then
|
|
Exit;
|
|
if (FTypeInfo <> nil) and (FRefCountedTypeInfo = nil) then
|
|
Freemem(FTypeInfo);
|
|
FTypeInfo := AValue;
|
|
FIsConstTypeInfo := False;
|
|
if FRefCountedTypeInfo <> nil then begin
|
|
if (FRefCountedTypeInfo.RefCount = 1) or (FRefCountedTypeInfo.FTypeInfo = nil) then
|
|
FRefCountedTypeInfo.SetTypeInfo(AValue)
|
|
else begin
|
|
FRefCountedTypeInfo.ReleaseLock;
|
|
FRefCountedTypeInfo := TRefCountedTypeInfo.Create(AValue);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJitTypeInfo.SetConstFTypeInfo(AValue: PTypeInfo);
|
|
begin
|
|
assert((AValue = nil) or (FTypeInfo <> AValue) or (FRefCountedTypeInfo = nil), 'TJitTypeInfo.SetConstFTypeInfo: (AValue = nil) or (FTypeInfo <> AValue) or (FRefCountedTypeInfo = nil)');
|
|
if FTypeInfo = AValue then
|
|
Exit;
|
|
if (FTypeInfo <> nil) and (FRefCountedTypeInfo = nil) then
|
|
Freemem(FTypeInfo);
|
|
FTypeInfo := AValue;
|
|
FIsConstTypeInfo := True;
|
|
if FRefCountedTypeInfo <> nil then begin
|
|
FRefCountedTypeInfo.ReleaseLock;
|
|
FRefCountedTypeInfo := Nil;
|
|
end;
|
|
end;
|
|
|
|
function TJitTypeInfo.RefCountedTypeInfo: TRefCountedTypeInfo;
|
|
begin
|
|
if FRefCountedTypeInfo = nil then begin
|
|
(* FTypeInfo may be nil, but a refernce can be got anyway *)
|
|
if FIsConstTypeInfo then
|
|
FRefCountedTypeInfo := TRefCountedTypeInfo.Create(nil)
|
|
else
|
|
FRefCountedTypeInfo := TRefCountedTypeInfo.Create(FTypeInfo);
|
|
end;
|
|
Result := FRefCountedTypeInfo;
|
|
end;
|
|
|
|
procedure TJitTypeInfo.ParseFromDeclaration(AParser: PJitDeclarationParser);
|
|
var
|
|
Decl: String;
|
|
TheTokenParser: TJitDeclarationParser;
|
|
TokenParser: PJitDeclarationParser;
|
|
|
|
function IsBeforeEOT(AnIncludeSoftEOT: Boolean = False): Boolean;
|
|
begin
|
|
if AnIncludeSoftEOT then
|
|
Result := TokenParser^.IsBeforeEotOrSectionEnd
|
|
else
|
|
Result := TokenParser^.IsBeforeEOT;
|
|
end;
|
|
|
|
procedure AssertIsIdent(tk: TJitParserTkKind; s: string = '');
|
|
begin
|
|
if tk <> ptIdent then begin
|
|
if s = '' then s := TokenParser^.CurrentToken;
|
|
raise JitTypeParserExceptionSyntaxError.Create(TokenParser^.CurPos, s, 'Identifier expected');
|
|
end;
|
|
end;
|
|
procedure AssertExpectedToken(tk: TJitParserTkKind; AExp: TJitParserTkKinds; s: string = '');
|
|
var
|
|
e, b: String;
|
|
a: TJitParserTkKind;
|
|
begin
|
|
if not (tk in AExp) then begin
|
|
if s = '' then s := TokenParser^.CurrentToken;
|
|
e := '';
|
|
for a in TJitParserTkKind do
|
|
if a in AExp then begin
|
|
WriteStr(b, a);
|
|
e := e + ', ' + copy(b, 3, Length(b));
|
|
end;
|
|
delete(e, 1, 2);
|
|
raise JitTypeParserExceptionSyntaxError.Create(TokenParser^.CurPos, s, 'Unexpected token, expected ' + e);
|
|
end;
|
|
end;
|
|
procedure AssertIsBeforeEOT(AnIncludeSoftEOT: Boolean = False);
|
|
begin
|
|
if not IsBeforeEOT(AnIncludeSoftEOT) then
|
|
raise JitTypeParserExceptionSyntaxError.Create(TokenParser^.CurPos, TokenParser^.CurrentToken, 'End of declaration expected');
|
|
end;
|
|
procedure AssertFoundTypeInfo(const t: PTypeInfo; const s: string);
|
|
begin
|
|
if t = nil then
|
|
raise JitTypeParserExceptionTypeNotFound.Create(TokenParser^.CurPos, s, 'No typeinfo for: ');
|
|
end;
|
|
|
|
var
|
|
LastIntTypeInfoFound: TJitType;
|
|
function FindTypeInfo(n: String; AddToRefList: Boolean; AnUnitName: String = '';
|
|
ASearchOptions: TTypeSearchOptions = []): PTypeInfo;
|
|
var
|
|
jt: TJitType;
|
|
begin
|
|
LastIntTypeInfoFound := nil;
|
|
if (AnUnitName = '') and (ASearchOptions = []) then
|
|
AnUnitName := FUnitName;
|
|
if TypeLibrary <> nil then begin
|
|
jt := TypeLibrary.FindType(n, AnUnitName, ASearchOptions);
|
|
if (jt = nil) and (n <> '') and (n[1] = '&') then begin
|
|
delete(n, 1, 1);
|
|
jt := TypeLibrary.FindType(n, AnUnitName, ASearchOptions);
|
|
end;
|
|
if jt <> nil then begin
|
|
Result := jt.TypeInfo;
|
|
AssertFoundTypeInfo(Result, n);
|
|
LastIntTypeInfoFound := jt;
|
|
if AddToRefList then
|
|
RefCountedTypeInfo.AddToList(jt);
|
|
exit;
|
|
end;
|
|
end;
|
|
if (n <> '') and (n[1] = '&') then
|
|
delete(n, 1, 1);
|
|
Result := TypeInfoByName(n);
|
|
end;
|
|
|
|
function GetIdentList(out AList: TStringArray; const ASeparator: TJitParserTkKinds): TJitParserTkKind;
|
|
var
|
|
l: Integer;
|
|
begin
|
|
l := 0;
|
|
AList := nil;
|
|
SetLength(AList, 10);
|
|
repeat
|
|
if l >= Length(AList) then
|
|
SetLength(AList, l + 10);
|
|
AssertIsIdent(TokenParser^.Next);
|
|
AList[l] := TokenParser^.CurrentToken;
|
|
inc(l);
|
|
Result := TokenParser^.Next;
|
|
until not(Result in ASeparator);
|
|
SetLength(AList, l);
|
|
end;
|
|
|
|
procedure DoEnum;
|
|
var
|
|
Elems: TStringArray;
|
|
s, un: String;
|
|
MemSize: Integer;
|
|
RttiWriter: TJitRttiWriterTkEnum;
|
|
tk: TJitParserTkKind;
|
|
begin
|
|
tk := GetIdentList(Elems, [ptComma]);
|
|
AssertExpectedToken(tk, [ptRoundClose]);
|
|
AssertIsBeforeEOT(AParser <> nil);
|
|
|
|
MemSize := TJitRttiWriterTypeInfo.NewSizeFor(FTypeName, tkEnumeration);
|
|
for s in Elems do
|
|
TJitRttiWriterTkEnum.AddSizeForElemName(MemSize, s);
|
|
un := FUnitName;
|
|
if un = '' then un := '$u'; // must write some unitname
|
|
TJitRttiWriterTkEnum.AddSizeForElemName(MemSize, un);
|
|
TJitRttiWriterTkEnum.FinishSize(MemSize);
|
|
|
|
SetFTypeInfo(AllocMem(MemSize));
|
|
RttiWriter := TJitRttiWriterTkEnum.Create(FTypeInfo, FTypeName, Length(Elems));
|
|
|
|
for s in Elems do
|
|
RttiWriter.WriteEnumElemName(s);
|
|
RttiWriter.WriteUnitName(un);
|
|
assert(RttiWriter.CurDestMemPos<=Pointer(FTypeInfo)+MemSize, 'DoEnum: RttiWriter.CurDestMemPos<=Pointer(FTypeInfo)+MemSize');
|
|
RttiWriter.Free;
|
|
end;
|
|
|
|
procedure DoProcedure(AnIsFunction: Boolean);
|
|
const
|
|
FORMAL_PARAM_NAME = '$formal';
|
|
function TNameToTInfo(tn, tu: string; var rmem: Pointer): TypeInfoPtr; inline;
|
|
var t: PTypeInfo;
|
|
begin
|
|
if (tn = '') or (tn = FORMAL_PARAM_NAME) then
|
|
exit(nil);
|
|
|
|
if tu <> '' then
|
|
t := FindTypeInfo(tn, True, tu, [tsoOnlyUnit])
|
|
else
|
|
t := FindTypeInfo(tn, True, FUnitName);
|
|
AssertFoundTypeInfo(t, tn);
|
|
Result := PTypeInfoToTypeInfoPtr(t, rmem);
|
|
end;
|
|
type
|
|
TFuncParam = record
|
|
Flags: TParamFlags;
|
|
NameList: TStringArray;
|
|
TypeName, TypeUnit: String;
|
|
end;
|
|
var
|
|
ParamList: array of TFuncParam;
|
|
CurParams: ^TFuncParam;
|
|
ResTypeName, ResTypeUnit, s, s2: String;
|
|
ti_int64: PTypeInfo;
|
|
HasName, OfObject, HasOpenArray: Boolean;
|
|
Flags: TParamFlags;
|
|
ParamListCnt, ParamCnt, MemSize, PtrRedirectMemSize: Integer;
|
|
param: TFuncParam;
|
|
PtrRedirectMem: Pointer;
|
|
RttiWriterMeth: TJitRttiWriterTkMethod;
|
|
RttiWriterProc: TJitRttiWriterTkProcVar;
|
|
tk: TJitParserTkKind;
|
|
begin
|
|
HasName := False;
|
|
OfObject := pfAlwaysAsMethod in FParseFlags;
|
|
ParamListCnt := 0;
|
|
ParamCnt := 0;
|
|
HasOpenArray := False;
|
|
|
|
tk := TokenParser^.PeekKind;
|
|
if (pfAllowProcName in FParseFlags) and (tk = ptIdent) then begin
|
|
TokenParser^.Next;
|
|
TokenParser^.GetQualifiedIdent(s, s2);
|
|
HasName := True;
|
|
OfObject := s <> '';
|
|
|
|
tk := TokenParser^.PeekKind;
|
|
end;
|
|
|
|
if tk = ptRoundOpen then begin
|
|
// read arguments
|
|
TokenParser^.Next;
|
|
tk := TokenParser^.PeekKind;
|
|
if (tk <> ptRoundClose) then begin
|
|
repeat
|
|
Flags := [];
|
|
tk := TokenParser^.PeekKind(pcFuncParamStart);
|
|
case tk of
|
|
kwVar: Flags := Flags + [pfVar];
|
|
kwOut: Flags := Flags + [pfOut];
|
|
kwConst: Flags := Flags + [pfConst];
|
|
kwConstRef: Flags := Flags + [pfConstRef];
|
|
end;
|
|
if Flags <> [] then
|
|
TokenParser^.Next;
|
|
|
|
SetLength(ParamList, ParamListCnt + 1);
|
|
CurParams := @ParamList[ParamListCnt];
|
|
CurParams^.Flags := Flags;
|
|
tk := GetIdentList(CurParams^.NameList, [ptComma]);
|
|
inc(ParamCnt, Length(CurParams^.NameList));
|
|
|
|
if tk = ptColon then begin
|
|
tk := TokenParser^.Next;
|
|
if tk = kwArray then begin
|
|
AssertExpectedToken(TokenParser^.Next(pcOfExpexted), [kwOf]);
|
|
HasOpenArray := True;
|
|
CurParams^.Flags := CurParams^.Flags + [pfArray, pfReference];
|
|
inc(ParamCnt, Length(CurParams^.NameList));
|
|
TokenParser^.Next;
|
|
// TODO: if s = 'const' // array of const => array of TVarRec
|
|
end;
|
|
TokenParser^.GetQualifiedIdent(CurParams^.TypeUnit, CurParams^.TypeName);
|
|
tk := TokenParser^.Next;
|
|
if tk = ptEqual then // default value
|
|
while not (tk in [ptSemicolon, ptRoundClose, ptEOT]) do
|
|
tk := TokenParser^.Next;
|
|
end
|
|
else begin
|
|
CurParams^.TypeName := FORMAL_PARAM_NAME;
|
|
CurParams^.TypeUnit := '';
|
|
end;
|
|
|
|
// else untyped param
|
|
inc(ParamListCnt);
|
|
until tk <> ptSemicolon;
|
|
AssertExpectedToken(TokenParser^.CurrentKind, [ptRoundClose]);
|
|
end
|
|
else
|
|
TokenParser^.Next; // get the ')' from TokenParser^.PeekKind;
|
|
end;
|
|
|
|
if AnIsFunction then begin
|
|
AssertExpectedToken(TokenParser^.Next, [ptColon]);
|
|
TokenParser^.Next;
|
|
TokenParser^.GetQualifiedIdent(ResTypeUnit, ResTypeName);
|
|
end
|
|
else begin
|
|
ResTypeName := '';
|
|
ResTypeUnit := '';
|
|
end;
|
|
|
|
tk := TokenParser^.PeekKind(pcOfExpexted);
|
|
if (not HasName) and (tk = kwOf) then begin
|
|
TokenParser^.Next;
|
|
AssertExpectedToken(TokenParser^.Next, [kwObject]);
|
|
OfObject := True;
|
|
tk := TokenParser^.PeekKind;
|
|
end;
|
|
if tk = ptSemicolon then begin
|
|
// TODO: check cdecl....
|
|
end;
|
|
AssertIsBeforeEOT(AParser <> nil);
|
|
|
|
|
|
if HasOpenArray then begin
|
|
ti_int64 := system.TypeInfo(Int64);
|
|
AssertFoundTypeInfo(ti_int64, 'int64');
|
|
end;
|
|
|
|
if OfObject then begin
|
|
// write tkMethod
|
|
inc(ParamCnt); // $self
|
|
MemSize := TJitRttiWriterTypeInfo.NewSizeFor(FTypeName, tkMethod);
|
|
PtrRedirectMemSize := 0;
|
|
TJitRttiWriterTkMethod.AddSizeForMethodField(MemSize, PtrRedirectMemSize, '$self', 'Pointer');
|
|
for param in ParamList do
|
|
for s in param.NameList do begin
|
|
TJitRttiWriterTkMethod.AddSizeForMethodField(MemSize, PtrRedirectMemSize, s, param.TypeName);
|
|
if pfArray in param.Flags then
|
|
TJitRttiWriterTkMethod.AddSizeForMethodField(MemSize, PtrRedirectMemSize, '$high'+s, 'Int64');
|
|
// TODO: '$formal' param do not need PtrRedirectMemSize
|
|
end;
|
|
if AnIsFunction then
|
|
TJitRttiWriterTkMethod.AddSizeForMethodResult(MemSize, PtrRedirectMemSize, ResTypeName);
|
|
|
|
TJitRttiWriterTkMethod.AddSizeForCallingConv(MemSize);
|
|
TJitRttiWriterTkMethod.FinalizeSizeForMethodTypeInfo(MemSize);
|
|
if PtrRedirectMemSize <> 0 then
|
|
MemSize := aligntoptr(MemSize); // for the start of PtrRedirectMemSize
|
|
|
|
SetFTypeInfo(AllocMem(MemSize + PtrRedirectMemSize));
|
|
PtrRedirectMem := aligntoptr(Pointer(FTypeInfo) + MemSize);
|
|
|
|
if AnIsFunction then
|
|
RttiWriterMeth := TJitRttiWriterTkMethod.Create(FTypeInfo, FTypeName, mkFunction, ParamCnt)
|
|
else
|
|
RttiWriterMeth := TJitRttiWriterTkMethod.Create(FTypeInfo, FTypeName, mkProcedure, ParamCnt);
|
|
|
|
try try
|
|
RttiWriterMeth.WriteParamInfo('$self', 'Pointer', [pfHidden, pfSelf]);
|
|
// Params: Flags, ParamName, TypeName
|
|
for param in ParamList do
|
|
for s in param.NameList do begin
|
|
RttiWriterMeth.WriteParamInfo(s, param.TypeName, param.Flags);
|
|
if pfArray in param.Flags then
|
|
RttiWriterMeth.WriteParamInfo('$high'+s, 'Int64', [pfHidden, pfHigh, pfConst]);
|
|
end;
|
|
if AnIsFunction then begin
|
|
RttiWriterMeth.WriteResultInfo(ResTypeName, TNameToTInfo(ResTypeName, ResTypeUnit, PtrRedirectMem));
|
|
end;
|
|
RttiWriterMeth.WriteParamCallConv(ccReg);
|
|
// Params: TypeInfo
|
|
RttiWriterMeth.WriteParamType(PTypeInfoToTypeInfoPtr(system.TypeInfo(Pointer), PtrRedirectMem));
|
|
for param in ParamList do begin
|
|
for s in param.NameList do begin
|
|
RttiWriterMeth.WriteParamType(TNameToTInfo(param.TypeName, param.TypeUnit, PtrRedirectMem));
|
|
if pfArray in param.Flags then
|
|
// TODO: int64 redirection can be created once and for all
|
|
RttiWriterMeth.WriteParamType(PTypeInfoToTypeInfoPtr(ti_int64, PtrRedirectMem));
|
|
end;
|
|
end;
|
|
assert(RttiWriterMeth.CurDestMemPos<=Pointer(FTypeInfo)+MemSize, 'DoProcedure: RttiWriterMeth.CurDestMemPos<=Pointer(FTypeInfo)+MemSize');
|
|
except
|
|
//TODO: at this point, any type that got our typeinfo during recursion, has an invalid typeinfo
|
|
SetFTypeInfo(nil);
|
|
raise;
|
|
end
|
|
finally
|
|
RttiWriterMeth.Free;
|
|
end;
|
|
end
|
|
|
|
else begin
|
|
// write tkProcVar
|
|
MemSize := TJitRttiWriterTypeInfo.NewSizeFor(FTypeName, tkProcVar);
|
|
if AnIsFunction then
|
|
PtrRedirectMemSize := SIZE_OF_TYPEINFO_PPOINTER
|
|
else
|
|
PtrRedirectMemSize := 0;
|
|
for param in ParamList do
|
|
for s in param.NameList do begin
|
|
TJitRttiWriterTkProcVar.AddSizeForProcVarField(MemSize, PtrRedirectMemSize, s);
|
|
if pfArray in param.Flags then
|
|
TJitRttiWriterTkProcVar.AddSizeForProcVarField(MemSize, PtrRedirectMemSize, '$high'+s);
|
|
end;
|
|
|
|
if PtrRedirectMemSize <> 0 then
|
|
MemSize := aligntoptr(MemSize);
|
|
SetFTypeInfo(AllocMem(MemSize + PtrRedirectMemSize));
|
|
PtrRedirectMem := Pointer(FTypeInfo) + MemSize;
|
|
|
|
RttiWriterProc := TJitRttiWriterTkProcVar.Create(FTypeInfo, FTypeName,
|
|
0, ccReg, TNameToTInfo(ResTypeName, ResTypeUnit, PtrRedirectMem), ParamCnt);
|
|
|
|
try try
|
|
for param in ParamList do begin
|
|
for s in param.NameList do begin
|
|
// TODO: keep a referenc => internal type-lib FInternalTypeLib
|
|
RttiWriterProc.WriteProcedureParam(s, TNameToTInfo(param.TypeName, param.TypeUnit, PtrRedirectMem), param.Flags);
|
|
if pfArray in param.Flags then
|
|
RttiWriterProc.WriteProcedureParam('$high'+s, PTypeInfoToTypeInfoPtr(ti_int64, PtrRedirectMem), [pfHidden, pfHigh, pfConst]);
|
|
end;
|
|
end;
|
|
assert(RttiWriterProc.CurDestMemPos<=Pointer(FTypeInfo)+MemSize, 'DoProcedure: RttiWriterProc.CurDestMemPos<=Pointer(FTypeInfo)+MemSize');
|
|
except
|
|
//TODO: at this point, any type that got our typeinfo during recursion, has an invalid typeinfo
|
|
SetFTypeInfo(nil);
|
|
raise;
|
|
end
|
|
finally
|
|
RttiWriterProc.Free;
|
|
end;
|
|
end;
|
|
assert(PtrRedirectMem <= Pointer(FTypeInfo) + MemSize + PtrRedirectMemSize, 'DoProcedure: PtrRedirectMem <= Pointer(FTypeInfo) + MemSize + PtrRedirectMemSize');
|
|
end;
|
|
|
|
procedure DoRecord;
|
|
type
|
|
TRecEntry = record
|
|
NameList: TStringArray;
|
|
TypeInfo: PTypeInfo;
|
|
end;
|
|
var
|
|
s, TpName, TpUnit: String;
|
|
tk: TJitParserTkKind;
|
|
RecEntryList: array of TRecEntry;
|
|
CurRecEntry: ^TRecEntry;
|
|
SubJitTypeInfo: TJitTypeInfo;
|
|
RecEntryListCnt, RecEntryCnt, ManagedCount: Integer;
|
|
RecMemSize, InitTblMemSize, PtrRedirectMemSize, RecInstanceSize: Integer;
|
|
RecEntry: TRecEntry;
|
|
PtrRedirectMem: Pointer;
|
|
RttiWriter: TJitRttiWriterTkRecord;
|
|
IsEmbeddedTp: Boolean;
|
|
begin
|
|
// Todo: peak "case"
|
|
RecEntryCnt := 0;
|
|
ManagedCount := 0;
|
|
RecEntryListCnt := 0;
|
|
tk := TokenParser^.PeekKind;
|
|
if tk <> kwEnd then begin
|
|
repeat
|
|
SetLength(RecEntryList, RecEntryListCnt + 1);
|
|
CurRecEntry := @RecEntryList[RecEntryListCnt];
|
|
|
|
tk := GetIdentList(CurRecEntry^.NameList, [ptComma]);
|
|
inc(RecEntryCnt, Length(CurRecEntry^.NameList));
|
|
AssertExpectedToken(tk, [ptColon]);
|
|
|
|
IsEmbeddedTp := True;
|
|
tk := TokenParser^.Next;
|
|
TokenParser^.SavePosition;
|
|
if tk = ptIdent then begin
|
|
TokenParser^.GetQualifiedIdent(TpUnit, TpName);
|
|
tk := TokenParser^.Next;
|
|
if tk in [ptSemicolon, kwEnd] then begin
|
|
IsEmbeddedTp := False;
|
|
if TpUnit <> '' then
|
|
CurRecEntry^.TypeInfo := FindTypeInfo(TpName, True, TpUnit, [tsoOnlyUnit])
|
|
else
|
|
CurRecEntry^.TypeInfo := FindTypeInfo(TpName, True, FUnitName);
|
|
end;
|
|
end;
|
|
if IsEmbeddedTp then begin
|
|
// scan embedded type
|
|
TokenParser^.RestorePosition;
|
|
SubJitTypeInfo := TJitTypeInfo.Create('', FDeclaration, FUnitName, TypeLibrary);
|
|
SubJitTypeInfo.ParseFromDeclaration(TokenParser);
|
|
CurRecEntry^.TypeInfo := SubJitTypeInfo.FTypeInfo; // do not use property / do not scan again if nil
|
|
RefCountedTypeInfo.AddToList(SubJitTypeInfo);
|
|
SubJitTypeInfo.Free;
|
|
tk := TokenParser^.Next;
|
|
end;
|
|
AssertFoundTypeInfo(CurRecEntry^.TypeInfo, '');
|
|
if CurRecEntry^.TypeInfo.isManaged then
|
|
inc(ManagedCount, Length(CurRecEntry^.NameList));
|
|
|
|
if (tk = ptSemicolon) and (TokenParser^.PeekKind = kwEnd) then
|
|
tk := TokenParser^.Next;
|
|
until tk <> ptSemicolon;
|
|
AssertExpectedToken(TokenParser^.CurrentKind, [kwEnd]);
|
|
end
|
|
else
|
|
TokenParser^.Next; // read the "end"
|
|
AssertIsBeforeEOT(AParser <> nil);
|
|
|
|
RecMemSize := TJitRttiWriterTypeInfo.NewSizeFor(FTypeName, tkRecord)
|
|
+ SizeOf(TManagedField) * RecEntryCnt;
|
|
InitTblMemSize := TJitRttiWriterRecInitInfo.NewSizeForInitTable(FTypeName, ManagedCount);
|
|
PtrRedirectMemSize := SIZE_OF_TYPEINFO_PPOINTER * RecEntryCnt;
|
|
|
|
RecInstanceSize := 0;
|
|
|
|
SetFTypeInfo(AllocMem(RecMemSize + InitTblMemSize + PtrRedirectMemSize));
|
|
PtrRedirectMem := pointer(FTypeInfo) + RecMemSize + InitTblMemSize;
|
|
|
|
RttiWriter := TJitRttiWriterTkRecord.Create(FTypeInfo, Pointer(FTypeInfo) + RecMemSize,
|
|
FTypeName, RecEntryCnt, ManagedCount);
|
|
|
|
for RecEntry in RecEntryList do
|
|
for s in RecEntry.NameList do begin
|
|
RttiWriter.WriteField(PTypeInfoToTypeInfoPtr(RecEntry.TypeInfo, PtrRedirectMem), RecInstanceSize, RecEntry.TypeInfo.IsManaged);
|
|
RecInstanceSize := RecInstanceSize + RecEntry.TypeInfo.DataSize;
|
|
end;
|
|
|
|
RttiWriter.WriteRecSize(RecInstanceSize);
|
|
RttiWriter.RecInitFieldWriter.WriteSize(RecInstanceSize);
|
|
|
|
assert(PtrRedirectMem <= Pointer(FTypeInfo)+RecMemSize+InitTblMemSize+PtrRedirectMemSize, 'DoRecord: PtrRedirectMem <= Pointer(FTypeInfo)+RecMemSize+InitTblMemSize+PtrRedirectMemSize');
|
|
assert(RttiWriter.CurDestMemPos <= Pointer(FTypeInfo)+RecMemSize, 'DoRecord: RttiWriter.CurDestMemPos <= Pointer(FTypeInfo)+RecMemSize');
|
|
assert(RttiWriter.RecInitFieldWriter.CurDestMemPos <= Pointer(FTypeInfo)+RecMemSize+InitTblMemSize, 'DoRecord: RttiWriter.RecInitFieldWriter.CurDestMemPos <= Pointer(FTypeInfo)+RecMemSize+InitTblMemSize');
|
|
RttiWriter.Free;
|
|
end;
|
|
|
|
procedure DoShortString;
|
|
var
|
|
i: Int64;
|
|
l: Integer;
|
|
qw: Boolean;
|
|
begin
|
|
AssertExpectedToken(TokenParser^.Next, [ptSquareOpen]);
|
|
TokenParser^.Next;
|
|
i := TokenParser^.CurrentTokenAsNum(qw);
|
|
if (i < 1) or (i > 255) then
|
|
raise JitTypeParserException.Create(TokenParser^.CurPos, TokenParser^.CurrentToken, 'shortstring length out of range');
|
|
AssertExpectedToken(TokenParser^.Next, [ptSquareClose]);
|
|
AssertIsBeforeEOT(AParser <> nil);
|
|
|
|
l := TJitRttiWriterTypeInfo.NewSizeFor(FTypeName, tkSString);
|
|
SetFTypeInfo(GetMem(l));
|
|
FTypeInfo^.Kind := tkSString;
|
|
FTypeInfo^.Name := FTypeName;
|
|
GetTypeData(FTypeInfo)^.MaxLength := i;
|
|
end;
|
|
|
|
procedure DoSet;
|
|
var
|
|
s, s2: String;
|
|
enum: PTypeInfo;
|
|
SubJitTypeInfo: TJitTypeInfo;
|
|
ByteSize, MemSize: LongInt;
|
|
PtrRedirectMem: Pointer;
|
|
RttiWriter: TJitRttiWriterTkSet;
|
|
IsEmbeddedTp: Boolean;
|
|
tk: TJitParserTkKind;
|
|
begin
|
|
AssertExpectedToken(TokenParser^.Next(pcOfExpexted), [kwOf]);
|
|
|
|
IsEmbeddedTp := True;
|
|
tk := TokenParser^.Next;
|
|
TokenParser^.SavePosition; // So we can roll back to current = 'of', because recursive call starts with NEXT
|
|
if tk = ptIdent then begin
|
|
TokenParser^.GetQualifiedIdent(s2, s);
|
|
if IsBeforeEOT(AParser <> nil) then begin
|
|
IsEmbeddedTp := False;
|
|
if s2 <> '' then
|
|
enum := FindTypeInfo(s, True, s2, [tsoOnlyUnit])
|
|
else
|
|
enum := FindTypeInfo(s, True, FUnitName);
|
|
end;
|
|
end;
|
|
if IsEmbeddedTp then begin
|
|
s := '';
|
|
TokenParser^.RestorePosition;
|
|
SubJitTypeInfo := TJitTypeInfo.Create('', FDeclaration, FUnitName, TypeLibrary);
|
|
SubJitTypeInfo.ParseFromDeclaration(TokenParser);
|
|
enum := SubJitTypeInfo.FTypeInfo; // do not use property / do not scan again if nil
|
|
RefCountedTypeInfo.AddToList(SubJitTypeInfo);
|
|
SubJitTypeInfo.Free;
|
|
AssertIsBeforeEOT(AParser <> nil);
|
|
end;
|
|
AssertFoundTypeInfo(enum, s);
|
|
|
|
case enum^.Kind of
|
|
tkEnumeration,
|
|
tkInteger,tkChar,tkBool,tkWChar:
|
|
ByteSize := GetTypeData(enum)^.MaxValue - GetTypeData(enum)^.MinValue + 1;
|
|
tkInt64: ByteSize := GetTypeData(enum)^.MaxInt64Value - GetTypeData(enum)^.MinInt64Value + 1;
|
|
tkQWord: ByteSize := GetTypeData(enum)^.MaxQWordValue - GetTypeData(enum)^.MinQWordValue + 1;
|
|
else
|
|
raise JitTypeParserExceptionSyntaxError.Create(TokenParser^.CurPos, TokenParser^.CurrentToken, 'Expected enumeration or sub-range');
|
|
end;
|
|
|
|
MemSize := aligntoptr(TJitRttiWriterTypeInfo.NewSizeFor(FTypeName, tkSet));
|
|
SetFTypeInfo(AllocMem(MemSize + SIZE_OF_TYPEINFO_PPOINTER));
|
|
PtrRedirectMem := Pointer(FTypeInfo) + MemSize;
|
|
|
|
RttiWriter := TJitRttiWriterTkSet.Create(FTypeInfo, FTypeName, ByteSize, PTypeInfoToTypeInfoPtr(enum, PtrRedirectMem));
|
|
RttiWriter.Free;
|
|
end;
|
|
|
|
procedure DoSubRange(tk: TJitParserTkKind; s: String); // s has been read with TokenParser^.Next
|
|
procedure ParseValue(CurKind: TJitParserTkKind; out AnOrdVal: Int64;
|
|
out AJitType: TJitType; out APTypeInfo: PTypeInfo; ALowJitType: TJitType = nil);
|
|
var
|
|
EnumUnit, EnumType, EnumMember: String;
|
|
JitDummy: TJitType;
|
|
InfoDummy: PTypeInfo;
|
|
MemberVal: Integer;
|
|
IsQw: Boolean;
|
|
begin
|
|
LastIntTypeInfoFound := nil;
|
|
APTypeInfo := nil;
|
|
case CurKind of
|
|
ptIdent: if (TypeLibrary <> nil) then begin
|
|
TokenParser^.GetQualifiedIdent(EnumUnit, EnumType, EnumMember);
|
|
case TokenParser^.PeekKind of
|
|
ptRoundOpen: begin // we have "EnumType(val)..
|
|
if EnumUnit <> '' then EnumUnit := EnumUnit + '.';
|
|
EnumUnit := EnumUnit + EnumType;
|
|
if EnumUnit = '' then
|
|
APTypeInfo := FindTypeInfo(EnumMember, False, FUnitName) // EnumMember countains the typename
|
|
else
|
|
APTypeInfo := FindTypeInfo(EnumMember, False, EnumUnit, [tsoOnlyUnit]);
|
|
TokenParser^.Next;
|
|
CurKind := TokenParser^.Next; // skip the "("
|
|
AJitType := LastIntTypeInfoFound;
|
|
ParseValue(CurKind, AnOrdVal, JitDummy, InfoDummy);
|
|
LastIntTypeInfoFound := AJitType;
|
|
AssertExpectedToken(TokenParser^.Next, [ptRoundClose]);
|
|
end;
|
|
else begin
|
|
if EnumType <> '' then begin // We have "unit.EnumType.Member" or "EnumType.Member"
|
|
if EnumUnit = '' then
|
|
APTypeInfo := FindTypeInfo(EnumType, False, FUnitName)
|
|
else
|
|
APTypeInfo := FindTypeInfo(EnumType, False, EnumUnit, [tsoOnlyUnit]);
|
|
AssertFoundTypeInfo(APTypeInfo, TokenParser^.CurrentTokenRaw);
|
|
if APTypeInfo^.Kind <> tkEnumeration then
|
|
raise JitTypeParserExceptionSyntaxError.Create(TokenParser^.CurPos, TokenParser^.CurrentToken, 'Enumeration type expected: ');
|
|
AnOrdVal := GetEnumValue(APTypeInfo, EnumMember);
|
|
if AnOrdVal < 0 then
|
|
raise JitTypeParserExceptionSyntaxError.Create(TokenParser^.CurPos, EnumMember,'Identifier not found');
|
|
end
|
|
else
|
|
begin // We have just "Member"
|
|
if (ALowJitType = nil) or (ALowJitType.TypeInfo^.Kind <> tkEnumeration) then begin
|
|
LastIntTypeInfoFound := TypeLibrary.FindTypeForEnumElem(EnumMember, MemberVal, FUnitName);
|
|
AnOrdVal := MemberVal;
|
|
if LastIntTypeInfoFound <> nil then
|
|
APTypeInfo := LastIntTypeInfoFound.TypeInfo;
|
|
end
|
|
else begin // parsing high end of range / must be same type
|
|
APTypeInfo := ALowJitType.TypeInfo;
|
|
LastIntTypeInfoFound := ALowJitType;
|
|
AnOrdVal := GetEnumValue(APTypeInfo, EnumMember);
|
|
if AnOrdVal < 0 then
|
|
raise JitTypeParserExceptionSyntaxError.Create(TokenParser^.CurPos, EnumMember,'Identifier not found');
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
AssertFoundTypeInfo(APTypeInfo, TokenParser^.CurrentTokenRaw);
|
|
end;
|
|
ptNum: begin
|
|
AnOrdVal := TokenParser^.CurrentTokenAsNum(IsQw);
|
|
if IsQw then
|
|
APTypeInfo := system.TypeInfo(QWord)
|
|
else
|
|
if (AnOrdVal > high(LongInt)) or (AnOrdVal < low(LongInt)) then
|
|
APTypeInfo := system.TypeInfo(Int64)
|
|
else
|
|
APTypeInfo := system.TypeInfo(longint);
|
|
end;
|
|
ptChar: begin
|
|
AnOrdVal := ord(s[1]);
|
|
APTypeInfo := system.TypeInfo(Char);
|
|
end;
|
|
else
|
|
raise JitTypeParserExceptionSyntaxError.Create(TokenParser^.CurPos, TokenParser^.CurrentToken, 'Expected enumeration or sub-range');
|
|
end;
|
|
AJitType := LastIntTypeInfoFound;
|
|
end;
|
|
|
|
var
|
|
LowVal, HighVal, i: Int64;
|
|
LowType, HighType: PTypeInfo;
|
|
MemSize, PtrRedirectMemSize: Integer;
|
|
RttiWriterEnum: TJitRttiWriterTkEnum;
|
|
PtrRedirectMem: Pointer;
|
|
Signed: Boolean;
|
|
RttiWriterIntRange: TJitRttiWriterOrdinal;
|
|
LowJitType, HighJitType: TJitType;
|
|
un: String;
|
|
begin
|
|
ParseValue(tk, LowVal, LowJitType, LowType);
|
|
AssertExpectedToken(TokenParser^.Next, [ptDotDot]);
|
|
|
|
ParseValue(TokenParser^.Next, HighVal, HighJitType, HighType, LowJitType);
|
|
|
|
if HighVal < LowVal then
|
|
raise JitTypeParserException.Create(TokenParser^.CurPos, '', 'Negative range for set');
|
|
AssertIsBeforeEOT(AParser <> nil);
|
|
|
|
case LowType^.Kind of
|
|
tkEnumeration: begin
|
|
if HighType <> LowType then
|
|
raise JitTypeParserException.Create(TokenParser^.CurPos, '', 'Type mismatch between low and high bound');
|
|
|
|
MemSize := TJitRttiWriterTypeInfo.NewSizeFor(FTypeName, tkEnumeration);
|
|
PtrRedirectMemSize := SIZE_OF_TYPEINFO_PPOINTER;
|
|
MemSize := aligntoptr(MemSize); // for the start of PtrRedirectMemSize
|
|
i := LowVal;
|
|
while i <= HighVal do begin
|
|
TJitRttiWriterTkEnum.AddSizeForElemName(MemSize, GetEnumName(LowType, i));
|
|
inc(i);
|
|
end;
|
|
un := FUnitName;
|
|
if un = '' then un := '$u'; // must write some unitname
|
|
TJitRttiWriterTkEnum.AddSizeForElemName(MemSize, un);
|
|
TJitRttiWriterTkEnum.FinishSize(MemSize);
|
|
|
|
SetFTypeInfo(AllocMem(MemSize + PtrRedirectMemSize));
|
|
PtrRedirectMem := Pointer(FTypeInfo) + MemSize;
|
|
RttiWriterEnum := TJitRttiWriterTkEnum.Create(FTypeInfo, FTypeName, HighVal - LowVal + 1);
|
|
|
|
i := LowVal;
|
|
while i <= HighVal do begin
|
|
RttiWriterEnum.WriteEnumElemName(GetEnumName(LowType, i));
|
|
inc(i);
|
|
end;
|
|
RttiWriterEnum.WriteMinMax(LowVal, HighVal);
|
|
RttiWriterEnum.WriteUnitName(un);
|
|
RttiWriterEnum.WriteBaseTypeRef(PTypeInfoToTypeInfoPtr(LowType, PtrRedirectMem));
|
|
|
|
assert(RttiWriterEnum.CurDestMemPos <= Pointer(FTypeInfo) + MemSize, 'DoSubRange: RttiWriterEnum.CurDestMemPos <= Pointer(FTypeInfo) + MemSize');
|
|
|
|
RttiWriterEnum.Free;
|
|
RefCountedTypeInfo.AddToList(LowJitType);
|
|
end;
|
|
|
|
tkInteger, tkInt64, tkQWord: begin
|
|
Signed := LowType^.Kind <> tkQWord;
|
|
if not Signed then begin
|
|
if (HighType^.Kind <> tkQWord) and (HighVal < 0) then begin
|
|
if QWord(LowVal) > QWord(high(int64)) then
|
|
raise JitTypeParserException.Create(TokenParser^.CurPos, '', 'range check error');
|
|
Signed := True;
|
|
if (LowVal <= High(longint)) then begin // TODO: maybe downgrade if: and (HighVal >= low(LongInt)) => force tkInteger;
|
|
LowType := HighType; // tkInteger or tkInt64
|
|
LowJitType := HighJitType;
|
|
end
|
|
else begin
|
|
LowType := system.TypeInfo(Int64);
|
|
LowJitType := nil;
|
|
end;
|
|
end;
|
|
end
|
|
else begin // LowType is Signed
|
|
case HighType^.Kind of
|
|
tkInt64: begin
|
|
LowType := HighType; // LowType was either tkInt64 or tkInteger;
|
|
LowJitType := HighJitType;
|
|
end;
|
|
tkQWord: begin
|
|
if (LowVal < 0) then begin
|
|
if QWord(HighVal) > QWord(high(int64)) then
|
|
raise JitTypeParserException.Create(TokenParser^.CurPos, '', 'range check error');
|
|
Signed := True;
|
|
if (HighVal > High(longint)) then begin
|
|
LowType := system.TypeInfo(Int64);
|
|
LowJitType := nil;
|
|
end;
|
|
end
|
|
else begin
|
|
LowType := HighType; // both QWord
|
|
LowJitType := HighJitType;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
MemSize := TJitRttiWriterTypeInfo.NewSizeFor(FTypeName, LowType^.Kind);
|
|
|
|
SetFTypeInfo(AllocMem(MemSize));
|
|
RttiWriterIntRange := TJitRttiWriterOrdinal.Create(FTypeInfo, FTypeName, LowType^.Kind);
|
|
RttiWriterIntRange.WriteOrdType(HighVal - LowVal + 1, Signed);
|
|
RttiWriterIntRange.WriteMinMax(LowVal, HighVal);
|
|
|
|
RttiWriterIntRange.Free;
|
|
RefCountedTypeInfo.AddToList(LowJitType);
|
|
end;
|
|
|
|
tkChar, tkWChar: begin
|
|
if HighType <> LowType then
|
|
raise JitTypeParserException.Create(TokenParser^.CurPos, '', 'Type mismatch between low and high bound');
|
|
|
|
MemSize := TJitRttiWriterTypeInfo.NewSizeFor(FTypeName, LowType^.Kind);
|
|
|
|
SetFTypeInfo(AllocMem(MemSize));
|
|
RttiWriterIntRange := TJitRttiWriterOrdinal.Create(FTypeInfo, FTypeName, LowType^.Kind);
|
|
RttiWriterIntRange.WriteOrdType(HighVal - LowVal + 1, False);
|
|
RttiWriterIntRange.WriteMinMax(LowVal, HighVal);
|
|
|
|
RttiWriterIntRange.Free;
|
|
RefCountedTypeInfo.AddToList(LowJitType);
|
|
end;
|
|
//tkBool:; // TODO:
|
|
else
|
|
raise JitTypeParserException.Create(TokenParser^.CurPos, LowType^.Name, 'unexpected sub range type');
|
|
end;
|
|
end;
|
|
|
|
procedure DoArray;
|
|
var
|
|
TpUnit, TpName: String;
|
|
ArrayTypeInfo: PTypeInfo;
|
|
SubJitTypeInfo: TJitTypeInfo;
|
|
TypeMemSize: Integer;
|
|
PtrRedirectMem: Pointer;
|
|
RttiWriter: TJitRttiWriterTkDynArray;
|
|
IsEmbeddedTp: Boolean;
|
|
tk: TJitParserTkKind;
|
|
begin
|
|
case TokenParser^.Next(pcOfExpexted) of
|
|
kwOf: begin
|
|
IsEmbeddedTp := True;
|
|
tk := TokenParser^.Next;
|
|
TokenParser^.SavePosition;
|
|
if tk = ptIdent then begin
|
|
TokenParser^.GetQualifiedIdent(TpUnit, TpName);
|
|
|
|
if not TokenParser^.IsBeforeEotOrSectionEnd then begin
|
|
IsEmbeddedTp := False;
|
|
if TpUnit <> '' then
|
|
ArrayTypeInfo := FindTypeInfo(TpName, True, TpUnit, [tsoOnlyUnit])
|
|
else
|
|
ArrayTypeInfo := FindTypeInfo(TpName, True, FUnitName);
|
|
end;
|
|
end;
|
|
if IsEmbeddedTp then begin
|
|
TokenParser^.RestorePosition;
|
|
SubJitTypeInfo := TJitTypeInfo.Create('', FDeclaration, FUnitName, TypeLibrary);
|
|
SubJitTypeInfo.ParseFromDeclaration(TokenParser);
|
|
ArrayTypeInfo := SubJitTypeInfo.FTypeInfo; // do not use property / do not scan again if nil
|
|
RefCountedTypeInfo.AddToList(SubJitTypeInfo);
|
|
SubJitTypeInfo.Free;
|
|
end;
|
|
AssertIsBeforeEOT(AParser <> nil);
|
|
|
|
TypeMemSize := TJitRttiWriterTypeInfo.NewSizeFor(FTypeName, tkDynArray);
|
|
SetFTypeInfo(AllocMem(TypeMemSize + SIZE_OF_TYPEINFO_PPOINTER));
|
|
PtrRedirectMem := Pointer(FTypeInfo) + TypeMemSize;
|
|
|
|
// TODO: variant type info
|
|
RttiWriter := TJitRttiWriterTkDynArray.Create(FTypeInfo, FTypeName,
|
|
FUnitName, ArrayTypeInfo.DataSize, PTypeInfoToTypeInfoPtr(ArrayTypeInfo, PtrRedirectMem), -1);
|
|
|
|
assert(RttiWriter.CurDestMemPos <= Pointer(FTypeInfo) + TypeMemSize, 'DoArray: RttiWriter.CurDestMemPos <= Pointer(FTypeInfo) + TypeMemSize');
|
|
RttiWriter.Free;
|
|
end;
|
|
//ptSquareOpen: ;
|
|
else
|
|
raise JitTypeParserExceptionSyntaxError.Create(TokenParser^.CurPos, TokenParser^.CurrentToken, 'Expected "of"'); // 'Expected "[" or "of"'
|
|
end;
|
|
end;
|
|
|
|
var
|
|
NewTypeInfo: PTypeInfo;
|
|
s: String;
|
|
tk, tk2: TJitParserTkKind;
|
|
begin
|
|
if FIsInParseFromDeclaration then
|
|
exit;
|
|
FIsInParseFromDeclaration := true;
|
|
try
|
|
Decl := FDeclaration;
|
|
if AParser = nil then
|
|
Decl := TrimDeclaration(Decl);
|
|
|
|
if AParser <> nil then begin
|
|
TokenParser := AParser;
|
|
end
|
|
else begin
|
|
TheTokenParser.Create(@Decl[1]);
|
|
TokenParser := @TheTokenParser;
|
|
end;
|
|
|
|
tk := TokenParser^.CurrentKind;
|
|
case tk of
|
|
ptRoundOpen: DoEnum;
|
|
kwProcedure: DoProcedure(False);
|
|
kwFunction: DoProcedure(True);
|
|
kwRecord: DoRecord;
|
|
kwSet: DoSet;
|
|
kwArray: DoArray;
|
|
else begin
|
|
if (tk = kwString) and (TokenParser^.PeekKind() = ptSquareOpen ) then begin
|
|
DoShortString;
|
|
exit;
|
|
end;
|
|
|
|
s := TokenParser^.CurrentToken;
|
|
if tk in [ptIdent, ptNum, ptChar] then begin
|
|
tk2 := TokenParser^.PeekKind;
|
|
if tk2 in [ptRoundOpen, ptDotDot, ptDot] then begin
|
|
// subrange: value .. value OR TypeCast(value) ..
|
|
DoSubRange(tk, s);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
if tk <> kwString then // string may be added as an alias
|
|
AssertIsIdent(tk, s);
|
|
|
|
NewTypeInfo := FindTypeInfo(s, False);
|
|
if IsBeforeEOT(AParser <> nil) and (NewTypeInfo <> nil) then begin
|
|
if (LastIntTypeInfoFound <> nil) and (not LastIntTypeInfoFound.IsConstTypeInfo) then
|
|
SetFTypeInfo(NewTypeInfo)
|
|
else
|
|
SetConstFTypeInfo(NewTypeInfo);
|
|
RefCountedTypeInfo.AddToList(LastIntTypeInfoFound);
|
|
exit;
|
|
end;
|
|
|
|
raise JitTypeParserExceptionTypeNotFound.Create(TokenParser^.CurPos, s, 'type not found')
|
|
end;
|
|
end;
|
|
|
|
finally
|
|
FIsInParseFromDeclaration := False;
|
|
end;
|
|
end;
|
|
|
|
constructor TJitTypeInfo.Create(ATypeName, ADeclaration, AUnitName: String;
|
|
ATypeLibrary: TJitTypeLibrary; AParseFlags: TJitTypeInfoParseFlags);
|
|
begin
|
|
inherited Create(ATypeName, AUnitName, ATypeLibrary);
|
|
FDeclaration := ADeclaration;
|
|
FParseFlags := AParseFlags;
|
|
end;
|
|
|
|
constructor TJitTypeInfo.Create(ATypeName, ADeclaration: String;
|
|
ATypeLibrary: TJitTypeLibrary; AParseFlags: TJitTypeInfoParseFlags);
|
|
begin
|
|
Create(ATypeName, ADeclaration, '', ATypeLibrary, AParseFlags);
|
|
end;
|
|
|
|
constructor TJitTypeInfo.Create(ATypeName: String; ATypeInfo: PTypeInfo;
|
|
ATypeLibrary: TJitTypeLibrary);
|
|
begin
|
|
SetConstFTypeInfo(ATypeInfo);
|
|
inherited Create(ATypeName, ATypeLibrary);
|
|
end;
|
|
|
|
destructor TJitTypeInfo.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
if FRefCountedTypeInfo <> nil then
|
|
FRefCountedTypeInfo.ReleaseLock
|
|
else
|
|
if (FTypeInfo <> nil) and not FIsConstTypeInfo then
|
|
Freemem(FTypeInfo);
|
|
end;
|
|
|
|
{ TJitTypeClass }
|
|
|
|
function TJitTypeClass.GetTypeInfo: PTypeInfo;
|
|
begin
|
|
Result := FClass.ClassInfo;
|
|
end;
|
|
|
|
function TJitTypeClass.GetJitClass: TClass;
|
|
begin
|
|
Result := FClass;
|
|
end;
|
|
|
|
constructor TJitTypeClass.Create(ATypeName: String; AClass: TClass;
|
|
ATypeLibrary: TJitTypeLibrary);
|
|
begin
|
|
Create(ATypeName, AClass.UnitName, AClass, ATypeLibrary);
|
|
end;
|
|
|
|
constructor TJitTypeClass.Create(ATypeName, AUnitName: String; AClass: TClass;
|
|
ATypeLibrary: TJitTypeLibrary);
|
|
begin
|
|
inherited Create(ATypeName, AUnitName, ATypeLibrary);
|
|
FClass := AClass;
|
|
end;
|
|
|
|
{ TJitTypeJitClass }
|
|
|
|
procedure TJitTypeJitClass.DoCreatorFreed(Sender: TObject);
|
|
begin
|
|
FJitClassCreator := nil;
|
|
end;
|
|
|
|
function TJitTypeJitClass.GetTypeInfo: PTypeInfo;
|
|
begin
|
|
if FJitClassCreator <> nil then
|
|
Result := FJitClassCreator.TypeInfo
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TJitTypeJitClass.GetJitClass: TClass;
|
|
begin
|
|
if FJitClassCreator <> nil then
|
|
Result := FJitClassCreator.GetJitClass
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TJitTypeJitClass.GetLockReferenceInc: TRefCountedJitReference;
|
|
begin
|
|
if FJitClassCreator <> nil then
|
|
Result := FJitClassCreator.LockReference
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TJitTypeJitClass.IsConstTypeInfo: Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
constructor TJitTypeJitClass.Create(ATypeName: String;
|
|
AJitClassCreator: TJitClassCreatorBase; ATypeLibrary: TJitTypeLibrary;
|
|
ATakeOwnerShip: Boolean);
|
|
begin
|
|
Create(ATypeName, AJitClassCreator.ClassUnit, AJitClassCreator, ATypeLibrary, ATakeOwnerShip);
|
|
end;
|
|
|
|
constructor TJitTypeJitClass.Create(ATypeName, AUnitName: String;
|
|
AJitClassCreator: TJitClassCreatorBase; ATypeLibrary: TJitTypeLibrary;
|
|
ATakeOwnerShip: Boolean);
|
|
begin
|
|
inherited Create(ATypeName, AUnitName, ATypeLibrary);
|
|
FJitClassCreator := AJitClassCreator;
|
|
if FJitClassCreator <> nil then
|
|
FJitClassCreator.AddFreeNotification(@DoCreatorFreed);
|
|
FOwnJitCreator := ATakeOwnerShip;
|
|
end;
|
|
|
|
destructor TJitTypeJitClass.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
if FJitClassCreator <> nil then begin
|
|
FJitClassCreator.RemoveFreeNotification(@DoCreatorFreed);
|
|
if FOwnJitCreator then
|
|
FJitClassCreator.Free;
|
|
end;
|
|
end;
|
|
|
|
{ TJitTypeAlias }
|
|
|
|
function TJitTypeAlias.GetRealType: TJitType;
|
|
begin
|
|
Result := FRealType;
|
|
if Result <> nil then
|
|
exit;
|
|
if TypeLibrary = nil then
|
|
exit;
|
|
|
|
if FInGetRealType then
|
|
raise Exception.Create('circular alias');
|
|
FInGetRealType := True;
|
|
try
|
|
Result := TypeLibrary[FRealTypeName];
|
|
finally
|
|
FInGetRealType := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TJitTypeAlias.DoRealTypeFreed(Sender: TObject);
|
|
begin
|
|
FRealType := nil;
|
|
FTypeName := ''; // no longer valid / do not look up a new type
|
|
end;
|
|
|
|
function TJitTypeAlias.GetTypeInfo: PTypeInfo;
|
|
var
|
|
RealType: TJitType;
|
|
begin
|
|
Result := nil;
|
|
RealType := GetRealType;
|
|
if RealType <> nil then begin
|
|
Result := RealType.TypeInfo;
|
|
exit;
|
|
end;
|
|
Result := TypeInfoByName(FRealTypeName);
|
|
end;
|
|
|
|
function TJitTypeAlias.GetLockReferenceInc: TRefCountedJitReference;
|
|
var
|
|
RealType: TJitType;
|
|
begin
|
|
RealType := GetRealType;
|
|
if RealType <> nil then
|
|
Result := GetRealType.LockReference
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TJitTypeAlias.IsConstTypeInfo: Boolean;
|
|
var
|
|
RealType: TJitType;
|
|
begin
|
|
RealType := GetRealType;
|
|
if RealType <> nil then
|
|
Result := GetRealType.IsConstTypeInfo
|
|
else
|
|
Result := inherited IsConstTypeInfo;
|
|
end;
|
|
|
|
function TJitTypeAlias.GetResolvedTypeName: String;
|
|
var
|
|
RealType: TJitType;
|
|
begin
|
|
RealType := GetRealType;
|
|
if RealType <> nil then
|
|
Result := GetRealType.ResolvedTypeName
|
|
else
|
|
Result := inherited ResolvedTypeName;
|
|
end;
|
|
|
|
constructor TJitTypeAlias.Create(ATypeName, ARealTypeName: String;
|
|
ATypeLibrary: TJitTypeLibrary);
|
|
begin
|
|
FRealTypeName := ARealTypeName;
|
|
inherited Create(ATypeName, ATypeLibrary);
|
|
end;
|
|
|
|
constructor TJitTypeAlias.Create(ATypeName: String; ARealType: TJitType;
|
|
ATypeLibrary: TJitTypeLibrary);
|
|
begin
|
|
FRealType := ARealType;
|
|
if FRealType <> nil then
|
|
FRealType.AddFreeNotification(@DoRealTypeFreed);
|
|
inherited Create(ATypeName, ATypeLibrary);
|
|
end;
|
|
|
|
destructor TJitTypeAlias.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
if FRealType <> nil then
|
|
FRealType.RemoveFreeNotification(@DoRealTypeFreed);
|
|
end;
|
|
|
|
{ TJitTypeLibrary }
|
|
|
|
procedure TJitTypeLibrary.DoTypeFreed(Sender: TObject);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i := FTypeMap.IndexOfData(TJitType(Sender));
|
|
if i < 0 then
|
|
exit;
|
|
FTypeMap.Delete(i);
|
|
end;
|
|
|
|
function TJitTypeLibrary.GetTypes(AName: String): TJitType;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := nil;
|
|
i := FTypeMap.IndexOf(LowerCase(AName));
|
|
if i >= 0 then
|
|
Result := FTypeMap.Data[i];
|
|
end;
|
|
|
|
procedure TJitTypeLibrary.SetAllowDuplicates(AValue: Boolean);
|
|
begin
|
|
if FAllowDuplicates = AValue then Exit;
|
|
FAllowDuplicates := AValue;
|
|
case AValue of
|
|
True: FTypeMap.Duplicates := dupAccept;
|
|
False: FTypeMap.Duplicates := dupError;
|
|
end;
|
|
end;
|
|
|
|
function TJitTypeLibrary.IndexOf(ATypeName, AnUnitName: String): Integer;
|
|
var
|
|
NoUnit: Integer;
|
|
n: String;
|
|
begin
|
|
if AnUnitName = '' then begin
|
|
Result := FTypeMap.IndexOf(ATypeName);
|
|
exit;
|
|
end;
|
|
Result := FTypeMap.Count - 1;
|
|
NoUnit := -1;
|
|
while Result >= 0 do begin
|
|
if (FTypeMap.Data[Result].TypeName = ATypeName) then begin
|
|
n := FTypeMap.Data[Result].UnitName;
|
|
if n = AnUnitName then
|
|
exit
|
|
else
|
|
if (NoUnit < 0) and (n = '') then
|
|
NoUnit := Result;
|
|
end;
|
|
dec(Result);
|
|
end;
|
|
Result := NoUnit;
|
|
end;
|
|
|
|
constructor TJitTypeLibrary.Create;
|
|
begin
|
|
FTypeMap := TTypeMap.Create;
|
|
AllowDuplicates := True;
|
|
end;
|
|
|
|
destructor TJitTypeLibrary.Destroy;
|
|
begin
|
|
Clear;
|
|
FTypeMap.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TJitTypeLibrary.AddType(AType: TJitType; ATakeOwnerShip: Boolean
|
|
): TJitType;
|
|
begin
|
|
Result := AType;
|
|
Result.AddFreeNotification(@DoTypeFreed);
|
|
if (IndexOf(AType.TypeName, AType.UnitName) >= 0) then
|
|
raise Exception.Create('Duplicate Type');
|
|
|
|
FTypeMap.Add(LowerCase(AType.TypeName), AType);
|
|
|
|
if (AType.TypeLibrary = nil) or ATakeOwnerShip then
|
|
AType.TypeLibrary := Self;
|
|
if ATakeOwnerShip then
|
|
AType.FOwnedByLibrary := True;
|
|
|
|
end;
|
|
|
|
function TJitTypeLibrary.AddType(ATypeName, ADeclaration: String;
|
|
AForceNewJitTypeInfo: Boolean): TJitType;
|
|
begin
|
|
Result := AddType(ATypeName, ADeclaration, '', AForceNewJitTypeInfo);
|
|
end;
|
|
|
|
function TJitTypeLibrary.AddType(ATypeName, ADeclaration, AUnitName: String;
|
|
AForceNewJitTypeInfo: Boolean): TJitType;
|
|
begin
|
|
if (IndexOf(ATypeName, AUnitName) >= 0) then
|
|
raise Exception.Create('Duplicate Type');
|
|
|
|
if not AForceNewJitTypeInfo then begin
|
|
Result := Self[ADeclaration];
|
|
if Result <> nil then begin
|
|
Result := AddAlias(ATypeName, ADeclaration);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
Result := TJitTypeInfo.Create(ATypeName, ADeclaration, AUnitName);
|
|
Result.AddFreeNotification(@DoTypeFreed);
|
|
FTypeMap.Add(LowerCase(ATypeName), Result);
|
|
Result.TypeLibrary := Self;
|
|
Result.FOwnedByLibrary := True;
|
|
end;
|
|
|
|
function TJitTypeLibrary.AddClass(ATypeName: String; AClass: TClass): TJitType;
|
|
begin
|
|
AddClass(ATypeName, AClass.UnitName, AClass);
|
|
end;
|
|
|
|
function TJitTypeLibrary.AddClass(ATypeName, AUnitName: String; AClass: TClass
|
|
): TJitType;
|
|
begin
|
|
if (IndexOf(ATypeName, AUnitName) >= 0) then
|
|
raise Exception.Create('Duplicate Type');
|
|
|
|
Result := TJitTypeClass.Create(ATypeName, AUnitName, AClass);
|
|
Result.AddFreeNotification(@DoTypeFreed);
|
|
FTypeMap.Add(LowerCase(ATypeName), Result);
|
|
Result.TypeLibrary := Self;
|
|
Result.FOwnedByLibrary := True;
|
|
end;
|
|
|
|
function TJitTypeLibrary.AddJitClass(ATypeName: String;
|
|
AJitClassCreator: TJitClassCreatorBase; ATakeCreatorOwnerShip: Boolean
|
|
): TJitType;
|
|
begin
|
|
if (IndexOf(ATypeName, AJitClassCreator.ClassUnit) >= 0) then
|
|
raise Exception.Create('Duplicate Type');
|
|
|
|
Result := TJitTypeJitClass.Create(ATypeName, AJitClassCreator, Self, ATakeCreatorOwnerShip);
|
|
Result.AddFreeNotification(@DoTypeFreed);
|
|
FTypeMap.Add(LowerCase(ATypeName), Result);
|
|
Result.FOwnedByLibrary := True;
|
|
end;
|
|
|
|
function TJitTypeLibrary.AddAlias(ATypeName, ARealTypeName: String): TJitType;
|
|
begin
|
|
Result := TJitTypeAlias.Create(ATypeName, ARealTypeName);
|
|
Result.AddFreeNotification(@DoTypeFreed);
|
|
FTypeMap.Add(LowerCase(ATypeName), Result);
|
|
Result.TypeLibrary := Self;
|
|
Result.FOwnedByLibrary := True;
|
|
end;
|
|
|
|
procedure TJitTypeLibrary.Remove(ATypeName: String; AnUnitName: String);
|
|
procedure RemoveEntry(AnIdx: Integer; AJytTyp: TJitType);
|
|
var
|
|
owned: Boolean;
|
|
begin
|
|
owned := AJytTyp.FOwnedByLibrary and (AJytTyp.TypeLibrary = Self);
|
|
if AJytTyp.TypeLibrary = Self then
|
|
AJytTyp.TypeLibrary := nil;
|
|
|
|
FTypeMap.Delete(AnIdx);
|
|
if owned then
|
|
AJytTyp.Free // TODO: Decrease RefCount ?
|
|
else
|
|
AJytTyp.RemoveFreeNotification(@DoTypeFreed);
|
|
end;
|
|
var
|
|
i: Integer;
|
|
JytTyp: TJitType;
|
|
begin
|
|
if AllowDuplicates or (AnUnitName <> '') then begin
|
|
i := FTypeMap.Count - 1;
|
|
while i >= 0 do begin
|
|
JytTyp := FTypeMap.Data[i];
|
|
if (JytTyp.TypeName = ATypeName) and
|
|
( (AnUnitName = '') or (AnUnitName = JytTyp.UnitName) )
|
|
then
|
|
RemoveEntry(i, JytTyp);
|
|
dec(i);
|
|
if i >= FTypeMap.Count then
|
|
i := FTypeMap.Count - 1;
|
|
end;
|
|
end
|
|
else begin
|
|
i := FTypeMap.IndexOf(ATypeName); // bin search if sorted
|
|
if i < 0 then
|
|
exit;
|
|
JytTyp := FTypeMap.Data[i];
|
|
|
|
RemoveEntry(i, JytTyp);
|
|
end;
|
|
end;
|
|
|
|
procedure TJitTypeLibrary.Clear;
|
|
var
|
|
i: Integer;
|
|
t: TJitType;
|
|
begin
|
|
for i := 0 to FTypeMap.Count - 1 do begin
|
|
t := FTypeMap.Data[i];
|
|
t.RemoveFreeNotification(@DoTypeFreed);
|
|
if t.FOwnedByLibrary and (t.TypeLibrary = Self) then
|
|
t.Free;
|
|
end;
|
|
FTypeMap.Clear;
|
|
end;
|
|
|
|
function TJitTypeLibrary.FindType(const AName: String; AnUnitName: String;
|
|
ASearchOptions: TTypeSearchOptions): TJitType;
|
|
var
|
|
i: Integer;
|
|
FOtherUnitResult: TJitType;
|
|
n, u: String;
|
|
begin
|
|
FOtherUnitResult := nil;
|
|
n := LowerCase(AName);
|
|
u := LowerCase(AnUnitName);
|
|
for i := 0 to FTypeMap.Count - 1 do begin
|
|
Result := FTypeMap.Data[i];
|
|
if LowerCase(Result.TypeName) <> n then
|
|
continue;
|
|
if (tsoOnlyUnit in ASearchOptions) and (LowerCase(Result.UnitName) <> u) then
|
|
continue;
|
|
|
|
if (AnUnitName <> '') and (u <> LowerCase(Result.UnitName)) and (FOtherUnitResult = nil) then
|
|
FOtherUnitResult := Result
|
|
else
|
|
exit;
|
|
end;
|
|
Result := FOtherUnitResult;
|
|
end;
|
|
|
|
function TJitTypeLibrary.FindTypeForEnumElem(const AnEnumElem: String;
|
|
AnUnitName: String; ASearchOptions: TTypeSearchOptions): TJitType;
|
|
var
|
|
x: Integer;
|
|
begin
|
|
Result := FindTypeForEnumElem(AnEnumElem, x, AnUnitName, ASearchOptions);
|
|
end;
|
|
|
|
function TJitTypeLibrary.FindTypeForEnumElem(const AnEnumElem: String; out
|
|
AnEnumVal: Integer; AnUnitName: String; ASearchOptions: TTypeSearchOptions
|
|
): TJitType;
|
|
var
|
|
i, FOtherUnitEnumVal: Integer;
|
|
t: PTypeInfo;
|
|
FOtherUnitResult: TJitType;
|
|
u: String;
|
|
begin
|
|
FOtherUnitResult := nil;
|
|
FOtherUnitEnumVal := -1;
|
|
u := LowerCase(AnUnitName);
|
|
for i := 0 to FTypeMap.Count - 1 do begin
|
|
Result := FTypeMap.Data[i];
|
|
if (not (tsoOnlyUnit in ASearchOptions)) or (LowerCase(Result.UnitName) = u) then begin
|
|
t := Result.TypeInfo;
|
|
if (t <> nil) and (t^.Kind = tkEnumeration) then begin
|
|
AnEnumVal := GetEnumValue(Result.TypeInfo, AnEnumElem);
|
|
if AnEnumVal >= 0 then begin
|
|
if (AnUnitName <> '') and (u <> LowerCase(Result.UnitName)) and (FOtherUnitResult = nil) then begin
|
|
FOtherUnitResult := Result;
|
|
FOtherUnitEnumVal := AnEnumVal;
|
|
end
|
|
else
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
Result := FOtherUnitResult;
|
|
AnEnumVal := FOtherUnitEnumVal;
|
|
end;
|
|
|
|
end.
|
|
|