Browse Source

pastojs: clone multi dim static array

mattias 3 years ago
parent
commit
8ae1b6eace

+ 215 - 73
packages/pastojs/src/fppas2js.pp

@@ -6880,17 +6880,25 @@ var
   ElType: TPasType;
   ElType: TPasType;
 begin
 begin
   l:=length(Arr.Ranges);
   l:=length(Arr.Ranges);
-  if l=0 then exit(false);
-  if l>1 then exit(false ); // ToDo: return true when cloning multi dims is implemented
-  ElType:=ResolveAliasType(Arr.ElType);
-  if ElType is TPasArrayType then
-    Result:=length(TPasArrayType(ElType).Ranges)>0
-  else if ElType is TPasRecordType then
-    Result:=true
-  else if ElType is TPasSetType then
-    Result:=true
+  case l of
+  0:
+    Result:=false; // dyn array
+  1:
+    begin
+    // 1-dim static array
+    ElType:=ResolveAliasType(Arr.ElType);
+    if ElType is TPasArrayType then
+      Result:=length(TPasArrayType(ElType).Ranges)>0
+    else if ElType is TPasRecordType then
+      Result:=true
+    else if ElType is TPasSetType then
+      Result:=true
+    else
+      Result:=false; // can use  arr.slice(0)
+    end
   else
   else
-    Result:=false;
+    Result:=true; // multi dim static array
+  end;
 end;
 end;
 
 
 function TPas2JSResolver.IsTGUID(TypeEl: TPasRecordType): boolean;
 function TPas2JSResolver.IsTGUID(TypeEl: TPasRecordType): boolean;
