Browse Source

pastojs: precompiled proc: local var for generic function

git-svn-id: trunk@47273 -
(cherry picked from commit 5b4e028da46f7d19a743c286e796e273ba92198c)
Mattias Gaertner 4 years ago
parent
commit
8d19444fdc

+ 296 - 125
packages/pastojs/src/fppas2js.pp

@@ -1387,8 +1387,7 @@ type
     coRTLVersionCheckMain, // insert rtl version check into main
     coRTLVersionCheckMain, // insert rtl version check into main
     coRTLVersionCheckSystem, // insert rtl version check into system unit init
     coRTLVersionCheckSystem, // insert rtl version check into system unit init
     coRTLVersionCheckUnit, // insert rtl version check into every unit init
     coRTLVersionCheckUnit, // insert rtl version check into every unit init
-    coShortRefGlobals, // use short local variables for global identifiers
-    coShortRefGenFunc // create short local vars for generic methods
+    coShortRefGlobals // use short local variables for global identifiers
     );
     );
   TPasToJsConverterOptions = set of TPasToJsConverterOption;
   TPasToJsConverterOptions = set of TPasToJsConverterOption;
 const
 const
@@ -1544,6 +1543,7 @@ type
     procedure SpecializeGenericImpl(SpecializedItem: TPRSpecializedItem);
     procedure SpecializeGenericImpl(SpecializedItem: TPRSpecializedItem);
       override;
       override;
     function SpecializeParamsNeedDelay(SpecializedItem: TPRSpecializedItem): TPasElement; virtual;
     function SpecializeParamsNeedDelay(SpecializedItem: TPRSpecializedItem): TPasElement; virtual;
+    function IsSpecializedNonStaticMethod(ProcType: TPasProcedureType): boolean;
   protected
   protected
     const
     const
       cJSValueConversion = 2*cTypeConversion;
       cJSValueConversion = 2*cTypeConversion;
@@ -2041,6 +2041,7 @@ type
     Function CreateStaticProcPath(El: TPasProcedure; AContext: TConvertContext): string; virtual;
     Function CreateStaticProcPath(El: TPasProcedure; AContext: TConvertContext): string; virtual;
     Function CreateGlobalElPath(El: TPasElement; AContext: TConvertContext): string; virtual;
     Function CreateGlobalElPath(El: TPasElement; AContext: TConvertContext): string; virtual;
     Function GetLocalName(El: TPasElement; const Filter: TCtxVarKinds; AContext: TConvertContext): string;
     Function GetLocalName(El: TPasElement; const Filter: TCtxVarKinds; AContext: TConvertContext): string;
+    Function ProcCanHaveShortRef(Proc: TPasProcedure): boolean;
     Procedure StoreImplJSLocal(El: TPasElement; AContext: TConvertContext); virtual;
     Procedure StoreImplJSLocal(El: TPasElement; AContext: TConvertContext); virtual;
     Procedure StoreImplJSLocals(ModScope: TPas2JSModuleScope; IntfContext: TSectionContext); virtual;
     Procedure StoreImplJSLocals(ModScope: TPas2JSModuleScope; IntfContext: TSectionContext); virtual;
     Procedure RestoreImplJSLocals(ModScope: TPas2JSModuleScope; IntfContext: TSectionContext); virtual;
     Procedure RestoreImplJSLocals(ModScope: TPas2JSModuleScope; IntfContext: TSectionContext); virtual;
@@ -5250,6 +5251,25 @@ begin
     end;
     end;
 end;
 end;
 
 
+function TPas2JSResolver.IsSpecializedNonStaticMethod(
+  ProcType: TPasProcedureType): boolean;
+var
+  Proc: TPasProcedure;
+  Scope: TPas2JSProcedureScope;
+begin
+  if not (ProcType.Parent is TPasProcedure) then
+    exit(false); // not a method
+  Proc:=TPasProcedure(ProcType.Parent);
+  if Proc.IsStatic or Proc.IsExternal then
+    exit(false);
+  if not (Proc.Parent is TPasMembersType) then
+    exit(false); // not a method
+  Scope:=TPas2JSProcedureScope(Proc.CustomData);
+  if Scope.SpecializedFromItem=nil then
+    exit(false);
+  Result:=true;
+end;
+
 function TPas2JSResolver.AddJSBaseType(const aName: string; Typ: TPas2jsBaseType
 function TPas2JSResolver.AddJSBaseType(const aName: string; Typ: TPas2jsBaseType
   ): TResElDataPas2JSBaseType;
   ): TResElDataPas2JSBaseType;
 var
 var
@@ -8281,7 +8301,7 @@ end;
 function TPasToJSConverter.ConvertInlineSpecializeExpr(
 function TPasToJSConverter.ConvertInlineSpecializeExpr(
   El: TInlineSpecializeExpr; AContext: TConvertContext): TJSElement;
   El: TInlineSpecializeExpr; AContext: TConvertContext): TJSElement;
 begin
 begin
