Browse Source

pastojs: convert exit(ComIntf) to setting Result variable, issue #39292

mattias 5 months ago
parent
commit
4d8c9c9d78
2 changed files with 99 additions and 9 deletions
  1. 37 9
      packages/pastojs/src/fppas2js.pp
  2. 62 0
      packages/pastojs/tests/tcmodules.pas

+ 37 - 9
packages/pastojs/src/fppas2js.pp

@@ -13422,8 +13422,10 @@ var
   St: TJSStatementList;
   St: TJSStatementList;
   ImplProc, DeclProc: TPasProcedure;
   ImplProc, DeclProc: TPasProcedure;
   ImplTry: TPasImplTry;
   ImplTry: TPasImplTry;
-  ResultIsRead: Boolean;
+  ResultIsRead, IsCOMIntf: Boolean;
   ResultEl: TPasResultElement;
   ResultEl: TPasResultElement;
+  TypeEl: TPasType;
+  Call: TJSCallExpression;
 begin
 begin
   {$IFDEF VerbosePas2JS}
   {$IFDEF VerbosePas2JS}
   writeln('TPasToJSConverter.ConvertBuiltIn_Exit ',GetObjName(El));
   writeln('TPasToJSConverter.ConvertBuiltIn_Exit ',GetObjName(El));
@@ -13434,16 +13436,30 @@ begin
   // ParentEl can be nil, when exit is in program begin block
   // ParentEl can be nil, when exit is in program begin block
   ImplProc:=TPasProcedure(ParentEl);
   ImplProc:=TPasProcedure(ParentEl);
   ResultVarName:='';
   ResultVarName:='';
+  ResultEl:=nil;
+  IsCOMIntf:=false;
   if ImplProc<>nil then
   if ImplProc<>nil then
     begin
     begin
     ImplProcScope:=ImplProc.CustomData as TPas2JSProcedureScope;
     ImplProcScope:=ImplProc.CustomData as TPas2JSProcedureScope;
-    if ImplProc.ProcType is TPasFunctionType then
+    DeclProc:=ImplProcScope.DeclarationProc;
+    if DeclProc=nil then
+      DeclProc:=ImplProc; // Note: references refer to ResultEl of DeclProc
+    if DeclProc.ProcType is TPasFunctionType then
       begin
       begin
       ResultVarName:=ImplProcScope.ResultVarName; // ResultVarName needs ImplProc
       ResultVarName:=ImplProcScope.ResultVarName; // ResultVarName needs ImplProc
       if ResultVarName='' then
       if ResultVarName='' then
         ResultVarName:=ResolverResultVar;
         ResultVarName:=ResolverResultVar;
+      ResultEl:=TPasFunctionType(DeclProc.ProcType).ResultEl;
+      TypeEl:=AContext.Resolver.ResolveAliasType(ResultEl.ResultType);
+      IsCOMIntf:=(TypeEl is TPasClassType)
+          and (TPasClassType(TypeEl).ObjKind=okInterface)
+          and (TPasClassType(TypeEl).InterfaceType=citCom);
       end;
       end;
-    end;
+    end
+  else
+    DeclProc:=nil;
+  FuncContext:=AContext.GetFunctionContext;
+
   Result:=TJSReturnStatement(CreateElement(TJSReturnStatement,El));
   Result:=TJSReturnStatement(CreateElement(TJSReturnStatement,El));
   if (El is TParamsExpr) and (length(TParamsExpr(El).Params)>0) then
   if (El is TParamsExpr) and (length(TParamsExpr(El).Params)>0) then
     begin
     begin
@@ -13451,10 +13467,6 @@ begin
     ResultIsRead:=false;
     ResultIsRead:=false;
     if (ResultVarName<>'') then
     if (ResultVarName<>'') then
       begin
       begin
-      DeclProc:=ImplProcScope.DeclarationProc;
-      if DeclProc=nil then
-        DeclProc:=ImplProc; // Note: references refer to ResultEl of DeclProc
-      ResultEl:=TPasFunctionType(DeclProc.ProcType).ResultEl;
       ParentEl:=El.Parent;
       ParentEl:=El.Parent;
       while (ParentEl<>ImplProc) do
       while (ParentEl<>ImplProc) do
         begin
         begin
@@ -13474,7 +13486,24 @@ begin
         end;
         end;
       end;
       end;
 
 