@@ -16645,13 +16653,13 @@ function TPasToJSConverter.ConvertArrayType(El: TPasArrayType;
 //    eltype: module.$rtti["ElTypeName"]
 //    eltype: module.$rtti["ElTypeName"]
 //  };
 //  };
 //
 //
-const
-  CloneArrName = 'a';
-  CloneResultName = 'r';
-  CloneRunName = 'i';
 var
 var
+  VarIndex: integer;
   ProcScope: TPas2JSProcedureScope;
   ProcScope: TPas2JSProcedureScope;
   Src: TJSSourceElements;
   Src: TJSSourceElements;
+  Index: Integer;
+  BodySrc: TJSSourceElements;
+  ForLoop: TJSForStatement;
 
 
   procedure StorePrecompiledJS(JS: TJSElement);
   procedure StorePrecompiledJS(JS: TJSElement);
   begin
   begin
@@ -16662,26 +16670,63 @@ var
       ProcScope.AddGlobalJS(CreatePrecompiledJS(JS));
       ProcScope.AddGlobalJS(CreatePrecompiledJS(JS));
   end;
   end;
 
 
+  function GetNextVarName: string;
+  var
+    i: integer;
+  begin
+    i:=VarIndex mod 52;
+    if i<26 then
+      Result:=chr(ord('a')+i)
+    else
+      Result:=chr(ord('A')+i);
+    if VarIndex>=52 then
+      Result:=Result+IntToStr(VarIndex div 52);
+    inc(VarIndex);
+  end;
+
+  procedure AddLoopSt(JS: TJSElement);
+  var
+    List: TJSStatementList;
+  begin
+    if Index=0 then
+      AddToSourceElements(BodySrc,JS)
+    else
+      begin
+      if ForLoop.Body=nil then
+        ForLoop.Body:=JS
+      else
+        begin
+        List:=TJSStatementList(CreateElement(TJSStatementList,El));
+        List.A:=ForLoop.Body;
+        List.B:=JS;
+        ForLoop.Body:=List;
+        end;
+      end;
+  end;
+
 var
 var
   aResolver: TPas2JSResolver;
   aResolver: TPas2JSResolver;
   AssignSt: TJSSimpleAssignStatement;
   AssignSt: TJSSimpleAssignStatement;
   ArrName: String;
   ArrName: String;
-  Index: Integer;
   ElTypeLo: TPasType;
   ElTypeLo: TPasType;
   RangeEl: TPasExpr;
   RangeEl: TPasExpr;
   Call: TJSCallExpression;
   Call: TJSCallExpression;
   RangeEnd: TMaxPrecInt;
   RangeEnd: TMaxPrecInt;
   List: TJSStatementList;
   List: TJSStatementList;
   Func: TJSFunctionDeclarationStatement;
   Func: TJSFunctionDeclarationStatement;
-  BodySrc: TJSSourceElements;
   VarSt: TJSVariableStatement;
   VarSt: TJSVariableStatement;
-  ForLoop: TJSForStatement;
   ExprLT: TJSRelationalExpressionLT;
   ExprLT: TJSRelationalExpressionLT;
   PlusPlus: TJSUnaryPostPlusPlusExpression;
   PlusPlus: TJSUnaryPostPlusPlusExpression;
-  BracketEx: TJSBracketMemberExpression;
+  BracketLeftEx, BracketRightEx: TJSBracketMemberExpression;
   ArraySt, CloneEl: TJSElement;
   ArraySt, CloneEl: TJSElement;
   ReturnSt: TJSReturnStatement;
   ReturnSt: TJSReturnStatement;
   FuncContext: TFunctionContext;
   FuncContext: TFunctionContext;
+  SrcArrName, ResultName, LoopVarName, NewArrName,
+    ParentNewArrName, ParentSrcArrName: string;
+  VarDecl: TJSVarDeclaration;
+  MaxIndex: SizeInt;
+  UseSlice: boolean;
+  NewLoop: TJSForStatement;
 begin
 begin
   Result:=nil;
   Result:=nil;
   aResolver:=AContext.Resolver;
   aResolver:=AContext.Resolver;
@@ -16700,72 +16745,172 @@ begin
 
 
   if aResolver.HasStaticArrayCloneFunc(El) then
   if aResolver.HasStaticArrayCloneFunc(El) then
     begin
     begin
-    // For example: type TArr = array[1..2] of array[1..2] of longint;
+    // Example1: type TStaticArray = array[1..2] of array[1..2] of longint;
     //  this.TStaticArray$clone = function(a){
     //  this.TStaticArray$clone = function(a){
-    //    var r = [];
-    //    for (var i=0; i<*High(a)*; i++) r.push(a[i].slice(0));
-    //    return r;
+    //    var b = [];
+    //    b.length = Dim1;
+    //    for (var c=0; c<Dim1; c++) b[c] = a[c].slice(0);
+    //    return b;
     //  };
     //  };
-    BracketEx:=nil;
+    // Example2: type TDim3 = array[1..3,2..4,3..5] of longint;
+    //  this.TDim3$clone = function(a){
+    //    var b = [];
+    //    b.length = Dim1;
+    //    for (var c=0; c<Dim1; c++){
+    //      var d = b[c] = [];
+    //      d.length = Dim2;
+    //      var e = a[c];
+    //      for (var f=0; f<Dim2; f++) d[f] = e[f].slice(0);
+    //    }
+    //    return b;
+    //  };
+    BracketLeftEx:=nil;
     AssignSt:=nil;
     AssignSt:=nil;
     Func:=nil;
     Func:=nil;
     FuncContext:=nil;
     FuncContext:=nil;
     try
     try
-      Index:=0;
-      RangeEl:=El.Ranges[Index];
+      VarIndex:=0;
+      SrcArrName:=GetNextVarName;
+      ResultName:=GetNextVarName;
+      LoopVarName:='';
+
+      ElTypeLo:=aResolver.ResolveAliasType(El.ElType);
+
       // function(a){...
       // function(a){...
       Func:=CreateFunctionSt(El,true,true);
       Func:=CreateFunctionSt(El,true,true);
-      Func.AFunction.Params.Add(CloneArrName);
+      Func.AFunction.Params.Add(SrcArrName);
       BodySrc:=Func.AFunction.Body.A as TJSSourceElements;
       BodySrc:=Func.AFunction.Body.A as TJSSourceElements;
       FuncContext:=TFunctionContext.Create(El,BodySrc,AContext);
       FuncContext:=TFunctionContext.Create(El,BodySrc,AContext);
       FuncContext.IsGlobal:=true;
       FuncContext.IsGlobal:=true;
-      // var r = [];
-      VarSt:=CreateVarStatement(CloneResultName,TJSArrayLiteral(CreateElement(TJSArrayLiteral,El)),El);
-      AddToSourceElements(BodySrc,VarSt);
-      // for (
-      ForLoop:=TJSForStatement(CreateElement(TJSForStatement,El));
-      AddToSourceElements(BodySrc,ForLoop);
-      // var i=0;
-      ForLoop.Init:=CreateVarStatement(CloneRunName,CreateLiteralNumber(El,0),El);
-      // i<high(a)
-      ExprLT:=TJSRelationalExpressionLT(CreateElement(TJSRelationalExpressionLT,El));
-      ForLoop.Cond:=ExprLT;
-      ExprLT.A:=CreatePrimitiveDotExpr(CloneRunName,El);
-      RangeEnd:=aResolver.GetRangeLength(RangeEl);
-      ExprLT.B:=CreateLiteralNumber(RangeEl,RangeEnd);
-      // i++
-      PlusPlus:=TJSUnaryPostPlusPlusExpression(CreateElement(TJSUnaryPostPlusPlusExpression,El));
-      ForLoop.Incr:=PlusPlus;
-      PlusPlus.A:=CreatePrimitiveDotExpr(CloneRunName,El);
-      // r.push(...
-      Call:=CreateCallExpression(El);
-      ForLoop.Body:=Call;
-      Call.Expr:=CreatePrimitiveDotExpr(CloneResultName+'.push',El);
-      // a[i]
-      BracketEx:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
-      BracketEx.MExpr:=CreatePrimitiveDotExpr(CloneArrName,El);
-      BracketEx.Name:=CreatePrimitiveDotExpr(CloneRunName,El);
-      // clone a[i]
-      ElTypeLo:=aResolver.ResolveAliasType(El.ElType);
-      CloneEl:=nil;
-      if ElTypeLo is TPasArrayType then
+
+      MaxIndex:=length(El.Ranges)-1;
+
+      UseSlice:=(ElTypeLo is TPasUnresolvedSymbolRef)
+             or (ElTypeLo is TPasRangeType);
+      ForLoop:=nil;
+      if UseSlice then
+        // static array of a base type -> inner loop is replaced with slice(0)
+        dec(MaxIndex);
+
+      for Index:=0 to MaxIndex do
         begin
         begin
-        if length(TPasArrayType(ElTypeLo).Ranges)=0 then
-          RaiseNotSupported(El,FuncContext,20180218223414,GetObjName(ElTypeLo));
-        CloneEl:=CreateCloneStaticArray(El,TPasArrayType(ElTypeLo),BracketEx,FuncContext);
-        end
-      else if ElTypeLo is TPasRecordType then
-        CloneEl:=CreateRecordCallClone(El,TPasRecordType(ElTypeLo),BracketEx,FuncContext)
-      else if ElTypeLo is TPasSetType then
-        CloneEl:=CreateReferencedSet(El,BracketEx)
-      else
-        RaiseNotSupported(El,FuncContext,20180218223618,GetObjName(ElTypeLo));
-      Call.AddArg(CloneEl);
-      BracketEx:=nil;
-      // return r;
+        RangeEl:=El.Ranges[Index];
+        RangeEnd:=aResolver.GetRangeLength(RangeEl);
+
+        if Index=0 then
+          NewArrName:=ResultName
+        else
+          begin
+          ParentNewArrName:=NewArrName;
+          NewArrName:=GetNextVarName;
+          end;
+
+        // var NewArr = [];
+        VarSt:=TJSVariableStatement(CreateElement(TJSVariableStatement,El));
+        VarDecl:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,El));
+        VarSt.A:=VarDecl;
+        VarDecl.Name:=NewArrName;
+        VarDecl.Init:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,El));
+        AddLoopSt(VarSt);
+        if Index>0 then
+          begin
+          // var NewArr = ParentNewArrName[LoopVar] = [];
+          AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
+          AssignSt.Expr:=VarDecl.Init; // ... = []
+          VarDecl.Init:=AssignSt;
+          // ... = ParentNewArrName[LoopVar] = ...
+          BracketLeftEx:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
+          AssignSt.LHS:=BracketLeftEx;
+          BracketLeftEx.MExpr:=CreatePrimitiveDotExpr(ParentNewArrName,El);
+          BracketLeftEx.Name:=CreatePrimitiveDotExpr(LoopVarName,El);
+          end;
+
+        // NewArr.length = Dim;
+        AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
+        AssignSt.LHS:=CreatePrimitiveDotExpr(NewArrName+'.length',El);
+        AssignSt.Expr:=CreateLiteralNumber(El,RangeEnd);
+        AddLoopSt(AssignSt);
+
+        if Index>0 then
+          begin
+          // var SrcArrName = ParentSrcArrName[LoopVar];
+          ParentSrcArrName:=SrcArrName;
+          SrcArrName:=GetNextVarName;
+
+          BracketLeftEx:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
+          VarSt:=CreateVarStatement(SrcArrName,BracketLeftEx,El);
+          BracketLeftEx.MExpr:=CreatePrimitiveDotExpr(ParentSrcArrName,El);
+          BracketLeftEx.Name:=CreatePrimitiveDotExpr(LoopVarName,El);
+          AddLoopSt(VarSt);
+          end;
+
+        // for (
+        LoopVarName:=GetNextVarName;
+        NewLoop:=TJSForStatement(CreateElement(TJSForStatement,El));
+        AddLoopSt(NewLoop);
+        ForLoop:=NewLoop;
+        // var LoopVar=0;
+        ForLoop.Init:=CreateVarStatement(LoopVarName,CreateLiteralNumber(El,0),El);
+        // LoopVar<Dim
+        ExprLT:=TJSRelationalExpressionLT(CreateElement(TJSRelationalExpressionLT,El));
+        ForLoop.Cond:=ExprLT;
+        ExprLT.A:=CreatePrimitiveDotExpr(LoopVarName,El);
+        ExprLT.B:=CreateLiteralNumber(El,RangeEnd);
+        // LoopVar++
+        PlusPlus:=TJSUnaryPostPlusPlusExpression(CreateElement(TJSUnaryPostPlusPlusExpression,El));
+        ForLoop.Incr:=PlusPlus;
+        PlusPlus.A:=CreatePrimitiveDotExpr(LoopVarName,El);
+
+        if Index=MaxIndex then
+          begin
+          // NewArr[LoopVar] = ...
+          AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
+          ForLoop.Body:=AssignSt;
+          BracketLeftEx:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
+          AssignSt.LHS:=BracketLeftEx;
+          BracketLeftEx.MExpr:=CreatePrimitiveDotExpr(NewArrName,El);
+          BracketLeftEx.Name:=CreatePrimitiveDotExpr(LoopVarName,El);
+          // SrcArr[LoopVar]
+          BracketRightEx:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
+          BracketRightEx.MExpr:=CreatePrimitiveDotExpr(SrcArrName,El);
+          BracketRightEx.Name:=CreatePrimitiveDotExpr(LoopVarName,El);
+          try
+            // clone array element
+            CloneEl:=nil;
+            if UseSlice then
+              begin
+              // SrcArr[LoopVar].slice(0)
+              Call:=CreateCallExpression(El);
+              CloneEl:=Call;
+              Call.Expr:=CreateDotNameExpr(El,BracketRightEx,'slice');
+              Call.AddArg(CreateLiteralNumber(El,0));
+              end
+            else if ElTypeLo is TPasArrayType then
+              begin
+              if length(TPasArrayType(ElTypeLo).Ranges)=0 then
+                RaiseNotSupported(El,FuncContext,20180218223414,GetObjName(ElTypeLo));
+              CloneEl:=CreateCloneStaticArray(El,TPasArrayType(ElTypeLo),BracketRightEx,FuncContext);
+              end
+            else if ElTypeLo is TPasRecordType then
+              CloneEl:=CreateRecordCallClone(El,TPasRecordType(ElTypeLo),BracketRightEx,FuncContext)
+            else if ElTypeLo is TPasSetType then
+              CloneEl:=CreateReferencedSet(El,BracketRightEx)
+            else
+              RaiseNotSupported(El,FuncContext,20180218223618,GetObjName(ElTypeLo));
+            AssignSt.Expr:=CloneEl;
+            BracketRightEx:=nil;
+          finally
+            BracketRightEx.Free;
+          end;
+
+          end;
+
+        end;
+
+      // return ResultName;
       ReturnSt:=TJSReturnStatement(CreateElement(TJSReturnStatement,El));
       ReturnSt:=TJSReturnStatement(CreateElement(TJSReturnStatement,El));
       AddToSourceElements(BodySrc,ReturnSt);
       AddToSourceElements(BodySrc,ReturnSt);
