소스 검색

* Patch from Mattias Gaertner: Arrays can be null

git-svn-id: trunk@35705 -
michael 8 년 전
부모
커밋
d4d31508c3
1개의 변경된 파일63개의 추가작업 그리고 58개의 파일을 삭제
  1. 63 58
      packages/pastojs/src/fppas2js.pp

+ 63 - 58
packages/pastojs/src/fppas2js.pp

@@ -103,14 +103,15 @@ Works:
   - reintroduced variables
   - external vars and methods
 - dynamic arrays
-  - init as "arr = []"  arrays must never be null
+  - arrays can be null
+  - init as "arr = []"  so typeof works
   - SetLength(arr,len) becomes  arr = SetLength(arr,len,defaultvalue)
   - length(), low(), high(), assigned(), concat()
-  - assign nil -> []  arrays must never be null
+  - assign nil -> []  so typeof works
   - read, write element arr[index]
   - multi dimensional [index1,index2] -> [index1][index2]
   - array of record
-  - equal, unequal nil -> array.length == 0
+  - equal, unequal nil -> rtl.length(array)==0  or >0
   - when passing nil to an array argument, pass []
   - allow type casting array to external class name 'Array'
   - type cast array to array of same dimensions and compatible element type
@@ -208,7 +209,7 @@ Works:
   - operators equal, not equal
 
 ToDos:
-- null arrays
+- proc type of nested function
 - [] operator of external class 'Array'
 - FuncName:= (instead of Result:=)
 - ord(s[i]) -> s.charCodeAt(i)
@@ -319,6 +320,7 @@ type
   TPas2JSBuiltInName = (
     pbifnArray_Concat,
     pbifnArray_Copy,
+    pbifnArray_Length,
     pbifnArray_NewMultiDim,
     pbifnArray_SetLength,
     pbifnAs,
@@ -365,6 +367,7 @@ const
   Pas2JSBuiltInNames: array[TPas2JSBuiltInName] of string = (
     'arrayConcat', // rtl.arrayConcat
     'arrayCopy', // rtl.arrayCopy
+    'length', // rtl.length
     'arrayNewMultiDim', // rtl.arrayNewMultiDim
     'arraySetLength', // rtl.arraySetLength
     'as', // rtl.as
@@ -930,6 +933,7 @@ type
       El: TPasElement; AContext: TConvertContext): TJSElement; virtual;
     Function CreateArrayInit(ArrayType: TPasArrayType; Expr: TPasElement;
       El: TPasElement; AContext: TConvertContext): TJSElement; virtual;
+    Function CreateCmpArrayWithNil(El: TPasElement; JSArray: TJSElement; OpCode: TExprOpCode): TJSElement; virtual;
     Function CreateReferencePath(El: TPasElement; AContext : TConvertContext;
       Kind: TRefPathKind; Full: boolean = false; Ref: TResolvedReference = nil): string; virtual;
     Function CreateReferencePathExpr(El: TPasElement; AContext : TConvertContext; Full: boolean = false; Ref: TResolvedReference = nil): TJSPrimaryExpressionIdent; virtual;
@@ -2954,8 +2958,6 @@ Var
   Flags: TPasResolverComputeFlags;
   ModeSwitches: TModeSwitches;
   NotEl: TJSUnaryNotExpression;
-  CmpExpr: TJSBinaryExpression;
-
 begin
   Result:=Nil;
 
@@ -3119,13 +3121,10 @@ begin
           begin
           if RightResolved.BaseType=btNil then
             begin
-            // convert "array = nil" to "array.length == 0"
+            // convert "array = nil" to "rtl.length(array) > 0"
             FreeAndNil(B);
-            CmpExpr:=TJSEqualityExpressionEQ(CreateElement(BinClasses[El.OpCode],El));
-            CmpExpr.A:=CreateDotExpression(El,A,CreateBuiltInIdentifierExpr('length'));
+            Result:=CreateCmpArrayWithNil(El,A,El.OpCode);
             A:=nil;
-            CmpExpr.B:=CreateLiteralNumber(El.right,0);
-            Result:=CmpExpr;
             exit;
             end;
           end
@@ -3133,13 +3132,10 @@ begin
           begin
           if LeftResolved.BaseType=btNil then
             begin
-            // convert "nil = array" to "0 == array.length"
+            // convert "nil = array" to "0 < rtl.length(array)"
             FreeAndNil(A);
-            CmpExpr:=TJSEqualityExpressionEQ(CreateElement(BinClasses[El.OpCode],El));
-            CmpExpr.A:=CreateLiteralNumber(El.left,0);
-            CmpExpr.B:=CreateDotExpression(El,B,CreateBuiltInIdentifierExpr('length'));
+            Result:=CreateCmpArrayWithNil(El,B,El.OpCode);
             B:=nil;
-            Result:=CmpExpr;
             exit;
             end;
           end;
@@ -4652,14 +4648,12 @@ end;
 
 function TPasToJSConverter.ConvertBuiltInLength(El: TParamsExpr;
   AContext: TConvertContext): TJSElement;
-// length(dynarray) -> array.length
-// length(staticarray) -> number
-// length(string) -> string.length
 var
   Arg: TJSElement;
   Param: TPasExpr;
   ParamResolved, RangeResolved: TPasResolverResult;
   Ranges: TPasExprArray;
+  Call: TJSCallExpression;
 begin
   Result:=nil;
   Param:=El.Params[0];
@@ -4671,7 +4665,7 @@ begin
       Ranges:=TPasArrayType(ParamResolved.TypeEl).Ranges;
       if length(Ranges)>0 then
         begin
-        // static array
+        // static array -> number literal
         if length(Ranges)>1 then
           RaiseNotSupported(El,AContext,20170223131042);
         AContext.Resolver.ComputeElement(Ranges[0],RangeResolved,[rcConstant]);
@@ -4688,6 +4682,17 @@ begin
           Result:=CreateLiteralNumber(El,2);
           exit;
           end;
+        end
+      else
+        begin
+        // dynamic array -> rtl.length(array)
+        Result:=ConvertElement(El.Params[0],AContext);
+        // Note: convert param first, it may raise an exception
+        Call:=CreateCallExpression(El);
+        Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Length]]);
+        Call.Args.Elements.AddElement.Expr:=Result;
+        Result:=Call;
+        exit;
         end;
       end;
     end;
