Jelajahi Sumber

pastojs: added FindAvailableLocalName

git-svn-id: trunk@38454 -
Mattias Gaertner 7 tahun lalu
induk
melakukan
27508cdd23
2 mengubah file dengan 182 tambahan dan 14 penghapusan
  1. 167 12
      packages/pastojs/src/fppas2js.pp
  2. 15 2
      packages/pastojs/tests/tcmodules.pas

+ 167 - 12
packages/pastojs/src/fppas2js.pp

@@ -1228,7 +1228,7 @@ type
 
 
   TPasToJSConverter = Class(TObject)
   TPasToJSConverter = Class(TObject)
   private
   private
-    // inline at top, only functions declared after the inline implementation actually use it
+    // inline at ttop, because fpc 3.1 requires inline implementation in front of use
     function GetUseEnumNumbers: boolean; inline;
     function GetUseEnumNumbers: boolean; inline;
     function GetUseLowerCase: boolean; inline;
     function GetUseLowerCase: boolean; inline;
     function GetUseSwitchStatement: boolean; inline;
     function GetUseSwitchStatement: boolean; inline;
@@ -1289,11 +1289,13 @@ type
     Function IsPreservedWord(const aName: string): boolean; virtual;
     Function IsPreservedWord(const aName: string): boolean; virtual;
     Function GetTypeInfoName(El: TPasType; AContext: TConvertContext;
     Function GetTypeInfoName(El: TPasType; AContext: TConvertContext;
       ErrorEl: TPasElement): String; virtual;
       ErrorEl: TPasElement): String; virtual;
-    // Never create an element manually, always use the below functions
+    // utility functions for creating stuff
     Function IsElementUsed(El: TPasElement): boolean; virtual;
     Function IsElementUsed(El: TPasElement): boolean; virtual;
     Function IsSystemUnit(aModule: TPasModule): boolean; virtual;
     Function IsSystemUnit(aModule: TPasModule): boolean; virtual;
     Function HasTypeInfo(El: TPasType; AContext: TConvertContext): boolean; virtual;
     Function HasTypeInfo(El: TPasType; AContext: TConvertContext): boolean; virtual;
     Function IsClassRTTICreatedBefore(aClass: TPasClassType; Before: TPasElement; AConText: TConvertContext): boolean;
     Function IsClassRTTICreatedBefore(aClass: TPasClassType; Before: TPasElement; AConText: TConvertContext): boolean;
+    Procedure FindAvailableLocalName(var aName: string; JSExpr: TJSElement);
+    // Never create an element manually, always use the below functions
     Function CreateElement(C: TJSElementClass; Src: TPasElement): TJSElement; virtual;
     Function CreateElement(C: TJSElementClass; Src: TPasElement): TJSElement; virtual;
     Function CreateFreeOrNewInstanceExpr(Ref: TResolvedReference;
     Function CreateFreeOrNewInstanceExpr(Ref: TResolvedReference;
       AContext : TConvertContext): TJSCallExpression; virtual;
       AContext : TConvertContext): TJSCallExpression; virtual;
@@ -1375,7 +1377,7 @@ type
     Function CreateCallRTLFreeLoc(Setter, Getter: TJSElement; Src: TPasElement): TJSElement; virtual;
     Function CreateCallRTLFreeLoc(Setter, Getter: TJSElement; Src: TPasElement): TJSElement; virtual;
     Function CreatePropertyGet(Prop: TPasProperty; Ref: TResolvedReference;
     Function CreatePropertyGet(Prop: TPasProperty; Ref: TResolvedReference;
       AContext: TConvertContext; PosEl: TPasElement): TJSElement; virtual;
       AContext: TConvertContext; PosEl: TPasElement): TJSElement; virtual;
