Skip to content

Commit

Permalink
[web] Avoid using js_util.{jsify,dartify}() in dart2wasm for conver…
Browse files Browse the repository at this point in the history
…ting to JS wrappers

The `js_util.jsify()` related code shows up in CPU profile of wonderous.

=> Any `SkwasmObjectWrapper` object invokes this logic in the
constructor and dispose method.

This PR

* makes `DomFinalizationRegistryExtension` accept JSAny types instead of
  Dart types and internally converting
  => Callsites can call more precise `<>.toJS*` extension methods
  => Avoids extra type checks on the objects

* avoids making `toJSAnyShallow` delegate to `toJSAnyDeep` in wasm
  => `toJSAnyDeep` uses `js_util.jsify()` which is slow
  => We cannot use `Object.toJSBox` due to it being slower to create JS
  boxes as it semantically does something different atm (see issue below)
  => Instead use conditional import of `dart:_wasm` which provides the
  necessary primitives
  => Similar for going from JS to Dart.

* Avoid calling converting from Dart object to JSAny more than needed
  (we did the operation twice for each registration and once for
  unregistration)

Issue dart-lang/sdk#55183

change condition

move
  • Loading branch information
mkustermann committed Mar 18, 2024
1 parent 638eddb commit 58afe07
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 30 deletions.
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/canvaskit/native_memory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ NativeMemoryFinalizationRegistry nativeMemoryFinalizationRegistry = NativeMemory
class NativeMemoryFinalizationRegistry {
void register(Object owner, UniqueRef<Object> ref) {
if (browserSupportsFinalizationRegistry) {
_finalizationRegistry.register(owner, ref.toJSBox);
_finalizationRegistry.register(owner.toJSAnyShallow, ref.toJSBox);
}
}
}
Expand Down
46 changes: 19 additions & 27 deletions lib/web_ui/lib/src/engine/dom.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:js_interop';
import 'dart:math' as math;
import 'dart:typed_data';

import 'package:ui/src/engine/skwasm/skwasm_stub.dart' if (dart.library.ffi) 'package:ui/src/engine/skwasm/skwasm_impl.dart';
import 'package:js/js_util.dart' as js_util;
import 'package:meta/meta.dart';

Expand Down Expand Up @@ -36,27 +37,27 @@ import 'browser_detection.dart';
/// are currently represented across web backends, these extensions should be
/// used carefully and only on types that are known to not contains `JSNull` and
/// `JSUndefined`.
extension ObjectToJSAnyExtension on Object {
JSAny get toJSAnyShallow {
if (isWasm) {
return toJSAnyDeep;
} else {
return this as JSAny;
}
}
extension ObjectToJSAnyExtension on Object {
// Once `Object.toJSBox` is faster (see
// https://github.com/dart-lang/sdk/issues/55183) we can remove this
// backend-specific workaround.
@pragma('wasm:prefer-inline')
@pragma('dart2js:tryInline')
JSAny get toJSAnyShallow => dartToJsWrapper(this);

@pragma('wasm:prefer-inline')
@pragma('dart2js:tryInline')
JSAny get toJSAnyDeep => js_util.jsify(this) as JSAny;
}

extension JSAnyToObjectExtension on JSAny {
Object get toObjectShallow {
if (isWasm) {
return toObjectDeep;
} else {
return this;
}
}
@pragma('wasm:prefer-inline')
@pragma('dart2js:tryInline')
Object get toObjectShallow => jsWrapperToDart(this);

@pragma('wasm:prefer-inline')
@pragma('dart2js:tryInline')
Object get toObjectDeep => js_util.dartify(this)!;
}

Expand Down Expand Up @@ -3662,22 +3663,13 @@ DomFinalizationRegistry createDomFinalizationRegistry(JSFunction cleanup) =>

extension DomFinalizationRegistryExtension on DomFinalizationRegistry {
@JS('register')
external JSVoid _register1(JSAny target, JSAny value);
external JSVoid register(JSAny target, JSAny value);

@JS('register')
external JSVoid _register2(JSAny target, JSAny value, JSAny token);
void register(Object target, Object value, [Object? token]) {
if (token != null) {
_register2(
target.toJSAnyShallow, value.toJSAnyShallow, token.toJSAnyShallow);
} else {
_register1(target.toJSAnyShallow, value.toJSAnyShallow);
}
}
external JSVoid registerWithToken(JSAny target, JSAny value, JSAny token);

@JS('unregister')
external JSVoid _unregister(JSAny token);
void unregister(Object token) => _unregister(token.toJSAnyShallow);
external JSVoid unregister(JSAny token);
}

/// Whether the current browser supports `FinalizationRegistry`.
Expand Down
1 change: 1 addition & 0 deletions lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'dart:ffi';

export 'skwasm_impl/canvas.dart';
export 'skwasm_impl/codecs.dart';
export 'skwasm_impl/dart_js_conversion.dart';
export 'skwasm_impl/filters.dart';
export 'skwasm_impl/font_collection.dart';
export 'skwasm_impl/image.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:_wasm';
import 'dart:js_interop';

@pragma('wasm:prefer-inline')
@pragma('dart2js:tryInline')
JSAny dartToJsWrapper(Object object) =>
WasmAnyRef.fromObject(object).externalize().toJS;

@pragma('wasm:prefer-inline')
@pragma('dart2js:tryInline')
Object jsWrapperToDart(JSAny jsWrapper) =>
externRefForJSAny(jsWrapper).internalize()!.toObject();
7 changes: 5 additions & 2 deletions lib/web_ui/lib/src/engine/skwasm/skwasm_impl/memory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,14 @@ class SkwasmFinalizationRegistry<T extends NativeType> {
final DisposeFunction<T> dispose;

void register(SkwasmObjectWrapper<T> wrapper) {
registry.register(wrapper, wrapper.handle.address, wrapper);
final JSAny jsWrapper = wrapper.toJSAnyShallow;
registry.registerWithToken(
jsWrapper, wrapper.handle.address.toJS, jsWrapper);
}

void evict(SkwasmObjectWrapper<T> wrapper) {
registry.unregister(wrapper);
final JSAny jsWrapper = wrapper.toJSAnyShallow;
registry.unregister(jsWrapper);
dispose(wrapper.handle);
}
}
1 change: 1 addition & 0 deletions lib/web_ui/lib/src/engine/skwasm/skwasm_stub.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
// ignore: unnecessary_library_directive
library skwasm_stub;

export 'skwasm_stub/dart_js_conversion.dart';
export 'skwasm_stub/renderer.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:js_interop';

@pragma('wasm:prefer-inline')
@pragma('dart2js:tryInline')
JSAny dartToJsWrapper(Object object) => object as JSAny;

@pragma('wasm:prefer-inline')
@pragma('dart2js:tryInline')
Object jsWrapperToDart(JSAny jsWrapper) => jsWrapper;

0 comments on commit 58afe07

Please sign in to comment.