Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

* [Dart] Enable explicit variable typing for more data types (by @ncave)
* [Dart] Fix tests for `round`, `sign`, `truncate`, `log`, `log2`, `log10`, `pow`, `DivRem`, `Min`, `Max`, `Clamp`, `MinMagnitude`, `MaxMagnitude`, `cosh`, `sinh`, `tanh`, and float `Parse` (by @ncave)
* [Python] Add `Math.DivRem` support for int, int64, and bigint (by @dbrattli)
* [Python] Fix modulo with negative numbers using Python floored semantics instead of .NET truncated semantics for bigint (fixes #4462) (by @dbrattli)
* [Beam] Fix `System.String.Concat` with 4+ arguments not being supported (by @dbrattli)
Expand Down
2 changes: 2 additions & 0 deletions src/Fable.Compiler/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

* [Dart] Enable explicit variable typing for more data types (by @ncave)
* [Dart] Fix tests for `round`, `sign`, `truncate`, `log`, `log2`, `log10`, `pow`, `DivRem`, `Min`, `Max`, `Clamp`, `MinMagnitude`, `MaxMagnitude`, `cosh`, `sinh`, `tanh`, and float `Parse` (by @ncave)
* [Python] Add `Math.DivRem` support for int, int64, and bigint (by @dbrattli)
* [Python] Fix modulo with negative numbers using Python floored semantics instead of .NET truncated semantics for bigint (fixes #4462) (by @dbrattli)
* [Beam] Fix `System.String.Concat` with 4+ arguments not being supported (by @dbrattli)
Expand Down
10 changes: 6 additions & 4 deletions src/Fable.Transforms/Dart/DartPrinter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,11 @@ module PrinterExtensions =
| DoubleLiteral value ->
let value =
match value.ToString(System.Globalization.CultureInfo.InvariantCulture) with
| "∞" -> "double.infinity"
| "-∞" -> "-double.infinity"
| "∞"
| "Infinity" -> "double.infinity"
| "-∞"
| "-Infinity" -> "double.negativeInfinity"
| "NaN" -> "double.nan"
| value when not (value.Contains(".")) -> value + ".0"
| value -> value

Expand Down Expand Up @@ -1060,9 +1063,8 @@ module PrinterExtensions =
// Print type also if ident and expression types are different?
// (this usually happens when removing unnecessary casts)
match ident.Type with
| Nullable _ -> true
| TypeReference(_, _, info) -> info.IsUnion
| _ -> false
| _ -> true

match kind with
| Const -> printer.Print("const ")
Expand Down
124 changes: 50 additions & 74 deletions src/Fable.Transforms/Dart/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ let applyOp (com: ICompiler) (ctx: Context) r t opName (args: Expr list) =
let truncateUnsigned operation = // see #1550
match t with
| Number(UInt32, _) -> Operation(Binary(BinaryShiftRightZeroFill, operation, makeIntConst 0), Tags.empty, t, r)
| Number(UInt64, _) -> Helper.LibCall(com, "Util", "toUInt64", t, [ operation ], ?loc = r)
| _ -> operation

let logicOp op left right =
Expand All @@ -326,6 +327,8 @@ let applyOp (com: ICompiler) (ctx: Context) r t opName (args: Expr list) =
| Operators.rightShift, [ left; right ] ->
match argTypes with
| Number(UInt32, _) :: _ -> binOp BinaryShiftRightZeroFill left right // See #646
| Number(UInt64, _) :: _ ->
Helper.LibCall(com, "Util", "rightShiftUnsigned64", t, [ left; right ], argTypes, ?loc = r)
| _ -> binOp BinaryShiftRightSignPropagating left right
| Operators.bitwiseAnd, [ left; right ] -> binOp BinaryAndBitwise left right |> truncateUnsigned
| Operators.bitwiseOr, [ left; right ] -> binOp BinaryOrBitwise left right |> truncateUnsigned
Expand All @@ -334,13 +337,12 @@ let applyOp (com: ICompiler) (ctx: Context) r t opName (args: Expr list) =
| Operators.booleanOr, [ left; right ] -> logicOp LogicalOr left right
| Operators.logicalNot, [ operand ] -> unOp UnaryNotBitwise operand |> truncateUnsigned
| Operators.unaryNegation, [ operand ] ->
// TODO: Check for min value, see "Unary negation with integer MinValue works" test
unOp UnaryMinus operand
// match argTypes with
// | Number(Int8,_)::_ -> Helper.LibCall(com, "Int32", "op_UnaryNegation_Int8", t, args, ?loc=r)
// | Number(Int16,_)::_ -> Helper.LibCall(com, "Int32", "op_UnaryNegation_Int16", t, args, ?loc=r)
// | Number(Int32,_)::_ -> Helper.LibCall(com, "Int32", "op_UnaryNegation_Int32", t, args, ?loc=r)
// | _ -> unOp UnaryMinus operand
match argTypes with
| Number(Int8, _) :: _ -> Helper.LibCall(com, "Util", "negateInt8", t, args, ?loc = r)
| Number(Int16, _) :: _ -> Helper.LibCall(com, "Util", "negateInt16", t, args, ?loc = r)
| Number(Int32, _) :: _ -> Helper.LibCall(com, "Util", "negateInt32", t, args, ?loc = r)
| Number(Int64, _) :: _ -> Helper.LibCall(com, "Util", "negateInt64", t, args, ?loc = r)
| _ -> unOp UnaryMinus operand
| Operators.unaryPlus, [ operand ] -> unOp UnaryPlus operand
| _ ->
$"Operator %s{opName} not found in %A{argTypes}"
Expand Down Expand Up @@ -944,10 +946,14 @@ let defaultValue com ctx r t defValue option =
|> Some

let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
let math r t (args: Expr list) argTypes genArgs methName =
let math r t (args: Expr list) argTypes methName =
let meth = Naming.lowerFirst methName

Helper.ImportedCall("dart:math", meth, t, args, argTypes, genArgs = genArgs, ?loc = r)
let call = Helper.ImportedCall("dart:math", meth, t, args, argTypes, ?loc = r)

match meth, t with
| "pow", Number((Float32 | Float64), _) -> Helper.InstanceCall(call, "toDouble", t, [], ?loc = r)
| _ -> call

match i.CompiledName, args with
| ("DefaultArg" | "DefaultValueArg"), [ option; defValue ] -> defaultValue com ctx r t defValue option
Expand Down Expand Up @@ -1037,7 +1043,7 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
)
|> Some
| CustomOp com ctx r t "Pow" args e -> Some e
| _ -> math r t args i.SignatureArgTypes i.GenericArgs "pow" |> Some
| _ -> math r t args i.SignatureArgTypes "pow" |> Some
| ("Ceiling" | "Floor" as meth), [ arg ] ->
let meth = Naming.lowerFirst meth

Expand Down Expand Up @@ -1065,11 +1071,14 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
Helper.InstanceCall(arg, meth, t, [], ?loc = r) |> Some
| "Log", [ arg1; arg2 ] ->
// "Math.log($0) / Math.log($1)"
let dividend = math None t [ arg1 ] [] (List.take 1 i.SignatureArgTypes) "log"
let dividend = math None t [ arg1 ] (List.take 1 i.SignatureArgTypes) "log"

let divisor = math None t [ arg2 ] [] (List.skip 1 i.SignatureArgTypes) "log"
let divisor = math None t [ arg2 ] (List.skip 1 i.SignatureArgTypes) "log"

makeBinOp r t dividend divisor BinaryDivide |> Some
| ("Cosh" | "Sinh" | "Tanh" | "Log2" | "Log10" as meth), _ ->
Helper.LibCall(com, "Util", Naming.lowerFirst meth, t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "Abs", [ arg ] ->
match arg with
| ExprType(Number(BigInt | Decimal as kind, _)) ->
Expand All @@ -1096,17 +1105,13 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
| "Atan", _
| "Atan2", _
| "Cos", _
| "Cosh", _
| "Exp", _
| "Log", _
| "Log10", _
| "Sin", _
| "Sinh", _
| "Sqrt", _
| "Tan", _
| "Tanh", _ ->
| "Tan", _ ->
match args with
| ExprType(Number(_, _)) :: _ -> math r t args i.SignatureArgTypes [] i.CompiledName |> Some
| ExprType(Number(_, _)) :: _ -> math r t args i.SignatureArgTypes i.CompiledName |> Some
| _ -> applyOp com ctx r t i.CompiledName args |> Some
| "Round", _ ->
match args with
Expand All @@ -1124,17 +1129,7 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
)
|> Some
| _ ->
Helper.LibCall(
com,
"Util",
"round",
t,
args,
i.SignatureArgTypes,
genArgs = i.GenericArgs,
?thisArg = thisArg,
?loc = r
)
Helper.LibCall(com, "Util", "round", t, args, i.SignatureArgTypes, ?thisArg = thisArg, ?loc = r)
|> Some
| "Truncate", _ ->
match args with
Expand All @@ -1151,21 +1146,14 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
?loc = r
)
|> Some
| _ ->
Helper.GlobalCall("Math", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, memb = "trunc", ?loc = r)
|> Some
| _ -> Helper.InstanceCall(args.Head, "truncateToDouble", t, [], ?loc = r) |> Some
| "Sign", _ ->
let args = toFloat com ctx r t args |> List.singleton

