ソースを参照

* Dictionary start

michael 5 年 前
コミット
8bf4930531

+ 526 - 1
packages/rtl/generics.collections.pas

@@ -6,7 +6,7 @@ unit Generics.Collections;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils,
+  Classes, SysUtils, rtlconsts, Types,
   {$IFDEF Pas2js}JS,{$ENDIF}
   {$IFDEF Pas2js}JS,{$ENDIF}
   Generics.Strings, Generics.Defaults;
   Generics.Strings, Generics.Defaults;
 
 
@@ -198,6 +198,151 @@ type
     property Items[Index: SizeInt]: T read GetItem write SetItem; default;
     property Items[Index: SizeInt]: T read GetItem write SetItem; default;
   end;
   end;
 
 
+  { TPair }
+
+  TPair<TKey,TValue> = record
+    Key: TKey;
+    Value: TValue;
+    constructor Create(const AKey: TKey; const AValue: TValue);
+  end;
+
+  // Hash table using linear probing
+
+  { TDictionary }
+  EDictionary = Class(Exception);
+
+  TDictionary<TKey,TValue> = class(TEnumerable<TPair<TKey,TValue>>)
+  private
+    FMap: TJSMap;
+    function GetItem(const Key: TKey): TValue;
+    procedure SetItem(const Key: TKey; const Value: TValue);
+    procedure DoAdd(const Key: TKey; const Value: TValue);
+    function DoRemove(const Key: TKey; Notification: TCollectionNotification): TValue;
+    Function GetCount : Integer;
+  protected
+    function DoGetEnumerator: TEnumerator<TPair<TKey,TValue>>; override;
+    procedure PairNotify(const Key: TKey; Value : TValue; Action: TCollectionNotification); virtual;
+    procedure KeyNotify(const Key: TKey; Action: TCollectionNotification); virtual;
+    procedure ValueNotify(const Value: TValue; Action: TCollectionNotification); virtual;
+  public
+    Type
+      TMyType = TDictionary<TKey,TValue>;
+      TMyPair = TPair<TKey,TValue>;
+
+    constructor Create(ACapacity: Integer); overload;
+    constructor Create2(const Collection: TEnumerable<TMyPair>); overload;
+    destructor Destroy; override;
+
+    procedure Add(const Key: TKey; const Value: TValue);
+    procedure Remove(const Key: TKey);
+    function ExtractPair(const Key: TKey): TMyPair;
+    procedure Clear;
+    function TryGetValue(const Key: TKey; out Value: TValue): Boolean;
+    procedure AddOrSetValue(const Key: TKey; const Value: TValue);
+    function ContainsKey(const Key: TKey): Boolean;
+    function ContainsValue(const Value: TValue): Boolean;
+    function ToArray: TArray<TMyPair>; override;
+
+    property Items[const Key: TKey]: TValue read GetItem write SetItem; default;
+    property Count: Integer read GetCount;
+
+    type
+      { TPairEnumerator }
+
+      TPairEnumerator = class(TEnumerator<TMyPair>)
+      private
+        FIter: TJSIterator;
+        FVal : TJSIteratorValue;
+        function GetCurrent: TMyPair;
+      protected
+        function DoGetCurrent: TMyPair; override;
+        function DoMoveNext: Boolean; override;
+      public
+        constructor Create(const ADictionary: TMyType);
+        function MoveNext: Boolean; reintroduce;
+        property Current: TMyPair read GetCurrent;
+      end;
+
+      { TKeyEnumerator }
+
+      TKeyEnumerator = class(TEnumerator<TKey>)
+      private
+        FIter: TJSIterator;
+        FVal : TJSIteratorValue;
+        function GetCurrent: TKey;
+      protected
+        function DoGetCurrent: TKey; override;
+        function DoMoveNext: Boolean; override;
+      public
+        constructor Create(const AIter: TJSIterator); overload;
+        constructor Create(const ADictionary: TMyType); overload;
+        function MoveNext: Boolean; reintroduce;
+        property Current: TKey read GetCurrent;
+      end;
+
+      { TValueEnumerator }
+
+      TValueEnumerator = class(TEnumerator<TValue>)
+      private
+        FIter: TJSIterator;
+        FVal : TJSIteratorValue;
+        function GetCurrent: TValue;
+      protected
+        function DoGetCurrent: TValue; override;
+        function DoMoveNext: Boolean; override;
+      public
+        constructor Create(const AIter: TJSIterator); overload;
+        constructor Create(const ADictionary: TMyType); overload;
+        function MoveNext: Boolean; reintroduce;
+        property Current: TValue read GetCurrent;
+      end;
+
+      { TValueCollection }
+
+      TValueCollection = class(TEnumerable<TValue>)
+      private
+        FMap: TJSMap;
+        function GetCount: Integer;
+      protected
+        function DoGetEnumerator: TEnumerator<TValue>; override;
+      public
+        constructor Create(const ADictionary: TMyType);
+        function GetEnumerator: TValueEnumerator; reintroduce;
+        function ToArray: TArray<TValue>; override;
+        property Count: Integer read GetCount;
+      end;
+
+      { TKeyCollection }
+
+      TKeyCollection = class(TEnumerable<TKey>)
+      private
+        FMap: TJSMap;
+        function GetCount: Integer;
+      protected
+        function DoGetEnumerator: TEnumerator<TKey>; override;
+      public
+        constructor Create(const ADictionary: TMyType);
+        function GetEnumerator: TKeyEnumerator; reintroduce;
+        function ToArray: TArray<TKey>; override;
+        property Count: Integer read GetCount;
+      end;
+
+  private
+    FOnKeyNotify: TCollectionNotifyEvent<TKey>;
+    FOnValueNotify: TCollectionNotifyEvent<TValue>;
+    FKeyCollection: TKeyCollection;
+    FValueCollection: TValueCollection;
+    function GetKeys: TKeyCollection;
+    function GetValues: TValueCollection;
+  public
+    function GetEnumerator: TPairEnumerator; reintroduce;
+    property Keys: TKeyCollection read GetKeys;
+    property Values: TValueCollection read GetValues;
+    property OnKeyNotify: TCollectionNotifyEvent<TKey> read FOnKeyNotify write FOnKeyNotify;
+    property OnValueNotify: TCollectionNotifyEvent<TValue> read FOnValueNotify write FOnValueNotify;
+  end;
+
+
 implementation
 implementation
 
 
 { TCustomArrayHelper }
 { TCustomArrayHelper }
