From 27508cdd238601ca362444b7941c93be77a70541 Mon Sep 17 00:00:00 2001 From: Mattias Gaertner Date: Thu, 8 Mar 2018 10:58:26 +0000 Subject: [PATCH] pastojs: added FindAvailableLocalName git-svn-id: trunk@38454 - --- packages/pastojs/src/fppas2js.pp | 179 +++++++++++++++++++++++++-- packages/pastojs/tests/tcmodules.pas | 17 ++- 2 files changed, 182 insertions(+), 14 deletions(-) diff --git a/packages/pastojs/src/fppas2js.pp b/packages/pastojs/src/fppas2js.pp index 4178e235d2..2f4664e9a0 100644 --- a/packages/pastojs/src/fppas2js.pp +++ b/packages/pastojs/src/fppas2js.pp @@ -1228,7 +1228,7 @@ type TPasToJSConverter = Class(TObject) 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 GetUseLowerCase: boolean; inline; function GetUseSwitchStatement: boolean; inline; @@ -1289,11 +1289,13 @@ type Function IsPreservedWord(const aName: string): boolean; virtual; Function GetTypeInfoName(El: TPasType; AContext: TConvertContext; 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 IsSystemUnit(aModule: TPasModule): boolean; virtual; Function HasTypeInfo(El: TPasType; AContext: TConvertContext): boolean; virtual; 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 CreateFreeOrNewInstanceExpr(Ref: TResolvedReference; AContext : TConvertContext): TJSCallExpression; virtual; @@ -1375,7 +1377,7 @@ type Function CreateCallRTLFreeLoc(Setter, Getter: TJSElement; Src: TPasElement): TJSElement; virtual; Function CreatePropertyGet(Prop: TPasProperty; Ref: TResolvedReference; AContext: TConvertContext; PosEl: TPasElement): TJSElement; virtual; - Function StorePrecompiledJS(El: TJSElement): string; virtual; + Function CreatePrecompiledJS(El: TJSElement): string; virtual; // Statements Function ConvertImplBlockElements(El: TPasImplBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual; Function ConvertBeginEndStatement(El: TPasImplBeginBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual; @@ -1545,7 +1547,6 @@ const TempRefObjGetterName = 'get'; TempRefObjSetterName = 'set'; TempRefObjSetterArgName = 'v'; - TempRefObjSetterArgNameAlt = 'p'; function CodePointToJSString(u: longword): TJSString; begin @@ -9701,7 +9702,7 @@ begin if (coStoreImplJS in Options) and (AContext.Resolver<>nil) then begin if AContext.Resolver.GetTopLvlProc(El)=El then - ImplProcScope.BodyJS:=StorePrecompiledJS(Result); + ImplProcScope.BodyJS:=CreatePrecompiledJS(Result); end; end; @@ -9801,7 +9802,7 @@ begin end; if (coStoreImplJS in Options) and (AContext.Resolver<>nil) then - Scope.JS:=StorePrecompiledJS(Result); + Scope.JS:=CreatePrecompiledJS(Result); end; function TPasToJSConverter.ConvertFinalizationSection(El: TFinalizationSection; @@ -11111,7 +11112,7 @@ begin end; end; -function TPasToJSConverter.StorePrecompiledJS(El: TJSElement): string; +function TPasToJSConverter.CreatePrecompiledJS(El: TJSElement): string; var aWriter: TBufferWriter; aJSWriter: TJSWriter; @@ -12303,6 +12304,162 @@ begin 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; var unary: TJSUnary; @@ -13407,7 +13564,7 @@ var GetPath, SetPath: String; BracketExpr: TJSBracketMemberExpression; DotExpr: TJSDotMemberExpression; - SetterArgName: Char; + SetterArgName: String; begin // pass reference -> create a temporary JS object with a FullGetter and setter Obj:=nil; @@ -13575,9 +13732,7 @@ begin // create SetExpr = v; AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El)); AssignSt.LHS:=SetExpr; - if (SetExpr is TJSPrimaryExpressionIdent) - and (TJSPrimaryExpressionIdent(SetExpr).Name=TJSString(SetterArgName)) then - SetterArgName:=TempRefObjSetterArgNameAlt; + FindAvailableLocalName(SetterArgName,SetExpr); AssignSt.Expr:=CreatePrimitiveDotExpr(SetterArgName,El); SetExpr:=AssignSt; end @@ -13800,7 +13955,7 @@ begin if Proc<>nil then begin ProcScope:=TPas2JSProcedureScope(Proc.CustomData); - ProcScope.AddGlobalJS(StorePrecompiledJS(V)); + ProcScope.AddGlobalJS(CreatePrecompiledJS(V)); end; end; end diff --git a/packages/pastojs/tests/tcmodules.pas b/packages/pastojs/tests/tcmodules.pas index 9fd2b89b7d..60b55fcc32 100644 --- a/packages/pastojs/tests/tcmodules.pas +++ b/packages/pastojs/tests/tcmodules.pas @@ -2987,8 +2987,10 @@ begin ' i:=i+2;', 'end;', 'procedure DoIt(v: longint);', + 'var p: array of longint;', 'begin', ' Inc2(v);', + ' Inc2(p[v]);', 'end;', 'begin']); ConvertProgram; @@ -2998,11 +3000,22 @@ begin ' i.set(i.get()+2);', '};', 'this.DoIt = function (v) {', + ' var p = [];', ' $mod.Inc2({get: function () {', ' 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([