Commit Graph

379 Commits

Author SHA1 Message Date
Martin Kustermann f32057642b [dart2wasm] Run an additional -Os in open world compilation
This reduces e main module size by ~ 0.5-1%

When compiling with deferred loading, we optimize each module
individually with open world assumptions.

Currently we run only one -Os pass which leaves code behind that can be
optimized by another -Os. See e.g. discussion at [0].

The additional -Os pass is much faster than the first, so it doesn't add
meaningful compile-time.

[0] https://github.com/WebAssembly/binaryen/issues/8077

Change-Id: Id8c5e49d97439e28b2f7d9c5a0c206eec0893cad
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/490101
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
2026-03-24 13:07:26 -07:00
Lasse R.H. Nielsen 56505e0575 Stop using Match.group.
The `Match.operator[]` does the same thing and is
generally recommended (and shorter).
(I want to deprecate `group` and `groups`)

Tested: Refactoring.
CoreLibraryReviewExempt: Calling equivalent function.
Change-Id: I4c758968ae622fe16b7322be1b29b05b91e7fcd9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/489021
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Lasse Nielsen <lrn@google.com>
2026-03-20 09:09:46 -07:00
pq a5bcbf8c1a [deps] bump tools to pull in new unified_analytics
See: https://github.com/dart-lang/tools/issues/2343

