Browse Source

* Patch from Mattias Gaertner
- function copy(array,start=0,count=max): array
- procedure insert(item,var array,const position)
- procedure delete(var array,const start,count)
- allow type casting array of jsvalue to any array

git-svn-id: trunk@35704 -

michael 8 years ago
parent
commit
ad99d1b015
2 changed files with 374 additions and 68 deletions
  1. 184 66
      packages/pastojs/src/fppas2js.pp
  2. 190 2
      packages/pastojs/tests/tcmodules.pas

+ 184 - 66
packages/pastojs/src/fppas2js.pp

@@ -114,6 +114,9 @@ Works:
   - when passing nil to an array argument, pass []
   - when passing nil to an array argument, pass []
   - allow type casting array to external class name 'Array'
   - allow type casting array to external class name 'Array'
   - type cast array to array of same dimensions and compatible element type
   - type cast array to array of same dimensions and compatible element type
+  - function copy(array,start=0,count=max): array
+  - procedure insert(item,var array,const position)
+  - procedure delete(var array,const start,count)
 - static arrays
 - static arrays
   - range: enumtype
   - range: enumtype
   - init as arr = rtl.arrayNewMultiDim([dim1,dim2,...],value)
   - init as arr = rtl.arrayNewMultiDim([dim1,dim2,...],value)
@@ -181,6 +184,7 @@ Works:
   - call inherited
   - call inherited
   - Pascal descendant can override newinstance
   - Pascal descendant can override newinstance
   - any class can be typecasted to any root class
   - any class can be typecasted to any root class
+  - class instances cannot access external class members (e.g. static class functions)
 - ECMAScript6:
 - ECMAScript6:
   - use 0b for binary literals
   - use 0b for binary literals
   - use 0o for octal literals
   - use 0o for octal literals
@@ -199,19 +203,13 @@ Works:
   - class of: assign to jsvalue, typecast jsvalue to a class-of
   - class of: assign to jsvalue, typecast jsvalue to a class-of
   - array of jsvalue,
   - array of jsvalue,
     allow to assign any array to an array of jsvalue
     allow to assign any array to an array of jsvalue
+    allow type casting to any array
   - parameter, result type, assign from/to untyped
   - parameter, result type, assign from/to untyped
   - operators equal, not equal
   - operators equal, not equal
 
 
 ToDos:
 ToDos:
-- external class: class functions are static, forbid calling instance.classfunction
-- function copy(array): array
-- function copy(array,start): array
-- function copy(array,start,count): array
-- proc insert(item,var array,const position)
-- proc delete(var array,const start,count)
-- function concat(array1,array2,...): array
-- function splice(var array, const start,deletecount,item1,item2,...): arrayofdeletedelements;
-- test param const R: TRect  r.Left:=3 fails
+- null arrays
+- [] operator of external class 'Array'
 - FuncName:= (instead of Result:=)
 - FuncName:= (instead of Result:=)
 - ord(s[i]) -> s.charCodeAt(i)
 - ord(s[i]) -> s.charCodeAt(i)
 - $modeswitch -> define <modeswitch>
 - $modeswitch -> define <modeswitch>
