Browse Source

pastojs: fixed concat append for nil and referenced array

mattias 3 years ago
parent
commit
1e3c5865de
3 changed files with 76 additions and 68 deletions
  1. 42 56
      packages/pastojs/src/fppas2js.pp
  2. 10 10
      packages/pastojs/tests/tcmodules.pas
  3. 24 2
      utils/pas2js/dist/rtl.js

+ 42 - 56
packages/pastojs/src/fppas2js.pp

@@ -558,6 +558,7 @@ type
     pbifnArray_Equal,
     pbifnArray_Equal,
     pbifnArray_Insert,
     pbifnArray_Insert,
     pbifnArray_Length,
     pbifnArray_Length,
+    pbifnArray_Push,
     pbifnArray_PushN,
     pbifnArray_PushN,
     pbifnArray_Reference,
     pbifnArray_Reference,
     pbifnArray_SetLength,
     pbifnArray_SetLength,
@@ -750,6 +751,7 @@ const
     'arrayEq', // rtl.arrayEq          pbifnArray_Equal
     'arrayEq', // rtl.arrayEq          pbifnArray_Equal
     'arrayInsert', // rtl.arrayCopy      pbifnArray_Insert
     'arrayInsert', // rtl.arrayCopy      pbifnArray_Insert
     'length', // rtl.length    pbifnArray_Length
     'length', // rtl.length    pbifnArray_Length
+    'arrayPush', // rtl.arrayPush   pbifnArray_Push
     'arrayPushN', // rtl.arrayPushN   pbifnArray_PushN
     'arrayPushN', // rtl.arrayPushN   pbifnArray_PushN
     'arrayRef', // rtl.arrayRef  pbifnArray_Reference
     'arrayRef', // rtl.arrayRef  pbifnArray_Reference
     'arraySetLength', // rtl.arraySetLength  pbifnArray_SetLength
     'arraySetLength', // rtl.arraySetLength  pbifnArray_SetLength
@@ -2138,9 +2140,9 @@ type
       FuncContext: TFunctionContext; out DelaySrc: TJSSourceElements): TFunctionContext; virtual;
       FuncContext: TFunctionContext; out DelaySrc: TJSSourceElements): TFunctionContext; virtual;
     // array
     // array
     Function CreateArrayConcat(ElTypeResolved: TPasResolverResult; PosEl: TPasElement;
     Function CreateArrayConcat(ElTypeResolved: TPasResolverResult; PosEl: TPasElement;
-      AContext: TConvertContext): TJSCallExpression; overload; virtual;
+      AContext: TConvertContext; IsAppend: boolean = false): TJSCallExpression; overload; virtual;
     Function CreateArrayConcat(ArrayType: TPasArrayType; PosEl: TPasElement;
     Function CreateArrayConcat(ArrayType: TPasArrayType; PosEl: TPasElement;
-      AContext: TConvertContext): TJSCallExpression; overload; virtual;
+      AContext: TConvertContext; IsAppend: boolean = false): TJSCallExpression; overload; virtual;
     Function CreateArrayInit(ArrayType: TPasArrayType; Expr: TPasExpr;
     Function CreateArrayInit(ArrayType: TPasArrayType; Expr: TPasExpr;
       El: TPasElement; AContext: TConvertContext): TJSElement; virtual;
       El: TPasElement; AContext: TConvertContext): TJSElement; virtual;
     Function CreateArrayRef(El: TPasElement; ArrayExpr: TJSElement): TJSElement; virtual;
     Function CreateArrayRef(El: TPasElement; ArrayExpr: TJSElement): TJSElement; virtual;
@@ -18928,9 +18930,10 @@ end;
 
 
 function TPasToJSConverter.CreateArrayConcat(
 function TPasToJSConverter.CreateArrayConcat(
   ElTypeResolved: TPasResolverResult; PosEl: TPasElement;
   ElTypeResolved: TPasResolverResult; PosEl: TPasElement;
-  AContext: TConvertContext): TJSCallExpression;
+  AContext: TConvertContext; IsAppend: boolean): TJSCallExpression;
 var
 var
   Call: TJSCallExpression;
   Call: TJSCallExpression;
