From 4fdeada0a5b43442f09ebe64cee3cac9f2a2a860 Mon Sep 17 00:00:00 2001 From: mattias <nc-gaertnma@netcologne.de> Date: Wed, 18 Aug 2021 09:45:26 +0200 Subject: [PATCH] pastojs: emulate compile time assign integer constant of different type --- packages/fcl-passrc/src/pasresolveeval.pas | 4 +- packages/fcl-passrc/src/pasresolver.pp | 22 ++++- packages/pastojs/src/fppas2js.pp | 107 ++++++++++++++++++++- packages/pastojs/tests/tcmodules.pas | 105 +++++++++++++++++++- 4 files changed, 230 insertions(+), 8 deletions(-) diff --git a/packages/fcl-passrc/src/pasresolveeval.pas b/packages/fcl-passrc/src/pasresolveeval.pas index ef77d79f55..3731543297 100644 --- a/packages/fcl-passrc/src/pasresolveeval.pas +++ b/packages/fcl-passrc/src/pasresolveeval.pas @@ -787,7 +787,7 @@ type function EvalStrFunc(Params: TParamsExpr; Flags: TResEvalFlags): TResEvalValue; virtual; function EvalStringAddExpr(Expr, LeftExpr, RightExpr: TPasExpr; LeftValue, RightValue: TResEvalValue): TResEvalValue; virtual; - function LoHiValue(Value: TResEvalValue; ShiftSize: Integer; Mask: LongWord; + function ShiftAndMaskValue(Value: TResEvalValue; ShiftSize: Integer; Mask: LongWord; ErrorEl: TPasElement): TResEvalValue; virtual; function EnumTypeCast(EnumType: TPasEnumType; Expr: TPasExpr; Flags: TResEvalFlags): TResEvalEnum; virtual; @@ -5273,7 +5273,7 @@ begin end; end; -function TResExprEvaluator.LoHiValue(Value: TResEvalValue; ShiftSize: Integer; +function TResExprEvaluator.ShiftAndMaskValue(Value: TResEvalValue; ShiftSize: Integer; Mask: LongWord; ErrorEl: TPasElement): TResEvalValue; var uint: LongWord; diff --git a/packages/fcl-passrc/src/pasresolver.pp b/packages/fcl-passrc/src/pasresolver.pp index f6619307a2..45d10fea4c 100644 --- a/packages/fcl-passrc/src/pasresolver.pp +++ b/packages/fcl-passrc/src/pasresolver.pp @@ -20088,7 +20088,7 @@ begin try ComputeElement(Param,ResolvedParam,[]); Shift := GetShiftAndMaskForLoHiFunc(ResolvedParam.BaseType, Proc.BuiltIn=bfLo, Mask); - Evaluated := fExprEvaluator.LoHiValue(Value,Shift,Mask,Params); + Evaluated := fExprEvaluator.ShiftAndMaskValue(Value,Shift,Mask,Params); finally ReleaseEvalValue(Value); end; @@ -27920,7 +27920,7 @@ begin writeln('TPasResolver.ComputeElement Unary Kind=',TUnaryExpr(El).Kind,' OpCode=',TUnaryExpr(El).OpCode,' OperandResolved=',GetResolverResultDbg(ResolvedEl),' ',GetElementSourcePosStr(El)); {$ENDIF} case TUnaryExpr(El).OpCode of - eopAdd, eopSubtract: + eopAdd: if ResolvedEl.BaseType in (btAllInteger+btAllFloats) then exit else if IsGenericTemplType(ResolvedEl) then @@ -27928,6 +27928,24 @@ begin else RaiseMsg(20170216152532,nIllegalQualifierInFrontOf,sIllegalQualifierInFrontOf, [OpcodeStrings[TUnaryExpr(El).OpCode],GetResolverResultDescription(ResolvedEl)],El); + eopSubtract: + if ResolvedEl.BaseType in (btAllSignedInteger+btAllFloats) then + exit + else if ResolvedEl.BaseType in btAllInteger then + begin + case ResolvedEl.BaseType of + btByte,btWord: + ResolvedEl.BaseType:=btLongint; + btLongWord,btUIntDouble: + ResolvedEl.BaseType:=btIntDouble; + end; + exit; + end + else if IsGenericTemplType(ResolvedEl) then + exit + else + RaiseMsg(20210815225815,nIllegalQualifierInFrontOf,sIllegalQualifierInFrontOf, + [OpcodeStrings[TUnaryExpr(El).OpCode],GetResolverResultDescription(ResolvedEl)],El); eopNot: begin if ResolvedEl.BaseType in (btAllInteger+btAllBooleans) then diff --git a/packages/pastojs/src/fppas2js.pp b/packages/pastojs/src/fppas2js.pp index 9ebb12f78d..a520805493 100644 --- a/packages/pastojs/src/fppas2js.pp +++ b/packages/pastojs/src/fppas2js.pp @@ -462,6 +462,9 @@ unit FPPas2Js; {$define HasInt64} {$endif} +{$IFOPT Q+}{$DEFINE OverflowCheckOn}{$ENDIF} +{$IFOPT R+}{$DEFINE RangeCheckOn}{$ENDIF} + interface uses @@ -2076,6 +2079,7 @@ type RTLFunc: TPas2JSBuiltInName; PosEl: TPasElement): TJSCallExpression; virtual; Function CreateRangeCheckCall_TypeRange(aType: TPasType; GetExpr: TJSElement; AContext: TConvertContext; PosEl: TPasElement): TJSCallExpression; virtual; + Procedure PrepareAssignDifferentIntegers(El: TPasImplAssign; AssignContext: TAssignContext); virtual; // reference Function CreateReferencePath(El: TPasElement; AContext: TConvertContext; Kind: TRefPathKind; Full: boolean = false; Ref: TResolvedReference = nil): string; virtual; @@ -13745,7 +13749,6 @@ begin end; btString: begin - writeln('AAA1 TPasToJSConverter.ConvertBuiltIn_LowHigh ',IsLow); if isLow then // low(aString) -> 1 Result:=CreateLiteralNumber(El,1) @@ -14262,7 +14265,7 @@ begin RaiseInconsistency(20190129102200,El); Param := El.Params[0]; AContext.Resolver.ComputeElement(Param,ResolvedParam,[]); - if not (ResolvedParam.BaseType in btAllInteger) then + if not (ResolvedParam.BaseType in btAllJSInteger) then DoError(20190129121100,nXExpectedButYFound,sXExpectedButYFound,['integer type', AContext.Resolver.GetResolverResultDescription(ResolvedParam)],Param); Shift := AContext.Resolver.GetShiftAndMaskForLoHiFunc(ResolvedParam.BaseType,IsLoFunc,Mask); @@ -22301,6 +22304,7 @@ begin end; if AssignContext.RightSide=nil then AssignContext.RightSide:=ConvertExpression(El.right,AContext); + if (AssignContext.RightResolved.BaseType in [btSet,btArrayOrSet]) and (AssignContext.RightResolved.IdentEl<>nil) then begin @@ -22335,6 +22339,13 @@ begin // e.g. double := currency -> double := currency/10000 AssignContext.RightSide:=CreateDivideNumber(El,AssignContext.RightSide,10000); end + else if (AssignContext.LeftResolved.BaseType<>AssignContext.RightResolved.BaseType) + and (AssignContext.LeftResolved.BaseType in btAllJSInteger) + and (AssignContext.RightResolved.BaseType in btAllJSInteger) then + begin + // AnInteger := OtherInteger + PrepareAssignDifferentIntegers(El,AssignContext); + end else if AssignContext.RightResolved.BaseType in btAllStringAndChars then begin if AssignContext.LeftResolved.BaseType=btContext then @@ -22539,6 +22550,7 @@ begin if (bsRangeChecks in AContext.ScannerBoolSwitches) and not (T.Expr is TJSLiteral) then begin + // range checks if AssignContext.LeftResolved.BaseType in btAllJSInteger then begin if LeftTypeEl is TPasUnresolvedSymbolRef then @@ -24800,6 +24812,97 @@ begin end; end; +procedure TPasToJSConverter.PrepareAssignDifferentIntegers(El: TPasImplAssign; + AssignContext: TAssignContext); + + function CutToUIntDouble(IntValue: TMaxPrecInt): TMaxPrecInt; + begin + {$IFDEF pas2js} + Result:=((IntValue div $80000000) and $003fffff)*$80000000 +(IntValue and $7FFFFFFF); + {$ELSE} + Result:=IntValue and MaxSafeIntDouble; + {$ENDIF} + end; + +var + aResolver: TPas2JSResolver; + LeftBT, RightBT: TResolverBaseType; + Value: TResEvalValue; + IntValue, LeftMinVal, LeftMaxVal, RightMinVal, RightMaxVal: TMaxPrecInt; +begin + aResolver:=AssignContext.Resolver; + LeftBT:=AssignContext.LeftResolved.BaseType; + RightBT:=AssignContext.RightResolved.BaseType; + + if not aResolver.GetIntegerRange(LeftBT,LeftMinVal,LeftMaxVal) then + RaiseNotSupported(El.left,AssignContext,20210815195159); + if not aResolver.GetIntegerRange(RightBT,RightMinVal,RightMaxVal) then + RaiseNotSupported(El.right,AssignContext,20210815195228); + if (LeftMinVal<=RightMinVal) and (LeftMaxVal>=RightMaxVal) then + exit; // right is subset of left + + // right might not fit into left + + Value:=aResolver.Eval(El.right,[]); + try + if Value<>nil then + begin + if Value.Kind=revkInt then + begin + IntValue:=TResEvalInt(Value).Int; + if (IntValue>=LeftMinVal) and (IntValue<=LeftMaxVal) then + exit; + end + else if Value.Kind=revkUInt then + begin + if TResEvalUInt(Value).UInt<=HighIntAsUInt then + begin + IntValue:=TMaxPrecInt(TResEvalUInt(Value).UInt); + if (IntValue>=LeftMinVal) and (IntValue<=LeftMaxVal) then + exit; + end + else + {$IFDEF Pas2js} + RaiseNotSupported(El.right,AssignContext,20210815214534); + {$ELSE} + IntValue:=PMaxPrecInt(@TResEvalUInt(Value).UInt)^; + {$ENDIF} + end + else + RaiseNotSupported(El.right,AssignContext,20210815204203,'right='+Value.AsDebugString); + + case LeftBT of + btByte: IntValue:=IntValue and $FF; // Note: "and" handles negative numbers + btShortInt: + begin + IntValue:=(IntValue and $FF); + if IntValue>$7F then IntValue:=IntValue-$100; + end; + btWord: IntValue:=IntValue and $FFFF; + btSmallInt: + begin + IntValue:=(IntValue and $FFFF); + if IntValue>$7FFF then IntValue:=IntValue-$10000; + end; + btLongWord: IntValue:=IntValue and $FFFFFFFF; + btLongint: + begin + IntValue:=(IntValue and $FFFFFFFF); + if IntValue>$7FFFFFFF then IntValue:=IntValue-$100000000; + end; + btUIntDouble: + IntValue:=CutToUIntDouble(IntValue); + btIntDouble: + IntValue:=CutToUIntDouble(IntValue); + end; + + AssignContext.RightSide:=CreateLiteralNumber(El.right,IntValue); + end; + finally + ReleaseEvalValue(Value); + end; +end; + function TPasToJSConverter.CreateReferencePath(El: TPasElement; AContext: TConvertContext; Kind: TRefPathKind; Full: boolean; Ref: TResolvedReference): string; diff --git a/packages/pastojs/tests/tcmodules.pas b/packages/pastojs/tests/tcmodules.pas index 9bc86951a8..c258b255de 100644 --- a/packages/pastojs/tests/tcmodules.pas +++ b/packages/pastojs/tests/tcmodules.pas @@ -277,6 +277,7 @@ type Procedure TestInteger_BitwiseShrNativeInt; Procedure TestInteger_BitwiseShlNativeInt; Procedure TestInteger_SystemFunc; + Procedure TestInteger_AssignOutsideConst; Procedure TestCurrency; Procedure TestForBoolDo; Procedure TestForIntDo; @@ -3159,8 +3160,8 @@ begin 'this.HiByte2 = (0x1234 >> 8) & 0xFF;', 'this.LoWord1 = 0x1234CDEF & 0xFFFF;', 'this.HiWord1 = (0x1234CDEF >> 16) & 0xFFFF;', - 'this.LoWord2 = -0x1234CDEF & 0xFFFF;', - 'this.HiWord2 = (-0x1234CDEF >> 16) & 0xFFFF;', + 'this.LoWord2 = -0x1234CDEF >>> 0;', + 'this.HiWord2 = Math.floor(-0x1234CDEF / 4294967296) >>> 0;', 'this.lo4 = 0x34 & 0xF;', 'this.hi4 = (0x34 >> 4) & 0xF;', 'this.lo5 = (((-0x34 & 255) << 24) >> 24) & 0xFF;', @@ -7463,6 +7464,106 @@ begin ''])); end; +procedure TTestModule.TestInteger_AssignOutsideConst; +begin + StartProgram(false); + Add([ + 'const', + ' MinInt = low(longint);', + ' MaxInt = high(longint);', + 'type', + ' {#TMyInt}TMyInt = MinInt..MaxInt;', + 'var', + ' i: TMyInt;', + ' aByte: byte;', + ' aShortInt: shortint;', + ' aWord: word;', + ' aSmallInt: smallint;', + ' aLongWord: longword;', + ' aLongInt: longint;', + ' aNativeInt: nativeint;', + ' aNativeUInt: nativeuint;', + 'begin', + ' aByte:=$FF;', + ' aByte:=$100;', + ' aByte:=-1;', + ' aByte:=-127;', + ' aByte:=-128;', + ' aByte:=-254;', + ' aByte:=-255;', + ' aByte:=-256;', + ' aShortInt:=127;', + ' aShortInt:=128;', + ' aShortInt:=-128;', + ' aShortInt:=-129;', + ' aWord:=$ffff;', + ' aWord:=$10000;', + ' aWord:=-1;', + ' aWord:=-$ffff;', + ' aWord:=-$10000;', + ' aWord:=-$10001;', + ' aSmallInt:=$7fff;', + ' aSmallInt:=$8000;', + ' aSmallInt:=-$8000;', + ' aSmallInt:=-$8001;', + ' aLongWord:=$ffffffff;', + ' aLongWord:=$100000000;', + ' aLongWord:=-1;', + ' aLongWord:=-$ffffffff;', + ' aNativeInt:=$1fffffffffffff;', + ' aNativeInt:=-$1fffffffffffff;', + ' aNativeUInt:=$1fffffffffffff;', + ' aNativeUInt:=-$1fffffffffffff;', + '']); + ConvertProgram; + CheckSource('TestInteger_AssignOutsideConst', + LinesToStr([ + 'this.MinInt = -2147483648;', + 'this.MaxInt = 2147483647;', + 'this.i = 0;', + 'this.aByte = 0;', + 'this.aShortInt = 0;', + 'this.aWord = 0;', + 'this.aSmallInt = 0;', + 'this.aLongWord = 0;', + 'this.aLongInt = 0;', + 'this.aNativeInt = 0;', + 'this.aNativeUInt = 0;', + '']), + LinesToStr([ + '$mod.aByte = 0xFF;', + '$mod.aByte = 0;', + '$mod.aByte = 255;', + '$mod.aByte = 129;', + '$mod.aByte = 128;', + '$mod.aByte = 2;', + '$mod.aByte = 1;', + '$mod.aByte = 0;', + '$mod.aShortInt = 127;', + '$mod.aShortInt = -128;', + '$mod.aShortInt = -128;', + '$mod.aShortInt = 127;', + '$mod.aWord = 0xffff;', + '$mod.aWord = 0;', + '$mod.aWord = 65535;', + '$mod.aWord = 1;', + '$mod.aWord = 0;', + '$mod.aWord = 65535;', + '$mod.aSmallInt = 0x7fff;', + '$mod.aSmallInt = -32768;', + '$mod.aSmallInt = -0x8000;', + '$mod.aSmallInt = 32767;', + '$mod.aLongWord = 0xffffffff;', + '$mod.aLongWord = 0;', + '$mod.aLongWord = 4294967295;', + '$mod.aLongWord = 1;', + '$mod.aNativeInt = 0x1fffffffffffff;', + '$mod.aNativeInt = -0x1fffffffffffff;', + '$mod.aNativeUInt = 0x1fffffffffffff;', + '$mod.aNativeUInt = 1;', + ''])); +end; + procedure TTestModule.TestCurrency; begin StartProgram(false);