This includes a few test updates to play nice w/ changes to `package_config` (https://github.com/dart-lang/tools/pull/2340).



Change-Id: Ia01b9172230a8c3045092d6c3da006e4bfaf3221
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/488240
Reviewed-by: Nicholas Shahan <nshahan@google.com>
Commit-Queue: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
2026-03-17 11:53:55 -07:00
Martin Kustermann 2e2f8ae7b3 [dart2wasm] Make normal dynamic calls avoid wasm array creations
Currently a dynamic call will call a helper which will create
3 array objects (for type, positional and named arguments).

It will then pass those wasm arrays to type checker methods.
Those will then get values out of the array, type check and call
the target.

We change this now such that in the normal case (**) we avoid
the array creations. Instead we make a dynamic forwarder function
per target and call shape.

We also outline the array creation when creating `Invocation`
objects. So for simple caller shapes - such as `clone()` the
creation of an `Invocation` object is a call without arguments
instead of various array creations.

Issue https://github.com/dart-lang/sdk/issues/62640

(**) If the dynamic call could be call-via-field we still create
those arrays, as we use them for closure type checking.

Change-Id: Ia8f3f3cd95f650bd8706a15ed68a43db4de80a6e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/487020
Reviewed-by: Srujan Gaddam <srujzs@google.com>
2026-03-12 02:17:07 -07:00
Martin Kustermann 2c8f4300d0 [dart2wasm] Fix @pragma('wasm:initialize-at-startup') in deferred loading scenario
The module owning the storage for the static field has to emit the
initialization code in it's start function.

Change-Id: I9089e99d6e12ea275343cebe3e456658a7c7a540
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/485480
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
2026-03-05 11:10:13 -08:00
Nate Biggs 3c0573bcd1 [dart2wasm] Update main invocation patching to avoid leaking types into the program.
Dart2wasm started using RTA to significantly speed up compilation.
However, a side effect of this is less exact type info during TFA.

In _invokeMain we conditionally use some js-interop logic when a
program's main takes arguments. On the web, most programs don't expect
any arguments so this is usually dead code. However, RTA sees
_invokeInternal is live and blindly treats all reachable classes as live
including the unused interop helper types in the other branches.

This code refactors the patching logic to make sure only the relevant
entry point is considered live.

This change uncovered a different bug that was suppressed by these
implicitly instantiated types. Dart2wasm was not marking some internally
instantiated classes as allocated in the function logic so entries in
the dispatch table were empty for those types. Instead any time we
generate the code for a constructor (which may not be reached via a
ConstructorInvocation like we were assuming before), we record the class
as allocated.

Change-Id: I556d3733c00b4a3e3455fa1ee2c90206d54c81f4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/484540
Commit-Queue: Nate Biggs <natebiggs@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2026-03-03 09:01:46 -08:00
Nate Biggs 0f3c75d1eb [js_interop] Add jsIdentical function that implements === on JS values backing Dart values.
dart2js and DDC implement `identical` using JS `===`. This means for
interop values the objects will be compared using JS strict equality
semantics. For Strings in particular this means value equality rather
than reference equality.

In wasm we use equality on the struct reference wrapping the JS value.
This leads to differing semantics between JS and wasm when using
identical on these JS wrapped values.

`jsIdentical` provides a way to have consistent semantics between JS and
wasm.

Change-Id: I207fbbb294254798372924a55cdb2691094a5461
CoreLibraryReviewExempt: Just updating web-related code.
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/483440
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
2026-02-27 16:27:06 -08:00
Nate Biggs 72d87adf69 [dart2wasm] Make stacktraces more consistent
Add `never-inline` pragma to all methods in the "StackTrace.current"
call hierarchy. If dart2wasm or wasm-opt inline any of these the number
of frames to remove is inconsistent.

Also only remove frames when the stack trace is from "current", not when
it's from the other JavaScriptStackTrace constructor.

Fixes issue raised in: https://dart-review.googlesource.com/c/sdk/+/479640/comments/7be8f913_489aa935

Change-Id: Id15252cc210bb79f28acad4bc78ce9ee8ff69ac7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/483624
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
2026-02-26 11:54:34 -08:00
Ömer Ağacan 590b656877 [dart2wasm] Update JS exception catching
This updates JS exception catching as discussed in #55481:

- Only catch JS exceptions when the exception type is `dynamic`,
  `Object`, or an extension of `JSValue`. (nullable or not)

  (Previously we also caught JS exceptions when the type is `Error`.)

- When the JS value caught in Wasm is `null` or `undefined`, box it as a
  non-interop class. For compatibility with dart2js, this class is
  copied from dart2js and has the same `toString` as the dart2js class.

- In other cases: box the JS values as `JSValue`. This means the value
  can be passed as any of the interop types, and can be passed back to
  JS without manual jsification.

Fixes #55481.

Issue: https://github.com/dart-lang/sdk/issues/55481
Change-Id: I23e73074729f740b90df2ca8b3c713fb39966556
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/479640
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Ömer Ağacan <omersa@google.com>
2026-02-24 01:43:10 -08:00
Nate Biggs 1fc8f6e988 [dart2wasm] Fix missing cases in deferred loading fine grained dependency calculation.
- The dependencies of a class should also include the initializers for its fields. These are used to initialize the class object. And they are not represented in the constructor Initializers list.
- Super gets/sets/invocations should all visit their children as well. These aren't leaf nodes.

Change-Id: I552bc87cf1bbc35b11b0dd7bcbd167b2fa5bcbe7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/481680
Commit-Queue: Nate Biggs <natebiggs@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2026-02-20 11:53:17 -08:00
Nate Biggs a484bb3973 [dart2wasm] Use constants to represent dummy values.
Today "dummy values" are generated per-module to stand in for things
like default parameter sentinels (where a given selector has multiple
default values for an optional parameter).

However, these values can end up crossing between modules. The logic is
set up to use ref_eq to check if an argument is one of these dummy
values. However, if one of these dummy values crosses between modules,
the passed value vs the ref_eq checked value will be different. Since
each module has its own canonical dummy value per type.

This new layout simplifies our handling of these dummy values by
treating them as Constants so that our normal module canonicalization
logic applies to them. We already have plenty of logic to canonicalize
constants across modules. This avoids the need for custom handling of
these dummy value globals.

Change-Id: Ia9c79923c788d7712b16705193ffbf3142141b5d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/480320
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
2026-02-19 12:48:49 -08:00
Kevin Moore 26fa4add14 dart2wasm: tool cleanup
DRY'd up some helpers and logic
Deleted validate_wasm_test.sh
Added `--run` and `--write-temp` flags to compile_benchmark

Change-Id: I666b25d13d0837a2b9f9fc40488882f1cfc6aff5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/478703
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Kevin Moore <kevmoo@google.com>
2026-02-18 19:16:22 -08:00
Kevin Moore e0b028808a [dart2wasm] Expand Wasm SIMD intrinsics for vector operations
Add more Wasm SIMD intrinsics and instructions to support efficient
vector operations, specifically targeting the requirements of types
like Offset and Size.

- Implement new intrinsics in `dart2wasm`:
  - `anyTrue` for V128.
  - `allTrue` for I64x2.
  - `pmin`/`pmax` (pseudo-minimum/maximum) for F64x2.
  - `fromLaneValues` for F32x4 and F64x2.
  - `shuffle` for F64x2.
- Update `wasm_builder` with support for the following instructions:
  - `v128.any_true`
  - `i8x16.all_true`, `i16x8.all_true`, `i32x4.all_true`, `i64x2.all_true`
  - `i8x16.shuffle` (including serialization and deserialization)
- Add `tests/web/wasm/simd/vector_test.dart`, which implements `Offset`
  and `Size` using these new SIMD intrinsics, demonstrating their
  practical utility and correctness.
- Update `tests/web/wasm/simd/simd_test.dart` with additional coverage.

Change-Id: Ifa5ba0fb265b0fa46c0a3469d9331f32a025c9ec
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/478860
Commit-Queue: Kevin Moore <kevmoo@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2026-02-17 16:05:32 -08:00
Lasse R.H. Nielsen 6fe105671c Remove var and final from parameters in test/.
Not removing from any file with a language marker.
Also not touching anything in `tests/language/primary_constructors`
or `.../private_named_parameters`, which are both assumed to be
primary constructor feature aware.

Two files rewritten from multi-test to error-test.

Change-Id: I43d444a35a41c7734f266794e9f167655692473f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/480640
Reviewed-by: Nate Bosch <nbosch@google.com>
Commit-Queue: Lasse Nielsen <lrn@google.com>
2026-02-17 08:28:57 -08:00
Martin Kustermann 8ee6ee4881 [dart2wasm] Fix issue of copying mixin applications with deferred prefix access
If a mixin application class gets copies of the members of a mixin, the
copies may reside in a different library. That means the copied member
body's references to import prefixes are "dangling" (or "pointing" to a
prefix of a different library).