-      ReturnSt.Expr:=CreatePrimitiveDotExpr(CloneResultName,El);
+      ReturnSt.Expr:=CreatePrimitiveDotExpr(ResultName,El);
 
 
       ArrName:=GetOverloadName(El,AContext)+GetBIName(pbifnArray_Static_Clone);
       ArrName:=GetOverloadName(El,AContext)+GetBIName(pbifnArray_Static_Clone);
       if El.Parent is TProcedureBody then
       if El.Parent is TProcedureBody then
@@ -16795,7 +16940,6 @@ begin
 
 
       ArraySt:=nil;
       ArraySt:=nil;
     finally
     finally
-      BracketEx.Free;
       Func.Free;
       Func.Free;
       ArraySt.Free;
       ArraySt.Free;
       FuncContext.Free;
       FuncContext.Free;
@@ -18865,8 +19009,6 @@ begin
     // TArrayType$clone(ArrayExpr);
     // TArrayType$clone(ArrayExpr);
     if ArrTypeEl.Name='' then
     if ArrTypeEl.Name='' then
       RaiseNotSupported(El,AContext,20180218230407,'copy anonymous multi dim static array');
       RaiseNotSupported(El,AContext,20180218230407,'copy anonymous multi dim static array');
-    if length(ArrTypeEl.Ranges)>1 then
-      RaiseNotSupported(El,AContext,20180218231700,'copy multi dim static array');
     FuncContext:=AContext.GetFunctionContext;
     FuncContext:=AContext.GetFunctionContext;
     Path:=CreateReferencePath(ArrTypeEl,FuncContext,rpkPathAndName)
     Path:=CreateReferencePath(ArrTypeEl,FuncContext,rpkPathAndName)
           +GetBIName(pbifnArray_Static_Clone);
           +GetBIName(pbifnArray_Static_Clone);