@@ -864,4 +1009,384 @@ begin
   Result := TMyArrayHelper.BinarySearch(FItems, AItem, AIndex, AComparer, 0, Count);
   Result := TMyArrayHelper.BinarySearch(FItems, AItem, AIndex, AComparer, 0, Count);
 end;
 end;
 
 
+{ TPair }
+
+constructor TPair<TKey,TValue>.Create(const AKey: TKey; const AValue: TValue);
+begin
+  Key:=aKey;
+  Value:=aValue;
+end;
+
+{ TDictionary }
+
+ResourceString
+  SErrDictKeyNotFound = 'Key value not found';
+  SErrDictDuplicateKey = 'Duplicate key value';
+
+function TDictionary<TKey, TValue>.GetItem(const Key: TKey): TValue;
+
+Var
+  V : JSValue;
+
+begin
+  V:=FMap.Get(Key);
+  if isUndefined(v) then
+    Raise EDictionary.Create(SErrDictKeyNotFound);
+  Result:=TValue(V);
+end;
+
+procedure TDictionary<TKey, TValue>.SetItem(const Key: TKey; const Value: TValue);
+
+Var
+  V : JSValue;
+
+begin
+  V:=FMap.Get(Key);
+  if isUndefined(v) then
+    ValueNotify(TValue(V),cnRemoved);
+  FMap.&Set(Key,Value);
+  ValueNotify(Value, cnAdded);
+end;
+
+procedure TDictionary<TKey, TValue>.DoAdd(const Key: TKey; const Value: TValue);
+begin
+  FMap.&Set(Key,Value);
+  KeyNotify(Key,cnAdded);
+  ValueNotify(Value,cnAdded);
+end;
+
+
+function TDictionary<TKey, TValue>.DoRemove(const Key: TKey; Notification: TCollectionNotification): TValue;
+Var
+  V : JSValue;
+
+begin
+  V:=FMap.Get(Key);
+  if Not isUndefined(v) then
+    begin
+    FMap.Delete(Key);
+    Result:=TValue(v);
+    KeyNotify(Key,Notification);
+    ValueNotify(Result,Notification);
+    end;
+end;
+
+function TDictionary<TKey, TValue>.GetCount: Integer;
+begin
+  Result:=FMap.Size;
+end;
+
+function TDictionary<TKey, TValue>.DoGetEnumerator: TEnumerator<TMyPair>;
+begin
+  Result:=TPairEnumerator.Create(Self);
+end;
+
+procedure TDictionary<TKey, TValue>.PairNotify(const Key: TKey; Value : TValue; Action: TCollectionNotification);
+
+begin
+  KeyNotify(Key,action);
+  ValueNotify(Value,action);
+end;
+
+procedure TDictionary<TKey, TValue>.KeyNotify(const Key: TKey; Action: TCollectionNotification);
+begin
+  if Assigned(FOnKeyNotify) then
+    FOnKeyNotify(Self,Key,Action);
+end;
+
+procedure TDictionary<TKey, TValue>.ValueNotify(const Value: TValue; Action: TCollectionNotification);
+begin
+  if Assigned(FOnValueNotify) then
+    FOnValueNotify(Self,Value,Action);
+end;
+
+constructor TDictionary<TKey, TValue>.Create(ACapacity: Integer = 0);
+begin
+
+  FMap:=TJSMap.New;
+end;
+
+constructor TDictionary<TKey, TValue>.Create2(const Collection: TEnumerable<TMyPair>);
+
+Var
+  aPair : TMyPair;
+
+begin
+  Create(0);
+  For aPair in Collection do
+    Add(aPair.Key,aPair.Value);
+end;
+
+destructor TDictionary<TKey, TValue>.Destroy;
+begin
+  FreeAndNil(FKeyCollection);
+  FreeAndNil(FValueCollection);
+  Clear;
+  FMap:=Nil;
+  inherited Destroy;
+end;
+
+procedure TDictionary<TKey, TValue>.Add(const Key: TKey; const Value: TValue);
+begin
+  if FMap.Has(Key) then
+    Raise EDictionary.Create(SErrDictDuplicateKey);
+  DoAdd(Key,Value);
+end;
+
+procedure TDictionary<TKey, TValue>.Remove(const Key: TKey);
+begin
+  doRemove(Key,cnRemoved);
+end;
+
+function TDictionary<TKey, TValue>.ExtractPair(const Key: TKey): TMyPair;
+
+begin
+  if FMap.Has(Key) then
+    begin
+    Result.Create(Key,TValue(FMap.get(key)));
+    FMap.Delete(Key);
+    end
+  else
+    Result.Create(Key,Default(TValue));
+end;
+
+procedure TDictionary<TKey, TValue>.Clear;
+begin
+  FMap.Clear;
+end;
+
+
+function TDictionary<TKey, TValue>.TryGetValue(const Key: TKey; out Value: TValue): Boolean;
+begin
+  Result:=FMap.Has(Key);
+  If Result then
+    Value:=TValue(FMap.get(Key));
+end;
+
+procedure TDictionary<TKey, TValue>.AddOrSetValue(const Key: TKey; const Value: TValue);
+begin
+  if Not FMap.Has(Key) then
+    DoAdd(Key,Value)
+  else
+    SetItem(Key,Value);
+end;
+
+function TDictionary<TKey, TValue>.ContainsKey(const Key: TKey): Boolean;
+begin
+  Result:=FMap.Has(Key);
+end;
+
+function TDictionary<TKey, TValue>.ContainsValue(const Value: TValue): Boolean;
+
+Var
+  It : TJSIterator;
+  Res : TJSIteratorValue;
+
+begin
+  Result:=False;
+  It:=FMap.Values;
+  Repeat
+    Res:=It.next;
+    if not Res.done then
+      Result:=(Value=TValue(Res.value));
+  Until (Result or Res.done);
+end;
+
+function TDictionary<TKey, TValue>.ToArray: TArray<TMyPair>;
+begin
+  Result:=inherited ToArray;
+end;
+
+function TDictionary<TKey, TValue>.GetKeys: TKeyCollection;
+begin
+  if FKeyCollection=Nil then
+    FKeyCollection:=TKeyCollection.Create(Self);
+  Result:=FKeyCollection;
+end;
+
+function TDictionary<TKey, TValue>.GetValues: TValueCollection;
+begin
+  if FValueCollection=Nil then
+    FValueCollection:=TValueCollection.Create(Self);
+  Result:=FValueCollection;
+end;
+
+function TDictionary<TKey, TValue>.GetEnumerator: TPairEnumerator;
+begin
+  Result:=TPairEnumerator.Create(Self);
+end;
+
+{ TDictionary.TPairEnumerator }
+
+function TDictionary<TKey, TValue>.TPairEnumerator.GetCurrent: TMyPair;
+begin
+  Result:=DoGetCurrent;
+end;
+
+function TDictionary<TKey, TValue>.TPairEnumerator.DoGetCurrent: TMyPair;
+
+Var
+  A : TJSValueDynArray;
+
+begin
+  A:=TJSValueDynArray(FVal.Value);
+  Result.Create(TKey(A[0]),TValue(A[1]));
+end;
+
+function TDictionary<TKey, TValue>.TPairEnumerator.DoMoveNext: Boolean;
+begin
+  FIter.Next;
+  Result:=Not FVal.Done;
+end;
+
+constructor TDictionary<TKey, TValue>.TPairEnumerator.Create(const ADictionary: TMyType);
+begin
+  FIter:=ADictionary.FMap.Entries;
+end;
+
+function TDictionary<TKey, TValue>.TPairEnumerator.MoveNext: Boolean;
+begin
+  Result:=DoMoveNext;
+end;
+
+{ TDictionary.TKeyEnumerator }
+
+function TDictionary<TKey, TValue>.TKeyEnumerator.GetCurrent: TKey;
+begin
+  Result:=DoGetCurrent;
+end;
+
+function TDictionary<TKey, TValue>.TKeyEnumerator.DoGetCurrent: TKey;
+begin
+  Result:=TKey(FVal.Value);
+end;
+
+function TDictionary<TKey, TValue>.TKeyEnumerator.DoMoveNext: Boolean;
+begin
+  FIter.Next;
+  Result:=Not FVal.Done;
+end;
+
+constructor TDictionary<TKey, TValue>.TKeyEnumerator.Create(const ADictionary: TMyType);
+begin
+  Create(ADictionary.FMap.Keys);
+end;
+
+constructor TDictionary<TKey, TValue>.TKeyEnumerator.Create(const AIter : TJSIterator);
+begin
+  FIter:=aIter;
+end;
+
+function TDictionary<TKey, TValue>.TKeyEnumerator.MoveNext: Boolean;
+begin
+  Result:=DoMoveNext;
+end;
+
+{ TDictionary.TValueEnumerator }
+
+function TDictionary<TKey, TValue>.TValueEnumerator.GetCurrent: TValue;
+begin
+  Result:=DoGetCurrent;
+end;
+
+function TDictionary<TKey, TValue>.TValueEnumerator.DoGetCurrent: TValue;
+begin
+  Result:=TValue(FVal.Value);
+end;
+
+function TDictionary<TKey, TValue>.TValueEnumerator.DoMoveNext: Boolean;
+begin
+  FIter.Next;
+  Result:=Not FVal.Done;
+end;
+
+constructor TDictionary<TKey, TValue>.TValueEnumerator.Create(const ADictionary: TMyType);
+begin
+  Create(aDictionary.FMap.Values);
+end;
+
+constructor TDictionary<TKey, TValue>.TValueEnumerator.Create(const AIter: TJSIterator);
+begin
+  FIter:=AIter;
+end;
+
+function TDictionary<TKey, TValue>.TValueEnumerator.MoveNext: Boolean;
+begin
+  Result:=DoMoveNext;
+end;
+
+{ TDictionary.TValueCollection }
+
+function TDictionary<TKey, TValue>.TValueCollection.GetCount: Integer;
+begin
+  Result:=FMap.Size;
+end;
+
+function TDictionary<TKey, TValue>.TValueCollection.DoGetEnumerator: TEnumerator<TValue>;
+begin
+  Result:=TValueEnumerator.Create(FMap.Values);
+end;
+
+constructor TDictionary<TKey, TValue>.TValueCollection.Create(const ADictionary: TMyType);
+begin
+  FMap:=ADictionary.FMap;
+end;
+
+function TDictionary<TKey, TValue>.TValueCollection.GetEnumerator: TValueEnumerator;
+begin
+  Result:=TValueEnumerator(DoGetEnumerator);
+end;
+
+function TDictionary<TKey, TValue>.TValueCollection.ToArray: TArray<TValue>;
+
+Var
+  I : Integer;
+  P : TValue;
+
+begin
+  SetLength(Result,FMap.Size);
+  For P in Self do
+    begin
+    Result[i]:=P;
+    Inc(I);
+    End;
+end;
+
+{ TDictionary.TKeyCollection }
+
+function TDictionary<TKey, TValue>.TKeyCollection.GetCount: Integer;
+begin
+  Result:=FMap.Size;
+end;
+
+function TDictionary<TKey, TValue>.TKeyCollection.DoGetEnumerator: TEnumerator<TKey>;
+begin
+  Result:=GetEnumerator;
+end;
+
+constructor TDictionary<TKey, TValue>.TKeyCollection.Create(const ADictionary: TMyType);
+begin
+  FMap:=aDictionary.FMap;
+end;
+
+function TDictionary<TKey, TValue>.TKeyCollection.GetEnumerator: TKeyEnumerator;
+begin
+  Result:=TKeyEnumerator.Create(FMap.Keys);
+end;
+
+function TDictionary<TKey, TValue>.TKeyCollection.ToArray: TArray<TKey>;
+begin
+  Result:=inherited ToArray;
+end;
+
+Type
+  TMyDict = TDictionary<integer,string>;
+
+Var
+  MyDict : TMyDict;
+
+begin
+  MyDict:=TMyDict.Create;
+  MyDict.Add(1,'aloga');
+  MyDict.Free;
 end.
 end.