We had a fast case: If there's no deferred import in a library we didn't
visit the body to lower `CheckLibraryIsLoaded` AST nodes.

=> We remove this optimization which will fix the bug.
=> Long term libraries may be allowed to refer prefixes of other
libraries (as part of work on [0])

[0] https://github.com/dart-lang/sdk/issues/61764

Fixes https://github.com/dart-lang/sdk/issues/62683

Change-Id: I74e81418ff2df176ff5481ea36f3b68ab3df4d4b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/480940
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
2026-02-17 00:03:26 -08:00
Martin Kustermann b87dd83013 [dart2wasm] Fix bug in handling of forwarding stubs to fields
The CFE may insert forwarding stubs not only for procedures
but also for fields that require covariance type checks on
the setter values.

This also fixes a bug where type parameter bounds weren't
correctly type checked against (The VM has the same bug, see [1])

[0] https://github.com/dart-lang/sdk/issues/62664

Fixes https://github.com/dart-lang/sdk/issues/62662

Change-Id: I61c005a26bd393df3279fdb349304ccc1eec9c6c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/480200
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
2026-02-13 11:56:53 -08:00
Ömer Ağacan 652565b083 [dart2wasm] Handle undefined and null when converting errors caught from JS to string
In `_JavaScriptError`, instead of using `toString` method of the caught
objects, use the `String` constructor.

Fixes crashes when printing `null` and `undefined` exception values.

This is not directly related to #55481, but the issue was caught while
working on it.

Change-Id: Id6f7124730b4ffa125bd9d0fc8bcf3ed4de15a81
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/478367
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Ömer Ağacan <omersa@google.com>
2026-02-09 05:40:11 -08:00
Ömer Ağacan a3a8a6b6ac [dart2js] Skip test js_error_stack_trace_test with CSP
The test uses `eval`.

Change-Id: I8b6c083b14d8d20d72dd0a53feab6486611ac3b3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/478882
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
2026-02-06 11:00:10 -08:00
Ömer Ağacan 51c73837b0 [dart2wasm] Tweak JS exception stack trace handling
Currently when we catch an exception thrown from JS we assume that it's
an `Error`, with a `stack` property.

This causes crashes when the exception value is not an `Error`, and the
behavior is also inconsistent with dart2js, which returns an empty stack
trace.

This fixes the crash and makes the behavior consistent with dart2js. To
make sure the behavior stays consistent, the relevant test is updated
and moved from a dart2wasm-specific directory to a web directory.

Change-Id: Ic6af7d919678ba585854c6531a103c0a5764e099
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/478400
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Ömer Ağacan <omersa@google.com>
2026-02-05 02:08:09 -08:00
Kevin Moore ef8682a9f8 [dart:wasm] implement most useful floating point SIMD ops
Change-Id: I44958086921812c7eb1a4be6b826c1be9a50738b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/478340
Reviewed-by: Nate Biggs <natebiggs@google.com>
Auto-Submit: Kevin Moore <kevmoo@google.com>
Commit-Queue: Kevin Moore <kevmoo@google.com>
2026-02-04 20:31:45 -08:00
Kevin Moore 152e01ff69 [dart2wasm] "publicly" implement WasmV128
And add corresponding operations

