Browse Source

* Implemented more Delphi-compatilibity functions + tests
* Adapted tests so they compile on Delphi

git-svn-id: branches/joost/classattributes@25121 -

joost 12 years ago
parent
commit
150689bb0f
2 changed files with 382 additions and 17 deletions
  1. 180 11
      packages/fcl-base/src/rtti.pp
  2. 202 6
      packages/fcl-base/tests/tests_rtti.pas

+ 180 - 11
packages/fcl-base/src/rtti.pp

@@ -76,15 +76,27 @@ type
   private
   private
     FData: TValueData;
     FData: TValueData;
     function GetTypeDataProp: PTypeData;
     function GetTypeDataProp: PTypeData;
+    function GetTypeInfo: PTypeInfo;
+    function GetTypeKind: TTypeKind;
   public
   public
+    function IsArray: boolean;
     function AsString: string;
     function AsString: string;
     function AsExtended: Extended;
     function AsExtended: Extended;
-    function AsObject: TObject;
+    function IsClass: boolean;
+    function AsClass: TClass;
     function IsObject: boolean;
     function IsObject: boolean;
+    function AsObject: TObject;
+    function IsOrdinal: boolean;
+    function AsOrdinal: Int64;
     function AsBoolean: boolean;
     function AsBoolean: boolean;
     function AsCurrency: Currency;
     function AsCurrency: Currency;
     function AsInteger: Integer;
     function AsInteger: Integer;
+    function ToString: string;
+    function IsType(ATypeInfo: PTypeInfo): boolean;
+    function TryAsOrdinal(out AResult: int64): boolean;
+    property Kind: TTypeKind read GetTypeKind;
     property TypeData: PTypeData read GetTypeDataProp;
     property TypeData: PTypeData read GetTypeDataProp;
+    property TypeInfo: PTypeInfo read GetTypeInfo;
   end;
   end;
 
 
   { TRttiContext }
   { TRttiContext }
@@ -130,7 +142,13 @@ type
     FTypeData: PTypeData;
     FTypeData: PTypeData;
     function GetName: string; override;
     function GetName: string; override;
     function GetIsInstance: boolean; virtual;
     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 GetTypeKind: TTypeKind; virtual;
+    function GetTypeSize: integer; virtual;
+    function GetBaseType: TRttiType; virtual;
   public
   public
     constructor create(ATypeInfo : PTypeInfo);
     constructor create(ATypeInfo : PTypeInfo);
     function GetAttributes: specialize TArray<TCustomAttribute>; override;
     function GetAttributes: specialize TArray<TCustomAttribute>; override;
@@ -138,8 +156,14 @@ type
     function GetProperty(const AName: string): TRttiProperty; virtual;
     function GetProperty(const AName: string): TRttiProperty; virtual;
     destructor destroy; override;
     destructor destroy; override;
     property IsInstance: boolean read GetIsInstance;
     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 AsInstance: TRttiInstanceType read GetAsInstance;
     property TypeKind: TTypeKind read GetTypeKind;
     property TypeKind: TTypeKind read GetTypeKind;
+    property TypeSize: integer read GetTypeSize;
   end;
   end;
 
 
   TRttiStructuredType = class(TRttiType)
   TRttiStructuredType = class(TRttiType)
@@ -172,11 +196,15 @@ type
 
 
   TRttiInstanceType = class(TRttiStructuredType)
   TRttiInstanceType = class(TRttiStructuredType)
   private
   private
+    function GetDeclaringUnitName: string;
     function GetMetaClassType: TClass;
     function GetMetaClassType: TClass;
   protected
   protected
     function GetIsInstance: boolean; override;
     function GetIsInstance: boolean; override;
+    function GetTypeSize: integer; override;
+    function GetBaseType: TRttiType; override;
   public
   public
     property MetaClassType: TClass read GetMetaClassType;
     property MetaClassType: TClass read GetMetaClassType;
+    property DeclaringUnitName: string read GetDeclaringUnitName;
 
 
   end;
   end;
 
 
@@ -214,6 +242,8 @@ type
 
 
   end;
   end;
 
 
+function IsManaged(TypeInfo: PTypeInfo): boolean;
+
 implementation
 implementation
 
 
 type
 type
@@ -267,6 +297,11 @@ var
   PoolRefCount : integer;
   PoolRefCount : integer;
   GRttiPool    : TRttiPool;
   GRttiPool    : TRttiPool;
 
 
