github.com/remobjects/goldbaselibrary@v0.0.0-20230924164425-d458680a936b/Source/Gold/Reflect.pas (about)

     1  namespace go.reflect;
     2  
     3  {$IF ECHOES}
     4  uses
     5    System.Reflection, System.Linq;
     6  {$ENDIF}
     7  
     8  type
     9    Kind = public type go.builtin.uint;
    10    ChanDir = public type go.builtin.int;
    11    PlatformEnumerator = {$IF ISLAND}IEnumerator<tuple of (go.reflect.Value, go.reflect.Value)>{$ELSEIF ECHOES}System.Collections.Generic.IEnumerator<tuple of (go.reflect.Value, go.reflect.Value)>{$ENDIF};
    12  
    13    SliceCtor = procedure(aInst: Object; aCount: Integer);
    14    SliceObjectCtor = procedure(aInst: Object; aObject: Object);
    15  
    16    ZeroFunction = method: Object;
    17  
    18    {$IF ISLAND}
    19    TEqualsMethod = public method(a: Object; b: Object): Boolean;
    20    {$ENDIF}
    21  
    22    SliceAlias = record
    23      VMT: IntPtr;
    24      aVal: Object;
    25    end;
    26  
    27    MemoryExt<T> = extension record(Memory<T>)
    28    public
    29      class method Get(aVal: Memory<T>): T;
    30      begin
    31        exit aVal^;
    32      end;
    33    end;
    34  
    35    [ValueTypeSemantics]
    36    MapIter = public class
    37    private
    38      fEnumerator: PlatformEnumerator;
    39    public
    40      constructor(aEnum: PlatformEnumerator);
    41      begin
    42        fEnumerator := aEnum;
    43      end;
    44  
    45      method Key: Value;
    46      begin
    47        result := fEnumerator.Current[0];
    48      end;
    49  
    50      method Value: Value;
    51      begin
    52        result := fEnumerator.Current[1];
    53      end;
    54  
    55      method Next: Boolean;
    56      begin
    57        result := fEnumerator.MoveNext;
    58      end;
    59    end;
    60  
    61  const
    62    Invalid: Kind = Kind(0);
    63    Bool: Kind = Kind(1);
    64    Int: Kind = Kind(2);
    65    Int8: Kind = Kind(3);
    66    Int16: Kind = Kind(4);
    67    Int32: Kind = Kind(5);
    68    Int64: Kind = Kind(6);
    69    Uint: Kind = Kind(7);
    70    Uint8: Kind = Kind(8);
    71    Uint16: Kind = Kind(9);
    72    Uint32: Kind = Kind(10);
    73    Uint64: Kind = Kind(11);
    74    Uintptr: Kind = Kind(12);
    75    Float32: Kind = Kind(13);
    76    Float64: Kind = Kind(14);
    77    Complex64: Kind = Kind(15);
    78    Complex128: Kind = Kind(16);
    79    &Array: Kind = Kind(17);
    80    Chan: Kind = Kind(18);
    81    Func: Kind = Kind(19);
    82    &Interface: Kind = Kind(20);
    83    Map: Kind = Kind(21);
    84    Ptr: Kind = Kind(22);
    85    Slice: Kind = Kind(23);
    86    String: Kind = Kind(24);
    87    Struct: Kind = Kind(25);
    88    UnsafePointer: Kind = Kind(26);
    89  
    90    RecvDir: ChanDir = ChanDir(1 shl 0);
    91    SendDir: ChanDir = ChanDir(1 shl 1);
    92    BothDor: ChanDir = ChanDir(Integer(RecvDir) + Integer(SendDir));
    93  
    94  type
    95    PlatformExtendedType = {$IF ISLAND}RemObjects.Elements.System.MemberInfo{$ELSEIF ECHOES}System.Reflection.MemberInfo{$ENDIF};
    96  
    97    ValueExtendedInfo = public (None, Slice);
    98  
    99    [ValueTypeSemantics]
   100    Value = public class
   101    private
   102    assembly
   103      fValue: Object;
   104      fType: &Type;
   105      fPtr: Object;
   106      fExtended: PlatformExtendedType; // basically for struct fields
   107      fExtendedInfo: ValueExtendedInfo;
   108      fExtendedObject: Object;
   109    public
   110      constructor;
   111      begin
   112  
   113      end;
   114  
   115      constructor(aValue: Object; aType: &Type := nil);
   116      begin
   117        fValue := aValue;
   118        if aType ≠ nil then
   119          fType := aType
   120        else
   121          if aValue ≠ nil then
   122            fType := TypeOf(aValue);
   123        fPtr := aValue;
   124      end;
   125  
   126      constructor(aValue: Object; aType: &Type; aPtr: Object; aExtendedInfo: PlatformExtendedType := nil);
   127      begin
   128        constructor(aValue, aType);
   129        fPtr := aPtr;
   130        fExtended := aExtendedInfo;
   131      end;
   132  
   133      constructor(aValue: Object; aValueExtendedInfo: ValueExtendedInfo; aExtendedPtr: Object; aExtendedObject: Object);
   134      begin
   135        constructor(aValue);
   136        fExtendedInfo := aValueExtendedInfo;
   137        fExtendedObject := aExtendedObject;
   138        fPtr := aExtendedPtr;
   139      end;
   140  
   141      class var fZero: Value := new Value();
   142      class property Zero: Value := fZero; published;
   143  
   144      method String: String;
   145      begin
   146        exit (if fValue is IMemory then IMemory(fValue).GetValue else fValue).ToString;
   147      end;
   148  
   149      method Int: Int64;
   150      begin
   151        var lValue := InternalGetValue;
   152        if lValue is IMemory then
   153          lValue := IMemory(lValue).GetValue;
   154        {$IFDEF ISLAND}
   155        exit RemObjects.Elements.System.Convert.ToInt64(lValue);
   156        {$ELSE}
   157        if lValue is IConvertible then
   158          exit System.Convert.ToInt64(lValue)
   159        else
   160          if lValue is System.IntPtr then
   161            exit System.IntPtr(lValue).ToInt64
   162          else
   163            raise new Exception("Can not convert value");
   164        {$ENDIF}
   165      end;
   166  
   167      method Uint: UInt64;
   168      begin
   169        var lValue := InternalGetValue;
   170        if lValue is IMemory then
   171          lValue := IMemory(lValue).GetValue;
   172  
   173        {$IFDEF ISLAND}
   174        exit RemObjects.Elements.System.Convert.ToUInt64(lValue);
   175        {$ELSE}
   176        if lValue is IConvertible then
   177          exit System.Convert.ToUInt64(lValue)
   178        else
   179          if lValue is System.UIntPtr then
   180            exit System.UIntPtr(lValue).ToUInt64
   181          else
   182            raise new Exception("Can not convert value");
   183        {$ENDIF}
   184      end;
   185      method Float: Double;
   186      begin
   187        exit {$IFDEF ISLAND}RemObjects.Elements.System.Convert{$ELSE}System.Convert{$ENDIF}.ToDouble(if fValue is IMemory then IMemory(fValue).GetValue else fValue);
   188      end;
   189  
   190      method IsNil: Boolean;
   191      begin
   192        case fType.Kind of
   193          Chan, Func, Map, go.reflect.Interface, UnsafePointer, __Global.Slice, go.reflect.Ptr: // missing &Interface Kind(20) --> &Interface
   194            exit (if fValue is IMemory then IMemory(fValue).GetValue else fValue) = nil;
   195  
   196          else
   197            raise new Exception('Wrong value type');
   198        end;
   199      end;
   200  
   201      method Complex: go.builtin.complex128;
   202      begin
   203        exit go.builtin.complex128(if fValue is IMemory then IMemory(fValue).GetValue else fValue);
   204      end;
   205  
   206      method Bool: Boolean;
   207      begin
   208        exit {$IFDEF ISLAND}RemObjects.Elements.System.Convert{$ELSE}System.Convert{$ENDIF}.ToBoolean(if fValue is IMemory then IMemory(fValue).GetValue else fValue);
   209      end;
   210  
   211      method Convert(aTo: &Type): Value;
   212      begin
   213        raise new NotImplementedException;
   214      end;
   215  
   216      method IsValid: Boolean;
   217      begin
   218        if fValue = nil then
   219          exit false;
   220  
   221        var lValue := if fValue is IMemory then IMemory(fValue).GetValue else fValue;
   222        result := lValue ≠ go.reflect.Zero(fType);
   223      end;
   224  
   225      method CanSet: Boolean;
   226      begin
   227        result := CanAddr;
   228      end;
   229  
   230      method Recv(): tuple of (Value, Boolean);
   231      begin
   232        raise new NotImplementedException();
   233      end;
   234  
   235      method MapIndex(key: Value): Value;
   236      begin
   237        if fType.Kind ≠ Map then
   238          raise new Exception('Wrong type, need a map');
   239  
   240        result := go.builtin.IMap(fValue).GetReflectValue(key);
   241      end;
   242  
   243      method MapIter: Memory<MapIter>;
   244      begin
   245        if fType.Kind ≠ Map then
   246          raise new Exception('Wrong type, need a map');
   247  
   248        var lIter := new MapIter(go.builtin.IMap(fValue).GetReflectSequence().GetEnumerator());
   249        result := new Memory<MapIter>(lIter);
   250      end;
   251  
   252      method MapKeys: go.builtin.Slice<Value>;
   253      begin
   254        if fType.Kind ≠ Map then
   255          raise new Exception('Wrong type, need a map');
   256  
   257        result := go.builtin.IMap(fValue).GetReflectKeys();
   258      end;
   259  
   260      method CanInterface(): Boolean;
   261      begin
   262        result := true;
   263        // TODO check, should be false for unexported struct fields or methods.
   264      end;
   265  
   266      method Slice(i, j: Integer): Value;
   267      begin
   268        case fType.Kind of
   269          go.reflect.Slice:
   270            exit go.builtin.ISlice(fValue).getReflectSlice(i, j);
   271  
   272          go.reflect.String: begin
   273            var lString := fValue as go.builtin.string;
   274            exit new Value(new go.builtin.Slice<Byte>(lString.Value, i, j-i));
   275          end;
   276  
   277          else
   278            raise new Exception('Wrong type, need a Map or String');
   279        end;
   280      end;
   281  
   282      method Slice3(i, j, k: Integer): Value;
   283      begin
   284        // TODO
   285      end;
   286  
   287      method &Method(i: Int64): Value;
   288      begin
   289        raise new NotImplementedException;
   290      end;
   291  
   292      method Pointer(): UInt64;
   293      begin
   294        var lValue := InternalGetValue;
   295        {$IF ISLAND}
   296        exit RemObjects.Elements.System.UInt64(InternalCalls.Cast(lValue));
   297        {$ELSEIF ECHOES}
   298        //exit System.Convert.ToUInt64(lValue);
   299        exit 0;
   300        {$ENDIF}
   301      end;
   302  
   303      method MethodByName(name: String): Value;
   304      begin
   305        raise new NotImplementedException;
   306      end;
   307  
   308      method OverflowFloat(x: Double): Boolean;
   309      begin
   310        raise new NotImplementedException;
   311      end;
   312  
   313      method OverflowInt(x: Int64): Boolean;
   314      begin
   315        raise new NotImplementedException;
   316      end;
   317  
   318      method OverflowUint(x: Int64): Boolean;
   319      begin
   320        raise new NotImplementedException;
   321      end;
   322  
   323      method FieldByIndex(idx: go.builtin.Slice<Int64>): Value;
   324      begin
   325        raise new NotImplementedException;
   326      end;
   327  
   328      method SetCap(n: Integer);
   329      begin
   330        raise new NotImplementedException;
   331      end;
   332  
   333      method SetLen(n: Integer);
   334      begin
   335        var lKind := Kind;
   336        if (lKind <> &Array) and (lKind <> go.reflect.Slice) then
   337          raise new Exception("Wrong type, need array or slice");
   338        go.builtin.ISlice(fValue).setLen(n);
   339      end;
   340  
   341      method Cap: Integer;
   342      begin
   343        var lKind := Kind;
   344        if (lKind <> &Array) and (lKind <> go.reflect.Slice) then
   345          raise new Exception("Wrong type, need array or slice");
   346  
   347        exit go.builtin.ISlice(fValue).getCap;
   348      end;
   349  
   350      method &Set(aVal: Value);
   351      begin
   352        var lValue := aVal.fValue;
   353  
   354        if TypeImpl(aVal.fType).RealType <> TypeImpl(self.fType).RealType then
   355          if not CanSet or not fType.AssignableTo(TypeOf(lValue)) then
   356            raise new Exception('Can not set object');
   357  
   358        if fExtendedInfo = ValueExtendedInfo.Slice then begin
   359          go.builtin.ISlice(fPtr).setAtIndex(Integer(fExtendedObject), lValue);
   360        end
   361        else begin
   362          if fExtended <> nil then begin // struct field
   363            //(fExtended as FieldInfo).SetValue(go.builtin.IReference(fPtr).Get, lValue);
   364            if fPtr is IMemory then
   365              (fExtended as FieldInfo).SetValue(IMemory(fPtr).GetValue, lValue)
   366            else
   367              (fExtended as FieldInfo).SetValue(fPtr, lValue);
   368            fValue := lValue;
   369          end
   370          else begin
   371            //if fPtr is go.builtin.IReference then
   372              //go.builtin.IReference(fPtr).Set(lValue);
   373            if fPtr is IMemory then
   374              IMemory(fPtr).SetValue(lValue);
   375            fValue := lValue;
   376          end;
   377        end;
   378      end;
   379  
   380      method InternalSet(aValue: Object); private;
   381      begin
   382        if fExtendedInfo = ValueExtendedInfo.Slice then begin
   383          go.builtin.ISlice(fPtr).setAtIndex(Integer(fExtendedObject), aValue);
   384        end
   385        else begin
   386          if fExtended <> nil then begin // struct field
   387            //(fExtended as FieldInfo).SetValue(go.builtin.IReference(fPtr).Get, aValue);
   388            if fPtr is IMemory then
   389              (fExtended as FieldInfo).SetValue(IMemory(fPtr).GetValue, aValue)
   390            else
   391              (fExtended as FieldInfo).SetValue(fPtr, aValue);
   392          end
   393          else begin
   394            //go.builtin.IReference(fPtr).Set(aValue);
   395            IMemory(fPtr).SetValue(aValue);
   396            fValue := aValue;
   397          end;
   398        end;
   399      end;
   400  
   401      method SetBytes(aVal: go.builtin.Slice<Byte>);
   402      begin
   403        var lValue := fValue as go.builtin.Slice<Byte>;
   404        if lValue = nil then
   405          raise new Exception('Wrong type, need a Slice of bytes');
   406  
   407        lValue.Assign(aVal);
   408      end;
   409  
   410      method &SetInt(aVal: Int64);
   411      begin
   412        if (not CanSet) or not (Integer(Kind) in [Integer(go.reflect.Int)..Integer(go.reflect.Int64)])  then
   413          raise new Exception('Can not set object to integer value');
   414  
   415        {$IF ECHOES}
   416        if (aVal ≥ :go.math.MinInt32) and (aVal ≤ :go.math.MaxInt32) then // .net runtime fails if a Int64 value try to set in a Int32 field, even if fit.
   417          InternalSet(Integer(aVal))
   418        else
   419          InternalSet(aVal);
   420        {$ELSE}
   421        InternalSet(aVal);
   422        {$ENDIF}
   423      end;
   424  
   425      method &SetBool(aVal: Boolean);
   426      begin
   427        if (not CanSet) or (Kind ≠ go.reflect.Bool) then
   428          raise new Exception('Can not set object to bool value');
   429  
   430        InternalSet(aVal);
   431      end;
   432  
   433      method &SetUint(aVal: UInt64);
   434      begin
   435        if (not CanSet) or not (Integer(Kind) in [Integer(go.reflect.Uint)..Integer(go.reflect.Uint64)])  then
   436          raise new Exception('Can not set object to unsigned integer value');
   437  
   438        InternalSet(aVal);
   439      end;
   440  
   441      method &SetFloat(aVal: Double);
   442      begin
   443        if (not CanSet) or ((Kind ≠ go.reflect.Float32) and (Kind ≠ go.reflect.Float64)) then
   444          raise new Exception('Can not set object to float value');
   445  
   446        InternalSet(aVal);
   447      end;
   448  
   449      method &SetComplex(aVal: go.builtin.complex128);
   450      begin
   451        raise new NotImplementedException;
   452      end;
   453  
   454      method &SetString(aVal: go.builtin.string);
   455      begin
   456        if (not CanSet) or (Kind ≠ go.reflect.String) then
   457          raise new Exception('Can not set object to string value');
   458  
   459        InternalSet(aVal);
   460      end;
   461  
   462      method &Type: &Type;
   463      begin
   464        result := fType;
   465      end;
   466  
   467      method Bytes: go.builtin.Slice<Byte>;
   468      begin
   469        var lValue := InternalGetValue;
   470        if lValue is go.builtin.Slice<Byte> then
   471          exit lValue as go.builtin.Slice<Byte>
   472        else
   473          raise new Exception('Wrong type, need a Slice of bytes');
   474      end;
   475  
   476      method Kind: Kind;
   477      begin
   478        if fType = nil then
   479          result := go.reflect.Invalid
   480        else
   481          result := fType.Kind;
   482      end;
   483  
   484      method Len: Integer;
   485      begin
   486        var lKind := Kind();
   487        var lValue := InternalGetValue;
   488  
   489        case lKind of
   490          go.reflect.Slice:
   491            exit go.builtin.ISlice(lValue).getLen();
   492  
   493          go.reflect.Array:
   494            exit System.Array(lValue).Length;
   495  
   496          go.reflect.Map:
   497            exit go.builtin.IMap(lValue).GetLen();
   498  
   499          go.reflect.String:
   500            exit go.builtin.string(lValue).Length;
   501  
   502          go.reflect.Chan:
   503            exit go.builtin.IChannel(lValue).Length;
   504  
   505          else
   506            raise new Exception("Wrong type calling reflect.Len method");
   507        end;
   508      end;
   509  
   510      method InternalGetValue: Object; private;
   511      begin
   512        var lType := TypeImpl(fType).RealType;
   513        if lType.IsValueType then begin
   514          {$IF ECHOES}
   515          var lFieldValue := lType.GetField('Value');
   516          {$ELSE}
   517          var lFieldValue := lType.Fields.FirstOrDefault(a -> a.Name = 'Value');
   518          {$ENDIF}
   519          if lFieldValue = nil then
   520            exit fValue;
   521          var lValue: Object;
   522          if fExtended <> nil then begin
   523            //lValue := (fExtended as FieldInfo).GetValue(if fPtr is go.builtin.IReference then go.builtin.IReference(fPtr).Get else fPtr);
   524            lValue := (fExtended as FieldInfo).GetValue(if fPtr is IMemory then IMemory(fPtr).GetValue else fPtr);
   525          end
   526          else
   527            lValue := fValue;
   528          exit lFieldValue.GetValue(lValue);
   529        end
   530        else
   531          exit fValue;
   532      end;
   533  
   534      method &Index(i: Integer): Value;
   535      begin
   536        var lKind := Kind;
   537        if (lKind <> &Array) and (lKind <> go.reflect.Slice) and (lKind <> go.reflect.String) then
   538          raise new Exception("Wrong type, need array, slice or string");
   539  
   540        //var lValue := if fValue is Memory<Object> then Memory<Object>.Get(Memory<Object>(fValue)) else fValue;
   541        var lValue := InternalGetValue;
   542        // TODO need to create and return a reference here???
   543        case lKind of
   544          go.reflect.Slice:
   545            result := new Value(go.builtin.ISlice(lValue).getAtIndex(i), ValueExtendedInfo.Slice, lValue, i);
   546  
   547          go.reflect.Array:
   548            {$IF ISLAND}
   549            result := new Value(System.Array(lValue).Get(i));
   550            {$ELSEIF ECHOES}
   551            result := new Value(System.Array(lValue).GetValue(i));
   552            {$ENDIF}
   553  
   554          go.reflect.String:
   555            result := new Value(go.builtin.string(lValue)[i]);
   556        end;
   557      end;
   558  
   559      method SetMapIndex(key: Value; val: Value);
   560      begin
   561        if Kind ≠ go.reflect.Map then
   562          raise new Exception("Wrong type, need a map");
   563  
   564        go.builtin.IMap(fValue).SetReflectKeyValue(key, val);
   565      end;
   566  
   567      method NumField: Integer;
   568      begin
   569        result := fType.NumField;
   570      end;
   571  
   572      method NumMethod: Integer;
   573      begin
   574        result := fType.NumMethod;
   575      end;
   576  
   577      method Field(i: Integer): Value;
   578      begin
   579        {$IF ISLAND}
   580        var lFields := TypeImpl(fType).fTrueType.Fields.ToArray();
   581        var lValue := InternalGetValue;
   582        result := new Value(lFields[i].GetValue(lValue), new TypeImpl(lFields[i].Type), new Memory<Object>(lValue), lFields[i]);
   583        {$ELSEIF ECHOES}
   584        var lFields := System.Reflection.TypeInfo(TypeImpl(fType).fTrueType).DeclaredFields.ToArray();
   585        var lValue := InternalGetValue;
   586        result := new Value(lFields[i].GetValue(lValue), new TypeImpl(lFields[i].FieldType), new Memory<Object>(lValue), lFields[i]);
   587        {$ENDIF}
   588      end;
   589  
   590      method FieldByName(name: String): tuple of (StructField, Boolean);
   591      begin
   592        var lField: &PlatformField;
   593        {$IF ISLAND}
   594        lField := TypeImpl(fType).fTrueType.Fields.Where(a->a.Name = name).FirstOrDefault;
   595        {$ELSEIF ECHOES}
   596        lField := System.Reflection.TypeInfo(TypeImpl(fType).fTrueType).DeclaredFields.Where(a->a.Name = name).FirstOrDefault;
   597        {$ENDIF}
   598        if lField ≠ nil then
   599          exit(new StructFieldImpl(lField), true)
   600        else
   601          exit(nil, false);
   602      end;
   603  
   604      /*method FieldByName(name: go.builtin.string): Value;
   605      begin
   606        raise new NotImplementedException;
   607      end;*/
   608  
   609      method CanAddr: Boolean;
   610      begin
   611        result := true;
   612        // TODO check when returns false
   613        //raise new NotImplementedException;
   614      end;
   615  
   616      method Call(inn: go.builtin.Slice<Value>): go.builtin.Slice<Value>;
   617      begin
   618        raise new NotImplementedException;
   619      end;
   620  
   621      method Addr: Value;
   622      begin
   623        result := new Memory<Value>(fValue);
   624      end;
   625  
   626      method MapRange() :Memory<MapIter>;
   627      begin
   628        if fType.Kind ≠ Map then
   629          raise new Exception('Wrong type, need a map');
   630  
   631        var lIter := new MapIter(go.builtin.IMap(fValue).GetReflectSequence().GetEnumerator());
   632        result := new Memory<MapIter>(lIter);
   633      end;
   634  
   635      method &Interface: Object;
   636      begin
   637        exit fValue;
   638      end;
   639  
   640      method Elem: Value;
   641      begin
   642        if fValue is IMemory then begin
   643          var lType := TypeImpl(fType).RealType;
   644          var lRealType: PlatformType;
   645          {$IF ISLAND}
   646          lRealType := lType.GenericArguments.FirstOrDefault;
   647          {$ELSEIF ECHOES}
   648          lRealType := lType.GenericTypeArguments[0];
   649          {$ENDIF}
   650          //exit new Value(go.builtin.IReference(fValue).Get, new TypeImpl(lRealType), fValue);
   651          exit new Value(IMemory(fValue).GetValue, new TypeImpl(lRealType), fValue);
   652        end;
   653        raise new NotSupportedException;
   654      end;
   655  
   656      method &Implements(u: &Type): Boolean;
   657      begin
   658        result := fType.Implements(u);
   659      end;
   660  
   661      method &AssignableTo(u: &Type): Boolean;
   662      begin
   663        result := fType.AssignableTo(u);
   664      end;
   665  
   666      method &ConvertibleTo(u: &Type): Boolean;
   667      begin
   668        result := fType.ConvertibleTo(u);
   669      end;
   670  
   671    end;
   672  
   673    &Type = public interface
   674      method Align: Integer;
   675      method FieldAlign: Integer;
   676      method &Method(i: Integer): &Method;
   677      method MethodByName(s: String): tuple of (&Method, Boolean);
   678      method NumMethod: Integer;
   679      method Name: String;
   680      method PkgPath: String;
   681      method Size: UIntPtr;
   682      method String: String;
   683      method Kind: Kind;
   684      method Implements(u: &Type): Boolean;
   685      method AssignableTo(u: &Type): Boolean;
   686      method ConvertibleTo(u: &Type): Boolean;
   687      method Comparable: Boolean;
   688      method Bits: Integer;
   689      method ChanDir: ChanDir;
   690      method IsVariadic: Boolean;
   691      method Elem: &Type;
   692      method Field(i: Integer): StructField;
   693      method FieldByIndex(i: go.builtin.Slice<Int64>): StructField;
   694      method FieldByName(aname: String): tuple of (StructField, Boolean);
   695      method FieldByNameFunc(match: delegate(aName: String): Boolean): tuple of (StructField, Boolean);
   696      method &In(i: Integer): &Type;
   697      method Key: &Type;
   698      method Len: Integer;
   699      method NumField: Integer;
   700      method NumIn: Integer;
   701      method NumOut: Integer;
   702      method &Out(i: Integer): &Type;
   703    end;
   704  
   705    [AliasSemantics]
   706    StructTag = public record
   707    public
   708      Value: String;
   709  
   710      constructor(aValue: String);
   711      begin
   712        Value := aValue;
   713      end;
   714  
   715      method Get(key: String): String;
   716      begin
   717        var lResult := Lookup(key);
   718        result := lResult[0];
   719      end;
   720  
   721      method Lookup(key: String): tuple of (String, Boolean);
   722      begin
   723        var lPos := Value.IndexOf(key + ':');
   724        if lPos ≥ 0 then begin
   725          var lResult := Value.Substring(lPos + key.Length + 1).Trim(['"', '''', ' ']);
   726          exit (lResult, true);
   727        end
   728        else
   729          result := ('', false);
   730      end;
   731    end;
   732  
   733    StructField = public interface
   734      method IsValid: Boolean;
   735      method &Interface: Object;
   736      property Name: String read;
   737      property PkgPath: String read;
   738      property &Type: &Type read;
   739      property Tag: StructTag read;
   740      property Offset: UIntPtr read;
   741      property &Index: go.builtin.Slice<Int64> read;
   742      property Anonymous: Boolean read;
   743    end;
   744  
   745    PlatformField = public {$IFDEF ECHOES}System.Reflection.FieldInfo{$ELSE}RemObjects.Elements.System.FieldInfo{$ENDIF};
   746    StructFieldImpl = class(StructField)
   747    private
   748      fField: PlatformField;
   749    public
   750      constructor(aField: PlatformField);
   751      begin
   752        fField := aField;
   753        PkgPath := '';
   754        var lTag := '';
   755        if aField ≠ nil then begin
   756          {$IF ISLAND}
   757          var lAttrs := aField.Attributes.Where(b->b.Type = TypeOf(go.builtin.TagAttribute)).ToList;
   758          if (lAttrs ≠ nil) and (lAttrs.Count > 0) then
   759            lTag := go.builtin.PlatformString(lAttrs[0].Arguments[0].Value);
   760          {$ELSEIF ECHOES}
   761          var lAttrs := aField.GetCustomAttributes(true);
   762          if lAttrs.Length > 0 then begin
   763            if lAttrs[0] is go.builtin.TagAttribute then
   764              lTag := (lAttrs[0] as go.builtin.TagAttribute).Tag;
   765          end;
   766          {$ENDIF}
   767        end;
   768        Tag := new StructTag(lTag);
   769        &Index := new go.builtin.Slice<Int64>(1);
   770      end;
   771  
   772      constructor(aField: PlatformField; aIndex: Integer);
   773      begin
   774        constructor(aField);
   775        &Index[0] := aIndex;
   776      end;
   777  
   778      method IsValid: Boolean;
   779      begin
   780        result := (fField ≠ nil) and (fField.Name ≠ '');
   781      end;
   782  
   783      method &Interface: Object;
   784      begin
   785        result := self;
   786      end;
   787  
   788      property Name: String read fField.Name;
   789      property PkgPath: String read;
   790      property &Type: &Type read {$IF ISLAND}new TypeImpl(fField.&Type){$ELSEIF ECHOES}new TypeImpl(fField.FieldType){$ENDIF};
   791      property Tag: StructTag read;
   792      property Offset: UIntPtr read;
   793      property &Index: go.builtin.Slice<Int64> read;
   794      property Anonymous: Boolean read;
   795    end;
   796  
   797    &Method = public interface
   798      property Name: String read;
   799      property PkgPath: String read;
   800      property &Type: &Type read;
   801      property Func: Value read;
   802      property &Index: Integer read;
   803    end;
   804  
   805    PlatformMethod = public {$IFDEF ECHOES}System.Reflection.MethodInfo{$ELSE}RemObjects.Elements.System.MethodInfo{$ENDIF};
   806    MethodImpl = class(&Method)
   807    private
   808      fMethod: PlatformMethod;
   809  
   810      method get_Index: Integer;
   811      begin
   812        {$IF ISLAND}
   813        result := fMethod.DeclaringType.Methods.ToList.IndexOf(fMethod);
   814        {$ELSEIF ECHOES}
   815        result := System.Array.IndexOf(fMethod.DeclaringType.GetMethods, fMethod);
   816        {$ENDIF}
   817      end;
   818  
   819    public
   820      constructor(aMethod: PlatformMethod);
   821      begin
   822        fMethod := aMethod;
   823      end;
   824  
   825      property Name: String read fMethod.Name;
   826      property PkgPath: String read;
   827      property &Type: &Type read {$IF ISLAND}new TypeImpl(fMethod.&Type){$ELSEIF ECHOES}new TypeImpl(fMethod.ReturnType){$ENDIF};
   828      property Func: Value read;
   829      property &Index: Integer read get_Index;
   830    end;
   831  
   832    PlatformType = public {$IFDEF ECHOES}System.Type{$ELSE}RemObjects.Elements.System.Type{$ENDIF};
   833    TypeImpl = public class(&Type)
   834    private
   835    assembly
   836      fRealType: PlatformType;
   837      fTrueType: PlatformType; // used for ValueType
   838  
   839      method IsInteger: Boolean;
   840      begin
   841        var lKind := self.Kind;
   842        result := (Integer(lKind) ≥ Integer(go.reflect.Bool)) and (Integer(lKind) ≤ Integer(go.reflect.Uint64));
   843      end;
   844  
   845      method IsFloatOrComplex: Boolean;
   846      begin
   847        var lKind := self.Kind;
   848        result := (Integer(lKind) ≥ Integer(go.reflect.Float32)) and (Integer(lKind) ≤ Integer(go.reflect.Complex128));
   849      end;
   850  
   851    public
   852  
   853      property RealType: PlatformType read fRealType;
   854  
   855      constructor(aType: PlatformType);
   856      begin
   857        fRealType := aType;
   858        {$IF ISLAND}
   859        var lCtors := fRealType.Methods.Where(a -> (MethodFlags.Constructor in a.Flags)).ToArray;
   860        var lFields := fRealType.Fields.ToArray;
   861        if fRealType.IsValueType and (lCtors.Count = 2) and (lFields.Count = 1) then
   862          fTrueType := lFields[0].Type
   863        else
   864          fTrueType := fRealType;
   865        {$ELSEIF ECHOES}
   866        if fRealType.IsValueType and (fRealType.GetConstructors().Count = 2) and (fRealType.GetFields().Count = 1) then
   867          fTrueType := fRealType.GetFields()[0].FieldType
   868        else
   869          fTrueType := fRealType;
   870        {$ENDIF}
   871      end;
   872  
   873      method Align: Integer;
   874      begin
   875        raise new NotImplementedException;
   876      end;
   877  
   878      method FieldAlign: Integer;
   879      begin
   880        raise new NotImplementedException;
   881      end;
   882  
   883      method &Method(i: Integer): &Method;
   884      begin
   885        if (i < 0) or (i ≥ NumMethod) then
   886          raise new Exception('Wrong method index');
   887  
   888        {$IF ISLAND}
   889        result := new MethodImpl(fTrueType.Methods.ToList[i]);
   890        {$ELSEIF ECHOES}
   891        result := new MethodImpl(fTrueType.GetMethods[i]);
   892        {$ENDIF}
   893      end;
   894  
   895      method MethodByName(s: String): tuple of (&Method, Boolean);
   896      begin
   897        var lMethod: &PlatformMethod;
   898        {$IF ISLAND}
   899        lMethod := fTrueType.Methods.Where(a->a.Name = s).FirstOrDefault;
   900        {$ELSEIF ECHOES}
   901        lMethod := System.Array.Find(fTrueType.GetMethods, a->a.Name = s);
   902        {$ENDIF}
   903        exit(new MethodImpl(lMethod), lMethod ≠ nil);
   904      end;
   905  
   906      method NumMethod: Integer;
   907      begin
   908        {$IF ISLAND}
   909        if Kind = go.reflect.Interface then
   910          exit 0
   911        else
   912          result := fTrueType.Methods.Count;
   913        {$ELSEIF ECHOES}
   914        // TODO!! do this in a better way
   915        if Kind = go.reflect.Interface then
   916          exit 0
   917        else
   918          result := fTrueType.GetMethods.Length;
   919        {$ENDIF}
   920      end;
   921  
   922      method Name: String;
   923      begin
   924        result := fRealType.Name;
   925      end;
   926  
   927      method PkgPath: String;
   928      begin
   929        raise new NotImplementedException;
   930      end;
   931  
   932      method Size: UIntPtr;
   933      begin
   934        {$IF ISLAND}
   935        result := fRealType.SizeOfType;
   936        {$ELSEIF ECHOES}
   937        result := System.Runtime.InteropServices.Marshal.SizeOf(fRealType);
   938        {$ENDIF}
   939      end;
   940  
   941      method String: String;
   942      begin
   943        result := fRealType.Name;
   944      end;
   945  
   946      method Kind: Kind;
   947      begin
   948        if fTrueType = nil then
   949          exit go.reflect.Invalid;
   950        {$IF ISLAND}
   951        if (fTrueType = TypeOf(go.builtin.string)) and not ((fTrueType.Flags and IslandTypeFlags.TypeKindMask) = IslandTypeFlags.Array) then
   952          exit go.reflect.String;
   953  
   954        if fTrueType.IsDelegate then
   955          exit go.reflect.Func;
   956  
   957        if fTrueType.Name.StartsWith('gt2_@') then begin
   958        //if (fTrueType.Flags and IslandTypeFlags.TypeKindMask) = IslandTypeFlags.Array  then begin
   959          exit go.reflect.Array;
   960        end;
   961  
   962        if (fTrueType.GenericArguments <> nil) and (fTrueType.GenericArguments.Count > 0) then begin
   963          if fTrueType.Name.Contains('go.builtin.Slice') then
   964            exit go.reflect.Slice;
   965  
   966          if fTrueType.Name.Contains('go.builtin.BidirectionalChannel') then
   967            exit go.reflect.Map
   968          else
   969            exit go.reflect.Ptr;
   970        end;
   971  
   972        if fTrueType = TypeOf(Object) then
   973          exit go.reflect.Interface;
   974  
   975        case fTrueType.Code of
   976          TypeCodes.Boolean: result := go.reflect.Bool;
   977          TypeCodes.Char: result := go.reflect.Uint16;
   978          TypeCodes.SByte: result := go.reflect.Int8;
   979          TypeCodes.Byte: result := go.reflect.UInt8;
   980          TypeCodes.Int16: result := go.reflect.Int16;
   981          TypeCodes.UInt16: result := go.reflect.Uint16;
   982          TypeCodes.Int32: result := go.reflect.Int32;
   983          TypeCodes.UInt32: result := go.reflect.Uint32;
   984          TypeCodes.Int64: result := go.reflect.Int64;
   985          TypeCodes.UInt64: result := go.reflect.Uint64;
   986          TypeCodes.Single: result := go.reflect.Float32;
   987          TypeCodes.Double: result := go.reflect.Float64;
   988          TypeCodes.UIntPtr: result := go.reflect.UintPtr;
   989          TypeCodes.IntPtr: result := go.reflect.Ptr;
   990          TypeCodes.String: result := go.reflect.String;
   991          TypeCodes.Object: result := go.reflect.Struct;
   992          TypeCodes.None: begin
   993            case (fTrueType.Flags and IslandTypeFlags.TypeKindMask) of
   994              IslandTypeFlags.Array: exit go.reflect.Array;
   995              IslandTypeFlags.Struct: exit go.reflect.Struct;
   996              IslandTypeFlags.Interface: exit go.reflect.Interface;
   997              IslandTypeFlags.Generic: exit go.reflect.Ptr;
   998              default: exit go.reflect.Struct;
   999            end;
  1000          end;
  1001        end;
  1002        {$ELSEIF ECHOES}
  1003        if (fTrueType = TypeOf(go.builtin.string)) and (not fTrueType.IsArray) then
  1004          exit go.reflect.String;
  1005  
  1006        if fTrueType.IsArray then
  1007          exit go.reflect.Array;
  1008  
  1009        if fTrueType.IsInterface then
  1010          exit go.reflect.Interface;
  1011  
  1012        if fTrueType.BaseType = TypeOf(System.MulticastDelegate) then
  1013          exit go.reflect.Func;
  1014  
  1015        if fTrueType.GenericTypeArguments.Length > 0 then
  1016          if fTrueType.AssemblyQualifiedName.StartsWith('go.builtin.Slice') then
  1017            exit go.reflect.Slice
  1018          else
  1019            if fTrueType.AssemblyQualifiedName.StartsWith('go.builtin.BidirectionalChannel') then
  1020              exit go.reflect.Map
  1021            else
  1022              exit go.reflect.Ptr;
  1023  
  1024        if fTrueType = TypeOf(System.Object) then
  1025          exit go.reflect.Interface;
  1026  
  1027        if fTrueType = TypeOf(System.UIntPtr) then
  1028          exit go.reflect.Uintptr;
  1029  
  1030        case System.Type.GetTypeCode(fTrueType) of
  1031          TypeCode.Boolean: result := go.reflect.Bool;
  1032          TypeCode.Byte: result := go.reflect.UInt8;
  1033          TypeCode.SByte: result := go.reflect.Int8;
  1034          TypeCode.Int16: result := go.reflect.Int16;
  1035          TypeCode.UInt16: result := go.reflect.Uint16;
  1036          TypeCode.Int32: result := go.reflect.Int32;
  1037          TypeCode.UInt32: result := go.reflect.Uint32;
  1038          TypeCode.Int64: result := go.reflect.Int64;
  1039          TypeCode.UInt64: result := go.reflect.Uint64;
  1040          TypeCode.Single: result := go.reflect.Float32;
  1041          TypeCode.Double: result := go.reflect.Float64;
  1042          TypeCode.String: result := go.reflect.String;
  1043          TypeCode.Char: result := go.reflect.Uint16;
  1044          TypeCode.Object:
  1045          begin
  1046            // check ValueType
  1047            var lTrueType: PlatformType;
  1048            if fRealType.IsValueType {and (fRealType.GetConstructors().Count = 2)} and (fRealType.GetFields().Count = 1) then
  1049              lTrueType := fRealType.GetFields()[0].FieldType
  1050            else
  1051              exit go.reflect.Struct;
  1052            exit new &TypeImpl(lTrueType).Kind;
  1053          end;
  1054          TypeCode.Empty: result := go.reflect.Invalid;
  1055        end;
  1056        {$ENDIF}
  1057      end;
  1058  
  1059      method Implements(u: &Type): Boolean;
  1060      begin
  1061        result := fRealType.IsSubclassOf(TypeImpl(u).fRealType);
  1062      end;
  1063  
  1064      method AssignableTo(u: &Type): Boolean;
  1065      begin
  1066        if u = nil then
  1067          raise new Exception('nil type in AssignableTo');
  1068  
  1069        if Kind = go.reflect.interface then
  1070          result := true
  1071        else
  1072          result := TypeImpl(u).fRealType.IsAssignableFrom(self.fRealType);
  1073      end;
  1074  
  1075      method ConvertibleTo(u: &Type): Boolean;
  1076      begin
  1077        if u = nil then
  1078          raise new Exception('nil type in ConvertibleTo');
  1079  
  1080        if AssignableTo(u) then
  1081          exit(true);
  1082  
  1083        // TODO Check underlying type
  1084  
  1085        if (IsInteger or IsFloatOrComplex) and (TypeImpl(u).IsInteger or TypeImpl(u).IsFloatOrComplex) then
  1086          exit(true);
  1087  
  1088        var lUKind := u.Kind;
  1089        if (IsInteger or (Kind = go.reflect.String) or (Kind = go.reflect.Slice)) and (lUKind = go.reflect.String) then
  1090          exit(true);
  1091  
  1092        if (Kind = go.reflect.String) and (TypeImpl(u).IsInteger or (lUKind = go.reflect.String) or (lUKind = go.reflect.Slice)) then
  1093          exit(true);
  1094  
  1095        exit(false);
  1096      end;
  1097  
  1098      method Comparable: Boolean;
  1099      begin
  1100        result := Kind <> go.reflect.Func;
  1101      end;
  1102  
  1103      method Bits: Integer;
  1104      begin
  1105        if IsInteger or IsFloatOrComplex then
  1106          result := sizeOf(fTrueType) * 8 // size in bits...
  1107        else
  1108          raise new Exception("Wrong type calling Bits method");
  1109      end;
  1110  
  1111      method ChanDir: ChanDir;
  1112      begin
  1113        raise new NotImplementedException;
  1114      end;
  1115  
  1116      method IsVariadic: Boolean;
  1117      begin
  1118        raise new NotImplementedException;
  1119      end;
  1120  
  1121      method Elem: &Type;
  1122      begin
  1123        //if IsMemoryType(fTrueType) then begin
  1124        var lKind := Kind;
  1125        if (lKind = go.reflect.ptr) or (lKind = go.reflect.slice) or (lKind = go.reflect.chan) then begin
  1126          var lRealType: PlatformType;
  1127          {$IF ISLAND}
  1128          lRealType := fTrueType.GenericArguments.FirstOrDefault;
  1129          {$ELSEIF ECHOES}
  1130          lRealType := fTrueType.GenericTypeArguments[0];
  1131          {$ENDIF}
  1132          exit new TypeImpl(lRealType);
  1133        end;
  1134      end;
  1135  
  1136      method Field(i: Integer): StructField;
  1137      begin
  1138        if Kind ≠ go.reflect.Struct then
  1139          raise new Exception('Wrong type, it needs to be struct');
  1140        {$IF ISLAND}
  1141        var lFields := fTrueType.Fields.ToList();
  1142        if i ≥ lFields.Count then
  1143          raise new IndexOutOfRangeException('Index out of range');
  1144        {$ELSEIF ECHOES}
  1145        var lFields := System.Reflection.TypeInfo(fTrueType).DeclaredFields.ToArray();
  1146        if i ≥ lFields.Length then
  1147          raise new IndexOutOfRangeException('Index out of range');
  1148        {$ENDIF}
  1149        result := new StructFieldImpl(lFields[i], i);
  1150      end;
  1151  
  1152      method FieldByIndex(i: go.builtin.Slice<Int64>): StructField;
  1153      begin
  1154        if Kind ≠ go.reflect.Struct then
  1155          raise new Exception('Wrong type, it needs to be struct');
  1156  
  1157        var lType := self;
  1158        for lIndex: Integer := 0 to i.Length - 1 do begin
  1159          result := lType.Field(i[lIndex]);
  1160          lType := TypeImpl(result.Type);
  1161        end;
  1162      end;
  1163  
  1164      method FieldByName(aname: String): tuple of (StructField, Boolean);
  1165      begin
  1166        var lField: &PlatformField;
  1167        {$IF ISLAND}
  1168        lField := fTrueType.Fields.Where(a->a.Name = aname).FirstOrDefault;
  1169        {$ELSEIF ECHOES}
  1170        lField := System.Reflection.TypeInfo(fTrueType).DeclaredFields.Where(a->a.Name = aname).FirstOrDefault;
  1171        {$ENDIF}
  1172        // TODO field index
  1173        exit(new StructFieldImpl(lField), lField ≠ nil);
  1174      end;
  1175  
  1176      method FieldByNameFunc(match: delegate(aName: String): Boolean): tuple of (StructField, Boolean);
  1177      begin
  1178        var lField: &PlatformField;
  1179        {$IF ISLAND}
  1180        lField := fTrueType.Fields.Where(a -> match(a.Name)).FirstOrDefault;
  1181        {$ELSEIF ECHOES}
  1182        lField := TypeInfo(fTrueType).DeclaredFields.Where((a) -> match(a.Name)).FirstOrDefault;
  1183        {$ENDIF}
  1184        // TODO field index
  1185        exit(new StructFieldImpl(lField), lField ≠ nil);
  1186      end;
  1187  
  1188      method &In(i: Integer): &Type;
  1189      begin
  1190        if Kind ≠ go.reflect.Func then
  1191          raise new Exception('Wrong type, it needs to be Func');
  1192        {$IF ISLAND}
  1193        var lMethod := fRealType.Methods.Where(a -> a.Name = 'Invoke').FirstOrDefault;
  1194        var lParameters := lMethod.Arguments.ToList();
  1195        if lParameters.Count ≤ i then
  1196          raise new IndexOutOfRangeException('Index out of range');
  1197        result := new TypeImpl(lParameters[i].Type);
  1198        {$ELSEIF ECHOES}
  1199        var lMethod := fRealType.GetMethod('Invoke');
  1200        var lParameters := lMethod.GetParameters();
  1201        if lParameters.Length ≤ i then
  1202          raise new IndexOutOfRangeException('Index out of range');
  1203        result := new TypeImpl(lParameters[i].ParameterType);
  1204        {$ENDIF}
  1205      end;
  1206  
  1207      method Key: &Type;
  1208      begin
  1209        raise new NotImplementedException;
  1210      end;
  1211  
  1212      method Len: Integer;
  1213      begin
  1214        raise new NotImplementedException;
  1215      end;
  1216  
  1217      method NumField: Integer;
  1218      begin
  1219        if Kind ≠ go.reflect.Struct then
  1220          raise new Exception('Wrong type, it needs to be struct');
  1221        {$IF ISLAND}
  1222        result := fTrueType.Fields.Count;
  1223        {$ELSEIF ECHOES}
  1224        result := System.Reflection.TypeInfo(fTrueType).DeclaredFields.ToArray().Length;
  1225        {$ENDIF}
  1226      end;
  1227  
  1228      method NumIn: Integer;
  1229      begin
  1230        if Kind ≠ go.reflect.Func then
  1231          raise new Exception('Wrong type, it needs to be Func');
  1232        {$IF ISLAND}
  1233        var lMethod := fTrueType.Methods.Where(a -> a.Name = 'Invoke').FirstOrDefault;
  1234        result := lMethod.Arguments.Count;
  1235        {$ELSEIF ECHOES}
  1236        var lMethod := fTrueType.GetMethod('Invoke');
  1237        result := lMethod.GetParameters().Length;
  1238        {$ENDIF}
  1239      end;
  1240  
  1241      method NumOut: Integer;
  1242      begin
  1243        if Kind ≠ go.reflect.Func then
  1244          raise new Exception('Wrong type, it needs to be Func');
  1245        {$IF ISLAND}
  1246        var lMethod := fTrueType.Methods.Where(a -> a.Name = 'Invoke').FirstOrDefault;
  1247        if lMethod.Type.GenericArguments.Count > 0 then
  1248          result := lMethod.Type.Fields.Count
  1249        else
  1250          result := 1;
  1251        {$ELSEIF ECHOES}
  1252        var lMethod := fTrueType.GetMethod('Invoke');
  1253        if System.Reflection.TypeInfo(lMethod.ReturnType).IsGenericType and (System.Reflection.TypeInfo(lMethod.ReturnType).FullName.StartsWith('System.Tuple')) then
  1254          result := System.Reflection.TypeInfo(lMethod.ReturnType).DeclaredFields.ToArray().Length
  1255        else
  1256          result := 1;
  1257        {$ENDIF}
  1258      end;
  1259  
  1260      method &Out(i: Integer): &Type;
  1261      begin
  1262        if Kind ≠ go.reflect.Func then
  1263          raise new Exception('Wrong type, it needs to be Func');
  1264        {$IF ISLAND}
  1265        var lMethod := fTrueType.Methods.Where(a -> a.Name = 'Invoke').FirstOrDefault;
  1266        var lParameters := lMethod.Type.Fields.ToList();
  1267        if lParameters.Count ≤ i then
  1268          raise new IndexOutOfRangeException('Index out of range');
  1269        result := new TypeImpl(lParameters[i].Type);
  1270        {$ELSEIF ECHOES}
  1271        var lMethod := fTrueType.GetMethod('Invoke');
  1272        var lTotal := 1;
  1273        if System.Reflection.TypeInfo(lMethod.ReturnType).IsGenericType and (System.Reflection.TypeInfo(lMethod.ReturnType).FullName.StartsWith('System.Tuple')) then
  1274          lTotal := System.Reflection.TypeInfo(lMethod.ReturnType).DeclaredFields.ToArray().Length;
  1275  
  1276        if lTotal < i then
  1277          raise new IndexOutOfRangeException('Index out of range');
  1278  
  1279        if lTotal > 1 then
  1280          result := new TypeImpl(System.Reflection.TypeInfo(lMethod.ReturnType).DeclaredFields.ToArray()[i].FieldType)
  1281        else
  1282          result := new TypeImpl(System.Reflection.TypeInfo(lMethod.ReturnType));
  1283        {$ENDIF}
  1284      end;
  1285    end;
  1286  
  1287    method DeepEqual(a, b: Object): Boolean; public;
  1288    begin
  1289      var lAType := TypeOf(a);
  1290      var lBType := TypeOf(b);
  1291  
  1292      if (lAType = lBType) and (lAType.Kind = go.reflect.Struct) then begin
  1293        {$IF ISLAND}
  1294        var lAFields := TypeImpl(lAType).fTrueType.Fields.ToArray();
  1295        var lBFields := TypeImpl(lBType).fTrueType.Fields.ToArray();
  1296        {$ELSEIF ECHOES}
  1297        var lAFields := System.Reflection.TypeInfo(TypeImpl(lAType).fTrueType).DeclaredFields.ToArray();
  1298        var lBFields := System.Reflection.TypeInfo(TypeImpl(lBType).fTrueType).DeclaredFields.ToArray();
  1299        {$ENDIF}
  1300        if lAFields.Length ≠ lBFields.Length then
  1301          exit false;
  1302        for i: Integer := 0 to lAFields.Length - 1 do begin
  1303          var lAValue := lAFields[i].GetValue(a);
  1304          var lBValue := lBFields[i].GetValue(b);
  1305          result := DeepEqual(lAValue, lBValue);
  1306          if not result then
  1307            exit;
  1308        end;
  1309      end
  1310      else begin
  1311        var lRealAType := TypeImpl(lAType).fTrueType;
  1312        {$IF ISLAND}
  1313        var lMethod := lRealAType.Methods.Where(m -> m.Name = 'op_Equality').FirstOrDefault;
  1314        if lMethod ≠ nil then begin
  1315          //result := lMethod.Invoke(lRealAType, [a, b]) as Boolean
  1316          var lCaller := TEqualsMethod(lMethod.Pointer);
  1317          result := lCaller(a, b);
  1318        end
  1319        else
  1320          result := a.Equals(b);
  1321        {$ELSEIF ECHOES}
  1322        var lMethod := System.Array.Find(lRealAType.GetMethods, m -> m.Name = 'op_Equality');
  1323        if lMethod ≠ nil then
  1324          result := lMethod.Invoke(lRealAType, [a, b]) as Boolean
  1325        else
  1326          result := Object.Equals(a, b);
  1327        {$ENDIF}
  1328      end;
  1329    end;
  1330  
  1331    Method &New(aType: &Type): Value;public;
  1332    begin
  1333      exit new Value(new Memory<Object>(Zero(aType)), aType);
  1334    end;
  1335  
  1336    method Zero(aType: &Type): Value;public;
  1337    begin
  1338      {$IFDEF ISLAND}
  1339      if aType.Kind = go.reflect.string then
  1340        exit new Value(go.builtin.string.Zero);
  1341  
  1342      var lZero := TypeImpl(aType).fRealType.Properties.Where(a->a.Name = 'Zero').FirstOrDefault;
  1343      if lZero <> nil then
  1344        exit new Value(ZeroFunction(lZero.Read.Pointer)());
  1345  
  1346      if not TypeImpl(aType).RealType.IsValueType and (TypeImpl(aType).RealType.Methods.Any(a -> a.Name = '__Set')) then
  1347        exit new Value(TypeImpl(aType).RealType.Instantiate())
  1348      else begin
  1349        if TypeImpl(aType).fRealType.IsValueType then
  1350          exit new Value(InternalCalls.Cast<Object>(DefaultGC.New(TypeImpl(aType).RealType.RTTI, sizeOf(^Void) + TypeImpl(aType).RealType.SizeOfType)))
  1351        else
  1352          exit new Value(nil);
  1353      end;
  1354      {$ELSE}
  1355      if TypeImpl(aType).fRealType = System.Type.GetType('go.builtin.string') then
  1356        exit new Value(go.builtin.string.Zero) // String .net does not have a constructor with no arguments.
  1357      else begin
  1358        var lZero := TypeImpl(aType).fRealType.GetProperty('Zero');
  1359        if lZero <> nil then
  1360          exit new Value(lZero.GetValue(nil));
  1361      end;
  1362  
  1363      if not TypeImpl(aType).RealType.IsValueType and (TypeImpl(aType).RealType.GetMethods.Any(a -> a.Name = '__Set')) then
  1364        exit new Value(Activator.CreateInstance(TypeImpl(aType).RealType))
  1365      else
  1366        if TypeImpl(aType).fRealType.IsValueType then
  1367          exit new Value(Activator.CreateInstance(TypeImpl(aType).fRealType))
  1368        else
  1369          exit new Value(nil);
  1370      {$ENDIF}
  1371    end;
  1372  
  1373    method PtrTo(t: &Type): Memory<&Type>; public;
  1374    begin
  1375      result := new Memory<&Type>(t);
  1376    end;
  1377  
  1378    method ValueOf(i: Object): Value;public;
  1379    begin
  1380      exit new Value(i);
  1381    end;
  1382  
  1383    method Indirect(v: Value): Value;public;
  1384    begin
  1385      exit new Value(new Memory<Object>(v.fValue), v.fType);
  1386    end;
  1387  
  1388    method TypeOf(v: Object): &Type;public;
  1389    begin
  1390      result := new TypeImpl(v.GetType());
  1391    end;
  1392  
  1393    method Swapper(aslice: Object): delegate(arg0: go.builtin.int; arg1: go.builtin.int); public;
  1394    begin
  1395      exit @go.sort.Interface(aslice).Swap;
  1396    end;
  1397  
  1398    method MakeMap(t: &Type): Value; public;
  1399    begin
  1400      raise new NotImplementedException;
  1401    end;
  1402  
  1403    method MakeMapWithSize(t: &Type;n: Integer): Value; public;
  1404    begin
  1405      raise new NotImplementedException;
  1406    end;
  1407  
  1408    method InternalGetValue(aVal: Value): Object;
  1409    begin
  1410      var lType := TypeImpl(aVal.fType).RealType;
  1411      if lType.IsValueType then begin
  1412        {$IF ECHOES}
  1413        var lFieldValue := lType.GetField('Value');
  1414        {$ELSE}
  1415        var lFieldValue := lType.Fields.FirstOrDefault(a -> a.Name = 'Value');
  1416        {$ENDIF}
  1417        var lValue: Object;
  1418        if aVal.fExtended <> nil then begin
  1419          //lValue := (aVal.fExtended as FieldInfo).GetValue(if aVal.fPtr is go.builtin.IReference then go.builtin.IReference(aVal.fPtr).Get else aVal.fPtr);
  1420          lValue := (aVal.fExtended as FieldInfo).GetValue(if aVal.fPtr is IMemory then IMemory(aVal.fPtr).GetValue else aVal.fPtr);
  1421        end
  1422        else
  1423          lValue := aVal.fValue;
  1424        exit lFieldValue.GetValue(lValue);
  1425      end
  1426      else
  1427        exit aVal.fValue;
  1428    end;
  1429  
  1430    method Copy(dst: Value; src: Value): Integer;
  1431    begin
  1432      var lDstKind := dst.Kind;
  1433      var lSrcKind := src.Kind;
  1434      if not (((lDstKind = go.reflect.Slice) and (lSrcKind = go.reflect.Slice)) or ((lDstKind = go.reflect.Array) and (lSrcKind = go.reflect.Array))
  1435        or ((lDstKind = go.reflect.Slice) and (lSrcKind = go.reflect.String))) then
  1436          raise new Exception("Wrong type on reflect.copy method");
  1437  
  1438      var lDst: go.builtin.ISlice;
  1439      if dst.fExtendedInfo = ValueExtendedInfo.Slice then begin
  1440        var lTmp := go.builtin.ISlice(dst.fPtr).getAtIndex(Integer(dst.fExtendedObject));
  1441        lDst := go.builtin.ISlice(InternalGetValue(new Value(lTmp)));
  1442      end
  1443      else
  1444        lDst := go.builtin.ISlice(InternalGetValue(dst));
  1445      var lSrc := go.builtin.ISlice(InternalGetValue(src));
  1446  
  1447      result := Math.Min(if lSrc = nil then 0 else lSrc.getLen, if lDst = nil then 0 else lDst.getLen);
  1448      for i: Integer := 0 to result -1 do
  1449        lDst.setAtIndex(i, lSrc.getAtIndex(i));
  1450    end;
  1451  
  1452    method InstantiateSlice(aType: PlatformType; aCount: Integer): Object; private;
  1453    begin
  1454      {$IF ISLAND}
  1455      if aType.IsValueType then begin
  1456        var lCtors := aType.Methods.Where(a -> (MethodFlags.Constructor in a.Flags)).ToArray;
  1457        var lFields := aType.Fields.ToArray;
  1458        if (lCtors.Count = 2) and (lFields.Count = 1) then begin
  1459          //var lCtorType: MethodInfo := aType.Methods.where(a -> (MethodFlags.Constructor in a.Flags) and (a.Arguments.Count = 1) and (a.Arguments.ToArray[0].Type = lFields[0].Type)).FirstOrDefault;
  1460          var lNewType := DefaultGC.New(aType.RTTI, aType.SizeOfType);
  1461          result := InternalCalls.Cast<Object>(lNewType);
  1462          ^SliceAlias(lNewType)^.aVal := InstantiateSlice(lFields[0].Type, aCount);
  1463          exit;
  1464        end;
  1465      end;
  1466  
  1467      var lCtor: MethodInfo := aType.Methods.FirstOrDefault(a -> (MethodFlags.Constructor in a.Flags) and (a.Arguments.Count = 1) and (a.Arguments.ToArray[0].Type.IsInteger));
  1468      var lRealCtor := SliceCtor(lCtor.Pointer);
  1469      var lNew := DefaultGC.New(aType.RTTI, aType.SizeOfType);
  1470      result := InternalCalls.Cast<Object>(lNew);
  1471      lRealCtor(result, aCount);
  1472      {$ELSEIF ECHOES}
  1473      if aType.IsValueType and (aType.GetConstructors().Count = 2) and (aType.GetFields().Count = 1) then begin
  1474        exit Activator.CreateInstance(aType, [InstantiateSlice(aType.GetFields()[0].FieldType, aCount)]);
  1475      end;
  1476      exit Activator.CreateInstance(aType, [aCount]);
  1477      {$ENDIF}
  1478    end;
  1479  
  1480    method MakeSlice(t: &Type; len, cap: Integer): Value;
  1481    begin
  1482      result := new Value(InstantiateSlice(TypeImpl(t).fRealType, cap), new TypeImpl(TypeImpl(t).fRealType));
  1483    end;
  1484  
  1485    method MakeFunc(typ: &Type; fn: delegate(args: go.builtin.Slice<Value>): go.builtin.Slice<Value>): Value;
  1486    begin
  1487      raise new NotImplementedException;
  1488    end;
  1489  
  1490    operator Equal(a, b: &Type): Boolean; public;
  1491    begin
  1492      if (a is TypeImpl) and (b is TypeImpl) then exit (TypeImpl(a).fRealType = TypeImpl(b).fRealType);
  1493      exit Object.ReferenceEquals(a, b);
  1494    end;
  1495  
  1496    operator NotEqual(a, b: &Type): Boolean;public;
  1497    begin
  1498      exit not (a = b);
  1499    end;
  1500  
  1501    operator Equal(a: &Type; b: TypeImpl): Boolean;public;
  1502    begin
  1503      if not assigned(a) and not assigned(b) then
  1504        exit true;
  1505  
  1506      if not assigned(a) or not assigned(b) then
  1507        exit false;
  1508  
  1509      if (a is TypeImpl) then exit (TypeImpl(a).fRealType = b.fRealType);
  1510      exit Object.ReferenceEquals(a, b);
  1511    end;
  1512  
  1513    operator Equal(a: TypeImpl; b: &Type): Boolean;public;
  1514    begin
  1515      if (b is TypeImpl) then exit (a.fRealType = TypeImpl(b).fRealType);
  1516      exit Object.ReferenceEquals(a, b);
  1517    end;
  1518  
  1519    operator Equal(a: TypeImpl; b: TypeImpl): Boolean;public;
  1520    begin
  1521      exit (a.fRealType = b.fRealType);
  1522      exit Object.ReferenceEquals(a, b);
  1523    end;
  1524  
  1525    method Append(s: Value; params x: array of Value): Value;
  1526    begin
  1527      var lSlice := go.builtin.ISlice(s.fValue);
  1528      var lNewValue: go.reflect.Value;
  1529      for each lValue in x do
  1530        lNewValue := lSlice.AppendObject(lValue.fValue);
  1531  
  1532      exit lNewValue;
  1533    end;
  1534  
  1535    method Append(s: Value; x: go.builtin.Slice<Value>): Value;
  1536    begin
  1537      raise new NotImplementedException;
  1538    end;
  1539  
  1540    method AppendSlice(s: Value; x: Value): Value;
  1541    begin
  1542      raise new NotImplementedException;
  1543    end;
  1544  
  1545    method SliceOf(t: &Type): &Type;
  1546    begin
  1547      raise new NotImplementedException;
  1548    end;
  1549  
  1550    method NewAt(t: &Type; p: go.unsafe.Pointer): Value;
  1551    begin
  1552      raise new NotImplementedException;
  1553    end;
  1554  
  1555    method IsMemoryType(aType: PlatformType): Boolean; assembly;
  1556    begin
  1557      {$IF ECHOES}
  1558      if aType.FullName.StartsWith('RemObjects.Elements.System.Memory') then
  1559        exit true;
  1560      {$ELSE}
  1561      if aType.Name.StartsWith('RemObjects.Elements.System.Memory`1') then
  1562        exit true;
  1563      {$ENDIF}
  1564      result := false;
  1565    end;
  1566  
  1567  end.