2bb32ec55a
Change-Id: Ib1afd38a7d3694c2e5ed591132f41b147f76d75a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/427560 Reviewed-by: Stephen Adams <sra@google.com> Commit-Queue: Mayank Patke <fishythefish@google.com>
282 lines
8.6 KiB
Dart
282 lines
8.6 KiB
Dart
// Copyright (c) 2016, 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.
|
|
|
|
library jsinterop.internal_annotations_test;
|
|
|
|
import 'package:expect/async_helper.dart';
|
|
import 'package:expect/expect.dart';
|
|
import 'package:compiler/src/common/elements.dart';
|
|
import 'package:compiler/src/compiler.dart';
|
|
import 'package:compiler/src/elements/entities.dart' show ClassEntity;
|
|
import 'package:compiler/src/elements/names.dart';
|
|
import 'package:compiler/src/universe/class_hierarchy.dart';
|
|
import 'package:compiler/src/universe/selector.dart';
|
|
import 'package:compiler/src/js_model/js_world.dart' show JClosedWorld;
|
|
import '../helpers/element_lookup.dart';
|
|
import 'package:compiler/src/util/memory_compiler.dart';
|
|
|
|
void main() {
|
|
asyncTest(() async {
|
|
await testClasses('package:js/js.dart', 'dart:_js_annotations');
|
|
await testClasses('package:js/js.dart', 'package:js/js.dart');
|
|
await testClasses('dart:_js_annotations', 'dart:_js_annotations');
|
|
});
|
|
}
|
|
|
|
testClasses(String import1, String import2) async {
|
|
test(
|
|
String mainSource, {
|
|
List<String> directlyInstantiated = const <String>[],
|
|
List<String> abstractlyInstantiated = const <String>[],
|
|
List<String> indirectlyInstantiated = const <String>[],
|
|
}) async {
|
|
String mainFile = 'sdk/tests/web/native/main.dart';
|
|
Uri entryPoint = Uri.parse('memory:$mainFile');
|
|
CompilationResult result = await runCompiler(
|
|
entryPoint: entryPoint,
|
|
memorySourceFiles: {
|
|
mainFile:
|
|
"""
|
|
import '$import1' as js1;
|
|
import '$import2' as js2;
|
|
|
|
@js1.JS()
|
|
class A {
|
|
external get foo;
|
|
|
|
external A(var foo);
|
|
}
|
|
|
|
@js2.JS('BClass')
|
|
class B {
|
|
external get foo;
|
|
|
|
external B(var foo);
|
|
}
|
|
|
|
@js1.JS()
|
|
@js1.anonymous
|
|
class C {
|
|
external get foo;
|
|
|
|
external factory C({foo});
|
|
}
|
|
|
|
@js2.JS()
|
|
@js2.anonymous
|
|
class D {
|
|
external get foo;
|
|
|
|
external factory D({foo});
|
|
}
|
|
|
|
class E {
|
|
final foo;
|
|
|
|
E(this.foo);
|
|
}
|
|
|
|
class F {
|
|
final foo;
|
|
|
|
F(this.foo);
|
|
}
|
|
|
|
newA() => A(0);
|
|
newB() => B(1);
|
|
newC() => C(foo: 2);
|
|
newD() => D(foo: 3);
|
|
newE() => E(4);
|
|
newF() => F(5);
|
|
|
|
$mainSource
|
|
""",
|
|
},
|
|
);
|
|
Compiler compiler = result.compiler!;
|
|
Map<String, ClassEntity> classEnvironment = <String, ClassEntity>{};
|
|
|
|
ClassEntity registerClass(ClassEntity cls) {
|
|
classEnvironment[cls.name] = cls;
|
|
return cls;
|
|
}
|
|
|
|
JClosedWorld world = compiler.backendClosedWorldForTesting!;
|
|
ElementEnvironment elementEnvironment = world.elementEnvironment;
|
|
ClassEntity Object_ = registerClass(world.commonElements.objectClass);
|
|
ClassEntity Interceptor = registerClass(
|
|
world.commonElements.jsInterceptorClass,
|
|
);
|
|
ClassEntity JavaScriptObject = registerClass(
|
|
world.commonElements.jsJavaScriptObjectClass,
|
|
);
|
|
ClassEntity LegacyJavaScriptObject = registerClass(
|
|
world.commonElements.jsLegacyJavaScriptObjectClass,
|
|
);
|
|
ClassEntity A = registerClass(findClass(world, 'A'));
|
|
ClassEntity B = registerClass(findClass(world, 'B'));
|
|
ClassEntity C = registerClass(findClass(world, 'C'));
|
|
ClassEntity D = registerClass(findClass(world, 'D'));
|
|
ClassEntity E = registerClass(findClass(world, 'E'));
|
|
ClassEntity F = registerClass(findClass(world, 'F'));
|
|
|
|
Selector nonExisting = Selector.getter(const PublicName('nonExisting'));
|
|
|
|
Expect.equals(elementEnvironment.getSuperClass(Interceptor), Object_);
|
|
Expect.equals(
|
|
elementEnvironment.getSuperClass(JavaScriptObject),
|
|
Interceptor,
|
|
);
|
|
Expect.equals(
|
|
elementEnvironment.getSuperClass(LegacyJavaScriptObject),
|
|
JavaScriptObject,
|
|
);
|
|
|
|
Expect.equals(elementEnvironment.getSuperClass(A), LegacyJavaScriptObject);
|
|
Expect.equals(elementEnvironment.getSuperClass(B), LegacyJavaScriptObject);
|
|
Expect.equals(elementEnvironment.getSuperClass(C), LegacyJavaScriptObject);
|
|
Expect.equals(elementEnvironment.getSuperClass(D), LegacyJavaScriptObject);
|
|
Expect.equals(elementEnvironment.getSuperClass(E), Object_);
|
|
Expect.equals(elementEnvironment.getSuperClass(F), Object_);
|
|
|
|
Expect.isFalse(world.nativeData.isJsInteropClass(Object_));
|
|
Expect.isTrue(world.nativeData.isJsInteropClass(A));
|
|
Expect.isTrue(world.nativeData.isJsInteropClass(B));
|
|
Expect.isTrue(world.nativeData.isJsInteropClass(C));
|
|
Expect.isTrue(world.nativeData.isJsInteropClass(D));
|
|
Expect.isFalse(world.nativeData.isJsInteropClass(E));
|
|
Expect.isFalse(world.nativeData.isJsInteropClass(F));
|
|
|
|
Expect.isFalse(world.nativeData.isAnonymousJsInteropClass(Object_));
|
|
Expect.isFalse(world.nativeData.isAnonymousJsInteropClass(A));
|
|
Expect.isFalse(world.nativeData.isAnonymousJsInteropClass(B));
|
|
Expect.isTrue(world.nativeData.isAnonymousJsInteropClass(C));
|
|
Expect.isTrue(world.nativeData.isAnonymousJsInteropClass(D));
|
|
Expect.isFalse(world.nativeData.isAnonymousJsInteropClass(E));
|
|
Expect.isFalse(world.nativeData.isAnonymousJsInteropClass(F));
|
|
|
|
Expect.equals('', world.nativeData.getJsInteropClassName(A));
|
|
Expect.equals('BClass', world.nativeData.getJsInteropClassName(B));
|
|
Expect.equals('', world.nativeData.getJsInteropClassName(C));
|
|
Expect.equals('', world.nativeData.getJsInteropClassName(D));
|
|
|
|
classEnvironment.forEach((name, cls) {
|
|
bool isInstantiated = false;
|
|
if (directlyInstantiated.contains(name)) {
|
|
isInstantiated = true;
|
|
Expect.isTrue(
|
|
world.classHierarchy.isDirectlyInstantiated(cls),
|
|
"Expected $name to be directly instantiated in `${mainSource}`:"
|
|
"\n${world.classHierarchy.dump(cls)}",
|
|
);
|
|
}
|
|
if (abstractlyInstantiated.contains(name)) {
|
|
isInstantiated = true;
|
|
Expect.isTrue(
|
|
world.classHierarchy.isAbstractlyInstantiated(cls),
|
|
"Expected $name to be abstractly instantiated in `${mainSource}`:"
|
|
"\n${world.classHierarchy.dump(cls)}",
|
|
);
|
|
Expect.isTrue(
|
|
world.needsNoSuchMethod(cls, nonExisting, ClassQuery.exact),
|
|
"Expected $name to need noSuchMethod for $nonExisting.",
|
|
);
|
|
Expect.isTrue(
|
|
world.needsNoSuchMethod(cls, nonExisting, ClassQuery.subclass),
|
|
"Expected $name to need noSuchMethod for $nonExisting.",
|
|
);
|
|
Expect.isTrue(
|
|
world.needsNoSuchMethod(cls, nonExisting, ClassQuery.subtype),
|
|
"Expected $name to need noSuchMethod for $nonExisting.",
|
|
);
|
|
}
|
|
if (indirectlyInstantiated.contains(name)) {
|
|
isInstantiated = true;
|
|
Expect.isTrue(
|
|
world.classHierarchy.isIndirectlyInstantiated(cls),
|
|
"Expected $name to be indirectly instantiated in `${mainSource}`:"
|
|
"\n${world.classHierarchy.dump(cls)}",
|
|
);
|
|
}
|
|
// Classes that are expected to be instantiated by default. `Object` and
|
|
// `Interceptor` are base types for non-native and native types, and
|
|
// `JavaScriptObject` is the base type for `dart:html` types.
|
|
var instantiatedBaseClasses = [
|
|
'Object',
|
|
'Interceptor',
|
|
'JavaScriptObject',
|
|
];
|
|
if (!isInstantiated && !instantiatedBaseClasses.contains(name)) {
|
|
Expect.isFalse(
|
|
world.classHierarchy.isInstantiated(cls),
|
|
"Expected $name to be uninstantiated in `${mainSource}`:"
|
|
"\n${world.classHierarchy.dump(cls)}",
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
await test('main() {}');
|
|
|
|
await test(
|
|
'main() => newA();',
|
|
abstractlyInstantiated: ['A', 'B', 'C', 'D'],
|
|
indirectlyInstantiated: [
|
|
'Object',
|
|
'Interceptor',
|
|
'JavaScriptObject',
|
|
'LegacyJavaScriptObject',
|
|
],
|
|
);
|
|
|
|
await test(
|
|
'main() => newB();',
|
|
abstractlyInstantiated: ['A', 'B', 'C', 'D'],
|
|
indirectlyInstantiated: [
|
|
'Object',
|
|
'Interceptor',
|
|
'JavaScriptObject',
|
|
'LegacyJavaScriptObject',
|
|
],
|
|
);
|
|
|
|
await test(
|
|
'main() => newC();',
|
|
abstractlyInstantiated: ['A', 'B', 'C', 'D'],
|
|
indirectlyInstantiated: [
|
|
'Object',
|
|
'Interceptor',
|
|
'JavaScriptObject',
|
|
'LegacyJavaScriptObject',
|
|
],
|
|
);
|
|
|
|
await test(
|
|
'main() => newD();',
|
|
abstractlyInstantiated: ['A', 'B', 'C', 'D'],
|
|
indirectlyInstantiated: [
|
|
'Object',
|
|
'Interceptor',
|
|
'JavaScriptObject',
|
|
'LegacyJavaScriptObject',
|
|
],
|
|
);
|
|
|
|
await test('main() => newE();', directlyInstantiated: ['E']);
|
|
|
|
await test('main() => newF();', directlyInstantiated: ['F']);
|
|
|
|
await test(
|
|
'main() => [newD(), newE()];',
|
|
directlyInstantiated: ['E'],
|
|
abstractlyInstantiated: ['A', 'B', 'C', 'D'],
|
|
indirectlyInstantiated: [
|
|
'Object',
|
|
'Interceptor',
|
|
'JavaScriptObject',
|
|
'LegacyJavaScriptObject',
|
|
],
|
|
);
|
|
}
|