+  Func: TPas2JSBuiltInName;
 begin
 begin
   Result:=nil;
   Result:=nil;
   Call:=CreateCallExpression(PosEl);
   Call:=CreateCallExpression(PosEl);
@@ -18938,25 +18941,33 @@ begin
     {$IFDEF VerbosePas2JS}
     {$IFDEF VerbosePas2JS}
     writeln('TPasToJSConverter.CreateArrayConcat ElType=',GetResolverResultDbg(ElTypeResolved));
     writeln('TPasToJSConverter.CreateArrayConcat ElType=',GetResolverResultDbg(ElTypeResolved));
     {$ENDIF}
     {$ENDIF}
+    if IsAppend then
+      Func:=pbifnArray_Push
+    else
+      Func:=pbifnArray_Concat;
     if ElTypeResolved.BaseType=btContext then
     if ElTypeResolved.BaseType=btContext then
       begin
       begin
       if ElTypeResolved.LoTypeEl.ClassType=TPasRecordType then
       if ElTypeResolved.LoTypeEl.ClassType=TPasRecordType then
         begin
         begin
         // record: rtl.arrayConcat(RecordType,array1,array2,...)
         // record: rtl.arrayConcat(RecordType,array1,array2,...)
-        Call.Expr:=CreateMemberExpression([GetBIName(pbivnRTL),GetBIName(pbifnArray_Concat)]);
+        Call.Expr:=CreateMemberExpression([GetBIName(pbivnRTL),GetBIName(Func)]);
         Call.AddArg(CreateReferencePathExpr(ElTypeResolved.LoTypeEl,AContext));
         Call.AddArg(CreateReferencePathExpr(ElTypeResolved.LoTypeEl,AContext));
         end;
         end;
       end
       end
     else if ElTypeResolved.BaseType=btSet then
     else if ElTypeResolved.BaseType=btSet then
       begin
       begin
       // set: rtl.arrayConcat("refSet",array1,array2,...)
       // set: rtl.arrayConcat("refSet",array1,array2,...)
-      Call.Expr:=CreateMemberExpression([GetBIName(pbivnRTL),GetBIName(pbifnArray_Concat)]);
+      Call.Expr:=CreateMemberExpression([GetBIName(pbivnRTL),GetBIName(Func)]);
       Call.AddArg(CreateLiteralString(PosEl,GetBIName(pbifnSet_Reference)));
       Call.AddArg(CreateLiteralString(PosEl,GetBIName(pbifnSet_Reference)));
       end;
       end;
     if Call.Expr=nil then
     if Call.Expr=nil then
       begin
       begin
       // default: rtl.arrayConcatN(array1,array2,...)
       // default: rtl.arrayConcatN(array1,array2,...)
-      Call.Expr:=CreateMemberExpression([GetBIName(pbivnRTL),GetBIName(pbifnArray_ConcatN)]);
+      if IsAppend then
+        Func:=pbifnArray_PushN
+      else
+        Func:=pbifnArray_ConcatN;
+      Call.Expr:=CreateMemberExpression([GetBIName(pbivnRTL),GetBIName(Func)]);
       end;
       end;
     Result:=Call;
     Result:=Call;
   finally
   finally
@@ -18966,7 +18977,8 @@ begin
 end;
 end;
 
 
 function TPasToJSConverter.CreateArrayConcat(ArrayType: TPasArrayType;
 function TPasToJSConverter.CreateArrayConcat(ArrayType: TPasArrayType;
-  PosEl: TPasElement; AContext: TConvertContext): TJSCallExpression;
+  PosEl: TPasElement; AContext: TConvertContext; IsAppend: boolean
+  ): TJSCallExpression;
 var
 var
   ElTypeResolved: TPasResolverResult;
   ElTypeResolved: TPasResolverResult;
   aResolver: TPas2JSResolver;
   aResolver: TPas2JSResolver;
