Ver Fonte

* Javascript side of webassembly object inspector

Michaël Van Canneyt há 1 ano atrás
pai
commit
c5da62bd2e

+ 665 - 0
packages/wasm-oi/src/debug.objectinspector.html.pas

@@ -0,0 +1,665 @@
+{
+    This file is part of the Pas2JS run time library.
+    Copyright (c) 2024 by the Pas2JS development team.
+
+    API to implement an object inspector in HTML
+    
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit debug.objectinspector.html;
+
+{$mode ObjFPC}
+
+interface
+
+uses
+  typinfo, Classes, SysUtils, Web, rtti;
+
+Type
+  EHTMLTreeBuilder = class(Exception);
+
+  { THTMLTreeBuilder }
+  TObjectSelectedEvent = procedure(Sender : TObject; aObjectId : Integer) of object;
+
+  THTMLTreeBuilder = class(TObject)
+  private
+    FOnObjectSelect: TObjectSelectedEvent;
+    FParentElement: TJSHTMLElement;
+    FRootElement : TJSHTMLElement;
+    FStartCollapsed: Boolean;
+    procedure HandleItemCollapse(Event: TJSEvent);
+    procedure HandleItemSelect(Event: TJSEvent);
+    procedure SetParentElement(AValue: TJSHTMLElement);
+  Public
+    Function AddItem(aParent : TJSHTMLElement; aCaption : String; aID : Integer) : TJSHTMLElement;
+    Function FindObjectItem(aID : Integer) : TJSHTMLElement;
+    procedure Clear;
+    Property ParentElement : TJSHTMLElement Read FParentElement Write SetParentElement;
+    Property OnObjectSelected : TObjectSelectedEvent Read FOnObjectSelect Write FOnObjectSelect;
+    Property StartCollapsed : Boolean Read FStartCollapsed Write FStartCollapsed;
+  end;
+
+  { THTMLObjectTree }
+  TOTOption = (otShowCaption,otStartCollapsed);
+  TOTOptions = set of TOTOption;
+
+  THTMLObjectTree = class(TComponent)
+  private
+    FBuilder: THTMLTreeBuilder;
+    FCaption: String;
+    FOptions: TOTOptions;
+    FParentElement,
+    FCaptionElement : TJSHTMLElement;
+    function GetOnObjectSelected: TObjectSelectedEvent;
+    function GetParentElement: TJSHTMLElement;
+    function GetParentElementID: String;
+    procedure SetCaption(AValue: String);
+    procedure SetOnObjectSelected(AValue: TObjectSelectedEvent);
+    procedure SetOptions(AValue: TOTOptions);
+    procedure SetParentElement(AValue: TJSHTMLElement);
+    procedure SetParentElementID(AValue: String);
+  Protected
+    function BuildWrapper(aParent: TJSHTMLElement): TJSHTMLElement;
+    procedure RenderCaption(aEl: TJSHTMLELement);
+  Public
+    Constructor Create(aOwner : TComponent); override;
+    Destructor Destroy; override;
+    Procedure AddObject(aID : integer; const aClassName,aCaption : String); overload;
+    Procedure AddObject(AParentID,aID : integer; const aClassName,aCaption : String); overload;
+    Procedure Clear;
+    Property ParentElement : TJSHTMLElement Read GetParentElement Write SetParentElement;
+  Published
+    Property ParentElementID : String Read GetParentElementID Write SetParentElementID;
+    Property OnObjectSelected : TObjectSelectedEvent Read GetOnObjectSelected Write SetOnObjectSelected;
+    Property Caption : String Read FCaption Write SetCaption;
+    Property Options : TOTOptions Read FOptions Write SetOptions;
+  end;
+
+  TPropDataFlag = (pdfNoValue,pdfError);
+  TPropDataFlags = Set of TPropDataFlag;
+  TOIPropData = record
+    ObjectID : Longint;
+    Index : Integer;
+    Visibility : TMembervisibility;
+    Kind : TTypeKind;
+    Flags : TPropDataFlags;
+    Name : String;
+    Value : String;
+    ValueObjectID : Longint;
+  end;
+
+  TOIColumn = (ocName,ocValue,ocKind,ocVisibility);
+  TOIColumns = set of TOIColumn;
+
+  TOIOption = (ooHidePropertiesWithoutValue,ooShowCaption);
+  TOIOptions = set of TOIOption;
+
+  { THTMLObjectInspector }
+
+  TBeforeAddPropertyEvent = procedure (Sender : TObject; aData : TOIPropData; var aAllow : Boolean) of object;
+  TAfterAddPropertyEvent = procedure (Sender : TObject; aData : TOIPropData) of object;
+
+  THTMLObjectInspector = class(TComponent)
+  private
+    FAfterAddProperty: TAfterAddPropertyEvent;
+    FBeforeAddProperty: TBeforeAddPropertyEvent;
+    FBorder: Boolean;
+    FCaption: String;
+    FOnRefresh: TNotifyEvent;
+    FOptions: TOIOptions;
+    FVisibleColumns: TOIColumns;
+    FObjectID: integer;
+    FParentElement : TJSHTMLElement;
+    FTableElement : TJSHTMLTableElement;
+    FCaptionElement : TJSHTMLElement;
+    function GetParentElement: TJSHTMLElement;
+    function GetParentElementID: String;
+    procedure RenderCaption(aEl: TJSHTMLElement);
+    procedure SetBorder(AValue: Boolean);
+    procedure SetCaption(AValue: String);
+    procedure SetOptions(AValue: TOIOptions);
+    procedure SetVisibleColumns(AValue: TOIColumns);
+    procedure SetParentElementID(AValue: String);
+  protected
+    procedure DisplayChanged;
+    procedure Refresh;
+    function CreateTable(aParent : TJSHTMLElement) : TJSHTMLTableElement; virtual;
+    procedure SetObjectID(AValue: integer); virtual;
+    procedure SetParentElement(AValue: TJSHTMLElement);virtual;
+    function CreateKindCell(aPropData: TOIPropData; const aKindName: String): TJSHTMLTableCellElement; virtual;
+    function CreateNameCell(aPropData: TOIPropData): TJSHTMLTableCellElement; virtual;
+    function CreateValueCell(aPropData: TOIPropData; const aKindName: string): TJSHTMLTableCellElement; virtual;
+    function CreateVisibilityCell(aPropData: TOIPropData): TJSHTMLTableCellElement; virtual;
+    procedure DoAddProperty(aPropData: TOIPropData); virtual;
+  Public
+    constructor Create(aOwner : TComponent); override;
+    destructor destroy; override;
+    procedure clear;
+    procedure AddProperty(aIndex : Integer; aVisibility : TMemberVisibility; aKind : TTypeKind; aFlags : TPropDataFlags; const aName,aValue : String);
+    procedure AddProperty(aPropData: TOIPropData);
+    Property ParentElement : TJSHTMLElement Read GetParentElement Write SetParentElement;
+  Published
+    Property ObjectID : integer Read FObjectID Write SetObjectID;
+    Property ParentElementID : String Read GetParentElementID Write SetParentElementID;
+    Property Border : Boolean Read FBorder Write SetBorder;
+    property VisibleColumns : TOIColumns read FVisibleColumns write SetVisibleColumns;
+    Property Options : TOIOptions Read FOptions Write SetOptions;
+    property BeforeAddProperty : TBeforeAddPropertyEvent Read FBeforeAddProperty Write FBeforeAddProperty;
+    property AfterAddProperty : TAfterAddPropertyEvent Read FAfterAddProperty Write FAfterAddProperty;
+    property OnRefresh : TNotifyEvent Read FOnRefresh write FOnRefresh;
+    Property Caption : String Read FCaption Write SetCaption;
+  end;
+
+
+implementation
+
+const
+  VisibilityNames : Array[TMemberVisibility] of string = ('Private','Protected','Public','Published');
+
+
+{ THTMLTreeBuilder }
+
+procedure THTMLTreeBuilder.SetParentElement(AValue: TJSHTMLElement);
+begin
+  if FParentElement=AValue then Exit;
+  FParentElement:=AValue;
+  FParentElement.innerHTML:='';
+  FRootElement:=nil;
+end;
+
+procedure THTMLTreeBuilder.HandleItemCollapse(Event : TJSEvent);
+
+var
+  El : TJSHTMLElement;
+
+begin
+  El:=TJSHTMLElement(event.targetElement.parentElement);
+  El.classList.toggle('expanded');
+  El.classList.toggle('collapsed');
+end;
+
+procedure THTMLTreeBuilder.HandleItemSelect(Event : TJSEvent);
+
+var
+  El : TJSHTMLElement;
+  lList : TJSNodeList;
+  lSelectID,I : integer;
+
+begin
+  // List element
+  El:=TJSHTMLElement(event.targetElement.parentElement);
+  lList:=FRootElement.querySelectorAll('li.selected');
+  for I:=0 to lList.length-1 do
+    if El<>lList.item(I) then
+      TJSHtmlElement(lList.item(I)).classList.remove('selected');
+  El.classList.add('selected');
+  if Assigned(FOnObjectSelect) then
+    begin
+    lSelectID:=StrToIntDef(el.dataset['objectId'],-1);
+    if (lSelectID<>-1) then
+      FOnObjectSelect(Self,lSelectID);
+    end;
+end;
+
+
+
+function THTMLTreeBuilder.AddItem(aParent: TJSHTMLElement; aCaption: String; aID: Integer): TJSHTMLElement;
+
+var
+  Span,Item,list : TJSHTMLELement;
+
+begin
+  if aParent=Nil then
+    begin
+    if FRootElement=Nil then
+      begin
+      FRootElement:=TJSHTMLElement(Document.createElement('ul'));
+      FRootElement.className:='tree-nested';
+      FParentElement.appendChild(FRootElement);
+      end;
+    aParent:=FParentElement;
+    end
+  else
+    begin
+    if Not SameText(aParent.tagName,'li') then
+      Raise EHTMLTreeBuilder.CreateFmt('Invalid parent item type: %s',[aParent.tagName]);
+    if Not StartCollapsed then
+      begin
+      aParent.ClassList.remove('collapsed');
+      aParent.ClassList.add('expanded');
+      end;
+    end;
+  List:=TJSHTMLELement(aParent.querySelector('ul.tree-nested'));
+  if List=Nil then
+    begin
+    List:=TJSHTMLElement(Document.createElement('ul'));
+    List.className:='tree-nested';
+    aParent.appendChild(List);
+    end;
+  Item:=TJSHTMLElement(Document.createElement('li'));
+  Item.className:='tree-item collapsed';
+  Item.dataset['objectId']:=IntToStr(aID);
+  Span:=TJSHTMLElement(Document.createElement('span'));
+  Span.InnerText:=aCaption;
+  Span.className:='tree-item-caption' ;
+  Span.addEventListener('click',@HandleItemCollapse);
+  Span.addEventListener('dblclick',@HandleItemSelect);
+  Item.appendChild(Span);
+  List.AppendChild(Item);
+  Result:=Item;
+end;
+
+function THTMLTreeBuilder.FindObjectItem(aID: Integer): TJSHTMLElement;
+begin
+  Result:=TJSHTMLElement(ParentElement.querySelector('li[data-object-id="'+IntToStr(aID)+'"]'));
+end;
+
+procedure THTMLTreeBuilder.Clear;
+begin
+  if Assigned(FParentElement) then
+    FParentElement.innerHTML:='';
+  FRootElement:=Nil;
+end;
+
+{ THTMLObjectTree }
+
+function THTMLObjectTree.GetParentElement: TJSHTMLElement;
+begin
+  Result:=FBuilder.ParentElement;
+end;
+
+
+function THTMLObjectTree.GetOnObjectSelected: TObjectSelectedEvent;
+begin
+  Result:=FBuilder.OnObjectSelected
+end;
+
+function THTMLObjectTree.GetParentElementID: String;
+begin
+  if Assigned(ParentElement) then
+    Result:=ParentElement.id
+  else
+    Result:='';
+end;
+
+procedure THTMLObjectTree.SetCaption(AValue: String);
+begin
+  if FCaption=AValue then Exit;
+  FCaption:=AValue;
+  if Assigned(FCaption) then
+    RenderCaption(FCaptionElement);
+end;
+
+procedure THTMLObjectTree.SetOnObjectSelected(AValue: TObjectSelectedEvent);
+begin
+  FBuilder.OnObjectSelected:=aValue;
+end;
+
+procedure THTMLObjectTree.SetOptions(AValue: TOTOptions);
+begin
+  if FOptions=AValue then Exit;
+  FOptions:=AValue;
+  FBuilder.StartCollapsed:=(otStartCollapsed in FOptions);
+end;
+
+procedure THTMLObjectTree.RenderCaption(aEl : TJSHTMLELement);
+
+begin
+  aEL.InnerText:=Caption;
+end;
+
+function THTMLObjectTree.BuildWrapper(aParent : TJSHTMLElement) : TJSHTMLElement;
+
+var
+  DW,DC,DT : TJSHTMLElement;
+
+begin
+  aParent.InnerHTML:='';
+  DC:=TJSHTMLElement(document.createElement('div'));
+  DC.className:='otCaption';
+  aParent.AppendChild(DC);
+  FCaptionElement:=DC;
+  if Not (otShowCaption in Options) then
+    DC.classList.Add('otHidden');
+  RenderCaption(DC);
+  DT:=TJSHTMLElement(document.createElement('div'));
+  DT.className:='otTree';
+  aParent.AppendChild(DT);
+  Result:=DT;
+end;
+
+procedure THTMLObjectTree.SetParentElement(AValue: TJSHTMLElement);
+begin
+  FParentElement:=aValue;
+  FBuilder.ParentElement:=BuildWrapper(FParentElement);
+end;
+
+procedure THTMLObjectTree.SetParentElementID(AValue: String);
+
+var
+  lParent : TJSHTMlelement;
+
+begin
+  lParent:=TJSHTMlelement(Document.getElementById(aValue));
+  if lParent=Nil then
+    Raise EHTMLTreeBuilder.CreateFmt('Unknown element id: "%s"',[aValue]);
+  ParentElement:=lParent;
+end;
+
+constructor THTMLObjectTree.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+  FBuilder:=THTMLTreeBuilder.Create;
+  FOptions:=[otShowCaption];
+  FCaption:='Object Tree';
+end;
+
+destructor THTMLObjectTree.Destroy;
+begin
+  FreeAndNil(FBuilder);
+  Inherited;
+end;
+
+procedure THTMLObjectTree.AddObject(aID: integer; const aClassName, aCaption: String);
+
+begin
+  AddObject(0,aID,aClassName,aCaption);
+end;
+
+procedure THTMLObjectTree.AddObject(AParentID, aID: integer; const aClassName, aCaption: String);
+
+var
+  lParent : TJSHTMLELement;
+
+begin
+  if aParentID<>0 then
+    lParent:=FBuilder.FindObjectItem(aParentID)
+  else
+    lParent:=Nil;
+  FBuilder.AddItem(lParent,aCaption,aID);
+end;
+
+procedure THTMLObjectTree.Clear;
+begin
+  FBuilder.Clear;
+end;
+
+{ THTMLObjectInspector }
+
+function THTMLObjectInspector.GetParentElement: TJSHTMLElement;
+begin
+  Result:=FParentElement;
+end;
+
+function THTMLObjectInspector.GetParentElementID: String;
+begin
+  if Assigned(FParentElement) then
+    Result:=FParentElement.ID
+  else
+    Result:='';
+end;
+
+procedure THTMLObjectInspector.SetBorder(AValue: Boolean);
+begin
+  if FBorder=AValue then Exit;
+  FBorder:=AValue;
+  if Assigned(FTableElement) then
+    FTableElement.Border:=IntToStr(Ord(aValue));
+end;
+
+procedure THTMLObjectInspector.SetCaption(AValue: String);
+begin
+  if FCaption=AValue then Exit;
+  FCaption:=AValue;
+  if (ooShowCaption in Options) and Assigned(FCaptionElement) then
+    RenderCaption(FCaptionElement);
+end;
+
+procedure THTMLObjectInspector.SetOptions(AValue: TOIOptions);
+begin
+  if FOptions=AValue then Exit;
+  FOptions:=AValue;
+  DisplayChanged;
+end;
+
+procedure THTMLObjectInspector.SetVisibleColumns(AValue: TOIColumns);
+begin
+  if FVisibleColumns=AValue then Exit;
+  FVisibleColumns:=AValue;
+  DisplayChanged;
+end;
+
+procedure THTMLObjectInspector.SetObjectID(AValue: integer);
+begin
+  if FObjectID=AValue then Exit;
+  FObjectID:=AValue;
+  DisplayChanged;
+end;
+
+procedure THTMLObjectInspector.SetParentElement(AValue: TJSHTMLElement);
+begin
+  FParentElement:=aValue;
+  DisplayChanged;
+end;
+
+procedure THTMLObjectInspector.SetParentElementID(AValue: String);
+
+var
+  lParent : TJSHTMlelement;
+
+begin
+  lParent:=TJSHTMlelement(Document.getElementById(aValue));
+  if lParent=Nil then
+    Raise EHTMLTreeBuilder.CreateFmt('Unknown element id: "%s"',[aValue]);
+  ParentElement:=lParent;
+end;
+
+procedure THTMLObjectInspector.DisplayChanged;
+begin
+  Clear;
+  Refresh;
+end;
+
+procedure THTMLObjectInspector.Refresh;
+begin
+  if Assigned(FOnRefresh) then
+    FonRefresh(Self);
+end;
+
+Procedure THTMLObjectInspector.RenderCaption(aEl : TJSHTMLElement);
+
+begin
+  aEl.innerText:=Caption;
+end;
+
+function THTMLObjectInspector.CreateTable(aParent : TJSHTMLElement): TJSHTMLTableElement;
+
+
+var
+  DP,DC,P,R,C : TJSHTMLElement;
+
+  function AddHeader(aText,aClass : string) : TJSHTMLTableCellElement;
+  begin
+    Result:=TJSHTMLTableCellElement(Document.createElement('TH'));
+    Result.InnerText:=aText;
+    Result.className:=aClass;
+    R.AppendChild(Result);
+  end;
+
+begin
+  if (ooShowCaption in Options) and (Caption<>'') then
+    begin
+    DP:=TJSHTMLElement(Document.createElement('div'));
+    DP.className:='oiWrapper';
+    aParent.AppendChild(DP);
+    DC:=TJSHTMLElement(Document.createElement('div'));
+    DC.className:='oiCaption';
+    RenderCaption(DC);
+    DP.AppendChild(DC);
+    FCaptionElement:=DC;
+    end
+  else
+    begin
+    FCaptionElement:=DC;
+    DP:=aParent;
+    end;
+  Result:=TJSHTMLTableElement(Document.createElement('TABLE'));
+  Result.ClassName:='objectInspectorTable';
+  P:=TJSHTMLTableElement(Document.createElement('THEAD'));
+  Result.appendChild(P);
+  R:=TJSHTMLTableRowElement(Document.createElement('TR'));
+  if ocName in VisibleColumns then
+    addHeader('Property Name','oiPropertyName');
+  if ocVisibility in VisibleColumns then
+    addHeader('Visibility','oiPropertyVisibility');
+  if ocKind in VisibleColumns then
+    addHeader('Kind','oiPropertyKind');
+  if ocValue in VisibleColumns then
+    addHeader('Value','oiPropertyValue');
+  P.appendChild(R);
+  P:=TJSHTMLTableElement(Document.createElement('TBODY'));
+  Result.border:=IntToStr(Ord(Border));
+  Result.appendChild(P);
+  DP.appendChild(Result);
+end;
+
+procedure THTMLObjectInspector.clear;
+begin
+  if not Assigned(FParentElement) then
+    exit;
+  FParentElement.innerHTML:='';
+  FTableElement:=CreateTable(FParentElement);
+end;
+
+
+procedure THTMLObjectInspector.AddProperty(aIndex: Integer; aVisibility: TMemberVisibility; aKind: TTypeKind; aFlags: TPropDataFlags;
+  const aName, aValue: String);
+
+var
+  aData : TOIPropData;
+
+begin
+  aData.Index:=aIndex;
+  aData.Value:=aValue;
+  aData.Name:=aName;
+  aData.Kind:=aKind;
+  aData.Flags:=aFlags;
+  aData.Visibility:=aVisibility;
+  AddProperty(aData);
+end;
+
+function THTMLObjectInspector.CreateNameCell(aPropData : TOIPropData) : TJSHTMLTableCellElement;
+
+begin
+  Result:=TJSHTMLTableCellElement(Document.createElement('TD'));
+  Result.InnerText:=aPropData.Name;
+  Result.className:='oiPropertyName';
+end;
+
+function THTMLObjectInspector.CreateKindCell(aPropData : TOIPropData; const aKindName: String) : TJSHTMLTableCellElement;
+
+begin
+  Result:=TJSHTMLTableCellElement(Document.createElement('TD'));
+  Result.InnerText:=aKindName;
+  Result.className:='oiPropertyKind';
+end;
+
+function THTMLObjectInspector.CreateVisibilityCell(aPropData : TOIPropData) : TJSHTMLTableCellElement;
+
+
+begin
+  Result:=TJSHTMLTableCellElement(Document.createElement('TD'));
+  Result.InnerText:=VisibilityNames[aPropData.Visibility];
+  Result.className:='oiPropertyVisibility';
+end;
+
+function THTMLObjectInspector.CreateValueCell(aPropData: TOIPropData; const aKindName : string): TJSHTMLTableCellElement;
+
+
+begin
+  Result:=TJSHTMLTableCellElement(Document.createElement('TD'));
+  Result.InnerText:=aPropData.Value;
+  Result.className:='oiPropertyValue '+aKindName;
+end;
+
+
+procedure THTMLObjectInspector.AddProperty(aPropData : TOIPropData);
+
+var
+  allow : Boolean;
+
+begin
+  if (ooHidePropertiesWithoutValue in Options) then
+    allow:=Not (pdfNoValue in aPropdata.Flags)
+  else
+    allow:=True;
+  if Assigned(BeforeAddProperty) then
+    BeforeAddProperty(Self,aPropData,allow);
+  if Allow then
+    begin
+    DoAddProperty(aPropData);
+    if Assigned(AfterAddProperty) then
+      AfterAddProperty(Self,aPropData);
+    end;
+end;
+
+procedure THTMLObjectInspector.DoAddProperty(aPropData : TOIPropData);
+
+
+var
+  TN : String;
+  PR,CN : TJSHTMLElement;
+
+begin
+  TN:=GetEnumName(TypeInfo(TTypeKind),Ord(aPropData.Kind));
+  PR:=TJSHTMLTableRowElement(Document.createElement('TR'));
+  PR.dataset['propertyIdx']:=IntToStr(aPropData.Index);
+  PR.dataset['propertyName']:=aPropData.Name;
+  PR.dataset['propertyKind']:=TN;
+  PR.dataset['propertyKindOrd']:=IntToStr(Ord(aPropData.Kind));
+  if ocName in VisibleColumns then
+    begin
+    cn:=CreateNameCell(aPropData);
+    PR.AppendChild(CN);
+    end;
+  if ocVisibility in VisibleColumns then
+    begin
+    cn:=CreateVisibilityCell(aPropData);
+    PR.AppendChild(CN);
+    end;
+  if ocKind in VisibleColumns then
+    begin
+    cn:=CreateKindCell(aPropData,TN);
+    PR.AppendChild(CN);
+    end;
+  if ocValue in VisibleColumns then
+    begin
+    cn:=CreateValueCell(aPropData,TN);
+    PR.AppendChild(CN);
+    end;
+  FTableElement.tBodies[0].AppendChild(PR);
+end;
+
+constructor THTMLObjectInspector.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+  Caption:='Property inspector';
+  Options:=[ooShowCaption,ooHidePropertiesWithoutValue];
+  VisibleColumns:=[ocName,ocValue];
+end;
+
+destructor THTMLObjectInspector.destroy;
+begin
+  Clear;
+  inherited destroy;
+end;
+
+end.
+

+ 480 - 0
packages/wasm-oi/src/debug.objectinspector.wasm.pas

@@ -0,0 +1,480 @@
+{
+    This file is part of the Pas2JS run time library.
+    Copyright (c) 2024 by the Pas2JS development team.
+
+    API to implement an object inspector for import in a WASM Module
+    
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+unit debug.objectinspector.wasm;
+
+{$mode ObjFPC}
+
+{ $DEFINE NOLOGAPICALLS} // Define this if you want to remove all API logging calls
+
+interface
+
+uses
+  Classes, SysUtils, js, typinfo, debug.objectinspector.html, wasm.debuginspector.shared, wasienv;
+
+
+type
+  EWasmOI = Class(Exception);
+
+  TWasmPointer = longint;
+
+  { TWasmObjectInspectorApi }
+
+  TWasmObjectInspectorApi = class(TImportExtension)
+  private
+    FHandleObjectSelection: Boolean;
+    FInspector: THTMLObjectInspector;
+    FLogAPICalls: Boolean;
+    FObjectTree: THTMLObjectTree;
+    procedure DoSelectObject(Sender: TObject; aObjectId: Integer);
+    procedure RaiseOILastError(const aOperation: String);
+    procedure SetHandleObjectSelection(AValue: Boolean);
+    procedure SetInspector(AValue: THTMLObjectInspector);
+    procedure SetLogAPICalls(AValue: Boolean);
+    procedure SetObjectTree(AValue: THTMLObjectTree);
+  protected
+    procedure Logcall(Const aMsg : string);
+    procedure LogCall(Const aFmt : string; aArgs : Array of const);
+    // Inspector allocation/deallocation commands
+    function InspectorAllocate(aID: TWasmPointer): TWasmOIResult;
+    function InspectorDeAllocate(aID: TInspectorID): TWasmOIResult;
+    // Property inspector commands
+    function InspectorSetCaption(aInspectorID: TInspectorID; aCaption : TWasmPointer; aCaptionLen : Longint): TWasmOIResult;
+    function InspectorAddProperty(aInspectorID: TInspectorID; PropertyData: TWasmPointer): TWasmOIResult;
+    function InspectorClear(aInspectorID: TInspectorID): TWasmOIResult;
+    // Object Tree commands
+    function TreeSetCaption(aInspectorID: TInspectorID; aCaption : TWasmPointer; aCaptionLen : Longint): TWasmOIResult;
+    function TreeAddObject(aInspectorID: TInspectorID; ObjectData : PObjectData): TWasmOIResult;
+    function TreeClear(aInspectorID: TInspectorID) : TWasmOIResult;
+    Procedure HookObjectTree;
+    Procedure UnhookObjectTree;
+    Function GetTree(aInspectorID : TInspectorID) : THTMLObjectTree;
+    Function GetInspector(aInspectorID : TInspectorID) : THTMLObjectInspector;
+  Public
+    Constructor Create(aEnv : TPas2JSWASIEnvironment); override;
+    Procedure FillImportObject(aObject : TJSObject); override;
+    Function ImportName : String; override;
+    Procedure GetObjectProperties(aObjectID : Integer);
+    Property DefaultObjectTree : THTMLObjectTree Read FObjectTree Write SetObjectTree;
+    property DefaultInspector : THTMLObjectInspector Read FInspector Write SetInspector;
+    Property HandleObjectSelection : Boolean Read FHandleObjectSelection Write SetHandleObjectSelection;
+    property LogAPICalls : Boolean read FLogAPICalls write SetLogAPICalls;
+  end;
+
+implementation
+
+uses rtti;
+
+type
+  TGetObjectProperties = function(aInspectorID : TInspectorID; aObjectID : TObjectID; aFlags : Longint) : Longint;
+
+{ TWasmObjectInspectorApi }
+
+procedure TWasmObjectInspectorApi.SetInspector(AValue: THTMLObjectInspector);
+begin
+  if FInspector=AValue then Exit;
+  FInspector:=AValue;
+  if assigned(FInspector) then
+    FInspector.Clear;
+end;
+
+procedure TWasmObjectInspectorApi.SetLogAPICalls(AValue: Boolean);
+begin
+  if FLogAPICalls=AValue then Exit;
+  FLogAPICalls:=AValue;
+end;
+
+procedure TWasmObjectInspectorApi.SetObjectTree(AValue: THTMLObjectTree);
+begin
+  if FObjectTree=AValue then Exit;
+  FObjectTree:=AValue;
+  if assigned(FObjectTree) then
+    FObjectTree.Clear;
+  if FHandleObjectSelection then
+    HookObjectTree;
+end;
+
+procedure TWasmObjectInspectorApi.Logcall(const aMsg: string);
+begin
+  {$IFNDEF NOLOGAPICALLS}
+  Writeln(aMsg);
+  {$ENDIF}
+end;
+
+procedure TWasmObjectInspectorApi.LogCall(const aFmt: string; aArgs: array of const);
+begin
+  {$IFNDEF NOLOGAPICALLS}
+  Writeln(Format(aFmt,aArgs));
+  {$ENDIF}
+end;
+
+function TWasmObjectInspectorApi.GetTree(aInspectorID: TInspectorID): THTMLObjectTree;
+begin
+  if aInspectorID=0 then
+    Result:=DefaultObjectTree
+  else
+    Result:=nil;
+end;
+
+function TWasmObjectInspectorApi.GetInspector(aInspectorID: TInspectorID): THTMLObjectInspector;
+begin
+  if aInspectorID=0 then
+    Result:=DefaultInspector
+  else
+    Result:=nil;
+end;
+
+procedure TWasmObjectInspectorApi.RaiseOILastError(const aOperation : String);
+
+var
+  S : String;
+
+begin
+  S:='Operation '+aOperation+' failed';
+  // Todo, get error
+  Raise EWasmOI.Create(S);
+end;
+
+procedure TWasmObjectInspectorApi.DoSelectObject(Sender: TObject; aObjectId: Integer);
+begin
+  GetObjectProperties(aObjectID);
+end;
+
+procedure TWasmObjectInspectorApi.SetHandleObjectSelection(AValue: Boolean);
+begin
+  if FHandleObjectSelection=AValue then Exit;
+  FHandleObjectSelection:=AValue;
+  if FHandleObjectSelection then
+    HookObjectTree
+  else
+    UnhookObjectTree;
+end;
+
+procedure TWasmObjectInspectorApi.GetObjectProperties(aObjectID: Integer);
+
+var
+  Proc : TGetObjectProperties;
+begin
+  Proc:=TGetObjectProperties(InstanceExports['wasm_oi_get_object_properties']);
+  if Not Assigned(Proc) then
+    Raise EWasmOI.Create('No wasm_oi_get_object_properties entry point');
+  if not Proc(0,aObjectID,WASM_SENDPROPERTYFLAG_ALLVISIBILITIES)=WASMOI_SUCCESS then
+    RaiseOILastError('GetObjectProperties');
+end;
+
+
+constructor TWasmObjectInspectorApi.Create(aEnv: TPas2JSWASIEnvironment);
+begin
+  inherited Create(aEnv);
+  FObjectTree:=Nil;
+  FInspector:=Nil;
+end;
+
+function TWasmObjectInspectorApi.InspectorAllocate(aID: TWasmPointer): TWasmOIResult;
+
+var
+  v : TJSDataView;
+
+begin
+  {$IFNDEF NOLOGAPICALLS}
+  If LogAPICalls then
+    begin
+    LogCall('OI.InspectorAllocate([%x])',[aID]);
+    end;
+  {$ENDIF}
+   Result:=WASMOI_NOT_IMPLEMENTED;
+{  v:=getModuleMemoryDataView;
+  v.setInt32(aID,0,Env.IsLittleEndian);
+  }
+end;
+
+function TWasmObjectInspectorApi.InspectorDeAllocate(aID: TInspectorID): TWasmOIResult;
+
+var
+  v : TJSDataView;
+
+begin
+  {$IFNDEF NOLOGAPICALLS}
+  If LogAPICalls then
+    begin
+    LogCall('OI.InspectorDeAllocate(%d)',[aID]);
+    end;
+  {$ENDIF}
+   Result:=WASMOI_NOT_IMPLEMENTED;
+{  v:=getModuleMemoryDataView;
+  v.setInt32(aID,0,Env.IsLittleEndian);
+  }
+end;
+
+function TWasmObjectInspectorApi.InspectorSetCaption(aInspectorID: TInspectorID; aCaption: TWasmPointer; aCaptionLen: Longint
+  ): TWasmOIResult;
+
+var
+  V : TJSDataView;
+  OI : THTMLObjectInspector;
+  lCaption : String;
+
+begin
+  V:=getModuleMemoryDataView;
+  lCaption:=Env.GetUTF8StringFromMem(aCaption,aCaptionLen);
+  {$IFNDEF NOLOGAPICALLS}
+  If LogAPICalls then
+    begin
+    LogCall('OI.InspectorSetCaption(%d,"%s")',[aInspectorID,lCaption]);
+    end;
+  {$ENDIF}
+  OI:=GetInspector(aInspectorID);
+  if Not Assigned(OI) then
+    Result:=WASMOI_NO_INSPECTOR
+  else
+    begin
+    Result:=WASMOI_SUCCESS;
+    OI.Caption:=lCaption;
+    end;
+end;
+
+
+
+function TWasmObjectInspectorApi.TreeClear(aInspectorID: TInspectorID): TWasmOIResult;
+
+var
+  T : THTMLObjectTree;
+
+begin
+  {$IFNDEF NOLOGAPICALLS}
+  If LogAPICalls then
+    begin
+    LogCall('OI.TreeClear(%d)',[aInspectorID]);
+    end;
+  {$ENDIF}
+  T:=GetTree(aInspectorID);
+  if T=Nil then
+    Result:=WASMOI_NO_INSPECTOR
+  else
+    begin
+    T.Clear;
+    Result:=WASMOI_SUCCESS;
+    end;
+end;
+
+procedure TWasmObjectInspectorApi.HookObjectTree;
+begin
+  if not Assigned(FObjectTree) then
+    Exit;
+  FObjectTree.OnObjectSelected:=@DoSelectObject;
+end;
+
+procedure TWasmObjectInspectorApi.UnhookObjectTree;
+begin
+  if not Assigned(FObjectTree) then
+    Exit;
+  FObjectTree.OnObjectSelected:=Nil;
+end;
+
+function TWasmObjectInspectorApi.InspectorClear(aInspectorID: TInspectorID): TWasmOIResult;
+
+var
+  OI : THTMLObjectInspector;
+
+
+begin
+  {$IFNDEF NOLOGAPICALLS}
+  If LogAPICalls then
+    begin
+    LogCall('OI.InspectorClear(%d)',[aInspectorID]);
+    end;
+  {$ENDIF}
+  OI:=GetInspector(aInspectorID);
+  if Not Assigned(OI) then
+    Result:=WASMOI_NO_INSPECTOR
+  else
+    begin
+    Result:=WASMOI_SUCCESS;
+    OI.Clear;
+    end;
+end;
+
+function TWasmObjectInspectorApi.TreeSetCaption(aInspectorID: TInspectorID; aCaption: TWasmPointer; aCaptionLen: Longint
+  ): TWasmOIResult;
+var
+  V : TJSDataView;
+  T : THTMLObjectTree;
+  lCaption : String;
+
+begin
+  V:=getModuleMemoryDataView;
+  lCaption:=Env.GetUTF8StringFromMem(aCaption,aCaptionLen);
+  {$IFNDEF NOLOGAPICALLS}
+  If LogAPICalls then
+    begin
+    LogCall('OI.InspectorSetCaption(%d,"%s")',[aInspectorID,lCaption]);
+    end;
+  {$ENDIF}
+  T:=GetTree(aInspectorID);
+  if Not Assigned(T) then
+    Result:=WASMOI_NO_INSPECTOR
+  else
+    begin
+    Result:=WASMOI_SUCCESS;
+    T.Caption:=lCaption;
+    end;
+end;
+
+function TWasmObjectInspectorApi.InspectorAddProperty(aInspectorID: TInspectorID; PropertyData : TWasmPointer): TWasmOIResult;
+
+
+  function GetElement(V : TJSDataView; aOffset : Longint) : Longint;
+  begin
+    Result:=V.getInt32(PropertyData+(aOffset*4),Env.IsLittleEndian);
+  end;
+
+  function GetString(V : TJSDataView; aNameOffset,aNameLenOffset : Longint) : string;
+
+  var
+    O,L : Longint;
+
+  begin
+    O:=GetElement(v,aNameOffset);
+    L:=GetElement(v,aNameLenOffset);
+    Result:=env.GetUTF8StringFromMem(O,L);
+  end;
+
+
+var
+  PropertyKind : TNativeTypeKind;
+  PropertyFlags : Longint;
+  PropDataFlags : TPropDataFlags;
+  V : TJSDataView;
+  OI : THTMLObjectInspector;
+  PropData : TOIPropData;
+
+begin
+  V:=getModuleMemoryDataView;
+
+  PropData.ObjectID:=GetElement(V,WASM_PROPERTY_OBJECT_ID);
+  PropertyKind:=TNativeTypeKind(GetElement(V,WASM_PROPERTY_KIND));
+  PropertyFlags:=GetElement(V,WASM_PROPERTY_FLAGS);
+  PropData.Index:=GetElement(V,WASM_PROPERTY_IDX);
+  PropData.Visibility:=TMemberVisibility(GetElement(V,WASM_PROPERTY_VISIBILITY));
+  PropData.Name:=GetString(V,WASM_PROPERTY_NAME,WASM_PROPERTY_NAME_LEN);
+  PropData.Value:=GetString(V,WASM_PROPERTY_VALUE,WASM_PROPERTY_VALUE_LEN);
+  PropData.ValueObjectID:=GetElement(V,WASM_PROPERTY_PROPERTYOBJECTID);
+  {$IFNDEF NOLOGAPICALLS}
+  If LogAPICalls then
+    begin
+    LogCall('OI.InspectorAddProperty(%d,%d,%d,%s,%s,"%s","%s",%d)',[
+       aInspectorID,
+       PropData.ObjectID,
+       PropData.Index,
+       GetEnumName(TypeInfo(TMemberVisibility),Ord(PropData.Visibility)),
+       GetEnumName(TypeInfo(TNativeTypeKind),Ord(PropertyKind)),
+       PropData.Name,
+       PropData.Value,
+       PropertyFlags]);
+    end;
+  {$ENDIF}
+  OI:=GetInspector(aInspectorID);
+  if Not Assigned(OI) then
+    Result:=WASMOI_NO_INSPECTOR
+  else
+    begin
+    Result:=WASMOI_SUCCESS;
+    PropData.Flags:=[];
+    if (PropertyFlags and WASM_PROPERTYFLAGS_NOVALUE)<>0 then
+      Include(PropData.Flags,pdfNoValue);
+    if (PropertyFlags and WASM_PROPERTYFLAGS_ERROR)<>0 then
+      Include(PropData.Flags,pdfError);
+    PropData.Kind:=GetPlatformTypeKind(PropertyKind);
+    OI.ObjectID:=PropData.ObjectID;
+    OI.AddProperty(Propdata);
+    end;
+end;
+
+function TWasmObjectInspectorApi.TreeAddObject(aInspectorID: TInspectorID; ObjectData : PObjectData): TWasmOIResult;
+
+
+  function GetElement(V : TJSDataView; aOffset : Longint) : Longint;
+  begin
+    Result:=V.getInt32(ObjectData+(aOffset*4),Env.IsLittleEndian);
+  end;
+
+  function GetString(V : TJSDataView; aNameOffset,aNameLenOffset : Longint) : string;
+
+  var
+    O,L : Longint;
+
+  begin
+    O:=GetElement(v,aNameOffset);
+    L:=GetElement(v,aNameLenOffset);
+    Result:=env.GetUTF8StringFromMem(O,L);
+  end;
+
+var
+  T : THTMLObjectTree;
+  lClassName,lCaption : String;
+  lObjectID,
+  lParentID : TObjectID;
+  lFLags : Longint;
+  V : TJSDataView;
+
+begin
+  V:=getModuleMemoryDataView;
+  lParentID:=GetElement(V,WASM_OBJECT_PARENTID);
+  lObjectID:=GetElement(V,WASM_OBJECT_ID);
+  lFlags:=GetElement(V,WASM_OBJECT_ID);
+  lClassName:=GetString(V,WASM_OBJECT_CLASSNAME,WASM_OBJECT_CLASSNAME_LEN);
+  lCaption:=GetString(V,WASM_OBJECT_CAPTION,WASM_OBJECT_CAPTION_LEN);
+  {$IFNDEF NOLOGAPICALLS}
+  If LogAPICalls then
+    begin
+    LogCall('OI.TreeAddObject(%d,%d,%d,%d,"%s","%s")',[
+       aInspectorID,
+       lParentId,
+       lObjectID,
+       lFlags,
+       lClassName,
+       lCaption
+    ]);
+    end;
+  {$ENDIF}
+  T:=GetTree(aInspectorID);
+  if T=Nil then
+    Result:=WASMOI_NO_INSPECTOR
+  else
+    begin
+    Result:=WASMOI_SUCCESS;
+    T.AddObject(lParentID,lObjectID,lClassName,lCaption);
+    end;
+end;
+
+
+procedure TWasmObjectInspectorApi.FillImportObject(aObject: TJSObject);
+begin
+  aObject[call_allocate]:=@InspectorAllocate;
+  aObject[call_deallocate]:=@InspectorDeAllocate;
+  aObject[call_tree_set_caption]:=@TreeSetCaption;
+  aObject[call_tree_clear]:=@TreeClear;
+  aObject[call_tree_add_object]:=@TreeAddObject;
+  aObject[call_inspector_clear]:=@InspectorClear;
+  aObject[call_inspector_add_property]:=@InspectorAddProperty;
+  aObject[call_inspector_set_caption]:=@InspectorSetCaption;
+end;
+
+function TWasmObjectInspectorApi.ImportName: String;
+begin
+  Result:=InspectorModuleName;
+end;
+
+end.
+

+ 167 - 0
packages/wasm-oi/src/wasm.debuginspector.shared.pas

@@ -0,0 +1,167 @@
+{
+    This file is part of the Pas2JS run time library.
+    Copyright (c) 2024 by the Pas2JS development team.
+
+    Constants for API to implement an object inspector for import in a WASM Module
+    
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit wasm.debuginspector.shared;
+
+{$mode objfpc}
+
+interface
+
+{$IFDEF PAS2JS}
+uses rtti;
+{$ENDIF}
+
+Const
+  // API return values
+  WASMOI_SUCCESS         = 0;
+  WASMOI_NOT_IMPLEMENTED = -1;
+  WASMOI_NO_INSPECTOR    = -2;
+  WASMOI_EXCEPTION       = -3;
+  WASMOI_INVALIDOBJECT   = -4;
+
+  // Property data array
+  WASM_PROPERTY_OBJECT_ID  = 0;
+  WASM_PROPERTY_IDX        = 1;
+  WASM_PROPERTY_KIND       = 2;
+  WASM_PROPERTY_VISIBILITY = 3;
+  WASM_PROPERTY_NAME       = 4;
+  WASM_PROPERTY_NAME_LEN   = 5;
+  WASM_PROPERTY_VALUE      = 6;
+  WASM_PROPERTY_VALUE_LEN  = 7;
+  WASM_PROPERTY_FLAGS      = 8;
+  WASM_PROPERTY_PROPERTYOBJECTID = 9;
+
+  WASM_PROPERTYDATA_MAXLEN = WASM_PROPERTY_PROPERTYOBJECTID;
+
+  // Property Flags
+  WASM_PROPERTYFLAGS_NOVALUE = 1;       // Value cannot be displayed
+  WASM_PROPERTYFLAGS_ERROR   = 1 shl 1; // Error while calculating value
+
+
+  // Send Property Flags
+  WASM_SENDPROPERTYFLAG_PRIVATE    = 1;
+  WASM_SENDPROPERTYFLAG_PROTECTED  = 1 shl 1;
+  WASM_SENDPROPERTYFLAG_PUBLIC     = 1 shl 2;
+  WASM_SENDPROPERTYFLAG_PUBLISHED  = 1 shl 3;
+  WASM_SENDPROPERTYFLAG_ALLVISIBILITIES = WASM_SENDPROPERTYFLAG_PRIVATE
+                                          or WASM_SENDPROPERTYFLAG_PROTECTED
+                                          or WASM_SENDPROPERTYFLAG_PUBLIC
+                                          or WASM_SENDPROPERTYFLAG_PUBLISHED;
+  WASM_SENDPROPERTYFLAG_NOCAPTION = 1 shl 4;
+
+  // Object data array
+  WASM_OBJECT_PARENTID      = 0;
+  WASM_OBJECT_ID            = 1;
+  WASM_OBJECT_FLAGS         = 2;
+  WASM_OBJECT_CLASSNAME     = 3;
+  WASM_OBJECT_CLASSNAME_LEN = 4;
+  WASM_OBJECT_CAPTION       = 5;
+  WASM_OBJECT_CAPTION_LEN   = 6;
+
+  WASM_OBJECTDATA_MAXLEN = WASM_OBJECT_CAPTION_LEN;
+
+//  aParentID, aObjectID: TObjectID; aFlags : Longint; aCaption: TWasmPointer; aCaptionLen : Longint
+
+type
+  TWasmOIResult = longint;
+  TInspectorID = longint;
+  TObjectID = longint;
+
+  TPropertyData = Array[0..WASM_PROPERTYDATA_MAXLEN] of longint;
+  TObjectData = Array[0..WASM_OBJECTDATA_MAXLEN] of longint;
+
+  {$IFNDEF PAS2JS}
+  TWasmPointer = Pointer;
+  PPropertyData = ^TPropertyData;
+  PObjectData = ^TObjectData;
+  PInspectorID = ^TInspectorID;
+  {$ELSE PAS2JS}
+  TWasmPointer = longint;
+  PPropertyData = TWasmPointer;
+  PObjectData = TWasmPointer;
+  PInspectorID = TWasmPointer;
+  {$ENDIF PAS2JS}
+
+Const
+  InspectorModuleName = 'wasm_oi';
+
+  call_allocate = 'allocate';
+  call_deallocate = 'deallocate';
+  call_tree_clear = 'tree_clear';
+  call_tree_set_caption = 'tree_set_caption';
+  call_tree_add_object = 'tree_add_object';
+  call_inspector_clear = 'inspector_clear';
+  call_inspector_add_property = 'inspector_add_property';
+  call_inspector_set_caption = 'inpector_set_caption';
+
+Type
+  // TTypeKind is different in Delphi/FPC and in Pas2JS
+  TNativeTypeKind = (ntkUnknown,ntkInteger,ntkChar,ntkEnumeration,ntkFloat,
+            ntkSet,ntkMethod,ntkSString,ntkLString,ntkAString,
+            ntkWString,ntkVariant,ntkArray,ntkRecord,ntkInterface,
+            ntkClass,ntkObject,ntkWChar,ntkBool,ntkInt64,ntkQWord,
+            ntkDynArray,ntkInterfaceRaw,ntkProcVar,ntkUString,ntkUChar,
+            ntkHelper,ntkFile,ntkClassRef,ntkPointer);
+
+
+
+function GetPlatformTypeKind(aKind : TNativeTypeKind) : TTypeKind;
+
+implementation
+
+{$IFDEF PAS2JS}
+function GetPlatformTypeKind(aKind : TNativeTypeKind) : TTypeKind;
+
+begin
+  case aKind of
+    ntkUnknown : Result:=tkUnknown;  // 0
+    ntkInt64,
+    ntkQWord,
+    ntkInteger : Result:=tkInteger;   // 1
+    ntkUChar,
+    ntkWChar,
+    ntkChar : Result:=tkChar;         // 2 in Delphi/FPC tkWChar; tkUChar
+    ntkSString,
+    ntkAString,
+    ntkWString,
+    ntkUString: Result:=tkString;      // 3 in Delphi/FPC tkSString; tkWString or tkUString
+    ntkEnumeration : Result:=tkEnumeration; // 4
+    ntkSet : Result:=tkSet;            // 5
+    ntkFloat : Result:=tkDouble;   // 6
+    ntkBool : Result:=tkBool;     // 7
+    ntkProcVar : Result:=tkProcVar;  // 8  function or procedure
+    ntkMethod : Result:=tkMethod;   // 9  proc var of object
+    ntkArray : Result:=tkArray;    // 10 static array
+    ntkDynArray : Result:=tkDynArray; // 11
+    ntkRecord : Result:=tkRecord;   // 12
+    ntkClass : Result:=tkClass;    // 13
+    ntkClassRef : Result:=tkClassRef; // 14
+    ntkPointer : Result:=tkPointer;  // 15
+    ntkVariant : Result:=tkJSValue;  // 16
+    ntkInterface : Result:=tkInterface; // 18
+  else
+    Result:=tkUnknown;
+  end;
+end;
+{$ELSE}
+function GetPlatformTypeKind(aKind : TNativeTypeKind) : TTypeKind;
+begin
+  Result:=TTypeKind(aKind);
+end;
+
+{$ENDIF}
+
+end.
+