+function IsManaged(TypeInfo: PTypeInfo): boolean;
+begin
+  result := TypeInfo^.Kind in [tkString, tkAString, tkLString, tkInterface, tkArray, tkDynArray];
+end;
+
 { TRttiPool }
 { TRttiPool }
 
 
 function TRttiPool.GetTypes: specialize TArray<TRttiType>;
 function TRttiPool.GetTypes: specialize TArray<TRttiType>;
@@ -423,28 +458,50 @@ begin
   result := GetTypeData(FData.FTypeInfo);
   result := GetTypeData(FData.FTypeInfo);
 end;
 end;
 
 
+function TValue.GetTypeInfo: PTypeInfo;
+begin
+  result := FData.FTypeInfo;
+end;
+
+function TValue.GetTypeKind: TTypeKind;
+begin
+  result := FData.FTypeInfo^.Kind;
+end;
+
+function TValue.IsArray: boolean;
+begin
+  result := kind in [tkArray, tkDynArray];
+end;
+
 function TValue.AsString: string;
 function TValue.AsString: string;
 var
 var
   s: string;
   s: string;
 begin
 begin
-  case fdata.FTypeInfo^.Kind of
+  case Kind of
     tkSString,
     tkSString,
     tkAString   : begin
     tkAString   : begin
                     setlength(s,FData.FValueData.GetDataSize);
                     setlength(s,FData.FValueData.GetDataSize);
                     system.move(FData.FValueData.GetReferenceToRawData^,s[1],FData.FValueData.GetDataSize);
                     system.move(FData.FValueData.GetReferenceToRawData^,s[1],FData.FValueData.GetDataSize);
                   end;
                   end;
   else
   else
-    raise exception.Create(SErrInvalidTypecast);
+    raise EInvalidCast.Create(SErrInvalidTypecast);
   end;
   end;
   result := s;
   result := s;
 end;
 end;
 
 
 function TValue.AsExtended: Extended;
 function TValue.AsExtended: Extended;
 begin
 begin
-  case TypeData^.FloatType of
-    ftDouble   : result := FData.FAsDouble;
-    ftExtended : result := FData.FAsExtenden;
-  end;
+  if Kind = tkFloat then
+    begin
+    case TypeData^.FloatType of
+      ftDouble   : result := FData.FAsDouble;
+      ftExtended : result := FData.FAsExtenden;
+    else
+      raise EInvalidCast.Create(SErrInvalidTypecast);
+    end;
+    end
+  else
+    raise EInvalidCast.Create(SErrInvalidTypecast);
 end;
 end;
 
 
 function TValue.AsObject: TObject;
 function TValue.AsObject: TObject;
@@ -452,29 +509,89 @@ begin
   if IsObject then
   if IsObject then
     result := FData.FAsObject
     result := FData.FAsObject
   else
   else
-    raise exception.Create(SErrInvalidTypecast);
+    raise EInvalidCast.Create(SErrInvalidTypecast);
 end;
 end;
 
 
 function TValue.IsObject: boolean;
 function TValue.IsObject: boolean;
+begin
+  result := fdata.FTypeInfo^.Kind = tkObject;
+end;
+
+function TValue.IsClass: boolean;
 begin
 begin
   result := fdata.FTypeInfo^.Kind = tkClass;
   result := fdata.FTypeInfo^.Kind = tkClass;
 end;
 end;
 
 
+function TValue.AsClass: TClass;
+begin
+  if IsClass then
+    result := FData.FAsClass
+  else
+    raise EInvalidCast.Create(SErrInvalidTypecast);
+end;
+
+function TValue.IsOrdinal: boolean;
+begin
+  result := Kind in [tkInteger, tkInt64, tkBool];
+end;
+
 function TValue.AsBoolean: boolean;
 function TValue.AsBoolean: boolean;
 begin
 begin
-  result := boolean(FData.FAsSInt64)
+  if (Kind = tkBool) then
+    result := boolean(FData.FAsSInt64)
+  else
+    raise EInvalidCast.Create(SErrInvalidTypecast);
+end;
+
+function TValue.AsOrdinal: int64;
+begin
+  if IsOrdinal then
+    result := FData.FAsSInt64
+  else
+    raise EInvalidCast.Create(SErrInvalidTypecast);
 end;
 end;
 
 
 function TValue.AsCurrency: Currency;
 function TValue.AsCurrency: Currency;
 begin
 begin
-  result := FData.FAsCurr;
+  if (Kind = tkFloat) and (TypeData^.FloatType=ftCurr) then
+    result := FData.FAsCurr
+  else
+    raise EInvalidCast.Create(SErrInvalidTypecast);
 end;
 end;
 
 
 function TValue.AsInteger: Integer;
 function TValue.AsInteger: Integer;
 begin
 begin