-  Result:=ConvertElement(El.NameExpr,AContext);
+  Result:=ConvertExpression(El.NameExpr,AContext);
 end;
 end;
 
 
 function TPasToJSConverter.GetExpressionValueType(El: TPasExpr;
 function TPasToJSConverter.GetExpressionValueType(El: TPasExpr;
@@ -9579,9 +9599,9 @@ begin
       exit(DotContext.JS);
       exit(DotContext.JS);
       end;
       end;
   finally
   finally
-    DotContext.Free;
-    if RightJS=nil then
+    if (RightJS=nil) and (DotContext.JSElement=LeftJS) then
       LeftJS.Free;
       LeftJS.Free;
+    DotContext.Free;
   end;
   end;
   if RightJS is TJSLiteral then
   if RightJS is TJSLiteral then
     begin
     begin
@@ -9756,6 +9776,7 @@ function TPasToJSConverter.ConvertIdentifierExpr(El: TPasExpr;
   const aName: string; AContext: TConvertContext): TJSElement;
   const aName: string; AContext: TConvertContext): TJSElement;
 var
 var
   AssignContext: TAssignContext;
   AssignContext: TAssignContext;
+  ApplyParam: TJSElement;
 
 
   procedure CallImplicit(Decl: TPasElement);
   procedure CallImplicit(Decl: TPasElement);
   var
   var
@@ -9787,6 +9808,13 @@ var
     Call:=nil;
     Call:=nil;
     try
     try
       CreateProcedureCall(Call,nil,ProcType,AContext);
       CreateProcedureCall(Call,nil,ProcType,AContext);
+      if ApplyParam<>nil then
+        begin
+        Call.InsertArg(0,ApplyParam);
+        ApplyParam:=nil;
+        if AContext is TDotContext then
+          TDotContext(AContext).JS:=Call;
+        end;
       Call.Expr:=Result;
       Call.Expr:=Result;
       if NeedIntfRef then
       if NeedIntfRef then
         // $ir.ref(id,fnname())
         // $ir.ref(id,fnname())
@@ -9794,10 +9822,40 @@ var
       Result:=Call;
       Result:=Call;
     finally
     finally
       if Result<>Call then
       if Result<>Call then
+        begin
         Call.Free;
         Call.Free;
+        ApplyParam.Free;
+        end;
     end;
     end;
   end;
   end;
 
 
+  function CreateShortRefImplictCall_Apply(TargetProc: TPasProcedure;
+    Ref: TResolvedReference): string;
+  var
+    ApplyPath: String;
+  begin
+    // ProcName; -> "$lp.apply(this,args);"  or  "$lp.apply($with,args);"
+    Result:=CreateStaticProcPath(TargetProc,AContext)+'.apply';
+
+    ApplyPath:=CreateReferencePath(TargetProc,AContext,rpkPath,false,Ref);
+    if AContext is TDotContext then
+      begin
+      ApplyParam:=AContext.JSElement;
+      AContext.JSElement:=nil;
+      if ApplyPath<>'' then
+        // e.g. "$class"
+        ApplyParam:=CreateDotNameExpr(El,ApplyParam,TJSString(ApplyPath));
+      end
+    else
+      begin
+      if ApplyPath='' then
+        RaiseNotSupported(El,AContext,20201101022637);
+      ApplyParam:=CreatePrimitiveDotExpr(ApplyPath,El);
+      end;
+    if ApplyParam=nil then
+      RaiseNotSupported(El,AContext,20201101021136);
+  end;
+
   procedure CallTypeSetter;
   procedure CallTypeSetter;
   var
   var
     Call: TJSCallExpression;
     Call: TJSCallExpression;
@@ -9888,6 +9946,7 @@ begin
 
 
   Prop:=nil;
   Prop:=nil;
   AssignContext:=nil;
   AssignContext:=nil;
+  ApplyParam:=nil;
   IsImplicitCall:=rrfImplicitCallWithoutParams in Ref.Flags;
   IsImplicitCall:=rrfImplicitCallWithoutParams in Ref.Flags;
   if AContext.Access=caAssign then
   if AContext.Access=caAssign then
     AssignContext:=AContext.AccessContext as TAssignContext;
     AssignContext:=AContext.AccessContext as TAssignContext;
@@ -10018,61 +10077,74 @@ begin
   //  end;
   //  end;
   {$ENDIF}
   {$ENDIF}
 
 
-  if Decl is TPasModule then
-    Name:=TransformModuleName(TPasModule(Decl),true,AContext)
-  else if (Decl is TPasResultElement) then
-    begin
-    Name:=ResolverResultVar;
-    Proc:=Decl.Parent.Parent as TPasProcedure;
-    FuncScope:=Proc.CustomData as TPas2JSProcedureScope;
-    if FuncScope.ImplProc<>nil then
-      FuncScope:=FuncScope.ImplProc.CustomData as TPas2JSProcedureScope;
-    if FuncScope.ResultVarName<>'' then
-      Name:=FuncScope.ResultVarName;
-    end
-  else if Decl.ClassType=TPasEnumValue then
-    begin
-    if UseEnumNumbers then
+  try
+    if Decl is TPasModule then
+      Name:=TransformModuleName(TPasModule(Decl),true,AContext)
+    else if (Decl is TPasResultElement) then
+      begin
+      Name:=ResolverResultVar;
+      Proc:=Decl.Parent.Parent as TPasProcedure;
+      FuncScope:=Proc.CustomData as TPas2JSProcedureScope;
+      if FuncScope.ImplProc<>nil then
+        FuncScope:=FuncScope.ImplProc.CustomData as TPas2JSProcedureScope;
+      if FuncScope.ResultVarName<>'' then
+        Name:=FuncScope.ResultVarName;
+      end
+    else if Decl.ClassType=TPasEnumValue then
       begin
       begin
-      Result:=CreateLiteralNumber(El,(Decl.Parent as TPasEnumType).Values.IndexOf(Decl));
-      exit;
+      if UseEnumNumbers then
+        begin
+        Result:=CreateLiteralNumber(El,(Decl.Parent as TPasEnumType).Values.IndexOf(Decl));
+        exit;
+        end
+      else
+        begin
+        // enums always need the full path
+        Name:=CreateReferencePath(Decl,AContext,rpkPathAndName,true);
+        end;
+      end
+    else if Decl.ClassType=TPasArgument then
+      Name:=TransformArgName(TPasArgument(Decl),AContext)
+    else if Decl is TPasProcedure then
+      begin
+      Proc:=TPasProcedure(Decl);
+      if (coShortRefGlobals in Options)
+          and aResolver.IsSpecializedNonStaticMethod(Proc.ProcType) then
+        Name:=CreateShortRefImplictCall_Apply(Proc,Ref)
+      else
+        Name:=CreateReferencePath(Decl,AContext,rpkPathAndName,false,Ref);
       end
       end
     else
     else
-      begin
-      // enums always need the full path
-      Name:=CreateReferencePath(Decl,AContext,rpkPathAndName,true);
-      end;
-    end
-  else if Decl.ClassType=TPasArgument then
-    Name:=TransformArgName(TPasArgument(Decl),AContext)
-  else
-    Name:=CreateReferencePath(Decl,AContext,rpkPathAndName,false,Ref);
-  if Name='' then
-    RaiseNotSupported(El,AContext,20180509134804,GetObjName(Decl));
+      Name:=CreateReferencePath(Decl,AContext,rpkPathAndName,false,Ref);
+    if Name='' then
+      RaiseNotSupported(El,AContext,20180509134804,GetObjName(Decl));
 
 
-  if Result=nil then
-    begin
-    if (Name[1]='[') and (Name[length(Name)]=']')
-        and (AContext is TDotContext)
-        and (AContext.JSElement<>nil) then
+    if Result=nil then
       begin
       begin
-      // e.g. Obj.A  with A having an external name '["name"]';
-      // -> Obj["name"]
-      if IsImplicitCall then
-        RaiseNotSupported(El,AContext,20180509134951,Name);
-      BracketExpr:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
-      TDotContext(AContext).JS:=BracketExpr;
-      BracketExpr.MExpr:=AContext.JSElement;
-      Result:=CreateLiteralCustomValue(El,TJSString(copy(Name,2,length(Name)-2)));
-      BracketExpr.Name:=Result;
-      exit;
+      if (Name[1]='[') and (Name[length(Name)]=']')
+          and (AContext is TDotContext)
+          and (AContext.JSElement<>nil) then
+        begin
+        // e.g. Obj.A  with A having an external name '["name"]';
+        // -> Obj["name"]
+        if IsImplicitCall then
+          RaiseNotSupported(El,AContext,20180509134951,Name);
+        BracketExpr:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
+        TDotContext(AContext).JS:=BracketExpr;
+        BracketExpr.MExpr:=AContext.JSElement;
+        Result:=CreateLiteralCustomValue(El,TJSString(copy(Name,2,length(Name)-2)));
+        BracketExpr.Name:=Result;
+        exit;
+        end;
+      Result:=CreatePrimitiveDotExpr(Name,El);
       end;
       end;
-    Result:=CreatePrimitiveDotExpr(Name,El);
-    end;
 
 
-  if IsImplicitCall then
-    CallImplicit(Decl);
-  CallTypeSetter;
+    if IsImplicitCall then
+      CallImplicit(Decl);
+    CallTypeSetter;
+  finally
+    ApplyParam.Free;
+  end;
 end;
 end;
 
 
 function TPasToJSConverter.ConvertBoolConstExpression(El: TBoolConstExpr;
 function TPasToJSConverter.ConvertBoolConstExpression(El: TBoolConstExpr;
@@ -11270,10 +11342,43 @@ var
       Elements:=Call.Args.Elements;
       Elements:=Call.Args.Elements;
   end;
   end;
 
 
+  procedure CreateShortRefApply(Value: TPasExpr; TargetProcType: TPasProcedureType);
+  var
+    TargetProc: TPasProcedure;
+    aName: String;
+    LeftJS: TJSElement;
+    Ref: TResolvedReference;
+  begin
+    // create  "$lp.apply(LeftJS,args);"
+    TargetProc:=TPasProcedure(TargetProcType.Parent);
+    aName:=CreateStaticProcPath(TargetProc,AContext);
+    Call.Expr:=CreatePrimitiveDotExpr(aName+'.apply',Value);
+    if DotBin<>nil then
+      begin
+      // a.b() -> "$lp.apply(a,args);"
+      LeftJS:=ConvertExpression(DotBin.left,AContext);
+      if LeftJS=nil then
+        RaiseNotSupported(DotBin,AContext,20201030235816);
+      end
+    else if Value.CustomData is TResolvedReference then
+      begin
+      // a() -> "$lp.apply(this,args);"  or  "$lp.apply($with,args);"
+      Ref:=TResolvedReference(Value.CustomData);
+      aName:=CreateReferencePath(TargetProc,AContext,rpkPath,false,Ref);
+      LeftJS:=CreatePrimitiveDotExpr(aName,Value);
+      if LeftJS=nil then
+        RaiseNotSupported(DotBin,AContext,20201031003202);
+      end
+    else
+      RaiseNotSupported(DotBin,AContext,202010310032046);
+    Elements.AddElement.Expr:=LeftJS;
+  end;
+
 var
 var
   Decl: TPasElement;
   Decl: TPasElement;
   Ref: TResolvedReference;
   Ref: TResolvedReference;
   BuiltInProc: TResElDataBuiltInProc;
   BuiltInProc: TResElDataBuiltInProc;
+  TargetProc: TPasProcedure;
   TargetProcType: TPasProcedureType;
   TargetProcType: TPasProcedureType;
   JsArrLit: TJSArrayLiteral;
   JsArrLit: TJSArrayLiteral;
   OldAccess: TCtxAccess;
   OldAccess: TCtxAccess;
@@ -11416,15 +11521,16 @@ begin
       end
       end
     else if C.InheritsFrom(TPasProcedure) then
     else if C.InheritsFrom(TPasProcedure) then
       begin
       begin
-      if aResolver.IsHelperMethod(Decl) then
+      TargetProc:=TPasProcedure(Decl);
+      if aResolver.IsHelperMethod(TargetProc) then
         begin
         begin
         // calling a helper method
         // calling a helper method
-        Result:=CreateCallHelperMethod(TPasProcedure(Decl),El.Value,AContext);
+        Result:=CreateCallHelperMethod(TargetProc,El.Value,AContext);
         exit;
         exit;
         end;
         end;
-      TargetProcType:=TPasProcedure(Decl).ProcType;
-      if aResolver.IsExternalBracketAccessor(Decl) then
+      if aResolver.IsExternalBracketAccessor(TargetProc) then
         exit(CreateExternalBracketAccessorCall(El,AContext));
         exit(CreateExternalBracketAccessorCall(El,AContext));
+      TargetProcType:=TargetProc.ProcType;
       end
       end
     else if (C=TPasClassType)
     else if (C=TPasClassType)
         or (C=TPasClassOfType)
         or (C=TPasClassOfType)
@@ -11677,15 +11783,6 @@ begin
   OldAccess:=AContext.Access;
   OldAccess:=AContext.Access;
   try
   try
     AContext.Access:=caRead;
     AContext.Access:=caRead;
-    if Call.Expr=nil then
-      begin
-      if DotBin<>nil then
-        Call.Expr:=ConvertSubIdentExprCustom(DotBin,AContext)
-      else
-        Call.Expr:=ConvertExpression(El.Value,AContext);
-      end;
-    //if Call.Expr is TPrimitiveExpr then
-    //  writeln('TPasToJSConverter.ConvertFuncParams ',TPrimitiveExpr(Call.Expr).GetDeclaration(true));
     if Call.Args=nil then
     if Call.Args=nil then
       begin
       begin
       // append ()
       // append ()
@@ -11694,12 +11791,26 @@ begin
       end
       end
     else if Elements=nil then
     else if Elements=nil then
       RaiseInconsistency(20180720154413,El);
       RaiseInconsistency(20180720154413,El);
+
+    if Call.Expr=nil then
+      begin
+      if (coShortRefGlobals in Options)
+          and aResolver.IsSpecializedNonStaticMethod(TargetProcType) then
+        CreateShortRefApply(Value,TargetProcType)
+      else if DotBin<>nil then
+        Call.Expr:=ConvertSubIdentExprCustom(DotBin,AContext)
+      else
+        Call.Expr:=ConvertExpression(Value,AContext);
+      end;
+    //if Call.Expr is TPrimitiveExpr then
+    //  writeln('TPasToJSConverter.ConvertFuncParams ',TPrimitiveExpr(Call.Expr).GetDeclaration(true));
     CreateProcedureCallArgs(Elements,El,TargetProcType,AContext);
     CreateProcedureCallArgs(Elements,El,TargetProcType,AContext);
     CallArgs:=Call.Args;
     CallArgs:=Call.Args;
+
     if (Elements.Count=0)
     if (Elements.Count=0)
-        and (CallArgs.Elements.Count>0)
-        then
+        and (CallArgs.Elements.Count>0) then
       begin
       begin
+      // for example: rrfNewInstance
       LastArg:=CallArgs.Elements[CallArgs.Elements.Count-1];
       LastArg:=CallArgs.Elements[CallArgs.Elements.Count-1];
       if not (LastArg.Expr is TJSArrayLiteral) then
       if not (LastArg.Expr is TJSArrayLiteral) then
         RaiseNotSupported(El,AContext,20180720161317);
         RaiseNotSupported(El,AContext,20180720161317);
@@ -11708,6 +11819,7 @@ begin
         RaiseNotSupported(El,AContext,20180720161324);
         RaiseNotSupported(El,AContext,20180720161324);
       LastArg.Free;
       LastArg.Free;
       end;
       end;
+
     if CallArgs.Elements.Count=0 then
     if CallArgs.Elements.Count=0 then
       begin
       begin
       CallArgs.Free;
       CallArgs.Free;
@@ -14881,20 +14993,45 @@ Var
     i: Integer;
     i: Integer;
     P: TPasElement;
     P: TPasElement;
     C: TClass;
     C: TClass;
+    Proc: TPasProcedure;
+    aResolver: TPas2JSResolver;
   begin
   begin
+    aResolver:=AContext.Resolver;
     For i:=0 to Decls.Count-1 do
     For i:=0 to Decls.Count-1 do
       begin
       begin
       P:=TPasElement(Decls[i]);
       P:=TPasElement(Decls[i]);
       if not IsElementUsed(P) then continue;
       if not IsElementUsed(P) then continue;
       C:=P.ClassType;
       C:=P.ClassType;
-      if (C=TPasClassType) and TPasClassType(P).IsForward then
-        continue;
       if (C=TPasClassType) or (C=TPasRecordType) or (C=TPasEnumType) then
       if (C=TPasClassType) or (C=TPasRecordType) or (C=TPasEnumType) then
         begin
         begin
+        if (C=TPasClassType) then
+          begin
+          if TPasClassType(P).IsForward then
+            continue;
+          if not aResolver.IsFullySpecialized(TPasClassType(P)) then
+            continue;
+          end
+        else if C=TPasRecordType then
+          begin
+          if not aResolver.IsFullySpecialized(TPasRecordType(P)) then
+            continue;
+          end;
         // add var $lt = null;
         // add var $lt = null;
         CreateGlobalAliasNull(P,pbivnLocalTypeRef,SectionContext);
         CreateGlobalAliasNull(P,pbivnLocalTypeRef,SectionContext);
         if (C=TPasClassType) or (C=TPasRecordType) then
         if (C=TPasClassType) or (C=TPasRecordType) then
           InitForwards(TPasMembersType(P).Members,SectionContext);
           InitForwards(TPasMembersType(P).Members,SectionContext);
+        end
+      else if C.InheritsFrom(TPasProcedure) then
+        begin
+        Proc:=TPasProcedure(P);
+        if Proc.IsForward or Proc.IsAbstract or Proc.IsExternal then
+          continue;
+        if TPas2JSProcedureScope(Proc.CustomData).SpecializedFromItem=nil then
+          continue;
+        if not aResolver.IsFullySpecialized(Proc) then
+          continue; // skip non specialized generics
+        // specialized proc: add var $lp = null;
+        CreateGlobalAliasNull(P,pbivnLocalProcRef,SectionContext);
         end;
         end;
       end;
       end;
   end;
   end;
@@ -16387,7 +16524,7 @@ Var
   FS : TJSFunctionDeclarationStatement;
   FS : TJSFunctionDeclarationStatement;
   FD : TJSFuncDef;
   FD : TJSFuncDef;
   n, i, Line, Col:Integer;
   n, i, Line, Col:Integer;
-  AssignSt: TJSSimpleAssignStatement;
+  AssignSt, AssignSt2: TJSSimpleAssignStatement;
   FuncContext, ConstContext: TFunctionContext;
   FuncContext, ConstContext: TFunctionContext;
   ProcScope, ImplProcScope: TPas2JSProcedureScope;
   ProcScope, ImplProcScope: TPas2JSProcedureScope;
   Arg, SelfArg: TPasArgument;
   Arg, SelfArg: TPasArgument;
@@ -16396,7 +16533,7 @@ Var
   BodyPas: TProcedureBody;
   BodyPas: TProcedureBody;
   PosEl, ThisPas: TPasElement;
   PosEl, ThisPas: TPasElement;
   Call: TJSCallExpression;
   Call: TJSCallExpression;
-  ClassPath: String;
+  ClassPath, aName: String;
   ArgResolved: TPasResolverResult;
   ArgResolved: TPasResolverResult;
   Lit: TJSLiteral;
   Lit: TJSLiteral;
   ConstSrcElems: TJSSourceElements;
   ConstSrcElems: TJSSourceElements;
@@ -16476,6 +16613,19 @@ begin
     AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,ImplProc));
     AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,ImplProc));
     Result:=AssignSt;
     Result:=AssignSt;
     AssignSt.LHS:=CreateSubDeclNameExpr(El,AContext,ImplProc);
     AssignSt.LHS:=CreateSubDeclNameExpr(El,AContext,ImplProc);
