Browse Source

TVirtualInterface equivalent to the Delphi implementation.

Henrique Gottardi Werlang 2 years ago
parent
commit
a2b77e865d
2 changed files with 124 additions and 41 deletions
  1. 117 41
      packages/rtl/rtti.pas
  2. 7 0
      packages/rtl/typinfo.pas

+ 117 - 41
packages/rtl/rtti.pas

@@ -460,20 +460,30 @@ type
 
   EInvoke = EJS;
 
-  TVirtualInterfaceInvokeEvent = function(const aMethodName: string;
-    const Args: TJSValueDynArray): JSValue of object;
+  TVirtualInterfaceInvokeEvent = reference to procedure(Method: TRttiMethod; const Args: specialize TArray<TValue>; out Result: TValue);
+  TVirtualInterfaceInvokeEventJS = reference to function(const aMethodName: String; const Args: TJSValueDynArray): JSValue;
 
   { TVirtualInterface: A class that can implement any IInterface. Any method
     call is handled by the OnInvoke event. }
   TVirtualInterface = class(TInterfacedObject, IInterface)
   private
+    FContext: TRttiContext;
+    FInterfaceType: TRttiInterfaceType;
     FOnInvoke: TVirtualInterfaceInvokeEvent;
+    FOnInvokeJS: TVirtualInterfaceInvokeEventJS;
+
+    function Invoke(const MethodName: String; const Args: TJSValueDynArray): JSValue;
   public
-    constructor Create(InterfaceTypeInfo: Pointer); overload; assembler;
-    constructor Create(InterfaceTypeInfo: Pointer;
-      const InvokeEvent: TVirtualInterfaceInvokeEvent); overload;
+    constructor Create(InterfaceTypeInfo: TTypeInfoInterface); overload;
+    constructor Create(InterfaceTypeInfo: TTypeInfoInterface; const InvokeEvent: TVirtualInterfaceInvokeEvent); overload;
+    constructor Create(InterfaceTypeInfo: TTypeInfoInterface; const InvokeEvent: TVirtualInterfaceInvokeEventJS); overload;
+
+    destructor Destroy; override;
+
     function QueryInterface(const iid: TGuid; out obj): Integer; override;
+
     property OnInvoke: TVirtualInterfaceInvokeEvent read FOnInvoke write FOnInvoke;