Change-Id: Ib39ec04969281a35615e1b4ae41d1b4ec1c100ef
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/478242
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Kevin Moore <kevmoo@google.com>
2026-02-03 22:17:49 -08:00
Kevin Moore 44cfe5b3fa [dart2wasm] Add equality to the v128 types
Change-Id: I6a07d1bec1ccc4b3a528399f9201c661bcef3e03
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/478083
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Kevin Moore <kevmoo@google.com>
2026-02-03 20:51:11 -08:00
Kevin Moore 0f230f3262 [dart2wasm] test all new simd instructions
With one fix
Also added a test script to make future hacking easier

Change-Id: I05c75dd4c458f7c3404ed7dba25484296764a549
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/477941
Commit-Queue: Kevin Moore <kevmoo@google.com>
Reviewed-by: Nate Biggs <natebiggs@google.com>
2026-02-03 11:07:42 -08:00
Nate Biggs 5a02a7a019 [dart2wasm] SIMD instructions template
SIMD proposal:
https://github.com/WebAssembly/spec/blob/main/proposals/simd/SIMD.md

This only implements a few of the available SIMD instructions as a
template for further contribution. kevmoo@ plans to continue
implementing these.

Bug: https://github.com/dart-lang/sdk/issues/62516
Change-Id: Ib229feecf58714e92fcb1461f968919658cbce29
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/476540
Commit-Queue: Nate Biggs <natebiggs@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2026-02-03 10:01:43 -08:00
Nate Biggs c2f0ff4739 [dart2js] Handle JS interop functions with more than 52 parameters.
Attached test fails without this new change in place.

Bug: https://github.com/dart-lang/sdk/issues/62528
Change-Id: I7b658b9a404294678f9f94e93da490dcd975c7e4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/476260
Commit-Queue: Nate Biggs <natebiggs@google.com>
Reviewed-by: Srujan Gaddam <srujzs@google.com>
2026-01-28 15:33:35 -08:00
Ömer Ağacan 992a07a552 [dart2wasm] Generate dart2js sourcemap extensions to map minified class names to full names
Reuse dart2js's source map extension format to map minified class names
shown in error messages and runtime type strings ("minified:Class123")
to original class names.

The extension field is only generated when minifying, and with multiple
modules, only in the main module's source map.

- Without minification runtime type strings and error messages already
  include the full class name, so mapping is not necessary.

- With multiple modules, the existing tools use the main module's source
  map to deobfuscate errors.

Because wasm-opt is not aware of this custom section and it also changes
the names section, when optimizing, we read the original names before
calling wasm-opt, and write the section back to the source map file for
the optimized Wasm.

When not optimizing we generate the source map with the custom section
directly.

Example deobfuscation using the new source maps:

    $ dart pkg/dart2js_tools/bin/lookup_name.dart test.wasm.map Class123
    Class123 => AsyncError (a global name)

The custom section is a bit verbose for what dart2wasm needs: we could
map numbers to name indices instead of strings to name indices, as
dart2wasm minifies class names to numbers. However to avoid updating a
bunch of tools in g3, SDK, maybe also in Flutter and devtools, we reuse
dart2js's format, at least for now.

Issue: https://github.com/dart-lang/sdk/issues/60711
Change-Id: I2cda331723c6f0c7e7ef5f4772feaf420dc8c6ba
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/474660
Commit-Queue: Ömer Ağacan <omersa@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2026-01-28 02:48:30 -08:00
Ömer Ağacan 99d503b105 [dart2wasm] Avoid redundant null checks of JS interop returns
Split JS-to-Dart conversion functions into two categories:

- Those that handle `null` and `undefined` and return Dart `null`.
- Those that don't expect `null` or `undefined`.

Then based on the return value of an interop function, call the right
one.

This moves null checks from interop call sites to conversion functions,
effectively outlining the duplicated null checks.

Boxing functions like `JSInt8ArrayImpl.fromArrayRef` are renamed as
`fromRef`, for consistency with other boxing functions, and also because
`fromRef` is more accurate. These functions already check the type and
so they can be passed any `externref`, not just those that represent JS
arrays.

Fixes #61906.

Issue: https://github.com/dart-lang/sdk/issues/61906
Change-Id: I54e80fe2e541ae6ef384c2c008f2dbc1e7e3bd76
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/473261
Commit-Queue: Ömer Ağacan <omersa@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2026-01-20 04:32:41 -08:00
Nate Biggs 2366fd740e [dart2wasm] Only use printable characters for minified string names.
Change-Id: I9f5de6bb28e6d9e9967e2dafdaf0261bec4c4571
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/473180
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
2026-01-16 08:26:13 -08:00
Simon Binder 7d8cd032dc [dart2wasm] Interfaces for accessing memories
This adds the `Memory` class to `dart:_wasm`, allowing Dart code to
load and store numeric types in linear memory.
Since `dart2wasm` doesn't generate a memory instance by default, there
is no singleton instance of `Memory`. Instead, memories are defined as
`external` top-level getters annotated with a pragma like
`@pragma('wasm:memory-tyype', MemoryType(limits: Limits(1, 10)))` to
declare their type.

