[dart2wasm] Implement toString methods on double using JS interop.

Change-Id: I5843e9cae59b8f152b922a01796eaf05494808a0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/250681
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Commit-Queue: Joshua Litt <joshualitt@google.com>
This commit is contained in:
Joshua Litt 2022-08-17 00:23:52 +00:00 committed by Commit Bot
parent 4fcbbb107a
commit 2c4ee2895f
3 changed files with 95 additions and 11 deletions

View file

@ -270,6 +270,34 @@ var dart2wasm = {
}
return null;
},
stringify: function(o) {
return stringToDartString(String(o));
},
doubleToString: function(v) {
return stringToDartString(v.toString());
},
toFixed: function(double, digits) {
return stringToDartString(double.toFixed(digits));
},
toExponential: function(double, fractionDigits) {
return stringToDartString(double.toExponential(fractionDigits));
},
toPrecision: function(double, precision) {
return stringToDartString(double.toPrecision(precision));
},
parseDouble: function(source) {
// Notice that JS parseFloat accepts garbage at the end of the string.
// Accept only:
// - [+/-]NaN
// - [+/-]Infinity
// - a Dart double literal
// We do allow leading or trailing whitespace.
var jsSource = stringFromDartString(source);
if (!/^\s*[+-]?(?:Infinity|NaN|(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(jsSource)) {
return NaN;
}
return parseFloat(jsSource);
},
};
function instantiate(filename, imports) {

View file

@ -4,6 +4,49 @@
// part of "core_patch.dart";
@pragma("wasm:import", "dart2wasm.doubleToString")
external String _doubleToString(double v);
@pragma("wasm:import", "dart2wasm.toFixed")
external String _toFixed(double d, double digits);
@pragma("wasm:import", "dart2wasm.toExponential")
external String _toExponential1(double d);
@pragma("wasm:import", "dart2wasm.toExponential")
external String _toExponential2(double d, double fractionDigits);
@pragma("wasm:import", "dart2wasm.toPrecision")
external String _toPrecision(double d, double precision);
@pragma("wasm:import", "dart2wasm.parseDouble")
external double _parseDouble(String doubleString);
@patch
class double {
@patch
static double parse(String source,
[@deprecated double onError(String source)?]) {
double? result = tryParse(source);
if (result == null) {
throw FormatException('Invalid double $source');
}
return result;
}
@patch
static double? tryParse(String source) {
double result = _parseDouble(source);
if (result.isNaN) {
String trimmed = source.trim();
if (!(trimmed == 'NaN' || trimmed == '+NaN' || trimmed == '-NaN')) {
return null;
}
}
return result;
}
}
@pragma("wasm:entry-point")
class _BoxedDouble implements double {
// A boxed double contains an unboxed double.
@ -142,8 +185,6 @@ class _BoxedDouble implements double {
static final List _cache = new List.filled(CACHE_LENGTH, null);
static int _cacheEvictIndex = 0;
external String _toString();
String toString() {
// TODO(koda): Consider starting at most recently inserted.
for (int i = 0; i < CACHE_LENGTH; i += 2) {
@ -156,7 +197,7 @@ class _BoxedDouble implements double {
if (identical(0.0, this)) {
return "0.0";
}
String result = _toString();
String result = _doubleToString(value);
// Replace the least recently inserted entry.
_cache[_cacheEvictIndex] = this;
_cache[_cacheEvictIndex + 1] = result;
@ -188,7 +229,8 @@ class _BoxedDouble implements double {
return _toStringAsFixed(fractionDigits);
}
external String _toStringAsFixed(int fractionDigits);
String _toStringAsFixed(int fractionDigits) =>
_toFixed(value, fractionDigits.toDouble());
String toStringAsExponential([int? fractionDigits]) {
// See ECMAScript-262, 15.7.4.6 for details.
@ -208,14 +250,16 @@ class _BoxedDouble implements double {
if (this == double.infinity) return "Infinity";
if (this == -double.infinity) return "-Infinity";
// The dart function prints the shortest representation when fractionDigits
// equals null. The native function wants -1 instead.
fractionDigits = (fractionDigits == null) ? -1 : fractionDigits;
return _toStringAsExponential(fractionDigits);
}
external String _toStringAsExponential(int fractionDigits);
String _toStringAsExponential(int? fractionDigits) {
if (fractionDigits == null) {
return _toExponential1(value);
} else {
return _toExponential2(value, fractionDigits.toDouble());
}
}
String toStringAsPrecision(int precision) {
// See ECMAScript-262, 15.7.4.7 for details.
@ -236,7 +280,8 @@ class _BoxedDouble implements double {
return _toStringAsPrecision(precision);
}
external String _toStringAsPrecision(int fractionDigits);
String _toStringAsPrecision(int fractionDigits) =>
_toPrecision(value, fractionDigits.toDouble());
// Order is: NaN > Infinity > ... > 0.0 > -0.0 > ... > -Infinity.
int compareTo(num other) {

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
// Helpers for working with JS.
library dart.js_helper;
library dart._js_helper;
import 'dart:_internal';
import 'dart:typed_data';
@ -23,6 +23,11 @@ class JSValue {
Object toObject() => jsObjectToDartObject(_ref);
}
extension DoubleToJS on double {
WasmAnyRef toAnyRef() => toJSNumber(this);
JSValue toJS() => JSValue(toAnyRef());
}
extension StringToJS on String {
WasmAnyRef toAnyRef() => jsStringFromDartString(this);
JSValue toJS() => JSValue(toAnyRef());
@ -108,6 +113,9 @@ external bool isJSWrappedDartFunction(WasmAnyRef? o);
@pragma("wasm:import", "dart2wasm.isJSObject")
external bool isJSObject(WasmAnyRef? o);
// The JS runtime will run helpful conversion routines between refs and bool /
// double. In the longer term hopefully we can find a way to avoid the round
// trip.
@pragma("wasm:import", "dart2wasm.roundtrip")
external double toDartNumber(WasmAnyRef ref);
@ -193,6 +201,9 @@ external WasmAnyRef? setPropertyRaw(
external WasmAnyRef? callMethodVarArgsRaw(
WasmAnyRef o, WasmAnyRef method, WasmAnyRef? args);
@pragma("wasm:import", "dart2wasm.stringify")
external String stringifyRaw(WasmAnyRef? object);
// Currently, `allowInterop` returns a Function type. This is unfortunate for
// Dart2wasm because it means arbitrary Dart functions can flow to JS util
// calls. Our only solutions is to cache every function called with