فهرست منبع

pastojs: fixed float / 0.0 results at compiletime in inf instead of divbyzero, issue 38815

mattias 4 سال پیش
والد
کامیت
2212fd0fcf

+ 50 - 35
compiler/packages/fcl-passrc/src/pasresolveeval.pas

@@ -747,6 +747,7 @@ type
     procedure SuccUnicodeString(Value: TResEvalUTF16; ErrorEl: TPasElement);
     procedure PredEnum(Value: TResEvalEnum; ErrorEl: TPasElement);
     procedure SuccEnum(Value: TResEvalEnum; ErrorEl: TPasElement);
+    function DivideByZero(LeftSign, RightSign: TValueSign): TMaxPrecFloat;
     function CreateResEvalInt(UInt: TMaxPrecUInt): TResEvalValue; virtual;
   public
     constructor Create;
@@ -798,6 +799,7 @@ type
 
 procedure ReleaseEvalValue(var Value: TResEvalValue);
 function NumberIsFloat(const Value: string): boolean;
+function Sign(const Value: TMaxPrecUInt): TValueSign; overload;
 
 {$ifdef FPC_HAS_CPSTRING}
 function RawStrToCaption(const r: RawByteString; MaxLength: integer): string;
@@ -836,6 +838,14 @@ begin
   Result:=false;
 end;
 
+function Sign(const Value: TMaxPrecUInt): TValueSign;
+begin
+  if Value>0 then
+    Result:=1
+  else
+    Result:=0;
+end;
+
 {$ifdef FPC_HAS_CPSTRING}
 function RawStrToCaption(const r: RawByteString; MaxLength: integer): string;
 var
@@ -2501,32 +2511,32 @@ var
   aCurrency: TMaxPrecCurrency;
 begin
   Result:=nil;
+  Flo:=0.0;
   case LeftValue.Kind of
   revkInt:
     begin
     Int:=TResEvalInt(LeftValue).Int;
     case RightValue.Kind of
     revkInt:
+      begin
       // int / int
       if TResEvalInt(RightValue).Int=0 then
-        RaiseDivByZero(20170711143925,Expr)
+        Flo:=DivideByZero(Sign(Int),Sign(TResEvalInt(RightValue).Int))
       else
-        Result:=TResEvalFloat.CreateValue(Int / TResEvalInt(RightValue).Int);
+        Flo:=Int / TResEvalInt(RightValue).Int;
+      end;
     revkUInt:
       // int / uint
       if TResEvalUInt(RightValue).UInt=0 then
-        RaiseDivByZero(20170711144013,Expr)
+        Flo:=DivideByZero(Math.Sign(Int),Sign(TResEvalUInt(RightValue).UInt))
       else
-        Result:=TResEvalFloat.CreateValue(Int / TResEvalUInt(RightValue).UInt);
+        Flo:=Int / TResEvalUInt(RightValue).UInt;
     revkFloat:
-      begin
       // int / float
       try
         Flo:=Int / TResEvalFloat(RightValue).FloatValue;
       except
-        RaiseMsg(20170711144525,nDivByZero,sDivByZero,[],Expr);
-      end;
-      Result:=TResEvalFloat.CreateValue(Flo);
+        Flo:=DivideByZero(Sign(Int),Sign(TResEvalFloat(RightValue).FloatValue))
       end;
     revkCurrency:
       begin
@@ -2537,6 +2547,7 @@ begin
         RaiseMsg(20180421164915,nDivByZero,sDivByZero,[],Expr);
       end;
       Result:=TResEvalCurrency.CreateValue(aCurrency);
+      exit;
       end;
     else
       {$IFDEF VerbosePasResolver}
@@ -2552,24 +2563,21 @@ begin
     revkInt:
       // uint / int
       if TResEvalInt(RightValue).Int=0 then
-        RaiseDivByZero(20170711144103,Expr)
+        Flo:=DivideByZero(Sign(UInt),Sign(TResEvalInt(RightValue).Int))
       else
-        Result:=TResEvalFloat.CreateValue(UInt / TResEvalInt(RightValue).Int);
+        Flo:=UInt / TResEvalInt(RightValue).Int;
     revkUInt:
       // uint / uint
       if TResEvalUInt(RightValue).UInt=0 then
-        RaiseDivByZero(20170711144203,Expr)
+        Flo:=DivideByZero(Sign(UInt),Sign(TResEvalUInt(RightValue).UInt))
       else
-        Result:=TResEvalFloat.CreateValue(UInt / TResEvalUInt(RightValue).UInt);
+        Flo:=UInt / TResEvalUInt(RightValue).UInt;
     revkFloat:
-      begin
       // uint / float
       try
         Flo:=UInt / TResEvalFloat(RightValue).FloatValue;
       except
-        RaiseMsg(20170711144912,nDivByZero,sDivByZero,[],Expr);
-      end;
-      Result:=TResEvalFloat.CreateValue(Flo);
+        Flo:=DivideByZero(Sign(UInt),Sign(TResEvalFloat(RightValue).FloatValue))
       end;
     revkCurrency:
       begin
