* Patch from Mattias Gaertner: Arrays can be null

git-svn-id: trunk@35705 -
This commit is contained in:
michael 2017-04-01 18:22:28 +00:00
parent ad99d1b015
commit d4d31508c3

View File

@ -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;