Interop happens in a static way: Methods on `Memory` cannot be torn-off
and, since the target memory is encoded directly in the store/load
instruction, there's also no polymorphism for memories in Dart.
Attempting to call methods on a memory instance that isn't a direct
reference to its definition is a compile-time error.

Memories can also be imported and exported through the existing
`wasm:import` and `wasm:export` pragmas.

TEST=tests/web/wasm/memory_test.dart

Change-Id: I726f33ac2ec04afab55c5a2b6bc09079d0193e02
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/470020
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2026-01-15 03:45:21 -08:00
Nate Biggs 600f8a9601 [dart2wasm] Use default value sentinel when member body unreachable
This issue was partially fixed by:
https://dart-review.googlesource.com/c/sdk/+/428541

However, this only looked at the case of a member in an abstract class
that itself was made abstract if it was unused. TFA does not make the
member abstract if the enclosing class is not abstract:
pkg/vm/lib/transformations/type_flow/transformer.dart#L2289

Instead we can use the unreachable metadata to identify these
unreachable body members (that have default values cleared):
pkg/vm/lib/transformations/type_flow/transformer.dart#L736

Attached test fails prior to this fix.

Bug: https://github.com/dart-lang/sdk/issues/62273
Change-Id: I1ccb818ec41bbe805ee828be0b96ba23e73beae4
Tested: Introduced unit test.
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/469280
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
2026-01-06 21:24:51 -08:00
Ömer Ağacan 02a3163219 [dart2wasm] Catch JS exceptions with the right tag, improve toString and stack traces
Example:

    import 'dart:js_interop';

    @JS()
    external void eval(String code);

    @JS()
    external void throwFunction();

    void main() {
      eval('''
        self.throwFunction = function() {
          throw new Error('Hi from JS');
        }
      ''');
      try {
        throwFunction();
      } catch (e, st) {
        print(e);
        print(st);
      }
    }

Output before: ("..." parts are code locations, omitted)

    JavaScriptError
        at module0.main ...
        at module0._invokeMain ...
        at InstantiatedApp.invokeMain ...
        at main ...
        at async action ...
        at async eventLoop ...

Output after:

    Error: Hi from JS
        at self.throwFunction ...
        at _277 ...
        at module0.main ...
        at module0._invokeMain ...
        at InstantiatedApp.invokeMain ...
        at main ...
        at async action ...
        at async eventLoop ...

Fixes #62218.

Issue: https://github.com/dart-lang/sdk/issues/62218
Change-Id: Ia9347e938af209b8b87752479d35b6236f721acf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/469062
Reviewed-by: Martin Kustermann <kustermann@google.com>
2026-01-02 02:27:58 -08:00
Martin Kustermann b0e18fa40b [dart2wasm] Fix bug in @pragma('wasm:static-dispatch') & remove corresponding dispatch table entries
There was a bug in `CallTarget.signature` which triggers if there's only
one entry that's statically dispatched against, which causes us to
inline the polymorphic dispatcher, which relies on this (previously
incorrect) signature.

The CL also changes the dispatch table building logic to not allocate
table entries for the statically dispatched regions (as they would
never be used).

Change-Id: Ic2d0c387e8863e89ef811e1892642fe81df9189a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/468000
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
2025-12-16 01:42:00 -08:00
Martin Kustermann d38e30d65a [dart2wasm] Fix bug in JS typed data .sublist() implementations
Issue https://github.com/flutter/flutter/issues/179853

Change-Id: I97ce8d7177dfdd612714fafbc2a6b13555d9bba1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/468300
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
2025-12-15 06:46:18 -08:00
Nate Biggs 43f234799e [dart2wasm] Fix JS callback wrappers invoked in deferred modules.
The generated callback wrappers were referring to exported functions via
'dartInstance.exports' which is always the main module. But in order to
avoid bloating the main module we put some of these JS exports into
submodules.

So the wrapper must be fetched from the correct module that defines the
wrapped function. In order to facilitate this we give each module a
self-reference that it can then pass up to the JS wrapper as an
externref.