+
+    if (coShortRefGlobals in Options) then
+      begin
+      aName:=AContext.GetLocalName(El,[cvkGlobal]);
+      if aName<>'' then
+        begin
+        // this.FuncName = $lp = ...;
+        AssignSt2:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,ImplProc));
+        AssignSt.Expr:=AssignSt2;
+        AssignSt:=AssignSt2;
+        AssignSt.LHS:=CreatePrimitiveDotExpr(aName,El);
+        end;
+      end;
     end;
     end;
 
 
   FS:=CreateFunctionSt(ImplProc,ImplProc.Body<>nil);
   FS:=CreateFunctionSt(ImplProc,ImplProc.Body<>nil);
@@ -24070,16 +24220,6 @@ var
     Result:=(C=TPasFunction) or (C=TPasProcedure) or (C=TPasConstructor) or (C=TPasDestructor);
     Result:=(C=TPasFunction) or (C=TPasProcedure) or (C=TPasConstructor) or (C=TPasDestructor);
   end;
   end;
 
 
-  function ProcSelfIsClassType(Proc: TPasElement): boolean;
-  var
-    C: TClass;
-  begin
-    if Proc=nil then exit(false);
-    C:=Proc.ClassType;
-    Result:=((C=TPasClassFunction) or (C=TPasClassProcedure) or (C=TPasClassOperator))
-         and not TPasProcedure(Proc).IsStatic;
-  end;
-
   function ProcHasNoSelf(Proc: TPasProcedure): boolean;
   function ProcHasNoSelf(Proc: TPasProcedure): boolean;
   begin
   begin
     if Proc=nil then exit(false);
     if Proc=nil then exit(false);
