Skip to content

Commit

Permalink
[dart2js] Erase static interop type in static invocation
Browse files Browse the repository at this point in the history
Static invocations of external factories are casted so that the
result, which is a @staticInterop type, can be treated as the erased
type instead. This CL fixes the issue where the type that it was
casted to was never replaced with the erased type.

Change-Id: Ic6eb529349ea2b5c42f91c2740d501d4f81bc38e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/323505
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Srujan Gaddam <srujzs@google.com>
  • Loading branch information
srujzs authored and Commit Queue committed Aug 31, 2023
1 parent 54faa31 commit b25873f
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,7 @@ class _TypeSubstitutor extends ReplacementVisitor {
}
}

/// Erases usage of `@JS` classes that are annotated with `@staticInterop` in
/// favor of `JavaScriptObject`.
/// Erases usage of `@JS` classes that are annotated with `@staticInterop`.
class StaticInteropClassEraser extends Transformer {
final CloneVisitorNotMembers _cloner = CloneVisitorNotMembers();
late final _StaticInteropConstantReplacer _constantReplacer;
Expand Down Expand Up @@ -259,7 +258,7 @@ class StaticInteropClassEraser extends Transformer {
//
// In order to circumvent this, we introduce a new static method that
// clones the factory body and has a return type of
// `JavaScriptObject`. Invocations of the factory are turned into
// the erased type. Invocations of the factory are turned into
// invocations of the static method. The original factory is still kept
// in order to make modular compilations work.
_findOrCreateFactoryStub(node);
Expand Down Expand Up @@ -290,7 +289,7 @@ class StaticInteropClassEraser extends Transformer {
@override
TreeNode visitConstructorInvocation(ConstructorInvocation node) {
if (hasStaticInteropAnnotation(node.target.enclosingClass)) {
// Add a cast so that the result gets typed as `JavaScriptObject`.
// Add a cast so that the result gets typed as the erased type.
var newInvocation = super.visitConstructorInvocation(node) as Expression;
return AsExpression(
newInvocation,
Expand Down Expand Up @@ -321,10 +320,12 @@ class StaticInteropClassEraser extends Transformer {
return StaticInvocation(stub, args, isConst: node.isConst)
..fileOffset = node.fileOffset;
} else {
// Add a cast so that the result gets typed as `JavaScriptObject`.
// Add a cast so that the result gets typed as the erased type.
var newInvocation = super.visitStaticInvocation(node) as Expression;
return AsExpression(
newInvocation, node.target.function.returnType as InterfaceType)
newInvocation,
_eraseStaticInteropType(
node.target.function.returnType as InterfaceType))
..fileOffset = newInvocation.fileOffset;
}
}
Expand Down
47 changes: 47 additions & 0 deletions tests/lib/js/static_interop_test/generic_factory_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) 2023, 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.

// Test type parameters on @staticInterop factories.

import 'package:js/js.dart';
import 'package:expect/minitest.dart';

@JS()
@staticInterop
class Array<T, U extends String> {
external factory Array(T t, U u);
factory Array.nonExternal(T t, U u) => Array(t, u);
}

extension on Array {
external Object operator [](int index);
}

@JS()
@staticInterop
@anonymous
class Anonymous<T, U extends String> {
external factory Anonymous({T? t, U? u});
factory Anonymous.nonExternal({T? t, U? u}) => Anonymous(t: t, u: u);
}

extension AnonymousExtension<T, U extends String> on Anonymous<T, U> {
external T get t;
external U get u;
}

void main() {
final arr1 = Array(true, '');
expect(arr1[0], true);
expect(arr1[1], '');
final arr2 = Array<bool, String>(false, '');
expect(arr2[0], false);
expect(arr2[1], '');
final anon1 = Anonymous(t: true, u: '');
expect(anon1.t, true);
expect(anon1.u, '');
final anon2 = Anonymous<bool, String>(t: false, u: '');
expect(anon2.t, false);
expect(anon2.u, '');
}

0 comments on commit b25873f

Please sign in to comment.