+ 12 - 9
packages/pastojs/tests/tcgenerics.pas

@@ -259,9 +259,10 @@ begin
     '      this.a = rtl.arraySetLength(null, $impl.TBird, 2);',
     '      this.a = rtl.arraySetLength(null, $impl.TBird, 2);',
     '    };',
     '    };',
     '    this.a$a$clone = function (a) {',
     '    this.a$a$clone = function (a) {',
-    '      var r = [];',
-    '      for (var i = 0; i < 2; i++) r.push($impl.TBird.$clone(a[i]));',
-    '      return r;',
+    '      var b = [];',
+    '      b.length = 2;',
+    '      for (var c = 0; c < 2; c++) b[c] = $impl.TBird.$clone(a[c]);',
+    '      return b;',
     '    };',
     '    };',
     '    this.$eq = function (b) {',
     '    this.$eq = function (b) {',
     '      return true;',
     '      return true;',
@@ -1226,9 +1227,10 @@ begin
     '      this.a = rtl.arraySetLength(null, $impl.TBird, 2);',
     '      this.a = rtl.arraySetLength(null, $impl.TBird, 2);',
     '    };',
     '    };',
     '    this.a$a$clone = function (a) {',
     '    this.a$a$clone = function (a) {',
-    '      var r = [];',
-    '      for (var i = 0; i < 2; i++) r.push($impl.TBird.$clone(a[i]));',
-    '      return r;',
+    '      var b = [];',
+    '      b.length = 2;',
+    '      for (var c = 0; c < 2; c++) b[c] = $impl.TBird.$clone(a[c]);',
+    '      return b;',
     '    };',
     '    };',
     '  }, "TAnt<UnitA.TBird>");',
     '  }, "TAnt<UnitA.TBird>");',
     '  $mod.$implcode = function () {',
     '  $mod.$implcode = function () {',
