Repository URL to install this package:
|
Version:
3.2.0 ▾
|
{
This file is part of the Free Pascal run time library.
Copyright (C) 2013 Joost van der Sluis joost@cnoc.nl
member of the Free Pascal development team.
Extended RTTI compatibility unit
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
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.
}
unit Rtti experimental;
{$mode objfpc}{$H+}
{$modeswitch advancedrecords}
{$goto on}
{$Assertions on}
{ Note: since the Lazarus IDE is not yet capable of correctly handling generic
functions it is best to define a InLazIDE define inside the IDE that disables
the generic code for CodeTools. To do this do this:
- go to Tools -> Codetools Defines Editor
- go to Edit -> Insert Node Below -> Define Recurse
- enter the following values:
Name: InLazIDE
Description: Define InLazIDE everywhere
Variable: InLazIDE
Value from text: 1
}
{$ifdef InLazIDE}
{$define NoGenericMethods}
{$endif}
interface
uses
Classes,
SysUtils,
typinfo;
type
TRttiObject = class;
TRttiType = class;
TRttiMethod = class;
TRttiProperty = class;
TRttiInstanceType = class;
TFunctionCallCallback = class
protected
function GetCodeAddress: CodePointer; virtual; abstract;
public
property CodeAddress: CodePointer read GetCodeAddress;
end;
TFunctionCallFlag = (
fcfStatic
);
TFunctionCallFlags = set of TFunctionCallFlag;
TFunctionCallParameterInfo = record
ParamType: PTypeInfo;
ParamFlags: TParamFlags;
ParaLocs: PParameterLocations;
end;
IValueData = interface
['{1338B2F3-2C21-4798-A641-CA2BC5BF2396}']
procedure ExtractRawData(ABuffer: pointer);
procedure ExtractRawDataNoCopy(ABuffer: pointer);
function GetDataSize: SizeInt;
function GetReferenceToRawData: pointer;
end;
TValueData = record
FTypeInfo: PTypeInfo;
FValueData: IValueData;
case integer of
0: (FAsUByte: Byte);
1: (FAsUWord: Word);
2: (FAsULong: LongWord);
3: (FAsObject: Pointer);
4: (FAsClass: TClass);
5: (FAsSByte: Shortint);
6: (FAsSWord: Smallint);
7: (FAsSLong: LongInt);
8: (FAsSingle: Single);
9: (FAsDouble: Double);
10: (FAsExtended: Extended);
11: (FAsComp: Comp);
12: (FAsCurr: Currency);
13: (FAsUInt64: QWord);
14: (FAsSInt64: Int64);
15: (FAsMethod: TMethod);
16: (FAsPointer: Pointer);
{ FPC addition for open arrays }
17: (FArrLength: SizeInt; FElSize: SizeInt);
end;
{ TValue }
TValue = record
private
FData: TValueData;
function GetDataSize: SizeInt;
function GetTypeDataProp: PTypeData; inline;
function GetTypeInfo: PTypeInfo; inline;
function GetTypeKind: TTypeKind; inline;
function GetIsEmpty: boolean; inline;
public
class function Empty: TValue; static;
class procedure Make(ABuffer: pointer; ATypeInfo: PTypeInfo; out result: TValue); static;
{ Note: a TValue based on an open array is only valid until the routine having the open array parameter is left! }
class procedure MakeOpenArray(AArray: Pointer; ALength: SizeInt; ATypeInfo: PTypeInfo; out Result: TValue); static;
{$ifndef NoGenericMethods}
generic class function From<T>(constref aValue: T): TValue; static; inline;
{ Note: a TValue based on an open array is only valid until the routine having the open array parameter is left! }
generic class function FromOpenArray<T>(constref aValue: array of T): TValue; static; inline;
{$endif}
class function FromOrdinal(aTypeInfo: PTypeInfo; aValue: Int64): TValue; static; {inline;}
function IsArray: boolean; inline;
function IsOpenArray: Boolean; inline;
function AsString: string; inline;
function AsUnicodeString: UnicodeString;
function AsAnsiString: AnsiString;
function AsExtended: Extended;
function IsClass: boolean; inline;
function AsClass: TClass;
function IsObject: boolean; inline;
function AsObject: TObject;
function IsOrdinal: boolean; inline;
function AsOrdinal: Int64;
function AsBoolean: boolean;
function AsCurrency: Currency;
function AsInteger: Integer;
function AsChar: Char; inline;
function AsAnsiChar: AnsiChar;
function AsWideChar: WideChar;
function AsInt64: Int64;
function AsUInt64: QWord;
function AsInterface: IInterface;
function ToString: String;
function GetArrayLength: SizeInt;
function GetArrayElement(AIndex: SizeInt): TValue;
procedure SetArrayElement(AIndex: SizeInt; constref AValue: TValue);
function IsType(ATypeInfo: PTypeInfo): boolean; inline;
{$ifndef NoGenericMethods}
generic function IsType<T>: Boolean; inline;
{$endif}
function TryAsOrdinal(out AResult: int64): boolean;
function GetReferenceToRawData: Pointer;
procedure ExtractRawData(ABuffer: Pointer);
procedure ExtractRawDataNoCopy(ABuffer: Pointer);
class operator := (const AValue: String): TValue; inline;
class operator := (AValue: LongInt): TValue; inline;
class operator := (AValue: Single): TValue; inline;
class operator := (AValue: Double): TValue; inline;
{$ifdef FPC_HAS_TYPE_EXTENDED}
class operator := (AValue: Extended): TValue; inline;
{$endif}
class operator := (AValue: Currency): TValue; inline;
class operator := (AValue: Comp): TValue; inline;
class operator := (AValue: Int64): TValue; inline;
class operator := (AValue: QWord): TValue; inline;
class operator := (AValue: TObject): TValue; inline;
class operator := (AValue: TClass): TValue; inline;
class operator := (AValue: Boolean): TValue; inline;
class operator := (AValue: IUnknown): TValue; inline;
property DataSize: SizeInt read GetDataSize;
property Kind: TTypeKind read GetTypeKind;
property TypeData: PTypeData read GetTypeDataProp;
property TypeInfo: PTypeInfo read GetTypeInfo;
property IsEmpty: boolean read GetIsEmpty;
end;
TValueArray = specialize TArray<TValue>;
{ TRttiContext }
TRttiContext = record
private
FContextToken: IInterface;
function GetByHandle(AHandle: Pointer): TRttiObject;
procedure AddObject(AObject: TRttiObject);
public
class function Create: TRttiContext; static;
procedure Free;
function GetType(ATypeInfo: PTypeInfo): TRttiType;
function GetType(AClass: TClass): TRttiType;
//function GetTypes: specialize TArray<TRttiType>;
end;
{ TRttiObject }
TRttiObject = class abstract
protected
function GetHandle: Pointer; virtual; abstract;
public
property Handle: Pointer read GetHandle;
end;
{ TRttiNamedObject }
TRttiNamedObject = class(TRttiObject)
protected
function GetName: string; virtual;
public
property Name: string read GetName;
end;
{ TRttiType }
TRttiType = class(TRttiNamedObject)
private
FTypeInfo: PTypeInfo;
FMethods: specialize TArray<TRttiMethod>;
function GetAsInstance: TRttiInstanceType;
protected
FTypeData: PTypeData;
function GetName: string; override;
function GetHandle: Pointer; override;
function GetIsInstance: boolean; virtual;
function GetIsManaged: boolean; virtual;
function GetIsOrdinal: boolean; virtual;
function GetIsRecord: boolean; virtual;
function GetIsSet: boolean; virtual;
function GetTypeKind: TTypeKind; virtual;
function GetTypeSize: integer; virtual;
function GetBaseType: TRttiType; virtual;
public
constructor Create(ATypeInfo : PTypeInfo);
function GetProperties: specialize TArray<TRttiProperty>; virtual;
function GetProperty(const AName: string): TRttiProperty; virtual;
function GetMethods: specialize TArray<TRttiMethod>; virtual;
function GetMethod(const aName: String): TRttiMethod; virtual;
function GetDeclaredMethods: specialize TArray<TRttiMethod>; virtual;
property IsInstance: boolean read GetIsInstance;
property isManaged: boolean read GetIsManaged;
property IsOrdinal: boolean read GetIsOrdinal;
property IsRecord: boolean read GetIsRecord;
property IsSet: boolean read GetIsSet;
property BaseType: TRttiType read GetBaseType;
property AsInstance: TRttiInstanceType read GetAsInstance;
property TypeKind: TTypeKind read GetTypeKind;
property TypeSize: integer read GetTypeSize;
end;
{ TRttiFloatType }
TRttiFloatType = class(TRttiType)
private
function GetFloatType: TFloatType; inline;
protected
function GetTypeSize: integer; override;
public
property FloatType: TFloatType read GetFloatType;
end;
TRttiOrdinalType = class(TRttiType)
private
function GetMaxValue: LongInt; inline;
function GetMinValue: LongInt; inline;
function GetOrdType: TOrdType; inline;
protected
function GetTypeSize: Integer; override;
public
property OrdType: TOrdType read GetOrdType;
property MinValue: LongInt read GetMinValue;
property MaxValue: LongInt read GetMaxValue;
end;
TRttiInt64Type = class(TRttiType)
private
function GetMaxValue: Int64; inline;
function GetMinValue: Int64; inline;
function GetUnsigned: Boolean; inline;
protected
function GetTypeSize: integer; override;
public
property MinValue: Int64 read GetMinValue;
property MaxValue: Int64 read GetMaxValue;
property Unsigned: Boolean read GetUnsigned;
end;
TRttiStringKind = (skShortString, skAnsiString, skWideString, skUnicodeString);
{ TRttiStringType }
TRttiStringType = class(TRttiType)
private
function GetStringKind: TRttiStringKind;
public
property StringKind: TRttiStringKind read GetStringKind;
end;
TRttiPointerType = class(TRttiType)
private
function GetReferredType: TRttiType;
public
property ReferredType: TRttiType read GetReferredType;
end;
{ TRttiMember }
TMemberVisibility=(mvPrivate, mvProtected, mvPublic, mvPublished);
TRttiMember = class(TRttiNamedObject)
private
FParent: TRttiType;
protected
function GetVisibility: TMemberVisibility; virtual;
public
constructor Create(AParent: TRttiType);
property Visibility: TMemberVisibility read GetVisibility;
property Parent: TRttiType read FParent;
end;
{ TRttiProperty }
TRttiProperty = class(TRttiMember)
private
FPropInfo: PPropInfo;
function GetPropertyType: TRttiType;
function GetIsWritable: boolean;
function GetIsReadable: boolean;
protected
function GetVisibility: TMemberVisibility; override;
function GetName: string; override;
function GetHandle: Pointer; override;
public
constructor Create(AParent: TRttiType; APropInfo: PPropInfo);
function GetValue(Instance: pointer): TValue;
procedure SetValue(Instance: pointer; const AValue: TValue);
property PropertyType: TRttiType read GetPropertyType;
property IsReadable: boolean read GetIsReadable;
property IsWritable: boolean read GetIsWritable;
property Visibility: TMemberVisibility read GetVisibility;
end;
TRttiParameter = class(TRttiNamedObject)
private
FString: String;
protected
function GetParamType: TRttiType; virtual; abstract;
function GetFlags: TParamFlags; virtual; abstract;
public
property ParamType: TRttiType read GetParamType;
property Flags: TParamFlags read GetFlags;
function ToString: String; override;
end;
TMethodImplementationCallbackMethod = procedure(aUserData: Pointer; const aArgs: TValueArray; out aResult: TValue) of object;
TMethodImplementationCallbackProc = procedure(aUserData: Pointer; const aArgs: TValueArray; out aResult: TValue);
TMethodImplementation = class
private
fLowLevelCallback: TFunctionCallCallback;
fCallbackProc: TMethodImplementationCallbackProc;
fCallbackMethod: TMethodImplementationCallbackMethod;
fArgs: specialize TArray<TFunctionCallParameterInfo>;
fArgLen: SizeInt;
fRefArgs: specialize TArray<SizeInt>;
fFlags: TFunctionCallFlags;
fResult: PTypeInfo;
fCC: TCallConv;
function GetCodeAddress: CodePointer;
procedure InitArgs;
procedure HandleCallback(const aArgs: specialize TArray<Pointer>; aResult: Pointer; aContext: Pointer);
constructor Create(aCC: TCallConv; aArgs: specialize TArray<TFunctionCallParameterInfo>; aResult: PTypeInfo; aFlags: TFunctionCallFlags; aUserData: Pointer; aCallback: TMethodImplementationCallbackMethod);
constructor Create(aCC: TCallConv; aArgs: specialize TArray<TFunctionCallParameterInfo>; aResult: PTypeInfo; aFlags: TFunctionCallFlags; aUserData: Pointer; aCallback: TMethodImplementationCallbackProc);
public
constructor Create;
destructor Destroy; override;
property CodeAddress: CodePointer read GetCodeAddress;
end;
TRttiInvokableType = class(TRttiType)
protected
function GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>; virtual; abstract;
function GetCallingConvention: TCallConv; virtual; abstract;
function GetReturnType: TRttiType; virtual; abstract;
function GetFlags: TFunctionCallFlags; virtual; abstract;
public type
TCallbackMethod = procedure(aInvokable: TRttiInvokableType; const aArgs: TValueArray; out aResult: TValue) of object;
TCallbackProc = procedure(aInvokable: TRttiInvokableType; const aArgs: TValueArray; out aResult: TValue);
public
function GetParameters: specialize TArray<TRttiParameter>; inline;
property CallingConvention: TCallConv read GetCallingConvention;
property ReturnType: TRttiType read GetReturnType;
function Invoke(const aProcOrMeth: TValue; const aArgs: array of TValue): TValue; virtual; abstract;
{ Note: once "reference to" is supported these will be replaced by a single method }
function CreateImplementation(aCallback: TCallbackMethod): TMethodImplementation;
function CreateImplementation(aCallback: TCallbackProc): TMethodImplementation;
end;
TRttiMethodType = class(TRttiInvokableType)
private
FCallConv: TCallConv;
FReturnType: TRttiType;
FParams, FParamsAll: specialize TArray<TRttiParameter>;
protected
function GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>; override;
function GetCallingConvention: TCallConv; override;
function GetReturnType: TRttiType; override;
function GetFlags: TFunctionCallFlags; override;
public
function Invoke(const aCallable: TValue; const aArgs: array of TValue): TValue; override;
end;
TRttiProcedureType = class(TRttiInvokableType)
private
FParams, FParamsAll: specialize TArray<TRttiParameter>;
protected
function GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>; override;
function GetCallingConvention: TCallConv; override;
function GetReturnType: TRttiType; override;
function GetFlags: TFunctionCallFlags; override;
public
function Invoke(const aCallable: TValue; const aArgs: array of TValue): TValue; override;
end;
TDispatchKind = (
dkStatic,
dkVtable,
dkDynamic,
dkMessage,
dkInterface,
{ the following are FPC-only and will be moved should Delphi add more }
dkMessageString
);
TRttiMethod = class(TRttiMember)
private
FString: String;
function GetFlags: TFunctionCallFlags;
protected
function GetCallingConvention: TCallConv; virtual; abstract;
function GetCodeAddress: CodePointer; virtual; abstract;
function GetDispatchKind: TDispatchKind; virtual; abstract;
function GetHasExtendedInfo: Boolean; virtual;
function GetIsClassMethod: Boolean; virtual; abstract;
function GetIsConstructor: Boolean; virtual; abstract;
function GetIsDestructor: Boolean; virtual; abstract;
function GetIsStatic: Boolean; virtual; abstract;
function GetMethodKind: TMethodKind; virtual; abstract;
function GetReturnType: TRttiType; virtual; abstract;
function GetVirtualIndex: SmallInt; virtual; abstract;
function GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>; virtual; abstract;
public
property CallingConvention: TCallConv read GetCallingConvention;
property CodeAddress: CodePointer read GetCodeAddress;
property DispatchKind: TDispatchKind read GetDispatchKind;
property HasExtendedInfo: Boolean read GetHasExtendedInfo;
property IsClassMethod: Boolean read GetIsClassMethod;
property IsConstructor: Boolean read GetIsConstructor;
property IsDestructor: Boolean read GetIsDestructor;
property IsStatic: Boolean read GetIsStatic;
property MethodKind: TMethodKind read GetMethodKind;
property ReturnType: TRttiType read GetReturnType;
property VirtualIndex: SmallInt read GetVirtualIndex;
function ToString: String; override;
function GetParameters: specialize TArray<TRttiParameter>; inline;
function Invoke(aInstance: TObject; const aArgs: array of TValue): TValue;
function Invoke(aInstance: TClass; const aArgs: array of TValue): TValue;
function Invoke(aInstance: TValue; const aArgs: array of TValue): TValue;
{ Note: once "reference to" is supported these will be replaced by a single method }
function CreateImplementation(aUserData: Pointer; aCallback: TMethodImplementationCallbackMethod): TMethodImplementation;
function CreateImplementation(aUserData: Pointer; aCallback: TMethodImplementationCallbackProc): TMethodImplementation;
end;
TRttiStructuredType = class(TRttiType)
end;
TInterfaceType = (
itRefCounted, { aka COM interface }
itRaw { aka CORBA interface }
);
TRttiInterfaceType = class(TRttiType)
private
fDeclaredMethods: specialize TArray<TRttiMethod>;
protected
function IntfMethodCount: Word;
function MethodTable: PIntfMethodTable; virtual; abstract;
function GetBaseType: TRttiType; override;
function GetIntfBaseType: TRttiInterfaceType; virtual; abstract;
function GetDeclaringUnitName: String; virtual; abstract;
function GetGUID: TGUID; virtual; abstract;
function GetGUIDStr: String; virtual;
function GetIntfFlags: TIntfFlags; virtual; abstract;
function GetIntfType: TInterfaceType; virtual; abstract;
public
property BaseType: TRttiInterfaceType read GetIntfBaseType;
property DeclaringUnitName: String read GetDeclaringUnitName;
property GUID: TGUID read GetGUID;
property GUIDStr: String read GetGUIDStr;
property IntfFlags: TIntfFlags read GetIntfFlags;
property IntfType: TInterfaceType read GetIntfType;
function GetDeclaredMethods: specialize TArray<TRttiMethod>; override;
end;
{ TRttiInstanceType }
TRttiInstanceType = class(TRttiStructuredType)
private
FPropertiesResolved: Boolean;
FProperties: specialize TArray<TRttiProperty>;
function GetDeclaringUnitName: string;
function GetMetaClassType: TClass;
protected
function GetIsInstance: boolean; override;
function GetTypeSize: integer; override;
function GetBaseType: TRttiType; override;
public
function GetProperties: specialize TArray<TRttiProperty>; override;
property MetaClassType: TClass read GetMetaClassType;
property DeclaringUnitName: string read GetDeclaringUnitName;
end;
TVirtualInterfaceInvokeEvent = procedure(aMethod: TRttiMethod; const aArgs: TValueArray; out aResult: TValue) of object;
TVirtualInterface = class(TInterfacedObject, IInterface)
private
fGUID: TGUID;
fOnInvoke: TVirtualInterfaceInvokeEvent;
fContext: TRttiContext;
fThunks: array[0..2] of CodePointer;
fImpls: array of TMethodImplementation;
fVmt: PCodePointer;
protected
function QueryInterface(constref aIID: TGuid; out aObj): LongInt;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
procedure HandleUserCallback(aUserData: Pointer; const aArgs: TValueArray; out aResult: TValue);
public
constructor Create(aPIID: PTypeInfo);
constructor Create(aPIID: PTypeInfo; aInvokeEvent: TVirtualInterfaceInvokeEvent);
destructor Destroy; override;
property OnInvoke: TVirtualInterfaceInvokeEvent read fOnInvoke write fOnInvoke;
end;
ERtti = class(Exception);
EInsufficientRtti = class(ERtti);
EInvocationError = class(ERtti);
ENonPublicType = class(ERtti);
TFunctionCallParameter = record
ValueRef: Pointer;
ValueSize: SizeInt;
Info: TFunctionCallParameterInfo;
end;
TFunctionCallParameterArray = specialize TArray<TFunctionCallParameter>;
TFunctionCallProc = procedure(const aArgs: specialize TArray<Pointer>; aResult: Pointer; aContext: Pointer);
TFunctionCallMethod = procedure(const aArgs: specialize TArray<Pointer>; aResult: Pointer; aContext: Pointer) of object;
TFunctionCallManager = record
Invoke: procedure(CodeAddress: CodePointer; const Args: TFunctionCallParameterArray; CallingConvention: TCallConv;
ResultType: PTypeInfo; ResultValue: Pointer; Flags: TFunctionCallFlags);
CreateCallbackProc: function(aHandler: TFunctionCallProc; aCallConv: TCallConv; aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags; aContext: Pointer): TFunctionCallCallback;
CreateCallbackMethod: function(aHandler: TFunctionCallMethod; aCallConv: TCallConv; aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags; aContext: Pointer): TFunctionCallCallback;
end;
TFunctionCallManagerArray = array[TCallConv] of TFunctionCallManager;
TCallConvSet = set of TCallConv;
procedure SetFunctionCallManager(aCallConv: TCallConv; constref aFuncCallMgr: TFunctionCallManager; out aOldFuncCallMgr: TFunctionCallManager);
procedure SetFunctionCallManager(aCallConv: TCallConv; constref aFuncCallMgr: TFunctionCallManager);
procedure SetFunctionCallManager(aCallConvs: TCallConvSet; constref aFuncCallMgr: TFunctionCallManager; out aOldFuncCallMgrs: TFunctionCallManagerArray);
procedure SetFunctionCallManager(aCallConvs: TCallConvSet; constref aFuncCallMgr: TFunctionCallManager);
procedure SetFunctionCallManagers(aCallConvs: TCallConvSet; constref aFuncCallMgrs: TFunctionCallManagerArray; out aOldFuncCallMgrs: TFunctionCallManagerArray);
procedure SetFunctionCallManagers(aCallConvs: TCallConvSet; constref aFuncCallMgrs: TFunctionCallManagerArray);
procedure SetFunctionCallManagers(constref aFuncCallMgrs: TFunctionCallManagerArray; out aOldFuncCallMgrs: TFunctionCallManagerArray);
procedure SetFunctionCallManagers(constref aFuncCallMgrs: TFunctionCallManagerArray);
procedure GetFunctionCallManager(aCallConv: TCallConv; out aFuncCallMgr: TFunctionCallManager);
procedure GetFunctionCallManagers(aCallConvs: TCallConvSet; out aFuncCallMgrs: TFunctionCallManagerArray);
procedure GetFunctionCallManagers(out aFuncCallMgrs: TFunctionCallManagerArray);
function Invoke(aCodeAddress: CodePointer; const aArgs: TValueArray; aCallConv: TCallConv;
aResultType: PTypeInfo; aIsStatic: Boolean; aIsConstructor: Boolean): TValue;
function CreateCallbackProc(aHandler: TFunctionCallProc; aCallConv: TCallConv; aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags; aContext: Pointer): TFunctionCallCallback;
function CreateCallbackMethod(aHandler: TFunctionCallMethod; aCallConv: TCallConv; aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags; aContext: Pointer): TFunctionCallCallback;
function IsManaged(TypeInfo: PTypeInfo): boolean;
{$ifndef InLazIDE}
generic function OpenArrayToDynArrayValue<T>(constref aArray: array of T): TValue;
{$endif}
{ these resource strings are needed by units implementing function call managers }
resourcestring
SErrInvokeNotImplemented = 'Invoke functionality is not implemented';
SErrInvokeResultTypeNoValue = 'Function has a result type, but no result pointer provided';
SErrInvokeFailed = 'Invoke call failed';
SErrMethodImplCreateFailed = 'Failed to create method implementation';
SErrCallbackNotImplemented = 'Callback functionality is not implemented';
SErrCallConvNotSupported = 'Calling convention not supported: %s';
SErrTypeKindNotSupported = 'Type kind is not supported: %s';
SErrCallbackHandlerNil = 'Callback handler is Nil';
SErrMissingSelfParam = 'Missing self parameter';
implementation
uses
{$ifdef windows}
Windows,
{$endif}
{$ifdef unix}
BaseUnix,
{$endif}
fgl;
function AlignToPtr(aPtr: Pointer): Pointer; inline;
begin
{$ifdef CPUM68K}
Result := AlignTypeData(aPtr);
{$else}
{$ifdef FPC_REQUIRES_PROPER_ALIGNMENT}
Result := Align(aPtr, SizeOf(Pointer));
{$else}
Result := aPtr;
{$endif}
{$endif}
end;
type
{ TRttiPool }
TRttiPool = class
private type
TRttiObjectMap = specialize TFPGMap<Pointer, TRttiObject>;
private
FObjectMap: TRttiObjectMap;
FTypesList: specialize TArray<TRttiType>;
FTypeCount: LongInt;
FLock: TRTLCriticalSection;
public
function GetTypes: specialize TArray<TRttiType>;
function GetType(ATypeInfo: PTypeInfo): TRttiType;
function GetByHandle(aHandle: Pointer): TRttiObject;
procedure AddObject(aObject: TRttiObject);
constructor Create;
destructor Destroy; override;
end;
IPooltoken = interface
['{3CDB3CE9-AB55-CBAA-7B9D-2F3BB1CF5AF8}']
function RttiPool: TRttiPool;
end;
{ TPoolToken }
TPoolToken = class(TInterfacedObject, IPooltoken)
public
constructor Create;
destructor Destroy; override;
function RttiPool: TRttiPool;
end;
{ TValueDataIntImpl }
TValueDataIntImpl = class(TInterfacedObject, IValueData)
private
FBuffer: Pointer;
FDataSize: SizeInt;
FTypeInfo: PTypeInfo;
FIsCopy: Boolean;
FUseAddRef: Boolean;
public
constructor CreateCopy(ACopyFromBuffer: Pointer; ALen: SizeInt; ATypeInfo: PTypeInfo; AAddRef: Boolean);
constructor CreateRef(AData: Pointer; ATypeInfo: PTypeInfo; AAddRef: Boolean);
destructor Destroy; override;
procedure ExtractRawData(ABuffer: pointer);
procedure ExtractRawDataNoCopy(ABuffer: pointer);
function GetDataSize: SizeInt;
function GetReferenceToRawData: pointer;
end;
TRttiRefCountedInterfaceType = class(TRttiInterfaceType)
private
function IntfData: PInterfaceData; inline;
protected
function MethodTable: PIntfMethodTable; override;
function GetIntfBaseType: TRttiInterfaceType; override;
function GetDeclaringUnitName: String; override;
function GetGUID: TGUID; override;
function GetIntfFlags: TIntfFlags; override;
function GetIntfType: TInterfaceType; override;
end;
TRttiRawInterfaceType = class(TRttiInterfaceType)
private
function IntfData: PInterfaceRawData; inline;
protected
function MethodTable: PIntfMethodTable; override;
function GetIntfBaseType: TRttiInterfaceType; override;
function GetDeclaringUnitName: String; override;
function GetGUID: TGUID; override;
function GetGUIDStr: String; override;
function GetIntfFlags: TIntfFlags; override;
function GetIntfType: TInterfaceType; override;
end;
TRttiVmtMethodParameter = class(TRttiParameter)
private
FVmtMethodParam: PVmtMethodParam;
protected
function GetHandle: Pointer; override;
function GetName: String; override;
function GetFlags: TParamFlags; override;
function GetParamType: TRttiType; override;
public
constructor Create(AVmtMethodParam: PVmtMethodParam);
end;
TRttiMethodTypeParameter = class(TRttiParameter)
private
fHandle: Pointer;
fName: String;
fFlags: TParamFlags;
fType: PTypeInfo;
protected
function GetHandle: Pointer; override;
function GetName: String; override;
function GetFlags: TParamFlags; override;
function GetParamType: TRttiType; override;
public
constructor Create(aHandle: Pointer; const aName: String; aFlags: TParamFlags; aType: PTypeInfo);
end;
TRttiIntfMethod = class(TRttiMethod)
private
FIntfMethodEntry: PIntfMethodEntry;
FIndex: SmallInt;
FParams, FParamsAll: specialize TArray<TRttiParameter>;
protected
function GetHandle: Pointer; override;
function GetName: String; override;
function GetCallingConvention: TCallConv; override;
function GetCodeAddress: CodePointer; override;
function GetDispatchKind: TDispatchKind; override;
function GetHasExtendedInfo: Boolean; override;
function GetIsClassMethod: Boolean; override;
function GetIsConstructor: Boolean; override;
function GetIsDestructor: Boolean; override;
function GetIsStatic: Boolean; override;
function GetMethodKind: TMethodKind; override;
function GetReturnType: TRttiType; override;
function GetVirtualIndex: SmallInt; override;
function GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>; override;
public
constructor Create(AParent: TRttiType; AIntfMethodEntry: PIntfMethodEntry; AIndex: SmallInt);
end;
resourcestring
SErrUnableToGetValueForType = 'Unable to get value for type %s';
SErrUnableToSetValueForType = 'Unable to set value for type %s';
SErrInvalidTypecast = 'Invalid class typecast';
SErrRttiObjectNoHandle = 'RTTI object instance has no valid handle property';
SErrRttiObjectAlreadyRegistered = 'A RTTI object with handle 0x%x is already registered';
SErrInvokeInsufficientRtti = 'Insufficient RTTI to invoke function';
SErrInvokeStaticNoSelf = 'Static function must not be called with in an instance: %s';
SErrInvokeNotStaticNeedsSelf = 'Non static function must be called with an instance: %s';
SErrInvokeClassMethodClassSelf = 'Class method needs to be called with a class type: %s';
SErrInvokeArrayArgExpected = 'Array argument expected for parameter %s of method %s';
SErrInvokeArgInvalidType = 'Invalid type of argument for parameter %s of method %s';
SErrInvokeArgCount = 'Invalid argument count for method %s; expected %d, but got %d';
SErrInvokeNoCodeAddr = 'Failed to determine code address for method: %s';
SErrInvokeRttiDataError = 'The RTTI data is inconsistent for method: %s';
SErrInvokeCallableNotProc = 'The callable value is not a procedure variable for: %s';
SErrInvokeCallableNotMethod = 'The callable value is not a method variable for: %s';
SErrMethodImplNoCallback = 'No callback specified for method implementation';
SErrMethodImplInsufficientRtti = 'Insufficient RTTI to create method implementation';
SErrMethodImplCreateNoArg = 'TMethodImplementation can not be created this way';
SErrVirtIntfTypeNil = 'No type information provided for TVirtualInterface';
SErrVirtIntfTypeMustBeIntf = 'Type ''%s'' is not an interface type';
SErrVirtIntfTypeNotFound = 'Type ''%s'' is not valid';
SErrVirtIntfNotAllMethodsRTTI = 'Not all methods of ''%s'' or its parents have the required RTTI';
SErrVirtIntfRetrieveIInterface = 'Failed to retrieve IInterface information';
SErrVirtIntfCreateThunk = 'Failed to create thunks for ''%0:s''';
SErrVirtIntfCreateImpl = 'Failed to create implementation for method ''%1:s'' of ''%0:s''';
SErrVirtIntfInvalidVirtIdx = 'Virtual index %2:d for method ''%1:s'' of ''%0:s'' is invalid';
SErrVirtIntfMethodNil = 'Method %1:d of ''%0:s'' is Nil';
SErrVirtIntfCreateVmt = 'Failed to create VMT for ''%s''';
SErrVirtIntfIInterface = 'Failed to prepare IInterface method callbacks';
var
PoolRefCount : integer;
GRttiPool : TRttiPool;
FuncCallMgr: TFunctionCallManagerArray;
function AllocateMemory(aSize: PtrUInt): Pointer;
begin
{$IF DEFINED(WINDOWS)}
Result := VirtualAlloc(Nil, aSize, MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
{$ELSEIF DEFINED(UNIX)}
Result := fpmmap(Nil, aSize, PROT_READ or PROT_WRITE, MAP_PRIVATE or MAP_ANONYMOUS, 0, 0);
{$ELSE}
Result := Nil;
{$ENDIF}
end;
function ProtectMemory(aPtr: Pointer; aSize: PtrUInt; aExecutable: Boolean): Boolean;
{$IF DEFINED(WINDOWS)}
var
oldprot: DWORD;
{$ENDIF}
begin
{$IF DEFINED(WINDOWS)}
if aExecutable then
Result := VirtualProtect(aPtr, aSize, PAGE_EXECUTE_READ, oldprot)
else
Result := VirtualProtect(aPtr, aSize, PAGE_READWRITE, oldprot);
{$ELSEIF DEFINED(UNIX)}
if aExecutable then
Result := Fpmprotect(aPtr, aSize, PROT_EXEC or PROT_READ) = 0
else
Result := Fpmprotect(aPtr, aSize, PROT_READ or PROT_WRITE) = 0;
{$ELSE}
Result := False;
{$ENDIF}
end;
procedure FreeMemory(aPtr: Pointer; aSize: PtrUInt);
begin
{$IF DEFINED(WINDOWS)}
VirtualFree(aPtr, 0, MEM_RELEASE);
{$ELSEIF DEFINED(UNIX)}
fpmunmap(aPtr, aSize);
{$ELSE}
{ nothing }
{$ENDIF}
end;
label
RawThunkEnd;
{$if defined(cpui386)}
const
RawThunkPlaceholderBytesToPop = $12341234;
RawThunkPlaceholderProc = $87658765;
RawThunkPlaceholderContext = $43214321;
type
TRawThunkBytesToPop = UInt32;
TRawThunkProc = PtrUInt;
TRawThunkContext = PtrUInt;
{ works for both cdecl and stdcall }
procedure RawThunk; assembler; nostackframe;
asm
{ the stack layout is
$ReturnAddr <- ESP
ArgN
ArgN - 1
...
Arg1
Arg0
aBytesToPop is the size of the stack to the Self argument }
movl RawThunkPlaceholderBytesToPop, %eax
movl %esp, %ecx
lea (%ecx,%eax), %eax
movl RawThunkPlaceholderContext, (%eax)
movl RawThunkPlaceholderProc, %eax
jmp %eax
RawThunkEnd:
end;
{$elseif defined(cpux86_64)}
const
RawThunkPlaceholderProc = PtrUInt($8765876587658765);
RawThunkPlaceholderContext = PtrUInt($4321432143214321);
type
TRawThunkProc = PtrUInt;
TRawThunkContext = PtrUInt;
{$ifdef win64}
procedure RawThunk; assembler; nostackframe;
asm
{ Self is always in register RCX }
movq RawThunkPlaceholderContext, %rcx
movq RawThunkPlaceholderProc, %rax
jmp %rax
RawThunkEnd:
end;
{$else}
procedure RawThunk; assembler; nostackframe;
asm
{ Self is always in register RDI }
movq RawThunkPlaceholderContext, %rdi
movq RawThunkPlaceholderProc, %rax
jmp %rax
RawThunkEnd:
end;
{$endif}
{$elseif defined(cpuarm)}
const
RawThunkPlaceholderProc = $87658765;
RawThunkPlaceholderContext = $43214321;
type
TRawThunkProc = PtrUInt;
TRawThunkContext = PtrUInt;
procedure RawThunk; assembler; nostackframe;
asm
(* To be compatible with Thumb we first load the function pointer into R0,
then move that to R12 which is volatile and then we load the new Self into
R0 *)
ldr r0, .LProc
mov r12, r0
ldr r0, .LContext
{$ifdef CPUARM_HAS_BX}
bx r12
{$else}
mov pc, r12
{$endif}
.LProc:
.long RawThunkPlaceholderProc
.LContext:
.long RawThunkPlaceholderContext
RawThunkEnd:
end;
{$elseif defined(cpuaarch64)}
const
RawThunkPlaceholderProc = $8765876587658765;
RawThunkPlaceholderContext = $4321432143214321;
type
TRawThunkProc = PtrUInt;
TRawThunkContext = PtrUInt;
procedure RawThunk; assembler; nostackframe;
asm
ldr x16, .LProc
ldr x0, .LContext
br x16
.LProc:
.quad RawThunkPlaceholderProc
.LContext:
.quad RawThunkPlaceholderContext
RawThunkEnd:
end;
{$elseif defined(cpum68k)}
const
RawThunkPlaceholderProc = $87658765;
RawThunkPlaceholderContext = $43214321;
type
TRawThunkProc = PtrUInt;
TRawThunkContext = PtrUInt;
procedure RawThunk; assembler; nostackframe;
asm
lea 4(sp), a0
move.l #RawThunkPlaceholderContext, (a0)
move.l #RawThunkPlaceholderProc, a0
jmp (a0)
RawThunkEnd:
end;
{$endif}
{$if declared(RawThunk)}
const
RawThunkEndPtr: Pointer = @RawThunkEnd;
type
{$if declared(TRawThunkBytesToPop)}
PRawThunkBytesToPop = ^TRawThunkBytesToPop;
{$endif}
PRawThunkContext = ^TRawThunkContext;
PRawThunkProc = ^TRawThunkProc;
{$endif}
{ Delphi has these as part of TRawVirtualClass.TVTable; until we have that we
simply leave that here in the implementation }
function AllocateRawThunk(aProc: CodePointer; aContext: Pointer; aBytesToPop: SizeInt): CodePointer;
{$if declared(RawThunk)}
var
size, i: SizeInt;
{$if declared(TRawThunkBytesToPop)}
btp: PRawThunkBytesToPop;
btpdone: Boolean;
{$endif}
context: PRawThunkContext;
contextdone: Boolean;
proc: PRawThunkProc;
procdone: Boolean;
{$endif}
begin
{$if not declared(RawThunk)}
{ platform dose not have thunk support... :/ }
Result := Nil;
{$else}
Size := PtrUInt(RawThunkEndPtr) - PtrUInt(@RawThunk) + 1;
Result := AllocateMemory(size);
Move(Pointer(@RawThunk)^, Result^, size);
{$if declared(TRawThunkBytesToPop)}
btpdone := False;
{$endif}
contextdone := False;
procdone := False;
for i := 0 to Size - 1 do begin
{$if declared(TRawThunkBytesToPop)}
if not btpdone and (i <= Size - SizeOf(TRawThunkBytesToPop)) then begin
btp := PRawThunkBytesToPop(PByte(Result) + i);
if btp^ = TRawThunkBytesToPop(RawThunkPlaceholderBytesToPop) then begin
btp^ := TRawThunkBytesToPop(aBytesToPop);
btpdone := True;
end;
end;
{$endif}
if not contextdone and (i <= Size - SizeOf(TRawThunkContext)) then begin
context := PRawThunkContext(PByte(Result) + i);
if context^ = TRawThunkContext(RawThunkPlaceholderContext) then begin
context^ := TRawThunkContext(aContext);
contextdone := True;
end;
end;
if not procdone and (i <= Size - SizeOf(TRawThunkProc)) then begin
proc := PRawThunkProc(PByte(Result) + i);
if proc^ = TRawThunkProc(RawThunkPlaceholderProc) then begin
proc^ := TRawThunkProc(aProc);
procdone := True;
end;
end;
end;
if not contextdone or not procdone
{$if declared(TRawThunkBytesToPop)}
or not btpdone
{$endif}
then begin
FreeMemory(Result, Size);
Result := Nil;
end else
ProtectMemory(Result, Size, True);
{$endif}
end;
procedure FreeRawThunk(aThunk: CodePointer);
begin
{$if declared(RawThunk)}
FreeMemory(aThunk, PtrUInt(RawThunkEndPtr) - PtrUInt(@RawThunk));
{$endif}
end;
function CCToStr(aCC: TCallConv): String; inline;
begin
WriteStr(Result, aCC);
end;
procedure NoInvoke(aCodeAddress: CodePointer; const aArgs: TFunctionCallParameterArray; aCallConv: TCallConv;
aResultType: PTypeInfo; aResultValue: Pointer; aFlags: TFunctionCallFlags);
begin
raise ENotImplemented.Create(SErrInvokeNotImplemented);
end;
function NoCreateCallbackProc(aFunc: TFunctionCallProc; aCallConv: TCallConv; aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags; aContext: Pointer): TFunctionCallCallback;
begin
Result := Nil;
raise ENotImplemented.Create(SErrCallbackNotImplemented);
end;
function NoCreateCallbackMethod(aFunc: TFunctionCallMethod; aCallConv: TCallConv; aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags; aContext: Pointer): TFunctionCallCallback;
begin
Result := Nil;
raise ENotImplemented.Create(SErrCallbackNotImplemented);
end;
const
NoFunctionCallManager: TFunctionCallManager = (
Invoke: @NoInvoke;
CreateCallbackProc: @NoCreateCallbackProc;
CreateCallbackMethod: @NoCreateCallbackMethod;
);
procedure SetFunctionCallManager(aCallConv: TCallConv; constref aFuncCallMgr: TFunctionCallManager;
out aOldFuncCallMgr: TFunctionCallManager);
begin
aOldFuncCallMgr := FuncCallMgr[aCallConv];
FuncCallMgr[aCallConv] := aFuncCallMgr;
end;
procedure SetFunctionCallManager(aCallConv: TCallConv; constref aFuncCallMgr: TFunctionCallManager);
var
dummy: TFunctionCallManager;
begin
SetFunctionCallManager(aCallConv, aFuncCallMgr, dummy);
end;
procedure SetFunctionCallManager(aCallConvs: TCallConvSet; constref aFuncCallMgr: TFunctionCallManager;
out aOldFuncCallMgrs: TFunctionCallManagerArray);
var
cc: TCallConv;
begin
for cc := Low(TCallConv) to High(TCallConv) do
if cc in aCallConvs then begin
aOldFuncCallMgrs[cc] := FuncCallMgr[cc];
FuncCallMgr[cc] := aFuncCallMgr;
end else
aOldFuncCallMgrs[cc] := Default(TFunctionCallManager);
end;
procedure SetFunctionCallManager(aCallConvs: TCallConvSet; constref aFuncCallMgr: TFunctionCallManager);
var
dummy: TFunctionCallManagerArray;
begin
SetFunctionCallManager(aCallConvs, aFuncCallMgr, dummy);
end;
procedure SetFunctionCallManagers(aCallConvs: TCallConvSet; constref aFuncCallMgrs: TFunctionCallManagerArray; out aOldFuncCallMgrs: TFunctionCallManagerArray);
var
cc: TCallConv;
begin
for cc := Low(TCallConv) to High(TCallConv) do
if cc in aCallConvs then begin
aOldFuncCallMgrs[cc] := FuncCallMgr[cc];
FuncCallMgr[cc] := aFuncCallMgrs[cc];
end else
aOldFuncCallMgrs[cc] := Default(TFunctionCallManager);
end;
procedure SetFunctionCallManagers(aCallConvs: TCallConvSet; constref aFuncCallMgrs: TFunctionCallManagerArray);
var
dummy: TFunctionCallManagerArray;
begin
SetFunctionCallManagers(aCallConvs, aFuncCallMgrs, dummy);
end;
procedure SetFunctionCallManagers(constref aFuncCallMgrs: TFunctionCallManagerArray; out aOldFuncCallMgrs: TFunctionCallManagerArray);
begin
aOldFuncCallMgrs := FuncCallMgr;
FuncCallMgr := aFuncCallMgrs;
end;
procedure SetFunctionCallManagers(constref aFuncCallMgrs: TFunctionCallManagerArray);
var
dummy: TFunctionCallManagerArray;
begin
SetFunctionCallManagers(aFuncCallMgrs, dummy);
end;
procedure GetFunctionCallManager(aCallConv: TCallConv; out aFuncCallMgr: TFunctionCallManager);
begin
aFuncCallMgr := FuncCallMgr[aCallConv];
end;
procedure GetFunctionCallManagers(aCallConvs: TCallConvSet; out aFuncCallMgrs: TFunctionCallManagerArray);
var
cc: TCallConv;
begin
for cc := Low(TCallConv) to High(TCallConv) do
if cc in aCallConvs then
aFuncCallMgrs[cc] := FuncCallMgr[cc]
else
aFuncCallMgrs[cc] := Default(TFunctionCallManager);
end;
procedure GetFunctionCallManagers(out aFuncCallMgrs: TFunctionCallManagerArray);
begin
aFuncCallMgrs := FuncCallMgr;
end;
procedure InitDefaultFunctionCallManager;
var
cc: TCallConv;
begin
for cc := Low(TCallConv) to High(TCallConv) do
FuncCallMgr[cc] := NoFunctionCallManager;
end;
{ TRttiPool }
function TRttiPool.GetTypes: specialize TArray<TRttiType>;
begin
if not Assigned(FTypesList) then
Exit(Nil);
{$ifdef FPC_HAS_FEATURE_THREADING}
EnterCriticalsection(FLock);
try
{$endif}
Result := Copy(FTypesList, 0, FTypeCount);
{$ifdef FPC_HAS_FEATURE_THREADING}
finally
LeaveCriticalsection(FLock);
end;
{$endif}
end;
function TRttiPool.GetType(ATypeInfo: PTypeInfo): TRttiType;
var
obj: TRttiObject;
begin
if not Assigned(ATypeInfo) then
Exit(Nil);
{$ifdef FPC_HAS_FEATURE_THREADING}
EnterCriticalsection(FLock);
try
{$endif}
Result := Nil;
obj := GetByHandle(ATypeInfo);
if Assigned(obj) then
Result := obj as TRttiType;
if not Assigned(Result) then
begin
if FTypeCount = Length(FTypesList) then
begin
SetLength(FTypesList, FTypeCount * 2);
end;
case ATypeInfo^.Kind of
tkClass : Result := TRttiInstanceType.Create(ATypeInfo);
tkInterface: Result := TRttiRefCountedInterfaceType.Create(ATypeInfo);
tkInterfaceRaw: Result := TRttiRawInterfaceType.Create(ATypeInfo);
tkInt64,
tkQWord: Result := TRttiInt64Type.Create(ATypeInfo);
tkInteger,
tkChar,
tkWChar: Result := TRttiOrdinalType.Create(ATypeInfo);
tkSString,
tkLString,
tkAString,
tkUString,
tkWString : Result := TRttiStringType.Create(ATypeInfo);
tkFloat : Result := TRttiFloatType.Create(ATypeInfo);
tkPointer : Result := TRttiPointerType.Create(ATypeInfo);
tkProcVar : Result := TRttiProcedureType.Create(ATypeInfo);
tkMethod : Result := TRttiMethodType.Create(ATypeInfo);
else
Result := TRttiType.Create(ATypeInfo);
end;
FTypesList[FTypeCount] := Result;
FObjectMap.Add(ATypeInfo, Result);
Inc(FTypeCount);
end;
{$ifdef FPC_HAS_FEATURE_THREADING}
finally
LeaveCriticalsection(FLock);
end;
{$endif}
end;
function TRttiPool.GetByHandle(aHandle: Pointer): TRttiObject;
var
idx: LongInt;
begin
if not Assigned(aHandle) then
Exit(Nil);
{$ifdef FPC_HAS_FEATURE_THREADING}
EnterCriticalsection(FLock);
try
{$endif}
idx := FObjectMap.IndexOf(aHandle);
if idx < 0 then
Result := Nil
else
Result := FObjectMap.Data[idx];
{$ifdef FPC_HAS_FEATURE_THREADING}
finally
LeaveCriticalsection(FLock);
end;
{$endif}
end;
procedure TRttiPool.AddObject(aObject: TRttiObject);
var
idx: LongInt;
begin
if not Assigned(aObject) then
Exit;
if not Assigned(aObject.Handle) then
raise EArgumentException.Create(SErrRttiObjectNoHandle);
{$ifdef FPC_HAS_FEATURE_THREADING}
EnterCriticalsection(FLock);
try
{$endif}
idx := FObjectMap.IndexOf(aObject.Handle);
if idx < 0 then
FObjectMap.Add(aObject.Handle, aObject)
else if FObjectMap.Data[idx] <> aObject then
raise EInvalidOpException.CreateFmt(SErrRttiObjectAlreadyRegistered, [aObject.Handle]);
{$ifdef FPC_HAS_FEATURE_THREADING}
finally
LeaveCriticalsection(FLock);
end;
{$endif}
end;
constructor TRttiPool.Create;
begin
{$ifdef FPC_HAS_FEATURE_THREADING}
InitCriticalSection(FLock);
{$endif}
SetLength(FTypesList, 32);
FObjectMap := TRttiObjectMap.Create;
end;
destructor TRttiPool.Destroy;
var
i: LongInt;
begin
for i := 0 to FObjectMap.Count - 1 do
FObjectMap.Data[i].Free;
FObjectMap.Free;
{$ifdef FPC_HAS_FEATURE_THREADING}
DoneCriticalsection(FLock);
{$endif}
inherited Destroy;
end;
{ TPoolToken }
constructor TPoolToken.Create;
begin
inherited Create;
if InterlockedIncrement(PoolRefCount)=1 then
GRttiPool := TRttiPool.Create;
end;
destructor TPoolToken.Destroy;
begin
if InterlockedDecrement(PoolRefCount)=0 then
GRttiPool.Free;
inherited;
end;
function TPoolToken.RttiPool: TRttiPool;
begin
result := GRttiPool;
end;
{ TValueDataIntImpl }
procedure IntFinalize(APointer, ATypeInfo: Pointer);
external name 'FPC_FINALIZE';
procedure IntInitialize(APointer, ATypeInfo: Pointer);
external name 'FPC_INITIALIZE';
procedure IntAddRef(APointer, ATypeInfo: Pointer);
external name 'FPC_ADDREF';
function IntCopy(ASource, ADest, ATypeInfo: Pointer): SizeInt;
external name 'FPC_COPY';
constructor TValueDataIntImpl.CreateCopy(ACopyFromBuffer: Pointer; ALen: SizeInt; ATypeInfo: PTypeInfo; AAddRef: Boolean);
begin
FTypeInfo := ATypeInfo;
FDataSize:=ALen;
if ALen>0 then
begin
Getmem(FBuffer,FDataSize);
if Assigned(ACopyFromBuffer) then
system.move(ACopyFromBuffer^,FBuffer^,FDataSize)
else
FillChar(FBuffer^, FDataSize, 0);
end;
FIsCopy := True;
FUseAddRef := AAddRef;
if AAddRef and (ALen > 0) then begin
if Assigned(ACopyFromBuffer) then
IntAddRef(FBuffer, FTypeInfo)
else
IntInitialize(FBuffer, FTypeInfo);
end;
end;
constructor TValueDataIntImpl.CreateRef(AData: Pointer; ATypeInfo: PTypeInfo; AAddRef: Boolean);
begin
FTypeInfo := ATypeInfo;
FDataSize := SizeOf(Pointer);
if Assigned(AData) then
FBuffer := PPointer(AData)^
else
FBuffer := Nil;
FIsCopy := False;
FUseAddRef := AAddRef;
if AAddRef and Assigned(AData) then
IntAddRef(@FBuffer, FTypeInfo);
end;
destructor TValueDataIntImpl.Destroy;
begin
if Assigned(FBuffer) then begin
if FUseAddRef then
if FIsCopy then
IntFinalize(FBuffer, FTypeInfo)
else
IntFinalize(@FBuffer, FTypeInfo);
if FIsCopy then
Freemem(FBuffer);
end;
inherited Destroy;
end;
procedure TValueDataIntImpl.ExtractRawData(ABuffer: pointer);
begin
if FDataSize = 0 then
Exit;
if FIsCopy then
System.Move(FBuffer^, ABuffer^, FDataSize)
else
System.Move(FBuffer{!}, ABuffer^, FDataSize);
if FUseAddRef then
IntAddRef(ABuffer, FTypeInfo);
end;
procedure TValueDataIntImpl.ExtractRawDataNoCopy(ABuffer: pointer);
begin
if FDataSize = 0 then
Exit;
if FIsCopy then
system.move(FBuffer^, ABuffer^, FDataSize)
else
System.Move(FBuffer{!}, ABuffer^, FDataSize);
end;
function TValueDataIntImpl.GetDataSize: SizeInt;
begin
result := FDataSize;
end;
function TValueDataIntImpl.GetReferenceToRawData: pointer;
begin
if FIsCopy then
result := FBuffer
else
result := @FBuffer;
end;
{ TValue }
class function TValue.Empty: TValue;
begin
result.FData.FTypeInfo := nil;
{$if SizeOf(TMethod) > SizeOf(QWord)}
Result.FData.FAsMethod.Code := Nil;
Result.FData.FAsMethod.Data := Nil;
{$else}
Result.FData.FAsUInt64 := 0;
{$endif}
end;
function TValue.GetTypeDataProp: PTypeData;
begin
result := GetTypeData(FData.FTypeInfo);
end;
function TValue.GetTypeInfo: PTypeInfo;
begin
result := FData.FTypeInfo;
end;
function TValue.GetTypeKind: TTypeKind;
begin
if not Assigned(FData.FTypeInfo) then
Result := tkUnknown
else
result := FData.FTypeInfo^.Kind;
end;
function TValue.GetDataSize: SizeInt;
begin
if Assigned(FData.FValueData) and (Kind <> tkSString) then
Result := FData.FValueData.GetDataSize
else begin
Result := 0;
case Kind of
tkEnumeration,
tkBool,
tkInt64,
tkQWord,
tkInteger:
case TypeData^.OrdType of
otSByte,
otUByte:
Result := SizeOf(Byte);
otSWord,
otUWord:
Result := SizeOf(Word);
otSLong,
otULong:
Result := SizeOf(LongWord);
otSQWord,
otUQWord:
Result := SizeOf(QWord);
end;
tkChar:
Result := SizeOf(AnsiChar);
tkFloat:
case TypeData^.FloatType of
ftSingle:
Result := SizeOf(Single);
ftDouble:
Result := SizeOf(Double);
ftExtended:
Result := SizeOf(Extended);
ftComp:
Result := SizeOf(Comp);
ftCurr:
Result := SizeOf(Currency);
end;
tkSet:
Result := TypeData^.SetSize;
tkMethod:
Result := SizeOf(TMethod);
tkSString:
{ ShortString can hold max. 254 characters as [0] is Length and [255] is #0 }
Result := SizeOf(ShortString) - 2;
tkVariant:
Result := SizeOf(Variant);
tkProcVar:
Result := SizeOf(CodePointer);
tkWChar:
Result := SizeOf(WideChar);
tkUChar:
Result := SizeOf(UnicodeChar);
tkFile:
{ ToDo }
Result := SizeOf(TTextRec);
tkAString,
tkWString,
tkUString,
tkInterface,
tkDynArray,
tkClass,
tkHelper,
tkClassRef,
tkInterfaceRaw,
tkPointer:
Result := SizeOf(Pointer);
tkObject,
tkRecord:
Result := TypeData^.RecSize;
tkArray:
Result := TypeData^.ArrayData.Size;
tkUnknown,
tkLString:
Assert(False);
end;
end;
end;
class procedure TValue.Make(ABuffer: pointer; ATypeInfo: PTypeInfo; out result: TValue);
type
PMethod = ^TMethod;
var
td: PTypeData;
begin
result.FData.FTypeInfo:=ATypeInfo;
{ resets the whole variant part; FValueData is already Nil }
{$if SizeOf(TMethod) > SizeOf(QWord)}
Result.FData.FAsMethod.Code := Nil;
Result.FData.FAsMethod.Data := Nil;
{$else}
Result.FData.FAsUInt64 := 0;
{$endif}
if not Assigned(ATypeInfo) then
Exit;
{ first handle those types that need a TValueData implementation }
case ATypeInfo^.Kind of
tkSString : begin
td := GetTypeData(ATypeInfo);
result.FData.FValueData := TValueDataIntImpl.CreateCopy(ABuffer, td^.MaxLength + 1, ATypeInfo, True);
end;
tkWString,
tkUString,
tkAString : result.FData.FValueData := TValueDataIntImpl.CreateRef(ABuffer, ATypeInfo, True);
tkDynArray : result.FData.FValueData := TValueDataIntImpl.CreateRef(ABuffer, ATypeInfo, True);
tkArray : result.FData.FValueData := TValueDataIntImpl.CreateCopy(ABuffer, Result.TypeData^.ArrayData.Size, ATypeInfo, False);
tkObject,
tkRecord : result.FData.FValueData := TValueDataIntImpl.CreateCopy(ABuffer, Result.TypeData^.RecSize, ATypeInfo, False);
tkInterface: result.FData.FValueData := TValueDataIntImpl.CreateRef(ABuffer, ATypeInfo, True);
end;
if not Assigned(ABuffer) then
Exit;
{ now handle those that are happy with the variant part of FData }
case ATypeInfo^.Kind of
tkSString,
tkWString,
tkUString,
tkAString,
tkDynArray,
tkArray,
tkObject,
tkRecord,
tkInterface:
{ ignore }
;
tkClass : result.FData.FAsObject := PPointer(ABuffer)^;
tkClassRef : result.FData.FAsClass := PClass(ABuffer)^;
tkInterfaceRaw : result.FData.FAsPointer := PPointer(ABuffer)^;
tkInt64 : result.FData.FAsSInt64 := PInt64(ABuffer)^;
tkQWord : result.FData.FAsUInt64 := PQWord(ABuffer)^;
tkProcVar : result.FData.FAsMethod.Code := PCodePointer(ABuffer)^;
tkMethod : result.FData.FAsMethod := PMethod(ABuffer)^;
tkPointer : result.FData.FAsPointer := PPointer(ABuffer)^;
tkSet : begin
td := GetTypeData(ATypeInfo);
case td^.OrdType of
otUByte: begin
{ this can either really be 1 Byte or a set > 32-bit, so
check the underlying type }
if not (td^.CompType^.Kind in [tkInteger,tkEnumeration]) then
raise Exception.CreateFmt(SErrUnableToGetValueForType,[ATypeInfo^.Name]);
case td^.SetSize of
0, 1:
Result.FData.FAsUByte := PByte(ABuffer)^;
{ these two cases shouldn't happen, but better safe than sorry... }
2:
Result.FData.FAsUWord := PWord(ABuffer)^;
3, 4:
Result.FData.FAsULong := PLongWord(ABuffer)^;
{ maybe we should also allow storage as otUQWord? }
5..8:
Result.FData.FAsUInt64 := PQWord(ABuffer)^;
else
Result.FData.FValueData := TValueDataIntImpl.CreateCopy(ABuffer, td^.SetSize, ATypeInfo, False);
end;
end;
otUWord:
Result.FData.FAsUWord := PWord(ABuffer)^;
otULong:
Result.FData.FAsULong := PLongWord(ABuffer)^;
else
{ ehm... Panic? }
raise Exception.CreateFmt(SErrUnableToGetValueForType,[ATypeInfo^.Name]);
end;
end;
tkChar,
tkWChar,
tkUChar,
tkEnumeration,
tkInteger : begin
case GetTypeData(ATypeInfo)^.OrdType of
otSByte: result.FData.FAsSByte := PShortInt(ABuffer)^;
otUByte: result.FData.FAsUByte := PByte(ABuffer)^;
otSWord: result.FData.FAsSWord := PSmallInt(ABuffer)^;
otUWord: result.FData.FAsUWord := PWord(ABuffer)^;
otSLong: result.FData.FAsSLong := PLongInt(ABuffer)^;
otULong: result.FData.FAsULong := PLongWord(ABuffer)^;
end;
end;
tkBool : begin
case GetTypeData(ATypeInfo)^.OrdType of
otUByte: result.FData.FAsUByte := Byte(System.PBoolean(ABuffer)^);
otUWord: result.FData.FAsUWord := Word(PBoolean16(ABuffer)^);
otULong: result.FData.FAsULong := DWord(PBoolean32(ABuffer)^);
otUQWord: result.FData.FAsUInt64 := QWord(PBoolean64(ABuffer)^);
otSByte: result.FData.FAsSByte := ShortInt(PByteBool(ABuffer)^);
otSWord: result.FData.FAsSWord := SmallInt(PWordBool(ABuffer)^);
otSLong: result.FData.FAsSLong := LongInt(PLongBool(ABuffer)^);
otSQWord: result.FData.FAsSInt64 := Int64(PQWordBool(ABuffer)^);
end;
end;
tkFloat : begin
case GetTypeData(ATypeInfo)^.FloatType of
ftCurr : result.FData.FAsCurr := PCurrency(ABuffer)^;
ftSingle : result.FData.FAsSingle := PSingle(ABuffer)^;
ftDouble : result.FData.FAsDouble := PDouble(ABuffer)^;
ftExtended: result.FData.FAsExtended := PExtended(ABuffer)^;
ftComp : result.FData.FAsComp := PComp(ABuffer)^;
end;
end;
else
raise Exception.CreateFmt(SErrUnableToGetValueForType,[ATypeInfo^.Name]);
end;
end;
class procedure TValue.MakeOpenArray(AArray: Pointer; ALength: SizeInt; ATypeInfo: PTypeInfo; out Result: TValue);
var
el: TValue;
begin
Result.FData.FTypeInfo := ATypeInfo;
{ resets the whole variant part; FValueData is already Nil }
{$if SizeOf(TMethod) > SizeOf(QWord)}
Result.FData.FAsMethod.Code := Nil;
Result.FData.FAsMethod.Data := Nil;
{$else}
Result.FData.FAsUInt64 := 0;
{$endif}
if not Assigned(ATypeInfo) then
Exit;
if ATypeInfo^.Kind <> tkArray then
Exit;
if not Assigned(AArray) then
Exit;
if ALength < 0 then
Exit;
Result.FData.FValueData := TValueDataIntImpl.CreateRef(@AArray, ATypeInfo, False);
Result.FData.FArrLength := ALength;
Make(Nil, Result.TypeData^.ArrayData.ElType, el);
Result.FData.FElSize := el.DataSize;
end;
{$ifndef NoGenericMethods}
generic class function TValue.From<T>(constref aValue: T): TValue;
begin
TValue.Make(@aValue, PTypeInfo(System.TypeInfo(T)), Result);
end;
generic class function TValue.FromOpenArray<T>(constref aValue: array of T): TValue;
var
arrdata: Pointer;
begin
if Length(aValue) > 0 then
arrdata := @aValue[0]
else
arrdata := Nil;
TValue.MakeOpenArray(arrdata, Length(aValue), PTypeInfo(System.TypeInfo(aValue)), Result);
end;
{$endif}
class function TValue.FromOrdinal(aTypeInfo: PTypeInfo; aValue: Int64): TValue;
{$ifdef ENDIAN_BIG}
var
p: PByte;
td: PTypeData;
{$endif}
begin
if not Assigned(aTypeInfo) or
not (aTypeInfo^.Kind in [tkInteger, tkInt64, tkQWord, tkEnumeration, tkBool, tkChar, tkWChar, tkUChar]) then
raise EInvalidCast.Create(SErrInvalidTypecast);
{$ifdef ENDIAN_BIG}
td := GetTypeData(aTypeInfo);
p := @aValue;
case td^.OrdType of
otSByte,
otUByte:
p := p + 7;
otSWord,
otUWord:
p := p + 6;
otSLong,
otULong:
p := p + 4;
otSQWord,
otUQWord: ;
end;
TValue.Make(p, aTypeInfo, Result);
{$else}
TValue.Make(@aValue, aTypeInfo, Result);
{$endif}
end;
function TValue.GetIsEmpty: boolean;
begin
result := (FData.FTypeInfo=nil) or
((Kind in [tkSString, tkObject, tkRecord, tkArray]) and not Assigned(FData.FValueData)) or
((Kind in [tkClass, tkClassRef, tkInterfaceRaw]) and not Assigned(FData.FAsPointer));
end;
function TValue.IsArray: boolean;
begin
result := kind in [tkArray, tkDynArray];
end;
function TValue.IsOpenArray: Boolean;
var
td: PTypeData;
begin
td := TypeData;
Result := (Kind = tkArray) and (td^.ArrayData.Size = 0) and (td^.ArrayData.ElCount = 0)
end;
function TValue.AsString: string;
begin
if System.GetTypeKind(String) = tkUString then
Result := String(AsUnicodeString)
else
Result := String(AsAnsiString);
end;
function TValue.AsUnicodeString: UnicodeString;
begin
if (Kind in [tkSString, tkAString, tkUString, tkWString]) and not Assigned(FData.FValueData) then
Result := ''
else
case Kind of
tkSString:
Result := UnicodeString(PShortString(FData.FValueData.GetReferenceToRawData)^);
tkAString:
Result := UnicodeString(PAnsiString(FData.FValueData.GetReferenceToRawData)^);
tkWString:
Result := UnicodeString(PWideString(FData.FValueData.GetReferenceToRawData)^);
tkUString:
Result := UnicodeString(PUnicodeString(FData.FValueData.GetReferenceToRawData)^);
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
end;
function TValue.AsAnsiString: AnsiString;
begin
if (Kind in [tkSString, tkAString, tkUString, tkWString]) and not Assigned(FData.FValueData) then
Result := ''
else
case Kind of
tkSString:
Result := AnsiString(PShortString(FData.FValueData.GetReferenceToRawData)^);
tkAString:
Result := AnsiString(PAnsiString(FData.FValueData.GetReferenceToRawData)^);
tkWString:
Result := AnsiString(PWideString(FData.FValueData.GetReferenceToRawData)^);
tkUString:
Result := AnsiString(PUnicodeString(FData.FValueData.GetReferenceToRawData)^);
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
end;
function TValue.AsExtended: Extended;
begin
if Kind = tkFloat then
begin
case TypeData^.FloatType of
ftSingle : result := FData.FAsSingle;
ftDouble : result := FData.FAsDouble;
ftExtended : result := FData.FAsExtended;
ftCurr : result := FData.FAsCurr;
ftComp : result := FData.FAsComp;
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
end
else if Kind in [tkInteger, tkInt64, tkQWord] then
Result := AsInt64
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
function TValue.IsObject: boolean;
begin
result := (Kind = tkClass) or ((Kind = tkUnknown) and not Assigned(FData.FAsObject));
end;
function TValue.IsClass: boolean;
begin
result := (Kind = tkClassRef) or ((Kind in [tkClass,tkUnknown]) and not Assigned(FData.FAsObject));
end;
function TValue.IsOrdinal: boolean;
begin
result := (Kind in [tkInteger, tkInt64, tkQWord, tkBool, tkEnumeration, tkChar, tkWChar, tkUChar]) or
((Kind in [tkClass, tkClassRef, tkInterfaceRaw, tkUnknown]) and not Assigned(FData.FAsPointer));
end;
function TValue.IsType(ATypeInfo: PTypeInfo): boolean;
begin
result := ATypeInfo = TypeInfo;
end;
{$ifndef NoGenericMethods}
generic function TValue.IsType<T>: Boolean;
begin
Result := IsType(PTypeInfo(System.TypeInfo(T)));
end;
{$endif}
function TValue.AsObject: TObject;
begin
if IsObject or (IsClass and not Assigned(FData.FAsObject)) then
result := TObject(FData.FAsObject)
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
function TValue.AsClass: TClass;
begin
if IsClass then
result := FData.FAsClass
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
function TValue.AsBoolean: boolean;
begin
if (Kind = tkBool) then
case TypeData^.OrdType of
otSByte: Result := ByteBool(FData.FAsSByte);
otUByte: Result := Boolean(FData.FAsUByte);
otSWord: Result := WordBool(FData.FAsSWord);
otUWord: Result := Boolean16(FData.FAsUWord);
otSLong: Result := LongBool(FData.FAsSLong);
otULong: Result := Boolean32(FData.FAsULong);
otSQWord: Result := QWordBool(FData.FAsSInt64);
otUQWord: Result := Boolean64(FData.FAsUInt64);
end
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
function TValue.AsOrdinal: Int64;
begin
if IsOrdinal then
if Kind in [tkClass, tkClassRef, tkInterfaceRaw, tkUnknown] then
Result := 0
else
case TypeData^.OrdType of
otSByte: Result := FData.FAsSByte;
otUByte: Result := FData.FAsUByte;
otSWord: Result := FData.FAsSWord;
otUWord: Result := FData.FAsUWord;
otSLong: Result := FData.FAsSLong;
otULong: Result := FData.FAsULong;
otSQWord: Result := FData.FAsSInt64;
otUQWord: Result := FData.FAsUInt64;
end
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
function TValue.AsCurrency: Currency;
begin
if (Kind = tkFloat) and (TypeData^.FloatType=ftCurr) then
result := FData.FAsCurr
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
function TValue.AsInteger: Integer;
begin
if Kind in [tkInteger, tkInt64, tkQWord] then
case TypeData^.OrdType of
otSByte: Result := FData.FAsSByte;
otUByte: Result := FData.FAsUByte;
otSWord: Result := FData.FAsSWord;
otUWord: Result := FData.FAsUWord;
otSLong: Result := FData.FAsSLong;
otULong: Result := FData.FAsULong;
otSQWord: Result := FData.FAsSInt64;
otUQWord: Result := FData.FAsUInt64;
end
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
function TValue.AsAnsiChar: AnsiChar;
begin
if Kind = tkChar then
Result := Chr(FData.FAsUByte)
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
function TValue.AsWideChar: WideChar;
begin
if Kind = tkWChar then
Result := WideChar(FData.FAsUWord)
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
function TValue.AsChar: Char;
begin
{$if SizeOf(Char) = 1}
Result := AsAnsiChar;
{$else}
Result := AsWideChar;
{$endif}
end;
function TValue.AsInt64: Int64;
begin
if Kind in [tkInteger, tkInt64, tkQWord] then
case TypeData^.OrdType of
otSByte: Result := FData.FAsSByte;
otUByte: Result := FData.FAsUByte;
otSWord: Result := FData.FAsSWord;
otUWord: Result := FData.FAsUWord;
otSLong: Result := FData.FAsSLong;
otULong: Result := FData.FAsULong;
otSQWord: Result := FData.FAsSInt64;
otUQWord: Result := FData.FAsUInt64;
end
else if (Kind = tkFloat) and (TypeData^.FloatType = ftComp) then
Result := Int64(FData.FAsComp)
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
function TValue.AsUInt64: QWord;
begin
if Kind in [tkInteger, tkInt64, tkQWord] then
case TypeData^.OrdType of
otSByte: Result := FData.FAsSByte;
otUByte: Result := FData.FAsUByte;
otSWord: Result := FData.FAsSWord;
otUWord: Result := FData.FAsUWord;
otSLong: Result := FData.FAsSLong;
otULong: Result := FData.FAsULong;
otSQWord: Result := FData.FAsSInt64;
otUQWord: Result := FData.FAsUInt64;
end
else if (Kind = tkFloat) and (TypeData^.FloatType = ftComp) then
Result := QWord(FData.FAsComp)
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
function TValue.AsInterface: IInterface;
begin
if Kind = tkInterface then
Result := PInterface(FData.FValueData.GetReferenceToRawData)^
else if (Kind in [tkClass, tkClassRef, tkUnknown]) and not Assigned(FData.FAsPointer) then
Result := Nil
else
raise EInvalidCast.Create(SErrInvalidTypecast);
end;
function TValue.ToString: String;
begin
case Kind of
tkWString,
tkUString : result := AsUnicodeString;
tkSString,
tkAString : result := AsAnsiString;
tkInteger : result := IntToStr(AsInteger);
tkQWord : result := IntToStr(AsUInt64);
tkInt64 : result := IntToStr(AsInt64);
tkBool : result := BoolToStr(AsBoolean, True);
tkPointer : result := '(pointer @ ' + HexStr(FData.FAsPointer) + ')';
tkInterface : result := '(interface @ ' + HexStr(PPointer(FData.FValueData.GetReferenceToRawData)^) + ')';
tkInterfaceRaw : result := '(raw interface @ ' + HexStr(FData.FAsPointer) + ')';
tkEnumeration: Result := GetEnumName(TypeInfo, Integer(AsOrdinal));
tkChar: Result := AnsiChar(FData.FAsUByte);
tkWChar: Result := UTF8Encode(WideChar(FData.FAsUWord));
else
result := '';
end;
end;
function TValue.GetArrayLength: SizeInt;
var
td: PTypeData;
begin
if not IsArray then
raise EInvalidCast.Create(SErrInvalidTypecast);
if Kind = tkDynArray then
Result := DynArraySize(PPointer(FData.FValueData.GetReferenceToRawData)^)
else begin
td := TypeData;
if (td^.ArrayData.Size = 0) and (td^.ArrayData.ElCount = 0) then
Result := FData.FArrLength
else
Result := td^.ArrayData.ElCount;
end;
end;
function TValue.GetArrayElement(AIndex: SizeInt): TValue;
var
data: Pointer;
eltype: PTypeInfo;
elsize: SizeInt;
td: PTypeData;
begin
if not IsArray then
raise EInvalidCast.Create(SErrInvalidTypecast);
if Kind = tkDynArray then begin
data := DynArrayIndex(PPointer(FData.FValueData.GetReferenceToRawData)^, [AIndex], FData.FTypeInfo);
eltype := TypeData^.elType2;
end else begin
td := TypeData;
eltype := td^.ArrayData.ElType;
{ open array? }
if (td^.ArrayData.Size = 0) and (td^.ArrayData.ElCount = 0) then begin
data := PPointer(FData.FValueData.GetReferenceToRawData)^;
elsize := FData.FElSize
end else begin
data := FData.FValueData.GetReferenceToRawData;
elsize := td^.ArrayData.Size div td^.ArrayData.ElCount;
end;
data := PByte(data) + AIndex * elsize;
end;
{ MakeWithoutCopy? }
Make(data, eltype, Result);
end;
procedure TValue.SetArrayElement(AIndex: SizeInt; constref AValue: TValue);
var
data: Pointer;
eltype: PTypeInfo;
elsize: SizeInt;
td, tdv: PTypeData;
begin
if not IsArray then
raise EInvalidCast.Create(SErrInvalidTypecast);
if Kind = tkDynArray then begin
data := DynArrayIndex(PPointer(FData.FValueData.GetReferenceToRawData)^, [AIndex], FData.FTypeInfo);
eltype := TypeData^.elType2;
end else begin
td := TypeData;
eltype := td^.ArrayData.ElType;
{ open array? }
if (td^.ArrayData.Size = 0) and (td^.ArrayData.ElCount = 0) then begin
data := PPointer(FData.FValueData.GetReferenceToRawData)^;
elsize := FData.FElSize
end else begin
data := FData.FValueData.GetReferenceToRawData;
elsize := td^.ArrayData.Size div td^.ArrayData.ElCount;
end;
data := PByte(data) + AIndex * elsize;
end;
{ maybe we'll later on allow some typecasts, but for now be restrictive }
if eltype^.Kind <> AValue.Kind then
raise EInvalidCast.Create(SErrInvalidTypecast);
td := GetTypeData(eltype);
tdv := AValue.TypeData;
if ((eltype^.Kind in [tkInteger, tkBool, tkEnumeration, tkSet]) and (td^.OrdType <> tdv^.OrdType)) or
((eltype^.Kind = tkFloat) and (td^.FloatType <> tdv^.FloatType)) then
raise EInvalidCast.Create(SErrInvalidTypecast);
if Assigned(AValue.FData.FValueData) and (eltype^.Kind <> tkSString) then
IntCopy(AValue.FData.FValueData.GetReferenceToRawData, data, eltype)
else
Move(AValue.GetReferenceToRawData^, data^, AValue.DataSize);
end;
function TValue.TryAsOrdinal(out AResult: int64): boolean;
begin
result := IsOrdinal;
if result then
AResult := AsOrdinal;
end;
function TValue.GetReferenceToRawData: Pointer;
begin
if not Assigned(FData.FTypeInfo) then
Result := Nil
else if Assigned(FData.FValueData) then
Result := FData.FValueData.GetReferenceToRawData
else begin
Result := Nil;
case Kind of
tkInteger,
tkEnumeration,
tkInt64,
tkQWord,
tkBool:
case TypeData^.OrdType of
otSByte:
Result := @FData.FAsSByte;
otUByte:
Result := @FData.FAsUByte;
otSWord:
Result := @FData.FAsSWord;
otUWord:
Result := @FData.FAsUWord;
otSLong:
Result := @FData.FAsSLong;
otULong:
Result := @FData.FAsULong;
otSQWord:
Result := @FData.FAsSInt64;
otUQWord:
Result := @FData.FAsUInt64;
end;
tkSet: begin
case TypeData^.OrdType of
otUByte: begin
case TypeData^.SetSize of
1:
Result := @FData.FAsUByte;
2:
Result := @FData.FAsUWord;
3, 4:
Result := @FData.FAsULong;
5..8:
Result := @FData.FAsUInt64;
else
{ this should have gone through FAsValueData :/ }
Result := Nil;
end;
end;
otUWord:
Result := @FData.FAsUWord;
otULong:
Result := @FData.FAsULong;
else
Result := Nil;
end;
end;
tkChar:
Result := @FData.FAsUByte;
tkFloat:
case TypeData^.FloatType of
ftSingle:
Result := @FData.FAsSingle;
ftDouble:
Result := @FData.FAsDouble;
ftExtended:
Result := @FData.FAsExtended;
ftComp:
Result := @FData.FAsComp;
ftCurr:
Result := @FData.FAsCurr;
end;
tkMethod:
Result := @FData.FAsMethod;
tkClass:
Result := @FData.FAsObject;
tkWChar:
Result := @FData.FAsUWord;
tkInterfaceRaw:
Result := @FData.FAsPointer;
tkProcVar:
Result := @FData.FAsMethod.Code;
tkUChar:
Result := @FData.FAsUWord;
tkFile:
Result := @FData.FAsPointer;
tkClassRef:
Result := @FData.FAsClass;
tkPointer:
Result := @FData.FAsPointer;
tkVariant,
tkDynArray,
tkArray,
tkObject,
tkRecord,
tkInterface,
tkSString,
tkLString,
tkAString,
tkUString,
tkWString:
Assert(false, 'Managed/complex type not handled through IValueData');
end;
end;
end;
procedure TValue.ExtractRawData(ABuffer: Pointer);
begin
if Assigned(FData.FValueData) then
FData.FValueData.ExtractRawData(ABuffer)
else if Assigned(FData.FTypeInfo) then
Move((@FData.FAsPointer)^, ABuffer^, DataSize);
end;
procedure TValue.ExtractRawDataNoCopy(ABuffer: Pointer);
begin
if Assigned(FData.FValueData) then
FData.FValueData.ExtractRawDataNoCopy(ABuffer)
else if Assigned(FData.FTypeInfo) then
Move((@FData.FAsPointer)^, ABuffer^, DataSize);
end;
class operator TValue.:=(const AValue: String): TValue;
begin
Make(@AValue, System.TypeInfo(AValue), Result);
end;
class operator TValue.:=(AValue: LongInt): TValue;
begin
Make(@AValue, System.TypeInfo(AValue), Result);
end;
class operator TValue.:=(AValue: Single): TValue;
begin
Make(@AValue, System.TypeInfo(AValue), Result);
end;
class operator TValue.:=(AValue: Double): TValue;
begin
Make(@AValue, System.TypeInfo(AValue), Result);
end;
{$ifdef FPC_HAS_TYPE_EXTENDED}
class operator TValue.:=(AValue: Extended): TValue;
begin
Make(@AValue, System.TypeInfo(AValue), Result);
end;
{$endif}
class operator TValue.:=(AValue: Currency): TValue;
begin
Make(@AValue, System.TypeInfo(AValue), Result);
end;
class operator TValue.:=(AValue: Comp): TValue;
begin
Make(@AValue, System.TypeInfo(AValue), Result);
end;
class operator TValue.:=(AValue: Int64): TValue;
begin
Make(@AValue, System.TypeInfo(AValue), Result);
end;
class operator TValue.:=(AValue: QWord): TValue;
begin
Make(@AValue, System.TypeInfo(AValue), Result);
end;
class operator TValue.:=(AValue: TObject): TValue;
begin
Make(@AValue, System.TypeInfo(AValue), Result);
end;
class operator TValue.:=(AValue: TClass): TValue;
begin
Make(@AValue, System.TypeInfo(AValue), Result);
end;
class operator TValue.:=(AValue: Boolean): TValue;
begin
Make(@AValue, System.TypeInfo(AValue), Result);
end;
class operator TValue.:=(AValue: IUnknown): TValue;
begin
Make(@AValue, System.TypeInfo(AValue), Result);
end;
function Invoke(aCodeAddress: CodePointer; const aArgs: TValueArray;
aCallConv: TCallConv; aResultType: PTypeInfo; aIsStatic: Boolean;
aIsConstructor: Boolean): TValue;
var
funcargs: TFunctionCallParameterArray;
i: LongInt;
flags: TFunctionCallFlags;
begin
{ sanity check }
if not Assigned(FuncCallMgr[aCallConv].Invoke) then
raise ENotImplemented.Create(SErrInvokeNotImplemented);
{ ToDo: handle IsConstructor }
if aIsConstructor then
raise ENotImplemented.Create(SErrInvokeNotImplemented);
flags := [];
if aIsStatic then
Include(flags, fcfStatic)
else if Length(aArgs) = 0 then
raise EInvocationError.Create(SErrMissingSelfParam);
SetLength(funcargs, Length(aArgs));
for i := Low(aArgs) to High(aArgs) do begin
funcargs[i - Low(aArgs) + Low(funcargs)].ValueRef := aArgs[i].GetReferenceToRawData;
funcargs[i - Low(aArgs) + Low(funcargs)].ValueSize := aArgs[i].DataSize;
funcargs[i - Low(aArgs) + Low(funcargs)].Info.ParamType := aArgs[i].TypeInfo;
funcargs[i - Low(aArgs) + Low(funcargs)].Info.ParamFlags := [];
funcargs[i - Low(aArgs) + Low(funcargs)].Info.ParaLocs := Nil;
end;
if Assigned(aResultType) then
TValue.Make(Nil, aResultType, Result)
else
Result := TValue.Empty;
FuncCallMgr[aCallConv].Invoke(aCodeAddress, funcargs, aCallConv, aResultType, Result.GetReferenceToRawData, flags);
end;
function Invoke(const aName: String; aCodeAddress: CodePointer; aCallConv: TCallConv; aStatic: Boolean; aInstance: TValue; constref aArgs: array of TValue; const aParams: specialize TArray<TRttiParameter>; aReturnType: TRttiType): TValue;
var
param: TRttiParameter;
unhidden, highs, i: SizeInt;
args: TFunctionCallParameterArray;
highargs: array of SizeInt;
restype: PTypeInfo;
resptr: Pointer;
mgr: TFunctionCallManager;
flags: TFunctionCallFlags;
begin
mgr := FuncCallMgr[aCallConv];
if not Assigned(mgr.Invoke) then
raise EInvocationError.CreateFmt(SErrCallConvNotSupported, [CCToStr(aCallConv)]);
if not Assigned(aCodeAddress) then
raise EInvocationError.CreateFmt(SErrInvokeNoCodeAddr, [aName]);
unhidden := 0;
highs := 0;
for param in aParams do begin
if unhidden < Length(aArgs) then begin
if pfArray in param.Flags then begin
if Assigned(aArgs[unhidden].TypeInfo) and not aArgs[unhidden].IsArray and (aArgs[unhidden].Kind <> param.ParamType.TypeKind) then
raise EInvocationError.CreateFmt(SErrInvokeArrayArgExpected, [param.Name, aName]);
end else if not (pfHidden in param.Flags) then begin
if Assigned(param.ParamType) and (aArgs[unhidden].Kind <> param.ParamType.TypeKind) then
raise EInvocationError.CreateFmt(SErrInvokeArgInvalidType, [param.Name, aName]);
end;
end;
if not (pfHidden in param.Flags) then
Inc(unhidden);
if pfHigh in param.Flags then
Inc(highs);
end;
if unhidden <> Length(aArgs) then
raise EInvocationError.CreateFmt(SErrInvokeArgCount, [aName, unhidden, Length(aArgs)]);
if Assigned(aReturnType) then begin
TValue.Make(Nil, aReturnType.FTypeInfo, Result);
resptr := Result.GetReferenceToRawData;
restype := aReturnType.FTypeInfo;
end else begin
Result := TValue.Empty;
resptr := Nil;
restype := Nil;
end;
SetLength(highargs, highs);
SetLength(args, Length(aParams));
unhidden := 0;
highs := 0;
for i := 0 to High(aParams) do begin
param := aParams[i];
if Assigned(param.ParamType) then
args[i].Info.ParamType := param.ParamType.FTypeInfo
else
args[i].Info.ParamType := Nil;
args[i].Info.ParamFlags := param.Flags;
args[i].Info.ParaLocs := Nil;
if pfHidden in param.Flags then begin
if pfSelf in param.Flags then
args[i].ValueRef := aInstance.GetReferenceToRawData
else if pfResult in param.Flags then begin
if not Assigned(restype) then
raise EInvocationError.CreateFmt(SErrInvokeRttiDataError, [aName]);
args[i].ValueRef := resptr;
restype := Nil;
resptr := Nil;
end else if pfHigh in param.Flags then begin
{ the corresponding array argument is the *previous* unhidden argument }
if aArgs[unhidden - 1].IsArray then
highargs[highs] := aArgs[unhidden - 1].GetArrayLength - 1
else if not Assigned(aArgs[unhidden - 1].TypeInfo) then
highargs[highs] := -1
else
highargs[highs] := 0;
args[i].ValueRef := @highargs[highs];
Inc(highs);
end;
end else begin
if (pfArray in param.Flags) then begin
if not Assigned(aArgs[unhidden].TypeInfo) then
args[i].ValueRef := Nil
else if aArgs[unhidden].Kind = tkDynArray then
args[i].ValueRef := PPointer(aArgs[unhidden].GetReferenceToRawData)^
else
args[i].ValueRef := aArgs[unhidden].GetReferenceToRawData;
end else
args[i].ValueRef := aArgs[unhidden].GetReferenceToRawData;
Inc(unhidden);
end;
end;
flags := [];
if aStatic then
Include(flags, fcfStatic);
mgr.Invoke(aCodeAddress, args, aCallConv, restype, resptr, flags);
end;
function CreateCallbackProc(aHandler: TFunctionCallProc; aCallConv: TCallConv; aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags; aContext: Pointer): TFunctionCallCallback;
begin
if not Assigned(FuncCallMgr[aCallConv].CreateCallbackProc) then
raise ENotImplemented.Create(SErrCallbackNotImplemented);
if not Assigned(aHandler) then
raise EArgumentNilException.Create(SErrCallbackHandlerNil);
Result := FuncCallMgr[aCallConv].CreateCallbackProc(aHandler, aCallConv, aArgs, aResultType, aFlags, aContext);
end;
function CreateCallbackMethod(aHandler: TFunctionCallMethod; aCallConv: TCallConv; aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags; aContext: Pointer): TFunctionCallCallback;
begin
if not Assigned(FuncCallMgr[aCallConv].CreateCallbackMethod) then
raise ENotImplemented.Create(SErrCallbackNotImplemented);
if not Assigned(aHandler) then
raise EArgumentNilException.Create(SErrCallbackHandlerNil);
Result := FuncCallMgr[aCallConv].CreateCallbackMethod(aHandler, aCallConv, aArgs, aResultType, aFlags, aContext);
end;
function IsManaged(TypeInfo: PTypeInfo): boolean;
begin
if Assigned(TypeInfo) then
case TypeInfo^.Kind of
tkAString,
tkLString,
tkWString,
tkUString,
tkInterface,
tkVariant,
tkDynArray : Result := true;
tkArray : Result := IsManaged(GetTypeData(TypeInfo)^.ArrayData.ElType);
tkRecord,
tkObject :
with GetTypeData(TypeInfo)^.RecInitData^ do
Result := (ManagedFieldCount > 0) or Assigned(ManagementOp);
else
Result := false;
end
else
Result := false;
end;
{$ifndef InLazIDE}
generic function OpenArrayToDynArrayValue<T>(constref aArray: array of T): TValue;
var
arr: specialize TArray<T>;
i: SizeInt;
begin
SetLength(arr, Length(aArray));
for i := 0 to High(aArray) do
arr[i] := aArray[i];
Result := TValue.specialize From<specialize TArray<T>>(arr);
end;
{$endif}
{ TRttiPointerType }
function TRttiPointerType.GetReferredType: TRttiType;
begin
Result := GRttiPool.GetType(FTypeData^.RefType);
end;
{ TRttiRefCountedInterfaceType }
function TRttiRefCountedInterfaceType.IntfData: PInterfaceData;
begin
Result := PInterfaceData(FTypeData);
end;
function TRttiRefCountedInterfaceType.MethodTable: PIntfMethodTable;
begin
Result := IntfData^.MethodTable;
end;
function TRttiRefCountedInterfaceType.GetIntfBaseType: TRttiInterfaceType;
var
context: TRttiContext;
begin
if not Assigned(IntfData^.Parent) then
Exit(Nil);
context := TRttiContext.Create;
try
Result := context.GetType(IntfData^.Parent^) as TRttiInterfaceType;
finally
context.Free;
end;
end;
function TRttiRefCountedInterfaceType.GetDeclaringUnitName: String;
begin
Result := IntfData^.UnitName;
end;
function TRttiRefCountedInterfaceType.GetGUID: TGUID;
begin
Result := IntfData^.GUID;
end;
function TRttiRefCountedInterfaceType.GetIntfFlags: TIntfFlags;
begin
Result := IntfData^.Flags;
end;
function TRttiRefCountedInterfaceType.GetIntfType: TInterfaceType;
begin
Result := itRefCounted;
end;
{ TRttiRawInterfaceType }
function TRttiRawInterfaceType.IntfData: PInterfaceRawData;
begin
Result := PInterfaceRawData(FTypeData);
end;
function TRttiRawInterfaceType.MethodTable: PIntfMethodTable;
begin
{ currently there is none! }
Result := Nil;
end;
function TRttiRawInterfaceType.GetIntfBaseType: TRttiInterfaceType;
var
context: TRttiContext;
begin
if not Assigned(IntfData^.Parent) then
Exit(Nil);
context := TRttiContext.Create;
try
Result := context.GetType(IntfData^.Parent^) as TRttiInterfaceType;
finally
context.Free;
end;
end;
function TRttiRawInterfaceType.GetDeclaringUnitName: String;
begin
Result := IntfData^.UnitName;
end;
function TRttiRawInterfaceType.GetGUID: TGUID;
begin
Result := IntfData^.IID;
end;
function TRttiRawInterfaceType.GetGUIDStr: String;
begin
Result := IntfData^.IIDStr;
end;
function TRttiRawInterfaceType.GetIntfFlags: TIntfFlags;
begin
Result := IntfData^.Flags;
end;
function TRttiRawInterfaceType.GetIntfType: TInterfaceType;
begin
Result := itRaw;
end;
{ TRttiVmtMethodParameter }
function TRttiVmtMethodParameter.GetHandle: Pointer;
begin
Result := FVmtMethodParam;
end;
function TRttiVmtMethodParameter.GetName: String;
begin
Result := FVmtMethodParam^.Name;
end;
function TRttiVmtMethodParameter.GetFlags: TParamFlags;
begin
Result := FVmtMethodParam^.Flags;
end;
function TRttiVmtMethodParameter.GetParamType: TRttiType;
var
context: TRttiContext;
begin
if not Assigned(FVmtMethodParam^.ParamType) then
Exit(Nil);
context := TRttiContext.Create;
try
Result := context.GetType(FVmtMethodParam^.ParamType^);
finally
context.Free;
end;
end;
constructor TRttiVmtMethodParameter.Create(AVmtMethodParam: PVmtMethodParam);
begin
inherited Create;
FVmtMethodParam := AVmtMethodParam;
end;
{ TRttiMethodTypeParameter }
function TRttiMethodTypeParameter.GetHandle: Pointer;
begin
Result := fHandle;
end;
function TRttiMethodTypeParameter.GetName: String;
begin
Result := fName;
end;
function TRttiMethodTypeParameter.GetFlags: TParamFlags;
begin
Result := fFlags;
end;
function TRttiMethodTypeParameter.GetParamType: TRttiType;
var
context: TRttiContext;
begin
context := TRttiContext.Create;
try
Result := context.GetType(FType);
finally
context.Free;
end;
end;
constructor TRttiMethodTypeParameter.Create(aHandle: Pointer; const aName: String; aFlags: TParamFlags; aType: PTypeInfo);
begin
fHandle := aHandle;
fName := aName;
fFlags := aFlags;
fType := aType;
end;
{ TRttiIntfMethod }
function TRttiIntfMethod.GetHandle: Pointer;
begin
Result := FIntfMethodEntry;
end;
function TRttiIntfMethod.GetName: String;
begin
Result := FIntfMethodEntry^.Name;
end;
function TRttiIntfMethod.GetCallingConvention: TCallConv;
begin
Result := FIntfMethodEntry^.CC;
end;
function TRttiIntfMethod.GetCodeAddress: CodePointer;
begin
Result := Nil;
end;
function TRttiIntfMethod.GetDispatchKind: TDispatchKind;
begin
Result := dkInterface;
end;
function TRttiIntfMethod.GetHasExtendedInfo: Boolean;
begin
Result := True;
end;
function TRttiIntfMethod.GetIsClassMethod: Boolean;
begin
Result := False;
end;
function TRttiIntfMethod.GetIsConstructor: Boolean;
begin
Result := False;
end;
function TRttiIntfMethod.GetIsDestructor: Boolean;
begin
Result := False;
end;
function TRttiIntfMethod.GetIsStatic: Boolean;
begin
Result := False;
end;
function TRttiIntfMethod.GetMethodKind: TMethodKind;
begin
Result := FIntfMethodEntry^.Kind;
end;
function TRttiIntfMethod.GetReturnType: TRttiType;
var
context: TRttiContext;
begin
if not Assigned(FIntfMethodEntry^.ResultType) then
Exit(Nil);
context := TRttiContext.Create;
try
Result := context.GetType(FIntfMethodEntry^.ResultType^);
finally
context.Free;
end;
end;
function TRttiIntfMethod.GetVirtualIndex: SmallInt;
begin
Result := FIndex;
end;
constructor TRttiIntfMethod.Create(AParent: TRttiType; AIntfMethodEntry: PIntfMethodEntry; AIndex: SmallInt);
begin
inherited Create(AParent);
FIntfMethodEntry := AIntfMethodEntry;
FIndex := AIndex;
end;
function TRttiIntfMethod.GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>;
var
param: PVmtMethodParam;
total, visible: SizeInt;
context: TRttiContext;
obj: TRttiObject;
begin
if aWithHidden and (Length(FParamsAll) > 0) then
Exit(FParamsAll);
if not aWithHidden and (Length(FParams) > 0) then
Exit(FParams);
if FIntfMethodEntry^.ParamCount = 0 then
Exit(Nil);
SetLength(FParams, FIntfMethodEntry^.ParamCount);
SetLength(FParamsAll, FIntfMethodEntry^.ParamCount);
context := TRttiContext.Create;
try
total := 0;
visible := 0;
param := FIntfMethodEntry^.Param[0];
while total < FIntfMethodEntry^.ParamCount do begin
obj := context.GetByHandle(param);
if Assigned(obj) then
FParamsAll[total] := obj as TRttiVmtMethodParameter
else begin
FParamsAll[total] := TRttiVmtMethodParameter.Create(param);
context.AddObject(FParamsAll[total]);
end;
if not (pfHidden in param^.Flags) then begin
FParams[visible] := FParamsAll[total];
Inc(visible);
end;
param := param^.Next;
Inc(total);
end;
if visible <> total then
SetLength(FParams, visible);
finally
context.Free;
end;
if aWithHidden then
Result := FParamsAll
else
Result := FParams;
end;
{ TRttiInt64Type }
function TRttiInt64Type.GetMaxValue: Int64;
begin
Result := FTypeData^.MaxInt64Value;
end;
function TRttiInt64Type.GetMinValue: Int64;
begin
Result := FTypeData^.MinInt64Value;
end;
function TRttiInt64Type.GetUnsigned: Boolean;
begin
Result := FTypeData^.OrdType = otUQWord;
end;
function TRttiInt64Type.GetTypeSize: integer;
begin
Result := SizeOf(QWord);
end;
{ TRttiOrdinalType }
function TRttiOrdinalType.GetMaxValue: LongInt;
begin
Result := FTypeData^.MaxValue;
end;
function TRttiOrdinalType.GetMinValue: LongInt;
begin
Result := FTypeData^.MinValue;
end;
function TRttiOrdinalType.GetOrdType: TOrdType;
begin
Result := FTypeData^.OrdType;
end;
function TRttiOrdinalType.GetTypeSize: Integer;
begin
case OrdType of
otSByte,
otUByte:
Result := SizeOf(Byte);
otSWord,
otUWord:
Result := SizeOf(Word);
otSLong,
otULong:
Result := SizeOf(LongWord);
otSQWord,
otUQWord:
Result := SizeOf(QWord);
end;
end;
{ TRttiFloatType }
function TRttiFloatType.GetFloatType: TFloatType;
begin
result := FTypeData^.FloatType;
end;
function TRttiFloatType.GetTypeSize: integer;
begin
case FloatType of
ftSingle:
Result := SizeOf(Single);
ftDouble:
Result := SizeOf(Double);
ftExtended:
Result := SizeOf(Extended);
ftComp:
Result := SizeOf(Comp);
ftCurr:
Result := SizeOf(Currency);
end;
end;
{ TRttiParameter }
function TRttiParameter.ToString: String;
var
f: TParamFlags;
n: String;
t: TRttiType;
begin
if FString = '' then begin
f := Flags;
if pfVar in f then
FString := 'var'
else if pfConst in f then
FString := 'const'
else if pfOut in f then
FString := 'out'
else if pfConstRef in f then
FString := 'constref';
if FString <> '' then
FString := FString + ' ';
n := Name;
if n = '' then
n := '<unknown>';
FString := FString + n;
t := ParamType;
if Assigned(t) then begin
FString := FString + ': ';
if pfArray in flags then
FString := 'array of ';
FString := FString + t.Name;
end;
end;
Result := FString;
end;
{ TMethodImplementation }
function TMethodImplementation.GetCodeAddress: CodePointer;
begin
Result := fLowLevelCallback.CodeAddress;
end;
procedure TMethodImplementation.InitArgs;
var
i, refargs: SizeInt;
begin
i := 0;
refargs := 0;
SetLength(fRefArgs, Length(fArgs));
while i < Length(fArgs) do begin
if (fArgs[i].ParamFlags * [pfVar, pfOut] <> []) and not (pfHidden in fArgs[i].ParamFlags) then begin
fRefArgs[refargs] := fArgLen;
Inc(refargs);
end;
if pfArray in fArgs[i].ParamFlags then begin
Inc(i);
if (i = Length(fArgs)) or not (pfHigh in fArgs[i].ParamFlags) then
raise EInsufficientRtti.Create(SErrMethodImplCreateFailed);
Inc(fArgLen);
end else if not (pfHidden in fArgs[i].ParamFlags) or (pfSelf in fArgs[i].ParamFlags) then
Inc(fArgLen)
else if (pfResult in fArgs[i].ParamFlags) then
fResult := fArgs[i].ParamType;
Inc(i);
end;
SetLength(fRefArgs, refargs);
end;
procedure TMethodImplementation.HandleCallback(const aArgs: specialize TArray<Pointer>; aResult: Pointer; aContext: Pointer);
var
i, argidx: SizeInt;
args: TValueArray;
res: TValue;
begin
Assert(fArgLen = Length(aArgs), 'Length of arguments does not match');
SetLength(args, fArgLen);
argidx := 0;
i := 0;
while i < Length(fArgs) do begin
if pfArray in fArgs[i].ParamFlags then begin
Inc(i);
Assert((i < Length(fArgs)) and (pfHigh in fArgs[i].ParamFlags), 'Expected high parameter after open array parameter');
TValue.MakeOpenArray(aArgs[i - 1], SizeInt(aArgs[i]), fArgs[i].ParamType, args[argidx]);
end else if not (pfHidden in fArgs[i].ParamFlags) or (pfSelf in fArgs[i].ParamFlags) then begin
if Assigned(fArgs[i].ParamType) then
TValue.Make(aArgs[i], fArgs[i].ParamType, args[argidx])
else
TValue.Make(@aArgs[i], TypeInfo(Pointer), args[argidx]);
end;
Inc(i);
Inc(argidx);
end;
if Assigned(fCallbackMethod) then
fCallbackMethod(aContext, args, res)
else
fCallbackProc(aContext, args, res);
{ copy back var/out parameters }
for i := 0 to High(fRefArgs) do begin
args[fRefArgs[i]].ExtractRawData(aArgs[fRefArgs[i]]);
end;
if Assigned(fResult) then
res.ExtractRawData(aResult);
end;
constructor TMethodImplementation.Create(aCC: TCallConv; aArgs: specialize TArray<TFunctionCallParameterInfo>; aResult: PTypeInfo; aFlags: TFunctionCallFlags; aUserData: Pointer; aCallback: TMethodImplementationCallbackMethod);
begin
fCC := aCC;
fArgs := aArgs;
fResult := aResult;
fFlags := aFlags;
fCallbackMethod := aCallback;
InitArgs;
fLowLevelCallback := CreateCallbackMethod(@HandleCallback, fCC, aArgs, aResult, aFlags, aUserData);
if not Assigned(fLowLevelCallback) then
raise EInsufficientRtti.Create(SErrMethodImplCreateFailed);
end;
constructor TMethodImplementation.Create(aCC: TCallConv; aArgs: specialize TArray<TFunctionCallParameterInfo>; aResult: PTypeInfo; aFlags: TFunctionCallFlags; aUserData: Pointer; aCallback: TMethodImplementationCallbackProc);
begin
fCC := aCC;
fArgs := aArgs;
fResult := aResult;
fFlags := aFlags;
fCallbackProc := aCallback;
InitArgs;
fLowLevelCallback := CreateCallbackMethod(@HandleCallback, fCC, aArgs, aResult, aFlags, aUserData);
if not Assigned(fLowLevelCallback) then
raise EInsufficientRtti.Create(SErrMethodImplCreateFailed);
end;
constructor TMethodImplementation.Create;
begin
raise EInvalidOpException.Create(SErrMethodImplCreateNoArg);
end;
destructor TMethodImplementation.Destroy;
begin
fLowLevelCallback.Free;
inherited Destroy;
end;
{ TRttiMethod }
function TRttiMethod.GetHasExtendedInfo: Boolean;
begin
Result := False;
end;
function TRttiMethod.GetFlags: TFunctionCallFlags;
begin
Result := [];
if IsStatic then
Include(Result, fcfStatic);
end;
function TRttiMethod.GetParameters: specialize TArray<TRttiParameter>;
begin
Result := GetParameters(False);
end;
function TRttiMethod.ToString: String;
var
ret: TRttiType;
n: String;
params: specialize TArray<TRttiParameter>;
i: LongInt;
begin
if FString = '' then begin
n := Name;
if n = '' then
n := '<unknown>';
if not HasExtendedInfo then begin
FString := 'method ' + n;
end else begin
ret := ReturnType;
if IsClassMethod then
FString := 'class ';
if IsConstructor then
FString := FString + 'constructor'
else if IsDestructor then
FString := FString + 'destructor'
else if Assigned(ret) then
FString := FString + 'function'
else
FString := FString + 'procedure';
FString := FString + ' ' + n;
params := GetParameters;
if Length(params) > 0 then begin
FString := FString + '(';
for i := 0 to High(params) do begin
if i > 0 then
FString := FString + '; ';
FString := FString + params[i].ToString;
end;
FString := FString + ')';
end;
if Assigned(ret) then
FString := FString + ': ' + ret.Name;
if IsStatic then
FString := FString + '; static';
end;
end;
Result := FString;
end;
function TRttiMethod.Invoke(aInstance: TObject; const aArgs: array of TValue): TValue;
var
instance: TValue;
begin
TValue.Make(@aInstance, TypeInfo(TObject), instance);
Result := Invoke(instance, aArgs);
end;
function TRttiMethod.Invoke(aInstance: TClass; const aArgs: array of TValue): TValue;
var
instance: TValue;
begin
TValue.Make(@aInstance, TypeInfo(TClass), instance);
Result := Invoke(instance, aArgs);
end;
function TRttiMethod.Invoke(aInstance: TValue; const aArgs: array of TValue): TValue;
var
addr: CodePointer;
vmt: PCodePointer;
begin
if not HasExtendedInfo then
raise EInvocationError.Create(SErrInvokeInsufficientRtti);
if IsStatic and not aInstance.IsEmpty then
raise EInvocationError.CreateFmt(SErrInvokeStaticNoSelf, [Name]);
if not IsStatic and aInstance.IsEmpty then
raise EInvocationError.CreateFmt(SErrInvokeNotStaticNeedsSelf, [Name]);
if not IsStatic and IsClassMethod and not aInstance.IsClass then
raise EInvocationError.CreateFmt(SErrInvokeClassMethodClassSelf, [Name]);
addr := Nil;
if IsStatic then
addr := CodeAddress
else begin
vmt := Nil;
if aInstance.Kind in [tkInterface, tkInterfaceRaw] then
vmt := PCodePointer(PPPointer(aInstance.GetReferenceToRawData)^^);
{ ToDo }
if Assigned(vmt) then
addr := vmt[VirtualIndex];
end;
Result := Rtti.Invoke(Name, addr, CallingConvention, IsStatic, aInstance, aArgs, GetParameters(True), ReturnType);
end;
function TRttiMethod.CreateImplementation(aUserData: Pointer; aCallback: TMethodImplementationCallbackMethod): TMethodImplementation;
var
params: specialize TArray<TRttiParameter>;
args: specialize TArray<TFunctionCallParameterInfo>;
res: PTypeInfo;
restype: TRttiType;
resinparam: Boolean;
i: SizeInt;
begin
if not Assigned(aCallback) then
raise EArgumentNilException.Create(SErrMethodImplNoCallback);
resinparam := False;
params := GetParameters(True);
SetLength(args, Length(params));
for i := 0 to High(params) do begin
if Assigned(params[i].ParamType) then
args[i].ParamType := params[i].ParamType.FTypeInfo
else
args[i].ParamType := Nil;
args[i].ParamFlags := params[i].Flags;
args[i].ParaLocs := Nil;
if pfResult in params[i].Flags then
resinparam := True;
end;
restype := GetReturnType;
if Assigned(restype) and not resinparam then
res := restype.FTypeInfo
else
res := Nil;
Result := TMethodImplementation.Create(GetCallingConvention, args, res, GetFlags, aUserData, aCallback);
end;
function TRttiMethod.CreateImplementation(aUserData: Pointer; aCallback: TMethodImplementationCallbackProc): TMethodImplementation;
var
params: specialize TArray<TRttiParameter>;
args: specialize TArray<TFunctionCallParameterInfo>;
res: PTypeInfo;
restype: TRttiType;
resinparam: Boolean;
i: SizeInt;
begin
if not Assigned(aCallback) then
raise EArgumentNilException.Create(SErrMethodImplNoCallback);
resinparam := False;
params := GetParameters(True);
SetLength(args, Length(params));
for i := 0 to High(params) do begin
if Assigned(params[i].ParamType) then
args[i].ParamType := params[i].ParamType.FTypeInfo
else
args[i].ParamType := Nil;
args[i].ParamFlags := params[i].Flags;
args[i].ParaLocs := Nil;
if pfResult in params[i].Flags then
resinparam := True;
end;
restype := GetReturnType;
if Assigned(restype) and not resinparam then
res := restype.FTypeInfo
else
res := Nil;
Result := TMethodImplementation.Create(GetCallingConvention, args, res, GetFlags, aUserData, aCallback);
end;
{ TRttiInvokableType }
function TRttiInvokableType.GetParameters: specialize TArray<TRttiParameter>;
begin
Result := GetParameters(False);
end;
function TRttiInvokableType.CreateImplementation(aCallback: TCallbackMethod): TMethodImplementation;
var
params: specialize TArray<TRttiParameter>;
args: specialize TArray<TFunctionCallParameterInfo>;
res: PTypeInfo;
restype: TRttiType;
resinparam: Boolean;
i: SizeInt;
begin
if not Assigned(aCallback) then
raise EArgumentNilException.Create(SErrMethodImplNoCallback);
resinparam := False;
params := GetParameters(True);
SetLength(args, Length(params));
for i := 0 to High(params) do begin
if Assigned(params[i].ParamType) then
args[i].ParamType := params[i].ParamType.FTypeInfo
else
args[i].ParamType := Nil;
args[i].ParamFlags := params[i].Flags;
args[i].ParaLocs := Nil;
if pfResult in params[i].Flags then
resinparam := True;
end;
restype := GetReturnType;
if Assigned(restype) and not resinparam then
res := restype.FTypeInfo
else
res := Nil;
Result := TMethodImplementation.Create(GetCallingConvention, args, res, GetFlags, Self, TMethodImplementationCallbackMethod(aCallback));
end;
function TRttiInvokableType.CreateImplementation(aCallback: TCallbackProc): TMethodImplementation;
var
params: specialize TArray<TRttiParameter>;
args: specialize TArray<TFunctionCallParameterInfo>;
res: PTypeInfo;
restype: TRttiType;
resinparam: Boolean;
i: SizeInt;
begin
if not Assigned(aCallback) then
raise EArgumentNilException.Create(SErrMethodImplNoCallback);
resinparam := False;
params := GetParameters(True);
SetLength(args, Length(params));
for i := 0 to High(params) do begin
if Assigned(params[i].ParamType) then
args[i].ParamType := params[i].ParamType.FTypeInfo
else
args[i].ParamType := Nil;
args[i].ParamFlags := params[i].Flags;
args[i].ParaLocs := Nil;
if pfResult in params[i].Flags then
resinparam := True;
end;
restype := GetReturnType;
if Assigned(restype) and not resinparam then
res := restype.FTypeInfo
else
res := Nil;
Result := TMethodImplementation.Create(GetCallingConvention, args, res, GetFlags, Self, TMethodImplementationCallbackProc(aCallback));
end;
{ TRttiMethodType }
function TRttiMethodType.GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>;
type
TParamInfo = record
Handle: Pointer;
Flags: TParamFlags;
Name: String;
end;
PParamFlags = ^TParamFlags;
PCallConv = ^TCallConv;
PPPTypeInfo = ^PPTypeInfo;
var
infos: array of TParamInfo;
total, visible, i: SizeInt;
ptr: PByte;
paramtypes: PPPTypeInfo;
paramtype: PTypeInfo;
context: TRttiContext;
obj: TRttiObject;
begin
if aWithHidden and (Length(FParamsAll) > 0) then
Exit(FParamsAll);
if not aWithHidden and (Length(FParams) > 0) then
Exit(FParams);
ptr := @FTypeData^.ParamList[0];
visible := 0;
total := 0;
if FTypeData^.ParamCount > 0 then begin
SetLength(infos, FTypeData^.ParamCount);
while total < FTypeData^.ParamCount do begin
{ align }
ptr := AlignTParamFlags(ptr);
infos[total].Handle := ptr;
infos[total].Flags := PParamFlags(ptr)^;
Inc(ptr, SizeOf(TParamFlags));
{ handle name }
infos[total].Name := PShortString(ptr)^;
Inc(ptr, ptr^ + SizeOf(Byte));
{ skip type name }
Inc(ptr, ptr^ + SizeOf(Byte));
if not (pfHidden in infos[total].Flags) then
Inc(visible);
Inc(total);
end;
end;
if FTypeData^.MethodKind in [mkFunction, mkClassFunction] then begin
{ skip return type name }
ptr := AlignToPtr(PByte(ptr) + ptr^ + SizeOf(Byte));
{ handle return type }
FReturnType := GRttiPool.GetType(PPPTypeInfo(ptr)^^);
Inc(ptr, SizeOf(PPTypeInfo));
end;
{ handle calling convention }
FCallConv := PCallConv(ptr)^;
Inc(ptr, SizeOf(TCallConv));
SetLength(FParamsAll, FTypeData^.ParamCount);
SetLength(FParams, visible);
if FTypeData^.ParamCount > 0 then begin
context := TRttiContext.Create;
try
paramtypes := PPPTypeInfo(AlignTypeData(ptr));
visible := 0;
for i := 0 to FTypeData^.ParamCount - 1 do begin
obj := context.GetByHandle(infos[i].Handle);
if Assigned(obj) then
FParamsAll[i] := obj as TRttiMethodTypeParameter
else begin
if Assigned(paramtypes[i]) then
paramtype := paramtypes[i]^
else
paramtype := Nil;
FParamsAll[i] := TRttiMethodTypeParameter.Create(infos[i].Handle, infos[i].Name, infos[i].Flags, paramtype);
context.AddObject(FParamsAll[i]);
end;
if not (pfHidden in infos[i].Flags) then begin
FParams[visible] := FParamsAll[i];
Inc(visible);
end;
end;
finally
context.Free;
end;
end;
if aWithHidden then
Result := FParamsAll
else
Result := FParams;
end;
function TRttiMethodType.GetCallingConvention: TCallConv;
begin
{ the calling convention is located after the parameters, so get the parameters
which will also initialize the calling convention }
GetParameters(True);
Result := FCallConv;
end;
function TRttiMethodType.GetReturnType: TRttiType;
begin
if FTypeData^.MethodKind in [mkFunction, mkClassFunction] then begin
{ the return type is located after the parameters, so get the parameters
which will also initialize the return type }
GetParameters(True);
Result := FReturnType;
end else
Result := Nil;
end;
function TRttiMethodType.GetFlags: TFunctionCallFlags;
begin
Result := [];
end;
function TRttiMethodType.Invoke(const aCallable: TValue; const aArgs: array of TValue): TValue;
var
method: PMethod;
inst: TValue;
begin
if aCallable.Kind <> tkMethod then
raise EInvocationError.CreateFmt(SErrInvokeCallableNotMethod, [Name]);
method := PMethod(aCallable.GetReferenceToRawData);
{ by using a pointer we can also use this for non-class instance methods }
TValue.Make(@method^.Data, PTypeInfo(TypeInfo(Pointer)), inst);
Result := Rtti.Invoke(Name, method^.Code, CallingConvention, False, inst, aArgs, GetParameters(True), ReturnType);
end;
{ TRttiProcedureType }
function TRttiProcedureType.GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>;
var
visible, i: SizeInt;
param: PProcedureParam;
obj: TRttiObject;
context: TRttiContext;
begin
if aWithHidden and (Length(FParamsAll) > 0) then
Exit(FParamsAll);
if not aWithHidden and (Length(FParams) > 0) then
Exit(FParams);
if FTypeData^.ProcSig.ParamCount = 0 then
Exit(Nil);
SetLength(FParamsAll, FTypeData^.ProcSig.ParamCount);
SetLength(FParams, FTypeData^.ProcSig.ParamCount);
context := TRttiContext.Create;
try
param := AlignToPtr(PProcedureParam(@FTypeData^.ProcSig.ParamCount + SizeOf(FTypeData^.ProcSig.ParamCount)));
visible := 0;
for i := 0 to FTypeData^.ProcSig.ParamCount - 1 do begin
obj := context.GetByHandle(param);
if Assigned(obj) then
FParamsAll[i] := obj as TRttiMethodTypeParameter
else begin
FParamsAll[i] := TRttiMethodTypeParameter.Create(param, param^.Name, param^.ParamFlags, param^.ParamType);
context.AddObject(FParamsAll[i]);
end;
if not (pfHidden in param^.ParamFlags) then begin
FParams[visible] := FParamsAll[i];
Inc(visible);
end;
param := PProcedureParam(AlignToPtr(PByte(@param^.Name) + Length(param^.Name) + SizeOf(param^.Name[0])));
end;
SetLength(FParams, visible);
finally
context.Free;
end;
if aWithHidden then
Result := FParamsAll
else
Result := FParams;
end;
function TRttiProcedureType.GetCallingConvention: TCallConv;
begin
Result := FTypeData^.ProcSig.CC;
end;
function TRttiProcedureType.GetReturnType: TRttiType;
var
context: TRttiContext;
begin
if not Assigned(FTypeData^.ProcSig.ResultTypeRef) then
Exit(Nil);
context := TRttiContext.Create;
try
Result := context.GetType(FTypeData^.ProcSig.ResultTypeRef^);
finally
context.Free;
end;
end;
function TRttiProcedureType.GetFlags: TFunctionCallFlags;
begin
Result := [fcfStatic];
end;
function TRttiProcedureType.Invoke(const aCallable: TValue; const aArgs: array of TValue): TValue;
begin
if aCallable.Kind <> tkProcVar then
raise EInvocationError.CreateFmt(SErrInvokeCallableNotProc, [Name]);
Result := Rtti.Invoke(Name, PCodePointer(aCallable.GetReferenceToRawData)^, CallingConvention, True, TValue.Empty, aArgs, GetParameters(True), ReturnType);
end;
{ TRttiStringType }
function TRttiStringType.GetStringKind: TRttiStringKind;
begin
case TypeKind of
tkSString : result := skShortString;
tkLString : result := skAnsiString;
tkAString : result := skAnsiString;
tkUString : result := skUnicodeString;
tkWString : result := skWideString;
end;
end;
{ TRttiInterfaceType }
function TRttiInterfaceType.IntfMethodCount: Word;
var
parent: TRttiInterfaceType;
table: PIntfMethodTable;
begin
parent := GetIntfBaseType;
if Assigned(parent) then
Result := parent.IntfMethodCount
else
Result := 0;
table := MethodTable;
if Assigned(table) then
Inc(Result, table^.Count);
end;
function TRttiInterfaceType.GetBaseType: TRttiType;
begin
Result := GetIntfBaseType;
end;
function TRttiInterfaceType.GetGUIDStr: String;
begin
Result := GUIDToString(GUID);
end;
function TRttiInterfaceType.GetDeclaredMethods: specialize TArray<TRttiMethod>;
var
methtable: PIntfMethodTable;
count, index: Word;
method: PIntfMethodEntry;
context: TRttiContext;
obj: TRttiObject;
parent: TRttiInterfaceType;
parentmethodcount: Word;
begin
if Assigned(fDeclaredMethods) then
Exit(fDeclaredMethods);
methtable := MethodTable;
if not Assigned(methtable) then
Exit(Nil);
if (methtable^.Count = 0) or (methtable^.RTTICount = $ffff) then
Exit(Nil);
parent := GetIntfBaseType;
if Assigned(parent) then
parentmethodcount := parent.IntfMethodCount
else
parentmethodcount := 0;
SetLength(fDeclaredMethods, methtable^.Count);
context := TRttiContext.Create;
try
method := methtable^.Method[0];
count := methtable^.Count;
while count > 0 do begin
index := methtable^.Count - count;
obj := context.GetByHandle(method);
if Assigned(obj) then
fDeclaredMethods[index] := obj as TRttiMethod
else begin
fDeclaredMethods[index] := TRttiIntfMethod.Create(Self, method, parentmethodcount + index);
context.AddObject(fDeclaredMethods[index]);
end;
method := method^.Next;
Dec(count);
end;
finally
context.Free;
end;
Result := fDeclaredMethods;
end;
{ TRttiInstanceType }
function TRttiInstanceType.GetMetaClassType: TClass;
begin
result := FTypeData^.ClassType;
end;
function TRttiInstanceType.GetDeclaringUnitName: string;
begin
result := FTypeData^.UnitName;
end;
function TRttiInstanceType.GetBaseType: TRttiType;
var
AContext: TRttiContext;
begin
AContext := TRttiContext.Create;
try
result := AContext.GetType(FTypeData^.ParentInfo);
finally
AContext.Free;
end;
end;
function TRttiInstanceType.GetIsInstance: boolean;
begin
Result:=True;
end;
function TRttiInstanceType.GetTypeSize: integer;
begin
Result:=sizeof(TObject);
end;
function TRttiInstanceType.GetProperties: specialize TArray<TRttiProperty>;
var
TypeInfo: PTypeInfo;
TypeRttiType: TRttiType;
TD: PTypeData;
PPD: PPropData;
TP: PPropInfo;
Count: longint;
obj: TRttiObject;
begin
if not FPropertiesResolved then
begin
TypeInfo := FTypeInfo;
// Get the total properties count
SetLength(FProperties,FTypeData^.PropCount);
TypeRttiType:= self;
repeat
TD:=GetTypeData(TypeInfo);
// published properties count for this object
// skip the attribute-info if available
PPD := PClassData(TD)^.PropertyTable;
Count:=PPD^.PropCount;
// Now point TP to first propinfo record.
TP:=PPropInfo(@PPD^.PropList);
While Count>0 do
begin
// Don't overwrite properties with the same name
if FProperties[TP^.NameIndex]=nil then begin
obj := GRttiPool.GetByHandle(TP);
if Assigned(obj) then
FProperties[TP^.NameIndex] := obj as TRttiProperty
else begin
FProperties[TP^.NameIndex] := TRttiProperty.Create(TypeRttiType, TP);
GRttiPool.AddObject(FProperties[TP^.NameIndex]);
end;
end;
// Point to TP next propinfo record.
// Located at Name[Length(Name)+1] !
TP:=TP^.Next;
Dec(Count);
end;
TypeInfo:=TD^.Parentinfo;
TypeRttiType:= GRttiPool.GetType(TypeInfo);
until TypeInfo=nil;
end;
result := FProperties;
end;
{ TRttiMember }
function TRttiMember.GetVisibility: TMemberVisibility;
begin
result := mvPublished;
end;
constructor TRttiMember.Create(AParent: TRttiType);
begin
inherited Create();
FParent := AParent;
end;
{ TRttiProperty }
function TRttiProperty.GetPropertyType: TRttiType;
begin
result := GRttiPool.GetType(FPropInfo^.PropType);
end;
function TRttiProperty.GetIsReadable: boolean;
begin
result := assigned(FPropInfo^.GetProc);
end;
function TRttiProperty.GetIsWritable: boolean;
begin
result := assigned(FPropInfo^.SetProc);
end;
function TRttiProperty.GetVisibility: TMemberVisibility;
begin
// At this moment only pulished rtti-property-info is supported by fpc
result := mvPublished;
end;
function TRttiProperty.GetName: string;
begin
Result:=FPropInfo^.Name;
end;
function TRttiProperty.GetHandle: Pointer;
begin
Result := FPropInfo;
end;
constructor TRttiProperty.Create(AParent: TRttiType; APropInfo: PPropInfo);
begin
inherited Create(AParent);
FPropInfo := APropInfo;
end;
function TRttiProperty.GetValue(Instance: pointer): TValue;
procedure ValueFromBool(value: Int64);
var
b8: Boolean;
b16: Boolean16;
b32: Boolean32;
bb: ByteBool;
bw: WordBool;
bl: LongBool;
td: PTypeData;
p: Pointer;
begin
td := GetTypeData(FPropInfo^.PropType);
case td^.OrdType of
otUByte:
begin
b8 := Boolean(value);
p := @b8;
end;
otUWord:
begin
b16 := Boolean16(value);
p := @b16;
end;
otULong:
begin
b32 := Boolean32(value);
p := @b32;
end;
otSByte:
begin
bb := ByteBool(value);
p := @bb;
end;
otSWord:
begin
bw := WordBool(value);
p := @bw;
end;
otSLong:
begin
bl := LongBool(value);
p := @bl;
end;
end;
TValue.Make(p, FPropInfo^.PropType, result);
end;
procedure ValueFromInt(value: Int64);
var
i8: UInt8;
i16: UInt16;
i32: UInt32;
td: PTypeData;
p: Pointer;
begin
td := GetTypeData(FPropInfo^.PropType);
case td^.OrdType of
otUByte,
otSByte:
begin
i8 := value;
p := @i8;
end;
otUWord,
otSWord:
begin
i16 := value;
p := @i16;
end;
otULong,
otSLong:
begin
i32 := value;
p := @i32;
end;
end;
TValue.Make(p, FPropInfo^.PropType, result);
end;
var
Values: record
case Integer of
0: (Enum: Int64);
1: (Bool: Int64);
2: (Int: Int64);
3: (Ch: Byte);
4: (Wch: Word);
5: (I64: Int64);
6: (Si: Single);
7: (Db: Double);
8: (Ex: Extended);
9: (Cur: Currency);
10: (Cp: Comp);
11: (A: Pointer;)
end;
s: String;
ss: ShortString;
O: TObject;
Int: IUnknown;
begin
case FPropinfo^.PropType^.Kind of
tkSString:
begin
ss := ShortString(GetStrProp(TObject(Instance), FPropInfo));
TValue.Make(@ss, FPropInfo^.PropType, result);
end;
tkAString:
begin
s := GetStrProp(TObject(Instance), FPropInfo);
TValue.Make(@s, FPropInfo^.PropType, result);
end;
tkEnumeration:
begin
Values.Enum := Integer(GetOrdProp(TObject(Instance), FPropInfo));
ValueFromInt(Values.Enum);
end;
tkBool:
begin
Values.Bool := GetOrdProp(TObject(Instance), FPropInfo);
ValueFromBool(Values.Bool);
end;
tkInteger:
begin
Values.Int := GetOrdProp(TObject(Instance), FPropInfo);
ValueFromInt(Values.Int);
end;
tkChar:
begin
Values.Ch := Byte(GetOrdProp(TObject(Instance), FPropInfo));
TValue.Make(@Values.Ch, FPropInfo^.PropType, result);
end;
tkWChar:
begin
Values.Wch := Word(GetOrdProp(TObject(Instance), FPropInfo));
TValue.Make(@Values.Wch, FPropInfo^.PropType, result);
end;
tkInt64,
tkQWord:
begin
Values.I64 := GetOrdProp(TObject(Instance), FPropInfo);
TValue.Make(@Values.I64, FPropInfo^.PropType, result);
end;
tkClass:
begin
O := GetObjectProp(TObject(Instance), FPropInfo);
TValue.Make(@O, FPropInfo^.PropType, Result);
end;
tkInterface:
begin
Int := GetInterfaceProp(TObject(Instance), FPropInfo);
TValue.Make(@Int, FPropInfo^.PropType, Result);
end;
tkFloat:
begin
case GetTypeData(FPropInfo^.PropType)^.FloatType of
ftCurr :
begin
Values.Cur := Currency(GetFloatProp(TObject(Instance), FPropInfo));
TValue.Make(@Values.Cur, FPropInfo^.PropType, Result);
end;
ftSingle :
begin
Values.Si := Single(GetFloatProp(TObject(Instance), FPropInfo));
TValue.Make(@Values.Si, FPropInfo^.PropType, Result);
end;
ftDouble :
begin
Values.Db := Double(GetFloatProp(TObject(Instance), FPropInfo));
TValue.Make(@Values.Db, FPropInfo^.PropType, Result);
end;
ftExtended:
begin
Values.Ex := GetFloatProp(TObject(Instance), FPropInfo);
TValue.Make(@Values.Ex, FPropInfo^.PropType, Result);
end;
ftComp :
begin
Values.Cp := Comp(GetFloatProp(TObject(Instance), FPropInfo));
TValue.Make(@Values.Cp, FPropInfo^.PropType, Result);
end;
end;
end;
tkDynArray:
begin
Values.A := GetDynArrayProp(TObject(Instance), FPropInfo);
TValue.Make(@Values.A, FPropInfo^.PropType, Result);
end
else
result := TValue.Empty;
end
end;
procedure TRttiProperty.SetValue(Instance: pointer; const AValue: TValue);
begin
case FPropinfo^.PropType^.Kind of
tkSString,
tkAString:
SetStrProp(TObject(Instance), FPropInfo, AValue.AsString);
tkInteger,
tkInt64,
tkQWord,
tkChar,
tkBool,
tkWChar,
tkEnumeration:
SetOrdProp(TObject(Instance), FPropInfo, AValue.AsOrdinal);
tkClass:
SetObjectProp(TObject(Instance), FPropInfo, AValue.AsObject);
tkInterface:
SetInterfaceProp(TObject(Instance), FPropInfo, AValue.AsInterface);
tkFloat:
SetFloatProp(TObject(Instance), FPropInfo, AValue.AsExtended);
tkDynArray:
SetDynArrayProp(TObject(Instance), FPropInfo, PPointer(AValue.GetReferenceToRawData)^);
else
raise exception.createFmt(SErrUnableToSetValueForType, [PropertyType.Name]);
end
end;
function TRttiType.GetIsInstance: boolean;
begin
result := false;
end;
function TRttiType.GetIsManaged: boolean;
begin
result := Rtti.IsManaged(FTypeInfo);
end;
function TRttiType.GetIsOrdinal: boolean;
begin
result := false;
end;
function TRttiType.GetIsRecord: boolean;
begin
result := false;
end;
function TRttiType.GetIsSet: boolean;
begin
result := false;
end;
function TRttiType.GetAsInstance: TRttiInstanceType;
begin
// This is a ridicoulous design, but Delphi-compatible...
result := TRttiInstanceType(self);
end;
function TRttiType.GetBaseType: TRttiType;
begin
result := nil;
end;
function TRttiType.GetTypeKind: TTypeKind;
begin
result := FTypeInfo^.Kind;
end;
function TRttiType.GetTypeSize: integer;
begin
result := -1;
end;
function TRttiType.GetName: string;
begin
Result:=FTypeInfo^.Name;
end;
function TRttiType.GetHandle: Pointer;
begin
Result := FTypeInfo;
end;
constructor TRttiType.Create(ATypeInfo: PTypeInfo);
begin
inherited Create();
FTypeInfo:=ATypeInfo;
if assigned(FTypeInfo) then
FTypeData:=GetTypeData(ATypeInfo);
end;
function TRttiType.GetProperties: specialize TArray<TRttiProperty>;
begin
Result := Nil;
end;
function TRttiType.GetProperty(const AName: string): TRttiProperty;
var
FPropList: specialize TArray<TRttiProperty>;
i: Integer;
begin
result := nil;
FPropList := GetProperties;
for i := 0 to length(FPropList)-1 do
if sametext(FPropList[i].Name,AName) then
begin
result := FPropList[i];
break;
end;
end;
function TRttiType.GetMethods: specialize TArray<TRttiMethod>;
var
parentmethods, selfmethods: specialize TArray<TRttiMethod>;
parent: TRttiType;
begin
if Assigned(fMethods) then
Exit(fMethods);
selfmethods := GetDeclaredMethods;
parent := GetBaseType;
if Assigned(parent) then begin
parentmethods := parent.GetMethods;
end;
fMethods := Concat(parentmethods, selfmethods);
Result := fMethods;
end;
function TRttiType.GetMethod(const aName: String): TRttiMethod;
var
methods: specialize TArray<TRttiMethod>;
method: TRttiMethod;
begin
methods := GetMethods;
for method in methods do
if SameText(method.Name, AName) then
Exit(method);
Result := Nil;
end;
function TRttiType.GetDeclaredMethods: specialize TArray<TRttiMethod>;
begin
Result := Nil;
end;
{ TRttiNamedObject }
function TRttiNamedObject.GetName: string;
begin
result := '';
end;
{ TRttiContext }
class function TRttiContext.Create: TRttiContext;
begin
result.FContextToken := nil;
end;
procedure TRttiContext.Free;
begin
FContextToken := nil;
end;
function TRttiContext.GetByHandle(AHandle: Pointer): TRttiObject;
begin
if not Assigned(FContextToken) then
FContextToken := TPoolToken.Create;
Result := (FContextToken as IPooltoken).RttiPool.GetByHandle(AHandle);
end;
procedure TRttiContext.AddObject(AObject: TRttiObject);
begin
if not Assigned(FContextToken) then
FContextToken := TPoolToken.Create;
(FContextToken as IPooltoken).RttiPool.AddObject(AObject);
end;
function TRttiContext.GetType(ATypeInfo: PTypeInfo): TRttiType;
begin
if not assigned(FContextToken) then
FContextToken := TPoolToken.Create;
result := (FContextToken as IPooltoken).RttiPool.GetType(ATypeInfo);
end;
function TRttiContext.GetType(AClass: TClass): TRttiType;
begin
if assigned(AClass) then
result := GetType(PTypeInfo(AClass.ClassInfo))
else
result := nil;
end;
{function TRttiContext.GetTypes: specialize TArray<TRttiType>;
begin
if not assigned(FContextToken) then
FContextToken := TPoolToken.Create;
result := (FContextToken as IPooltoken).RttiPool.GetTypes;
end;}
{ TVirtualInterface }
{.$define DEBUG_VIRTINTF}
constructor TVirtualInterface.Create(aPIID: PTypeInfo);
const
BytesToPopQueryInterface =
{$ifdef cpui386}
3 * SizeOf(Pointer); { aIID + aObj + $RetAddr }
{$else}
0;
{$endif}
BytesToPopAddRef =
{$ifdef cpui386}
1 * SizeOf(Pointer); { $RetAddr }
{$else}
0;
{$endif}
BytesToPopRelease =
{$ifdef cpui386}
1 * SizeOf(Pointer); { $RetAddr }
{$else}
0;
{$endif}
var
t: TRttiType;
ti: PTypeInfo;
td: PInterfaceData;
methods: specialize TArray<TRttiMethod>;
m: TRttiMethod;
mt: PIntfMethodTable;
count, i: SizeInt;
begin
if not Assigned(aPIID) then
raise EArgumentNilException.Create(SErrVirtIntfTypeNil);
{ ToDo: add support for raw interfaces once they support RTTI }
if aPIID^.Kind <> tkInterface then
raise EArgumentException.CreateFmt(SErrVirtIntfTypeMustBeIntf, [aPIID^.Name]);
fContext := TRttiContext.Create;
t := fContext.GetType(aPIID);
if not Assigned(t) then
raise EInsufficientRtti.CreateFmt(SErrVirtIntfTypeNotFound, [aPIID^.Name]);
{ check whether the interface and all its parents have RTTI enabled (the only
exception is IInterface as we know the methods of that) }
td := PInterfaceData(GetTypeData(aPIID));
fGUID := td^.GUID;
fThunks[0] := AllocateRawThunk(TMethod(@QueryInterface).Code, Pointer(Self), BytesToPopQueryInterface);
fThunks[1] := AllocateRawThunk(TMethod(@_AddRef).Code, Pointer(Self), BytesToPopAddRef);
fThunks[2] := AllocateRawThunk(TMethod(@_Release).Code, Pointer(Self), BytesToPopRelease);
for i := Low(fThunks) to High(fThunks) do
if not Assigned(fThunks[i]) then
raise ENotImplemented.CreateFmt(SErrVirtIntfCreateThunk, [aPIID^.Name]);
ti := aPIID;
{ ignore the three methods of IInterface }
count := 0;
while ti <> TypeInfo(IInterface) do begin
mt := td^.MethodTable;
if (mt^.Count > 0) and (mt^.RTTICount <> mt^.Count) then
raise EInsufficientRtti.CreateFmt(SErrVirtIntfNotAllMethodsRTTI, [aPIID^.Name]);
Inc(count, mt^.Count);
ti := td^.Parent^;
td := PInterfaceData(GetTypeData(ti));
end;
SetLength(fImpls, count);
methods := t.GetMethods;
for m in methods do begin
if m.VirtualIndex > High(fImpls) + Length(fThunks) then
raise ERtti.CreateFmt(SErrVirtIntfInvalidVirtIdx, [aPIID^.Name, m.Name, m.VirtualIndex]);
if m.VirtualIndex < Length(fThunks) then
raise ERtti.CreateFmt(SErrVirtIntfInvalidVirtIdx, [aPIID^.Name, m.Name, m.VirtualIndex]);
{ we use the childmost entry, except for the IInterface methods }
if Assigned(fImpls[m.VirtualIndex - Length(fThunks)]) then begin
{$IFDEF DEBUG_VIRTINTF}Writeln('Ignoring duplicate implementation for index ', m.VirtualIndex);{$ENDIF}
Continue;
end;
fImpls[m.VirtualIndex - Length(fThunks)] := m.CreateImplementation(m, @HandleUserCallback);
end;
for i := 0 to High(fImpls) do
if not Assigned(fImpls) then
raise ERtti.CreateFmt(SErrVirtIntfMethodNil, [aPIID^.Name, i]);
fVmt := GetMem(Length(fImpls) * SizeOf(CodePointer) + Length(fThunks) * SizeOf(CodePointer));
if not Assigned(fVmt) then
raise ERtti.CreateFmt(SErrVirtIntfCreateVmt, [aPIID^.Name]);
for i := 0 to High(fThunks) do begin
fVmt[i] := fThunks[i];
{$IFDEF DEBUG_VIRTINTF}Writeln('VMT ', i, ': ', HexStr(fVmt[i]));{$ENDIF}
end;
for i := 0 to High(fImpls) do begin
fVmt[i + Length(fThunks)] := fImpls[i].CodeAddress;
{$IFDEF DEBUG_VIRTINTF}Writeln('VMT ', i + Length(fThunks), ': ', HexStr(fVmt[i + Length(fThunks)]));{$ENDIF}
end;
end;
constructor TVirtualInterface.Create(aPIID: PTypeInfo; aInvokeEvent: TVirtualInterfaceInvokeEvent);
begin
Create(aPIID);
OnInvoke := aInvokeEvent;
end;
destructor TVirtualInterface.Destroy;
var
impl: TMethodImplementation;
thunk: CodePointer;
begin
{$IFDEF DEBUG_VIRTINTF}Writeln('Freeing implementations');{$ENDIF}
for impl in fImpls do
impl.Free;
{$IFDEF DEBUG_VIRTINTF}Writeln('Freeing thunks');{$ENDIF}
for thunk in fThunks do
FreeRawThunk(thunk);
{$IFDEF DEBUG_VIRTINTF}Writeln('Freeing VMT');{$ENDIF}
if Assigned(fVmt) then
FreeMem(fVmt);
{$IFDEF DEBUG_VIRTINTF}Writeln('Freeing Context');{$ENDIF}
fContext.Free;
{$IFDEF DEBUG_VIRTINTF}Writeln('Done');{$ENDIF}
inherited Destroy;
end;
function TVirtualInterface.QueryInterface(constref aIID: TGuid; out aObj): LongInt;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
begin
{$IFDEF DEBUG_VIRTINTF}Writeln('QueryInterface for ', GUIDToString(aIID));{$ENDIF}
if IsEqualGUID(aIID, fGUID) then begin
{$IFDEF DEBUG_VIRTINTF}Writeln('Returning ', HexStr(@fVmt));{$ENDIF}
Pointer(aObj) := @fVmt;
{ QueryInterface increases the reference count }
_AddRef;
Result := S_OK;
end else
Result := inherited QueryInterface(aIID, aObj);
end;
procedure TVirtualInterface.HandleUserCallback(aUserData: Pointer; const aArgs: TValueArray; out aResult: TValue);
begin
{$IFDEF DEBUG_VIRTINTF}Writeln('Call for ', TRttiMethod(aUserData).Name);{$ENDIF}
if Assigned(fOnInvoke) then
fOnInvoke(TRttiMethod(aUserData), aArgs, aResult);
end;
{$ifndef InLazIDE}
{$if defined(CPUI386) or (defined(CPUX86_64) and defined(WIN64))}
{$I invoke.inc}
{$endif}
{$endif}
initialization
PoolRefCount := 0;
InitDefaultFunctionCallManager;
{$ifdef SYSTEM_HAS_INVOKE}
InitSystemFunctionCallManager;
{$endif}
end.