Version 1.13.1
Cherry-pick 3fa0cae192dce46aef1c9040e8782702aa1e2d9c to stable
Cherry-pick b837d63d47aed38ceae39b78ba4d04048475d0e5 to stable
Cherry-pick c315da5a06a6cbbe2b3424b5313254721cd40785 to stable
Cherry-pick dff13bef8de104d33b04820136da2d80f3c835d7 to stable
diff --git a/pkg/compiler/lib/src/apiimpl.dart b/pkg/compiler/lib/src/apiimpl.dart
index fdcbd37..5d6d2b89 100644
--- a/pkg/compiler/lib/src/apiimpl.dart
+++ b/pkg/compiler/lib/src/apiimpl.dart
@@ -69,6 +69,8 @@
hasOption(options, Flags.trustTypeAnnotations),
trustPrimitives:
hasOption(options, Flags.trustPrimitives),
+ trustJSInteropTypeAnnotations:
+ hasOption(options, Flags.trustJSInteropTypeAnnotations),
enableMinification: hasOption(options, Flags.minify),
useFrequencyNamer:
!hasOption(options, Flags.noFrequencyBasedMinification),
diff --git a/pkg/compiler/lib/src/commandline_options.dart b/pkg/compiler/lib/src/commandline_options.dart
index 668cb28..03f274e 100644
--- a/pkg/compiler/lib/src/commandline_options.dart
+++ b/pkg/compiler/lib/src/commandline_options.dart
@@ -40,6 +40,8 @@
static const String testMode = '--test-mode';
static const String trustPrimitives = '--trust-primitives';
static const String trustTypeAnnotations = '--trust-type-annotations';
+ static const String trustJSInteropTypeAnnotations =
+ '--experimental-trust-js-interop-type-annotations';
static const String useContentSecurityPolicy = '--csp';
static const String useCpsIr = '--use-cps-ir';
static const String verbose = '--verbose';
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 8fab748..2f980bb 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -181,6 +181,7 @@
final bool enableUserAssertions;
final bool trustTypeAnnotations;
final bool trustPrimitives;
+ final bool trustJSInteropTypeAnnotations;
final bool disableTypeInferenceFlag;
final Uri deferredMapUri;
final bool dumpInfo;
@@ -432,6 +433,7 @@
this.enableUserAssertions: false,
this.trustTypeAnnotations: false,
this.trustPrimitives: false,
+ this.trustJSInteropTypeAnnotations: false,
bool disableTypeInferenceFlag: false,
this.maxConcreteTypeSize: 5,
this.enableMinification: false,
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index fa7aeb7..662316b 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -119,6 +119,7 @@
bool dumpInfo = false;
bool allowNativeExtensions = false;
bool trustTypeAnnotations = false;
+ bool trustJSInteropTypeAnnotations = false;
bool checkedMode = false;
// List of provided options that imply that output is expected.
List<String> optionsImplyCompilation = <String>[];
@@ -227,6 +228,11 @@
implyCompilation(argument);
}
+ setTrustJSInteropTypeAnnotations(String argument) {
+ trustJSInteropTypeAnnotations = true;
+ implyCompilation(argument);
+ }
+
setTrustPrimitives(String argument) {
implyCompilation(argument);
}
@@ -326,6 +332,9 @@
new OptionHandler(Flags.trustPrimitives,
(_) => setTrustPrimitives(
Flags.trustPrimitives)),
+ new OptionHandler(Flags.trustJSInteropTypeAnnotations,
+ (_) => setTrustJSInteropTypeAnnotations(
+ Flags.trustJSInteropTypeAnnotations)),
new OptionHandler(r'--help|/\?|/h', (_) => wantHelp = true),
new OptionHandler('--packages=.+', setPackageConfig),
new OptionHandler('--package-root=.+|-p.+', setPackageRoot),
diff --git a/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart b/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart
index e6cce5b..4199c69 100644
--- a/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart
+++ b/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart
@@ -116,6 +116,10 @@
ClassElement classElement = element;
+ // Skip classes that are completely unreachable. This should only happen
+ // when all of jsinterop types are unreachable from main.
+ if (!backend.compiler.world.isImplemented(classElement)) return;
+
if (!classElement
.implementsInterface(backend.jsJavaScriptObjectClass)) {
backend.reporter.reportErrorMessage(classElement,
diff --git a/pkg/compiler/lib/src/native/behavior.dart b/pkg/compiler/lib/src/native/behavior.dart
index 7dbab30..63d5bd6 100644
--- a/pkg/compiler/lib/src/native/behavior.dart
+++ b/pkg/compiler/lib/src/native/behavior.dart
@@ -645,12 +645,26 @@
static NativeBehavior ofMethod(FunctionElement method, Compiler compiler) {
FunctionType type = method.computeType(compiler.resolution);
var behavior = new NativeBehavior();
- behavior.typesReturned.add(type.returnType);
+ var returnType = type.returnType;
+ bool isInterop = method.isJsInterop;
+ // Note: For dart:html and other internal libraries we maintain, we can
+ // trust the return type and use it to limit what we enqueue. We have to
+ // be more conservative about JS interop types and assume they can return
+ // anything (unless the user provides the experimental flag to trust the
+ // type of js-interop APIs). We do restrict the allocation effects and say
+ // that interop calls create only interop types (which may be unsound if
+ // an interop call returns a DOM type and declares a dynamic return type,
+ // but otherwise we would include a lot of code by default).
+ // TODO(sigmund,sra): consider doing something better for numeric types.
+ behavior.typesReturned.add(
+ !isInterop || compiler.trustJSInteropTypeAnnotations ? returnType
+ : const DynamicType());
if (!type.returnType.isVoid) {
// Declared types are nullable.
behavior.typesReturned.add(compiler.coreTypes.nullType);
}
- behavior._capture(type, compiler.resolution);
+ behavior._capture(type, compiler.resolution,
+ isInterop: isInterop, compiler: compiler);
// TODO(sra): Optional arguments are currently missing from the
// DartType. This should be fixed so the following work-around can be
@@ -668,10 +682,15 @@
Resolution resolution = compiler.resolution;
DartType type = field.computeType(resolution);
var behavior = new NativeBehavior();
- behavior.typesReturned.add(type);
+ bool isInterop = field.isJsInterop;
+ // TODO(sigmund,sra): consider doing something better for numeric types.
+ behavior.typesReturned.add(
+ !isInterop || compiler.trustJSInteropTypeAnnotations ? type
+ : const DynamicType());
// Declared types are nullable.
behavior.typesReturned.add(resolution.coreTypes.nullType);
- behavior._capture(type, resolution);
+ behavior._capture(type, resolution,
+ isInterop: isInterop, compiler: compiler);
behavior._overrideWithAnnotations(field, compiler);
return behavior;
}
@@ -765,17 +784,50 @@
/// Models the behavior of Dart code receiving instances and methods of [type]
/// from native code. We usually start the analysis by capturing a native
/// method that has been used.
- void _capture(DartType type, Resolution resolution) {
+ ///
+ /// We assume that JS-interop APIs cannot instantiate Dart types or
+ /// non-JSInterop native types.
+ void _capture(DartType type, Resolution resolution,
+ {bool isInterop: false, Compiler compiler}) {
type.computeUnaliased(resolution);
type = type.unaliased;
if (type is FunctionType) {
FunctionType functionType = type;
- _capture(functionType.returnType, resolution);
+ _capture(functionType.returnType, resolution,
+ isInterop: isInterop, compiler: compiler);
for (DartType parameter in functionType.parameterTypes) {
_escape(parameter, resolution);
}
} else {
- typesInstantiated.add(type);
+ DartType instantiated = null;
+ JavaScriptBackend backend = compiler?.backend;
+ if (!isInterop) {
+ typesInstantiated.add(type);
+ } else {
+ if (type.element != null && type.element.isNative) {
+ // Any declared native or interop type (isNative implies isJsInterop)
+ // is assumed to be allocated.
+ typesInstantiated.add(type);
+ }
+
+ if (!compiler.trustJSInteropTypeAnnotations ||
+ type.isDynamic || type.isObject) {
+ // By saying that only JS-interop types can be created, we prevent
+ // pulling in every other native type (e.g. all of dart:html) when a
+ // JS interop API returns dynamic or when we don't trust the type
+ // annotations. This means that to some degree we still use the return
+ // type to decide whether to include native types, even if we don't
+ // trust the type annotation.
+ ClassElement cls = backend.jsJavaScriptObjectClass;
+ cls.ensureResolved(resolution);
+ typesInstantiated.add(cls.thisType);
+ } else {
+ // Otherwise, when the declared type is a Dart type, we do not
+ // register an allocation because we assume it cannot be instantiated
+ // from within the JS-interop code. It must have escaped from another
+ // API.
+ }
+ }
}
}
diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart
index 0258d7d..a913c77 100644
--- a/pkg/compiler/lib/src/ssa/builder.dart
+++ b/pkg/compiler/lib/src/ssa/builder.dart
@@ -5852,7 +5852,7 @@
&& params.optionalParametersAreNamed;
}
- HForeignCode invokeJsInteropFunction(Element element,
+ HForeignCode invokeJsInteropFunction(FunctionElement element,
List<HInstruction> arguments,
SourceInformation sourceInformation) {
assert(element.isJsInterop);
@@ -5886,6 +5886,9 @@
var nativeBehavior = new native.NativeBehavior()
..codeTemplate = codeTemplate;
+ if (compiler.trustJSInteropTypeAnnotations) {
+ nativeBehavior.typesReturned.add(constructor.enclosingClass.thisType);
+ }
return new HForeignCode(
codeTemplate,
backend.dynamicType, filteredArguments,
@@ -5905,34 +5908,43 @@
arguments = arguments.where((arg) => arg != null).toList();
var inputs = <HInstruction>[target]..addAll(arguments);
- js.Template codeTemplate;
- if (element.isGetter) {
- codeTemplate = js.js.parseForeignJS("#");
- } else if (element.isSetter) {
- codeTemplate = js.js.parseForeignJS("# = #");
- } else {
- FunctionElement function = element;
- FunctionSignature params = function.functionSignature;
+ var nativeBehavior = new native.NativeBehavior()
+ ..sideEffects.setAllSideEffects();
- var argsStub = <String>[];
- for (int i = 0; i < arguments.length; i++) {
- argsStub.add('#');
- }
+ DartType type = element.isConstructor ?
+ element.enclosingClass.thisType : element.type.returnType;
+ // Native behavior effects here are similar to native/behavior.dart.
+ // The return type is dynamic if we don't trust js-interop type
+ // declarations.
+ nativeBehavior.typesReturned.add(
+ compiler.trustJSInteropTypeAnnotations ? type : const DynamicType());
- if (element.isConstructor) {
- codeTemplate = js.js.parseForeignJS("new #(${argsStub.join(",")})");
- } else {
- codeTemplate = js.js.parseForeignJS("#(${argsStub.join(",")})");
- }
+ // The allocation effects include the declared type if it is native (which
+ // includes js interop types).
+ if (type.element != null && type.element.isNative) {
+ nativeBehavior.typesInstantiated.add(type);
}
- var nativeBehavior = new native.NativeBehavior()
- ..codeTemplate = codeTemplate
- ..typesReturned.add(
- backend.jsJavaScriptObjectClass.thisType)
- ..typesInstantiated.add(
- backend.jsJavaScriptObjectClass.thisType)
- ..sideEffects.setAllSideEffects();
+ // It also includes any other JS interop type if we don't trust the
+ // annotation or if is declared too broad.
+ if (!compiler.trustJSInteropTypeAnnotations || type.isObject ||
+ type.isDynamic) {
+ nativeBehavior.typesInstantiated.add(
+ backend.jsJavaScriptObjectClass.thisType);
+ }
+
+ String code;
+ if (element.isGetter) {
+ code = "#";
+ } else if (element.isSetter) {
+ code = "# = #";
+ } else {
+ var args = new List.filled(arguments.length, '#').join(',');
+ code = element.isConstructor ? "new #($args)" : "#($args)";
+ }
+ js.Template codeTemplate = js.js.parseForeignJS(code);
+ nativeBehavior.codeTemplate = codeTemplate;
+
return new HForeignCode(
codeTemplate,
backend.dynamicType, inputs,
diff --git a/runtime/vm/assembler_arm.cc b/runtime/vm/assembler_arm.cc
index 815b796..12fb7ff 100644
--- a/runtime/vm/assembler_arm.cc
+++ b/runtime/vm/assembler_arm.cc
@@ -104,6 +104,8 @@
Address ad) {
ASSERT(rd != kNoRegister);
ASSERT(cond != kNoCondition);
+ ASSERT(!ad.has_writeback() || (ad.rn() != rd)); // Unpredictable.
+
int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
B26 | (ad.kind() == Address::Immediate ? 0 : B25) |
(load ? L : 0) |
diff --git a/runtime/vm/assembler_arm.h b/runtime/vm/assembler_arm.h
index 9ba279c..78fb22d 100644
--- a/runtime/vm/assembler_arm.h
+++ b/runtime/vm/assembler_arm.h
@@ -321,6 +321,11 @@
Mode mode() const { return static_cast<Mode>(encoding() & kModeMask); }
+ bool has_writeback() const {
+ return (mode() == PreIndex) || (mode() == PostIndex) ||
+ (mode() == NegPreIndex) || (mode() == NegPostIndex);
+ }
+
uint32_t encoding() const { return encoding_; }
// Encoding for addressing mode 3.
diff --git a/runtime/vm/flow_graph_type_propagator.cc b/runtime/vm/flow_graph_type_propagator.cc
index 3448fd0..29ec735 100644
--- a/runtime/vm/flow_graph_type_propagator.cc
+++ b/runtime/vm/flow_graph_type_propagator.cc
@@ -149,8 +149,6 @@
}
}
- HandleBranchOnStrictCompare(block);
-
for (intptr_t i = 0; i < block->dominated_blocks().length(); ++i) {
PropagateRecursive(block->dominated_blocks()[i]);
}
@@ -159,61 +157,6 @@
}
-void FlowGraphTypePropagator::HandleBranchOnStrictCompare(
- BlockEntryInstr* block) {
- BranchInstr* branch = block->last_instruction()->AsBranch();
- if (branch == NULL) {
- return;
- }
-
- StrictCompareInstr* compare = branch->comparison()->AsStrictCompare();
- if ((compare == NULL) || !compare->right()->BindsToConstant()) {
- return;
- }
-
- const intptr_t rollback_point = rollback_.length();
-
- Definition* defn = compare->left()->definition();
- const Object& right = compare->right()->BoundConstant();
- intptr_t cid = right.GetClassId();
-
- if (defn->IsLoadClassId() && right.IsSmi()) {
- defn = defn->AsLoadClassId()->object()->definition();
- cid = Smi::Cast(right).Value();
- }
-
- if (!CheckClassInstr::IsImmutableClassId(cid)) {
- if ((cid == kOneByteStringCid) || (cid == kTwoByteStringCid)) {
- SetTypeOf(defn, ZoneCompileType::Wrap(CompileType::String()));
- PropagateRecursive((compare->kind() == Token::kEQ_STRICT) ?
- branch->true_successor() : branch->false_successor());
- RollbackTo(rollback_point);
- }
- return;
- }
-
- if (compare->kind() == Token::kEQ_STRICT) {
- if (cid == kNullCid) {
- branch->set_constrained_type(MarkNonNullable(defn));
- PropagateRecursive(branch->false_successor());
- }
-
- SetCid(defn, cid);
- PropagateRecursive(branch->true_successor());
- } else if (compare->kind() == Token::kNE_STRICT) {
- if (cid == kNullCid) {
- branch->set_constrained_type(MarkNonNullable(defn));
- PropagateRecursive(branch->true_successor());
- }
-
- SetCid(defn, cid);
- PropagateRecursive(branch->false_successor());
- }
-
- RollbackTo(rollback_point);
-}
-
-
void FlowGraphTypePropagator::RollbackTo(intptr_t rollback_point) {
for (intptr_t i = rollback_.length() - 1; i >= rollback_point; i--) {
types_[rollback_[i].index()] = rollback_[i].type();
diff --git a/runtime/vm/flow_graph_type_propagator.h b/runtime/vm/flow_graph_type_propagator.h
index 4b1baf4..3c10917 100644
--- a/runtime/vm/flow_graph_type_propagator.h
+++ b/runtime/vm/flow_graph_type_propagator.h
@@ -20,7 +20,6 @@
void Propagate();
void PropagateRecursive(BlockEntryInstr* block);
- void HandleBranchOnStrictCompare(BlockEntryInstr* block);
void RollbackTo(intptr_t rollback_point);
diff --git a/runtime/vm/simulator_arm.cc b/runtime/vm/simulator_arm.cc
index 5419aaa..531ed4a 100644
--- a/runtime/vm/simulator_arm.cc
+++ b/runtime/vm/simulator_arm.cc
@@ -1996,6 +1996,7 @@
HandleIllegalAccess(addr, instr);
} else {
if (write_back) {
+ ASSERT(rd != rn); // Unpredictable.
set_register(rn, rn_val);
}
if (!instr->HasSign()) {
@@ -2312,6 +2313,7 @@
HandleIllegalAccess(addr, instr);
} else {
if (write_back) {
+ ASSERT(rd != rn); // Unpredictable.
set_register(rn, rn_val);
}
if (instr->HasB()) {
@@ -2424,6 +2426,7 @@
HandleIllegalAccess(addr, instr);
} else {
if (write_back) {
+ ASSERT(rd != rn); // Unpredictable.
set_register(rn, rn_val);
}
if (instr->HasB()) {
diff --git a/runtime/vm/stub_code_arm.cc b/runtime/vm/stub_code_arm.cc
index 5acd669..1629107 100644
--- a/runtime/vm/stub_code_arm.cc
+++ b/runtime/vm/stub_code_arm.cc
@@ -419,6 +419,9 @@
__ eor(IP, IP, Operand(LR));
// Set up the frame manually with return address now stored in IP.
+ COMPILE_ASSERT(PP < CODE_REG);
+ COMPILE_ASSERT(CODE_REG < FP);
+ COMPILE_ASSERT(FP < IP);
__ EnterFrame((1 << PP) | (1 << CODE_REG) | (1 << FP) | (1 << IP), 0);
__ LoadPoolPointer();
@@ -434,9 +437,12 @@
if (i == CODE_REG) {
// Save the original value of CODE_REG pushed before invoking this stub
// instead of the value used to call this stub.
- COMPILE_ASSERT(IP > CODE_REG); // Assert IP is pushed first.
__ ldr(IP, Address(FP, kCallerSpSlotFromFp * kWordSize));
__ Push(IP);
+ } else if (i == SP) {
+ // Push(SP) has unpredictable behavior.
+ __ mov(IP, Operand(SP));
+ __ Push(IP);
} else {
__ Push(static_cast<Register>(i));
}
diff --git a/tests/compiler/dart2js/compiler_helper.dart b/tests/compiler/dart2js/compiler_helper.dart
index d3ac2ac..2604401 100644
--- a/tests/compiler/dart2js/compiler_helper.dart
+++ b/tests/compiler/dart2js/compiler_helper.dart
@@ -43,7 +43,8 @@
import 'output_collector.dart';
export 'output_collector.dart';
-/// Compile [code] and returns the code for [entry].
+/// Compile [code] and returns either the code for [entry] or, if [returnAll] is
+/// true, the code for the entire program.
///
/// If [check] is provided, it is executed on the code for [entry] before
/// returning. If [useMock] is `true` the [MockCompiler] is used for
@@ -54,8 +55,11 @@
bool minify: false,
bool analyzeAll: false,
bool disableInlining: true,
+ bool trustJSInteropTypeAnnotations: false,
bool useMock: false,
- void check(String generated)}) async {
+ void check(String generatedEntry),
+ bool returnAll: false}) async {
+ OutputCollector outputCollector = returnAll ? new OutputCollector() : null;
if (useMock) {
// TODO(johnniwinther): Remove this when no longer needed by
// `arithmetic_simplication_test.dart`.
@@ -65,7 +69,9 @@
// compiling a method.
disableTypeInference: true,
enableMinification: minify,
- disableInlining: disableInlining);
+ disableInlining: disableInlining,
+ trustJSInteropTypeAnnotations: trustJSInteropTypeAnnotations,
+ outputProvider: outputCollector);
await compiler.init();
compiler.parseScript(code);
lego.Element element = compiler.mainApp.find(entry);
@@ -89,7 +95,7 @@
if (check != null) {
check(generated);
}
- return generated;
+ return returnAll ? outputCollector.getOutput('', 'js') : generated;
} else {
List<String> options = <String>[
Flags.disableTypeInference];
@@ -102,6 +108,9 @@
if (analyzeAll) {
options.add(Flags.analyzeAll);
}
+ if (trustJSInteropTypeAnnotations) {
+ options.add(Flags.trustJSInteropTypeAnnotations);
+ }
Map<String, String> source;
if (entry != 'main') {
@@ -113,6 +122,7 @@
CompilationResult result = await runCompiler(
memorySourceFiles: source,
options: options,
+ outputProvider: outputCollector,
beforeRun: (compiler) {
if (disableInlining) {
compiler.disableInlining = true;
@@ -126,7 +136,7 @@
if (check != null) {
check(generated);
}
- return generated;
+ return returnAll ? outputCollector.getOutput('', 'js') : generated;
}
}
diff --git a/tests/compiler/dart2js/interop_anonymous_unreachable_test.dart b/tests/compiler/dart2js/interop_anonymous_unreachable_test.dart
new file mode 100644
index 0000000..3f38205
--- /dev/null
+++ b/tests/compiler/dart2js/interop_anonymous_unreachable_test.dart
@@ -0,0 +1,177 @@
+// Copyright (c) 2015, 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 tests.dart2js.interop_anonymous_unreachable_test;
+
+import 'dart:async';
+
+import 'package:test/test.dart';
+import 'compiler_helper.dart';
+
+
+main() {
+ test("unreachable code doesn't crash the compiler", () async {
+ // This test is a regression for Issue #24974
+ String generated = await compile("""
+ import 'package:js/js.dart';
+
+ @JS() @anonymous
+ class UniqueLongNameForTesting_A {
+ external factory UniqueLongNameForTesting_A();
+ }
+ main() {}
+ """, returnAll: true);
+
+ // the code should not be included in the output either.
+ expect(generated, isNot(contains("UniqueLongNameForTesting_A")));
+ });
+
+ group('tree-shaking interop types', () {
+ String program = """
+ import 'package:js/js.dart';
+
+ // reachable and allocated
+ @JS() @anonymous
+ class UniqueLongNameForTesting_A {
+ external bool get x;
+ external UniqueLongNameForTesting_D get d;
+ external UniqueLongNameForTesting_E get e;
+ external factory UniqueLongNameForTesting_A(
+ {UniqueLongNameForTesting_B arg0});
+ }
+
+ // visible through the parameter above, but not used.
+ @JS() @anonymous
+ class UniqueLongNameForTesting_B {
+ external factory UniqueLongNameForTesting_B();
+ }
+
+ // unreachable
+ @JS() @anonymous
+ class UniqueLongNameForTesting_C {
+ external factory UniqueLongNameForTesting_C();
+ }
+
+ // visible and reached through `d`.
+ @JS() @anonymous
+ class UniqueLongNameForTesting_D {
+ external factory UniqueLongNameForTesting_D();
+ }
+
+ // visible through `e`, but not reached.
+ @JS() @anonymous
+ class UniqueLongNameForTesting_E {
+ external factory UniqueLongNameForTesting_E();
+ }
+
+ main() {
+ print(new UniqueLongNameForTesting_A().x);
+ print(new UniqueLongNameForTesting_A().d);
+ }
+ """;
+
+ test('no tree-shaking by default', () async {
+ String generated = await compile(program, returnAll: true);
+ expect(generated.contains("UniqueLongNameForTesting_A"), isTrue);
+ expect(generated.contains("UniqueLongNameForTesting_D"), isTrue);
+
+ expect(generated.contains("UniqueLongNameForTesting_B"), isTrue);
+ expect(generated.contains("UniqueLongNameForTesting_C"), isTrue);
+ expect(generated.contains("UniqueLongNameForTesting_E"), isTrue);
+ });
+
+ test('tree-shake when using flag', () async {
+ String generated = await compile(program,
+ trustJSInteropTypeAnnotations: true,
+ returnAll: true);
+ expect(generated.contains("UniqueLongNameForTesting_A"), isTrue);
+ expect(generated.contains("UniqueLongNameForTesting_D"), isTrue);
+
+ expect(generated.contains("UniqueLongNameForTesting_B"), isFalse);
+ expect(generated.contains("UniqueLongNameForTesting_C"), isFalse);
+ expect(generated.contains("UniqueLongNameForTesting_E"), isFalse);
+ });
+ });
+
+ group('tree-shaking other native types', () {
+ String program = """
+ import 'dart:html';
+ import 'package:js/js.dart';
+
+ @JS() @anonymous
+ class UniqueLongNameForTesting_A {
+ external dynamic get x;
+ }
+
+ @JS() @anonymous
+ class UniqueLongNameForTesting_B {
+ external dynamic get y;
+ }
+
+ main() {
+ print(new UniqueLongNameForTesting_A().x);
+ }
+ """;
+
+ test('allocation effect of dynamic excludes native types', () async {
+ String generated = await compile(program, returnAll: true);
+ expect(generated.contains("UniqueLongNameForTesting_A"), isTrue);
+ // any js-interop type could be allocated by `get x`
+ expect(generated.contains("UniqueLongNameForTesting_B"), isTrue);
+ // but we exclude other native types like HTMLAudioElement
+ expect(generated.contains("HTMLAudioElement"), isFalse);
+ });
+
+ test('allocation effect of dynamic excludes native types [flag]', () async {
+ // Trusting types doesn't make a difference.
+ String generated = await compile(program,
+ trustJSInteropTypeAnnotations: true,
+ returnAll: true);
+ expect(generated.contains("UniqueLongNameForTesting_A"), isTrue);
+ expect(generated.contains("UniqueLongNameForTesting_B"), isTrue);
+ expect(generated.contains("HTMLAudioElement"), isFalse);
+ });
+
+ test('declared native types are included in allocation effect', () async {
+ String program2 = """
+ import 'dart:html';
+ import 'package:js/js.dart';
+
+ @JS() @anonymous
+ class UniqueLongNameForTesting_A {
+ external AudioElement get x;
+ }
+
+ main() {
+ print(new UniqueLongNameForTesting_A().x is AudioElement);
+ }
+ """;
+
+ String generated = await compile(program2, returnAll: true);
+ expect(generated.contains("UniqueLongNameForTesting_A"), isTrue);
+ expect(generated.contains("HTMLAudioElement"), isTrue);
+
+ program2 = """
+ import 'dart:html';
+ import 'package:js/js.dart';
+
+ @JS() @anonymous
+ class UniqueLongNameForTesting_A {
+ external dynamic get x;
+ }
+
+ main() {
+ print(new UniqueLongNameForTesting_A().x is AudioElement);
+ }
+ """;
+
+ generated = await compile(program2, returnAll: true);
+ expect(generated.contains("UniqueLongNameForTesting_A"), isTrue);
+ // This extra check is to make sure that we don't include HTMLAudioElement
+ // just because of the is-check. It is optimized away in this case because
+ // we believe it was never instantiated.
+ expect(generated.contains("HTMLAudioElement"), isFalse);
+ });
+ });
+}
diff --git a/tests/compiler/dart2js/mock_compiler.dart b/tests/compiler/dart2js/mock_compiler.dart
index 7456c29..2195fe6 100644
--- a/tests/compiler/dart2js/mock_compiler.dart
+++ b/tests/compiler/dart2js/mock_compiler.dart
@@ -87,6 +87,7 @@
// affected by inlining support.
bool disableInlining: true,
bool trustTypeAnnotations: false,
+ bool trustJSInteropTypeAnnotations: false,
bool enableAsyncAwait: false,
int this.expectedWarnings,
int this.expectedErrors,
@@ -106,6 +107,7 @@
emitJavaScript: emitJavaScript,
preserveComments: preserveComments,
trustTypeAnnotations: trustTypeAnnotations,
+ trustJSInteropTypeAnnotations: trustJSInteropTypeAnnotations,
diagnosticOptions:
new DiagnosticOptions(showPackageWarnings: true),
outputProvider: new LegacyCompilerOutput(outputProvider)) {
diff --git a/tests/html/html.status b/tests/html/html.status
index 56276ae..bba61dd 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -9,8 +9,8 @@
[ $compiler == none && ($runtime == dartium || $runtime == drt) ]
js_array_test: Skip # Dartium JSInterop failure
-js_typed_interop_test: Skip # Dartium JSInterop failure
-mirrors_js_typed_interop_test: Skip # Dartium JSInterop failure
+mirrors_js_typed_interop_test: Fail # Missing expected failure (Issue 25044)
+js_typed_interop_side_cast_exp_test: Fail, OK # tests dart2js-specific behavior.
cross_domain_iframe_test: RuntimeError # Dartium JSInterop failure
indexeddb_2_test: Fail # Dartium JSInterop failure. Identity preservation on array deferred copy.
@@ -19,7 +19,6 @@
native_gc_test: Skip # Dartium JSInterop failure
transferables_test: RuntimeError # Dartium JSInterop failure
-
[ $compiler == none && ($runtime == drt || $runtime == dartium ) ]
worker_api_test: Fail # Issue 10223
resource_http_test: Fail # Issue 24203
diff --git a/tests/html/js_dart_to_string_test.dart b/tests/html/js_dart_to_string_test.dart
index 5293053..d2c23f4 100644
--- a/tests/html/js_dart_to_string_test.dart
+++ b/tests/html/js_dart_to_string_test.dart
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
@JS()
-library js_typed_interop_test;
+library js_dart_to_string_test;
import 'dart:html';
diff --git a/tests/html/js_typed_interop_anonymous2_exp_test.dart b/tests/html/js_typed_interop_anonymous2_exp_test.dart
new file mode 100644
index 0000000..baebd03
--- /dev/null
+++ b/tests/html/js_typed_interop_anonymous2_exp_test.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2015, 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.
+//
+// SharedOptions=--experimental-trust-js-interop-type-annotations
+
+// Same test as js_typed_interop_anonymous2, but using the
+// --experimental-trust-js-interop-type-annotations flag.
+library js_typed_interop_anonymous2_exp_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class A {
+ external factory A({B b});
+
+ external B get b;
+}
+
+@JS() @anonymous
+class B {
+ external factory B({C c});
+
+ external C get c;
+}
+
+@JS() @anonymous
+class C {
+ external factory C();
+}
+
+// D is unreachable, and that is OK
+@JS() @anonymous
+class D {
+ external factory D();
+}
+
+main() {
+ useHtmlConfiguration();
+
+ test('simple', () {
+ var b = new B();
+ var a = new A(b: b);
+ expect(a.b, equals(b));
+ expect(b.c, isNull);
+ });
+}
diff --git a/tests/html/js_typed_interop_anonymous2_test.dart b/tests/html/js_typed_interop_anonymous2_test.dart
new file mode 100644
index 0000000..ed0e732
--- /dev/null
+++ b/tests/html/js_typed_interop_anonymous2_test.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2015, 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 js_typed_interop_anonymous2_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class A {
+ external factory A({B b});
+
+ external B get b;
+}
+
+@JS() @anonymous
+class B {
+ external factory B({C c});
+
+ external C get c;
+}
+
+@JS() @anonymous
+class C {
+ external factory C();
+}
+
+// D is unreachable, and that is OK
+@JS() @anonymous
+class D {
+ external factory D();
+}
+
+main() {
+ useHtmlConfiguration();
+
+ test('simple', () {
+ var b = new B();
+ var a = new A(b: b);
+ expect(a.b, equals(b));
+ expect(b.c, isNull);
+ });
+}
diff --git a/tests/html/js_typed_interop_anonymous_exp_test.dart b/tests/html/js_typed_interop_anonymous_exp_test.dart
new file mode 100644
index 0000000..40a04aa
--- /dev/null
+++ b/tests/html/js_typed_interop_anonymous_exp_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2015, 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.
+//
+// SharedOptions=--experimental-trust-js-interop-type-annotations
+
+// Same test as js_typed_interop_anonymous, but using the
+// --experimental-trust-js-interop-type-annotations flag.
+library js_typed_interop_anonymous_exp_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class Literal {
+ external factory Literal({int x, String y, num z});
+
+ external int get x;
+ external String get y;
+ external num get z;
+}
+
+main() {
+ useHtmlConfiguration();
+
+ test('simple', () {
+ var l = new Literal(x: 3, y: "foo");
+ expect(l.x, equals(3));
+ expect(l.y, equals("foo"));
+ expect(l.z, isNull);
+ });
+}
diff --git a/tests/html/js_typed_interop_anonymous_test.dart b/tests/html/js_typed_interop_anonymous_test.dart
new file mode 100644
index 0000000..b2d1593
--- /dev/null
+++ b/tests/html/js_typed_interop_anonymous_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2015, 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 js_typed_interop_anonymous_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class Literal {
+ external factory Literal({int x, String y, num z});
+
+ external int get x;
+ external String get y;
+ external num get z;
+}
+
+main() {
+ useHtmlConfiguration();
+
+ test('simple', () {
+ var l = new Literal(x: 3, y: "foo");
+ expect(l.x, equals(3));
+ expect(l.y, equals("foo"));
+ expect(l.z, isNull);
+ });
+}
diff --git a/tests/html/js_typed_interop_anonymous_unreachable_exp_test.dart b/tests/html/js_typed_interop_anonymous_unreachable_exp_test.dart
new file mode 100644
index 0000000..a4e2c96
--- /dev/null
+++ b/tests/html/js_typed_interop_anonymous_unreachable_exp_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2015, 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.
+//
+// SharedOptions=--experimental-trust-js-interop-type-annotations
+
+// Same test as js_typed_interop_anonymous_unreachable, but using the
+// --experimental-trust-js-interop-type-annotations flag.
+library js_typed_interop_anonymous_unreachable_exp_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class Literal {
+ external factory Literal({int x, String y, num z});
+
+ external int get x;
+ external String get y;
+ external num get z;
+}
+
+main() {
+ useHtmlConfiguration();
+ test('nothing to do', () {
+ // This test is empty, but it is a regression for Issue# 24974: dartjs
+ // would crash trying to compile code that used @anonymous and that was
+ // not reachable from main.
+ });
+}
diff --git a/tests/html/js_typed_interop_anonymous_unreachable_test.dart b/tests/html/js_typed_interop_anonymous_unreachable_test.dart
new file mode 100644
index 0000000..ba8758e
--- /dev/null
+++ b/tests/html/js_typed_interop_anonymous_unreachable_test.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2015, 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 js_typed_interop_anonymous_unreachable_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class Literal {
+ external factory Literal({int x, String y, num z});
+
+ external int get x;
+ external String get y;
+ external num get z;
+}
+
+main() {
+ useHtmlConfiguration();
+ test('nothing to do', () {
+ // This test is empty, but it is a regression for Issue# 24974: dartjs
+ // would crash trying to compile code that used @anonymous and that was
+ // not reachable from main.
+ });
+}
diff --git a/tests/html/js_typed_interop_side_cast_exp_test.dart b/tests/html/js_typed_interop_side_cast_exp_test.dart
new file mode 100644
index 0000000..6fd929a
--- /dev/null
+++ b/tests/html/js_typed_interop_side_cast_exp_test.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2015, 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.
+//
+// SharedOptions=--experimental-trust-js-interop-type-annotations
+
+// Similar test to js_typed_interop_side_cast, but because we are using the
+// --experimental-trust-js-interop-type-annotations flag, we test a slighly
+// different behavior.
+library js_typed_interop_side_cast_exp_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class A {
+ external int get x;
+ external factory A({int x});
+}
+
+@JS() @anonymous
+class B {
+ external int get x;
+ external factory B({int x});
+}
+
+@JS() @anonymous
+class C {
+ external int get x;
+ external factory C({int x});
+}
+
+main() {
+ useHtmlConfiguration();
+
+ test('side-casts work for reachable types', () {
+ new C(x: 3); // make C reachable
+ var a = new A(x: 3);
+ expect(a is C, isTrue);
+ C c = a;
+ expect(c.x, equals(3));
+ });
+
+ // Note: this test would fail without the experimental flag.
+ test('side-casts do not work for unreachable types', () {
+ var a = new A(x: 3);
+ expect(a is B, isFalse);
+ });
+}
diff --git a/tests/html/js_typed_interop_side_cast_test.dart b/tests/html/js_typed_interop_side_cast_test.dart
new file mode 100644
index 0000000..d50e1ac
--- /dev/null
+++ b/tests/html/js_typed_interop_side_cast_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2015, 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 js_typed_interop_anonymous2_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class A {
+ external int get x;
+ external factory A({int x});
+}
+
+@JS() @anonymous
+class C {
+ external int get x;
+ external factory C({int x});
+}
+
+@JS() @anonymous
+class B {
+ external int get x;
+ external factory B({int x});
+}
+
+main() {
+ useHtmlConfiguration();
+
+ test('side-casts work for reachable types', () {
+ new C(x: 3); // make C reachable
+ var a = new A(x: 3);
+ expect(a is C, isTrue);
+ C c = a;
+ expect(c.x, equals(3));
+ });
+
+ test('side-casts work for otherwise unreachable types', () {
+ var a = new A(x: 3);
+ expect(a is B, isTrue);
+ });
+}
diff --git a/tests/language/vm/type_propagation_test.dart b/tests/language/vm/type_propagation_test.dart
new file mode 100644
index 0000000..935c096
--- /dev/null
+++ b/tests/language/vm/type_propagation_test.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--optimization-counter-threshold=1000 --max-polymorphic-checks=1
+
+// Test correct loop invariant code motion and type propagation from is-checks
+// and null-comparisons.
+
+class B {
+ var b;
+ B(this.b);
+}
+
+class C {
+ final f0;
+
+ final a;
+ C() : a = new B(0);
+}
+
+foo(x) {
+ for (var i = 0; i < 10; i++) {
+ i + i;
+ i + i;
+ if (x is C) {
+ x.a.b < 0;
+ }
+ }
+}
+
+class Y { var f = null; }
+
+bar(y) {
+ var x = y.f;
+ for (var i = 0; i < 10; i++) {
+ if (x != null) {
+ x.a.b < 0;
+ }
+ }
+}
+
+
+main () {
+ var o = new Y();
+ o.f = new C();
+ bar(o);
+ o.f = null;
+ bar(o);
+
+ for (var i = 0; i < 1000; i++) bar(o);
+
+ foo(new C());
+ foo(0);
+
+ for (var i = 0; i < 1000; i++) foo(0);
+}
diff --git a/tools/VERSION b/tools/VERSION
index b920ee5..73cc853 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -26,6 +26,6 @@
CHANNEL stable
MAJOR 1
MINOR 13
-PATCH 0
+PATCH 1
PRERELEASE 0
PRERELEASE_PATCH 0