@@ -2596,9 +2598,10 @@ begin
     '  this.$rtti.$DynArray("TDyn<UnitA.TAnt>", {});',
     '  this.$rtti.$DynArray("TDyn<UnitA.TAnt>", {});',
     '  this.$rtti.$DynArray("TDyn<UnitA.TBird>", {});',
     '  this.$rtti.$DynArray("TDyn<UnitA.TBird>", {});',
     '  this.TStatic$G1$clone = function (a) {',
     '  this.TStatic$G1$clone = function (a) {',
-    '    var r = [];',
-    '    for (var i = 0; i < 2; i++) r.push($impl.TBird.$clone(a[i]));',
-    '    return r;',
+    '    var b = [];',
+    '    b.length = 2;',
+    '    for (var c = 0; c < 2; c++) b[c] = $impl.TBird.$clone(a[c]);',
+    '    return b;',
     '  };',
     '  };',
     '  this.$rtti.$StaticArray("TStatic<UnitA.TBird>", {',
     '  this.$rtti.$StaticArray("TStatic<UnitA.TBird>", {',
     '    dims: [2]',
     '    dims: [2]',

+ 115 - 18
packages/pastojs/tests/tcmodules.pas

@@ -9629,10 +9629,18 @@ begin
   'type',
   'type',
   '  TArrayInt = array[1..3] of longint;',
   '  TArrayInt = array[1..3] of longint;',
   '  TArrayArrayInt = array[5..6] of TArrayInt;',
   '  TArrayArrayInt = array[5..6] of TArrayInt;',