-  result := Integer(FData.FAsSInt64)
+  if Kind in [tkInteger, tkInt64] then
+    result := integer(FData.FAsSInt64)
+  else
+    raise EInvalidCast.Create(SErrInvalidTypecast);
+end;
+
+function TValue.ToString: String;
+begin
+  case Kind of
+    tkString,
+    tkAString : result := AsString;
+    tkInteger : result := IntToStr(AsInteger);
+    tkBool    : result := BoolToStr(AsBoolean, True);
+  else
+    result := '';
+  end;
 end;
 end;
 
 
+function TValue.IsType(ATypeInfo: PTypeInfo): boolean;
+begin
+  result := ATypeInfo = TypeInfo;
+end;
+
+function TValue.TryAsOrdinal(out AResult: int64): boolean;
+begin
+  result := IsOrdinal;
+  if result then
+    AResult := AsOrdinal;
+end;
+
+
 { TRttiStringType }
 { TRttiStringType }
 
 
 function TRttiStringType.GetStringKind: TRttiStringKind;
 function TRttiStringType.GetStringKind: TRttiStringKind;
@@ -495,11 +612,33 @@ begin
   result := FTypeData^.ClassType;
   result := FTypeData^.ClassType;
 end;
 end;
 
 
+function TRttiInstanceType.GetDeclaringUnitName: string;
+begin
+  result := FTypeData^.UnitInfo^.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;
 function TRttiInstanceType.GetIsInstance: boolean;
 begin
 begin
   Result:=True;
   Result:=True;
 end;
 end;
 
 
+function TRttiInstanceType.GetTypeSize: integer;
+begin
+  Result:=sizeof(TObject);
+end;
+
 { TRttiMember }
 { TRttiMember }
 
 
 function TRttiMember.GetVisibility: TMemberVisibility;
 function TRttiMember.GetVisibility: TMemberVisibility;
@@ -578,17 +717,47 @@ begin
   result := false;
   result := false;
 end;
 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;
 function TRttiType.GetAsInstance: TRttiInstanceType;
 begin
 begin
   // This is a ridicoulous design, but Delphi-compatible...
   // This is a ridicoulous design, but Delphi-compatible...
   result := TRttiInstanceType(self);
   result := TRttiInstanceType(self);
 end;
 end;
 
 
+function TRttiType.GetBaseType: TRttiType;
+begin
+  result := nil;
+end;
+
 function TRttiType.GetTypeKind: TTypeKind;
 function TRttiType.GetTypeKind: TTypeKind;
 begin
 begin
   result := FTypeInfo^.Kind;
   result := FTypeInfo^.Kind;
 end;
 end;
 
 
+function TRttiType.GetTypeSize: integer;
+begin
+  result := -1;
+end;
+
 function TRttiType.GetName: string;
 function TRttiType.GetName: string;
 begin
 begin
   Result:=FTypeInfo^.Name;
   Result:=FTypeInfo^.Name;

+ 202 - 6
packages/fcl-base/tests/tests_rtti.pas

@@ -1,11 +1,18 @@
 unit tests_rtti;
 unit tests_rtti;
 
 
+{$ifdef fpc}
 {$mode objfpc}{$H+}
 {$mode objfpc}{$H+}
+{$endif}
 
 
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, fpcunit, testutils, testregistry,
+{$IFDEF FPC}
+  fpcunit,testregistry, testutils,
+{$ELSE FPC}
+  TestFramework,
+{$ENDIF FPC}
+  Classes, SysUtils, typinfo,
   Rtti;
   Rtti;
 
 
 type
 type
@@ -22,8 +29,17 @@ type
     procedure GetClassPropertiesAttributes;
     procedure GetClassPropertiesAttributes;
 
 
     procedure GetClassPropertiesValue;
     procedure GetClassPropertiesValue;
+
+    procedure TestTRttiTypeProperties;
+    procedure TestPropGetValueString;
+    procedure TestPropGetValueInteger;
+    procedure TestPropGetValueBoolean;
+    procedure TestGetValueStringCastError;
   end;
   end;
 
 
+implementation
+
+type
   { TIntAttribute }
   { TIntAttribute }
 
 
   TIntAttribute = class(TCustomAttribute)
   TIntAttribute = class(TCustomAttribute)
@@ -49,7 +65,17 @@ type
     property PubPropSetRW: integer read FPubPropRW write FPubPropRW;
     property PubPropSetRW: integer read FPubPropRW write FPubPropRW;
   end;
   end;
 
 