@@ -4702,14 +4707,13 @@ function TPasToJSConverter.ConvertBuiltInSetLength(El: TParamsExpr;
 // convert "SetLength(a,Len)" to "a = rtl.arraySetLength(a,Len)"
 var
   Param0: TPasExpr;
-  ResolvedParam0, ArrayElTypeResolved: TPasResolverResult;
+  ResolvedParam0: TPasResolverResult;
   ArrayType: TPasArrayType;
   Call: TJSCallExpression;
   ValInit, Arg: TJSElement;
   AssignSt: TJSSimpleAssignStatement;
   AssignContext: TAssignContext;
   ElType: TPasType;
-  JSBaseType: TPas2jsBaseType;
 begin
   Result:=nil;
   Param0:=El.Params[0];
@@ -4726,43 +4730,16 @@ begin
     {$IFDEF VerbosePasResolver}
     writeln('TPasToJSConverter.ConvertBuiltInSetLength array');
     {$ENDIF}
-    AContext.Resolver.ComputeElement(ArrayType.ElType,ArrayElTypeResolved,[]);
-    if (ArrayElTypeResolved.BaseType=btCustom)
-        and (ArrayElTypeResolved.TypeEl.CustomData is TResElDataPas2JSBaseType) then
-      begin
-      JSBaseType:=TResElDataPas2JSBaseType(ArrayElTypeResolved.TypeEl.CustomData).JSBaseType;
-      if JSBaseType=pbtJSValue then
-        begin
-        // array of jsvalue  ->  "AnArray.length=newlength"
-        AssignSt:=nil;
-        Arg:=nil;
-        try
-          AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
-          Arg:=ConvertElement(Param0,AContext);
-          AssignSt.LHS:=CreateDotExpression(El,Arg,CreateBuiltInIdentifierExpr('length'));
-          Arg:=nil;
-          AssignSt.Expr:=ConvertElement(El.Params[1],AContext);
-          Result:=AssignSt;
-        finally
-          if Result=nil then
-            begin
-            AssignSt.Free;
-            Arg.Free;
-            end;
-        end;
-        exit;
-        end;
-      end;
 
-    // ->  array = rtl.setArrayLength(AnArray,newlength,initvalue)
+    // ->  AnArray = rtl.setArrayLength(AnArray,newlength,initvalue)
     AssignContext:=TAssignContext.Create(El,nil,AContext);
     try
-      AContext.Resolver.ComputeElement(El.Value,AssignContext.LeftResolved,[rcNoImplicitProc]);
+      AContext.Resolver.ComputeElement(Param0,AssignContext.LeftResolved,[rcNoImplicitProc]);
       AssignContext.RightResolved:=ResolvedParam0;
 
       // create right side
       // rtl.setArrayLength()
-      Call:=CreateCallExpression(Param0);
+      Call:=CreateCallExpression(El);
       AssignContext.RightSide:=Call;
       Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_SetLength]]);
       // 1st param: AnArray