+  '  TArrayArrayArrayInt = array[7..8] of TArrayArrayInt;',
+  '  TArrayDim2Int = array[1..2,1..3] of longint;',
+  '  TArrayDim3Int = array[1..2,1..3,1..4] of longint;',
+  '  TArrayDim4Int = array[1..2,1..3,1..4,1..5] of longint;',
   'var',
   'var',
   '  Arr: TArrayInt;',
   '  Arr: TArrayInt;',
   '  Arr2: TArrayArrayInt;',
   '  Arr2: TArrayArrayInt;',
   '  Arr3: array[boolean] of TArrayInt = ((11,12,13),(21,22,23));',
   '  Arr3: array[boolean] of TArrayInt = ((11,12,13),(21,22,23));',
+  '  Arr4: TArrayArrayInt;',
+  '  ArrDim2: TArrayDim2Int;',
+  '  ArrDim3: TArrayDim3Int;',
+  '  ArrDim4: TArrayDim4Int;',
   '  i: longint;',
   '  i: longint;',
   'begin',
   'begin',
   '  i:=low(arr);',
   '  i:=low(arr);',
@@ -9648,23 +9656,80 @@ begin
   '  i:=arr2[5,2];',
   '  i:=arr2[5,2];',
   '  arr2:=arr2;',// clone multi dim static array
   '  arr2:=arr2;',// clone multi dim static array
   '  arr3:=arr3;',// clone anonymous multi dim static array
   '  arr3:=arr3;',// clone anonymous multi dim static array
+  '  arr4:=arr4;',
+  '  Arr:=Arr;',
+  '  ArrDim2:=ArrDim2;',
+  '  ArrDim3:=ArrDim3;',
+  '  ArrDim4:=ArrDim4;',
   '']);
   '']);
   ConvertProgram;
   ConvertProgram;
   CheckSource('TestArray_StaticMultiDim',
   CheckSource('TestArray_StaticMultiDim',
     LinesToStr([ // statements
     LinesToStr([ // statements
     'this.TArrayArrayInt$clone = function (a) {',
     'this.TArrayArrayInt$clone = function (a) {',
-    '  var r = [];',
-    '  for (var i = 0; i < 2; i++) r.push(a[i].slice(0));',
-    '  return r;',
+    '  var b = [];',
+    '  b.length = 2;',
+    '  for (var c = 0; c < 2; c++) b[c] = a[c].slice(0);',
+    '  return b;',
+    '};',
+    'this.TArrayArrayArrayInt$clone = function (a) {',
+    '  var b = [];',
+    '  b.length = 2;',
+    '  for (var c = 0; c < 2; c++) b[c] = $mod.TArrayArrayInt$clone(a[c]);',
+    '  return b;',
+    '};',
+    'this.TArrayDim2Int$clone = function (a) {',
+    '  var b = [];',
+    '  b.length = 2;',
+    '  for (var c = 0; c < 2; c++) b[c] = a[c].slice(0);',
+    '  return b;',
+    '};',
+    'this.TArrayDim3Int$clone = function (a) {',
+    '  var b = [];',
+    '  b.length = 2;',
+    '  for (var c = 0; c < 2; c++) {',
+    '    var d = b[c] = [];',
+    '    d.length = 3;',
+    '    var e = a[c];',
+    '    for (var f = 0; f < 3; f++) d[f] = e[f].slice(0);',
+    '  };',
+    '  return b;',
+    '};',
+    'this.TArrayDim4Int$clone = function (a) {',
+    '  var b = [];',
+    '  b.length = 2;',
+    '  for (var c = 0; c < 2; c++) {',
+    '    var d = b[c] = [];',
+    '    d.length = 3;',
+    '    var e = a[c];',
+    '    for (var f = 0; f < 3; f++) {',
+    '      var g = d[f] = [];',
+    '      g.length = 4;',
+    '      var h = e[f];',
+    '      for (var i = 0; i < 4; i++) g[i] = h[i].slice(0);',
+    '    };',
+    '  };',
+    '  return b;',
     '};',
     '};',
     'this.Arr = rtl.arraySetLength(null, 0, 3);',
     'this.Arr = rtl.arraySetLength(null, 0, 3);',
     'this.Arr2 = rtl.arraySetLength(null, 0, 2, 3);',
     'this.Arr2 = rtl.arraySetLength(null, 0, 2, 3);',
     'this.Arr3$a$clone = function (a) {',
     'this.Arr3$a$clone = function (a) {',
-    '  var r = [];',
-    '  for (var i = 0; i < 2; i++) r.push(a[i].slice(0));',
-    '  return r;',
+    '  var b = [];',
+    '  b.length = 2;',
+    '  for (var c = 0; c < 2; c++) b[c] = a[c].slice(0);',
+    '  return b;',
     '};',
     '};',
     'this.Arr3 = [[11, 12, 13], [21, 22, 23]];',
     'this.Arr3 = [[11, 12, 13], [21, 22, 23]];',
+    'this.Arr4 = rtl.arraySetLength(null, 0, 2, 3);',
+    'this.ArrDim2 = rtl.arraySetLength(null, 0, 2, 3);',
+    'this.ArrDim3 = rtl.arraySetLength(null, 0, 2, 3, 4);',
+    'this.ArrDim4 = rtl.arraySetLength(',
+    '  null,',
+    '  0,',
+    '  2,',
+    '  3,',
+    '  4,',
+    '  5',
+    ');',
     'this.i = 0;'
     'this.i = 0;'
     ]),
     ]),
     LinesToStr([ // $mod.$main
     LinesToStr([ // $mod.$main
@@ -9681,6 +9746,11 @@ begin
     '$mod.i = $mod.Arr2[0][1];',
     '$mod.i = $mod.Arr2[0][1];',
     '$mod.Arr2 = $mod.TArrayArrayInt$clone($mod.Arr2);',
     '$mod.Arr2 = $mod.TArrayArrayInt$clone($mod.Arr2);',
     '$mod.Arr3 = $mod.Arr3$a$clone($mod.Arr3);',
     '$mod.Arr3 = $mod.Arr3$a$clone($mod.Arr3);',
+    '$mod.Arr4 = $mod.TArrayArrayInt$clone($mod.Arr4);',
+    '$mod.Arr = $mod.Arr.slice(0);',
+    '$mod.ArrDim2 = $mod.TArrayDim2Int$clone($mod.ArrDim2);',
+    '$mod.ArrDim3 = $mod.TArrayDim3Int$clone($mod.ArrDim3);',
+    '$mod.ArrDim4 = $mod.TArrayDim4Int$clone($mod.ArrDim4);',
     '']));
     '']));
 end;
 end;
 
 