-implementation
+  TTestValueClass = class
+  private
+    FAInteger: integer;
+    FAString: string;
+    FABoolean: boolean;
+  published
+    property AInteger: Integer read FAInteger write FAInteger;
+    property AString: string read FAString write FAString;
+    property ABoolean: boolean read FABoolean write FABoolean;
+  end;
+
 
 
 { TIntAttribute }
 { TIntAttribute }
 
 
@@ -76,6 +102,172 @@ begin
   CheckTrue(IsTestCaseClassFound, 'RTTI information does not contain class of testcase.');
   CheckTrue(IsTestCaseClassFound, 'RTTI information does not contain class of testcase.');
 end;
 end;
 
 
+procedure TTestCase1.TestGetValueStringCastError;
+var
+  ATestClass : TTestValueClass;
+  c: TRttiContext;
+  ARttiType: TRttiType;
+  AValue: TValue;
+  i: integer;
+  HadException: boolean;
+begin
+  c := TRttiContext.Create;
+  try
+    ATestClass := TTestValueClass.Create;
+    ATestClass.AString := '12';
+    try
+      ARttiType := c.GetType(ATestClass.ClassInfo);
+      AValue := ARttiType.GetProperty('astring').GetValue(ATestClass);
+      HadException := false;
+      try
+        i := AValue.AsInteger;
+      except
+        on E: Exception do
+          if E.ClassType=EInvalidCast then
+            HadException := true;
+      end;
+      Check(HadException, 'No or invalid exception on invalid cast');
+    finally
+      AtestClass.Free;
+    end;
+  finally
+    c.Free;
+  end;
+end;
+
+procedure TTestCase1.TestPropGetValueBoolean;
+var
+  ATestClass : TTestValueClass;
+  c: TRttiContext;
+  ARttiType: TRttiType;
+  AProperty: TRttiProperty;
+  AValue: TValue;
+begin
+  c := TRttiContext.Create;
+  try
+    ATestClass := TTestValueClass.Create;
+    ATestClass.ABoolean := true;
+    try
+      ARttiType := c.GetType(ATestClass.ClassInfo);
+      Check(assigned(ARttiType));
+      AProperty := ARttiType.GetProperty('aBoolean');
+      AValue := AProperty.GetValue(ATestClass);
+      CheckEquals(true,AValue.AsBoolean);
+      ATestClass.ABoolean := false;
+      CheckEquals(true, AValue.AsBoolean);
+      CheckEquals('True', AValue.ToString);
+      CheckEquals(True, AValue.IsOrdinal);
+      CheckEquals(1, AValue.AsOrdinal);
+    finally
+      AtestClass.Free;
+    end;
+      CheckEquals(True,AValue.AsBoolean);
+  finally
+    c.Free;
+  end;
+end;
+
+procedure TTestCase1.TestPropGetValueInteger;
+var
+  ATestClass : TTestValueClass;
+  c: TRttiContext;
+  ARttiType: TRttiType;
+  AProperty: TRttiProperty;
+  AValue: TValue;
+begin
+  c := TRttiContext.Create;
+  try
+    ATestClass := TTestValueClass.Create;
+    ATestClass.AInteger := 472349;
+    try
+      ARttiType := c.GetType(ATestClass.ClassInfo);
+      Check(assigned(ARttiType));
+      AProperty := ARttiType.GetProperty('ainteger');
+      AValue := AProperty.GetValue(ATestClass);
+      CheckEquals(472349,AValue.AsInteger);
+      ATestClass.AInteger := 12;
+      CheckEquals(472349, AValue.AsInteger);
+      CheckEquals('472349', AValue.ToString);
+      CheckEquals(True, AValue.IsOrdinal);
+    finally
+      AtestClass.Free;
+    end;
+      CheckEquals(472349,AValue.AsInteger);
+  finally
+    c.Free;
+  end;
+end;
+
+procedure TTestCase1.TestPropGetValueString;
+var
+  ATestClass : TTestValueClass;
+  c: TRttiContext;
+  ARttiType: TRttiType;
+  AProperty: TRttiProperty;
+  AValue: TValue;
+  i: int64;
+begin
+  c := TRttiContext.Create;
+  try
+    ATestClass := TTestValueClass.Create;
+    ATestClass.AString := 'Hello World';
+    try
+      ARttiType := c.GetType(ATestClass.ClassInfo);
+      Check(assigned(ARttiType));
+      AProperty := ARttiType.GetProperty('astring');
+      AValue := AProperty.GetValue(ATestClass);
+      CheckEquals('Hello World',AValue.AsString);
+      ATestClass.AString := 'Goodbye World';
+      CheckEquals('Hello World',AValue.AsString);
+      CheckEquals('Hello World',AValue.ToString);
+      Check(TypeInfo(string)=AValue.TypeInfo);
+      Check(AValue.TypeData=GetTypeData(AValue.TypeInfo));
+//      Check(AValue.IsEmpty=false);
+      Check(AValue.IsObject=false);
+      Check(AValue.IsClass=false);
+      CheckEquals(AValue.IsOrdinal, false);
+      CheckEquals(AValue.TryAsOrdinal(i), false);
+      CheckEquals(AValue.IsType(TypeInfo(string)), true);
+      CheckEquals(AValue.IsType(TypeInfo(integer)), false);
+      CheckEquals(AValue.IsArray, false);
+    finally
+      AtestClass.Free;
+    end;
+    CheckEquals('Hello World',AValue.AsString);
+  finally
+    c.Free;
+  end;
+end;
+
+procedure TTestCase1.TestTRttiTypeProperties;
+var
+  c: TRttiContext;
+  ARttiType: TRttiType;
+
+begin
+  c := TRttiContext.Create;
+  try
+    ARttiType := c.GetType(TTestValueClass);
+    Check(assigned(ARttiType));
+    CheckEquals(ARttiType.Name,'TTestValueClass');
+    Check(ARttiType.TypeKind=tkClass);
+//    CheckEquals(ARttiType.IsPublicType,false);
+    CheckEquals(ARttiType.TypeSize,4);
+    CheckEquals(ARttiType.IsManaged,false);
+    CheckEquals(ARttiType.BaseType.classname,'TRttiInstanceType');
+    CheckEquals(ARttiType.IsInstance,True);
+    CheckEquals(ARttiType.AsInstance.DeclaringUnitName,'tests_rtti');
+    Check(ARttiType.BaseType.Name='TObject');
+    Check(ARttiType.AsInstance.BaseType.Name='TObject');
+    CheckEquals(ARttiType.IsOrdinal,False);
+    CheckEquals(ARttiType.IsRecord,False);
+    CheckEquals(ARttiType.IsSet,False);
+  finally
+    c.Free;
+  end;
+
+end;
+
 procedure TTestCase1.GetTypeInteger;
 procedure TTestCase1.GetTypeInteger;
 var
 var
   LContext: TRttiContext;
   LContext: TRttiContext;