@@ -24164,6 +24304,43 @@ var
     Result:=false;
     Result:=false;
   end;
   end;
 
 
+  function ShortRefGlobal: boolean;
+  var
+    ElClass: TClass;
+    Proc: TPasProcedure;
+  begin
+    ElClass:=El.ClassType;
+    if ElClass.InheritsFrom(TPasType) then
+      begin
+      if El.Parent.ClassType=TProcedureBody then
+        exit(false);
+      CreateReferencePath:=CreateGlobalTypePath(TPasType(El),AContext);
+      exit(true);
+      end
+    else if ElClass.InheritsFrom(TPasProcedure) then
+      begin
+      Proc:=TPasProcedure(El);
+      if ProcCanHaveShortRef(Proc) then
+        begin
+        if aResolver.ProcHasSelf(Proc) then
+          begin
+          {$IFDEF VerbosePas2JS}
+          writeln('TPasToJSConverter.CreateReferencePath El=',GetObjName(El),' Parent=',GetObjName(El.Parent),' Kind=',Kind,' Context=',GetObjName(AContext),' SelfContext=',GetObjName(AContext.GetSelfContext));
+          {$ENDIF}
+          aResolver.RaiseNotYetImplemented(20201030233511,El);
+          end;
+        CreateReferencePath:=CreateStaticProcPath(Proc,AContext);
+        exit(true);
+        end;
+      end
+    else if (ElClass=TPasEnumValue) then
+      begin
+      CreateReferencePath:=CreateGlobalElPath(El,AContext);
+      exit(true);
+      end;
+    Result:=false;
+  end;
+
 var
 var
   FoundModule: TPasModule;
   FoundModule: TPasModule;
   ParentEl, CurEl: TPasElement;
   ParentEl, CurEl: TPasElement;
