[dart2wasm] Minify more errors
Transform front-end generated `throw` expressions (as indicated by the
kernel node's `forErrorHandling` flag) to error throwing functions that,
in minify mode, throw without details.
ACX demo sizes: (`-O2 --minify`)
- Before: 12,841,863 bytes
- After: 12,396,399 bytes
- Diff: -445,464 bytes, -3.46%
Issue: https://github.com/dart-lang/sdk/issues/60432
Change-Id: I3c0bfebd5a9cb460af312dafb63b5a0c9aeda22e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/430380
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Ömer Ağacan <omersa@google.com>
diff --git a/pkg/dart2wasm/lib/transformers.dart b/pkg/dart2wasm/lib/transformers.dart
index 0552186..af643d6 100644
--- a/pkg/dart2wasm/lib/transformers.dart
+++ b/pkg/dart2wasm/lib/transformers.dart
@@ -72,6 +72,55 @@
CoreTypes get coreTypes => env.coreTypes;
+ /// Maps error handling function, constructor, factory references to functions
+ /// that, when minifying, throw errors without details, saving binary space.
+ ///
+ /// Calls to these error handling function etc. references are transformed
+ /// when they are introduced by the front-end, as indicated by
+ /// [Throw.forErrorHandling].
+ late final Map<Reference, Procedure> _errorHandlingFunctions = {
+ coreTypes.index
+ .getConstructor('dart:_internal', 'LateError', 'fieldADI')
+ .reference:
+ coreTypes.index.getTopLevelProcedure(
+ 'dart:_error_utils', '_throwLateErrorFieldADI'),
+ coreTypes.index
+ .getConstructor('dart:_internal', 'LateError', 'localADI')
+ .reference:
+ coreTypes.index.getTopLevelProcedure(
+ 'dart:_error_utils', '_throwLateErrorLocalADI'),
+ coreTypes.index
+ .getConstructor('dart:_internal', 'LateError', 'fieldNI')
+ .reference:
+ coreTypes.index.getTopLevelProcedure(
+ 'dart:_error_utils', '_throwLateErrorFieldNI'),
+ coreTypes.index
+ .getConstructor('dart:_internal', 'LateError', 'localNI')
+ .reference:
+ coreTypes.index.getTopLevelProcedure(
+ 'dart:_error_utils', '_throwLateErrorLocalNI'),
+ coreTypes.index
+ .getConstructor('dart:_internal', 'LateError', 'fieldAI')
+ .reference:
+ coreTypes.index.getTopLevelProcedure(
+ 'dart:_error_utils', '_throwLateErrorFieldAI'),
+ coreTypes.index
+ .getConstructor('dart:_internal', 'LateError', 'localAI')
+ .reference:
+ coreTypes.index.getTopLevelProcedure(
+ 'dart:_error_utils', '_throwLateErrorLocalAI'),
+ coreTypes.index
+ .getProcedure('dart:core', 'NoSuchMethodError', 'withInvocation')
+ .reference:
+ coreTypes.index.getTopLevelProcedure(
+ 'dart:_error_utils', '_throwNoSuchMethodErrorWithInvocation'),
+ coreTypes.index
+ .getConstructor('dart:_internal', 'ReachabilityError', '')
+ .reference:
+ coreTypes.index.getTopLevelProcedure(
+ 'dart:_error_utils', '_throwReachabilityError'),
+ };
+
_WasmTransformer(CoreTypes coreTypes, ClassHierarchy hierarchy)
: env = TypeEnvironment(coreTypes, hierarchy),
_nonNullableTypeType = coreTypes.index
@@ -812,6 +861,28 @@
StringLiteral(import.name!)
]));
}
+
+ @override
+ TreeNode visitThrow(Throw node) {
+ node.transformChildren(this);
+ if (node.forErrorHandling) {
+ final expression = node.expression;
+ if (expression is ConstructorInvocation) {
+ final throwFunction =
+ _errorHandlingFunctions[expression.targetReference];
+ if (throwFunction != null) {
+ return StaticInvocation(throwFunction, expression.arguments);
+ }
+ } else if (expression is StaticInvocation) {
+ final throwFunction =
+ _errorHandlingFunctions[expression.targetReference];
+ if (throwFunction != null) {
+ return StaticInvocation(throwFunction, expression.arguments);
+ }
+ }
+ }
+ return node;
+ }
}
class _AsyncStarFrame {
diff --git a/pkg/front_end/testcases/dart2wasm/inference_update_2/issue52452.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2wasm/inference_update_2/issue52452.dart.strong.transformed.expect
index 94c3211..fd6a3c9 100644
--- a/pkg/front_end/testcases/dart2wasm/inference_update_2/issue52452.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2wasm/inference_update_2/issue52452.dart.strong.transformed.expect
@@ -1,7 +1,7 @@
library;
import self as self;
import "dart:core" as core;
-import "dart:_internal" as _in;
+import "dart:_error_utils" as _er;
class C extends core::Object {
final field core::int? _f2;
@@ -36,16 +36,16 @@
field core::int? _#M1#_f4 = null;
field core::bool _#M1#_f4#isSet = false;
get _f2() → core::int?
- return this.{self::M1::_#M1#_f2#isSet}{core::bool} ?{core::int?} this.{self::M1::_#M1#_f2}{core::int?} : throw{for-error-handling} new _in::LateError::fieldNI("_f2");
+ return this.{self::M1::_#M1#_f2#isSet}{core::bool} ?{core::int?} this.{self::M1::_#M1#_f2}{core::int?} : _er::_throwLateErrorFieldNI("_f2");
set _f2(final core::int? _f2#param) → void {
this.{self::M1::_#M1#_f2#isSet} = true;
this.{self::M1::_#M1#_f2} = _f2#param;
}
get _f3() → core::int?
- return this.{self::M1::_#M1#_f3#isSet}{core::bool} ?{core::int?} this.{self::M1::_#M1#_f3}{core::int?} : throw{for-error-handling} new _in::LateError::fieldNI("_f3");
+ return this.{self::M1::_#M1#_f3#isSet}{core::bool} ?{core::int?} this.{self::M1::_#M1#_f3}{core::int?} : _er::_throwLateErrorFieldNI("_f3");
set _f3(final core::int? _f3#param) → void
if(this.{self::M1::_#M1#_f3#isSet}{core::bool})
- throw{for-error-handling} new _in::LateError::fieldAI("_f3");
+ _er::_throwLateErrorFieldAI("_f3");
else {
this.{self::M1::_#M1#_f3#isSet} = true;
this.{self::M1::_#M1#_f3} = _f3#param;
@@ -54,7 +54,7 @@
if(!this.{self::M1::_#M1#_f4#isSet}{core::bool}) {
final core::int? #t1 = 0;
if(this.{self::M1::_#M1#_f4#isSet}{core::bool})
- throw{for-error-handling} new _in::LateError::fieldADI("_f4");
+ _er::_throwLateErrorFieldADI("_f4");
this.{self::M1::_#M1#_f4} = #t1;
this.{self::M1::_#M1#_f4#isSet} = true;
}
@@ -94,14 +94,14 @@
: super self::B::•(i)
;
get _f2() → core::int?
- return this.{self::M1::_#M1#_f2#isSet}{core::bool} ?{core::int?} this.{self::M1::_#M1#_f2}{core::int?} : throw{for-error-handling} new _in::LateError::fieldNI("_f2");
+ return this.{self::M1::_#M1#_f2#isSet}{core::bool} ?{core::int?} this.{self::M1::_#M1#_f2}{core::int?} : _er::_throwLateErrorFieldNI("_f2");
get _f3() → core::int?
- return this.{self::M1::_#M1#_f3#isSet}{core::bool} ?{core::int?} this.{self::M1::_#M1#_f3}{core::int?} : throw{for-error-handling} new _in::LateError::fieldNI("_f3");
+ return this.{self::M1::_#M1#_f3#isSet}{core::bool} ?{core::int?} this.{self::M1::_#M1#_f3}{core::int?} : _er::_throwLateErrorFieldNI("_f3");
get _f4() → core::int? {
if(!this.{self::M1::_#M1#_f4#isSet}{core::bool}) {
final core::int? #t2 = 0;
if(this.{self::M1::_#M1#_f4#isSet}{core::bool})
- throw{for-error-handling} new _in::LateError::fieldADI("_f4");
+ _er::_throwLateErrorFieldADI("_f4");
this.{self::M1::_#M1#_f4} = #t2;
this.{self::M1::_#M1#_f4#isSet} = true;
}
@@ -113,7 +113,7 @@
}
set _f3(final core::int? _f3#param) → void
if(this.{self::M1::_#M1#_f3#isSet}{core::bool})
- throw{for-error-handling} new _in::LateError::fieldAI("_f3");
+ _er::_throwLateErrorFieldAI("_f3");
else {
this.{self::M1::_#M1#_f3#isSet} = true;
this.{self::M1::_#M1#_f3} = _f3#param;
diff --git a/sdk/lib/_internal/wasm/lib/error_utils.dart b/sdk/lib/_internal/wasm/lib/error_utils.dart
index 4cbb401..f2c8408 100644
--- a/sdk/lib/_internal/wasm/lib/error_utils.dart
+++ b/sdk/lib/_internal/wasm/lib/error_utils.dart
@@ -168,6 +168,49 @@
throw ArgumentError.notNull();
}
+Never _throwLateErrorFieldADI(String fieldName) {
+ if (minify) throw _lateErrorFieldADI;
+ throw LateError.fieldADI(fieldName);
+}
+
+Never _throwLateErrorLocalADI(String fieldName) {
+ if (minify) throw _lateErrorLocalADI;
+ throw LateError.localADI(fieldName);
+}
+
+Never _throwLateErrorFieldNI(String fieldName) {
+ if (minify) throw _lateErrorFieldNI;
+ throw LateError.fieldNI(fieldName);
+}
+
+Never _throwLateErrorLocalNI(String fieldName) {
+ if (minify) throw _lateErrorLocalNI;
+ throw LateError.localNI(fieldName);
+}
+
+Never _throwLateErrorFieldAI(String fieldName) {
+ if (minify) throw _lateErrorFieldAI;
+ throw LateError.fieldAI(fieldName);
+}
+
+Never _throwLateErrorLocalAI(String fieldName) {
+ if (minify) throw _lateErrorLocalAI;
+ throw LateError.localAI(fieldName);
+}
+
+Never _throwNoSuchMethodErrorWithInvocation(
+ Object? receiver,
+ Invocation invocation,
+) {
+ if (minify) throw _noSuchMethodErrorWithoutDetails;
+ throw NoSuchMethodError.withInvocation(receiver, invocation);
+}
+
+Never _throwReachabilityError([String? _message]) {
+ if (minify) throw _reachabilityError;
+ throw ReachabilityError(_message);
+}
+
const _indexErrorWithoutDetails = _ErrorWithoutDetails(
'IndexError (details omitted due to --minify)',
);
@@ -183,10 +226,44 @@
const _negativeOrZeroValueErrorWithoutDetails = _ErrorWithoutDetails(
'Value was negative or zero (details omitted due to --minify)',
);
-const _nullErrorWithoutDetails = _ErrorWithoutDetails(
+const _nullErrorWithoutDetails = _LateErrorWithoutDetails(
'Value must not be null (details omitted due to --minify)',
);
+const _lateErrorFieldADI = _LateErrorWithoutDetails(
+ 'LateInitializationError: Field has been assigned during initialization (details omitted due to --minify)',
+);
+
+const _lateErrorLocalADI = _LateErrorWithoutDetails(
+ 'LateInitializationError: Local has been assigned during initialization (details omitted due to --minify)',
+);
+
+const _lateErrorFieldNI = _LateErrorWithoutDetails(
+ 'LateInitializationError: Field has not been initialized (details omitted due to --minify)',
+);
+
+const _lateErrorLocalNI = _LateErrorWithoutDetails(
+ 'LateInitializationError: Local has not been initialized (details omitted due to --minify)',
+);
+
+const _lateErrorFieldAI = _LateErrorWithoutDetails(
+ 'LateInitializationError: Field has already been initialized (details omitted due to --minify)',
+);
+
+const _lateErrorLocalAI = _LateErrorWithoutDetails(
+ 'LateInitializationError: Local has already been initialized (details omitted due to --minify)',
+);
+
+const _reachabilityError = _LateErrorWithoutDetails(
+ 'ReachabilityError (details omitted due to --minify)',
+);
+
+const _NoSuchMethodErrorWithoutDetails _noSuchMethodErrorWithoutDetails =
+ _NoSuchMethodErrorWithoutDetails();
+
+const _TypeErrorWithoutDetails typeErrorWithoutDetails =
+ _TypeErrorWithoutDetails();
+
class _ErrorWithoutDetails implements Error {
final String _message;
const _ErrorWithoutDetails(this._message);
@@ -196,9 +273,6 @@
String toString() => _message;
}
-const _TypeErrorWithoutDetails typeErrorWithoutDetails =
- _TypeErrorWithoutDetails();
-
class _TypeErrorWithoutDetails implements TypeError {
const _TypeErrorWithoutDetails();
@@ -207,3 +281,21 @@
String toString() =>
'Runtime type check failed (details omitted due to --minify)';
}
+
+class _NoSuchMethodErrorWithoutDetails implements NoSuchMethodError {
+ const _NoSuchMethodErrorWithoutDetails();
+
+ StackTrace? get stackTrace => null;
+
+ String toString() => 'NoSuchMethodError (details omitted due to --minify)';
+}
+
+class _LateErrorWithoutDetails implements LateError {
+ final String _message;
+
+ const _LateErrorWithoutDetails(this._message);
+
+ StackTrace? get stackTrace => null;
+
+ String toString() => _message;
+}