@@ -320,6 +318,7 @@ resourcestring
 type
 type
   TPas2JSBuiltInName = (
   TPas2JSBuiltInName = (
     pbifnArray_Concat,
     pbifnArray_Concat,
+    pbifnArray_Copy,
     pbifnArray_NewMultiDim,
     pbifnArray_NewMultiDim,
     pbifnArray_SetLength,
     pbifnArray_SetLength,
     pbifnAs,
     pbifnAs,
@@ -365,6 +364,7 @@ type
 const
 const
   Pas2JSBuiltInNames: array[TPas2JSBuiltInName] of string = (
   Pas2JSBuiltInNames: array[TPas2JSBuiltInName] of string = (
     'arrayConcat', // rtl.arrayConcat
     'arrayConcat', // rtl.arrayConcat
+    'arrayCopy', // rtl.arrayCopy
     'arrayNewMultiDim', // rtl.arrayNewMultiDim
     'arrayNewMultiDim', // rtl.arrayNewMultiDim
     'arraySetLength', // rtl.arraySetLength
     'arraySetLength', // rtl.arraySetLength
     'as', // rtl.as
     'as', // rtl.as
@@ -611,7 +611,9 @@ const
     proFixCaseOfOverrides,
     proFixCaseOfOverrides,
     proClassPropertyNonStatic,
     proClassPropertyNonStatic,
     proPropertyAsVarParam,
     proPropertyAsVarParam,
-    proClassOfIs
+    proClassOfIs,
+    proExtClassInstanceNoTypeMembers,
+    proOpenAsDynArrays
     ];
     ];
 type
 type
   TPas2JSResolver = class(TPasResolver)
   TPas2JSResolver = class(TPasResolver)
@@ -990,6 +992,8 @@ type
     Function ConvertBuiltInStrParam(El: TPasExpr; AContext: TConvertContext; IsStrFunc, IsFirst: boolean): TJSElement; virtual;
     Function ConvertBuiltInStrParam(El: TPasExpr; AContext: TConvertContext; IsStrFunc, IsFirst: boolean): TJSElement; virtual;
     Function ConvertBuiltInConcatArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertBuiltInConcatArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertBuiltInCopyArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertBuiltInCopyArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
+    Function ConvertBuiltInInsertArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
+    Function ConvertBuiltInDeleteArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertRecordValues(El: TRecordValues; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertRecordValues(El: TRecordValues; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertSelfExpression(El: TSelfExpr; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertSelfExpression(El: TSelfExpr; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertBinaryExpression(El: TBinaryExpr; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertBinaryExpression(El: TBinaryExpr; AContext: TConvertContext): TJSElement; virtual;
@@ -1940,81 +1944,95 @@ function TPas2JSResolver.CheckTypeCastRes(const FromResolved,
 var
 var
   JSBaseType: TPas2jsBaseType;
   JSBaseType: TPas2jsBaseType;
   C: TClass;
   C: TClass;
-  CurClass: TPasClassType;
+  ToClass: TPasClassType;
 begin
 begin
   Result:=cIncompatible;
   Result:=cIncompatible;
   {$IFDEF VerbosePas2JS}
   {$IFDEF VerbosePas2JS}
   writeln('TPas2JSResolver.CheckTypeCastCustomBaseType To=',GetResolverResultDesc(ToResolved),' From=',GetResolverResultDesc(FromResolved));
   writeln('TPas2JSResolver.CheckTypeCastCustomBaseType To=',GetResolverResultDesc(ToResolved),' From=',GetResolverResultDesc(FromResolved));
   {$ENDIF}
   {$ENDIF}
-  if (ToResolved.BaseType=btCustom) then
+  if rrfReadable in FromResolved.Flags then
     begin
     begin
-    if not (ToResolved.TypeEl is TPasUnresolvedSymbolRef) then
-      RaiseInternalError(20170325142826);
-    if (ToResolved.TypeEl.CustomData is TResElDataPas2JSBaseType) then
+    if (ToResolved.BaseType=btCustom) then
       begin
       begin
-      // type cast to pas2js type, e.g. JSValue(V)
-      JSBaseType:=TResElDataPas2JSBaseType(ToResolved.TypeEl.CustomData).JSBaseType;
-      if JSBaseType=pbtJSValue then
+      if not (ToResolved.TypeEl is TPasUnresolvedSymbolRef) then
+        RaiseInternalError(20170325142826);
+      if (ToResolved.TypeEl.CustomData is TResElDataPas2JSBaseType) then
         begin
         begin
-        if rrfReadable in FromResolved.Flags then
+        // type cast to pas2js type, e.g. JSValue(V)
+        JSBaseType:=TResElDataPas2JSBaseType(ToResolved.TypeEl.CustomData).JSBaseType;
+        if JSBaseType=pbtJSValue then
           begin
           begin
-          if (FromResolved.BaseType in btAllJSValueSrcTypes) then
-            Result:=cExact+1 // type cast to JSValue
-          else if FromResolved.BaseType=btCustom then
+          if rrfReadable in FromResolved.Flags then
             begin
             begin
-            if IsJSBaseType(FromResolved,pbtJSValue) then
-              Result:=cExact;
-            end
-          else if FromResolved.BaseType=btContext then
-            Result:=cExact+1;
+            if (FromResolved.BaseType in btAllJSValueSrcTypes) then
+              Result:=cExact+1 // type cast to JSValue
+            else if FromResolved.BaseType=btCustom then
+              begin
+              if IsJSBaseType(FromResolved,pbtJSValue) then
+                Result:=cExact;
+              end
+            else if FromResolved.BaseType=btContext then
+              Result:=cExact+1;
+            end;
           end;
           end;
+        exit;
         end;
         end;
-      exit;
-      end;
-    end
-  else if FromResolved.BaseType=btCustom then
-    begin
-    if not (FromResolved.TypeEl is TPasUnresolvedSymbolRef) then
-      RaiseInternalError(20170325143016);
-    if (FromResolved.TypeEl.CustomData is TResElDataPas2JSBaseType) then
+      end
+    else if FromResolved.BaseType=btCustom then
       begin
       begin
-      // type cast a pas2js value, e.g. T(jsvalue)
-      if not (rrfReadable in FromResolved.Flags) then
-        exit;
-      JSBaseType:=TResElDataPas2JSBaseType(FromResolved.TypeEl.CustomData).JSBaseType;
-      if JSBaseType=pbtJSValue then
+      if not (FromResolved.TypeEl is TPasUnresolvedSymbolRef) then
+        RaiseInternalError(20170325143016);
+      if (FromResolved.TypeEl.CustomData is TResElDataPas2JSBaseType) then
         begin
         begin
-        if (ToResolved.BaseType in btAllJSValueTypeCastTo) then
-          Result:=cExact+1 // type cast JSValue to simple base type
-        else if ToResolved.BaseType=btContext then
+        // type cast a pas2js value, e.g. T(jsvalue)
+        if not (rrfReadable in FromResolved.Flags) then
+          exit;
+        JSBaseType:=TResElDataPas2JSBaseType(FromResolved.TypeEl.CustomData).JSBaseType;
+        if JSBaseType=pbtJSValue then
           begin
           begin
-          C:=ToResolved.TypeEl.ClassType;
-          if (C=TPasClassType)
-              or (C=TPasClassOfType)
-              or (C=TPasEnumType) then
-            Result:=cExact+1;
+          if (ToResolved.BaseType in btAllJSValueTypeCastTo) then
+            Result:=cExact+1 // type cast JSValue to simple base type
+          else if ToResolved.BaseType=btContext then
+            begin
+            C:=ToResolved.TypeEl.ClassType;
+            if (C=TPasClassType)
+                or (C=TPasClassOfType)
+                or (C=TPasEnumType) then
+              Result:=cExact+1;
+            end;
           end;
           end;
+        exit;
         end;
         end;
-      exit;
-      end;
-    end
-  else if ToResolved.BaseType=btContext then
-    begin
-    C:=ToResolved.TypeEl.ClassType;
-    if C=TPasClassType then
+      end
+    else if ToResolved.BaseType=btContext then
       begin
       begin
-      CurClass:=TPasClassType(ToResolved.TypeEl);
-      if CurClass.IsExternal then
+      C:=ToResolved.TypeEl.ClassType;
+      if C=TPasClassType then
+        begin
+        ToClass:=TPasClassType(ToResolved.TypeEl);
+        if ToClass.IsExternal then
+          begin
+          if (ToClass.ExternalName='String')
+              and (FromResolved.BaseType in btAllStringAndChars) then
+            exit(cExact);
+          if (ToClass.ExternalName='Array')
+              and ((FromResolved.BaseType=btArray)
+                  or (FromResolved.BaseType=btContext)) then
+            exit(cExact);
+          end;
+        end
+      else if C=TPasArrayType then
         begin
         begin
-        if (CurClass.ExternalName='String')
-            and (FromResolved.BaseType in btAllStringAndChars) then
-          exit(cExact);
-        if (CurClass.ExternalName='Array')
-            and ((FromResolved.BaseType=btArray)
-                or (FromResolved.BaseType=btContext)) then
-          exit(cExact);
+        if (FromResolved.BaseType=btContext)
+            and (FromResolved.TypeEl.ClassType=TPasClassType)
+            and TPasClassType(FromResolved.TypeEl).IsExternal
+            and (TPasClassType(FromResolved.TypeEl).ExternalName='Array') then
+          begin
+            // type cast external Array to an array
+            exit(cExact+1);
+          end;
         end;
         end;
-      end
+      end;
     end;
     end;
   Result:=inherited CheckTypeCastRes(FromResolved,ToResolved,ErrorEl,RaiseOnError);
   Result:=inherited CheckTypeCastRes(FromResolved,ToResolved,ErrorEl,RaiseOnError);
 end;
 end;
@@ -4123,6 +4141,8 @@ begin
           bfStrFunc: Result:=ConvertBuiltInStrFunc(El,AContext);
           bfStrFunc: Result:=ConvertBuiltInStrFunc(El,AContext);
           bfConcatArray: Result:=ConvertBuiltInConcatArray(El,AContext);
           bfConcatArray: Result:=ConvertBuiltInConcatArray(El,AContext);
           bfCopyArray: Result:=ConvertBuiltInCopyArray(El,AContext);
           bfCopyArray: Result:=ConvertBuiltInCopyArray(El,AContext);
+          bfInsertArray: Result:=ConvertBuiltInInsertArray(El,AContext);
+          bfDeleteArray: Result:=ConvertBuiltInDeleteArray(El,AContext);
         else
         else
           RaiseNotSupported(El,AContext,20161130164955,'built in proc '+ResolverBuiltInProcNames[BuiltInProc.BuiltIn]);
           RaiseNotSupported(El,AContext,20161130164955,'built in proc '+ResolverBuiltInProcNames[BuiltInProc.BuiltIn]);
         end;
         end;
@@ -5446,7 +5466,7 @@ begin
         begin
         begin
         // set: rtl.arrayConcat("refSet",array1,array2,...)
         // set: rtl.arrayConcat("refSet",array1,array2,...)
         Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Concat]]);
         Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Concat]]);
-        Call.Args.Elements.AddElement.Expr:=CreateLiteralString(El,'refSet');
+        Call.Args.Elements.AddElement.Expr:=CreateLiteralString(El,FBuiltInNames[pbifnSet_Reference]);
         end;
         end;
       if Call.Expr=nil then
       if Call.Expr=nil then
         // default: array1.concat(array2,...)
         // default: array1.concat(array2,...)
@@ -5464,12 +5484,109 @@ end;
 
 
 function TPasToJSConverter.ConvertBuiltInCopyArray(El: TParamsExpr;
 function TPasToJSConverter.ConvertBuiltInCopyArray(El: TParamsExpr;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
+var
+  Param: TPasExpr;
+  ParamResolved, ElTypeResolved: TPasResolverResult;
+  C: TClass;
+  TypeParam: TJSElement;
+  Call: TJSCallExpression;
+  ArrayType: TPasArrayType;
 begin
 begin
   Result:=nil;
   Result:=nil;
+  Call:=nil;
+  try
+    Param:=El.Params[0];
+    AContext.Resolver.ComputeElement(El,ParamResolved,[]);
+    if ParamResolved.BaseType<>btContext then
+      RaiseInconsistency(20170401003242);
+    if ParamResolved.TypeEl.ClassType<>TPasArrayType then
+      RaiseInconsistency(20170401003256);
+    ArrayType:=TPasArrayType(ParamResolved.TypeEl);
+    AContext.Resolver.ComputeElement(ArrayType.ElType,ElTypeResolved,[rcType]);
+    // rtl.arrayCopy(type,src,start,count)
+    TypeParam:=nil;
+    if ElTypeResolved.BaseType=btContext then
+      begin
+      C:=ElTypeResolved.TypeEl.ClassType;
+      if C=TPasRecordType then
+        TypeParam:=CreateReferencePathExpr(TPasRecordType(ElTypeResolved.TypeEl),AContext);
+      end
+    else if ElTypeResolved.BaseType=btSet then
+      TypeParam:=CreateLiteralString(El,FBuiltInNames[pbifnSet_Reference]);
+    if TypeParam=nil then
+      TypeParam:=CreateLiteralNumber(El,0);
+    Call:=CreateCallExpression(El);
+    // rtl.arrayCopy
+    Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Copy]]);
+    // param: type
+    Call.Args.Elements.AddElement.Expr:=TypeParam;
+    // param: src
+    Call.Args.Elements.AddElement.Expr:=ConvertElement(Param,AContext);
+    // param: start
+    if length(El.Params)=1 then
+      Call.Args.Elements.AddElement.Expr:=CreateLiteralNumber(El,0)
+    else
+      Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[1],AContext);
+    // param: count
+    if length(El.Params)>=3 then
+      Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[2],AContext);
+    Result:=Call;
+  finally
+    if Result=nil then
+      Call.Free;
+  end;
+
   if El=nil then ;
   if El=nil then ;
   if AContext=nil then;
   if AContext=nil then;
 end;
 end;
 
 