@@ -24206,7 +24383,7 @@ begin
           Append_GetClass(El);
           Append_GetClass(El);
           end;
           end;
         end
         end
-      else if ProcSelfIsClassType(El)
+      else if aResolver.IsMethod_SelfIsClass(El)
           and aResolver.ResolvedElIsClassOrRecordInstance(Dot.LeftResolved) then
           and aResolver.ResolvedElIsClassOrRecordInstance(Dot.LeftResolved) then
         // accessing a class method from an object, 'this' must be the class/record
         // accessing a class method from an object, 'this' must be the class/record
         Append_GetClass(El);
         Append_GetClass(El);
@@ -24251,7 +24428,7 @@ begin
       RaiseNotSupported(WithData.Expr,AContext,20190209092506,GetObjName(El));
       RaiseNotSupported(WithData.Expr,AContext,20190209092506,GetObjName(El));
     Prepend(Result,WithData.WithVarName);
     Prepend(Result,WithData.WithVarName);
     if not (wesfOnlyTypeMembers in WithData.Flags)
     if not (wesfOnlyTypeMembers in WithData.Flags)
-        and ProcSelfIsClassType(El) then
+        and aResolver.IsMethod_SelfIsClass(El) then
       begin
       begin
       // with Obj do NonStaticClassMethod -> append .$class
       // with Obj do NonStaticClassMethod -> append .$class
       Append_GetClass(El);
       Append_GetClass(El);