-    Function StorePrecompiledJS(El: TJSElement): string; virtual;
+    Function CreatePrecompiledJS(El: TJSElement): string; virtual;
     // Statements
     // Statements
     Function ConvertImplBlockElements(El: TPasImplBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual;
     Function ConvertImplBlockElements(El: TPasImplBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual;
     Function ConvertBeginEndStatement(El: TPasImplBeginBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual;
     Function ConvertBeginEndStatement(El: TPasImplBeginBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual;
@@ -1545,7 +1547,6 @@ const
   TempRefObjGetterName = 'get';
   TempRefObjGetterName = 'get';
   TempRefObjSetterName = 'set';
   TempRefObjSetterName = 'set';
   TempRefObjSetterArgName = 'v';
   TempRefObjSetterArgName = 'v';
-  TempRefObjSetterArgNameAlt = 'p';
 
 
 function CodePointToJSString(u: longword): TJSString;
 function CodePointToJSString(u: longword): TJSString;
 begin
 begin
@@ -9701,7 +9702,7 @@ begin
   if (coStoreImplJS in Options) and (AContext.Resolver<>nil) then
   if (coStoreImplJS in Options) and (AContext.Resolver<>nil) then
     begin
     begin
     if AContext.Resolver.GetTopLvlProc(El)=El then
     if AContext.Resolver.GetTopLvlProc(El)=El then
-      ImplProcScope.BodyJS:=StorePrecompiledJS(Result);
+      ImplProcScope.BodyJS:=CreatePrecompiledJS(Result);
     end;
     end;
 end;
 end;
 
 
@@ -9801,7 +9802,7 @@ begin
   end;
   end;
 
 
   if (coStoreImplJS in Options) and (AContext.Resolver<>nil) then
   if (coStoreImplJS in Options) and (AContext.Resolver<>nil) then
-    Scope.JS:=StorePrecompiledJS(Result);
+    Scope.JS:=CreatePrecompiledJS(Result);
 end;
 end;
 
 
 function TPasToJSConverter.ConvertFinalizationSection(El: TFinalizationSection;
 function TPasToJSConverter.ConvertFinalizationSection(El: TFinalizationSection;
@@ -11111,7 +11112,7 @@ begin
     end;
     end;
 end;
 end;
 
 
-function TPasToJSConverter.StorePrecompiledJS(El: TJSElement): string;
+function TPasToJSConverter.CreatePrecompiledJS(El: TJSElement): string;
 var
 var
   aWriter: TBufferWriter;
   aWriter: TBufferWriter;
   aJSWriter: TJSWriter;
   aJSWriter: TJSWriter;
@@ -12303,6 +12304,162 @@ begin
     end;
     end;
 end;
 end;
 
 
+procedure TPasToJSConverter.FindAvailableLocalName(var aName: string;
+  JSExpr: TJSElement);
+var
+  StartJSName, JSName: TJSString;
+  n: integer;
+  Changed: boolean;
+
+  procedure Next;
+  var
+    ch: WideChar;
+  begin
+    Changed:=true;
+    // name clash -> change JSName
+    if (n=0) and (length(JSName)=1) then
+      begin
+      // single letter -> choose next single letter
+      ch:=JSName[1];
+      case ch of
+      'a'..'x': JSName:=succ(ch);
+      'z': JSName:='a';
+      end;
+      if JSName=StartJSName then
+        begin
+        n:=1;
+        JSName:=StartJSName+TJSString(IntToStr(n));
+        end;
+      end
+    else
+      begin
+      inc(n);
+      JSName:=StartJSName+TJSString(IntToStr(n));
+      end;
+  end;
+
+  procedure Find(El: TJSElement);
+  var
+    C: TClass;
+    Call: TJSCallExpression;
+    i: Integer;
+  begin
+    if El=nil then exit;
+    C:=El.ClassType;
+    if C=TJSPrimaryExpressionIdent then
+      begin
+      if TJSPrimaryExpressionIdent(El).Name=JSName then
+        Next;
+      end
+    else if C.InheritsFrom(TJSMemberExpression) then
+      begin
+      Find(TJSMemberExpression(El).MExpr);
+      if C=TJSBracketMemberExpression then
+        Find(TJSBracketMemberExpression(El).Name)
+      else if C=TJSNewMemberExpression then
+        with TJSNewMemberExpression(El).Args.Elements do
+          for i:=0 to Count-1 do
+            Find(Elements[i].Expr)
+      end
+    else if C=TJSCallExpression then
+      begin
+      Call:=TJSCallExpression(El);
+      Find(Call.Expr);
+      if Call.Args<>nil then
+        with Call.Args.Elements do
+          for i:=0 to Count-1 do
+            Find(Elements[i].Expr);
+      end
+    else if C.InheritsFrom(TJSUnary) then
+      Find(TJSUnary(El).A)
+    else if C.InheritsFrom(TJSBinary) then
+      begin
+      Find(TJSBinary(El).A);
+      Find(TJSBinary(El).B);
+      end
+    else if C=TJSArrayLiteral then
+      begin
+      with TJSArrayLiteral(El).Elements do
+        for i:=0 to Count-1 do
+          Find(Elements[i].Expr);
+      end
+    else if C=TJSConditionalExpression then
+      begin
+      Find(TJSConditionalExpression(El).A);
+      Find(TJSConditionalExpression(El).B);
+      Find(TJSConditionalExpression(El).C);
+      end
+    else if C.InheritsFrom(TJSAssignStatement) then
+      begin
+      Find(TJSAssignStatement(El).LHS);
+      Find(TJSAssignStatement(El).Expr);
+      end
+    else if C=TJSVarDeclaration then
+      Find(TJSVarDeclaration(El).Init)
+    else if C=TJSObjectLiteral then
+      begin
+      with TJSObjectLiteral(El).Elements do
+        for i:=0 to Count-1 do
+          Find(Elements[i].Expr);
+      end
+    else if C=TJSIfStatement then
+      begin
+      Find(TJSIfStatement(El).Cond);
+      Find(TJSIfStatement(El).BTrue);
+      Find(TJSIfStatement(El).BFalse);
+      end
+    else if C.InheritsFrom(TJSBodyStatement) then
+      begin
+      Find(TJSBodyStatement(El).Body);
+      if C.InheritsFrom(TJSCondLoopStatement) then
+        begin
+        Find(TJSCondLoopStatement(El).Cond);
+        if C=TJSForStatement then
+          begin
+          Find(TJSForStatement(El).Init);
+          Find(TJSForStatement(El).Incr);
+          end;
+        end
+      else if C=TJSForInStatement then
+        begin
+        Find(TJSForInStatement(El).LHS);
+        Find(TJSForInStatement(El).List);
+        end;
+      end
+    else if C=TJSSwitchStatement then
+      begin
+      Find(TJSSwitchStatement(El).Cond);
+      with TJSSwitchStatement(El).Cases do
+        for i:=0 to Count-1 do
+          with Cases[i] do
+            begin
+            Find(Expr);
+            Find(Body);
+            end;
+      if TJSSwitchStatement(El).TheDefault<>nil then
+        with TJSSwitchStatement(El).TheDefault do
+          begin
+          Find(Expr);
+          Find(Body);
+          end;
+      end;
+  end;
+
+begin
+  if JSExpr=nil then exit;
+  StartJSName:=TJSString(aName);
+  JSName:=StartJSName;
+  n:=0;
+  Changed:=false;
+  Find(JSExpr);
+  if not Changed then exit;
+  repeat
+    Changed:=false;
+    Find(JSExpr);
+  until not changed;
+  aName:=UTF8Encode(JSName);
+end;
+
 function TPasToJSConverter.CreateUnary(Members: array of string; E: TJSElement): TJSUnary;
 function TPasToJSConverter.CreateUnary(Members: array of string; E: TJSElement): TJSUnary;
 var
 var
   unary: TJSUnary;
   unary: TJSUnary;
@@ -13407,7 +13564,7 @@ var
   GetPath, SetPath: String;
   GetPath, SetPath: String;
   BracketExpr: TJSBracketMemberExpression;
   BracketExpr: TJSBracketMemberExpression;
   DotExpr: TJSDotMemberExpression;
   DotExpr: TJSDotMemberExpression;
-  SetterArgName: Char;
+  SetterArgName: String;
 begin
 begin
   // pass reference -> create a temporary JS object with a FullGetter and setter
   // pass reference -> create a temporary JS object with a FullGetter and setter
   Obj:=nil;
   Obj:=nil;
@@ -13575,9 +13732,7 @@ begin
       // create   SetExpr = v;
       // create   SetExpr = v;
       AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
       AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
       AssignSt.LHS:=SetExpr;
       AssignSt.LHS:=SetExpr;
-      if (SetExpr is TJSPrimaryExpressionIdent)
-          and (TJSPrimaryExpressionIdent(SetExpr).Name=TJSString(SetterArgName)) then
-        SetterArgName:=TempRefObjSetterArgNameAlt;
+      FindAvailableLocalName(SetterArgName,SetExpr);
       AssignSt.Expr:=CreatePrimitiveDotExpr(SetterArgName,El);
       AssignSt.Expr:=CreatePrimitiveDotExpr(SetterArgName,El);
       SetExpr:=AssignSt;
       SetExpr:=AssignSt;
       end
       end
@@ -13800,7 +13955,7 @@ begin
       if Proc<>nil then
       if Proc<>nil then
         begin
         begin
         ProcScope:=TPas2JSProcedureScope(Proc.CustomData);
         ProcScope:=TPas2JSProcedureScope(Proc.CustomData);
-        ProcScope.AddGlobalJS(StorePrecompiledJS(V));
+        ProcScope.AddGlobalJS(CreatePrecompiledJS(V));
         end;
         end;
       end;
       end;
     end
     end

+ 15 - 2
packages/pastojs/tests/tcmodules.pas

@@ -2987,8 +2987,10 @@ begin
   '  i:=i+2;',
   '  i:=i+2;',
   'end;',
   'end;',
   'procedure DoIt(v: longint);',
   'procedure DoIt(v: longint);',
+  'var p: array of longint;',
   'begin',
   'begin',
   '  Inc2(v);',
   '  Inc2(v);',
+  '  Inc2(p[v]);',
   'end;',
   'end;',
   'begin']);
   'begin']);
   ConvertProgram;
   ConvertProgram;
@@ -2998,11 +3000,22 @@ begin
     '  i.set(i.get()+2);',
     '  i.set(i.get()+2);',
     '};',
     '};',
     'this.DoIt = function (v) {',
     'this.DoIt = function (v) {',
+    '  var p = [];',
     '  $mod.Inc2({get: function () {',
     '  $mod.Inc2({get: function () {',
     '    return v;',
     '    return v;',
-    '  }, set: function (p) {',
-    '    v = p;',
+    '  }, set: function (w) {',
+    '    v = w;',
     '  }});',
     '  }});',
+    '  $mod.Inc2({',
+    '    a: v,',
+    '    p: p,',
+    '    get: function () {',
+    '        return this.p[this.a];',
+    '      },',
+    '    set: function (v) {',
+    '        this.p[this.a] = v;',
+    '      }',
+    '  });',
     '};',
     '};',
     '']),
     '']),
     LinesToStr([
     LinesToStr([