@@ -18975,7 +18987,7 @@ begin
     RaiseNotSupported(PosEl,AContext,20170331001021);
     RaiseNotSupported(PosEl,AContext,20170331001021);
   aResolver:=AContext.Resolver;
   aResolver:=AContext.Resolver;
   aResolver.ComputeElement(aResolver.GetArrayElType(ArrayType),ElTypeResolved,[rcType]);
   aResolver.ComputeElement(aResolver.GetArrayElType(ArrayType),ElTypeResolved,[rcType]);
-  Result:=CreateArrayConcat(ElTypeResolved,PosEl,AContext);
+  Result:=CreateArrayConcat(ElTypeResolved,PosEl,AContext,IsAppend);
 end;
 end;
 
 
 function TPasToJSConverter.CreateArrayInit(ArrayType: TPasArrayType;
 function TPasToJSConverter.CreateArrayInit(ArrayType: TPasArrayType;
@@ -23197,27 +23209,24 @@ function TPasToJSConverter.ConvertDirectAssignArrayConcat(El: TPasImplAssign;
   Params: TParamsExpr; AssignContext: TAssignContext): TJSElement;
   Params: TParamsExpr; AssignContext: TAssignContext): TJSElement;
 // AnArrayVar:=Concat()
 // AnArrayVar:=Concat()
 var
 var
-  Param1, LeftExpr, Param2: TPasExpr;
+  FirstParam, LeftExpr, SecondParam: TPasExpr;
   LeftRef, ParamRef: TResolvedReference;
   LeftRef, ParamRef: TResolvedReference;
   SubParams: TParamsExpr;
   SubParams: TParamsExpr;
+  ParentContext: TConvertContext;
+  AssignSt: TJSSimpleAssignStatement;
   Call: TJSCallExpression;
   Call: TJSCallExpression;
-  DotExpr: TJSDotMemberExpression;
   i: Integer;
   i: Integer;
-  ParentContext: TConvertContext;
   JS: TJSElement;
   JS: TJSElement;
-  CondExpr: TJSConditionalExpression;
-  ArrLit: TJSArrayLiteral;
-  ok: Boolean;
 begin
 begin
   Result:=nil;
   Result:=nil;
   LeftExpr:=El.Left;
   LeftExpr:=El.Left;
   if not (LeftExpr.CustomData is TResolvedReference) then exit;
   if not (LeftExpr.CustomData is TResolvedReference) then exit;
   LeftRef:=TResolvedReference(LeftExpr.CustomData);
   LeftRef:=TResolvedReference(LeftExpr.CustomData);
 
 
-  Param1:=Params.Params[0];
-  if Param1.CustomData is TResolvedReference then
+  FirstParam:=Params.Params[0];
+  if FirstParam.CustomData is TResolvedReference then
     begin
     begin
-    ParamRef:=TResolvedReference(Param1.CustomData);
+    ParamRef:=TResolvedReference(FirstParam.CustomData);
     if LeftRef.Declaration=ParamRef.Declaration then
     if LeftRef.Declaration=ParamRef.Declaration then
       begin
       begin
       {$IFDEF VerbosePas2JS}
       {$IFDEF VerbosePas2JS}
@@ -23227,64 +23236,41 @@ begin
       if length(Params.Params)=1 then
       if length(Params.Params)=1 then
         begin
         begin
         // A:=Concat(A)  ->  A;
         // A:=Concat(A)  ->  A;
-        Result:=ConvertExpression(Param1,ParentContext);
+        Result:=ConvertExpression(FirstParam,ParentContext);
         exit;
         exit;
         end;
         end;
       // A:=Concat(A,...)  ->  append to array
       // A:=Concat(A,...)  ->  append to array
       if length(Params.Params)=2 then
       if length(Params.Params)=2 then
         begin
         begin
-        Param2:=Params.Params[1];
-        if (Param2.Kind=pekSet) then
+        SecondParam:=Params.Params[1];
+        if (SecondParam.Kind=pekSet) then
           begin
           begin
           // A:=Concat(A,[b,c,...])
           // A:=Concat(A,[b,c,...])
-          SubParams:=TParamsExpr(Param2);
+          SubParams:=TParamsExpr(SecondParam);
           if length(SubParams.Params)=0 then
           if length(SubParams.Params)=0 then
             begin
             begin
             // A:=Concat(A,[])  ->  A;
             // A:=Concat(A,[])  ->  A;
-            Result:=ConvertExpression(Param1,ParentContext);
+            Result:=ConvertExpression(FirstParam,ParentContext);
             exit;
             exit;
             end;
             end;
-          ok:=false;
+          // A:=Concat(A,[b,c])  ->  A=rtl.arrayPushN(A,b,c);
+          AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,AssignContext.PasElement));
           try
           try