+    property OnInvokeJS: TVirtualInterfaceInvokeEventJS read FOnInvokeJS write FOnInvokeJS;
   end;
 
 procedure CreateVirtualCorbaInterface(InterfaceTypeInfo: Pointer;
@@ -2018,46 +2028,68 @@ end;
 
 { TVirtualInterface }
 
-constructor TVirtualInterface.Create(InterfaceTypeInfo: Pointer); assembler;
-asm
-  var IntfType = InterfaceTypeInfo.interface;
-  if (IntfType.$kind !== 'com') rtl.raiseE('EInvalidCast');
-  var guid = IntfType.$guid;
-  var i = Object.create(IntfType); // needed by IntfVar is IntfType
-  i.$o = this;
-  // copy IInterface methods: _AddRef, _Release, QueryInterface
-  var iinterfaceguid = '{00000000-0000-0000-C000-000000000046}';
-  var map = this.$intfmaps[iinterfaceguid];
-  for (var key in map){
-    var v = map[key];
-    if (typeof(v)!=='function') continue;
-    i[key] = map[key];
-  }
-  // all other methods call OnInvoke
-  do {
-    var names = IntfType.$names;
-    if (!names) break;
-    for (var j=0; j<names.length; j++){
-      let fnname = names[j];
-      if (i[fnname]) continue;
-      i[fnname] = function(){ return this.$o.FOnInvoke(fnname,arguments); };
-    }
-    IntfType = Object.getPrototypeOf(IntfType);
-  } while(IntfType!=null);
-  // create a new list of interface map, supporting IInterface and IntfType
-  this.$intfmaps = {};
-  this.$intfmaps[iinterfaceguid] = map;
-  this.$intfmaps[guid] = {};
-  // store the implementation of IntfType (used by the as-operator)
-  this.$interfaces = {};
-  this.$interfaces[guid] = i;
+constructor TVirtualInterface.Create(InterfaceTypeInfo: TTypeInfoInterface);
+var
+  SelfInterfaceObject, InterfaceObject: TInterfaceObject;
+
+  Method: TRttiMethod;
+
+  MethodName: String;
+
+begin
+  FContext := TRttiContext.Create;
+  FInterfaceType := FContext.GetType(InterfaceTypeInfo) as TRttiInterfaceType;
+
+  if FInterfaceType.InterfaceTypeInfo.InterfaceInfo.kind <> 'com' then
+    raise EInvalidCast.Create;
+
+  InterfaceObject := TInterfaceObject(TJSObject.Create(FInterfaceType.InterfaceTypeInfo.InterfaceInfo));
+  InterfaceObject.Obj := Self;
+
+  for Method in FInterfaceType.GetMethods do
+  begin
+    asm
+      let MethodName = Method.GetName();
+    end;
+
+    InterfaceObject[MethodName] :=
+      function: JSValue
+      begin
+        Result := TVirtualInterface(TInterfaceObject(JSThis).Obj).Invoke(MethodName, TJSValueDynArray(JSValue(JSArguments)));
+      end;
+  end;
+
+  InterfaceObject['_AddRef'] := @_AddRef;
+  InterfaceObject['_Release'] := @_Release;
+  InterfaceObject['QueryInterface'] := @QueryInterface;
+
+  SelfInterfaceObject := TInterfaceObject(Self);
+  SelfInterfaceObject.InterfaceMaps := TJSObject.New;
+  SelfInterfaceObject.InterfaceMaps[GUIDToString(IInterface)] := InterfaceObject;
+  SelfInterfaceObject.InterfaceMaps[FInterfaceType.Guid.ToString] := TJSObject.New;
+  SelfInterfaceObject.Interfaces := TJSObject.New;
+  SelfInterfaceObject.Interfaces[FInterfaceType.Guid.ToString] := InterfaceObject;
+end;
+
+constructor TVirtualInterface.Create(InterfaceTypeInfo: TTypeInfoInterface; const InvokeEvent: TVirtualInterfaceInvokeEvent);
+begin
+  Create(InterfaceTypeInfo);
+
+  OnInvoke := InvokeEvent;
 end;
 
-constructor TVirtualInterface.Create(InterfaceTypeInfo: Pointer;
-  const InvokeEvent: TVirtualInterfaceInvokeEvent);
+constructor TVirtualInterface.Create(InterfaceTypeInfo: TTypeInfoInterface; const InvokeEvent: TVirtualInterfaceInvokeEventJS);
 begin
   Create(InterfaceTypeInfo);
-  OnInvoke:=InvokeEvent;
+
+  OnInvokeJS := InvokeEvent;
+end;
+
+destructor TVirtualInterface.Destroy;
+begin
+  FContext.Free;
+
+  inherited;
 end;
 
 function TVirtualInterface.QueryInterface(const iid: TGuid; out obj): Integer;
@@ -2065,6 +2097,50 @@ begin
   Result := inherited QueryInterface(iid, obj);
 end;
 
+function TVirtualInterface.Invoke(const MethodName: String; const Args: TJSValueDynArray): JSValue;
+var
+  Method: TRttiMethod;
+
+  Return: TValue;
+
+  function GenerateParams: specialize TArray<TValue>;
+  var
+    A: Integer;
+
+    Return: TValue;
+
+    Param: TRttiParameter;
+
+    Parameters: specialize TArray<TRttiParameter>;
+
+  begin
+    Parameters := Method.GetParameters;
+
+    SetLength(Result, Length(Parameters));
+
+    for A := Low(Parameters) to High(Parameters) do
+    begin
+      Param := Parameters[A];
+
+      TValue.Make(Args[A], Param.ParamType.Handle, Result[A]);
+
+      Result[A].FReferenceVariableData := (pfVar in Param.Flags) or (pfOut in Param.Flags);
+    end;
+  end;
+
+begin
+  if Assigned(FOnInvokeJS) then
+    Result := FOnInvokeJS(MethodName, Args)
+  else
+  begin
+    Method := FInterfaceType.GetMethod(MethodName);
+
+    FOnInvoke(Method, GenerateParams, Return);
+
+    Result := Return.AsJSValue;
+  end;
+end;
+
 function Invoke(ACodeAddress: Pointer; const AArgs: TJSValueDynArray;
   ACallConv: TCallConv; AResultType: PTypeInfo; AIsStatic: Boolean;
   AIsConstructor: Boolean): TValue;

+ 7 - 0
packages/rtl/typinfo.pas

@@ -352,6 +352,13 @@ type
     procedure &set(const value: JSValue);
   end;
 
+  TInterfaceObject = class external name 'Object' (TJSObject)
+  public
+    InterfaceMaps: TJSObject external name '$intfmaps';
+    Interfaces: TJSObject external name '$interfaces';
+    Obj: TInterfacedObject external name '$o';
+  end;
+
   EPropertyError  = class(Exception);
 
 function GetTypeName(TypeInfo: TTypeInfo): string;