@@ -9712,14 +9782,16 @@ begin
     'this.TArrayInt = 3;',
     'this.TArrayInt = 3;',
     'this.TArrayArrayInt = 4;',
     'this.TArrayArrayInt = 4;',
     'var TArrayArrayInt$1$clone = function (a) {',
     'var TArrayArrayInt$1$clone = function (a) {',
-    '  var r = [];',
-    '  for (var i = 0; i < 2; i++) r.push(a[i].slice(0));',
-    '  return r;',
+    '  var b = [];',
+    '  b.length = 2;',
+    '  for (var c = 0; c < 2; c++) b[c] = a[c].slice(0);',
+    '  return b;',
     '};',
     '};',
     'var Arr3$a$clone = function (a) {',
     'var Arr3$a$clone = function (a) {',
-    '  var r = [];',
-    '  for (var i = 0; i < 2; i++) r.push(a[i].slice(0));',
-    '  return r;',
+    '  var b = [];',
+    '  b.length = 2;',
+    '  for (var c = 0; c < 2; c++) b[c] = a[c].slice(0);',
+    '  return b;',
     '};',
     '};',
     'this.DoIt = function () {',
     'this.DoIt = function () {',
     '  var Arr = rtl.arraySetLength(null, 0, 3);',
     '  var Arr = rtl.arraySetLength(null, 0, 3);',
@@ -9837,9 +9909,10 @@ begin
     '  };',
     '  };',
     '});',
     '});',
     'this.TArrayRec$clone = function (a) {',
     'this.TArrayRec$clone = function (a) {',
-    '  var r = [];',
-    '  for (var i = 0; i < 2; i++) r.push($mod.TRec.$clone(a[i]));',
-    '  return r;',
+    '  var b = [];',
+    '  b.length = 2;',
+    '  for (var c = 0; c < 2; c++) b[c] = $mod.TRec.$clone(a[c]);',
+    '  return b;',
     '};',
     '};',
     'this.Arr = rtl.arraySetLength(null, this.TRec, 2);',
     'this.Arr = rtl.arraySetLength(null, this.TRec, 2);',
     '']),
     '']),
