diff --git a/pkg/compiler/lib/src/inferrer/list_tracer.dart b/pkg/compiler/lib/src/inferrer/list_tracer.dart index db867520de1..17bb7ed4a28 100644 --- a/pkg/compiler/lib/src/inferrer/list_tracer.dart +++ b/pkg/compiler/lib/src/inferrer/list_tracer.dart @@ -6,6 +6,7 @@ library compiler.src.inferrer.list_tracer; import '../common/names.dart'; import '../elements/entities.dart'; +import '../native/behavior.dart'; import '../universe/selector.dart' show Selector; import '../util/util.dart' show Setlet; import 'node_tracer.dart'; @@ -162,10 +163,16 @@ class ListTracerVisitor extends TracerVisitor { @override visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { super.visitStaticCallSiteTypeInformation(info); + final commonElements = inferrer.closedWorld.commonElements; MemberEntity called = info.calledElement; - if (inferrer.closedWorld.commonElements.isForeign(called) && - called.name == Identifiers.JS) { - bailout('Used in JS ${info.debugName}'); + if (commonElements.isForeign(called) && called.name == Identifiers.JS) { + NativeBehavior nativeBehavior = inferrer.closedWorld.elementMap + .getNativeBehaviorForJsCall(info.invocationNode); + // Assume side-effects means that the list has escaped to some unknown + // location. + if (nativeBehavior.sideEffects.hasSideEffects()) { + bailout('Used in JS ${info.debugName}'); + } } } diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart index 365500e6e9d..78664ebac93 100644 --- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart +++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart @@ -968,6 +968,8 @@ class StaticCallSiteTypeInformation extends CallSiteTypeInformation { : super(abstractValueDomain, context, call, enclosing, selector, arguments, inLoop); + ir.StaticInvocation get invocationNode => _call as ir.StaticInvocation; + MemberTypeInformation _getCalledTypeInfo(InferrerEngine inferrer) { return inferrer.types.getInferredTypeOfMember(calledElement); } diff --git a/pkg/compiler/test/inference/data/closure_tracer.dart b/pkg/compiler/test/inference/data/closure_tracer.dart index 08105f70dc7..edccdb3193a 100644 --- a/pkg/compiler/test/inference/data/closure_tracer.dart +++ b/pkg/compiler/test/inference/data/closure_tracer.dart @@ -51,22 +51,22 @@ testStoredInInstance() { return res; } -/*member: testStoredInMapOfList:[null|subclass=Object]*/ +/*member: testStoredInMapOfList:[null|exact=JSUInt31]*/ testStoredInMapOfList() { var res; - /*[null|subclass=Object]*/ closure(/*[null|subclass=Object]*/ a) => res = a; + /*[exact=JSUInt31]*/ closure(/*[exact=JSUInt31]*/ a) => res = a; dynamic a = [closure]; dynamic b = {'foo': 1}; b - /*update: Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSExtendableArray], [exact=JSUInt31]), map: {foo: [exact=JSUInt31], bar: Container([null|exact=JSExtendableArray], element: [null|subclass=Object], length: null)})*/ + /*update: Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSExtendableArray], [exact=JSUInt31]), map: {foo: [exact=JSUInt31], bar: Container([null|exact=JSExtendableArray], element: [subclass=Closure], length: 1)})*/ ['bar'] = a; b - /*Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSExtendableArray], [exact=JSUInt31]), map: {foo: [exact=JSUInt31], bar: Container([null|exact=JSExtendableArray], element: [null|subclass=Object], length: null)})*/ + /*Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSExtendableArray], [exact=JSUInt31]), map: {foo: [exact=JSUInt31], bar: Container([null|exact=JSExtendableArray], element: [subclass=Closure], length: 1)})*/ ['bar'] - /*Container([null|exact=JSExtendableArray], element: [null|subclass=Object], length: null)*/ + /*Container([null|exact=JSExtendableArray], element: [subclass=Closure], length: 1)*/ [0](42); return res; } diff --git a/pkg/compiler/test/inference/data/list_js.dart b/pkg/compiler/test/inference/data/list_js.dart new file mode 100644 index 00000000000..9b640606357 --- /dev/null +++ b/pkg/compiler/test/inference/data/list_js.dart @@ -0,0 +1,60 @@ +// 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. + +// Test effect of NativeBehavior on list tracing. + +/// ignore: IMPORT_INTERNAL_LIBRARY +import 'dart:_foreign_helper' show JS; + +/*member: main:[null]*/ +main() { + test1(); + test2(); + test3(); + test4(); +} + +/*member: test1:[null]*/ +test1() { + var list = [42]; + JS('', '#', list); // '#' is by default a no-op. + witness1(list); +} + +/*member: witness1:[null]*/ +witness1( + /*Container([exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)*/ x) {} + +/*member: test2:[null]*/ +test2() { + var list = [42]; + JS('effects:all;depends:all', '#', list); + witness2(list); +} + +/*member: witness2:[null]*/ +witness2( + /*Container([exact=JSExtendableArray], element: [null|subclass=Object], length: null)*/ x) {} + +/*member: test3:[null]*/ +test3() { + var list = [42]; + JS('', '#.slice(0)', list); + witness3(list); +} + +/*member: witness3:[null]*/ +witness3( + /*Container([exact=JSExtendableArray], element: [null|subclass=Object], length: null)*/ x) {} + +/*member: test4:[null]*/ +test4() { + var list = [42]; + JS('effects:none;depends:all', '#.slice(0)', list); + witness4(list); +} + +/*member: witness4:[null]*/ +witness4( + /*Container([exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)*/ x) {} diff --git a/pkg/compiler/test/inference/data/map_tracer_keys.dart b/pkg/compiler/test/inference/data/map_tracer_keys.dart index 20e469e8c96..3f183a6adca 100644 --- a/pkg/compiler/test/inference/data/map_tracer_keys.dart +++ b/pkg/compiler/test/inference/data/map_tracer_keys.dart @@ -79,28 +79,28 @@ test2() { /*member: aDouble3:Union(null, [exact=JSDouble], [exact=JSExtendableArray])*/ dynamic aDouble3 = 42.5; -/*member: aList3:Container([exact=JSExtendableArray], element: [null|subclass=Object], length: null)*/ +/*member: aList3:Container([exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)*/ dynamic aList3 = [42]; -/*member: consume3:Container([exact=JSExtendableArray], element: [null|subclass=Object], length: null)*/ +/*member: consume3:Container([exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)*/ consume3( - /*Container([exact=JSExtendableArray], element: [null|subclass=Object], length: null)*/ x) => + /*Container([exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)*/ x) => x; /*member: test3:[null]*/ test3() { dynamic theMap = {'a': 2.2, 'b': 3.3, 'c': 4.4}; theMap - /*update: Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSDouble], [exact=JSExtendableArray]), map: {a: [exact=JSDouble], b: [exact=JSDouble], c: [exact=JSDouble], d: Container([null|exact=JSExtendableArray], element: [null|subclass=Object], length: null)})*/ + /*update: Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSDouble], [exact=JSExtendableArray]), map: {a: [exact=JSDouble], b: [exact=JSDouble], c: [exact=JSDouble], d: Container([null|exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)})*/ ['d'] = aList3; /*iterator: [exact=LinkedHashMapKeyIterable]*/ /*current: [exact=LinkedHashMapKeyIterator]*/ /*moveNext: [exact=LinkedHashMapKeyIterator]*/ for (var key in theMap. - /*Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSDouble], [exact=JSExtendableArray]), map: {a: [exact=JSDouble], b: [exact=JSDouble], c: [exact=JSDouble], d: Container([null|exact=JSExtendableArray], element: [null|subclass=Object], length: null)})*/ + /*Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSDouble], [exact=JSExtendableArray]), map: {a: [exact=JSDouble], b: [exact=JSDouble], c: [exact=JSDouble], d: Container([null|exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)})*/ keys) { aDouble3 = theMap - /*Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSDouble], [exact=JSExtendableArray]), map: {a: [exact=JSDouble], b: [exact=JSDouble], c: [exact=JSDouble], d: Container([null|exact=JSExtendableArray], element: [null|subclass=Object], length: null)})*/ + /*Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSDouble], [exact=JSExtendableArray]), map: {a: [exact=JSDouble], b: [exact=JSDouble], c: [exact=JSDouble], d: Container([null|exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)})*/ [key]; } // We have to reference it somewhere, so that it always gets resolved. diff --git a/pkg/compiler/test/inference/list_tracer_test.dart b/pkg/compiler/test/inference/list_tracer_test.dart index 96583746b39..4e23f1b4f33 100644 --- a/pkg/compiler/test/inference/list_tracer_test.dart +++ b/pkg/compiler/test/inference/list_tracer_test.dart @@ -237,9 +237,7 @@ doTest(String allocation, {bool nullify}) async { checkType('listEscapingInSetterValue', commonMasks.numType); checkType('listEscapingInIndex', commonMasks.numType); checkType('listEscapingInIndexSet', commonMasks.uint31Type); - // TODO(johnniwinther): Since Iterable.iterableToString is part of the closed - // world we find the `dynamicType` instead of `numType`. - checkType('listEscapingTwiceInIndexSet', commonMasks.dynamicType); + checkType('listEscapingTwiceInIndexSet', commonMasks.numType); checkType('listSetInNonFinalField', commonMasks.numType); checkType('listWithChangedLength', commonMasks.uint31Type.nullable()); diff --git a/sdk/lib/_internal/js_runtime/lib/core_patch.dart b/sdk/lib/_internal/js_runtime/lib/core_patch.dart index 2b48b5dc0cf..1cd3bc414d8 100644 --- a/sdk/lib/_internal/js_runtime/lib/core_patch.dart +++ b/sdk/lib/_internal/js_runtime/lib/core_patch.dart @@ -26,7 +26,7 @@ import 'dart:_js_helper' getTraceFromException, RuntimeError; -import 'dart:_foreign_helper' show JS, JS_GET_FLAG; +import 'dart:_foreign_helper' show JS; import 'dart:_native_typed_data' show NativeUint8List; import 'dart:_rti' show getRuntimeType; @@ -459,9 +459,15 @@ class List { assertUnreachable(); } + factory List._ofArray(Iterable elements) { + return JSArray.markGrowable( + JS('effects:none;depends:no-static', '#.slice(0)', elements)); + } + factory List._of(Iterable elements) { - // This is essentially `addAll`, but without a check for modifiability or - // ConcurrentModificationError on the receiver. + if (elements is JSArray) return List._ofArray(elements); + // This is essentially `[]..addAll(elements)`, but without a check for + // modifiability or ConcurrentModificationError on the receiver. List list = []; for (final e in elements) { list.add(e);