Change-Id: Id2514de2c13d38a0cee1f6c935e45a9ef826f805
Fixes: https://github.com/dart-lang/sdk/issues/62094
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/464820
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
2025-12-01 12:12:48 -08:00
Martin Kustermann c942b23b07 [dart2wasm] Fix access of constants under deferred load guard
When accessing a constant under a load guard we can push the constant to
the deferred module (as we only access it under the guard that the
module has been loaded). The access has to therefore also use the
constant initializer function from the deferred module.

We also remove `--extra-compiler-option` prefix for
`--enable-deferred-loading` as the prefix isn't recognized by
dartdev.

Change-Id: I58489303dc10bb265ef730c05a4fc78a8f598edb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/464980
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
2025-11-28 03:23:01 -08:00
Martin Kustermann ba35b8c59c [dart2wasm] Fix pkg/dart2wasm/tool/compile_benchmark script
The script was incorrectly handling source maps when deferred
loading is enabled and we emit & optimize multiple wasm modules
(the source maps very completely bogus)

Change-Id: Ib65b72a4d2a3c8958bbe80e94c76145b6e1a0e5b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/464480
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
2025-11-26 01:49:53 -08:00
Martin Kustermann 407afe84ca [dart2wasm] Remove dynamic closure call vtable entry in most cases
This reduces acx main: module -0.35%,  global section: -2.3%

Previous change in [0] made dynamic call sites have per-caller-shape
dynamic forwarders.

In this change we utilize this: Since we know the dynamic caller shape
we can make the dynamic forwarder call via a closure representation that
matches this caller shape (modulo the type arguments - as a dynamic call
without type arguments may get default type arguments of unknown count).

=> This means we no longer have to pass types, positionals and named
   arguments as wasm arrays to dynamic call forwarder. Instead we can
   pass the arguments unpacked to the vtable.

Additionally we change `Function.apply`: If we know there's no
`Function.apply` usage with named arguments, we can enumerate all
possible closure representations and call the corresponding vtable
entry. We use 2 `br_table` instructions for this:

  * branch on number of type parameters
  * branch on number of positional parameters

Together these two changes means that neither dynamic forwarders nor
`Function.apply` will use the dynamic call entry.

=> We remove the dynamic call entry from the vtable construction

The only exception to the above are:

* If dynamic submodules is enabled we don't compute closed world
  information about closures and always call via dynamic call entry

* If `Function.apply` is used with named arguments then call via dynamic
  call entry (and make vtables have it).

[0] https://dart-review.googlesource.com/c/sdk/+/461720


Issue https://github.com/dart-lang/sdk/issues/60458

Change-Id: I26e6a2a48ee37616061d701dc4d4327c2e109e82
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/462060
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
2025-11-26 01:02:31 -08:00
Tess Strickland 06795bb3a5 [pkg/smith] Convert NamedEnums to enhanced Enums.
This allows Enums created elsewhere to be used for enumOption
declarations.

Also change the analysis options for pkg/test_runner so that
exhaustive case errors are no longer ignored.

TEST=ci

