[js_runtime] Safe-print records in Error messages

The js_runtime implementation of `Error.safeToString` now understands
Records.

`ArgumentError.value((1, 2))` now prints as

    Invalid argument: Record (1, 2)

instead of the old way:

    Invalid argument: Instance of '_Record_2'

I don't think `Record` is necessary, but this is consistent with the
VM error messages.

Bug: 49718
Change-Id: Icde50f12a3956aa8954ee083307c1e5b13871e41
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/290349
Reviewed-by: Mayank Patke <fishythefish@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
This commit is contained in:
Stephen Adams 2023-03-22 19:41:38 +00:00 committed by Commit Queue
parent 37c7f05a19
commit cbe7075b7a
3 changed files with 44 additions and 11 deletions

View file

@ -220,14 +220,12 @@ class double {
class Error {
@patch
static String _objectToString(Object object) {
// Closures all have useful and safe toString methods.
if (object is Closure) return object.toString();
return Primitives.objectToHumanReadableString(object);
return Primitives.safeToString(object);
}
@patch
static String _stringToSafeString(String string) {
return jsonEncodeNative(string);
return Primitives.stringSafeToString(string);
}
@patch

View file

@ -483,6 +483,28 @@ class Primitives {
return "Instance of '$name'";
}
static String stringSafeToString(String string) {
return jsonEncodeNative(string);
}
static String safeToString(Object? object) {
if (object == null || object is num || object is bool) {
return object.toString();
}
if (object is String) {
return stringSafeToString(object);
}
// Closures all have useful and safe toString methods.
if (object is Closure) {
return object.toString();
}
if (object is _Record) {
return object._toString(true);
}
return Primitives.objectToHumanReadableString(object);
}
static int dateNow() => JS('int', r'Date.now()');
static void initTicker() {
@ -1878,11 +1900,16 @@ int getLength(var array) {
invokeClosure(Function closure, int numberOfArguments, var arg1, var arg2,
var arg3, var arg4) {
switch (numberOfArguments) {
case 0: return closure();
case 1: return closure(arg1);
case 2: return closure(arg1, arg2);
case 3: return closure(arg1, arg2, arg3);
case 4: return closure(arg1, arg2, arg3, arg4);
case 0:
return closure();
case 1:
return closure(arg1);
case 2:
return closure(arg1, arg2);
case 3:
return closure(arg1, arg2, arg3);
case 4:
return closure(arg1, arg2, arg3, arg4);
}
throw new Exception('Unsupported number of arguments for wrapped closure');
}

View file

@ -29,12 +29,15 @@ abstract final class _Record implements Record {
}
@override
String toString() {
String toString() => _toString(false);
String _toString(bool safe) {
final keys = _fieldKeys();
final values = _getFieldValues();
assert(keys.length == values.length);
final sb = StringBuffer();
String separator = '';
if (safe) sb.write('Record ');
sb.write('(');
for (int i = 0; i < keys.length; i++) {
sb.write(separator);
@ -43,7 +46,12 @@ abstract final class _Record implements Record {
sb.write(key);
sb.write(': ');
}
sb.write(values[i]);
final value = values[i];
if (safe) {
sb.write(Primitives.safeToString(value));
} else {
sb.write(value);
}
separator = ', ';
}
sb.write(')');