+function TPasToJSConverter.ConvertBuiltInInsertArray(El: TParamsExpr;
+  AContext: TConvertContext): TJSElement;
+// procedure insert(item,var array,const position)
+// ->  array.splice(position,1,item);
+var
+  ArrEl: TJSElement;
+  Call: TJSCallExpression;
+begin
+  Result:=nil;
+  Call:=nil;
+  try
+    Call:=CreateCallExpression(El);
+    ArrEl:=ConvertElement(El.Params[1],AContext);
+    Call.Expr:=CreateDotExpression(El,ArrEl,CreateBuiltInIdentifierExpr('splice'));
+    Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[2],AContext);
+    Call.Args.Elements.AddElement.Expr:=CreateLiteralNumber(El,1);
+    Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[0],AContext);
+    Result:=Call;
+  finally
+    if Result=nil then
+      Call.Free;
+  end;
+end;
+
+function TPasToJSConverter.ConvertBuiltInDeleteArray(El: TParamsExpr;
+  AContext: TConvertContext): TJSElement;
+// proc delete(var array,const start,count)
+// ->  array.splice(start,count)
+var
+  ArrEl: TJSElement;
+  Call: TJSCallExpression;
+begin
+  Result:=nil;
+  Call:=nil;
+  try
+    Call:=CreateCallExpression(El);
+    ArrEl:=ConvertElement(El.Params[0],AContext);
+    Call.Expr:=CreateDotExpression(El,ArrEl,CreateBuiltInIdentifierExpr('splice'));
+    Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[1],AContext);
+    Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[2],AContext);
+    Result:=Call;
+  finally
+    if Result=nil then
+      Call.Free;
+  end;
+end;
+
 function TPasToJSConverter.ConvertRecordValues(El: TRecordValues;
 function TPasToJSConverter.ConvertRecordValues(El: TRecordValues;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
@@ -7966,6 +8083,7 @@ function TPasToJSConverter.CreateReferencePath(El: TPasElement;
    - in initialization 'this' is interface
    - in initialization 'this' is interface
    - in method body 'this' is the instance
    - in method body 'this' is the instance
    - in class method body 'this' is the class
    - in class method body 'this' is the class
+ - with context uses the local $withnnn var
  otherwise use absolute path
  otherwise use absolute path
 }
 }
 
 

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

@@ -274,7 +274,10 @@ type
     Procedure TestArray_SetLengthProperty;
     Procedure TestArray_SetLengthProperty;
     Procedure TestArray_OpenArrayOfString;
     Procedure TestArray_OpenArrayOfString;
     Procedure TestArray_Concat;
     Procedure TestArray_Concat;
+    Procedure TestArray_Copy;
+    Procedure TestArray_InsertDelete;
     Procedure TestExternalClass_TypeCastArrayToExternalArray;
     Procedure TestExternalClass_TypeCastArrayToExternalArray;
+    Procedure TestExternalClass_TypeCastArrayFromExternalArray;
     // ToDo: const array
     // ToDo: const array
     // ToDo: SetLength(array of static array)
     // ToDo: SetLength(array of static array)
 
 
@@ -363,6 +366,7 @@ type
     Procedure TestExternalClass_NewInstance_SecondParamTyped_Fail;
     Procedure TestExternalClass_NewInstance_SecondParamTyped_Fail;
     Procedure TestExternalClass_TypeCastToRootClass;
     Procedure TestExternalClass_TypeCastToRootClass;
     Procedure TestExternalClass_TypeCastStringToExternalString;
     Procedure TestExternalClass_TypeCastStringToExternalString;
+    Procedure TestExternalClass_CallClassFunctionOfInstanceFail;
 
 
     // proc types
     // proc types
     Procedure TestProcType;
     Procedure TestProcType;
@@ -4549,6 +4553,146 @@ begin
     '']));
     '']));
 end;
 end;
 
 