-    if ResultIsRead then
+    if IsCOMIntf then
+      begin
+      FuncContext.ResultNeedsIntfRelease:=true;
+      // create "Result = rtl.setIntfL(Result,param); return Result;"
+      Call:=CreateCallExpression(El);
+      Call.Expr:=CreateMemberExpression([GetBIName(pbivnRTL),GetBIName(pbifnIntfSetIntfL)]);
+      Call.AddArg(CreatePrimitiveDotExpr(ResultVarName,El));
+      Call.AddArg(ConvertExpression(TParamsExpr(El).Params[0],AContext));
+      AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
+      AssignSt.LHS:=CreatePrimitiveDotExpr(ResultVarName,El);
+      AssignSt.Expr:=Call;
+      TJSReturnStatement(Result).Expr:=CreatePrimitiveDotExpr(ResultVarName,El);
+      St:=TJSStatementList(CreateElement(TJSStatementList,El));
+      St.A:=AssignSt;
+      St.B:=Result;
+      Result:=St;
+      end
+    else if ResultIsRead then
       begin
       begin
       // create "Result = param; return Result;"
       // create "Result = param; return Result;"
       AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
       AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
@@ -13504,7 +13533,6 @@ begin
       ; // in a procedure, "return;" which means "return undefined;"
       ; // in a procedure, "return;" which means "return undefined;"
     end;
     end;
 
 
-  FuncContext:=AContext.GetFunctionContext;
   if (FuncContext<>nil) and FuncContext.ResultNeedsIntfRelease then
   if (FuncContext<>nil) and FuncContext.ResultNeedsIntfRelease then
     begin
     begin
     // add "$ok = true;"
     // add "$ok = true;"

+ 62 - 0
packages/pastojs/tests/tcmodules.pas

@@ -722,6 +722,7 @@ type
     Procedure TestClassInterface_COM_AssignArg;
     Procedure TestClassInterface_COM_AssignArg;
     Procedure TestClassInterface_COM_FunctionResult;
     Procedure TestClassInterface_COM_FunctionResult;
     Procedure TestClassInterface_COM_InheritedFuncResult;
     Procedure TestClassInterface_COM_InheritedFuncResult;
+    Procedure TestClassInterface_COM_FunctionExit;
     Procedure TestClassInterface_COM_IsAsTypeCasts;
     Procedure TestClassInterface_COM_IsAsTypeCasts;
     Procedure TestClassInterface_COM_PassAsArg;
     Procedure TestClassInterface_COM_PassAsArg;
     Procedure TestClassInterface_COM_PassToUntypedParam;
     Procedure TestClassInterface_COM_PassToUntypedParam;
@@ -22083,6 +22084,67 @@ begin
     '']));
     '']));
 end;
 end;
 
 
+procedure TTestModule.TestClassInterface_COM_FunctionExit;
+begin
+  StartProgram(false);
+  Add([
+  '{$interfaces com}',
+  'type',
+  '  IUnknown = interface',
+  '    function _AddRef: longint;',
+  '    function _Release: longint;',
+  '  end;',
+  '  TObject = class(IUnknown)',
+  '    function _AddRef: longint; virtual; abstract;',
+  '    function _Release: longint; virtual; abstract;',
+  '    constructor Create;',
+  '  end;',
+  'constructor TObject.Create;',
+  'begin',
+  'end;',
+  'function GetIntf: IUnknown;',
+  'var Intf: IUnknown;',
+  'begin',
+  '  Intf := TObject.Create;',
+  '  Exit(Intf);',
+  'end;',
+  'begin',
+  '']);
+  ConvertProgram;
+  CheckSource('TestClassInterface_COM_FunctionExit',
+    LinesToStr([ // statements
+    'rtl.createInterface(this, "IUnknown", "{D7ADB0E1-758A-322B-BDDF-21CD521DDFA9}", ["_AddRef", "_Release"], null);',
+    'rtl.createClass(this, "TObject", null, function () {',
+    '  this.$init = function () {',
+    '  };',
+    '  this.$final = function () {',
+    '  };',
+    '  this.Create = function () {',
+    '    return this;',
+    '  };',
+    '  rtl.addIntf(this, $mod.IUnknown);',
+    '});',
+    'this.GetIntf = function () {',
+    '  var Result = null;',
+    '  var Intf = null;',
+    '  var $ok = false;',
+    '  try {',
+    '    Intf = rtl.setIntfL(Intf, rtl.queryIntfT($mod.TObject.$create("Create"), $mod.IUnknown), true);',
+    '    $ok = true;',
+    '    Result = rtl.setIntfL(Result, Intf);',
+    '    return Result;',
+    '    $ok = true;',
+    '  } finally {',
+    '    rtl._Release(Intf);',
+    '    if (!$ok) rtl._Release(Result);',
+    '  };',
+    '  return Result;',
+    '};',
+    '']),
+    LinesToStr([ // $mod.$main
+    '']));
+end;
+
 procedure TTestModule.TestClassInterface_COM_IsAsTypeCasts;
 procedure TTestModule.TestClassInterface_COM_IsAsTypeCasts;
 begin
 begin
   StartProgram(false);
   StartProgram(false);