@@ -24267,22 +24444,7 @@ begin
 
 
     if (coShortRefGlobals in Options) and (Kind=rpkPathAndName) then
     if (coShortRefGlobals in Options) and (Kind=rpkPathAndName) then
       begin
       begin
-      ElClass:=El.ClassType;
-      if ElClass.InheritsFrom(TPasType) then
-        begin
-        Result:=CreateGlobalTypePath(TPasType(El),AContext);
-        exit;
-        end
-      else if ElClass.InheritsFrom(TPasProcedure) and ProcHasNoSelf(TPasProcedure(El)) then
-        begin
-        Result:=CreateStaticProcPath(TPasProcedure(El),AContext);
-        exit;
-        end
-      else if (ElClass=TPasEnumValue) then
-        begin
-        Result:=CreateGlobalElPath(El,AContext);
-        exit;
-        end;
+      if ShortRefGlobal then exit;
       end;
       end;
 
 
     El:=ImplToDecl(El);
     El:=ImplToDecl(El);
@@ -24373,7 +24535,7 @@ begin
             if ProcSelfIsInstance(SelfContext.PasElement) then
             if ProcSelfIsInstance(SelfContext.PasElement) then
               begin
               begin
               // inside a method -> Self is a class instance
               // inside a method -> Self is a class instance
-              if ProcSelfIsClassType(El) then
+              if aResolver.IsMethod_SelfIsClass(El) then
                 Append_GetClass(El); // accessing a class function -> this.$class.procname
                 Append_GetClass(El); // accessing a class function -> this.$class.procname
               end;
               end;
             Prepend(Result,ShortName);
             Prepend(Result,ShortName);
@@ -24444,6 +24606,8 @@ begin
     if Result<>'' then Result:=Result+'.';
     if Result<>'' then Result:=Result+'.';
   rpkPathAndName:
   rpkPathAndName:
     begin
     begin
+    if (coShortRefGlobals in Options) then
+      if ShortRefGlobal then exit;
     ShortName:=TransformElToJSName(El,AContext);
     ShortName:=TransformElToJSName(El,AContext);
     if Result='' then
     if Result='' then
       Result:=ShortName
       Result:=ShortName
@@ -24485,9 +24649,14 @@ end;
 function TPasToJSConverter.CreateStaticProcPath(El: TPasProcedure;
 function TPasToJSConverter.CreateStaticProcPath(El: TPasProcedure;
   AContext: TConvertContext): string;
   AContext: TConvertContext): string;
 begin
 begin
-  if (not El.IsStatic) and (El.Parent is TPasMembersType) then
+  if El.IsAbstract or El.IsExternal then
+    RaiseNotSupported(El,AContext,20201101185117)
+  else if El.IsStatic
+      or (El.Parent is TPasSection)
+      or (TPas2JSProcedureScope(El.CustomData).SpecializedFromItem<>nil) then
+    Result:=CreateGlobalElPath(El,AContext)
+  else
     RaiseNotSupported(El,AContext,20200925104007);
     RaiseNotSupported(El,AContext,20200925104007);