+procedure TTestModule.TestArray_Copy;
+begin
+  StartProgram(false);
+  Add('type');
+  Add('  integer = longint;');
+  Add('  TFlag = (big,small);');
+  Add('  TFlags = set of TFlag;');
+  Add('  TRec = record');
+  Add('    i: integer;');
+  Add('  end;');
+  Add('  TArrInt = array of integer;');
+  Add('  TArrRec = array of TRec;');
+  Add('  TArrSet = array of TFlags;');
+  Add('  TArrJSValue = array of jsvalue;');
+  Add('var');
+  Add('  ArrInt: tarrint;');
+  Add('  ArrRec: tarrrec;');
+  Add('  ArrSet: tarrset;');
+  Add('  ArrJSValue: tarrjsvalue;');
+  Add('begin');
+  Add('  arrint:=copy(arrint);');
+  Add('  arrint:=copy(arrint,2);');
+  Add('  arrint:=copy(arrint,3,4);');
+  Add('  arrrec:=copy(arrrec);');
+  Add('  arrrec:=copy(arrrec,5);');
+  Add('  arrrec:=copy(arrrec,6,7);');
+  Add('  arrset:=copy(arrset);');
+  Add('  arrset:=copy(arrset,8);');
+  Add('  arrset:=copy(arrset,9,10);');
+  Add('  arrjsvalue:=copy(arrjsvalue);');
+  Add('  arrjsvalue:=copy(arrjsvalue,11);');
+  Add('  arrjsvalue:=copy(arrjsvalue,12,13);');
+  ConvertProgram;
+  CheckSource('TestArray_Copy',
+    LinesToStr([ // statements
+    'this.TFlag = {',
+    '  "0": "big",',
+    '  big: 0,',
+    '  "1": "small",',
+    '  small: 1',
+    '};',
+    'this.TRec = function (s) {',
+    '  if (s) {',
+    '    this.i = s.i;',
+    '  } else {',
+    '    this.i = 0;',
+    '  };',
+    '  this.$equal = function (b) {',
+    '    return this.i == b.i;',
+    '  };',
+    '};',
+    'this.ArrInt = [];',
+    'this.ArrRec = [];',
+    'this.ArrSet = [];',
+    'this.ArrJSValue = [];',
+    '']),
+    LinesToStr([ // this.$main
+    'this.ArrInt = rtl.arrayCopy(0, this.ArrInt, 0);',
+    'this.ArrInt = rtl.arrayCopy(0, this.ArrInt, 2);',
+    'this.ArrInt = rtl.arrayCopy(0, this.ArrInt, 3, 4);',
+    'this.ArrRec = rtl.arrayCopy(this.TRec, this.ArrRec, 0);',
+    'this.ArrRec = rtl.arrayCopy(this.TRec, this.ArrRec, 5);',
+    'this.ArrRec = rtl.arrayCopy(this.TRec, this.ArrRec, 6, 7);',
+    'this.ArrSet = rtl.arrayCopy("refSet", this.ArrSet, 0);',
+    'this.ArrSet = rtl.arrayCopy("refSet", this.ArrSet, 8);',
+    'this.ArrSet = rtl.arrayCopy("refSet", this.ArrSet, 9, 10);',
+    'this.ArrJSValue = rtl.arrayCopy(0, this.ArrJSValue, 0);',
+    'this.ArrJSValue = rtl.arrayCopy(0, this.ArrJSValue, 11);',
+    'this.ArrJSValue = rtl.arrayCopy(0, this.ArrJSValue, 12, 13);',
+    '']));
+end;
+
+procedure TTestModule.TestArray_InsertDelete;
+begin
+  StartProgram(false);
+  Add('type');
+  Add('  integer = longint;');
+  Add('  TFlag = (big,small);');
+  Add('  TFlags = set of TFlag;');
+  Add('  TRec = record');
+  Add('    i: integer;');
+  Add('  end;');
+  Add('  TArrInt = array of integer;');
+  Add('  TArrRec = array of TRec;');
+  Add('  TArrSet = array of TFlags;');
+  Add('  TArrJSValue = array of jsvalue;');
+  Add('var');
+  Add('  ArrInt: tarrint;');
+  Add('  ArrRec: tarrrec;');
+  Add('  ArrSet: tarrset;');
+  Add('  ArrJSValue: tarrjsvalue;');
+  Add('begin');
+  Add('  Insert(1,arrint,2);');
+  Add('  Insert(arrint[3],arrint,4);');
+  Add('  Insert(arrrec[5],arrrec,6);');
+  Add('  Insert(arrset[7],arrset,7);');
+  Add('  Insert(arrjsvalue[8],arrjsvalue,9);');
+  Add('  Insert(10,arrjsvalue,11);');
+  Add('  Delete(arrint,12,13);');
+  Add('  Delete(arrrec,14,15);');
+  Add('  Delete(arrset,17,18);');
+  Add('  Delete(arrjsvalue,19,10);');
+  ConvertProgram;
+  CheckSource('TestArray_InsertDelete',
+    LinesToStr([ // statements
+    'this.TFlag = {',
+    '  "0": "big",',
+    '  big: 0,',
+    '  "1": "small",',
+    '  small: 1',
+    '};',
+    'this.TRec = function (s) {',
+    '  if (s) {',
+    '    this.i = s.i;',
+    '  } else {',
+    '    this.i = 0;',
+    '  };',
+    '  this.$equal = function (b) {',
+    '    return this.i == b.i;',
+    '  };',
+    '};',
+    'this.ArrInt = [];',
+    'this.ArrRec = [];',
+    'this.ArrSet = [];',
+    'this.ArrJSValue = [];',
+    '']),
+    LinesToStr([ // this.$main
+    'this.ArrInt.splice(2, 1, 1);',
+    'this.ArrInt.splice(4, 1, this.ArrInt[3]);',
+    'this.ArrRec.splice(6, 1, this.ArrRec[5]);',
+    'this.ArrSet.splice(7, 1, this.ArrSet[7]);',
+    'this.ArrJSValue.splice(9, 1, this.ArrJSValue[8]);',
+    'this.ArrJSValue.splice(11, 1, 10);',
+    'this.ArrInt.splice(12, 13);',
+    'this.ArrRec.splice(14, 15);',
+    'this.ArrSet.splice(17, 18);',
+    'this.ArrJSValue.splice(19, 10);',
+    '']));
+end;
+
 procedure TTestModule.TestExternalClass_TypeCastArrayToExternalArray;
 procedure TTestModule.TestExternalClass_TypeCastArrayToExternalArray;
 begin
 begin
   StartProgram(false);
   StartProgram(false);