Change-Id: Ib1cf1c015f4a6df9ce018117edfd9b626e91c5f6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/438725
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
2025-11-20 03:19:03 -08:00
Martin Kustermann a1cca73a28 [dart2wasm] Fix exponential compile time in dynamic closure calls
In the loop that has 2 recursive calls: with a name and without a name
we should stop early if we already know that this is unreachable (e.g.
all name combinations require a name or don't have a name).

This should make us now have O(#name-combinations) recursive calls,
emitting unreachables as soon as there's a set of names that aren't
feasible in any name combination.

Change-Id: I4c91a5fa91709398aa80ffd8dc278e30638acb0c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/462400
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
2025-11-17 05:26:21 -08:00
Nate Biggs e2cfee0f65 [dart2wasm] Improve module import naming conventions.
- Use the empty string for the string import object since these are the most numerous import objects.
- Use ascii index encoding for module names (when minified). This mostly improve the size of submodules, the main module only has 1 copy of this name.
- Use same ascii encoding instead of base64 encoding for export names. These all show up in the main bundle once and we save a 1-3 bytes per name.

Change-Id: I84bdab3ff5e17c8f2db456cf348e1abf1784e30a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/461280
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
2025-11-13 12:31:51 -08:00
Martin Kustermann 59c8081950 [dart2wasm] Use shared closure argument dispatchers for dynamic call entries
This removes 4591 functions from ACX gallery main module
which translates to -43 KB / -1.6% (-3.5% code section)

We have already today a callsite guarantee that the closure call
(type, positional, named) arguments are valid arguments to the
target closure (they also have the right type).

That means the `closure.vtable.dynamicCall` entry's only purpose
is to unpack the (type, positional, named) argument arrays and call
the target.

Instead of calling the target directly (as we did so far) we now
unpack argument arrays and call the right vtable entry. This logic
can be shared amongst all closures of the same representation and
therefore leads to big reduction in wasm functions.

=> We do that in this CL.

There's two exceptions to this:

* In dynamic module scenario we don't have closed-world knowledge
  of closure definitions & closure call site. There's no specific
  vtable entries for positional+name combinations we could forward
  to.

* In closed world scenario where there's a usage of `Function.apply`
  with named arguments: We don't generate vtable entries for all
  possible name combinations a closure can be called with.

So we change the closure layouter algorithm to find out if there's
a usage of `Function.apply` with named arguments.

A few tangential changes:

* Fix a bug revealed by this change: The static tearoff
  instantiation constant's dynamic call entry must pass the generic
  closure object when calling the generic closure.
  => The shared dynamic call entry dispatchers will now verify
     (in assertion) mode the assumptions, which revealed this issue

* The closure layouter algorithm will now consider `obj.foo(a: ...)`
  as a potential dynamic call site (due to call-via-field) and
  therefore record the name combinations used there
  => Tested via `web/wasm/closures/dynamic_call_via_field_test`

We test the optimization by checking in 3 tests that show what
ends up in the vtables:

* `pkg/dart2wasm/test/ir_tests/dyn_closure.dart`
  => uses dynamic calls
  => dynamic call entries are "closure arguments dispatcher"

* `pkg/dart2wasm/test/ir_tests/dyn_closure_function_apply.dart`
  => uses Function.apply without names
  => dynamic call entries are "closure arguments dispatcher"

* `pkg/dart2wasm/test/ir_tests/dyn_closure_function_apply_named.dart`
  => uses Function.apply with named arguments
  => dynamic call entries are closure specific

Issue https://github.com/dart-lang/sdk/issues/60458

Change-Id: I099984b542b05920b02596410a1bf6a08d2a0302
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/460080
Reviewed-by: Ömer Ağacan <omersa@google.com>
2025-11-07 11:17:57 -08:00
Nate Biggs 0507f059ee [dart2js] Fix error when comparing constants referenced across deferred import boundaries.
Constant folding was considering strings (or other primitives) unequal if one of them was a DeferredGlobalConstantValue and the other was not. This was producing incorrect results in the cases where the underlying constants were in fact equal.

For the purposes of constant folding equality checks, we should just ignore the deferred boundary and compare the constants themselves.

Fixes: https://github.com/dart-lang/sdk/issues/61833
Change-Id: Ibbd950ae910ee2377353f5fae8d9bea5c64d9d84
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/460020
Commit-Queue: Nate Biggs <natebiggs@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
2025-11-06 20:49:28 -08:00
Stephen Adams 456efb510d Make wasm/uri_base_test run on browsers
Change-Id: I6bc542bc6b2f68e7043d4c840da26dda30faad2d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/456141
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
2025-10-23 11:21:40 -07:00
Mayank Patke a63045d197 [dart2wasm] Remove dart:js_util from supported libraries
Since that stops `dart:_js_annotations` from building on dart2wasm,
let's stop supporting that, too.

This causes `package:js` to no longer work on dart2wasm, but none of the
allowlisted packages use it any longer, so we can simply migrate tests
off of it.

Fixes: #56502
Fixes: #61550
Change-Id: I6a6a696438255ebf25a5a17131cdbd327ba81581
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/453645
Commit-Queue: Mayank Patke <fishythefish@google.com>
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Nate Biggs <natebiggs@google.com>
2025-10-15 15:38:35 -07:00
Nate Biggs 10be4da5dc [dart2wasm] Run wasm-opt local optimizations on deferred loading modules.
Output for ACX gallery (monolith):
```
acx_gallery.Time.Total(CompileTime): 216.80 s
acx_gallery.Time.Dart2Wasm(CompileTime): 88.37 s
acx_gallery.Time.Wasm2WasmOpt(CompileTime): 128.43 s
acx_gallery.Size.mjs(CodeSize): 421228 bytes
acx_gallery.Size.mjs.gz(CodeSize): 29255 bytes
acx_gallery.Size.wasm(CodeSize): 38083554 bytes
acx_gallery.Size.wasm.gz(CodeSize): 9040482 bytes
acx_gallery.Size.wasm.opt(CodeSize): 13147207 bytes
acx_gallery.Size.wasm.opt.gz(CodeSize): 3288834 bytes
acx_gallery.MemoryUse.Max(MemoryUse): 2398687232 bytes
acx_gallery.MemoryUse.Dart2Wasm(MemoryUse): 2359529472 bytes
acx_gallery.MemoryUse.Wasm2WasmOpt(MemoryUse): 2398687232 bytes
```

Output for ACX gallery (monolith -O0):
```
acx_gallery.Time.Dart2Wasm(CompileTime): 87.49 s
acx_gallery.Size.mjs(CodeSize): 421228 bytes
acx_gallery.Size.mjs.gz(CodeSize): 29255 bytes
acx_gallery.Size.wasm(CodeSize): 38466066 bytes
acx_gallery.Size.wasm.gz(CodeSize): 8509479 bytes
acx_gallery.MemoryUse.Dart2Wasm(MemoryUse): 2261155840 bytes
```

Output for ACX gallery (deferred):
```
acx_gallery.Time.Total(CompileTime): 138.03 s
acx_gallery.Time.Dart2Wasm(CompileTime): 92.67 s
acx_gallery.Time.Wasm2WasmOpt(CompileTime): 45.36 s
acx_gallery.Size.mjs(CodeSize): 422584 bytes
acx_gallery.Size.mjs.gz(CodeSize): 29610 bytes
acx_gallery.Size.wasm(CodeSize): 50639314 bytes
acx_gallery.Size.wasm.gz(CodeSize): 13063929 bytes
acx_gallery.Size.wasm.opt(CodeSize): 20308884 bytes
acx_gallery.Size.wasm.opt.gz(CodeSize): 5569508 bytes
acx_gallery.MemoryUse.Max(MemoryUse): 2165276672 bytes
acx_gallery.MemoryUse.Dart2Wasm(MemoryUse): 2165276672 bytes
acx_gallery.MemoryUse.Wasm2WasmOpt(MemoryUse): 454402048 bytes
```

Change-Id: Ia6627b94ff80019b3c180850a3940d7e714c068b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/453240
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
2025-10-06 10:28:40 -07:00
Mayank Patke 20056805f7 [dart2wasm] Clean up dart2wasm tests using js_util
Bug: #61550
Change-Id: I6a6a696405a6a8df62c3088fb3f7bdba2e27b783
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/450967
Reviewed-by: Srujan Gaddam <srujzs@google.com>
2025-09-25 14:22:43 -07:00
Mayank Patke f626a3ce71 [dart2wasm] Remove tests/web/wasm/js_util_test
Bug: #61550
Change-Id: I6a6a6964cb56a64028e6fedd5f986ef0efecf52a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/450961
Auto-Submit: Mayank Patke <fishythefish@google.com>
Reviewed-by: Srujan Gaddam <srujzs@google.com>
2025-09-25 14:22:43 -07:00
Srujan Gaddam b01bf92f9a [dart2wasm] Typed array wrappers should unwrap instead of reinstantiate
Fixes https://github.com/dart-lang/sdk/issues/61543

toJS and jsify conversions instantiate a new typed array using
the same underlying buffer instead of returning the original
typed array. To fix this, the typed array wrappers now capture
the original array ref (if any) and return it in toJSArrayExternRef
if it exists and the caller didn't ask for a subrange.

Note that _ref in the wrappers refer to a DataView, and not
the array ref. Some methods are renamed to reduce that confusion.

Change-Id: Ia9befd114a77489aedbb3644c9604aef4b3af7c1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/450966
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Srujan Gaddam <srujzs@google.com>
2025-09-25 09:24:31 -07:00
Martin Kustermann 520607a6a7 [dart2wasm] Ensure we never emit string import names that are longer than 100_000 bytes
D8 currently allows longer import names bot JSShell and JSC do
issue a validation error if the name is larger than 100_000.

This fixes e.g. StarryStrings benchmarks on JSC/JSShell

TEST=tests/web/wasm/long_string_import_test.dart

Change-Id: I93df5d0e3014f6157750511fe486677e9b9413d7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/450922
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
2025-09-23 04:02:01 -07:00
Nicholas Shahan 3f2801d63f [ddc] Avoid revisiting sub-expressions on chained invocations
Similar to https://dart-review.googlesource.com/c/sdk/+/445540 this
avoids compilation timeouts by avoiding recompiling the receiver in long
chains of invocations.

Guards all of the soundness checking behind a single generation check.
This was already true for nested calls but not for chains of invocations.

This also resolves a correctness issue where sub expressions of the
receiver in chained calls would be evaluated multiple times at runtime.

Change-Id: Iaf192a639e90d77995da55c7fa9c248e7bb93491
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/450183
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Nicholas Shahan <nshahan@google.com>
2025-09-22 14:25:22 -07:00