@@ -4908,6 +4885,7 @@ var
   ParamResolved: TPasResolverResult;
   C: TClass;
   GT: TJSRelationalExpressionGT;
+  Call: TJSCallExpression;
 begin
   Result:=nil;
   if AContext.Resolver=nil then
@@ -4934,11 +4912,14 @@ begin
       end
     else if C=TPasArrayType then
       begin
-      // convert Assigned(value)  ->  value.length>0
+      // convert Assigned(value)  ->  rtl.length(value)>0
       Result:=ConvertElement(Param,AContext);
       // Note: convert Param first, it may raise an exception
       GT:=TJSRelationalExpressionGT(CreateElement(TJSRelationalExpressionGT,El));
-      GT.A:=CreateDotExpression(El,Result,CreateBuiltInIdentifierExpr('length'));
+      Call:=CreateCallExpression(El);
+      Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Length]]);
+      Call.Args.Elements.AddElement.Expr:=Result;
+      GT.A:=Call;
       GT.B:=CreateLiteralNumber(El,0);
       Result:=GT;
       end
@@ -5123,8 +5104,8 @@ var
   ResolvedEl, RangeResolved: TPasResolverResult;
   Param, Range: TPasExpr;
   TypeEl: TPasType;
-  Arg: TJSElement;
   MinusExpr: TJSAdditiveExpressionMinus;
+  Call: TJSCallExpression;
 begin
   Result:=nil;
   if AContext.Resolver=nil then
@@ -5153,10 +5134,14 @@ begin
         begin
         if length(TPasArrayType(TypeEl).Ranges)=0 then
           begin
-          // dynamic array -> Param.length-1
-          Arg:=ConvertElement(Param,AContext);
+          // dynamic array -> rtl.length(Param)-1
+          Result:=ConvertElement(Param,AContext);
+          // Note: convert Param first, it may raise an exception
+          Call:=CreateCallExpression(El);
+          Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Length]]);
+          Call.Args.Elements.AddElement.Expr:=Result;
           MinusExpr:=TJSAdditiveExpressionMinus(CreateElement(TJSAdditiveExpressionMinus,El));
-          MinusExpr.A:=CreateDotExpression(Param,Arg,CreateBuiltInIdentifierExpr('length'));
+          MinusExpr.A:=Call;
           MinusExpr.B:=CreateLiteralNumber(El,1);
           Result:=MinusExpr;
           exit;
@@ -8071,6 +8056,26 @@ begin
     end;
 end;
 
+function TPasToJSConverter.CreateCmpArrayWithNil(El: TPasElement;
+  JSArray: TJSElement; OpCode: TExprOpCode): TJSElement;
+var
+  Call: TJSCallExpression;
+  BinExpr: TJSBinaryExpression;
+begin
+  if not (OpCode in [eopEqual,eopNotEqual]) then
+    RaiseInconsistency(20170401184819);
+  Call:=CreateCallExpression(El);
+  Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Length]]);
+  Call.Args.Elements.AddElement.Expr:=JSArray;
+  if OpCode=eopEqual then
+    BinExpr:=TJSEqualityExpressionEQ(CreateElement(TJSEqualityExpressionEQ,El))
+  else
+    BinExpr:=TJSRelationalExpressionGT(CreateElement(TJSRelationalExpressionGT,El));
+  BinExpr.A:=Call;
+  BinExpr.B:=CreateLiteralNumber(El,0);
+  Result:=BinExpr;
+end;
+
 function TPasToJSConverter.CreateReferencePath(El: TPasElement;
   AContext: TConvertContext; Kind: TRefPathKind; Full: boolean;
   Ref: TResolvedReference): string;