@@ -4576,6 +4720,32 @@ begin
     '']));
     '']));
 end;
 end;
 
 
+procedure TTestModule.TestExternalClass_TypeCastArrayFromExternalArray;
+begin
+  StartProgram(false);
+  Add('{$modeswitch externalclass}');
+  Add('type');
+  Add('  TArrStr = array of string;');
+  Add('  TJSArray = class external name ''Array''');
+  Add('  end;');
+  Add('var');
+  Add('  aObj: TJSArray;');
+  Add('  a: TArrStr;');
+  Add('begin');
+  Add('  a:=TArrStr(aObj);');
+  Add('  TArrStr(aObj)[1]:=TArrStr(aObj)[2];');
+  ConvertProgram;
+  CheckSource('TestExternalClass_TypeCastArrayFromExternalArray',
+    LinesToStr([ // statements
+    'this.aObj = null;',
+    'this.a = [];',
+    '']),
+    LinesToStr([ // this.$main
+    'this.a = this.aObj;',
+    'this.aObj[1] = this.aObj[2];',
+    '']));
+end;
+
 procedure TTestModule.TestRecord_Var;
 procedure TTestModule.TestRecord_Var;
 begin
 begin
   StartProgram(false);
   StartProgram(false);
@@ -7642,7 +7812,7 @@ begin
   Add('begin');
   Add('begin');
   Add('  texta.year:=texta.year+1;');
   Add('  texta.year:=texta.year+1;');
   Add('  textb.year:=textb.year+2;');
   Add('  textb.year:=textb.year+2;');
