From 549443a7cc600e93cc1d1d1630108e7d0a035500 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 17 Jan 2012 16:20:37 +0100 Subject: [PATCH 01/30] typed arrays: set class name Make obj.toString and Object.prototype.toString work correctly for typed arrays. --- src/v8_typed_array.cc | 15 +++++++ test/simple/test-typed-arrays-typenames.js | 48 ++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 test/simple/test-typed-arrays-typenames.js diff --git a/src/v8_typed_array.cc b/src/v8_typed_array.cc index 00eff8705db..4b80da0c871 100644 --- a/src/v8_typed_array.cc +++ b/src/v8_typed_array.cc @@ -140,6 +140,7 @@ class TypedArray { v8::HandleScope scope; ft_cache = v8::Persistent::New( v8::FunctionTemplate::New(&TypedArray::V8New)); + ft_cache->SetClassName(v8::String::New(TypeName())); v8::Local instance = ft_cache->InstanceTemplate(); instance->SetInternalFieldCount(0); @@ -376,6 +377,20 @@ class TypedArray { return TypedArray::GetTemplate()-> GetFunction()->NewInstance(3, argv); } + + static const char* TypeName() { + switch (TEAType) { + case v8::kExternalByteArray: return "Int8Array"; + case v8::kExternalUnsignedByteArray: return "Uint8Array"; + case v8::kExternalShortArray: return "Int16Array"; + case v8::kExternalUnsignedShortArray: return "Uint16Array"; + case v8::kExternalIntArray: return "Int32Array"; + case v8::kExternalUnsignedIntArray: return "Uint32Array"; + case v8::kExternalFloatArray: return "Float32Array"; + case v8::kExternalDoubleArray: return "Float64Array"; + } + abort(); + } }; class Int8Array : public TypedArray<1, v8::kExternalByteArray> { }; diff --git a/test/simple/test-typed-arrays-typenames.js b/test/simple/test-typed-arrays-typenames.js new file mode 100644 index 00000000000..a78c7598acb --- /dev/null +++ b/test/simple/test-typed-arrays-typenames.js @@ -0,0 +1,48 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +// TODO: merge with test-typed-arrays.js some time in the future. +// That file only exists in master right now. +[ + 'ArrayBuffer', + 'Int8Array', + 'Uint8Array', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array' +].forEach(function(name) { + var expected = '[object ' + name + ']'; + var clazz = global[name]; + var obj = new clazz(1); + + assert.equal(obj.toString(), expected); + assert.equal(Object.prototype.toString.call(obj), expected); + + obj = new DataView(obj); + assert.equal(obj.toString(), '[object DataView]'); + assert.equal(Object.prototype.toString.call(obj), '[object DataView]'); +}); From 534df2f8d207877f9d09d172d4ba92406e351aa0 Mon Sep 17 00:00:00 2001 From: koichik Date: Tue, 17 Jan 2012 17:34:53 +0900 Subject: [PATCH 02/30] tls: fix double 'error' events on HTTPS Requests Fixes #2549. --- lib/tls.js | 21 +++++++------ test/simple/test-tls-client-abort2.js | 45 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 test/simple/test-tls-client-abort2.js diff --git a/lib/tls.js b/lib/tls.js index b77dec43d18..0dd17823509 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -271,7 +271,11 @@ CryptoStream.prototype._done = function() { this.pair.encrypted._doneFlag && !this.pair._doneFlag) { // If both streams are done: - this.pair.destroy(); + if (!this.pair._secureEstablished) { + this.pair.error(); + } else { + this.pair.destroy(); + } } }; @@ -720,7 +724,6 @@ SecurePair.prototype.maybeInitFinished = function() { SecurePair.prototype.destroy = function() { var self = this; - var error = this.ssl.error; if (!this._doneFlag) { this._doneFlag = true; @@ -736,21 +739,19 @@ SecurePair.prototype.destroy = function() { self.encrypted.emit('close'); self.cleartext.emit('close'); }); - - if (!this._secureEstablished) { - if (!error) { - error = new Error('socket hang up'); - error.code = 'ECONNRESET'; - } - this.emit('error', error); - } } }; SecurePair.prototype.error = function() { if (!this._secureEstablished) { + var error = this.ssl.error; + if (!error) { + error = new Error('socket hang up'); + error.code = 'ECONNRESET'; + } this.destroy(); + this.emit('error', error); } else { var err = this.ssl.error; this.ssl.error = null; diff --git a/test/simple/test-tls-client-abort2.js b/test/simple/test-tls-client-abort2.js new file mode 100644 index 00000000000..f8119627687 --- /dev/null +++ b/test/simple/test-tls-client-abort2.js @@ -0,0 +1,45 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +if (!process.versions.openssl) { + console.error('Skipping because node compiled without OpenSSL.'); + process.exit(0); +} + +var common = require('../common'); +var assert = require('assert'); +var tls = require('tls'); + +var errors = 0; + +var conn = tls.connect(common.PORT, function() { + assert(false); // callback should never be executed +}); +conn.on('error', function() { + ++errors; + assert.doesNotThrow(function() { + conn.destroy(); + }); +}); + +process.on('exit', function() { + assert.equal(errors, 1); +}); From 16953329413831b32f4c3b2255aeacec874ed69d Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Tue, 17 Jan 2012 22:20:28 +0600 Subject: [PATCH 03/30] Land number collision fix for v8 3.6 by Erik Corry - If V8 snapshots are enabled then the hash is only randomized at build time. --- backport @10366, @10367 and @10402 to 3.6 Add seed to hash of numeric keyed properties. Minor cleanups of numeric seeded hashing patch. Split NumberDictionary into a randomly seeded and an unseeded version. We don't want to randomize the stub cache. Review URL: http://codereview.chromium.org/9190001/ --- deps/v8/src/arm/code-stubs-arm.cc | 2 +- deps/v8/src/arm/macro-assembler-arm.cc | 58 +++--- deps/v8/src/arm/macro-assembler-arm.h | 1 + deps/v8/src/code-stubs.cc | 8 +- deps/v8/src/debug.h | 4 +- deps/v8/src/elements.cc | 15 +- deps/v8/src/factory.cc | 32 ++- deps/v8/src/factory.h | 15 +- deps/v8/src/flag-definitions.h | 8 +- deps/v8/src/frames.cc | 3 +- deps/v8/src/handles.cc | 10 +- deps/v8/src/handles.h | 6 +- deps/v8/src/heap.cc | 20 +- deps/v8/src/heap.h | 16 +- deps/v8/src/ia32/code-stubs-ia32.cc | 4 +- deps/v8/src/ia32/macro-assembler-ia32.cc | 82 +++++--- deps/v8/src/ia32/macro-assembler-ia32.h | 1 + deps/v8/src/mips/code-stubs-mips.cc | 2 +- deps/v8/src/mips/macro-assembler-mips.cc | 78 ++++---- deps/v8/src/mips/macro-assembler-mips.h | 1 + deps/v8/src/objects-debug.cc | 2 +- deps/v8/src/objects-inl.h | 35 ++-- deps/v8/src/objects.cc | 242 ++++++++++++++--------- deps/v8/src/objects.h | 107 ++++++++-- deps/v8/src/profile-generator.cc | 30 +-- deps/v8/src/profile-generator.h | 12 +- deps/v8/src/runtime.cc | 48 ++--- deps/v8/src/stub-cache.cc | 5 +- deps/v8/src/type-info.cc | 6 +- deps/v8/src/type-info.h | 2 +- deps/v8/src/utils.h | 8 +- deps/v8/src/v8globals.h | 3 +- deps/v8/src/x64/code-stubs-x64.cc | 2 +- deps/v8/src/x64/macro-assembler-x64.cc | 75 ++++--- deps/v8/src/x64/macro-assembler-x64.h | 1 + deps/v8/test/cctest/test-hashing.cc | 88 +++++++++ 36 files changed, 684 insertions(+), 348 deletions(-) diff --git a/deps/v8/src/arm/code-stubs-arm.cc b/deps/v8/src/arm/code-stubs-arm.cc index f07f20f0327..a59b6fd6333 100644 --- a/deps/v8/src/arm/code-stubs-arm.cc +++ b/deps/v8/src/arm/code-stubs-arm.cc @@ -5363,7 +5363,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, Register hash, Register character) { // hash = character + (character << 10); - __ LoadRoot(hash, Heap::kStringHashSeedRootIndex); + __ LoadRoot(hash, Heap::kHashSeedRootIndex); // Untag smi seed and add the character. __ add(hash, character, Operand(hash, LSR, kSmiTagSize)); // hash += hash << 10; diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index 2f68eb9b63d..7a1f8024508 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -1337,6 +1337,34 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, } +void MacroAssembler::GetNumberHash(Register t0, Register scratch) { + // First of all we assign the hash seed to scratch. + LoadRoot(scratch, Heap::kHashSeedRootIndex); + SmiUntag(scratch); + + // Xor original key with a seed. + eor(t0, t0, Operand(scratch)); + + // Compute the hash code from the untagged key. This must be kept in sync + // with ComputeIntegerHash in utils.h. + // + // hash = ~hash + (hash << 15); + mvn(scratch, Operand(t0)); + add(t0, scratch, Operand(t0, LSL, 15)); + // hash = hash ^ (hash >> 12); + eor(t0, t0, Operand(t0, LSR, 12)); + // hash = hash + (hash << 2); + add(t0, t0, Operand(t0, LSL, 2)); + // hash = hash ^ (hash >> 4); + eor(t0, t0, Operand(t0, LSR, 4)); + // hash = hash * 2057; + mov(scratch, Operand(2057)); + mul(t0, t0, scratch); + // hash = hash ^ (hash >> 16); + eor(t0, t0, Operand(t0, LSR, 16)); +} + + void MacroAssembler::LoadFromNumberDictionary(Label* miss, Register elements, Register key, @@ -1366,26 +1394,10 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, // t2 - used for the index into the dictionary. Label done; - // Compute the hash code from the untagged key. This must be kept in sync - // with ComputeIntegerHash in utils.h. - // - // hash = ~hash + (hash << 15); - mvn(t1, Operand(t0)); - add(t0, t1, Operand(t0, LSL, 15)); - // hash = hash ^ (hash >> 12); - eor(t0, t0, Operand(t0, LSR, 12)); - // hash = hash + (hash << 2); - add(t0, t0, Operand(t0, LSL, 2)); - // hash = hash ^ (hash >> 4); - eor(t0, t0, Operand(t0, LSR, 4)); - // hash = hash * 2057; - mov(t1, Operand(2057)); - mul(t0, t0, t1); - // hash = hash ^ (hash >> 16); - eor(t0, t0, Operand(t0, LSR, 16)); + GetNumberHash(t0, t1); // Compute the capacity mask. - ldr(t1, FieldMemOperand(elements, NumberDictionary::kCapacityOffset)); + ldr(t1, FieldMemOperand(elements, SeededNumberDictionary::kCapacityOffset)); mov(t1, Operand(t1, ASR, kSmiTagSize)); // convert smi to int sub(t1, t1, Operand(1)); @@ -1396,17 +1408,17 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, mov(t2, t0); // Compute the masked index: (hash + i + i * i) & mask. if (i > 0) { - add(t2, t2, Operand(NumberDictionary::GetProbeOffset(i))); + add(t2, t2, Operand(SeededNumberDictionary::GetProbeOffset(i))); } and_(t2, t2, Operand(t1)); // Scale the index by multiplying by the element size. - ASSERT(NumberDictionary::kEntrySize == 3); + ASSERT(SeededNumberDictionary::kEntrySize == 3); add(t2, t2, Operand(t2, LSL, 1)); // t2 = t2 * 3 // Check if the key is identical to the name. add(t2, elements, Operand(t2, LSL, kPointerSizeLog2)); - ldr(ip, FieldMemOperand(t2, NumberDictionary::kElementsStartOffset)); + ldr(ip, FieldMemOperand(t2, SeededNumberDictionary::kElementsStartOffset)); cmp(key, Operand(ip)); if (i != kProbes - 1) { b(eq, &done); @@ -1419,14 +1431,14 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, // Check that the value is a normal property. // t2: elements + (index * kPointerSize) const int kDetailsOffset = - NumberDictionary::kElementsStartOffset + 2 * kPointerSize; + SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize; ldr(t1, FieldMemOperand(t2, kDetailsOffset)); tst(t1, Operand(Smi::FromInt(PropertyDetails::TypeField::kMask))); b(ne, miss); // Get the value at the masked, scaled index and return. const int kValueOffset = - NumberDictionary::kElementsStartOffset + kPointerSize; + SeededNumberDictionary::kElementsStartOffset + kPointerSize; ldr(result, FieldMemOperand(t2, kValueOffset)); } diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h index 4f695fff208..0546e6a15e3 100644 --- a/deps/v8/src/arm/macro-assembler-arm.h +++ b/deps/v8/src/arm/macro-assembler-arm.h @@ -441,6 +441,7 @@ class MacroAssembler: public Assembler { Register scratch, Label* miss); + void GetNumberHash(Register t0, Register scratch); void LoadFromNumberDictionary(Label* miss, Register elements, diff --git a/deps/v8/src/code-stubs.cc b/deps/v8/src/code-stubs.cc index 00da4cba623..724445e849d 100644 --- a/deps/v8/src/code-stubs.cc +++ b/deps/v8/src/code-stubs.cc @@ -40,7 +40,7 @@ namespace internal { bool CodeStub::FindCodeInCache(Code** code_out) { Heap* heap = Isolate::Current()->heap(); int index = heap->code_stubs()->FindEntry(GetKey()); - if (index != NumberDictionary::kNotFound) { + if (index != UnseededNumberDictionary::kNotFound) { *code_out = Code::cast(heap->code_stubs()->ValueAt(index)); return true; } @@ -121,9 +121,9 @@ Handle CodeStub::GetCode() { FinishCode(*new_object); // Update the dictionary and the root in Heap. - Handle dict = + Handle dict = factory->DictionaryAtNumberPut( - Handle(heap->code_stubs()), + Handle(heap->code_stubs()), GetKey(), new_object); heap->public_set_code_stubs(*dict); @@ -165,7 +165,7 @@ MaybeObject* CodeStub::TryGetCode() { MaybeObject* maybe_new_object = heap->code_stubs()->AtNumberPut(GetKey(), code); if (maybe_new_object->ToObject(&new_object)) { - heap->public_set_code_stubs(NumberDictionary::cast(new_object)); + heap->public_set_code_stubs(UnseededNumberDictionary::cast(new_object)); } } diff --git a/deps/v8/src/debug.h b/deps/v8/src/debug.h index a098040c0df..a5083eb4fe7 100644 --- a/deps/v8/src/debug.h +++ b/deps/v8/src/debug.h @@ -178,7 +178,9 @@ class ScriptCache : private HashMap { private: // Calculate the hash value from the key (script id). - static uint32_t Hash(int key) { return ComputeIntegerHash(key); } + static uint32_t Hash(int key) { + return ComputeIntegerHash(key, v8::internal::kZeroHashSeed); + } // Scripts match if their keys (script id) match. static bool ScriptMatch(void* key1, void* key2) { return key1 == key2; } diff --git a/deps/v8/src/elements.cc b/deps/v8/src/elements.cc index e4ecfe8dd63..04546446174 100644 --- a/deps/v8/src/elements.cc +++ b/deps/v8/src/elements.cc @@ -392,7 +392,7 @@ class PixelElementsAccessor class DictionaryElementsAccessor : public ElementsAccessorBase { + SeededNumberDictionary> { public: static MaybeObject* DeleteCommon(JSObject* obj, uint32_t key, @@ -405,9 +405,10 @@ class DictionaryElementsAccessor if (is_arguments) { backing_store = FixedArray::cast(backing_store->get(1)); } - NumberDictionary* dictionary = NumberDictionary::cast(backing_store); + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(backing_store); int entry = dictionary->FindEntry(key); - if (entry != NumberDictionary::kNotFound) { + if (entry != SeededNumberDictionary::kNotFound) { Object* result = dictionary->DeleteProperty(entry, mode); if (result == heap->true_value()) { MaybeObject* maybe_elements = dictionary->Shrink(key); @@ -440,7 +441,7 @@ class DictionaryElementsAccessor protected: friend class ElementsAccessorBase; + SeededNumberDictionary>; virtual MaybeObject* Delete(JSObject* obj, uint32_t key, @@ -448,12 +449,12 @@ class DictionaryElementsAccessor return DeleteCommon(obj, key, mode); } - static MaybeObject* Get(NumberDictionary* backing_store, + static MaybeObject* Get(SeededNumberDictionary* backing_store, uint32_t key, JSObject* obj, Object* receiver) { int entry = backing_store->FindEntry(key); - if (entry != NumberDictionary::kNotFound) { + if (entry != SeededNumberDictionary::kNotFound) { Object* element = backing_store->ValueAt(entry); PropertyDetails details = backing_store->DetailsAt(entry); if (details.type() == CALLBACKS) { @@ -468,7 +469,7 @@ class DictionaryElementsAccessor return obj->GetHeap()->the_hole_value(); } - static uint32_t GetKeyForIndex(NumberDictionary* dict, + static uint32_t GetKeyForIndex(SeededNumberDictionary* dict, uint32_t index) { Object* key = dict->KeyAt(index); return Smi::cast(key)->value(); diff --git a/deps/v8/src/factory.cc b/deps/v8/src/factory.cc index 97289266e37..971f9f9cee1 100644 --- a/deps/v8/src/factory.cc +++ b/deps/v8/src/factory.cc @@ -77,11 +77,21 @@ Handle Factory::NewStringDictionary(int at_least_space_for) { } -Handle Factory::NewNumberDictionary(int at_least_space_for) { +Handle Factory::NewSeededNumberDictionary( + int at_least_space_for) { ASSERT(0 <= at_least_space_for); CALL_HEAP_FUNCTION(isolate(), - NumberDictionary::Allocate(at_least_space_for), - NumberDictionary); + SeededNumberDictionary::Allocate(at_least_space_for), + SeededNumberDictionary); +} + + +Handle Factory::NewUnseededNumberDictionary( + int at_least_space_for) { + ASSERT(0 <= at_least_space_for); + CALL_HEAP_FUNCTION(isolate(), + UnseededNumberDictionary::Allocate(at_least_space_for), + UnseededNumberDictionary); } @@ -990,13 +1000,23 @@ Handle Factory::NumberToString(Handle number) { } -Handle Factory::DictionaryAtNumberPut( - Handle dictionary, +Handle Factory::DictionaryAtNumberPut( + Handle dictionary, uint32_t key, Handle value) { CALL_HEAP_FUNCTION(isolate(), dictionary->AtNumberPut(key, *value), - NumberDictionary); + SeededNumberDictionary); +} + + +Handle Factory::DictionaryAtNumberPut( + Handle dictionary, + uint32_t key, + Handle value) { + CALL_HEAP_FUNCTION(isolate(), + dictionary->AtNumberPut(key, *value), + UnseededNumberDictionary); } diff --git a/deps/v8/src/factory.h b/deps/v8/src/factory.h index 71ae750b38b..c9817fefecb 100644 --- a/deps/v8/src/factory.h +++ b/deps/v8/src/factory.h @@ -54,7 +54,11 @@ class Factory { int size, PretenureFlag pretenure = NOT_TENURED); - Handle NewNumberDictionary(int at_least_space_for); + Handle NewSeededNumberDictionary( + int at_least_space_for); + + Handle NewUnseededNumberDictionary( + int at_least_space_for); Handle NewStringDictionary(int at_least_space_for); @@ -412,8 +416,13 @@ class Factory { Handle stack_trace, Handle stack_frames); - Handle DictionaryAtNumberPut( - Handle, + Handle DictionaryAtNumberPut( + Handle, + uint32_t key, + Handle value); + + Handle DictionaryAtNumberPut( + Handle, uint32_t key, Handle value); diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index 53cc4856f33..e8f6349e0e4 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -319,13 +319,13 @@ DEFINE_bool(trace_exception, false, "print stack trace when throwing exceptions") DEFINE_bool(preallocate_message_memory, false, "preallocate some memory to build stack traces.") -DEFINE_bool(randomize_string_hashes, +DEFINE_bool(randomize_hashes, true, - "randomize string hashes to avoid predictable hash collisions " + "randomize hashes to avoid predictable hash collisions " "(with snapshots this option cannot override the baked-in seed)") -DEFINE_int(string_hash_seed, +DEFINE_int(hash_seed, 0, - "Fixed seed to use to string hashing (0 means random)" + "Fixed seed to use to hash property keys (0 means random)" "(with snapshots this option cannot override the baked-in seed)") // v8.cc diff --git a/deps/v8/src/frames.cc b/deps/v8/src/frames.cc index bebd10a8060..60b1aadfcae 100644 --- a/deps/v8/src/frames.cc +++ b/deps/v8/src/frames.cc @@ -1187,7 +1187,8 @@ PcToCodeCache::PcToCodeCacheEntry* PcToCodeCache::GetCacheEntry(Address pc) { isolate_->counters()->pc_to_code()->Increment(); ASSERT(IsPowerOf2(kPcToCodeCacheSize)); uint32_t hash = ComputeIntegerHash( - static_cast(reinterpret_cast(pc))); + static_cast(reinterpret_cast(pc)), + v8::internal::kZeroHashSeed); uint32_t index = hash & (kPcToCodeCacheSize - 1); PcToCodeCacheEntry* entry = cache(index); if (entry->pc == pc) { diff --git a/deps/v8/src/handles.cc b/deps/v8/src/handles.cc index 35c363c10c9..c482fa6f688 100644 --- a/deps/v8/src/handles.cc +++ b/deps/v8/src/handles.cc @@ -214,10 +214,10 @@ void NormalizeProperties(Handle object, } -Handle NormalizeElements(Handle object) { +Handle NormalizeElements(Handle object) { CALL_HEAP_FUNCTION(object->GetIsolate(), object->NormalizeElements(), - NumberDictionary); + SeededNumberDictionary); } @@ -229,14 +229,14 @@ void TransformToFastProperties(Handle object, } -Handle NumberDictionarySet( - Handle dictionary, +Handle SeededNumberDictionarySet( + Handle dictionary, uint32_t index, Handle value, PropertyDetails details) { CALL_HEAP_FUNCTION(dictionary->GetIsolate(), dictionary->Set(index, *value, details), - NumberDictionary); + SeededNumberDictionary); } diff --git a/deps/v8/src/handles.h b/deps/v8/src/handles.h index 7eaf4de9273..5674120643c 100644 --- a/deps/v8/src/handles.h +++ b/deps/v8/src/handles.h @@ -170,11 +170,11 @@ class HandleScope { void NormalizeProperties(Handle object, PropertyNormalizationMode mode, int expected_additional_properties); -Handle NormalizeElements(Handle object); +Handle NormalizeElements(Handle object); void TransformToFastProperties(Handle object, int unused_property_fields); -MUST_USE_RESULT Handle NumberDictionarySet( - Handle dictionary, +MUST_USE_RESULT Handle SeededNumberDictionarySet( + Handle dictionary, uint32_t index, Handle value, PropertyDetails details); diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index 6815c2da479..c91f7691b03 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -2181,17 +2181,17 @@ bool Heap::CreateInitialObjects() { // Allocate the code_stubs dictionary. The initial size is set to avoid // expanding the dictionary during bootstrapping. - { MaybeObject* maybe_obj = NumberDictionary::Allocate(128); + { MaybeObject* maybe_obj = UnseededNumberDictionary::Allocate(128); if (!maybe_obj->ToObject(&obj)) return false; } - set_code_stubs(NumberDictionary::cast(obj)); + set_code_stubs(UnseededNumberDictionary::cast(obj)); // Allocate the non_monomorphic_cache used in stub-cache.cc. The initial size // is set to avoid expanding the dictionary during bootstrapping. - { MaybeObject* maybe_obj = NumberDictionary::Allocate(64); + { MaybeObject* maybe_obj = UnseededNumberDictionary::Allocate(64); if (!maybe_obj->ToObject(&obj)) return false; } - set_non_monomorphic_cache(NumberDictionary::cast(obj)); + set_non_monomorphic_cache(UnseededNumberDictionary::cast(obj)); { MaybeObject* maybe_obj = AllocatePolymorphicCodeCache(); if (!maybe_obj->ToObject(&obj)) return false; @@ -5362,14 +5362,14 @@ bool Heap::Setup(bool create_heap_objects) { if (lo_space_ == NULL) return false; if (!lo_space_->Setup()) return false; - // Set up the seed that is used to randomize the string hash function. - ASSERT(string_hash_seed() == 0); - if (FLAG_randomize_string_hashes) { - if (FLAG_string_hash_seed == 0) { - set_string_hash_seed( + // Setup the seed that is used to randomize the string hash function. + ASSERT(hash_seed() == 0); + if (FLAG_randomize_hashes) { + if (FLAG_hash_seed == 0) { + set_hash_seed( Smi::FromInt(V8::RandomPrivate(isolate()) & 0x3fffffff)); } else { - set_string_hash_seed(Smi::FromInt(FLAG_string_hash_seed)); + set_hash_seed(Smi::FromInt(FLAG_hash_seed)); } } diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index 26d8722e219..b1948a933c4 100644 --- a/deps/v8/src/heap.h +++ b/deps/v8/src/heap.h @@ -79,7 +79,7 @@ inline Heap* _inline_get_heap_(); V(FixedArray, single_character_string_cache, SingleCharacterStringCache) \ V(FixedArray, string_split_cache, StringSplitCache) \ V(Object, termination_exception, TerminationException) \ - V(Smi, string_hash_seed, StringHashSeed) \ + V(Smi, hash_seed, HashSeed) \ V(FixedArray, empty_fixed_array, EmptyFixedArray) \ V(ByteArray, empty_byte_array, EmptyByteArray) \ V(FixedDoubleArray, empty_fixed_double_array, EmptyFixedDoubleArray) \ @@ -128,8 +128,8 @@ inline Heap* _inline_get_heap_(); V(Map, neander_map, NeanderMap) \ V(JSObject, message_listeners, MessageListeners) \ V(Foreign, prototype_accessors, PrototypeAccessors) \ - V(NumberDictionary, code_stubs, CodeStubs) \ - V(NumberDictionary, non_monomorphic_cache, NonMonomorphicCache) \ + V(UnseededNumberDictionary, code_stubs, CodeStubs) \ + V(UnseededNumberDictionary, non_monomorphic_cache, NonMonomorphicCache) \ V(PolymorphicCodeCache, polymorphic_code_cache, PolymorphicCodeCache) \ V(Code, js_entry_code, JsEntryCode) \ V(Code, js_construct_entry_code, JsConstructEntryCode) \ @@ -1037,7 +1037,7 @@ class Heap { inline AllocationSpace TargetSpaceId(InstanceType type); // Sets the stub_cache_ (only used when expanding the dictionary). - void public_set_code_stubs(NumberDictionary* value) { + void public_set_code_stubs(UnseededNumberDictionary* value) { roots_[kCodeStubsRootIndex] = value; } @@ -1049,7 +1049,7 @@ class Heap { } // Sets the non_monomorphic_cache_ (only used when expanding the dictionary). - void public_set_non_monomorphic_cache(NumberDictionary* value) { + void public_set_non_monomorphic_cache(UnseededNumberDictionary* value) { roots_[kNonMonomorphicCacheRootIndex] = value; } @@ -1301,9 +1301,9 @@ class Heap { if (global_gc_epilogue_callback_ != NULL) global_gc_epilogue_callback_(); } - uint32_t StringHashSeed() { - uint32_t seed = static_cast(string_hash_seed()->value()); - ASSERT(FLAG_randomize_string_hashes || seed == 0); + uint32_t HashSeed() { + uint32_t seed = static_cast(hash_seed()->value()); + ASSERT(FLAG_randomize_hashes || seed == 0); return seed; } diff --git a/deps/v8/src/ia32/code-stubs-ia32.cc b/deps/v8/src/ia32/code-stubs-ia32.cc index d67a9ba25e6..dd0b8e8b65b 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.cc +++ b/deps/v8/src/ia32/code-stubs-ia32.cc @@ -5610,7 +5610,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, if (Serializer::enabled()) { ExternalReference roots_address = ExternalReference::roots_address(masm->isolate()); - __ mov(scratch, Immediate(Heap::kStringHashSeedRootIndex)); + __ mov(scratch, Immediate(Heap::kHashSeedRootIndex)); __ mov(scratch, Operand::StaticArray(scratch, times_pointer_size, roots_address)); @@ -5619,7 +5619,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, __ shl(scratch, 10); __ add(hash, Operand(scratch)); } else { - int32_t seed = masm->isolate()->heap()->StringHashSeed(); + int32_t seed = masm->isolate()->heap()->HashSeed(); __ lea(scratch, Operand(character, seed)); __ shl(scratch, 10); __ lea(hash, Operand(scratch, character, times_1, seed)); diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc index 5a3b9836dd9..ce6d6a6ea0a 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -752,6 +752,51 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, } +// Compute the hash code from the untagged key. This must be kept in sync +// with ComputeIntegerHash in utils.h. +// +// Note: r0 will contain hash code +void MacroAssembler::GetNumberHash(Register r0, Register scratch) { + // Xor original key with a seed. + if (Serializer::enabled()) { + ExternalReference roots_address = + ExternalReference::roots_address(isolate()); + mov(scratch, Immediate(Heap::kHashSeedRootIndex)); + mov(scratch, Operand::StaticArray(scratch, + times_pointer_size, + roots_address)); + SmiUntag(scratch); + xor_(r0, Operand(scratch)); + } else { + int32_t seed = isolate()->heap()->HashSeed(); + xor_(r0, seed); + } + + // hash = ~hash + (hash << 15); + mov(scratch, r0); + not_(r0); + shl(scratch, 15); + add(r0, Operand(scratch)); + // hash = hash ^ (hash >> 12); + mov(scratch, r0); + shr(scratch, 12); + xor_(r0, Operand(scratch)); + // hash = hash + (hash << 2); + lea(r0, Operand(r0, r0, times_4, 0)); + // hash = hash ^ (hash >> 4); + mov(scratch, r0); + shr(scratch, 4); + xor_(r0, Operand(scratch)); + // hash = hash * 2057; + imul(r0, r0, 2057); + // hash = hash ^ (hash >> 16); + mov(scratch, r0); + shr(scratch, 16); + xor_(r0, Operand(scratch)); +} + + + void MacroAssembler::LoadFromNumberDictionary(Label* miss, Register elements, Register key, @@ -777,33 +822,10 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, Label done; - // Compute the hash code from the untagged key. This must be kept in sync - // with ComputeIntegerHash in utils.h. - // - // hash = ~hash + (hash << 15); - mov(r1, r0); - not_(r0); - shl(r1, 15); - add(r0, Operand(r1)); - // hash = hash ^ (hash >> 12); - mov(r1, r0); - shr(r1, 12); - xor_(r0, Operand(r1)); - // hash = hash + (hash << 2); - lea(r0, Operand(r0, r0, times_4, 0)); - // hash = hash ^ (hash >> 4); - mov(r1, r0); - shr(r1, 4); - xor_(r0, Operand(r1)); - // hash = hash * 2057; - imul(r0, r0, 2057); - // hash = hash ^ (hash >> 16); - mov(r1, r0); - shr(r1, 16); - xor_(r0, Operand(r1)); + GetNumberHash(r0, r1); // Compute capacity mask. - mov(r1, FieldOperand(elements, NumberDictionary::kCapacityOffset)); + mov(r1, FieldOperand(elements, SeededNumberDictionary::kCapacityOffset)); shr(r1, kSmiTagSize); // convert smi to int dec(r1); @@ -814,19 +836,19 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, mov(r2, r0); // Compute the masked index: (hash + i + i * i) & mask. if (i > 0) { - add(Operand(r2), Immediate(NumberDictionary::GetProbeOffset(i))); + add(Operand(r2), Immediate(SeededNumberDictionary::GetProbeOffset(i))); } and_(r2, Operand(r1)); // Scale the index by multiplying by the entry size. - ASSERT(NumberDictionary::kEntrySize == 3); + ASSERT(SeededNumberDictionary::kEntrySize == 3); lea(r2, Operand(r2, r2, times_2, 0)); // r2 = r2 * 3 // Check if the key matches. cmp(key, FieldOperand(elements, r2, times_pointer_size, - NumberDictionary::kElementsStartOffset)); + SeededNumberDictionary::kElementsStartOffset)); if (i != (kProbes - 1)) { j(equal, &done); } else { @@ -837,7 +859,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, bind(&done); // Check that the value is a normal propety. const int kDetailsOffset = - NumberDictionary::kElementsStartOffset + 2 * kPointerSize; + SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize; ASSERT_EQ(NORMAL, 0); test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset), Immediate(PropertyDetails::TypeField::kMask << kSmiTagSize)); @@ -845,7 +867,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, // Get the value at the masked, scaled index. const int kValueOffset = - NumberDictionary::kElementsStartOffset + kPointerSize; + SeededNumberDictionary::kElementsStartOffset + kPointerSize; mov(result, FieldOperand(elements, r2, times_pointer_size, kValueOffset)); } diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h index 1906644c351..8c5f5e9421f 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.h +++ b/deps/v8/src/ia32/macro-assembler-ia32.h @@ -354,6 +354,7 @@ class MacroAssembler: public Assembler { Register scratch, Label* miss); + void GetNumberHash(Register r0, Register scratch); void LoadFromNumberDictionary(Label* miss, Register elements, diff --git a/deps/v8/src/mips/code-stubs-mips.cc b/deps/v8/src/mips/code-stubs-mips.cc index 3500ba7530a..6838e54478f 100644 --- a/deps/v8/src/mips/code-stubs-mips.cc +++ b/deps/v8/src/mips/code-stubs-mips.cc @@ -5580,7 +5580,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, Register hash, Register character) { // hash = seed + character + ((seed + character) << 10); - __ LoadRoot(hash, Heap::kStringHashSeedRootIndex); + __ LoadRoot(hash, Heap::kHashSeedRootIndex); // Untag smi seed and add the character. __ SmiUntag(hash); __ addu(hash, hash, character); diff --git a/deps/v8/src/mips/macro-assembler-mips.cc b/deps/v8/src/mips/macro-assembler-mips.cc index 4c48ef183c2..1c0af5d629d 100644 --- a/deps/v8/src/mips/macro-assembler-mips.cc +++ b/deps/v8/src/mips/macro-assembler-mips.cc @@ -343,6 +343,44 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, } +void MacroAssembler::GetNumberHash(Register reg0, Register scratch) { + // First of all we assign the hash seed to scratch. + LoadRoot(scratch, Heap::kHashSeedRootIndex); + SmiUntag(scratch); + + // Xor original key with a seed. + xor_(reg0, reg0, scratch); + + // Compute the hash code from the untagged key. This must be kept in sync + // with ComputeIntegerHash in utils.h. + // + // hash = ~hash + (hash << 15); + nor(scratch, reg0, zero_reg); + sll(at, reg0, 15); + addu(reg0, scratch, at); + + // hash = hash ^ (hash >> 12); + srl(at, reg0, 12); + xor_(reg0, reg0, at); + + // hash = hash + (hash << 2); + sll(at, reg0, 2); + addu(reg0, reg0, at); + + // hash = hash ^ (hash >> 4); + srl(at, reg0, 4); + xor_(reg0, reg0, at); + + // hash = hash * 2057; + li(scratch, Operand(2057)); + mul(reg0, reg0, scratch); + + // hash = hash ^ (hash >> 16); + srl(at, reg0, 16); + xor_(reg0, reg0, at); +} + + void MacroAssembler::LoadFromNumberDictionary(Label* miss, Register elements, Register key, @@ -374,36 +412,10 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, // at - Temporary (avoid MacroAssembler instructions also using 'at'). Label done; - // Compute the hash code from the untagged key. This must be kept in sync - // with ComputeIntegerHash in utils.h. - // - // hash = ~hash + (hash << 15); - nor(reg1, reg0, zero_reg); - sll(at, reg0, 15); - addu(reg0, reg1, at); - - // hash = hash ^ (hash >> 12); - srl(at, reg0, 12); - xor_(reg0, reg0, at); - - // hash = hash + (hash << 2); - sll(at, reg0, 2); - addu(reg0, reg0, at); - - // hash = hash ^ (hash >> 4); - srl(at, reg0, 4); - xor_(reg0, reg0, at); - - // hash = hash * 2057; - li(reg1, Operand(2057)); - mul(reg0, reg0, reg1); - - // hash = hash ^ (hash >> 16); - srl(at, reg0, 16); - xor_(reg0, reg0, at); + GetNumberHash(reg0, reg1); // Compute the capacity mask. - lw(reg1, FieldMemOperand(elements, NumberDictionary::kCapacityOffset)); + lw(reg1, FieldMemOperand(elements, SeededNumberDictionary::kCapacityOffset)); sra(reg1, reg1, kSmiTagSize); Subu(reg1, reg1, Operand(1)); @@ -414,12 +426,12 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, mov(reg2, reg0); // Compute the masked index: (hash + i + i * i) & mask. if (i > 0) { - Addu(reg2, reg2, Operand(NumberDictionary::GetProbeOffset(i))); + Addu(reg2, reg2, Operand(SeededNumberDictionary::GetProbeOffset(i))); } and_(reg2, reg2, reg1); // Scale the index by multiplying by the element size. - ASSERT(NumberDictionary::kEntrySize == 3); + ASSERT(SeededNumberDictionary::kEntrySize == 3); sll(at, reg2, 1); // 2x. addu(reg2, reg2, at); // reg2 = reg2 * 3. @@ -427,7 +439,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, sll(at, reg2, kPointerSizeLog2); addu(reg2, elements, at); - lw(at, FieldMemOperand(reg2, NumberDictionary::kElementsStartOffset)); + lw(at, FieldMemOperand(reg2, SeededNumberDictionary::kElementsStartOffset)); if (i != kProbes - 1) { Branch(&done, eq, key, Operand(at)); } else { @@ -439,14 +451,14 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, // Check that the value is a normal property. // reg2: elements + (index * kPointerSize). const int kDetailsOffset = - NumberDictionary::kElementsStartOffset + 2 * kPointerSize; + SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize; lw(reg1, FieldMemOperand(reg2, kDetailsOffset)); And(at, reg1, Operand(Smi::FromInt(PropertyDetails::TypeField::kMask))); Branch(miss, ne, at, Operand(zero_reg)); // Get the value at the masked, scaled index and return. const int kValueOffset = - NumberDictionary::kElementsStartOffset + kPointerSize; + SeededNumberDictionary::kElementsStartOffset + kPointerSize; lw(result, FieldMemOperand(reg2, kValueOffset)); } diff --git a/deps/v8/src/mips/macro-assembler-mips.h b/deps/v8/src/mips/macro-assembler-mips.h index ce19a4eff64..c968ffcd151 100644 --- a/deps/v8/src/mips/macro-assembler-mips.h +++ b/deps/v8/src/mips/macro-assembler-mips.h @@ -266,6 +266,7 @@ class MacroAssembler: public Assembler { Register scratch, Label* miss); + void GetNumberHash(Register reg0, Register scratch); void LoadFromNumberDictionary(Label* miss, Register elements, diff --git a/deps/v8/src/objects-debug.cc b/deps/v8/src/objects-debug.cc index 8de7162ab2c..e9ca6c09076 100644 --- a/deps/v8/src/objects-debug.cc +++ b/deps/v8/src/objects-debug.cc @@ -700,7 +700,7 @@ void JSObject::IncrementSpillStatistics(SpillInformation* info) { break; } case DICTIONARY_ELEMENTS: { - NumberDictionary* dict = element_dictionary(); + SeededNumberDictionary* dict = element_dictionary(); info->number_of_slow_used_elements_ += dict->NumberOfElements(); info->number_of_slow_unused_elements_ += dict->Capacity() - dict->NumberOfElements(); diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index bc4ce79db29..e7b6a34296f 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -1789,7 +1789,7 @@ void FixedDoubleArray::Initialize(FixedArray* from) { } -void FixedDoubleArray::Initialize(NumberDictionary* from) { +void FixedDoubleArray::Initialize(SeededNumberDictionary* from) { int offset = kHeaderSize; for (int current = 0; current < length(); ++current) { WRITE_DOUBLE_FIELD(this, offset, hole_nan_as_double()); @@ -2077,7 +2077,7 @@ int HashTable::FindEntry(Key key) { template int HashTable::FindEntry(Isolate* isolate, Key key) { uint32_t capacity = Capacity(); - uint32_t entry = FirstProbe(Shape::Hash(key), capacity); + uint32_t entry = FirstProbe(HashTable::Hash(key), capacity); uint32_t count = 1; // EnsureCapacity will guarantee the hash table is never full. while (true) { @@ -2092,14 +2092,14 @@ int HashTable::FindEntry(Isolate* isolate, Key key) { } -bool NumberDictionary::requires_slow_elements() { +bool SeededNumberDictionary::requires_slow_elements() { Object* max_index_object = get(kMaxNumberKeyIndex); if (!max_index_object->IsSmi()) return false; return 0 != (Smi::cast(max_index_object)->value() & kRequiresSlowElementsMask); } -uint32_t NumberDictionary::max_number_key() { +uint32_t SeededNumberDictionary::max_number_key() { ASSERT(!requires_slow_elements()); Object* max_index_object = get(kMaxNumberKeyIndex); if (!max_index_object->IsSmi()) return 0; @@ -2107,7 +2107,7 @@ uint32_t NumberDictionary::max_number_key() { return value >> kRequiresSlowElementsTagSize; } -void NumberDictionary::set_requires_slow_elements() { +void SeededNumberDictionary::set_requires_slow_elements() { set(kMaxNumberKeyIndex, Smi::FromInt(kRequiresSlowElementsMask)); } @@ -4211,9 +4211,9 @@ StringDictionary* JSObject::property_dictionary() { } -NumberDictionary* JSObject::element_dictionary() { +SeededNumberDictionary* JSObject::element_dictionary() { ASSERT(HasDictionaryElements()); - return NumberDictionary::cast(elements()); + return SeededNumberDictionary::cast(elements()); } @@ -4243,7 +4243,7 @@ StringHasher::StringHasher(int length, uint32_t seed) is_array_index_(0 < length_ && length_ <= String::kMaxArrayIndexSize), is_first_char_(true), is_valid_(true) { - ASSERT(FLAG_randomize_string_hashes || raw_running_hash_ == 0); + ASSERT(FLAG_randomize_hashes || raw_running_hash_ == 0); } @@ -4476,16 +4476,27 @@ bool NumberDictionaryShape::IsMatch(uint32_t key, Object* other) { } -uint32_t NumberDictionaryShape::Hash(uint32_t key) { - return ComputeIntegerHash(key); +uint32_t UnseededNumberDictionaryShape::Hash(uint32_t key) { + return ComputeIntegerHash(key, 0); } -uint32_t NumberDictionaryShape::HashForObject(uint32_t key, Object* other) { +uint32_t UnseededNumberDictionaryShape::HashForObject(uint32_t key, + Object* other) { ASSERT(other->IsNumber()); - return ComputeIntegerHash(static_cast(other->Number())); + return ComputeIntegerHash(static_cast(other->Number()), 0); } +uint32_t SeededNumberDictionaryShape::SeededHash(uint32_t key, uint32_t seed) { + return ComputeIntegerHash(key, seed); +} + +uint32_t SeededNumberDictionaryShape::SeededHashForObject(uint32_t key, + uint32_t seed, + Object* other) { + ASSERT(other->IsNumber()); + return ComputeIntegerHash(static_cast(other->Number()), seed); +} MaybeObject* NumberDictionaryShape::AsObject(uint32_t key) { return Isolate::Current()->heap()->NumberFromUint32(key); diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 595459681f9..42245d48b0c 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -1951,9 +1951,10 @@ MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes( if (!JSObject::cast(pt)->HasDictionaryElements()) { continue; } - NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary(); + SeededNumberDictionary* dictionary = + JSObject::cast(pt)->element_dictionary(); int entry = dictionary->FindEntry(index); - if (entry != NumberDictionary::kNotFound) { + if (entry != SeededNumberDictionary::kNotFound) { PropertyDetails details = dictionary->DetailsAt(entry); if (details.type() == CALLBACKS) { *found = true; @@ -2928,11 +2929,11 @@ MaybeObject* JSObject::NormalizeElements() { int old_capacity = 0; int used_elements = 0; GetElementsCapacityAndUsage(&old_capacity, &used_elements); - NumberDictionary* dictionary = NULL; + SeededNumberDictionary* dictionary = NULL; { Object* object; - MaybeObject* maybe = NumberDictionary::Allocate(used_elements); + MaybeObject* maybe = SeededNumberDictionary::Allocate(used_elements); if (!maybe->ToObject(&object)) return maybe; - dictionary = NumberDictionary::cast(object); + dictionary = SeededNumberDictionary::cast(object); } // Copy the elements to the new backing store. @@ -2962,7 +2963,7 @@ MaybeObject* JSObject::NormalizeElements() { MaybeObject* maybe_result = dictionary->AddNumberEntry(i, value, details); if (!maybe_result->ToObject(&result)) return maybe_result; - dictionary = NumberDictionary::cast(result); + dictionary = SeededNumberDictionary::cast(result); } } @@ -3277,7 +3278,8 @@ bool JSObject::ReferencesObjectFromElements(FixedArray* elements, if (!element->IsTheHole() && element == object) return true; } } else { - Object* key = NumberDictionary::cast(elements)->SlowReverseLookup(object); + Object* key = + SeededNumberDictionary::cast(elements)->SlowReverseLookup(object); if (!key->IsUndefined()) return true; } return false; @@ -3416,9 +3418,9 @@ MaybeObject* JSObject::PreventExtensions() { } // If there are fast elements we normalize. - NumberDictionary* dictionary = NULL; + SeededNumberDictionary* dictionary = NULL; { MaybeObject* maybe = NormalizeElements(); - if (!maybe->To(&dictionary)) return maybe; + if (!maybe->To(&dictionary)) return maybe; } ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); // Make sure that we never go back to fast case. @@ -3581,11 +3583,11 @@ void JSObject::LookupCallback(String* name, LookupResult* result) { // undefined if the element is read-only, or the getter/setter pair (fixed // array) if there is an existing one, or the hole value if the element does // not exist or is a normal non-getter/setter data element. -static Object* FindGetterSetterInDictionary(NumberDictionary* dictionary, +static Object* FindGetterSetterInDictionary(SeededNumberDictionary* dictionary, uint32_t index, Heap* heap) { int entry = dictionary->FindEntry(index); - if (entry != NumberDictionary::kNotFound) { + if (entry != SeededNumberDictionary::kNotFound) { Object* result = dictionary->ValueAt(entry); PropertyDetails details = dictionary->DetailsAt(entry); if (details.IsReadOnly()) return heap->undefined_value(); @@ -3647,7 +3649,8 @@ MaybeObject* JSObject::DefineGetterSetter(String* name, if (probe == NULL || probe->IsTheHole()) { FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); if (arguments->IsDictionary()) { - NumberDictionary* dictionary = NumberDictionary::cast(arguments); + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(arguments); probe = FindGetterSetterInDictionary(dictionary, index, heap); if (!probe->IsTheHole()) return probe; } @@ -3716,11 +3719,11 @@ MaybeObject* JSObject::SetElementCallback(uint32_t index, PropertyDetails details = PropertyDetails(attributes, CALLBACKS); // Normalize elements to make this operation simple. - NumberDictionary* dictionary = NULL; + SeededNumberDictionary* dictionary = NULL; { Object* result; MaybeObject* maybe = NormalizeElements(); if (!maybe->ToObject(&result)) return maybe; - dictionary = NumberDictionary::cast(result); + dictionary = SeededNumberDictionary::cast(result); } ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); @@ -3728,7 +3731,7 @@ MaybeObject* JSObject::SetElementCallback(uint32_t index, { Object* result; MaybeObject* maybe = dictionary->Set(index, structure, details); if (!maybe->ToObject(&result)) return maybe; - dictionary = NumberDictionary::cast(result); + dictionary = SeededNumberDictionary::cast(result); } dictionary->set_requires_slow_elements(); @@ -3933,9 +3936,9 @@ Object* JSObject::LookupAccessor(String* name, bool is_getter) { obj = JSObject::cast(obj)->GetPrototype()) { JSObject* js_object = JSObject::cast(obj); if (js_object->HasDictionaryElements()) { - NumberDictionary* dictionary = js_object->element_dictionary(); + SeededNumberDictionary* dictionary = js_object->element_dictionary(); int entry = dictionary->FindEntry(index); - if (entry != NumberDictionary::kNotFound) { + if (entry != SeededNumberDictionary::kNotFound) { Object* element = dictionary->ValueAt(entry); PropertyDetails details = dictionary->DetailsAt(entry); if (details.type() == CALLBACKS) { @@ -6077,14 +6080,14 @@ uint32_t String::ComputeAndSetHash() { if (StringShape(this).IsSequentialAscii()) { field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), len, - GetHeap()->StringHashSeed()); + GetHeap()->HashSeed()); } else if (StringShape(this).IsSequentialTwoByte()) { field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), len, - GetHeap()->StringHashSeed()); + GetHeap()->HashSeed()); } else { StringInputBuffer buffer(this); - field = ComputeHashField(&buffer, len, GetHeap()->StringHashSeed()); + field = ComputeHashField(&buffer, len, GetHeap()->HashSeed()); } // Store the hash code in the object. @@ -7276,7 +7279,7 @@ static void CopyFastElementsToFast(FixedArray* source, } -static void CopySlowElementsToFast(NumberDictionary* source, +static void CopySlowElementsToFast(SeededNumberDictionary* source, FixedArray* destination, WriteBarrierMode mode) { for (int i = 0; i < source->Capacity(); ++i) { @@ -7324,7 +7327,7 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, case DICTIONARY_ELEMENTS: { AssertNoAllocation no_gc; WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc); - CopySlowElementsToFast(NumberDictionary::cast(elements()), + CopySlowElementsToFast(SeededNumberDictionary::cast(elements()), new_elements, mode); set_map(new_map); @@ -7339,7 +7342,7 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, FixedArray* parameter_map = FixedArray::cast(elements()); FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); if (arguments->IsDictionary()) { - CopySlowElementsToFast(NumberDictionary::cast(arguments), + CopySlowElementsToFast(SeededNumberDictionary::cast(arguments), new_elements, mode); } else { @@ -7426,7 +7429,7 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( break; } case DICTIONARY_ELEMENTS: { - elems->Initialize(NumberDictionary::cast(elements())); + elems->Initialize(SeededNumberDictionary::cast(elements())); break; } default: @@ -7861,7 +7864,7 @@ bool JSObject::HasElementPostInterceptor(JSReceiver* receiver, uint32_t index) { } case DICTIONARY_ELEMENTS: { if (element_dictionary()->FindEntry(index) - != NumberDictionary::kNotFound) { + != SeededNumberDictionary::kNotFound) { return true; } break; @@ -7993,7 +7996,7 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { } case DICTIONARY_ELEMENTS: { if (element_dictionary()->FindEntry(index) != - NumberDictionary::kNotFound) { + SeededNumberDictionary::kNotFound) { return DICTIONARY_ELEMENT; } break; @@ -8010,8 +8013,9 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { // If not aliased, check the arguments. FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); if (arguments->IsDictionary()) { - NumberDictionary* dictionary = NumberDictionary::cast(arguments); - if (dictionary->FindEntry(index) != NumberDictionary::kNotFound) { + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(arguments); + if (dictionary->FindEntry(index) != SeededNumberDictionary::kNotFound) { return DICTIONARY_ELEMENT; } } else { @@ -8040,8 +8044,8 @@ bool JSObject::HasElementInElements(FixedArray* elements, return true; } } else { - if (NumberDictionary::cast(elements)->FindEntry(index) != - NumberDictionary::kNotFound) { + if (SeededNumberDictionary::cast(elements)->FindEntry(index) != + SeededNumberDictionary::kNotFound) { return true; } } @@ -8107,7 +8111,7 @@ bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) { } case DICTIONARY_ELEMENTS: { if (element_dictionary()->FindEntry(index) - != NumberDictionary::kNotFound) { + != SeededNumberDictionary::kNotFound) { return true; } break; @@ -8387,15 +8391,15 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, FixedArray* elements = FixedArray::cast(this->elements()); bool is_arguments = (elements->map() == heap->non_strict_arguments_elements_map()); - NumberDictionary* dictionary = NULL; + SeededNumberDictionary* dictionary = NULL; if (is_arguments) { - dictionary = NumberDictionary::cast(elements->get(1)); + dictionary = SeededNumberDictionary::cast(elements->get(1)); } else { - dictionary = NumberDictionary::cast(elements); + dictionary = SeededNumberDictionary::cast(elements); } int entry = dictionary->FindEntry(index); - if (entry != NumberDictionary::kNotFound) { + if (entry != SeededNumberDictionary::kNotFound) { Object* element = dictionary->ValueAt(entry); PropertyDetails details = dictionary->DetailsAt(entry); if (details.type() == CALLBACKS) { @@ -8440,13 +8444,13 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, FixedArrayBase* new_dictionary; MaybeObject* maybe = dictionary->AtNumberPut(index, value); if (!maybe->To(&new_dictionary)) return maybe; - if (dictionary != NumberDictionary::cast(new_dictionary)) { + if (dictionary != SeededNumberDictionary::cast(new_dictionary)) { if (is_arguments) { elements->set(1, new_dictionary); } else { set_elements(new_dictionary); } - dictionary = NumberDictionary::cast(new_dictionary); + dictionary = SeededNumberDictionary::cast(new_dictionary); } } @@ -8767,7 +8771,8 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { FixedArray::cast(FixedArray::cast(backing_store_base)->get(1)); backing_store = FixedArray::cast(backing_store_base); if (backing_store->IsDictionary()) { - NumberDictionary* dictionary = NumberDictionary::cast(backing_store); + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(backing_store); *capacity = dictionary->Capacity(); *used = dictionary->NumberOfElements(); break; @@ -8781,8 +8786,8 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { } break; case DICTIONARY_ELEMENTS: { - NumberDictionary* dictionary = - NumberDictionary::cast(FixedArray::cast(elements())); + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(FixedArray::cast(elements())); *capacity = dictionary->Capacity(); *used = dictionary->NumberOfElements(); break; @@ -8827,8 +8832,8 @@ bool JSObject::ShouldConvertToSlowElements(int new_capacity) { int old_capacity = 0; int used_elements = 0; GetElementsCapacityAndUsage(&old_capacity, &used_elements); - int dictionary_size = NumberDictionary::ComputeCapacity(used_elements) * - NumberDictionary::kEntrySize; + int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) * + SeededNumberDictionary::kEntrySize; return 3 * dictionary_size <= new_capacity; } @@ -8842,11 +8847,11 @@ bool JSObject::ShouldConvertToFastElements() { if (IsAccessCheckNeeded()) return false; FixedArray* elements = FixedArray::cast(this->elements()); - NumberDictionary* dictionary = NULL; + SeededNumberDictionary* dictionary = NULL; if (elements->map() == GetHeap()->non_strict_arguments_elements_map()) { - dictionary = NumberDictionary::cast(elements->get(1)); + dictionary = SeededNumberDictionary::cast(elements->get(1)); } else { - dictionary = NumberDictionary::cast(elements); + dictionary = SeededNumberDictionary::cast(elements); } // If an element has been added at a very high index in the elements // dictionary, we cannot go back to fast case. @@ -8861,7 +8866,7 @@ bool JSObject::ShouldConvertToFastElements() { array_size = dictionary->max_number_key(); } uint32_t dictionary_size = static_cast(dictionary->Capacity()) * - NumberDictionary::kEntrySize; + SeededNumberDictionary::kEntrySize; return 2 * dictionary_size >= array_size; } @@ -8869,7 +8874,8 @@ bool JSObject::ShouldConvertToFastElements() { bool JSObject::CanConvertToFastDoubleElements() { if (FLAG_unbox_double_arrays) { ASSERT(HasDictionaryElements()); - NumberDictionary* dictionary = NumberDictionary::cast(elements()); + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(elements()); for (int i = 0; i < dictionary->Capacity(); i++) { Object* key = dictionary->KeyAt(i); if (key->IsNumber()) { @@ -9082,7 +9088,7 @@ bool JSObject::HasRealElementProperty(uint32_t index) { } case DICTIONARY_ELEMENTS: { return element_dictionary()->FindEntry(index) - != NumberDictionary::kNotFound; + != SeededNumberDictionary::kNotFound; } case NON_STRICT_ARGUMENTS_ELEMENTS: UNIMPLEMENTED(); @@ -9350,7 +9356,7 @@ int JSObject::GetLocalElementKeys(FixedArray* storage, if (storage != NULL) { element_dictionary()->CopyKeysTo(storage, filter, - NumberDictionary::SORTED); + SeededNumberDictionary::SORTED); } counter += element_dictionary()->NumberOfElementsFilterAttributes(filter); break; @@ -9362,9 +9368,11 @@ int JSObject::GetLocalElementKeys(FixedArray* storage, if (arguments->IsDictionary()) { // Copy the keys from arguments first, because Dictionary::CopyKeysTo // will insert in storage starting at index 0. - NumberDictionary* dictionary = NumberDictionary::cast(arguments); + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(arguments); if (storage != NULL) { - dictionary->CopyKeysTo(storage, filter, NumberDictionary::UNSORTED); + dictionary->CopyKeysTo( + storage, filter, SeededNumberDictionary::UNSORTED); } counter += dictionary->NumberOfElementsFilterAttributes(filter); for (int i = 0; i < mapped_length; ++i) { @@ -9671,7 +9679,7 @@ class SubStringAsciiSymbolKey : public HashTableKey { uint32_t Hash() { ASSERT(length_ >= 0); ASSERT(from_ + length_ <= string_->length()); - StringHasher hasher(length_, string_->GetHeap()->StringHashSeed()); + StringHasher hasher(length_, string_->GetHeap()->HashSeed()); // Very long strings have a trivial hash that doesn't inspect the // string contents. @@ -9880,7 +9888,7 @@ MaybeObject* HashTable::Rehash(HashTable* new_table, Key key) { uint32_t from_index = EntryToIndex(i); Object* k = get(from_index); if (IsKey(k)) { - uint32_t hash = Shape::HashForObject(key, k); + uint32_t hash = HashTable::HashForObject(key, k); uint32_t insertion_index = EntryToIndex(new_table->FindInsertionEntry(hash)); for (int j = 0; j < Shape::kEntrySize; j++) { @@ -9976,38 +9984,46 @@ template class HashTable; template class Dictionary; -template class Dictionary; +template class Dictionary; -template MaybeObject* Dictionary::Allocate( - int); +template class Dictionary; + +template MaybeObject* Dictionary:: + Allocate(int at_least_space_for); + +template MaybeObject* Dictionary:: + Allocate(int at_least_space_for); template MaybeObject* Dictionary::Allocate( int); -template MaybeObject* Dictionary::AtPut( +template MaybeObject* Dictionary::AtPut( uint32_t, Object*); -template Object* Dictionary::SlowReverseLookup( - Object*); +template MaybeObject* Dictionary:: + AtPut(uint32_t, Object*); + +template Object* Dictionary:: + SlowReverseLookup(Object* value); template Object* Dictionary::SlowReverseLookup( Object*); -template void Dictionary::CopyKeysTo( +template void Dictionary::CopyKeysTo( FixedArray*, PropertyAttributes, - Dictionary::SortMode); + Dictionary::SortMode); template Object* Dictionary::DeleteProperty( int, JSObject::DeleteMode); -template Object* Dictionary::DeleteProperty( - int, JSObject::DeleteMode); +template Object* Dictionary:: + DeleteProperty(int, JSObject::DeleteMode); template MaybeObject* Dictionary::Shrink( String*); -template MaybeObject* Dictionary::Shrink( +template MaybeObject* Dictionary::Shrink( uint32_t); template void Dictionary::CopyKeysTo( @@ -10026,32 +10042,41 @@ template MaybeObject* Dictionary::GenerateNewEnumerationIndices(); template int -Dictionary::NumberOfElementsFilterAttributes( - PropertyAttributes); +Dictionary:: + NumberOfElementsFilterAttributes(PropertyAttributes); -template MaybeObject* Dictionary::Add( +template MaybeObject* Dictionary::Add( uint32_t, Object*, PropertyDetails); -template MaybeObject* Dictionary:: +template MaybeObject* Dictionary::Add( + uint32_t, Object*, PropertyDetails); + +template MaybeObject* Dictionary:: + EnsureCapacity(int, uint32_t); + +template MaybeObject* Dictionary:: EnsureCapacity(int, uint32_t); template MaybeObject* Dictionary:: EnsureCapacity(int, String*); -template MaybeObject* Dictionary::AddEntry( - uint32_t, Object*, PropertyDetails, uint32_t); +template MaybeObject* Dictionary:: + AddEntry(uint32_t, Object*, PropertyDetails, uint32_t); + +template MaybeObject* Dictionary:: + AddEntry(uint32_t, Object*, PropertyDetails, uint32_t); template MaybeObject* Dictionary::AddEntry( String*, Object*, PropertyDetails, uint32_t); template -int Dictionary::NumberOfEnumElements(); +int Dictionary::NumberOfEnumElements(); template int Dictionary::NumberOfEnumElements(); template -int HashTable::FindEntry(uint32_t); +int HashTable::FindEntry(uint32_t); // Collates undefined and unexisting elements below limit from position @@ -10061,7 +10086,7 @@ MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) { // Must stay in dictionary mode, either because of requires_slow_elements, // or because we are not going to sort (and therefore compact) all of the // elements. - NumberDictionary* dict = element_dictionary(); + SeededNumberDictionary* dict = element_dictionary(); HeapNumber* result_double = NULL; if (limit > static_cast(Smi::kMaxValue)) { // Allocate space for result before we start mutating the object. @@ -10074,10 +10099,10 @@ MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) { Object* obj; { MaybeObject* maybe_obj = - NumberDictionary::Allocate(dict->NumberOfElements()); + SeededNumberDictionary::Allocate(dict->NumberOfElements()); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } - NumberDictionary* new_dict = NumberDictionary::cast(obj); + SeededNumberDictionary* new_dict = SeededNumberDictionary::cast(obj); AssertNoAllocation no_alloc; @@ -10163,7 +10188,7 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { if (HasDictionaryElements()) { // Convert to fast elements containing only the existing properties. // Ordering is irrelevant, since we are going to sort anyway. - NumberDictionary* dict = element_dictionary(); + SeededNumberDictionary* dict = element_dictionary(); if (IsJSArray() || dict->requires_slow_elements() || dict->max_number_key() >= limit) { return PrepareSlowElementsForSort(limit); @@ -10576,7 +10601,7 @@ bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) { bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1, uint32_t c2, String** symbol) { - TwoCharHashTableKey key(c1, c2, GetHeap()->StringHashSeed()); + TwoCharHashTableKey key(c1, c2, GetHeap()->HashSeed()); int entry = FindEntry(&key); if (entry == kNotFound) { return false; @@ -10591,14 +10616,14 @@ bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1, MaybeObject* SymbolTable::LookupSymbol(Vector str, Object** s) { - Utf8SymbolKey key(str, GetHeap()->StringHashSeed()); + Utf8SymbolKey key(str, GetHeap()->HashSeed()); return LookupKey(&key, s); } MaybeObject* SymbolTable::LookupAsciiSymbol(Vector str, Object** s) { - AsciiSymbolKey key(str, GetHeap()->StringHashSeed()); + AsciiSymbolKey key(str, GetHeap()->HashSeed()); return LookupKey(&key, s); } @@ -10607,14 +10632,14 @@ MaybeObject* SymbolTable::LookupSubStringAsciiSymbol(Handle str, int from, int length, Object** s) { - SubStringAsciiSymbolKey key(str, from, length, GetHeap()->StringHashSeed()); + SubStringAsciiSymbolKey key(str, from, length, GetHeap()->HashSeed()); return LookupKey(&key, s); } MaybeObject* SymbolTable::LookupTwoByteSymbol(Vector str, Object** s) { - TwoByteSymbolKey key(str, GetHeap()->StringHashSeed()); + TwoByteSymbolKey key(str, GetHeap()->HashSeed()); return LookupKey(&key, s); } @@ -10905,7 +10930,7 @@ MaybeObject* Dictionary::EnsureCapacity(int n, Key key) { } -void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) { +void SeededNumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) { // Do nothing if the interval [from, to) is empty. if (from >= to) return; @@ -10971,8 +10996,9 @@ MaybeObject* Dictionary::AtPut(Key key, Object* value) { if (!maybe_k->ToObject(&k)) return maybe_k; } PropertyDetails details = PropertyDetails(NONE, NORMAL); - return Dictionary::cast(obj)-> - AddEntry(key, value, details, Shape::Hash(key)); + + return Dictionary::cast(obj)->AddEntry(key, value, details, + Dictionary::Hash(key)); } @@ -10987,8 +11013,9 @@ MaybeObject* Dictionary::Add(Key key, { MaybeObject* maybe_obj = EnsureCapacity(1, key); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } - return Dictionary::cast(obj)-> - AddEntry(key, value, details, Shape::Hash(key)); + + return Dictionary::cast(obj)->AddEntry(key, value, details, + Dictionary::Hash(key)); } @@ -11021,7 +11048,7 @@ MaybeObject* Dictionary::AddEntry(Key key, } -void NumberDictionary::UpdateMaxNumberKey(uint32_t key) { +void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key) { // If the dictionary requires slow elements an element has already // been added at a high index. if (requires_slow_elements()) return; @@ -11040,31 +11067,44 @@ void NumberDictionary::UpdateMaxNumberKey(uint32_t key) { } -MaybeObject* NumberDictionary::AddNumberEntry(uint32_t key, - Object* value, - PropertyDetails details) { +MaybeObject* SeededNumberDictionary::AddNumberEntry(uint32_t key, + Object* value, + PropertyDetails details) { UpdateMaxNumberKey(key); SLOW_ASSERT(this->FindEntry(key) == kNotFound); return Add(key, value, details); } -MaybeObject* NumberDictionary::AtNumberPut(uint32_t key, Object* value) { +MaybeObject* UnseededNumberDictionary::AddNumberEntry(uint32_t key, + Object* value) { + SLOW_ASSERT(this->FindEntry(key) == kNotFound); + return Add(key, value, PropertyDetails(NONE, NORMAL)); +} + + +MaybeObject* SeededNumberDictionary::AtNumberPut(uint32_t key, Object* value) { UpdateMaxNumberKey(key); return AtPut(key, value); } -MaybeObject* NumberDictionary::Set(uint32_t key, - Object* value, - PropertyDetails details) { +MaybeObject* UnseededNumberDictionary::AtNumberPut(uint32_t key, + Object* value) { + return AtPut(key, value); +} + + +MaybeObject* SeededNumberDictionary::Set(uint32_t key, + Object* value, + PropertyDetails details) { int entry = FindEntry(key); if (entry == kNotFound) return AddNumberEntry(key, value, details); // Preserve enumeration index. details = PropertyDetails(details.attributes(), details.type(), DetailsAt(entry).index()); - MaybeObject* maybe_object_key = NumberDictionaryShape::AsObject(key); + MaybeObject* maybe_object_key = SeededNumberDictionaryShape::AsObject(key); Object* object_key; if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key; SetEntry(entry, object_key, value, details); @@ -11072,6 +11112,18 @@ MaybeObject* NumberDictionary::Set(uint32_t key, } +MaybeObject* UnseededNumberDictionary::Set(uint32_t key, + Object* value) { + int entry = FindEntry(key); + if (entry == kNotFound) return AddNumberEntry(key, value); + MaybeObject* maybe_object_key = UnseededNumberDictionaryShape::AsObject(key); + Object* object_key; + if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key; + SetEntry(entry, object_key, value); + return this; +} + + template int Dictionary::NumberOfElementsFilterAttributes( diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 2fb2e006512..6a82a908120 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -1541,7 +1541,7 @@ class JSObject: public JSReceiver { bool HasFastArgumentsElements(); bool HasDictionaryArgumentsElements(); inline bool AllowsSetElementsLength(); - inline NumberDictionary* element_dictionary(); // Gets slow elements. + inline SeededNumberDictionary* element_dictionary(); // Gets slow elements. // Requires: HasFastElements(). MUST_USE_RESULT inline MaybeObject* EnsureWritableFastElements(); @@ -1902,8 +1902,6 @@ class JSObject: public JSReceiver { PropertyNormalizationMode mode, int expected_additional_properties); - // Convert and update the elements backing store to be a NumberDictionary - // dictionary. Returns the backing after conversion. MUST_USE_RESULT MaybeObject* NormalizeElements(); MUST_USE_RESULT MaybeObject* UpdateMapCodeCache(String* name, Code* code); @@ -2221,7 +2219,7 @@ class FixedDoubleArray: public FixedArrayBase { public: inline void Initialize(FixedArray* from); inline void Initialize(FixedDoubleArray* from); - inline void Initialize(NumberDictionary* from); + inline void Initialize(SeededNumberDictionary* from); // Setter and getter for elements. inline double get_scalar(int index); @@ -2514,9 +2512,46 @@ class DescriptorArray: public FixedArray { // beginning of the backing storage that can be used for non-element // information by subclasses. +template +class BaseShape { + public: + static const bool UsesSeed = false; + static uint32_t Hash(Key key) { return 0; } + static uint32_t SeededHash(Key key, uint32_t seed) { + ASSERT(UsesSeed); + return Hash(key); + } + static uint32_t HashForObject(Key key, Object* object) { return 0; } + static uint32_t SeededHashForObject(Key key, uint32_t seed, Object* object) { + // Won't be called if UsesSeed isn't overridden by child class. + return HashForObject(key, object); + } +}; + template class HashTable: public FixedArray { public: + // Wrapper methods + inline uint32_t Hash(Key key) { + if (Shape::UsesSeed) { + // I'm using map()->heap() to skip is_safe_to_read_maps assertion. + // That was done, because NumberDictionary is used inside GC. + return Shape::SeededHash(key, map()->heap()->HashSeed()); + } else { + return Shape::Hash(key); + } + } + + inline uint32_t HashForObject(Key key, Object* object) { + if (Shape::UsesSeed) { + // I'm using map()->heap() to skip is_safe_to_read_maps assertion. + // That was done, because NumberDictionary is used inside GC. + return Shape::SeededHashForObject(key, map()->heap()->HashSeed(), object); + } else { + return Shape::HashForObject(key, object); + } + } + // Returns the number of elements in the hash table. int NumberOfElements() { return Smi::cast(get(kNumberOfElementsIndex))->value(); @@ -2658,7 +2693,6 @@ class HashTable: public FixedArray { }; - // HashTableKey is an abstract superclass for virtual key behavior. class HashTableKey { public: @@ -2675,7 +2709,8 @@ class HashTableKey { virtual ~HashTableKey() {} }; -class SymbolTableShape { + +class SymbolTableShape : public BaseShape { public: static inline bool IsMatch(HashTableKey* key, Object* value) { return key->IsMatch(value); @@ -2734,7 +2769,7 @@ class SymbolTable: public HashTable { }; -class MapCacheShape { +class MapCacheShape : public BaseShape { public: static inline bool IsMatch(HashTableKey* key, Object* value) { return key->IsMatch(value); @@ -2890,7 +2925,7 @@ class Dictionary: public HashTable { }; -class StringDictionaryShape { +class StringDictionaryShape : public BaseShape { public: static inline bool IsMatch(String* key, Object* other); static inline uint32_t Hash(String* key); @@ -2923,23 +2958,42 @@ class StringDictionary: public Dictionary { }; -class NumberDictionaryShape { +class NumberDictionaryShape : public BaseShape { public: static inline bool IsMatch(uint32_t key, Object* other); - static inline uint32_t Hash(uint32_t key); - static inline uint32_t HashForObject(uint32_t key, Object* object); MUST_USE_RESULT static inline MaybeObject* AsObject(uint32_t key); - static const int kPrefixSize = 2; static const int kEntrySize = 3; static const bool kIsEnumerable = false; }; -class NumberDictionary: public Dictionary { +class SeededNumberDictionaryShape : public NumberDictionaryShape { public: - static NumberDictionary* cast(Object* obj) { + static const bool UsesSeed = true; + static const int kPrefixSize = 2; + + static inline uint32_t SeededHash(uint32_t key, uint32_t seed); + static inline uint32_t SeededHashForObject(uint32_t key, + uint32_t seed, + Object* object); +}; + + +class UnseededNumberDictionaryShape : public NumberDictionaryShape { + public: + static const int kPrefixSize = 0; + + static inline uint32_t Hash(uint32_t key); + static inline uint32_t HashForObject(uint32_t key, Object* object); +}; + + +class SeededNumberDictionary + : public Dictionary { + public: + static SeededNumberDictionary* cast(Object* obj) { ASSERT(obj->IsDictionary()); - return reinterpret_cast(obj); + return reinterpret_cast(obj); } // Type specific at put (default NONE attributes is used when adding). @@ -2978,7 +3032,24 @@ class NumberDictionary: public Dictionary { }; -class ObjectHashTableShape { +class UnseededNumberDictionary + : public Dictionary { + public: + static UnseededNumberDictionary* cast(Object* obj) { + ASSERT(obj->IsDictionary()); + return reinterpret_cast(obj); + } + + // Type specific at put (default NONE attributes is used when adding). + MUST_USE_RESULT MaybeObject* AtNumberPut(uint32_t key, Object* value); + MUST_USE_RESULT MaybeObject* AddNumberEntry(uint32_t key, Object* value); + + // Set an existing entry or add a new one if needed. + MUST_USE_RESULT MaybeObject* Set(uint32_t key, Object* value); +}; + + +class ObjectHashTableShape : public BaseShape { public: static inline bool IsMatch(JSObject* key, Object* other); static inline uint32_t Hash(JSObject* key); @@ -5543,7 +5614,7 @@ class JSRegExp: public JSObject { }; -class CompilationCacheShape { +class CompilationCacheShape : public BaseShape { public: static inline bool IsMatch(HashTableKey* key, Object* value) { return key->IsMatch(value); @@ -5643,7 +5714,7 @@ class CodeCache: public Struct { }; -class CodeCacheHashTableShape { +class CodeCacheHashTableShape : public BaseShape { public: static inline bool IsMatch(HashTableKey* key, Object* value) { return key->IsMatch(value); diff --git a/deps/v8/src/profile-generator.cc b/deps/v8/src/profile-generator.cc index ed1b90a2e7f..e319efb043d 100644 --- a/deps/v8/src/profile-generator.cc +++ b/deps/v8/src/profile-generator.cc @@ -111,7 +111,7 @@ const char* StringsStorage::GetCopy(const char* src) { OS::StrNCpy(dst, src, len); dst[len] = '\0'; uint32_t hash = - HashSequentialString(dst.start(), len, HEAP->StringHashSeed()); + HashSequentialString(dst.start(), len, HEAP->HashSeed()); return AddOrDisposeString(dst.start(), hash); } @@ -145,7 +145,7 @@ const char* StringsStorage::GetVFormatted(const char* format, va_list args) { return format; } uint32_t hash = HashSequentialString( - str.start(), len, HEAP->StringHashSeed()); + str.start(), len, HEAP->HashSeed()); return AddOrDisposeString(str.start(), hash); } @@ -178,18 +178,21 @@ void CodeEntry::CopyData(const CodeEntry& source) { uint32_t CodeEntry::GetCallUid() const { - uint32_t hash = ComputeIntegerHash(tag_); + uint32_t hash = ComputeIntegerHash(tag_, v8::internal::kZeroHashSeed); if (shared_id_ != 0) { - hash ^= ComputeIntegerHash( - static_cast(shared_id_)); + hash ^= ComputeIntegerHash(static_cast(shared_id_), + v8::internal::kZeroHashSeed); } else { hash ^= ComputeIntegerHash( - static_cast(reinterpret_cast(name_prefix_))); + static_cast(reinterpret_cast(name_prefix_)), + v8::internal::kZeroHashSeed); hash ^= ComputeIntegerHash( - static_cast(reinterpret_cast(name_))); + static_cast(reinterpret_cast(name_)), + v8::internal::kZeroHashSeed); hash ^= ComputeIntegerHash( - static_cast(reinterpret_cast(resource_name_))); - hash ^= ComputeIntegerHash(line_number_); + static_cast(reinterpret_cast(resource_name_)), + v8::internal::kZeroHashSeed); + hash ^= ComputeIntegerHash(line_number_, v8::internal::kZeroHashSeed); } return hash; } @@ -1213,7 +1216,7 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, entries_sorted_(false) { STATIC_ASSERT( sizeof(HeapGraphEdge) == - SnapshotSizeConstants::kExpectedHeapGraphEdgeSize); // NOLINT + SnapshotSizeConstants::kExpectedHeapGraphEdgeSize); STATIC_ASSERT( sizeof(HeapEntry) == SnapshotSizeConstants::kExpectedHeapEntrySize); // NOLINT @@ -1466,10 +1469,11 @@ uint64_t HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) { const char* label = info->GetLabel(); id ^= HashSequentialString(label, static_cast(strlen(label)), - HEAP->StringHashSeed()); + HEAP->HashSeed()); intptr_t element_count = info->GetElementCount(); if (element_count != -1) - id ^= ComputeIntegerHash(static_cast(element_count)); + id ^= ComputeIntegerHash(static_cast(element_count), + v8::internal::kZeroHashSeed); return id << 1; } @@ -2131,7 +2135,7 @@ void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj, } } } else if (js_obj->HasDictionaryElements()) { - NumberDictionary* dictionary = js_obj->element_dictionary(); + SeededNumberDictionary* dictionary = js_obj->element_dictionary(); int length = dictionary->Capacity(); for (int i = 0; i < length; ++i) { Object* k = dictionary->KeyAt(i); diff --git a/deps/v8/src/profile-generator.h b/deps/v8/src/profile-generator.h index da1fdc33efb..0beb109e245 100644 --- a/deps/v8/src/profile-generator.h +++ b/deps/v8/src/profile-generator.h @@ -735,7 +735,8 @@ class HeapObjectsMap { static uint32_t AddressHash(Address addr) { return ComputeIntegerHash( - static_cast(reinterpret_cast(addr))); + static_cast(reinterpret_cast(addr)), + v8::internal::kZeroHashSeed); } bool initial_fill_mode_; @@ -836,7 +837,8 @@ class HeapEntriesMap { static uint32_t Hash(HeapThing thing) { return ComputeIntegerHash( - static_cast(reinterpret_cast(thing))); + static_cast(reinterpret_cast(thing)), + v8::internal::kZeroHashSeed); } static bool HeapThingsMatch(HeapThing key1, HeapThing key2) { return key1 == key2; @@ -1018,7 +1020,8 @@ class NativeObjectsExplorer : public HeapEntriesAllocator { void VisitSubtreeWrapper(Object** p, uint16_t class_id); static uint32_t InfoHash(v8::RetainedObjectInfo* info) { - return ComputeIntegerHash(static_cast(info->GetHash())); + return ComputeIntegerHash(static_cast(info->GetHash()), + v8::internal::kZeroHashSeed); } static bool RetainedInfosMatch(void* key1, void* key2) { return key1 == key2 || @@ -1096,7 +1099,8 @@ class HeapSnapshotJSONSerializer { INLINE(static uint32_t ObjectHash(const void* key)) { return ComputeIntegerHash( - static_cast(reinterpret_cast(key))); + static_cast(reinterpret_cast(key)), + v8::internal::kZeroHashSeed); } void EnumerateNodes(); diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index 813f98f684a..b1c4c102ec3 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -202,7 +202,7 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, break; } case DICTIONARY_ELEMENTS: { - NumberDictionary* element_dictionary = copy->element_dictionary(); + SeededNumberDictionary* element_dictionary = copy->element_dictionary(); int capacity = element_dictionary->Capacity(); for (int i = 0; i < capacity; i++) { Object* k = element_dictionary->KeyAt(i); @@ -978,14 +978,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOwnProperty) { holder = Handle(JSObject::cast(proto)); } FixedArray* elements = FixedArray::cast(holder->elements()); - NumberDictionary* dictionary = NULL; + SeededNumberDictionary* dictionary = NULL; if (elements->map() == heap->non_strict_arguments_elements_map()) { - dictionary = NumberDictionary::cast(elements->get(1)); + dictionary = SeededNumberDictionary::cast(elements->get(1)); } else { - dictionary = NumberDictionary::cast(elements); + dictionary = SeededNumberDictionary::cast(elements); } int entry = dictionary->FindEntry(index); - ASSERT(entry != NumberDictionary::kNotFound); + ASSERT(entry != SeededNumberDictionary::kNotFound); PropertyDetails details = dictionary->DetailsAt(entry); switch (details.type()) { case CALLBACKS: { @@ -4342,12 +4342,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) { return isolate->Throw(*error); } - Handle dictionary = NormalizeElements(js_object); + Handle dictionary = NormalizeElements(js_object); // Make sure that we never go back to fast case. dictionary->set_requires_slow_elements(); PropertyDetails details = PropertyDetails(attr, NORMAL); - Handle extended_dictionary = - NumberDictionarySet(dictionary, index, obj_value, details); + Handle extended_dictionary = + SeededNumberDictionarySet(dictionary, index, obj_value, details); if (*extended_dictionary != *dictionary) { if (js_object->GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS) { FixedArray::cast(js_object->elements())->set(1, *extended_dictionary); @@ -4408,12 +4408,12 @@ static MaybeObject* NormalizeObjectSetElement(Isolate* isolate, Handle value, PropertyAttributes attr) { // Normalize the elements to enable attributes on the property. - Handle dictionary = NormalizeElements(js_object); + Handle dictionary = NormalizeElements(js_object); // Make sure that we never go back to fast case. dictionary->set_requires_slow_elements(); PropertyDetails details = PropertyDetails(attr, NORMAL); - Handle extended_dictionary = - NumberDictionarySet(dictionary, index, value, details); + Handle extended_dictionary = + SeededNumberDictionarySet(dictionary, index, value, details); if (*extended_dictionary != *dictionary) { js_object->set_elements(*extended_dictionary); } @@ -9485,8 +9485,9 @@ class ArrayConcatVisitor { // Fall-through to dictionary mode. } ASSERT(!fast_elements_); - Handle dict(NumberDictionary::cast(*storage_)); - Handle result = + Handle dict( + SeededNumberDictionary::cast(*storage_)); + Handle result = isolate_->factory()->DictionaryAtNumberPut(dict, index, elm); if (!result.is_identical_to(dict)) { // Dictionary needed to grow. @@ -9524,14 +9525,15 @@ class ArrayConcatVisitor { void SetDictionaryMode(uint32_t index) { ASSERT(fast_elements_); Handle current_storage(*storage_); - Handle slow_storage( - isolate_->factory()->NewNumberDictionary(current_storage->length())); + Handle slow_storage( + isolate_->factory()->NewSeededNumberDictionary( + current_storage->length())); uint32_t current_length = static_cast(current_storage->length()); for (uint32_t i = 0; i < current_length; i++) { HandleScope loop_scope; Handle element(current_storage->get(i)); if (!element->IsTheHole()) { - Handle new_storage = + Handle new_storage = isolate_->factory()->DictionaryAtNumberPut(slow_storage, i, element); if (!new_storage.is_identical_to(slow_storage)) { slow_storage = loop_scope.CloseAndEscape(new_storage); @@ -9578,8 +9580,8 @@ static uint32_t EstimateElementCount(Handle array) { break; } case DICTIONARY_ELEMENTS: { - Handle dictionary( - NumberDictionary::cast(array->elements())); + Handle dictionary( + SeededNumberDictionary::cast(array->elements())); int capacity = dictionary->Capacity(); for (int i = 0; i < capacity; i++) { Handle key(dictionary->KeyAt(i)); @@ -9667,7 +9669,8 @@ static void CollectElementIndices(Handle object, break; } case DICTIONARY_ELEMENTS: { - Handle dict(NumberDictionary::cast(object->elements())); + Handle dict( + SeededNumberDictionary::cast(object->elements())); uint32_t capacity = dict->Capacity(); for (uint32_t j = 0; j < capacity; j++) { HandleScope loop_scope; @@ -9796,7 +9799,7 @@ static bool IterateElements(Isolate* isolate, break; } case DICTIONARY_ELEMENTS: { - Handle dict(receiver->element_dictionary()); + Handle dict(receiver->element_dictionary()); List indices(dict->Capacity() / 2); // Collect all indices in the object and the prototypes less // than length. This might introduce duplicates in the indices list. @@ -9945,7 +9948,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayConcat) { uint32_t at_least_space_for = estimate_nof_elements + (estimate_nof_elements >> 2); storage = Handle::cast( - isolate->factory()->NewNumberDictionary(at_least_space_for)); + isolate->factory()->NewSeededNumberDictionary(at_least_space_for)); } ArrayConcatVisitor visitor(isolate, storage, fast_case); @@ -10031,7 +10034,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_EstimateNumberOfElements) { CONVERT_CHECKED(JSObject, object, args[0]); HeapObject* elements = object->elements(); if (elements->IsDictionary()) { - return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements()); + int result = SeededNumberDictionary::cast(elements)->NumberOfElements(); + return Smi::FromInt(result); } else if (object->IsJSArray()) { return JSArray::cast(object)->length(); } else { diff --git a/deps/v8/src/stub-cache.cc b/deps/v8/src/stub-cache.cc index 55963303c44..cdb4874fcb8 100644 --- a/deps/v8/src/stub-cache.cc +++ b/deps/v8/src/stub-cache.cc @@ -860,7 +860,7 @@ MaybeObject* StubCache::ComputeCallGlobal(int argc, static Object* GetProbeValue(Isolate* isolate, Code::Flags flags) { // Use raw_unchecked... so we don't get assert failures during GC. - NumberDictionary* dictionary = + UnseededNumberDictionary* dictionary = isolate->heap()->raw_unchecked_non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate, flags); if (entry != -1) return dictionary->ValueAt(entry); @@ -882,7 +882,8 @@ MUST_USE_RESULT static MaybeObject* ProbeCache(Isolate* isolate, heap->undefined_value()); if (!maybe_result->ToObject(&result)) return maybe_result; } - heap->public_set_non_monomorphic_cache(NumberDictionary::cast(result)); + heap->public_set_non_monomorphic_cache( + UnseededNumberDictionary::cast(result)); return probe; } diff --git a/deps/v8/src/type-info.cc b/deps/v8/src/type-info.cc index c64368e5997..4df7ece086f 100644 --- a/deps/v8/src/type-info.cc +++ b/deps/v8/src/type-info.cc @@ -69,7 +69,7 @@ TypeFeedbackOracle::TypeFeedbackOracle(Handle code, Handle TypeFeedbackOracle::GetInfo(unsigned ast_id) { int entry = dictionary_->FindEntry(ast_id); - return entry != NumberDictionary::kNotFound + return entry != UnseededNumberDictionary::kNotFound ? Handle(dictionary_->ValueAt(entry)) : Isolate::Current()->factory()->undefined_value(); } @@ -470,7 +470,7 @@ void TypeFeedbackOracle::CreateDictionary(Handle code, ZoneList* infos) { DisableAssertNoAllocation allocation_allowed; byte* old_start = code->instruction_start(); - dictionary_ = FACTORY->NewNumberDictionary(infos->length()); + dictionary_ = FACTORY->NewUnseededNumberDictionary(infos->length()); byte* new_start = code->instruction_start(); RelocateRelocInfos(infos, old_start, new_start); } @@ -536,7 +536,7 @@ void TypeFeedbackOracle::ProcessTarget(unsigned ast_id, Code* target) { void TypeFeedbackOracle::SetInfo(unsigned ast_id, Object* target) { - ASSERT(dictionary_->FindEntry(ast_id) == NumberDictionary::kNotFound); + ASSERT(dictionary_->FindEntry(ast_id) == UnseededNumberDictionary::kNotFound); MaybeObject* maybe_result = dictionary_->AtNumberPut(ast_id, target); USE(maybe_result); #ifdef DEBUG diff --git a/deps/v8/src/type-info.h b/deps/v8/src/type-info.h index 448e4c94e7e..a0317402b0f 100644 --- a/deps/v8/src/type-info.h +++ b/deps/v8/src/type-info.h @@ -280,7 +280,7 @@ class TypeFeedbackOracle BASE_EMBEDDED { Handle GetInfo(unsigned ast_id); Handle global_context_; - Handle dictionary_; + Handle dictionary_; DISALLOW_COPY_AND_ASSIGN(TypeFeedbackOracle); }; diff --git a/deps/v8/src/utils.h b/deps/v8/src/utils.h index 26c522b89f2..cf7819e4a20 100644 --- a/deps/v8/src/utils.h +++ b/deps/v8/src/utils.h @@ -237,10 +237,13 @@ class BitField { // ---------------------------------------------------------------------------- // Hash function. +static const uint32_t kZeroHashSeed = 0; + // Thomas Wang, Integer Hash Functions. // http://www.concentric.net/~Ttwang/tech/inthash.htm -static inline uint32_t ComputeIntegerHash(uint32_t key) { +static inline uint32_t ComputeIntegerHash(uint32_t key, uint32_t seed) { uint32_t hash = key; + hash = hash ^ seed; hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1; hash = hash ^ (hash >> 12); hash = hash + (hash << 2); @@ -253,7 +256,8 @@ static inline uint32_t ComputeIntegerHash(uint32_t key) { static inline uint32_t ComputePointerHash(void* ptr) { return ComputeIntegerHash( - static_cast(reinterpret_cast(ptr))); + static_cast(reinterpret_cast(ptr)), + v8::internal::kZeroHashSeed); } diff --git a/deps/v8/src/v8globals.h b/deps/v8/src/v8globals.h index eb5c49d7513..bf843e5f361 100644 --- a/deps/v8/src/v8globals.h +++ b/deps/v8/src/v8globals.h @@ -131,7 +131,8 @@ class FixedArray; class FunctionEntry; class FunctionLiteral; class FunctionTemplateInfo; -class NumberDictionary; +class SeededNumberDictionary; +class UnseededNumberDictionary; class StringDictionary; template class Handle; class Heap; diff --git a/deps/v8/src/x64/code-stubs-x64.cc b/deps/v8/src/x64/code-stubs-x64.cc index 947cda4a0e8..347c17f7992 100644 --- a/deps/v8/src/x64/code-stubs-x64.cc +++ b/deps/v8/src/x64/code-stubs-x64.cc @@ -4610,7 +4610,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, Register character, Register scratch) { // hash = (seed + character) + ((seed + character) << 10); - __ LoadRoot(scratch, Heap::kStringHashSeedRootIndex); + __ LoadRoot(scratch, Heap::kHashSeedRootIndex); __ SmiToInteger32(scratch, scratch); __ addl(scratch, character); __ movl(hash, scratch); diff --git a/deps/v8/src/x64/macro-assembler-x64.cc b/deps/v8/src/x64/macro-assembler-x64.cc index 9cfc9b65887..8fcad232723 100644 --- a/deps/v8/src/x64/macro-assembler-x64.cc +++ b/deps/v8/src/x64/macro-assembler-x64.cc @@ -3210,6 +3210,42 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, } +void MacroAssembler::GetNumberHash(Register r0, Register scratch) { + // First of all we assign the hash seed to scratch. + LoadRoot(scratch, Heap::kHashSeedRootIndex); + SmiToInteger32(scratch, scratch); + + // Xor original key with a seed. + xorl(r0, scratch); + + // Compute the hash code from the untagged key. This must be kept in sync + // with ComputeIntegerHash in utils.h. + // + // hash = ~hash + (hash << 15); + movl(scratch, r0); + notl(r0); + shll(scratch, Immediate(15)); + addl(r0, scratch); + // hash = hash ^ (hash >> 12); + movl(scratch, r0); + shrl(scratch, Immediate(12)); + xorl(r0, scratch); + // hash = hash + (hash << 2); + leal(r0, Operand(r0, r0, times_4, 0)); + // hash = hash ^ (hash >> 4); + movl(scratch, r0); + shrl(scratch, Immediate(4)); + xorl(r0, scratch); + // hash = hash * 2057; + imull(r0, r0, Immediate(2057)); + // hash = hash ^ (hash >> 16); + movl(scratch, r0); + shrl(scratch, Immediate(16)); + xorl(r0, scratch); +} + + + void MacroAssembler::LoadFromNumberDictionary(Label* miss, Register elements, Register key, @@ -3240,34 +3276,11 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, Label done; - // Compute the hash code from the untagged key. This must be kept in sync - // with ComputeIntegerHash in utils.h. - // - // hash = ~hash + (hash << 15); - movl(r1, r0); - notl(r0); - shll(r1, Immediate(15)); - addl(r0, r1); - // hash = hash ^ (hash >> 12); - movl(r1, r0); - shrl(r1, Immediate(12)); - xorl(r0, r1); - // hash = hash + (hash << 2); - leal(r0, Operand(r0, r0, times_4, 0)); - // hash = hash ^ (hash >> 4); - movl(r1, r0); - shrl(r1, Immediate(4)); - xorl(r0, r1); - // hash = hash * 2057; - imull(r0, r0, Immediate(2057)); - // hash = hash ^ (hash >> 16); - movl(r1, r0); - shrl(r1, Immediate(16)); - xorl(r0, r1); + GetNumberHash(r0, r1); // Compute capacity mask. - SmiToInteger32(r1, - FieldOperand(elements, NumberDictionary::kCapacityOffset)); + SmiToInteger32(r1, FieldOperand(elements, + SeededNumberDictionary::kCapacityOffset)); decl(r1); // Generate an unrolled loop that performs a few probes before giving up. @@ -3277,19 +3290,19 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, movq(r2, r0); // Compute the masked index: (hash + i + i * i) & mask. if (i > 0) { - addl(r2, Immediate(NumberDictionary::GetProbeOffset(i))); + addl(r2, Immediate(SeededNumberDictionary::GetProbeOffset(i))); } and_(r2, r1); // Scale the index by multiplying by the entry size. - ASSERT(NumberDictionary::kEntrySize == 3); + ASSERT(SeededNumberDictionary::kEntrySize == 3); lea(r2, Operand(r2, r2, times_2, 0)); // r2 = r2 * 3 // Check if the key matches. cmpq(key, FieldOperand(elements, r2, times_pointer_size, - NumberDictionary::kElementsStartOffset)); + SeededNumberDictionary::kElementsStartOffset)); if (i != (kProbes - 1)) { j(equal, &done); } else { @@ -3300,7 +3313,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, bind(&done); // Check that the value is a normal propety. const int kDetailsOffset = - NumberDictionary::kElementsStartOffset + 2 * kPointerSize; + SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize; ASSERT_EQ(NORMAL, 0); Test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset), Smi::FromInt(PropertyDetails::TypeField::kMask)); @@ -3308,7 +3321,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, // Get the value at the masked, scaled index. const int kValueOffset = - NumberDictionary::kElementsStartOffset + kPointerSize; + SeededNumberDictionary::kElementsStartOffset + kPointerSize; movq(result, FieldOperand(elements, r2, times_pointer_size, kValueOffset)); } diff --git a/deps/v8/src/x64/macro-assembler-x64.h b/deps/v8/src/x64/macro-assembler-x64.h index e7eb104c0b8..ff6edc5b335 100644 --- a/deps/v8/src/x64/macro-assembler-x64.h +++ b/deps/v8/src/x64/macro-assembler-x64.h @@ -845,6 +845,7 @@ class MacroAssembler: public Assembler { Register scratch, Label* miss); + void GetNumberHash(Register r0, Register scratch); void LoadFromNumberDictionary(Label* miss, Register elements, diff --git a/deps/v8/test/cctest/test-hashing.cc b/deps/v8/test/cctest/test-hashing.cc index 9aa84614d6b..a6265105eb7 100644 --- a/deps/v8/test/cctest/test-hashing.cc +++ b/deps/v8/test/cctest/test-hashing.cc @@ -117,6 +117,41 @@ void generate(MacroAssembler* masm, i::Vector string) { } +void generate(MacroAssembler* masm, uint32_t key) { +#ifdef V8_TARGET_ARCH_IA32 + __ push(ebx); + __ mov(eax, Immediate(key)); + __ GetNumberHash(eax, ebx); + __ pop(ebx); + __ Ret(); +#elif V8_TARGET_ARCH_X64 + __ push(kRootRegister); + __ InitializeRootRegister(); + __ push(rbx); + __ movq(rax, Immediate(key)); + __ GetNumberHash(rax, rbx); + __ pop(rbx); + __ pop(kRootRegister); + __ Ret(); +#elif V8_TARGET_ARCH_ARM + __ push(kRootRegister); + __ InitializeRootRegister(); + __ mov(r0, Operand(key)); + __ GetNumberHash(r0, ip); + __ pop(kRootRegister); + __ mov(pc, Operand(lr)); +#elif V8_TARGET_ARCH_MIPS + __ push(kRootRegister); + __ InitializeRootRegister(); + __ li(v0, Operand(key)); + __ GetNumberHash(v0, t1); + __ pop(kRootRegister); + __ jr(ra); + __ nop(); +#endif +} + + void check(i::Vector string) { v8::HandleScope scope; v8::internal::byte buffer[2048]; @@ -146,12 +181,47 @@ void check(i::Vector string) { } +void check(uint32_t key) { + v8::HandleScope scope; + v8::internal::byte buffer[2048]; + MacroAssembler masm(Isolate::Current(), buffer, sizeof buffer); + + generate(&masm, key); + + CodeDesc desc; + masm.GetCode(&desc); + Code* code = Code::cast(HEAP->CreateCode( + desc, + Code::ComputeFlags(Code::STUB), + Handle(HEAP->undefined_value()))->ToObjectChecked()); + CHECK(code->IsCode()); + + HASH_FUNCTION hash = FUNCTION_CAST(code->entry()); +#ifdef USE_SIMULATOR + uint32_t codegen_hash = + reinterpret_cast(CALL_GENERATED_CODE(hash, 0, 0, 0, 0, 0)); +#else + uint32_t codegen_hash = hash(); +#endif + + uint32_t runtime_hash = ComputeIntegerHash( + key, + Isolate::Current()->heap()->HashSeed()); + CHECK(runtime_hash == codegen_hash); +} + + void check_twochars(char a, char b) { char ab[2] = {a, b}; check(i::Vector(ab, 2)); } +static uint32_t PseudoRandom(uint32_t i, uint32_t j) { + return ~(~((i * 781) ^ (j * 329))); +} + + TEST(StringHash) { if (env.IsEmpty()) env = v8::Context::New(); for (int a = 0; a < String::kMaxAsciiCharCode; a++) { @@ -169,4 +239,22 @@ TEST(StringHash) { check(i::Vector("-=[ vee eight ftw ]=-", 21)); } + +TEST(NumberHash) { + if (env.IsEmpty()) env = v8::Context::New(); + + // Some specific numbers + for (uint32_t key = 0; key < 42; key += 7) { + check(key); + } + + // Some pseudo-random numbers + static const uint32_t kLimit = 1000; + for (uint32_t i = 0; i < 5; i++) { + for (uint32_t j = 0; j < 5; j++) { + check(PseudoRandom(i, j) % kLimit); + } + } +} + #undef __ From ca6ededbd1af5cc759ec98952b84c0b34edb2d40 Mon Sep 17 00:00:00 2001 From: Andreas Madsen Date: Tue, 17 Jan 2012 08:04:50 +0100 Subject: [PATCH 04/30] child_process: add errno property to exceptions In case of a write failure when using fork() an error would be thrown. The thrown exception was missing the `errno` property. --- lib/child_process.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/child_process.js b/lib/child_process.js index 55412b94949..145a34ff4b9 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -132,7 +132,7 @@ function setupChannel(target, channel) { var writeReq = channel.write(buffer, 0, buffer.length, sendHandle); if (!writeReq) { - throw new Error(errno + 'cannot write to IPC channel.'); + throw errnoException(errno, 'write', 'cannot write to IPC channel.'); } writeReq.oncomplete = nop; @@ -471,11 +471,15 @@ ChildProcess.prototype.spawn = function(options) { }; -function errnoException(errorno, syscall) { +function errnoException(errorno, syscall, errmsg) { // TODO make this more compatible with ErrnoException from src/node.cc // Once all of Node is using this function the ErrnoException from // src/node.cc should be removed. - var e = new Error(syscall + ' ' + errorno); + var message = syscall + ' ' + errorno; + if (errmsg) { + message += ' - ' + errmsg; + } + var e = new Error(message); e.errno = e.code = errorno; e.syscall = syscall; return e; From 48650639248e73f78b2bd4ba2d3d1590749753ce Mon Sep 17 00:00:00 2001 From: Andreas Madsen Date: Tue, 17 Jan 2012 19:43:34 +0100 Subject: [PATCH 05/30] Tests: fix jslint issues --- test/internet/test-dns.js | 4 +- test/message/throw_custom_error.js | 2 +- test/message/throw_non_error.js | 2 +- test/pummel/test-net-timeout2.js | 14 ++-- test/simple/test-assert.js | 2 +- test/simple/test-child-process-fork2.js | 6 +- test/simple/test-cluster-kill-workers.js | 18 ++--- test/simple/test-crypto-ecb.js | 9 +-- test/simple/test-dgram-send-error.js | 4 +- test/simple/test-eval.js | 4 +- test/simple/test-fs-long-path.js | 6 +- test/simple/test-fs-mkdir.js | 2 +- test/simple/test-fs-symlink.js | 10 +-- test/simple/test-fs-utimes.js | 10 +-- test/simple/test-http-abort-before-end.js | 2 +- ...test-http-res-write-end-dont-take-array.js | 6 +- test/simple/test-http-response-no-headers.js | 9 ++- test/simple/test-init.js | 15 ++-- test/simple/test-module-load-list.js | 13 +-- test/simple/test-net-connect-buffer.js | 2 +- test/simple/test-net-pipe-connect-errors.js | 26 +++--- test/simple/test-path.js | 10 ++- test/simple/test-punycode.js | 81 ++++++++++--------- test/simple/test-readdir.js | 2 +- test/simple/test-regress-GH-1899.js | 10 ++- test/simple/test-regress-GH-877.js | 15 ++-- test/simple/test-repl-.save.load.js | 23 +++--- test/simple/test-setproctitle.js | 4 +- test/simple/test-stdin-pause-resume.js | 12 +-- test/simple/test-tls-passphrase.js | 2 +- test/simple/test-tls-session-cache.js | 6 +- test/simple/test-tls-set-ciphers.js | 3 +- test/simple/test-tty-stdout-end.js | 2 +- test/simple/test-util-inspect.js | 6 +- test/simple/test-util.js | 74 ++++++++--------- test/simple/test-zlib-from-gzip.js | 2 +- test/simple/test-zlib-from-string.js | 35 ++++++-- test/simple/test-zlib-random-byte-pipes.js | 14 ++-- 38 files changed, 251 insertions(+), 216 deletions(-) diff --git a/test/internet/test-dns.js b/test/internet/test-dns.js index 095b47ad664..9da388cb54f 100644 --- a/test/internet/test-dns.js +++ b/test/internet/test-dns.js @@ -383,11 +383,11 @@ TEST(function test_lookup_localhost_ipv4(done) { var getaddrinfoCallbackCalled = false; -console.log("looking up nodejs.org..."); +console.log('looking up nodejs.org...'); var req = process.binding('cares_wrap').getaddrinfo('nodejs.org'); req.oncomplete = function(domains) { - console.log("nodejs.org = ", domains); + console.log('nodejs.org = ', domains); assert.ok(Array.isArray(domains)); assert.ok(domains.length >= 1); assert.ok(typeof domains[0] == 'string'); diff --git a/test/message/throw_custom_error.js b/test/message/throw_custom_error.js index 5c994c009db..bef7f5dab01 100644 --- a/test/message/throw_custom_error.js +++ b/test/message/throw_custom_error.js @@ -28,6 +28,6 @@ var assert = require('assert'); common.error('before'); // custom error throwing -throw { name: 'MyCustomError', message: 'This is a custom message' } +throw { name: 'MyCustomError', message: 'This is a custom message' }; common.error('after'); diff --git a/test/message/throw_non_error.js b/test/message/throw_non_error.js index 702a40d9b6d..afea96f49ce 100644 --- a/test/message/throw_non_error.js +++ b/test/message/throw_non_error.js @@ -28,6 +28,6 @@ var assert = require('assert'); common.error('before'); // custom error throwing -throw { foo: 'bar' } +throw { foo: 'bar' }; common.error('after'); diff --git a/test/pummel/test-net-timeout2.js b/test/pummel/test-net-timeout2.js index 2e2568b490c..16f69cc022a 100644 --- a/test/pummel/test-net-timeout2.js +++ b/test/pummel/test-net-timeout2.js @@ -32,12 +32,12 @@ var counter = 0; var server = net.createServer(function(socket) { socket.setTimeout((seconds / 2) * 1000, function() { gotTimeout = true; - console.log('timeout!!'); - socket.destroy(); + console.log('timeout!!'); + socket.destroy(); process.exit(1); - }); + }); - var interval = setInterval(function() { + var interval = setInterval(function() { counter++; if (counter == seconds) { @@ -46,9 +46,9 @@ var server = net.createServer(function(socket) { socket.destroy(); } - if (socket.writable) { - socket.write(Date.now()+'\n'); - } + if (socket.writable) { + socket.write(Date.now() + '\n'); + } }, 1000); }); diff --git a/test/simple/test-assert.js b/test/simple/test-assert.js index 9a3b021f3ce..df8d810204d 100644 --- a/test/simple/test-assert.js +++ b/test/simple/test-assert.js @@ -197,7 +197,7 @@ threw = false; try { assert.throws( function() { - throw {} + throw {}; }, Array ); diff --git a/test/simple/test-child-process-fork2.js b/test/simple/test-child-process-fork2.js index 3116320830f..6f292fa1992 100644 --- a/test/simple/test-child-process-fork2.js +++ b/test/simple/test-child-process-fork2.js @@ -47,12 +47,12 @@ server.listen(common.PORT, function() { function makeConnections() { for (var i = 0; i < N; i++) { var socket = net.connect(common.PORT, function() { - console.log("CLIENT connected"); + console.log('CLIENT connected'); }); - socket.on("close", function() { + socket.on('close', function() { socketCloses++; - console.log("CLIENT closed " + socketCloses); + console.log('CLIENT closed ' + socketCloses); if (socketCloses == N) { n.kill(); server.close(); diff --git a/test/simple/test-cluster-kill-workers.js b/test/simple/test-cluster-kill-workers.js index c9f2bf260b9..39846fab035 100644 --- a/test/simple/test-cluster-kill-workers.js +++ b/test/simple/test-cluster-kill-workers.js @@ -32,17 +32,17 @@ var fork = require('child_process').fork; var isTestRunner = process.argv[2] != 'child'; if (isTestRunner) { - console.log("starting master..."); - var master = fork(__filename, [ 'child' ]); + console.log('starting master...'); + var master = fork(__filename, ['child']); - console.log("master pid =", master.pid); + console.log('master pid =', master.pid); var workerPID; - master.on("message", function(m) { - console.log("got message from master:", m); + master.on('message', function(m) { + console.log('got message from master:', m); if (m.workerPID) { - console.log("worker pid =", m.workerPID); + console.log('worker pid =', m.workerPID); workerPID = m.workerPID; } }); @@ -56,13 +56,13 @@ if (isTestRunner) { assert(workerPID > 0); try { process.kill(workerPID, 0); - } catch(e) { + } catch (e) { // workerPID is no longer running - console.log(e) + console.log(e); assert(e.code == 'ESRCH'); gotKillException = true; } - }) + }); process.on('exit', function() { assert(gotExit); diff --git a/test/simple/test-crypto-ecb.js b/test/simple/test-crypto-ecb.js index 23a5e682f12..1ffaa4f24be 100644 --- a/test/simple/test-crypto-ecb.js +++ b/test/simple/test-crypto-ecb.js @@ -35,17 +35,16 @@ try { // Testing whether EVP_CipherInit_ex is functioning correctly. // Reference: bug#1997 -(function() -{ +(function() { var encrypt = crypto.createCipheriv('BF-ECB', 'SomeRandomBlahz0c5GZVnR', ''); var hex = encrypt.update('Hello World!', 'ascii', 'hex'); hex += encrypt.final('hex'); assert.equal(hex.toUpperCase(), '6D385F424AAB0CFBF0BB86E07FFB7D71'); }()); -(function() -{ - var decrypt = crypto.createDecipheriv('BF-ECB', 'SomeRandomBlahz0c5GZVnR', ''); +(function() { + var decrypt = crypto.createDecipheriv('BF-ECB', 'SomeRandomBlahz0c5GZVnR', + ''); var msg = decrypt.update('6D385F424AAB0CFBF0BB86E07FFB7D71', 'hex', 'ascii'); msg += decrypt.final('ascii'); assert.equal(msg, 'Hello World!'); diff --git a/test/simple/test-dgram-send-error.js b/test/simple/test-dgram-send-error.js index 803a371fccc..346f36dbe29 100644 --- a/test/simple/test-dgram-send-error.js +++ b/test/simple/test-dgram-send-error.js @@ -73,9 +73,9 @@ function doSend() { } process.on('exit', function() { - console.log(packetsSent + ' UDP packets sent, ' + + console.log(packetsSent + ' UDP packets sent, ' + packetsReceived + ' received'); - + assert.strictEqual(packetsSent, ITERATIONS * 2); assert.strictEqual(packetsReceived, ITERATIONS); }); diff --git a/test/simple/test-eval.js b/test/simple/test-eval.js index a2e56e02c98..9c656a8770b 100644 --- a/test/simple/test-eval.js +++ b/test/simple/test-eval.js @@ -30,8 +30,8 @@ var exec = require('child_process').exec; var success_count = 0; var error_count = 0; -var cmd = [process.execPath, '-e', '"console.error(process.argv)"', 'foo', 'bar'] - .join(' '); +var cmd = [process.execPath, '-e', '"console.error(process.argv)"', + 'foo', 'bar'].join(' '); var expected = util.format([process.execPath, 'foo', 'bar']) + '\n'; var child = exec(cmd, function(err, stdout, stderr) { if (err) { diff --git a/test/simple/test-fs-long-path.js b/test/simple/test-fs-long-path.js index 2ae80d0edbc..eac58f68e5b 100644 --- a/test/simple/test-fs-long-path.js +++ b/test/simple/test-fs-long-path.js @@ -31,8 +31,10 @@ var fileNameLen = Math.max(260 - common.tmpDir.length - 1, 1); var fileName = path.join(common.tmpDir, new Array(fileNameLen + 1).join('x')); var fullPath = path.resolve(fileName); -console.log({ filenameLength: fileName.length, - fullPathLength: fullPath.length }); +console.log({ + filenameLength: fileName.length, + fullPathLength: fullPath.length +}); fs.writeFile(fullPath, 'ok', function(err) { if (err) throw err; diff --git a/test/simple/test-fs-mkdir.js b/test/simple/test-fs-mkdir.js index 324c08a5f91..176cc4cf8d3 100644 --- a/test/simple/test-fs-mkdir.js +++ b/test/simple/test-fs-mkdir.js @@ -81,4 +81,4 @@ function unlink(pathname) { // Keep the event loop alive so the async mkdir() requests // have a chance to run (since they don't ref the event loop). -process.nextTick(function(){}); +process.nextTick(function() {}); diff --git a/test/simple/test-fs-symlink.js b/test/simple/test-fs-symlink.js index 83bf13e4943..6b23b1e1c10 100644 --- a/test/simple/test-fs-symlink.js +++ b/test/simple/test-fs-symlink.js @@ -38,8 +38,7 @@ var runtest = function(skip_symlinks) { // Delete previously created link try { fs.unlinkSync(linkPath); - } catch(e) - {} + } catch (e) {} fs.symlink(linkData, linkPath, function(err) { if (err) throw err; @@ -60,8 +59,7 @@ var runtest = function(skip_symlinks) { // Delete previously created link try { fs.unlinkSync(dstPath); - } catch(e) - {} + } catch (e) {} fs.link(srcPath, dstPath, function(err) { if (err) throw err; @@ -71,12 +69,12 @@ var runtest = function(skip_symlinks) { assert.equal(srcContent, dstContent); completed++; }); -} +}; if (is_windows) { // On Windows, creating symlinks requires admin privileges. // We'll only try to run symlink test if we have enough privileges. - exec("whoami /priv", function(err, o) { + exec('whoami /priv', function(err, o) { if (err || o.indexOf('SeCreateSymbolicLinkPrivilege') == -1) { expected_tests = 1; runtest(true); diff --git a/test/simple/test-fs-utimes.js b/test/simple/test-fs-utimes.js index df75fcb6413..eae80238bc1 100644 --- a/test/simple/test-fs-utimes.js +++ b/test/simple/test-fs-utimes.js @@ -68,7 +68,7 @@ function expect_ok(syscall, resource, err, atime, mtime) { // the tests assume that __filename belongs to the user running the tests // this should be a fairly safe assumption; testing against a temp file // would be even better though (node doesn't have such functionality yet) -function runTests(atime, mtime, callback) { +function runTest(atime, mtime, callback) { var fd, err; // @@ -144,10 +144,10 @@ function runTests(atime, mtime, callback) { var stats = fs.statSync(__filename); -runTests(new Date('1982-09-10 13:37'), new Date('1982-09-10 13:37'), function() { - runTests(new Date(), new Date(), function() { - runTests(123456.789, 123456.789, function() { - runTests(stats.mtime, stats.mtime, function() { +runTest(new Date('1982-09-10 13:37'), new Date('1982-09-10 13:37'), function() { + runTest(new Date(), new Date(), function() { + runTest(123456.789, 123456.789, function() { + runTest(stats.mtime, stats.mtime, function() { // done }); }); diff --git a/test/simple/test-http-abort-before-end.js b/test/simple/test-http-abort-before-end.js index e701ed90500..11e424fbe1a 100644 --- a/test/simple/test-http-abort-before-end.js +++ b/test/simple/test-http-abort-before-end.js @@ -28,7 +28,7 @@ var server = http.createServer(function(req, res) { }); server.listen(common.PORT, function() { - var req = http.request({method:'GET', host:'127.0.0.1', port:common.PORT}); + var req = http.request({method: 'GET', host: '127.0.0.1', port: common.PORT}); req.on('error', function(ex) { // https://github.com/joyent/node/issues/1399#issuecomment-2597359 diff --git a/test/simple/test-http-res-write-end-dont-take-array.js b/test/simple/test-http-res-write-end-dont-take-array.js index 2a7c025a067..f4b3f8ccee4 100644 --- a/test/simple/test-http-res-write-end-dont-take-array.js +++ b/test/simple/test-http-res-write-end-dont-take-array.js @@ -25,7 +25,7 @@ var http = require('http'); var test = 1; -var server = http.createServer(function (req, res) { +var server = http.createServer(function(req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); if (test === 1) { // write should accept string @@ -53,11 +53,11 @@ var server = http.createServer(function (req, res) { server.listen(common.PORT, function() { // just make a request, other tests handle responses - http.get({port:common.PORT}, function() { + http.get({port: common.PORT}, function() { // lazy serial test, becuase we can only call end once per request test += 1; // do it again to test .end(Buffer); - http.get({port:common.PORT}, function() { + http.get({port: common.PORT}, function() { server.close(); }); }); diff --git a/test/simple/test-http-response-no-headers.js b/test/simple/test-http-response-no-headers.js index 95bd01aed56..cc5250d826f 100644 --- a/test/simple/test-http-response-no-headers.js +++ b/test/simple/test-http-response-no-headers.js @@ -27,8 +27,8 @@ var net = require('net'); var expected = { '0.9': 'I AM THE WALRUS', '1.0': 'I AM THE WALRUS', - '1.1': '', -} + '1.1': '' +}; var gotExpected = false; @@ -38,11 +38,12 @@ function test(httpVersion, callback) { }); var server = net.createServer(function(conn) { - var reply = 'HTTP/' + httpVersion + ' 200 OK\r\n\r\n' + expected[httpVersion]; + var reply = 'HTTP/' + httpVersion + ' 200 OK\r\n\r\n' + + expected[httpVersion]; conn.write(reply, function() { conn.destroy(); - }) + }); }); server.listen(common.PORT, '127.0.0.1', function() { diff --git a/test/simple/test-init.js b/test/simple/test-init.js index 0aaa0d3d5a9..da059846d15 100644 --- a/test/simple/test-init.js +++ b/test/simple/test-init.js @@ -36,11 +36,13 @@ child.exec(process.execPath + ' test-init', {env: {'TEST_INIT': 1}}, function(err, stdout, stderr) { - assert.equal(stdout, 'Loaded successfully!', '`node test-init` failed!'); + assert.equal(stdout, 'Loaded successfully!', + '`node test-init` failed!'); }); child.exec(process.execPath + ' test-init.js', {env: {'TEST_INIT': 1}}, function(err, stdout, stderr) { - assert.equal(stdout, 'Loaded successfully!', '`node test-init.js` failed!'); + assert.equal(stdout, 'Loaded successfully!', + '`node test-init.js` failed!'); }); // test-init-index is in fixtures dir as requested by ry, so go there @@ -48,16 +50,19 @@ child.exec(process.execPath + ' test-init-index', {env: {'TEST_INIT': 1}}, function(err, stdout, stderr) { - assert.equal(stdout, 'Loaded successfully!', '`node test-init-index failed!'); + assert.equal(stdout, 'Loaded successfully!', + '`node test-init-index failed!'); }); // ensures that `node fs` does not mistakenly load the native 'fs' module - // instead of the desired file and that the fs module loads as expected in node + // instead of the desired file and that the fs module loads as + // expected in node process.chdir(common.fixturesDir + '/test-init-native/'); child.exec(process.execPath + ' fs', {env: {'TEST_INIT': 1}}, function(err, stdout, stderr) { - assert.equal(stdout, 'fs loaded successfully', '`node fs` failed!'); + assert.equal(stdout, 'fs loaded successfully', + '`node fs` failed!'); }); } })(); diff --git a/test/simple/test-module-load-list.js b/test/simple/test-module-load-list.js index 0ecf7a8798d..a2a1b332306 100644 --- a/test/simple/test-module-load-list.js +++ b/test/simple/test-module-load-list.js @@ -24,19 +24,20 @@ // beginning of this file. function assertEqual(x, y) { - if (x !== y) throw new Error("Expected '" + x + "' got '" + y + "'"); + if (x !== y) throw new Error('Expected \'' + x + '\' got \'' + y + '\''); } function checkExpected() { var toCompare = Math.max(expected.length, process.moduleLoadList.length); for (var i = 0; i < toCompare; i++) { if (expected[i] !== process.moduleLoadList[i]) { - console.error("process.moduleLoadList[" + i + "] = " + process.moduleLoadList[i]); - console.error("expected[" + i + "] = " + expected[i]); + console.error('process.moduleLoadList[' + i + '] = ' + + process.moduleLoadList[i]); + console.error('expected[' + i + '] = ' + expected[i]); - console.error("process.moduleLoadList", process.moduleLoadList); - console.error("expected = ", expected); - throw new Error("mismatch"); + console.error('process.moduleLoadList', process.moduleLoadList); + console.error('expected = ', expected); + throw new Error('mismatch'); } } } diff --git a/test/simple/test-net-connect-buffer.js b/test/simple/test-net-connect-buffer.js index 75486af9741..d0878ebb4c1 100644 --- a/test/simple/test-net-connect-buffer.js +++ b/test/simple/test-net-connect-buffer.js @@ -85,7 +85,7 @@ tcp.listen(common.PORT, function() { // Write a string that contains a multi-byte character sequence to test that // `bytesWritten` is incremented with the # of bytes, not # of characters. var a = "L'État, c'est "; - var b = "moi"; + var b = 'moi'; // We're still connecting at this point so the datagram is first pushed onto // the connect queue. Make sure that it's not added to `bytesWritten` again diff --git a/test/simple/test-net-pipe-connect-errors.js b/test/simple/test-net-pipe-connect-errors.js index f5896bf8896..b659f439832 100644 --- a/test/simple/test-net-pipe-connect-errors.js +++ b/test/simple/test-net-pipe-connect-errors.js @@ -31,42 +31,40 @@ var accessErrorFired = false; // Test if ENOTSOCK is fired when trying to connect to a file which is not // a socket. -var notSocketClient = net.createConnection( - path.join(common.fixturesDir, 'empty.txt'), - function () { - assert.ok(false); - } -); +var emptyTxt = path.join(common.fixturesDir, 'empty.txt'); +var notSocketClient = net.createConnection(emptyTxt, function() { + assert.ok(false); +}); -notSocketClient.on('error', function (err) { +notSocketClient.on('error', function(err) { assert(err.code === 'ENOTSOCK' || err.code === 'ECONNREFUSED'); notSocketErrorFired = true; }); // Trying to connect to not-existing socket should result in ENOENT error -var noEntSocketClient = net.createConnection('no-ent-file', function () { +var noEntSocketClient = net.createConnection('no-ent-file', function() { assert.ok(false); }); -noEntSocketClient.on('error', function (err) { +noEntSocketClient.on('error', function(err) { assert.equal(err.code, 'ENOENT'); noEntErrorFired = true; }); // Trying to connect to a socket one has no access to should result in EACCES -var accessServer = net.createServer(function () { +var accessServer = net.createServer(function() { assert.ok(false); }); -accessServer.listen(common.PIPE, function () { +accessServer.listen(common.PIPE, function() { fs.chmodSync(common.PIPE, 0); - var accessClient = net.createConnection(common.PIPE, function () { + var accessClient = net.createConnection(common.PIPE, function() { assert.ok(false); }); - accessClient.on('error', function (err) { + accessClient.on('error', function(err) { assert.equal(err.code, 'EACCES'); accessErrorFired = true; accessServer.close(); @@ -75,7 +73,7 @@ accessServer.listen(common.PIPE, function () { // Assert that all error events were fired -process.on('exit', function () { +process.on('exit', function() { assert.ok(notSocketErrorFired); assert.ok(noEntErrorFired); assert.ok(accessErrorFired); diff --git a/test/simple/test-path.js b/test/simple/test-path.js index e16b459d924..1ee78dad68f 100644 --- a/test/simple/test-path.js +++ b/test/simple/test-path.js @@ -35,12 +35,14 @@ assert.equal(path.basename(f, '.js'), 'test-path'); // c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html if (!isWindows) { var controlCharFilename = 'Icon' + String.fromCharCode(13); - assert.equal(path.basename('/a/b/' + controlCharFilename), controlCharFilename); + assert.equal(path.basename('/a/b/' + controlCharFilename), + controlCharFilename); } assert.equal(path.extname(f), '.js'); -assert.equal(path.dirname(f).substr(-11), isWindows ? 'test\\simple' : 'test/simple'); +assert.equal(path.dirname(f).substr(-11), + isWindows ? 'test\\simple' : 'test/simple'); assert.equal(path.dirname('/a/b/'), '/a'); assert.equal(path.dirname('/a/b'), '/a'); assert.equal(path.dirname('/a'), '/'); @@ -237,7 +239,9 @@ var failures = []; relativeTests.forEach(function(test) { var actual = path.relative(test[0], test[1]); var expected = test[2]; - var message = 'path.relative(' + test.slice(0, 2).map(JSON.stringify).join(',') + ')' + + var message = 'path.relative(' + + test.slice(0, 2).map(JSON.stringify).join(',') + + ')' + '\n expect=' + JSON.stringify(expected) + '\n actual=' + JSON.stringify(actual); if (actual !== expected) failures.push('\n' + message); diff --git a/test/simple/test-punycode.js b/test/simple/test-punycode.js index 9a144c58a10..1fd67105d6a 100644 --- a/test/simple/test-punycode.js +++ b/test/simple/test-punycode.js @@ -62,101 +62,102 @@ assert.equal(punycode.decode('wgv71a119e'), '日本語'); var tests = { // (A) Arabic (Egyptian) 'egbpdaj6bu4bxfgehfvwxn': - '\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644\u0645\u0648' + - '\u0634\u0639\u0631\u0628\u064A\u061F', + '\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644\u0645\u0648' + + '\u0634\u0639\u0631\u0628\u064A\u061F', // (B) Chinese (simplified) 'ihqwcrb4cv8a8dqg056pqjye': - '\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2D\u6587', + '\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2D\u6587', // (C) Chinese (traditional) 'ihqwctvzc91f659drss3x8bo0yb': - '\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587', + '\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587', // (D) Czech: Proprostnemluvesky 'Proprostnemluvesky-uyb24dma41a': - '\u0050\u0072\u006F\u010D\u0070\u0072\u006F\u0073\u0074\u011B\u006E' + - '\u0065\u006D\u006C\u0075\u0076\u00ED\u010D\u0065\u0073\u006B\u0079', + '\u0050\u0072\u006F\u010D\u0070\u0072\u006F\u0073\u0074\u011B\u006E' + + '\u0065\u006D\u006C\u0075\u0076\u00ED\u010D\u0065\u0073\u006B\u0079', // (E) Hebrew '4dbcagdahymbxekheh6e0a7fei0b': - '\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8\u05DC\u05D0' + - '\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2\u05D1\u05E8\u05D9\u05EA', + '\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8\u05DC\u05D0' + + '\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2\u05D1\u05E8\u05D9\u05EA', // (F) Hindi (Devanagari) 'i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd': - '\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D\u0926\u0940' + - '\u0915\u094D\u092F\u094B\u0902\u0928\u0939\u0940\u0902\u092C\u094B' + - '\u0932\u0938\u0915\u0924\u0947\u0939\u0948\u0902', + '\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D\u0926\u0940' + + '\u0915\u094D\u092F\u094B\u0902\u0928\u0939\u0940\u0902\u092C\u094B' + + '\u0932\u0938\u0915\u0924\u0947\u0939\u0948\u0902', // (G) Japanese (kanji and hiragana) 'n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa': - '\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092\u8A71\u3057' + - '\u3066\u304F\u308C\u306A\u3044\u306E\u304B', + '\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092\u8A71\u3057' + + '\u3066\u304F\u308C\u306A\u3044\u306E\u304B', // (H) Korean (Hangul syllables) '989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5jpsd879ccm6fea98c': - '\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774\uD55C\uAD6D' + - '\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74\uC5BC\uB9C8\uB098\uC88B' + - '\uC744\uAE4C', + '\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774\uD55C\uAD6D' + + '\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74\uC5BC\uB9C8\uB098\uC88B' + + '\uC744\uAE4C', // (I) Russian (Cyrillic) /* XXX disabled, fails - possibly a bug in the RFC 'b1abfaaepdrnnbgefbaDotcwatmq2g4l': - '\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E\u043D\u0438' + - '\u043D\u0435\u0433\u043E\u0432\u043E\u0440\u044F\u0442\u043F\u043E' + - '\u0440\u0443\u0441\u0441\u043A\u0438', + '\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E\u043D\u0438' + + '\u043D\u0435\u0433\u043E\u0432\u043E\u0440\u044F\u0442\u043F\u043E' + + '\u0440\u0443\u0441\u0441\u043A\u0438', */ // (J) Spanish: PorqunopuedensimplementehablarenEspaol 'PorqunopuedensimplementehablarenEspaol-fmd56a': - '\u0050\u006F\u0072\u0071\u0075\u00E9\u006E\u006F\u0070\u0075\u0065' + - '\u0064\u0065\u006E\u0073\u0069\u006D\u0070\u006C\u0065\u006D\u0065' + - '\u006E\u0074\u0065\u0068\u0061\u0062\u006C\u0061\u0072\u0065\u006E' + - '\u0045\u0073\u0070\u0061\u00F1\u006F\u006C', + '\u0050\u006F\u0072\u0071\u0075\u00E9\u006E\u006F\u0070\u0075\u0065' + + '\u0064\u0065\u006E\u0073\u0069\u006D\u0070\u006C\u0065\u006D\u0065' + + '\u006E\u0074\u0065\u0068\u0061\u0062\u006C\u0061\u0072\u0065\u006E' + + '\u0045\u0073\u0070\u0061\u00F1\u006F\u006C', - // (K) Vietnamese: TisaohkhngthchnitingVit + // (K) Vietnamese: Tisaohkhngth + // chnitingVit 'TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g': - '\u0054\u1EA1\u0069\u0073\u0061\u006F\u0068\u1ECD\u006B\u0068\u00F4' + - '\u006E\u0067\u0074\u0068\u1EC3\u0063\u0068\u1EC9\u006E\u00F3\u0069' + - '\u0074\u0069\u1EBF\u006E\u0067\u0056\u0069\u1EC7\u0074', + '\u0054\u1EA1\u0069\u0073\u0061\u006F\u0068\u1ECD\u006B\u0068\u00F4' + + '\u006E\u0067\u0074\u0068\u1EC3\u0063\u0068\u1EC9\u006E\u00F3\u0069' + + '\u0074\u0069\u1EBF\u006E\u0067\u0056\u0069\u1EC7\u0074', // (L) 3B '3B-ww4c5e180e575a65lsy2b': - '\u0033\u5E74\u0042\u7D44\u91D1\u516B\u5148\u751F', + '\u0033\u5E74\u0042\u7D44\u91D1\u516B\u5148\u751F', // (M) -with-SUPER-MONKEYS '-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n': - '\u5B89\u5BA4\u5948\u7F8E\u6075\u002D\u0077\u0069\u0074\u0068\u002D' + - '\u0053\u0055\u0050\u0045\u0052\u002D\u004D\u004F\u004E\u004B\u0045' + - '\u0059\u0053', + '\u5B89\u5BA4\u5948\u7F8E\u6075\u002D\u0077\u0069\u0074\u0068\u002D' + + '\u0053\u0055\u0050\u0045\u0052\u002D\u004D\u004F\u004E\u004B\u0045' + + '\u0059\u0053', // (N) Hello-Another-Way- 'Hello-Another-Way--fc4qua05auwb3674vfr0b': - '\u0048\u0065\u006C\u006C\u006F\u002D\u0041\u006E\u006F\u0074\u0068' + - '\u0065\u0072\u002D\u0057\u0061\u0079\u002D\u305D\u308C\u305E\u308C' + - '\u306E\u5834\u6240', + '\u0048\u0065\u006C\u006C\u006F\u002D\u0041\u006E\u006F\u0074\u0068' + + '\u0065\u0072\u002D\u0057\u0061\u0079\u002D\u305D\u308C\u305E\u308C' + + '\u306E\u5834\u6240', // (O) 2 '2-u9tlzr9756bt3uc0v': - '\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B\u0032', + '\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B\u0032', // (P) MajiKoi5 'MajiKoi5-783gue6qz075azm5e': - '\u004D\u0061\u006A\u0069\u3067\u004B\u006F\u0069\u3059\u308B\u0035' + - '\u79D2\u524D', + '\u004D\u0061\u006A\u0069\u3067\u004B\u006F\u0069\u3059\u308B\u0035' + + '\u79D2\u524D', // (Q) de 'de-jg4avhby1noc0d': - '\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0', + '\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0', // (R) 'd9juau41awczczp': - '\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067', + '\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067', // (S) -> $1.00 <- '-> $1.00 <--': - '\u002D\u003E\u0020\u0024\u0031\u002E\u0030\u0030\u0020\u003C\u002D' + '\u002D\u003E\u0020\u0024\u0031\u002E\u0030\u0030\u0020\u003C\u002D' }; var errors = 0; diff --git a/test/simple/test-readdir.js b/test/simple/test-readdir.js index 0f95ea28520..24997241f9b 100644 --- a/test/simple/test-readdir.js +++ b/test/simple/test-readdir.js @@ -66,7 +66,7 @@ process.on('exit', function() { var has_caught = false; try { - fs.readdirSync(__filename) + fs.readdirSync(__filename); } catch (e) { has_caught = true; diff --git a/test/simple/test-regress-GH-1899.js b/test/simple/test-regress-GH-1899.js index 350e6947a3a..7fc8007a24c 100644 --- a/test/simple/test-regress-GH-1899.js +++ b/test/simple/test-regress-GH-1899.js @@ -20,18 +20,20 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. var path = require('path'); -var assert = require('assert') +var assert = require('assert'); var spawn = require('child_process').spawn; var common = require('../common'); -var child = spawn(process.argv[0], [path.join(common.fixturesDir, 'GH-1899-output.js')]); +var child = spawn(process.argv[0], [ + path.join(common.fixturesDir, 'GH-1899-output.js') +]); var output = ''; -child.stdout.on('data', function (data) { +child.stdout.on('data', function(data) { output += data; }); -child.on('exit', function (code, signal) { +child.on('exit', function(code, signal) { assert.equal(code, 0); assert.equal(output, 'hello, world!\n'); }); diff --git a/test/simple/test-regress-GH-877.js b/test/simple/test-regress-GH-877.js index 0714e6df5de..d9877f431f2 100644 --- a/test/simple/test-regress-GH-877.js +++ b/test/simple/test-regress-GH-877.js @@ -35,6 +35,8 @@ var server = http.createServer(function(req, res) { res.end('Hello World\n'); }); +var addrString = '127.0.0.1:' + common.PORT; + server.listen(common.PORT, '127.0.0.1', function() { for (var i = 0; i < N; i++) { var options = { @@ -50,12 +52,15 @@ server.listen(common.PORT, '127.0.0.1', function() { assert.equal(req.agent, agent); - console.log('Socket: ' + agent.sockets['127.0.0.1:' + common.PORT].length + - '/' + agent.maxSockets + - ' queued: ' + (agent.requests['127.0.0.1:' + common.PORT] ? agent.requests['127.0.0.1:' + common.PORT].length : 0)); + console.log('Socket: ' + agent.sockets[addrString].length + '/' + + agent.maxSockets + ' queued: ' + (agent.requests[addrString] ? + agent.requests['127.0.0.1:' + common.PORT].length : 0)); - if (maxQueued < (agent.requests['127.0.0.1:' + common.PORT] ? agent.requests['127.0.0.1:' + common.PORT].length : 0)) { - maxQueued = (agent.requests['127.0.0.1:' + common.PORT] ? agent.requests['127.0.0.1:' + common.PORT].length : 0); + var agentRequests = agent.requests[addrString] ? + agent.requests[addrString].length : 0; + + if (maxQueued < agentRequests) { + maxQueued = agentRequests; } } }); diff --git a/test/simple/test-repl-.save.load.js b/test/simple/test-repl-.save.load.js index 97a0a53de93..ef318311711 100644 --- a/test/simple/test-repl-.save.load.js +++ b/test/simple/test-repl-.save.load.js @@ -29,9 +29,9 @@ var repl = require('repl'); // A stream to push an array into a REPL function ArrayStream() { - this.run = function (data) { + this.run = function(data) { var self = this; - data.forEach(function (line) { + data.forEach(function(line) { self.emit('data', line); }); } @@ -39,10 +39,10 @@ function ArrayStream() { util.inherits(ArrayStream, require('stream').Stream); ArrayStream.prototype.readable = true; ArrayStream.prototype.writable = true; -ArrayStream.prototype.resume = function () {}; -ArrayStream.prototype.write = function () {}; +ArrayStream.prototype.resume = function() {}; +ArrayStream.prototype.write = function() {}; -var works = [ [ 'inner.one' ], 'inner.o' ]; +var works = [['inner.one'], 'inner.o']; var putIn = new ArrayStream(); var testMe = repl.start('', putIn); @@ -61,13 +61,11 @@ putIn.run(testFile); putIn.run(['.save ' + saveFileName]); // the file should have what I wrote -assert.equal( - fs.readFileSync(saveFileName, 'utf8'), - testFile.join('\n') + '\n'); +assert.equal(fs.readFileSync(saveFileName, 'utf8'), testFile.join('\n') + '\n'); // make sure that the REPL data is "correct" // so when I load it back I know I'm good -testMe.complete('inner.o', function (error, data) { +testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, works); }); @@ -78,7 +76,7 @@ putIn.run(['.clear']); putIn.run(['.load ' + saveFileName]); // make sure that the REPL data is "correct" -testMe.complete('inner.o', function (error, data) { +testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, works); }); @@ -90,12 +88,11 @@ var loadFile = join(common.tmpDir, 'file.does.not.exist'); // shold not break putIn.write = function(data) { // make sure I get a failed to load message and not some crazy error - assert.equal(data, - 'Failed to load:' + loadFile + '\n'); + assert.equal(data, 'Failed to load:' + loadFile + '\n'); // eat me to avoid work putIn.write = function() {}; }; -putIn.run(['.load ' +loadFile]); +putIn.run(['.load ' + loadFile]); //TODO how do I do a failed .save test? diff --git a/test/simple/test-setproctitle.js b/test/simple/test-setproctitle.js index 0bf6fd35c3e..207c2e3f510 100644 --- a/test/simple/test-setproctitle.js +++ b/test/simple/test-setproctitle.js @@ -23,14 +23,14 @@ // FIXME add sunos support if ('linux freebsd'.indexOf(process.platform) === -1) { - console.error("Skipping test, platform not supported."); + console.error('Skipping test, platform not supported.'); process.exit(); } var assert = require('assert'); var exec = require('child_process').exec; -var title = "testTestTESTtest123123123123123123HiHaiJo"; +var title = 'testTestTESTtest123123123123123123HiHaiJo'; assert.notEqual(process.title, title); process.title = title; diff --git a/test/simple/test-stdin-pause-resume.js b/test/simple/test-stdin-pause-resume.js index cfa44bae2c0..fed5d665329 100644 --- a/test/simple/test-stdin-pause-resume.js +++ b/test/simple/test-stdin-pause-resume.js @@ -19,19 +19,19 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -console.error("before opening stdin"); +console.error('before opening stdin'); process.stdin.resume(); -console.error("stdin opened"); +console.error('stdin opened'); setTimeout(function() { - console.error("pausing stdin"); + console.error('pausing stdin'); process.stdin.pause(); setTimeout(function() { - console.error("opening again"); + console.error('opening again'); process.stdin.resume(); setTimeout(function() { - console.error("pausing again"); + console.error('pausing again'); process.stdin.pause(); - console.error("should exit now"); + console.error('should exit now'); }, 1); }, 1); }, 1); diff --git a/test/simple/test-tls-passphrase.js b/test/simple/test-tls-passphrase.js index 7dbb1d711f5..bb458bcc252 100644 --- a/test/simple/test-tls-passphrase.js +++ b/test/simple/test-tls-passphrase.js @@ -37,7 +37,7 @@ var server = tls.Server({ key: key, passphrase: 'passphrase', cert: cert, - ca: [ cert ], + ca: [cert], requestCert: true, rejectUnauthorized: true }, function(s) { diff --git a/test/simple/test-tls-session-cache.js b/test/simple/test-tls-session-cache.js index 53f736b84d9..64e41993589 100644 --- a/test/simple/test-tls-session-cache.js +++ b/test/simple/test-tls-session-cache.js @@ -20,12 +20,12 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. if (!process.versions.openssl) { - console.error("Skipping because node compiled without OpenSSL."); + console.error('Skipping because node compiled without OpenSSL.'); process.exit(0); } require('child_process').exec('openssl version', function(err) { if (err !== null) { - console.error("Skipping because openssl command is not available."); + console.error('Skipping because openssl command is not available.'); process.exit(0); } doTest(); @@ -46,7 +46,7 @@ function doTest() { var options = { key: key, cert: cert, - ca: [ cert ], + ca: [cert], requestCert: true }; var requestCount = 0; diff --git a/test/simple/test-tls-set-ciphers.js b/test/simple/test-tls-set-ciphers.js index ba5c868d12c..576094dcb73 100644 --- a/test/simple/test-tls-set-ciphers.js +++ b/test/simple/test-tls-set-ciphers.js @@ -51,7 +51,8 @@ var server = tls.createServer(options, function(conn) { }); server.listen(common.PORT, '127.0.0.1', function() { - var cmd = 'openssl s_client -cipher NULL-MD5 -connect 127.0.0.1:' + common.PORT; + var cmd = 'openssl s_client -cipher NULL-MD5 -connect 127.0.0.1:' + + common.PORT; exec(cmd, function(err, stdout, stderr) { if (err) throw err; diff --git a/test/simple/test-tty-stdout-end.js b/test/simple/test-tty-stdout-end.js index f09bf8ac76f..7c7f7feb2ba 100644 --- a/test/simple/test-tty-stdout-end.js +++ b/test/simple/test-tty-stdout-end.js @@ -27,7 +27,7 @@ var exceptionCaught = false; try { process.stdout.end(); -} catch(e) { +} catch (e) { exceptionCaught = true; assert.ok(common.isError(e)); assert.equal('process.stdout cannot be closed', e.message); diff --git a/test/simple/test-util-inspect.js b/test/simple/test-util-inspect.js index c7a64f63906..d866b41dd2e 100644 --- a/test/simple/test-util-inspect.js +++ b/test/simple/test-util-inspect.js @@ -61,16 +61,16 @@ assert.ok(ex.indexOf('[type]') != -1); // GH-1941 // should not throw: -assert.equal(util.inspect(Object.create(Date.prototype)), '{}') +assert.equal(util.inspect(Object.create(Date.prototype)), '{}'); // GH-1944 -assert.doesNotThrow(function () { +assert.doesNotThrow(function() { var d = new Date(); d.toUTCString = null; util.inspect(d); }); -assert.doesNotThrow(function () { +assert.doesNotThrow(function() { var r = /regexp/; r.toString = null; util.inspect(r); diff --git a/test/simple/test-util.js b/test/simple/test-util.js index bb1e7c012b1..87ee77509a9 100644 --- a/test/simple/test-util.js +++ b/test/simple/test-util.js @@ -26,46 +26,46 @@ var util = require('util'); var context = require('vm').runInNewContext; // isArray -assert.equal(true, util.isArray([])) -assert.equal(true, util.isArray(Array())) -assert.equal(true, util.isArray(new Array())) -assert.equal(true, util.isArray(new Array(5))) -assert.equal(true, util.isArray(new Array('with', 'some', 'entries'))) -assert.equal(true, util.isArray(context('Array')())) -assert.equal(false, util.isArray({})) -assert.equal(false, util.isArray({ push: function () {} })) -assert.equal(false, util.isArray(/regexp/)) -assert.equal(false, util.isArray(new Error)) -assert.equal(false, util.isArray(Object.create(Array.prototype))) +assert.equal(true, util.isArray([])); +assert.equal(true, util.isArray(Array())); +assert.equal(true, util.isArray(new Array())); +assert.equal(true, util.isArray(new Array(5))); +assert.equal(true, util.isArray(new Array('with', 'some', 'entries'))); +assert.equal(true, util.isArray(context('Array')())); +assert.equal(false, util.isArray({})); +assert.equal(false, util.isArray({ push: function() {} })); +assert.equal(false, util.isArray(/regexp/)); +assert.equal(false, util.isArray(new Error)); +assert.equal(false, util.isArray(Object.create(Array.prototype))); // isRegExp -assert.equal(true, util.isRegExp(/regexp/)) -assert.equal(true, util.isRegExp(RegExp())) -assert.equal(true, util.isRegExp(new RegExp())) -assert.equal(true, util.isRegExp(context('RegExp')())) -assert.equal(false, util.isRegExp({})) -assert.equal(false, util.isRegExp([])) -assert.equal(false, util.isRegExp(new Date())) -assert.equal(false, util.isRegExp(Object.create(RegExp.prototype))) +assert.equal(true, util.isRegExp(/regexp/)); +assert.equal(true, util.isRegExp(RegExp())); +assert.equal(true, util.isRegExp(new RegExp())); +assert.equal(true, util.isRegExp(context('RegExp')())); +assert.equal(false, util.isRegExp({})); +assert.equal(false, util.isRegExp([])); +assert.equal(false, util.isRegExp(new Date())); +assert.equal(false, util.isRegExp(Object.create(RegExp.prototype))); // isDate -assert.equal(true, util.isDate(new Date())) -assert.equal(true, util.isDate(new Date(0))) -assert.equal(true, util.isDate(new (context('Date')))) -assert.equal(false, util.isDate(Date())) -assert.equal(false, util.isDate({})) -assert.equal(false, util.isDate([])) -assert.equal(false, util.isDate(new Error)) -assert.equal(false, util.isDate(Object.create(Date.prototype))) +assert.equal(true, util.isDate(new Date())); +assert.equal(true, util.isDate(new Date(0))); +assert.equal(true, util.isDate(new (context('Date')))); +assert.equal(false, util.isDate(Date())); +assert.equal(false, util.isDate({})); +assert.equal(false, util.isDate([])); +assert.equal(false, util.isDate(new Error)); +assert.equal(false, util.isDate(Object.create(Date.prototype))); // isError -assert.equal(true, util.isError(new Error)) -assert.equal(true, util.isError(new TypeError)) -assert.equal(true, util.isError(new SyntaxError)) -assert.equal(true, util.isError(new (context('Error')))) -assert.equal(true, util.isError(new (context('TypeError')))) -assert.equal(true, util.isError(new (context('SyntaxError')))) -assert.equal(false, util.isError({})) -assert.equal(false, util.isError({ name: 'Error', message: '' })) -assert.equal(false, util.isError([])) -assert.equal(false, util.isError(Object.create(Error.prototype))) +assert.equal(true, util.isError(new Error)); +assert.equal(true, util.isError(new TypeError)); +assert.equal(true, util.isError(new SyntaxError)); +assert.equal(true, util.isError(new (context('Error')))); +assert.equal(true, util.isError(new (context('TypeError')))); +assert.equal(true, util.isError(new (context('SyntaxError')))); +assert.equal(false, util.isError({})); +assert.equal(false, util.isError({ name: 'Error', message: '' })); +assert.equal(false, util.isError([])); +assert.equal(false, util.isError(Object.create(Error.prototype))); diff --git a/test/simple/test-zlib-from-gzip.js b/test/simple/test-zlib-from-gzip.js index 9607bd39117..39935420999 100644 --- a/test/simple/test-zlib-from-gzip.js +++ b/test/simple/test-zlib-from-gzip.js @@ -43,6 +43,6 @@ out.on('close', function() { var actual = fs.readFileSync(outputFile); assert.equal(actual.length, expect.length, 'length should match'); for (var i = 0, l = actual.length; i < l; i++) { - assert.equal(actual[i], expect[i], 'byte['+i+']'); + assert.equal(actual[i], expect[i], 'byte[' + i + ']'); } }); diff --git a/test/simple/test-zlib-from-string.js b/test/simple/test-zlib-from-string.js index fe4fb498407..83bdeea83a9 100644 --- a/test/simple/test-zlib-from-string.js +++ b/test/simple/test-zlib-from-string.js @@ -25,12 +25,31 @@ var common = require('../common.js'); var assert = require('assert'); var zlib = require('zlib'); -var inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi faucibus, purus at gravida dictum, libero arcu convallis lacus, in commodo libero metus eu nisi. Nullam commodo, neque nec porta placerat, nisi est fermentum augue, vitae gravida tellus sapien sit amet tellus. Aenean non diam orci. Proin quis elit turpis. Suspendisse non diam ipsum. Suspendisse nec ullamcorper odio. Vestibulum arcu mi, sodales non suscipit id, ultrices ut massa. Sed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; -var expectedBase64Deflate = 'eJxdUUtOQzEMvMoc4OndgT0gJCT2buJWlpI4jePeqZfpmXAKLRKbLOzx/HK73q6vOrhCunlF1qIDJhNUeW5I2ozT5OkDlKWLJWkncJG5403HQXAkT3Jw29B9uIEmToMukglZ0vS6ociBh4JG8sV4oVLEUCitK2kxq1WzPnChHDzsaGKy491LofoAbWh8do43oeuYhB5EPCjcLjzYJo48KrfQBvnJecNFJvHT1+RSQsGoC7dn2t/xjhduTA1NWyQIZR0pbHwMDatnD+crPqKSqGPHp1vnlsWM/07ubf7bheF7kqSj84Bm0R1fYTfaK8vqqqfKBtNMhe3OZh6N95CTvMX5HJJi4xOVzCgUOIMSLH7wmeOHaFE4RdpnGavKtrB5xzfO/Ll9'; -var expectedBase64Gzip = 'H4sIAAAAAAAAA11RS05DMQy8yhzg6d2BPSAkJPZu4laWkjiN496pl+mZcAotEpss7PH8crverq86uEK6eUXWogMmE1R5bkjajNPk6QOUpYslaSdwkbnjTcdBcCRPcnDb0H24gSZOgy6SCVnS9LqhyIGHgkbyxXihUsRQKK0raTGrVbM+cKEcPOxoYrLj3Uuh+gBtaHx2jjeh65iEHkQ8KNwuPNgmjjwqt9AG+cl5w0Um8dPX5FJCwagLt2fa3/GOF25MDU1bJAhlHSlsfAwNq2cP5ys+opKoY8enW+eWxYz/Tu5t/tuF4XuSpKPzgGbRHV9hN9ory+qqp8oG00yF7c5mHo33kJO8xfkckmLjE5XMKBQ4gxIsfvCZ44doUThF2mcZq8q2sHnHNzRtagj5AQAA'; +var inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing el' + + 'it. Morbi faucibus, purus at gravida dictum, libero arcu convallis la' + + 'cus, in commodo libero metus eu nisi. Nullam commodo, neque nec porta' + + ' placerat, nisi est fermentum augue, vitae gravida tellus sapien sit ' + + 'amet tellus. Aenean non diam orci. Proin quis elit turpis. Suspendiss' + + 'e non diam ipsum. Suspendisse nec ullamcorper odio. Vestibulum arcu m' + + 'i, sodales non suscipit id, ultrices ut massa. Sed ac sem sit amet ar' + + 'cu malesuada fermentum. Nunc sed. '; +var expectedBase64Deflate = 'eJxdUUtOQzEMvMoc4OndgT0gJCT2buJWlpI4jePeqZfpm' + + 'XAKLRKbLOzx/HK73q6vOrhCunlF1qIDJhNUeW5I2ozT5OkDlKWLJWkncJG5403HQXAkT3' + + 'Jw29B9uIEmToMukglZ0vS6ociBh4JG8sV4oVLEUCitK2kxq1WzPnChHDzsaGKy491Lofo' + + 'AbWh8do43oeuYhB5EPCjcLjzYJo48KrfQBvnJecNFJvHT1+RSQsGoC7dn2t/xjhduTA1N' + + 'WyQIZR0pbHwMDatnD+crPqKSqGPHp1vnlsWM/07ubf7bheF7kqSj84Bm0R1fYTfaK8vqq' + + 'qfKBtNMhe3OZh6N95CTvMX5HJJi4xOVzCgUOIMSLH7wmeOHaFE4RdpnGavKtrB5xzfO/Ll9'; +var expectedBase64Gzip = 'H4sIAAAAAAAAA11RS05DMQy8yhzg6d2BPSAkJPZu4laWkjiN' + + '496pl+mZcAotEpss7PH8crverq86uEK6eUXWogMmE1R5bkjajNPk6QOUpYslaSdwkbnjT' + + 'cdBcCRPcnDb0H24gSZOgy6SCVnS9LqhyIGHgkbyxXihUsRQKK0raTGrVbM+cKEcPOxoYr' + + 'Lj3Uuh+gBtaHx2jjeh65iEHkQ8KNwuPNgmjjwqt9AG+cl5w0Um8dPX5FJCwagLt2fa3/G' + + 'OF25MDU1bJAhlHSlsfAwNq2cP5ys+opKoY8enW+eWxYz/Tu5t/tuF4XuSpKPzgGbRHV9h' + + 'N9ory+qqp8oG00yF7c5mHo33kJO8xfkckmLjE5XMKBQ4gxIsfvCZ44doUThF2mcZq8q2s' + + 'HnHNzRtagj5AQAA'; zlib.deflate(inputString, function(err, buffer) { - assert.equal(buffer.toString('base64'), expectedBase64Deflate, 'deflate encoded string should match'); + assert.equal(buffer.toString('base64'), expectedBase64Deflate, + 'deflate encoded string should match'); }); zlib.gzip(inputString, function(err, buffer) { @@ -40,7 +59,7 @@ zlib.gzip(inputString, function(err, buffer) { // However, decrypting it should definitely yield the same // result that we're expecting, and this should match what we get // from inflating the known valid deflate data. - zlib.gunzip(buffer, function (err, gunzipped) { + zlib.gunzip(buffer, function(err, gunzipped) { assert.equal(gunzipped.toString(), inputString, 'Should get original string after gzip/gunzip'); }); @@ -48,10 +67,12 @@ zlib.gzip(inputString, function(err, buffer) { var buffer = new Buffer(expectedBase64Deflate, 'base64'); zlib.unzip(buffer, function(err, buffer) { - assert.equal(buffer.toString(), inputString, 'decoded inflated string should match'); + assert.equal(buffer.toString(), inputString, + 'decoded inflated string should match'); }); buffer = new Buffer(expectedBase64Gzip, 'base64'); zlib.unzip(buffer, function(err, buffer) { - assert.equal(buffer.toString(), inputString, 'decoded gunzipped string should match'); + assert.equal(buffer.toString(), inputString, + 'decoded gunzipped string should match'); }); diff --git a/test/simple/test-zlib-random-byte-pipes.js b/test/simple/test-zlib-random-byte-pipes.js index ec5d7015e75..88838c68ca9 100644 --- a/test/simple/test-zlib-random-byte-pipes.js +++ b/test/simple/test-zlib-random-byte-pipes.js @@ -29,7 +29,7 @@ var zlib = require('zlib'); // emit random bytes, and keep a shasum -function RandomReadStream (opt) { +function RandomReadStream(opt) { Stream.call(this); this.readable = true; @@ -67,7 +67,7 @@ RandomReadStream.prototype.resume = function() { // console.error("rrs resume"); this._paused = false; this.emit('resume'); - this._process() + this._process(); }; RandomReadStream.prototype._process = function() { @@ -91,9 +91,9 @@ RandomReadStream.prototype._process = function() { if (jitter) { block += Math.ceil(Math.random() * jitter - (jitter / 2)); } - block = Math.min(block, this._remaining) + block = Math.min(block, this._remaining); var buf = new Buffer(block); - for (var i = 0; i < block; i ++) { + for (var i = 0; i < block; i++) { buf[i] = Math.random() * 256; } @@ -110,7 +110,7 @@ RandomReadStream.prototype._process = function() { // a filter that just verifies a shasum -function HashStream () { +function HashStream() { Stream.call(this); this.readable = this.writable = true; @@ -152,7 +152,7 @@ var gunz = zlib.createGunzip(); inp.pipe(gzip).pipe(gunz).pipe(out); var didSomething = false; -out.on('data', function (c) { +out.on('data', function(c) { didSomething = true; console.error('hash=%s', c); assert.equal(c, inp._hash, 'hashes should match'); @@ -160,4 +160,4 @@ out.on('data', function (c) { process.on('exit', function() { assert(didSomething, 'should have done something'); -}) +}); From 892056bf97842b1bfe6a2e328df67d5a0043b79e Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 17 Jan 2012 20:16:49 +0100 Subject: [PATCH 06/30] tests: fix more lint issues --- test/common.js | 2 +- test/simple/test-child-process-double-pipe.js | 15 ++- test/simple/test-executable-path.js | 16 +-- test/simple/test-http-1.0.js | 6 +- test/simple/test-path-makelong.js | 8 +- test/simple/test-repl-tab-complete.js | 101 ++++++++++-------- test/simple/test-script-context.js | 6 +- test/simple/test-stdin-child-proc.js | 2 +- 8 files changed, 87 insertions(+), 69 deletions(-) diff --git a/test/common.js b/test/common.js index d9fa7b70e82..202d1fa9bce 100644 --- a/test/common.js +++ b/test/common.js @@ -55,7 +55,7 @@ exports.indirectInstanceOf = function(obj, cls) { exports.ddCommand = function(filename, kilobytes) { if (process.platform == 'win32') { return '"' + process.argv[0] + '" "' + path.resolve(exports.fixturesDir, - 'create-file.js') + '" "' + filename + '" ' + (kilobytes * 1024); + 'create-file.js') + '" "' + filename + '" ' + (kilobytes * 1024); } else { return 'dd if=/dev/zero of="' + filename + '" bs=1024 count=' + kilobytes; } diff --git a/test/simple/test-child-process-double-pipe.js b/test/simple/test-child-process-double-pipe.js index cb1edfae54e..b0fc11d33ff 100644 --- a/test/simple/test-child-process-double-pipe.js +++ b/test/simple/test-child-process-double-pipe.js @@ -28,11 +28,16 @@ var assert = require('assert'), // We're trying to reproduce: // $ echo "hello\nnode\nand\nworld" | grep o | sed s/o/a/ -var echo = is_windows ? spawn('cmd.exe', ['/c', 'echo', 'hello&&', 'echo', - 'node&&', 'echo', 'and&&', 'echo', 'world']) : - spawn('echo', ['hello\nnode\nand\nworld\n']), - grep = spawn('grep', ['o']), - sed = spawn('sed', ['s/o/O/']); +var grep = spawn('grep', ['o']), + sed = spawn('sed', ['s/o/O/']), + echo; + +if (is_windows) { + echo = spawn('cmd.exe', ['/c', 'echo', 'hello&&', 'echo', + 'node&&', 'echo', 'and&&', 'echo', 'world']); +} else { + echo = spawn('echo', ['hello\nnode\nand\nworld\n']); +} /* * grep and sed hang if the spawn function leaks file descriptors to child diff --git a/test/simple/test-executable-path.js b/test/simple/test-executable-path.js index c33a8007488..8d82b745010 100644 --- a/test/simple/test-executable-path.js +++ b/test/simple/test-executable-path.js @@ -26,14 +26,14 @@ var match = false; var isDebug = process.features.debug; -var debugPaths = [ path.normalize(path.join(__dirname, '..', '..', - 'out', 'Debug', 'node')), - path.normalize(path.join(__dirname, '..', '..', - 'Debug', 'node'))]; -var defaultPaths = [ path.normalize(path.join(__dirname, '..', '..', - 'out', 'Release', 'node')), - path.normalize(path.join(__dirname, '..', '..', - 'Release', 'node'))]; +var debugPaths = [path.normalize(path.join(__dirname, '..', '..', + 'out', 'Debug', 'node')), + path.normalize(path.join(__dirname, '..', '..', + 'Debug', 'node'))]; +var defaultPaths = [path.normalize(path.join(__dirname, '..', '..', + 'out', 'Release', 'node')), + path.normalize(path.join(__dirname, '..', '..', + 'Release', 'node'))]; console.error('debugPaths: ' + debugPaths); console.error('defaultPaths: ' + defaultPaths); diff --git a/test/simple/test-http-1.0.js b/test/simple/test-http-1.0.js index 2ed1d13fb1c..fadaaf0f1e6 100644 --- a/test/simple/test-http-1.0.js +++ b/test/simple/test-http-1.0.js @@ -112,7 +112,8 @@ function test(handler, request_generator, response_validator) { function request_generator() { return ('GET / HTTP/1.0\r\n' + - 'User-Agent: curl/7.19.7 (x86_64-pc-linux-gnu) libcurl/7.19.7 OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15\r\n' + + 'User-Agent: curl/7.19.7 (x86_64-pc-linux-gnu) libcurl/7.19.7 ' + + 'OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15\r\n' + 'Host: 127.0.0.1:1337\r\n' + 'Accept: */*\r\n' + '\r\n'); @@ -147,7 +148,8 @@ function test(handler, request_generator, response_validator) { function request_generator() { return ('GET / HTTP/1.1\r\n' + - 'User-Agent: curl/7.19.7 (x86_64-pc-linux-gnu) libcurl/7.19.7 OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15\r\n' + + 'User-Agent: curl/7.19.7 (x86_64-pc-linux-gnu) libcurl/7.19.7 ' + + 'OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15\r\n' + 'Connection: close\r\n' + 'Host: 127.0.0.1:1337\r\n' + 'Accept: */*\r\n' + diff --git a/test/simple/test-path-makelong.js b/test/simple/test-path-makelong.js index 885eb12a9a0..0ec298fe3d8 100644 --- a/test/simple/test-path-makelong.js +++ b/test/simple/test-path-makelong.js @@ -26,13 +26,13 @@ if (process.platform === 'win32') { var file = path.join(common.fixturesDir, 'a.js'); var resolvedFile = path.resolve(file); - var networkFile = '\\\\someserver\\someshare\\somefile'; assert.equal('\\\\?\\' + resolvedFile, path._makeLong(file)); assert.equal('\\\\?\\' + resolvedFile, path._makeLong('\\\\?\\' + file)); assert.equal('\\\\?\\UNC\\someserver\\someshare\\somefile', - path._makeLong('\\\\someserver\\someshare\\somefile')); + path._makeLong('\\\\someserver\\someshare\\somefile')); assert.equal('\\\\?\\UNC\\someserver\\someshare\\somefile', - path._makeLong('\\\\?\\UNC\\someserver\\someshare\\somefile')); - assert.equal('\\\\.\\pipe\\somepipe', path._makeLong('\\\\.\\pipe\\somepipe')); + path._makeLong('\\\\?\\UNC\\someserver\\someshare\\somefile')); + assert.equal('\\\\.\\pipe\\somepipe', + path._makeLong('\\\\.\\pipe\\somepipe')); } diff --git a/test/simple/test-repl-tab-complete.js b/test/simple/test-repl-tab-complete.js index 4fdd923c9fe..ba511046b51 100644 --- a/test/simple/test-repl-tab-complete.js +++ b/test/simple/test-repl-tab-complete.js @@ -26,9 +26,9 @@ var repl = require('repl'); // A stream to push an array into a REPL function ArrayStream() { - this.run = function (data) { + this.run = function(data) { var self = this; - data.forEach(function (line) { + data.forEach(function(line) { self.emit('data', line); }); } @@ -36,11 +36,11 @@ function ArrayStream() { util.inherits(ArrayStream, require('stream').Stream); ArrayStream.prototype.readable = true; ArrayStream.prototype.writable = true; -ArrayStream.prototype.resume = function () {}; -ArrayStream.prototype.write = function () {}; +ArrayStream.prototype.resume = function() {}; +ArrayStream.prototype.write = function() {}; -var works = [ [ 'inner.one' ], 'inner.o' ]; -var doesNotBreak = [ [], 'inner.o' ]; +var works = [['inner.one'], 'inner.o']; +var doesNotBreak = [[], 'inner.o']; var putIn = new ArrayStream(); var testMe = repl.start('', putIn); @@ -49,14 +49,15 @@ var testMe = repl.start('', putIn); putIn.run(['.clear']); putIn.run([ 'var inner = {', - 'one:1']); -testMe.complete('inner.o', function (error, data) { + 'one:1' +]); +testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, doesNotBreak); }); // Tab Complete will return globaly scoped variables putIn.run(['};']); -testMe.complete('inner.o', function (error, data) { +testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, works); }); @@ -66,8 +67,9 @@ putIn.run(['.clear']); putIn.run([ 'var inner = ( true ' , '?', - '{one: 1} : ']); -testMe.complete('inner.o', function (error, data) { + '{one: 1} : ' +]); +testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, doesNotBreak); }); @@ -76,15 +78,16 @@ putIn.run(['.clear']); // Tab Complete will return a simple local variable putIn.run([ 'var top = function () {', - 'var inner = {one:1};']); -testMe.complete('inner.o', function (error, data) { + 'var inner = {one:1};' +]); +testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, works); }); // When you close the function scope tab complete will not return the // locally scoped variable putIn.run(['};']); -testMe.complete('inner.o', function (error, data) { +testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, doesNotBreak); }); @@ -93,10 +96,11 @@ putIn.run(['.clear']); // Tab Complete will return a complex local variable putIn.run([ 'var top = function () {', - 'var inner = {', - ' one:1', - '};']); -testMe.complete('inner.o', function (error, data) { + 'var inner = {', + ' one:1', + '};' +]); +testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, works); }); @@ -106,10 +110,11 @@ putIn.run(['.clear']); // has paramaters putIn.run([ 'var top = function (one, two) {', - 'var inner = {', - ' one:1', - '};']); -testMe.complete('inner.o', function (error, data) { + 'var inner = {', + ' one:1', + '};' +]); +testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, works); }); @@ -119,11 +124,12 @@ putIn.run(['.clear']); // scope is nested inside an immediately executed function putIn.run([ 'var top = function () {', - '(function test () {', - 'var inner = {', - ' one:1', - '};']); -testMe.complete('inner.o', function (error, data) { + '(function test () {', + 'var inner = {', + ' one:1', + '};' +]); +testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, works); }); @@ -133,12 +139,13 @@ putIn.run(['.clear']); // def has the params and { on a seperate line putIn.run([ 'var top = function () {', - 'r = function test (', - ' one, two) {', - 'var inner = {', - ' one:1', - '};']); -testMe.complete('inner.o', function (error, data) { + 'r = function test (', + ' one, two) {', + 'var inner = {', + ' one:1', + '};' +]); +testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, doesNotBreak); }); @@ -147,12 +154,13 @@ putIn.run(['.clear']); // currently does not work, but should not break, not the { putIn.run([ 'var top = function () {', - 'r = function test ()', - '{', - 'var inner = {', - ' one:1', - '};']); -testMe.complete('inner.o', function (error, data) { + 'r = function test ()', + '{', + 'var inner = {', + ' one:1', + '};' +]); +testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, doesNotBreak); }); @@ -161,13 +169,14 @@ putIn.run(['.clear']); // currently does not work, but should not break putIn.run([ 'var top = function () {', - 'r = function test (', - ')', - '{', - 'var inner = {', - ' one:1', - '};']); -testMe.complete('inner.o', function (error, data) { + 'r = function test (', + ')', + '{', + 'var inner = {', + ' one:1', + '};' +]); +testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, doesNotBreak); }); diff --git a/test/simple/test-script-context.js b/test/simple/test-script-context.js index 581974418d4..a8d6c7a694a 100644 --- a/test/simple/test-script-context.js +++ b/test/simple/test-script-context.js @@ -54,9 +54,11 @@ try { } catch (e) { gh1140Exception = e; - assert.ok(/expected-filename/.test(e.stack), 'expected appearance of filename in Error stack'); + assert.ok(/expected-filename/.test(e.stack), + 'expected appearance of filename in Error stack'); } -assert.ok(gh1140Exception, 'expected exception from runInContext signature test'); +assert.ok(gh1140Exception, + 'expected exception from runInContext signature test'); // GH-558, non-context argument segfaults / raises assertion function isTypeError(o) { diff --git a/test/simple/test-stdin-child-proc.js b/test/simple/test-stdin-child-proc.js index 6c499995d2a..ee23eee36e7 100644 --- a/test/simple/test-stdin-child-proc.js +++ b/test/simple/test-stdin-child-proc.js @@ -24,4 +24,4 @@ var child_process = require('child_process'); var path = require('path'); child_process.spawn(process.execPath, - [ path.resolve(__dirname, 'test-stdin-pause-resume.js') ]); + [path.resolve(__dirname, 'test-stdin-pause-resume.js')]); From 54ad2f6cbb553eb55c5c3f687c8e47edf93d1c46 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Tue, 17 Jan 2012 11:32:58 -0800 Subject: [PATCH 07/30] Link to Marak's module rap in community page --- doc/community/index.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/community/index.html b/doc/community/index.html index dec8b58b900..3219f7bf847 100644 --- a/doc/community/index.html +++ b/doc/community/index.html @@ -28,7 +28,11 @@
-

Node's most valuable feature is the friendly and colorful community of developers. There are many places where this group congregates on the internet. This page attempts to highlight the best forums.

+

Node's most valuable feature is the friendly and colorful + community of developers. There are many places where + this group congregates on the internet. This page attempts + to highlight the best forums.

Periodicals

From 091ab850aaca9591b3f77d57a55c3c78fc0b5442 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 16 Jan 2012 23:36:01 -0500 Subject: [PATCH 08/30] docs: replace expresso with mocha in appendix 1 --- doc/api/appendix_1.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/appendix_1.markdown b/doc/api/appendix_1.markdown index d25d7f9f3c3..59430c20e25 100644 --- a/doc/api/appendix_1.markdown +++ b/doc/api/appendix_1.markdown @@ -38,7 +38,7 @@ elsewhere. - [ncurses](https://github.com/mscdex/node-ncurses) - Testing/TDD/BDD: [vows](http://vowsjs.org/), - [expresso](https://github.com/visionmedia/expresso), + [mocha](https://github.com/visionmedia/mocha), [mjsunit.runner](https://github.com/tmpvar/mjsunit.runner) Patches to this list are welcome. From d44ce9790948c69f0b9bc449f2488b30cd099105 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 18 Jan 2012 00:10:22 +0100 Subject: [PATCH 09/30] cluster: don't always kill the master on uncaughtException uncaughtException handlers installed by the user override the default one that the cluster module installs, the one that kills off the master process. Fixes #2556. --- lib/cluster.js | 3 + .../simple/test-cluster-uncaught-exception.js | 59 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 test/simple/test-cluster-uncaught-exception.js diff --git a/lib/cluster.js b/lib/cluster.js index 6c7d2dfa834..1b565cac791 100644 --- a/lib/cluster.js +++ b/lib/cluster.js @@ -78,6 +78,9 @@ function startMaster() { workerArgs = process.argv.slice(2); process.on('uncaughtException', function(e) { + // Did the user install a listener? If so, it overrides this one. + if (process.listeners('uncaughtException').length > 1) return; + // Quickly try to kill all the workers. // TODO: be session leader - will cause auto SIGHUP to the children. eachWorker(function(worker) { diff --git a/test/simple/test-cluster-uncaught-exception.js b/test/simple/test-cluster-uncaught-exception.js new file mode 100644 index 00000000000..48a82512704 --- /dev/null +++ b/test/simple/test-cluster-uncaught-exception.js @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Installing a custom uncaughtException handler should override the default +// one that the cluster module installs. +// https://github.com/joyent/node/issues/2556 + +var common = require('../common'); +var assert = require('assert'); +var cluster = require('cluster'); +var fork = require('child_process').fork; + +var MAGIC_EXIT_CODE = 42; + +var isTestRunner = process.argv[2] != 'child'; + +if (isTestRunner) { + var exitCode = -1; + + process.on('exit', function() { + assert.equal(exitCode, MAGIC_EXIT_CODE); + }); + + var master = fork(__filename, ['child']); + master.on('exit', function(code) { + exitCode = code; + }); +} +else if (cluster.isMaster) { + process.on('uncaughtException', function() { + process.nextTick(function() { + process.exit(MAGIC_EXIT_CODE); + }); + }); + + cluster.fork(); + throw new Error('kill master'); +} +else { // worker + process.exit(); +} From d489a01f81d84739bde92e77b023f4b934c59212 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 18 Jan 2012 15:09:42 +0100 Subject: [PATCH 10/30] Make path.extname do the right thing when the last path component is . or .. Closes GH-2526 --- lib/path.js | 4 ++-- test/simple/test-path.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/path.js b/lib/path.js index b70225b1d62..c10ff7efce5 100644 --- a/lib/path.js +++ b/lib/path.js @@ -61,7 +61,7 @@ if (isWindows) { /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?([\s\S]*?)$/; // Regex to split the tail part of the above into [*, dir, basename, ext] - var splitTailRe = /^([\s\S]+[\\\/](?!$)|[\\\/])?((?:[\s\S]+?)?(\.[^.]*)?)$/; + var splitTailRe = /^([\s\S]+[\\\/](?!$)|[\\\/])?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/\\]*)?)$/; // Function to split a filename into [root, dir, basename, ext] // windows version @@ -255,7 +255,7 @@ if (isWindows) { // Split a filename into [root, dir, basename, ext], unix version // 'root' is just a slash, or nothing. - var splitPathRe = /^(\/?)([\s\S]+\/(?!$)|\/)?((?:[\s\S]+?)?(\.[^.]*)?)$/; + var splitPathRe = /^(\/?)([\s\S]+\/(?!$)|\/)?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/]*)?)$/; var splitPath = function(filename) { var result = splitPathRe.exec(filename); return [result[1] || '', result[2] || '', result[3] || '', result[4] || '']; diff --git a/test/simple/test-path.js b/test/simple/test-path.js index 1ee78dad68f..92c2647fd26 100644 --- a/test/simple/test-path.js +++ b/test/simple/test-path.js @@ -102,6 +102,34 @@ assert.equal(path.extname('/.file.ext'), '.ext'); assert.equal(path.extname('.path/file.ext'), '.ext'); assert.equal(path.extname('file.ext.ext'), '.ext'); assert.equal(path.extname('file.'), '.'); +assert.equal(path.extname('.'), ''); +assert.equal(path.extname('./'), ''); +assert.equal(path.extname('.file.ext'), '.ext'); +assert.equal(path.extname('.file'), ''); +assert.equal(path.extname('.file.'), '.'); +assert.equal(path.extname('.file..'), '.'); +assert.equal(path.extname('..'), ''); +assert.equal(path.extname('../'), ''); +assert.equal(path.extname('..file.ext'), '.ext'); +assert.equal(path.extname('..file'), '.file'); +assert.equal(path.extname('..file.'), '.'); +assert.equal(path.extname('..file..'), '.'); +assert.equal(path.extname('...'), '.'); +assert.equal(path.extname('...ext'), '.ext'); +assert.equal(path.extname('....'), '.'); +assert.equal(path.extname('file.ext/'), ''); + +if (isWindows) { + // On windows, backspace is a path separator. + assert.equal(path.extname('.\\'), ''); + assert.equal(path.extname('..\\'), ''); + assert.equal(path.extname('file.ext\\'), ''); +} else { + // On unix, backspace is a valid name component like any other character. + assert.equal(path.extname('.\\'), ''); + assert.equal(path.extname('..\\'), '.\\'); + assert.equal(path.extname('file.ext\\'), '.ext\\'); +} // path.join tests var failures = []; From f7b612550a8716b0266a83a86bad060e2688eb1b Mon Sep 17 00:00:00 2001 From: Brandon Benvie Date: Thu, 29 Dec 2011 02:39:56 -0500 Subject: [PATCH 11/30] Add missing TTY key translations for F1-F5 on Windows --- lib/tty.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/tty.js b/lib/tty.js index 32ba512d525..dccda5a4812 100644 --- a/lib/tty.js +++ b/lib/tty.js @@ -225,6 +225,13 @@ ReadStream.prototype._emitKey = function(s) { case '[13~': key.name = 'f3'; break; case '[14~': key.name = 'f4'; break; + /* from Cygwin and used in libuv */ + case '[[A': key.name = 'f1'; break; + case '[[B': key.name = 'f2'; break; + case '[[C': key.name = 'f3'; break; + case '[[D': key.name = 'f4'; break; + case '[[E': key.name = 'f5'; break; + /* common */ case '[15~': key.name = 'f5'; break; case '[17~': key.name = 'f6'; break; From e282c0a0813dd700cc6d9862e27eba448f43b1c9 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 19 Jan 2012 10:18:15 -0500 Subject: [PATCH 12/30] docs: document common add-on design patterns --- doc/api/addons.markdown | 547 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 542 insertions(+), 5 deletions(-) diff --git a/doc/api/addons.markdown b/doc/api/addons.markdown index 6f99f8be48e..4527b24063b 100644 --- a/doc/api/addons.markdown +++ b/doc/api/addons.markdown @@ -6,7 +6,8 @@ knowledge of several libraries: - V8 JavaScript, a C++ library. Used for interfacing with JavaScript: creating objects, calling functions, etc. Documented mostly in the - `v8.h` header file (`deps/v8/include/v8.h` in the Node source tree). + `v8.h` header file (`deps/v8/include/v8.h` in the Node source tree), + which is also available [online](http://izs.me/v8-docs/main.html). - [libuv](https://github.com/joyent/libuv), C event loop library. Anytime one needs to wait for a file descriptor to become readable, wait for a timer, or @@ -22,12 +23,15 @@ Node statically compiles all its dependencies into the executable. When compiling your module, you don't need to worry about linking to any of these libraries. + +### Hello world + To get started let's make a small Addon which is the C++ equivalent of the following Javascript code: exports.hello = function() { return 'world'; }; -To get started we create a file `hello.cc`: +First we create a file `hello.cc`: #include #include @@ -40,7 +44,8 @@ To get started we create a file `hello.cc`: } void init(Handle target) { - NODE_SET_METHOD(target, "hello", Method); + target->Set(String::NewSymbol("hello"), + FunctionTemplate::New(Method)->GetFunction()); } NODE_MODULE(hello, init) @@ -87,5 +92,537 @@ the recently built module: console.log(addon.hello()); // 'world' -For the moment, that is all the documentation on addons. Please see - for a real example. +Please see patterns below for further information or + for an example in production. + + +## Addon patterns + +Below are some addon patterns to help you get started. Consult the online +[v8 reference](http://izs.me/v8-docs/main.html) for help with the various v8 +calls, and v8's [Embedder's Guide](http://code.google.com/apis/v8/embed.html) +for an explanation of several concepts used such as handles, scopes, +function templates, etc. + +To compile these examples, create the `wscript` file below and run +`node-waf configure build`: + + srcdir = '.' + blddir = 'build' + VERSION = '0.0.1' + + def set_options(opt): + opt.tool_options('compiler_cxx') + + def configure(conf): + conf.check_tool('compiler_cxx') + conf.check_tool('node_addon') + + def build(bld): + obj = bld.new_task_gen('cxx', 'shlib', 'node_addon') + obj.target = 'addon' + obj.source = ['addon.cc'] + +In cases where there is more than one `.cc` file, simply add the file name to the +`obj.source` array, e.g.: + + obj.source = ['addon.cc', 'myexample.cc'] + + +#### Function arguments + +The following pattern illustrates how to read arguments from JavaScript +function calls and return a result. This is the main and only needed source +`addon.cc`: + + #define BUILDING_NODE_EXTENSION + #include + + using namespace v8; + + Handle Add(const Arguments& args) { + HandleScope scope; + + if (args.Length() < 2) { + ThrowException(Exception::TypeError(String::New("Wrong number of arguments"))); + return scope.Close(Undefined()); + } + + if (!args[0]->IsNumber() || !args[1]->IsNumber()) { + ThrowException(Exception::TypeError(String::New("Wrong arguments"))); + return scope.Close(Undefined()); + } + + Local num = Number::New(args[0]->NumberValue() + + args[1]->NumberValue()); + return scope.Close(num); + } + + void Init(Handle target) { + target->Set(String::NewSymbol("add"), + FunctionTemplate::New(Add)->GetFunction()); + } + + NODE_MODULE(addon, Init) + +You can test it with the following JavaScript snippet: + + var addon = require('./build/Release/addon'); + + console.log( 'This should be eight:', addon.add(3,5) ); + + +#### Callbacks + +You can pass JavaScript functions to a C++ function and execute them from +there. Here's `addon.cc`: + + #define BUILDING_NODE_EXTENSION + #include + + using namespace v8; + + Handle RunCallback(const Arguments& args) { + HandleScope scope; + + Local cb = Local::Cast(args[0]); + const unsigned argc = 1; + Local argv[argc] = { Local::New(String::New("hello world")) }; + cb->Call(Context::GetCurrent()->Global(), argc, argv); + + return scope.Close(Undefined()); + } + + void Init(Handle target) { + target->Set(String::NewSymbol("runCallback"), + FunctionTemplate::New(RunCallback)->GetFunction()); + } + + NODE_MODULE(addon, Init) + +To test it run the following JavaScript snippet: + + var addon = require('./build/Release/addon'); + + addon.runCallback(function(msg){ + console.log(msg); // 'hello world' + }); + + +#### Object factory + +You can create and return new objects from within a C++ function with this +`addon.cc` pattern, which returns an object with property `msg` that echoes +the string passed to `createObject()`: + + #define BUILDING_NODE_EXTENSION + #include + + using namespace v8; + + Handle CreateObject(const Arguments& args) { + HandleScope scope; + + Local obj = Object::New(); + obj->Set(String::NewSymbol("msg"), args[0]->ToString()); + + return scope.Close(obj); + } + + void Init(Handle target) { + target->Set(String::NewSymbol("createObject"), + FunctionTemplate::New(CreateObject)->GetFunction()); + } + + NODE_MODULE(addon, Init) + +To test it in JavaScript: + + var addon = require('./build/Release/addon'); + + var obj1 = addon.createObject('hello'); + var obj2 = addon.createObject('world'); + console.log(obj1.msg+' '+obj2.msg); // 'hello world' + + +#### Function factory + +This pattern illustrates how to create and return a JavaScript function that +wraps a C++ function: + + #define BUILDING_NODE_EXTENSION + #include + + using namespace v8; + + Handle MyFunction(const Arguments& args) { + HandleScope scope; + return scope.Close(String::New("hello world")); + } + + Handle CreateFunction(const Arguments& args) { + HandleScope scope; + + Local tpl = FunctionTemplate::New(MyFunction); + Local fn = tpl->GetFunction(); + fn->SetName(String::NewSymbol("theFunction")); // omit this to make it anonymous + + return scope.Close(fn); + } + + void Init(Handle target) { + target->Set(String::NewSymbol("createFunction"), + FunctionTemplate::New(CreateFunction)->GetFunction()); + } + + NODE_MODULE(addon, Init) + + +To test: + + var addon = require('./build/Release/addon'); + + var fn = addon.createFunction(); + console.log(fn()); // 'hello world' + + +#### Wrapping C++ objects + +Here we will create a wrapper for a C++ object/class `MyObject` that can be +instantiated in JavaScript through the `new` operator. First prepare the main +module `addon.cc`: + + #define BUILDING_NODE_EXTENSION + #include + #include "myobject.h" + + using namespace v8; + + void InitAll(Handle target) { + MyObject::Init(target); + } + + NODE_MODULE(addon, InitAll) + +Then in `myobject.h` make your wrapper inherit from `node::ObjectWrap`: + + #ifndef MYOBJECT_H + #define MYOBJECT_H + + #include + + class MyObject : public node::ObjectWrap { + public: + static void Init(v8::Handle target); + + private: + MyObject(); + ~MyObject(); + + static v8::Handle New(const v8::Arguments& args); + static v8::Handle PlusOne(const v8::Arguments& args); + double counter_; + }; + + #endif + +And in `myobject.cc` implement the various methods that you want to expose. +Here we expose the method `plusOne` by adding it to the constructor's +prototype: + + #define BUILDING_NODE_EXTENSION + #include + #include "myobject.h" + + using namespace v8; + + MyObject::MyObject() {}; + MyObject::~MyObject() {}; + + void MyObject::Init(Handle target) { + // Prepare constructor template + Local tpl = FunctionTemplate::New(New); + tpl->SetClassName(String::NewSymbol("MyObject")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + // Prototype + tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"), + FunctionTemplate::New(PlusOne)->GetFunction()); + + Persistent constructor = Persistent::New(tpl->GetFunction()); + target->Set(String::NewSymbol("MyObject"), constructor); + } + + Handle MyObject::New(const Arguments& args) { + HandleScope scope; + + MyObject* obj = new MyObject(); + obj->counter_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(); + obj->Wrap(args.This()); + + return args.This(); + } + + Handle MyObject::PlusOne(const Arguments& args) { + HandleScope scope; + + MyObject* obj = ObjectWrap::Unwrap(args.This()); + obj->counter_ += 1; + + return scope.Close(Number::New(obj->counter_)); + } + +Test it with: + + var addon = require('./build/Release/addon'); + + var obj = new addon.MyObject(10); + console.log( obj.plusOne() ); // 11 + console.log( obj.plusOne() ); // 12 + console.log( obj.plusOne() ); // 13 + + +#### Factory of wrapped objects + +This is useful when you want to be able to create native objects without +explicitly instantiating them with the `new` operator in JavaScript, e.g. + + var obj = addon.createObject(); + // instead of: + // var obj = new addon.Object(); + +Let's register our `createObject` method in `addon.cc`: + + #define BUILDING_NODE_EXTENSION + #include + #include "myobject.h" + + using namespace v8; + + Handle CreateObject(const Arguments& args) { + HandleScope scope; + return scope.Close(MyObject::NewInstance(args)); + } + + void InitAll(Handle target) { + MyObject::Init(); + + target->Set(String::NewSymbol("createObject"), + FunctionTemplate::New(CreateObject)->GetFunction()); + } + + NODE_MODULE(addon, InitAll) + +In `myobject.h` we now introduce the static method `NewInstance` that takes +care of instantiating the object (i.e. it does the job of `new` in JavaScript): + + #define BUILDING_NODE_EXTENSION + #ifndef MYOBJECT_H + #define MYOBJECT_H + + #include + + class MyObject : public node::ObjectWrap { + public: + static void Init(); + static v8::Handle NewInstance(const v8::Arguments& args); + + private: + MyObject(); + ~MyObject(); + + static v8::Persistent constructor; + static v8::Handle New(const v8::Arguments& args); + static v8::Handle PlusOne(const v8::Arguments& args); + double counter_; + }; + + #endif + +The implementation is similar to the above in `myobject.cc`: + + #define BUILDING_NODE_EXTENSION + #include + #include "myobject.h" + + using namespace v8; + + MyObject::MyObject() {}; + MyObject::~MyObject() {}; + + Persistent MyObject::constructor; + + void MyObject::Init() { + // Prepare constructor template + Local tpl = FunctionTemplate::New(New); + tpl->SetClassName(String::NewSymbol("MyObject")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + // Prototype + tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"), + FunctionTemplate::New(PlusOne)->GetFunction()); + + constructor = Persistent::New(tpl->GetFunction()); + } + + Handle MyObject::New(const Arguments& args) { + HandleScope scope; + + MyObject* obj = new MyObject(); + obj->counter_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(); + obj->Wrap(args.This()); + + return args.This(); + } + + Handle MyObject::NewInstance(const Arguments& args) { + HandleScope scope; + + const unsigned argc = 1; + Handle argv[argc] = { args[0] }; + Local instance = constructor->NewInstance(argc, argv); + + return scope.Close(instance); + } + + Handle MyObject::PlusOne(const Arguments& args) { + HandleScope scope; + + MyObject* obj = ObjectWrap::Unwrap(args.This()); + obj->counter_ += 1; + + return scope.Close(Number::New(obj->counter_)); + } + +Test it with: + + var addon = require('./build/Release/addon'); + + var obj = addon.createObject(10); + console.log( obj.plusOne() ); // 11 + console.log( obj.plusOne() ); // 12 + console.log( obj.plusOne() ); // 13 + + var obj2 = addon.createObject(20); + console.log( obj2.plusOne() ); // 21 + console.log( obj2.plusOne() ); // 22 + console.log( obj2.plusOne() ); // 23 + + +#### Passing wrapped objects around + +In addition to wrapping and returning C++ objects, you can pass them around +by unwrapping them with Node's `node::ObjectWrap::Unwrap` helper function. +In the following `addon.cc` we introduce a function `add()` that can take on two +`MyObject` objects: + + #define BUILDING_NODE_EXTENSION + #include + #include "myobject.h" + + using namespace v8; + + Handle CreateObject(const Arguments& args) { + HandleScope scope; + return scope.Close(MyObject::NewInstance(args)); + } + + Handle Add(const Arguments& args) { + HandleScope scope; + + MyObject* obj1 = node::ObjectWrap::Unwrap( + args[0]->ToObject()); + MyObject* obj2 = node::ObjectWrap::Unwrap( + args[1]->ToObject()); + + double sum = obj1->Val() + obj2->Val(); + return scope.Close(Number::New(sum)); + } + + void InitAll(Handle target) { + MyObject::Init(); + + target->Set(String::NewSymbol("createObject"), + FunctionTemplate::New(CreateObject)->GetFunction()); + + target->Set(String::NewSymbol("add"), + FunctionTemplate::New(Add)->GetFunction()); + } + + NODE_MODULE(addon, InitAll) + +To make things interesting we introduce a public method in `myobject.h` so we +can probe private values after unwrapping the object: + + #define BUILDING_NODE_EXTENSION + #ifndef MYOBJECT_H + #define MYOBJECT_H + + #include + + class MyObject : public node::ObjectWrap { + public: + static void Init(); + static v8::Handle NewInstance(const v8::Arguments& args); + double Val() const { return val_; } + + private: + MyObject(); + ~MyObject(); + + static v8::Persistent constructor; + static v8::Handle New(const v8::Arguments& args); + double val_; + }; + + #endif + +The implementation of `myobject.cc` is similar as before: + + #define BUILDING_NODE_EXTENSION + #include + #include "myobject.h" + + using namespace v8; + + MyObject::MyObject() {}; + MyObject::~MyObject() {}; + + Persistent MyObject::constructor; + + void MyObject::Init() { + // Prepare constructor template + Local tpl = FunctionTemplate::New(New); + tpl->SetClassName(String::NewSymbol("MyObject")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + constructor = Persistent::New(tpl->GetFunction()); + } + + Handle MyObject::New(const Arguments& args) { + HandleScope scope; + + MyObject* obj = new MyObject(); + obj->val_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(); + obj->Wrap(args.This()); + + return args.This(); + } + + Handle MyObject::NewInstance(const Arguments& args) { + HandleScope scope; + + const unsigned argc = 1; + Handle argv[argc] = { args[0] }; + Local instance = constructor->NewInstance(argc, argv); + + return scope.Close(instance); + } + +Test it with: + + var addon = require('./build/Release/addon'); + + var obj1 = addon.createObject(10); + var obj2 = addon.createObject(20); + var result = addon.add(obj1, obj2); + + console.log(result); // 30 From 4fdec07bd81a604762218a8e9d0649fb5cef7b6c Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 19 Jan 2012 21:44:59 +0600 Subject: [PATCH 13/30] Land number collision fix for v8 3.6 by Erik Corry Update with fixes from: http://codereview.chromium.org/9190001/ --- deps/v8/src/ia32/code-stubs-ia32.cc | 1 + deps/v8/src/objects.h | 8 ++------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/deps/v8/src/ia32/code-stubs-ia32.cc b/deps/v8/src/ia32/code-stubs-ia32.cc index dd0b8e8b65b..2bf486d3a6f 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.cc +++ b/deps/v8/src/ia32/code-stubs-ia32.cc @@ -5614,6 +5614,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, __ mov(scratch, Operand::StaticArray(scratch, times_pointer_size, roots_address)); + __ SmiUntag(scratch); __ add(scratch, Operand(character)); __ mov(hash, scratch); __ shl(scratch, 10); diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 6a82a908120..be35401d037 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -2534,9 +2534,7 @@ class HashTable: public FixedArray { // Wrapper methods inline uint32_t Hash(Key key) { if (Shape::UsesSeed) { - // I'm using map()->heap() to skip is_safe_to_read_maps assertion. - // That was done, because NumberDictionary is used inside GC. - return Shape::SeededHash(key, map()->heap()->HashSeed()); + return Shape::SeededHash(key, GetHeap()->HashSeed()); } else { return Shape::Hash(key); } @@ -2544,9 +2542,7 @@ class HashTable: public FixedArray { inline uint32_t HashForObject(Key key, Object* object) { if (Shape::UsesSeed) { - // I'm using map()->heap() to skip is_safe_to_read_maps assertion. - // That was done, because NumberDictionary is used inside GC. - return Shape::SeededHashForObject(key, map()->heap()->HashSeed(), object); + return Shape::SeededHashForObject(key, GetHeap()->HashSeed(), object); } else { return Shape::HashForObject(key, object); } From 4afc46d7bcbb1caec986ddba642c943d79b83105 Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 19 Jan 2012 18:07:12 -0800 Subject: [PATCH 14/30] Upgrade V8 to 3.6.6.19 --- deps/v8/src/arm/assembler-arm-inl.h | 9 +- deps/v8/src/arm/assembler-arm.h | 10 +- deps/v8/src/arm/code-stubs-arm.cc | 7 +- deps/v8/src/arm/deoptimizer-arm.cc | 76 ++---- deps/v8/src/arm/lithium-codegen-arm.cc | 222 ++++++++-------- deps/v8/src/arm/lithium-codegen-arm.h | 30 +-- deps/v8/src/arm/lithium-gap-resolver-arm.cc | 21 +- deps/v8/src/deoptimizer.cc | 18 +- deps/v8/src/hydrogen-instructions.h | 1 + deps/v8/src/ia32/code-stubs-ia32.cc | 6 +- deps/v8/src/ia32/deoptimizer-ia32.cc | 116 ++++----- deps/v8/src/ia32/lithium-codegen-ia32.cc | 212 ++++++++-------- deps/v8/src/ia32/lithium-codegen-ia32.h | 34 +-- deps/v8/src/ia32/lithium-ia32.h | 10 - deps/v8/src/lithium.h | 8 +- deps/v8/src/mips/code-stubs-mips.cc | 2 +- deps/v8/src/objects.cc | 11 +- deps/v8/src/objects.h | 9 +- deps/v8/src/safepoint-table.cc | 60 ++--- deps/v8/src/safepoint-table.h | 78 +++--- deps/v8/src/version.cc | 4 +- deps/v8/src/x64/code-stubs-x64.cc | 5 +- deps/v8/src/x64/deoptimizer-x64.cc | 147 ++--------- deps/v8/src/x64/lithium-codegen-x64.cc | 236 +++++++----------- deps/v8/src/x64/lithium-codegen-x64.h | 29 +-- .../mjsunit/compiler/regress-funcaller.js | 11 +- .../mjsunit/compiler/regress-lazy-deopt.js | 48 ++++ 27 files changed, 586 insertions(+), 834 deletions(-) create mode 100644 deps/v8/test/mjsunit/compiler/regress-lazy-deopt.js diff --git a/deps/v8/src/arm/assembler-arm-inl.h b/deps/v8/src/arm/assembler-arm-inl.h index 3e19a45385b..5ad7b5abaec 100644 --- a/deps/v8/src/arm/assembler-arm-inl.h +++ b/deps/v8/src/arm/assembler-arm-inl.h @@ -32,7 +32,7 @@ // The original source code covered by the above license above has been modified // significantly by Google Inc. -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. #ifndef V8_ARM_ASSEMBLER_ARM_INL_H_ #define V8_ARM_ASSEMBLER_ARM_INL_H_ @@ -46,6 +46,13 @@ namespace v8 { namespace internal { +int DwVfpRegister::ToAllocationIndex(DwVfpRegister reg) { + ASSERT(!reg.is(kDoubleRegZero)); + ASSERT(!reg.is(kScratchDoubleReg)); + return reg.code(); +} + + void RelocInfo::apply(intptr_t delta) { if (RelocInfo::IsInternalReference(rmode_)) { // absolute code pointer inside code object moves with the code object. diff --git a/deps/v8/src/arm/assembler-arm.h b/deps/v8/src/arm/assembler-arm.h index 9a586936fe0..eeadaca8a82 100644 --- a/deps/v8/src/arm/assembler-arm.h +++ b/deps/v8/src/arm/assembler-arm.h @@ -32,7 +32,7 @@ // The original source code covered by the above license above has been // modified significantly by Google Inc. -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // A light-weight ARM Assembler // Generates user mode instructions for the ARM architecture up to version 5 @@ -176,14 +176,11 @@ struct DwVfpRegister { static const int kNumAllocatableRegisters = kNumRegisters - kNumReservedRegisters; - static int ToAllocationIndex(DwVfpRegister reg) { - ASSERT(reg.code() != 0); - return reg.code() - 1; - } + inline static int ToAllocationIndex(DwVfpRegister reg); static DwVfpRegister FromAllocationIndex(int index) { ASSERT(index >= 0 && index < kNumAllocatableRegisters); - return from_code(index + 1); + return from_code(index); } static const char* AllocationIndexToString(int index) { @@ -307,6 +304,7 @@ const DwVfpRegister d15 = { 15 }; const DwVfpRegister kFirstCalleeSavedDoubleReg = d8; const DwVfpRegister kLastCalleeSavedDoubleReg = d15; const DwVfpRegister kDoubleRegZero = d14; +const DwVfpRegister kScratchDoubleReg = d15; // Coprocessor register diff --git a/deps/v8/src/arm/code-stubs-arm.cc b/deps/v8/src/arm/code-stubs-arm.cc index a59b6fd6333..36450c945f3 100644 --- a/deps/v8/src/arm/code-stubs-arm.cc +++ b/deps/v8/src/arm/code-stubs-arm.cc @@ -5392,13 +5392,12 @@ void StringHelper::GenerateHashGetHash(MacroAssembler* masm, // hash ^= hash >> 11; __ eor(hash, hash, Operand(hash, LSR, 11)); // hash += hash << 15; - __ add(hash, hash, Operand(hash, LSL, 15), SetCC); + __ add(hash, hash, Operand(hash, LSL, 15)); - uint32_t kHashShiftCutOffMask = (1 << (32 - String::kHashShift)) - 1; - __ and_(hash, hash, Operand(kHashShiftCutOffMask)); + __ and_(hash, hash, Operand(String::kHashBitMask), SetCC); // if (hash == 0) hash = 27; - __ mov(hash, Operand(27), LeaveCC, eq); + __ mov(hash, Operand(StringHasher::kZeroHash), LeaveCC, eq); } diff --git a/deps/v8/src/arm/deoptimizer-arm.cc b/deps/v8/src/arm/deoptimizer-arm.cc index 21e2d0fee1c..d4f251f7603 100644 --- a/deps/v8/src/arm/deoptimizer-arm.cc +++ b/deps/v8/src/arm/deoptimizer-arm.cc @@ -44,12 +44,6 @@ int Deoptimizer::patch_size() { } -void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle code) { - // Nothing to do. No new relocation information is written for lazy - // deoptimization on ARM. -} - - void Deoptimizer::DeoptimizeFunction(JSFunction* function) { HandleScope scope; AssertNoAllocation no_allocation; @@ -58,59 +52,38 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { // Get the optimized code. Code* code = function->code(); + Address code_start_address = code->instruction_start(); // Invalidate the relocation information, as it will become invalid by the // code patching below, and is not needed any more. code->InvalidateRelocation(); - // For each return after a safepoint insert an absolute call to the - // corresponding deoptimization entry. - unsigned last_pc_offset = 0; - SafepointTable table(function->code()); - for (unsigned i = 0; i < table.length(); i++) { - unsigned pc_offset = table.GetPcOffset(i); - SafepointEntry safepoint_entry = table.GetEntry(i); - int deoptimization_index = safepoint_entry.deoptimization_index(); - int gap_code_size = safepoint_entry.gap_code_size(); - // Check that we did not shoot past next safepoint. - CHECK(pc_offset >= last_pc_offset); + // For each LLazyBailout instruction insert a call to the corresponding + // deoptimization entry. + DeoptimizationInputData* deopt_data = + DeoptimizationInputData::cast(code->deoptimization_data()); #ifdef DEBUG - // Destroy the code which is not supposed to be run again. - int instructions = (pc_offset - last_pc_offset) / Assembler::kInstrSize; - CodePatcher destroyer(code->instruction_start() + last_pc_offset, - instructions); - for (int x = 0; x < instructions; x++) { - destroyer.masm()->bkpt(0); - } + Address prev_call_address = NULL; #endif - last_pc_offset = pc_offset; - if (deoptimization_index != Safepoint::kNoDeoptimizationIndex) { - Address deoptimization_entry = Deoptimizer::GetDeoptimizationEntry( - deoptimization_index, Deoptimizer::LAZY); - last_pc_offset += gap_code_size; - int call_size_in_bytes = MacroAssembler::CallSize(deoptimization_entry, - RelocInfo::NONE); - int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize; - ASSERT(call_size_in_bytes % Assembler::kInstrSize == 0); - ASSERT(call_size_in_bytes <= patch_size()); - CodePatcher patcher(code->instruction_start() + last_pc_offset, - call_size_in_words); - patcher.masm()->Call(deoptimization_entry, RelocInfo::NONE); - last_pc_offset += call_size_in_bytes; - } - } - + for (int i = 0; i < deopt_data->DeoptCount(); i++) { + if (deopt_data->Pc(i)->value() == -1) continue; + Address call_address = code_start_address + deopt_data->Pc(i)->value(); + Address deopt_entry = GetDeoptimizationEntry(i, LAZY); + int call_size_in_bytes = MacroAssembler::CallSize(deopt_entry, + RelocInfo::NONE); + int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize; + ASSERT(call_size_in_bytes % Assembler::kInstrSize == 0); + ASSERT(call_size_in_bytes <= patch_size()); + CodePatcher patcher(call_address, call_size_in_words); + patcher.masm()->Call(deopt_entry, RelocInfo::NONE); + ASSERT(prev_call_address == NULL || + call_address >= prev_call_address + patch_size()); + ASSERT(call_address + patch_size() <= code->instruction_end()); #ifdef DEBUG - // Destroy the code which is not supposed to be run again. - int instructions = - (code->safepoint_table_offset() - last_pc_offset) / Assembler::kInstrSize; - CodePatcher destroyer(code->instruction_start() + last_pc_offset, - instructions); - for (int x = 0; x < instructions; x++) { - destroyer.masm()->bkpt(0); - } + prev_call_address = call_address; #endif + } // Add the deoptimizing code to the list. DeoptimizingCodeListNode* node = new DeoptimizingCodeListNode(code); @@ -125,11 +98,6 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { PrintF("[forced deoptimization: "); function->PrintName(); PrintF(" / %x]\n", reinterpret_cast(function)); -#ifdef DEBUG - if (FLAG_print_code) { - code->PrintLn(); - } -#endif } } diff --git a/deps/v8/src/arm/lithium-codegen-arm.cc b/deps/v8/src/arm/lithium-codegen-arm.cc index ee155e4de1c..4a201ab987b 100644 --- a/deps/v8/src/arm/lithium-codegen-arm.cc +++ b/deps/v8/src/arm/lithium-codegen-arm.cc @@ -40,37 +40,22 @@ class SafepointGenerator : public CallWrapper { public: SafepointGenerator(LCodeGen* codegen, LPointerMap* pointers, - int deoptimization_index) + Safepoint::DeoptMode mode) : codegen_(codegen), pointers_(pointers), - deoptimization_index_(deoptimization_index) { } + deopt_mode_(mode) { } virtual ~SafepointGenerator() { } - virtual void BeforeCall(int call_size) const { - ASSERT(call_size >= 0); - // Ensure that we have enough space after the previous safepoint position - // for the generated code there. - int call_end = codegen_->masm()->pc_offset() + call_size; - int prev_jump_end = - codegen_->LastSafepointEnd() + Deoptimizer::patch_size(); - if (call_end < prev_jump_end) { - int padding_size = prev_jump_end - call_end; - ASSERT_EQ(0, padding_size % Assembler::kInstrSize); - while (padding_size > 0) { - codegen_->masm()->nop(); - padding_size -= Assembler::kInstrSize; - } - } - } + virtual void BeforeCall(int call_size) const { } virtual void AfterCall() const { - codegen_->RecordSafepoint(pointers_, deoptimization_index_); + codegen_->RecordSafepoint(pointers_, deopt_mode_); } private: LCodeGen* codegen_; LPointerMap* pointers_; - int deoptimization_index_; + Safepoint::DeoptMode deopt_mode_; }; @@ -95,7 +80,6 @@ void LCodeGen::FinishCode(Handle code) { code->set_stack_slots(GetStackSlotCount()); code->set_safepoint_table_offset(safepoints_.GetCodeOffset()); PopulateDeoptimizationData(code); - Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(code); } @@ -192,7 +176,7 @@ bool LCodeGen::GeneratePrologue() { } else { __ CallRuntime(Runtime::kNewFunctionContext, 1); } - RecordSafepoint(Safepoint::kNoDeoptimizationIndex); + RecordSafepoint(Safepoint::kNoLazyDeopt); // Context is returned in both r0 and cp. It replaces the context // passed to us. It's saved in the stack and kept live in cp. __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); @@ -243,19 +227,11 @@ bool LCodeGen::GenerateBody() { instr->CompileToNative(this); } } + EnsureSpaceForLazyDeopt(); return !is_aborted(); } -LInstruction* LCodeGen::GetNextInstruction() { - if (current_instruction_ < instructions_->length() - 1) { - return instructions_->at(current_instruction_ + 1); - } else { - return NULL; - } -} - - bool LCodeGen::GenerateDeferredCode() { ASSERT(is_generating()); if (deferred_.length() > 0) { @@ -265,13 +241,6 @@ bool LCodeGen::GenerateDeferredCode() { code->Generate(); __ jmp(code->exit()); } - - // Pad code to ensure that the last piece of deferred code have - // room for lazy bailout. - while ((masm()->pc_offset() - LastSafepointEnd()) - < Deoptimizer::patch_size()) { - __ nop(); - } } // Force constant pool emission at the end of the deferred code to make @@ -551,7 +520,7 @@ void LCodeGen::CallCodeGeneric(Handle code, LPointerMap* pointers = instr->pointer_map(); RecordPosition(pointers->position()); __ Call(code, mode); - RegisterLazyDeoptimization(instr, safepoint_mode); + RecordSafepointWithLazyDeopt(instr, safepoint_mode); // Signal that we don't inline smi code before these stubs in the // optimizing code generator. @@ -571,7 +540,7 @@ void LCodeGen::CallRuntime(const Runtime::Function* function, RecordPosition(pointers->position()); __ CallRuntime(function, num_arguments); - RegisterLazyDeoptimization(instr, RECORD_SIMPLE_SAFEPOINT); + RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); } @@ -580,37 +549,12 @@ void LCodeGen::CallRuntimeFromDeferred(Runtime::FunctionId id, LInstruction* instr) { __ CallRuntimeSaveDoubles(id); RecordSafepointWithRegisters( - instr->pointer_map(), argc, Safepoint::kNoDeoptimizationIndex); + instr->pointer_map(), argc, Safepoint::kNoLazyDeopt); } -void LCodeGen::RegisterLazyDeoptimization(LInstruction* instr, - SafepointMode safepoint_mode) { - // Create the environment to bailout to. If the call has side effects - // execution has to continue after the call otherwise execution can continue - // from a previous bailout point repeating the call. - LEnvironment* deoptimization_environment; - if (instr->HasDeoptimizationEnvironment()) { - deoptimization_environment = instr->deoptimization_environment(); - } else { - deoptimization_environment = instr->environment(); - } - - RegisterEnvironmentForDeoptimization(deoptimization_environment); - if (safepoint_mode == RECORD_SIMPLE_SAFEPOINT) { - RecordSafepoint(instr->pointer_map(), - deoptimization_environment->deoptimization_index()); - } else { - ASSERT(safepoint_mode == RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); - RecordSafepointWithRegisters( - instr->pointer_map(), - 0, - deoptimization_environment->deoptimization_index()); - } -} - - -void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment) { +void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment, + Safepoint::DeoptMode mode) { if (!environment->HasBeenRegistered()) { // Physical stack frame layout: // -x ............. -4 0 ..................................... y @@ -632,14 +576,17 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment) { Translation translation(&translations_, frame_count); WriteTranslation(environment, &translation); int deoptimization_index = deoptimizations_.length(); - environment->Register(deoptimization_index, translation.index()); + int pc_offset = masm()->pc_offset(); + environment->Register(deoptimization_index, + translation.index(), + (mode == Safepoint::kLazyDeopt) ? pc_offset : -1); deoptimizations_.Add(environment); } } void LCodeGen::DeoptimizeIf(Condition cc, LEnvironment* environment) { - RegisterEnvironmentForDeoptimization(environment); + RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); ASSERT(environment->HasBeenRegistered()); int id = environment->deoptimization_index(); Address entry = Deoptimizer::GetDeoptimizationEntry(id, Deoptimizer::EAGER); @@ -701,6 +648,7 @@ void LCodeGen::PopulateDeoptimizationData(Handle code) { data->SetTranslationIndex(i, Smi::FromInt(env->translation_index())); data->SetArgumentsStackHeight(i, Smi::FromInt(env->arguments_stack_height())); + data->SetPc(i, Smi::FromInt(env->pc_offset())); } code->set_deoptimization_data(*data); } @@ -732,16 +680,28 @@ void LCodeGen::PopulateDeoptimizationLiteralsWithInlinedFunctions() { } +void LCodeGen::RecordSafepointWithLazyDeopt( + LInstruction* instr, SafepointMode safepoint_mode) { + if (safepoint_mode == RECORD_SIMPLE_SAFEPOINT) { + RecordSafepoint(instr->pointer_map(), Safepoint::kLazyDeopt); + } else { + ASSERT(safepoint_mode == RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + RecordSafepointWithRegisters( + instr->pointer_map(), 0, Safepoint::kLazyDeopt); + } +} + + void LCodeGen::RecordSafepoint( LPointerMap* pointers, Safepoint::Kind kind, int arguments, - int deoptimization_index) { + Safepoint::DeoptMode deopt_mode) { ASSERT(expected_safepoint_kind_ == kind); const ZoneList* operands = pointers->operands(); Safepoint safepoint = safepoints_.DefineSafepoint(masm(), - kind, arguments, deoptimization_index); + kind, arguments, deopt_mode); for (int i = 0; i < operands->length(); i++) { LOperand* pointer = operands->at(i); if (pointer->IsStackSlot()) { @@ -758,31 +718,31 @@ void LCodeGen::RecordSafepoint( void LCodeGen::RecordSafepoint(LPointerMap* pointers, - int deoptimization_index) { - RecordSafepoint(pointers, Safepoint::kSimple, 0, deoptimization_index); + Safepoint::DeoptMode deopt_mode) { + RecordSafepoint(pointers, Safepoint::kSimple, 0, deopt_mode); } -void LCodeGen::RecordSafepoint(int deoptimization_index) { +void LCodeGen::RecordSafepoint(Safepoint::DeoptMode deopt_mode) { LPointerMap empty_pointers(RelocInfo::kNoPosition); - RecordSafepoint(&empty_pointers, deoptimization_index); + RecordSafepoint(&empty_pointers, deopt_mode); } void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers, int arguments, - int deoptimization_index) { - RecordSafepoint(pointers, Safepoint::kWithRegisters, arguments, - deoptimization_index); + Safepoint::DeoptMode deopt_mode) { + RecordSafepoint( + pointers, Safepoint::kWithRegisters, arguments, deopt_mode); } void LCodeGen::RecordSafepointWithRegistersAndDoubles( LPointerMap* pointers, int arguments, - int deoptimization_index) { - RecordSafepoint(pointers, Safepoint::kWithRegistersAndDoubles, arguments, - deoptimization_index); + Safepoint::DeoptMode deopt_mode) { + RecordSafepoint( + pointers, Safepoint::kWithRegistersAndDoubles, arguments, deopt_mode); } @@ -817,12 +777,6 @@ void LCodeGen::DoGap(LGap* gap) { LParallelMove* move = gap->GetParallelMove(inner_pos); if (move != NULL) DoParallelMove(move); } - - LInstruction* next = GetNextInstruction(); - if (next != NULL && next->IsLazyBailout()) { - int pc = masm()->pc_offset(); - safepoints_.SetPcAfterGap(pc); - } } @@ -1129,7 +1083,7 @@ void LCodeGen::DoDeferredBinaryOpStub(LTemplateInstruction<1, 2, T>* instr, __ CallStub(&stub); RecordSafepointWithRegistersAndDoubles(instr->pointer_map(), 0, - Safepoint::kNoDeoptimizationIndex); + Safepoint::kNoLazyDeopt); // Overwrite the stored value of r0 with the result of the stub. __ StoreToSafepointRegistersAndDoublesSlot(r0, r0); } @@ -2014,7 +1968,7 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { LInstanceOfKnownGlobal* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { - codegen()->DoDeferredLInstanceOfKnownGlobal(instr_, &map_check_); + codegen()->DoDeferredInstanceOfKnownGlobal(instr_, &map_check_); } Label* map_check() { return &map_check_; } @@ -2082,8 +2036,8 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { } -void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, - Label* map_check) { +void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, + Label* map_check) { Register result = ToRegister(instr->result()); ASSERT(result.is(r0)); @@ -2115,6 +2069,9 @@ void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, RelocInfo::CODE_TARGET, instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + ASSERT(instr->HasDeoptimizationEnvironment()); + LEnvironment* env = instr->deoptimization_environment(); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); // Put the result value into the result register slot and // restore all registers. __ StoreToSafepointRegisterSlot(result, result); @@ -2712,12 +2669,9 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { __ bind(&invoke); ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); - LEnvironment* env = instr->deoptimization_environment(); RecordPosition(pointers->position()); - RegisterEnvironmentForDeoptimization(env); - SafepointGenerator safepoint_generator(this, - pointers, - env->deoptimization_index()); + SafepointGenerator safepoint_generator( + this, pointers, Safepoint::kLazyDeopt); // The number of arguments is stored in receiver which is r0, as expected // by InvokeFunction. v8::internal::ParameterCount actual(receiver); @@ -2799,7 +2753,7 @@ void LCodeGen::CallKnownFunction(Handle function, __ Call(ip); // Setup deoptimization. - RegisterLazyDeoptimization(instr, RECORD_SIMPLE_SAFEPOINT); + RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); // Restore context. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); @@ -3163,10 +3117,8 @@ void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) { ASSERT(instr->HasPointerMap()); ASSERT(instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); - LEnvironment* env = instr->deoptimization_environment(); RecordPosition(pointers->position()); - RegisterEnvironmentForDeoptimization(env); - SafepointGenerator generator(this, pointers, env->deoptimization_index()); + SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt); ParameterCount count(instr->arity()); __ InvokeFunction(r1, count, CALL_FUNCTION, generator, CALL_AS_METHOD); __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); @@ -4403,9 +4355,29 @@ void LCodeGen::EmitIsConstructCall(Register temp1, Register temp2) { } +void LCodeGen::EnsureSpaceForLazyDeopt() { + // Ensure that we have enough space after the previous lazy-bailout + // instruction for patching the code here. + int current_pc = masm()->pc_offset(); + int patch_size = Deoptimizer::patch_size(); + if (current_pc < last_lazy_deopt_pc_ + patch_size) { + int padding_size = last_lazy_deopt_pc_ + patch_size - current_pc; + ASSERT_EQ(0, padding_size % Assembler::kInstrSize); + while (padding_size > 0) { + __ nop(); + padding_size -= Assembler::kInstrSize; + } + } + last_lazy_deopt_pc_ = masm()->pc_offset(); +} + + void LCodeGen::DoLazyBailout(LLazyBailout* instr) { - // No code for lazy bailout instruction. Used to capture environment after a - // call for populating the safepoint data with deoptimization data. + EnsureSpaceForLazyDeopt(); + ASSERT(instr->HasEnvironment()); + LEnvironment* env = instr->environment(); + RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); } @@ -4422,12 +4394,9 @@ void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) { __ Push(object, key, strict); ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); - LEnvironment* env = instr->deoptimization_environment(); RecordPosition(pointers->position()); - RegisterEnvironmentForDeoptimization(env); - SafepointGenerator safepoint_generator(this, - pointers, - env->deoptimization_index()); + SafepointGenerator safepoint_generator( + this, pointers, Safepoint::kLazyDeopt); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, safepoint_generator); } @@ -4438,27 +4407,20 @@ void LCodeGen::DoIn(LIn* instr) { __ Push(key, obj); ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); - LEnvironment* env = instr->deoptimization_environment(); RecordPosition(pointers->position()); - RegisterEnvironmentForDeoptimization(env); - SafepointGenerator safepoint_generator(this, - pointers, - env->deoptimization_index()); + SafepointGenerator safepoint_generator(this, pointers, Safepoint::kLazyDeopt); __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION, safepoint_generator); } void LCodeGen::DoDeferredStackCheck(LStackCheck* instr) { - { - PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); - __ CallRuntimeSaveDoubles(Runtime::kStackGuard); - RegisterLazyDeoptimization( - instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); - } - - // The gap code includes the restoring of the safepoint registers. - int pc = masm()->pc_offset(); - safepoints_.SetPcAfterGap(pc); + PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); + __ CallRuntimeSaveDoubles(Runtime::kStackGuard); + RecordSafepointWithLazyDeopt( + instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + ASSERT(instr->HasEnvironment()); + LEnvironment* env = instr->environment(); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); } @@ -4472,6 +4434,10 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { LStackCheck* instr_; }; + ASSERT(instr->HasEnvironment()); + LEnvironment* env = instr->environment(); + // There is no LLazyBailout instruction for stack-checks. We have to + // prepare for lazy deoptimization explicitly here. if (instr->hydrogen()->is_function_entry()) { // Perform stack overflow check. Label done; @@ -4480,7 +4446,10 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { __ b(hs, &done); StackCheckStub stub; CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + EnsureSpaceForLazyDeopt(); __ bind(&done); + RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); } else { ASSERT(instr->hydrogen()->is_backwards_branch()); // Perform stack overflow check if this goto needs it before jumping. @@ -4489,8 +4458,13 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { __ LoadRoot(ip, Heap::kStackLimitRootIndex); __ cmp(sp, Operand(ip)); __ b(lo, deferred_stack_check->entry()); + EnsureSpaceForLazyDeopt(); __ bind(instr->done_label()); deferred_stack_check->SetExit(instr->done_label()); + RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); + // Don't record a deoptimization index for the safepoint here. + // This will be done explicitly when emitting call and the safepoint in + // the deferred code. } } @@ -4506,7 +4480,7 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) { // If the environment were already registered, we would have no way of // backpatching it with the spill slot operands. ASSERT(!environment->HasBeenRegistered()); - RegisterEnvironmentForDeoptimization(environment); + RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); ASSERT(osr_pc_offset_ == -1); osr_pc_offset_ = masm()->pc_offset(); } diff --git a/deps/v8/src/arm/lithium-codegen-arm.h b/deps/v8/src/arm/lithium-codegen-arm.h index ead84890346..0e34c9f8542 100644 --- a/deps/v8/src/arm/lithium-codegen-arm.h +++ b/deps/v8/src/arm/lithium-codegen-arm.h @@ -58,6 +58,7 @@ class LCodeGen BASE_EMBEDDED { status_(UNUSED), deferred_(8), osr_pc_offset_(-1), + last_lazy_deopt_pc_(0), resolver_(this), expected_safepoint_kind_(Safepoint::kSimple) { PopulateDeoptimizationLiteralsWithInlinedFunctions(); @@ -111,8 +112,8 @@ class LCodeGen BASE_EMBEDDED { void DoDeferredStackCheck(LStackCheck* instr); void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredStringCharFromCode(LStringCharFromCode* instr); - void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, - Label* map_check); + void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, + Label* map_check); // Parallel move support. void DoParallelMove(LParallelMove* move); @@ -148,7 +149,7 @@ class LCodeGen BASE_EMBEDDED { HGraph* graph() const { return chunk_->graph(); } Register scratch0() { return r9; } - DwVfpRegister double_scratch0() { return d15; } + DwVfpRegister double_scratch0() { return kScratchDoubleReg; } int GetNextEmittedBlock(int block); LInstruction* GetNextInstruction(); @@ -214,10 +215,11 @@ class LCodeGen BASE_EMBEDDED { void LoadHeapObject(Register result, Handle object); - void RegisterLazyDeoptimization(LInstruction* instr, - SafepointMode safepoint_mode); + void RecordSafepointWithLazyDeopt(LInstruction* instr, + SafepointMode safepoint_mode); - void RegisterEnvironmentForDeoptimization(LEnvironment* environment); + void RegisterEnvironmentForDeoptimization(LEnvironment* environment, + Safepoint::DeoptMode mode); void DeoptimizeIf(Condition cc, LEnvironment* environment); void AddToTranslation(Translation* translation, @@ -246,19 +248,16 @@ class LCodeGen BASE_EMBEDDED { void RecordSafepoint(LPointerMap* pointers, Safepoint::Kind kind, int arguments, - int deoptimization_index); - void RecordSafepoint(LPointerMap* pointers, int deoptimization_index); - void RecordSafepoint(int deoptimization_index); + Safepoint::DeoptMode mode); + void RecordSafepoint(LPointerMap* pointers, Safepoint::DeoptMode mode); + void RecordSafepoint(Safepoint::DeoptMode mode); void RecordSafepointWithRegisters(LPointerMap* pointers, int arguments, - int deoptimization_index); + Safepoint::DeoptMode mode); void RecordSafepointWithRegistersAndDoubles(LPointerMap* pointers, int arguments, - int deoptimization_index); + Safepoint::DeoptMode mode); void RecordPosition(int position); - int LastSafepointEnd() { - return static_cast(safepoints_.GetPcAfterGap()); - } static Condition TokenToCondition(Token::Value op, bool is_unsigned); void EmitGoto(int block); @@ -300,6 +299,8 @@ class LCodeGen BASE_EMBEDDED { Address address; }; + void EnsureSpaceForLazyDeopt(); + LChunk* const chunk_; MacroAssembler* const masm_; CompilationInfo* const info_; @@ -316,6 +317,7 @@ class LCodeGen BASE_EMBEDDED { TranslationBuffer translations_; ZoneList deferred_; int osr_pc_offset_; + int last_lazy_deopt_pc_; // Builder that keeps track of safepoints in the code. The table // itself is emitted at the end of the generated code. diff --git a/deps/v8/src/arm/lithium-gap-resolver-arm.cc b/deps/v8/src/arm/lithium-gap-resolver-arm.cc index 26f60fac112..1cfdc791cc2 100644 --- a/deps/v8/src/arm/lithium-gap-resolver-arm.cc +++ b/deps/v8/src/arm/lithium-gap-resolver-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -34,7 +34,6 @@ namespace v8 { namespace internal { static const Register kSavedValueRegister = { 9 }; -static const DoubleRegister kSavedDoubleValueRegister = { 0 }; LGapResolver::LGapResolver(LCodeGen* owner) : cgen_(owner), moves_(32), root_index_(0), in_cycle_(false), @@ -172,9 +171,9 @@ void LGapResolver::BreakCycle(int index) { } else if (source->IsStackSlot()) { __ ldr(kSavedValueRegister, cgen_->ToMemOperand(source)); } else if (source->IsDoubleRegister()) { - __ vmov(kSavedDoubleValueRegister, cgen_->ToDoubleRegister(source)); + __ vmov(kScratchDoubleReg, cgen_->ToDoubleRegister(source)); } else if (source->IsDoubleStackSlot()) { - __ vldr(kSavedDoubleValueRegister, cgen_->ToMemOperand(source)); + __ vldr(kScratchDoubleReg, cgen_->ToMemOperand(source)); } else { UNREACHABLE(); } @@ -193,11 +192,9 @@ void LGapResolver::RestoreValue() { } else if (saved_destination_->IsStackSlot()) { __ str(kSavedValueRegister, cgen_->ToMemOperand(saved_destination_)); } else if (saved_destination_->IsDoubleRegister()) { - __ vmov(cgen_->ToDoubleRegister(saved_destination_), - kSavedDoubleValueRegister); + __ vmov(cgen_->ToDoubleRegister(saved_destination_), kScratchDoubleReg); } else if (saved_destination_->IsDoubleStackSlot()) { - __ vstr(kSavedDoubleValueRegister, - cgen_->ToMemOperand(saved_destination_)); + __ vstr(kScratchDoubleReg, cgen_->ToMemOperand(saved_destination_)); } else { UNREACHABLE(); } @@ -235,8 +232,8 @@ void LGapResolver::EmitMove(int index) { // ip is overwritten while saving the value to the destination. // Therefore we can't use ip. It is OK if the read from the source // destroys ip, since that happens before the value is read. - __ vldr(kSavedDoubleValueRegister.low(), source_operand); - __ vstr(kSavedDoubleValueRegister.low(), destination_operand); + __ vldr(kScratchDoubleReg.low(), source_operand); + __ vstr(kScratchDoubleReg.low(), destination_operand); } else { __ ldr(ip, source_operand); __ str(ip, destination_operand); @@ -286,8 +283,8 @@ void LGapResolver::EmitMove(int index) { __ ldr(kSavedValueRegister, source_high_operand); __ str(kSavedValueRegister, destination_high_operand); } else { - __ vldr(kSavedDoubleValueRegister, source_operand); - __ vstr(kSavedDoubleValueRegister, destination_operand); + __ vldr(kScratchDoubleReg, source_operand); + __ vstr(kScratchDoubleReg, destination_operand); } } } else { diff --git a/deps/v8/src/deoptimizer.cc b/deps/v8/src/deoptimizer.cc index 5feb73d739a..0ada28b0155 100644 --- a/deps/v8/src/deoptimizer.cc +++ b/deps/v8/src/deoptimizer.cc @@ -112,25 +112,11 @@ DeoptimizedFrameInfo* Deoptimizer::DebuggerInspectableFrame( // Get the function and code from the frame. JSFunction* function = JSFunction::cast(frame->function()); Code* code = frame->LookupCode(); - Address code_start_address = code->instruction_start(); // Locate the deoptimization point in the code. As we are at a call the // return address must be at a place in the code with deoptimization support. - int deoptimization_index = Safepoint::kNoDeoptimizationIndex; - // Scope this as the safe point constructor will disallow allocation. - { - SafepointTable table(code); - for (unsigned i = 0; i < table.length(); ++i) { - Address address = code_start_address + table.GetPcOffset(i); - if (address == frame->pc()) { - SafepointEntry safepoint_entry = table.GetEntry(i); - ASSERT(safepoint_entry.deoptimization_index() != - Safepoint::kNoDeoptimizationIndex); - deoptimization_index = safepoint_entry.deoptimization_index(); - break; - } - } - } + SafepointEntry safepoint_entry = code->GetSafepointEntry(frame->pc()); + int deoptimization_index = safepoint_entry.deoptimization_index(); ASSERT(deoptimization_index != Safepoint::kNoDeoptimizationIndex); // Always use the actual stack slots when calculating the fp to sp diff --git a/deps/v8/src/hydrogen-instructions.h b/deps/v8/src/hydrogen-instructions.h index 1bc28ba82dd..0af5489bed1 100644 --- a/deps/v8/src/hydrogen-instructions.h +++ b/deps/v8/src/hydrogen-instructions.h @@ -4058,6 +4058,7 @@ class HRegExpLiteral: public HMaterializedLiteral<1> { pattern_(pattern), flags_(flags) { SetOperandAt(0, context); + SetAllSideEffects(); } HValue* context() { return OperandAt(0); } diff --git a/deps/v8/src/ia32/code-stubs-ia32.cc b/deps/v8/src/ia32/code-stubs-ia32.cc index 2bf486d3a6f..8a5bd50ece1 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.cc +++ b/deps/v8/src/ia32/code-stubs-ia32.cc @@ -5665,14 +5665,12 @@ void StringHelper::GenerateHashGetHash(MacroAssembler* masm, __ shl(scratch, 15); __ add(hash, Operand(scratch)); - uint32_t kHashShiftCutOffMask = (1 << (32 - String::kHashShift)) - 1; - __ and_(hash, kHashShiftCutOffMask); + __ and_(hash, String::kHashBitMask); // if (hash == 0) hash = 27; Label hash_not_zero; - __ test(hash, Operand(hash)); __ j(not_zero, &hash_not_zero, Label::kNear); - __ mov(hash, Immediate(27)); + __ mov(hash, Immediate(StringHasher::kZeroHash)); __ bind(&hash_not_zero); } diff --git a/deps/v8/src/ia32/deoptimizer-ia32.cc b/deps/v8/src/ia32/deoptimizer-ia32.cc index e23f3e9eff3..080ad644dc4 100644 --- a/deps/v8/src/ia32/deoptimizer-ia32.cc +++ b/deps/v8/src/ia32/deoptimizer-ia32.cc @@ -45,16 +45,6 @@ int Deoptimizer::patch_size() { } -static void ZapCodeRange(Address start, Address end) { -#ifdef DEBUG - ASSERT(start <= end); - int size = end - start; - CodePatcher destroyer(start, size); - while (size-- > 0) destroyer.masm()->int3(); -#endif -} - - void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle code) { Isolate* isolate = code->GetIsolate(); HandleScope scope(isolate); @@ -62,30 +52,23 @@ void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle code) { // Compute the size of relocation information needed for the code // patching in Deoptimizer::DeoptimizeFunction. int min_reloc_size = 0; - Address prev_reloc_address = code->instruction_start(); - Address code_start_address = code->instruction_start(); - SafepointTable table(*code); - for (unsigned i = 0; i < table.length(); ++i) { - Address curr_reloc_address = code_start_address + table.GetPcOffset(i); - ASSERT_GE(curr_reloc_address, prev_reloc_address); - SafepointEntry safepoint_entry = table.GetEntry(i); - int deoptimization_index = safepoint_entry.deoptimization_index(); - if (deoptimization_index != Safepoint::kNoDeoptimizationIndex) { - // The gap code is needed to get to the state expected at the - // bailout and we need to skip the call opcode to get to the - // address that needs reloc. - curr_reloc_address += safepoint_entry.gap_code_size() + 1; - int pc_delta = curr_reloc_address - prev_reloc_address; - // We use RUNTIME_ENTRY reloc info which has a size of 2 bytes - // if encodable with small pc delta encoding and up to 6 bytes - // otherwise. - if (pc_delta <= RelocInfo::kMaxSmallPCDelta) { - min_reloc_size += 2; - } else { - min_reloc_size += 6; - } - prev_reloc_address = curr_reloc_address; + int prev_pc_offset = 0; + DeoptimizationInputData* deopt_data = + DeoptimizationInputData::cast(code->deoptimization_data()); + for (int i = 0; i < deopt_data->DeoptCount(); i++) { + int pc_offset = deopt_data->Pc(i)->value(); + if (pc_offset == -1) continue; + ASSERT_GE(pc_offset, prev_pc_offset); + int pc_delta = pc_offset - prev_pc_offset; + // We use RUNTIME_ENTRY reloc info which has a size of 2 bytes + // if encodable with small pc delta encoding and up to 6 bytes + // otherwise. + if (pc_delta <= RelocInfo::kMaxSmallPCDelta) { + min_reloc_size += 2; + } else { + min_reloc_size += 6; } + prev_pc_offset = pc_offset; } // If the relocation information is not big enough we create a new @@ -150,40 +133,40 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { Address reloc_end_address = reloc_info->address() + reloc_info->Size(); RelocInfoWriter reloc_info_writer(reloc_end_address, code_start_address); - // For each return after a safepoint insert a call to the corresponding - // deoptimization entry. Since the call is a relative encoding, write new + // For each LLazyBailout instruction insert a call to the corresponding + // deoptimization entry. + + // Since the call is a relative encoding, write new // reloc info. We do not need any of the existing reloc info because the // existing code will not be used again (we zap it in debug builds). - SafepointTable table(code); - Address prev_address = code_start_address; - for (unsigned i = 0; i < table.length(); ++i) { - Address curr_address = code_start_address + table.GetPcOffset(i); - ASSERT_GE(curr_address, prev_address); - ZapCodeRange(prev_address, curr_address); - - SafepointEntry safepoint_entry = table.GetEntry(i); - int deoptimization_index = safepoint_entry.deoptimization_index(); - if (deoptimization_index != Safepoint::kNoDeoptimizationIndex) { - // The gap code is needed to get to the state expected at the bailout. - curr_address += safepoint_entry.gap_code_size(); - - CodePatcher patcher(curr_address, patch_size()); - Address deopt_entry = GetDeoptimizationEntry(deoptimization_index, LAZY); - patcher.masm()->call(deopt_entry, RelocInfo::NONE); - - // We use RUNTIME_ENTRY for deoptimization bailouts. - RelocInfo rinfo(curr_address + 1, // 1 after the call opcode. - RelocInfo::RUNTIME_ENTRY, - reinterpret_cast(deopt_entry)); - reloc_info_writer.Write(&rinfo); - ASSERT_GE(reloc_info_writer.pos(), - reloc_info->address() + ByteArray::kHeaderSize); - curr_address += patch_size(); - } - prev_address = curr_address; + // + // Emit call to lazy deoptimization at all lazy deopt points. + DeoptimizationInputData* deopt_data = + DeoptimizationInputData::cast(code->deoptimization_data()); +#ifdef DEBUG + Address prev_call_address = NULL; +#endif + for (int i = 0; i < deopt_data->DeoptCount(); i++) { + if (deopt_data->Pc(i)->value() == -1) continue; + // Patch lazy deoptimization entry. + Address call_address = code_start_address + deopt_data->Pc(i)->value(); + CodePatcher patcher(call_address, patch_size()); + Address deopt_entry = GetDeoptimizationEntry(i, LAZY); + patcher.masm()->call(deopt_entry, RelocInfo::NONE); + // We use RUNTIME_ENTRY for deoptimization bailouts. + RelocInfo rinfo(call_address + 1, // 1 after the call opcode. + RelocInfo::RUNTIME_ENTRY, + reinterpret_cast(deopt_entry)); + reloc_info_writer.Write(&rinfo); + ASSERT_GE(reloc_info_writer.pos(), + reloc_info->address() + ByteArray::kHeaderSize); + ASSERT(prev_call_address == NULL || + call_address >= prev_call_address + patch_size()); + ASSERT(call_address + patch_size() <= code->instruction_end()); +#ifdef DEBUG + prev_call_address = call_address; +#endif } - ZapCodeRange(prev_address, - code_start_address + code->safepoint_table_offset()); // Move the relocation info to the beginning of the byte array. int new_reloc_size = reloc_end_address - reloc_info_writer.pos(); @@ -212,11 +195,6 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { PrintF("[forced deoptimization: "); function->PrintName(); PrintF(" / %x]\n", reinterpret_cast(function)); -#ifdef DEBUG - if (FLAG_print_code) { - code->PrintLn(); - } -#endif } } diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.cc b/deps/v8/src/ia32/lithium-codegen-ia32.cc index 71375c33a72..d5a4fe66105 100644 --- a/deps/v8/src/ia32/lithium-codegen-ia32.cc +++ b/deps/v8/src/ia32/lithium-codegen-ia32.cc @@ -44,22 +44,22 @@ class SafepointGenerator : public CallWrapper { public: SafepointGenerator(LCodeGen* codegen, LPointerMap* pointers, - int deoptimization_index) + Safepoint::DeoptMode mode) : codegen_(codegen), pointers_(pointers), - deoptimization_index_(deoptimization_index) {} + deopt_mode_(mode) {} virtual ~SafepointGenerator() { } virtual void BeforeCall(int call_size) const {} virtual void AfterCall() const { - codegen_->RecordSafepoint(pointers_, deoptimization_index_); + codegen_->RecordSafepoint(pointers_, deopt_mode_); } private: LCodeGen* codegen_; LPointerMap* pointers_; - int deoptimization_index_; + Safepoint::DeoptMode deopt_mode_; }; @@ -187,7 +187,7 @@ bool LCodeGen::GeneratePrologue() { } else { __ CallRuntime(Runtime::kNewFunctionContext, 1); } - RecordSafepoint(Safepoint::kNoDeoptimizationIndex); + RecordSafepoint(Safepoint::kNoLazyDeopt); // Context is returned in both eax and esi. It replaces the context // passed to us. It's saved in the stack and kept live in esi. __ mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi); @@ -241,19 +241,11 @@ bool LCodeGen::GenerateBody() { instr->CompileToNative(this); } } + EnsureSpaceForLazyDeopt(); return !is_aborted(); } -LInstruction* LCodeGen::GetNextInstruction() { - if (current_instruction_ < instructions_->length() - 1) { - return instructions_->at(current_instruction_ + 1); - } else { - return NULL; - } -} - - bool LCodeGen::GenerateDeferredCode() { ASSERT(is_generating()); if (deferred_.length() > 0) { @@ -263,13 +255,6 @@ bool LCodeGen::GenerateDeferredCode() { code->Generate(); __ jmp(code->exit()); } - - // Pad code to ensure that the last piece of deferred code have - // room for lazy bailout. - while ((masm()->pc_offset() - LastSafepointEnd()) - < Deoptimizer::patch_size()) { - __ nop(); - } } // Deferred code is the last part of the instruction sequence. Mark @@ -442,10 +427,8 @@ void LCodeGen::CallCodeGeneric(Handle code, ASSERT(instr != NULL); LPointerMap* pointers = instr->pointer_map(); RecordPosition(pointers->position()); - __ call(code, mode); - - RegisterLazyDeoptimization(instr, safepoint_mode); + RecordSafepointWithLazyDeopt(instr, safepoint_mode); // Signal that we don't inline smi code before these stubs in the // optimizing code generator. @@ -473,7 +456,7 @@ void LCodeGen::CallRuntime(const Runtime::Function* fun, __ CallRuntime(fun, argc); - RegisterLazyDeoptimization(instr, RECORD_SIMPLE_SAFEPOINT); + RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); } @@ -493,37 +476,12 @@ void LCodeGen::CallRuntimeFromDeferred(Runtime::FunctionId id, __ CallRuntimeSaveDoubles(id); RecordSafepointWithRegisters( - instr->pointer_map(), argc, Safepoint::kNoDeoptimizationIndex); + instr->pointer_map(), argc, Safepoint::kNoLazyDeopt); } -void LCodeGen::RegisterLazyDeoptimization(LInstruction* instr, - SafepointMode safepoint_mode) { - // Create the environment to bailout to. If the call has side effects - // execution has to continue after the call otherwise execution can continue - // from a previous bailout point repeating the call. - LEnvironment* deoptimization_environment; - if (instr->HasDeoptimizationEnvironment()) { - deoptimization_environment = instr->deoptimization_environment(); - } else { - deoptimization_environment = instr->environment(); - } - - RegisterEnvironmentForDeoptimization(deoptimization_environment); - if (safepoint_mode == RECORD_SIMPLE_SAFEPOINT) { - RecordSafepoint(instr->pointer_map(), - deoptimization_environment->deoptimization_index()); - } else { - ASSERT(safepoint_mode == RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); - RecordSafepointWithRegisters( - instr->pointer_map(), - 0, - deoptimization_environment->deoptimization_index()); - } -} - - -void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment) { +void LCodeGen::RegisterEnvironmentForDeoptimization( + LEnvironment* environment, Safepoint::DeoptMode mode) { if (!environment->HasBeenRegistered()) { // Physical stack frame layout: // -x ............. -4 0 ..................................... y @@ -545,14 +503,17 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment) { Translation translation(&translations_, frame_count); WriteTranslation(environment, &translation); int deoptimization_index = deoptimizations_.length(); - environment->Register(deoptimization_index, translation.index()); + int pc_offset = masm()->pc_offset(); + environment->Register(deoptimization_index, + translation.index(), + (mode == Safepoint::kLazyDeopt) ? pc_offset : -1); deoptimizations_.Add(environment); } } void LCodeGen::DeoptimizeIf(Condition cc, LEnvironment* environment) { - RegisterEnvironmentForDeoptimization(environment); + RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); ASSERT(environment->HasBeenRegistered()); int id = environment->deoptimization_index(); Address entry = Deoptimizer::GetDeoptimizationEntry(id, Deoptimizer::EAGER); @@ -632,6 +593,7 @@ void LCodeGen::PopulateDeoptimizationData(Handle code) { data->SetTranslationIndex(i, Smi::FromInt(env->translation_index())); data->SetArgumentsStackHeight(i, Smi::FromInt(env->arguments_stack_height())); + data->SetPc(i, Smi::FromInt(env->pc_offset())); } code->set_deoptimization_data(*data); } @@ -663,15 +625,27 @@ void LCodeGen::PopulateDeoptimizationLiteralsWithInlinedFunctions() { } +void LCodeGen::RecordSafepointWithLazyDeopt( + LInstruction* instr, SafepointMode safepoint_mode) { + if (safepoint_mode == RECORD_SIMPLE_SAFEPOINT) { + RecordSafepoint(instr->pointer_map(), Safepoint::kLazyDeopt); + } else { + ASSERT(safepoint_mode == RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + RecordSafepointWithRegisters( + instr->pointer_map(), 0, Safepoint::kLazyDeopt); + } +} + + void LCodeGen::RecordSafepoint( LPointerMap* pointers, Safepoint::Kind kind, int arguments, - int deoptimization_index) { + Safepoint::DeoptMode deopt_mode) { ASSERT(kind == expected_safepoint_kind_); const ZoneList* operands = pointers->operands(); - Safepoint safepoint = safepoints_.DefineSafepoint(masm(), - kind, arguments, deoptimization_index); + Safepoint safepoint = + safepoints_.DefineSafepoint(masm(), kind, arguments, deopt_mode); for (int i = 0; i < operands->length(); i++) { LOperand* pointer = operands->at(i); if (pointer->IsStackSlot()) { @@ -684,22 +658,21 @@ void LCodeGen::RecordSafepoint( void LCodeGen::RecordSafepoint(LPointerMap* pointers, - int deoptimization_index) { - RecordSafepoint(pointers, Safepoint::kSimple, 0, deoptimization_index); + Safepoint::DeoptMode mode) { + RecordSafepoint(pointers, Safepoint::kSimple, 0, mode); } -void LCodeGen::RecordSafepoint(int deoptimization_index) { +void LCodeGen::RecordSafepoint(Safepoint::DeoptMode mode) { LPointerMap empty_pointers(RelocInfo::kNoPosition); - RecordSafepoint(&empty_pointers, deoptimization_index); + RecordSafepoint(&empty_pointers, mode); } void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers, int arguments, - int deoptimization_index) { - RecordSafepoint(pointers, Safepoint::kWithRegisters, arguments, - deoptimization_index); + Safepoint::DeoptMode mode) { + RecordSafepoint(pointers, Safepoint::kWithRegisters, arguments, mode); } @@ -734,12 +707,6 @@ void LCodeGen::DoGap(LGap* gap) { LParallelMove* move = gap->GetParallelMove(inner_pos); if (move != NULL) DoParallelMove(move); } - - LInstruction* next = GetNextInstruction(); - if (next != NULL && next->IsLazyBailout()) { - int pc = masm()->pc_offset(); - safepoints_.SetPcAfterGap(pc); - } } @@ -1849,7 +1816,7 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { LInstanceOfKnownGlobal* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { - codegen()->DoDeferredLInstanceOfKnownGlobal(instr_, &map_check_); + codegen()->DoDeferredInstanceOfKnownGlobal(instr_, &map_check_); } Label* map_check() { return &map_check_; } @@ -1905,8 +1872,8 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { } -void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, - Label* map_check) { +void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, + Label* map_check) { PushSafepointRegistersScope scope(this); InstanceofStub::Flags flags = InstanceofStub::kNoFlags; @@ -1933,6 +1900,10 @@ void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, RelocInfo::CODE_TARGET, instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + ASSERT(instr->HasDeoptimizationEnvironment()); + LEnvironment* env = instr->deoptimization_environment(); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); + // Put the result value into the eax slot and restore all registers. __ StoreToSafepointRegisterSlot(eax, eax); } @@ -2502,12 +2473,9 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { __ bind(&invoke); ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); - LEnvironment* env = instr->deoptimization_environment(); RecordPosition(pointers->position()); - RegisterEnvironmentForDeoptimization(env); - SafepointGenerator safepoint_generator(this, - pointers, - env->deoptimization_index()); + SafepointGenerator safepoint_generator( + this, pointers, Safepoint::kLazyDeopt); ParameterCount actual(eax); __ InvokeFunction(function, actual, CALL_FUNCTION, safepoint_generator, CALL_AS_METHOD); @@ -2590,8 +2558,7 @@ void LCodeGen::CallKnownFunction(Handle function, __ call(FieldOperand(edi, JSFunction::kCodeEntryOffset)); } - // Setup deoptimization. - RegisterLazyDeoptimization(instr, RECORD_SIMPLE_SAFEPOINT); + RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); } @@ -2966,10 +2933,9 @@ void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) { ASSERT(instr->HasPointerMap()); ASSERT(instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); - LEnvironment* env = instr->deoptimization_environment(); RecordPosition(pointers->position()); - RegisterEnvironmentForDeoptimization(env); - SafepointGenerator generator(this, pointers, env->deoptimization_index()); + SafepointGenerator generator( + this, pointers, Safepoint::kLazyDeopt); ParameterCount count(instr->arity()); __ InvokeFunction(edi, count, CALL_FUNCTION, generator, CALL_AS_METHOD); } @@ -3463,7 +3429,7 @@ void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) { __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber); RecordSafepointWithRegisters( - instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex); + instr->pointer_map(), 0, Safepoint::kNoLazyDeopt); if (!reg.is(eax)) __ mov(reg, eax); // Done. Put the value in xmm0 into the value of the allocated heap @@ -3514,8 +3480,8 @@ void LCodeGen::DoDeferredNumberTagD(LNumberTagD* instr) { // not have easy access to the local context. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber); - RecordSafepointWithRegisters(instr->pointer_map(), 0, - Safepoint::kNoDeoptimizationIndex); + RecordSafepointWithRegisters( + instr->pointer_map(), 0, Safepoint::kNoLazyDeopt); __ StoreToSafepointRegisterSlot(reg, eax); } @@ -4246,9 +4212,27 @@ void LCodeGen::EmitIsConstructCall(Register temp) { } +void LCodeGen::EnsureSpaceForLazyDeopt() { + // Ensure that we have enough space after the previous lazy-bailout + // instruction for patching the code here. + int current_pc = masm()->pc_offset(); + int patch_size = Deoptimizer::patch_size(); + if (current_pc < last_lazy_deopt_pc_ + patch_size) { + int padding_size = last_lazy_deopt_pc_ + patch_size - current_pc; + while (padding_size-- > 0) { + __ nop(); + } + } + last_lazy_deopt_pc_ = masm()->pc_offset(); +} + + void LCodeGen::DoLazyBailout(LLazyBailout* instr) { - // No code for lazy bailout instruction. Used to capture environment after a - // call for populating the safepoint data with deoptimization data. + EnsureSpaceForLazyDeopt(); + ASSERT(instr->HasEnvironment()); + LEnvironment* env = instr->environment(); + RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); } @@ -4268,32 +4252,26 @@ void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) { } ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); - LEnvironment* env = instr->deoptimization_environment(); RecordPosition(pointers->position()); - RegisterEnvironmentForDeoptimization(env); // Create safepoint generator that will also ensure enough space in the // reloc info for patching in deoptimization (since this is invoking a // builtin) - SafepointGenerator safepoint_generator(this, - pointers, - env->deoptimization_index()); + SafepointGenerator safepoint_generator( + this, pointers, Safepoint::kLazyDeopt); __ push(Immediate(Smi::FromInt(strict_mode_flag()))); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, safepoint_generator); } void LCodeGen::DoDeferredStackCheck(LStackCheck* instr) { - { - PushSafepointRegistersScope scope(this); - __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - __ CallRuntimeSaveDoubles(Runtime::kStackGuard); - RegisterLazyDeoptimization( - instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); - } - - // The gap code includes the restoring of the safepoint registers. - int pc = masm()->pc_offset(); - safepoints_.SetPcAfterGap(pc); + PushSafepointRegistersScope scope(this); + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + __ CallRuntimeSaveDoubles(Runtime::kStackGuard); + RecordSafepointWithLazyDeopt( + instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + ASSERT(instr->HasEnvironment()); + LEnvironment* env = instr->environment(); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); } @@ -4307,6 +4285,10 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { LStackCheck* instr_; }; + ASSERT(instr->HasEnvironment()); + LEnvironment* env = instr->environment(); + // There is no LLazyBailout instruction for stack-checks. We have to + // prepare for lazy deoptimization explicitly here. if (instr->hydrogen()->is_function_entry()) { // Perform stack overflow check. Label done; @@ -4319,7 +4301,10 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { ASSERT(ToRegister(instr->context()).is(esi)); StackCheckStub stub; CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + EnsureSpaceForLazyDeopt(); __ bind(&done); + RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); } else { ASSERT(instr->hydrogen()->is_backwards_branch()); // Perform stack overflow check if this goto needs it before jumping. @@ -4329,8 +4314,13 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { ExternalReference::address_of_stack_limit(isolate()); __ cmp(esp, Operand::StaticVariable(stack_limit)); __ j(below, deferred_stack_check->entry()); + EnsureSpaceForLazyDeopt(); __ bind(instr->done_label()); deferred_stack_check->SetExit(instr->done_label()); + RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); + // Don't record a deoptimization index for the safepoint here. + // This will be done explicitly when emitting call and the safepoint in + // the deferred code. } } @@ -4346,7 +4336,7 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) { // If the environment were already registered, we would have no way of // backpatching it with the spill slot operands. ASSERT(!environment->HasBeenRegistered()); - RegisterEnvironmentForDeoptimization(environment); + RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); ASSERT(osr_pc_offset_ == -1); osr_pc_offset_ = masm()->pc_offset(); } @@ -4367,15 +4357,9 @@ void LCodeGen::DoIn(LIn* instr) { } ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); - LEnvironment* env = instr->deoptimization_environment(); RecordPosition(pointers->position()); - RegisterEnvironmentForDeoptimization(env); - // Create safepoint generator that will also ensure enough space in the - // reloc info for patching in deoptimization (since this is invoking a - // builtin) - SafepointGenerator safepoint_generator(this, - pointers, - env->deoptimization_index()); + SafepointGenerator safepoint_generator( + this, pointers, Safepoint::kLazyDeopt); __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION, safepoint_generator); } diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.h b/deps/v8/src/ia32/lithium-codegen-ia32.h index 61563274201..d9554501dce 100644 --- a/deps/v8/src/ia32/lithium-codegen-ia32.h +++ b/deps/v8/src/ia32/lithium-codegen-ia32.h @@ -60,7 +60,7 @@ class LCodeGen BASE_EMBEDDED { status_(UNUSED), deferred_(8), osr_pc_offset_(-1), - deoptimization_reloc_size(), + last_lazy_deopt_pc_(0), resolver_(this), expected_safepoint_kind_(Safepoint::kSimple) { PopulateDeoptimizationLiteralsWithInlinedFunctions(); @@ -100,8 +100,8 @@ class LCodeGen BASE_EMBEDDED { void DoDeferredStackCheck(LStackCheck* instr); void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredStringCharFromCode(LStringCharFromCode* instr); - void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, - Label* map_check); + void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, + Label* map_check); // Parallel move support. void DoParallelMove(LParallelMove* move); @@ -139,7 +139,6 @@ class LCodeGen BASE_EMBEDDED { HGraph* graph() const { return chunk_->graph(); } int GetNextEmittedBlock(int block); - LInstruction* GetNextInstruction(); void EmitClassOfTest(Label* if_true, Label* if_false, @@ -205,10 +204,11 @@ class LCodeGen BASE_EMBEDDED { void LoadHeapObject(Register result, Handle object); - void RegisterLazyDeoptimization(LInstruction* instr, - SafepointMode safepoint_mode); + void RecordSafepointWithLazyDeopt(LInstruction* instr, + SafepointMode safepoint_mode); - void RegisterEnvironmentForDeoptimization(LEnvironment* environment); + void RegisterEnvironmentForDeoptimization(LEnvironment* environment, + Safepoint::DeoptMode mode); void DeoptimizeIf(Condition cc, LEnvironment* environment); void AddToTranslation(Translation* translation, @@ -242,16 +242,13 @@ class LCodeGen BASE_EMBEDDED { void RecordSafepoint(LPointerMap* pointers, Safepoint::Kind kind, int arguments, - int deoptimization_index); - void RecordSafepoint(LPointerMap* pointers, int deoptimization_index); - void RecordSafepoint(int deoptimization_index); + Safepoint::DeoptMode mode); + void RecordSafepoint(LPointerMap* pointers, Safepoint::DeoptMode mode); + void RecordSafepoint(Safepoint::DeoptMode mode); void RecordSafepointWithRegisters(LPointerMap* pointers, int arguments, - int deoptimization_index); + Safepoint::DeoptMode mode); void RecordPosition(int position); - int LastSafepointEnd() { - return static_cast(safepoints_.GetPcAfterGap()); - } static Condition TokenToCondition(Token::Value op, bool is_unsigned); void EmitGoto(int block); @@ -284,6 +281,7 @@ class LCodeGen BASE_EMBEDDED { Register object, Handle type, Handle name); + void EnsureSpaceForLazyDeopt(); LChunk* const chunk_; MacroAssembler* const masm_; @@ -300,13 +298,7 @@ class LCodeGen BASE_EMBEDDED { TranslationBuffer translations_; ZoneList deferred_; int osr_pc_offset_; - - struct DeoptimizationRelocSize { - int min_size; - int last_pc_offset; - }; - - DeoptimizationRelocSize deoptimization_reloc_size; + int last_lazy_deopt_pc_; // Builder that keeps track of safepoints in the code. The table // itself is emitted at the end of the generated code. diff --git a/deps/v8/src/ia32/lithium-ia32.h b/deps/v8/src/ia32/lithium-ia32.h index 038049ca068..b0ab6b416e8 100644 --- a/deps/v8/src/ia32/lithium-ia32.h +++ b/deps/v8/src/ia32/lithium-ia32.h @@ -368,17 +368,7 @@ class LGoto: public LTemplateInstruction<0, 0, 0> { class LLazyBailout: public LTemplateInstruction<0, 0, 0> { public: - LLazyBailout() : gap_instructions_size_(0) { } - DECLARE_CONCRETE_INSTRUCTION(LazyBailout, "lazy-bailout") - - void set_gap_instructions_size(int gap_instructions_size) { - gap_instructions_size_ = gap_instructions_size; - } - int gap_instructions_size() { return gap_instructions_size_; } - - private: - int gap_instructions_size_; }; diff --git a/deps/v8/src/lithium.h b/deps/v8/src/lithium.h index 20da21a63ce..a933f72aefa 100644 --- a/deps/v8/src/lithium.h +++ b/deps/v8/src/lithium.h @@ -442,6 +442,7 @@ class LEnvironment: public ZoneObject { translation_index_(-1), ast_id_(ast_id), parameter_count_(parameter_count), + pc_offset_(-1), values_(value_count), representations_(value_count), spilled_registers_(NULL), @@ -455,6 +456,7 @@ class LEnvironment: public ZoneObject { int translation_index() const { return translation_index_; } int ast_id() const { return ast_id_; } int parameter_count() const { return parameter_count_; } + int pc_offset() const { return pc_offset_; } LOperand** spilled_registers() const { return spilled_registers_; } LOperand** spilled_double_registers() const { return spilled_double_registers_; @@ -471,10 +473,13 @@ class LEnvironment: public ZoneObject { return representations_[index].IsTagged(); } - void Register(int deoptimization_index, int translation_index) { + void Register(int deoptimization_index, + int translation_index, + int pc_offset) { ASSERT(!HasBeenRegistered()); deoptimization_index_ = deoptimization_index; translation_index_ = translation_index; + pc_offset_ = pc_offset; } bool HasBeenRegistered() const { return deoptimization_index_ != Safepoint::kNoDeoptimizationIndex; @@ -495,6 +500,7 @@ class LEnvironment: public ZoneObject { int translation_index_; int ast_id_; int parameter_count_; + int pc_offset_; ZoneList values_; ZoneList representations_; diff --git a/deps/v8/src/mips/code-stubs-mips.cc b/deps/v8/src/mips/code-stubs-mips.cc index 6838e54478f..c3c38744220 100644 --- a/deps/v8/src/mips/code-stubs-mips.cc +++ b/deps/v8/src/mips/code-stubs-mips.cc @@ -5607,7 +5607,7 @@ void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm, void StringHelper::GenerateHashGetHash(MacroAssembler* masm, - Register hash) { + Register hash) { // hash += hash << 3; __ sll(at, hash, 3); __ addu(hash, hash, at); diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 42245d48b0c..88ebbf4e9dc 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -6995,11 +6995,14 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) { PrintF(out, "Deoptimization Input Data (deopt points = %d)\n", deopt_count); if (0 == deopt_count) return; - PrintF(out, "%6s %6s %6s %12s\n", "index", "ast id", "argc", + PrintF(out, "%6s %6s %6s %6s %12s\n", "index", "ast id", "argc", "pc", FLAG_print_code_verbose ? "commands" : ""); for (int i = 0; i < deopt_count; i++) { - PrintF(out, "%6d %6d %6d", - i, AstId(i)->value(), ArgumentsStackHeight(i)->value()); + PrintF(out, "%6d %6d %6d %6d", + i, + AstId(i)->value(), + ArgumentsStackHeight(i)->value(), + Pc(i)->value()); if (!FLAG_print_code_verbose) { PrintF(out, "\n"); @@ -10542,7 +10545,7 @@ class TwoCharHashTableKey : public HashTableKey { hash += hash << 3; hash ^= hash >> 11; hash += hash << 15; - if ((hash & String::kHashBitMask) == 0) hash = 27; + if ((hash & String::kHashBitMask) == 0) hash = String::kZeroHash; #ifdef DEBUG StringHasher hasher(2, seed); hasher.AddCharacter(c1); diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index be35401d037..1245ed0c127 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -3535,7 +3535,8 @@ class DeoptimizationInputData: public FixedArray { static const int kAstIdOffset = 0; static const int kTranslationIndexOffset = 1; static const int kArgumentsStackHeightOffset = 2; - static const int kDeoptEntrySize = 3; + static const int kPcOffset = 3; + static const int kDeoptEntrySize = 4; // Simple element accessors. #define DEFINE_ELEMENT_ACCESSORS(name, type) \ @@ -3571,6 +3572,7 @@ class DeoptimizationInputData: public FixedArray { DEFINE_ENTRY_ACCESSORS(AstId, Smi) DEFINE_ENTRY_ACCESSORS(TranslationIndex, Smi) DEFINE_ENTRY_ACCESSORS(ArgumentsStackHeight, Smi) + DEFINE_ENTRY_ACCESSORS(Pc, Smi) #undef DEFINE_ENTRY_ACCESSORS @@ -5832,6 +5834,11 @@ class StringHasher { // value is represented decimal value. static uint32_t MakeArrayIndexHash(uint32_t value, int length); + // No string is allowed to have a hash of zero. That value is reserved + // for internal properties. If the hash calculation yields zero then we + // use 27 instead. + static const int kZeroHash = 27; + private: uint32_t array_index() { ASSERT(is_array_index()); diff --git a/deps/v8/src/safepoint-table.cc b/deps/v8/src/safepoint-table.cc index bcd0a1d63d9..89ad8afabec 100644 --- a/deps/v8/src/safepoint-table.cc +++ b/deps/v8/src/safepoint-table.cc @@ -122,17 +122,20 @@ void Safepoint::DefinePointerRegister(Register reg) { Safepoint SafepointTableBuilder::DefineSafepoint( - Assembler* assembler, Safepoint::Kind kind, int arguments, - int deoptimization_index) { - ASSERT(deoptimization_index != -1); + Assembler* assembler, + Safepoint::Kind kind, + int arguments, + Safepoint::DeoptMode deopt_mode) { ASSERT(arguments >= 0); - DeoptimizationInfo pc_and_deoptimization_index; - pc_and_deoptimization_index.pc = assembler->pc_offset(); - pc_and_deoptimization_index.deoptimization_index = deoptimization_index; - pc_and_deoptimization_index.pc_after_gap = assembler->pc_offset(); - pc_and_deoptimization_index.arguments = arguments; - pc_and_deoptimization_index.has_doubles = (kind & Safepoint::kWithDoubles); - deoptimization_info_.Add(pc_and_deoptimization_index); + DeoptimizationInfo info; + info.pc = assembler->pc_offset(); + info.arguments = arguments; + info.has_doubles = (kind & Safepoint::kWithDoubles); + deoptimization_info_.Add(info); + deopt_index_list_.Add(Safepoint::kNoDeoptimizationIndex); + if (deopt_mode == Safepoint::kNoLazyDeopt) { + last_lazy_safepoint_ = deopt_index_list_.length(); + } indexes_.Add(new ZoneList(8)); registers_.Add((kind & Safepoint::kWithRegisters) ? new ZoneList(4) @@ -141,6 +144,12 @@ Safepoint SafepointTableBuilder::DefineSafepoint( } +void SafepointTableBuilder::RecordLazyDeoptimizationIndex(int index) { + while (last_lazy_safepoint_ < deopt_index_list_.length()) { + deopt_index_list_[last_lazy_safepoint_++] = index; + } +} + unsigned SafepointTableBuilder::GetCodeOffset() const { ASSERT(emitted_); return offset_; @@ -173,11 +182,11 @@ void SafepointTableBuilder::Emit(Assembler* assembler, int bits_per_entry) { assembler->dd(length); assembler->dd(bytes_per_entry); - // Emit sorted table of pc offsets together with deoptimization indexes and - // pc after gap information. + // Emit sorted table of pc offsets together with deoptimization indexes. for (int i = 0; i < length; i++) { assembler->dd(deoptimization_info_[i].pc); - assembler->dd(EncodeExceptPC(deoptimization_info_[i])); + assembler->dd(EncodeExceptPC(deoptimization_info_[i], + deopt_index_list_[i])); } // Emit table of bitmaps. @@ -222,35 +231,14 @@ void SafepointTableBuilder::Emit(Assembler* assembler, int bits_per_entry) { } -uint32_t SafepointTableBuilder::EncodeExceptPC(const DeoptimizationInfo& info) { - unsigned index = info.deoptimization_index; - unsigned gap_size = info.pc_after_gap - info.pc; +uint32_t SafepointTableBuilder::EncodeExceptPC(const DeoptimizationInfo& info, + unsigned index) { uint32_t encoding = SafepointEntry::DeoptimizationIndexField::encode(index); - encoding |= SafepointEntry::GapCodeSizeField::encode(gap_size); encoding |= SafepointEntry::ArgumentsField::encode(info.arguments); encoding |= SafepointEntry::SaveDoublesField::encode(info.has_doubles); return encoding; } -int SafepointTableBuilder::CountShortDeoptimizationIntervals(unsigned limit) { - int result = 0; - if (!deoptimization_info_.is_empty()) { - unsigned previous_gap_end = deoptimization_info_[0].pc_after_gap; - for (int i = 1, n = deoptimization_info_.length(); i < n; i++) { - DeoptimizationInfo info = deoptimization_info_[i]; - if (static_cast(info.deoptimization_index) != - Safepoint::kNoDeoptimizationIndex) { - if (previous_gap_end + limit > info.pc) { - result++; - } - previous_gap_end = info.pc_after_gap; - } - } - } - return result; -} - - } } // namespace v8::internal diff --git a/deps/v8/src/safepoint-table.h b/deps/v8/src/safepoint-table.h index de537f98284..57fceecd92e 100644 --- a/deps/v8/src/safepoint-table.h +++ b/deps/v8/src/safepoint-table.h @@ -62,10 +62,20 @@ class SafepointEntry BASE_EMBEDDED { return DeoptimizationIndexField::decode(info_); } - int gap_code_size() const { - ASSERT(is_valid()); - return GapCodeSizeField::decode(info_); - } + static const int kArgumentsFieldBits = 3; + static const int kSaveDoublesFieldBits = 1; + static const int kDeoptIndexBits = + 32 - kArgumentsFieldBits - kSaveDoublesFieldBits; + class DeoptimizationIndexField: + public BitField {}; // NOLINT + class ArgumentsField: + public BitField {}; // NOLINT + class SaveDoublesField: + public BitField { }; // NOLINT int argument_count() const { ASSERT(is_valid()); @@ -85,27 +95,6 @@ class SafepointEntry BASE_EMBEDDED { bool HasRegisters() const; bool HasRegisterAt(int reg_index) const; - // Reserve 13 bits for the gap code size. On ARM a constant pool can be - // emitted when generating the gap code. The size of the const pool is less - // than what can be represented in 12 bits, so 13 bits gives room for having - // instructions before potentially emitting a constant pool. - static const int kGapCodeSizeBits = 13; - static const int kArgumentsFieldBits = 3; - static const int kSaveDoublesFieldBits = 1; - static const int kDeoptIndexBits = - 32 - kGapCodeSizeBits - kArgumentsFieldBits - kSaveDoublesFieldBits; - class GapCodeSizeField: public BitField {}; - class DeoptimizationIndexField: public BitField {}; // NOLINT - class ArgumentsField: public BitField {}; // NOLINT - class SaveDoublesField: public BitField { }; // NOLINT - private: unsigned info_; uint8_t* bits_; @@ -186,6 +175,11 @@ class Safepoint BASE_EMBEDDED { kWithRegistersAndDoubles = kWithRegisters | kWithDoubles } Kind; + enum DeoptMode { + kNoLazyDeopt, + kLazyDeopt + }; + static const int kNoDeoptimizationIndex = (1 << (SafepointEntry::kDeoptIndexBits)) - 1; @@ -206,9 +200,11 @@ class SafepointTableBuilder BASE_EMBEDDED { public: SafepointTableBuilder() : deoptimization_info_(32), + deopt_index_list_(32), indexes_(32), registers_(32), - emitted_(false) { } + emitted_(false), + last_lazy_safepoint_(0) { } // Get the offset of the emitted safepoint table in the code. unsigned GetCodeOffset() const; @@ -217,50 +213,34 @@ class SafepointTableBuilder BASE_EMBEDDED { Safepoint DefineSafepoint(Assembler* assembler, Safepoint::Kind kind, int arguments, - int deoptimization_index); + Safepoint::DeoptMode mode); - // Update the last safepoint with the size of the code generated until the - // end of the gap following it. - void SetPcAfterGap(int pc) { - ASSERT(!deoptimization_info_.is_empty()); - int index = deoptimization_info_.length() - 1; - deoptimization_info_[index].pc_after_gap = pc; - } - - // Get the end pc offset of the last safepoint, including the code generated - // until the end of the gap following it. - unsigned GetPcAfterGap() { - int index = deoptimization_info_.length(); - if (index == 0) return 0; - return deoptimization_info_[index - 1].pc_after_gap; - } + // Record deoptimization index for lazy deoptimization for the last + // outstanding safepoints. + void RecordLazyDeoptimizationIndex(int index); // Emit the safepoint table after the body. The number of bits per // entry must be enough to hold all the pointer indexes. void Emit(Assembler* assembler, int bits_per_entry); - // Count the number of deoptimization points where the next - // following deoptimization point comes less than limit bytes - // after the end of this point's gap. - int CountShortDeoptimizationIntervals(unsigned limit); private: struct DeoptimizationInfo { unsigned pc; - unsigned deoptimization_index; - unsigned pc_after_gap; unsigned arguments; bool has_doubles; }; - uint32_t EncodeExceptPC(const DeoptimizationInfo& info); + uint32_t EncodeExceptPC(const DeoptimizationInfo& info, unsigned index); ZoneList deoptimization_info_; + ZoneList deopt_index_list_; ZoneList*> indexes_; ZoneList*> registers_; unsigned offset_; bool emitted_; + int last_lazy_safepoint_; DISALLOW_COPY_AND_ASSIGN(SafepointTableBuilder); }; diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index f09914b43a8..2c21152f1be 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -35,7 +35,7 @@ #define MAJOR_VERSION 3 #define MINOR_VERSION 6 #define BUILD_NUMBER 6 -#define PATCH_LEVEL 15 +#define PATCH_LEVEL 19 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) #define IS_CANDIDATE_VERSION 0 diff --git a/deps/v8/src/x64/code-stubs-x64.cc b/deps/v8/src/x64/code-stubs-x64.cc index 347c17f7992..6499ea02141 100644 --- a/deps/v8/src/x64/code-stubs-x64.cc +++ b/deps/v8/src/x64/code-stubs-x64.cc @@ -4654,13 +4654,12 @@ void StringHelper::GenerateHashGetHash(MacroAssembler* masm, __ shll(scratch, Immediate(15)); __ addl(hash, scratch); - uint32_t kHashShiftCutOffMask = (1 << (32 - String::kHashShift)) - 1; - __ andl(hash, Immediate(kHashShiftCutOffMask)); + __ andl(hash, Immediate(String::kHashBitMask)); // if (hash == 0) hash = 27; Label hash_not_zero; __ j(not_zero, &hash_not_zero); - __ Set(hash, 27); + __ Set(hash, StringHasher::kZeroHash); __ bind(&hash_not_zero); } diff --git a/deps/v8/src/x64/deoptimizer-x64.cc b/deps/v8/src/x64/deoptimizer-x64.cc index b52e659320a..f322312b416 100644 --- a/deps/v8/src/x64/deoptimizer-x64.cc +++ b/deps/v8/src/x64/deoptimizer-x64.cc @@ -42,67 +42,7 @@ const int Deoptimizer::table_entry_size_ = 10; int Deoptimizer::patch_size() { - return MacroAssembler::kCallInstructionLength; -} - - -#ifdef DEBUG -// Overwrites code with int3 instructions. -static void ZapCodeRange(Address from, Address to) { - CHECK(from <= to); - int length = static_cast(to - from); - CodePatcher destroyer(from, length); - while (length-- > 0) { - destroyer.masm()->int3(); - } -} -#endif - - -// Iterate through the entries of a SafepointTable that corresponds to -// deoptimization points. -class SafepointTableDeoptimiztionEntryIterator { - public: - explicit SafepointTableDeoptimiztionEntryIterator(Code* code) - : code_(code), table_(code), index_(-1), limit_(table_.length()) { - FindNextIndex(); - } - - SafepointEntry Next(Address* pc) { - if (index_ >= limit_) { - *pc = NULL; - return SafepointEntry(); // Invalid entry. - } - *pc = code_->instruction_start() + table_.GetPcOffset(index_); - SafepointEntry entry = table_.GetEntry(index_); - FindNextIndex(); - return entry; - } - - private: - void FindNextIndex() { - ASSERT(index_ < limit_); - while (++index_ < limit_) { - if (table_.GetEntry(index_).deoptimization_index() != - Safepoint::kNoDeoptimizationIndex) { - return; - } - } - } - - Code* code_; - SafepointTable table_; - // Index of next deoptimization entry. If negative after calling - // FindNextIndex, there are no more, and Next will return an invalid - // SafepointEntry. - int index_; - // Table length. - int limit_; -}; - - -void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle code) { - // TODO(1276): Implement. + return Assembler::kCallInstructionLength; } @@ -119,84 +59,34 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { // code patching below, and is not needed any more. code->InvalidateRelocation(); - // For each return after a safepoint insert a absolute call to the + // For each LLazyBailout instruction insert a absolute call to the // corresponding deoptimization entry, or a short call to an absolute // jump if space is short. The absolute jumps are put in a table just // before the safepoint table (space was allocated there when the Code // object was created, if necessary). Address instruction_start = function->code()->instruction_start(); - Address jump_table_address = - instruction_start + function->code()->safepoint_table_offset(); #ifdef DEBUG - Address previous_pc = instruction_start; -#endif - - SafepointTableDeoptimiztionEntryIterator deoptimizations(function->code()); - Address entry_pc = NULL; - - SafepointEntry current_entry = deoptimizations.Next(&entry_pc); - while (current_entry.is_valid()) { - int gap_code_size = current_entry.gap_code_size(); - unsigned deoptimization_index = current_entry.deoptimization_index(); - -#ifdef DEBUG - // Destroy the code which is not supposed to run again. - ZapCodeRange(previous_pc, entry_pc); + Address prev_call_address = NULL; #endif + DeoptimizationInputData* deopt_data = + DeoptimizationInputData::cast(code->deoptimization_data()); + for (int i = 0; i < deopt_data->DeoptCount(); i++) { + if (deopt_data->Pc(i)->value() == -1) continue; // Position where Call will be patched in. - Address call_address = entry_pc + gap_code_size; - // End of call instruction, if using a direct call to a 64-bit address. - Address call_end_address = - call_address + MacroAssembler::kCallInstructionLength; - - // Find next deoptimization entry, if any. - Address next_pc = NULL; - SafepointEntry next_entry = deoptimizations.Next(&next_pc); - - if (!next_entry.is_valid() || next_pc >= call_end_address) { - // Room enough to write a long call instruction. - CodePatcher patcher(call_address, Assembler::kCallInstructionLength); - patcher.masm()->Call(GetDeoptimizationEntry(deoptimization_index, LAZY), - RelocInfo::NONE); + Address call_address = instruction_start + deopt_data->Pc(i)->value(); + // There is room enough to write a long call instruction because we pad + // LLazyBailout instructions with nops if necessary. + CodePatcher patcher(call_address, Assembler::kCallInstructionLength); + patcher.masm()->Call(GetDeoptimizationEntry(i, LAZY), RelocInfo::NONE); + ASSERT(prev_call_address == NULL || + call_address >= prev_call_address + patch_size()); + ASSERT(call_address + patch_size() <= code->instruction_end()); #ifdef DEBUG - previous_pc = call_end_address; + prev_call_address = call_address; #endif - } else { - // Not room enough for a long Call instruction. Write a short call - // instruction to a long jump placed elsewhere in the code. -#ifdef DEBUG - Address short_call_end_address = - call_address + MacroAssembler::kShortCallInstructionLength; -#endif - ASSERT(next_pc >= short_call_end_address); - - // Write jump in jump-table. - jump_table_address -= MacroAssembler::kJumpInstructionLength; - CodePatcher jump_patcher(jump_table_address, - MacroAssembler::kJumpInstructionLength); - jump_patcher.masm()->Jump( - GetDeoptimizationEntry(deoptimization_index, LAZY), - RelocInfo::NONE); - - // Write call to jump at call_offset. - CodePatcher call_patcher(call_address, - MacroAssembler::kShortCallInstructionLength); - call_patcher.masm()->call(jump_table_address); -#ifdef DEBUG - previous_pc = short_call_end_address; -#endif - } - - // Continue with next deoptimization entry. - current_entry = next_entry; - entry_pc = next_pc; } -#ifdef DEBUG - // Destroy the code which is not supposed to run again. - ZapCodeRange(previous_pc, jump_table_address); -#endif // Add the deoptimizing code to the list. DeoptimizingCodeListNode* node = new DeoptimizingCodeListNode(code); @@ -211,11 +101,6 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { PrintF("[forced deoptimization: "); function->PrintName(); PrintF(" / %" V8PRIxPTR "]\n", reinterpret_cast(function)); -#ifdef DEBUG - if (FLAG_print_code) { - code->PrintLn(); - } -#endif } } diff --git a/deps/v8/src/x64/lithium-codegen-x64.cc b/deps/v8/src/x64/lithium-codegen-x64.cc index 3efbb8bdcb5..b82dc54f3bd 100644 --- a/deps/v8/src/x64/lithium-codegen-x64.cc +++ b/deps/v8/src/x64/lithium-codegen-x64.cc @@ -43,35 +43,24 @@ class SafepointGenerator : public CallWrapper { public: SafepointGenerator(LCodeGen* codegen, LPointerMap* pointers, - int deoptimization_index) + Safepoint::DeoptMode mode) : codegen_(codegen), pointers_(pointers), - deoptimization_index_(deoptimization_index) { } + deopt_mode_(mode) { } virtual ~SafepointGenerator() { } virtual void BeforeCall(int call_size) const { - ASSERT(call_size >= 0); - // Ensure that we have enough space after the previous safepoint position - // for the jump generated there. - int call_end = codegen_->masm()->pc_offset() + call_size; - int prev_jump_end = codegen_->LastSafepointEnd() + kMinSafepointSize; - if (call_end < prev_jump_end) { - int padding_size = prev_jump_end - call_end; - STATIC_ASSERT(kMinSafepointSize <= 9); // One multibyte nop is enough. - codegen_->masm()->nop(padding_size); - } + codegen_->EnsureSpaceForLazyDeopt(Deoptimizer::patch_size() - call_size); } virtual void AfterCall() const { - codegen_->RecordSafepoint(pointers_, deoptimization_index_); + codegen_->RecordSafepoint(pointers_, deopt_mode_); } private: - static const int kMinSafepointSize = - MacroAssembler::kShortCallInstructionLength; LCodeGen* codegen_; LPointerMap* pointers_; - int deoptimization_index_; + Safepoint::DeoptMode deopt_mode_; }; @@ -94,7 +83,6 @@ void LCodeGen::FinishCode(Handle code) { code->set_stack_slots(GetStackSlotCount()); code->set_safepoint_table_offset(safepoints_.GetCodeOffset()); PopulateDeoptimizationData(code); - Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(code); } @@ -200,7 +188,7 @@ bool LCodeGen::GeneratePrologue() { } else { __ CallRuntime(Runtime::kNewFunctionContext, 1); } - RecordSafepoint(Safepoint::kNoDeoptimizationIndex); + RecordSafepoint(Safepoint::kNoLazyDeopt); // Context is returned in both rax and rsi. It replaces the context // passed to us. It's saved in the stack and kept live in rsi. __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi); @@ -252,19 +240,11 @@ bool LCodeGen::GenerateBody() { instr->CompileToNative(this); } } + EnsureSpaceForLazyDeopt(Deoptimizer::patch_size()); return !is_aborted(); } -LInstruction* LCodeGen::GetNextInstruction() { - if (current_instruction_ < instructions_->length() - 1) { - return instructions_->at(current_instruction_ + 1); - } else { - return NULL; - } -} - - bool LCodeGen::GenerateJumpTable() { for (int i = 0; i < jump_table_.length(); i++) { __ bind(&jump_table_[i].label); @@ -283,18 +263,6 @@ bool LCodeGen::GenerateDeferredCode() { code->Generate(); __ jmp(code->exit()); } - - // Pad code to ensure that the last piece of deferred code have - // room for lazy bailout. - while ((masm()->pc_offset() - LastSafepointEnd()) - < Deoptimizer::patch_size()) { - int padding = masm()->pc_offset() - LastSafepointEnd(); - if (padding > 9) { - __ nop(9); - } else { - __ nop(padding); - } - } } // Deferred code is the last part of the instruction sequence. Mark @@ -306,20 +274,6 @@ bool LCodeGen::GenerateDeferredCode() { bool LCodeGen::GenerateSafepointTable() { ASSERT(is_done()); - // Ensure that there is space at the end of the code to write a number - // of jump instructions, as well as to afford writing a call near the end - // of the code. - // The jumps are used when there isn't room in the code stream to write - // a long call instruction. Instead it writes a shorter call to a - // jump instruction in the same code object. - // The calls are used when lazy deoptimizing a function and calls to a - // deoptimization function. - int short_deopts = safepoints_.CountShortDeoptimizationIntervals( - static_cast(MacroAssembler::kJumpInstructionLength)); - int byte_count = (short_deopts) * MacroAssembler::kJumpInstructionLength; - while (byte_count-- > 0) { - __ int3(); - } safepoints_.Emit(masm(), GetStackSlotCount()); return !is_aborted(); } @@ -475,11 +429,12 @@ void LCodeGen::CallCodeGeneric(Handle code, LInstruction* instr, SafepointMode safepoint_mode, int argc) { + EnsureSpaceForLazyDeopt(Deoptimizer::patch_size() - masm()->CallSize(code)); ASSERT(instr != NULL); LPointerMap* pointers = instr->pointer_map(); RecordPosition(pointers->position()); __ call(code, mode); - RegisterLazyDeoptimization(instr, safepoint_mode, argc); + RecordSafepointWithLazyDeopt(instr, safepoint_mode, argc); // Signal that we don't inline smi code before these stubs in the // optimizing code generator. @@ -506,7 +461,7 @@ void LCodeGen::CallRuntime(const Runtime::Function* function, RecordPosition(pointers->position()); __ CallRuntime(function, num_arguments); - RegisterLazyDeoptimization(instr, RECORD_SIMPLE_SAFEPOINT, 0); + RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT, 0); } @@ -516,39 +471,12 @@ void LCodeGen::CallRuntimeFromDeferred(Runtime::FunctionId id, __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); __ CallRuntimeSaveDoubles(id); RecordSafepointWithRegisters( - instr->pointer_map(), argc, Safepoint::kNoDeoptimizationIndex); + instr->pointer_map(), argc, Safepoint::kNoLazyDeopt); } -void LCodeGen::RegisterLazyDeoptimization(LInstruction* instr, - SafepointMode safepoint_mode, - int argc) { - // Create the environment to bailout to. If the call has side effects - // execution has to continue after the call otherwise execution can continue - // from a previous bailout point repeating the call. - LEnvironment* deoptimization_environment; - if (instr->HasDeoptimizationEnvironment()) { - deoptimization_environment = instr->deoptimization_environment(); - } else { - deoptimization_environment = instr->environment(); - } - - RegisterEnvironmentForDeoptimization(deoptimization_environment); - if (safepoint_mode == RECORD_SIMPLE_SAFEPOINT) { - ASSERT(argc == 0); - RecordSafepoint(instr->pointer_map(), - deoptimization_environment->deoptimization_index()); - } else { - ASSERT(safepoint_mode == RECORD_SAFEPOINT_WITH_REGISTERS); - RecordSafepointWithRegisters( - instr->pointer_map(), - argc, - deoptimization_environment->deoptimization_index()); - } -} - - -void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment) { +void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment, + Safepoint::DeoptMode mode) { if (!environment->HasBeenRegistered()) { // Physical stack frame layout: // -x ............. -4 0 ..................................... y @@ -570,14 +498,17 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment) { Translation translation(&translations_, frame_count); WriteTranslation(environment, &translation); int deoptimization_index = deoptimizations_.length(); - environment->Register(deoptimization_index, translation.index()); + int pc_offset = masm()->pc_offset(); + environment->Register(deoptimization_index, + translation.index(), + (mode == Safepoint::kLazyDeopt) ? pc_offset : -1); deoptimizations_.Add(environment); } } void LCodeGen::DeoptimizeIf(Condition cc, LEnvironment* environment) { - RegisterEnvironmentForDeoptimization(environment); + RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); ASSERT(environment->HasBeenRegistered()); int id = environment->deoptimization_index(); Address entry = Deoptimizer::GetDeoptimizationEntry(id, Deoptimizer::EAGER); @@ -629,6 +560,7 @@ void LCodeGen::PopulateDeoptimizationData(Handle code) { data->SetTranslationIndex(i, Smi::FromInt(env->translation_index())); data->SetArgumentsStackHeight(i, Smi::FromInt(env->arguments_stack_height())); + data->SetPc(i, Smi::FromInt(env->pc_offset())); } code->set_deoptimization_data(*data); } @@ -660,17 +592,29 @@ void LCodeGen::PopulateDeoptimizationLiteralsWithInlinedFunctions() { } +void LCodeGen::RecordSafepointWithLazyDeopt( + LInstruction* instr, SafepointMode safepoint_mode, int argc) { + if (safepoint_mode == RECORD_SIMPLE_SAFEPOINT) { + RecordSafepoint(instr->pointer_map(), Safepoint::kLazyDeopt); + } else { + ASSERT(safepoint_mode == RECORD_SAFEPOINT_WITH_REGISTERS); + RecordSafepointWithRegisters( + instr->pointer_map(), argc, Safepoint::kLazyDeopt); + } +} + + void LCodeGen::RecordSafepoint( LPointerMap* pointers, Safepoint::Kind kind, int arguments, - int deoptimization_index) { + Safepoint::DeoptMode deopt_mode) { ASSERT(kind == expected_safepoint_kind_); const ZoneList* operands = pointers->operands(); Safepoint safepoint = safepoints_.DefineSafepoint(masm(), - kind, arguments, deoptimization_index); + kind, arguments, deopt_mode); for (int i = 0; i < operands->length(); i++) { LOperand* pointer = operands->at(i); if (pointer->IsStackSlot()) { @@ -687,22 +631,21 @@ void LCodeGen::RecordSafepoint( void LCodeGen::RecordSafepoint(LPointerMap* pointers, - int deoptimization_index) { - RecordSafepoint(pointers, Safepoint::kSimple, 0, deoptimization_index); + Safepoint::DeoptMode deopt_mode) { + RecordSafepoint(pointers, Safepoint::kSimple, 0, deopt_mode); } -void LCodeGen::RecordSafepoint(int deoptimization_index) { +void LCodeGen::RecordSafepoint(Safepoint::DeoptMode deopt_mode) { LPointerMap empty_pointers(RelocInfo::kNoPosition); - RecordSafepoint(&empty_pointers, deoptimization_index); + RecordSafepoint(&empty_pointers, deopt_mode); } void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers, int arguments, - int deoptimization_index) { - RecordSafepoint(pointers, Safepoint::kWithRegisters, arguments, - deoptimization_index); + Safepoint::DeoptMode deopt_mode) { + RecordSafepoint(pointers, Safepoint::kWithRegisters, arguments, deopt_mode); } @@ -737,12 +680,6 @@ void LCodeGen::DoGap(LGap* gap) { LParallelMove* move = gap->GetParallelMove(inner_pos); if (move != NULL) DoParallelMove(move); } - - LInstruction* next = GetNextInstruction(); - if (next != NULL && next->IsLazyBailout()) { - int pc = masm()->pc_offset(); - safepoints_.SetPcAfterGap(pc); - } } @@ -1851,7 +1788,7 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { LInstanceOfKnownGlobal* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { - codegen()->DoDeferredLInstanceOfKnownGlobal(instr_, &map_check_); + codegen()->DoDeferredInstanceOfKnownGlobal(instr_, &map_check_); } Label* map_check() { return &map_check_; } @@ -1910,8 +1847,8 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { } -void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, - Label* map_check) { +void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, + Label* map_check) { { PushSafepointRegistersScope scope(this); InstanceofStub::Flags flags = static_cast( @@ -1937,6 +1874,9 @@ void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, RECORD_SAFEPOINT_WITH_REGISTERS, 2); ASSERT(delta == masm_->SizeOfCodeGeneratedSince(map_check)); + ASSERT(instr->HasDeoptimizationEnvironment()); + LEnvironment* env = instr->deoptimization_environment(); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); // Move result to a register that survives the end of the // PushSafepointRegisterScope. __ movq(kScratchRegister, rax); @@ -2508,12 +2448,9 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { __ bind(&invoke); ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); - LEnvironment* env = instr->deoptimization_environment(); RecordPosition(pointers->position()); - RegisterEnvironmentForDeoptimization(env); - SafepointGenerator safepoint_generator(this, - pointers, - env->deoptimization_index()); + SafepointGenerator safepoint_generator( + this, pointers, Safepoint::kLazyDeopt); v8::internal::ParameterCount actual(rax); __ InvokeFunction(function, actual, CALL_FUNCTION, safepoint_generator, CALL_AS_METHOD); @@ -2591,7 +2528,7 @@ void LCodeGen::CallKnownFunction(Handle function, } // Setup deoptimization. - RegisterLazyDeoptimization(instr, RECORD_SIMPLE_SAFEPOINT, 0); + RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT, 0); // Restore context. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); @@ -2938,10 +2875,8 @@ void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) { ASSERT(instr->HasPointerMap()); ASSERT(instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); - LEnvironment* env = instr->deoptimization_environment(); RecordPosition(pointers->position()); - RegisterEnvironmentForDeoptimization(env); - SafepointGenerator generator(this, pointers, env->deoptimization_index()); + SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt); ParameterCount count(instr->arity()); __ InvokeFunction(rdi, count, CALL_FUNCTION, generator, CALL_AS_METHOD); __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); @@ -4037,9 +3972,28 @@ void LCodeGen::EmitIsConstructCall(Register temp) { } +void LCodeGen::EnsureSpaceForLazyDeopt(int space_needed) { + // Ensure that we have enough space after the previous lazy-bailout + // instruction for patching the code here. + int current_pc = masm()->pc_offset(); + if (current_pc < last_lazy_deopt_pc_ + space_needed) { + int padding_size = last_lazy_deopt_pc_ + space_needed - current_pc; + while (padding_size > 0) { + int nop_size = padding_size > 9 ? 9 : padding_size; + __ nop(nop_size); + padding_size -= nop_size; + } + } +} + + void LCodeGen::DoLazyBailout(LLazyBailout* instr) { - // No code for lazy bailout instruction. Used to capture environment after a - // call for populating the safepoint data with deoptimization data. + EnsureSpaceForLazyDeopt(Deoptimizer::patch_size()); + last_lazy_deopt_pc_ = masm()->pc_offset(); + ASSERT(instr->HasEnvironment()); + LEnvironment* env = instr->environment(); + RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); } @@ -4055,15 +4009,12 @@ void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) { EmitPushTaggedOperand(key); ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); - LEnvironment* env = instr->deoptimization_environment(); RecordPosition(pointers->position()); - RegisterEnvironmentForDeoptimization(env); // Create safepoint generator that will also ensure enough space in the // reloc info for patching in deoptimization (since this is invoking a // builtin) - SafepointGenerator safepoint_generator(this, - pointers, - env->deoptimization_index()); + SafepointGenerator safepoint_generator( + this, pointers, Safepoint::kLazyDeopt); __ Push(Smi::FromInt(strict_mode_flag())); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, safepoint_generator); } @@ -4076,30 +4027,21 @@ void LCodeGen::DoIn(LIn* instr) { EmitPushTaggedOperand(obj); ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); - LEnvironment* env = instr->deoptimization_environment(); RecordPosition(pointers->position()); - RegisterEnvironmentForDeoptimization(env); - // Create safepoint generator that will also ensure enough space in the - // reloc info for patching in deoptimization (since this is invoking a - // builtin) - SafepointGenerator safepoint_generator(this, - pointers, - env->deoptimization_index()); + SafepointGenerator safepoint_generator( + this, pointers, Safepoint::kLazyDeopt); __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION, safepoint_generator); } void LCodeGen::DoDeferredStackCheck(LStackCheck* instr) { - { - PushSafepointRegistersScope scope(this); - __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); - __ CallRuntimeSaveDoubles(Runtime::kStackGuard); - RegisterLazyDeoptimization(instr, RECORD_SAFEPOINT_WITH_REGISTERS, 0); - } - - // The gap code includes the restoring of the safepoint registers. - int pc = masm()->pc_offset(); - safepoints_.SetPcAfterGap(pc); + PushSafepointRegistersScope scope(this); + __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + __ CallRuntimeSaveDoubles(Runtime::kStackGuard); + RecordSafepointWithLazyDeopt(instr, RECORD_SAFEPOINT_WITH_REGISTERS, 0); + ASSERT(instr->HasEnvironment()); + LEnvironment* env = instr->environment(); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); } @@ -4113,6 +4055,10 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { LStackCheck* instr_; }; + ASSERT(instr->HasEnvironment()); + LEnvironment* env = instr->environment(); + // There is no LLazyBailout instruction for stack-checks. We have to + // prepare for lazy deoptimization explicitly here. if (instr->hydrogen()->is_function_entry()) { // Perform stack overflow check. Label done; @@ -4120,7 +4066,11 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { __ j(above_equal, &done, Label::kNear); StackCheckStub stub; CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + EnsureSpaceForLazyDeopt(Deoptimizer::patch_size()); + last_lazy_deopt_pc_ = masm()->pc_offset(); __ bind(&done); + RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); } else { ASSERT(instr->hydrogen()->is_backwards_branch()); // Perform stack overflow check if this goto needs it before jumping. @@ -4128,8 +4078,14 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { new DeferredStackCheck(this, instr); __ CompareRoot(rsp, Heap::kStackLimitRootIndex); __ j(below, deferred_stack_check->entry()); + EnsureSpaceForLazyDeopt(Deoptimizer::patch_size()); + last_lazy_deopt_pc_ = masm()->pc_offset(); __ bind(instr->done_label()); deferred_stack_check->SetExit(instr->done_label()); + RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); + // Don't record a deoptimization index for the safepoint here. + // This will be done explicitly when emitting call and the safepoint in + // the deferred code. } } @@ -4145,7 +4101,7 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) { // If the environment were already registered, we would have no way of // backpatching it with the spill slot operands. ASSERT(!environment->HasBeenRegistered()); - RegisterEnvironmentForDeoptimization(environment); + RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); ASSERT(osr_pc_offset_ == -1); osr_pc_offset_ = masm()->pc_offset(); } diff --git a/deps/v8/src/x64/lithium-codegen-x64.h b/deps/v8/src/x64/lithium-codegen-x64.h index 8cb4cece960..43c045f7bc6 100644 --- a/deps/v8/src/x64/lithium-codegen-x64.h +++ b/deps/v8/src/x64/lithium-codegen-x64.h @@ -60,6 +60,7 @@ class LCodeGen BASE_EMBEDDED { status_(UNUSED), deferred_(8), osr_pc_offset_(-1), + last_lazy_deopt_pc_(0), resolver_(this), expected_safepoint_kind_(Safepoint::kSimple) { PopulateDeoptimizationLiteralsWithInlinedFunctions(); @@ -97,8 +98,8 @@ class LCodeGen BASE_EMBEDDED { void DoDeferredStackCheck(LStackCheck* instr); void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredStringCharFromCode(LStringCharFromCode* instr); - void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, - Label* map_check); + void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, + Label* map_check); // Parallel move support. void DoParallelMove(LParallelMove* move); @@ -134,7 +135,6 @@ class LCodeGen BASE_EMBEDDED { HGraph* graph() const { return chunk_->graph(); } int GetNextEmittedBlock(int block); - LInstruction* GetNextInstruction(); void EmitClassOfTest(Label* if_true, Label* if_false, @@ -199,10 +199,11 @@ class LCodeGen BASE_EMBEDDED { void LoadHeapObject(Register result, Handle object); - void RegisterLazyDeoptimization(LInstruction* instr, - SafepointMode safepoint_mode, - int argc); - void RegisterEnvironmentForDeoptimization(LEnvironment* environment); + void RecordSafepointWithLazyDeopt(LInstruction* instr, + SafepointMode safepoint_mode, + int argc); + void RegisterEnvironmentForDeoptimization(LEnvironment* environment, + Safepoint::DeoptMode mode); void DeoptimizeIf(Condition cc, LEnvironment* environment); void AddToTranslation(Translation* translation, @@ -236,16 +237,13 @@ class LCodeGen BASE_EMBEDDED { void RecordSafepoint(LPointerMap* pointers, Safepoint::Kind kind, int arguments, - int deoptimization_index); - void RecordSafepoint(LPointerMap* pointers, int deoptimization_index); - void RecordSafepoint(int deoptimization_index); + Safepoint::DeoptMode mode); + void RecordSafepoint(LPointerMap* pointers, Safepoint::DeoptMode mode); + void RecordSafepoint(Safepoint::DeoptMode mode); void RecordSafepointWithRegisters(LPointerMap* pointers, int arguments, - int deoptimization_index); + Safepoint::DeoptMode mode); void RecordPosition(int position); - int LastSafepointEnd() { - return static_cast(safepoints_.GetPcAfterGap()); - } static Condition TokenToCondition(Token::Value op, bool is_unsigned); void EmitGoto(int block); @@ -290,6 +288,8 @@ class LCodeGen BASE_EMBEDDED { Address address; }; + void EnsureSpaceForLazyDeopt(int space_needed); + LChunk* const chunk_; MacroAssembler* const masm_; CompilationInfo* const info_; @@ -306,6 +306,7 @@ class LCodeGen BASE_EMBEDDED { TranslationBuffer translations_; ZoneList deferred_; int osr_pc_offset_; + int last_lazy_deopt_pc_; // Builder that keeps track of safepoints in the code. The table // itself is emitted at the end of the generated code. diff --git a/deps/v8/test/mjsunit/compiler/regress-funcaller.js b/deps/v8/test/mjsunit/compiler/regress-funcaller.js index 88db147a8f0..5c2a59720be 100644 --- a/deps/v8/test/mjsunit/compiler/regress-funcaller.js +++ b/deps/v8/test/mjsunit/compiler/regress-funcaller.js @@ -25,6 +25,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Flags: --allow-natives-syntax + // Test function.caller. function A() {} @@ -40,9 +42,10 @@ A.prototype.g = gee; var o = new A(); -for (var i=0; i<5000000; i++) { +for (var i=0; i<5; i++) { o.g(i); } +%OptimizeFunctionOnNextCall(o.g); assertEquals(gee, o.g(0)); assertEquals(null, o.g(1)); @@ -53,9 +56,10 @@ function hej(x) { return o.g(x); } -for (var j=0; j<5000000; j++) { +for (var j=0; j<5; j++) { hej(j); } +%OptimizeFunctionOnNextCall(hej); assertEquals(gee, hej(0)); assertEquals(hej, hej(1)); @@ -66,8 +70,9 @@ function from_eval(x) { return o.g(x); } -for (var j=0; j<5000000; j++) { +for (var j=0; j<5; j++) { from_eval(j); } +%OptimizeFunctionOnNextCall(from_eval); assertEquals(gee, from_eval(0)); assertEquals(from_eval, from_eval(1)); diff --git a/deps/v8/test/mjsunit/compiler/regress-lazy-deopt.js b/deps/v8/test/mjsunit/compiler/regress-lazy-deopt.js new file mode 100644 index 00000000000..d1c3d01dc6f --- /dev/null +++ b/deps/v8/test/mjsunit/compiler/regress-lazy-deopt.js @@ -0,0 +1,48 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +// Test lazy deoptimization after CallFunctionStub. + +function foo() { return 1; } + +function f(x, y) { + var a = [0]; + if (x == 0) { + %DeoptimizeFunction(f); + return 1; + } + a[0] = %_CallFunction(null, x - 1, f); + return x >> a[0]; +} + +f(42); +f(42); +assertEquals(42, f(42)); +%OptimizeFunctionOnNextCall(f); +assertEquals(42, f(42)); From d18cebaf8a7ac701dabd71a3aa4eb0571db6a645 Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 19 Jan 2012 17:23:56 -0800 Subject: [PATCH 15/30] 2012.01.19, Version 0.6.8 (stable) * Update V8 to 3.6.6.19 * Numeric key hash collision fix for V8 (Erik Corry, Fedor Indutny) * Add missing TTY key translations for F1-F5 on Windows (Brandon Benvie) * path.extname bugfix with . and .. paths (Bert Belder) * cluster: don't always kill the master on uncaughtException (Ben * Noordhuis) * Update npm to 1.1.0-2 (isaacs) * typed arrays: set class name (Ben Noordhuis) * zlib binding cleanup (isaacs, Bert Belder) * dgram: use slab memory allocator (Michael Bernstein) * fix segfault #2473 --- AUTHORS | 5 +++++ ChangeLog | 25 ++++++++++++++++++++++++- doc/index.html | 14 +++++++------- doc/logos/index.html | 2 +- doc/template.html | 4 ++-- src/node_version.h | 2 +- 6 files changed, 40 insertions(+), 12 deletions(-) diff --git a/AUTHORS b/AUTHORS index 5a212e2828c..8c2a9055b9b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -253,3 +253,8 @@ Dave Irvine Ju-yeong Park Phil Sung Damon Oehlman +Ryunosuke SATO +Michael Bernstein +Guillermo Rauch +Dan Williams +Brandon Benvie diff --git a/ChangeLog b/ChangeLog index c23fe9b8ee9..105d9b34e73 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,27 @@ -2012.01.06, Version 0.6.7 (stable) +2012.01.19, Version 0.6.8 (stable) + +* Update V8 to 3.6.6.19 + +* Numeric key hash collision fix for V8 (Erik Corry, Fedor Indutny) + +* Add missing TTY key translations for F1-F5 on Windows (Brandon Benvie) + +* path.extname bugfix with . and .. paths (Bert Belder) + +* cluster: don't always kill the master on uncaughtException (Ben Noordhuis) + +* Update npm to 1.1.0-2 (isaacs) + +* typed arrays: set class name (Ben Noordhuis) + +* zlib binding cleanup (isaacs, Bert Belder) + +* dgram: use slab memory allocator (Michael Bernstein) + +* fix segfault #2473 + + +2012.01.06, Version 0.6.7 (stable), d5a189acef14a851287ee555f7a39431fe276e1c * V8 hash collision fix (Breaks MIPS) (Bert Belder, Erik Corry) diff --git a/doc/index.html b/doc/index.html index 61f10893d7b..3769002f911 100644 --- a/doc/index.html +++ b/doc/index.html @@ -77,15 +77,15 @@ X
    -
  • Change Log
  • -
  • Documentation
  • -
  • Other release files
  • -
  • License
  • +
  • Change Log
  • +
  • Documentation
  • +
  • Other release files
  • +
  • License
  • Git Repository
  • Installing with a Package Manager diff --git a/doc/logos/index.html b/doc/logos/index.html index 403abe2da0e..35c575cb551 100644 --- a/doc/logos/index.html +++ b/doc/logos/index.html @@ -60,7 +60,7 @@ diff --git a/doc/template.html b/doc/template.html index 4eae50ea79e..52b0d26df62 100644 --- a/doc/template.html +++ b/doc/template.html @@ -2,7 +2,7 @@ - {{section}}Node.js v0.6.7 Manual & Documentation + {{section}}Node.js v0.6.8 Manual & Documentation @@ -10,7 +10,7 @@
    -

    Node.js v0.6.7 Manual & Documentation

    +

    Node.js v0.6.8 Manual & Documentation

    diff --git a/src/node_version.h b/src/node_version.h index 1db4b0a9b6a..faf54fa4311 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -29,7 +29,7 @@ #define NODE_MAJOR_VERSION 0 #define NODE_MINOR_VERSION 6 #define NODE_PATCH_VERSION 8 -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n) From ec1d1ee61fa90471e1f9f1f89a4582ffc32424a0 Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 19 Jan 2012 21:15:12 -0800 Subject: [PATCH 16/30] Now working on 0.6.9 --- src/node_version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node_version.h b/src/node_version.h index faf54fa4311..c46c88b697e 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -28,8 +28,8 @@ #define NODE_MAJOR_VERSION 0 #define NODE_MINOR_VERSION 6 -#define NODE_PATCH_VERSION 8 -#define NODE_VERSION_IS_RELEASE 1 +#define NODE_PATCH_VERSION 9 +#define NODE_VERSION_IS_RELEASE 0 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n) From f25ec0782e379469d2dd097b6168a5a120f624e4 Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 19 Jan 2012 21:15:12 -0800 Subject: [PATCH 17/30] Add missing ChangeLog item re: fs.stat improvement This was a very significant improvement that should have been called out explicitly. I added 'review all libuv changes' to my build checklist. It won't happen again. Sorry, @igorzi. --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index 105d9b34e73..64d001dcf8f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20,6 +20,8 @@ * fix segfault #2473 +* #2521 60% improvement in fs.stat on Windows (Igor Zinkovsky) + 2012.01.06, Version 0.6.7 (stable), d5a189acef14a851287ee555f7a39431fe276e1c From 40c93486e8b08f7f37dfd8be95ff03990ab858bd Mon Sep 17 00:00:00 2001 From: Nicolas LaCasse Date: Fri, 20 Jan 2012 15:36:28 +0100 Subject: [PATCH 18/30] Fix #2365 zlib crashing on invalid input Fix zlib crashes on error due to improper use of removeListener in the error handler --- lib/zlib.js | 20 ++++++++------ test/simple/test-zlib-invalid-input.js | 38 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 test/simple/test-zlib-invalid-input.js diff --git a/lib/zlib.js b/lib/zlib.js index 840927b770d..155924f4ebf 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -122,18 +122,18 @@ function zlibBuffer(engine, buffer, callback) { var buffers = []; var nread = 0; - engine.on('error', function(err) { - engine.removeListener('end'); - engine.removeListener('error'); + function onError(err) { + engine.removeListener('end', onEnd); + engine.removeListener('error', onError); callback(err); - }); + } - engine.on('data', function(chunk) { + function onData(chunk) { buffers.push(chunk); nread += chunk.length; - }); + } - engine.on('end', function() { + function onEnd() { var buffer; switch(buffers.length) { case 0: @@ -153,7 +153,11 @@ function zlibBuffer(engine, buffer, callback) { break; } callback(null, buffer); - }); + } + + engine.on('error', onError); + engine.on('data', onData); + engine.on('end', onEnd); engine.write(buffer); engine.end(); diff --git a/test/simple/test-zlib-invalid-input.js b/test/simple/test-zlib-invalid-input.js new file mode 100644 index 00000000000..4c581e1c33d --- /dev/null +++ b/test/simple/test-zlib-invalid-input.js @@ -0,0 +1,38 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// test uncompressing invalid input + +var common = require('../common.js'), + assert = require('assert'), + zlib = require('zlib'); + +var nonStringInputs = [1, true, {a: 1}, ['a']]; + +nonStringInputs.forEach(function(input) { + // zlib.gunzip should not throw an error when called with bad input. + assert.doesNotThrow(function () { + zlib.gunzip(input, function (err, buffer) { + // zlib.gunzip should pass the error to the callback. + assert.ok(err); + }); + }); +}); From 35fe3eb5c774d1c21fdafb010c633ab6bc42d2c7 Mon Sep 17 00:00:00 2001 From: koichik Date: Fri, 20 Jan 2012 21:54:34 +0900 Subject: [PATCH 19/30] http: reduce creating http-parser instances Fixes #2577. --- lib/http.js | 22 ++++++++++-- test/simple/test-http-parser-free.js | 54 ++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 test/simple/test-http-parser-free.js diff --git a/lib/http.js b/lib/http.js index c4e0d54f777..df891867610 100644 --- a/lib/http.js +++ b/lib/http.js @@ -1122,13 +1122,23 @@ ClientRequest.prototype.onSocket = function(socket) { // Setup "drain" propogation. httpSocketSetup(socket); + var freeParser = function() { + if (parser) { + parsers.free(parser); + parser = null; + } + }; + var errorListener = function(err) { debug('HTTP SOCKET ERROR: ' + err.message + '\n' + err.stack); req.emit('error', err); // For Safety. Some additional errors might fire later on // and we need to make sure we don't double-fire the error event. req._hadError = true; - parser.finish(); + if (parser) { + parser.finish(); + freeParser(); + } socket.destroy(); } socket.on('error', errorListener); @@ -1137,6 +1147,7 @@ ClientRequest.prototype.onSocket = function(socket) { var ret = parser.execute(d, start, end - start); if (ret instanceof Error) { debug('parse error'); + freeParser(); socket.destroy(ret); } else if (parser.incoming && parser.incoming.upgrade) { var bytesParsed = ret; @@ -1157,6 +1168,9 @@ ClientRequest.prototype.onSocket = function(socket) { // Got upgrade header, but have no handler. socket.destroy(); } + freeParser(); + } else if (parser.incoming && parser.incoming.complete) { + freeParser(); } }; @@ -1167,8 +1181,10 @@ ClientRequest.prototype.onSocket = function(socket) { req.emit('error', createHangUpError()); req._hadError = true; } - parser.finish(); - parsers.free(parser); // I don't know if this is necessary --Mikeal + if (parser) { + parser.finish(); + freeParser(); + } socket.destroy(); }; diff --git a/test/simple/test-http-parser-free.js b/test/simple/test-http-parser-free.js new file mode 100644 index 00000000000..bbf4a502749 --- /dev/null +++ b/test/simple/test-http-parser-free.js @@ -0,0 +1,54 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var http = require('http'); +var N = 100; +var responses = 0; + +var server = http.createServer(function(req, res) { + res.end('Hello'); +}); + +server.listen(common.PORT, function() { + http.globalAgent.maxSockets = 1; + var parser; + for (var i = 0; i < N; ++i) { + (function makeRequest(i) { + var req = http.get({port: common.PORT}, function(res) { + if (!parser) { + parser = req.parser; + } else { + assert.strictEqual(req.parser, parser); + } + + if (++responses === N) { + server.close(); + } + }); + })(i); + } +}); + +process.on('exit', function() { + assert.equal(responses, N); +}); From 08ab306afd0161aebde527a145c49cb848718df2 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 21 Jan 2012 03:13:58 +0100 Subject: [PATCH 20/30] uv: upgrade to 497b1ec --- deps/uv/common.gypi | 1 - deps/uv/gyp_uv | 4 +- deps/uv/include/uv-private/ev.h | 2 + deps/uv/include/uv.h | 29 ++++++ deps/uv/src/unix/core.c | 11 +-- deps/uv/src/unix/ev/ev.c | 6 ++ deps/uv/src/unix/ev/ev_kqueue.c | 2 - deps/uv/src/unix/tty.c | 6 +- deps/uv/src/unix/udp.c | 36 ++++++- deps/uv/src/win/core.c | 5 + deps/uv/test/echo-server.c | 64 ++++++++++++ deps/uv/test/task.h | 1 + deps/uv/test/test-fs-event.c | 18 ---- deps/uv/test/test-list.h | 46 ++++++--- deps/uv/test/test-ref.c | 169 ++++++++++++++++++++++++++++++++ deps/uv/test/test-tcp-close.c | 47 --------- deps/uv/test/test-timer.c | 40 -------- deps/uv/test/test-tty.c | 2 + 18 files changed, 353 insertions(+), 136 deletions(-) diff --git a/deps/uv/common.gypi b/deps/uv/common.gypi index e0eb76d267b..0d7ec83dd9b 100644 --- a/deps/uv/common.gypi +++ b/deps/uv/common.gypi @@ -153,7 +153,6 @@ 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden 'GCC_THREADSAFE_STATICS': 'NO', # -fno-threadsafe-statics 'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES', # -Wnewline-eof - 'MACOSX_DEPLOYMENT_TARGET': '10.4', # -mmacosx-version-min=10.4 'PREBINDING': 'NO', # No -Wl,-prebind 'USE_HEADERMAP': 'NO', 'OTHER_CFLAGS': [ diff --git a/deps/uv/gyp_uv b/deps/uv/gyp_uv index a7a9689c265..225c9768db1 100755 --- a/deps/uv/gyp_uv +++ b/deps/uv/gyp_uv @@ -45,12 +45,12 @@ if __name__ == '__main__': # There's a bug with windows which doesn't allow this feature. if sys.platform != 'win32': - # Tell gyp to write the Makefiles into output_dir args.extend(['--generator-output', output_dir]) - # Tell make to write its output into the same dir args.extend(['-Goutput_dir=' + output_dir]) + # Create Makefiles, not XCode projects + args.extend('-f make'.split()) args.append('-Dtarget_arch=ia32') args.append('-Dcomponent=static_library') diff --git a/deps/uv/include/uv-private/ev.h b/deps/uv/include/uv-private/ev.h index 5d2d7a1e37e..11e81cda5e1 100644 --- a/deps/uv/include/uv-private/ev.h +++ b/deps/uv/include/uv-private/ev.h @@ -562,6 +562,8 @@ EV_MAYBE_UNUSED ev_is_default_loop (EV_P) /* create and destroy alternative loops that don't handle signals */ struct ev_loop *ev_loop_new (unsigned int flags EV_CPP (= 0)); +int ev_loop_refcount (EV_P); + ev_tstamp ev_now (EV_P); /* time w.r.t. timers and the eventloop, updated after each poll */ #else diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index a2007087550..fb4e40ab4cc 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -200,6 +200,9 @@ typedef struct uv_work_s uv_work_t; UV_EXTERN uv_loop_t* uv_loop_new(void); UV_EXTERN void uv_loop_delete(uv_loop_t*); +/* This is a debugging tool. It's NOT part of the official API. */ +UV_EXTERN int uv_loop_refcount(const uv_loop_t*); + /* * Returns the default loop. @@ -628,6 +631,32 @@ UV_EXTERN int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, uv_membership membership); +/* + * Set the multicast ttl + * + * Arguments: + * handle UDP handle. Should have been initialized with + * `uv_udp_init`. + * ttl 1 through 255 + * + * Returns: + * 0 on success, -1 on error. + */ +int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl); + +/* + * Set broadcast on or off + * + * Arguments: + * handle UDP handle. Should have been initialized with + * `uv_udp_init`. + * on 1 for on, 0 for off + * + * Returns: + * 0 on success, -1 on error. + */ +int uv_udp_set_broadcast(uv_udp_t* handle, int on); + /* * Send data. If the socket has not previously been bound with `uv_udp_bind` * or `uv_udp_bind6`, it is bound to 0.0.0.0 (the "all interfaces" address) diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index cf323adc20a..40fb3d95e44 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -63,12 +63,6 @@ void uv__next(EV_P_ ev_idle* watcher, int revents); static void uv__finish_close(uv_handle_t* handle); - -#ifndef __GNUC__ -#define __attribute__(a) -#endif - - void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { uv_udp_t* udp; uv_async_t* async; @@ -181,6 +175,11 @@ void uv_loop_delete(uv_loop_t* loop) { } +int uv_loop_refcount(const uv_loop_t* loop) { + return ev_loop_refcount(loop->ev); +} + + uv_loop_t* uv_default_loop(void) { if (!default_loop_ptr) { default_loop_ptr = &default_loop_struct; diff --git a/deps/uv/src/unix/ev/ev.c b/deps/uv/src/unix/ev/ev.c index a3bec43fbec..b6e190f75f6 100644 --- a/deps/uv/src/unix/ev/ev.c +++ b/deps/uv/src/unix/ev/ev.c @@ -1958,6 +1958,12 @@ ev_loop_new (unsigned int flags) #endif /* multiplicity */ +int +ev_loop_refcount (EV_P) +{ + return activecnt; +} + #if EV_VERIFY static void noinline verify_watcher (EV_P_ W w) diff --git a/deps/uv/src/unix/ev/ev_kqueue.c b/deps/uv/src/unix/ev/ev_kqueue.c index 212ca29ae64..f03cb8083d7 100644 --- a/deps/uv/src/unix/ev/ev_kqueue.c +++ b/deps/uv/src/unix/ev/ev_kqueue.c @@ -200,8 +200,6 @@ kqueue_destroy (EV_P) void inline_size kqueue_fork (EV_P) { - close (backend_fd); - while ((backend_fd = kqueue ()) < 0) ev_syserr ("(libev) kqueue"); diff --git a/deps/uv/src/unix/tty.c b/deps/uv/src/unix/tty.c index de77f5c46ab..18a892168fb 100644 --- a/deps/uv/src/unix/tty.c +++ b/deps/uv/src/unix/tty.c @@ -120,8 +120,7 @@ uv_handle_type uv_guess_handle(uv_file file) { struct stat s; if (file < 0) { - uv__set_sys_error(NULL, EINVAL); /* XXX Need loop? */ - return -1; + return UV_UNKNOWN_HANDLE; } if (isatty(file)) { @@ -129,8 +128,7 @@ uv_handle_type uv_guess_handle(uv_file file) { } if (fstat(file, &s)) { - uv__set_sys_error(NULL, errno); /* XXX Need loop? */ - return -1; + return UV_UNKNOWN_HANDLE; } if (!S_ISSOCK(s.st_mode) && !S_ISFIFO(s.st_mode)) { diff --git a/deps/uv/src/unix/udp.c b/deps/uv/src/unix/udp.c index cbb37669a24..0ebfe7079cc 100644 --- a/deps/uv/src/unix/udp.c +++ b/deps/uv/src/unix/udp.c @@ -42,6 +42,10 @@ static int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], static void uv__udp_watcher_start(uv_udp_t* handle, ev_io* w) { int flags; + if (ev_is_active(w)) { + return; + } + assert(w == &handle->read_watcher || w == &handle->write_watcher); @@ -51,17 +55,23 @@ static void uv__udp_watcher_start(uv_udp_t* handle, ev_io* w) { ev_set_cb(w, uv__udp_io); ev_io_set(w, handle->fd, flags); ev_io_start(handle->loop->ev, w); + ev_unref(handle->loop->ev); } void uv__udp_watcher_stop(uv_udp_t* handle, ev_io* w) { int flags; + if (!ev_is_active(w)) { + return; + } + assert(w == &handle->read_watcher || w == &handle->write_watcher); flags = (w == &handle->read_watcher ? EV_READ : EV_WRITE); + ev_ref(handle->loop->ev); ev_io_stop(handle->loop->ev, w); ev_io_set(w, -1, flags); ev_set_cb(w, NULL); @@ -324,6 +334,12 @@ static int uv__bind(uv_udp_t* handle, goto out; } + yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) { + uv__set_sys_error(handle->loop, errno); + goto out; + } + if (flags & UV_UDP_IPV6ONLY) { #ifdef IPV6_V6ONLY yes = 1; @@ -332,7 +348,7 @@ static int uv__bind(uv_udp_t* handle, goto out; } #else - uv__set_sys_error((uv_handle_t*)handle, ENOTSUP); + uv__set_sys_error(handle->loop, ENOTSUP); goto out; #endif } @@ -493,6 +509,24 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, return 0; } +int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) { + if (setsockopt(handle->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof ttl) == -1) { + uv__set_sys_error(handle->loop, errno); + return -1; + } + + return 0; +} + +int uv_udp_set_broadcast(uv_udp_t* handle, int on) { + if (setsockopt(handle->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof on) == -1) { + uv__set_sys_error(handle->loop, errno); + return -1; + } + + return 0; +} + int uv_udp_getsockname(uv_udp_t* handle, struct sockaddr* name, int* namelen) { diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c index 72fee1c80bf..b55c3cea48a 100644 --- a/deps/uv/src/win/core.c +++ b/deps/uv/src/win/core.c @@ -116,6 +116,11 @@ void uv_loop_delete(uv_loop_t* loop) { } +int uv_loop_refcount(const uv_loop_t* loop) { + return loop->refs; +} + + void uv_ref(uv_loop_t* loop) { loop->refs++; } diff --git a/deps/uv/test/echo-server.c b/deps/uv/test/echo-server.c index 8b1754410cd..6b0d8972144 100644 --- a/deps/uv/test/echo-server.c +++ b/deps/uv/test/echo-server.c @@ -34,6 +34,7 @@ static uv_loop_t* loop; static int server_closed; static stream_type serverType; static uv_tcp_t tcpServer; +static uv_udp_t udpServer; static uv_pipe_t pipeServer; static uv_handle_t* server; @@ -176,6 +177,34 @@ static void on_server_close(uv_handle_t* handle) { } +static void on_send(uv_udp_send_t* req, int status); + + +static void on_recv(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + uv_udp_send_t* req; + int r; + + ASSERT(nread > 0); + ASSERT(addr->sa_family == AF_INET); + + req = malloc(sizeof(*req)); + ASSERT(req != NULL); + + r = uv_udp_send(req, handle, &buf, 1, *(struct sockaddr_in*)addr, on_send); + ASSERT(r == 0); +} + + +static void on_send(uv_udp_send_t* req, int status) { + ASSERT(status == 0); + free(req); +} + + static int tcp4_echo_start(int port) { struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", port); int r; @@ -242,6 +271,30 @@ static int tcp6_echo_start(int port) { } +static int udp4_echo_start(int port) { + int r; + + server = (uv_handle_t*)&udpServer; + serverType = UDP; + + r = uv_udp_init(loop, &udpServer); + if (r) { + fprintf(stderr, "uv_udp_init: %s\n", + uv_strerror(uv_last_error(loop))); + return 1; + } + + r = uv_udp_recv_start(&udpServer, echo_alloc, on_recv); + if (r) { + fprintf(stderr, "uv_udp_recv_start: %s\n", + uv_strerror(uv_last_error(loop))); + return 1; + } + + return 0; +} + + static int pipe_echo_start(char* pipeName) { int r; @@ -304,3 +357,14 @@ HELPER_IMPL(pipe_echo_server) { uv_run(loop); return 0; } + + +HELPER_IMPL(udp4_echo_server) { + loop = uv_default_loop(); + + if (udp4_echo_start(TEST_PORT)) + return 1; + + uv_run(loop); + return 0; +} diff --git a/deps/uv/test/task.h b/deps/uv/test/task.h index e28b393bb72..b553f862fc7 100644 --- a/deps/uv/test/task.h +++ b/deps/uv/test/task.h @@ -42,6 +42,7 @@ typedef enum { TCP = 0, + UDP, PIPE } stream_type; diff --git a/deps/uv/test/test-fs-event.c b/deps/uv/test/test-fs-event.c index 8b52f67ff36..7f02e68a776 100644 --- a/deps/uv/test/test-fs-event.c +++ b/deps/uv/test/test-fs-event.c @@ -308,21 +308,3 @@ TEST_IMPL(fs_event_immediate_close) { return 0; } - - -TEST_IMPL(fs_event_unref) { - uv_loop_t* loop; - int r; - - loop = uv_default_loop(); - - r = uv_fs_event_init(loop, &fs_event, ".", fs_event_fail, 0); - ASSERT(r == 0); - - uv_unref(loop); - - r = uv_run(loop); - ASSERT(r == 0); - - return 0; -} diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index f6b58a693f1..5ff162301f0 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -25,8 +25,6 @@ TEST_DECLARE (ipc_listen_before_write) TEST_DECLARE (ipc_listen_after_write) TEST_DECLARE (tcp_ping_pong) TEST_DECLARE (tcp_ping_pong_v6) -TEST_DECLARE (tcp_ref) -TEST_DECLARE (tcp_ref2) TEST_DECLARE (pipe_ping_pong) TEST_DECLARE (delayed_accept) TEST_DECLARE (multiple_listen) @@ -63,18 +61,29 @@ TEST_DECLARE (shutdown_eof) TEST_DECLARE (callback_stack) TEST_DECLARE (error_message) TEST_DECLARE (timer) -TEST_DECLARE (timer_ref) -TEST_DECLARE (timer_ref2) TEST_DECLARE (timer_again) TEST_DECLARE (idle_starvation) TEST_DECLARE (loop_handles) +TEST_DECLARE (get_loadavg) TEST_DECLARE (ref) TEST_DECLARE (idle_ref) -TEST_DECLARE (get_loadavg) TEST_DECLARE (async_ref) TEST_DECLARE (prepare_ref) TEST_DECLARE (check_ref) TEST_DECLARE (unref_in_prepare_cb) +TEST_DECLARE (timer_ref) +TEST_DECLARE (timer_ref2) +TEST_DECLARE (fs_event_ref) +TEST_DECLARE (tcp_ref) +TEST_DECLARE (tcp_ref2) +TEST_DECLARE (tcp_ref3) +TEST_DECLARE (udp_ref) +TEST_DECLARE (udp_ref2) +TEST_DECLARE (udp_ref3) +TEST_DECLARE (pipe_ref) +TEST_DECLARE (pipe_ref2) +TEST_DECLARE (pipe_ref3) +TEST_DECLARE (process_ref) TEST_DECLARE (async) TEST_DECLARE (get_currentexe) TEST_DECLARE (cwd_and_chdir) @@ -112,7 +121,6 @@ TEST_DECLARE (fs_event_watch_file) TEST_DECLARE (fs_event_watch_file_current_dir) TEST_DECLARE (fs_event_no_callback_on_close) TEST_DECLARE (fs_event_immediate_close) -TEST_DECLARE (fs_event_unref) TEST_DECLARE (fs_readdir_empty_dir) TEST_DECLARE (fs_readdir_file) TEST_DECLARE (fs_open_dir) @@ -128,6 +136,7 @@ TEST_DECLARE (listen_no_simultaneous_accepts) #endif HELPER_DECLARE (tcp4_echo_server) HELPER_DECLARE (tcp6_echo_server) +HELPER_DECLARE (udp4_echo_server) HELPER_DECLARE (pipe_echo_server) @@ -139,11 +148,6 @@ TASK_LIST_START TEST_ENTRY (ipc_listen_before_write) TEST_ENTRY (ipc_listen_after_write) - TEST_ENTRY (tcp_ref) - - TEST_ENTRY (tcp_ref2) - TEST_HELPER (tcp_ref2, tcp4_echo_server) - TEST_ENTRY (tcp_ping_pong) TEST_HELPER (tcp_ping_pong, tcp4_echo_server) @@ -200,9 +204,6 @@ TASK_LIST_START TEST_ENTRY (error_message) TEST_ENTRY (timer) - TEST_ENTRY (timer_ref) - TEST_ENTRY (timer_ref2) - TEST_ENTRY (timer_again) TEST_ENTRY (idle_starvation) @@ -213,6 +214,22 @@ TASK_LIST_START TEST_ENTRY (prepare_ref) TEST_ENTRY (check_ref) TEST_ENTRY (unref_in_prepare_cb) + TEST_ENTRY (timer_ref) + TEST_ENTRY (timer_ref2) + TEST_ENTRY (fs_event_ref) + TEST_ENTRY (tcp_ref) + TEST_ENTRY (tcp_ref2) + TEST_ENTRY (tcp_ref3) + TEST_HELPER (tcp_ref3, tcp4_echo_server) + TEST_ENTRY (udp_ref) + TEST_ENTRY (udp_ref2) + TEST_ENTRY (udp_ref3) + TEST_HELPER (udp_ref3, udp4_echo_server) + TEST_ENTRY (pipe_ref) + TEST_ENTRY (pipe_ref2) + TEST_ENTRY (pipe_ref3) + TEST_HELPER (pipe_ref3, pipe_echo_server) + TEST_ENTRY (process_ref) TEST_ENTRY (loop_handles) @@ -269,7 +286,6 @@ TASK_LIST_START TEST_ENTRY (fs_event_watch_file_current_dir) TEST_ENTRY (fs_event_no_callback_on_close) TEST_ENTRY (fs_event_immediate_close) - TEST_ENTRY (fs_event_unref) TEST_ENTRY (fs_readdir_empty_dir) TEST_ENTRY (fs_readdir_file) TEST_ENTRY (fs_open_dir) diff --git a/deps/uv/test/test-ref.c b/deps/uv/test/test-ref.c index 0083335fd12..2b8aabbc6f9 100644 --- a/deps/uv/test/test-ref.c +++ b/deps/uv/test/test-ref.c @@ -22,6 +22,14 @@ #include "uv.h" #include "task.h" +#include +#include + + +static void fail_cb(void) { + FATAL("fail_cb should not have been called"); +} + TEST_IMPL(ref) { uv_run(uv_default_loop()); @@ -83,3 +91,164 @@ TEST_IMPL(unref_in_prepare_cb) { uv_run(uv_default_loop()); return 0; } + + +TEST_IMPL(timer_ref) { + uv_timer_t h; + uv_timer_init(uv_default_loop(), &h); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(timer_ref2) { + uv_timer_t h; + uv_timer_init(uv_default_loop(), &h); + uv_timer_start(&h, (uv_timer_cb) fail_cb, 42, 42); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(fs_event_ref) { + uv_fs_event_t h; + uv_fs_event_init(uv_default_loop(), &h, ".", (uv_fs_event_cb) fail_cb, 0); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(tcp_ref) { + uv_tcp_t h; + uv_tcp_init(uv_default_loop(), &h); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(tcp_ref2) { + uv_tcp_t h; + uv_tcp_init(uv_default_loop(), &h); + uv_listen((uv_stream_t*)&h, 128, (uv_connection_cb)fail_cb); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(tcp_ref3) { + struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + uv_connect_t req; + uv_tcp_t h; + uv_tcp_init(uv_default_loop(), &h); + uv_tcp_connect(&req, &h, addr, (uv_connect_cb)fail_cb); + uv_unref(uv_default_loop()); + uv_unref(uv_default_loop()); /* connect req refs the loop */ + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(udp_ref) { + uv_udp_t h; + uv_udp_init(uv_default_loop(), &h); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(udp_ref2) { + struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + uv_udp_t h; + uv_udp_init(uv_default_loop(), &h); + uv_udp_bind(&h, addr, 0); + uv_udp_recv_start(&h, (uv_alloc_cb)fail_cb, (uv_udp_recv_cb)fail_cb); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(udp_ref3) { + struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + uv_buf_t buf = uv_buf_init("PING", 4); + uv_udp_send_t req; + uv_udp_t h; + + uv_udp_init(uv_default_loop(), &h); + uv_udp_send(&req, &h, &buf, 1, addr, (uv_udp_send_cb)fail_cb); + uv_unref(uv_default_loop()); + uv_unref(uv_default_loop()); /* send req refs the loop */ + uv_run(uv_default_loop()); + + return 0; +} + + +TEST_IMPL(pipe_ref) { + uv_pipe_t h; + uv_pipe_init(uv_default_loop(), &h, 0); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(pipe_ref2) { + uv_pipe_t h; + uv_pipe_init(uv_default_loop(), &h, 0); + uv_listen((uv_stream_t*)&h, 128, (uv_connection_cb)fail_cb); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(pipe_ref3) { + uv_connect_t req; + uv_pipe_t h; + uv_pipe_init(uv_default_loop(), &h, 0); + uv_pipe_connect(&req, &h, TEST_PIPENAME, (uv_connect_cb)fail_cb); + uv_unref(uv_default_loop()); + uv_unref(uv_default_loop()); /* connect req refs the loop */ + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(process_ref) { + /* spawn_helper4 blocks indefinitely. */ + char *argv[] = { NULL, "spawn_helper4", NULL }; + uv_process_options_t options; + size_t exepath_size; + char exepath[256]; + uv_process_t h; + int r; + + memset(&options, 0, sizeof(options)); + exepath_size = sizeof(exepath); + + r = uv_exepath(exepath, &exepath_size); + ASSERT(r == 0); + + argv[0] = exepath; + options.file = exepath; + options.args = argv; + options.exit_cb = NULL; + + r = uv_spawn(uv_default_loop(), &h, options); + ASSERT(r == 0); + + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + + r = uv_process_kill(&h, /* SIGTERM */ 15); + ASSERT(r == 0); + + return 0; +} diff --git a/deps/uv/test/test-tcp-close.c b/deps/uv/test/test-tcp-close.c index f5dd0a44058..5da8a84f8a2 100644 --- a/deps/uv/test/test-tcp-close.c +++ b/deps/uv/test/test-tcp-close.c @@ -127,50 +127,3 @@ TEST_IMPL(tcp_close) { return 0; } - - -TEST_IMPL(tcp_ref) { - uv_tcp_t never; - int r; - - /* A tcp just initialized should count as one reference. */ - r = uv_tcp_init(uv_default_loop(), &never); - ASSERT(r == 0); - - /* One unref should set the loop ref count to zero. */ - uv_unref(uv_default_loop()); - - /* Therefore this does not block */ - uv_run(uv_default_loop()); - - return 0; -} - - -static void never_cb(uv_connect_t* conn_req, int status) { - FATAL("never_cb should never be called"); -} - - -TEST_IMPL(tcp_ref2) { - uv_tcp_t never; - int r; - - /* A tcp just initialized should count as one reference. */ - r = uv_tcp_init(uv_default_loop(), &never); - ASSERT(r == 0); - - r = uv_tcp_connect(&connect_req, - &never, - uv_ip4_addr("127.0.0.1", TEST_PORT), - never_cb); - ASSERT(r == 0); - - /* One unref should set the loop ref count to zero. */ - uv_unref(uv_default_loop()); - - /* Therefore this does not block */ - uv_run(uv_default_loop()); - - return 0; -} diff --git a/deps/uv/test/test-timer.c b/deps/uv/test/test-timer.c index 87235a51bc7..17bcb84b770 100644 --- a/deps/uv/test/test-timer.c +++ b/deps/uv/test/test-timer.c @@ -130,43 +130,3 @@ TEST_IMPL(timer) { return 0; } - - -TEST_IMPL(timer_ref) { - uv_timer_t never; - int r; - - /* A timer just initialized should count as one reference. */ - r = uv_timer_init(uv_default_loop(), &never); - ASSERT(r == 0); - - /* One unref should set the loop ref count to zero. */ - uv_unref(uv_default_loop()); - - /* Therefore this does not block */ - uv_run(uv_default_loop()); - - return 0; -} - - -TEST_IMPL(timer_ref2) { - uv_timer_t never; - int r; - - /* A timer just initialized should count as one reference. */ - r = uv_timer_init(uv_default_loop(), &never); - ASSERT(r == 0); - - /* We start the timer, this should not effect the ref count. */ - r = uv_timer_start(&never, never_cb, 1000, 1000); - ASSERT(r == 0); - - /* One unref should set the loop ref count to zero. */ - uv_unref(uv_default_loop()); - - /* Therefore this does not block */ - uv_run(uv_default_loop()); - - return 0; -} diff --git a/deps/uv/test/test-tty.c b/deps/uv/test/test-tty.c index d1f6ae6fe68..1e3e1f280c1 100644 --- a/deps/uv/test/test-tty.c +++ b/deps/uv/test/test-tty.c @@ -27,6 +27,8 @@ TEST_IMPL(tty) { uv_tty_t tty; uv_loop_t* loop = uv_default_loop(); + ASSERT(UV_UNKNOWN_HANDLE == uv_guess_handle(-1)); + /* * Not necessarily a problem if this assert goes off. E.G you are piping * this test to a file. 0 == stdin. From a38fd6056c2c6d4e2266a65fdefdbdc8dcbe4dee Mon Sep 17 00:00:00 2001 From: Roman Shtylman Date: Wed, 12 Oct 2011 13:55:15 -0400 Subject: [PATCH 21/30] dgram: reintroduce addMembership() and dropMembership() Removed during the early stages of node 0.5 refactoring to libuv. --- lib/dgram.js | 22 ++- src/udp_wrap.cc | 41 +++++ .../test-dgram-multicast-multi-process.js | 160 ++++++++++++++++++ 3 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 test/simple/test-dgram-multicast-multi-process.js diff --git a/lib/dgram.js b/lib/dgram.js index 26e0a2a3e81..42473b40e77 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -243,16 +243,26 @@ Socket.prototype.setMulticastLoopback = function(arg) { Socket.prototype.addMembership = function(multicastAddress, - multicastInterface) { - // are we ever going to support this in libuv? - throw new Error('not yet implemented'); + interfaceAddress) { + this._healthCheck(); + + if (!multicastAddress) { + throw new Error('multicast address must be specified'); + } + + return this._handle.addMembership(multicastAddress, interfaceAddress); }; Socket.prototype.dropMembership = function(multicastAddress, - multicastInterface) { - // are we ever going to support this in libuv? - throw new Error('not yet implemented'); + interfaceAddress) { + this._healthCheck(); + + if (!multicastAddress) { + throw new Error('multicast address must be specified'); + } + + return this._handle.dropMembership(multicastAddress, interfaceAddress); }; diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index aa5160cebbc..5b2fb52923c 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -91,6 +91,8 @@ public: static Handle RecvStart(const Arguments& args); static Handle RecvStop(const Arguments& args); static Handle GetSockName(const Arguments& args); + static Handle AddMembership(const Arguments& args); + static Handle DropMembership(const Arguments& args); private: static inline char* NewSlab(v8::Handle global, v8::Handle wrap_obj); @@ -100,6 +102,8 @@ private: static Handle DoBind(const Arguments& args, int family); static Handle DoSend(const Arguments& args, int family); + static Handle SetMembership(const Arguments& args, + uv_membership membership); static uv_buf_t OnAlloc(uv_handle_t* handle, size_t suggested_size); static void OnSend(uv_udp_send_t* req, int status); @@ -147,6 +151,8 @@ void UDPWrap::Initialize(Handle target) { NODE_SET_PROTOTYPE_METHOD(t, "recvStart", RecvStart); NODE_SET_PROTOTYPE_METHOD(t, "recvStop", RecvStop); NODE_SET_PROTOTYPE_METHOD(t, "getsockname", GetSockName); + NODE_SET_PROTOTYPE_METHOD(t, "addMembership", AddMembership); + NODE_SET_PROTOTYPE_METHOD(t, "dropMembership", DropMembership); target->Set(String::NewSymbol("UDP"), Persistent::New(t)->GetFunction()); @@ -204,6 +210,41 @@ Handle UDPWrap::Bind6(const Arguments& args) { } +Handle UDPWrap::SetMembership(const Arguments& args, + uv_membership membership) { + HandleScope scope; + UNWRAP + + assert(args.Length() == 2); + + String::Utf8Value address(args[0]->ToString()); + String::Utf8Value interface(args[1]->ToString()); + + const char* interface_cstr = *interface; + if (args[1]->IsUndefined() || args[1]->IsNull()) { + interface_cstr = NULL; + } + + int r = uv_udp_set_membership(&wrap->handle_, *address, interface_cstr, + membership); + + if (r) + SetErrno(uv_last_error(uv_default_loop())); + + return scope.Close(Integer::New(r)); +} + + +Handle UDPWrap::AddMembership(const Arguments& args) { + return SetMembership(args, UV_JOIN_GROUP); +} + + +Handle UDPWrap::DropMembership(const Arguments& args) { + return SetMembership(args, UV_LEAVE_GROUP); +} + + Handle UDPWrap::DoSend(const Arguments& args, int family) { HandleScope scope; int r; diff --git a/test/simple/test-dgram-multicast-multi-process.js b/test/simple/test-dgram-multicast-multi-process.js new file mode 100644 index 00000000000..784bb6c0943 --- /dev/null +++ b/test/simple/test-dgram-multicast-multi-process.js @@ -0,0 +1,160 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'), + assert = require('assert'), + cluster = require('cluster'), + dgram = require('dgram'), + util = require('util'), + assert = require('assert'), + Buffer = require('buffer').Buffer, + LOCAL_BROADCAST_HOST = '224.0.0.1', + messages = [ + new Buffer('First message to send'), + new Buffer('Second message to send'), + new Buffer('Third message to send'), + new Buffer('Fourth message to send') + ]; + +if (cluster.isMaster) { + var workers = {}, + listeners = 3, + listening = 0, + i = 0, + done = 0; + + //launch child processes + for (var x = 0; x < listeners; x++) { + (function () { + var worker = cluster.fork(); + workers[worker.pid] = worker; + + worker.messagesReceived = []; + + worker.on('message', function (msg) { + if (msg.listening) { + listening += 1; + + if (listening === listeners) { + //all child process are listening, so start sending + sendSocket.sendNext(); + } + } + else if (msg.message) { + worker.messagesReceived.push(msg.message); + + if (worker.messagesReceived.length === messages.length) { + done += 1; + console.error('%d received %d messages total.', worker.pid, + worker.messagesReceived.length); + } + + if (done === listeners) { + console.error('All workers have received the required number of' + + 'messages. Will now compare.'); + + Object.keys(workers).forEach(function (pid) { + var worker = workers[pid]; + + var count = 0; + + worker.messagesReceived.forEach(function(buf) { + for (var i = 0; i < messages.length; ++i) { + if (buf.toString() === messages[i].toString()) { + count++; + break; + } + } + }); + + console.error('%d received %d matching messges.', worker.pid + , count); + + assert.equal(count, messages.length + ,'A worker received an invalid multicast message'); + }); + } + } + }); + })(x); + } + + var sendSocket = dgram.createSocket('udp4'); + + //sendSocket.setBroadcast(true); + //sendSocket.setMulticastTTL(1); + //sendSocket.setMulticastLoopback(true); + + sendSocket.on('close', function() { + console.error('sendSocket closed'); + }); + + sendSocket.sendNext = function() { + var buf = messages[i++]; + + if (!buf) { + try { sendSocket.close(); } catch (e) {} + return; + } + + sendSocket.send(buf, 0, buf.length, + common.PORT, LOCAL_BROADCAST_HOST, function(err) { + if (err) throw err; + console.error('sent %s to %s', util.inspect(buf.toString()), + LOCAL_BROADCAST_HOST + common.PORT); + process.nextTick(sendSocket.sendNext); + }); + }; +} + +if (!cluster.isMaster) { + var receivedMessages = []; + var listenSocket = dgram.createSocket('udp4'); + + listenSocket.addMembership(LOCAL_BROADCAST_HOST); + + listenSocket.on('message', function(buf, rinfo) { + console.error('%s received %s from %j', process.pid + ,util.inspect(buf.toString()), rinfo); + + receivedMessages.push(buf); + + process.send({ message : buf.toString() }); + + if (receivedMessages.length == messages.length) { + listenSocket.dropMembership(LOCAL_BROADCAST_HOST); + process.nextTick(function() { // TODO should be changed to below. + // listenSocket.dropMembership(LOCAL_BROADCAST_HOST, function() { + listenSocket.close(); + }); + } + }); + + listenSocket.on('close', function() { + process.exit(); + }); + + listenSocket.on('listening', function() { + process.send({ listening : true }); + }); + + listenSocket.bind(common.PORT); +} From f749338e1eee390fc654f129f4419895db4f8e50 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 27 Oct 2011 10:48:38 -0400 Subject: [PATCH 22/30] dgram: reintroduce setBroadcast() Removed during the early stages of node 0.5 refactoring to libuv. --- lib/dgram.js | 6 +- src/udp_wrap.cc | 16 ++ .../test-dgram-broadcast-multi-process.js | 160 ++++++++++++++++++ 3 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 test/simple/test-dgram-broadcast-multi-process.js diff --git a/lib/dgram.js b/lib/dgram.js index 42473b40e77..d548ea2fb1c 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -223,7 +223,11 @@ Socket.prototype.address = function() { Socket.prototype.setBroadcast = function(arg) { - throw new Error('not yet implemented'); + if (this._handle.setBroadcast((arg) ? 1 : 0) == -1) { + throw errnoException(errno, 'setBroadcast'); + } + + return true; }; diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 5b2fb52923c..e73f634cde6 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -93,6 +93,7 @@ public: static Handle GetSockName(const Arguments& args); static Handle AddMembership(const Arguments& args); static Handle DropMembership(const Arguments& args); + static Handle SetBroadcast(const Arguments& args); private: static inline char* NewSlab(v8::Handle global, v8::Handle wrap_obj); @@ -153,6 +154,7 @@ void UDPWrap::Initialize(Handle target) { NODE_SET_PROTOTYPE_METHOD(t, "getsockname", GetSockName); NODE_SET_PROTOTYPE_METHOD(t, "addMembership", AddMembership); NODE_SET_PROTOTYPE_METHOD(t, "dropMembership", DropMembership); + NODE_SET_PROTOTYPE_METHOD(t, "setBroadcast", SetBroadcast); target->Set(String::NewSymbol("UDP"), Persistent::New(t)->GetFunction()); @@ -209,6 +211,20 @@ Handle UDPWrap::Bind6(const Arguments& args) { return DoBind(args, AF_INET6); } +Handle UDPWrap::SetBroadcast(const Arguments& args) { + HandleScope scope; + UNWRAP + + assert(args.Length() == 1); + + int on = args[0]->Uint32Value(); + int r = uv_udp_set_broadcast(&wrap->handle_, on); + + if (r) + SetErrno(uv_last_error(uv_default_loop())); + + return scope.Close(Integer::New(r)); +} Handle UDPWrap::SetMembership(const Arguments& args, uv_membership membership) { diff --git a/test/simple/test-dgram-broadcast-multi-process.js b/test/simple/test-dgram-broadcast-multi-process.js new file mode 100644 index 00000000000..d0a98aa1cf3 --- /dev/null +++ b/test/simple/test-dgram-broadcast-multi-process.js @@ -0,0 +1,160 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'), + assert = require('assert'), + cluster = require('cluster'), + dgram = require('dgram'), + util = require('util'), + assert = require('assert'), + Buffer = require('buffer').Buffer, + LOCAL_BROADCAST_HOST = '255.255.255.255', + messages = [ + new Buffer('First message to send'), + new Buffer('Second message to send'), + new Buffer('Third message to send'), + new Buffer('Fourth message to send') + ]; + +if (cluster.isMaster) { + var workers = {}, + listeners = 3, + listening = 0, + i = 0, + done = 0; + + //launch child processes + for (var x = 0; x < listeners; x++) { + (function () { + var worker = cluster.fork(); + workers[worker.pid] = worker; + + worker.messagesReceived = []; + + worker.on('message', function (msg) { + if (msg.listening) { + listening += 1; + + if (listening === listeners) { + //all child process are listening, so start sending + sendSocket.sendNext(); + } + } + else if (msg.message) { + worker.messagesReceived.push(msg.message); + + if (worker.messagesReceived.length === messages.length) { + done += 1; + console.error('%d received %d messages total.', worker.pid, + worker.messagesReceived.length); + } + + if (done === listeners) { + console.error('All workers have received the required number of ' + + 'messages. Will now compare.'); + + Object.keys(workers).forEach(function (pid) { + var worker = workers[pid]; + + var count = 0; + + worker.messagesReceived.forEach(function(buf) { + for (var i = 0; i < messages.length; ++i) { + if (buf.toString() === messages[i].toString()) { + count++; + break; + } + } + }); + + console.error('%d received %d matching messges.', worker.pid + , count); + + assert.equal(count, messages.length + ,'A worker received an invalid multicast message'); + }); + } + } + }); + })(x); + } + + var sendSocket = dgram.createSocket('udp4'); + + sendSocket.bind(common.PORT); + sendSocket.setBroadcast(true); + + sendSocket.on('close', function() { + console.error('sendSocket closed'); + }); + + sendSocket.sendNext = function() { + var buf = messages[i++]; + + if (!buf) { + try { sendSocket.close(); } catch (e) {} + return; + } + + sendSocket.send(buf, 0, buf.length, + common.PORT, LOCAL_BROADCAST_HOST, function(err) { + + if (err) throw err; + + console.error('sent %s to %s:%s', util.inspect(buf.toString()) + , LOCAL_BROADCAST_HOST, common.PORT); + + process.nextTick(sendSocket.sendNext); + }); + }; +} + +if (!cluster.isMaster) { + var receivedMessages = []; + var listenSocket = dgram.createSocket('udp4'); + + listenSocket.on('message', function(buf, rinfo) { + console.error('%s received %s from %j', process.pid + , util.inspect(buf.toString()), rinfo); + + receivedMessages.push(buf); + + process.send({ message : buf.toString() }); + + if (receivedMessages.length == messages.length) { + listenSocket.dropMembership(LOCAL_BROADCAST_HOST); + process.nextTick(function() { // TODO should be changed to below. + // listenSocket.dropMembership(LOCAL_BROADCAST_HOST, function() { + listenSocket.close(); + }); + } + }); + + listenSocket.on('close', function() { + process.exit(); + }); + + listenSocket.on('listening', function() { + process.send({ listening : true }); + }); + + listenSocket.bind(common.PORT); +} From f2b1f57f74ce23ee7160de76844a71ab34276aa6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 22 Nov 2011 16:04:40 -0500 Subject: [PATCH 23/30] dgram: reintroduce setMulticastTTL() Removed during the early stages of node 0.5 refactoring to libuv. --- lib/dgram.js | 6 +++- src/udp_wrap.cc | 16 +++++++++ test/simple/test-dgram-multicast-setTTL.js | 41 ++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 test/simple/test-dgram-multicast-setTTL.js diff --git a/lib/dgram.js b/lib/dgram.js index d548ea2fb1c..d5c2e0da69c 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -237,7 +237,11 @@ Socket.prototype.setTTL = function(arg) { Socket.prototype.setMulticastTTL = function(arg) { - throw new Error('not yet implemented'); + if (this._handle.setMulticastTTL(arg) == -1) { + throw errnoException(errno, 'setMulticastTTL'); + } + + return true; }; diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index e73f634cde6..e1be2d92da1 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -93,6 +93,7 @@ public: static Handle GetSockName(const Arguments& args); static Handle AddMembership(const Arguments& args); static Handle DropMembership(const Arguments& args); + static Handle SetMulticastTTL(const Arguments& args); static Handle SetBroadcast(const Arguments& args); private: @@ -154,6 +155,7 @@ void UDPWrap::Initialize(Handle target) { NODE_SET_PROTOTYPE_METHOD(t, "getsockname", GetSockName); NODE_SET_PROTOTYPE_METHOD(t, "addMembership", AddMembership); NODE_SET_PROTOTYPE_METHOD(t, "dropMembership", DropMembership); + NODE_SET_PROTOTYPE_METHOD(t, "setMulticastTTL", SetMulticastTTL); NODE_SET_PROTOTYPE_METHOD(t, "setBroadcast", SetBroadcast); target->Set(String::NewSymbol("UDP"), @@ -260,6 +262,20 @@ Handle UDPWrap::DropMembership(const Arguments& args) { return SetMembership(args, UV_LEAVE_GROUP); } +Handle UDPWrap::SetMulticastTTL(const Arguments& args) { + HandleScope scope; + UNWRAP + + assert(args.Length() == 1); + + int ttl = args[0]->Uint32Value(); + int r = uv_udp_set_multicast_ttl(&wrap->handle_, ttl); + + if (r) + SetErrno(uv_last_error(uv_default_loop())); + + return scope.Close(Integer::New(r)); +} Handle UDPWrap::DoSend(const Arguments& args, int family) { HandleScope scope; diff --git a/test/simple/test-dgram-multicast-setTTL.js b/test/simple/test-dgram-multicast-setTTL.js new file mode 100644 index 00000000000..a2207a78d00 --- /dev/null +++ b/test/simple/test-dgram-multicast-setTTL.js @@ -0,0 +1,41 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'), + assert = require('assert'), + dgram = require('dgram'), + thrown = false, + socket = dgram.createSocket('udp4'); + +socket.bind(common.PORT); +socket.setMulticastTTL(16); + +//Try to set an invalid TTL (valid ttl is > 0 and < 256) +try { + socket.setMulticastTTL(1000); +} catch (e) { + thrown = true; +} + +assert(thrown, 'Setting an invalid mutlicast TTL should throw some error'); + +//close the socket +socket.close(); \ No newline at end of file From 3df7c90c3059f49c81f9b8f59fc3abf18d5d392b Mon Sep 17 00:00:00 2001 From: koichik Date: Sun, 22 Jan 2012 14:55:41 +0900 Subject: [PATCH 24/30] http: keep-alive should default with HTTP/1.1 server As RFC 2616 says we should, assume that servers will provide a persistent connection by default. > A significant difference between HTTP/1.1 and earlier versions of > HTTP is that persistent connections are the default behavior of any > HTTP connection. That is, unless otherwise indicated, the client > SHOULD assume that the server will maintain a persistent connection, > even after error responses from the server. > HTTP/1.1 applications that do not support persistent connections MUST > include the "close" connection option in every message. Fixes #2436. --- lib/http.js | 2 +- test/simple/test-http-should-keep-alive.js | 71 ++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 test/simple/test-http-should-keep-alive.js diff --git a/lib/http.js b/lib/http.js index df891867610..a9aaac1c734 100644 --- a/lib/http.js +++ b/lib/http.js @@ -1231,7 +1231,7 @@ ClientRequest.prototype.onSocket = function(socket) { return true; } - if (req.shouldKeepAlive && res.headers.connection !== 'keep-alive' && !req.upgraded) { + if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgraded) { // Server MUST respond with Connection:keep-alive for us to enable it. // If we've been upgraded (via WebSockets) we also shouldn't try to // keep the connection open. diff --git a/test/simple/test-http-should-keep-alive.js b/test/simple/test-http-should-keep-alive.js new file mode 100644 index 00000000000..9e3dcf677e1 --- /dev/null +++ b/test/simple/test-http-should-keep-alive.js @@ -0,0 +1,71 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var http = require('http'); +var net = require('net'); + +var SERVER_RESPONSES = [ + 'HTTP/1.0 200 ok\r\nContent-Length: 0\r\n\r\n', + 'HTTP/1.0 200 ok\r\nContent-Length: 0\r\nConnection: keep-alive\r\n\r\n', + 'HTTP/1.0 200 ok\r\nContent-Length: 0\r\nConnection: close\r\n\r\n', + 'HTTP/1.1 200 ok\r\nContent-Length: 0\r\n\r\n', + 'HTTP/1.1 200 ok\r\nContent-Length: 0\r\nConnection: keep-alive\r\n\r\n', + 'HTTP/1.1 200 ok\r\nContent-Length: 0\r\nConnection: close\r\n\r\n', +]; +var SHOULD_KEEP_ALIVE = [ + false, // HTTP/1.0, default + true, // HTTP/1.0, Connection: keep-alive + false, // HTTP/1.0, Connection: close + true, // HTTP/1.1, default + true, // HTTP/1.1, Connection: keep-alive + false, // HTTP/1.1, Connection: close +]; +var requests = 0; +var responses = 0; + +var server = net.createServer(function(socket) { + socket.write(SERVER_RESPONSES[requests]); + ++requests; +}).listen(common.PORT, function() { + function makeRequest() { + var req = http.get({port: common.PORT}, function(res) { + assert.equal(req.shouldKeepAlive, SHOULD_KEEP_ALIVE[responses], + SERVER_RESPONSES[responses] + ' should ' + + (SHOULD_KEEP_ALIVE[responses] ? '' : 'not ') + + 'Keep-Alive'); + ++responses; + if (responses < SHOULD_KEEP_ALIVE.length) { + makeRequest(); + } else { + server.close(); + } + }); + } + + makeRequest(); +}); + +process.on('exit', function() { + assert.equal(requests, SERVER_RESPONSES.length); + assert.equal(responses, SHOULD_KEEP_ALIVE.length); +}); From f9014438c79b06d222df861f87e9af3cb919e495 Mon Sep 17 00:00:00 2001 From: Brandon Benvie Date: Mon, 14 Nov 2011 15:42:14 -0500 Subject: [PATCH 25/30] util: use getOwnPropertyDescripter Change formatProperty in util.js to use Object.getOwnPropertyDescriptor instead of __lookup[GS]etter__. Use the cached value from the descriptor to reduce number of property lookups from 3 to 1. Fallback to standard lookup if the descriptor is empty. This doesn't ever happen with normal JS objects (this function is called only when the key exists) but apparently does with Node's custom ENV interface. Fixes: #2109. --- lib/util.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/util.js b/lib/util.js index cd4cc0e9645..6e410d98aef 100644 --- a/lib/util.js +++ b/lib/util.js @@ -296,29 +296,28 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { - var name, str; - if (value.__lookupGetter__) { - if (value.__lookupGetter__(key)) { - if (value.__lookupSetter__(key)) { - str = ctx.stylize('[Getter/Setter]', 'special'); - } else { - str = ctx.stylize('[Getter]', 'special'); - } + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); } else { - if (value.__lookupSetter__(key)) { - str = ctx.stylize('[Setter]', 'special'); - } + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); } } if (visibleKeys.indexOf(key) < 0) { name = '[' + key + ']'; } if (!str) { - if (ctx.seen.indexOf(value[key]) < 0) { + if (ctx.seen.indexOf(desc.value) < 0) { if (recurseTimes === null) { - str = formatValue(ctx, value[key], null); + str = formatValue(ctx, desc.value, null); } else { - str = formatValue(ctx, value[key], recurseTimes - 1); + str = formatValue(ctx, desc.value, recurseTimes - 1); } if (str.indexOf('\n') > -1) { if (array) { From 93298afc4e2835e5b526ab871922018999e98bcb Mon Sep 17 00:00:00 2001 From: koichik Date: Sun, 22 Jan 2012 17:35:28 +0900 Subject: [PATCH 26/30] test: for #2109 --- test/simple/test-util-inspect.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/simple/test-util-inspect.js b/test/simple/test-util-inspect.js index d866b41dd2e..f21123e82f1 100644 --- a/test/simple/test-util-inspect.js +++ b/test/simple/test-util-inspect.js @@ -42,6 +42,27 @@ assert.equal(util.inspect(a), "[ 'foo', , 'baz' ]"); assert.equal(util.inspect(a, true), "[ 'foo', , 'baz', [length]: 3 ]"); assert.equal(util.inspect(new Array(5)), '[ , , , , ]'); +// test for property descriptors +var getter = Object.create(null, { + a: { + get: function() { return 'aaa'; } + } +}); +var setter = Object.create(null, { + b: { + set: function() {} + }, +}); +var getterAndSetter = Object.create(null, { + c: { + get: function() { return 'ccc'; }, + set: function() {} + } +}); +assert.equal(util.inspect(getter, true), "{ [a]: [Getter] }"); +assert.equal(util.inspect(setter, true), "{ [b]: [Setter] }"); +assert.equal(util.inspect(getterAndSetter, true), "{ [c]: [Getter/Setter] }"); + // exceptions should print the error message, not "{}" assert.equal(util.inspect(new Error()), '[Error]'); assert.equal(util.inspect(new Error('FAIL')), '[Error: FAIL]'); From 827180097c45d2ce84d798ca141bb3a170a20df9 Mon Sep 17 00:00:00 2001 From: koichik Date: Sun, 22 Jan 2012 23:23:04 +0900 Subject: [PATCH 27/30] test: fix test/simple/test-net-server-max-connections.js is racey Fixes #1333. --- .../simple/test-net-server-max-connections.js | 82 ++++++++++--------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/test/simple/test-net-server-max-connections.js b/test/simple/test-net-server-max-connections.js index f45c1e53d3f..43bc2fee17e 100644 --- a/test/simple/test-net-server-max-connections.js +++ b/test/simple/test-net-server-max-connections.js @@ -42,9 +42,7 @@ var server = net.createServer(function(connection) { }); server.listen(common.PORT, function() { - for (var i = 0; i < N; i++) { - makeConnection(i); - } + makeConnection(0); }); server.maxConnections = N / 2; @@ -53,50 +51,54 @@ console.error('server.maxConnections = %d', server.maxConnections); function makeConnection(index) { - setTimeout(function() { - var c = net.createConnection(common.PORT); - var gotData = false; + var c = net.createConnection(common.PORT); + var gotData = false; - c.on('end', function() { c.end(); }); + c.on('connect', function() { + if (index + 1 < N) { + makeConnection(index + 1); + } + }); - c.on('data', function(b) { - gotData = true; - assert.ok(0 < b.length); - }); + c.on('end', function() { c.end(); }); - c.on('error', function(e) { - console.error('error %d: %s', index, e); - }); + c.on('data', function(b) { + gotData = true; + assert.ok(0 < b.length); + }); - c.on('close', function() { - console.error('closed %d', index); - closes++; + c.on('error', function(e) { + console.error('error %d: %s', index, e); + }); - if (closes < N / 2) { - assert.ok(server.maxConnections <= index, - index + - ' was one of the first closed connections ' + - 'but shouldnt have been'); + c.on('close', function() { + console.error('closed %d', index); + closes++; + + if (closes < N / 2) { + assert.ok(server.maxConnections <= index, + index + + ' was one of the first closed connections ' + + 'but shouldnt have been'); + } + + if (closes === N / 2) { + var cb; + console.error('calling wait callback.'); + while (cb = waits.shift()) { + cb(); } + server.close(); + } - if (closes === N / 2) { - var cb; - console.error('calling wait callback.'); - while (cb = waits.shift()) { - cb(); - } - server.close(); - } - - if (index < server.maxConnections) { - assert.equal(true, gotData, - index + ' didn\'t get data, but should have'); - } else { - assert.equal(false, gotData, - index + ' got data, but shouldn\'t have'); - } - }); - }, index); + if (index < server.maxConnections) { + assert.equal(true, gotData, + index + ' didn\'t get data, but should have'); + } else { + assert.equal(false, gotData, + index + ' got data, but shouldn\'t have'); + } + }); } From 9edb9842742c0734a9063576ae283c86b90d0676 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 22 Jan 2012 18:19:10 +0100 Subject: [PATCH 28/30] crypto: silence unused variable warning `retry` is not used if SSL_PRINT_DEBUG is not defined. --- src/node_crypto.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 9f73df35caf..5274120e2cb 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -607,6 +607,7 @@ int Connection::HandleBIOError(BIO *bio, const char* func, int rv) { if (rv >= 0) return rv; int retry = BIO_should_retry(bio); + (void) retry; // unused if !defined(SSL_PRINT_DEBUG) if (BIO_should_write(bio)) { DEBUG_PRINT("[%p] BIO: %s want write. should retry %d\n", ssl_, func, retry); From f5e742b26445c82177e786b71480d66f7eef9ebd Mon Sep 17 00:00:00 2001 From: Igor Zinkovsky Date: Mon, 23 Jan 2012 01:34:05 -0800 Subject: [PATCH 29/30] upgrade libuv to edbabe6f83 --- deps/uv/src/win/udp.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/deps/uv/src/win/udp.c b/deps/uv/src/win/udp.c index 0299e775421..8a36396b473 100644 --- a/deps/uv/src/win/udp.c +++ b/deps/uv/src/win/udp.c @@ -578,3 +578,24 @@ void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle, DECREASE_PENDING_REQ_COUNT(handle); } + +int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) { + if (setsockopt(handle->socket, IPPROTO_IP, IP_MULTICAST_TTL, + (const char*)&ttl, sizeof ttl) == -1) { + uv__set_sys_error(handle->loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +int uv_udp_set_broadcast(uv_udp_t* handle, int on) { + if (setsockopt(handle->socket, SOL_SOCKET, SO_BROADCAST, (const char*)&on, + sizeof on) == -1) { + uv__set_sys_error(handle->loop, WSAGetLastError()); + return -1; + } + + return 0; +} From 6c0c00a2052ec613503a0322dcbabb6fcf41dab0 Mon Sep 17 00:00:00 2001 From: Igor Zinkovsky Date: Mon, 23 Jan 2012 01:30:56 -0800 Subject: [PATCH 30/30] fix windows build --- src/node.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/node.h b/src/node.h index 38d3c621bfa..05e3f7535f4 100644 --- a/src/node.h +++ b/src/node.h @@ -66,6 +66,10 @@ #include +#if defined(_MSC_VER) +#undef interface +#endif + #ifndef offset_of // g++ in strict mode complains loudly about the system offsetof() macro // because it uses NULL as the base address.