@@ -11876,6 +11949,12 @@ begin
   CheckSource('TestRecord_FieldArray',
   CheckSource('TestRecord_FieldArray',
     LinesToStr([ // statements
     LinesToStr([ // statements
     'rtl.recNewT(this, "TRec", function () {',
     'rtl.recNewT(this, "TRec", function () {',
+    '  this.m$a$clone = function (a) {',
+    '    var b = [];',
+    '    b.length = 2;',
+    '    for (var c = 0; c < 2; c++) b[c] = a[c].slice(0);',
+    '    return b;',
+    '  };',
     '  this.$new = function () {',
     '  this.$new = function () {',
     '    var r = Object.create(this);',
     '    var r = Object.create(this);',
     '    r.a = [];',
     '    r.a = [];',
@@ -11890,7 +11969,7 @@ begin
     '  this.$assign = function (s) {',
     '  this.$assign = function (s) {',
     '    this.a = rtl.arrayRef(s.a);',
     '    this.a = rtl.arrayRef(s.a);',
     '    this.s = s.s.slice(0);',
     '    this.s = s.s.slice(0);',
-    '    this.m = s.m.slice(0);',
+    '    this.m = this.m$a$clone(s.m);',
     '    this.o = s.o.slice(0);',
     '    this.o = s.o.slice(0);',
     '    return this;',
     '    return this;',
     '  };',
     '  };',
@@ -11942,6 +12021,12 @@ begin
     '});',
     '});',
     'rtl.recNewT(this, "TRec", function () {',
     'rtl.recNewT(this, "TRec", function () {',
     '  this.i = 0;',
     '  this.i = 0;',
+    '  this.m$a$clone = function (a) {',
+    '    var b = [];',
+    '    b.length = 2;',
+    '    for (var c = 0; c < 2; c++) b[c] = a[c].slice(0);',
+    '    return b;',
+    '  };',
     '  this.$new = function () {',
     '  this.$new = function () {',
     '    var r = Object.create(this);',
     '    var r = Object.create(this);',
     '    r.a = [];',
     '    r.a = [];',
@@ -11957,7 +12042,7 @@ begin
     '    this.i = s.i;',
     '    this.i = s.i;',
     '    this.a = rtl.arrayRef(s.a);',
     '    this.a = rtl.arrayRef(s.a);',
     '    this.s = s.s.slice(0);',
     '    this.s = s.s.slice(0);',
-    '    this.m = s.m.slice(0);',
+    '    this.m = this.m$a$clone(s.m);',
     '    this.p.$assign(s.p);',
     '    this.p.$assign(s.p);',
     '    return this;',
     '    return this;',
     '  };',
     '  };',
@@ -12351,6 +12436,12 @@ begin
     '}, true);',
     '}, true);',
     'rtl.recNewT(this, "TRec", function () {',
     'rtl.recNewT(this, "TRec", function () {',
     '  this.i = 0;',
     '  this.i = 0;',
+    '  this.m$a$clone = function (a) {',
+    '    var b = [];',
+    '    b.length = 2;',
+    '    for (var c = 0; c < 2; c++) b[c] = a[c].slice(0);',
+    '    return b;',
+    '  };',
     '  this.$new = function () {',
     '  this.$new = function () {',
     '    var r = Object.create(this);',
     '    var r = Object.create(this);',
     '    r.a = [];',
     '    r.a = [];',
@@ -12366,7 +12457,7 @@ begin
     '    this.i = s.i;',
     '    this.i = s.i;',
     '    this.a = rtl.arrayRef(s.a);',
     '    this.a = rtl.arrayRef(s.a);',
     '    this.s = s.s.slice(0);',
     '    this.s = s.s.slice(0);',
-    '    this.m = s.m.slice(0);',
+    '    this.m = this.m$a$clone(s.m);',
     '    this.p.$assign(s.p);',
     '    this.p.$assign(s.p);',
     '    return this;',
     '    return this;',
     '  };',
     '  };',
@@ -32668,6 +32759,12 @@ begin
   ConvertProgram;
   ConvertProgram;
   CheckSource('TestRangeChecks_ArrayIndex',
   CheckSource('TestRangeChecks_ArrayIndex',
     LinesToStr([ // statements
     LinesToStr([ // statements
+    'this.TArrByteChar$clone = function (a) {',
+    '  var b = [];',
+    '  b.length = 256;',
+    '  for (var c = 0; c < 256; c++) b[c] = a[c].slice(0);',
+    '  return b;',
+    '};',
     'rtl.createClass(this, "TObject", null, function () {',
     'rtl.createClass(this, "TObject", null, function () {',
     '  this.$init = function () {',
     '  this.$init = function () {',
     '    this.A = [];',
     '    this.A = [];',