-  Add('  a.year:=a.year+3;');
+  Add('  TextA.year:=TextA.year+3;');
   Add('  b.year:=b.year+4;');
   Add('  b.year:=b.year+4;');
   Add('  textb.century:=textb.century+5;');
   Add('  textb.century:=textb.century+5;');
   Add('  b.century:=b.century+6;');
   Add('  b.century:=b.century+6;');
@@ -7672,7 +7842,7 @@ begin
     LinesToStr([ // this.$main
     LinesToStr([ // this.$main
     'ExtA.setYear(ExtA.getYear() + 1);',
     'ExtA.setYear(ExtA.getYear() + 1);',
     'this.TExtB.setYear(this.TExtB.getYear() + 2);',
     'this.TExtB.setYear(this.TExtB.getYear() + 2);',
-    'this.A.setYear(this.A.getYear() + 3);',
+    'ExtA.setYear(ExtA.getYear() + 3);',
     'this.B.setYear(this.B.getYear() + 4);',
     'this.B.setYear(this.B.getYear() + 4);',
     'this.TExtB.SetCentury(this.TExtB.GetCentury() + 5);',
     'this.TExtB.SetCentury(this.TExtB.GetCentury() + 5);',
     'this.B.$class.SetCentury(this.B.$class.GetCentury() + 6);',
     'this.B.$class.SetCentury(this.B.$class.GetCentury() + 6);',
@@ -8290,6 +8460,24 @@ begin
     '']));
     '']));
 end;
 end;
 
 
+procedure TTestModule.TestExternalClass_CallClassFunctionOfInstanceFail;
+begin
+  StartProgram(false);
+  Add('{$modeswitch externalclass}');
+  Add('type');
+  Add('  TJSString = class external name ''String''');
+  Add('    class function fromCharCode() : string; varargs;');
+  Add('  end;');
+  Add('var');
+  Add('  s: string;');
+  Add('  sObj: TJSString;');
+  Add('begin');
+  Add('  s:=sObj.fromCharCode(65,66);');
+  SetExpectedPasResolverError('External class instance cannot access static class function fromCharCode',
+    nExternalClassInstanceCannotAccessStaticX);
+  ConvertProgram;
+end;
+
 procedure TTestModule.TestProcType;
 procedure TTestModule.TestProcType;
 begin
 begin
   StartProgram(false);
   StartProgram(false);