Helper.LibCall(com, "Util", "sign", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
Helper.LibCall(com, "Util", "sign", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "DivRem", _ ->
let modName =
match i.SignatureArgTypes with
| Number(Int64, _) :: _ -> "Long"
| _ -> "Int32"

Helper.LibCall(com, modName, "divRem", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
Helper.LibCall(com, "Types", "divRem", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
// Numbers
| ("Infinity" | "InfinitySingle"), _ -> Helper.GlobalIdent("Number", "POSITIVE_INFINITY", t, ?loc = r) |> Some
Expand Down Expand Up @@ -1209,12 +1197,15 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
| (Operators.greaterThan | "Gt"), [ left; right ] -> booleanCompare com ctx r left right BinaryGreater |> Some
| (Operators.greaterThanOrEqual | "Gte"), [ left; right ] ->
booleanCompare com ctx r left right BinaryGreaterOrEqual |> Some
| ("Min" | "Max" | "Clamp" as meth), _ ->
| ("Min" | "Max" | "MinMagnitude" | "MaxMagnitude" | "Clamp" as meth), _ ->
let meth = Naming.lowerFirst meth

match meth, t with
| ("min" | "max"), Number((DartInt | DartDouble), NumberInfo.Empty) ->
Helper.ImportedCall("dart:math", meth, t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
Helper.ImportedCall("dart:math", meth, t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| ("minMagnitude" | "maxMagnitude"), Number((DartInt | DartDouble), NumberInfo.Empty) ->
Helper.LibCall(com, "Util", meth, t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| _ ->
let f = makeComparerFunction com ctx t
Expand Down Expand Up @@ -2046,46 +2037,31 @@ let parseNum (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr op
[ str; makeIntConst style; makeBoolConst unsigned; makeIntConst bitsize ]
@ outValue

Helper.LibCall(com, numberModule, Naming.lowerFirst meth, t, args, ?loc = r)
|> Some
match kind, meth with
| (Float32 | Float64), "Parse" -> Helper.GlobalCall("double", t, [ str ], memb = "parse", ?loc = r) |> Some
| _ ->
Helper.LibCall(com, numberModule, Naming.lowerFirst meth, t, args, ?loc = r)
|> Some

let isFloat =
match i.SignatureArgTypes with
| Number((Float32 | Float64), _) :: _ -> true
| _ -> false

match i.CompiledName, args with
| "IsNaN", [ _ ] when isFloat -> Helper.GlobalCall("Number", t, args, memb = "isNaN", ?loc = r) |> Some
| "IsPositiveInfinity", [ _ ] when isFloat ->
Helper.LibCall(
com,
"Double",
"isPositiveInfinity",
t,
args,
i.SignatureArgTypes,
genArgs = i.GenericArgs,
?loc = r
)
|> Some
| "IsNegativeInfinity", [ _ ] when isFloat ->
Helper.LibCall(
com,
"Double",
"isNegativeInfinity",
t,
args,
i.SignatureArgTypes,
genArgs = i.GenericArgs,
?loc = r
)
|> Some
| "IsInfinity", [ _ ] when isFloat ->
Helper.LibCall(com, "Double", "isInfinity", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
|> Some
| "IsInfinity", [ _ ] when isFloat ->
Helper.LibCall(com, "Double", "isInfinity", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
|> Some
| "IsNaN", [ _ ] when isFloat -> emitExpr r t args "$0.isNaN" |> Some
| "Log2", _ ->
let log =
Helper.LibCall(com, "Util", "log2", Float64.Number, args, i.SignatureArgTypes, ?loc = r)

match t with
| Number((Float32 | Float64), _) -> TypeCast(log, t) |> Some
| Number _ -> toInt com ctx r t [ log ] |> Some
| _ -> log |> Some
| "IsPositiveInfinity", [ _ ] when isFloat -> emitExpr r t args "$0 == double.infinity" |> Some
| "IsNegativeInfinity", [ _ ] when isFloat -> emitExpr r t args "$0 == double.negativeInfinity" |> Some
| "IsInfinity", [ _ ] when isFloat -> emitExpr r t args "$0.isInfinite" |> Some
| ("Min" | "Max" | "MinMagnitude" | "MaxMagnitude" | "Clamp"), _ -> operators com ctx r t i thisArg args
| ("Parse" | "TryParse") as meth, str :: NumberConst(NumberValue.Int32 style, _) :: _ ->
let hexConst = int System.Globalization.NumberStyles.HexNumber
let intConst = int System.Globalization.NumberStyles.Integer
Expand Down
5 changes: 3 additions & 2 deletions src/Fable.Transforms/Rust/Fable2Rust.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5360,14 +5360,15 @@ module Compiler =
|> Seq.toList

member _.ClearAllImports(ctx) =
for import in imports do
let importsList = imports |> Seq.toList

for import in importsList do
import.Value.Depths <-
// remove all import depths at this module level or deeper
import.Value.Depths |> List.filter (fun d -> d < ctx.ModuleDepth)

if import.Value.Depths.Length = 0 then
imports.Remove(import.Key) |> ignore

ctx.UsedNames.RootScope.Remove(import.Value.LocalIdent) |> ignore

member _.GetAllModules() = importModules.Keys |> Seq.toList
Expand Down
10 changes: 10 additions & 0 deletions src/fable-library-dart/Types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@ bool removeKey<K, V>(Map<K, V> map, K key) {
return false;
}

dynamic divRem(int x, int y, [FSharpRef<int>? remainder]) {
final quotient = x ~/ y;
final modulus = x.remainder(y);
if (remainder != null) {
remainder.contents = modulus;
return quotient;
}
return Tuple2(quotient, modulus);
}

Set<T> setWith<T>(IEqualityComparer<T> comparer, [Iterable<T>? initialValues]) {
final set =
LinkedHashSet<T>(equals: comparer.Equals, hashCode: comparer.GetHashCode);
Expand Down
71 changes: 71 additions & 0 deletions src/fable-library-dart/Util.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// ignore_for_file: file_names
import 'dart:math' as dart_math;

final _curried = Expando();

Expand Down Expand Up @@ -1007,6 +1008,76 @@ T clamp<T>(int Function(T, T) comparer, T value, T min, T max) {
: value;
}

// Banker's rounding (round half to even), compatible with .NET Math.Round
double round(double x, [int digits = 0]) {
if (digits == 0) {
final truncated = x.truncateToDouble();
final diff = (x - truncated).abs();
if (diff == 0.5) {
return truncated.remainder(2.0) == 0.0
? truncated
: (x > 0 ? truncated + 1.0 : truncated - 1.0);
}
return x.roundToDouble();
} else {
final scale = dart_math.pow(10, digits).toDouble();
return round(x * scale) / scale;
}
}

int negateInt8(int x) => (((-x) + 0x80) & 0xFF) - 0x80;

int negateInt16(int x) => (((-x) + 0x8000) & 0xFFFF) - 0x8000;

int negateInt32(int x) => (((-x) + 0x80000000) & 0xFFFFFFFF) - 0x80000000;

int toUInt64(int x) => BigInt.from(x).toUnsigned(64).toInt();

int toInt64(int x) => BigInt.from(x).toUnsigned(64).toSigned(64).toInt();

int negateInt64(int x) => toInt64(-x);

int rightShiftUnsigned64(int x, int bits) =>
(BigInt.from(x).toUnsigned(64) >> bits).toInt();

int sign(double x) {
return x > 0 ? 1 : x < 0 ? -1 : 0;
}

double cosh(double x) {
final expX = dart_math.exp(x);
final expNegX = dart_math.exp(-x);
return (expX + expNegX) / 2.0;
}

double sinh(double x) {
final expX = dart_math.exp(x);
final expNegX = dart_math.exp(-x);
return (expX - expNegX) / 2.0;
}

double tanh(double x) {
final expX = dart_math.exp(x);
final expNegX = dart_math.exp(-x);
return (expX - expNegX) / (expX + expNegX);
}

double log2(double x) {
return dart_math.log(x) / dart_math.ln2;
}

double log10(double x) {
return dart_math.log(x) / dart_math.ln10;
}

T minMagnitude<T extends num>(T x, T y) {
return x.abs() < y.abs() ? x : y;
}

T maxMagnitude<T extends num>(T x, T y) {
return x.abs() > y.abs() ? x : y;
}

String int16ToString(int i, [int radix = 10]) {
if (radix == 10) {
return i.toString();
Expand Down
Loading
Loading