@@ -2580,6 +2588,7 @@ begin
         RaiseMsg(20180421164959,nDivByZero,sDivByZero,[],Expr);
       end;
       Result:=TResEvalCurrency.CreateValue(aCurrency);
+      exit;
       end;
     else
       {$IFDEF VerbosePasResolver}
@@ -2595,24 +2604,21 @@ begin
     revkInt:
       // float / int
       if TResEvalInt(RightValue).Int=0 then
-        RaiseDivByZero(20170711144954,Expr)
+        Flo:=DivideByZero(Sign(Flo),Sign(TResEvalInt(RightValue).Int))
       else
-        Result:=TResEvalFloat.CreateValue(Flo / TResEvalInt(RightValue).Int);
+        Flo:=Flo / TResEvalInt(RightValue).Int;
     revkUInt:
       // float / uint
       if TResEvalUInt(RightValue).UInt=0 then
-        RaiseDivByZero(20170711145023,Expr)
+        Flo:=DivideByZero(Sign(Flo),Sign(TResEvalUInt(RightValue).UInt))
       else
-        Result:=TResEvalFloat.CreateValue(Flo / TResEvalUInt(RightValue).UInt);
+        Flo:=Flo / TResEvalUInt(RightValue).UInt;
     revkFloat:
-      begin
       // float / float
       try
         Flo:=Flo / TResEvalFloat(RightValue).FloatValue;
       except
-        RaiseMsg(20170711145040,nDivByZero,sDivByZero,[],Expr);
-      end;
-      Result:=TResEvalFloat.CreateValue(Flo);
+        Flo:=DivideByZero(Sign(Flo),Sign(TResEvalFloat(RightValue).FloatValue))
       end;
     revkCurrency:
       begin
@@ -2638,48 +2644,45 @@ begin
     revkInt:
       // currency / int
       if TResEvalInt(RightValue).Int=0 then
-        RaiseDivByZero(20180421165154,Expr)
+        RaiseMsg(20210515133307,nDivByZero,sDivByZero,[],Expr)
       else
-        Result:=TResEvalCurrency.CreateValue(aCurrency / TResEvalInt(RightValue).Int);
+        aCurrency:=aCurrency / TResEvalInt(RightValue).Int;
     revkUInt:
       // currency / uint
       if TResEvalUInt(RightValue).UInt=0 then
-        RaiseDivByZero(20180421165205,Expr)
+        RaiseMsg(20210515133318,nDivByZero,sDivByZero,[],Expr)
       else
-        Result:=TResEvalCurrency.CreateValue(aCurrency / TResEvalUInt(RightValue).UInt);
+        aCurrency:=aCurrency / TResEvalUInt(RightValue).UInt;
     revkFloat:
-      begin
       // currency / float
       try
         aCurrency:=aCurrency / TResEvalFloat(RightValue).FloatValue;
       except
         RaiseMsg(20180421165237,nDivByZero,sDivByZero,[],Expr);
       end;
-      Result:=TResEvalCurrency.CreateValue(aCurrency);
-      end;
     revkCurrency:
-      begin
       // currency / currency
       try
         aCurrency:=aCurrency / TResEvalCurrency(RightValue).Value;
       except
         RaiseMsg(20180421165252,nDivByZero,sDivByZero,[],Expr);
       end;
-      Result:=TResEvalCurrency.CreateValue(aCurrency);
-      end;
     else
       {$IFDEF VerbosePasResolver}
       writeln('TResExprEvaluator.EvalBinaryDivideExpr currency / ? Left=',LeftValue.AsDebugString,' Right=',RightValue.AsDebugString);
       {$ENDIF}
       RaiseNotYetImplemented(20180421165301,Expr);
     end;
+    Result:=TResEvalCurrency.CreateValue(aCurrency);
+    exit;
     end;
   else
     {$IFDEF VerbosePasResolver}
-    writeln('TResExprEvaluator.EvalBinaryDivExpr div ?- Left=',LeftValue.AsDebugString,' Right=',RightValue.AsDebugString);
+    writeln('TResExprEvaluator.EvalBinaryDivExpr ? / - Left=',LeftValue.AsDebugString,' Right=',RightValue.AsDebugString);
     {$ENDIF}
     RaiseNotYetImplemented(20170530102352,Expr);
   end;