-            if length(SubParams.Params)=1 then
-              begin
-              // A:=Concat(A,[b])  ->  A?A.push(b):[b];
-              CondExpr:=TJSConditionalExpression(CreateElement(TJSConditionalExpression,El));
-              Result:=CondExpr;
-
-              // A?
-              CondExpr.A:=ConvertExpression(Param1,ParentContext);
-
-              // A.push(b)
-              Call:=CreateCallExpression(El);
-              CondExpr.B:=Call;
-              DotExpr:=TJSDotMemberExpression(CreateElement(TJSDotMemberExpression,El));
-              Call.Expr:=DotExpr;
-              DotExpr.MExpr:=ConvertExpression(Param1,ParentContext);
-              DotExpr.Name:='push';
-
-              // [b]
-              ArrLit:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,El));
-              CondExpr.C:=ArrLit;
-              ArrLit.AddElement(CreateArrayEl(SubParams.Params[0],ParentContext));
-              end
-            else
-              begin
-              // A:=Concat(A,[b,c])  ->  rtl.arrayPushN(A,b,c);
-              Call:=CreateCallExpression(El);
-              Result:=Call;
-              Call.Expr:=CreatePrimitiveDotExpr(GetBIName(pbivnRTL)+'.'+GetBIName(pbifnArray_PushN),El);
-              Call.AddArg(ConvertExpression(Param1,ParentContext));
-              end;
+            AssignSt.LHS:=ConvertExpression(FirstParam,ParentContext);
+            Call:=CreateArrayConcat(AssignContext.LeftResolved.LoTypeEl as TPasArrayType,
+                                             El,ParentContext,true);
+            AssignSt.Expr:=Call;
+            Call.AddArg(ConvertExpression(FirstParam,ParentContext));
             for i:=0 to length(SubParams.Params)-1 do
             for i:=0 to length(SubParams.Params)-1 do
               begin
               begin
               JS:=CreateArrayEl(SubParams.Params[i],ParentContext);
               JS:=CreateArrayEl(SubParams.Params[i],ParentContext);
               Call.AddArg(JS);
               Call.AddArg(JS);
               end;
               end;
-            ok:=true;
+
+            Result:=AssignSt;
           finally
           finally
-            if not ok then
-              Result.Free;
+            if Result=nil then
+              AssignSt.Free;
           end;
           end;
           end;
           end;
         end;
         end;

+ 10 - 10
packages/pastojs/tests/tcmodules.pas