+ 1 - 1
test/tcgenarrayhelper.pas

@@ -58,7 +58,7 @@ procedure TTestArrayHelper.CheckBinarySearch(constref AArray: TArray<Integer>;
 
 
 begin
 begin
   CheckEquals(AExpectedResult,
   CheckEquals(AExpectedResult,
-    TArrayHelper<Integer>.BinarySearch(AArray,AValue,ASearchResult,aComparer),
+    TArrayHelper<Integer>.BinarySearch(AArray,AValue,ASearchResult,aComparer,0,Length(AArray)),
     'Wrong BinarySearch result for ' + AValue.ToString);
     'Wrong BinarySearch result for ' + AValue.ToString);
 end;
 end;
 
 

+ 202 - 0
test/tcgenericdictionary.pp

@@ -0,0 +1,202 @@
+unit tcgenericdictionary;
+
+{$mode objfpc}
+
+interface
+
+uses
+  fpcunit, testregistry, Classes, SysUtils, Generics.Defaults, Generics.Collections;
+
+Type
+  TMySimpleDict = Class(Specialize TDictionary<Integer,String>);
+
+  { TTestSimpleDictionary }
+
+  TTestSimpleDictionary = Class(TTestCase)
+  Private
+    FDict : TMySimpleDict;
+    procedure DoAdd(aCount: Integer; aOffset: Integer=0);
+    procedure DoAdd2;
+    procedure DoGetValue(aKey: Integer; Match: String; ExceptionClass: TClass=nil);
+  Public
+    Procedure SetUp; override;
+    Procedure TearDown; override;
+    Property Dict : TMySimpleDict Read FDict;
+  Published
+    Procedure TestEmpty;
+    Procedure TestAdd;
+    Procedure TestClear;
+    Procedure TestTryGetValue;
+    Procedure TestGetValue;
+    Procedure TestSetValue;
+    Procedure TestAddDuplicate;
+    Procedure TestAddOrSet;
+    Procedure TestContainsKey;
+    Procedure TestDelete;
+  end;
+
+implementation
+
+{ TTestSimpleDictionary }
+
+procedure TTestSimpleDictionary.SetUp;
+begin
+  inherited SetUp;
+  FDict:=TMySimpleDict.Create;
+end;
+
+procedure TTestSimpleDictionary.TearDown;
+begin
+  FreeAndNil(FDict);
+  inherited TearDown;
+end;
+
+procedure TTestSimpleDictionary.TestEmpty;
+begin
+  AssertNotNull('Have dictionary',Dict);
+  AssertEquals('empty dictionary',0,Dict.Count);
+end;
+
+procedure TTestSimpleDictionary.DoAdd(aCount : Integer; aOffset : Integer=0);
+
+Var
+  I : Integer;
+
+begin
+  if aOffset=-1 then
+    aOffset:=Dict.Count;
+  For I:=aOffset+1 to aOffset+aCount do
+    Dict.Add(I,IntToStr(i));
+end;
+
+procedure TTestSimpleDictionary.TestAdd;
+
+begin
+  DoAdd(1);
+  AssertEquals('Count OK',1,Dict.Count);
+  AssertTrue('Has added value',Dict.ContainsKey(1));
+  DoAdd(1,1);
+  AssertEquals('Count OK',2,Dict.Count);
+  AssertTrue('Has added value',Dict.ContainsKey(2));
+end;
+
+procedure TTestSimpleDictionary.TestClear;
+begin
+  DoAdd(3);
+  AssertEquals('Count OK',3,Dict.Count);
+  Dict.Clear;
+  AssertEquals('Count after clear OK',0,Dict.Count);
+end;
+
+procedure TTestSimpleDictionary.TestTryGetValue;
+
+Var
+  I : integer;
+  SI,A : string;
+
+begin
+  DoAdd(3);
+  For I:=1 to 3 do
+    begin
+    SI:=IntToStr(I);
+    AssertTrue('Have value '+SI,Dict.TryGetValue(I,A));
+    AssertEquals('Value is correct '+SI,SI,A);
+    end;
+  AssertFalse('Have no value 4',Dict.TryGetValue(4,A));
+end;
+
+procedure TTestSimpleDictionary.DoGetValue(aKey: Integer; Match: String; ExceptionClass: TClass);
+
+Var
+  EC : TClass;
+  A,EM : String;
+
+begin
+  EC:=Nil;
+  try
+    A:=Dict.Items[aKey];
+  except
+    On E : Exception do
+      begin
+      EC:=E.ClassType;
+      EM:=E.Message;
+      end
+  end;
+  if ExceptionClass=Nil then
+    begin
+    if EC<>Nil then
+      Fail('Got exception '+EC.ClassName+' with message: '+EM);
+    AssertEquals('Value is correct for '+IntToStr(aKey),Match,A)
+    end
+  else
+    begin
+    if EC=Nil then
+      Fail('Expected exception '+ExceptionClass.ClassName+' but got none');
+    if EC<>ExceptionClass then
+      Fail('Expected exception class '+ExceptionClass.ClassName+' but got '+EC.ClassName+' with message '+EM);
+    end;
+end;
+
+procedure TTestSimpleDictionary.TestGetValue;
+
+Var
+  I : integer;
+
+begin
+  DoAdd(3);
+  For I:=1 to 3 do
+    DoGetValue(I,IntToStr(I));
+  DoGetValue(4,'4',EDictionary);
+end;
+
+procedure TTestSimpleDictionary.TestSetValue;
+begin
+  TestGetValue;
+  Dict.Items[3]:='Six';
+  DoGetValue(3,'Six');
+end;
+
+procedure TTestSimpleDictionary.DoAdd2;
+
+begin
+  Dict.Add(2,'A new 2');
+end;
+
+procedure TTestSimpleDictionary.TestAddDuplicate;
+begin
+  DoAdd(3);
+  AssertException('Cannot add duplicate',EDictionary,@DoAdd2);
+end;
+
+procedure TTestSimpleDictionary.TestAddOrSet;
+
+begin
+  DoAdd(3);
+  Dict.AddOrSetValue(2,'a new 2');
+  DoGetValue(2,'a new 2');
+end;
+
+procedure TTestSimpleDictionary.TestContainsKey;
+
+Var
+  I : Integer;
+
+begin
+  DoAdd(3);
+  For I:=1 to 3 do
+    AssertTrue('Has '+IntToStr(i),Dict.ContainsKey(I));
+  AssertFalse('Has not 4',Dict.ContainsKey(4));
+end;
+
+procedure TTestSimpleDictionary.TestDelete;
+begin
+  DoAdd(3);
+  Dict.Remove(2);
+  AssertEquals('Count',2,Dict.Count);
+  AssertFalse('Has not 2',Dict.ContainsKey(2));
+end;
+
+begin
+  RegisterTest(TTestSimpleDictionary);
+end.
+

+ 8 - 1
test/testrtl.lpi

@@ -76,6 +76,10 @@
         <Filename Value="tcgenarrayhelper.pas"/>
         <Filename Value="tcgenarrayhelper.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
       </Unit>
       </Unit>
+      <Unit>
+        <Filename Value="tcgenericdictionary.pp"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
     </Units>
     </Units>
   </ProjectOptions>
   </ProjectOptions>
   <CompilerOptions>
   <CompilerOptions>
@@ -95,6 +99,9 @@
     </Parsing>
     </Parsing>
     <CodeGeneration>
     <CodeGeneration>
       <TargetOS Value="browser"/>
       <TargetOS Value="browser"/>
+      <Optimizations>
+        <OptimizationLevel Value="0"/>
+      </Optimizations>
     </CodeGeneration>
     </CodeGeneration>
     <Linking>
     <Linking>
       <Debugging>
       <Debugging>
@@ -103,7 +110,7 @@
       </Debugging>
       </Debugging>
     </Linking>
     </Linking>
     <Other>
     <Other>
-      <CustomOptions Value="-Jeutf-8 -Jirtl.js -Jc"/>
+      <CustomOptions Value="-Jeutf-8 -Jirtl.js -Jc -O-"/>
       <CompilerPath Value="$(pas2js)"/>
       <CompilerPath Value="$(pas2js)"/>
     </Other>
     </Other>
   </CompilerOptions>
   </CompilerOptions>

+ 4 - 2
test/testrtl.lpr

@@ -27,14 +27,16 @@ program testrtl;
 uses
 uses
   browserconsole, consoletestrunner, frmrtlrun,
   browserconsole, consoletestrunner, frmrtlrun,
 //  tcstream, tccompstreaming, simplelinkedlist, tcsyshelpers
 //  tcstream, tccompstreaming, simplelinkedlist, tcsyshelpers
-  tcgenarrayhelper,
+//  tcgenarrayhelper,
 //  tcstringhelp
 //  tcstringhelp
-  strutils;
+  tcgenericdictionary,
+  strutils, sysutils;
 
 
 var
 var
   Application : TTestRunner;
   Application : TTestRunner;
 
 
 begin
 begin
+  SysUtils.HookUncaughtExceptions;
   Application:=TTestRunner.Create(nil);
   Application:=TTestRunner.Create(nil);
   Application.RunFormClass:=TConsoleRunner;
   Application.RunFormClass:=TConsoleRunner;
   Application.Initialize;
   Application.Initialize;