@@ -93,7 +285,7 @@ procedure TTestCase1.GetClassProperties;
 var
 var
   LContext: TRttiContext;
   LContext: TRttiContext;
   LType: TRttiType;
   LType: TRttiType;
-  PropList: specialize TArray<TRttiProperty>;
+  PropList: {$ifdef fpc}specialize{$endif} TArray<TRttiProperty>;
 begin
 begin
   LContext := TRttiContext.Create;
   LContext := TRttiContext.Create;
 
 
@@ -113,7 +305,7 @@ procedure TTestCase1.GetClassAttributes;
 var
 var
   LContext: TRttiContext;
   LContext: TRttiContext;
   LType: TRttiType;
   LType: TRttiType;
-  AttrList: specialize TArray<TCustomAttribute>;
+  AttrList: {$ifdef fpc}specialize{$endif} TArray<TCustomAttribute>;
 begin
 begin
   LContext := TRttiContext.Create;
   LContext := TRttiContext.Create;
 
 
@@ -133,8 +325,8 @@ procedure TTestCase1.GetClassPropertiesAttributes;
 var
 var
   LContext: TRttiContext;
   LContext: TRttiContext;
   LType: TRttiType;
   LType: TRttiType;
-  PropList: specialize TArray<TRttiProperty>;
-  AttrList: specialize TArray<TCustomAttribute>;
+  PropList: {$ifdef fpc}specialize{$endif} TArray<TRttiProperty>;
+  AttrList: {$ifdef fpc}specialize{$endif} TArray<TCustomAttribute>;
 begin
 begin
   LContext := TRttiContext.Create;
   LContext := TRttiContext.Create;
 
 
@@ -178,6 +370,10 @@ begin
 end;
 end;
 
 
 initialization
 initialization
+{$ifdef fpc}
   RegisterTest(TTestCase1);
   RegisterTest(TTestCase1);
+{$else fpc}
+  RegisterTest(TTestCase1.Suite);
+{$endif fpc}
 end.
 end.