@@ -11432,20 +11432,20 @@ begin
     '']),
     '']),
     LinesToStr([ // $mod.$main
     LinesToStr([ // $mod.$main
     '$mod.ArrInt;',
     '$mod.ArrInt;',
-    '($mod.ArrInt ? $mod.ArrInt.push(2) : [2]);',
-    'rtl.arrayPushN($mod.ArrInt, 3, 4);',
+    '$mod.ArrInt = rtl.arrayPushN($mod.ArrInt, 2);',
+    '$mod.ArrInt = rtl.arrayPushN($mod.ArrInt, 3, 4);',
     '$mod.ArrRec;',
     '$mod.ArrRec;',
-    '($mod.ArrRec ? $mod.ArrRec.push($mod.TRec.$clone($mod.r)) : [$mod.TRec.$clone($mod.r)]);',
-    'rtl.arrayPushN($mod.ArrRec, $mod.TRec.$clone($mod.r), $mod.TRec.$clone($mod.r));',
+    '$mod.ArrRec = rtl.arrayPush($mod.TRec, $mod.ArrRec, $mod.TRec.$clone($mod.r));',
+    '$mod.ArrRec = rtl.arrayPush($mod.TRec, $mod.ArrRec, $mod.TRec.$clone($mod.r), $mod.TRec.$clone($mod.r));',
     '$mod.ArrSet;',
     '$mod.ArrSet;',
-    '($mod.ArrSet ? $mod.ArrSet.push(rtl.refSet($mod.f)) : [rtl.refSet($mod.f)]);',
-    'rtl.arrayPushN($mod.ArrSet, rtl.refSet($mod.f), rtl.refSet($mod.f));',
+    '$mod.ArrSet = rtl.arrayPush("refSet", $mod.ArrSet, rtl.refSet($mod.f));',
+    '$mod.ArrSet = rtl.arrayPush("refSet", $mod.ArrSet, rtl.refSet($mod.f), rtl.refSet($mod.f));',
     '$mod.ArrJSValue;',
     '$mod.ArrJSValue;',
-    '($mod.ArrJSValue ? $mod.ArrJSValue.push(11) : [11]);',
-    'rtl.arrayPushN($mod.ArrJSValue, 12, 13);',
+    '$mod.ArrJSValue = rtl.arrayPushN($mod.ArrJSValue, 11);',
+    '$mod.ArrJSValue = rtl.arrayPushN($mod.ArrJSValue, 12, 13);',
     '$mod.ArrFlag;',
     '$mod.ArrFlag;',
-    '($mod.ArrFlag ? $mod.ArrFlag.push($mod.TFlag.small) : [$mod.TFlag.small]);',
-    'rtl.arrayPushN($mod.ArrFlag, $mod.TFlag.small, $mod.TFlag.big);',
+    '$mod.ArrFlag = rtl.arrayPushN($mod.ArrFlag, $mod.TFlag.small);',
+    '$mod.ArrFlag = rtl.arrayPushN($mod.ArrFlag, $mod.TFlag.small, $mod.TFlag.big);',
     '']));
     '']));
 end;
 end;
 
 

+ 24 - 2
utils/pas2js/dist/rtl.js

@@ -1035,6 +1035,8 @@ var rtl = {
       if (src === null) continue;
       if (src === null) continue;
       if (a===null){
       if (a===null){
         a=rtl.arrayRef(src); // Note: concat(a) does not clone
         a=rtl.arrayRef(src); // Note: concat(a) does not clone
+      } else if (a['$pas2jsrefcnt']){
+        a=a.concat(src); // clone a and append src
       } else {
       } else {
         for (var i=0; i<src.length; i++){
         for (var i=0; i<src.length; i++){
           a.push(src[i]);
           a.push(src[i]);
@@ -1044,13 +1046,33 @@ var rtl = {
     return a;
     return a;
   },
   },
 
 
+  arrayPush: function(type,a){
+    if(a===null){
+      a=[];
+    } else if (a['$pas2jsrefcnt']){
+      if (type===0){
+        a=a.concat();
+      } else {
+        a=rtl.arrayCopy(type,a,0,a.length);
+      }
+    }
+    for (var i=1; i<arguments.length; i++){
+      a.push(arguments[i]);
+    }
+    return a;
+  },
+
   arrayPushN: function(a){
   arrayPushN: function(a){
-    if(a==null) a=[];
+    if(a===null){
+      a=[];
+    } else if (a['$pas2jsrefcnt']){
+      a=a.concat();
+    }
     for (var i=1; i<arguments.length; i++){
     for (var i=1; i<arguments.length; i++){
       a.push(arguments[i]);
       a.push(arguments[i]);
     }
     }
     return a;
     return a;
-    },
+  },
 
 
   arrayCopy: function(type, srcarray, index, count){
   arrayCopy: function(type, srcarray, index, count){
     // type: see rtl.arrayClone
     // type: see rtl.arrayClone