Version 2.14.0-181.0.dev
Merge commit '6c254fab89adbeebfb616cdec497260db4cb7cf7' into 'dev'
diff --git a/runtime/lib/object.cc b/runtime/lib/object.cc
index fb999cf..93b086d 100644
--- a/runtime/lib/object.cc
+++ b/runtime/lib/object.cc
@@ -52,19 +52,15 @@
DEFINE_NATIVE_ENTRY(Object_setHashIfNotSetYet, 0, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(Smi, hash, arguments->NativeArgAt(1));
- const intptr_t current_hash = GetHash(isolate, arguments->NativeArgAt(0));
- if (current_hash != 0) {
- return Smi::New(current_hash);
- }
#if defined(HASH_IN_OBJECT_HEADER)
- Object::SetCachedHash(arguments->NativeArgAt(0), hash.Value());
+ return Smi::New(
+ Object::SetCachedHashIfNotSet(arguments->NativeArgAt(0), hash.Value()));
#else
const Instance& instance =
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
- Heap* heap = isolate->group()->heap();
- heap->SetHash(instance.ptr(), hash.Value());
+ Heap* heap = thread->heap();
+ return Smi::New(heap->SetHashIfNotSet(instance.ptr(), hash.Value()));
#endif
- return hash.ptr();
}
DEFINE_NATIVE_ENTRY(Object_toString, 0, 1) {
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
index 9b1b6e38..cf58e60 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
@@ -1601,26 +1601,27 @@
void AsmIntrinsifier::Object_setHashIfNotSetYet(Assembler* assembler,
Label* normal_ir_body) {
- Label already_set;
- __ ldr(R0, Address(SP, 1 * target::kWordSize)); // Object.
- __ ldr(R1, FieldAddress(R0, target::String::hash_offset(), kFourBytes),
- kUnsignedFourBytes);
- __ cbnz(&already_set, R1, kFourBytes);
- __ ldr(R1, Address(SP, 0 * target::kWordSize)); // Value.
+ __ ldp(/*Value=*/R1, /*Object=*/R0, Address(SP, 0, Address::PairOffset));
// R0: Untagged address of header word (ldxr/stxr do not support offsets).
__ sub(R0, R0, Operand(kHeapObjectTag));
__ SmiUntag(R1);
__ LslImmediate(R3, R1, target::UntaggedObject::kHashTagPos);
- Label retry;
+
+ Label retry, already_set_in_r4;
__ Bind(&retry);
__ ldxr(R2, R0, kEightBytes);
+ __ LsrImmediate(R4, R2, target::UntaggedObject::kHashTagPos);
+ __ cbnz(&already_set_in_r4, R4);
__ orr(R2, R2, Operand(R3));
__ stxr(R4, R2, R0, kEightBytes);
__ cbnz(&retry, R4);
// Fall-through with R1 containing new hash value (untagged).
- __ Bind(&already_set);
__ SmiTag(R0, R1);
__ ret();
+ __ Bind(&already_set_in_r4);
+ __ clrex();
+ __ SmiTag(R0, R4);
+ __ ret();
}
void GenerateSubstringMatchesSpecialization(Assembler* assembler,
diff --git a/runtime/vm/compiler/asm_intrinsifier_test.cc b/runtime/vm/compiler/asm_intrinsifier_test.cc
new file mode 100644
index 0000000..6fbc49d
--- /dev/null
+++ b/runtime/vm/compiler/asm_intrinsifier_test.cc
@@ -0,0 +1,81 @@
+// 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.
+
+#include "platform/assert.h"
+
+#include "vm/globals.h"
+#include "vm/symbols.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+
+static intptr_t GetHash(Isolate* isolate, const ObjectPtr obj) {
+#if defined(HASH_IN_OBJECT_HEADER)
+ return Object::GetCachedHash(obj);
+#else
+ Heap* heap = isolate->group()->heap();
+ ASSERT(obj->IsDartInstance());
+ return heap->GetHash(obj);
+#endif
+}
+
+ISOLATE_UNIT_TEST_CASE(AsmIntrinsifier_SetHashIfNotSetYet) {
+ auto I = Isolate::Current();
+ const auto& corelib = Library::Handle(Library::CoreLibrary());
+ const auto& name = String::Handle(String::New("_setHashIfNotSetYet"));
+ const auto& symbol = String::Handle(Symbols::New(thread, name));
+
+ const auto& function =
+ Function::Handle(corelib.LookupFunctionAllowPrivate(symbol));
+ const auto& object_class =
+ Class::Handle(corelib.LookupClass(Symbols::Object()));
+
+ auto& smi0 = Smi::Handle(Smi::New(0));
+ auto& smi21 = Smi::Handle(Smi::New(21));
+ auto& smi42 = Smi::Handle(Smi::New(42));
+ const auto& obj = Object::Handle(Instance::New(object_class));
+ const auto& args = Array::Handle(Array::New(2));
+
+ const auto& args_descriptor_array =
+ Array::Handle(ArgumentsDescriptor::NewBoxed(0, 2, Array::empty_array()));
+
+ // Initialized to 0
+ EXPECT_EQ(smi0.ptr(), Smi::New(GetHash(I, obj.ptr())));
+
+ // Lazily set to 42 on first call.
+ args.SetAt(0, obj);
+ args.SetAt(1, smi42);
+ EXPECT_EQ(smi42.ptr(),
+ DartEntry::InvokeFunction(function, args, args_descriptor_array));
+ EXPECT_EQ(smi42.ptr(), Smi::New(GetHash(I, obj.ptr())));
+
+ // Stays at 42 on subsequent calls.
+ args.SetAt(0, obj);
+ args.SetAt(1, smi21);
+ EXPECT_EQ(smi42.ptr(),
+ DartEntry::InvokeFunction(function, args, args_descriptor_array));
+ EXPECT_EQ(smi42.ptr(), Smi::New(GetHash(I, obj.ptr())));
+
+ const auto& obj2 = Object::Handle(Instance::New(object_class));
+ const auto& smiMax = Smi::Handle(Smi::New(0xffffffff));
+
+ // Initialized to 0
+ EXPECT_EQ(smi0.ptr(), Smi::New(GetHash(I, obj2.ptr())));
+
+ // Lazily set to smiMax first call.
+ args.SetAt(0, obj2);
+ args.SetAt(1, smiMax);
+ EXPECT_EQ(smiMax.ptr(),
+ DartEntry::InvokeFunction(function, args, args_descriptor_array));
+ EXPECT_EQ(smiMax.ptr(), Smi::New(GetHash(I, obj2.ptr())));
+
+ // Stays at smiMax on subsequent calls.
+ args.SetAt(0, obj2);
+ args.SetAt(1, smi21);
+ EXPECT_EQ(smiMax.ptr(),
+ DartEntry::InvokeFunction(function, args, args_descriptor_array));
+ EXPECT_EQ(smiMax.ptr(), Smi::New(GetHash(I, obj2.ptr())));
+}
+
+} // namespace dart
diff --git a/runtime/vm/compiler/asm_intrinsifier_x64.cc b/runtime/vm/compiler/asm_intrinsifier_x64.cc
index 7c068d9..eb5b694 100644
--- a/runtime/vm/compiler/asm_intrinsifier_x64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_x64.cc
@@ -1455,23 +1455,35 @@
void AsmIntrinsifier::Object_setHashIfNotSetYet(Assembler* assembler,
Label* normal_ir_body) {
- Label already_set;
- __ movq(RAX, Address(RSP, +2 * target::kWordSize)); // Object.
- __ movl(RDX, FieldAddress(RAX, target::String::hash_offset()));
- __ testl(RDX, RDX);
- __ j(NOT_ZERO, &already_set, AssemblerBase::kNearJump);
- __ movq(RDX, Address(RSP, +1 * target::kWordSize)); // Value.
- __ SmiUntag(RDX);
- __ shlq(RDX, Immediate(target::UntaggedObject::kHashTagPos));
- // lock+orq is an atomic read-modify-write.
- __ lock();
- __ orq(FieldAddress(RAX, target::Object::tags_offset()), RDX);
- __ movq(RAX, Address(RSP, +1 * target::kWordSize));
- __ ret();
- __ Bind(&already_set);
- __ movl(RAX, RDX);
+ ASSERT(target::String::hash_offset() == 4);
+
+ __ movq(RBX, Address(RSP, +2 * target::kWordSize)); // Object.
+ __ movq(RCX, Address(RSP, +1 * target::kWordSize)); // Value.
+ __ SmiUntag(RCX);
+ __ MoveRegister(RDX, RCX);
+ __ shlq(RDX, Immediate(32));
+
+ Label retry, success, already_in_rax;
+ __ Bind(&retry);
+ // RAX is used by "cmpxchgq" as comparison value (if comparison succeeds the
+ // store is performed).
+ __ movq(RAX, FieldAddress(RBX, 0));
+ __ TestImmediate(RAX, Immediate(0xffffffff00000000));
+ __ BranchIf(NOT_ZERO, &already_in_rax);
+ __ MoveRegister(RSI, RAX);
+ __ orq(RSI, RDX);
+ __ LockCmpxchgq(FieldAddress(RBX, 0), RSI);
+ __ BranchIf(NOT_ZERO, &retry);
+ // Fall-through with RCX containing new hash value (untagged)
+ __ Bind(&success);
+ __ SmiTag(RCX);
+ __ MoveRegister(RAX, RCX);
+ __ Ret();
+
+ __ Bind(&already_in_rax);
+ __ shrq(RAX, Immediate(32));
__ SmiTag(RAX);
- __ ret();
+ __ Ret();
}
void GenerateSubstringMatchesSpecialization(Assembler* assembler,
diff --git a/runtime/vm/compiler/backend/linearscan.cc b/runtime/vm/compiler/backend/linearscan.cc
index 219cc0d..8e3e457 100644
--- a/runtime/vm/compiler/backend/linearscan.cc
+++ b/runtime/vm/compiler/backend/linearscan.cc
@@ -2239,17 +2239,29 @@
}
}
- TRACE_ALLOC(THR_Print("assigning free register "));
- TRACE_ALLOC(MakeRegisterLocation(candidate).Print());
- TRACE_ALLOC(THR_Print(" to v%" Pd "\n", unallocated->vreg()));
-
if (free_until != kMaxPosition) {
// There was an intersection. Split unallocated.
TRACE_ALLOC(THR_Print(" splitting at %" Pd "\n", free_until));
LiveRange* tail = unallocated->SplitAt(free_until);
AddToUnallocated(tail);
+
+ // If unallocated represents a constant value and does not have
+ // any uses then avoid using a register for it.
+ if (unallocated->first_use() == NULL) {
+ if (unallocated->vreg() >= 0) {
+ LiveRange* parent = GetLiveRange(unallocated->vreg());
+ if (parent->spill_slot().IsConstant()) {
+ Spill(unallocated);
+ return true;
+ }
+ }
+ }
}
+ TRACE_ALLOC(THR_Print(" assigning free register "));
+ TRACE_ALLOC(MakeRegisterLocation(candidate).Print());
+ TRACE_ALLOC(THR_Print(" to v%" Pd "\n", unallocated->vreg()));
+
registers_[candidate]->Add(unallocated);
unallocated->set_assigned_location(MakeRegisterLocation(candidate));
return true;
diff --git a/runtime/vm/compiler/compiler_sources.gni b/runtime/vm/compiler/compiler_sources.gni
index 51b81d1..05fd213 100644
--- a/runtime/vm/compiler/compiler_sources.gni
+++ b/runtime/vm/compiler/compiler_sources.gni
@@ -154,6 +154,7 @@
]
compiler_sources_tests = [
+ "asm_intrinsifier_test.cc",
"assembler/assembler_arm64_test.cc",
"assembler/assembler_arm_test.cc",
"assembler/assembler_ia32_test.cc",
diff --git a/runtime/vm/heap/become.cc b/runtime/vm/heap/become.cc
index 1e733b8..4bb646d 100644
--- a/runtime/vm/heap/become.cc
+++ b/runtime/vm/heap/become.cc
@@ -297,7 +297,7 @@
ForwardObjectTo(before_obj, after_obj);
heap->ForwardWeakEntries(before_obj, after_obj);
#if defined(HASH_IN_OBJECT_HEADER)
- Object::SetCachedHash(after_obj, Object::GetCachedHash(before_obj));
+ Object::SetCachedHashIfNotSet(after_obj, Object::GetCachedHash(before_obj));
#endif
}
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index 616c6bf..51c2b28 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -893,6 +893,17 @@
}
}
+intptr_t Heap::SetWeakEntryIfNonExistent(ObjectPtr raw_obj,
+ WeakSelector sel,
+ intptr_t val) {
+ if (!raw_obj->IsSmiOrOldObject()) {
+ return new_weak_tables_[sel]->SetValueIfNonExistent(raw_obj, val);
+ } else {
+ ASSERT(raw_obj->IsSmiOrOldObject());
+ return old_weak_tables_[sel]->SetValueIfNonExistent(raw_obj, val);
+ }
+}
+
void Heap::ForwardWeakEntries(ObjectPtr before_object, ObjectPtr after_object) {
const auto before_space =
!before_object->IsSmiOrOldObject() ? Heap::kNew : Heap::kOld;
diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h
index 6ec7705..a410336 100644
--- a/runtime/vm/heap/heap.h
+++ b/runtime/vm/heap/heap.h
@@ -211,8 +211,8 @@
#if !defined(HASH_IN_OBJECT_HEADER)
// Associate an identity hashCode with an object. An non-existent hashCode
// is equal to 0.
- void SetHash(ObjectPtr raw_obj, intptr_t hash) {
- SetWeakEntry(raw_obj, kIdentityHashes, hash);
+ intptr_t SetHashIfNotSet(ObjectPtr raw_obj, intptr_t hash) {
+ return SetWeakEntryIfNonExistent(raw_obj, kIdentityHashes, hash);
}
intptr_t GetHash(ObjectPtr raw_obj) const {
return GetWeakEntry(raw_obj, kIdentityHashes);
@@ -251,6 +251,9 @@
// Used by the GC algorithms to propagate weak entries.
intptr_t GetWeakEntry(ObjectPtr raw_obj, WeakSelector sel) const;
void SetWeakEntry(ObjectPtr raw_obj, WeakSelector sel, intptr_t val);
+ intptr_t SetWeakEntryIfNonExistent(ObjectPtr raw_obj,
+ WeakSelector sel,
+ intptr_t val);
WeakTable* GetWeakTable(Space space, WeakSelector selector) const {
if (space == kNew) {
diff --git a/runtime/vm/heap/weak_table.h b/runtime/vm/heap/weak_table.h
index d5ddc2e..3062b53 100644
--- a/runtime/vm/heap/weak_table.h
+++ b/runtime/vm/heap/weak_table.h
@@ -62,6 +62,16 @@
return SetValueExclusive(key, val);
}
+ intptr_t SetValueIfNonExistent(ObjectPtr key, intptr_t val) {
+ MutexLocker ml(&mutex_);
+ const auto old_value = GetValueExclusive(key);
+ if (old_value == kNoValue) {
+ SetValueExclusive(key, val);
+ return val;
+ }
+ return old_value;
+ }
+
// The following "exclusive" methods must only be called from call sites
// which are known to have exclusive access to the weak table.
//
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index 46124a8..2402951 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -224,7 +224,7 @@
}
#if defined(HASH_IN_OBJECT_HEADER)
const uint32_t hash = Object::GetCachedHash(instance.ptr());
- Object::SetCachedHash(result.ptr(), hash);
+ Object::SetCachedHashIfNotSet(result.ptr(), hash);
#endif
// Morph the context from instance to result using mapping_.
diff --git a/runtime/vm/json_test.cc b/runtime/vm/json_test.cc
index 60c679e..ed619c2 100644
--- a/runtime/vm/json_test.cc
+++ b/runtime/vm/json_test.cc
@@ -169,8 +169,12 @@
JSONObject jsobj(&jsarr);
jsobj.AddProperty("object_key", Object::Handle(Object::null()));
}
- char buffer[1024];
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 2048;
+ char buffer[kBufferSize];
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
ElideJSONSubstring("objects", buffer, buffer);
EXPECT_STREQ(
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index abe44ee..cf4a20d 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -1323,7 +1323,7 @@
counter_ += 2011; // The year Dart was announced and a prime.
counter_ &= 0x3fffffff;
if (counter_ == 0) counter_++;
- Object::SetCachedHash(obj, counter_);
+ Object::SetCachedHashIfNotSet(obj, counter_);
}
}
#endif
@@ -1431,7 +1431,7 @@
OneByteStringPtr str = static_cast<OneByteStringPtr>(object);
if (String::GetCachedHash(str) == 0) {
intptr_t hash = String::Hash(str);
- String::SetCachedHash(str, hash);
+ String::SetCachedHashIfNotSet(str, hash);
}
intptr_t size = OneByteString::UnroundedSize(str);
ASSERT(size <= str->untag()->HeapSize());
@@ -1441,7 +1441,7 @@
TwoByteStringPtr str = static_cast<TwoByteStringPtr>(object);
if (String::GetCachedHash(str) == 0) {
intptr_t hash = String::Hash(str);
- String::SetCachedHash(str, hash);
+ String::SetCachedHashIfNotSet(str, hash);
}
ASSERT(String::GetCachedHash(str) != 0);
intptr_t size = TwoByteString::UnroundedSize(str);
@@ -1453,14 +1453,14 @@
static_cast<ExternalOneByteStringPtr>(object);
if (String::GetCachedHash(str) == 0) {
intptr_t hash = String::Hash(str);
- String::SetCachedHash(str, hash);
+ String::SetCachedHashIfNotSet(str, hash);
}
} else if (cid == kExternalTwoByteStringCid) {
ExternalTwoByteStringPtr str =
static_cast<ExternalTwoByteStringPtr>(object);
if (String::GetCachedHash(str) == 0) {
intptr_t hash = String::Hash(str);
- String::SetCachedHash(str, hash);
+ String::SetCachedHashIfNotSet(str, hash);
}
} else if (cid == kCodeSourceMapCid) {
CodeSourceMapPtr map = CodeSourceMap::RawCast(object);
@@ -2592,6 +2592,9 @@
tags = UntaggedObject::OldAndNotMarkedBit::update(is_old, tags);
tags = UntaggedObject::OldAndNotRememberedBit::update(is_old, tags);
tags = UntaggedObject::NewBit::update(!is_old, tags);
+#if defined(HASH_IN_OBJECT_HEADER)
+ tags = UntaggedObject::HashTag::update(0, tags);
+#endif
reinterpret_cast<UntaggedObject*>(address)->tags_ = tags;
}
@@ -23712,7 +23715,9 @@
NoSafepointScope no_safepoint;
result ^= raw;
result.SetLength(len);
- result.SetHash(0);
+#if !defined(HASH_IN_OBJECT_HEADER)
+ result.ptr()->untag()->set_hash(Smi::New(0));
+#endif
}
return TwoByteString::raw(result);
}
@@ -23869,7 +23874,9 @@
NoSafepointScope no_safepoint;
result ^= raw;
result.SetLength(len);
- result.SetHash(0);
+#if !defined(HASH_IN_OBJECT_HEADER)
+ result.ptr()->untag()->set_hash(Smi::New(0));
+#endif
SetExternalData(result, data, peer);
}
AddFinalizer(result, peer, callback, external_allocation_size);
@@ -23899,7 +23906,9 @@
NoSafepointScope no_safepoint;
result ^= raw;
result.SetLength(len);
- result.SetHash(0);
+#if !defined(HASH_IN_OBJECT_HEADER)
+ result.ptr()->untag()->set_hash(Smi::New(0));
+#endif
SetExternalData(result, data, peer);
}
AddFinalizer(result, peer, callback, external_allocation_size);
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index d778a53..fe21539 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -417,8 +417,8 @@
return obj->untag()->GetHeaderHash();
}
- static void SetCachedHash(ObjectPtr obj, uint32_t hash) {
- obj->untag()->SetHeaderHash(hash);
+ static uint32_t SetCachedHashIfNotSet(ObjectPtr obj, uint32_t hash) {
+ return obj->untag()->SetHeaderHashIfNotSet(hash);
}
#endif
@@ -9079,7 +9079,8 @@
return result;
}
result = String::Hash(*this, 0, this->Length());
- SetCachedHash(ptr(), result);
+ uword set_hash = SetCachedHashIfNotSet(ptr(), result);
+ ASSERT(set_hash == result);
return result;
}
@@ -9326,8 +9327,18 @@
return Smi::Value(obj->untag()->hash_);
}
- static void SetCachedHash(StringPtr obj, uint32_t hash) {
+ static uint32_t SetCachedHashIfNotSet(StringPtr obj, uint32_t hash) {
+ ASSERT(Smi::Value(obj->untag()->hash_) == 0 ||
+ Smi::Value(obj->untag()->hash_) == static_cast<intptr_t>(hash));
+ return SetCachedHash(obj, hash);
+ }
+ static uint32_t SetCachedHash(StringPtr obj, uint32_t hash) {
obj->untag()->hash_ = Smi::New(hash);
+ return hash;
+ }
+#else
+ static uint32_t SetCachedHash(StringPtr obj, uint32_t hash) {
+ return Object::SetCachedHashIfNotSet(obj, hash);
}
#endif
@@ -9344,7 +9355,10 @@
untag()->set_length(Smi::New(value));
}
- void SetHash(intptr_t value) const { SetCachedHash(ptr(), value); }
+ void SetHash(intptr_t value) const {
+ const intptr_t hash_set = SetCachedHashIfNotSet(ptr(), value);
+ ASSERT(hash_set == value);
+ }
template <typename HandleType, typename ElementType, typename CallbackType>
static void ReadFromImpl(SnapshotReader* reader,
diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc
index 1ce7b76..9ee1b59 100644
--- a/runtime/vm/object_graph.cc
+++ b/runtime/vm/object_graph.cc
@@ -1346,7 +1346,7 @@
if (hash == 0) {
ASSERT(!thread->heap()->old_space()->IsObjectFromImagePages(obj));
hash = GenerateHash(thread->random());
- Object::SetCachedHash(obj, hash);
+ Object::SetCachedHashIfNotSet(obj, hash);
}
#else
Heap* heap = thread->heap();
@@ -1354,7 +1354,7 @@
if (hash == 0) {
ASSERT(!heap->old_space()->IsObjectFromImagePages(obj));
hash = GenerateHash(thread->random());
- heap->SetHash(obj, hash);
+ heap->SetHashIfNotSet(obj, hash);
}
#endif
return hash;
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 6136da2..7afae88 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -4335,7 +4335,9 @@
}
ISOLATE_UNIT_TEST_CASE(PrintJSONPrimitives) {
- char buffer[1024];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
Isolate* isolate = Isolate::Current();
// Class reference
@@ -4343,7 +4345,9 @@
JSONStream js;
Class& cls = Class::Handle(isolate->group()->object_store()->bool_class());
cls.PrintJSON(&js, true);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
@@ -4366,7 +4370,9 @@
Function::Handle(Resolver::ResolveFunction(Z, cls, func_name));
ASSERT(!func.IsNull());
func.PrintJSON(&js, true);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
"{\"type\":\"@Function\",\"fixedId\":true,\"id\":\"\","
@@ -4392,7 +4398,9 @@
Library& lib =
Library::Handle(isolate->group()->object_store()->core_library());
lib.PrintJSON(&js, true);
- ElideJSONSubstring("libraries", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("libraries", json_str, buffer);
EXPECT_STREQ(
"{\"type\":\"@Library\",\"fixedId\":true,\"id\":\"\","
"\"name\":\"dart.core\",\"uri\":\"dart:core\"}",
@@ -4402,7 +4410,9 @@
{
JSONStream js;
Bool::True().PrintJSON(&js, true);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
"{\"type\":\"@Instance\",\"_vmType\":\"Bool\",\"class\":{\"type\":\"@"
@@ -4421,7 +4431,9 @@
JSONStream js;
const Integer& smi = Integer::Handle(Integer::New(7));
smi.PrintJSON(&js, true);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("_Smi@", buffer, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
@@ -4441,7 +4453,9 @@
JSONStream js;
const Integer& smi = Integer::Handle(Integer::New(Mint::kMinValue));
smi.PrintJSON(&js, true);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("objects", buffer, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
ElideJSONSubstring("_Mint@", buffer, buffer);
@@ -4462,7 +4476,9 @@
JSONStream js;
const Double& dub = Double::Handle(Double::New(0.1234));
dub.PrintJSON(&js, true);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("objects", buffer, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
ElideJSONSubstring("_Double@", buffer, buffer);
@@ -4483,7 +4499,9 @@
JSONStream js;
const String& str = String::Handle(String::New("dw"));
str.PrintJSON(&js, true);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("objects", buffer, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
ElideJSONSubstring("_OneByteString@", buffer, buffer);
@@ -4504,7 +4522,9 @@
JSONStream js;
const Array& array = Array::Handle(Array::New(0));
array.PrintJSON(&js, true);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("objects", buffer, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
ElideJSONSubstring("_List@", buffer, buffer);
@@ -4525,7 +4545,9 @@
const GrowableObjectArray& array =
GrowableObjectArray::Handle(GrowableObjectArray::New());
array.PrintJSON(&js, true);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("objects", buffer, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
ElideJSONSubstring("_GrowableList@", buffer, buffer);
@@ -4547,7 +4569,9 @@
const LinkedHashMap& array =
LinkedHashMap::Handle(LinkedHashMap::NewDefault());
array.PrintJSON(&js, true);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("objects", buffer, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
ElideJSONSubstring("_InternalLinkedHashMap@", buffer, buffer);
@@ -4569,7 +4593,9 @@
JSONStream js;
Instance& tag = Instance::Handle(isolate->default_tag());
tag.PrintJSON(&js, true);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("objects", buffer, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
ElideJSONSubstring("_UserTag@", buffer, buffer);
@@ -4597,7 +4623,9 @@
Instance& type =
Instance::Handle(isolate->group()->object_store()->bool_type());
type.PrintJSON(&js, true);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("objects", buffer, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
ElideJSONSubstring("_Type@", buffer, buffer);
@@ -4628,7 +4656,9 @@
{
JSONStream js;
Object::null_object().PrintJSON(&js, true);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
"{\"type\":\"@Instance\",\"_vmType\":\"null\",\"class\":{\"type\":\"@"
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 9955114..8d3185d 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -318,6 +318,26 @@
}
template <class TagBitField>
+ typename TagBitField::Type UpdateConditional(
+ typename TagBitField::Type value_to_be_set,
+ typename TagBitField::Type conditional_old_value) {
+ T old_tags = tags_.load(std::memory_order_relaxed);
+ while (true) {
+ // This operation is only performed if the condition is met.
+ auto old_value = TagBitField::decode(old_tags);
+ if (old_value != conditional_old_value) {
+ return old_value;
+ }
+ T new_tags = TagBitField::update(value_to_be_set, old_tags);
+ if (tags_.compare_exchange_weak(old_tags, new_tags,
+ std::memory_order_relaxed)) {
+ return value_to_be_set;
+ }
+ // [old_tags] was updated to it's current value.
+ }
+ }
+
+ template <class TagBitField>
bool TryAcquire() {
T mask = TagBitField::encode(true);
T old_tags = tags_.fetch_or(mask, std::memory_order_relaxed);
@@ -426,7 +446,9 @@
#if defined(HASH_IN_OBJECT_HEADER)
uint32_t GetHeaderHash() const { return tags_.Read<HashTag>(); }
- void SetHeaderHash(uint32_t h) { tags_.Update<HashTag>(h); }
+ uint32_t SetHeaderHashIfNotSet(uint32_t h) {
+ return tags_.UpdateConditional<HashTag>(h, /*conditional_old_value=*/0);
+ }
#endif
intptr_t HeapSize() const {
diff --git a/runtime/vm/source_report_test.cc b/runtime/vm/source_report_test.cc
index f7314c0..ad8c574 100644
--- a/runtime/vm/source_report_test.cc
+++ b/runtime/vm/source_report_test.cc
@@ -27,7 +27,9 @@
}
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_NoCalls) {
- char buffer[1024];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
const char* kScript =
"main() {\n"
"}";
@@ -40,7 +42,9 @@
SourceReport report(SourceReport::kCoverage);
JSONStream js;
report.PrintJSON(&js, script);
- ElideJSONSubstring("libraries", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("libraries", json_str, buffer);
EXPECT_STREQ(
"{\"type\":\"SourceReport\",\"ranges\":"
@@ -55,7 +59,9 @@
}
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_SimpleCall) {
- char buffer[1024];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
const char* kScript =
"helper0() {}\n"
"helper1() {}\n"
@@ -76,7 +82,9 @@
SourceReport report(SourceReport::kCoverage);
JSONStream js;
report.PrintJSON(&js, script);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
"{\"type\":\"SourceReport\",\"ranges\":["
@@ -99,7 +107,9 @@
}
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_ForceCompile) {
- char buffer[1024];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
const char* kScript =
"helper0() {}\n"
"helper1() {}\n"
@@ -120,7 +130,9 @@
SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile);
JSONStream js;
report.PrintJSON(&js, script);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
@@ -145,7 +157,9 @@
}
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_UnusedClass_NoForceCompile) {
- char buffer[1024];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
const char* kScript =
"helper0() {}\n"
"class Unused {\n"
@@ -164,7 +178,9 @@
SourceReport report(SourceReport::kCoverage);
JSONStream js;
report.PrintJSON(&js, script);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
"{\"type\":\"SourceReport\",\"ranges\":["
@@ -187,7 +203,9 @@
}
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_UnusedClass_ForceCompile) {
- char buffer[1024];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
const char* kScript =
"helper0() {}\n"
"class Unused {\n"
@@ -206,7 +224,9 @@
SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile);
JSONStream js;
report.PrintJSON(&js, script);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
"{\"type\":\"SourceReport\",\"ranges\":["
@@ -230,7 +250,9 @@
}
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_UnusedClass_ForceCompileError) {
- char buffer[1024];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
const char* kScript =
"helper0() {}\n"
"class Unused {\n"
@@ -249,7 +271,9 @@
SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile);
JSONStream js;
report.PrintJSON(&js, script);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
"{\"type\":\"SourceReport\",\"ranges\":["
@@ -278,7 +302,9 @@
}
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_NestedFunctions) {
- char buffer[1024];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
const char* kScript =
"helper0() {\n"
" nestedHelper0() {}\n"
@@ -303,7 +329,9 @@
SourceReport report(SourceReport::kCoverage);
JSONStream js;
report.PrintJSON(&js, script);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
@@ -334,7 +362,9 @@
}
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_RestrictedRange) {
- char buffer[1024];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
const char* kScript =
"helper0() {\n"
" nestedHelper0() {}\n"
@@ -362,7 +392,9 @@
JSONStream js;
// Restrict the report to only helper0 and it's nested functions.
report.PrintJSON(&js, script, helper.token_pos(), helper.end_token_pos());
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
@@ -464,7 +496,9 @@
}
ISOLATE_UNIT_TEST_CASE(SourceReport_CallSites_SimpleCall) {
- char buffer[1024];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
const char* kScript =
"helper0() {}\n"
"helper1() {}\n"
@@ -481,7 +515,9 @@
SourceReport report(SourceReport::kCallSites);
JSONStream js;
report.PrintJSON(&js, script);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
"{\"type\":\"SourceReport\",\"ranges\":["
@@ -513,7 +549,9 @@
}
ISOLATE_UNIT_TEST_CASE(SourceReport_CallSites_PolymorphicCall) {
- char buffer[4096];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 4096;
+ char buffer[kBufferSize];
const char* kScript =
"class Common {\n"
" func() {}\n"
@@ -543,7 +581,9 @@
SourceReport report(SourceReport::kCallSites);
JSONStream js;
report.PrintJSON(&js, script, helper.token_pos(), helper.end_token_pos());
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
"{\"type\":\"SourceReport\",\"ranges\":["
@@ -625,7 +665,9 @@
}
ISOLATE_UNIT_TEST_CASE(SourceReport_MultipleReports) {
- char buffer[2048];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 2048;
+ char buffer[kBufferSize];
const char* kScript =
"helper0() {}\n"
"helper1() {}\n"
@@ -642,7 +684,9 @@
SourceReport report(SourceReport::kCallSites | SourceReport::kCoverage);
JSONStream js;
report.PrintJSON(&js, script);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
"{\"type\":\"SourceReport\",\"ranges\":["
@@ -675,7 +719,9 @@
}
ISOLATE_UNIT_TEST_CASE(SourceReport_PossibleBreakpoints_Simple) {
- char buffer[1024];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
const char* kScript =
"helper0() {}\n"
"helper1() {}\n"
@@ -696,7 +742,9 @@
SourceReport report(SourceReport::kPossibleBreakpoints);
JSONStream js;
report.PrintJSON(&js, script);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
"{\"type\":\"SourceReport\",\"ranges\":["
@@ -719,7 +767,9 @@
}
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_Issue35453_NoSuchMethod) {
- char buffer[1024];
+ // WARNING: This MUST be big enough for the serialised JSON string.
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
const char* kScript =
"class Foo {\n"
" void bar() {}\n"
@@ -740,7 +790,9 @@
SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile);
JSONStream js;
report.PrintJSON(&js, script);
- ElideJSONSubstring("classes", js.ToCString(), buffer);
+ const char* json_str = js.ToCString();
+ ASSERT(strlen(json_str) < kBufferSize);
+ ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
EXPECT_STREQ(
"{\"type\":\"SourceReport\",\"ranges\":["
diff --git a/runtime/vm/unit_test.h b/runtime/vm/unit_test.h
index aa82e8d..025f8cd 100644
--- a/runtime/vm/unit_test.h
+++ b/runtime/vm/unit_test.h
@@ -690,6 +690,7 @@
//
// out = "\"id\":\"\""
//
+// WARNING: This function is not safe to use if `in` is bigger than `out`!
void ElideJSONSubstring(const char* prefix, const char* in, char* out);
template <typename T>
diff --git a/sdk/lib/_http/http_headers.dart b/sdk/lib/_http/http_headers.dart
index 7d87b88..77b9c22 100644
--- a/sdk/lib/_http/http_headers.dart
+++ b/sdk/lib/_http/http_headers.dart
@@ -503,8 +503,19 @@
_mutable = false;
}
- void _build(BytesBuilder builder) {
+ void _build(BytesBuilder builder, {bool skipZeroContentLength = false}) {
+ // per https://tools.ietf.org/html/rfc7230#section-3.3.2
+ // A user agent SHOULD NOT send a
+ // Content-Length header field when the request message does not
+ // contain a payload body and the method semantics do not anticipate
+ // such a body.
+ String? ignoreHeader = _contentLength == 0 && skipZeroContentLength
+ ? HttpHeaders.contentLengthHeader
+ : null;
_headers.forEach((String name, List<String> values) {
+ if (ignoreHeader == name) {
+ return;
+ }
String originalName = _originalHeaderName(name);
bool fold = _foldHeader(name);
var nameData = originalName.codeUnits;
diff --git a/sdk/lib/_http/http_impl.dart b/sdk/lib/_http/http_impl.dart
index b61f273..6c7b768 100644
--- a/sdk/lib/_http/http_impl.dart
+++ b/sdk/lib/_http/http_impl.dart
@@ -1583,7 +1583,11 @@
headers._finalize();
// Write headers.
- headers._build(buffer);
+ headers._build(buffer,
+ skipZeroContentLength: method == "CONNECT" ||
+ method == "DELETE" ||
+ method == "GET" ||
+ method == "HEAD");
buffer.addByte(_CharCode.CR);
buffer.addByte(_CharCode.LF);
Uint8List headerBytes = buffer.takeBytes();
diff --git a/tests/standalone/io/http_content_length_test.dart b/tests/standalone/io/http_content_length_test.dart
index 15d6520..12e1e99 100644
--- a/tests/standalone/io/http_content_length_test.dart
+++ b/tests/standalone/io/http_content_length_test.dart
@@ -16,8 +16,8 @@
int count = 0;
HttpServer.bind("127.0.0.1", 0, backlog: totalConnections).then((server) {
server.listen((HttpRequest request) {
- Expect.equals("0", request.headers.value('content-length'));
- Expect.equals(0, request.contentLength);
+ Expect.equals(null, request.headers.value('content-length'));
+ Expect.equals(-1, request.contentLength);
var response = request.response;
response.contentLength = 0;
response.done.then((_) {
diff --git a/tests/standalone/io/http_detach_socket_test.dart b/tests/standalone/io/http_detach_socket_test.dart
index fdbc257..480c88d 100644
--- a/tests/standalone/io/http_detach_socket_test.dart
+++ b/tests/standalone/io/http_detach_socket_test.dart
@@ -110,14 +110,13 @@
socket.listen((data) => body.write(new String.fromCharCodes(data)),
onDone: () {
List<String> lines = body.toString().split("\r\n");
- Expect.equals(6, lines.length);
+ Expect.equals(5, lines.length);
Expect.equals("GET / HTTP/1.1", lines[0]);
- Expect.equals("", lines[4]);
- Expect.equals("Some data", lines[5]);
- lines.sort(); // Lines 1-3 becomes 3-5 in a fixed order.
+ Expect.equals("", lines[3]);
+ Expect.equals("Some data", lines[4]);
+ lines.sort(); // Lines 1-2 becomes 3-4 in a fixed order.
Expect.equals("accept-encoding: gzip", lines[3]);
- Expect.equals("content-length: 0", lines[4]);
- Expect.equals("host: 127.0.0.1:${port}", lines[5]);
+ Expect.equals("host: 127.0.0.1:${port}", lines[4]);
socket.close();
});
server.close();
diff --git a/tests/standalone_2/io/http_content_length_test.dart b/tests/standalone_2/io/http_content_length_test.dart
index 7b81398..708a7c1 100644
--- a/tests/standalone_2/io/http_content_length_test.dart
+++ b/tests/standalone_2/io/http_content_length_test.dart
@@ -18,8 +18,8 @@
int count = 0;
HttpServer.bind("127.0.0.1", 0, backlog: totalConnections).then((server) {
server.listen((HttpRequest request) {
- Expect.equals("0", request.headers.value('content-length'));
- Expect.equals(0, request.contentLength);
+ Expect.equals(null, request.headers.value('content-length'));
+ Expect.equals(-1, request.contentLength);
var response = request.response;
response.contentLength = 0;
response.done.then((_) {
diff --git a/tests/standalone_2/io/http_detach_socket_test.dart b/tests/standalone_2/io/http_detach_socket_test.dart
index bb33690..1613ace 100644
--- a/tests/standalone_2/io/http_detach_socket_test.dart
+++ b/tests/standalone_2/io/http_detach_socket_test.dart
@@ -112,14 +112,13 @@
socket.listen((data) => body.write(new String.fromCharCodes(data)),
onDone: () {
List<String> lines = body.toString().split("\r\n");
- Expect.equals(6, lines.length);
+ Expect.equals(5, lines.length);
Expect.equals("GET / HTTP/1.1", lines[0]);
- Expect.equals("", lines[4]);
- Expect.equals("Some data", lines[5]);
- lines.sort(); // Lines 1-3 becomes 3-5 in a fixed order.
+ Expect.equals("", lines[3]);
+ Expect.equals("Some data", lines[4]);
+ lines.sort(); // Lines 1-2 becomes 3-4 in a fixed order.
Expect.equals("accept-encoding: gzip", lines[3]);
- Expect.equals("content-length: 0", lines[4]);
- Expect.equals("host: 127.0.0.1:${port}", lines[5]);
+ Expect.equals("host: 127.0.0.1:${port}", lines[4]);
socket.close();
});
server.close();
diff --git a/tools/VERSION b/tools/VERSION
index 888f7d0..93fc29b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 180
+PRERELEASE 181
PRERELEASE_PATCH 0
\ No newline at end of file