-  Result:=CreateGlobalElPath(El,AContext);
 end;
 end;
 
 
 function TPasToJSConverter.CreateGlobalElPath(El: TPasElement;
 function TPasToJSConverter.CreateGlobalElPath(El: TPasElement;
@@ -24571,6 +24740,28 @@ begin
   Result:=AContext.GetLocalName(El,Filter);
   Result:=AContext.GetLocalName(El,Filter);
 end;
 end;
 
 
+function TPasToJSConverter.ProcCanHaveShortRef(Proc: TPasProcedure): boolean;
+var
+  C: TClass;
+begin
+  // can not:
+  if Proc.IsExternal or Proc.IsVirtual then
+    exit(false);
+  C:=Proc.Parent.ClassType;
+  if C=TProcedureBody then
+    exit(false);
+
+  // can:
+  if C.InheritsFrom(TPasSection) then
+    exit(true);
+  if Proc.IsStatic then
+    exit(true);
+  if TPas2JSProcedureScope(Proc.CustomData).SpecializedFromItem<>nil then
+    exit(true);
+
+  Result:=false;
+end;
+
 procedure TPasToJSConverter.StoreImplJSLocal(El: TPasElement;
 procedure TPasToJSConverter.StoreImplJSLocal(El: TPasElement;
   AContext: TConvertContext);
   AContext: TConvertContext);
 var
 var
@@ -26511,8 +26702,6 @@ end;
 function TPasToJSConverter.ElNeedsGlobalAlias(El: TPasElement): boolean;
 function TPasToJSConverter.ElNeedsGlobalAlias(El: TPasElement): boolean;
 var
 var
   C: TClass;
   C: TClass;
-  Proc: TPasProcedure;
-  ProcScope: TPas2JSProcedureScope;
 begin
 begin
   Result:=false;
   Result:=false;
   if El=nil then exit;
   if El=nil then exit;
@@ -26523,26 +26712,8 @@ begin
     exit(false)
     exit(false)
   else if C.InheritsFrom(TPasType) then
   else if C.InheritsFrom(TPasType) then
     exit(true)
     exit(true)
-  else if (C=TPasConstructor)
-      or (C=TPasDestructor)
-      or (C=TPasClassConstructor)
-      or (C=TPasClassDestructor)
-      or (C=TPasClassProcedure)
-      or (C=TPasClassOperator)
-      or (C=TPasClassFunction) then
-    exit(true)
-  else if (C=TPasProcedure) or (C=TPasFunction) or (C=TPasOperator) then
-    begin
-    Proc:=TPasProcedure(El);
-    if Proc.IsStatic or (Proc.Parent is TPasSection) then
-      exit(true);
-    if coShortRefGenFunc in Options then
-      begin
-      ProcScope:=TPas2JSProcedureScope(Proc.CustomData);
-      if ProcScope.SpecializedFromItem<>nil then
-        exit(true);
-      end;
-    end
+  else if C.InheritsFrom(TPasProcedure) then
+    exit(ProcCanHaveShortRef(TPasProcedure(El)))
   else if C=TPasEnumValue then
   else if C=TPasEnumValue then
     begin
     begin
     if not (coEnumNumbers in Options) then
     if not (coEnumNumbers in Options) then

+ 2 - 3
packages/pastojs/src/pas2jsfiler.pp

@@ -243,7 +243,7 @@ const
     );
     );
 
 
   PCUDefaultConverterOptions: TPasToJsConverterOptions =
   PCUDefaultConverterOptions: TPasToJsConverterOptions =
-    [coUseStrict,coStoreImplJS,coShortRefGlobals,coShortRefGenFunc];
+    [coUseStrict,coStoreImplJS,coShortRefGlobals];
   PCUConverterOptions: array[TPasToJsConverterOption] of string = (
   PCUConverterOptions: array[TPasToJsConverterOption] of string = (
     'LowerCase',
     'LowerCase',
     'SwitchStatement',
     'SwitchStatement',
@@ -255,8 +255,7 @@ const
     'RTLVersionCheckMain',
     'RTLVersionCheckMain',
     'RTLVersionCheckSystem',
     'RTLVersionCheckSystem',
     'RTLVersionCheckUnit',
     'RTLVersionCheckUnit',
-    'ShortRefGlobals',
-    'ShortRefGenFuncs'
+    'ShortRefGlobals'
     );
     );
 
 
   PCUDefaultTargetPlatform = PlatformBrowser;
   PCUDefaultTargetPlatform = PlatformBrowser;

+ 1 - 1
packages/pastojs/tests/tcfiler.pas

@@ -422,7 +422,7 @@ var
 begin
 begin
   InitialParserOptions:=Parser.Options;
   InitialParserOptions:=Parser.Options;
   Analyzer.Options:=Analyzer.Options+[paoSkipGenericProc];
   Analyzer.Options:=Analyzer.Options+[paoSkipGenericProc];
-  Converter.Options:=Converter.Options+[coShortRefGlobals,coShortRefGenFunc];
+  Converter.Options:=Converter.Options+[coShortRefGlobals];
   ConvertUnit;
   ConvertUnit;
 
 
   FPCUWriter:=TPCUWriter.Create;
   FPCUWriter:=TPCUWriter.Create;

+ 120 - 0
packages/pastojs/tests/tcoptimizations.pas

@@ -60,7 +60,15 @@ type
     procedure TestOptShortRefGlobals_Program;
     procedure TestOptShortRefGlobals_Program;
     procedure TestOptShortRefGlobals_Unit_FromIntfImpl_ToIntfImpl;
     procedure TestOptShortRefGlobals_Unit_FromIntfImpl_ToIntfImpl;
     procedure TestOptShortRefGlobals_Property;
     procedure TestOptShortRefGlobals_Property;
+    // ToDo: ShortRefGlobals_ExternalAndAbstract ObjFPC+Delphi
     procedure TestOptShortRefGlobals_GenericFunction;
     procedure TestOptShortRefGlobals_GenericFunction;
