From 33044ddd9a678c784ed7376e294be50f22a1efc2 Mon Sep 17 00:00:00 2001 From: Mark Zhou Date: Wed, 30 Sep 2020 00:08:23 +0000 Subject: [PATCH] [dartdevc] Resetting lazy js types on hot restart Fixes https://github.com/flutter/flutter/issues/66361 Change-Id: I4e68963a36c71bb4dac964d8b0fdcd8ee9187e97 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164101 Reviewed-by: Nicholas Shahan Reviewed-by: Sigmund Cherem Commit-Queue: Mark Zhou --- .../private/ddc_runtime/runtime.dart | 4 +- .../private/ddc_runtime/types.dart | 6 +-- .../dartdevc/hot_restart_js_interop_test.dart | 51 +++++++++++++++++++ 3 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 tests/dartdevc/hot_restart_js_interop_test.dart diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart index cb832e068c6..3738a3d7a6a 100644 --- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart +++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart @@ -179,8 +179,8 @@ final List _cacheMaps = JS('!', '[]'); /// A list of functions to reset static fields back to their uninitialized /// state. /// -/// This is populated by [defineLazyField], and only contains the list of fields -/// that have actually been initialized. +/// This is populated by [defineLazyField] and [LazyJSType] and only contains +/// fields that have been initialized. @notNull final List _resetFields = JS('', '[]'); diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart index acb1615f6e9..31e7e91b668 100644 --- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart +++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart @@ -177,7 +177,7 @@ F tearoffInterop(F f) { /// we disable type checks for in these cases, and allow any JS object to work /// as if it were an instance of this JS type. class LazyJSType extends DartType { - Function()? _getRawJSTypeFn; + Function() _getRawJSTypeFn; @notNull final String _dartName; Object? _rawJSType; @@ -199,14 +199,14 @@ class LazyJSType extends DartType { // overhead, especially if exceptions are being thrown. Also it means the // behavior of a given type check can change later on. try { - raw = _getRawJSTypeFn!(); + raw = _getRawJSTypeFn(); } catch (e) {} if (raw == null) { _warn('Cannot find native JavaScript type ($_dartName) for type check'); } else { _rawJSType = raw; - _getRawJSTypeFn = null; // Free the function that computes the JS type. + JS('', '#.push(() => # = null)', _resetFields, _rawJSType); } return raw; } diff --git a/tests/dartdevc/hot_restart_js_interop_test.dart b/tests/dartdevc/hot_restart_js_interop_test.dart new file mode 100644 index 00000000000..e3899df6bff --- /dev/null +++ b/tests/dartdevc/hot_restart_js_interop_test.dart @@ -0,0 +1,51 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Tests that JS interop works with hot restart. + +// Requirements=nnbd + +@JS() +library hot_restart_js_interop_test; + +import 'dart:js' show context; +import 'dart:js_util'; +import 'dart:_foreign_helper' as helper show JS; +import 'dart:_runtime' as dart; + +import 'package:expect/expect.dart'; +import 'package:js/js.dart'; + +@JS() +external void eval(String code); + +@JS('window.MyClass') +class MyClass { + external MyClass(); +} + +abstract class Wrapper { + T? rawObject; + + Wrapper() { + final T defaultObject = createDefault(); + rawObject = defaultObject; + } + + T createDefault(); +} + +class WrappedClass extends Wrapper { + @override + MyClass createDefault() => MyClass(); +} + +void main() { + // See: https://github.com/flutter/flutter/issues/66361 + eval("self.MyClass = function MyClass() {}"); + var c = WrappedClass(); + dart.hotRestart(); + eval("self.MyClass = function MyClass() {}"); + c = WrappedClass(); +}