Skip to content

Commit

Permalink
Version 3.2.0-193.0.dev
Browse files Browse the repository at this point in the history
Merge e130bb3 into dev
  • Loading branch information
Dart CI committed Sep 23, 2023
2 parents 3833ae9 + e130bb3 commit ad9acca
Show file tree
Hide file tree
Showing 16 changed files with 208 additions and 117 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ constraint][language version] lower bound to 3.2 or greater (`sdk: '^3.2.0'`).
of allowed types. Namely, this include the primitive types like `String`, JS
types from `dart:js_interop`, and other static interop types (either through
`@staticInterop` or extension types).
- **Breaking Change on `dart:js_interop` `isNull` and `isUndefined`**:
`null` and `undefined` can only be discerned in the JS backends. dart2wasm
conflates the two values and treats them both as Dart null. Therefore, these
two helper methods should not be used on dart2wasm and will throw to avoid
potentially erroneous code.

### Tools

Expand Down
24 changes: 7 additions & 17 deletions pkg/dartdev/lib/src/commands/run.dart
Original file line number Diff line number Diff line change
Expand Up @@ -388,23 +388,13 @@ class _DebuggingSession {
) async {
final sdkDir = dirname(sdk.dart);
final fullSdk = sdkDir.endsWith('bin');
final ddsSnapshot = fullSdk
? sdk.ddsSnapshot
: absolute(sdkDir, 'gen', 'dds.dart.snapshot');
final devToolsBinaries =
fullSdk ? sdk.devToolsBinaries : absolute(sdkDir, 'devtools');
String snapshotName = fullSdk
? sdk.ddsAotSnapshot
: absolute(sdkDir, 'gen', 'dds_aot.dart.snapshot');
String execName = sdk.dartAotRuntime;
if (!Sdk.checkArtifactExists(snapshotName)) {
// An AOT snapshot of dds is not available, we could
// be running on the ia32 platform so check for a regular
// kernel file being present.
snapshotName = fullSdk
? sdk.ddsSnapshot
: absolute(sdkDir, 'gen', 'dds.dart.snapshot');
if (!Sdk.checkArtifactExists(snapshotName)) {
return false;
}
execName = sdk.dart;
if (!Sdk.checkArtifactExists(ddsSnapshot)) {
return false;
}
ServiceProtocolInfo serviceInfo = await Service.getInfo();
// Wait for VM service to publish its connection info.
Expand All @@ -413,10 +403,10 @@ class _DebuggingSession {
serviceInfo = await Service.getInfo();
}
final process = await Process.start(
execName,
sdk.dart,
[
if (debugDds) '--enable-vm-service=0',
snapshotName,
ddsSnapshot,
serviceInfo.serverUri.toString(),
host,
port,
Expand Down
7 changes: 0 additions & 7 deletions pkg/dartdev/lib/src/sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,6 @@ class Sdk {
'dds.dart.snapshot',
);

String get ddsAotSnapshot => path.absolute(
sdkPath,
'bin',
'snapshots',
'dds_aot.dart.snapshot',
);

String get frontendServerSnapshot => path.absolute(
sdkPath,
'bin',
Expand Down
6 changes: 1 addition & 5 deletions pkg/dartdev/test/sdk_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ void _sdk() {
});

test('dds snapshot', () {
expectSnapshotExists(Sdk().ddsAotSnapshot, Sdk().ddsSnapshot);
expectFileExists(Sdk().ddsSnapshot);
});

test('dart2js snapshot', () {
Expand All @@ -45,10 +45,6 @@ void expectFileExists(String path) {
expect(File(path).existsSync(), isTrue);
}

void expectSnapshotExists(String aotpath, String jitpath) {
expect(File(aotpath).existsSync() || File(jitpath).existsSync(), isTrue);
}

void expectDirectoryExists(String path) {
expect(Directory(path).existsSync(), isTrue);
}
17 changes: 5 additions & 12 deletions sdk/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ declare_args() {
# ........dart2wasm_product.snapshot (if not on ia32)
# ........dartdev.dart.snapshot (app-jit snapshot or kernel dill file)
# ........dartdevc.dart.snapshot
# ........dds_aot.dart.snapshot (AOT snapshot) or dds.dart.snapshot (ia32)
# ........dds.dart.snapshot
# ........frontend_server_aot.dart.snapshot (AOT snapshot, if not on ia32)
# ........frontend_server.dart.snapshot
# ........gen_kernel_aot.dart.snapshot (if not on ia32)
Expand Down Expand Up @@ -116,22 +116,15 @@ _platform_sdk_snapshots = [
"dartdev",
"../utils/dartdev:dartdev",
],
[
"dds",
"../utils/dds:dds",
],
[
"frontend_server",
"../utils/kernel-service:frontend_server",
],
]
if (dart_target_arch != "ia32" && dart_target_arch != "x86") {
_platform_sdk_snapshots += [ [
"dds_aot",
"../utils/dds:dds_aot",
] ]
} else {
_platform_sdk_snapshots += [ [
"dds",
"../utils/dds:dds",
] ]
}
if (dart_snapshot_kind == "app-jit") {
_platform_sdk_snapshots += [ [
"kernel-service",
Expand Down
7 changes: 2 additions & 5 deletions sdk/lib/_internal/js_shared/lib/js_interop_patch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,15 @@ import 'dart:typed_data';
JSObject get globalContext => staticInteropGlobalContext as JSObject;

/// Helper for working with the [JSAny?] top type in a backend agnostic way.
/// TODO(joshualitt): Remove conflation of null and undefined after migration.
@patch
extension NullableUndefineableJSAnyExtension on JSAny? {
@patch
@pragma('dart2js:prefer-inline')
bool get isUndefined =>
this == null || js_util.typeofEquals(this, 'undefined');
bool get isUndefined => js_util.typeofEquals(this, 'undefined');

@patch
@pragma('dart2js:prefer-inline')
bool get isNull =>
this == null || foreign_helper.JS('bool', '# === null', this);
bool get isNull => foreign_helper.JS('bool', '# === null', this);

@patch
@pragma('dart2js:prefer-inline')
Expand Down
6 changes: 4 additions & 2 deletions sdk/lib/_internal/js_shared/lib/js_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ class JSBigInt implements JSAny {}
/// in JS trampolines.
typedef JSVoid = void;

@JS()
@JS('Promise')
@staticInterop
class JSPromise implements JSObject {}
class JSPromise implements JSObject {
external factory JSPromise(JSFunction executor);
}
21 changes: 4 additions & 17 deletions sdk/lib/_internal/vm/bin/vmservice_io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,24 +81,11 @@ class _DebuggingSession {

final fullSdk = dartDir.endsWith('bin');

final dartAotPath = [
dartDir,
fullSdk ? 'dartaotruntime' : 'dart_precompiled_runtime_product',
].join('/');
String snapshotName = [
final ddsSnapshot = [
dartDir,
fullSdk ? 'snapshots' : 'gen',
'dds_aot.dart.snapshot',
'dds.dart.snapshot',
].join('/');
String execName = dartAotPath;
if (!File(snapshotName).existsSync()) {
snapshotName = [
dartDir,
fullSdk ? 'snapshots' : 'gen',
'dds.dart.snapshot',
].join('/');
execName = dartPath.toString();
}

final devToolsBinaries = [
dartDir,
Expand All @@ -108,9 +95,9 @@ class _DebuggingSession {

const enableLogging = false;
_process = await Process.start(
execName,
dartPath.toString(),
[
snapshotName,
ddsSnapshot,
server!.serverAddress!.toString(),
host,
port,
Expand Down
10 changes: 8 additions & 2 deletions sdk/lib/_internal/wasm/lib/js_interop_patch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,16 @@ extension NullableUndefineableJSAnyExtension on JSAny? {
// reified `JSUndefined` and `JSNull`, we have to handle the case where
// `this == null`. However, after migration we can remove these checks.
@patch
bool get isUndefined => this == null || isJSUndefined(this?.toExternRef);
bool get isUndefined =>
throw UnimplementedError("JS 'null' and 'undefined' are internalized as "
"Dart null in dart2wasm. As such, they can not be differentiated and "
"this API should not be used when compiling to Wasm.");

@patch
bool get isNull => this == null || this!.toExternRef.isNull;
bool get isNull =>
throw UnimplementedError("JS 'null' and 'undefined' are internalized as "
"Dart null in dart2wasm. As such, they can not be differentiated and "
"this API should not be used when compiling to Wasm.");

@patch
JSBoolean typeofEquals(JSString type) =>
Expand Down
6 changes: 4 additions & 2 deletions sdk/lib/_internal/wasm/lib/js_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,11 @@ class JSFunction implements JSObject {}
@staticInterop
class JSExportedDartFunction implements JSFunction {}

@JS()
@JS('Promise')
@staticInterop
class JSPromise implements JSObject {}
class JSPromise implements JSObject {
external factory JSPromise(JSFunction executor);
}

@JS('Array')
@staticInterop
Expand Down
99 changes: 89 additions & 10 deletions sdk/lib/js_interop/js_interop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
library dart.js_interop;

import 'dart:_js_types' as js_types;
import 'dart:js_interop_unsafe';
import 'dart:typed_data';

/// Allow use of `@staticInterop` classes with JS types as well as export
Expand Down Expand Up @@ -146,19 +147,36 @@ typedef JSBigInt = js_types.JSBigInt;
/// lowering.
external JSObject get globalContext;

/// `JSUndefined` and `JSNull` are actual reified types on some backends, but
/// not others. Instead, users should use nullable types for any type that could
/// contain `JSUndefined` or `JSNull`. However, instead of trying to determine
/// the nullability of a JS type in Dart, i.e. using `?`, `!`, `!= null` or `==
/// null`, users should use the provided helpers below to determine if it is
/// safe to downcast a potentially `JSNullable` or `JSUndefineable` object to a
/// defined and non-null JS type.
// TODO(joshualitt): Investigate whether or not it will be possible to reify
// `JSUndefined` and `JSNull` on all backends.
/// JS `undefined` and JS `null` are internalized differently based on the
/// backends. In the JS backends, Dart `null` can actually be JS `undefined` or
/// JS `null`. In dart2wasm, that's not the case: there's only one Wasm value
/// `null` can be. Therefore, when we get back JS `null` or JS `undefined`, we
/// internalize both as Dart `null` in dart2wasm, and when we pass Dart `null`
/// to an interop API, we pass JS `null`. In the JS backends, Dart `null`
/// retains its original value when passed back to an interop API. Be wary of
/// writing code where this distinction between `null` and `undefined` matters.
// TODO(srujzs): Investigate what it takes to allow users to distinguish between
// the two "nullish" values. An annotation-based model where users annotate
// interop APIs to internalize `undefined` differently seems promising, but does
// not handle some cases like converting a `JSArray` with `undefined`s in it to
// `List<JSAny?>`. In this case, the implementation of the list wrapper needs to
// make the decision, not the user.
extension NullableUndefineableJSAnyExtension on JSAny? {
/// Determine if this value corresponds to JS `undefined`.
///
/// **WARNING**: Currently, there isn't a way to distinguish between JS
/// `undefined` and JS `null` in dart2wasm. As such, this should only be used
/// for code that compiles to JS and will throw on dart2wasm.
external bool get isUndefined;

/// Determine if this value corresponds to JS `null`.
///
/// **WARNING**: Currently, there isn't a way to distinguish between JS
/// `undefined` and JS `null` in dart2wasm. As such, this should only be used
/// for code that compiles to JS and will throw on dart2wasm.
external bool get isNull;
bool get isUndefinedOrNull => isUndefined || isNull;

bool get isUndefinedOrNull => this == null;
bool get isDefinedAndNotNull => !isUndefinedOrNull;
external JSBoolean typeofEquals(JSString typeString);

Expand Down Expand Up @@ -198,6 +216,20 @@ extension FunctionToJSExportedDartFunction on Function {
external JSExportedDartFunction get toJS;
}

/// Utility extensions for [JSFunction].
extension JSFunctionUtilExtension on JSFunction {
// Take at most 4 args for consistency with other APIs and relative brevity.
// If more are needed, you can declare your own external member. We rename
// this function since declaring a `call` member makes a class callable in
// Dart. This is convenient, but unlike Dart functions, JS functions
// explicitly take a `this` argument (which users can provide `null` for in
// the case where the function doesn't need it), which may lead to confusion.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
@JS('call')
external JSAny? callAsFunction(
[JSAny? thisArg, JSAny? arg1, JSAny? arg2, JSAny? arg3, JSAny? arg4]);
}

/// [JSBoxedDartObject] <-> [Object]
extension JSBoxedDartObjectToObject on JSBoxedDartObject {
external Object get toDart;
Expand All @@ -212,6 +244,53 @@ extension JSPromiseToFuture on JSPromise {
external Future<JSAny?> get toDart;
}

extension FutureOfJSAnyToJSPromise on Future<JSAny?> {
JSPromise get toJS {
return JSPromise((JSFunction resolve, JSFunction reject) {
this.then((JSAny? value) {
resolve.callAsFunction(resolve, value);
return value;
}, onError: (Object error, StackTrace stackTrace) {
// TODO(srujzs): Can we do something better here? This is pretty much
// useless to the user unless they call a Dart callback that consumes
// this value and unboxes.
final errorConstructor = globalContext['Error'] as JSFunction;
final wrapper = errorConstructor.callAsConstructor<JSObject>(
"Dart exception thrown from converted Future. Use the properties "
"'error' to fetch the boxed error and 'stack' to recover "
"the stack trace."
.toJS);
wrapper['error'] = error.toJSBox;
wrapper['stack'] = stackTrace.toString().toJS;
reject.callAsFunction(reject, wrapper);
return wrapper;
});
}.toJS);
}
}

extension FutureOfVoidToJSPromise on Future<void> {
JSPromise get toJS {
return JSPromise((JSFunction resolve, JSFunction reject) {
this.then((_) => resolve.callAsFunction(resolve),
onError: (Object error, StackTrace stackTrace) {
// TODO(srujzs): Can we do something better here? This is pretty much
// useless to the user unless they call a Dart callback that consumes
// this value and unboxes.
final errorConstructor = globalContext['Error'] as JSFunction;
final wrapper = errorConstructor.callAsConstructor<JSObject>(
"Dart exception thrown from converted Future. Use the properties "
"'error' to fetch the boxed error and 'stack' to recover "
"the stack trace."
.toJS);
wrapper['error'] = error.toJSBox;
wrapper['stack'] = stackTrace.toString().toJS;
reject.callAsFunction(reject, wrapper);
});
}.toJS);
}
}

// **WARNING**:
// Currently, the `toJS` getters on `dart:typed_data` types have inconsistent
// semantics today between dart2wasm and the JS compilers. dart2wasm copies the
Expand Down
Loading

0 comments on commit ad9acca

Please sign in to comment.