Version 2.14.0-305.0.dev
Merge commit 'd5a97be46681feb703031587bfc35945d9d36744' into 'dev'
diff --git a/pkg/front_end/testcases/general/async_nested.dart.weak.transformed.expect b/pkg/front_end/testcases/general/async_nested.dart.weak.transformed.expect
index 45a2ff8..758329d 100644
--- a/pkg/front_end/testcases/general/async_nested.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/async_nested.dart.weak.transformed.expect
@@ -36,9 +36,9 @@
core::int* :await_jump_var = 0;
dynamic :await_ctx_var;
dynamic :saved_try_context_var0;
- dynamic :async_temporary_0;
- dynamic :async_temporary_1;
- dynamic :async_temporary_2;
+ self::Node* :async_temporary_0;
+ self::Node* :async_temporary_1;
+ self::Node* :async_temporary_2;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try {
#L1:
diff --git a/pkg/front_end/testcases/general/await_complex.dart.weak.transformed.expect b/pkg/front_end/testcases/general/await_complex.dart.weak.transformed.expect
index cb5a62a..c35babf 100644
--- a/pkg/front_end/testcases/general/await_complex.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/await_complex.dart.weak.transformed.expect
@@ -65,12 +65,12 @@
core::int* :await_jump_var = 0;
dynamic :await_ctx_var;
dynamic :saved_try_context_var0;
- dynamic :async_temporary_0;
- dynamic :async_temporary_1;
- dynamic :async_temporary_2;
- dynamic :async_temporary_3;
- dynamic :async_temporary_4;
- dynamic :async_temporary_5;
+ core::int* :async_temporary_0;
+ core::int* :async_temporary_1;
+ core::int* :async_temporary_2;
+ core::int* :async_temporary_3;
+ core::int* :async_temporary_4;
+ core::int* :async_temporary_5;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try {
#L1:
@@ -121,11 +121,11 @@
core::int* :await_jump_var = 0;
dynamic :await_ctx_var;
dynamic :saved_try_context_var0;
- dynamic :async_temporary_0;
- dynamic :async_temporary_1;
- dynamic :async_temporary_2;
- dynamic :async_temporary_3;
- dynamic :async_temporary_4;
+ core::int* :async_temporary_0;
+ core::int* :async_temporary_1;
+ core::int* :async_temporary_2;
+ core::int* :async_temporary_3;
+ core::int* :async_temporary_4;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try {
#L2:
@@ -172,11 +172,11 @@
core::int* :await_jump_var = 0;
dynamic :await_ctx_var;
dynamic :saved_try_context_var0;
- dynamic :async_temporary_0;
- dynamic :async_temporary_1;
- dynamic :async_temporary_2;
- dynamic :async_temporary_3;
- dynamic :async_temporary_4;
+ core::int* :async_temporary_0;
+ core::int* :async_temporary_1;
+ core::int* :async_temporary_2;
+ core::int* :async_temporary_3;
+ core::int* :async_temporary_4;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try {
#L3:
@@ -225,10 +225,10 @@
dynamic :await_ctx_var;
dynamic :saved_try_context_var0;
dynamic :async_temporary_0;
- dynamic :async_temporary_1;
- dynamic :async_temporary_2;
- dynamic :async_temporary_3;
- dynamic :async_temporary_4;
+ core::int* :async_temporary_1;
+ core::int* :async_temporary_2;
+ core::List<core::int*>* :async_temporary_3;
+ core::int* :async_temporary_4;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try {
#L4:
@@ -288,13 +288,13 @@
core::bool* a = false;
core::bool* b = true;
:async_temporary_0 = (a || b) =={core::Object::==}{(core::Object) → core::bool} true;
- if(:async_temporary_0)
+ if(_in::unsafeCast<core::bool*>(:async_temporary_0))
;
else {
[yield] let dynamic #t22 = asy::_awaitHelper(self::dummy(), :async_op_then, :async_op_error, :async_op) in null;
:async_temporary_0 = :result as{TypeError,ForDynamic} core::bool* =={core::Object::==}{(core::Object) → core::bool} true;
}
- core::bool* c = :async_temporary_0;
+ core::bool* c = _in::unsafeCast<core::bool*>(:async_temporary_0);
self::expect(true, c);
if(a || b) {
:async_temporary_1 = a;
@@ -426,8 +426,8 @@
dynamic :saved_try_context_var3;
dynamic :exception0;
dynamic :stack_trace0;
- dynamic :async_temporary_0;
- dynamic :async_temporary_1;
+ core::List<dynamic>* :async_temporary_0;
+ core::List<dynamic>* :async_temporary_1;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try {
#L7:
diff --git a/pkg/front_end/testcases/general/bug33206.dart.weak.transformed.expect b/pkg/front_end/testcases/general/bug33206.dart.weak.transformed.expect
index 7178e45..85541c5 100644
--- a/pkg/front_end/testcases/general/bug33206.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/bug33206.dart.weak.transformed.expect
@@ -105,7 +105,7 @@
core::int* :await_jump_var = 0;
dynamic :await_ctx_var;
dynamic :saved_try_context_var0;
- dynamic :async_temporary_0;
+ self::Y* :async_temporary_0;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try {
#L3:
diff --git a/pkg/front_end/testcases/general/issue40662.dart.weak.transformed.expect b/pkg/front_end/testcases/general/issue40662.dart.weak.transformed.expect
index b5dfa01..79b6c00 100644
--- a/pkg/front_end/testcases/general/issue40662.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/issue40662.dart.weak.transformed.expect
@@ -17,10 +17,10 @@
core::int* :await_jump_var = 0;
dynamic :await_ctx_var;
dynamic :saved_try_context_var0;
- dynamic :async_temporary_0;
- dynamic :async_temporary_1;
+ core::int* :async_temporary_0;
+ core::int* :async_temporary_1;
core::List<core::int*>* :async_temporary_2;
- dynamic :async_temporary_3;
+ core::int* :async_temporary_3;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try {
#L1:
@@ -35,7 +35,7 @@
else {
:async_temporary_2 = null;
}
- :return_value = self::bar(_in::unsafeCast<core::int*>(:async_temporary_3), :async_temporary_2);
+ :return_value = self::bar(_in::unsafeCast<core::int*>(:async_temporary_3), _in::unsafeCast<core::List<core::int*>*>(:async_temporary_2));
break #L1;
}
asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
@@ -88,4 +88,4 @@
Extra constant evaluation status:
Evaluated: InstanceInvocation @ org-dartlang-testcase:///issue40662.dart:8:10 -> IntConstant(-1)
Evaluated: InstanceInvocation @ org-dartlang-testcase:///issue40662.dart:9:10 -> IntConstant(-1)
-Extra constant evaluation: evaluated: 93, effectively constant: 2
+Extra constant evaluation: evaluated: 94, effectively constant: 2
diff --git a/pkg/front_end/testcases/general/regression_flutter51828.dart.weak.transformed.expect b/pkg/front_end/testcases/general/regression_flutter51828.dart.weak.transformed.expect
index 23aa402..3d879b8 100644
--- a/pkg/front_end/testcases/general/regression_flutter51828.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/regression_flutter51828.dart.weak.transformed.expect
@@ -91,7 +91,7 @@
core::int* :await_jump_var = 0;
dynamic :await_ctx_var;
dynamic :saved_try_context_var0;
- dynamic :async_temporary_0;
+ self::B* :async_temporary_0;
dynamic :async_temporary_1;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try {
diff --git a/pkg/front_end/testcases/inference/future_then_conditional.dart.weak.transformed.expect b/pkg/front_end/testcases/inference/future_then_conditional.dart.weak.transformed.expect
index 6efd0ab..2c0fd45 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional.dart.weak.transformed.expect
@@ -56,7 +56,7 @@
[yield] let dynamic #t1 = asy::_awaitHelper(asy::Future::value<core::int*>(3), :async_op_then, :async_op_error, :async_op) in null;
:async_temporary_0 = _in::unsafeCast<core::int*>(:result);
}
- :return_value = :async_temporary_0;
+ :return_value = _in::unsafeCast<core::int*>(:async_temporary_0);
break #L1;
}
asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_2.dart.weak.transformed.expect b/pkg/front_end/testcases/inference/future_then_conditional_2.dart.weak.transformed.expect
index 29a38cc..ada0903 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_2.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_2.dart.weak.transformed.expect
@@ -56,7 +56,7 @@
[yield] let dynamic #t1 = asy::_awaitHelper(new self::MyFuture::value<core::int*>(3), :async_op_then, :async_op_error, :async_op) in null;
:async_temporary_0 = _in::unsafeCast<core::int*>(:result);
}
- :return_value = :async_temporary_0;
+ :return_value = _in::unsafeCast<core::int*>(:async_temporary_0);
break #L1;
}
asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_3.dart.weak.transformed.expect b/pkg/front_end/testcases/inference/future_then_conditional_3.dart.weak.transformed.expect
index 9ac9e7e..057adde 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_3.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_3.dart.weak.transformed.expect
@@ -56,7 +56,7 @@
[yield] let dynamic #t1 = asy::_awaitHelper(asy::Future::value<core::int*>(3), :async_op_then, :async_op_error, :async_op) in null;
:async_temporary_0 = _in::unsafeCast<core::int*>(:result);
}
- :return_value = :async_temporary_0;
+ :return_value = _in::unsafeCast<core::int*>(:async_temporary_0);
break #L1;
}
asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_4.dart.weak.transformed.expect b/pkg/front_end/testcases/inference/future_then_conditional_4.dart.weak.transformed.expect
index 768aaeb..fd4b819 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_4.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_4.dart.weak.transformed.expect
@@ -56,7 +56,7 @@
[yield] let dynamic #t1 = asy::_awaitHelper(new self::MyFuture::value<core::int*>(3), :async_op_then, :async_op_error, :async_op) in null;
:async_temporary_0 = _in::unsafeCast<core::int*>(:result);
}
- :return_value = :async_temporary_0;
+ :return_value = _in::unsafeCast<core::int*>(:async_temporary_0);
break #L1;
}
asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_5.dart.weak.transformed.expect b/pkg/front_end/testcases/inference/future_then_conditional_5.dart.weak.transformed.expect
index 6fc6957..6e05707 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_5.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_5.dart.weak.transformed.expect
@@ -56,7 +56,7 @@
[yield] let dynamic #t1 = asy::_awaitHelper(new self::MyFuture::value<core::int*>(3), :async_op_then, :async_op_error, :async_op) in null;
:async_temporary_0 = _in::unsafeCast<core::int*>(:result);
}
- :return_value = :async_temporary_0;
+ :return_value = _in::unsafeCast<core::int*>(:async_temporary_0);
break #L1;
}
asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
diff --git a/pkg/front_end/testcases/inference/future_then_conditional_6.dart.weak.transformed.expect b/pkg/front_end/testcases/inference/future_then_conditional_6.dart.weak.transformed.expect
index 4bf5a96..1968cbf 100644
--- a/pkg/front_end/testcases/inference/future_then_conditional_6.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_conditional_6.dart.weak.transformed.expect
@@ -56,7 +56,7 @@
[yield] let dynamic #t1 = asy::_awaitHelper(asy::Future::value<core::int*>(3), :async_op_then, :async_op_error, :async_op) in null;
:async_temporary_0 = _in::unsafeCast<core::int*>(:result);
}
- :return_value = :async_temporary_0;
+ :return_value = _in::unsafeCast<core::int*>(:async_temporary_0);
break #L1;
}
asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
diff --git a/pkg/front_end/testcases/inference/future_then_ifNull.dart.weak.transformed.expect b/pkg/front_end/testcases/inference/future_then_ifNull.dart.weak.transformed.expect
index d6e68fc..077bfa1 100644
--- a/pkg/front_end/testcases/inference/future_then_ifNull.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/inference/future_then_ifNull.dart.weak.transformed.expect
@@ -57,7 +57,7 @@
else {
:async_temporary_0 = #t1;
}
- :return_value = :async_temporary_0;
+ :return_value = _in::unsafeCast<core::int*>(:async_temporary_0);
break #L1;
}
asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
diff --git a/pkg/kernel/lib/transformations/async.dart b/pkg/kernel/lib/transformations/async.dart
index b18d98f..43fca30 100644
--- a/pkg/kernel/lib/transformations/async.dart
+++ b/pkg/kernel/lib/transformations/async.dart
@@ -115,21 +115,40 @@
return result;
}
+ // Wraps VariableGet in an unsafeCast if `type` isn't dynamic.
+ Expression unsafeCastVariableGet(
+ VariableDeclaration variable, DartType type) {
+ if (type != const DynamicType()) {
+ return StaticInvocation(
+ continuationRewriter.helper.unsafeCast,
+ Arguments(<Expression>[VariableGet(variable)],
+ types: <DartType>[type]));
+ }
+ return VariableGet(variable);
+ }
+
// Name an expression by emitting an assignment to a temporary variable.
Expression name(Expression expr) {
- // Allocate as dynamic as temps might be reused with different types.
- VariableDeclaration temp =
- allocateTemporary(nameIndex, const DynamicType());
- statements.add(ExpressionStatement(VariableSet(temp, expr)));
- // Type annotate the get via an unsafe cast since all temps are allocated
- // as dynamic.
DartType type = expr.getStaticType(_staticTypeContext);
- return StaticInvocation(continuationRewriter.helper.unsafeCast,
- Arguments(<Expression>[VariableGet(temp)], types: <DartType>[type]));
+ VariableDeclaration temp = allocateTemporary(nameIndex, type);
+ statements.add(ExpressionStatement(VariableSet(temp, expr)));
+ // Wrap in unsafeCast to make sure we pass type information even if we later
+ // have to re-type the temporary variable to dynamic.
+ return unsafeCastVariableGet(temp, type);
}
VariableDeclaration allocateTemporary(int index,
[DartType type = const DynamicType()]) {
+ if (variables.length > index) {
+ // Re-type temporary to dynamic if we detect reuse with different type.
+ // Note: We should make sure all uses use `unsafeCast(...)` to pass their
+ // type information on, as that is lost otherwise.
+ if (variables[index].type != const DynamicType() &&
+ variables[index].type != type) {
+ variables[index].type = const DynamicType();
+ }
+ return variables[index];
+ }
for (var i = variables.length; i <= index; i++) {
variables.add(VariableDeclaration(":async_temporary_${i}", type: type));
}
@@ -384,10 +403,9 @@
// so any statements it emits occur after in the accumulated list (that is,
// so they occur before in the corresponding block).
var rightBody = blockOf(rightStatements);
- var result = allocateTemporary(
- nameIndex,
- _staticTypeContext.typeEnvironment.coreTypes
- .boolRawType(_staticTypeContext.nonNullable));
+ final type = _staticTypeContext.typeEnvironment.coreTypes
+ .boolRawType(_staticTypeContext.nonNullable);
+ final result = allocateTemporary(nameIndex, type);
final objectEquals = continuationRewriter.helper.coreTypes.objectEquals;
rightBody.addStatement(new ExpressionStatement(new VariableSet(
result,
@@ -402,7 +420,8 @@
then = new EmptyStatement();
otherwise = rightBody;
}
- statements.add(new IfStatement(new VariableGet(result), then, otherwise));
+ statements.add(
+ new IfStatement(unsafeCastVariableGet(result, type), then, otherwise));
final test = new EqualsCall(expr.left, new BoolLiteral(true),
interfaceTarget: objectEquals,
@@ -414,7 +433,7 @@
++nameIndex;
seenAwait = seenAwait || rightAwait;
- return new VariableGet(result);
+ return unsafeCastVariableGet(result, type);
}
TreeNode visitConditionalExpression(ConditionalExpression expr) {
@@ -455,15 +474,15 @@
});
}
- // If then or otherwise has emitted statements we will produce a temporary t
- // and emit:
+ // If `then` or `otherwise` has emitted statements we will produce a
+ // temporary t and emit:
//
// if ([condition]) {
// t = [left];
// } else {
// t = [right];
// }
- var result = allocateTemporary(nameIndex, expr.staticType);
+ final result = allocateTemporary(nameIndex, expr.staticType);
var thenBody = blockOf(thenStatements);
var otherwiseBody = blockOf(otherwiseStatements);
thenBody.addStatement(
@@ -478,7 +497,7 @@
++nameIndex;
seenAwait = seenAwait || thenAwait || otherwiseAwait;
- return new VariableGet(result);
+ return unsafeCastVariableGet(result, expr.staticType);
}
// Others.
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/async_await.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/async_await.dart.expect
index d1f3c4e..6e5364a 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/async_await.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/async_await.dart.expect
@@ -2,7 +2,6 @@
import self as self;
import "dart:core" as core;
import "dart:async" as asy;
-import "dart:_internal" as _in;
class A extends core::Object {
synthetic constructor •() → self::A*
@@ -86,7 +85,7 @@
{
:async_temporary_0 = [@vm.inferred-type.metadata=#lib::A] self::foo();
[yield] let dynamic #t1 = asy::_awaitHelper([@vm.inferred-type.metadata=dart.async::_Future<dynamic>] self::baz(), :async_op_then, :async_op_error, :async_op) in null;
- [yield] let dynamic #t2 = asy::_awaitHelper([@vm.direct-call.metadata=#lib::A.bar??] [@vm.inferred-type.metadata=dart.async::_Future<dynamic> (receiver not int)] _in::unsafeCast<dynamic>(:async_temporary_0){dynamic}.bar(:result), :async_op_then, :async_op_error, :async_op) in null;
+ [yield] let dynamic #t2 = asy::_awaitHelper([@vm.direct-call.metadata=#lib::A.bar??] [@vm.inferred-type.metadata=dart.async::_Future<dynamic> (receiver not int)] :async_temporary_0{dynamic}.bar(:result), :async_op_then, :async_op_error, :async_op) in null;
:result;
}
asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index d296cc7cb..c0ad639 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -239,33 +239,35 @@
#if defined(DEBUG)
// Basic checking.
cls = object_store->object_class();
- ASSERT(Instance::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(Instance::InstanceSize(), cls.host_instance_size());
cls = object_store->integer_implementation_class();
- ASSERT(Integer::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(Integer::InstanceSize(), cls.host_instance_size());
cls = object_store->smi_class();
- ASSERT(Smi::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(Smi::InstanceSize(), cls.host_instance_size());
cls = object_store->mint_class();
- ASSERT(Mint::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(Mint::InstanceSize(), cls.host_instance_size());
cls = object_store->one_byte_string_class();
- ASSERT(OneByteString::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(OneByteString::InstanceSize(), cls.host_instance_size());
cls = object_store->two_byte_string_class();
- ASSERT(TwoByteString::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(TwoByteString::InstanceSize(), cls.host_instance_size());
cls = object_store->external_one_byte_string_class();
- ASSERT(ExternalOneByteString::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(ExternalOneByteString::InstanceSize(), cls.host_instance_size());
cls = object_store->external_two_byte_string_class();
- ASSERT(ExternalTwoByteString::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(ExternalTwoByteString::InstanceSize(), cls.host_instance_size());
cls = object_store->double_class();
- ASSERT(Double::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(Double::InstanceSize(), cls.host_instance_size());
cls = object_store->bool_class();
- ASSERT(Bool::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(Bool::InstanceSize(), cls.host_instance_size());
cls = object_store->array_class();
- ASSERT(Array::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(Array::InstanceSize(), cls.host_instance_size());
cls = object_store->immutable_array_class();
- ASSERT(ImmutableArray::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(ImmutableArray::InstanceSize(), cls.host_instance_size());
cls = object_store->weak_property_class();
- ASSERT(WeakProperty::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(WeakProperty::InstanceSize(), cls.host_instance_size());
cls = object_store->linked_hash_map_class();
- ASSERT(LinkedHashMap::InstanceSize() == cls.host_instance_size());
+ ASSERT_EQUAL(LinkedHashMap::InstanceSize(), cls.host_instance_size());
+ cls = object_store->linked_hash_set_class();
+ ASSERT_EQUAL(LinkedHashMap::InstanceSize(), cls.host_instance_size());
#endif // defined(DEBUG)
// Remember the currently pending classes.
diff --git a/runtime/vm/class_id.h b/runtime/vm/class_id.h
index 2fbc03f..5a41a68 100644
--- a/runtime/vm/class_id.h
+++ b/runtime/vm/class_id.h
@@ -17,7 +17,7 @@
// Size of the class-id part of the object header. See UntaggedObject.
typedef uint16_t ClassIdTagType;
-#define CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY(V) \
+#define CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY_NOR_MAP(V) \
V(Class) \
V(PatchClass) \
V(Function) \
@@ -88,11 +88,16 @@
V(RegExp) \
V(WeakProperty) \
V(MirrorReference) \
- V(LinkedHashMap) \
V(FutureOr) \
V(UserTag) \
V(TransferableTypedData)
+// TODO(http://dartbug.com/45908): Add ImmutableLinkedHashMap.
+#define CLASS_LIST_MAPS(V) V(LinkedHashMap)
+
+// TODO(http://dartbug.com/45908): Add ImmutableLinkedHashSet.
+#define CLASS_LIST_SETS(V) V(LinkedHashSet)
+
#define CLASS_LIST_ARRAYS(V) \
V(Array) \
V(ImmutableArray)
@@ -163,12 +168,16 @@
V(Float64x2)
#define CLASS_LIST_FOR_HANDLES(V) \
- CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY(V) \
+ CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY_NOR_MAP(V) \
+ V(LinkedHashMap) \
+ V(LinkedHashSet) \
V(Array) \
V(String)
#define CLASS_LIST_NO_OBJECT(V) \
- CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY(V) \
+ CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY_NOR_MAP(V) \
+ CLASS_LIST_MAPS(V) \
+ CLASS_LIST_SETS(V) \
CLASS_LIST_ARRAYS(V) \
CLASS_LIST_STRINGS(V)
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 55d5b84..d47ec82 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -6473,7 +6473,11 @@
case kWeakPropertyCid:
return new (Z) WeakPropertySerializationCluster();
case kLinkedHashMapCid:
- return new (Z) LinkedHashMapSerializationCluster();
+ // We do not have mutable hash maps in snapshots.
+ UNREACHABLE();
+ case kLinkedHashSetCid:
+ // We do not have mutable hash sets in snapshots.
+ UNREACHABLE();
case kArrayCid:
return new (Z) ArraySerializationCluster(is_canonical, kArrayCid);
case kImmutableArrayCid:
@@ -7380,7 +7384,11 @@
ASSERT(!is_canonical);
return new (Z) WeakPropertyDeserializationCluster();
case kLinkedHashMapCid:
- return new (Z) LinkedHashMapDeserializationCluster(is_canonical);
+ // We do not have mutable hash maps in snapshots.
+ UNREACHABLE();
+ case kLinkedHashSetCid:
+ // We do not have mutable hash sets in snapshots.
+ UNREACHABLE();
case kArrayCid:
return new (Z) ArrayDeserializationCluster(is_canonical, kArrayCid);
case kImmutableArrayCid:
diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h
index 9dab2b8..d80cb7453 100644
--- a/runtime/vm/clustered_snapshot.h
+++ b/runtime/vm/clustered_snapshot.h
@@ -440,7 +440,7 @@
V8SnapshotProfileWriter* profile_writer() const { return profile_writer_; }
- // If the given [obj] was not included into the snaposhot and have not
+ // If the given [obj] was not included into the snapshot and have not
// yet gotten an artificial node created for it create an artificial node
// in the profile representing this object.
// Returns true if [obj] has an artificial profile node associated with it.
diff --git a/runtime/vm/compiler/runtime_api.cc b/runtime/vm/compiler/runtime_api.cc
index c289e46..f711e69 100644
--- a/runtime/vm/compiler/runtime_api.cc
+++ b/runtime/vm/compiler/runtime_api.cc
@@ -410,6 +410,8 @@
return TypedDataBase::InstanceSize();
case kLinkedHashMapCid:
return LinkedHashMap::InstanceSize();
+ case kLinkedHashSetCid:
+ return LinkedHashSet::InstanceSize();
case kUnhandledExceptionCid:
return UnhandledException::InstanceSize();
case kWeakPropertyCid:
@@ -960,243 +962,19 @@
return TranslateOffsetInWords(dart::Instance::NextFieldOffset());
}
-word Pointer::NextFieldOffset() {
- return TranslateOffsetInWords(dart::Pointer::NextFieldOffset());
-}
-
-word WeakSerializationReference::NextFieldOffset() {
- return -kWordSize;
-}
-
-word ObjectPool::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Class::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Function::NextFieldOffset() {
- return -kWordSize;
-}
-
-word ICData::NextFieldOffset() {
- return -kWordSize;
-}
-
-word MegamorphicCache::NextFieldOffset() {
- return -kWordSize;
-}
-
-word SingleTargetCache::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Array::NextFieldOffset() {
- return -kWordSize;
-}
-
intptr_t Array::index_at_offset(intptr_t offset_in_bytes) {
return dart::Array::index_at_offset(
TranslateOffsetInWordsToHost(offset_in_bytes));
}
-word GrowableObjectArray::NextFieldOffset() {
- return -kWordSize;
-}
-
-word TypedDataBase::NextFieldOffset() {
- return -kWordSize;
-}
-
-word TypedData::NextFieldOffset() {
- return -kWordSize;
-}
-
-word ExternalTypedData::NextFieldOffset() {
- return -kWordSize;
-}
-
-word TypedDataView::NextFieldOffset() {
- return -kWordSize;
-}
-
-word LinkedHashMap::NextFieldOffset() {
- return -kWordSize;
-}
-
-word AbstractType::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Type::NextFieldOffset() {
- return -kWordSize;
-}
-
-word FunctionType::NextFieldOffset() {
- return -kWordSize;
-}
-
-word TypeRef::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Double::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Mint::NextFieldOffset() {
- return -kWordSize;
-}
-
-word String::NextFieldOffset() {
- return -kWordSize;
-}
-
word String::InstanceSize(word payload_size) {
return RoundedAllocationSize(String::InstanceSize() + payload_size);
}
-word OneByteString::NextFieldOffset() {
- return -kWordSize;
-}
-
-word TwoByteString::NextFieldOffset() {
- return -kWordSize;
-}
-
-word ExternalOneByteString::NextFieldOffset() {
- return -kWordSize;
-}
-
-word ExternalTwoByteString::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Int32x4::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Float32x4::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Float64x2::NextFieldOffset() {
- return -kWordSize;
-}
-
-word DynamicLibrary::NextFieldOffset() {
- return -kWordSize;
-}
-
-word PatchClass::NextFieldOffset() {
- return -kWordSize;
-}
-
-word FfiTrampolineData::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Script::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Library::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Namespace::NextFieldOffset() {
- return -kWordSize;
-}
-
-word KernelProgramInfo::NextFieldOffset() {
- return -kWordSize;
-}
-
-word PcDescriptors::NextFieldOffset() {
- return -kWordSize;
-}
-
-word CodeSourceMap::NextFieldOffset() {
- return -kWordSize;
-}
-
-word CompressedStackMaps::NextFieldOffset() {
- return -kWordSize;
-}
-
word LocalVarDescriptors::InstanceSize() {
return 0;
}
-word LocalVarDescriptors::NextFieldOffset() {
- return -kWordSize;
-}
-
-word ExceptionHandlers::NextFieldOffset() {
- return -kWordSize;
-}
-
-word ContextScope::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Sentinel::NextFieldOffset() {
- return -kWordSize;
-}
-
-word UnlinkedCall::NextFieldOffset() {
- return -kWordSize;
-}
-
-word ApiError::NextFieldOffset() {
- return -kWordSize;
-}
-
-word LanguageError::NextFieldOffset() {
- return -kWordSize;
-}
-
-word UnhandledException::NextFieldOffset() {
- return -kWordSize;
-}
-
-word UnwindError::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Bool::NextFieldOffset() {
- return -kWordSize;
-}
-
-word TypeParameter::NextFieldOffset() {
- return -kWordSize;
-}
-
-word LibraryPrefix::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Capability::NextFieldOffset() {
- return -kWordSize;
-}
-
-word ReceivePort::NextFieldOffset() {
- return -kWordSize;
-}
-
-word SendPort::NextFieldOffset() {
- return -kWordSize;
-}
-
-word TransferableTypedData::NextFieldOffset() {
- return -kWordSize;
-}
-
-word StackTrace::NextFieldOffset() {
- return -kWordSize;
-}
-
word Integer::NextFieldOffset() {
return TranslateOffsetInWords(dart::Integer::NextFieldOffset());
}
@@ -1205,94 +983,10 @@
return 0;
}
-word Smi::NextFieldOffset() {
- return -kWordSize;
-}
-
-word WeakProperty::NextFieldOffset() {
- return -kWordSize;
-}
-
-word MirrorReference::NextFieldOffset() {
- return -kWordSize;
-}
-
word Number::NextFieldOffset() {
return TranslateOffsetInWords(dart::Number::NextFieldOffset());
}
-word MonomorphicSmiableCall::NextFieldOffset() {
- return -kWordSize;
-}
-
-word InstructionsSection::NextFieldOffset() {
- return -kWordSize;
-}
-
-word InstructionsTable::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Instructions::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Code::NextFieldOffset() {
- return -kWordSize;
-}
-
-word SubtypeTestCache::NextFieldOffset() {
- return -kWordSize;
-}
-
-word LoadingUnit::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Context::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Closure::NextFieldOffset() {
- return -kWordSize;
-}
-
-word ClosureData::NextFieldOffset() {
- return -kWordSize;
-}
-
-word RegExp::NextFieldOffset() {
- return -kWordSize;
-}
-
-word UserTag::NextFieldOffset() {
- return -kWordSize;
-}
-
-word FutureOr::NextFieldOffset() {
- return -kWordSize;
-}
-
-word Field::NextFieldOffset() {
- return -kWordSize;
-}
-
-word TypeParameters::NextFieldOffset() {
- return -kWordSize;
-}
-
-word TypeArguments::NextFieldOffset() {
- return -kWordSize;
-}
-
-word FreeListElement::FakeInstance::NextFieldOffset() {
- return -kWordSize;
-}
-
-word ForwardingCorpse::FakeInstance::NextFieldOffset() {
- return -kWordSize;
-}
-
} // namespace target
} // namespace compiler
} // namespace dart
diff --git a/runtime/vm/compiler/runtime_api.h b/runtime/vm/compiler/runtime_api.h
index fb22d3b..96342d7 100644
--- a/runtime/vm/compiler/runtime_api.h
+++ b/runtime/vm/compiler/runtime_api.h
@@ -419,6 +419,9 @@
bool WillAllocateNewOrRememberedArray(intptr_t length);
+#define FINAL_CLASS() \
+ static word NextFieldOffset() { return -kWordSize; }
+
//
// Target specific offsets and constants.
//
@@ -461,7 +464,7 @@
static word element_offset(intptr_t index);
static word InstanceSize(intptr_t length);
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Class : public AllStatic {
@@ -480,7 +483,7 @@
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
// Return class id of the given class on the target.
static classid_t GetId(const dart::Class& handle);
@@ -526,7 +529,7 @@
static word signature_offset();
static word usage_counter_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class CallSiteData : public AllStatic {
@@ -550,7 +553,7 @@
static word NumArgsTestedShift();
static word NumArgsTestedMask();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class MegamorphicCache : public AllStatic {
@@ -559,7 +562,7 @@
static word mask_offset();
static word buckets_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class SingleTargetCache : public AllStatic {
@@ -569,7 +572,7 @@
static word entry_point_offset();
static word target_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Array : public AllStatic {
@@ -583,7 +586,7 @@
static intptr_t index_at_offset(intptr_t offset_in_bytes);
static word InstanceSize(intptr_t length);
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
static const word kMaxElements;
static const word kMaxNewSpaceElements;
@@ -595,7 +598,7 @@
static word type_arguments_offset();
static word length_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class PointerBase : public AllStatic {
@@ -607,7 +610,7 @@
public:
static word length_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class TypedData : public AllStatic {
@@ -616,14 +619,14 @@
static word HeaderSize();
static word InstanceSize();
static word InstanceSize(word lengthInBytes);
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class ExternalTypedData : public AllStatic {
public:
static word data_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class TypedDataView : public AllStatic {
@@ -631,7 +634,7 @@
static word offset_in_bytes_offset();
static word data_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class LinkedHashBase : public AllStatic {
@@ -647,14 +650,19 @@
class LinkedHashMap : public LinkedHashBase {
public:
- static word NextFieldOffset();
+ FINAL_CLASS();
+};
+
+class LinkedHashSet : public LinkedHashBase {
+ public:
+ FINAL_CLASS();
};
class FutureOr : public AllStatic {
public:
static word type_arguments_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class ArgumentsDescriptor : public AllStatic {
@@ -678,14 +686,14 @@
public:
static word type_arguments_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class AbstractType : public AllStatic {
public:
static word type_test_stub_entry_point_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Type : public AllStatic {
@@ -696,7 +704,7 @@
static word type_class_id_offset();
static word nullability_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class FunctionType : public AllStatic {
@@ -710,14 +718,14 @@
static word type_parameters_offset();
static word nullability_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class TypeRef : public AllStatic {
public:
static word type_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Nullability : public AllStatic {
@@ -731,14 +739,14 @@
public:
static word value_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Mint : public AllStatic {
public:
static word value_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class String : public AllStatic {
@@ -748,8 +756,8 @@
static word hash_offset();
static word length_offset();
static word InstanceSize();
- static word NextFieldOffset();
static word InstanceSize(word payload_size);
+ FINAL_CLASS();
};
class OneByteString : public AllStatic {
@@ -757,7 +765,7 @@
static word data_offset();
static word InstanceSize(intptr_t length);
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
private:
static word element_offset(intptr_t index);
@@ -768,7 +776,7 @@
static word data_offset();
static word InstanceSize(intptr_t length);
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
private:
static word element_offset(intptr_t index);
@@ -778,77 +786,77 @@
public:
static word external_data_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class ExternalTwoByteString : public AllStatic {
public:
static word external_data_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Int32x4 : public AllStatic {
public:
static word value_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Float32x4 : public AllStatic {
public:
static word value_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Float64x2 : public AllStatic {
public:
static word value_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class DynamicLibrary : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class PatchClass : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class FfiTrampolineData : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Script : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Library : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Namespace : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class KernelProgramInfo : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class PcDescriptors : public AllStatic {
@@ -856,7 +864,7 @@
static word HeaderSize();
static word InstanceSize();
static word InstanceSize(word payload_size);
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class CodeSourceMap : public AllStatic {
@@ -864,7 +872,7 @@
static word HeaderSize();
static word InstanceSize();
static word InstanceSize(word payload_size);
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class CompressedStackMaps : public AllStatic {
@@ -872,13 +880,13 @@
static word HeaderSize();
static word InstanceSize();
static word InstanceSize(word payload_size);
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class LocalVarDescriptors : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class ExceptionHandlers : public AllStatic {
@@ -886,7 +894,7 @@
static word element_offset(intptr_t index);
static word InstanceSize(intptr_t length);
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class ContextScope : public AllStatic {
@@ -894,31 +902,31 @@
static word element_offset(intptr_t index);
static word InstanceSize(intptr_t length);
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Sentinel : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class UnlinkedCall : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class ApiError : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class LanguageError : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class UnhandledException : public AllStatic {
@@ -926,19 +934,19 @@
static word exception_offset();
static word stacktrace_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class UnwindError : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Bool : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class TypeParameter : public AllStatic {
@@ -946,7 +954,7 @@
static word bound_offset();
static word flags_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
static word parameterized_class_id_offset();
static word index_offset();
static word nullability_offset();
@@ -955,37 +963,37 @@
class LibraryPrefix : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Capability : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class ReceivePort : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class SendPort : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class TransferableTypedData : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class StackTrace : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Integer : public AllStatic {
@@ -997,7 +1005,7 @@
class Smi : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class WeakProperty : public AllStatic {
@@ -1005,13 +1013,13 @@
static word key_offset();
static word value_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class MirrorReference : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Number : public AllStatic {
@@ -1036,7 +1044,7 @@
static word entrypoint_offset();
static word target_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Thread : public AllStatic {
@@ -1222,7 +1230,7 @@
static word HeaderSize();
static word InstanceSize();
static word InstanceSize(word payload_size);
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class InstructionsTable : public AllStatic {
@@ -1230,7 +1238,7 @@
static word HeaderSize();
static word InstanceSize();
static word InstanceSize(intptr_t length);
- static word NextFieldOffset();
+ FINAL_CLASS();
private:
static word element_offset(intptr_t index);
@@ -1248,7 +1256,7 @@
static word HeaderSize();
static word InstanceSize();
static word InstanceSize(word payload_size);
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Code : public AllStatic {
@@ -1264,7 +1272,7 @@
static word HeaderSize();
static word InstanceSize();
static word InstanceSize(intptr_t length);
- static word NextFieldOffset();
+ FINAL_CLASS();
private:
static word element_offset(intptr_t index);
@@ -1273,7 +1281,7 @@
class WeakSerializationReference : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class SubtypeTestCache : public AllStatic {
@@ -1290,13 +1298,13 @@
static const word kInstanceDelayedFunctionTypeArguments;
static const word kTestResult;
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class LoadingUnit : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Context : public AllStatic {
@@ -1307,7 +1315,7 @@
static word variable_offset(intptr_t index);
static word InstanceSize(intptr_t length);
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Closure : public AllStatic {
@@ -1320,14 +1328,14 @@
static word instantiator_type_arguments_offset();
static word hash_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class ClosureData : public AllStatic {
public:
static word default_type_arguments_kind_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class OldPage : public AllStatic {
@@ -1363,14 +1371,14 @@
public:
static word function_offset(classid_t cid, bool sticky);
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class UserTag : public AllStatic {
public:
static word tag_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class Symbols : public AllStatic {
@@ -1391,7 +1399,7 @@
static word initializer_function_offset();
static word host_offset_or_field_id_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class TypeParameters : public AllStatic {
@@ -1401,7 +1409,7 @@
static word bounds_offset();
static word defaults_offset();
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
class TypeArguments : public AllStatic {
@@ -1413,7 +1421,7 @@
static word types_offset();
static word InstanceSize(intptr_t length);
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
static const word kMaxElements;
};
@@ -1423,7 +1431,7 @@
class FakeInstance : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
};
@@ -1432,7 +1440,7 @@
class FakeInstance : public AllStatic {
public:
static word InstanceSize();
- static word NextFieldOffset();
+ FINAL_CLASS();
};
};
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 06dcf56..e3e73a8 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -606,7 +606,23 @@
clazz fake_handle; \
builtin_vtables_[k##clazz##Cid] = fake_handle.vtable(); \
}
- CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY(INIT_VTABLE)
+ CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY_NOR_MAP(INIT_VTABLE)
+#undef INIT_VTABLE
+
+#define INIT_VTABLE(clazz) \
+ { \
+ LinkedHashMap fake_handle; \
+ builtin_vtables_[k##clazz##Cid] = fake_handle.vtable(); \
+ }
+ CLASS_LIST_MAPS(INIT_VTABLE)
+#undef INIT_VTABLE
+
+#define INIT_VTABLE(clazz) \
+ { \
+ LinkedHashSet fake_handle; \
+ builtin_vtables_[k##clazz##Cid] = fake_handle.vtable(); \
+ }
+ CLASS_LIST_SETS(INIT_VTABLE)
#undef INIT_VTABLE
#define INIT_VTABLE(clazz) \
@@ -1948,6 +1964,15 @@
RegisterPrivateClass(cls, Symbols::_LinkedHashMap(), lib);
pending_classes.Add(cls);
+ cls = Class::New<LinkedHashSet, RTN::LinkedHashSet>(isolate_group);
+ object_store->set_linked_hash_set_class(cls);
+ cls.set_type_arguments_field_offset(
+ LinkedHashSet::type_arguments_offset(),
+ RTN::LinkedHashSet::type_arguments_offset());
+ cls.set_num_type_arguments_unsafe(1);
+ RegisterPrivateClass(cls, Symbols::_LinkedHashSet(), lib);
+ pending_classes.Add(cls);
+
// Pre-register the async library so we can place the vm class
// FutureOr there rather than the core library.
lib = Library::LookupLibrary(thread, Symbols::DartAsync());
@@ -2414,6 +2439,9 @@
cls = Class::New<LinkedHashMap, RTN::LinkedHashMap>(isolate_group);
object_store->set_linked_hash_map_class(cls);
+ cls = Class::New<LinkedHashSet, RTN::LinkedHashSet>(isolate_group);
+ object_store->set_linked_hash_set_class(cls);
+
cls = Class::New<Float32x4, RTN::Float32x4>(isolate_group);
object_store->set_float32x4_class(cls);
@@ -24417,6 +24445,54 @@
return zone->PrintToString("_LinkedHashMap len:%" Pd, Length());
}
+LinkedHashSetPtr LinkedHashSet::New(const Array& data,
+ const TypedData& index,
+ intptr_t hash_mask,
+ intptr_t used_data,
+ intptr_t deleted_keys,
+ Heap::Space space) {
+ ASSERT(IsolateGroup::Current()->object_store()->linked_hash_map_class() !=
+ Class::null());
+ LinkedHashSet& result =
+ LinkedHashSet::Handle(LinkedHashSet::NewUninitialized(space));
+ result.SetData(data);
+ result.SetIndex(index);
+ result.SetHashMask(hash_mask);
+ result.SetUsedData(used_data);
+ result.SetDeletedKeys(deleted_keys);
+ return result.ptr();
+}
+
+LinkedHashSetPtr LinkedHashSet::NewDefault(Heap::Space space) {
+ const Array& data = Array::Handle(Array::New(kInitialIndexSize, space));
+ const TypedData& index = TypedData::Handle(
+ TypedData::New(kTypedDataUint32ArrayCid, kInitialIndexSize, space));
+ // On 32-bit, the top bits are wasted to avoid Mint allocation.
+ static const intptr_t kAvailableBits = (kSmiBits >= 32) ? 32 : kSmiBits;
+ static const intptr_t kInitialHashMask =
+ (1 << (kAvailableBits - kInitialIndexBits)) - 1;
+ return LinkedHashSet::New(data, index, kInitialHashMask, 0, 0, space);
+}
+
+LinkedHashSetPtr LinkedHashSet::NewUninitialized(Heap::Space space) {
+ ASSERT(IsolateGroup::Current()->object_store()->linked_hash_map_class() !=
+ Class::null());
+ LinkedHashSet& result = LinkedHashSet::Handle();
+ {
+ ObjectPtr raw =
+ Object::Allocate(kLinkedHashSetCid, LinkedHashSet::InstanceSize(),
+ space, LinkedHashSet::ContainsCompressedPointers());
+ NoSafepointScope no_safepoint;
+ result ^= raw;
+ }
+ return result.ptr();
+}
+
+const char* LinkedHashSet::ToCString() const {
+ Zone* zone = Thread::Current()->zone();
+ return zone->PrintToString("LinkedHashSet len:%" Pd, Length());
+}
+
const char* FutureOr::ToCString() const {
// FutureOr is an abstract class.
UNREACHABLE();
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 7cc8063..cd72a01 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -10874,8 +10874,6 @@
return OFFSET_OF(UntaggedPointer, type_arguments_);
}
- static intptr_t NextFieldOffset() { return sizeof(UntaggedPointer); }
-
static const intptr_t kNativeTypeArgPos = 0;
// Fetches the NativeType type argument.
@@ -10885,7 +10883,7 @@
}
private:
- HEAP_OBJECT_IMPLEMENTATION(Pointer, Instance);
+ FINAL_HEAP_OBJECT_IMPLEMENTATION(Pointer, Instance);
friend class Class;
};
@@ -11072,6 +11070,113 @@
friend class LinkedHashMapDeserializationCluster;
};
+class LinkedHashSet : public LinkedHashBase {
+ public:
+ static intptr_t InstanceSize() {
+ return RoundedAllocationSize(sizeof(UntaggedLinkedHashSet));
+ }
+
+ // Allocates a set with some default capacity, just like "new Set()".
+ static LinkedHashSetPtr NewDefault(Heap::Space space = Heap::kNew);
+ static LinkedHashSetPtr New(const Array& data,
+ const TypedData& index,
+ intptr_t hash_mask,
+ intptr_t used_data,
+ intptr_t deleted_keys,
+ Heap::Space space = Heap::kNew);
+
+ virtual TypeArgumentsPtr GetTypeArguments() const {
+ return untag()->type_arguments();
+ }
+ virtual void SetTypeArguments(const TypeArguments& value) const {
+ ASSERT(value.IsNull() ||
+ ((value.Length() >= 1) &&
+ value.IsInstantiated() /*&& value.IsCanonical()*/));
+ // TODO(asiva): Values read from a message snapshot are not properly marked
+ // as canonical. See for example tests/isolate/message3_test.dart.
+ untag()->set_type_arguments(value.ptr());
+ }
+
+ TypedDataPtr index() const { return untag()->index(); }
+ void SetIndex(const TypedData& value) const {
+ ASSERT(!value.IsNull());
+ untag()->set_index(value.ptr());
+ }
+
+ ArrayPtr data() const { return untag()->data(); }
+ void SetData(const Array& value) const { untag()->set_data(value.ptr()); }
+
+ SmiPtr hash_mask() const { return untag()->hash_mask(); }
+ void SetHashMask(intptr_t value) const {
+ untag()->set_hash_mask(Smi::New(value));
+ }
+
+ SmiPtr used_data() const { return untag()->used_data(); }
+ void SetUsedData(intptr_t value) const {
+ untag()->set_used_data(Smi::New(value));
+ }
+
+ SmiPtr deleted_keys() const { return untag()->deleted_keys(); }
+ void SetDeletedKeys(intptr_t value) const {
+ untag()->set_deleted_keys(Smi::New(value));
+ }
+
+ intptr_t Length() const {
+ // The map may be uninitialized.
+ if (untag()->used_data() == Object::null()) return 0;
+ if (untag()->deleted_keys() == Object::null()) return 0;
+
+ intptr_t used = Smi::Value(untag()->used_data());
+ intptr_t deleted = Smi::Value(untag()->deleted_keys());
+ return used - deleted;
+ }
+
+ // This iterator differs somewhat from its Dart counterpart (_CompactIterator
+ // in runtime/lib/compact_hash.dart):
+ // - There are no checks for concurrent modifications.
+ // - Accessing a key or value before the first call to MoveNext and after
+ // MoveNext returns false will result in crashes.
+ class Iterator : ValueObject {
+ public:
+ explicit Iterator(const LinkedHashSet& set)
+ : data_(Array::Handle(set.data())),
+ scratch_(Object::Handle()),
+ offset_(-1),
+ length_(Smi::Value(set.used_data())) {}
+
+ bool MoveNext() {
+ while (true) {
+ offset_++;
+ if (offset_ >= length_) {
+ return false;
+ }
+ scratch_ = data_.At(offset_);
+ if (scratch_.ptr() != data_.ptr()) {
+ // Slot is not deleted (self-reference indicates deletion).
+ return true;
+ }
+ }
+ }
+
+ ObjectPtr CurrentKey() const { return data_.At(offset_); }
+
+ private:
+ const Array& data_;
+ Object& scratch_;
+ intptr_t offset_;
+ const intptr_t length_;
+ };
+
+ private:
+ FINAL_HEAP_OBJECT_IMPLEMENTATION(LinkedHashSet, LinkedHashBase);
+
+ // Allocate a set, but leave all fields set to null.
+ // Used during deserialization (since set might contain itself as key/value).
+ static LinkedHashSetPtr NewUninitialized(Heap::Space space = Heap::kNew);
+
+ friend class Class;
+};
+
class Closure : public Instance {
public:
#if defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc
index 1bd42fa..d924ea2 100644
--- a/runtime/vm/object_graph.cc
+++ b/runtime/vm/object_graph.cc
@@ -944,6 +944,10 @@
writer_->WriteUnsigned(kLengthData);
writer_->WriteUnsigned(
Smi::Value(static_cast<LinkedHashMapPtr>(obj)->untag()->used_data()));
+ } else if (cid == kLinkedHashSetCid) {
+ writer_->WriteUnsigned(kLengthData);
+ writer_->WriteUnsigned(
+ Smi::Value(static_cast<LinkedHashSetPtr>(obj)->untag()->used_data()));
} else if (cid == kObjectPoolCid) {
writer_->WriteUnsigned(kLengthData);
writer_->WriteUnsigned(static_cast<ObjectPoolPtr>(obj)->untag()->length_);
@@ -1308,6 +1312,7 @@
case kInstructionsSectionCid:
case kInstructionsTableCid:
case kLinkedHashMapCid:
+ case kLinkedHashSetCid:
case kMintCid:
case kNeverCid:
case kSentinelCid:
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index 2fa6cda..95358c6 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -1390,6 +1390,41 @@
}
}
+void LinkedHashSet::PrintJSONImpl(JSONStream* stream, bool ref) const {
+ JSONObject jsobj(stream);
+ PrintSharedInstanceJSON(&jsobj, ref);
+ jsobj.AddProperty("kind", "PlainInstance");
+ jsobj.AddServiceId(*this);
+ jsobj.AddProperty("length", Length());
+ if (ref) {
+ return;
+ }
+ intptr_t offset;
+ intptr_t count;
+ stream->ComputeOffsetAndCount(Length(), &offset, &count);
+ if (offset > 0) {
+ jsobj.AddProperty("offset", offset);
+ }
+ if (count < Length()) {
+ jsobj.AddProperty("count", count);
+ }
+ intptr_t limit = offset + count;
+ ASSERT(limit <= Length());
+ {
+ JSONArray jsarr(&jsobj, "elements");
+ Object& object = Object::Handle();
+ LinkedHashSet::Iterator iterator(*this);
+ int i = 0;
+ while (iterator.MoveNext() && i < limit) {
+ if (i >= offset) {
+ object = iterator.CurrentKey();
+ jsarr.AddValue(object);
+ }
+ i++;
+ }
+ }
+}
+
void Float32x4::PrintJSONImpl(JSONStream* stream, bool ref) const {
JSONObject jsobj(stream);
PrintSharedInstanceJSON(&jsobj, ref);
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 7e5c738..2553f7c 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -4965,6 +4965,41 @@
EXPECT(!iterator.MoveNext());
}
+TEST_CASE(LinkedHashSet_iteration) {
+ const char* kScript =
+ "makeSet() {\n"
+ " var set = {'x', 'y', 'z', 'w'};\n"
+ " set.remove('y');\n"
+ " set.remove('w');\n"
+ " return set;\n"
+ "}";
+ Dart_Handle h_lib = TestCase::LoadTestScript(kScript, NULL);
+ EXPECT_VALID(h_lib);
+ Dart_Handle h_result = Dart_Invoke(h_lib, NewString("makeSet"), 0, NULL);
+ EXPECT_VALID(h_result);
+
+ TransitionNativeToVM transition(thread);
+ Instance& dart_set = Instance::Handle();
+ dart_set ^= Api::UnwrapHandle(h_result);
+ ASSERT(dart_set.IsLinkedHashSet());
+ const LinkedHashSet& cc_set = LinkedHashSet::Cast(dart_set);
+
+ EXPECT_EQ(2, cc_set.Length());
+
+ LinkedHashSet::Iterator iterator(cc_set);
+ Object& object = Object::Handle();
+
+ EXPECT(iterator.MoveNext());
+ object = iterator.CurrentKey();
+ EXPECT_STREQ("x", object.ToCString());
+
+ EXPECT(iterator.MoveNext());
+ object = iterator.CurrentKey();
+ EXPECT_STREQ("z", object.ToCString());
+
+ EXPECT(!iterator.MoveNext());
+}
+
static void CheckConcatAll(const String* data[], intptr_t n) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index d7a1beb..b79c6b9 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -562,6 +562,7 @@
COMPRESSED_VISITOR(ExternalTwoByteString)
COMPRESSED_VISITOR(GrowableObjectArray)
COMPRESSED_VISITOR(LinkedHashMap)
+COMPRESSED_VISITOR(LinkedHashSet)
COMPRESSED_VISITOR(ExternalTypedData)
TYPED_DATA_VIEW_VISITOR(TypedDataView)
COMPRESSED_VISITOR(ReceivePort)
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 4d0bfbb..f16ef82 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -3006,6 +3006,12 @@
friend class SnapshotReader;
};
+class UntaggedLinkedHashSet : public UntaggedLinkedHashBase {
+ RAW_HEAP_OBJECT_IMPLEMENTATION(LinkedHashSet);
+
+ friend class SnapshotReader;
+};
+
class UntaggedFloat32x4 : public UntaggedInstance {
RAW_HEAP_OBJECT_IMPLEMENTATION(Float32x4);
VISIT_NOTHING();
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index 7e6df08..82f51c6 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -1157,38 +1157,8 @@
map = LinkedHashMap::NewUninitialized();
reader->AddBackRef(object_id, &map, kIsDeserialized);
- // Read the type arguments.
- *reader->TypeArgumentsHandle() ^= reader->ReadObjectImpl(kAsInlinedObject);
- map.SetTypeArguments(*reader->TypeArgumentsHandle());
+ reader->MapReadFrom(object_id, map, tags);
- // Read the number of key/value pairs.
- intptr_t len = reader->ReadSmiValue();
- intptr_t used_data = (len << 1);
- map.SetUsedData(used_data);
-
- // Allocate the data array.
- intptr_t data_size =
- Utils::Maximum(Utils::RoundUpToPowerOfTwo(used_data),
- static_cast<uintptr_t>(LinkedHashMap::kInitialIndexSize));
- Array& data = Array::ZoneHandle(reader->zone(), Array::New(data_size));
- map.SetData(data);
- map.SetDeletedKeys(0);
-
- // The index and hashMask is regenerated by the maps themselves on demand.
- // Thus, the index will probably be allocated in new space (unless it's huge).
- // TODO(koda): Eagerly rehash here when no keys have user-defined '==', and
- // in particular, if/when (const) maps are needed in the VM isolate snapshot.
- ASSERT(reader->isolate_group() != Dart::vm_isolate_group());
- map.SetHashMask(0); // Prefer sentinel 0 over null for better type feedback.
-
- reader->EnqueueRehashingOfMap(map);
-
- // Read the keys and values.
- bool read_as_reference = UntaggedObject::IsCanonical(tags) ? false : true;
- for (intptr_t i = 0; i < used_data; i++) {
- *reader->PassiveObjectHandle() = reader->ReadObjectImpl(read_as_reference);
- data.SetAt(i, *reader->PassiveObjectHandle());
- }
return map.ptr();
}
@@ -1197,44 +1167,39 @@
Snapshot::Kind kind,
bool as_reference) {
ASSERT(writer != NULL);
+ ASSERT(!this->IsCanonical());
+ writer->MapWriteTo(object_id, kLinkedHashMapCid, writer->GetObjectTags(this),
+ type_arguments(), used_data(), deleted_keys(), data(),
+ as_reference);
+}
- // Write out the serialization header value for this object.
- writer->WriteInlinedObjectHeader(object_id);
+LinkedHashSetPtr LinkedHashSet::ReadFrom(SnapshotReader* reader,
+ intptr_t object_id,
+ intptr_t tags,
+ Snapshot::Kind kind,
+ bool as_reference) {
+ ASSERT(reader != NULL);
- // Write out the class and tags information.
- writer->WriteIndexedObject(kLinkedHashMapCid);
- writer->WriteTags(writer->GetObjectTags(this));
+ LinkedHashSet& set =
+ LinkedHashSet::ZoneHandle(reader->zone(), LinkedHashSet::null());
+ // Since the set might contain itself as a key or value, allocate first.
+ set = LinkedHashSet::NewUninitialized();
+ reader->AddBackRef(object_id, &set, kIsDeserialized);
- // Write out the type arguments.
- writer->WriteObjectImpl(type_arguments(), kAsInlinedObject);
+ reader->SetReadFrom(object_id, set, tags);
- const intptr_t num_used_data = Smi::Value(used_data());
- ASSERT((num_used_data & 1) == 0); // Keys + values, so must be even.
- const intptr_t num_deleted_keys = Smi::Value(deleted_keys());
+ return set.ptr();
+}
- // Write out the number of (not deleted) key/value pairs that will follow.
- writer->Write<ObjectPtr>(Smi::New((num_used_data >> 1) - num_deleted_keys));
-
- // Write out the keys and values.
- const bool write_as_reference = this->IsCanonical() ? false : true;
- ArrayPtr data_array = data();
- ASSERT(num_used_data <= Smi::Value(data_array->untag()->length()));
-#if defined(DEBUG)
- intptr_t deleted_keys_found = 0;
-#endif // DEBUG
- for (intptr_t i = 0; i < num_used_data; i += 2) {
- ObjectPtr key = data_array->untag()->element(i);
- if (key == data_array) {
-#if defined(DEBUG)
- ++deleted_keys_found;
-#endif // DEBUG
- continue;
- }
- ObjectPtr value = data_array->untag()->element(i + 1);
- writer->WriteObjectImpl(key, write_as_reference);
- writer->WriteObjectImpl(value, write_as_reference);
- }
- DEBUG_ASSERT(deleted_keys_found == num_deleted_keys);
+void UntaggedLinkedHashSet::WriteTo(SnapshotWriter* writer,
+ intptr_t object_id,
+ Snapshot::Kind kind,
+ bool as_reference) {
+ ASSERT(writer != NULL);
+ ASSERT(!this->IsCanonical());
+ writer->SetWriteTo(object_id, kLinkedHashSetCid, writer->GetObjectTags(this),
+ type_arguments(), used_data(), deleted_keys(), data(),
+ as_reference);
}
Float32x4Ptr Float32x4::ReadFrom(SnapshotReader* reader,
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 238bc27..2decacb 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -5211,7 +5211,12 @@
}
{
JSONArray internals(&map, "Map");
- DEFINE_ADD_VALUE_F_CID(LinkedHashMap)
+ CLASS_LIST_MAPS(DEFINE_ADD_VALUE_F_CID)
+ }
+
+ {
+ JSONArray internals(&map, "Set");
+ CLASS_LIST_SETS(DEFINE_ADD_VALUE_F_CID)
}
#define DEFINE_ADD_MAP_KEY(clazz) \
{ \
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index f849156..affccc9 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -42,6 +42,7 @@
return (class_id == kObjectCid ||
(class_id >= kInstanceCid && class_id <= kUserTagCid) ||
class_id == kArrayCid || class_id == kImmutableArrayCid ||
+ class_id == kLinkedHashMapCid || class_id == kLinkedHashSetCid ||
IsStringClassId(class_id) || IsTypedDataClassId(class_id) ||
IsExternalTypedDataClassId(class_id) ||
IsTypedDataViewClassId(class_id) || class_id == kNullCid ||
@@ -333,7 +334,8 @@
result = obj.ptr();
}
RunDelayedTypePostprocessing();
- const Object& ok = Object::Handle(zone_, RunDelayedRehashingOfMaps());
+ const Object& ok =
+ Object::Handle(zone_, RunDelayedRehashingOfMapsAndSets());
objects_to_rehash_ = GrowableObjectArray::null();
if (!ok.IsNull()) {
return ok.ptr();
@@ -367,13 +369,14 @@
}
void SnapshotReader::EnqueueRehashingOfMap(const LinkedHashMap& map) {
+ ASSERT(!map.IsCanonical());
if (objects_to_rehash_.IsNull()) {
objects_to_rehash_ = GrowableObjectArray::New();
}
objects_to_rehash_.Add(map);
}
-ObjectPtr SnapshotReader::RunDelayedRehashingOfMaps() {
+ObjectPtr SnapshotReader::RunDelayedRehashingOfMapsAndSets() {
if (!objects_to_rehash_.IsNull()) {
return DartLibraryCalls::RehashObjects(thread(), objects_to_rehash_);
}
@@ -577,7 +580,7 @@
#undef SNAPSHOT_READ
#define SNAPSHOT_READ(clazz) case kFfi##clazz##Cid:
- CLASS_LIST_FFI(SNAPSHOT_READ) { UNREACHABLE(); }
+ CLASS_LIST_FFI(SNAPSHOT_READ) { UNREACHABLE(); }
#undef SNAPSHOT_READ
default:
UNREACHABLE();
@@ -586,7 +589,8 @@
return pobj_.ptr();
}
-void SnapshotReader::EnqueueRehashingOfSet(const Object& set) {
+void SnapshotReader::EnqueueRehashingOfSet(const LinkedHashSet& set) {
+ ASSERT(!set.IsCanonical());
if (objects_to_rehash_.IsNull()) {
objects_to_rehash_ = GrowableObjectArray::New();
}
@@ -623,9 +627,6 @@
ASSERT(!cls_.IsNull());
instance_size = cls_.host_instance_size();
}
- if (cls_.id() == set_class_.id()) {
- EnqueueRehashingOfSet(*result);
- }
if (!as_reference) {
// Read all the individual fields for inlined objects.
intptr_t next_field_offset = cls_.host_next_field_offset();
@@ -890,6 +891,84 @@
}
}
+void SnapshotReader::MapReadFrom(intptr_t object_id,
+ const LinkedHashMap& map,
+ intptr_t tags) {
+ // Read the type arguments.
+ *TypeArgumentsHandle() ^= ReadObjectImpl(kAsInlinedObject);
+ map.SetTypeArguments(*TypeArgumentsHandle());
+
+ // Read the number of key/value pairs.
+ intptr_t len = ReadSmiValue();
+ intptr_t used_data = (len << 1);
+ map.SetUsedData(used_data);
+
+ // Allocate the data array.
+ intptr_t data_size =
+ Utils::Maximum(Utils::RoundUpToPowerOfTwo(used_data),
+ static_cast<uintptr_t>(LinkedHashMap::kInitialIndexSize));
+ Array& data = Array::ZoneHandle(zone(), Array::New(data_size));
+ map.SetData(data);
+ map.SetDeletedKeys(0);
+
+ // The index and hashMask is regenerated by the maps themselves on demand.
+ // Thus, the index will probably be allocated in new space (unless it's huge).
+ // TODO(koda): Eagerly rehash here when no keys have user-defined '==', and
+ // in particular, if/when (const) maps are needed in the VM isolate snapshot.
+ ASSERT(isolate_group() != Dart::vm_isolate_group());
+ map.SetHashMask(0); // Prefer sentinel 0 over null for better type feedback.
+
+ // Read the keys and values.
+ bool read_as_reference = UntaggedObject::IsCanonical(tags) ? false : true;
+ for (intptr_t i = 0; i < used_data; i++) {
+ *PassiveObjectHandle() = ReadObjectImpl(read_as_reference);
+ data.SetAt(i, *PassiveObjectHandle());
+ }
+
+ // TODO(http://dartbug.com/45908): Treat immutable, canonical maps.
+ ASSERT(!UntaggedObject::IsCanonical(tags));
+ // Can have user-defined hashCode for keys, compute in Dart.
+ EnqueueRehashingOfMap(map);
+}
+
+void SnapshotReader::SetReadFrom(intptr_t object_id,
+ const LinkedHashSet& set,
+ intptr_t tags) {
+ // Read the type arguments.
+ *TypeArgumentsHandle() ^= ReadObjectImpl(kAsInlinedObject);
+ set.SetTypeArguments(*TypeArgumentsHandle());
+
+ intptr_t used_data = ReadSmiValue();
+ set.SetUsedData(used_data);
+
+ // Allocate the data array.
+ intptr_t data_size =
+ Utils::Maximum(Utils::RoundUpToPowerOfTwo(used_data),
+ static_cast<uintptr_t>(LinkedHashSet::kInitialIndexSize));
+ Array& data = Array::ZoneHandle(zone(), Array::New(data_size));
+ set.SetData(data);
+ set.SetDeletedKeys(0);
+
+ // The index and hashMask is regenerated by the sets themselves on demand.
+ // Thus, the index will probably be allocated in new space (unless it's huge).
+ // TODO(koda): Eagerly rehash here when no keys have user-defined '==', and
+ // in particular, if/when (const) sets are needed in the VM isolate snapshot.
+ ASSERT(isolate_group() != Dart::vm_isolate_group());
+ set.SetHashMask(0); // Prefer sentinel 0 over null for better type feedback.
+
+ // Read the keys and values.
+ bool read_as_reference = UntaggedObject::IsCanonical(tags) ? false : true;
+ for (intptr_t i = 0; i < used_data; i++) {
+ *PassiveObjectHandle() = ReadObjectImpl(read_as_reference);
+ data.SetAt(i, *PassiveObjectHandle());
+ }
+
+ // TODO(http://dartbug.com/45908): Treat immutable, canonical sets.
+ ASSERT(!UntaggedObject::IsCanonical(tags));
+ // Can have user-defined hashCode for keys, compute in Dart.
+ EnqueueRehashingOfSet(set);
+}
+
MessageSnapshotReader::MessageSnapshotReader(Message* message, Thread* thread)
: SnapshotReader(message->snapshot(),
message->snapshot_length(),
@@ -1402,6 +1481,99 @@
}
}
+void SnapshotWriter::MapWriteTo(intptr_t object_id,
+ intptr_t class_id,
+ intptr_t tags,
+ TypeArgumentsPtr type_arguments,
+ SmiPtr used_data_smi,
+ SmiPtr deleted_keys_smi,
+ ArrayPtr data,
+ bool as_reference) {
+ // Write out the serialization header value for this object.
+ WriteInlinedObjectHeader(object_id);
+
+ // Write out the class and tags information.
+ WriteIndexedObject(class_id);
+ WriteTags(tags);
+
+ // Write out the type arguments.
+ WriteObjectImpl(type_arguments, kAsInlinedObject);
+
+ const intptr_t num_used_data = Smi::Value(used_data_smi);
+ ASSERT((num_used_data & 1) == 0); // Keys + values, so must be even.
+ const intptr_t num_deleted_keys = Smi::Value(deleted_keys_smi);
+
+ // Write out the number of (not deleted) key/value pairs that will follow.
+ Write<ObjectPtr>(Smi::New((num_used_data >> 1) - num_deleted_keys));
+
+ // Write out the keys and values.
+ const bool write_as_reference =
+ UntaggedObject::IsCanonical(tags) ? false : true;
+ ASSERT(num_used_data <= Smi::Value(data->untag()->length()));
+#if defined(DEBUG)
+ intptr_t deleted_keys_found = 0;
+#endif // DEBUG
+ for (intptr_t i = 0; i < num_used_data; i += 2) {
+ ObjectPtr key = data->untag()->element(i);
+ if (key == data) {
+#if defined(DEBUG)
+ ++deleted_keys_found;
+#endif // DEBUG
+ continue;
+ }
+ ObjectPtr value = data->untag()->element(i + 1);
+ WriteObjectImpl(key, write_as_reference);
+ WriteObjectImpl(value, write_as_reference);
+ }
+ DEBUG_ASSERT(deleted_keys_found == num_deleted_keys);
+}
+
+void SnapshotWriter::SetWriteTo(intptr_t object_id,
+ intptr_t class_id,
+ intptr_t tags,
+ TypeArgumentsPtr type_arguments,
+ SmiPtr used_data_smi,
+ SmiPtr deleted_keys_smi,
+ ArrayPtr data,
+ bool as_reference) {
+ // Write out the serialization header value for this object.
+ WriteInlinedObjectHeader(object_id);
+
+ // Write out the class and tags information.
+ WriteIndexedObject(class_id);
+ WriteTags(tags);
+
+ // Write out the type arguments.
+ WriteObjectImpl(type_arguments, kAsInlinedObject);
+
+ const intptr_t num_used_data = Smi::Value(used_data_smi);
+ const intptr_t num_deleted_keys = Smi::Value(deleted_keys_smi);
+
+ // Write out the number of (not deleted) key/value pairs that will follow.
+ Write<ObjectPtr>(Smi::New(num_used_data - num_deleted_keys));
+
+ // Write out the keys and values.
+ const bool write_as_reference =
+ UntaggedObject::IsCanonical(tags) ? false : true;
+ ASSERT(num_used_data <= Smi::Value(data->untag()->length()));
+#if defined(DEBUG)
+ intptr_t deleted_keys_found = 0;
+#endif // DEBUG
+ for (intptr_t i = 0; i < num_used_data; i++) {
+ ObjectPtr key = data->untag()->element(i);
+ if (key == data) {
+#if defined(DEBUG)
+ ++deleted_keys_found;
+#endif // DEBUG
+ continue;
+ }
+ ObjectPtr value = data->untag()->element(i + 1);
+ WriteObjectImpl(key, write_as_reference);
+ WriteObjectImpl(value, write_as_reference);
+ }
+ DEBUG_ASSERT(deleted_keys_found == num_deleted_keys);
+}
+
FunctionPtr SnapshotWriter::IsSerializableClosure(ClosurePtr closure) {
// Extract the function object to check if this closure
// can be sent in an isolate message.
diff --git a/runtime/vm/snapshot.h b/runtime/vm/snapshot.h
index 60da2b3..8123e46 100644
--- a/runtime/vm/snapshot.h
+++ b/runtime/vm/snapshot.h
@@ -321,8 +321,8 @@
void RunDelayedTypePostprocessing();
void EnqueueRehashingOfMap(const LinkedHashMap& map);
- void EnqueueRehashingOfSet(const Object& set);
- ObjectPtr RunDelayedRehashingOfMaps();
+ void EnqueueRehashingOfSet(const LinkedHashSet& set);
+ ObjectPtr RunDelayedRehashingOfMapsAndSets();
ClassPtr ReadClassId(intptr_t object_id);
ObjectPtr ReadStaticImplicitClosure(intptr_t object_id, intptr_t cls_header);
@@ -349,6 +349,14 @@
intptr_t len,
intptr_t tags);
+ void MapReadFrom(intptr_t object_id,
+ const LinkedHashMap& result,
+ intptr_t tags);
+
+ void SetReadFrom(intptr_t object_id,
+ const LinkedHashSet& result,
+ intptr_t tags);
+
intptr_t NextAvailableObjectId() const;
void SetReadException(const char* msg);
@@ -357,32 +365,32 @@
bool is_vm_isolate() const;
- Snapshot::Kind kind_; // Indicates type of the snapshot.
- Thread* thread_; // Current thread.
- Zone* zone_; // Zone for allocations while reading.
- Heap* heap_; // Heap of the current isolate.
- PageSpace* old_space_; // Old space of the current isolate.
- Class& cls_; // Temporary Class handle.
- Code& code_; // Temporary Code handle.
- Instance& instance_; // Temporary Instance handle
- Instructions& instructions_; // Temporary Instructions handle
- Object& obj_; // Temporary Object handle.
- PassiveObject& pobj_; // Temporary PassiveObject handle.
- Array& array_; // Temporary Array handle.
- Field& field_; // Temporary Field handle.
- String& str_; // Temporary String handle.
- Library& library_; // Temporary library handle.
- AbstractType& type_; // Temporary type handle.
- TypeArguments& type_arguments_; // Temporary type argument handle.
- GrowableObjectArray& tokens_; // Temporary tokens handle.
- ExternalTypedData& data_; // Temporary stream data handle.
+ Snapshot::Kind kind_; // Indicates type of the snapshot.
+ Thread* thread_; // Current thread.
+ Zone* zone_; // Zone for allocations while reading.
+ Heap* heap_; // Heap of the current isolate.
+ PageSpace* old_space_; // Old space of the current isolate.
+ Class& cls_; // Temporary Class handle.
+ Code& code_; // Temporary Code handle.
+ Instance& instance_; // Temporary Instance handle
+ Instructions& instructions_; // Temporary Instructions handle
+ Object& obj_; // Temporary Object handle.
+ PassiveObject& pobj_; // Temporary PassiveObject handle.
+ Array& array_; // Temporary Array handle.
+ Field& field_; // Temporary Field handle.
+ String& str_; // Temporary String handle.
+ Library& library_; // Temporary library handle.
+ AbstractType& type_; // Temporary type handle.
+ TypeArguments& type_arguments_; // Temporary type argument handle.
+ GrowableObjectArray& tokens_; // Temporary tokens handle.
+ ExternalTypedData& data_; // Temporary stream data handle.
TypedDataBase& typed_data_base_; // Temporary typed data base handle.
- TypedData& typed_data_; // Temporary typed data handle.
+ TypedData& typed_data_; // Temporary typed data handle.
TypedDataView& typed_data_view_; // Temporary typed data view handle.
- Function& function_; // Temporary function handle.
- Smi& smi_; // Temporary Smi handle.
- UnhandledException& error_; // Error handle.
- const Class& set_class_; // The LinkedHashSet class.
+ Function& function_; // Temporary function handle.
+ Smi& smi_; // Temporary Smi handle.
+ UnhandledException& error_; // Error handle.
+ const Class& set_class_; // The LinkedHashSet class.
intptr_t max_vm_isolate_object_id_;
ZoneGrowableArray<BackRefNode>* backward_references_;
GrowableObjectArray& types_to_postprocess_;
@@ -406,6 +414,7 @@
friend class Library;
friend class LibraryPrefix;
friend class LinkedHashMap;
+ friend class LinkedHashSet;
friend class MirrorReference;
friend class Namespace;
friend class PatchClass;
@@ -649,6 +658,22 @@
TypeArgumentsPtr type_arguments,
CompressedObjectPtr data[],
bool as_reference);
+ void MapWriteTo(intptr_t object_id,
+ intptr_t class_id,
+ intptr_t tags,
+ TypeArgumentsPtr type_arguments,
+ SmiPtr used_data_smi,
+ SmiPtr deleted_keys_smi,
+ ArrayPtr data,
+ bool as_reference);
+ void SetWriteTo(intptr_t object_id,
+ intptr_t class_id,
+ intptr_t tags,
+ TypeArgumentsPtr type_arguments,
+ SmiPtr used_data_smi,
+ SmiPtr deleted_keys_smi,
+ ArrayPtr data,
+ bool as_reference);
ClassPtr GetFunctionOwner(FunctionPtr func);
void CheckForNativeFields(ClassPtr cls);
void SetWriteException(Exceptions::ExceptionType type, const char* msg);
@@ -686,6 +711,7 @@
friend class UntaggedInstructions;
friend class UntaggedLibrary;
friend class UntaggedLinkedHashMap;
+ friend class UntaggedLinkedHashSet;
friend class UntaggedLocalVarDescriptors;
friend class UntaggedMirrorReference;
friend class UntaggedObjectPool;
diff --git a/runtime/vm/tagged_pointer.h b/runtime/vm/tagged_pointer.h
index 4f3b1f6..ffd7d32 100644
--- a/runtime/vm/tagged_pointer.h
+++ b/runtime/vm/tagged_pointer.h
@@ -396,6 +396,7 @@
DEFINE_TAGGED_POINTER(GrowableObjectArray, Instance)
DEFINE_TAGGED_POINTER(LinkedHashBase, Instance)
DEFINE_TAGGED_POINTER(LinkedHashMap, LinkedHashBase)
+DEFINE_TAGGED_POINTER(LinkedHashSet, LinkedHashBase)
DEFINE_TAGGED_POINTER(Float32x4, Instance)
DEFINE_TAGGED_POINTER(Int32x4, Instance)
DEFINE_TAGGED_POINTER(Float64x2, Instance)
diff --git a/sdk/lib/_internal/vm/lib/compact_hash.dart b/sdk/lib/_internal/vm/lib/compact_hash.dart
index 492e9bd..dc552c7 100644
--- a/sdk/lib/_internal/vm/lib/compact_hash.dart
+++ b/sdk/lib/_internal/vm/lib/compact_hash.dart
@@ -490,18 +490,14 @@
E get current => _current as E;
}
-// Set implementation, analogous to _CompactLinkedHashMap.
-@pragma('vm:entry-point')
-class _CompactLinkedHashSet<E> extends _HashFieldBase
- with _HashBase, _OperatorEqualsAndHashCode, SetMixin<E>
- implements LinkedHashSet<E> {
- _CompactLinkedHashSet() : super(_HashBase._INITIAL_INDEX_SIZE >> 1) {
- assert(_HashBase._UNUSED_PAIR == 0);
- }
+abstract class _LinkedHashSetMixin<E> implements _HashBase {
+ int _hashCode(e);
+ bool _equals(e1, e2);
+ int get _checkSum;
+ bool _isModifiedSince(List oldData, int oldCheckSum);
- static Set<R> _newEmpty<R>() => new _CompactLinkedHashSet<R>();
-
- Set<R> cast<R>() => Set.castFrom<E, R>(this, newSet: _newEmpty);
+ bool get isEmpty => length == 0;
+ bool get isNotEmpty => !isEmpty;
int get length => _usedData - _deletedKeys;
E get first {
@@ -650,19 +646,51 @@
Iterator<E> get iterator =>
new _CompactIterator<E>(this, _data, _usedData, -1, 1);
- // Returns a set of the same type, although this
- // is not required by the spec. (For instance, always using an identity set
- // would be technically correct, albeit surprising.)
- Set<E> toSet() => new _CompactLinkedHashSet<E>()..addAll(this);
-
// This method is called by [_rehashObjects] (see above).
void _regenerateIndex() {
+ _index = _data.length == 0 ? _initialIndex : new Uint32List(_data.length);
+ assert(_hashMask == 0);
+ _hashMask = _HashBase._indexSizeToHashMask(_index.length);
_rehash();
}
}
-class _CompactLinkedIdentityHashSet<E> extends _CompactLinkedHashSet<E>
- with _IdenticalAndIdentityHashCode {
+// Set implementation, analogous to _CompactLinkedHashMap.
+@pragma('vm:entry-point')
+class _CompactLinkedHashSet<E> extends _HashVMBase
+ with
+ SetMixin<E>,
+ _LinkedHashSetMixin<E>,
+ _HashBase,
+ _OperatorEqualsAndHashCode
+ implements LinkedHashSet<E> {
+ _CompactLinkedHashSet() {
+ _index = _initialIndex;
+ _hashMask = 0;
+ _data = _initialData;
+ _usedData = 0;
+ _deletedKeys = 0;
+ }
+
+ Set<R> cast<R>() => Set.castFrom<E, R>(this, newSet: _newEmpty);
+
+ static Set<R> _newEmpty<R>() => new _CompactLinkedHashSet<R>();
+
+ // Returns a set of the same type, although this
+ // is not required by the spec. (For instance, always using an identity set
+ // would be technically correct, albeit surprising.)
+ Set<E> toSet() => new _CompactLinkedHashSet<E>()..addAll(this);
+}
+
+class _CompactLinkedIdentityHashSet<E> extends _HashFieldBase
+ with
+ SetMixin<E>,
+ _LinkedHashSetMixin<E>,
+ _HashBase,
+ _IdenticalAndIdentityHashCode
+ implements LinkedHashSet<E> {
+ _CompactLinkedIdentityHashSet() : super(_HashBase._INITIAL_INDEX_SIZE);
+
Set<E> toSet() => new _CompactLinkedIdentityHashSet<E>()..addAll(this);
static Set<R> _newEmpty<R>() => new _CompactLinkedIdentityHashSet<R>();
@@ -670,7 +698,9 @@
Set<R> cast<R>() => Set.castFrom<E, R>(this, newSet: _newEmpty);
}
-class _CompactLinkedCustomHashSet<E> extends _CompactLinkedHashSet<E> {
+class _CompactLinkedCustomHashSet<E> extends _HashFieldBase
+ with SetMixin<E>, _LinkedHashSetMixin<E>, _HashBase
+ implements LinkedHashSet<E> {
final _equality;
final _hasher;
final _validKey;
@@ -683,7 +713,8 @@
bool remove(Object? o) => _validKey(o) ? super.remove(o) : false;
_CompactLinkedCustomHashSet(this._equality, this._hasher, validKey)
- : _validKey = (validKey != null) ? validKey : new _TypeTest<E>().test;
+ : _validKey = (validKey != null) ? validKey : new _TypeTest<E>().test,
+ super(_HashBase._INITIAL_INDEX_SIZE);
Set<R> cast<R>() => Set.castFrom<E, R>(this);
Set<E> toSet() =>
diff --git a/tests/language/const/map2_runtime_test.dart b/tests/language/const/map2_runtime_test.dart
index 9e1fe97..02e6a9e 100644
--- a/tests/language/const/map2_runtime_test.dart
+++ b/tests/language/const/map2_runtime_test.dart
@@ -13,12 +13,10 @@
class B implements A {
const B();
-
-
}
confuse(x) {
- if (new DateTime.now() == 42) return confuse(2);
+ if (DateTime.now().millisecondsSinceEpoch == 42) return confuse(2);
return x;
}
diff --git a/tests/language/const/map2_test.dart b/tests/language/const/map2_test.dart
index a5f3798..dd392be 100644
--- a/tests/language/const/map2_test.dart
+++ b/tests/language/const/map2_test.dart
@@ -15,7 +15,7 @@
}
confuse(x) {
- if (new DateTime.now() == 42) return confuse(2);
+ if (DateTime.now().millisecondsSinceEpoch == 42) return confuse(2);
return x;
}
diff --git a/tests/language/const/map_hashcode_override2_test.dart b/tests/language/const/map_hashcode_override2_test.dart
index b747a97..871b14a 100644
--- a/tests/language/const/map_hashcode_override2_test.dart
+++ b/tests/language/const/map_hashcode_override2_test.dart
@@ -9,7 +9,8 @@
/// Prevents static optimizations and inlining.
getValueNonOptimized(x) {
// DateTime.now() cannot be predicted statically, never equal to ASCII 42 '*'.
- if (new DateTime.now() == 42) return getValueNonOptimized(2);
+ if (DateTime.now().millisecondsSinceEpoch == 42)
+ return getValueNonOptimized(2);
return x;
}
diff --git a/tests/language/const/map_hashcode_override_test.dart b/tests/language/const/map_hashcode_override_test.dart
index e378e05..a88d1a8 100644
--- a/tests/language/const/map_hashcode_override_test.dart
+++ b/tests/language/const/map_hashcode_override_test.dart
@@ -9,7 +9,8 @@
/// Prevents static optimizations and inlining.
getValueNonOptimized(x) {
// DateTime.now() cannot be predicted statically, never equal to ASCII 42 '*'.
- if (new DateTime.now() == 42) return getValueNonOptimized(2);
+ if (DateTime.now().millisecondsSinceEpoch == 42)
+ return getValueNonOptimized(2);
return x;
}
diff --git a/tests/language/const/map_test.dart b/tests/language/const/map_test.dart
index 13c6720..3658459 100644
--- a/tests/language/const/map_test.dart
+++ b/tests/language/const/map_test.dart
@@ -9,7 +9,8 @@
/// Prevents static optimizations and inlining.
getValueNonOptimized(x) {
// DateTime.now() cannot be predicted statically, never equal to ASCII 42 '*'.
- if (new DateTime.now() == 42) return getValueNonOptimized(2);
+ if (DateTime.now().millisecondsSinceEpoch == 42)
+ return getValueNonOptimized(2);
return x;
}
diff --git a/tests/language/const/set_hashcode_override2_test.dart b/tests/language/const/set_hashcode_override2_test.dart
index 1fc8665..0775822 100644
--- a/tests/language/const/set_hashcode_override2_test.dart
+++ b/tests/language/const/set_hashcode_override2_test.dart
@@ -9,7 +9,8 @@
/// Prevents static optimizations and inlining.
getValueNonOptimized(x) {
// DateTime.now() cannot be predicted statically, never equal to ASCII 42 '*'.
- if (new DateTime.now() == 42) return getValueNonOptimized(2);
+ if (DateTime.now().millisecondsSinceEpoch == 42)
+ return getValueNonOptimized(2);
return x;
}
diff --git a/tests/language/regress/regress15720_test.dart b/tests/language/regress/regress15720_test.dart
index 638fc98..85c102c 100644
--- a/tests/language/regress/regress15720_test.dart
+++ b/tests/language/regress/regress15720_test.dart
@@ -7,7 +7,7 @@
class B {}
confuse(x) {
- if (new DateTime.now() == 42) return confuse(x);
+ if (DateTime.now().millisecondsSinceEpoch == 42) return confuse(x);
return x;
}
diff --git a/tests/language/vm/regress_flutter_85311_test.dart b/tests/language/vm/regress_flutter_85311_test.dart
new file mode 100644
index 0000000..122d13a
--- /dev/null
+++ b/tests/language/vm/regress_flutter_85311_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2021, 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.
+
+// This is a regression test for the bug in
+// https://github.com/flutter/flutter/issues/51828.
+// Verifies that temporaries aren't incorrectly assigned types when they're
+// reused.
+
+import "package:expect/expect.dart";
+
+bool wasCalled = false;
+
+class Z {
+ bool operator ==(Object other) {
+ wasCalled = true;
+ return true;
+ }
+}
+
+class Y {
+ final dynamic v;
+ Y(this.v);
+}
+
+Future<bool> crash(dynamic y) async {
+ return (y.v == (await 7) || y == (await 9));
+}
+
+void main() async {
+ await crash(Y(Z()));
+
+ Expect.isTrue(wasCalled);
+}
diff --git a/tests/language_2/const/map2_runtime_test.dart b/tests/language_2/const/map2_runtime_test.dart
index 5ce67f2..d9a2e80 100644
--- a/tests/language_2/const/map2_runtime_test.dart
+++ b/tests/language_2/const/map2_runtime_test.dart
@@ -15,12 +15,10 @@
class B implements A {
const B();
-
-
}
confuse(x) {
- if (new DateTime.now() == 42) return confuse(2);
+ if (DateTime.now().millisecondsSinceEpoch == 42) return confuse(2);
return x;
}
diff --git a/tests/language_2/const/map2_test.dart b/tests/language_2/const/map2_test.dart
index 55f729e..216886c 100644
--- a/tests/language_2/const/map2_test.dart
+++ b/tests/language_2/const/map2_test.dart
@@ -17,7 +17,7 @@
}
confuse(x) {
- if (new DateTime.now() == 42) return confuse(2);
+ if (DateTime.now().millisecondsSinceEpoch == 42) return confuse(2);
return x;
}
diff --git a/tests/language_2/const/map_hashcode_override2_test.dart b/tests/language_2/const/map_hashcode_override2_test.dart
index 86ced20..26ffaf6 100644
--- a/tests/language_2/const/map_hashcode_override2_test.dart
+++ b/tests/language_2/const/map_hashcode_override2_test.dart
@@ -11,7 +11,8 @@
/// Prevents static optimizations and inlining.
getValueNonOptimized(x) {
// DateTime.now() cannot be predicted statically, never equal to ASCII 42 '*'.
- if (new DateTime.now() == 42) return getValueNonOptimized(2);
+ if (DateTime.now().millisecondsSinceEpoch == 42)
+ return getValueNonOptimized(2);
return x;
}
diff --git a/tests/language_2/const/map_hashcode_override_test.dart b/tests/language_2/const/map_hashcode_override_test.dart
index 47223e7..c297c54 100644
--- a/tests/language_2/const/map_hashcode_override_test.dart
+++ b/tests/language_2/const/map_hashcode_override_test.dart
@@ -11,7 +11,8 @@
/// Prevents static optimizations and inlining.
getValueNonOptimized(x) {
// DateTime.now() cannot be predicted statically, never equal to ASCII 42 '*'.
- if (new DateTime.now() == 42) return getValueNonOptimized(2);
+ if (DateTime.now().millisecondsSinceEpoch == 42)
+ return getValueNonOptimized(2);
return x;
}
diff --git a/tests/language_2/const/map_test.dart b/tests/language_2/const/map_test.dart
index bbd4a8f..543596a 100644
--- a/tests/language_2/const/map_test.dart
+++ b/tests/language_2/const/map_test.dart
@@ -11,7 +11,8 @@
/// Prevents static optimizations and inlining.
getValueNonOptimized(x) {
// DateTime.now() cannot be predicted statically, never equal to ASCII 42 '*'.
- if (new DateTime.now() == 42) return getValueNonOptimized(2);
+ if (DateTime.now().millisecondsSinceEpoch == 42)
+ return getValueNonOptimized(2);
return x;
}
diff --git a/tests/language_2/const/set_hashcode_override2_test.dart b/tests/language_2/const/set_hashcode_override2_test.dart
index 7035373..d14e8e2 100644
--- a/tests/language_2/const/set_hashcode_override2_test.dart
+++ b/tests/language_2/const/set_hashcode_override2_test.dart
@@ -11,7 +11,8 @@
/// Prevents static optimizations and inlining.
getValueNonOptimized(x) {
// DateTime.now() cannot be predicted statically, never equal to ASCII 42 '*'.
- if (new DateTime.now() == 42) return getValueNonOptimized(2);
+ if (DateTime.now().millisecondsSinceEpoch == 42)
+ return getValueNonOptimized(2);
return x;
}
diff --git a/tests/language_2/regress/regress15720_test.dart b/tests/language_2/regress/regress15720_test.dart
index f07405e..14d5b18 100644
--- a/tests/language_2/regress/regress15720_test.dart
+++ b/tests/language_2/regress/regress15720_test.dart
@@ -9,7 +9,7 @@
class B {}
confuse(x) {
- if (new DateTime.now() == 42) return confuse(x);
+ if (DateTime.now().millisecondsSinceEpoch == 42) return confuse(x);
return x;
}
diff --git a/tests/language_2/vm/regress_flutter_85311_test.dart b/tests/language_2/vm/regress_flutter_85311_test.dart
new file mode 100644
index 0000000..a3458f9
--- /dev/null
+++ b/tests/language_2/vm/regress_flutter_85311_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2021, 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.
+
+// @dart = 2.9
+
+// This is a regression test for the bug in
+// https://github.com/flutter/flutter/issues/51828.
+// Verifies that temporaries aren't incorrectly assigned types when they're
+// reused.
+
+import "package:expect/expect.dart";
+
+bool wasCalled = false;
+
+class Z {
+ bool operator ==(Object other) {
+ wasCalled = true;
+ return true;
+ }
+}
+
+class Y {
+ final dynamic v;
+ Y(this.v);
+}
+
+Future<bool> crash(dynamic y) async {
+ return (y.v == (await 7) || y == (await 9));
+}
+
+void main() async {
+ await crash(Y(Z()));
+
+ Expect.isTrue(wasCalled);
+}
diff --git a/tools/VERSION b/tools/VERSION
index addff9c..b0c12a4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 304
+PRERELEASE 305
PRERELEASE_PATCH 0
\ No newline at end of file