+  Result:=TResEvalFloat.CreateValue(Flo);
 end;
 
 function TResExprEvaluator.EvalBinaryDivExpr(Expr: TBinaryExpr; LeftValue,
@@ -5601,6 +5604,18 @@ begin
   Value.IdentEl:=TPasEnumValue(EnumType.Values[Value.Index]);
 end;
 
+function TResExprEvaluator.DivideByZero(LeftSign, RightSign: TValueSign
+  ): TMaxPrecFloat;
+// FPC/Delphi compatibility: exception at runtime, no exception at compile time
+begin
+  if LeftSign=0 then
+    Result:=0.0
+  else if (LeftSign<0)<>(RightSign<0) then
+    Result:=Math.NegInfinity
+  else
+    Result:=Math.Infinity;
+end;
+
 { TResolveData }
 
 procedure TResolveData.SetElement(AValue: TPasElement);

+ 26 - 1
compiler/packages/pastojs/src/fppas2js.pp

@@ -2029,6 +2029,7 @@ type
     Function CreateVarDecl(const aName: String; Init: TJSElement; El: TPasElement): TJSVarDeclaration; virtual;
     // JS literals
     Function CreateLiteralNumber(El: TPasElement; const n: TJSNumber): TJSLiteral; virtual;
+    Function CreateLiteralFloat(El: TPasElement; const n: TJSNumber): TJSElement; virtual;
     Function CreateLiteralHexNumber(El: TPasElement; const n: TMaxPrecInt; Digits: byte): TJSLiteral; virtual;
     Function CreateLiteralString(El: TPasElement; const s: string): TJSLiteral; virtual;
     Function CreateLiteralJSString(El: TPasElement; const s: TJSString): TJSLiteral; virtual;
@@ -17635,7 +17636,7 @@ begin
   revkUInt:
     Result:=CreateLiteralNumber(El,TResEvalUInt(Value).UInt);
   revkFloat:
-    Result:=CreateLiteralNumber(El,TResEvalFloat(Value).FloatValue);
+    Result:=CreateLiteralFloat(El,TResEvalFloat(Value).FloatValue);
   {$IFDEF FPC_HAS_CPSTRING}
   revkString:
     Result:=CreateLiteralString(El,TResEvalString(Value).S);
@@ -24182,6 +24183,30 @@ begin
   Result.Value.AsNumber:=n;
 end;
 
+function TPasToJSConverter.CreateLiteralFloat(El: TPasElement;
+  const n: TJSNumber): TJSElement;
+var
+  DivExpr: TJSMultiplicativeExpressionDiv;
+  Lit: TJSLiteral;
+begin
+  if IsInfinite(n) then
+    begin
+    DivExpr:=TJSMultiplicativeExpressionDiv(CreateElement(TJSMultiplicativeExpressionDiv,El));
+    if n<0 then
+      DivExpr.A:=CreateLiteralNumber(El,-1)
+    else
+      DivExpr.A:=CreateLiteralNumber(El,1);
+    DivExpr.B:=CreateLiteralNumber(El,0);
+    Result:=DivExpr;
+    end
+  else
+    begin
+    Lit:=TJSLiteral(CreateElement(TJSLiteral,El));
+    Lit.Value.AsNumber:=n;
+    Result:=Lit;
+    end;
+end;
+
 function TPasToJSConverter.CreateLiteralHexNumber(El: TPasElement;
   const n: TMaxPrecInt; Digits: byte): TJSLiteral;
 begin

+ 13 - 2
compiler/packages/pastojs/tests/tcmodules.pas

@@ -7078,11 +7078,15 @@ begin
   '  Test999 = 2.9999999999999;',
   '  Test111999 = 211199999999999000.0;',
   '  TestMinus111999 = -211199999999999000.0;',
+  '  Inf = 1.0 / 0.0;',
+  '  NegInf = -1.0 / 0.0;',
+  'procedure Run(d: double); external name ''Run'';',
   'var',
   '  d: double = b;',
   'begin',
   '  d:=1.0;',
   '  d:=1.0/3.0;',
+  '  d:=1.0/(3-2-1);',
   '  d:=1/3;',
   '  d:=5.0E-324;',
   '  d:=1.7E308;',
@@ -7115,6 +7119,8 @@ begin
   '  d:=double(MinSafeIntDouble2);',
   '  d:=MaxSafeIntDouble;',
   '  d:=default(double);',
+  '  Run(Inf);',
+  '  Run(NegInf);',
   '']);
   ConvertProgram;
   CheckSource('TestDouble',
@@ -7148,11 +7154,14 @@ begin
     'this.Test999 = 2.9999999999999;',
     'this.Test111999 = 211199999999999000.0;',
     'this.TestMinus111999 = -211199999999999000.0;',
-    'this.d = 4.4;'
-    ]),
+    'this.Inf = 1.0 / 0.0;',
+    'this.NegInf = -1.0 / 0.0;',
+    'this.d = 4.4;',
+    '']),
     LinesToStr([
     '$mod.d = 1.0;',
     '$mod.d = 1.0 / 3.0;',
+    '$mod.d = 1.0 / (3 - 2 - 1);',
     '$mod.d = 1 / 3;',
     '$mod.d = 5.0E-324;',
     '$mod.d = 1.7E308;',
@@ -7185,6 +7194,8 @@ begin
     '$mod.d = -9.007199254740992E15;',
     '$mod.d = 9007199254740991;',
     '$mod.d = 0.0;',
+    'Run(1 / 0);',
+    'Run(-1 / 0);',
     '']));
 end;