From 4129305b7cdfb6df9695225e62714ab6e80fccd3 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 27 Nov 2009 22:53:39 +0100 Subject: [PATCH] Upgrade v8 to 2.0.2 --- deps/v8/ChangeLog | 15 +++ deps/v8/src/allocation.h | 2 +- deps/v8/src/arm/codegen-arm.cc | 45 +++++++++ deps/v8/src/arm/codegen-arm.h | 2 + deps/v8/src/builtins.cc | 6 ++ deps/v8/src/codegen.cc | 4 +- deps/v8/src/flag-definitions.h | 2 +- deps/v8/src/heap.cc | 52 ++++++++++ deps/v8/src/heap.h | 1 + deps/v8/src/ia32/codegen-ia32.cc | 49 ++++++++++ deps/v8/src/ia32/codegen-ia32.h | 2 + deps/v8/src/log-inl.h | 2 +- deps/v8/src/log.cc | 49 +++++++++- deps/v8/src/log.h | 11 ++- deps/v8/src/macros.py | 5 +- deps/v8/src/objects-inl.h | 15 ++- deps/v8/src/objects.cc | 94 ++++++++++++++++++- deps/v8/src/objects.h | 2 + deps/v8/src/runtime.cc | 28 ++++-- deps/v8/src/serialize.cc | 8 +- deps/v8/src/v8.cc | 2 +- deps/v8/src/version.cc | 2 +- deps/v8/src/x64/codegen-x64.cc | 42 +++++++++ deps/v8/src/x64/codegen-x64.h | 2 + deps/v8/test/cctest/test-log.cc | 66 ++++++++++++- .../v8/test/cctest/test-thread-termination.cc | 45 ++++++++- deps/v8/test/mjsunit/regress/regress-515.js | 40 ++++++++ deps/v8/tools/gyp/v8.gyp | 1 + 28 files changed, 553 insertions(+), 41 deletions(-) create mode 100644 deps/v8/test/mjsunit/regress/regress-515.js diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index 668cd9a920..bf2c244b41 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,3 +1,18 @@ +2009-11-24: Version 2.0.2 + + Improved profiler support. + + Fixed bug that broke compilation of d8 with readline support. + + +2009-11-20: Version 2.0.1 + + Fixed crash bug in String.prototype.replace. + + Reverted a change which caused Chromium interactive ui test + failures. + + 2009-11-18: Version 2.0.0 Added support for VFP on ARM. diff --git a/deps/v8/src/allocation.h b/deps/v8/src/allocation.h index 586c4fd0d1..70a3a03889 100644 --- a/deps/v8/src/allocation.h +++ b/deps/v8/src/allocation.h @@ -124,7 +124,7 @@ static void DeleteArray(T* array) { // and StrNDup uses new and calls the FatalProcessOutOfMemory handler // if allocation fails. char* StrDup(const char* str); -char* StrNDup(const char* str, size_t n); +char* StrNDup(const char* str, int n); // Allocation policy for allocating in the C free store using malloc diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc index b08615e613..c62756d5a5 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -3385,6 +3385,51 @@ void CodeGenerator::GenerateIsArray(ZoneList* args) { } +void CodeGenerator::GenerateIsObject(ZoneList* args) { + // This generates a fast version of: + // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp') + VirtualFrame::SpilledScope spilled_scope; + ASSERT(args->length() == 1); + LoadAndSpill(args->at(0)); + frame_->EmitPop(r1); + __ tst(r1, Operand(kSmiTagMask)); + false_target()->Branch(eq); + + __ LoadRoot(ip, Heap::kNullValueRootIndex); + __ cmp(r1, ip); + true_target()->Branch(eq); + + Register map_reg = r2; + __ ldr(map_reg, FieldMemOperand(r1, HeapObject::kMapOffset)); + // Undetectable objects behave like undefined when tested with typeof. + __ ldrb(r1, FieldMemOperand(map_reg, Map::kBitFieldOffset)); + __ and_(r1, r1, Operand(1 << Map::kIsUndetectable)); + __ cmp(r1, Operand(1 << Map::kIsUndetectable)); + false_target()->Branch(eq); + + __ ldrb(r1, FieldMemOperand(map_reg, Map::kInstanceTypeOffset)); + __ cmp(r1, Operand(FIRST_JS_OBJECT_TYPE)); + false_target()->Branch(lt); + __ cmp(r1, Operand(LAST_JS_OBJECT_TYPE)); + cc_reg_ = le; +} + + +void CodeGenerator::GenerateIsFunction(ZoneList* args) { + // This generates a fast version of: + // (%_ClassOf(arg) === 'Function') + VirtualFrame::SpilledScope spilled_scope; + ASSERT(args->length() == 1); + LoadAndSpill(args->at(0)); + frame_->EmitPop(r0); + __ tst(r0, Operand(kSmiTagMask)); + false_target()->Branch(eq); + Register map_reg = r2; + __ CompareObjectType(r0, map_reg, r1, JS_FUNCTION_TYPE); + cc_reg_ = eq; +} + + void CodeGenerator::GenerateIsConstructCall(ZoneList* args) { VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 0); diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h index 8cbf450f32..30a1ae5729 100644 --- a/deps/v8/src/arm/codegen-arm.h +++ b/deps/v8/src/arm/codegen-arm.h @@ -334,6 +334,8 @@ class CodeGenerator: public AstVisitor { void GenerateIsSmi(ZoneList* args); void GenerateIsNonNegativeSmi(ZoneList* args); void GenerateIsArray(ZoneList* args); + void GenerateIsObject(ZoneList* args); + void GenerateIsFunction(ZoneList* args); // Support for construct call checks. void GenerateIsConstructCall(ZoneList* args); diff --git a/deps/v8/src/builtins.cc b/deps/v8/src/builtins.cc index fa1b34e655..b66635c500 100644 --- a/deps/v8/src/builtins.cc +++ b/deps/v8/src/builtins.cc @@ -380,6 +380,9 @@ BUILTIN(HandleApiCall) { { // Leaving JavaScript. VMState state(EXTERNAL); +#ifdef ENABLE_LOGGING_AND_PROFILING + state.set_external_callback(v8::ToCData
(callback_obj)); +#endif value = callback(new_args); } if (value.IsEmpty()) { @@ -446,6 +449,9 @@ static Object* HandleApiCallAsFunctionOrConstructor(bool is_construct_call, { // Leaving JavaScript. VMState state(EXTERNAL); +#ifdef ENABLE_LOGGING_AND_PROFILING + state.set_external_callback(v8::ToCData
(callback_obj)); +#endif value = callback(new_args); } if (value.IsEmpty()) { diff --git a/deps/v8/src/codegen.cc b/deps/v8/src/codegen.cc index 6917d459c7..a6d5fb47f9 100644 --- a/deps/v8/src/codegen.cc +++ b/deps/v8/src/codegen.cc @@ -343,7 +343,9 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = { {&CodeGenerator::GenerateLog, "_Log"}, {&CodeGenerator::GenerateRandomPositiveSmi, "_RandomPositiveSmi"}, {&CodeGenerator::GenerateMathSin, "_Math_sin"}, - {&CodeGenerator::GenerateMathCos, "_Math_cos"} + {&CodeGenerator::GenerateMathCos, "_Math_cos"}, + {&CodeGenerator::GenerateIsObject, "_IsObject"}, + {&CodeGenerator::GenerateIsFunction, "_IsFunction"}, }; diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index 975350353e..8c9bb22dcd 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -143,7 +143,7 @@ DEFINE_bool(debug_info, true, "add debug information to compiled functions") DEFINE_bool(strict, false, "strict error checking") DEFINE_int(min_preparse_length, 1024, "minimum length for automatic enable preparsing") -DEFINE_bool(fast_compiler, true, +DEFINE_bool(fast_compiler, false, "use the fast-mode compiler for some top-level code") DEFINE_bool(trace_bailout, false, "print reasons for failing to use fast compilation") diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index 43886c144c..06dc59cdd8 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -1762,6 +1762,41 @@ Object* Heap::AllocateSharedFunctionInfo(Object* name) { } +// Returns true for a character in a range. Both limits are inclusive. +static inline bool Between(uint32_t character, uint32_t from, uint32_t to) { + // This makes uses of the the unsigned wraparound. + return character - from <= to - from; +} + + +static inline Object* MakeOrFindTwoCharacterString(uint32_t c1, uint32_t c2) { + String* symbol; + // Numeric strings have a different hash algorithm not known by + // LookupTwoCharsSymbolIfExists, so we skip this step for such strings. + if ((!Between(c1, '0', '9') || !Between(c2, '0', '9')) && + Heap::symbol_table()->LookupTwoCharsSymbolIfExists(c1, c2, &symbol)) { + return symbol; + // Now we know the length is 2, we might as well make use of that fact + // when building the new string. + } else if ((c1 | c2) <= String::kMaxAsciiCharCodeU) { // We can do this + ASSERT(IsPowerOf2(String::kMaxAsciiCharCodeU + 1)); // because of this. + Object* result = Heap::AllocateRawAsciiString(2); + if (result->IsFailure()) return result; + char* dest = SeqAsciiString::cast(result)->GetChars(); + dest[0] = c1; + dest[1] = c2; + return result; + } else { + Object* result = Heap::AllocateRawTwoByteString(2); + if (result->IsFailure()) return result; + uc16* dest = SeqTwoByteString::cast(result)->GetChars(); + dest[0] = c1; + dest[1] = c2; + return result; + } +} + + Object* Heap::AllocateConsString(String* first, String* second) { int first_length = first->length(); if (first_length == 0) { @@ -1774,6 +1809,16 @@ Object* Heap::AllocateConsString(String* first, String* second) { } int length = first_length + second_length; + + // Optimization for 2-byte strings often used as keys in a decompression + // dictionary. Check whether we already have the string in the symbol + // table to prevent creation of many unneccesary strings. + if (length == 2) { + unsigned c1 = first->Get(0); + unsigned c2 = second->Get(0); + return MakeOrFindTwoCharacterString(c1, c2); + } + bool is_ascii = first->IsAsciiRepresentation() && second->IsAsciiRepresentation(); @@ -1843,6 +1888,13 @@ Object* Heap::AllocateSubString(String* buffer, if (length == 1) { return Heap::LookupSingleCharacterStringFromCode( buffer->Get(start)); + } else if (length == 2) { + // Optimization for 2-byte strings often used as keys in a decompression + // dictionary. Check whether we already have the string in the symbol + // table to prevent creation of many unneccesary strings. + unsigned c1 = buffer->Get(start); + unsigned c2 = buffer->Get(start + 1); + return MakeOrFindTwoCharacterString(c1, c2); } // Make an attempt to flatten the buffer to reduce access time. diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index 8c1bb1887b..90d714c7fb 100644 --- a/deps/v8/src/heap.h +++ b/deps/v8/src/heap.h @@ -631,6 +631,7 @@ class Heap : public AllStatic { } static Object* LookupSymbol(String* str); static bool LookupSymbolIfExists(String* str, String** symbol); + static bool LookupTwoCharsSymbolIfExists(String* str, String** symbol); // Compute the matching symbol map for a string if possible. // NULL is returned if string is in new space or not flattened. diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc index 69a17cd9b5..ac2a7a0262 100644 --- a/deps/v8/src/ia32/codegen-ia32.cc +++ b/deps/v8/src/ia32/codegen-ia32.cc @@ -4870,6 +4870,55 @@ void CodeGenerator::GenerateIsArray(ZoneList* args) { } +void CodeGenerator::GenerateIsObject(ZoneList* args) { + // This generates a fast version of: + // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp') + ASSERT(args->length() == 1); + Load(args->at(0)); + Result obj = frame_->Pop(); + obj.ToRegister(); + + __ test(obj.reg(), Immediate(kSmiTagMask)); + destination()->false_target()->Branch(zero); + __ cmp(obj.reg(), Factory::null_value()); + destination()->true_target()->Branch(equal); + + Result map = allocator()->Allocate(); + ASSERT(map.is_valid()); + __ mov(map.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset)); + // Undetectable objects behave like undefined when tested with typeof. + __ movzx_b(map.reg(), FieldOperand(map.reg(), Map::kBitFieldOffset)); + __ test(map.reg(), Immediate(1 << Map::kIsUndetectable)); + destination()->false_target()->Branch(not_zero); + __ mov(map.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset)); + __ movzx_b(map.reg(), FieldOperand(map.reg(), Map::kInstanceTypeOffset)); + __ cmp(map.reg(), FIRST_JS_OBJECT_TYPE); + destination()->false_target()->Branch(less); + __ cmp(map.reg(), LAST_JS_OBJECT_TYPE); + obj.Unuse(); + map.Unuse(); + destination()->Split(less_equal); +} + + +void CodeGenerator::GenerateIsFunction(ZoneList* args) { + // This generates a fast version of: + // (%_ClassOf(arg) === 'Function') + ASSERT(args->length() == 1); + Load(args->at(0)); + Result obj = frame_->Pop(); + obj.ToRegister(); + __ test(obj.reg(), Immediate(kSmiTagMask)); + destination()->false_target()->Branch(zero); + Result temp = allocator()->Allocate(); + ASSERT(temp.is_valid()); + __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, temp.reg()); + obj.Unuse(); + temp.Unuse(); + destination()->Split(equal); +} + + void CodeGenerator::GenerateIsConstructCall(ZoneList* args) { ASSERT(args->length() == 0); diff --git a/deps/v8/src/ia32/codegen-ia32.h b/deps/v8/src/ia32/codegen-ia32.h index 0e69a63d89..ebab3caa12 100644 --- a/deps/v8/src/ia32/codegen-ia32.h +++ b/deps/v8/src/ia32/codegen-ia32.h @@ -512,6 +512,8 @@ class CodeGenerator: public AstVisitor { void GenerateIsSmi(ZoneList* args); void GenerateIsNonNegativeSmi(ZoneList* args); void GenerateIsArray(ZoneList* args); + void GenerateIsObject(ZoneList* args); + void GenerateIsFunction(ZoneList* args); // Support for construct call checks. void GenerateIsConstructCall(ZoneList* args); diff --git a/deps/v8/src/log-inl.h b/deps/v8/src/log-inl.h index 1844d2bf72..1500252a5c 100644 --- a/deps/v8/src/log-inl.h +++ b/deps/v8/src/log-inl.h @@ -55,7 +55,7 @@ inline const char* StateToString(StateTag state) { } } -VMState::VMState(StateTag state) : disabled_(true) { +VMState::VMState(StateTag state) : disabled_(true), external_callback_(NULL) { if (!Logger::is_logging()) { return; } diff --git a/deps/v8/src/log.cc b/deps/v8/src/log.cc index 9acb7f7857..aec813d97b 100644 --- a/deps/v8/src/log.cc +++ b/deps/v8/src/log.cc @@ -30,6 +30,7 @@ #include "v8.h" #include "bootstrapper.h" +#include "global-handles.h" #include "log.h" #include "macro-assembler.h" #include "serialize.h" @@ -154,12 +155,18 @@ void StackTracer::Trace(TickSample* sample) { return; } + int i = 0; + const Address callback = Logger::current_state_ != NULL ? + Logger::current_state_->external_callback() : NULL; + if (callback != NULL) { + sample->stack[i++] = callback; + } + SafeStackTraceFrameIterator it( reinterpret_cast
(sample->fp), reinterpret_cast
(sample->sp), reinterpret_cast
(sample->sp), js_entry_sp); - int i = 0; while (!it.done() && i < TickSample::kMaxFramesCount) { sample->stack[i++] = it.frame()->pc(); it.Advance(); @@ -673,6 +680,26 @@ class CompressionHelper { #endif // ENABLE_LOGGING_AND_PROFILING +void Logger::CallbackEvent(String* name, Address entry_point) { +#ifdef ENABLE_LOGGING_AND_PROFILING + if (!Log::IsEnabled() || !FLAG_log_code) return; + LogMessageBuilder msg; + msg.Append("%s,%s,", + log_events_[CODE_CREATION_EVENT], log_events_[CALLBACK_TAG]); + msg.AppendAddress(entry_point); + SmartPointer str = + name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); + msg.Append(",1,\"%s\"", *str); + if (FLAG_compress_log) { + ASSERT(compression_helper_ != NULL); + if (!compression_helper_->HandleMessage(&msg)) return; + } + msg.Append('\n'); + msg.WriteToLogFile(); +#endif +} + + void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, const char* comment) { @@ -1191,11 +1218,25 @@ void Logger::LogCompiledFunctions() { LOG(CodeCreateEvent(Logger::SCRIPT_TAG, shared->code(), *script_name)); } - continue; + } else { + LOG(CodeCreateEvent( + Logger::LAZY_COMPILE_TAG, shared->code(), *func_name)); } + } else if (shared->function_data()->IsFunctionTemplateInfo()) { + // API function. + FunctionTemplateInfo* fun_data = + FunctionTemplateInfo::cast(shared->function_data()); + Object* raw_call_data = fun_data->call_code(); + if (!raw_call_data->IsUndefined()) { + CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data); + Object* callback_obj = call_data->callback(); + Address entry_point = v8::ToCData
(callback_obj); + LOG(CallbackEvent(*func_name, entry_point)); + } + } else { + LOG(CodeCreateEvent( + Logger::LAZY_COMPILE_TAG, shared->code(), *func_name)); } - // If no script or script has no name. - LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG, shared->code(), *func_name)); } DeleteArray(sfis); diff --git a/deps/v8/src/log.h b/deps/v8/src/log.h index e7931ca42d..f099f02b64 100644 --- a/deps/v8/src/log.h +++ b/deps/v8/src/log.h @@ -91,15 +91,20 @@ class CompressionHelper; class VMState BASE_EMBEDDED { #ifdef ENABLE_LOGGING_AND_PROFILING public: - inline explicit VMState(StateTag state); + inline VMState(StateTag state); inline ~VMState(); StateTag state() { return state_; } + Address external_callback() { return external_callback_; } + void set_external_callback(Address external_callback) { + external_callback_ = external_callback; + } private: bool disabled_; StateTag state_; VMState* previous_; + Address external_callback_; #else public: explicit VMState(StateTag state) {} @@ -122,6 +127,7 @@ class VMState BASE_EMBEDDED { V(CALL_MISS_TAG, "CallMiss", "cm") \ V(CALL_NORMAL_TAG, "CallNormal", "cn") \ V(CALL_PRE_MONOMORPHIC_TAG, "CallPreMonomorphic", "cpm") \ + V(CALLBACK_TAG, "Callback", "cb") \ V(EVAL_TAG, "Eval", "e") \ V(FUNCTION_TAG, "Function", "f") \ V(KEYED_LOAD_IC_TAG, "KeyedLoadIC", "klic") \ @@ -200,6 +206,8 @@ class Logger { // ==== Events logged by --log-code. ==== + // Emits a code event for a callback function. + static void CallbackEvent(String* name, Address entry_point); // Emits a code create event. static void CodeCreateEvent(LogEventsAndTags tag, Code* code, const char* source); @@ -330,6 +338,7 @@ class Logger { friend class TimeLog; friend class Profiler; friend class SlidingStateWindow; + friend class StackTracer; friend class VMState; friend class LoggerTestHelper; diff --git a/deps/v8/src/macros.py b/deps/v8/src/macros.py index d6a2426c5a..5b06099a85 100644 --- a/deps/v8/src/macros.py +++ b/deps/v8/src/macros.py @@ -79,11 +79,10 @@ macro IS_NULL_OR_UNDEFINED(arg) = (arg == null); macro IS_UNDEFINED(arg) = (typeof(arg) === 'undefined'); macro IS_NUMBER(arg) = (typeof(arg) === 'number'); macro IS_STRING(arg) = (typeof(arg) === 'string'); -macro IS_OBJECT(arg) = (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp'); macro IS_BOOLEAN(arg) = (typeof(arg) === 'boolean'); +macro IS_OBJECT(arg) = (%_IsObject(arg)); macro IS_ARRAY(arg) = (%_IsArray(arg)); -# IS_FUNCTION uses %_ClassOf rather than typeof so as to exclude regexps. -macro IS_FUNCTION(arg) = (%_ClassOf(arg) === 'Function'); +macro IS_FUNCTION(arg) = (%_IsFunction(arg)); macro IS_REGEXP(arg) = (%_ClassOf(arg) === 'RegExp'); macro IS_DATE(arg) = (%_ClassOf(arg) === 'Date'); macro IS_NUMBER_WRAPPER(arg) = (%_ClassOf(arg) === 'Number'); diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index 507a3ab6b1..7a6444de52 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -3103,8 +3103,19 @@ void Map::ClearCodeCache() { void JSArray::EnsureSize(int required_size) { ASSERT(HasFastElements()); - if (elements()->length() >= required_size) return; - Expand(required_size); + Array* elts = elements(); + const int kArraySizeThatFitsComfortablyInNewSpace = 128; + if (elts->length() < required_size) { + // Doubling in size would be overkill, but leave some slack to avoid + // constantly growing. + Expand(required_size + (required_size >> 3)); + // It's a performance benefit to keep a frequently used array in new-space. + } else if (!Heap::new_space()->Contains(elts) && + required_size < kArraySizeThatFitsComfortablyInNewSpace) { + // Expand will allocate a new backing store in new space even if the size + // we asked for isn't larger than what we had before. + Expand(required_size); + } } diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 5ccacb7860..f37658551f 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -1463,8 +1463,8 @@ Object* JSObject::SetPropertyPostInterceptor(String* name, Object* JSObject::ReplaceSlowProperty(String* name, - Object* value, - PropertyAttributes attributes) { + Object* value, + PropertyAttributes attributes) { StringDictionary* dictionary = property_dictionary(); int old_index = dictionary->FindEntry(name); int new_enumeration_index = 0; // 0 means "Use the next available index." @@ -1478,6 +1478,7 @@ Object* JSObject::ReplaceSlowProperty(String* name, return SetNormalizedProperty(name, value, new_details); } + Object* JSObject::ConvertDescriptorToFieldAndMapTransition( String* name, Object* new_value, @@ -1869,6 +1870,14 @@ Object* JSObject::SetProperty(LookupResult* result, // interceptor calls. AssertNoContextChange ncc; + // Optimization for 2-byte strings often used as keys in a decompression + // dictionary. We make these short keys into symbols to avoid constantly + // reallocating them. + if (!name->IsSymbol() && name->length() <= 2) { + Object* symbol_version = Heap::LookupSymbol(name); + if (!symbol_version->IsFailure()) name = String::cast(symbol_version); + } + // Check access rights if needed. if (IsAccessCheckNeeded() && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) { @@ -5240,9 +5249,7 @@ void JSArray::Expand(int required_size) { Handle self(this); Handle old_backing(FixedArray::cast(elements())); int old_size = old_backing->length(); - // Doubling in size would be overkill, but leave some slack to avoid - // constantly growing. - int new_size = required_size + (required_size >> 3); + int new_size = required_size > old_size ? required_size : old_size; Handle new_backing = Factory::NewFixedArray(new_size); // Can't use this any more now because we may have had a GC! for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i)); @@ -7327,6 +7334,67 @@ Object* SymbolTable::LookupString(String* string, Object** s) { } +// This class is used for looking up two character strings in the symbol table. +// If we don't have a hit we don't want to waste much time so we unroll the +// string hash calculation loop here for speed. Doesn't work if the two +// characters form a decimal integer, since such strings have a different hash +// algorithm. +class TwoCharHashTableKey : public HashTableKey { + public: + TwoCharHashTableKey(uint32_t c1, uint32_t c2) + : c1_(c1), c2_(c2) { + // Char 1. + uint32_t hash = c1 + (c1 << 10); + hash ^= hash >> 6; + // Char 2. + hash += c2; + hash += hash << 10; + hash ^= hash >> 6; + // GetHash. + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + if (hash == 0) hash = 27; +#ifdef DEBUG + StringHasher hasher(2); + hasher.AddCharacter(c1); + hasher.AddCharacter(c2); + // If this assert fails then we failed to reproduce the two-character + // version of the string hashing algorithm above. One reason could be + // that we were passed two digits as characters, since the hash + // algorithm is different in that case. + ASSERT_EQ(static_cast(hasher.GetHash()), static_cast(hash)); +#endif + hash_ = hash; + } + + bool IsMatch(Object* o) { + if (!o->IsString()) return false; + String* other = String::cast(o); + if (other->length() != 2) return false; + if (other->Get(0) != c1_) return false; + return other->Get(1) == c2_; + } + + uint32_t Hash() { return hash_; } + uint32_t HashForObject(Object* key) { + if (!key->IsString()) return 0; + return String::cast(key)->Hash(); + } + + Object* AsObject() { + // The TwoCharHashTableKey is only used for looking in the symbol + // table, not for adding to it. + UNREACHABLE(); + return NULL; + } + private: + uint32_t c1_; + uint32_t c2_; + uint32_t hash_; +}; + + bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) { SymbolKey key(string); int entry = FindEntry(&key); @@ -7341,6 +7409,22 @@ bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) { } +bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1, + uint32_t c2, + String** symbol) { + TwoCharHashTableKey key(c1, c2); + int entry = FindEntry(&key); + if (entry == kNotFound) { + return false; + } else { + String* result = String::cast(KeyAt(entry)); + ASSERT(StringShape(result).IsSymbol()); + *symbol = result; + return true; + } +} + + Object* SymbolTable::LookupSymbol(Vector str, Object** s) { Utf8SymbolKey key(str); return LookupKey(&key, s); diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 6ea0a820c7..d22e7231fa 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -2188,6 +2188,7 @@ class SymbolTable: public HashTable { // true if it is found, assigning the symbol to the given output // parameter. bool LookupSymbolIfExists(String* str, String** symbol); + bool LookupTwoCharsSymbolIfExists(uint32_t c1, uint32_t c2, String** symbol); // Casting. static inline SymbolTable* cast(Object* obj); @@ -3846,6 +3847,7 @@ class StringHasher { bool is_array_index_; bool is_first_char_; bool is_valid_; + friend class TwoCharHashTableKey; }; diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index ccb88851b8..6ae22330a0 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -1750,10 +1750,10 @@ static Object* StringReplaceRegExpWithString(String* subject, // Index of end of last match. int prev = 0; - // Number of parts added by compiled replacement plus preceeding string - // and possibly suffix after last match. It is possible for compiled - // replacements to use two elements when encoded as two smis. - const int parts_added_per_loop = compiled_replacement.parts() * 2 + 2; + // Number of parts added by compiled replacement plus preceeding + // string and possibly suffix after last match. It is possible for + // all components to use two elements when encoded as two smis. + const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2); bool matched = true; do { ASSERT(last_match_info_handle->HasFastElements()); @@ -2356,12 +2356,20 @@ static Object* Runtime_SubString(Arguments args) { ASSERT(args.length() == 3); CONVERT_CHECKED(String, value, args[0]); - CONVERT_DOUBLE_CHECKED(from_number, args[1]); - CONVERT_DOUBLE_CHECKED(to_number, args[2]); - - int start = FastD2I(from_number); - int end = FastD2I(to_number); - + Object* from = args[1]; + Object* to = args[2]; + int start, end; + // We have a fast integer-only case here to avoid a conversion to double in + // the common case where from and to are Smis. + if (from->IsSmi() && to->IsSmi()) { + start = Smi::cast(from)->value(); + end = Smi::cast(to)->value(); + } else { + CONVERT_DOUBLE_CHECKED(from_number, from); + CONVERT_DOUBLE_CHECKED(to_number, to); + start = FastD2I(from_number); + end = FastD2I(to_number); + } RUNTIME_ASSERT(end >= start); RUNTIME_ASSERT(start >= 0); RUNTIME_ASSERT(end <= value->length()); diff --git a/deps/v8/src/serialize.cc b/deps/v8/src/serialize.cc index de87022e00..00cd69ec0d 100644 --- a/deps/v8/src/serialize.cc +++ b/deps/v8/src/serialize.cc @@ -668,7 +668,7 @@ void Deserializer::ReadChunk(Object** current, break; case OBJECT_SERIALIZATION + CODE_SPACE: ReadObject(CODE_SPACE, Heap::code_space(), current++); - Logger::LogCodeObject(current[-1]); + LOG(LogCodeObject(current[-1])); break; case OBJECT_SERIALIZATION + CELL_SPACE: ReadObject(CELL_SPACE, Heap::cell_space(), current++); @@ -678,7 +678,7 @@ void Deserializer::ReadChunk(Object** current, break; case OBJECT_SERIALIZATION + kLargeCode: ReadObject(kLargeCode, Heap::lo_space(), current++); - Logger::LogCodeObject(current[-1]); + LOG(LogCodeObject(current[-1])); break; case OBJECT_SERIALIZATION + kLargeFixedArray: ReadObject(kLargeFixedArray, Heap::lo_space(), current++); @@ -687,7 +687,7 @@ void Deserializer::ReadChunk(Object** current, Object* new_code_object = NULL; ReadObject(kLargeCode, Heap::lo_space(), &new_code_object); Code* code_object = reinterpret_cast(new_code_object); - Logger::LogCodeObject(code_object); + LOG(LogCodeObject(code_object)); // Setting a branch/call to another code object from code. Address location_of_branch_data = reinterpret_cast
(current); Assembler::set_target_at(location_of_branch_data, @@ -700,7 +700,7 @@ void Deserializer::ReadChunk(Object** current, Object* new_code_object = NULL; ReadObject(CODE_SPACE, Heap::code_space(), &new_code_object); Code* code_object = reinterpret_cast(new_code_object); - Logger::LogCodeObject(code_object); + LOG(LogCodeObject(code_object)); // Setting a branch/call to another code object from code. Address location_of_branch_data = reinterpret_cast
(current); Assembler::set_target_at(location_of_branch_data, diff --git a/deps/v8/src/v8.cc b/deps/v8/src/v8.cc index d172742531..3bec827aa6 100644 --- a/deps/v8/src/v8.cc +++ b/deps/v8/src/v8.cc @@ -116,7 +116,7 @@ bool V8::Initialize(Deserializer *des) { if (FLAG_log_code) { HandleScope scope; - Logger::LogCompiledFunctions(); + LOG(LogCompiledFunctions()); } return true; diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index b448032a6a..f9dc9b75fb 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -34,7 +34,7 @@ // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 2 #define MINOR_VERSION 0 -#define BUILD_NUMBER 0 +#define BUILD_NUMBER 2 #define PATCH_LEVEL 0 #define CANDIDATE_VERSION false diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc index e2296d9bd9..b323572665 100644 --- a/deps/v8/src/x64/codegen-x64.cc +++ b/deps/v8/src/x64/codegen-x64.cc @@ -3616,6 +3616,48 @@ void CodeGenerator::GenerateIsArray(ZoneList* args) { } +void CodeGenerator::GenerateIsObject(ZoneList* args) { + // This generates a fast version of: + // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp') + ASSERT(args->length() == 1); + Load(args->at(0)); + Result obj = frame_->Pop(); + obj.ToRegister(); + Condition is_smi = masm_->CheckSmi(obj.reg()); + destination()->false_target()->Branch(is_smi); + + __ Move(kScratchRegister, Factory::null_value()); + __ cmpq(obj.reg(), kScratchRegister); + destination()->true_target()->Branch(equal); + + __ movq(kScratchRegister, FieldOperand(obj.reg(), HeapObject::kMapOffset)); + // Undetectable objects behave like undefined when tested with typeof. + __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset), + Immediate(1 << Map::kIsUndetectable)); + destination()->false_target()->Branch(not_zero); + __ CmpInstanceType(kScratchRegister, FIRST_JS_OBJECT_TYPE); + destination()->false_target()->Branch(less); + __ CmpInstanceType(kScratchRegister, LAST_JS_OBJECT_TYPE); + obj.Unuse(); + destination()->Split(less_equal); +} + + +void CodeGenerator::GenerateIsFunction(ZoneList* args) { + // This generates a fast version of: + // (%_ClassOf(arg) === 'Function') + ASSERT(args->length() == 1); + Load(args->at(0)); + Result obj = frame_->Pop(); + obj.ToRegister(); + Condition is_smi = masm_->CheckSmi(obj.reg()); + destination()->false_target()->Branch(is_smi); + __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, kScratchRegister); + obj.Unuse(); + destination()->Split(equal); +} + + void CodeGenerator::GenerateIsConstructCall(ZoneList* args) { ASSERT(args->length() == 0); diff --git a/deps/v8/src/x64/codegen-x64.h b/deps/v8/src/x64/codegen-x64.h index 0301daf3d7..20df41a50b 100644 --- a/deps/v8/src/x64/codegen-x64.h +++ b/deps/v8/src/x64/codegen-x64.h @@ -510,6 +510,8 @@ class CodeGenerator: public AstVisitor { void GenerateIsSmi(ZoneList* args); void GenerateIsNonNegativeSmi(ZoneList* args); void GenerateIsArray(ZoneList* args); + void GenerateIsObject(ZoneList* args); + void GenerateIsFunction(ZoneList* args); // Support for construct call checks. void GenerateIsConstructCall(ZoneList* args); diff --git a/deps/v8/test/cctest/test-log.cc b/deps/v8/test/cctest/test-log.cc index 57004d7c88..de29fe0978 100644 --- a/deps/v8/test/cctest/test-log.cc +++ b/deps/v8/test/cctest/test-log.cc @@ -247,7 +247,7 @@ TEST(ProfLazyMode) { i::FLAG_logfile = "*"; // If tests are being run manually, V8 will be already initialized - // by the test below. + // by the bottom test. const bool need_to_set_up_logger = i::V8::IsRunning(); v8::HandleScope scope; v8::Handle env = v8::Context::New(); @@ -474,6 +474,70 @@ TEST(Issue23768) { } +static v8::Handle ObjMethod1(const v8::Arguments& args) { + return v8::Handle(); +} + +TEST(LogCallbacks) { + const bool saved_prof_lazy = i::FLAG_prof_lazy; + const bool saved_prof = i::FLAG_prof; + const bool saved_prof_auto = i::FLAG_prof_auto; + i::FLAG_prof = true; + i::FLAG_prof_lazy = false; + i::FLAG_prof_auto = false; + i::FLAG_logfile = "*"; + + // If tests are being run manually, V8 will be already initialized + // by the bottom test. + const bool need_to_set_up_logger = i::V8::IsRunning(); + v8::HandleScope scope; + v8::Handle env = v8::Context::New(); + if (need_to_set_up_logger) Logger::Setup(); + env->Enter(); + + // Skip all initially logged stuff. + EmbeddedVector buffer; + int log_pos = GetLogLines(0, &buffer); + + v8::Persistent obj = + v8::Persistent::New(v8::FunctionTemplate::New()); + obj->SetClassName(v8::String::New("Obj")); + v8::Handle proto = obj->PrototypeTemplate(); + v8::Local signature = v8::Signature::New(obj); + proto->Set(v8::String::New("method1"), + v8::FunctionTemplate::New(ObjMethod1, + v8::Handle(), + signature), + static_cast(v8::DontDelete)); + + env->Global()->Set(v8_str("Obj"), obj->GetFunction()); + CompileAndRunScript("Obj.prototype.method1.toString();"); + + i::Logger::LogCompiledFunctions(); + log_pos = GetLogLines(log_pos, &buffer); + CHECK_GT(log_pos, 0); + buffer[log_pos] = 0; + + const char* callback_rec = "code-creation,Callback,"; + char* pos = strstr(buffer.start(), callback_rec); + CHECK_NE(NULL, pos); + pos += strlen(callback_rec); + EmbeddedVector ref_data; + i::OS::SNPrintF(ref_data, + "0x%" V8PRIxPTR ",1,\"method1\"", ObjMethod1); + *(pos + strlen(ref_data.start())) = '\0'; + CHECK_EQ(ref_data.start(), pos); + + obj.Dispose(); + + env->Exit(); + Logger::TearDown(); + i::FLAG_prof_lazy = saved_prof_lazy; + i::FLAG_prof = saved_prof; + i::FLAG_prof_auto = saved_prof_auto; +} + + static inline bool IsStringEqualTo(const char* r, const char* s) { return strncmp(r, s, strlen(r)) == 0; } diff --git a/deps/v8/test/cctest/test-thread-termination.cc b/deps/v8/test/cctest/test-thread-termination.cc index 552f49df17..1e8102ec7f 100644 --- a/deps/v8/test/cctest/test-thread-termination.cc +++ b/deps/v8/test/cctest/test-thread-termination.cc @@ -82,14 +82,30 @@ v8::Handle DoLoop(const v8::Arguments& args) { } +v8::Handle DoLoopNoCall(const v8::Arguments& args) { + v8::TryCatch try_catch; + v8::Script::Compile(v8::String::New("var term = true;" + "while(true) {" + " if (term) terminate();" + " term = false;" + "}"))->Run(); + CHECK(try_catch.HasCaught()); + CHECK(try_catch.Exception()->IsNull()); + CHECK(try_catch.Message().IsEmpty()); + CHECK(!try_catch.CanContinue()); + return v8::Undefined(); +} + + v8::Handle CreateGlobalTemplate( - v8::InvocationCallback terminate) { + v8::InvocationCallback terminate, + v8::InvocationCallback doloop) { v8::Handle global = v8::ObjectTemplate::New(); global->Set(v8::String::New("terminate"), v8::FunctionTemplate::New(terminate)); global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail)); global->Set(v8::String::New("loop"), v8::FunctionTemplate::New(Loop)); - global->Set(v8::String::New("doloop"), v8::FunctionTemplate::New(DoLoop)); + global->Set(v8::String::New("doloop"), v8::FunctionTemplate::New(doloop)); return global; } @@ -99,7 +115,25 @@ v8::Handle CreateGlobalTemplate( TEST(TerminateOnlyV8ThreadFromThreadItself) { v8::HandleScope scope; v8::Handle global = - CreateGlobalTemplate(TerminateCurrentThread); + CreateGlobalTemplate(TerminateCurrentThread, DoLoop); + v8::Persistent context = v8::Context::New(NULL, global); + v8::Context::Scope context_scope(context); + // Run a loop that will be infinite if thread termination does not work. + v8::Handle source = + v8::String::New("try { loop(); fail(); } catch(e) { fail(); }"); + v8::Script::Compile(source)->Run(); + // Test that we can run the code again after thread termination. + v8::Script::Compile(source)->Run(); + context.Dispose(); +} + + +// Test that a single thread of JavaScript execution can terminate +// itself in a loop that performs no calls. +TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) { + v8::HandleScope scope; + v8::Handle global = + CreateGlobalTemplate(TerminateCurrentThread, DoLoopNoCall); v8::Persistent context = v8::Context::New(NULL, global); v8::Context::Scope context_scope(context); // Run a loop that will be infinite if thread termination does not work. @@ -128,7 +162,7 @@ TEST(TerminateOnlyV8ThreadFromOtherThread) { thread.Start(); v8::HandleScope scope; - v8::Handle global = CreateGlobalTemplate(Signal); + v8::Handle global = CreateGlobalTemplate(Signal, DoLoop); v8::Persistent context = v8::Context::New(NULL, global); v8::Context::Scope context_scope(context); // Run a loop that will be infinite if thread termination does not work. @@ -149,7 +183,8 @@ class LoopingThread : public v8::internal::Thread { v8::Locker locker; v8::HandleScope scope; v8_thread_id_ = v8::V8::GetCurrentThreadId(); - v8::Handle global = CreateGlobalTemplate(Signal); + v8::Handle global = + CreateGlobalTemplate(Signal, DoLoop); v8::Persistent context = v8::Context::New(NULL, global); v8::Context::Scope context_scope(context); // Run a loop that will be infinite if thread termination does not work. diff --git a/deps/v8/test/mjsunit/regress/regress-515.js b/deps/v8/test/mjsunit/regress/regress-515.js new file mode 100644 index 0000000000..7675fe19ed --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-515.js @@ -0,0 +1,40 @@ +// Copyright 2009 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. + +// Regression test for http://code.google.com/p/v8/issues/detail?id=515. +// +// The test passes if it does not crash. + +var length = 2048; +var s = ""; +for (var i = 0; i < 2048; i++) { + s += '.'; +} + +var string = s + 'x' + s + 'x' + s; + +string.replace(/x/g, "") diff --git a/deps/v8/tools/gyp/v8.gyp b/deps/v8/tools/gyp/v8.gyp index 75464f206a..d7ee73f6ed 100644 --- a/deps/v8/tools/gyp/v8.gyp +++ b/deps/v8/tools/gyp/v8.gyp @@ -81,6 +81,7 @@ ['OS=="linux"', { 'cflags!': [ '-O2', + '-Os', ], 'cflags': [ '-fomit-frame-pointer',