+    procedure TestOptShortRefGlobals_GenericMethod_Call_ObjFPC;
+    // ToDo: procedure TestOptShortRefGlobals_GenericMethod_Call_Delphi;
+    // ToDo: GenericStaticMethod_Call ObjFPC+Delphi
+    // ToDo: GenericMethod_CallInherited ObjFPC+Delphi
+    // ToDo: GenericMethod_External ObjFPC+Delphi
+    // ToDo: procedure TestOptShortRefGlobals_GenericHelperMethod_Call_Delphi;
+    // ToDo: proc var
     procedure TestOptShortRefGlobals_SameUnit_EnumType;
     procedure TestOptShortRefGlobals_SameUnit_EnumType;
     procedure TestOptShortRefGlobals_SameUnit_ClassType;
     procedure TestOptShortRefGlobals_SameUnit_ClassType;
     procedure TestOptShortRefGlobals_SameUnit_RecordType;
     procedure TestOptShortRefGlobals_SameUnit_RecordType;
@@ -503,6 +511,118 @@ begin
     '']));
     '']));
 end;
 end;
 
 
+procedure TTestOptimizations.TestOptShortRefGlobals_GenericMethod_Call_ObjFPC;
+begin
+  AddModuleWithIntfImplSrc('UnitA.pas',
+  LinesToStr([
+    'type',
+    '  TBird = class',
+    '    generic function Fly<T>(a: word = 13): T;',
+    '    generic class function Jump<T>(b: word = 14): T;',
+    '  end;',
+    '']),
+  LinesToStr([
+    'generic function TBird.Fly<T>(a: word): T;',
+    'begin',
+    'end;',
+    'generic class function TBird.Jump<T>(b: word): T;',
+    'begin',
+    'end;',
+    '']));
+  StartUnit(true,[supTObject]);
+  Add([
+  '{$optimization JSShortRefGlobals}',
+  'interface',
+  'uses unita;',
+  'type',
+  '  TEagle = class(TBird)',
+  '    procedure Test;',
+  '    generic function Run<T>(c: word = 25): T;',
+  '    generic class function Sing<T>(d: word = 26): T;',
+  '  end;',
+  'implementation',
+  'procedure TEagle.Test;',
+  'begin',
+  '  specialize Run<Word>;',
+  '  specialize Run<Word>(1);',
+  '  specialize Sing<Word>;',
+  '  specialize Sing<Word>(2);',
+  '  specialize Fly<Word>;',
+  '  specialize Fly<Word>(3);',
+  '  specialize Jump<Word>;',
+  '  specialize Jump<Word>(4);',
+  '  Self.specialize Fly<Word>;',
+  '  Self.specialize Fly<Word>(5);',
+  '  Self.specialize Jump<Word>;',
+  '  Self.specialize Jump<Word>(6);',
+  '  with Self do begin',
+  '    specialize Fly<Word>;',
+  '    specialize Fly<Word>(7);',
+  '    specialize Jump<Word>;',
+  '    specialize Jump<Word>(8);',
+  '  end;',
+  'end;',
+  'generic function TEagle.Run<T>(c: word): T;',
+  'begin',
+  '  specialize Fly<T>;',
+  '  specialize Fly<T>(7);',
+  'end;',
+  'generic class function TEagle.Sing<T>(d: word): T;',
+  'begin',
+  '  specialize Jump<T>;',
+  '  specialize Jump<T>(8);',
+  'end;',
+  '']);
+  ConvertUnit;
+  CheckSource('TestOptShortRefGlobals_GenericMethod_Call',
+    LinesToStr([
+    'var $lt = null;',
+    'var $lp = null;',
+    'var $lp1 = null;',
+    'var $lm = pas.UnitA;',
+    'var $lt1 = $lm.TBird;',
+    'var $lp2 = $lt1.Fly$G1;',
+    'var $lp3 = $lt1.Jump$G1;',
+    'rtl.createClass(this, "TEagle", $lt1, function () {',
+    '  $lt = this;',
+    '  this.Test = function () {',
+    '    $lp.apply(this, 25);',
+    '    $lp.apply(this, 1);',
+    '    $lp1.apply(this.$class, 26);',
+    '    $lp1.apply(this.$class, 2);',
+    '    $lp2.apply(this, 13);',
+    '    $lp2.apply(this, 3);',
+    '    $lp3.apply(this.$class, 14);',
+    '    $lp3.apply(this.$class, 4);',
+    '    $lp2.apply(this, 13);',
+    '    $lp2.apply(this, 5);',
+    '    $lp3.apply(this.$class, 14);',
+    '    $lp3.apply(this, 6);',
+    '    $lp2.apply(this, 13);',
+    '    $lp2.apply(this, 7);',
+    '    $lp3.apply(this.$class, 14);',
+    '    $lp3.apply(this.$class, 8);',
+    '  };',
+    '  this.Run$G1 = $lp = function (c) {',
+    '    var Result = 0;',
+    '    $lp2.apply(this, 13);',
+    '    $lp2.apply(this, 7);',
+    '    return Result;',
+    '  };',
+    '  this.Sing$G1 = $lp1 = function (d) {',
+    '    var Result = 0;',
+    '    $lp3.apply(this, 14);',
+    '    $lp3.apply(this, 8);',
+    '    return Result;',
+    '  };',
+    '});',
+    '']),
+    LinesToStr([
+    '']),
+    LinesToStr([
+    '']));
+end;
+
 procedure TTestOptimizations.TestOptShortRefGlobals_SameUnit_EnumType;
 procedure TTestOptimizations.TestOptShortRefGlobals_SameUnit_EnumType;
 begin
 begin
   StartUnit(true,[supTObject]);
   StartUnit(true,[supTObject]);