diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2009-12-05 15:27:56 +0100 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2009-12-05 15:27:56 +0100 |
commit | c8b6ef248e5fc32df62041ec83463923bc8bdc68 (patch) | |
tree | 9012b1d0b0f9c380f6c6aa1b8b43dde44532e19e /deps/v8 | |
parent | c5d82380f46ed0c992ff56a9e9ddfe6ab2540e62 (diff) | |
download | node-c8b6ef248e5fc32df62041ec83463923bc8bdc68.tar.gz |
upgrade v8 to 2.0.3
Diffstat (limited to 'deps/v8')
99 files changed, 4250 insertions, 1460 deletions
diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index bf2c244b4..825431cde 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,3 +1,26 @@ +2009-12-03: Version 2.0.3 + + Optimized handling and adding of strings, for-in and Array.join. + + Heap serialization is now non-destructive. + + Improved profiler support with information on time spend in C++ + callbacks registered through the API. + + Added commands to the debugger protocol for starting/stopping + profiling. + + Enabled the non-optimizing compiler for top-level code. + + Changed the API to only allow strings to be set as data objects on + Contexts and scripts to avoid potentially keeping global objects + around for too long (issue 528). + + OpenBSD support patch by Peter Valchev <pvalchev@gmail.com>. + + Fixed bugs. + + 2009-11-24: Version 2.0.2 Improved profiler support. @@ -34,7 +57,7 @@ Reverted a change which caused crashes in RegExp replace. - Reverted a change which caused Chromium ui_tests failure. + Reverted a change which caused Chromium ui_tests failure. 2009-10-28: Version 1.3.17 diff --git a/deps/v8/SConstruct b/deps/v8/SConstruct index 4da9b5b4f..edaa66b75 100755 --- a/deps/v8/SConstruct +++ b/deps/v8/SConstruct @@ -149,6 +149,11 @@ LIBRARY_FLAGS = { 'LIBPATH' : ['/usr/local/lib'], 'CCFLAGS': ['-ansi'], }, + 'os:openbsd': { + 'CPPPATH' : ['/usr/local/include'], + 'LIBPATH' : ['/usr/local/lib'], + 'CCFLAGS': ['-ansi'], + }, 'os:win32': { 'CCFLAGS': ['-DWIN32'], 'CXXFLAGS': ['-DWIN32'], @@ -298,6 +303,9 @@ MKSNAPSHOT_EXTRA_FLAGS = { 'os:freebsd': { 'LIBS': ['execinfo', 'pthread'] }, + 'os:openbsd': { + 'LIBS': ['execinfo', 'pthread'] + }, 'os:win32': { 'LIBS': ['winmm', 'ws2_32'], }, @@ -344,6 +352,9 @@ CCTEST_EXTRA_FLAGS = { 'os:freebsd': { 'LIBS': ['execinfo', 'pthread'] }, + 'os:openbsd': { + 'LIBS': ['execinfo', 'pthread'] + }, 'os:win32': { 'LIBS': ['winmm', 'ws2_32'] }, @@ -397,7 +408,11 @@ SAMPLE_FLAGS = { }, 'os:freebsd': { 'LIBPATH' : ['/usr/local/lib'], - 'LIBS': ['execinfo', 'pthread'] + 'LIBS': ['execinfo', 'pthread'] + }, + 'os:openbsd': { + 'LIBPATH' : ['/usr/local/lib'], + 'LIBS': ['execinfo', 'pthread'] }, 'os:win32': { 'LIBS': ['winmm', 'ws2_32'] @@ -504,6 +519,9 @@ D8_FLAGS = { 'os:freebsd': { 'LIBS': ['pthread'], }, + 'os:openbsd': { + 'LIBS': ['pthread'], + }, 'os:android': { 'LIBPATH': [ANDROID_TOP + '/out/target/product/generic/obj/lib'], 'LINKFLAGS': ANDROID_LINKFLAGS, @@ -544,7 +562,7 @@ def GuessToolchain(os): OS_GUESS = utils.GuessOS() TOOLCHAIN_GUESS = GuessToolchain(OS_GUESS) -ARCH_GUESS = utils.GuessArchitecture() or "" +ARCH_GUESS = utils.GuessArchitecture() SIMPLE_OPTIONS = { @@ -554,7 +572,7 @@ SIMPLE_OPTIONS = { 'help': 'the toolchain to use (' + TOOLCHAIN_GUESS + ')' }, 'os': { - 'values': ['freebsd', 'linux', 'macos', 'win32', 'android'], + 'values': ['freebsd', 'linux', 'macos', 'win32', 'android', 'openbsd'], 'default': OS_GUESS, 'help': 'the os to build for (' + OS_GUESS + ')' }, diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 78b46136f..a8ee8d432 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -598,7 +598,7 @@ class V8EXPORT Script { * with the debugger as this data object is only available through the * debugger API. */ - void SetData(Handle<Value> data); + void SetData(Handle<String> data); }; @@ -2634,7 +2634,7 @@ class V8EXPORT Context { * with the debugger to provide additional information on the context through * the debugger API. */ - void SetData(Handle<Value> data); + void SetData(Handle<String> data); Local<Value> GetData(); /** @@ -2819,6 +2819,18 @@ template <> struct SmiConstants<8> { const int kSmiShiftSize = SmiConstants<sizeof(void*)>::kSmiShiftSize; const int kSmiValueSize = SmiConstants<sizeof(void*)>::kSmiValueSize; +template <size_t ptr_size> struct InternalConstants; + +// Internal constants for 32-bit systems. +template <> struct InternalConstants<4> { + static const int kStringResourceOffset = 3 * sizeof(void*); +}; + +// Internal constants for 64-bit systems. +template <> struct InternalConstants<8> { + static const int kStringResourceOffset = 2 * sizeof(void*); +}; + /** * This class exports constants and functionality from within v8 that * is necessary to implement inline functions in the v8 api. Don't @@ -2831,7 +2843,9 @@ class Internals { // the implementation of v8. static const int kHeapObjectMapOffset = 0; static const int kMapInstanceTypeOffset = sizeof(void*) + sizeof(int); - static const int kStringResourceOffset = 2 * sizeof(void*); + static const int kStringResourceOffset = + InternalConstants<sizeof(void*)>::kStringResourceOffset; + static const int kProxyProxyOffset = sizeof(void*); static const int kJSObjectHeaderSize = 3 * sizeof(void*); static const int kFullStringRepresentationMask = 0x07; diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript index cfa462f50..3b0df1718 100755 --- a/deps/v8/src/SConscript +++ b/deps/v8/src/SConscript @@ -159,6 +159,7 @@ SOURCES = { """), 'simulator:arm': ['arm/simulator-arm.cc'], 'os:freebsd': ['platform-freebsd.cc', 'platform-posix.cc'], + 'os:openbsd': ['platform-openbsd.cc', 'platform-posix.cc'], 'os:linux': ['platform-linux.cc', 'platform-posix.cc'], 'os:android': ['platform-linux.cc', 'platform-posix.cc'], 'os:macos': ['platform-macos.cc', 'platform-posix.cc'], @@ -187,6 +188,9 @@ D8_FILES = { 'os:freebsd': [ 'd8-posix.cc' ], + 'os:openbsd': [ + 'd8-posix.cc' + ], 'os:win32': [ 'd8-windows.cc' ], diff --git a/deps/v8/src/accessors.cc b/deps/v8/src/accessors.cc index 734c36445..56cf13598 100644 --- a/deps/v8/src/accessors.cc +++ b/deps/v8/src/accessors.cc @@ -315,14 +315,11 @@ Object* Accessors::ScriptGetLineEnds(Object* object, void*) { HandleScope scope; Handle<Script> script(Script::cast(JSValue::cast(object)->value())); InitScriptLineEnds(script); - if (script->line_ends_js_array()->IsUndefined()) { - Handle<FixedArray> line_ends_fixed_array( - FixedArray::cast(script->line_ends_fixed_array())); - Handle<FixedArray> copy = Factory::CopyFixedArray(line_ends_fixed_array); - Handle<JSArray> js_array = Factory::NewJSArrayWithElements(copy); - script->set_line_ends_js_array(*js_array); - } - return script->line_ends_js_array(); + ASSERT(script->line_ends()->IsFixedArray()); + Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends())); + Handle<FixedArray> copy = Factory::CopyFixedArray(line_ends); + Handle<JSArray> js_array = Factory::NewJSArrayWithElements(copy); + return *js_array; } @@ -352,29 +349,38 @@ const AccessorDescriptor Accessors::ScriptContextData = { // -// Accessors::ScriptGetEvalFromFunction +// Accessors::ScriptGetEvalFromScript // -Object* Accessors::ScriptGetEvalFromFunction(Object* object, void*) { +Object* Accessors::ScriptGetEvalFromScript(Object* object, void*) { Object* script = JSValue::cast(object)->value(); - return Script::cast(script)->eval_from_function(); + if (!Script::cast(script)->eval_from_shared()->IsUndefined()) { + Handle<SharedFunctionInfo> eval_from_shared( + SharedFunctionInfo::cast(Script::cast(script)->eval_from_shared())); + + if (eval_from_shared->script()->IsScript()) { + Handle<Script> eval_from_script(Script::cast(eval_from_shared->script())); + return *GetScriptWrapper(eval_from_script); + } + } + return Heap::undefined_value(); } -const AccessorDescriptor Accessors::ScriptEvalFromFunction = { - ScriptGetEvalFromFunction, +const AccessorDescriptor Accessors::ScriptEvalFromScript = { + ScriptGetEvalFromScript, IllegalSetter, 0 }; // -// Accessors::ScriptGetEvalFromPosition +// Accessors::ScriptGetEvalFromScriptPosition // -Object* Accessors::ScriptGetEvalFromPosition(Object* object, void*) { +Object* Accessors::ScriptGetEvalFromScriptPosition(Object* object, void*) { HandleScope scope; Handle<Script> script(Script::cast(JSValue::cast(object)->value())); @@ -386,14 +392,42 @@ Object* Accessors::ScriptGetEvalFromPosition(Object* object, void*) { // Get the function from where eval was called and find the source position // from the instruction offset. - Handle<Code> code(JSFunction::cast(script->eval_from_function())->code()); + Handle<Code> code(SharedFunctionInfo::cast( + script->eval_from_shared())->code()); return Smi::FromInt(code->SourcePosition(code->instruction_start() + script->eval_from_instructions_offset()->value())); } -const AccessorDescriptor Accessors::ScriptEvalFromPosition = { - ScriptGetEvalFromPosition, +const AccessorDescriptor Accessors::ScriptEvalFromScriptPosition = { + ScriptGetEvalFromScriptPosition, + IllegalSetter, + 0 +}; + + +// +// Accessors::ScriptGetEvalFromFunctionName +// + + +Object* Accessors::ScriptGetEvalFromFunctionName(Object* object, void*) { + Object* script = JSValue::cast(object)->value(); + Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast( + Script::cast(script)->eval_from_shared())); + + + // Find the name of the function calling eval. + if (!shared->name()->IsUndefined()) { + return shared->name(); + } else { + return shared->inferred_name(); + } +} + + +const AccessorDescriptor Accessors::ScriptEvalFromFunctionName = { + ScriptGetEvalFromFunctionName, IllegalSetter, 0 }; diff --git a/deps/v8/src/accessors.h b/deps/v8/src/accessors.h index 51d322ec8..7a840a191 100644 --- a/deps/v8/src/accessors.h +++ b/deps/v8/src/accessors.h @@ -51,8 +51,9 @@ namespace internal { V(ScriptCompilationType) \ V(ScriptLineEnds) \ V(ScriptContextData) \ - V(ScriptEvalFromFunction) \ - V(ScriptEvalFromPosition) \ + V(ScriptEvalFromScript) \ + V(ScriptEvalFromScriptPosition) \ + V(ScriptEvalFromFunctionName) \ V(ObjectPrototype) // Accessors contains all predefined proxy accessors. @@ -95,8 +96,9 @@ class Accessors : public AllStatic { static Object* ScriptGetCompilationType(Object* object, void*); static Object* ScriptGetLineEnds(Object* object, void*); static Object* ScriptGetContextData(Object* object, void*); - static Object* ScriptGetEvalFromFunction(Object* object, void*); - static Object* ScriptGetEvalFromPosition(Object* object, void*); + static Object* ScriptGetEvalFromScript(Object* object, void*); + static Object* ScriptGetEvalFromScriptPosition(Object* object, void*); + static Object* ScriptGetEvalFromFunctionName(Object* object, void*); static Object* ObjectGetPrototype(Object* receiver, void*); static Object* ObjectSetPrototype(JSObject* receiver, Object* value, void*); diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 220788ba5..93807a7c7 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -126,6 +126,48 @@ static FatalErrorCallback& GetFatalErrorHandler() { // When V8 cannot allocated memory FatalProcessOutOfMemory is called. // The default fatal error handler is called and execution is stopped. void i::V8::FatalProcessOutOfMemory(const char* location) { + i::HeapStats heap_stats; + int start_marker; + heap_stats.start_marker = &start_marker; + int new_space_size; + heap_stats.new_space_size = &new_space_size; + int new_space_capacity; + heap_stats.new_space_capacity = &new_space_capacity; + int old_pointer_space_size; + heap_stats.old_pointer_space_size = &old_pointer_space_size; + int old_pointer_space_capacity; + heap_stats.old_pointer_space_capacity = &old_pointer_space_capacity; + int old_data_space_size; + heap_stats.old_data_space_size = &old_data_space_size; + int old_data_space_capacity; + heap_stats.old_data_space_capacity = &old_data_space_capacity; + int code_space_size; + heap_stats.code_space_size = &code_space_size; + int code_space_capacity; + heap_stats.code_space_capacity = &code_space_capacity; + int map_space_size; + heap_stats.map_space_size = &map_space_size; + int map_space_capacity; + heap_stats.map_space_capacity = &map_space_capacity; + int cell_space_size; + heap_stats.cell_space_size = &cell_space_size; + int cell_space_capacity; + heap_stats.cell_space_capacity = &cell_space_capacity; + int lo_space_size; + heap_stats.lo_space_size = &lo_space_size; + int global_handle_count; + heap_stats.global_handle_count = &global_handle_count; + int weak_global_handle_count; + heap_stats.weak_global_handle_count = &weak_global_handle_count; + int pending_global_handle_count; + heap_stats.pending_global_handle_count = &pending_global_handle_count; + int near_death_global_handle_count; + heap_stats.near_death_global_handle_count = &near_death_global_handle_count; + int destroyed_global_handle_count; + heap_stats.destroyed_global_handle_count = &destroyed_global_handle_count; + int end_marker; + heap_stats.end_marker = &end_marker; + i::Heap::RecordStats(&heap_stats); i::V8::SetFatalError(); FatalErrorCallback callback = GetFatalErrorHandler(); { @@ -451,7 +493,7 @@ void Context::Exit() { } -void Context::SetData(v8::Handle<Value> data) { +void Context::SetData(v8::Handle<String> data) { if (IsDeadCheck("v8::Context::SetData()")) return; ENTER_V8; { @@ -1175,7 +1217,7 @@ Local<Value> Script::Id() { } -void Script::SetData(v8::Handle<Value> data) { +void Script::SetData(v8::Handle<String> data) { ON_BAILOUT("v8::Script::SetData()", return); LOG_API("Script::SetData"); { diff --git a/deps/v8/src/arm/assembler-arm.h b/deps/v8/src/arm/assembler-arm.h index ca0184e35..86bc18a24 100644 --- a/deps/v8/src/arm/assembler-arm.h +++ b/deps/v8/src/arm/assembler-arm.h @@ -566,6 +566,7 @@ class Assembler : public Malloced { // register. static const int kPcLoadDelta = 8; + static const int kJSReturnSequenceLength = 4; // --------------------------------------------------------------------------- // Code generation diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc index c62756d5a..ea3df6cfb 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -326,7 +326,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { // Calculate the exact length of the return sequence and make sure that // the constant pool is not emitted inside of the return sequence. int32_t sp_delta = (scope_->num_parameters() + 1) * kPointerSize; - int return_sequence_length = Debug::kARMJSReturnSequenceLength; + int return_sequence_length = Assembler::kJSReturnSequenceLength; if (!masm_->ImmediateFitsAddrMode1Instruction(sp_delta)) { // Additional mov instruction generated. return_sequence_length++; @@ -1560,7 +1560,7 @@ void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) { CheckStack(); // TODO(1222600): ignore if body contains calls. VisitAndSpill(node->body()); - // Compile the test. + // Compile the test. switch (info) { case ALWAYS_TRUE: // If control can fall off the end of the body, jump back to the @@ -1775,19 +1775,77 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { jsobject.Bind(); // Get the set of properties (as a FixedArray or Map). - frame_->EmitPush(r0); // duplicate the object being enumerated - frame_->EmitPush(r0); + // r0: value to be iterated over + frame_->EmitPush(r0); // Push the object being iterated over. + + // Check cache validity in generated code. This is a fast case for + // the JSObject::IsSimpleEnum cache validity checks. If we cannot + // guarantee cache validity, call the runtime system to check cache + // validity or get the property names in a fixed array. + JumpTarget call_runtime; + JumpTarget loop(JumpTarget::BIDIRECTIONAL); + JumpTarget check_prototype; + JumpTarget use_cache; + __ mov(r1, Operand(r0)); + loop.Bind(); + // Check that there are no elements. + __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset)); + __ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex); + __ cmp(r2, r4); + call_runtime.Branch(ne); + // Check that instance descriptors are not empty so that we can + // check for an enum cache. Leave the map in r3 for the subsequent + // prototype load. + __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); + __ ldr(r2, FieldMemOperand(r3, Map::kInstanceDescriptorsOffset)); + __ LoadRoot(ip, Heap::kEmptyDescriptorArrayRootIndex); + __ cmp(r2, ip); + call_runtime.Branch(eq); + // Check that there in an enum cache in the non-empty instance + // descriptors. This is the case if the next enumeration index + // field does not contain a smi. + __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumerationIndexOffset)); + __ tst(r2, Operand(kSmiTagMask)); + call_runtime.Branch(eq); + // For all objects but the receiver, check that the cache is empty. + // r4: empty fixed array root. + __ cmp(r1, r0); + check_prototype.Branch(eq); + __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheBridgeCacheOffset)); + __ cmp(r2, r4); + call_runtime.Branch(ne); + check_prototype.Bind(); + // Load the prototype from the map and loop if non-null. + __ ldr(r1, FieldMemOperand(r3, Map::kPrototypeOffset)); + __ LoadRoot(ip, Heap::kNullValueRootIndex); + __ cmp(r1, ip); + loop.Branch(ne); + // The enum cache is valid. Load the map of the object being + // iterated over and use the cache for the iteration. + __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset)); + use_cache.Jump(); + + call_runtime.Bind(); + // Call the runtime to get the property names for the object. + frame_->EmitPush(r0); // push the object (slot 4) for the runtime call frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1); - // If we got a Map, we can do a fast modification check. - // Otherwise, we got a FixedArray, and we have to do a slow check. + // If we got a map from the runtime call, we can do a fast + // modification check. Otherwise, we got a fixed array, and we have + // to do a slow check. + // r0: map or fixed array (result from call to + // Runtime::kGetPropertyNamesFast) __ mov(r2, Operand(r0)); __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); __ LoadRoot(ip, Heap::kMetaMapRootIndex); __ cmp(r1, ip); fixed_array.Branch(ne); + use_cache.Bind(); // Get enum cache + // r0: map (either the result from a call to + // Runtime::kGetPropertyNamesFast or has been fetched directly from + // the object) __ mov(r1, Operand(r0)); __ ldr(r1, FieldMemOperand(r1, Map::kInstanceDescriptorsOffset)); __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset)); @@ -3308,9 +3366,6 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) { // Now r2 has the string type. __ ldr(r3, FieldMemOperand(r1, String::kLengthOffset)); - __ and_(r4, r2, Operand(kStringSizeMask)); - __ add(r4, r4, Operand(String::kLongLengthShift)); - __ mov(r3, Operand(r3, LSR, r4)); // Now r3 has the length of the string. Compare with the index. __ cmp(r3, Operand(r0, LSR, kSmiTagSize)); __ b(le, &slow); @@ -3508,6 +3563,17 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) { + ASSERT_EQ(2, args->length()); + + Load(args->at(0)); + Load(args->at(1)); + + frame_->CallRuntime(Runtime::kStringAdd, 2); + frame_->EmitPush(r0); +} + + void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) { VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 2); @@ -5149,8 +5215,53 @@ static void HandleBinaryOpSlowCases(MacroAssembler* masm, // We jump to here if something goes wrong (one param is not a number of any // sort or new-space allocation fails). __ bind(&slow); + + // Push arguments to the stack __ push(r1); __ push(r0); + + if (Token::ADD == operation) { + // Test for string arguments before calling runtime. + // r1 : first argument + // r0 : second argument + // sp[0] : second argument + // sp[1] : first argument + + Label not_strings, not_string1, string1; + __ tst(r1, Operand(kSmiTagMask)); + __ b(eq, ¬_string1); + __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE); + __ b(ge, ¬_string1); + + // First argument is a a string, test second. + __ tst(r0, Operand(kSmiTagMask)); + __ b(eq, &string1); + __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE); + __ b(ge, &string1); + + // First and second argument are strings. + __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1); + + // Only first argument is a string. + __ bind(&string1); + __ mov(r0, Operand(2)); // Set number of arguments. + __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_JS); + + // First argument was not a string, test second. + __ bind(¬_string1); + __ tst(r0, Operand(kSmiTagMask)); + __ b(eq, ¬_strings); + __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE); + __ b(ge, ¬_strings); + + // Only second argument is a string. + __ b(¬_strings); + __ mov(r0, Operand(2)); // Set number of arguments. + __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS); + + __ bind(¬_strings); + } + __ mov(r0, Operand(1)); // Set number of arguments. __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return. @@ -6388,7 +6499,7 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) { __ b(eq, &adaptor); // Check index against formal parameters count limit passed in - // through register eax. Use unsigned comparison to get negative + // through register r0. Use unsigned comparison to get negative // check for free. __ cmp(r1, r0); __ b(cs, &slow); diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h index 30a1ae572..ba7f93626 100644 --- a/deps/v8/src/arm/codegen-arm.h +++ b/deps/v8/src/arm/codegen-arm.h @@ -366,6 +366,9 @@ class CodeGenerator: public AstVisitor { inline void GenerateMathSin(ZoneList<Expression*>* args); inline void GenerateMathCos(ZoneList<Expression*>* args); + // Fast support for StringAdd. + void GenerateStringAdd(ZoneList<Expression*>* args); + // Simple condition analysis. enum ConditionAnalysis { ALWAYS_TRUE, diff --git a/deps/v8/src/arm/constants-arm.h b/deps/v8/src/arm/constants-arm.h index 58396f877..943220739 100644 --- a/deps/v8/src/arm/constants-arm.h +++ b/deps/v8/src/arm/constants-arm.h @@ -43,24 +43,27 @@ # define USE_THUMB_INTERWORK 1 #endif -#if defined(__ARM_ARCH_5T__) || \ - defined(__ARM_ARCH_5TE__) || \ - defined(__ARM_ARCH_6__) || \ - defined(__ARM_ARCH_7A__) || \ +#if defined(__ARM_ARCH_7A__) || \ + defined(__ARM_ARCH_7R__) || \ defined(__ARM_ARCH_7__) -# define CAN_USE_ARMV5_INSTRUCTIONS 1 -# define CAN_USE_THUMB_INSTRUCTIONS 1 +# define CAN_USE_ARMV7_INSTRUCTIONS 1 #endif -#if defined(__ARM_ARCH_6__) || \ - defined(__ARM_ARCH_7A__) || \ - defined(__ARM_ARCH_7__) +#if defined(__ARM_ARCH_6__) || \ + defined(__ARM_ARCH_6J__) || \ + defined(__ARM_ARCH_6K__) || \ + defined(__ARM_ARCH_6Z__) || \ + defined(__ARM_ARCH_6ZK__) || \ + defined(__ARM_ARCH_6T2__) || \ + defined(CAN_USE_ARMV7_INSTRUCTIONS) # define CAN_USE_ARMV6_INSTRUCTIONS 1 #endif -#if defined(__ARM_ARCH_7A__) || \ - defined(__ARM_ARCH_7__) -# define CAN_USE_ARMV7_INSTRUCTIONS 1 +#if defined(__ARM_ARCH_5T__) || \ + defined(__ARM_ARCH_5TE__) || \ + defined(CAN_USE_ARMV6_INSTRUCTIONS) +# define CAN_USE_ARMV5_INSTRUCTIONS 1 +# define CAN_USE_THUMB_INSTRUCTIONS 1 #endif // Simulator should support ARM5 instructions. diff --git a/deps/v8/src/arm/debug-arm.cc b/deps/v8/src/arm/debug-arm.cc index 102952da7..fc9808d52 100644 --- a/deps/v8/src/arm/debug-arm.cc +++ b/deps/v8/src/arm/debug-arm.cc @@ -61,7 +61,7 @@ void BreakLocationIterator::SetDebugBreakAtReturn() { // Restore the JS frame exit code. void BreakLocationIterator::ClearDebugBreakAtReturn() { rinfo()->PatchCode(original_rinfo()->pc(), - Debug::kARMJSReturnSequenceLength); + Assembler::kJSReturnSequenceLength); } diff --git a/deps/v8/src/arm/fast-codegen-arm.cc b/deps/v8/src/arm/fast-codegen-arm.cc index 6d0510e32..ab636b6b8 100644 --- a/deps/v8/src/arm/fast-codegen-arm.cc +++ b/deps/v8/src/arm/fast-codegen-arm.cc @@ -73,16 +73,46 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) { bool function_in_register = true; + // Possibly allocate a local context. + if (fun->scope()->num_heap_slots() > 0) { + Comment cmnt(masm_, "[ Allocate local context"); + // Argument to NewContext is the function, which is in r1. + __ push(r1); + __ CallRuntime(Runtime::kNewContext, 1); + function_in_register = false; + // 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)); + // Copy any necessary parameters into the context. + int num_parameters = fun->scope()->num_parameters(); + for (int i = 0; i < num_parameters; i++) { + Slot* slot = fun->scope()->parameter(i)->slot(); + if (slot != NULL && slot->type() == Slot::CONTEXT) { + int parameter_offset = StandardFrameConstants::kCallerSPOffset + + (num_parameters - 1 - i) * kPointerSize; + // Load parameter from stack. + __ ldr(r0, MemOperand(fp, parameter_offset)); + // Store it in the context + __ str(r0, MemOperand(cp, Context::SlotOffset(slot->index()))); + } + } + } + Variable* arguments = fun->scope()->arguments()->AsVariable(); if (arguments != NULL) { // Function uses arguments object. Comment cmnt(masm_, "[ Allocate arguments object"); - __ mov(r3, r1); + if (!function_in_register) { + // Load this again, if it's used by the local context below. + __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); + } else { + __ mov(r3, r1); + } // Receiver is just before the parameters on the caller's stack. __ add(r2, fp, Operand(StandardFrameConstants::kCallerSPOffset + fun->num_parameters() * kPointerSize)); __ mov(r1, Operand(Smi::FromInt(fun->num_parameters()))); - __ stm(db_w, sp, r1.bit() | r2.bit() | r3.bit()); + __ stm(db_w, sp, r3.bit() | r2.bit() | r1.bit()); // Arguments to ArgumentsAccessStub: // function, receiver address, parameter count. @@ -90,33 +120,12 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) { // stack frame was an arguments adapter frame. ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); __ CallStub(&stub); - __ str(r0, MemOperand(fp, SlotOffset(arguments->slot()))); + // Duplicate the value; move-to-slot operation might clobber registers. + __ mov(r3, r0); + Move(arguments->slot(), r0, r1, r2); Slot* dot_arguments_slot = fun->scope()->arguments_shadow()->AsVariable()->slot(); - __ str(r0, MemOperand(fp, SlotOffset(dot_arguments_slot))); - function_in_register = false; - } - - // Possibly allocate a local context. - if (fun->scope()->num_heap_slots() > 0) { - Comment cmnt(masm_, "[ Allocate local context"); - if (!function_in_register) { - // Load this again, if it's used by the local context below. - __ ldr(r1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); - } - // Argument to NewContext is the function, which is in r1. - __ push(r1); - __ CallRuntime(Runtime::kNewContext, 1); - // 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)); -#ifdef DEBUG - // Assert we do not have to copy any parameters into the context. - for (int i = 0, len = fun->scope()->num_parameters(); i < len; i++) { - Slot* slot = fun->scope()->parameter(i)->slot(); - ASSERT(slot != NULL && slot->type() != Slot::CONTEXT); - } -#endif + Move(dot_arguments_slot, r3, r1, r2); } // Check the stack for overflow or break request. @@ -179,7 +188,7 @@ void FastCodeGenerator::EmitReturnSequence(int position) { // the constant pool is not emitted inside of the return sequence. int num_parameters = function_->scope()->num_parameters(); int32_t sp_delta = (num_parameters + 1) * kPointerSize; - int return_sequence_length = Debug::kARMJSReturnSequenceLength; + int return_sequence_length = Assembler::kJSReturnSequenceLength; if (!masm_->ImmediateFitsAddrMode1Instruction(sp_delta)) { // Additional mov instruction generated. return_sequence_length++; @@ -238,7 +247,42 @@ void FastCodeGenerator::Move(Expression::Context context, Register source) { } -void FastCodeGenerator::Move(Expression::Context context, Slot* source) { +template <> +MemOperand FastCodeGenerator::CreateSlotOperand<MemOperand>( + Slot* source, + Register scratch) { + switch (source->type()) { + case Slot::PARAMETER: + case Slot::LOCAL: + return MemOperand(fp, SlotOffset(source)); + case Slot::CONTEXT: { + int context_chain_length = + function_->scope()->ContextChainLength(source->var()->scope()); + __ LoadContext(scratch, context_chain_length); + return CodeGenerator::ContextOperand(scratch, source->index()); + break; + } + case Slot::LOOKUP: + UNIMPLEMENTED(); + // Fall-through. + default: + UNREACHABLE(); + return MemOperand(r0, 0); // Dead code to make the compiler happy. + } +} + + +void FastCodeGenerator::Move(Register dst, Slot* source) { + // Use dst as scratch. + MemOperand location = CreateSlotOperand<MemOperand>(source, dst); + __ ldr(dst, location); +} + + + +void FastCodeGenerator::Move(Expression::Context context, + Slot* source, + Register scratch) { switch (context) { case Expression::kUninitialized: UNREACHABLE(); @@ -248,8 +292,8 @@ void FastCodeGenerator::Move(Expression::Context context, Slot* source) { case Expression::kTest: // Fall through. case Expression::kValueTest: // Fall through. case Expression::kTestValue: - __ ldr(ip, MemOperand(fp, SlotOffset(source))); - Move(context, ip); + Move(scratch, source); + Move(context, scratch); break; } } @@ -272,24 +316,60 @@ void FastCodeGenerator::Move(Expression::Context context, Literal* expr) { } +void FastCodeGenerator::Move(Slot* dst, + Register src, + Register scratch1, + Register scratch2) { + switch (dst->type()) { + case Slot::PARAMETER: + case Slot::LOCAL: + __ str(src, MemOperand(fp, SlotOffset(dst))); + break; + case Slot::CONTEXT: { + int context_chain_length = + function_->scope()->ContextChainLength(dst->var()->scope()); + __ LoadContext(scratch1, context_chain_length); + int index = Context::SlotOffset(dst->index()); + __ mov(scratch2, Operand(index)); + __ str(src, MemOperand(scratch1, index)); + __ RecordWrite(scratch1, scratch2, src); + break; + } + case Slot::LOOKUP: + UNIMPLEMENTED(); + default: + UNREACHABLE(); + } +} + + + void FastCodeGenerator::DropAndMove(Expression::Context context, - Register source) { + Register source, + int drop_count) { + ASSERT(drop_count > 0); switch (context) { case Expression::kUninitialized: UNREACHABLE(); case Expression::kEffect: - __ pop(); + __ add(sp, sp, Operand(drop_count * kPointerSize)); break; case Expression::kValue: + if (drop_count > 1) { + __ add(sp, sp, Operand((drop_count - 1) * kPointerSize)); + } __ str(source, MemOperand(sp)); break; case Expression::kTest: ASSERT(!source.is(sp)); - __ pop(); + __ add(sp, sp, Operand(drop_count * kPointerSize)); TestAndBranch(source, true_label_, false_label_); break; case Expression::kValueTest: { Label discard; + if (drop_count > 1) { + __ add(sp, sp, Operand((drop_count - 1) * kPointerSize)); + } __ str(source, MemOperand(sp)); TestAndBranch(source, true_label_, &discard); __ bind(&discard); @@ -299,6 +379,9 @@ void FastCodeGenerator::DropAndMove(Expression::Context context, } case Expression::kTestValue: { Label discard; + if (drop_count > 1) { + __ add(sp, sp, Operand((drop_count - 1) * kPointerSize)); + } __ str(source, MemOperand(sp)); TestAndBranch(source, &discard, false_label_); __ bind(&discard); @@ -376,26 +459,26 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { __ mov(r0, Operand(Factory::the_hole_value())); if (FLAG_debug_code) { // Check if we have the correct context pointer. - __ ldr(r1, CodeGenerator::ContextOperand( - cp, Context::FCONTEXT_INDEX)); + __ ldr(r1, CodeGenerator::ContextOperand(cp, + Context::FCONTEXT_INDEX)); __ cmp(r1, cp); __ Check(eq, "Unexpected declaration in current context."); } __ str(r0, CodeGenerator::ContextOperand(cp, slot->index())); // No write barrier since the_hole_value is in old space. - ASSERT(Heap::InNewSpace(*Factory::the_hole_value())); + ASSERT(!Heap::InNewSpace(*Factory::the_hole_value())); } else if (decl->fun() != NULL) { Visit(decl->fun()); __ pop(r0); if (FLAG_debug_code) { // Check if we have the correct context pointer. - __ ldr(r1, CodeGenerator::ContextOperand( - cp, Context::FCONTEXT_INDEX)); + __ ldr(r1, CodeGenerator::ContextOperand(cp, + Context::FCONTEXT_INDEX)); __ cmp(r1, cp); __ Check(eq, "Unexpected declaration in current context."); } __ str(r0, CodeGenerator::ContextOperand(cp, slot->index())); - int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; + int offset = Context::SlotOffset(slot->index()); __ mov(r2, Operand(offset)); // We know that we have written a function, which is not a smi. __ RecordWrite(cp, r2, r0); @@ -467,53 +550,60 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { DropAndMove(expr->context(), r0); } else if (rewrite->AsSlot() != NULL) { Slot* slot = rewrite->AsSlot(); - ASSERT_NE(NULL, slot); - switch (slot->type()) { - case Slot::LOCAL: - case Slot::PARAMETER: { - Comment cmnt(masm_, "Stack slot"); - Move(expr->context(), rewrite->AsSlot()); - break; - } - - case Slot::CONTEXT: { - Comment cmnt(masm_, "Context slot"); - int chain_length = - function_->scope()->ContextChainLength(slot->var()->scope()); - if (chain_length > 0) { - // Move up the chain of contexts to the context containing the slot. - __ ldr(r0, CodeGenerator::ContextOperand(cp, Context::CLOSURE_INDEX)); - // Load the function context (which is the incoming, outer context). - __ ldr(r0, FieldMemOperand(r0, JSFunction::kContextOffset)); - for (int i = 1; i < chain_length; i++) { - __ ldr(r0, - CodeGenerator::ContextOperand(r0, Context::CLOSURE_INDEX)); - // Load the function context (which is the incoming, outer context). - __ ldr(r0, FieldMemOperand(r0, JSFunction::kContextOffset)); - } - // The context may be an intermediate context, not a function context. - __ ldr(r0, - CodeGenerator::ContextOperand(r0, Context::FCONTEXT_INDEX)); - } else { // Slot is in the current context. - __ ldr(r0, - CodeGenerator::ContextOperand(cp, Context::FCONTEXT_INDEX)); + if (FLAG_debug_code) { + switch (slot->type()) { + case Slot::LOCAL: + case Slot::PARAMETER: { + Comment cmnt(masm_, "Stack slot"); + break; } - __ ldr(r0, CodeGenerator::ContextOperand(r0, slot->index())); - Move(expr->context(), r0); - break; + case Slot::CONTEXT: { + Comment cmnt(masm_, "Context slot"); + break; + } + case Slot::LOOKUP: + UNIMPLEMENTED(); + break; + default: + UNREACHABLE(); } - - case Slot::LOOKUP: - UNREACHABLE(); - break; } + Move(expr->context(), slot, r0); } else { - // The parameter variable has been rewritten into an explict access to - // the arguments object. + // A variable has been rewritten into an explicit access to + // an object property. Property* property = rewrite->AsProperty(); ASSERT_NOT_NULL(property); - ASSERT_EQ(expr->context(), property->context()); - Visit(property); + + // Currently the only parameter expressions that can occur are + // on the form "slot[literal]". + + // Check that the object is in a slot. + Variable* object_var = property->obj()->AsVariableProxy()->AsVariable(); + ASSERT_NOT_NULL(object_var); + Slot* object_slot = object_var->slot(); + ASSERT_NOT_NULL(object_slot); + + // Load the object. + Move(r2, object_slot); + + // Check that the key is a smi. + Literal* key_literal = property->key()->AsLiteral(); + ASSERT_NOT_NULL(key_literal); + ASSERT(key_literal->handle()->IsSmi()); + + // Load the key. + __ mov(r1, Operand(key_literal->handle())); + + // Push both as arguments to ic. + __ stm(db_w, sp, r2.bit() | r1.bit()); + + // Do a KEYED property load. + Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); + __ Call(ic, RelocInfo::CODE_TARGET); + + // Drop key and object left on the stack by IC, and push the result. + DropAndMove(expr->context(), r0, 2); } } @@ -575,8 +665,9 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ CallRuntime(Runtime::kCloneShallowLiteralBoilerplate, 1); } - // If result_saved == true: the result is saved on top of the stack. - // If result_saved == false: the result is in r0. + // If result_saved == true: The result is saved on top of the + // stack and in r0. + // If result_saved == false: The result not on the stack, just in r0. bool result_saved = false; for (int i = 0; i < expr->properties()->length(); i++) { @@ -604,6 +695,7 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); // StoreIC leaves the receiver on the stack. + __ ldr(r0, MemOperand(sp)); // Restore result into r0. break; } // Fall through. @@ -615,7 +707,7 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { Visit(value); ASSERT_EQ(Expression::kValue, value->context()); __ CallRuntime(Runtime::kSetProperty, 3); - __ ldr(r0, MemOperand(sp)); // Restore result into r0 + __ ldr(r0, MemOperand(sp)); // Restore result into r0. break; case ObjectLiteral::Property::GETTER: // Fall through. @@ -785,7 +877,7 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) { // Overwrite the global object on the stack with the result if needed. DropAndMove(expr->context(), r0); - } else { + } else if (var->slot()) { Slot* slot = var->slot(); ASSERT_NOT_NULL(slot); // Variables rewritten as properties not handled. switch (slot->type()) { @@ -884,6 +976,35 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) { UNREACHABLE(); break; } + } else { + Property* property = var->rewrite()->AsProperty(); + ASSERT_NOT_NULL(property); + + // Load object and key onto the stack. + Slot* object_slot = property->obj()->AsSlot(); + ASSERT_NOT_NULL(object_slot); + Move(Expression::kValue, object_slot, r0); + + Literal* key_literal = property->key()->AsLiteral(); + ASSERT_NOT_NULL(key_literal); + Move(Expression::kValue, key_literal); + + // Value to store was pushed before object and key on the stack. + __ ldr(r0, MemOperand(sp, 2 * kPointerSize)); + + // Arguments to ic is value in r0, object and key on stack. + Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + __ Call(ic, RelocInfo::CODE_TARGET); + + if (expr->context() == Expression::kEffect) { + __ add(sp, sp, Operand(3 * kPointerSize)); + } else if (expr->context() == Expression::kValue) { + // Value is still on the stack in esp[2 * kPointerSize] + __ add(sp, sp, Operand(2 * kPointerSize)); + } else { + __ ldr(r0, MemOperand(sp, 2 * kPointerSize)); + DropAndMove(expr->context(), r0, 3); + } } } @@ -1134,9 +1255,14 @@ void FastCodeGenerator::VisitCallNew(CallNew* expr) { void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Comment cmnt(masm_, "[ CallRuntime"); ZoneList<Expression*>* args = expr->arguments(); - Runtime::Function* function = expr->function(); - ASSERT(function != NULL); + if (expr->is_jsruntime()) { + // Prepare for calling JS runtime function. + __ mov(r1, Operand(expr->name())); + __ ldr(r0, CodeGenerator::GlobalObject()); + __ ldr(r0, FieldMemOperand(r0, GlobalObject::kBuiltinsOffset)); + __ stm(db_w, sp, r1.bit() | r0.bit()); + } // Push the arguments ("left-to-right"). int arg_count = args->length(); @@ -1145,8 +1271,20 @@ void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) { ASSERT_EQ(Expression::kValue, args->at(i)->context()); } - __ CallRuntime(function, arg_count); - Move(expr->context(), r0); + if (expr->is_jsruntime()) { + // Call the JS runtime function. + Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, + NOT_IN_LOOP); + __ Call(ic, RelocInfo::CODE_TARGET); + // Restore context register. + __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + // Discard the function left on TOS. + DropAndMove(expr->context(), r0); + } else { + // Call the C runtime function. + __ CallRuntime(expr->function(), arg_count); + Move(expr->context(), r0); + } } diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc index ba8364545..c56f414a1 100644 --- a/deps/v8/src/arm/ic-arm.cc +++ b/deps/v8/src/arm/ic-arm.cc @@ -107,12 +107,17 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, static const int kProbes = 4; for (int i = 0; i < kProbes; i++) { // Compute the masked index: (hash + i + i * i) & mask. - __ ldr(t1, FieldMemOperand(r2, String::kLengthOffset)); - __ mov(t1, Operand(t1, LSR, String::kHashShift)); + __ ldr(t1, FieldMemOperand(r2, String::kHashFieldOffset)); if (i > 0) { - __ add(t1, t1, Operand(StringDictionary::GetProbeOffset(i))); + // Add the probe offset (i + i * i) left shifted to avoid right shifting + // the hash in a separate instruction. The value hash + i + i * i is right + // shifted in the following and instruction. + ASSERT(StringDictionary::GetProbeOffset(i) < + 1 << (32 - String::kHashFieldOffset)); + __ add(t1, t1, Operand( + StringDictionary::GetProbeOffset(i) << String::kHashShift)); } - __ and_(t1, t1, Operand(r3)); + __ and_(t1, r3, Operand(t1, LSR, String::kHashShift)); // Scale the index by multiplying by the element size. ASSERT(StringDictionary::kEntrySize == 3); diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index a668cb1f7..aa6570ce1 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -155,6 +155,15 @@ void MacroAssembler::Ret(Condition cond) { } +void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) { + LoadRoot(ip, Heap::kStackLimitRootIndex); + cmp(sp, Operand(ip)); + b(lo, on_stack_overflow); +} + + + + void MacroAssembler::SmiJumpTable(Register index, Vector<Label*> targets) { // Empty the const pool. CheckConstPool(true, true); @@ -785,15 +794,13 @@ void MacroAssembler::AllocateInNewSpace(int object_size, mov(scratch1, Operand(new_space_allocation_top)); if ((flags & RESULT_CONTAINS_TOP) == 0) { ldr(result, MemOperand(scratch1)); - } else { -#ifdef DEBUG + } else if (FLAG_debug_code) { // Assert that result actually contains top on entry. scratch2 is used // immediately below so this use of scratch2 does not cause difference with // respect to register content between debug and release mode. ldr(scratch2, MemOperand(scratch1)); cmp(result, scratch2); Check(eq, "Unexpected allocation top"); -#endif } // Calculate new top and bail out if new space is exhausted. Use result @@ -806,7 +813,11 @@ void MacroAssembler::AllocateInNewSpace(int object_size, cmp(result, Operand(scratch2)); b(hi, gc_required); - // Update allocation top. result temporarily holds the new top, + // Update allocation top. result temporarily holds the new top. + if (FLAG_debug_code) { + tst(result, Operand(kObjectAlignmentMask)); + Check(eq, "Unaligned allocation in new space"); + } str(result, MemOperand(scratch1)); // Tag and adjust back to start of new object. @@ -835,15 +846,13 @@ void MacroAssembler::AllocateInNewSpace(Register object_size, mov(scratch1, Operand(new_space_allocation_top)); if ((flags & RESULT_CONTAINS_TOP) == 0) { ldr(result, MemOperand(scratch1)); - } else { -#ifdef DEBUG + } else if (FLAG_debug_code) { // Assert that result actually contains top on entry. scratch2 is used // immediately below so this use of scratch2 does not cause difference with // respect to register content between debug and release mode. ldr(scratch2, MemOperand(scratch1)); cmp(result, scratch2); Check(eq, "Unexpected allocation top"); -#endif } // Calculate new top and bail out if new space is exhausted. Use result @@ -857,7 +866,11 @@ void MacroAssembler::AllocateInNewSpace(Register object_size, cmp(result, Operand(scratch2)); b(hi, gc_required); - // Update allocation top. result temporarily holds the new top, + // Update allocation top. result temporarily holds the new top. + if (FLAG_debug_code) { + tst(result, Operand(kObjectAlignmentMask)); + Check(eq, "Unaligned allocation in new space"); + } str(result, MemOperand(scratch1)); // Adjust back to start of new object. @@ -1153,6 +1166,9 @@ void MacroAssembler::Abort(const char* msg) { RecordComment(msg); } #endif + // Disable stub call restrictions to always allow calls to abort. + set_allow_stub_calls(true); + mov(r0, Operand(p0)); push(r0); mov(r0, Operand(Smi::FromInt(p1 - p0))); @@ -1162,6 +1178,26 @@ void MacroAssembler::Abort(const char* msg) { } +void MacroAssembler::LoadContext(Register dst, int context_chain_length) { + if (context_chain_length > 0) { + // Move up the chain of contexts to the context containing the slot. + ldr(dst, MemOperand(cp, Context::SlotOffset(Context::CLOSURE_INDEX))); + // Load the function context (which is the incoming, outer context). + ldr(dst, FieldMemOperand(dst, JSFunction::kContextOffset)); + for (int i = 1; i < context_chain_length; i++) { + ldr(dst, MemOperand(dst, Context::SlotOffset(Context::CLOSURE_INDEX))); + ldr(dst, FieldMemOperand(dst, JSFunction::kContextOffset)); + } + // The context may be an intermediate context, not a function context. + ldr(dst, MemOperand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX))); + } else { // Slot is in the current function context. + // The context may be an intermediate context, not a function context. + ldr(dst, MemOperand(cp, Context::SlotOffset(Context::FCONTEXT_INDEX))); + } +} + + + #ifdef ENABLE_DEBUGGER_SUPPORT CodePatcher::CodePatcher(byte* address, int instructions) : address_(address), diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h index 8c247bfbc..09743290f 100644 --- a/deps/v8/src/arm/macro-assembler-arm.h +++ b/deps/v8/src/arm/macro-assembler-arm.h @@ -79,6 +79,11 @@ class MacroAssembler: public Assembler { void RecordWrite(Register object, Register offset, Register scratch); // --------------------------------------------------------------------------- + // Stack limit support + + void StackLimitCheck(Label* on_stack_limit_hit); + + // --------------------------------------------------------------------------- // Activation frames void EnterInternalFrame() { EnterFrame(StackFrame::INTERNAL); } @@ -99,6 +104,8 @@ class MacroAssembler: public Assembler { // Align the stack by optionally pushing a Smi zero. void AlignStack(int offset); + void LoadContext(Register dst, int context_chain_length); + // --------------------------------------------------------------------------- // JavaScript invokes diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc index 8282655f7..efccaf496 100644 --- a/deps/v8/src/arm/stub-cache-arm.cc +++ b/deps/v8/src/arm/stub-cache-arm.cc @@ -105,7 +105,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm, __ b(eq, &miss); // Get the map of the receiver and compute the hash. - __ ldr(scratch, FieldMemOperand(name, String::kLengthOffset)); + __ ldr(scratch, FieldMemOperand(name, String::kHashFieldOffset)); __ ldr(ip, FieldMemOperand(receiver, HeapObject::kMapOffset)); __ add(scratch, scratch, Operand(ip)); __ eor(scratch, scratch, Operand(flags)); @@ -229,10 +229,7 @@ void StubCompiler::GenerateLoadStringLength2(MacroAssembler* masm, miss, &check_wrapper); // Load length directly from the string. - __ and_(scratch1, scratch1, Operand(kStringSizeMask)); - __ add(scratch1, scratch1, Operand(String::kHashShift)); __ ldr(r0, FieldMemOperand(receiver, String::kLengthOffset)); - __ mov(r0, Operand(r0, LSR, scratch1)); __ mov(r0, Operand(r0, LSL, kSmiTagSize)); __ Ret(); diff --git a/deps/v8/src/array.js b/deps/v8/src/array.js index 94d74a508..20d884ee6 100644 --- a/deps/v8/src/array.js +++ b/deps/v8/src/array.js @@ -77,7 +77,8 @@ function SparseJoin(array, len, convert) { var key = keys[i]; if (key != last_key) { var e = array[key]; - builder.add(convert(e)); + if (typeof(e) !== 'string') e = convert(e); + builder.add(e); last_key = key; } } @@ -114,17 +115,36 @@ function Join(array, length, separator, convert) { if (length == 1) { var e = array[0]; if (!IS_UNDEFINED(e) || (0 in array)) { + if (typeof(e) === 'string') return e; return convert(e); } } var builder = new StringBuilder(); - for (var i = 0; i < length; i++) { - var e = array[i]; - if (i != 0) builder.add(separator); - if (!IS_UNDEFINED(e) || (i in array)) { - builder.add(convert(e)); + // We pull the empty separator check outside the loop for speed! + if (separator.length == 0) { + for (var i = 0; i < length; i++) { + var e = array[i]; + if (!IS_UNDEFINED(e) || (i in array)) { + if (typeof(e) !== 'string') e = convert(e); + if (e.length > 0) { + var elements = builder.elements; + elements[elements.length] = e; + } + } + } + } else { + for (var i = 0; i < length; i++) { + var e = array[i]; + if (i != 0) builder.add(separator); + if (!IS_UNDEFINED(e) || (i in array)) { + if (typeof(e) !== 'string') e = convert(e); + if (e.length > 0) { + var elements = builder.elements; + elements[elements.length] = e; + } + } } } return builder.generate(); @@ -136,12 +156,14 @@ function Join(array, length, separator, convert) { function ConvertToString(e) { + if (typeof(e) === 'string') return e; if (e == null) return ''; else return ToString(e); } function ConvertToLocaleString(e) { + if (typeof(e) === 'string') return e; if (e == null) return ''; else { // e_obj's toLocaleString might be overwritten, check if it is a function. @@ -149,7 +171,7 @@ function ConvertToLocaleString(e) { // See issue 877615. var e_obj = ToObject(e); if (IS_FUNCTION(e_obj.toLocaleString)) - return e_obj.toLocaleString(); + return ToString(e_obj.toLocaleString()); else return ToString(e); } diff --git a/deps/v8/src/ast.h b/deps/v8/src/ast.h index 560470f7e..c27d558a2 100644 --- a/deps/v8/src/ast.h +++ b/deps/v8/src/ast.h @@ -1080,6 +1080,7 @@ class CallRuntime: public Expression { Handle<String> name() const { return name_; } Runtime::Function* function() const { return function_; } ZoneList<Expression*>* arguments() const { return arguments_; } + bool is_jsruntime() const { return function_ == NULL; } private: Handle<String> name_; diff --git a/deps/v8/src/bootstrapper.cc b/deps/v8/src/bootstrapper.cc index 449196216..deda96f31 100644 --- a/deps/v8/src/bootstrapper.cc +++ b/deps/v8/src/bootstrapper.cc @@ -1111,21 +1111,29 @@ bool Genesis::InstallNatives() { Factory::LookupAsciiSymbol("context_data"), proxy_context_data, common_attributes); - Handle<Proxy> proxy_eval_from_function = - Factory::NewProxy(&Accessors::ScriptEvalFromFunction); + Handle<Proxy> proxy_eval_from_script = + Factory::NewProxy(&Accessors::ScriptEvalFromScript); script_descriptors = Factory::CopyAppendProxyDescriptor( script_descriptors, - Factory::LookupAsciiSymbol("eval_from_function"), - proxy_eval_from_function, + Factory::LookupAsciiSymbol("eval_from_script"), + proxy_eval_from_script, common_attributes); - Handle<Proxy> proxy_eval_from_position = - Factory::NewProxy(&Accessors::ScriptEvalFromPosition); + Handle<Proxy> proxy_eval_from_script_position = + Factory::NewProxy(&Accessors::ScriptEvalFromScriptPosition); script_descriptors = Factory::CopyAppendProxyDescriptor( script_descriptors, - Factory::LookupAsciiSymbol("eval_from_position"), - proxy_eval_from_position, + Factory::LookupAsciiSymbol("eval_from_script_position"), + proxy_eval_from_script_position, + common_attributes); + Handle<Proxy> proxy_eval_from_function_name = + Factory::NewProxy(&Accessors::ScriptEvalFromFunctionName); + script_descriptors = + Factory::CopyAppendProxyDescriptor( + script_descriptors, + Factory::LookupAsciiSymbol("eval_from_function_name"), + proxy_eval_from_function_name, common_attributes); Handle<Map> script_map = Handle<Map>(script_fun->initial_map()); @@ -1338,8 +1346,6 @@ bool Genesis::InstallExtension(v8::RegisteredExtension* current) { ASSERT(Top::has_pending_exception() != result); if (!result) { Top::clear_pending_exception(); - v8::Utils::ReportApiFailure( - "v8::Context::New()", "Error installing extension"); } current->set_state(v8::INSTALLED); return result; diff --git a/deps/v8/src/code-stubs.h b/deps/v8/src/code-stubs.h index 6c50c6db0..25a2d0f55 100644 --- a/deps/v8/src/code-stubs.h +++ b/deps/v8/src/code-stubs.h @@ -36,6 +36,7 @@ namespace internal { #define CODE_STUB_LIST_ALL_PLATFORMS(V) \ V(CallFunction) \ V(GenericBinaryOp) \ + V(StringAdd) \ V(SmiOp) \ V(Compare) \ V(RecordWrite) \ diff --git a/deps/v8/src/codegen.cc b/deps/v8/src/codegen.cc index a6d5fb47f..26e8d7de0 100644 --- a/deps/v8/src/codegen.cc +++ b/deps/v8/src/codegen.cc @@ -346,6 +346,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = { {&CodeGenerator::GenerateMathCos, "_Math_cos"}, {&CodeGenerator::GenerateIsObject, "_IsObject"}, {&CodeGenerator::GenerateIsFunction, "_IsFunction"}, + {&CodeGenerator::GenerateStringAdd, "_StringAdd"}, }; diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc index 4e80a2450..22b0a03c6 100644 --- a/deps/v8/src/compiler.cc +++ b/deps/v8/src/compiler.cc @@ -124,9 +124,12 @@ static Handle<Code> MakeCode(FunctionLiteral* literal, // If there is no shared function info, try the fast code // generator for code in the global scope. Otherwise obey the // explicit hint in the shared function info. - if (shared.is_null() && !literal->scope()->is_global_scope()) { + // If always_fast_compiler is true, always try the fast compiler. + if (shared.is_null() && !literal->scope()->is_global_scope() && + !FLAG_always_fast_compiler) { if (FLAG_trace_bailout) PrintF("Non-global scope\n"); - } else if (!shared.is_null() && !shared->try_fast_codegen()) { + } else if (!shared.is_null() && !shared->try_fast_codegen() && + !FLAG_always_fast_compiler) { if (FLAG_trace_bailout) PrintF("No hint to try fast\n"); } else { CodeGenSelector selector; @@ -176,7 +179,8 @@ static Handle<JSFunction> MakeFunction(bool is_global, // called. if (is_eval) { JavaScriptFrameIterator it; - script->set_eval_from_function(it.frame()->function()); + script->set_eval_from_shared( + JSFunction::cast(it.frame()->function())->shared()); int offset = static_cast<int>( it.frame()->pc() - it.frame()->code()->instruction_start()); script->set_eval_from_instructions_offset(Smi::FromInt(offset)); @@ -599,11 +603,6 @@ CodeGenSelector::CodeGenTag CodeGenSelector::Select(FunctionLiteral* fun) { } } - if (scope->arguments() != NULL) { - if (FLAG_trace_bailout) PrintF("function uses 'arguments'\n"); - return NORMAL; - } - has_supported_syntax_ = true; VisitDeclarations(scope->declarations()); if (!has_supported_syntax_) return NORMAL; @@ -802,7 +801,17 @@ void CodeGenSelector::VisitVariableProxy(VariableProxy* expr) { BAILOUT("Lookup slot"); } } else { - BAILOUT("access to arguments object"); +#ifdef DEBUG + // Only remaining possibility is a property where the object is + // a slotted variable and the key is a smi. + Property* property = rewrite->AsProperty(); + ASSERT_NOT_NULL(property); + Variable* object = property->obj()->AsVariableProxy()->AsVariable(); + ASSERT_NOT_NULL(object); + ASSERT_NOT_NULL(object->slot()); + ASSERT_NOT_NULL(property->key()->AsLiteral()); + ASSERT(property->key()->AsLiteral()->handle()->IsSmi()); +#endif } } } @@ -886,12 +895,21 @@ void CodeGenSelector::VisitAssignment(Assignment* expr) { // All global variables are supported. if (!var->is_global()) { if (var->slot() == NULL) { - // This is a parameter that has rewritten to an arguments access. - BAILOUT("non-global/non-slot assignment"); - } - Slot::Type type = var->slot()->type(); - if (type == Slot::LOOKUP) { - BAILOUT("Lookup slot"); + Property* property = var->AsProperty(); + if (property == NULL) { + BAILOUT("non-global/non-slot/non-property assignment"); + } + if (property->obj()->AsSlot() == NULL) { + BAILOUT("variable rewritten to property non slot object assignment"); + } + if (property->key()->AsLiteral() == NULL) { + BAILOUT("variable rewritten to property non literal key assignment"); + } + } else { + Slot::Type type = var->slot()->type(); + if (type == Slot::LOOKUP) { + BAILOUT("Lookup slot"); + } } } } else if (prop != NULL) { @@ -979,8 +997,6 @@ void CodeGenSelector::VisitCallNew(CallNew* expr) { void CodeGenSelector::VisitCallRuntime(CallRuntime* expr) { - // In case of JS runtime function bail out. - if (expr->function() == NULL) BAILOUT("call JS runtime function"); // Check for inline runtime call if (expr->name()->Get(0) == '_' && CodeGenerator::FindInlineRuntimeLUT(expr->name()) != NULL) { diff --git a/deps/v8/src/d8.cc b/deps/v8/src/d8.cc index e4658b1ce..dedbd55b4 100644 --- a/deps/v8/src/d8.cc +++ b/deps/v8/src/d8.cc @@ -159,7 +159,11 @@ Handle<Value> Shell::Write(const Arguments& args) { printf(" "); } v8::String::Utf8Value str(args[i]); - fwrite(*str, sizeof(**str), str.length(), stdout); + int n = fwrite(*str, sizeof(**str), str.length(), stdout); + if (n != str.length()) { + printf("Error in fwrite\n"); + exit(1); + } } return Undefined(); } @@ -203,7 +207,7 @@ Handle<Value> Shell::Load(const Arguments& args) { return ThrowException(String::New("Error loading file")); } if (!ExecuteString(source, String::New(*file), false, false)) { - return ThrowException(String::New("Error executing file")); + return ThrowException(String::New("Error executing file")); } } return Undefined(); diff --git a/deps/v8/src/debug-delay.js b/deps/v8/src/debug-delay.js index 35f7fcd7e..04fde1f99 100644 --- a/deps/v8/src/debug-delay.js +++ b/deps/v8/src/debug-delay.js @@ -1245,6 +1245,8 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) this.suspendRequest_(request, response); } else if (request.command == 'version') { this.versionRequest_(request, response); + } else if (request.command == 'profile') { + this.profileRequest_(request, response); } else { throw new Error('Unknown command "' + request.command + '" in request'); } @@ -1924,6 +1926,25 @@ DebugCommandProcessor.prototype.versionRequest_ = function(request, response) { }; +DebugCommandProcessor.prototype.profileRequest_ = function(request, response) { + if (!request.arguments) { + return response.failed('Missing arguments'); + } + var modules = parseInt(request.arguments.modules); + if (isNaN(modules)) { + return response.failed('Modules is not an integer'); + } + if (request.arguments.command == 'resume') { + %ProfilerResume(modules); + } else if (request.arguments.command == 'pause') { + %ProfilerPause(modules); + } else { + return response.failed('Unknown command'); + } + response.body = {}; +}; + + // Check whether the previously processed command caused the VM to become // running. DebugCommandProcessor.prototype.isRunning = function() { diff --git a/deps/v8/src/debug.h b/deps/v8/src/debug.h index c5c6b5ee5..24f0db413 100644 --- a/deps/v8/src/debug.h +++ b/deps/v8/src/debug.h @@ -370,17 +370,6 @@ class Debug { // Garbage collection notifications. static void AfterGarbageCollection(); - // Code generation assumptions. - static const int kIa32CallInstructionLength = 5; - static const int kIa32JSReturnSequenceLength = 6; - - // The x64 JS return sequence is padded with int3 to make it large - // enough to hold a call instruction when the debugger patches it. - static const int kX64CallInstructionLength = 13; - static const int kX64JSReturnSequenceLength = 13; - - static const int kARMJSReturnSequenceLength = 4; - // Code generator routines. static void GenerateLoadICDebugBreak(MacroAssembler* masm); static void GenerateStoreICDebugBreak(MacroAssembler* masm); diff --git a/deps/v8/src/dtoa-config.c b/deps/v8/src/dtoa-config.c index bc0a58a17..a1acd2dd4 100644 --- a/deps/v8/src/dtoa-config.c +++ b/deps/v8/src/dtoa-config.c @@ -38,7 +38,7 @@ */ #if !(defined(__APPLE__) && defined(__MACH__)) && \ - !defined(WIN32) && !defined(__FreeBSD__) + !defined(WIN32) && !defined(__FreeBSD__) && !defined(__OpenBSD__) #include <endian.h> #endif #include <math.h> @@ -47,14 +47,16 @@ /* The floating point word order on ARM is big endian when floating point * emulation is used, even if the byte order is little endian */ #if !(defined(__APPLE__) && defined(__MACH__)) && !defined(WIN32) && \ - !defined(__FreeBSD__) && __FLOAT_WORD_ORDER == __BIG_ENDIAN + !defined(__FreeBSD__) && !defined(__OpenBSD__) && \ + __FLOAT_WORD_ORDER == __BIG_ENDIAN #define IEEE_MC68k #else #define IEEE_8087 #endif #define __MATH_H__ -#if defined(__APPLE__) && defined(__MACH__) || defined(__FreeBSD__) +#if defined(__APPLE__) && defined(__MACH__) || defined(__FreeBSD__) || \ + defined(__OpenBSD__) /* stdlib.h on FreeBSD and Apple's 10.5 and later SDKs will mangle the * name of strtod. If it's included after strtod is redefined as * gay_strtod, it will mangle the name of gay_strtod, which is diff --git a/deps/v8/src/factory.cc b/deps/v8/src/factory.cc index 3d9cd7a11..83775ef65 100644 --- a/deps/v8/src/factory.cc +++ b/deps/v8/src/factory.cc @@ -188,9 +188,8 @@ Handle<Script> Factory::NewScript(Handle<String> source) { script->set_type(Smi::FromInt(Script::TYPE_NORMAL)); script->set_compilation_type(Smi::FromInt(Script::COMPILATION_TYPE_HOST)); script->set_wrapper(*wrapper); - script->set_line_ends_fixed_array(Heap::undefined_value()); - script->set_line_ends_js_array(Heap::undefined_value()); - script->set_eval_from_function(Heap::undefined_value()); + script->set_line_ends(Heap::undefined_value()); + script->set_eval_from_shared(Heap::undefined_value()); script->set_eval_from_instructions_offset(Smi::FromInt(0)); return script; diff --git a/deps/v8/src/fast-codegen.cc b/deps/v8/src/fast-codegen.cc index 53fcf3112..20de80853 100644 --- a/deps/v8/src/fast-codegen.cc +++ b/deps/v8/src/fast-codegen.cc @@ -305,12 +305,15 @@ void FastCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { Comment cmnt(masm_, "[ DoWhileStatement"); increment_loop_depth(); - Label body, exit; + Label body, exit, stack_limit_hit, stack_check_success; - // Emit the test at the bottom of the loop. __ bind(&body); Visit(stmt->body()); + // Check stack before looping. + __ StackLimitCheck(&stack_limit_hit); + __ bind(&stack_check_success); + // We are not in an expression context because we have been compiling // statements. Set up a test expression context for the condition. ASSERT_EQ(NULL, true_label_); @@ -322,6 +325,11 @@ void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { true_label_ = NULL; false_label_ = NULL; + __ bind(&stack_limit_hit); + StackCheckStub stack_stub; + __ CallStub(&stack_stub); + __ jmp(&stack_check_success); + __ bind(&exit); decrement_loop_depth(); @@ -331,7 +339,7 @@ void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) { Comment cmnt(masm_, "[ WhileStatement"); increment_loop_depth(); - Label test, body, exit; + Label test, body, exit, stack_limit_hit, stack_check_success; // Emit the test at the bottom of the loop. __ jmp(&test); @@ -340,6 +348,10 @@ void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) { Visit(stmt->body()); __ bind(&test); + // Check stack before looping. + __ StackLimitCheck(&stack_limit_hit); + __ bind(&stack_check_success); + // We are not in an expression context because we have been compiling // statements. Set up a test expression context for the condition. ASSERT_EQ(NULL, true_label_); @@ -351,6 +363,11 @@ void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) { true_label_ = NULL; false_label_ = NULL; + __ bind(&stack_limit_hit); + StackCheckStub stack_stub; + __ CallStub(&stack_stub); + __ jmp(&stack_check_success); + __ bind(&exit); decrement_loop_depth(); @@ -359,7 +376,7 @@ void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) { void FastCodeGenerator::VisitForStatement(ForStatement* stmt) { Comment cmnt(masm_, "[ ForStatement"); - Label test, body, exit; + Label test, body, exit, stack_limit_hit, stack_check_success; if (stmt->init() != NULL) Visit(stmt->init()); increment_loop_depth(); @@ -367,9 +384,15 @@ void FastCodeGenerator::VisitForStatement(ForStatement* stmt) { __ jmp(&test); __ bind(&body); Visit(stmt->body()); + + // Check stack before looping. + __ StackLimitCheck(&stack_limit_hit); + __ bind(&stack_check_success); + if (stmt->next() != NULL) Visit(stmt->next()); __ bind(&test); + if (stmt->cond() == NULL) { // For an empty test jump to the top of the loop. __ jmp(&body); @@ -378,6 +401,7 @@ void FastCodeGenerator::VisitForStatement(ForStatement* stmt) { // statements. Set up a test expression context for the condition. ASSERT_EQ(NULL, true_label_); ASSERT_EQ(NULL, false_label_); + true_label_ = &body; false_label_ = &exit; ASSERT(stmt->cond()->context() == Expression::kTest); @@ -386,6 +410,11 @@ void FastCodeGenerator::VisitForStatement(ForStatement* stmt) { false_label_ = NULL; } + __ bind(&stack_limit_hit); + StackCheckStub stack_stub; + __ CallStub(&stack_stub); + __ jmp(&stack_check_success); + __ bind(&exit); decrement_loop_depth(); } diff --git a/deps/v8/src/fast-codegen.h b/deps/v8/src/fast-codegen.h index ee2e1d345..9b262a739 100644 --- a/deps/v8/src/fast-codegen.h +++ b/deps/v8/src/fast-codegen.h @@ -56,14 +56,21 @@ class FastCodeGenerator: public AstVisitor { private: int SlotOffset(Slot* slot); - void Move(Expression::Context destination, Register source); - void Move(Expression::Context destination, Slot* source); + void Move(Expression::Context destination, Slot* source, Register scratch); void Move(Expression::Context destination, Literal* source); + void Move(Slot* dst, Register source, Register scratch1, Register scratch2); + void Move(Register dst, Slot* source); + + // Templated to allow for Operand on intel and MemOperand on ARM. + template <typename MemoryLocation> + MemoryLocation CreateSlotOperand(Slot* slot, Register scratch); // Drop the TOS, and store source to destination. // If destination is TOS, just overwrite TOS with source. - void DropAndMove(Expression::Context destination, Register source); + void DropAndMove(Expression::Context destination, + Register source, + int drop_count = 1); // Test the JavaScript value in source as if in a test context, compile // control flow to a pair of labels. diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index 8c9bb22dc..88fda123b 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -143,10 +143,12 @@ 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, false, +DEFINE_bool(fast_compiler, true, "use the fast-mode compiler for some top-level code") DEFINE_bool(trace_bailout, false, "print reasons for failing to use fast compilation") +DEFINE_bool(always_fast_compiler, false, + "always try using the fast compiler") // compilation-cache.cc DEFINE_bool(compilation_cache, true, "enable compilation cache") @@ -154,9 +156,9 @@ DEFINE_bool(compilation_cache, true, "enable compilation cache") // debug.cc DEFINE_bool(remote_debugging, false, "enable remote debugging") DEFINE_bool(trace_debug_json, false, "trace debugging JSON request/response") -DEFINE_bool(debugger_auto_break, false, +DEFINE_bool(debugger_auto_break, true, "automatically set the debug break flag when debugger commands are " - "in the queue (experimental)") + "in the queue") // frames.cc DEFINE_int(max_stack_trace_source_length, 300, diff --git a/deps/v8/src/global-handles.cc b/deps/v8/src/global-handles.cc index e519d7077..f3b2b0c50 100644 --- a/deps/v8/src/global-handles.cc +++ b/deps/v8/src/global-handles.cc @@ -429,6 +429,26 @@ GlobalHandles::Node* GlobalHandles::head_ = NULL; GlobalHandles::Node* GlobalHandles::first_free_ = NULL; GlobalHandles::Node* GlobalHandles::first_deallocated_ = NULL; +void GlobalHandles::RecordStats(HeapStats* stats) { + *stats->global_handle_count = 0; + *stats->weak_global_handle_count = 0; + *stats->pending_global_handle_count = 0; + *stats->near_death_global_handle_count = 0; + *stats->destroyed_global_handle_count = 0; + for (Node* current = head_; current != NULL; current = current->next()) { + *stats->global_handle_count++; + if (current->state_ == Node::WEAK) { + *stats->weak_global_handle_count++; + } else if (current->state_ == Node::PENDING) { + *stats->pending_global_handle_count++; + } else if (current->state_ == Node::NEAR_DEATH) { + *stats->near_death_global_handle_count++; + } else if (current->state_ == Node::DESTROYED) { + *stats->destroyed_global_handle_count++; + } + } +} + #ifdef DEBUG void GlobalHandles::PrintStats() { diff --git a/deps/v8/src/global-handles.h b/deps/v8/src/global-handles.h index b9cac5c2a..659f86eca 100644 --- a/deps/v8/src/global-handles.h +++ b/deps/v8/src/global-handles.h @@ -78,6 +78,8 @@ class GlobalHandles : public AllStatic { // Returns the current number of weak handles. static int NumberOfWeakHandles() { return number_of_weak_handles_; } + static void RecordStats(HeapStats* stats); + // Returns the current number of weak handles to global objects. // These handles are also included in NumberOfWeakHandles(). static int NumberOfGlobalObjectWeakHandles() { diff --git a/deps/v8/src/handles.cc b/deps/v8/src/handles.cc index b42ad241a..d551e21c5 100644 --- a/deps/v8/src/handles.cc +++ b/deps/v8/src/handles.cc @@ -429,12 +429,12 @@ Handle<JSValue> GetScriptWrapper(Handle<Script> script) { // Init line_ends array with code positions of line ends inside script // source. void InitScriptLineEnds(Handle<Script> script) { - if (!script->line_ends_fixed_array()->IsUndefined()) return; + if (!script->line_ends()->IsUndefined()) return; if (!script->source()->IsString()) { ASSERT(script->source()->IsUndefined()); - script->set_line_ends_fixed_array(*(Factory::NewFixedArray(0))); - ASSERT(script->line_ends_fixed_array()->IsFixedArray()); + script->set_line_ends(*(Factory::NewFixedArray(0))); + ASSERT(script->line_ends()->IsFixedArray()); return; } @@ -467,8 +467,8 @@ void InitScriptLineEnds(Handle<Script> script) { } ASSERT(array_index == line_count); - script->set_line_ends_fixed_array(*array); - ASSERT(script->line_ends_fixed_array()->IsFixedArray()); + script->set_line_ends(*array); + ASSERT(script->line_ends()->IsFixedArray()); } @@ -477,7 +477,7 @@ int GetScriptLineNumber(Handle<Script> script, int code_pos) { InitScriptLineEnds(script); AssertNoAllocation no_allocation; FixedArray* line_ends_array = - FixedArray::cast(script->line_ends_fixed_array()); + FixedArray::cast(script->line_ends()); const int line_ends_len = line_ends_array->length(); int line = -1; @@ -548,6 +548,12 @@ v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSObject> receiver, Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object, KeyCollectionType type) { Handle<FixedArray> content = Factory::empty_fixed_array(); + Handle<JSObject> arguments_boilerplate = + Handle<JSObject>( + Top::context()->global_context()->arguments_boilerplate()); + Handle<JSFunction> arguments_function = + Handle<JSFunction>( + JSFunction::cast(arguments_boilerplate->map()->constructor())); // Only collect keys if access is permitted. for (Handle<Object> p = object; @@ -577,8 +583,21 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object, content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result)); } - // Compute the property keys. - content = UnionOfKeys(content, GetEnumPropertyKeys(current)); + // We can cache the computed property keys if access checks are + // not needed and no interceptors are involved. + // + // We do not use the cache if the object has elements and + // therefore it does not make sense to cache the property names + // for arguments objects. Arguments objects will always have + // elements. + bool cache_enum_keys = + ((current->map()->constructor() != *arguments_function) && + !current->IsAccessCheckNeeded() && + !current->HasNamedInterceptor() && + !current->HasIndexedInterceptor()); + // Compute the property keys and cache them if possible. + content = + UnionOfKeys(content, GetEnumPropertyKeys(current, cache_enum_keys)); // Add the property keys from the interceptor. if (current->HasNamedInterceptor()) { @@ -605,7 +624,8 @@ Handle<JSArray> GetKeysFor(Handle<JSObject> object) { } -Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object) { +Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object, + bool cache_result) { int index = 0; if (object->HasFastProperties()) { if (object->map()->instance_descriptors()->HasEnumCache()) { @@ -628,10 +648,12 @@ Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object) { } } (*storage)->SortPairs(*sort_array, sort_array->length()); - Handle<FixedArray> bridge_storage = - Factory::NewFixedArray(DescriptorArray::kEnumCacheBridgeLength); - DescriptorArray* desc = object->map()->instance_descriptors(); - desc->SetEnumCache(*bridge_storage, *storage); + if (cache_result) { + Handle<FixedArray> bridge_storage = + Factory::NewFixedArray(DescriptorArray::kEnumCacheBridgeLength); + DescriptorArray* desc = object->map()->instance_descriptors(); + desc->SetEnumCache(*bridge_storage, *storage); + } ASSERT(storage->length() == index); return storage; } else { diff --git a/deps/v8/src/handles.h b/deps/v8/src/handles.h index f610a34d0..fe820d59e 100644 --- a/deps/v8/src/handles.h +++ b/deps/v8/src/handles.h @@ -277,7 +277,8 @@ enum KeyCollectionType { LOCAL_ONLY, INCLUDE_PROTOS }; Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object, KeyCollectionType type); Handle<JSArray> GetKeysFor(Handle<JSObject> object); -Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object); +Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object, + bool cache_result); // Computes the union of keys and return the result. // Used for implementing "for (n in object) { }" diff --git a/deps/v8/src/heap-inl.h b/deps/v8/src/heap-inl.h index 0646878ef..eccd5ee2d 100644 --- a/deps/v8/src/heap-inl.h +++ b/deps/v8/src/heap-inl.h @@ -41,10 +41,10 @@ int Heap::MaxObjectSizeInPagedSpace() { Object* Heap::AllocateSymbol(Vector<const char> str, int chars, - uint32_t length_field) { + uint32_t hash_field) { unibrow::Utf8InputBuffer<> buffer(str.start(), static_cast<unsigned>(str.length())); - return AllocateInternalSymbol(&buffer, chars, length_field); + return AllocateInternalSymbol(&buffer, chars, hash_field); } diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index 06dc59cdd..4e4cd1c05 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -1187,34 +1187,14 @@ bool Heap::CreateInitialMaps() { roots_[entry.index] = Map::cast(obj); } - obj = AllocateMap(SHORT_STRING_TYPE, SeqTwoByteString::kAlignedSize); + obj = AllocateMap(STRING_TYPE, SeqTwoByteString::kAlignedSize); if (obj->IsFailure()) return false; - set_undetectable_short_string_map(Map::cast(obj)); + set_undetectable_string_map(Map::cast(obj)); Map::cast(obj)->set_is_undetectable(); - obj = AllocateMap(MEDIUM_STRING_TYPE, SeqTwoByteString::kAlignedSize); + obj = AllocateMap(ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize); if (obj->IsFailure()) return false; - set_undetectable_medium_string_map(Map::cast(obj)); - Map::cast(obj)->set_is_undetectable(); - - obj = AllocateMap(LONG_STRING_TYPE, SeqTwoByteString::kAlignedSize); - if (obj->IsFailure()) return false; - set_undetectable_long_string_map(Map::cast(obj)); - Map::cast(obj)->set_is_undetectable(); - - obj = AllocateMap(SHORT_ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize); - if (obj->IsFailure()) return false; - set_undetectable_short_ascii_string_map(Map::cast(obj)); - Map::cast(obj)->set_is_undetectable(); - - obj = AllocateMap(MEDIUM_ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize); - if (obj->IsFailure()) return false; - set_undetectable_medium_ascii_string_map(Map::cast(obj)); - Map::cast(obj)->set_is_undetectable(); - - obj = AllocateMap(LONG_ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize); - if (obj->IsFailure()) return false; - set_undetectable_long_ascii_string_map(Map::cast(obj)); + set_undetectable_ascii_string_map(Map::cast(obj)); Map::cast(obj)->set_is_undetectable(); obj = AllocateMap(BYTE_ARRAY_TYPE, ByteArray::kAlignedSize); @@ -1839,10 +1819,19 @@ Object* Heap::AllocateConsString(String* first, String* second) { // Copy the characters into the new object. char* dest = SeqAsciiString::cast(result)->GetChars(); // Copy first part. - char* src = SeqAsciiString::cast(first)->GetChars(); + const char* src; + if (first->IsExternalString()) { + src = ExternalAsciiString::cast(first)->resource()->data(); + } else { + src = SeqAsciiString::cast(first)->GetChars(); + } for (int i = 0; i < first_length; i++) *dest++ = src[i]; // Copy second part. - src = SeqAsciiString::cast(second)->GetChars(); + if (second->IsExternalString()) { + src = ExternalAsciiString::cast(second)->resource()->data(); + } else { + src = SeqAsciiString::cast(second)->GetChars(); + } for (int i = 0; i < second_length; i++) *dest++ = src[i]; return result; } else { @@ -1856,26 +1845,17 @@ Object* Heap::AllocateConsString(String* first, String* second) { } } - Map* map; - if (length <= String::kMaxShortSize) { - map = is_ascii ? short_cons_ascii_string_map() - : short_cons_string_map(); - } else if (length <= String::kMaxMediumSize) { - map = is_ascii ? medium_cons_ascii_string_map() - : medium_cons_string_map(); - } else { - map = is_ascii ? long_cons_ascii_string_map() - : long_cons_string_map(); - } + Map* map = is_ascii ? cons_ascii_string_map() : cons_string_map(); Object* result = Allocate(map, always_allocate() ? OLD_POINTER_SPACE : NEW_SPACE); if (result->IsFailure()) return result; ConsString* cons_string = ConsString::cast(result); WriteBarrierMode mode = cons_string->GetWriteBarrierMode(); + cons_string->set_length(length); + cons_string->set_hash_field(String::kEmptyHashField); cons_string->set_first(first, mode); cons_string->set_second(second, mode); - cons_string->set_length(length); return result; } @@ -1925,25 +1905,20 @@ Object* Heap::AllocateSubString(String* buffer, Object* Heap::AllocateExternalStringFromAscii( ExternalAsciiString::Resource* resource) { - Map* map; size_t length = resource->length(); - if (length <= static_cast<size_t>(String::kMaxShortSize)) { - map = short_external_ascii_string_map(); - } else if (length <= static_cast<size_t>(String::kMaxMediumSize)) { - map = medium_external_ascii_string_map(); - } else if (length <= static_cast<size_t>(String::kMaxLength)) { - map = long_external_ascii_string_map(); - } else { + if (length > static_cast<size_t>(String::kMaxLength)) { Top::context()->mark_out_of_memory(); return Failure::OutOfMemoryException(); } + Map* map = external_ascii_string_map(); Object* result = Allocate(map, always_allocate() ? OLD_DATA_SPACE : NEW_SPACE); if (result->IsFailure()) return result; ExternalAsciiString* external_string = ExternalAsciiString::cast(result); external_string->set_length(static_cast<int>(length)); + external_string->set_hash_field(String::kEmptyHashField); external_string->set_resource(resource); return result; @@ -1957,13 +1932,15 @@ Object* Heap::AllocateExternalStringFromTwoByte( Top::context()->mark_out_of_memory(); return Failure::OutOfMemoryException(); } - Map* map = ExternalTwoByteString::StringMap(static_cast<int>(length)); + + Map* map = Heap::external_string_map(); Object* result = Allocate(map, always_allocate() ? OLD_DATA_SPACE : NEW_SPACE); if (result->IsFailure()) return result; ExternalTwoByteString* external_string = ExternalTwoByteString::cast(result); external_string->set_length(static_cast<int>(length)); + external_string->set_hash_field(String::kEmptyHashField); external_string->set_resource(resource); return result; @@ -2604,48 +2581,12 @@ Map* Heap::SymbolMapForString(String* string) { // Find the corresponding symbol map for strings. Map* map = string->map(); - - if (map == short_ascii_string_map()) return short_ascii_symbol_map(); - if (map == medium_ascii_string_map()) return medium_ascii_symbol_map(); - if (map == long_ascii_string_map()) return long_ascii_symbol_map(); - - if (map == short_string_map()) return short_symbol_map(); - if (map == medium_string_map()) return medium_symbol_map(); - if (map == long_string_map()) return long_symbol_map(); - - if (map == short_cons_string_map()) return short_cons_symbol_map(); - if (map == medium_cons_string_map()) return medium_cons_symbol_map(); - if (map == long_cons_string_map()) return long_cons_symbol_map(); - - if (map == short_cons_ascii_string_map()) { - return short_cons_ascii_symbol_map(); - } - if (map == medium_cons_ascii_string_map()) { - return medium_cons_ascii_symbol_map(); - } - if (map == long_cons_ascii_string_map()) { - return long_cons_ascii_symbol_map(); - } - - if (map == short_external_string_map()) { - return short_external_symbol_map(); - } - if (map == medium_external_string_map()) { - return medium_external_symbol_map(); - } - if (map == long_external_string_map()) { - return long_external_symbol_map(); - } - - if (map == short_external_ascii_string_map()) { - return short_external_ascii_symbol_map(); - } - if (map == medium_external_ascii_string_map()) { - return medium_external_ascii_symbol_map(); - } - if (map == long_external_ascii_string_map()) { - return long_external_ascii_symbol_map(); - } + if (map == ascii_string_map()) return ascii_symbol_map(); + if (map == string_map()) return symbol_map(); + if (map == cons_string_map()) return cons_symbol_map(); + if (map == cons_ascii_string_map()) return cons_ascii_symbol_map(); + if (map == external_string_map()) return external_symbol_map(); + if (map == external_ascii_string_map()) return external_ascii_symbol_map(); // No match found. return NULL; @@ -2654,7 +2595,7 @@ Map* Heap::SymbolMapForString(String* string) { Object* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer, int chars, - uint32_t length_field) { + uint32_t hash_field) { // Ensure the chars matches the number of characters in the buffer. ASSERT(static_cast<unsigned>(chars) == buffer->Length()); // Determine whether the string is ascii. @@ -2669,22 +2610,10 @@ Object* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer, Map* map; if (is_ascii) { - if (chars <= String::kMaxShortSize) { - map = short_ascii_symbol_map(); - } else if (chars <= String::kMaxMediumSize) { - map = medium_ascii_symbol_map(); - } else { - map = long_ascii_symbol_map(); - } + map = ascii_symbol_map(); size = SeqAsciiString::SizeFor(chars); } else { - if (chars <= String::kMaxShortSize) { - map = short_symbol_map(); - } else if (chars <= String::kMaxMediumSize) { - map = medium_symbol_map(); - } else { - map = long_symbol_map(); - } + map = symbol_map(); size = SeqTwoByteString::SizeFor(chars); } @@ -2695,9 +2624,10 @@ Object* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer, if (result->IsFailure()) return result; reinterpret_cast<HeapObject*>(result)->set_map(map); - // The hash value contains the length of the string. + // Set length and hash fields of the allocated string. String* answer = String::cast(result); - answer->set_length_field(length_field); + answer->set_length(chars); + answer->set_hash_field(hash_field); ASSERT_EQ(size, answer->Size()); @@ -2728,19 +2658,10 @@ Object* Heap::AllocateRawAsciiString(int length, PretenureFlag pretenure) { } if (result->IsFailure()) return result; - // Determine the map based on the string's length. - Map* map; - if (length <= String::kMaxShortSize) { - map = short_ascii_string_map(); - } else if (length <= String::kMaxMediumSize) { - map = medium_ascii_string_map(); - } else { - map = long_ascii_string_map(); - } - // Partially initialize the object. - HeapObject::cast(result)->set_map(map); + HeapObject::cast(result)->set_map(ascii_string_map()); String::cast(result)->set_length(length); + String::cast(result)->set_hash_field(String::kEmptyHashField); ASSERT_EQ(size, HeapObject::cast(result)->Size()); return result; } @@ -2765,19 +2686,10 @@ Object* Heap::AllocateRawTwoByteString(int length, PretenureFlag pretenure) { } if (result->IsFailure()) return result; - // Determine the map based on the string's length. - Map* map; - if (length <= String::kMaxShortSize) { - map = short_string_map(); - } else if (length <= String::kMaxMediumSize) { - map = medium_string_map(); - } else { - map = long_string_map(); - } - // Partially initialize the object. - HeapObject::cast(result)->set_map(map); + HeapObject::cast(result)->set_map(string_map()); String::cast(result)->set_length(length); + String::cast(result)->set_hash_field(String::kEmptyHashField); ASSERT_EQ(size, HeapObject::cast(result)->Size()); return result; } @@ -2998,6 +2910,11 @@ bool Heap::IdleNotification() { last_gc_count = gc_count_; } else if (number_idle_notifications == kIdlesBeforeMarkSweep) { + // Before doing the mark-sweep collections we clear the + // compilation cache to avoid hanging on to source code and + // generated code for cached functions. + CompilationCache::Clear(); + CollectAllGarbage(false); new_space_.Shrink(); last_gc_count = gc_count_; @@ -3356,6 +3273,26 @@ bool Heap::ConfigureHeapDefault() { } +void Heap::RecordStats(HeapStats* stats) { + *stats->start_marker = 0xDECADE00; + *stats->end_marker = 0xDECADE01; + *stats->new_space_size = new_space_.Size(); + *stats->new_space_capacity = new_space_.Capacity(); + *stats->old_pointer_space_size = old_pointer_space_->Size(); + *stats->old_pointer_space_capacity = old_pointer_space_->Capacity(); + *stats->old_data_space_size = old_data_space_->Size(); + *stats->old_data_space_capacity = old_data_space_->Capacity(); + *stats->code_space_size = code_space_->Size(); + *stats->code_space_capacity = code_space_->Capacity(); + *stats->map_space_size = map_space_->Size(); + *stats->map_space_capacity = map_space_->Capacity(); + *stats->cell_space_size = cell_space_->Size(); + *stats->cell_space_capacity = cell_space_->Capacity(); + *stats->lo_space_size = lo_space_->Size(); + GlobalHandles::RecordStats(stats); +} + + int Heap::PromotedSpaceSize() { return old_pointer_space_->Size() + old_data_space_->Size() diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index 90d714c7f..b37fe4b5b 100644 --- a/deps/v8/src/heap.h +++ b/deps/v8/src/heap.h @@ -59,50 +59,20 @@ namespace internal { V(Object, termination_exception, TerminationException) \ V(Map, hash_table_map, HashTableMap) \ V(FixedArray, empty_fixed_array, EmptyFixedArray) \ - V(Map, short_string_map, ShortStringMap) \ - V(Map, medium_string_map, MediumStringMap) \ - V(Map, long_string_map, LongStringMap) \ - V(Map, short_ascii_string_map, ShortAsciiStringMap) \ - V(Map, medium_ascii_string_map, MediumAsciiStringMap) \ - V(Map, long_ascii_string_map, LongAsciiStringMap) \ - V(Map, short_symbol_map, ShortSymbolMap) \ - V(Map, medium_symbol_map, MediumSymbolMap) \ - V(Map, long_symbol_map, LongSymbolMap) \ - V(Map, short_ascii_symbol_map, ShortAsciiSymbolMap) \ - V(Map, medium_ascii_symbol_map, MediumAsciiSymbolMap) \ - V(Map, long_ascii_symbol_map, LongAsciiSymbolMap) \ - V(Map, short_cons_symbol_map, ShortConsSymbolMap) \ - V(Map, medium_cons_symbol_map, MediumConsSymbolMap) \ - V(Map, long_cons_symbol_map, LongConsSymbolMap) \ - V(Map, short_cons_ascii_symbol_map, ShortConsAsciiSymbolMap) \ - V(Map, medium_cons_ascii_symbol_map, MediumConsAsciiSymbolMap) \ - V(Map, long_cons_ascii_symbol_map, LongConsAsciiSymbolMap) \ - V(Map, short_external_symbol_map, ShortExternalSymbolMap) \ - V(Map, medium_external_symbol_map, MediumExternalSymbolMap) \ - V(Map, long_external_symbol_map, LongExternalSymbolMap) \ - V(Map, short_external_ascii_symbol_map, ShortExternalAsciiSymbolMap) \ - V(Map, medium_external_ascii_symbol_map, MediumExternalAsciiSymbolMap) \ - V(Map, long_external_ascii_symbol_map, LongExternalAsciiSymbolMap) \ - V(Map, short_cons_string_map, ShortConsStringMap) \ - V(Map, medium_cons_string_map, MediumConsStringMap) \ - V(Map, long_cons_string_map, LongConsStringMap) \ - V(Map, short_cons_ascii_string_map, ShortConsAsciiStringMap) \ - V(Map, medium_cons_ascii_string_map, MediumConsAsciiStringMap) \ - V(Map, long_cons_ascii_string_map, LongConsAsciiStringMap) \ - V(Map, short_external_string_map, ShortExternalStringMap) \ - V(Map, medium_external_string_map, MediumExternalStringMap) \ - V(Map, long_external_string_map, LongExternalStringMap) \ - V(Map, short_external_ascii_string_map, ShortExternalAsciiStringMap) \ - V(Map, medium_external_ascii_string_map, MediumExternalAsciiStringMap) \ - V(Map, long_external_ascii_string_map, LongExternalAsciiStringMap) \ - V(Map, undetectable_short_string_map, UndetectableShortStringMap) \ - V(Map, undetectable_medium_string_map, UndetectableMediumStringMap) \ - V(Map, undetectable_long_string_map, UndetectableLongStringMap) \ - V(Map, undetectable_short_ascii_string_map, UndetectableShortAsciiStringMap) \ - V(Map, \ - undetectable_medium_ascii_string_map, \ - UndetectableMediumAsciiStringMap) \ - V(Map, undetectable_long_ascii_string_map, UndetectableLongAsciiStringMap) \ + V(Map, string_map, StringMap) \ + V(Map, ascii_string_map, AsciiStringMap) \ + V(Map, symbol_map, SymbolMap) \ + V(Map, ascii_symbol_map, AsciiSymbolMap) \ + V(Map, cons_symbol_map, ConsSymbolMap) \ + V(Map, cons_ascii_symbol_map, ConsAsciiSymbolMap) \ + V(Map, external_symbol_map, ExternalSymbolMap) \ + V(Map, external_ascii_symbol_map, ExternalAsciiSymbolMap) \ + V(Map, cons_string_map, ConsStringMap) \ + V(Map, cons_ascii_string_map, ConsAsciiStringMap) \ + V(Map, external_string_map, ExternalStringMap) \ + V(Map, external_ascii_string_map, ExternalAsciiStringMap) \ + V(Map, undetectable_string_map, UndetectableStringMap) \ + V(Map, undetectable_ascii_string_map, UndetectableAsciiStringMap) \ V(Map, pixel_array_map, PixelArrayMap) \ V(Map, external_byte_array_map, ExternalByteArrayMap) \ V(Map, external_unsigned_byte_array_map, ExternalUnsignedByteArrayMap) \ @@ -219,6 +189,7 @@ namespace internal { // Forward declaration of the GCTracer class. class GCTracer; +class HeapStats; // The all static Heap captures the interface to the global object heap. @@ -409,11 +380,11 @@ class Heap : public AllStatic { // Please note this function does not perform a garbage collection. static inline Object* AllocateSymbol(Vector<const char> str, int chars, - uint32_t length_field); + uint32_t hash_field); static Object* AllocateInternalSymbol(unibrow::CharacterStream* buffer, int chars, - uint32_t length_field); + uint32_t hash_field); static Object* AllocateExternalSymbol(Vector<const char> str, int chars); @@ -895,6 +866,8 @@ class Heap : public AllStatic { static RootListIndex RootIndexForExternalArrayType( ExternalArrayType array_type); + static void RecordStats(HeapStats* stats); + private: static int reserved_semispace_size_; static int max_semispace_size_; @@ -910,7 +883,10 @@ class Heap : public AllStatic { static int linear_allocation_scope_depth_; static bool context_disposed_pending_; - static const int kMaxMapSpaceSize = 8*MB; + // The number of MapSpace pages is limited by the way we pack + // Map pointers during GC. + static const int kMaxMapSpaceSize = + (1 << MapWord::kMapPageIndexBits) * Page::kPageSize; #if defined(V8_TARGET_ARCH_X64) static const int kMaxObjectSizeInNewSpace = 512*KB; @@ -1127,6 +1103,31 @@ class Heap : public AllStatic { }; +class HeapStats { + public: + int *start_marker; + int *new_space_size; + int *new_space_capacity; + int *old_pointer_space_size; + int *old_pointer_space_capacity; + int *old_data_space_size; + int *old_data_space_capacity; + int *code_space_size; + int *code_space_capacity; + int *map_space_size; + int *map_space_capacity; + int *cell_space_size; + int *cell_space_capacity; + int *lo_space_size; + int *global_handle_count; + int *weak_global_handle_count; + int *pending_global_handle_count; + int *near_death_global_handle_count; + int *destroyed_global_handle_count; + int *end_marker; +}; + + class AlwaysAllocateScope { public: AlwaysAllocateScope() { diff --git a/deps/v8/src/ia32/assembler-ia32.h b/deps/v8/src/ia32/assembler-ia32.h index 962206fb7..662ebc902 100644 --- a/deps/v8/src/ia32/assembler-ia32.h +++ b/deps/v8/src/ia32/assembler-ia32.h @@ -464,6 +464,8 @@ class Assembler : public Malloced { // to jump to. static const int kPatchReturnSequenceAddressOffset = 1; // JMP imm32. + static const int kCallInstructionLength = 5; + static const int kJSReturnSequenceLength = 6; // --------------------------------------------------------------------------- // Code generation diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc index ac2a7a026..7c8ff31f6 100644 --- a/deps/v8/src/ia32/codegen-ia32.cc +++ b/deps/v8/src/ia32/codegen-ia32.cc @@ -2490,7 +2490,7 @@ void CodeGenerator::GenerateReturnSequence(Result* return_value) { #ifdef ENABLE_DEBUGGER_SUPPORT // Check that the size of the code used for returning matches what is // expected by the debugger. - ASSERT_EQ(Debug::kIa32JSReturnSequenceLength, + ASSERT_EQ(Assembler::kJSReturnSequenceLength, masm_->SizeOfCodeGeneratedSince(&check_exit_codesize)); #endif } @@ -3056,13 +3056,59 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { jsobject.Bind(); // Get the set of properties (as a FixedArray or Map). // eax: value to be iterated over - frame_->EmitPush(eax); // push the object being iterated over (slot 4) + frame_->EmitPush(eax); // Push the object being iterated over. + // Check cache validity in generated code. This is a fast case for + // the JSObject::IsSimpleEnum cache validity checks. If we cannot + // guarantee cache validity, call the runtime system to check cache + // validity or get the property names in a fixed array. + JumpTarget call_runtime; + JumpTarget loop(JumpTarget::BIDIRECTIONAL); + JumpTarget check_prototype; + JumpTarget use_cache; + __ mov(ecx, eax); + loop.Bind(); + // Check that there are no elements. + __ mov(edx, FieldOperand(ecx, JSObject::kElementsOffset)); + __ cmp(Operand(edx), Immediate(Factory::empty_fixed_array())); + call_runtime.Branch(not_equal); + // Check that instance descriptors are not empty so that we can + // check for an enum cache. Leave the map in ebx for the subsequent + // prototype load. + __ mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset)); + __ mov(edx, FieldOperand(ebx, Map::kInstanceDescriptorsOffset)); + __ cmp(Operand(edx), Immediate(Factory::empty_descriptor_array())); + call_runtime.Branch(equal); + // Check that there in an enum cache in the non-empty instance + // descriptors. This is the case if the next enumeration index + // field does not contain a smi. + __ mov(edx, FieldOperand(edx, DescriptorArray::kEnumerationIndexOffset)); + __ test(edx, Immediate(kSmiTagMask)); + call_runtime.Branch(zero); + // For all objects but the receiver, check that the cache is empty. + __ cmp(ecx, Operand(eax)); + check_prototype.Branch(equal); + __ mov(edx, FieldOperand(edx, DescriptorArray::kEnumCacheBridgeCacheOffset)); + __ cmp(Operand(edx), Immediate(Factory::empty_fixed_array())); + call_runtime.Branch(not_equal); + check_prototype.Bind(); + // Load the prototype from the map and loop if non-null. + __ mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset)); + __ cmp(Operand(ecx), Immediate(Factory::null_value())); + loop.Branch(not_equal); + // The enum cache is valid. Load the map of the object being + // iterated over and use the cache for the iteration. + __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); + use_cache.Jump(); + + call_runtime.Bind(); + // Call the runtime to get the property names for the object. frame_->EmitPush(eax); // push the Object (slot 4) for the runtime call frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1); - // If we got a Map, we can do a fast modification check. - // Otherwise, we got a FixedArray, and we have to do a slow check. + // If we got a map from the runtime call, we can do a fast + // modification check. Otherwise, we got a fixed array, and we have + // to do a slow check. // eax: map or fixed array (result from call to // Runtime::kGetPropertyNamesFast) __ mov(edx, Operand(eax)); @@ -3070,9 +3116,13 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { __ cmp(ecx, Factory::meta_map()); fixed_array.Branch(not_equal); + use_cache.Bind(); // Get enum cache - // eax: map (result from call to Runtime::kGetPropertyNamesFast) + // eax: map (either the result from a call to + // Runtime::kGetPropertyNamesFast or has been fetched directly from + // the object) __ mov(ecx, Operand(eax)); + __ mov(ecx, FieldOperand(ecx, Map::kInstanceDescriptorsOffset)); // Get the bridge array held in the enumeration index field. __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumerationIndexOffset)); @@ -4777,18 +4827,8 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) { __ test(ecx, Immediate(kIsNotStringMask)); __ j(not_zero, &slow_case); - // Here we make assumptions about the tag values and the shifts needed. - // See the comment in objects.h. - ASSERT(kLongStringTag == 0); - ASSERT(kMediumStringTag + String::kLongLengthShift == - String::kMediumLengthShift); - ASSERT(kShortStringTag + String::kLongLengthShift == - String::kShortLengthShift); - __ and_(ecx, kStringSizeMask); - __ add(Operand(ecx), Immediate(String::kLongLengthShift)); // Fetch the length field into the temporary register. __ mov(temp.reg(), FieldOperand(object.reg(), String::kLengthOffset)); - __ shr_cl(temp.reg()); // Check for index out of range. __ cmp(index.reg(), Operand(temp.reg())); __ j(greater_equal, &slow_case); @@ -5222,6 +5262,18 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) { + ASSERT_EQ(2, args->length()); + + Load(args->at(0)); + Load(args->at(1)); + + StringAddStub stub(NO_STRING_ADD_FLAGS); + Result answer = frame_->CallStub(&stub, 2); + frame_->Push(&answer); +} + + void CodeGenerator::VisitCallRuntime(CallRuntime* node) { if (CheckForInlineRuntimeCall(node)) { return; @@ -6502,11 +6554,8 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { // String value => false iff empty. __ cmp(ecx, FIRST_NONSTRING_TYPE); __ j(above_equal, ¬_string); - __ and_(ecx, kStringSizeMask); - __ cmp(ecx, kShortStringTag); - __ j(not_equal, &true_result); // Empty string is always short. __ mov(edx, FieldOperand(eax, String::kLengthOffset)); - __ shr(edx, String::kShortLengthShift); + __ test(edx, Operand(edx)); __ j(zero, &false_result); __ jmp(&true_result); @@ -7042,7 +7091,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { switch (op_) { case Token::ADD: { // Test for string arguments before calling runtime. - Label not_strings, both_strings, not_string1, string1; + Label not_strings, not_string1, string1; Result answer; __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument. __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument. @@ -7057,8 +7106,9 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, edx); __ j(above_equal, &string1); - // First and second argument are strings. - __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1); + // First and second argument are strings. Jump to the string add stub. + StringAddStub stub(NO_STRING_CHECK_IN_STUB); + __ TailCallStub(&stub); // Only first argument is a string. __ bind(&string1); @@ -8185,6 +8235,224 @@ int CompareStub::MinorKey() { return (static_cast<unsigned>(cc_) << 1) | (strict_ ? 1 : 0); } + +void StringAddStub::Generate(MacroAssembler* masm) { + Label string_add_runtime; + + // Load the two arguments. + __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument. + __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument. + + // Make sure that both arguments are strings if not known in advance. + if (string_check_) { + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, &string_add_runtime); + __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx); + __ j(above_equal, &string_add_runtime); + + // First argument is a a string, test second. + __ test(edx, Immediate(kSmiTagMask)); + __ j(zero, &string_add_runtime); + __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx); + __ j(above_equal, &string_add_runtime); + } + + // Both arguments are strings. + // eax: first string + // edx: second string + // Check if either of the strings are empty. In that case return the other. + Label second_not_zero_length, both_not_zero_length; + __ mov(ecx, FieldOperand(edx, String::kLengthOffset)); + __ test(ecx, Operand(ecx)); + __ j(not_zero, &second_not_zero_length); + // Second string is empty, result is first string which is already in eax. + __ IncrementCounter(&Counters::string_add_native, 1); + __ ret(2 * kPointerSize); + __ bind(&second_not_zero_length); + __ mov(ebx, FieldOperand(eax, String::kLengthOffset)); + __ test(ebx, Operand(ebx)); + __ j(not_zero, &both_not_zero_length); + // First string is empty, result is second string which is in edx. + __ mov(eax, edx); + __ IncrementCounter(&Counters::string_add_native, 1); + __ ret(2 * kPointerSize); + + // Both strings are non-empty. + // eax: first string + // ebx: length of first string + // ecx: length of second string + // edx: second string + // Look at the length of the result of adding the two strings. + Label string_add_flat_result; + __ bind(&both_not_zero_length); + __ add(ebx, Operand(ecx)); + // Use the runtime system when adding two one character strings, as it + // contains optimizations for this specific case using the symbol table. + __ cmp(ebx, 2); + __ j(equal, &string_add_runtime); + // Check if resulting string will be flat. + __ cmp(ebx, String::kMinNonFlatLength); + __ j(below, &string_add_flat_result); + // Handle exceptionally long strings in the runtime system. + ASSERT((String::kMaxLength & 0x80000000) == 0); + __ cmp(ebx, String::kMaxLength); + __ j(above, &string_add_runtime); + + // If result is not supposed to be flat allocate a cons string object. If both + // strings are ascii the result is an ascii cons string. + Label non_ascii, allocated; + __ mov(edi, FieldOperand(eax, HeapObject::kMapOffset)); + __ movzx_b(ecx, FieldOperand(edi, Map::kInstanceTypeOffset)); + __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset)); + __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset)); + __ and_(ecx, Operand(edi)); + __ test(ecx, Immediate(kAsciiStringTag)); + __ j(zero, &non_ascii); + // Allocate an acsii cons string. + __ AllocateAsciiConsString(ecx, edi, no_reg, &string_add_runtime); + __ bind(&allocated); + // Fill the fields of the cons string. + __ mov(FieldOperand(ecx, ConsString::kLengthOffset), ebx); + __ mov(FieldOperand(ecx, ConsString::kHashFieldOffset), + Immediate(String::kEmptyHashField)); + __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax); + __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx); + __ mov(eax, ecx); + __ IncrementCounter(&Counters::string_add_native, 1); + __ ret(2 * kPointerSize); + __ bind(&non_ascii); + // Allocate a two byte cons string. + __ AllocateConsString(ecx, edi, no_reg, &string_add_runtime); + __ jmp(&allocated); + + // Handle creating a flat result. First check that both strings are not + // external strings. + // eax: first string + // ebx: length of resulting flat string + // edx: second string + __ bind(&string_add_flat_result); + __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); + __ and_(ecx, kStringRepresentationMask); + __ cmp(ecx, kExternalStringTag); + __ j(equal, &string_add_runtime); + __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); + __ and_(ecx, kStringRepresentationMask); + __ cmp(ecx, kExternalStringTag); + __ j(equal, &string_add_runtime); + // Now check if both strings are ascii strings. + // eax: first string + // ebx: length of resulting flat string + // edx: second string + Label non_ascii_string_add_flat_result; + __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); + ASSERT(kAsciiStringTag != 0); + __ test(ecx, Immediate(kAsciiStringTag)); + __ j(zero, &non_ascii_string_add_flat_result); + __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); + __ test(ecx, Immediate(kAsciiStringTag)); + __ j(zero, &string_add_runtime); + // Both strings are ascii strings. As they are short they are both flat. + __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime); + // eax: result string + __ mov(ecx, eax); + // Locate first character of result. + __ add(Operand(ecx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + // Load first argument and locate first character. + __ mov(edx, Operand(esp, 2 * kPointerSize)); + __ mov(edi, FieldOperand(edx, String::kLengthOffset)); + __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + // eax: result string + // ecx: first character of result + // edx: first char of first argument + // edi: length of first argument + GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true); + // Load second argument and locate first character. + __ mov(edx, Operand(esp, 1 * kPointerSize)); + __ mov(edi, FieldOperand(edx, String::kLengthOffset)); + __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + // eax: result string + // ecx: next character of result + // edx: first char of second argument + // edi: length of second argument + GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true); + __ IncrementCounter(&Counters::string_add_native, 1); + __ ret(2 * kPointerSize); + + // Handle creating a flat two byte result. + // eax: first string - known to be two byte + // ebx: length of resulting flat string + // edx: second string + __ bind(&non_ascii_string_add_flat_result); + __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); + __ and_(ecx, kAsciiStringTag); + __ j(not_zero, &string_add_runtime); + // Both strings are two byte strings. As they are short they are both + // flat. + __ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &string_add_runtime); + // eax: result string + __ mov(ecx, eax); + // Locate first character of result. + __ add(Operand(ecx), + Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); + // Load first argument and locate first character. + __ mov(edx, Operand(esp, 2 * kPointerSize)); + __ mov(edi, FieldOperand(edx, String::kLengthOffset)); + __ add(Operand(edx), + Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); + // eax: result string + // ecx: first character of result + // edx: first char of first argument + // edi: length of first argument + GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false); + // Load second argument and locate first character. + __ mov(edx, Operand(esp, 1 * kPointerSize)); + __ mov(edi, FieldOperand(edx, String::kLengthOffset)); + __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + // eax: result string + // ecx: next character of result + // edx: first char of second argument + // edi: length of second argument + GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false); + __ IncrementCounter(&Counters::string_add_native, 1); + __ ret(2 * kPointerSize); + + // Just jump to runtime to add the two strings. + __ bind(&string_add_runtime); + __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1); +} + + +void StringAddStub::GenerateCopyCharacters(MacroAssembler* masm, + Register dest, + Register src, + Register count, + Register scratch, + bool ascii) { + Label loop; + __ bind(&loop); + // This loop just copies one character at a time, as it is only used for very + // short strings. + if (ascii) { + __ mov_b(scratch, Operand(src, 0)); + __ mov_b(Operand(dest, 0), scratch); + __ add(Operand(src), Immediate(1)); + __ add(Operand(dest), Immediate(1)); + } else { + __ mov_w(scratch, Operand(src, 0)); + __ mov_w(Operand(dest, 0), scratch); + __ add(Operand(src), Immediate(2)); + __ add(Operand(dest), Immediate(2)); + } + __ sub(Operand(count), Immediate(1)); + __ j(not_zero, &loop); +} + + #undef __ } } // namespace v8::internal diff --git a/deps/v8/src/ia32/codegen-ia32.h b/deps/v8/src/ia32/codegen-ia32.h index ebab3caa1..11a5163db 100644 --- a/deps/v8/src/ia32/codegen-ia32.h +++ b/deps/v8/src/ia32/codegen-ia32.h @@ -546,6 +546,9 @@ class CodeGenerator: public AstVisitor { inline void GenerateMathSin(ZoneList<Expression*>* args); inline void GenerateMathCos(ZoneList<Expression*>* args); + // Fast support for StringAdd. + void GenerateStringAdd(ZoneList<Expression*>* args); + // Simple condition analysis. enum ConditionAnalysis { ALWAYS_TRUE, @@ -737,6 +740,37 @@ class GenericBinaryOpStub: public CodeStub { }; +// Flag that indicates how to generate code for the stub StringAddStub. +enum StringAddFlags { + NO_STRING_ADD_FLAGS = 0, + NO_STRING_CHECK_IN_STUB = 1 << 0 // Omit string check in stub. +}; + + +class StringAddStub: public CodeStub { + public: + explicit StringAddStub(StringAddFlags flags) { + string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0); + } + + private: + Major MajorKey() { return StringAdd; } + int MinorKey() { return string_check_ ? 0 : 1; } + + void Generate(MacroAssembler* masm); + + void GenerateCopyCharacters(MacroAssembler* masm, + Register desc, + Register src, + Register count, + Register scratch, + bool ascii); + + // Should the stub check whether arguments are strings? + bool string_check_; +}; + + } } // namespace v8::internal #endif // V8_IA32_CODEGEN_IA32_H_ diff --git a/deps/v8/src/ia32/debug-ia32.cc b/deps/v8/src/ia32/debug-ia32.cc index 2d20117aa..5ebe1e070 100644 --- a/deps/v8/src/ia32/debug-ia32.cc +++ b/deps/v8/src/ia32/debug-ia32.cc @@ -45,17 +45,17 @@ bool BreakLocationIterator::IsDebugBreakAtReturn() { // CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-ia32.cc // for the precise return instructions sequence. void BreakLocationIterator::SetDebugBreakAtReturn() { - ASSERT(Debug::kIa32JSReturnSequenceLength >= - Debug::kIa32CallInstructionLength); + ASSERT(Assembler::kJSReturnSequenceLength >= + Assembler::kCallInstructionLength); rinfo()->PatchCodeWithCall(Debug::debug_break_return()->entry(), - Debug::kIa32JSReturnSequenceLength - Debug::kIa32CallInstructionLength); + Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength); } // Restore the JS frame exit code. void BreakLocationIterator::ClearDebugBreakAtReturn() { rinfo()->PatchCode(original_rinfo()->pc(), - Debug::kIa32JSReturnSequenceLength); + Assembler::kJSReturnSequenceLength); } diff --git a/deps/v8/src/ia32/fast-codegen-ia32.cc b/deps/v8/src/ia32/fast-codegen-ia32.cc index a01d754e4..c5d544127 100644 --- a/deps/v8/src/ia32/fast-codegen-ia32.cc +++ b/deps/v8/src/ia32/fast-codegen-ia32.cc @@ -74,11 +74,41 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) { bool function_in_register = true; + // Possibly allocate a local context. + if (fun->scope()->num_heap_slots() > 0) { + Comment cmnt(masm_, "[ Allocate local context"); + // Argument to NewContext is the function, which is still in edi. + __ push(edi); + __ CallRuntime(Runtime::kNewContext, 1); + function_in_register = false; + // 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); + + // Copy parameters into context if necessary. + int num_parameters = fun->scope()->num_parameters(); + for (int i = 0; i < num_parameters; i++) { + Slot* slot = fun->scope()->parameter(i)->slot(); + if (slot != NULL && slot->type() == Slot::CONTEXT) { + int parameter_offset = StandardFrameConstants::kCallerSPOffset + + (num_parameters - 1 - i) * kPointerSize; + // Load parameter from stack. + __ mov(eax, Operand(ebp, parameter_offset)); + // Store it in the context + __ mov(Operand(esi, Context::SlotOffset(slot->index())), eax); + } + } + } + Variable* arguments = fun->scope()->arguments()->AsVariable(); if (arguments != NULL) { // Function uses arguments object. Comment cmnt(masm_, "[ Allocate arguments object"); - __ push(edi); + if (function_in_register) { + __ push(edi); + } else { + __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + } // Receiver is just before the parameters on the caller's stack. __ lea(edx, Operand(ebp, StandardFrameConstants::kCallerSPOffset + fun->num_parameters() * kPointerSize)); @@ -90,36 +120,13 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) { // stack frame was an arguments adapter frame. ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); __ CallStub(&stub); - __ mov(Operand(ebp, SlotOffset(arguments->slot())), eax); + __ mov(ecx, eax); // Duplicate result. + Move(arguments->slot(), eax, ebx, edx); Slot* dot_arguments_slot = fun->scope()->arguments_shadow()->AsVariable()->slot(); - __ mov(Operand(ebp, SlotOffset(dot_arguments_slot)), eax); - - function_in_register = false; + Move(dot_arguments_slot, ecx, ebx, edx); } - // Possibly allocate a local context. - if (fun->scope()->num_heap_slots() > 0) { - Comment cmnt(masm_, "[ Allocate local context"); - if (function_in_register) { - // Argument to NewContext is the function, still in edi. - __ push(edi); - } else { - // Argument to NewContext is the function, no longer in edi. - __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - } - __ CallRuntime(Runtime::kNewContext, 1); - // 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); -#ifdef DEBUG - // Assert we do not have to copy any parameters into the context. - for (int i = 0, len = fun->scope()->num_parameters(); i < len; i++) { - Slot* slot = fun->scope()->parameter(i)->slot(); - ASSERT(slot != NULL && slot->type() != Slot::CONTEXT); - } -#endif - } { Comment cmnt(masm_, "[ Declarations"); VisitDeclarations(fun->scope()->declarations()); @@ -180,7 +187,7 @@ void FastCodeGenerator::EmitReturnSequence(int position) { #ifdef ENABLE_DEBUGGER_SUPPORT // Check that the size of the code used for returning matches what is // expected by the debugger. - ASSERT_EQ(Debug::kIa32JSReturnSequenceLength, + ASSERT_EQ(Assembler::kJSReturnSequenceLength, masm_->SizeOfCodeGeneratedSince(&check_exit_codesize)); #endif } @@ -220,20 +227,54 @@ void FastCodeGenerator::Move(Expression::Context context, Register source) { } -void FastCodeGenerator::Move(Expression::Context context, Slot* source) { +template <> +Operand FastCodeGenerator::CreateSlotOperand<Operand>(Slot* source, + Register scratch) { + switch (source->type()) { + case Slot::PARAMETER: + case Slot::LOCAL: + return Operand(ebp, SlotOffset(source)); + case Slot::CONTEXT: { + int context_chain_length = + function_->scope()->ContextChainLength(source->var()->scope()); + __ LoadContext(scratch, context_chain_length); + return CodeGenerator::ContextOperand(scratch, source->index()); + break; + } + case Slot::LOOKUP: + UNIMPLEMENTED(); + // Fall-through. + default: + UNREACHABLE(); + return Operand(eax, 0); // Dead code to make the compiler happy. + } +} + + +void FastCodeGenerator::Move(Register dst, Slot* source) { + Operand location = CreateSlotOperand<Operand>(source, dst); + __ mov(dst, location); +} + + +void FastCodeGenerator::Move(Expression::Context context, + Slot* source, + Register scratch) { switch (context) { case Expression::kUninitialized: UNREACHABLE(); case Expression::kEffect: break; - case Expression::kValue: - __ push(Operand(ebp, SlotOffset(source))); + case Expression::kValue: { + Operand location = CreateSlotOperand<Operand>(source, scratch); + __ push(location); break; + } case Expression::kTest: // Fall through. case Expression::kValueTest: // Fall through. case Expression::kTestValue: - __ mov(eax, Operand(ebp, SlotOffset(source))); - Move(context, eax); + Move(scratch, source); + Move(context, scratch); break; } } @@ -258,24 +299,61 @@ void FastCodeGenerator::Move(Expression::Context context, Literal* expr) { } +void FastCodeGenerator::Move(Slot* dst, + Register src, + Register scratch1, + Register scratch2) { + switch (dst->type()) { + case Slot::PARAMETER: + case Slot::LOCAL: + __ mov(Operand(ebp, SlotOffset(dst)), src); + break; + case Slot::CONTEXT: { + ASSERT(!src.is(scratch1)); + ASSERT(!src.is(scratch2)); + ASSERT(!scratch1.is(scratch2)); + int context_chain_length = + function_->scope()->ContextChainLength(dst->var()->scope()); + __ LoadContext(scratch1, context_chain_length); + __ mov(Operand(scratch1, Context::SlotOffset(dst->index())), src); + int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize; + __ RecordWrite(scratch1, offset, src, scratch2); + break; + } + case Slot::LOOKUP: + UNIMPLEMENTED(); + default: + UNREACHABLE(); + } +} + + void FastCodeGenerator::DropAndMove(Expression::Context context, - Register source) { + Register source, + int count) { + ASSERT(count > 0); switch (context) { case Expression::kUninitialized: UNREACHABLE(); case Expression::kEffect: - __ add(Operand(esp), Immediate(kPointerSize)); + __ add(Operand(esp), Immediate(count * kPointerSize)); break; case Expression::kValue: + if (count > 1) { + __ add(Operand(esp), Immediate((count - 1) * kPointerSize)); + } __ mov(Operand(esp, 0), source); break; case Expression::kTest: ASSERT(!source.is(esp)); - __ add(Operand(esp), Immediate(kPointerSize)); + __ add(Operand(esp), Immediate(count * kPointerSize)); TestAndBranch(source, true_label_, false_label_); break; case Expression::kValueTest: { Label discard; + if (count > 1) { + __ add(Operand(esp), Immediate((count - 1) * kPointerSize)); + } __ mov(Operand(esp, 0), source); TestAndBranch(source, true_label_, &discard); __ bind(&discard); @@ -285,6 +363,9 @@ void FastCodeGenerator::DropAndMove(Expression::Context context, } case Expression::kTestValue: { Label discard; + if (count > 1) { + __ add(Operand(esp), Immediate((count - 1) * kPointerSize)); + } __ mov(Operand(esp, 0), source); TestAndBranch(source, &discard, false_label_); __ bind(&discard); @@ -380,6 +461,7 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { } __ mov(CodeGenerator::ContextOperand(esi, slot->index()), eax); // No write barrier since the_hole_value is in old space. + ASSERT(!Heap::InNewSpace(*Factory::the_hole_value())); } else if (decl->fun() != NULL) { Visit(decl->fun()); __ pop(eax); @@ -391,7 +473,7 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { __ Check(equal, "Unexpected declaration in current context."); } __ mov(CodeGenerator::ContextOperand(esi, slot->index()), eax); - int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; + int offset = Context::SlotOffset(slot->index()); __ RecordWrite(esi, offset, eax, ecx); } break; @@ -464,53 +546,61 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { DropAndMove(expr->context(), eax); } else if (rewrite->AsSlot() != NULL) { Slot* slot = rewrite->AsSlot(); - switch (slot->type()) { - case Slot::LOCAL: - case Slot::PARAMETER: { - Comment cmnt(masm_, "Stack slot"); - Move(expr->context(), slot); - break; - } - - case Slot::CONTEXT: { - Comment cmnt(masm_, "Context slot"); - int chain_length = - function_->scope()->ContextChainLength(slot->var()->scope()); - if (chain_length > 0) { - // Move up the chain of contexts to the context containing the slot. - __ mov(eax, - Operand(esi, Context::SlotOffset(Context::CLOSURE_INDEX))); - // Load the function context (which is the incoming, outer context). - __ mov(eax, FieldOperand(eax, JSFunction::kContextOffset)); - for (int i = 1; i < chain_length; i++) { - __ mov(eax, - Operand(eax, Context::SlotOffset(Context::CLOSURE_INDEX))); - __ mov(eax, FieldOperand(eax, JSFunction::kContextOffset)); - } - // The context may be an intermediate context, not a function context. - __ mov(eax, - Operand(eax, Context::SlotOffset(Context::FCONTEXT_INDEX))); - } else { // Slot is in the current function context. - // The context may be an intermediate context, not a function context. - __ mov(eax, - Operand(esi, Context::SlotOffset(Context::FCONTEXT_INDEX))); + if (FLAG_debug_code) { + switch (slot->type()) { + case Slot::LOCAL: + case Slot::PARAMETER: { + Comment cmnt(masm_, "Stack slot"); + break; } - __ mov(eax, Operand(eax, Context::SlotOffset(slot->index()))); - Move(expr->context(), eax); - break; + case Slot::CONTEXT: { + Comment cmnt(masm_, "Context slot"); + break; + } + case Slot::LOOKUP: + UNIMPLEMENTED(); + break; + default: + UNREACHABLE(); } - - case Slot::LOOKUP: - UNREACHABLE(); - break; } + Move(expr->context(), slot, eax); } else { - // The parameter variable has been rewritten into an explict access to - // the arguments object. + Comment cmnt(masm_, "Variable rewritten to Property"); + // A variable has been rewritten into an explicit access to + // an object property. Property* property = rewrite->AsProperty(); ASSERT_NOT_NULL(property); - ASSERT_EQ(expr->context(), property->context()); - Visit(property); + + // Currently the only parameter expressions that can occur are + // on the form "slot[literal]". + + // Check that the object is in a slot. + Variable* object_var = property->obj()->AsVariableProxy()->AsVariable(); + ASSERT_NOT_NULL(object_var); + Slot* object_slot = object_var->slot(); + ASSERT_NOT_NULL(object_slot); + + // Load the object. + Move(Expression::kValue, object_slot, eax); + + // Check that the key is a smi. + Literal* key_literal = property->key()->AsLiteral(); + ASSERT_NOT_NULL(key_literal); + ASSERT(key_literal->handle()->IsSmi()); + + // Load the key. + Move(Expression::kValue, key_literal); + + // Do a KEYED property load. + Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + // Notice: We must not have a "test eax, ..." instruction after + // the call. It is treated specially by the LoadIC code. + __ nop(); + + // Drop key and object left on the stack by IC, and push the result. + DropAndMove(expr->context(), eax, 2); } } @@ -575,8 +665,9 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ CallRuntime(Runtime::kCloneLiteralBoilerplate, 1); } - // If result_saved == true: the result is saved on top of the stack. - // If result_saved == false: the result not on the stack, just is in eax. + // If result_saved == true: The result is saved on top of the + // stack and in eax. + // If result_saved == false: The result not on the stack, just in eax. bool result_saved = false; for (int i = 0; i < expr->properties()->length(); i++) { @@ -601,6 +692,7 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ call(ic, RelocInfo::CODE_TARGET); // StoreIC leaves the receiver on the stack. + __ mov(eax, Operand(esp, 0)); // Restore result into eax. break; } // fall through @@ -776,34 +868,34 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) { // Overwrite the receiver on the stack with the result if needed. DropAndMove(expr->context(), eax); - } else { + } else if (var->slot() != NULL) { Slot* slot = var->slot(); - ASSERT_NOT_NULL(slot); // Variables rewritten as properties not handled. switch (slot->type()) { case Slot::LOCAL: case Slot::PARAMETER: { + Operand target = Operand(ebp, SlotOffset(var->slot())); switch (expr->context()) { case Expression::kUninitialized: UNREACHABLE(); case Expression::kEffect: // Perform assignment and discard value. - __ pop(Operand(ebp, SlotOffset(var->slot()))); + __ pop(target); break; case Expression::kValue: // Perform assignment and preserve value. __ mov(eax, Operand(esp, 0)); - __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + __ mov(target, eax); break; case Expression::kTest: // Perform assignment and test (and discard) value. __ pop(eax); - __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + __ mov(target, eax); TestAndBranch(eax, true_label_, false_label_); break; case Expression::kValueTest: { Label discard; __ mov(eax, Operand(esp, 0)); - __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + __ mov(target, eax); TestAndBranch(eax, true_label_, &discard); __ bind(&discard); __ add(Operand(esp), Immediate(kPointerSize)); @@ -813,7 +905,7 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) { case Expression::kTestValue: { Label discard; __ mov(eax, Operand(esp, 0)); - __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + __ mov(target, eax); TestAndBranch(eax, &discard, false_label_); __ bind(&discard); __ add(Operand(esp), Immediate(kPointerSize)); @@ -868,6 +960,35 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) { UNREACHABLE(); break; } + } else { + Property* property = var->rewrite()->AsProperty(); + ASSERT_NOT_NULL(property); + + // Load object and key onto the stack. + Slot* object_slot = property->obj()->AsSlot(); + ASSERT_NOT_NULL(object_slot); + Move(Expression::kValue, object_slot, eax); + + Literal* key_literal = property->key()->AsLiteral(); + ASSERT_NOT_NULL(key_literal); + Move(Expression::kValue, key_literal); + + // Value to store was pushed before object and key on the stack. + __ mov(eax, Operand(esp, 2 * kPointerSize)); + + // Arguments to ic is value in eax, object and key on stack. + Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + + if (expr->context() == Expression::kEffect) { + __ add(Operand(esp), Immediate(3 * kPointerSize)); + } else if (expr->context() == Expression::kValue) { + // Value is still on the stack in esp[2 * kPointerSize] + __ add(Operand(esp), Immediate(2 * kPointerSize)); + } else { + __ mov(eax, Operand(esp, 2 * kPointerSize)); + DropAndMove(expr->context(), eax, 3); + } } } @@ -1127,9 +1248,13 @@ void FastCodeGenerator::VisitCallNew(CallNew* expr) { void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Comment cmnt(masm_, "[ CallRuntime"); ZoneList<Expression*>* args = expr->arguments(); - Runtime::Function* function = expr->function(); - ASSERT(function != NULL); + if (expr->is_jsruntime()) { + // Prepare for calling JS runtime function. + __ push(Immediate(expr->name())); + __ mov(eax, CodeGenerator::GlobalObject()); + __ push(FieldOperand(eax, GlobalObject::kBuiltinsOffset)); + } // Push the arguments ("left-to-right"). int arg_count = args->length(); @@ -1138,8 +1263,20 @@ void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) { ASSERT_EQ(Expression::kValue, args->at(i)->context()); } - __ CallRuntime(function, arg_count); - Move(expr->context(), eax); + if (expr->is_jsruntime()) { + // Call the JS runtime function. + Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, + NOT_IN_LOOP); + __ call(ic, RelocInfo::CODE_TARGET); + // Restore context register. + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + // Discard the function left on TOS. + DropAndMove(expr->context(), eax); + } else { + // Call the C runtime function. + __ CallRuntime(expr->function(), arg_count); + Move(expr->context(), eax); + } } diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc index 3aa3c3467..6988fe09f 100644 --- a/deps/v8/src/ia32/ic-ia32.cc +++ b/deps/v8/src/ia32/ic-ia32.cc @@ -31,6 +31,7 @@ #include "ic-inl.h" #include "runtime.h" #include "stub-cache.h" +#include "utils.h" namespace v8 { namespace internal { @@ -108,7 +109,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label, StringDictionary::kElementsStartIndex * kPointerSize; for (int i = 0; i < kProbes; i++) { // Compute the masked index: (hash + i + i * i) & mask. - __ mov(r1, FieldOperand(name, String::kLengthOffset)); + __ mov(r1, FieldOperand(name, String::kHashFieldOffset)); __ shr(r1, String::kHashShift); if (i > 0) { __ add(Operand(r1), Immediate(StringDictionary::GetProbeOffset(i))); @@ -216,18 +217,6 @@ void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) { } -#ifdef DEBUG -// For use in assert below. -static int TenToThe(int exponent) { - ASSERT(exponent <= 9); - ASSERT(exponent >= 1); - int answer = 10; - for (int i = 1; i < exponent; i++) answer *= 10; - return answer; -} -#endif - - void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- esp[0] : return address @@ -309,7 +298,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, edx); __ j(above_equal, &slow); // Is the string an array index, with cached numeric value? - __ mov(ebx, FieldOperand(eax, String::kLengthOffset)); + __ mov(ebx, FieldOperand(eax, String::kHashFieldOffset)); __ test(ebx, Immediate(String::kIsArrayIndexMask)); __ j(not_zero, &index_string, not_taken); @@ -324,20 +313,16 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ mov(eax, Operand(ecx)); __ IncrementCounter(&Counters::keyed_load_generic_symbol, 1); __ ret(0); - // Array index string: If short enough use cache in length/hash field (ebx). - // We assert that there are enough bits in an int32_t after the hash shift - // bits have been subtracted to allow space for the length and the cached - // array index. + // If the hash field contains an array index pick it out. The assert checks + // that the constants for the maximum number of digits for an array index + // cached in the hash field and the number of bits reserved for it does not + // conflict. ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) < - (1 << (String::kShortLengthShift - String::kHashShift))); + (1 << String::kArrayIndexValueBits)); __ bind(&index_string); - const int kLengthFieldLimit = - (String::kMaxCachedArrayIndexLength + 1) << String::kShortLengthShift; - __ cmp(ebx, kLengthFieldLimit); - __ j(above_equal, &slow); __ mov(eax, Operand(ebx)); - __ and_(eax, (1 << String::kShortLengthShift) - 1); - __ shr(eax, String::kLongLengthShift); + __ and_(eax, String::kArrayIndexHashMask); + __ shr(eax, String::kHashShift); __ jmp(&index_int); } @@ -403,13 +388,13 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, __ movsx_b(eax, Operand(ecx, eax, times_1, 0)); break; case kExternalUnsignedByteArray: - __ mov_b(eax, Operand(ecx, eax, times_1, 0)); + __ movzx_b(eax, Operand(ecx, eax, times_1, 0)); break; case kExternalShortArray: __ movsx_w(eax, Operand(ecx, eax, times_2, 0)); break; case kExternalUnsignedShortArray: - __ mov_w(eax, Operand(ecx, eax, times_2, 0)); + __ movzx_w(eax, Operand(ecx, eax, times_2, 0)); break; case kExternalIntArray: case kExternalUnsignedIntArray: diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc index 010433e16..b91caa8cc 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -213,6 +213,13 @@ void MacroAssembler::RecordWrite(Register object, int offset, } +void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) { + cmp(esp, + Operand::StaticVariable(ExternalReference::address_of_stack_limit())); + j(below, on_stack_overflow); +} + + #ifdef ENABLE_DEBUGGER_SUPPORT void MacroAssembler::SaveRegistersToMemory(RegList regs) { ASSERT((regs & ~kJSCallerSaved) == 0); @@ -680,6 +687,11 @@ void MacroAssembler::LoadAllocationTopHelper(Register result, void MacroAssembler::UpdateAllocationTopHelper(Register result_end, Register scratch) { + if (FLAG_debug_code) { + test(result_end, Immediate(kObjectAlignmentMask)); + Check(zero, "Unaligned allocation in new space"); + } + ExternalReference new_space_allocation_top = ExternalReference::new_space_allocation_top_address(); @@ -813,6 +825,109 @@ void MacroAssembler::AllocateHeapNumber(Register result, } +void MacroAssembler::AllocateTwoByteString(Register result, + Register length, + Register scratch1, + Register scratch2, + Register scratch3, + Label* gc_required) { + // Calculate the number of bytes needed for the characters in the string while + // observing object alignment. + ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0); + mov(scratch1, length); + ASSERT(kShortSize == 2); + shl(scratch1, 1); + add(Operand(scratch1), Immediate(kObjectAlignmentMask)); + and_(Operand(scratch1), Immediate(~kObjectAlignmentMask)); + + // Allocate two byte string in new space. + AllocateInNewSpace(SeqTwoByteString::kHeaderSize, + times_1, + scratch1, + result, + scratch2, + scratch3, + gc_required, + TAG_OBJECT); + + // Set the map, length and hash field. + mov(FieldOperand(result, HeapObject::kMapOffset), + Immediate(Factory::string_map())); + mov(FieldOperand(result, String::kLengthOffset), length); + mov(FieldOperand(result, String::kHashFieldOffset), + Immediate(String::kEmptyHashField)); +} + + +void MacroAssembler::AllocateAsciiString(Register result, + Register length, + Register scratch1, + Register scratch2, + Register scratch3, + Label* gc_required) { + // Calculate the number of bytes needed for the characters in the string while + // observing object alignment. + ASSERT((SeqAsciiString::kHeaderSize & kObjectAlignmentMask) == 0); + mov(scratch1, length); + ASSERT(kCharSize == 1); + add(Operand(scratch1), Immediate(kObjectAlignmentMask)); + and_(Operand(scratch1), Immediate(~kObjectAlignmentMask)); + + // Allocate ascii string in new space. + AllocateInNewSpace(SeqAsciiString::kHeaderSize, + times_1, + scratch1, + result, + scratch2, + scratch3, + gc_required, + TAG_OBJECT); + + // Set the map, length and hash field. + mov(FieldOperand(result, HeapObject::kMapOffset), + Immediate(Factory::ascii_string_map())); + mov(FieldOperand(result, String::kLengthOffset), length); + mov(FieldOperand(result, String::kHashFieldOffset), + Immediate(String::kEmptyHashField)); +} + + +void MacroAssembler::AllocateConsString(Register result, + Register scratch1, + Register scratch2, + Label* gc_required) { + // Allocate heap number in new space. + AllocateInNewSpace(ConsString::kSize, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + + // Set the map. The other fields are left uninitialized. + mov(FieldOperand(result, HeapObject::kMapOffset), + Immediate(Factory::cons_string_map())); +} + + +void MacroAssembler::AllocateAsciiConsString(Register result, + Register scratch1, + Register scratch2, + Label* gc_required) { + // Allocate heap number in new space. + AllocateInNewSpace(ConsString::kSize, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + + // Set the map. The other fields are left uninitialized. + mov(FieldOperand(result, HeapObject::kMapOffset), + Immediate(Factory::cons_ascii_string_map())); +} + + void MacroAssembler::NegativeZeroTest(CodeGenerator* cgen, Register result, Register op, @@ -906,6 +1021,12 @@ void MacroAssembler::CallStub(CodeStub* stub) { } +void MacroAssembler::TailCallStub(CodeStub* stub) { + ASSERT(allow_stub_calls()); // calls are not allowed in some stubs + jmp(stub->GetCode(), RelocInfo::CODE_TARGET); +} + + void MacroAssembler::StubReturn(int argc) { ASSERT(argc >= 1 && generating_stub()); ret((argc - 1) * kPointerSize); @@ -1185,6 +1306,26 @@ Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id, } +void MacroAssembler::LoadContext(Register dst, int context_chain_length) { + if (context_chain_length > 0) { + // Move up the chain of contexts to the context containing the slot. + mov(dst, Operand(esi, Context::SlotOffset(Context::CLOSURE_INDEX))); + // Load the function context (which is the incoming, outer context). + mov(dst, FieldOperand(dst, JSFunction::kContextOffset)); + for (int i = 1; i < context_chain_length; i++) { + mov(dst, Operand(dst, Context::SlotOffset(Context::CLOSURE_INDEX))); + mov(dst, FieldOperand(dst, JSFunction::kContextOffset)); + } + // The context may be an intermediate context, not a function context. + mov(dst, Operand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX))); + } else { // Slot is in the current function context. + // The context may be an intermediate context, not a function context. + mov(dst, Operand(esi, Context::SlotOffset(Context::FCONTEXT_INDEX))); + } +} + + + void MacroAssembler::Ret() { ret(0); } @@ -1252,11 +1393,15 @@ void MacroAssembler::Abort(const char* msg) { RecordComment(msg); } #endif + // Disable stub call restrictions to always allow calls to abort. + set_allow_stub_calls(true); + push(eax); push(Immediate(p0)); push(Immediate(reinterpret_cast<intptr_t>(Smi::FromInt(p1 - p0)))); CallRuntime(Runtime::kAbort, 2); // will not return here + int3(); } diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h index 248aa7776..a41d42e82 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.h +++ b/deps/v8/src/ia32/macro-assembler-ia32.h @@ -69,6 +69,12 @@ class MacroAssembler: public Assembler { #endif // --------------------------------------------------------------------------- + // Stack limit support + + // Do simple test for stack overflow. This doesn't handle an overflow. + void StackLimitCheck(Label* on_stack_limit_hit); + + // --------------------------------------------------------------------------- // Activation frames void EnterInternalFrame() { EnterFrame(StackFrame::INTERNAL); } @@ -90,6 +96,8 @@ class MacroAssembler: public Assembler { // argument in register esi. void LeaveExitFrame(ExitFrame::Mode mode); + // Find the function context up the context chain. + void LoadContext(Register dst, int context_chain_length); // --------------------------------------------------------------------------- // JavaScript invokes @@ -175,7 +183,7 @@ class MacroAssembler: public Assembler { // scratch can be passed as no_reg in which case an additional object // reference will be added to the reloc info. The returned pointers in result // and result_end have not yet been tagged as heap objects. If - // result_contains_top_on_entry is true the contnt of result is known to be + // result_contains_top_on_entry is true the content of result is known to be // the allocation top on entry (could be result_end from a previous call to // AllocateInNewSpace). If result_contains_top_on_entry is true scratch // should be no_reg as it is never used. @@ -217,6 +225,32 @@ class MacroAssembler: public Assembler { Register scratch2, Label* gc_required); + // Allocate a sequential string. All the header fields of the string object + // are initialized. + void AllocateTwoByteString(Register result, + Register length, + Register scratch1, + Register scratch2, + Register scratch3, + Label* gc_required); + void AllocateAsciiString(Register result, + Register length, + Register scratch1, + Register scratch2, + Register scratch3, + Label* gc_required); + + // Allocate a raw cons string object. Only the map field of the result is + // initialized. + void AllocateConsString(Register result, + Register scratch1, + Register scratch2, + Label* gc_required); + void AllocateAsciiConsString(Register result, + Register scratch1, + Register scratch2, + Label* gc_required); + // --------------------------------------------------------------------------- // Support functions. @@ -254,6 +288,9 @@ class MacroAssembler: public Assembler { // Call a code stub. void CallStub(CodeStub* stub); + // Tail call a code stub (jump). + void TailCallStub(CodeStub* stub); + // Return from a code stub after popping its arguments. void StubReturn(int argc); diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc index f9f986afe..425c51dca 100644 --- a/deps/v8/src/ia32/stub-cache-ia32.cc +++ b/deps/v8/src/ia32/stub-cache-ia32.cc @@ -126,7 +126,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm, __ j(zero, &miss, not_taken); // Get the map of the receiver and compute the hash. - __ mov(scratch, FieldOperand(name, String::kLengthOffset)); + __ mov(scratch, FieldOperand(name, String::kHashFieldOffset)); __ add(scratch, FieldOperand(receiver, HeapObject::kMapOffset)); __ xor_(scratch, flags); __ and_(scratch, (kPrimaryTableSize - 1) << kHeapObjectTagSize); @@ -135,7 +135,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ProbeTable(masm, flags, kPrimary, name, scratch, extra); // Primary miss: Compute hash for secondary probe. - __ mov(scratch, FieldOperand(name, String::kLengthOffset)); + __ mov(scratch, FieldOperand(name, String::kHashFieldOffset)); __ add(scratch, FieldOperand(receiver, HeapObject::kMapOffset)); __ xor_(scratch, flags); __ and_(scratch, (kPrimaryTableSize - 1) << kHeapObjectTagSize); @@ -234,13 +234,9 @@ void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm, // scratch register. GenerateStringCheck(masm, receiver, scratch, miss, &check_wrapper); - // Load length directly from the string. + // Load length from the string and convert to a smi. __ bind(&load_length); - __ and_(scratch, kStringSizeMask); __ mov(eax, FieldOperand(receiver, String::kLengthOffset)); - // ecx is also the receiver. - __ lea(ecx, Operand(scratch, String::kLongLengthShift)); - __ shr_cl(eax); __ shl(eax, kSmiTagSize); __ ret(0); diff --git a/deps/v8/src/log.cc b/deps/v8/src/log.cc index aec813d97..bbce926c8 100644 --- a/deps/v8/src/log.cc +++ b/deps/v8/src/log.cc @@ -680,22 +680,51 @@ class CompressionHelper { #endif // ENABLE_LOGGING_AND_PROFILING -void Logger::CallbackEvent(String* name, Address entry_point) { #ifdef ENABLE_LOGGING_AND_PROFILING +void Logger::CallbackEventInternal(const char* prefix, const char* name, + Address entry_point) { 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<char> str = - name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); - msg.Append(",1,\"%s\"", *str); + msg.Append(",1,\"%s%s\"", prefix, name); if (FLAG_compress_log) { ASSERT(compression_helper_ != NULL); if (!compression_helper_->HandleMessage(&msg)) return; } msg.Append('\n'); msg.WriteToLogFile(); +} +#endif + + +void Logger::CallbackEvent(String* name, Address entry_point) { +#ifdef ENABLE_LOGGING_AND_PROFILING + if (!Log::IsEnabled() || !FLAG_log_code) return; + SmartPointer<char> str = + name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); + CallbackEventInternal("", *str, entry_point); +#endif +} + + +void Logger::GetterCallbackEvent(String* name, Address entry_point) { +#ifdef ENABLE_LOGGING_AND_PROFILING + if (!Log::IsEnabled() || !FLAG_log_code) return; + SmartPointer<char> str = + name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); + CallbackEventInternal("get ", *str, entry_point); +#endif +} + + +void Logger::SetterCallbackEvent(String* name, Address entry_point) { +#ifdef ENABLE_LOGGING_AND_PROFILING + if (!Log::IsEnabled() || !FLAG_log_code) return; + SmartPointer<char> str = + name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); + CallbackEventInternal("set ", *str, entry_point); #endif } @@ -1098,6 +1127,7 @@ void Logger::ResumeProfiler(int flags) { LOG(UncheckedStringEvent("profiler", "resume")); FLAG_log_code = true; LogCompiledFunctions(); + LogAccessorCallbacks(); if (!FLAG_sliding_state_window) ticker_->Start(); } profiler_->resume(); @@ -1242,6 +1272,28 @@ void Logger::LogCompiledFunctions() { DeleteArray(sfis); } + +void Logger::LogAccessorCallbacks() { + AssertNoAllocation no_alloc; + HeapIterator iterator; + while (iterator.has_next()) { + HeapObject* obj = iterator.next(); + ASSERT(obj != NULL); + if (!obj->IsAccessorInfo()) continue; + AccessorInfo* ai = AccessorInfo::cast(obj); + if (!ai->name()->IsString()) continue; + String* name = String::cast(ai->name()); + Address getter_entry = v8::ToCData<Address>(ai->getter()); + if (getter_entry != 0) { + LOG(GetterCallbackEvent(name, getter_entry)); + } + Address setter_entry = v8::ToCData<Address>(ai->setter()); + if (setter_entry != 0) { + LOG(SetterCallbackEvent(name, setter_entry)); + } + } +} + #endif diff --git a/deps/v8/src/log.h b/deps/v8/src/log.h index f099f02b6..4d5acced6 100644 --- a/deps/v8/src/log.h +++ b/deps/v8/src/log.h @@ -208,6 +208,8 @@ class Logger { // ==== Events logged by --log-code. ==== // Emits a code event for a callback function. static void CallbackEvent(String* name, Address entry_point); + static void GetterCallbackEvent(String* name, Address entry_point); + static void SetterCallbackEvent(String* name, Address entry_point); // Emits a code create event. static void CodeCreateEvent(LogEventsAndTags tag, Code* code, const char* source); @@ -273,6 +275,8 @@ class Logger { // Logs all compiled functions found in the heap. static void LogCompiledFunctions(); + // Logs all accessor callbacks found in the heap. + static void LogAccessorCallbacks(); // Used for logging stubs found in the snapshot. static void LogCodeObject(Object* code_object); @@ -287,6 +291,11 @@ class Logger { // Emits the profiler's first message. static void ProfilerBeginEvent(); + // Emits callback event messages. + static void CallbackEventInternal(const char* prefix, + const char* name, + Address entry_point); + // Emits aliases for compressed messages. static void LogAliases(); diff --git a/deps/v8/src/messages.js b/deps/v8/src/messages.js index 27207928c..1e5053d7e 100644 --- a/deps/v8/src/messages.js +++ b/deps/v8/src/messages.js @@ -238,14 +238,15 @@ function MakeError(type, args) { Script.prototype.lineFromPosition = function(position) { var lower = 0; var upper = this.lineCount() - 1; + var line_ends = this.line_ends; // We'll never find invalid positions so bail right away. - if (position > this.line_ends[upper]) { + if (position > line_ends[upper]) { return -1; } // This means we don't have to safe-guard indexing line_ends[i - 1]. - if (position <= this.line_ends[0]) { + if (position <= line_ends[0]) { return 0; } @@ -253,9 +254,9 @@ Script.prototype.lineFromPosition = function(position) { while (upper >= 1) { var i = (lower + upper) >> 1; - if (position > this.line_ends[i]) { + if (position > line_ends[i]) { lower = i + 1; - } else if (position <= this.line_ends[i - 1]) { + } else if (position <= line_ends[i - 1]) { upper = i - 1; } else { return i; @@ -278,8 +279,9 @@ Script.prototype.locationFromPosition = function (position, if (line == -1) return null; // Determine start, end and column. - var start = line == 0 ? 0 : this.line_ends[line - 1] + 1; - var end = this.line_ends[line]; + var line_ends = this.line_ends; + var start = line == 0 ? 0 : line_ends[line - 1] + 1; + var end = line_ends[line]; if (end > 0 && StringCharAt.call(this.source, end - 1) == '\r') end--; var column = position - start; @@ -368,8 +370,9 @@ Script.prototype.sourceSlice = function (opt_from_line, opt_to_line) { return null; } - var from_position = from_line == 0 ? 0 : this.line_ends[from_line - 1] + 1; - var to_position = to_line == 0 ? 0 : this.line_ends[to_line - 1] + 1; + var line_ends = this.line_ends; + var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1; + var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1; // Return a source slice with line numbers re-adjusted to the resource. return new SourceSlice(this, from_line + this.line_offset, to_line + this.line_offset, @@ -391,8 +394,9 @@ Script.prototype.sourceLine = function (opt_line) { } // Return the source line. - var start = line == 0 ? 0 : this.line_ends[line - 1] + 1; - var end = this.line_ends[line]; + var line_ends = this.line_ends; + var start = line == 0 ? 0 : line_ends[line - 1] + 1; + var end = line_ends[line]; return StringSubstring.call(this.source, start, end); } @@ -625,10 +629,7 @@ CallSite.prototype.isEval = function () { CallSite.prototype.getEvalOrigin = function () { var script = %FunctionGetScript(this.fun); - if (!script || script.compilation_type != 1) - return null; - return new CallSite(null, script.eval_from_function, - script.eval_from_position); + return FormatEvalOrigin(script); }; CallSite.prototype.getFunction = function () { @@ -696,7 +697,7 @@ CallSite.prototype.getColumnNumber = function () { if (script) { location = script.locationFromPosition(this.pos, true); } - return location ? location.column : null; + return location ? location.column + 1: null; }; CallSite.prototype.isNative = function () { @@ -715,12 +716,44 @@ CallSite.prototype.isConstructor = function () { return this.fun === constructor; }; +function FormatEvalOrigin(script) { + var eval_origin = ""; + if (script.eval_from_function_name) { + eval_origin += script.eval_from_function_name; + } else { + eval_origin += "<anonymous>"; + } + + var eval_from_script = script.eval_from_script; + if (eval_from_script) { + if (eval_from_script.compilation_type == 1) { + // eval script originated from another eval. + eval_origin += " (eval at " + FormatEvalOrigin(eval_from_script) + ")"; + } else { + // eval script originated from "real" scource. + if (eval_from_script.name) { + eval_origin += " (" + eval_from_script.name; + var location = eval_from_script.locationFromPosition(script.eval_from_script_position, true); + if (location) { + eval_origin += ":" + (location.line + 1); + eval_origin += ":" + (location.column + 1); + } + eval_origin += ")" + } else { + eval_origin += " (unknown source)"; + } + } + } + + return eval_origin; +}; + function FormatSourcePosition(frame) { var fileLocation = ""; if (frame.isNative()) { fileLocation = "native"; } else if (frame.isEval()) { - fileLocation = "eval at " + FormatSourcePosition(frame.getEvalOrigin()); + fileLocation = "eval at " + frame.getEvalOrigin(); } else { var fileName = frame.getFileName(); if (fileName) { diff --git a/deps/v8/src/mirror-delay.js b/deps/v8/src/mirror-delay.js index 82fb9c228..ba663b2a2 100644 --- a/deps/v8/src/mirror-delay.js +++ b/deps/v8/src/mirror-delay.js @@ -1793,16 +1793,21 @@ ScriptMirror.prototype.context = function() { }; -ScriptMirror.prototype.evalFromFunction = function() { - return MakeMirror(this.script_.eval_from_function); +ScriptMirror.prototype.evalFromScript = function() { + return MakeMirror(this.script_.eval_from_script); +}; + + +ScriptMirror.prototype.evalFromFunctionName = function() { + return MakeMirror(this.script_.eval_from_function_name); }; ScriptMirror.prototype.evalFromLocation = function() { - var eval_from_function = this.evalFromFunction(); - if (!eval_from_function.isUndefined()) { - var position = this.script_.eval_from_position; - return eval_from_function.script().locationFromPosition(position, true); + var eval_from_script = this.evalFromScript(); + if (!eval_from_script.isUndefined()) { + var position = this.script_.eval_from_script_position; + return eval_from_script.locationFromPosition(position, true); } }; @@ -2080,12 +2085,15 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, // For compilation type eval emit information on the script from which // eval was called if a script is present. if (mirror.compilationType() == 1 && - mirror.evalFromFunction().script()) { + mirror.evalFromScript()) { content.evalFromScript = - this.serializeReference(mirror.evalFromFunction().script()); + this.serializeReference(mirror.evalFromScript()); var evalFromLocation = mirror.evalFromLocation() content.evalFromLocation = { line: evalFromLocation.line, column: evalFromLocation.column} + if (mirror.evalFromFunctionName()) { + content.evalFromFunctionName = mirror.evalFromFunctionName(); + } } if (mirror.context()) { content.context = this.serializeReference(mirror.context()); diff --git a/deps/v8/src/objects-debug.cc b/deps/v8/src/objects-debug.cc index 19c945d98..36f65eee8 100644 --- a/deps/v8/src/objects-debug.cc +++ b/deps/v8/src/objects-debug.cc @@ -547,42 +547,18 @@ static const char* TypeToString(InstanceType type) { case INVALID_TYPE: return "INVALID"; case MAP_TYPE: return "MAP"; case HEAP_NUMBER_TYPE: return "HEAP_NUMBER"; - case SHORT_SYMBOL_TYPE: - case MEDIUM_SYMBOL_TYPE: - case LONG_SYMBOL_TYPE: return "SYMBOL"; - case SHORT_ASCII_SYMBOL_TYPE: - case MEDIUM_ASCII_SYMBOL_TYPE: - case LONG_ASCII_SYMBOL_TYPE: return "ASCII_SYMBOL"; - case SHORT_CONS_SYMBOL_TYPE: - case MEDIUM_CONS_SYMBOL_TYPE: - case LONG_CONS_SYMBOL_TYPE: return "CONS_SYMBOL"; - case SHORT_CONS_ASCII_SYMBOL_TYPE: - case MEDIUM_CONS_ASCII_SYMBOL_TYPE: - case LONG_CONS_ASCII_SYMBOL_TYPE: return "CONS_ASCII_SYMBOL"; - case SHORT_EXTERNAL_ASCII_SYMBOL_TYPE: - case MEDIUM_EXTERNAL_ASCII_SYMBOL_TYPE: - case LONG_EXTERNAL_ASCII_SYMBOL_TYPE: - case SHORT_EXTERNAL_SYMBOL_TYPE: - case MEDIUM_EXTERNAL_SYMBOL_TYPE: - case LONG_EXTERNAL_SYMBOL_TYPE: return "EXTERNAL_SYMBOL"; - case SHORT_ASCII_STRING_TYPE: - case MEDIUM_ASCII_STRING_TYPE: - case LONG_ASCII_STRING_TYPE: return "ASCII_STRING"; - case SHORT_STRING_TYPE: - case MEDIUM_STRING_TYPE: - case LONG_STRING_TYPE: return "TWO_BYTE_STRING"; - case SHORT_CONS_STRING_TYPE: - case MEDIUM_CONS_STRING_TYPE: - case LONG_CONS_STRING_TYPE: - case SHORT_CONS_ASCII_STRING_TYPE: - case MEDIUM_CONS_ASCII_STRING_TYPE: - case LONG_CONS_ASCII_STRING_TYPE: return "CONS_STRING"; - case SHORT_EXTERNAL_ASCII_STRING_TYPE: - case MEDIUM_EXTERNAL_ASCII_STRING_TYPE: - case LONG_EXTERNAL_ASCII_STRING_TYPE: - case SHORT_EXTERNAL_STRING_TYPE: - case MEDIUM_EXTERNAL_STRING_TYPE: - case LONG_EXTERNAL_STRING_TYPE: return "EXTERNAL_STRING"; + case SYMBOL_TYPE: return "SYMBOL"; + case ASCII_SYMBOL_TYPE: return "ASCII_SYMBOL"; + case CONS_SYMBOL_TYPE: return "CONS_SYMBOL"; + case CONS_ASCII_SYMBOL_TYPE: return "CONS_ASCII_SYMBOL"; + case EXTERNAL_ASCII_SYMBOL_TYPE: + case EXTERNAL_SYMBOL_TYPE: return "EXTERNAL_SYMBOL"; + case ASCII_STRING_TYPE: return "ASCII_STRING"; + case STRING_TYPE: return "TWO_BYTE_STRING"; + case CONS_STRING_TYPE: + case CONS_ASCII_STRING_TYPE: return "CONS_STRING"; + case EXTERNAL_ASCII_STRING_TYPE: + case EXTERNAL_STRING_TYPE: return "EXTERNAL_STRING"; case FIXED_ARRAY_TYPE: return "FIXED_ARRAY"; case BYTE_ARRAY_TYPE: return "BYTE_ARRAY"; case PIXEL_ARRAY_TYPE: return "PIXEL_ARRAY"; @@ -1140,8 +1116,7 @@ void Script::ScriptVerify() { VerifyPointer(data()); VerifyPointer(wrapper()); type()->SmiVerify(); - VerifyPointer(line_ends_fixed_array()); - VerifyPointer(line_ends_js_array()); + VerifyPointer(line_ends()); VerifyPointer(id()); } @@ -1160,6 +1135,20 @@ void Script::ScriptPrint() { type()->ShortPrint(); PrintF("\n - id: "); id()->ShortPrint(); + PrintF("\n - data: "); + data()->ShortPrint(); + PrintF("\n - context data: "); + context_data()->ShortPrint(); + PrintF("\n - wrapper: "); + wrapper()->ShortPrint(); + PrintF("\n - compilation type: "); + compilation_type()->ShortPrint(); + PrintF("\n - line ends: "); + line_ends()->ShortPrint(); + PrintF("\n - eval from shared: "); + eval_from_shared()->ShortPrint(); + PrintF("\n - eval from instructions offset: "); + eval_from_instructions_offset()->ShortPrint(); PrintF("\n"); } diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index 7a6444de5..8514a412c 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -280,11 +280,6 @@ STATIC_CHECK((kStringRepresentationMask | kStringEncodingMask) == Internals::kFullStringRepresentationMask); -uint32_t StringShape::size_tag() { - return (type_ & kStringSizeMask); -} - - bool StringShape::IsSequentialAscii() { return full_representation_tag() == (kSeqStringTag | kAsciiStringTag); } @@ -921,25 +916,6 @@ HeapObject* MapWord::ToForwardingAddress() { } -bool MapWord::IsSerializationAddress() { - return HAS_SMI_TAG(reinterpret_cast<Object*>(value_)); -} - - -MapWord MapWord::FromSerializationAddress(int raw) { - // When the map word is being used as a serialization address we Smi-encode - // the serialization address (which is always a smallish positive integer). - return MapWord(reinterpret_cast<uintptr_t>(Smi::FromInt(raw))); -} - - -int MapWord::ToSerializationAddress() { - // When the map word is being used as a serialization address we treat the - // map word as a Smi and get the small integer that it encodes. - return reinterpret_cast<Smi*>(value_)->value(); -} - - bool MapWord::IsMarked() { return (value_ & kMarkingMask) == 0; } @@ -1635,44 +1611,25 @@ HashTable<Shape, Key>* HashTable<Shape, Key>::cast(Object* obj) { INT_ACCESSORS(Array, length, kLengthOffset) -bool String::Equals(String* other) { - if (other == this) return true; - if (StringShape(this).IsSymbol() && StringShape(other).IsSymbol()) { - return false; - } - return SlowEquals(other); -} - - -int String::length() { - uint32_t len = READ_INT_FIELD(this, kLengthOffset); +INT_ACCESSORS(String, length, kLengthOffset) - ASSERT(kShortStringTag + kLongLengthShift == kShortLengthShift); - ASSERT(kMediumStringTag + kLongLengthShift == kMediumLengthShift); - ASSERT(kLongStringTag == 0); - return len >> (StringShape(this).size_tag() + kLongLengthShift); +uint32_t String::hash_field() { + return READ_UINT32_FIELD(this, kHashFieldOffset); } -void String::set_length(int value) { - ASSERT(kShortStringTag + kLongLengthShift == kShortLengthShift); - ASSERT(kMediumStringTag + kLongLengthShift == kMediumLengthShift); - ASSERT(kLongStringTag == 0); - - WRITE_INT_FIELD(this, - kLengthOffset, - value << (StringShape(this).size_tag() + kLongLengthShift)); -} - - -uint32_t String::length_field() { - return READ_UINT32_FIELD(this, kLengthOffset); +void String::set_hash_field(uint32_t value) { + WRITE_UINT32_FIELD(this, kHashFieldOffset, value); } -void String::set_length_field(uint32_t value) { - WRITE_UINT32_FIELD(this, kLengthOffset, value); +bool String::Equals(String* other) { + if (other == this) return true; + if (StringShape(this).IsSymbol() && StringShape(other).IsSymbol()) { + return false; + } + return SlowEquals(other); } @@ -1779,30 +1736,12 @@ void SeqTwoByteString::SeqTwoByteStringSet(int index, uint16_t value) { int SeqTwoByteString::SeqTwoByteStringSize(InstanceType instance_type) { uint32_t length = READ_INT_FIELD(this, kLengthOffset); - - ASSERT(kShortStringTag + kLongLengthShift == kShortLengthShift); - ASSERT(kMediumStringTag + kLongLengthShift == kMediumLengthShift); - ASSERT(kLongStringTag == 0); - - // Use the map (and not 'this') to compute the size tag, since - // TwoByteStringSize is called during GC when maps are encoded. - length >>= StringShape(instance_type).size_tag() + kLongLengthShift; - return SizeFor(length); } int SeqAsciiString::SeqAsciiStringSize(InstanceType instance_type) { uint32_t length = READ_INT_FIELD(this, kLengthOffset); - - ASSERT(kShortStringTag + kLongLengthShift == kShortLengthShift); - ASSERT(kMediumStringTag + kLongLengthShift == kMediumLengthShift); - ASSERT(kLongStringTag == 0); - - // Use the map (and not 'this') to compute the size tag, since - // AsciiStringSize is called during GC when maps are encoded. - length >>= StringShape(instance_type).size_tag() + kLongLengthShift; - return SizeFor(length); } @@ -1850,34 +1789,6 @@ void ExternalAsciiString::set_resource( } -Map* ExternalAsciiString::StringMap(int length) { - Map* map; - // Number of characters: determines the map. - if (length <= String::kMaxShortSize) { - map = Heap::short_external_ascii_string_map(); - } else if (length <= String::kMaxMediumSize) { - map = Heap::medium_external_ascii_string_map(); - } else { - map = Heap::long_external_ascii_string_map(); - } - return map; -} - - -Map* ExternalAsciiString::SymbolMap(int length) { - Map* map; - // Number of characters: determines the map. - if (length <= String::kMaxShortSize) { - map = Heap::short_external_ascii_symbol_map(); - } else if (length <= String::kMaxMediumSize) { - map = Heap::medium_external_ascii_symbol_map(); - } else { - map = Heap::long_external_ascii_symbol_map(); - } - return map; -} - - ExternalTwoByteString::Resource* ExternalTwoByteString::resource() { return *reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)); } @@ -1889,34 +1800,6 @@ void ExternalTwoByteString::set_resource( } -Map* ExternalTwoByteString::StringMap(int length) { - Map* map; - // Number of characters: determines the map. - if (length <= String::kMaxShortSize) { - map = Heap::short_external_string_map(); - } else if (length <= String::kMaxMediumSize) { - map = Heap::medium_external_string_map(); - } else { - map = Heap::long_external_string_map(); - } - return map; -} - - -Map* ExternalTwoByteString::SymbolMap(int length) { - Map* map; - // Number of characters: determines the map. - if (length <= String::kMaxShortSize) { - map = Heap::short_external_symbol_map(); - } else if (length <= String::kMaxMediumSize) { - map = Heap::medium_external_symbol_map(); - } else { - map = Heap::long_external_symbol_map(); - } - return map; -} - - byte ByteArray::get(int index) { ASSERT(index >= 0 && index < this->length()); return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize); @@ -2441,9 +2324,8 @@ ACCESSORS(Script, context_data, Object, kContextOffset) ACCESSORS(Script, wrapper, Proxy, kWrapperOffset) ACCESSORS(Script, type, Smi, kTypeOffset) ACCESSORS(Script, compilation_type, Smi, kCompilationTypeOffset) -ACCESSORS(Script, line_ends_fixed_array, Object, kLineEndsFixedArrayOffset) -ACCESSORS(Script, line_ends_js_array, Object, kLineEndsJSArrayOffset) -ACCESSORS(Script, eval_from_function, Object, kEvalFromFunctionOffset) +ACCESSORS(Script, line_ends, Object, kLineEndsOffset) +ACCESSORS(Script, eval_from_shared, Object, kEvalFromSharedOffset) ACCESSORS(Script, eval_from_instructions_offset, Smi, kEvalFrominstructionsOffsetOffset) @@ -2900,13 +2782,13 @@ NumberDictionary* JSObject::element_dictionary() { bool String::HasHashCode() { - return (length_field() & kHashComputedMask) != 0; + return (hash_field() & kHashComputedMask) != 0; } uint32_t String::Hash() { // Fast case: has hash code already been computed? - uint32_t field = length_field(); + uint32_t field = hash_field(); if (field & kHashComputedMask) return field >> kHashShift; // Slow case: compute hash code and set it. return ComputeAndSetHash(); @@ -2923,7 +2805,7 @@ StringHasher::StringHasher(int length) bool StringHasher::has_trivial_hash() { - return length_ > String::kMaxMediumSize; + return length_ > String::kMaxHashCalcLength; } @@ -2979,7 +2861,7 @@ uint32_t StringHasher::GetHash() { bool String::AsArrayIndex(uint32_t* index) { - uint32_t field = length_field(); + uint32_t field = hash_field(); if ((field & kHashComputedMask) && !(field & kIsArrayIndexMask)) return false; return SlowAsArrayIndex(index); } diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index f37658551..0f8dca398 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -37,6 +37,7 @@ #include "scanner.h" #include "scopeinfo.h" #include "string-stream.h" +#include "utils.h" #ifdef ENABLE_DISASSEMBLER #include "disassembler.h" @@ -754,19 +755,21 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { ASSERT(size >= ExternalString::kSize); bool is_symbol = this->IsSymbol(); int length = this->length(); + int hash_field = this->hash_field(); // Morph the object to an external string by adjusting the map and // reinitializing the fields. - this->set_map(ExternalTwoByteString::StringMap(length)); + this->set_map(Heap::external_string_map()); ExternalTwoByteString* self = ExternalTwoByteString::cast(this); self->set_length(length); + self->set_hash_field(hash_field); self->set_resource(resource); // Additionally make the object into an external symbol if the original string // was a symbol to start with. if (is_symbol) { self->Hash(); // Force regeneration of the hash value. // Now morph this external string into a external symbol. - self->set_map(ExternalTwoByteString::SymbolMap(length)); + this->set_map(Heap::external_symbol_map()); } // Fill the remainder of the string with dead wood. @@ -798,19 +801,21 @@ bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) { ASSERT(size >= ExternalString::kSize); bool is_symbol = this->IsSymbol(); int length = this->length(); + int hash_field = this->hash_field(); // Morph the object to an external string by adjusting the map and // reinitializing the fields. - this->set_map(ExternalAsciiString::StringMap(length)); + this->set_map(Heap::external_ascii_string_map()); ExternalAsciiString* self = ExternalAsciiString::cast(this); self->set_length(length); + self->set_hash_field(hash_field); self->set_resource(resource); // Additionally make the object into an external symbol if the original string // was a symbol to start with. if (is_symbol) { self->Hash(); // Force regeneration of the hash value. // Now morph this external string into a external symbol. - self->set_map(ExternalAsciiString::SymbolMap(length)); + this->set_map(Heap::external_ascii_symbol_map()); } // Fill the remainder of the string with dead wood. @@ -822,7 +827,7 @@ bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) { void String::StringShortPrint(StringStream* accumulator) { int len = length(); - if (len > kMaxMediumSize) { + if (len > kMaxShortPrintLength) { accumulator->Add("<Very long string[%u]>", len); return; } @@ -2628,33 +2633,24 @@ bool JSObject::ReferencesObject(Object* obj) { // Tests for the fast common case for property enumeration: -// - this object has an enum cache -// - this object has no elements -// - no prototype has enumerable properties/elements -// - neither this object nor any prototype has interceptors +// - This object and all prototypes has an enum cache (which means that it has +// no interceptors and needs no access checks). +// - This object has no elements. +// - No prototype has enumerable properties/elements. bool JSObject::IsSimpleEnum() { - JSObject* arguments_boilerplate = - Top::context()->global_context()->arguments_boilerplate(); - JSFunction* arguments_function = - JSFunction::cast(arguments_boilerplate->map()->constructor()); - if (IsAccessCheckNeeded()) return false; - if (map()->constructor() == arguments_function) return false; - for (Object* o = this; o != Heap::null_value(); o = JSObject::cast(o)->GetPrototype()) { JSObject* curr = JSObject::cast(o); - if (!curr->HasFastProperties()) return false; if (!curr->map()->instance_descriptors()->HasEnumCache()) return false; + ASSERT(!curr->HasNamedInterceptor()); + ASSERT(!curr->HasIndexedInterceptor()); + ASSERT(!curr->IsAccessCheckNeeded()); if (curr->NumberOfEnumElements() > 0) return false; - if (curr->HasNamedInterceptor()) return false; - if (curr->HasIndexedInterceptor()) return false; if (curr != this) { FixedArray* curr_fixed_array = FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache()); - if (curr_fixed_array->length() > 0) { - return false; - } + if (curr_fixed_array->length() > 0) return false; } } return true; @@ -4484,23 +4480,11 @@ bool String::MarkAsUndetectable() { if (StringShape(this).IsSymbol()) return false; Map* map = this->map(); - if (map == Heap::short_string_map()) { - this->set_map(Heap::undetectable_short_string_map()); - return true; - } else if (map == Heap::medium_string_map()) { - this->set_map(Heap::undetectable_medium_string_map()); + if (map == Heap::string_map()) { + this->set_map(Heap::undetectable_string_map()); return true; - } else if (map == Heap::long_string_map()) { - this->set_map(Heap::undetectable_long_string_map()); - return true; - } else if (map == Heap::short_ascii_string_map()) { - this->set_map(Heap::undetectable_short_ascii_string_map()); - return true; - } else if (map == Heap::medium_ascii_string_map()) { - this->set_map(Heap::undetectable_medium_ascii_string_map()); - return true; - } else if (map == Heap::long_ascii_string_map()) { - this->set_map(Heap::undetectable_long_ascii_string_map()); + } else if (map == Heap::ascii_string_map()) { + this->set_map(Heap::undetectable_ascii_string_map()); return true; } // Rest cannot be marked as undetectable @@ -4523,17 +4507,17 @@ bool String::IsEqualTo(Vector<const char> str) { uint32_t String::ComputeAndSetHash() { // Should only be called if hash code has not yet been computed. - ASSERT(!(length_field() & kHashComputedMask)); + ASSERT(!(hash_field() & kHashComputedMask)); // Compute the hash code. StringInputBuffer buffer(this); - uint32_t field = ComputeLengthAndHashField(&buffer, length()); + uint32_t field = ComputeHashField(&buffer, length()); // Store the hash code in the object. - set_length_field(field); + set_hash_field(field); // Check the hash code is there. - ASSERT(length_field() & kHashComputedMask); + ASSERT(hash_field() & kHashComputedMask); uint32_t result = field >> kHashShift; ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. return result; @@ -4573,9 +4557,10 @@ bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer, bool String::SlowAsArrayIndex(uint32_t* index) { if (length() <= kMaxCachedArrayIndexLength) { Hash(); // force computation of hash code - uint32_t field = length_field(); + uint32_t field = hash_field(); if ((field & kIsArrayIndexMask) == 0) return false; - *index = (field & ((1 << kShortLengthShift) - 1)) >> kLongLengthShift; + // Isolate the array index form the full hash field. + *index = (kArrayIndexHashMask & field) >> kHashShift; return true; } else { StringInputBuffer buffer(this); @@ -4584,37 +4569,42 @@ bool String::SlowAsArrayIndex(uint32_t* index) { } -static inline uint32_t HashField(uint32_t hash, bool is_array_index) { +static inline uint32_t HashField(uint32_t hash, + bool is_array_index, + int length = -1) { uint32_t result = - (hash << String::kLongLengthShift) | String::kHashComputedMask; - if (is_array_index) result |= String::kIsArrayIndexMask; + (hash << String::kHashShift) | String::kHashComputedMask; + if (is_array_index) { + // For array indexes mix the length into the hash as an array index could + // be zero. + ASSERT(length > 0); + ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) < + (1 << String::kArrayIndexValueBits)); + result |= String::kIsArrayIndexMask; + result |= length << String::kArrayIndexHashLengthShift; + } return result; } uint32_t StringHasher::GetHashField() { ASSERT(is_valid()); - if (length_ <= String::kMaxShortSize) { - uint32_t payload; + if (length_ <= String::kMaxHashCalcLength) { if (is_array_index()) { - payload = v8::internal::HashField(array_index(), true); + return v8::internal::HashField(array_index(), true, length_); } else { - payload = v8::internal::HashField(GetHash(), false); + return v8::internal::HashField(GetHash(), false); } - return (payload & ((1 << String::kShortLengthShift) - 1)) | - (length_ << String::kShortLengthShift); - } else if (length_ <= String::kMaxMediumSize) { uint32_t payload = v8::internal::HashField(GetHash(), false); - return (payload & ((1 << String::kMediumLengthShift) - 1)) | - (length_ << String::kMediumLengthShift); + return payload; } else { return v8::internal::HashField(length_, false); } } -uint32_t String::ComputeLengthAndHashField(unibrow::CharacterStream* buffer, - int length) { +uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer, + int length) { StringHasher hasher(length); // Very long strings have a trivial hash that doesn't inspect the @@ -6177,6 +6167,7 @@ Object* JSObject::GetPropertyPostInterceptor(JSObject* receiver, return pt->GetPropertyWithReceiver(receiver, name, attributes); } + Object* JSObject::GetLocalPropertyPostInterceptor( JSObject* receiver, String* name, @@ -6478,6 +6469,15 @@ int JSObject::NumberOfLocalElements(PropertyAttributes filter) { int JSObject::NumberOfEnumElements() { + // Fast case for objects with no elements. + if (!IsJSValue() && HasFastElements()) { + uint32_t length = IsJSArray() ? + static_cast<uint32_t>( + Smi::cast(JSArray::cast(this)->length())->value()) : + static_cast<uint32_t>(FixedArray::cast(elements())->length()); + if (length == 0) return 0; + } + // Compute the number of enumerable elements. return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM)); } @@ -6737,19 +6737,19 @@ class RegExpKey : public HashTableKey { class Utf8SymbolKey : public HashTableKey { public: explicit Utf8SymbolKey(Vector<const char> string) - : string_(string), length_field_(0) { } + : string_(string), hash_field_(0) { } bool IsMatch(Object* string) { return String::cast(string)->IsEqualTo(string_); } uint32_t Hash() { - if (length_field_ != 0) return length_field_ >> String::kHashShift; + if (hash_field_ != 0) return hash_field_ >> String::kHashShift; unibrow::Utf8InputBuffer<> buffer(string_.start(), static_cast<unsigned>(string_.length())); chars_ = buffer.Length(); - length_field_ = String::ComputeLengthAndHashField(&buffer, chars_); - uint32_t result = length_field_ >> String::kHashShift; + hash_field_ = String::ComputeHashField(&buffer, chars_); + uint32_t result = hash_field_ >> String::kHashShift; ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. return result; } @@ -6759,12 +6759,12 @@ class Utf8SymbolKey : public HashTableKey { } Object* AsObject() { - if (length_field_ == 0) Hash(); - return Heap::AllocateSymbol(string_, chars_, length_field_); + if (hash_field_ == 0) Hash(); + return Heap::AllocateSymbol(string_, chars_, hash_field_); } Vector<const char> string_; - uint32_t length_field_; + uint32_t hash_field_; int chars_; // Caches the number of characters when computing the hash code. }; @@ -6805,7 +6805,7 @@ class SymbolKey : public HashTableKey { StringInputBuffer buffer(string_); return Heap::AllocateInternalSymbol(&buffer, string_->length(), - string_->length_field()); + string_->hash_field()); } static uint32_t StringHash(Object* obj) { diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index d22e7231f..671978ab7 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -221,248 +221,128 @@ enum PropertyNormalizationMode { // NOTE: Everything following JS_VALUE_TYPE is considered a // JSObject for GC purposes. The first four entries here have typeof // 'object', whereas JS_FUNCTION_TYPE has typeof 'function'. -#define INSTANCE_TYPE_LIST_ALL(V) \ - V(SHORT_SYMBOL_TYPE) \ - V(MEDIUM_SYMBOL_TYPE) \ - V(LONG_SYMBOL_TYPE) \ - V(SHORT_ASCII_SYMBOL_TYPE) \ - V(MEDIUM_ASCII_SYMBOL_TYPE) \ - V(LONG_ASCII_SYMBOL_TYPE) \ - V(SHORT_CONS_SYMBOL_TYPE) \ - V(MEDIUM_CONS_SYMBOL_TYPE) \ - V(LONG_CONS_SYMBOL_TYPE) \ - V(SHORT_CONS_ASCII_SYMBOL_TYPE) \ - V(MEDIUM_CONS_ASCII_SYMBOL_TYPE) \ - V(LONG_CONS_ASCII_SYMBOL_TYPE) \ - V(SHORT_EXTERNAL_SYMBOL_TYPE) \ - V(MEDIUM_EXTERNAL_SYMBOL_TYPE) \ - V(LONG_EXTERNAL_SYMBOL_TYPE) \ - V(SHORT_EXTERNAL_ASCII_SYMBOL_TYPE) \ - V(MEDIUM_EXTERNAL_ASCII_SYMBOL_TYPE) \ - V(LONG_EXTERNAL_ASCII_SYMBOL_TYPE) \ - V(SHORT_STRING_TYPE) \ - V(MEDIUM_STRING_TYPE) \ - V(LONG_STRING_TYPE) \ - V(SHORT_ASCII_STRING_TYPE) \ - V(MEDIUM_ASCII_STRING_TYPE) \ - V(LONG_ASCII_STRING_TYPE) \ - V(SHORT_CONS_STRING_TYPE) \ - V(MEDIUM_CONS_STRING_TYPE) \ - V(LONG_CONS_STRING_TYPE) \ - V(SHORT_CONS_ASCII_STRING_TYPE) \ - V(MEDIUM_CONS_ASCII_STRING_TYPE) \ - V(LONG_CONS_ASCII_STRING_TYPE) \ - V(SHORT_EXTERNAL_STRING_TYPE) \ - V(MEDIUM_EXTERNAL_STRING_TYPE) \ - V(LONG_EXTERNAL_STRING_TYPE) \ - V(SHORT_EXTERNAL_ASCII_STRING_TYPE) \ - V(MEDIUM_EXTERNAL_ASCII_STRING_TYPE) \ - V(LONG_EXTERNAL_ASCII_STRING_TYPE) \ - V(LONG_PRIVATE_EXTERNAL_ASCII_STRING_TYPE) \ - \ - V(MAP_TYPE) \ - V(HEAP_NUMBER_TYPE) \ - V(FIXED_ARRAY_TYPE) \ - V(CODE_TYPE) \ - V(JS_GLOBAL_PROPERTY_CELL_TYPE) \ - V(ODDBALL_TYPE) \ - V(PROXY_TYPE) \ - V(BYTE_ARRAY_TYPE) \ - V(PIXEL_ARRAY_TYPE) \ - /* Note: the order of these external array */ \ - /* types is relied upon in */ \ - /* Object::IsExternalArray(). */ \ - V(EXTERNAL_BYTE_ARRAY_TYPE) \ - V(EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE) \ - V(EXTERNAL_SHORT_ARRAY_TYPE) \ - V(EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE) \ - V(EXTERNAL_INT_ARRAY_TYPE) \ - V(EXTERNAL_UNSIGNED_INT_ARRAY_TYPE) \ - V(EXTERNAL_FLOAT_ARRAY_TYPE) \ - V(FILLER_TYPE) \ - \ - V(ACCESSOR_INFO_TYPE) \ - V(ACCESS_CHECK_INFO_TYPE) \ - V(INTERCEPTOR_INFO_TYPE) \ - V(SHARED_FUNCTION_INFO_TYPE) \ - V(CALL_HANDLER_INFO_TYPE) \ - V(FUNCTION_TEMPLATE_INFO_TYPE) \ - V(OBJECT_TEMPLATE_INFO_TYPE) \ - V(SIGNATURE_INFO_TYPE) \ - V(TYPE_SWITCH_INFO_TYPE) \ - V(SCRIPT_TYPE) \ - \ - V(JS_VALUE_TYPE) \ - V(JS_OBJECT_TYPE) \ - V(JS_CONTEXT_EXTENSION_OBJECT_TYPE) \ - V(JS_GLOBAL_OBJECT_TYPE) \ - V(JS_BUILTINS_OBJECT_TYPE) \ - V(JS_GLOBAL_PROXY_TYPE) \ - V(JS_ARRAY_TYPE) \ - V(JS_REGEXP_TYPE) \ - \ - V(JS_FUNCTION_TYPE) \ +#define INSTANCE_TYPE_LIST_ALL(V) \ + V(SYMBOL_TYPE) \ + V(ASCII_SYMBOL_TYPE) \ + V(CONS_SYMBOL_TYPE) \ + V(CONS_ASCII_SYMBOL_TYPE) \ + V(EXTERNAL_SYMBOL_TYPE) \ + V(EXTERNAL_ASCII_SYMBOL_TYPE) \ + V(STRING_TYPE) \ + V(ASCII_STRING_TYPE) \ + V(CONS_STRING_TYPE) \ + V(CONS_ASCII_STRING_TYPE) \ + V(EXTERNAL_STRING_TYPE) \ + V(EXTERNAL_ASCII_STRING_TYPE) \ + V(PRIVATE_EXTERNAL_ASCII_STRING_TYPE) \ + \ + V(MAP_TYPE) \ + V(HEAP_NUMBER_TYPE) \ + V(FIXED_ARRAY_TYPE) \ + V(CODE_TYPE) \ + V(JS_GLOBAL_PROPERTY_CELL_TYPE) \ + V(ODDBALL_TYPE) \ + V(PROXY_TYPE) \ + V(BYTE_ARRAY_TYPE) \ + V(PIXEL_ARRAY_TYPE) \ + /* Note: the order of these external array */ \ + /* types is relied upon in */ \ + /* Object::IsExternalArray(). */ \ + V(EXTERNAL_BYTE_ARRAY_TYPE) \ + V(EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE) \ + V(EXTERNAL_SHORT_ARRAY_TYPE) \ + V(EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE) \ + V(EXTERNAL_INT_ARRAY_TYPE) \ + V(EXTERNAL_UNSIGNED_INT_ARRAY_TYPE) \ + V(EXTERNAL_FLOAT_ARRAY_TYPE) \ + V(FILLER_TYPE) \ + \ + V(ACCESSOR_INFO_TYPE) \ + V(ACCESS_CHECK_INFO_TYPE) \ + V(INTERCEPTOR_INFO_TYPE) \ + V(SHARED_FUNCTION_INFO_TYPE) \ + V(CALL_HANDLER_INFO_TYPE) \ + V(FUNCTION_TEMPLATE_INFO_TYPE) \ + V(OBJECT_TEMPLATE_INFO_TYPE) \ + V(SIGNATURE_INFO_TYPE) \ + V(TYPE_SWITCH_INFO_TYPE) \ + V(SCRIPT_TYPE) \ + \ + V(JS_VALUE_TYPE) \ + V(JS_OBJECT_TYPE) \ + V(JS_CONTEXT_EXTENSION_OBJECT_TYPE) \ + V(JS_GLOBAL_OBJECT_TYPE) \ + V(JS_BUILTINS_OBJECT_TYPE) \ + V(JS_GLOBAL_PROXY_TYPE) \ + V(JS_ARRAY_TYPE) \ + V(JS_REGEXP_TYPE) \ + \ + V(JS_FUNCTION_TYPE) \ #ifdef ENABLE_DEBUGGER_SUPPORT -#define INSTANCE_TYPE_LIST_DEBUGGER(V) \ - V(DEBUG_INFO_TYPE) \ +#define INSTANCE_TYPE_LIST_DEBUGGER(V) \ + V(DEBUG_INFO_TYPE) \ V(BREAK_POINT_INFO_TYPE) #else #define INSTANCE_TYPE_LIST_DEBUGGER(V) #endif -#define INSTANCE_TYPE_LIST(V) \ - INSTANCE_TYPE_LIST_ALL(V) \ +#define INSTANCE_TYPE_LIST(V) \ + INSTANCE_TYPE_LIST_ALL(V) \ INSTANCE_TYPE_LIST_DEBUGGER(V) // Since string types are not consecutive, this macro is used to // iterate over them. #define STRING_TYPE_LIST(V) \ - V(SHORT_SYMBOL_TYPE, \ + V(SYMBOL_TYPE, \ SeqTwoByteString::kAlignedSize, \ - short_symbol, \ - ShortSymbol) \ - V(MEDIUM_SYMBOL_TYPE, \ - SeqTwoByteString::kAlignedSize, \ - medium_symbol, \ - MediumSymbol) \ - V(LONG_SYMBOL_TYPE, \ - SeqTwoByteString::kAlignedSize, \ - long_symbol, \ - LongSymbol) \ - V(SHORT_ASCII_SYMBOL_TYPE, \ - SeqAsciiString::kAlignedSize, \ - short_ascii_symbol, \ - ShortAsciiSymbol) \ - V(MEDIUM_ASCII_SYMBOL_TYPE, \ + symbol, \ + Symbol) \ + V(ASCII_SYMBOL_TYPE, \ SeqAsciiString::kAlignedSize, \ - medium_ascii_symbol, \ - MediumAsciiSymbol) \ - V(LONG_ASCII_SYMBOL_TYPE, \ - SeqAsciiString::kAlignedSize, \ - long_ascii_symbol, \ - LongAsciiSymbol) \ - V(SHORT_CONS_SYMBOL_TYPE, \ - ConsString::kSize, \ - short_cons_symbol, \ - ShortConsSymbol) \ - V(MEDIUM_CONS_SYMBOL_TYPE, \ + ascii_symbol, \ + AsciiSymbol) \ + V(CONS_SYMBOL_TYPE, \ ConsString::kSize, \ - medium_cons_symbol, \ - MediumConsSymbol) \ - V(LONG_CONS_SYMBOL_TYPE, \ + cons_symbol, \ + ConsSymbol) \ + V(CONS_ASCII_SYMBOL_TYPE, \ ConsString::kSize, \ - long_cons_symbol, \ - LongConsSymbol) \ - V(SHORT_CONS_ASCII_SYMBOL_TYPE, \ - ConsString::kSize, \ - short_cons_ascii_symbol, \ - ShortConsAsciiSymbol) \ - V(MEDIUM_CONS_ASCII_SYMBOL_TYPE, \ - ConsString::kSize, \ - medium_cons_ascii_symbol, \ - MediumConsAsciiSymbol) \ - V(LONG_CONS_ASCII_SYMBOL_TYPE, \ - ConsString::kSize, \ - long_cons_ascii_symbol, \ - LongConsAsciiSymbol) \ - V(SHORT_EXTERNAL_SYMBOL_TYPE, \ - ExternalTwoByteString::kSize, \ - short_external_symbol, \ - ShortExternalSymbol) \ - V(MEDIUM_EXTERNAL_SYMBOL_TYPE, \ + cons_ascii_symbol, \ + ConsAsciiSymbol) \ + V(EXTERNAL_SYMBOL_TYPE, \ ExternalTwoByteString::kSize, \ - medium_external_symbol, \ - MediumExternalSymbol) \ - V(LONG_EXTERNAL_SYMBOL_TYPE, \ - ExternalTwoByteString::kSize, \ - long_external_symbol, \ - LongExternalSymbol) \ - V(SHORT_EXTERNAL_ASCII_SYMBOL_TYPE, \ - ExternalAsciiString::kSize, \ - short_external_ascii_symbol, \ - ShortExternalAsciiSymbol) \ - V(MEDIUM_EXTERNAL_ASCII_SYMBOL_TYPE, \ + external_symbol, \ + ExternalSymbol) \ + V(EXTERNAL_ASCII_SYMBOL_TYPE, \ ExternalAsciiString::kSize, \ - medium_external_ascii_symbol, \ - MediumExternalAsciiSymbol) \ - V(LONG_EXTERNAL_ASCII_SYMBOL_TYPE, \ - ExternalAsciiString::kSize, \ - long_external_ascii_symbol, \ - LongExternalAsciiSymbol) \ - V(SHORT_STRING_TYPE, \ - SeqTwoByteString::kAlignedSize, \ - short_string, \ - ShortString) \ - V(MEDIUM_STRING_TYPE, \ + external_ascii_symbol, \ + ExternalAsciiSymbol) \ + V(STRING_TYPE, \ SeqTwoByteString::kAlignedSize, \ - medium_string, \ - MediumString) \ - V(LONG_STRING_TYPE, \ - SeqTwoByteString::kAlignedSize, \ - long_string, \ - LongString) \ - V(SHORT_ASCII_STRING_TYPE, \ - SeqAsciiString::kAlignedSize, \ - short_ascii_string, \ - ShortAsciiString) \ - V(MEDIUM_ASCII_STRING_TYPE, \ + string, \ + String) \ + V(ASCII_STRING_TYPE, \ SeqAsciiString::kAlignedSize, \ - medium_ascii_string, \ - MediumAsciiString) \ - V(LONG_ASCII_STRING_TYPE, \ - SeqAsciiString::kAlignedSize, \ - long_ascii_string, \ - LongAsciiString) \ - V(SHORT_CONS_STRING_TYPE, \ - ConsString::kSize, \ - short_cons_string, \ - ShortConsString) \ - V(MEDIUM_CONS_STRING_TYPE, \ + ascii_string, \ + AsciiString) \ + V(CONS_STRING_TYPE, \ ConsString::kSize, \ - medium_cons_string, \ - MediumConsString) \ - V(LONG_CONS_STRING_TYPE, \ + cons_string, \ + ConsString) \ + V(CONS_ASCII_STRING_TYPE, \ ConsString::kSize, \ - long_cons_string, \ - LongConsString) \ - V(SHORT_CONS_ASCII_STRING_TYPE, \ - ConsString::kSize, \ - short_cons_ascii_string, \ - ShortConsAsciiString) \ - V(MEDIUM_CONS_ASCII_STRING_TYPE, \ - ConsString::kSize, \ - medium_cons_ascii_string, \ - MediumConsAsciiString) \ - V(LONG_CONS_ASCII_STRING_TYPE, \ - ConsString::kSize, \ - long_cons_ascii_string, \ - LongConsAsciiString) \ - V(SHORT_EXTERNAL_STRING_TYPE, \ - ExternalTwoByteString::kSize, \ - short_external_string, \ - ShortExternalString) \ - V(MEDIUM_EXTERNAL_STRING_TYPE, \ - ExternalTwoByteString::kSize, \ - medium_external_string, \ - MediumExternalString) \ - V(LONG_EXTERNAL_STRING_TYPE, \ + cons_ascii_string, \ + ConsAsciiString) \ + V(EXTERNAL_STRING_TYPE, \ ExternalTwoByteString::kSize, \ - long_external_string, \ - LongExternalString) \ - V(SHORT_EXTERNAL_ASCII_STRING_TYPE, \ + external_string, \ + ExternalString) \ + V(EXTERNAL_ASCII_STRING_TYPE, \ ExternalAsciiString::kSize, \ - short_external_ascii_string, \ - ShortExternalAsciiString) \ - V(MEDIUM_EXTERNAL_ASCII_STRING_TYPE, \ - ExternalAsciiString::kSize, \ - medium_external_ascii_string, \ - MediumExternalAsciiString) \ - V(LONG_EXTERNAL_ASCII_STRING_TYPE, \ - ExternalAsciiString::kSize, \ - long_external_ascii_string, \ - LongExternalAsciiString) + external_ascii_string, \ + ExternalAsciiString) \ // A struct is a simple object a set of object-valued fields. Including an // object type in this causes the compiler to generate most of the boilerplate @@ -473,27 +353,27 @@ enum PropertyNormalizationMode { // Note that for subtle reasons related to the ordering or numerical values of // type tags, elements in this list have to be added to the INSTANCE_TYPE_LIST // manually. -#define STRUCT_LIST_ALL(V) \ - V(ACCESSOR_INFO, AccessorInfo, accessor_info) \ - V(ACCESS_CHECK_INFO, AccessCheckInfo, access_check_info) \ - V(INTERCEPTOR_INFO, InterceptorInfo, interceptor_info) \ - V(CALL_HANDLER_INFO, CallHandlerInfo, call_handler_info) \ - V(FUNCTION_TEMPLATE_INFO, FunctionTemplateInfo, function_template_info) \ - V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info) \ - V(SIGNATURE_INFO, SignatureInfo, signature_info) \ - V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info) \ +#define STRUCT_LIST_ALL(V) \ + V(ACCESSOR_INFO, AccessorInfo, accessor_info) \ + V(ACCESS_CHECK_INFO, AccessCheckInfo, access_check_info) \ + V(INTERCEPTOR_INFO, InterceptorInfo, interceptor_info) \ + V(CALL_HANDLER_INFO, CallHandlerInfo, call_handler_info) \ + V(FUNCTION_TEMPLATE_INFO, FunctionTemplateInfo, function_template_info) \ + V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info) \ + V(SIGNATURE_INFO, SignatureInfo, signature_info) \ + V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info) \ V(SCRIPT, Script, script) #ifdef ENABLE_DEBUGGER_SUPPORT -#define STRUCT_LIST_DEBUGGER(V) \ - V(DEBUG_INFO, DebugInfo, debug_info) \ +#define STRUCT_LIST_DEBUGGER(V) \ + V(DEBUG_INFO, DebugInfo, debug_info) \ V(BREAK_POINT_INFO, BreakPointInfo, break_point_info) #else #define STRUCT_LIST_DEBUGGER(V) #endif -#define STRUCT_LIST(V) \ - STRUCT_LIST_ALL(V) \ +#define STRUCT_LIST(V) \ + STRUCT_LIST_ALL(V) \ STRUCT_LIST_DEBUGGER(V) // We use the full 8 bits of the instance_type field to encode heap object @@ -509,15 +389,6 @@ const uint32_t kIsSymbolMask = 0x20; const uint32_t kNotSymbolTag = 0x0; const uint32_t kSymbolTag = 0x20; -// If bit 7 is clear, bits 3 and 4 are the string's size (short, medium or -// long). These values are very special in that they are also used to shift -// the length field to get the length, removing the hash value. This avoids -// using if or switch when getting the length of a string. -const uint32_t kStringSizeMask = 0x18; -const uint32_t kShortStringTag = 0x18; -const uint32_t kMediumStringTag = 0x10; -const uint32_t kLongStringTag = 0x00; - // If bit 7 is clear then bit 2 indicates whether the string consists of // two-byte characters or one-byte characters. const uint32_t kStringEncodingMask = 0x4; @@ -547,60 +418,20 @@ const uint32_t kShortcutTypeTag = kConsStringTag; enum InstanceType { - SHORT_SYMBOL_TYPE = kShortStringTag | kSymbolTag | kSeqStringTag, - MEDIUM_SYMBOL_TYPE = kMediumStringTag | kSymbolTag | kSeqStringTag, - LONG_SYMBOL_TYPE = kLongStringTag | kSymbolTag | kSeqStringTag, - SHORT_ASCII_SYMBOL_TYPE = - kShortStringTag | kAsciiStringTag | kSymbolTag | kSeqStringTag, - MEDIUM_ASCII_SYMBOL_TYPE = - kMediumStringTag | kAsciiStringTag | kSymbolTag | kSeqStringTag, - LONG_ASCII_SYMBOL_TYPE = - kLongStringTag | kAsciiStringTag | kSymbolTag | kSeqStringTag, - SHORT_CONS_SYMBOL_TYPE = kShortStringTag | kSymbolTag | kConsStringTag, - MEDIUM_CONS_SYMBOL_TYPE = kMediumStringTag | kSymbolTag | kConsStringTag, - LONG_CONS_SYMBOL_TYPE = kLongStringTag | kSymbolTag | kConsStringTag, - SHORT_CONS_ASCII_SYMBOL_TYPE = - kShortStringTag | kAsciiStringTag | kSymbolTag | kConsStringTag, - MEDIUM_CONS_ASCII_SYMBOL_TYPE = - kMediumStringTag | kAsciiStringTag | kSymbolTag | kConsStringTag, - LONG_CONS_ASCII_SYMBOL_TYPE = - kLongStringTag | kAsciiStringTag | kSymbolTag | kConsStringTag, - SHORT_EXTERNAL_SYMBOL_TYPE = - kShortStringTag | kSymbolTag | kExternalStringTag, - MEDIUM_EXTERNAL_SYMBOL_TYPE = - kMediumStringTag | kSymbolTag | kExternalStringTag, - LONG_EXTERNAL_SYMBOL_TYPE = kLongStringTag | kSymbolTag | kExternalStringTag, - SHORT_EXTERNAL_ASCII_SYMBOL_TYPE = - kShortStringTag | kAsciiStringTag | kSymbolTag | kExternalStringTag, - MEDIUM_EXTERNAL_ASCII_SYMBOL_TYPE = - kMediumStringTag | kAsciiStringTag | kSymbolTag | kExternalStringTag, - LONG_EXTERNAL_ASCII_SYMBOL_TYPE = - kLongStringTag | kAsciiStringTag | kSymbolTag | kExternalStringTag, - SHORT_STRING_TYPE = kShortStringTag | kSeqStringTag, - MEDIUM_STRING_TYPE = kMediumStringTag | kSeqStringTag, - LONG_STRING_TYPE = kLongStringTag | kSeqStringTag, - SHORT_ASCII_STRING_TYPE = kShortStringTag | kAsciiStringTag | kSeqStringTag, - MEDIUM_ASCII_STRING_TYPE = kMediumStringTag | kAsciiStringTag | kSeqStringTag, - LONG_ASCII_STRING_TYPE = kLongStringTag | kAsciiStringTag | kSeqStringTag, - SHORT_CONS_STRING_TYPE = kShortStringTag | kConsStringTag, - MEDIUM_CONS_STRING_TYPE = kMediumStringTag | kConsStringTag, - LONG_CONS_STRING_TYPE = kLongStringTag | kConsStringTag, - SHORT_CONS_ASCII_STRING_TYPE = - kShortStringTag | kAsciiStringTag | kConsStringTag, - MEDIUM_CONS_ASCII_STRING_TYPE = - kMediumStringTag | kAsciiStringTag | kConsStringTag, - LONG_CONS_ASCII_STRING_TYPE = - kLongStringTag | kAsciiStringTag | kConsStringTag, - SHORT_EXTERNAL_STRING_TYPE = kShortStringTag | kExternalStringTag, - MEDIUM_EXTERNAL_STRING_TYPE = kMediumStringTag | kExternalStringTag, - LONG_EXTERNAL_STRING_TYPE = kLongStringTag | kExternalStringTag, - SHORT_EXTERNAL_ASCII_STRING_TYPE = - kShortStringTag | kAsciiStringTag | kExternalStringTag, - MEDIUM_EXTERNAL_ASCII_STRING_TYPE = - kMediumStringTag | kAsciiStringTag | kExternalStringTag, - LONG_EXTERNAL_ASCII_STRING_TYPE = - kLongStringTag | kAsciiStringTag | kExternalStringTag, - LONG_PRIVATE_EXTERNAL_ASCII_STRING_TYPE = LONG_EXTERNAL_ASCII_STRING_TYPE, + SYMBOL_TYPE = kSymbolTag | kSeqStringTag, + ASCII_SYMBOL_TYPE = kAsciiStringTag | kSymbolTag | kSeqStringTag, + CONS_SYMBOL_TYPE = kSymbolTag | kConsStringTag, + CONS_ASCII_SYMBOL_TYPE = kAsciiStringTag | kSymbolTag | kConsStringTag, + EXTERNAL_SYMBOL_TYPE = kSymbolTag | kExternalStringTag, + EXTERNAL_ASCII_SYMBOL_TYPE = + kAsciiStringTag | kSymbolTag | kExternalStringTag, + STRING_TYPE = kSeqStringTag, + ASCII_STRING_TYPE = kAsciiStringTag | kSeqStringTag, + CONS_STRING_TYPE = kConsStringTag, + CONS_ASCII_STRING_TYPE = kAsciiStringTag | kConsStringTag, + EXTERNAL_STRING_TYPE = kExternalStringTag, + EXTERNAL_ASCII_STRING_TYPE = kAsciiStringTag | kExternalStringTag, + PRIVATE_EXTERNAL_ASCII_STRING_TYPE = EXTERNAL_ASCII_STRING_TYPE, MAP_TYPE = kNotStringTag, HEAP_NUMBER_TYPE, @@ -999,16 +830,6 @@ class MapWord BASE_EMBEDDED { // View this map word as a forwarding address. inline HeapObject* ToForwardingAddress(); - // True if this map word is a serialization address. This will only be the - // case during a destructive serialization of the heap. - inline bool IsSerializationAddress(); - - // Create a map word from a serialization address. - static inline MapWord FromSerializationAddress(int raw); - - // View this map word as a serialization address. - inline int ToSerializationAddress(); - // Marking phase of full collection: the map word of live objects is // marked, and may be marked as overflowed (eg, the object is live, its // children have not been visited, and it does not fit in the marking @@ -3182,14 +3003,11 @@ class Script: public Struct { DECL_ACCESSORS(compilation_type, Smi) // [line_ends]: FixedArray of line ends positions. - DECL_ACCESSORS(line_ends_fixed_array, Object) + DECL_ACCESSORS(line_ends, Object) - // [line_ends]: JSArray of line ends positions. - DECL_ACCESSORS(line_ends_js_array, Object) - - // [eval_from_function]: for eval scripts the funcion from which eval was - // called. - DECL_ACCESSORS(eval_from_function, Object) + // [eval_from_shared]: for eval scripts the shared funcion info for the + // function from which eval was called. + DECL_ACCESSORS(eval_from_shared, Object) // [eval_from_instructions_offset]: the instruction offset in the code for the // function from which eval was called where eval was called. @@ -3215,19 +3033,11 @@ class Script: public Struct { static const int kWrapperOffset = kContextOffset + kPointerSize; static const int kTypeOffset = kWrapperOffset + kPointerSize; static const int kCompilationTypeOffset = kTypeOffset + kPointerSize; - // We have the line ends array both in FixedArray form and in JSArray form. - // The FixedArray form is useful when we don't have a context and so can't - // create a JSArray. The JSArray form is useful when we want to see the - // array from JS code (e.g. debug-delay.js) which cannot handle unboxed - // FixedArray objects. - static const int kLineEndsFixedArrayOffset = - kCompilationTypeOffset + kPointerSize; - static const int kLineEndsJSArrayOffset = - kLineEndsFixedArrayOffset + kPointerSize; - static const int kIdOffset = kLineEndsJSArrayOffset + kPointerSize; - static const int kEvalFromFunctionOffset = kIdOffset + kPointerSize; + static const int kLineEndsOffset = kCompilationTypeOffset + kPointerSize; + static const int kIdOffset = kLineEndsOffset + kPointerSize; + static const int kEvalFromSharedOffset = kIdOffset + kPointerSize; static const int kEvalFrominstructionsOffsetOffset = - kEvalFromFunctionOffset + kPointerSize; + kEvalFromSharedOffset + kPointerSize; static const int kSize = kEvalFrominstructionsOffsetOffset + kPointerSize; private: @@ -3910,12 +3720,9 @@ class String: public HeapObject { inline int length(); inline void set_length(int value); - // Get and set the uninterpreted length field of the string. Notice - // that the length field is also used to cache the hash value of - // strings. In order to get or set the actual length of the string - // use the length() and set_length methods. - inline uint32_t length_field(); - inline void set_length_field(uint32_t value); + // Get and set the hash field of the string. + inline uint32_t hash_field(); + inline void set_hash_field(uint32_t value); inline bool IsAsciiRepresentation(); inline bool IsTwoByteRepresentation(); @@ -3986,8 +3793,8 @@ class String: public HeapObject { // Returns a hash value used for the property table inline uint32_t Hash(); - static uint32_t ComputeLengthAndHashField(unibrow::CharacterStream* buffer, - int length); + static uint32_t ComputeHashField(unibrow::CharacterStream* buffer, + int length); static bool ComputeArrayIndex(unibrow::CharacterStream* buffer, uint32_t* index, @@ -4018,7 +3825,8 @@ class String: public HeapObject { // Layout description. static const int kLengthOffset = HeapObject::kHeaderSize; - static const int kSize = kLengthOffset + kIntSize; + static const int kHashFieldOffset = kLengthOffset + kIntSize; + static const int kSize = kHashFieldOffset + kIntSize; // Notice: kSize is not pointer-size aligned if pointers are 64-bit. // Maximum number of characters to consider when trying to convert a string @@ -4042,22 +3850,30 @@ class String: public HeapObject { static const int kIsArrayIndexMask = 1 << 1; static const int kNofLengthBitFields = 2; + // Shift constant retrieving hash code from hash field. + static const int kHashShift = kNofLengthBitFields; + // Array index strings this short can keep their index in the hash // field. static const int kMaxCachedArrayIndexLength = 7; - // Shift constants for retrieving length and hash code from - // length/hash field. - static const int kHashShift = kNofLengthBitFields; - static const int kShortLengthShift = kHashShift + kShortStringTag; - static const int kMediumLengthShift = kHashShift + kMediumStringTag; - static const int kLongLengthShift = kHashShift + kLongStringTag; + // For strings which are array indexes the hash value has the string length + // mixed into the hash, mainly to avoid a hash value of zero which would be + // the case for the string '0'. 24 bits are used for the array index value. + static const int kArrayIndexHashLengthShift = 24 + kNofLengthBitFields; + static const int kArrayIndexHashMask = (1 << kArrayIndexHashLengthShift) - 1; + static const int kArrayIndexValueBits = + kArrayIndexHashLengthShift - kHashShift; + + // Value of empty hash field indicating that the hash is not computed. + static const int kEmptyHashField = 0; + + // Maximal string length. + static const int kMaxLength = (1 << (32 - 2)) - 1; - // Maximal string length that can be stored in the hash/length field for - // different types of strings. - static const int kMaxShortSize = (1 << (32 - kShortLengthShift)) - 1; - static const int kMaxMediumSize = (1 << (32 - kMediumLengthShift)) - 1; - static const int kMaxLength = (1 << (32 - kLongLengthShift)) - 1; + // Max length for computing hash. For strings longer than this limit the + // string length is used as the hash value. + static const int kMaxHashCalcLength = 16383; // Limit for truncation in short printing. static const int kMaxShortPrintLength = 1024; @@ -4339,9 +4155,6 @@ class ExternalAsciiString: public ExternalString { unsigned* offset, unsigned chars); - // Identify the map for the external string/symbol with a particular length. - static inline Map* StringMap(int length); - static inline Map* SymbolMap(int length); private: DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalAsciiString); }; @@ -4374,9 +4187,6 @@ class ExternalTwoByteString: public ExternalString { unsigned* offset_ptr, unsigned chars); - // Identify the map for the external string/symbol with a particular length. - static inline Map* StringMap(int length); - static inline Map* SymbolMap(int length); private: DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalTwoByteString); }; diff --git a/deps/v8/src/platform-freebsd.cc b/deps/v8/src/platform-freebsd.cc index 9b452fa9e..353d16540 100644 --- a/deps/v8/src/platform-freebsd.cc +++ b/deps/v8/src/platform-freebsd.cc @@ -89,11 +89,6 @@ uint64_t OS::CpuFeaturesImpliedByPlatform() { } -double OS::nan_value() { - return NAN; -} - - int OS::ActivationFrameAlignment() { // 16 byte alignment on FreeBSD return 16; diff --git a/deps/v8/src/platform-linux.cc b/deps/v8/src/platform-linux.cc index 9ce0be011..bfcd8fba7 100644 --- a/deps/v8/src/platform-linux.cc +++ b/deps/v8/src/platform-linux.cc @@ -95,11 +95,6 @@ uint64_t OS::CpuFeaturesImpliedByPlatform() { } -double OS::nan_value() { - return NAN; -} - - #ifdef __arm__ bool OS::ArmCpuHasFeature(CpuFeature feature) { const char* search_string = NULL; diff --git a/deps/v8/src/platform-macos.cc b/deps/v8/src/platform-macos.cc index d79cff14a..0d5be45ee 100644 --- a/deps/v8/src/platform-macos.cc +++ b/deps/v8/src/platform-macos.cc @@ -252,11 +252,6 @@ uint64_t OS::CpuFeaturesImpliedByPlatform() { } -double OS::nan_value() { - return NAN; -} - - int OS::ActivationFrameAlignment() { // OS X activation frames must be 16 byte-aligned; see "Mac OS X ABI // Function Call Guide". diff --git a/deps/v8/src/platform-openbsd.cc b/deps/v8/src/platform-openbsd.cc new file mode 100644 index 000000000..6d273047d --- /dev/null +++ b/deps/v8/src/platform-openbsd.cc @@ -0,0 +1,597 @@ +// Copyright 2006-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. + +// Platform specific code for OpenBSD goes here. For the POSIX comaptible parts +// the implementation is in platform-posix.cc. + +#include <pthread.h> +#include <semaphore.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/types.h> +#include <stdlib.h> + +#include <sys/types.h> // mmap & munmap +#include <sys/mman.h> // mmap & munmap +#include <sys/stat.h> // open +#include <sys/fcntl.h> // open +#include <unistd.h> // getpagesize +#include <execinfo.h> // backtrace, backtrace_symbols +#include <strings.h> // index +#include <errno.h> +#include <stdarg.h> +#include <limits.h> + +#undef MAP_TYPE + +#include "v8.h" + +#include "platform.h" + + +namespace v8 { +namespace internal { + +// 0 is never a valid thread id on OpenBSD since tids and pids share a +// name space and pid 0 is used to kill the group (see man 2 kill). +static const pthread_t kNoThread = (pthread_t) 0; + + +double ceiling(double x) { + // Correct as on OS X + if (-1.0 < x && x < 0.0) { + return -0.0; + } else { + return ceil(x); + } +} + + +void OS::Setup() { + // Seed the random number generator. + // Convert the current time to a 64-bit integer first, before converting it + // to an unsigned. Going directly can cause an overflow and the seed to be + // set to all ones. The seed will be identical for different instances that + // call this setup code within the same millisecond. + uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis()); + srandom(static_cast<unsigned int>(seed)); +} + + +uint64_t OS::CpuFeaturesImpliedByPlatform() { + return 0; // OpenBSD runs on anything. +} + + +int OS::ActivationFrameAlignment() { + // 16 byte alignment on OpenBSD + return 16; +} + + +// We keep the lowest and highest addresses mapped as a quick way of +// determining that pointers are outside the heap (used mostly in assertions +// and verification). The estimate is conservative, ie, not all addresses in +// 'allocated' space are actually allocated to our heap. The range is +// [lowest, highest), inclusive on the low and and exclusive on the high end. +static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); +static void* highest_ever_allocated = reinterpret_cast<void*>(0); + + +static void UpdateAllocatedSpaceLimits(void* address, int size) { + lowest_ever_allocated = Min(lowest_ever_allocated, address); + highest_ever_allocated = + Max(highest_ever_allocated, + reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size)); +} + + +bool OS::IsOutsideAllocatedSpace(void* address) { + return address < lowest_ever_allocated || address >= highest_ever_allocated; +} + + +size_t OS::AllocateAlignment() { + return getpagesize(); +} + + +void* OS::Allocate(const size_t requested, + size_t* allocated, + bool executable) { + const size_t msize = RoundUp(requested, getpagesize()); + int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0); + void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0); + + if (mbase == MAP_FAILED) { + LOG(StringEvent("OS::Allocate", "mmap failed")); + return NULL; + } + *allocated = msize; + UpdateAllocatedSpaceLimits(mbase, msize); + return mbase; +} + + +void OS::Free(void* buf, const size_t length) { + int result = munmap(buf, length); + USE(result); + ASSERT(result == 0); +} + + +#ifdef ENABLE_HEAP_PROTECTION + +void OS::Protect(void* address, size_t size) { + UNIMPLEMENTED(); +} + + +void OS::Unprotect(void* address, size_t size, bool is_executable) { + UNIMPLEMENTED(); +} + +#endif + + +void OS::Sleep(int milliseconds) { + unsigned int ms = static_cast<unsigned int>(milliseconds); + usleep(1000 * ms); +} + + +void OS::Abort() { + // Redirect to std abort to signal abnormal program termination. + abort(); +} + + +void OS::DebugBreak() { +#if defined(__arm__) || defined(__thumb__) + asm("bkpt 0"); +#else + asm("int $3"); +#endif +} + + +class PosixMemoryMappedFile : public OS::MemoryMappedFile { + public: + PosixMemoryMappedFile(FILE* file, void* memory, int size) + : file_(file), memory_(memory), size_(size) { } + virtual ~PosixMemoryMappedFile(); + virtual void* memory() { return memory_; } + private: + FILE* file_; + void* memory_; + int size_; +}; + + +OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, + void* initial) { + FILE* file = fopen(name, "w+"); + if (file == NULL) return NULL; + int result = fwrite(initial, size, 1, file); + if (result < 1) { + fclose(file); + return NULL; + } + void* memory = + mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); + return new PosixMemoryMappedFile(file, memory, size); +} + + +PosixMemoryMappedFile::~PosixMemoryMappedFile() { + if (memory_) munmap(memory_, size_); + fclose(file_); +} + + +#ifdef ENABLE_LOGGING_AND_PROFILING +static unsigned StringToLong(char* buffer) { + return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT +} +#endif + + +void OS::LogSharedLibraryAddresses() { +#ifdef ENABLE_LOGGING_AND_PROFILING + static const int MAP_LENGTH = 1024; + int fd = open("/proc/self/maps", O_RDONLY); + if (fd < 0) return; + while (true) { + char addr_buffer[11]; + addr_buffer[0] = '0'; + addr_buffer[1] = 'x'; + addr_buffer[10] = 0; + int result = read(fd, addr_buffer + 2, 8); + if (result < 8) break; + unsigned start = StringToLong(addr_buffer); + result = read(fd, addr_buffer + 2, 1); + if (result < 1) break; + if (addr_buffer[2] != '-') break; + result = read(fd, addr_buffer + 2, 8); + if (result < 8) break; + unsigned end = StringToLong(addr_buffer); + char buffer[MAP_LENGTH]; + int bytes_read = -1; + do { + bytes_read++; + if (bytes_read >= MAP_LENGTH - 1) + break; + result = read(fd, buffer + bytes_read, 1); + if (result < 1) break; + } while (buffer[bytes_read] != '\n'); + buffer[bytes_read] = 0; + // Ignore mappings that are not executable. + if (buffer[3] != 'x') continue; + char* start_of_path = index(buffer, '/'); + // There may be no filename in this line. Skip to next. + if (start_of_path == NULL) continue; + buffer[bytes_read] = 0; + LOG(SharedLibraryEvent(start_of_path, start, end)); + } + close(fd); +#endif +} + + +int OS::StackWalk(Vector<OS::StackFrame> frames) { + UNIMPLEMENTED(); + return 1; +} + + +// Constants used for mmap. +static const int kMmapFd = -1; +static const int kMmapFdOffset = 0; + + +VirtualMemory::VirtualMemory(size_t size) { + address_ = mmap(NULL, size, PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, + kMmapFd, kMmapFdOffset); + size_ = size; +} + + +VirtualMemory::~VirtualMemory() { + if (IsReserved()) { + if (0 == munmap(address(), size())) address_ = MAP_FAILED; + } +} + + +bool VirtualMemory::IsReserved() { + return address_ != MAP_FAILED; +} + + +bool VirtualMemory::Commit(void* address, size_t size, bool executable) { + int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0); + if (MAP_FAILED == mmap(address, size, prot, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + kMmapFd, kMmapFdOffset)) { + return false; + } + + UpdateAllocatedSpaceLimits(address, size); + return true; +} + + +bool VirtualMemory::Uncommit(void* address, size_t size) { + return mmap(address, size, PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, + kMmapFd, kMmapFdOffset) != MAP_FAILED; +} + + +class ThreadHandle::PlatformData : public Malloced { + public: + explicit PlatformData(ThreadHandle::Kind kind) { + Initialize(kind); + } + + void Initialize(ThreadHandle::Kind kind) { + switch (kind) { + case ThreadHandle::SELF: thread_ = pthread_self(); break; + case ThreadHandle::INVALID: thread_ = kNoThread; break; + } + } + pthread_t thread_; // Thread handle for pthread. +}; + + +ThreadHandle::ThreadHandle(Kind kind) { + data_ = new PlatformData(kind); +} + + +void ThreadHandle::Initialize(ThreadHandle::Kind kind) { + data_->Initialize(kind); +} + + +ThreadHandle::~ThreadHandle() { + delete data_; +} + + +bool ThreadHandle::IsSelf() const { + return pthread_equal(data_->thread_, pthread_self()); +} + + +bool ThreadHandle::IsValid() const { + return data_->thread_ != kNoThread; +} + + +Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) { +} + + +Thread::~Thread() { +} + + +static void* ThreadEntry(void* arg) { + Thread* thread = reinterpret_cast<Thread*>(arg); + // This is also initialized by the first argument to pthread_create() but we + // don't know which thread will run first (the original thread or the new + // one) so we initialize it here too. + thread->thread_handle_data()->thread_ = pthread_self(); + ASSERT(thread->IsValid()); + thread->Run(); + return NULL; +} + + +void Thread::Start() { + pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this); + ASSERT(IsValid()); +} + + +void Thread::Join() { + pthread_join(thread_handle_data()->thread_, NULL); +} + + +Thread::LocalStorageKey Thread::CreateThreadLocalKey() { + pthread_key_t key; + int result = pthread_key_create(&key, NULL); + USE(result); + ASSERT(result == 0); + return static_cast<LocalStorageKey>(key); +} + + +void Thread::DeleteThreadLocalKey(LocalStorageKey key) { + pthread_key_t pthread_key = static_cast<pthread_key_t>(key); + int result = pthread_key_delete(pthread_key); + USE(result); + ASSERT(result == 0); +} + + +void* Thread::GetThreadLocal(LocalStorageKey key) { + pthread_key_t pthread_key = static_cast<pthread_key_t>(key); + return pthread_getspecific(pthread_key); +} + + +void Thread::SetThreadLocal(LocalStorageKey key, void* value) { + pthread_key_t pthread_key = static_cast<pthread_key_t>(key); + pthread_setspecific(pthread_key, value); +} + + +void Thread::YieldCPU() { + sched_yield(); +} + + +class OpenBSDMutex : public Mutex { + public: + + OpenBSDMutex() { + pthread_mutexattr_t attrs; + int result = pthread_mutexattr_init(&attrs); + ASSERT(result == 0); + result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE); + ASSERT(result == 0); + result = pthread_mutex_init(&mutex_, &attrs); + ASSERT(result == 0); + } + + virtual ~OpenBSDMutex() { pthread_mutex_destroy(&mutex_); } + + virtual int Lock() { + int result = pthread_mutex_lock(&mutex_); + return result; + } + + virtual int Unlock() { + int result = pthread_mutex_unlock(&mutex_); + return result; + } + + private: + pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms. +}; + + +Mutex* OS::CreateMutex() { + return new OpenBSDMutex(); +} + + +class OpenBSDSemaphore : public Semaphore { + public: + explicit OpenBSDSemaphore(int count) { sem_init(&sem_, 0, count); } + virtual ~OpenBSDSemaphore() { sem_destroy(&sem_); } + + virtual void Wait(); + virtual bool Wait(int timeout); + virtual void Signal() { sem_post(&sem_); } + private: + sem_t sem_; +}; + + +void OpenBSDSemaphore::Wait() { + while (true) { + int result = sem_wait(&sem_); + if (result == 0) return; // Successfully got semaphore. + CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. + } +} + + +bool OpenBSDSemaphore::Wait(int timeout) { + const long kOneSecondMicros = 1000000; // NOLINT + + // Split timeout into second and nanosecond parts. + struct timeval delta; + delta.tv_usec = timeout % kOneSecondMicros; + delta.tv_sec = timeout / kOneSecondMicros; + + struct timeval current_time; + // Get the current time. + if (gettimeofday(¤t_time, NULL) == -1) { + return false; + } + + // Calculate time for end of timeout. + struct timeval end_time; + timeradd(¤t_time, &delta, &end_time); + + struct timespec ts; + TIMEVAL_TO_TIMESPEC(&end_time, &ts); + while (true) { + int result = sem_trywait(&sem_); + if (result == 0) return true; // Successfully got semaphore. + if (result == -1 && errno == ETIMEDOUT) return false; // Timeout. + CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. + } +} + + +Semaphore* OS::CreateSemaphore(int count) { + return new OpenBSDSemaphore(count); +} + + +#ifdef ENABLE_LOGGING_AND_PROFILING + +static Sampler* active_sampler_ = NULL; + +static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { + USE(info); + if (signal != SIGPROF) return; + if (active_sampler_ == NULL) return; + + TickSample sample; + + // We always sample the VM state. + sample.state = Logger::state(); + + active_sampler_->Tick(&sample); +} + + +class Sampler::PlatformData : public Malloced { + public: + PlatformData() { + signal_handler_installed_ = false; + } + + bool signal_handler_installed_; + struct sigaction old_signal_handler_; + struct itimerval old_timer_value_; +}; + + +Sampler::Sampler(int interval, bool profiling) + : interval_(interval), profiling_(profiling), active_(false) { + data_ = new PlatformData(); +} + + +Sampler::~Sampler() { + delete data_; +} + + +void Sampler::Start() { + // There can only be one active sampler at the time on POSIX + // platforms. + if (active_sampler_ != NULL) return; + + // Request profiling signals. + struct sigaction sa; + sa.sa_sigaction = ProfilerSignalHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return; + data_->signal_handler_installed_ = true; + + // Set the itimer to generate a tick for each interval. + itimerval itimer; + itimer.it_interval.tv_sec = interval_ / 1000; + itimer.it_interval.tv_usec = (interval_ % 1000) * 1000; + itimer.it_value.tv_sec = itimer.it_interval.tv_sec; + itimer.it_value.tv_usec = itimer.it_interval.tv_usec; + setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_); + + // Set this sampler as the active sampler. + active_sampler_ = this; + active_ = true; +} + + +void Sampler::Stop() { + // Restore old signal handler + if (data_->signal_handler_installed_) { + setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL); + sigaction(SIGPROF, &data_->old_signal_handler_, 0); + data_->signal_handler_installed_ = false; + } + + // This sampler is no longer the active sampler. + active_sampler_ = NULL; + active_ = false; +} + +#endif // ENABLE_LOGGING_AND_PROFILING + +} } // namespace v8::internal diff --git a/deps/v8/src/platform-posix.cc b/deps/v8/src/platform-posix.cc index 1e1245c5d..41e0e64f3 100644 --- a/deps/v8/src/platform-posix.cc +++ b/deps/v8/src/platform-posix.cc @@ -27,7 +27,7 @@ // Platform specific code for POSIX goes here. This is not a platform on its // own but contains the parts which are the same across POSIX platforms Linux, -// Mac OS and FreeBSD. +// Mac OS, FreeBSD and OpenBSD. #include <unistd.h> #include <errno.h> @@ -61,6 +61,13 @@ double modulo(double x, double y) { return fmod(x, y); } + +double OS::nan_value() { + // NAN from math.h is defined in C99 and not in POSIX. + return NAN; +} + + // ---------------------------------------------------------------------------- // POSIX date/time support. // diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index 6ae22330a..65dfd1326 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -788,51 +788,72 @@ static Object* Runtime_InitializeVarGlobal(Arguments args) { // case of callbacks in the prototype chain (this rules out using // SetProperty). We have IgnoreAttributesAndSetLocalProperty for // this. + // Note that objects can have hidden prototypes, so we need to traverse + // the whole chain of hidden prototypes to do a 'local' lookup. + JSObject* real_holder = global; LookupResult lookup; - global->LocalLookup(*name, &lookup); - if (!lookup.IsProperty()) { - if (assign) { - return global->IgnoreAttributesAndSetLocalProperty(*name, - args[1], - attributes); + while (true) { + real_holder->LocalLookup(*name, &lookup); + if (lookup.IsProperty()) { + // Determine if this is a redeclaration of something read-only. + if (lookup.IsReadOnly()) { + // If we found readonly property on one of hidden prototypes, + // just shadow it. + if (real_holder != Top::context()->global()) break; + return ThrowRedeclarationError("const", name); + } + + // Determine if this is a redeclaration of an intercepted read-only + // property and figure out if the property exists at all. + bool found = true; + PropertyType type = lookup.type(); + if (type == INTERCEPTOR) { + HandleScope handle_scope; + Handle<JSObject> holder(real_holder); + PropertyAttributes intercepted = holder->GetPropertyAttribute(*name); + real_holder = *holder; + if (intercepted == ABSENT) { + // The interceptor claims the property isn't there. We need to + // make sure to introduce it. + found = false; + } else if ((intercepted & READ_ONLY) != 0) { + // The property is present, but read-only. Since we're trying to + // overwrite it with a variable declaration we must throw a + // re-declaration error. However if we found readonly property + // on one of hidden prototypes, just shadow it. + if (real_holder != Top::context()->global()) break; + return ThrowRedeclarationError("const", name); + } + } + + if (found && !assign) { + // The global property is there and we're not assigning any value + // to it. Just return. + return Heap::undefined_value(); + } + + // Assign the value (or undefined) to the property. + Object* value = (assign) ? args[1] : Heap::undefined_value(); + return real_holder->SetProperty(&lookup, *name, value, attributes); } - return Heap::undefined_value(); - } - // Determine if this is a redeclaration of something read-only. - if (lookup.IsReadOnly()) { - return ThrowRedeclarationError("const", name); - } + Object* proto = real_holder->GetPrototype(); + if (!proto->IsJSObject()) + break; - // Determine if this is a redeclaration of an intercepted read-only - // property and figure out if the property exists at all. - bool found = true; - PropertyType type = lookup.type(); - if (type == INTERCEPTOR) { - PropertyAttributes intercepted = global->GetPropertyAttribute(*name); - if (intercepted == ABSENT) { - // The interceptor claims the property isn't there. We need to - // make sure to introduce it. - found = false; - } else if ((intercepted & READ_ONLY) != 0) { - // The property is present, but read-only. Since we're trying to - // overwrite it with a variable declaration we must throw a - // re-declaration error. - return ThrowRedeclarationError("const", name); - } - // Restore global object from context (in case of GC). - global = Top::context()->global(); - } + if (!JSObject::cast(proto)->map()->is_hidden_prototype()) + break; - if (found && !assign) { - // The global property is there and we're not assigning any value - // to it. Just return. - return Heap::undefined_value(); + real_holder = JSObject::cast(proto); } - // Assign the value (or undefined) to the property. - Object* value = (assign) ? args[1] : Heap::undefined_value(); - return global->SetProperty(&lookup, *name, value, attributes); + global = Top::context()->global(); + if (assign) { + return global->IgnoreAttributesAndSetLocalProperty(*name, + args[1], + attributes); + } + return Heap::undefined_value(); } @@ -3762,6 +3783,7 @@ static Object* Runtime_StringAdd(Arguments args) { ASSERT(args.length() == 2); CONVERT_CHECKED(String, str1, args[0]); CONVERT_CHECKED(String, str2, args[1]); + Counters::string_add_runtime.Increment(); return Heap::AllocateConsString(str1, str2); } @@ -4987,6 +5009,9 @@ static Object* Runtime_DebugPrint(Arguments args) { PrintF("DebugPrint: "); } args[0]->Print(); + if (args[0]->IsHeapObject()) { + HeapObject::cast(args[0])->map()->Print(); + } #else // ShortPrint is available in release mode. Print is not. args[0]->ShortPrint(); @@ -7667,8 +7692,31 @@ static Object* Runtime_FunctionGetInferredName(Arguments args) { CONVERT_CHECKED(JSFunction, f, args[0]); return f->shared()->inferred_name(); } + #endif // ENABLE_DEBUGGER_SUPPORT +#ifdef ENABLE_LOGGING_AND_PROFILING + +static Object* Runtime_ProfilerResume(Arguments args) { + NoHandleAllocation ha; + ASSERT(args.length() == 1); + + CONVERT_CHECKED(Smi, smi_modules, args[0]); + v8::V8::ResumeProfilerEx(smi_modules->value()); + return Heap::undefined_value(); +} + + +static Object* Runtime_ProfilerPause(Arguments args) { + NoHandleAllocation ha; + ASSERT(args.length() == 1); + + CONVERT_CHECKED(Smi, smi_modules, args[0]); + v8::V8::PauseProfilerEx(smi_modules->value()); + return Heap::undefined_value(); +} + +#endif // ENABLE_LOGGING_AND_PROFILING // Finds the script object from the script data. NOTE: This operation uses // heap traversal to find the function generated for the source position diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h index c05ae6b7b..858023317 100644 --- a/deps/v8/src/runtime.h +++ b/deps/v8/src/runtime.h @@ -318,6 +318,14 @@ namespace internal { #define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F) #endif +#ifdef ENABLE_LOGGING_AND_PROFILING +#define RUNTIME_FUNCTION_LIST_PROFILER_SUPPORT(F) \ + F(ProfilerResume, 1, 1) \ + F(ProfilerPause, 1, 1) +#else +#define RUNTIME_FUNCTION_LIST_PROFILER_SUPPORT(F) +#endif + #ifdef DEBUG #define RUNTIME_FUNCTION_LIST_DEBUG(F) \ /* Testing */ \ @@ -336,7 +344,8 @@ namespace internal { RUNTIME_FUNCTION_LIST_ALWAYS_1(F) \ RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \ RUNTIME_FUNCTION_LIST_DEBUG(F) \ - RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F) + RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F) \ + RUNTIME_FUNCTION_LIST_PROFILER_SUPPORT(F) // ---------------------------------------------------------------------------- // Runtime provides access to all C++ runtime functions. diff --git a/deps/v8/src/runtime.js b/deps/v8/src/runtime.js index ba19871f1..105749a75 100644 --- a/deps/v8/src/runtime.js +++ b/deps/v8/src/runtime.js @@ -146,16 +146,16 @@ function COMPARE(x, ncr) { function ADD(x) { // Fast case: Check for number operands and do the addition. if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberAdd(this, x); - if (IS_STRING(this) && IS_STRING(x)) return %StringAdd(this, x); + if (IS_STRING(this) && IS_STRING(x)) return %_StringAdd(this, x); // Default implementation. var a = %ToPrimitive(this, NO_HINT); var b = %ToPrimitive(x, NO_HINT); if (IS_STRING(a)) { - return %StringAdd(a, %ToString(b)); + return %_StringAdd(a, %ToString(b)); } else if (IS_STRING(b)) { - return %StringAdd(%ToString(a), b); + return %_StringAdd(%ToString(a), b); } else { return %NumberAdd(%ToNumber(a), %ToNumber(b)); } @@ -173,7 +173,7 @@ function STRING_ADD_LEFT(y) { : %ToString(%ToPrimitive(y, NO_HINT)); } } - return %StringAdd(this, y); + return %_StringAdd(this, y); } @@ -189,7 +189,7 @@ function STRING_ADD_RIGHT(y) { : %ToString(%ToPrimitive(x, NO_HINT)); } } - return %StringAdd(x, y); + return %_StringAdd(x, y); } diff --git a/deps/v8/src/serialize.cc b/deps/v8/src/serialize.cc index 00cd69ec0..899e2e7a5 100644 --- a/deps/v8/src/serialize.cc +++ b/deps/v8/src/serialize.cc @@ -44,6 +44,69 @@ namespace v8 { namespace internal { +// Mapping objects to their location after deserialization. +// This is used during building, but not at runtime by V8. +class SerializationAddressMapper { + public: + static bool IsMapped(HeapObject* obj) { + EnsureMapExists(); + return serialization_map_->Lookup(Key(obj), Hash(obj), false) != NULL; + } + + static int MappedTo(HeapObject* obj) { + ASSERT(IsMapped(obj)); + return reinterpret_cast<intptr_t>(serialization_map_->Lookup(Key(obj), + Hash(obj), + false)->value); + } + + static void Map(HeapObject* obj, int to) { + EnsureMapExists(); + ASSERT(!IsMapped(obj)); + HashMap::Entry* entry = + serialization_map_->Lookup(Key(obj), Hash(obj), true); + entry->value = Value(to); + } + + static void Zap() { + if (serialization_map_ != NULL) { + delete serialization_map_; + } + serialization_map_ = NULL; + } + + private: + static bool SerializationMatchFun(void* key1, void* key2) { + return key1 == key2; + } + + static uint32_t Hash(HeapObject* obj) { + return reinterpret_cast<intptr_t>(obj->address()); + } + + static void* Key(HeapObject* obj) { + return reinterpret_cast<void*>(obj->address()); + } + + static void* Value(int v) { + return reinterpret_cast<void*>(v); + } + + static void EnsureMapExists() { + if (serialization_map_ == NULL) { + serialization_map_ = new HashMap(&SerializationMatchFun); + } + } + + static HashMap* serialization_map_; +}; + + +HashMap* SerializationAddressMapper::serialization_map_ = NULL; + + + + // ----------------------------------------------------------------------------- // Coding of external references. @@ -871,6 +934,7 @@ void Serializer::Serialize() { Heap::IterateRoots(this, VISIT_ONLY_STRONG); delete external_reference_encoder_; external_reference_encoder_ = NULL; + SerializationAddressMapper::Zap(); } @@ -894,10 +958,9 @@ void Serializer::SerializeObject( ReferenceRepresentation reference_representation) { CHECK(o->IsHeapObject()); HeapObject* heap_object = HeapObject::cast(o); - MapWord map_word = heap_object->map_word(); - if (map_word.IsSerializationAddress()) { + if (SerializationAddressMapper::IsMapped(heap_object)) { int space = SpaceOfAlreadySerializedObject(heap_object); - int address = map_word.ToSerializationAddress(); + int address = SerializationAddressMapper::MappedTo(heap_object); int offset = CurrentAllocationAddress(space) - address; bool from_start = true; if (SpaceIsPaged(space)) { @@ -965,24 +1028,23 @@ void Serializer::ObjectSerializer::Serialize() { } sink_->PutInt(size >> kObjectAlignmentBits, "Size in words"); - // Get the map before overwriting it. - Map* map = object_->map(); // Mark this object as already serialized. bool start_new_page; - object_->set_map_word(MapWord::FromSerializationAddress( - serializer_->Allocate(space, size, &start_new_page))); + SerializationAddressMapper::Map( + object_, + serializer_->Allocate(space, size, &start_new_page)); if (start_new_page) { sink_->Put(START_NEW_PAGE_SERIALIZATION, "NewPage"); sink_->PutSection(space, "NewPageSpace"); } // Serialize the map (first word of the object). - serializer_->SerializeObject(map, TAGGED_REPRESENTATION); + serializer_->SerializeObject(object_->map(), TAGGED_REPRESENTATION); // Serialize the rest of the object. CHECK_EQ(0, bytes_processed_so_far_); bytes_processed_so_far_ = kPointerSize; - object_->IterateBody(map->instance_type(), size, this); + object_->IterateBody(object_->map()->instance_type(), size, this); OutputRawData(object_->address() + size); } @@ -1044,12 +1106,9 @@ void Serializer::ObjectSerializer::VisitExternalAsciiString( Address references_start = reinterpret_cast<Address>(resource_pointer); OutputRawData(references_start); for (int i = 0; i < Natives::GetBuiltinsCount(); i++) { - // Use raw_unchecked when maps are munged. - Object* source = Heap::raw_unchecked_natives_source_cache()->get(i); + Object* source = Heap::natives_source_cache()->get(i); if (!source->IsUndefined()) { - // Don't use cast when maps are munged. - ExternalAsciiString* string = - reinterpret_cast<ExternalAsciiString*>(source); + ExternalAsciiString* string = ExternalAsciiString::cast(source); typedef v8::String::ExternalAsciiStringResource Resource; Resource* resource = string->resource(); if (resource == *resource_pointer) { diff --git a/deps/v8/src/string-stream.cc b/deps/v8/src/string-stream.cc index eb5d1e31e..d1859a20f 100644 --- a/deps/v8/src/string-stream.cc +++ b/deps/v8/src/string-stream.cc @@ -188,7 +188,7 @@ void StringStream::Add(Vector<const char> format, Vector<FmtElm> elms) { void StringStream::PrintObject(Object* o) { o->ShortPrint(this); if (o->IsString()) { - if (String::cast(o)->length() <= String::kMaxMediumSize) { + if (String::cast(o)->length() <= String::kMaxShortPrintLength) { return; } } else if (o->IsNumber() || o->IsOddball()) { diff --git a/deps/v8/src/stub-cache.cc b/deps/v8/src/stub-cache.cc index a399e4563..51d9ddb8f 100644 --- a/deps/v8/src/stub-cache.cc +++ b/deps/v8/src/stub-cache.cc @@ -750,6 +750,9 @@ Object* LoadCallbackProperty(Arguments args) { { // Leaving JavaScript. VMState state(EXTERNAL); +#ifdef ENABLE_LOGGING_AND_PROFILING + state.set_external_callback(getter_address); +#endif result = fun(v8::Utils::ToLocal(args.at<String>(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(); @@ -773,6 +776,9 @@ Object* StoreCallbackProperty(Arguments args) { { // Leaving JavaScript. VMState state(EXTERNAL); +#ifdef ENABLE_LOGGING_AND_PROFILING + state.set_external_callback(setter_address); +#endif fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(); diff --git a/deps/v8/src/stub-cache.h b/deps/v8/src/stub-cache.h index e26892026..788c5324f 100644 --- a/deps/v8/src/stub-cache.h +++ b/deps/v8/src/stub-cache.h @@ -226,9 +226,9 @@ class StubCache : public AllStatic { // hash code would effectively throw away two bits of the hash // code. ASSERT(kHeapObjectTagSize == String::kHashShift); - // Compute the hash of the name (use entire length field). + // Compute the hash of the name (use entire hash field). ASSERT(name->HasHashCode()); - uint32_t field = name->length_field(); + uint32_t field = name->hash_field(); // Using only the low bits in 64-bit mode is unlikely to increase the // risk of collision even if the heap is spread over an area larger than // 4Gb (and not at all if it isn't). diff --git a/deps/v8/src/utils.cc b/deps/v8/src/utils.cc index ce5aceda3..08ee16ff2 100644 --- a/deps/v8/src/utils.cc +++ b/deps/v8/src/utils.cc @@ -309,4 +309,13 @@ char* StringBuilder::Finalize() { return buffer_.start(); } + +int TenToThe(int exponent) { + ASSERT(exponent <= 9); + ASSERT(exponent >= 1); + int answer = 10; + for (int i = 1; i < exponent; i++) answer *= 10; + return answer; +} + } } // namespace v8::internal diff --git a/deps/v8/src/utils.h b/deps/v8/src/utils.h index c271ae17f..0fd24ec9a 100644 --- a/deps/v8/src/utils.h +++ b/deps/v8/src/utils.h @@ -584,6 +584,9 @@ static inline void CopyChars(sinkchar* dest, const sourcechar* src, int chars) { } +// Calculate 10^exponent. +int TenToThe(int exponent); + } } // namespace v8::internal #endif // V8_UTILS_H_ diff --git a/deps/v8/src/v8-counters.h b/deps/v8/src/v8-counters.h index b3f29f530..d6f53fab1 100644 --- a/deps/v8/src/v8-counters.h +++ b/deps/v8/src/v8-counters.h @@ -153,8 +153,9 @@ namespace internal { SC(zone_segment_bytes, V8.ZoneSegmentBytes) \ SC(compute_entry_frame, V8.ComputeEntryFrame) \ SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \ - SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) - + SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) \ + SC(string_add_runtime, V8.StringAddRuntime) \ + SC(string_add_native, V8.StringAddNative) // This file contains all the v8 counters that are in use. class Counters : AllStatic { diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index f9dc9b75f..36a42f8f3 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 2 +#define BUILD_NUMBER 3 #define PATCH_LEVEL 0 #define CANDIDATE_VERSION false diff --git a/deps/v8/src/x64/assembler-x64.h b/deps/v8/src/x64/assembler-x64.h index 50f4e0e45..fa7d33b1b 100644 --- a/deps/v8/src/x64/assembler-x64.h +++ b/deps/v8/src/x64/assembler-x64.h @@ -482,6 +482,12 @@ class Assembler : public Malloced { static const int kPatchReturnSequenceAddressOffset = 13 - 4; // TODO(X64): Rename this, removing the "Real", after changing the above. static const int kRealPatchReturnSequenceAddressOffset = 2; + + // The x64 JS return sequence is padded with int3 to make it large + // enough to hold a call instruction when the debugger patches it. + static const int kCallInstructionLength = 13; + static const int kJSReturnSequenceLength = 13; + // --------------------------------------------------------------------------- // Code generation // diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc index b32357266..36f0e635f 100644 --- a/deps/v8/src/x64/codegen-x64.cc +++ b/deps/v8/src/x64/codegen-x64.cc @@ -505,13 +505,13 @@ void CodeGenerator::GenerateReturnSequence(Result* return_value) { // Add padding that will be overwritten by a debugger breakpoint. // frame_->Exit() generates "movq rsp, rbp; pop rbp; ret k" // with length 7 (3 + 1 + 3). - const int kPadding = Debug::kX64JSReturnSequenceLength - 7; + const int kPadding = Assembler::kJSReturnSequenceLength - 7; for (int i = 0; i < kPadding; ++i) { masm_->int3(); } // Check that the size of the code used for returning matches what is // expected by the debugger. - ASSERT_EQ(Debug::kX64JSReturnSequenceLength, + ASSERT_EQ(Assembler::kJSReturnSequenceLength, masm_->SizeOfCodeGeneratedSince(&check_exit_codesize)); #endif DeleteFrame(); @@ -1662,8 +1662,54 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { jsobject.Bind(); // Get the set of properties (as a FixedArray or Map). // rax: value to be iterated over - frame_->EmitPush(rax); // push the object being iterated over (slot 4) + frame_->EmitPush(rax); // Push the object being iterated over. + + // Check cache validity in generated code. This is a fast case for + // the JSObject::IsSimpleEnum cache validity checks. If we cannot + // guarantee cache validity, call the runtime system to check cache + // validity or get the property names in a fixed array. + JumpTarget call_runtime; + JumpTarget loop(JumpTarget::BIDIRECTIONAL); + JumpTarget check_prototype; + JumpTarget use_cache; + __ movq(rcx, rax); + loop.Bind(); + // Check that there are no elements. + __ movq(rdx, FieldOperand(rcx, JSObject::kElementsOffset)); + __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex); + call_runtime.Branch(not_equal); + // Check that instance descriptors are not empty so that we can + // check for an enum cache. Leave the map in ebx for the subsequent + // prototype load. + __ movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset)); + __ movq(rdx, FieldOperand(rbx, Map::kInstanceDescriptorsOffset)); + __ CompareRoot(rdx, Heap::kEmptyDescriptorArrayRootIndex); + call_runtime.Branch(equal); + // Check that there in an enum cache in the non-empty instance + // descriptors. This is the case if the next enumeration index + // field does not contain a smi. + __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumerationIndexOffset)); + is_smi = masm_->CheckSmi(rdx); + call_runtime.Branch(is_smi); + // For all objects but the receiver, check that the cache is empty. + __ cmpq(rcx, rax); + check_prototype.Branch(equal); + __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumCacheBridgeCacheOffset)); + __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex); + call_runtime.Branch(not_equal); + check_prototype.Bind(); + // Load the prototype from the map and loop if non-null. + __ movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset)); + __ CompareRoot(rcx, Heap::kNullValueRootIndex); + loop.Branch(not_equal); + // The enum cache is valid. Load the map of the object being + // iterated over and use the cache for the iteration. + __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset)); + use_cache.Jump(); + + call_runtime.Bind(); + // Call the runtime to get the property names for the object. frame_->EmitPush(rax); // push the Object (slot 4) for the runtime call frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1); @@ -1676,8 +1722,11 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { __ CompareRoot(rcx, Heap::kMetaMapRootIndex); fixed_array.Branch(not_equal); + use_cache.Bind(); // Get enum cache - // rax: map (result from call to Runtime::kGetPropertyNamesFast) + // rax: map (either the result from a call to + // Runtime::kGetPropertyNamesFast or has been fetched directly from + // the object) __ movq(rcx, rax); __ movq(rcx, FieldOperand(rcx, Map::kInstanceDescriptorsOffset)); // Get the bridge array held in the enumeration index field. @@ -3767,20 +3816,8 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) { __ testb(rcx, Immediate(kIsNotStringMask)); __ j(not_zero, &slow_case); - // Here we make assumptions about the tag values and the shifts needed. - // See the comment in objects.h. - ASSERT(kLongStringTag == 0); - ASSERT(kMediumStringTag + String::kLongLengthShift == - String::kMediumLengthShift); - ASSERT(kShortStringTag + String::kLongLengthShift == - String::kShortLengthShift); - __ and_(rcx, Immediate(kStringSizeMask)); - __ addq(rcx, Immediate(String::kLongLengthShift)); - // Fetch the length field into the temporary register. - __ movl(temp.reg(), FieldOperand(object.reg(), String::kLengthOffset)); - __ shrl_cl(temp.reg()); // Check for index out of range. - __ cmpl(index.reg(), temp.reg()); + __ cmpl(index.reg(), FieldOperand(object.reg(), String::kLengthOffset)); __ j(greater_equal, &slow_case); // Reload the instance type (into the temp register this time).. __ movq(temp.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset)); @@ -4008,6 +4045,17 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) { + ASSERT_EQ(2, args->length()); + + Load(args->at(0)); + Load(args->at(1)); + + Result answer = frame_->CallRuntime(Runtime::kStringAdd, 2); + frame_->Push(&answer); +} + + void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) { ASSERT(args->length() == 1); JumpTarget leave, null, function, non_function_constructor; @@ -6175,11 +6223,8 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { // String value => false iff empty. __ cmpq(rcx, Immediate(FIRST_NONSTRING_TYPE)); __ j(above_equal, ¬_string); - __ and_(rcx, Immediate(kStringSizeMask)); - __ cmpq(rcx, Immediate(kShortStringTag)); - __ j(not_equal, &true_result); // Empty string is always short. __ movl(rdx, FieldOperand(rax, String::kLengthOffset)); - __ shr(rdx, Immediate(String::kShortLengthShift)); + __ testl(rdx, rdx); __ j(zero, &false_result); __ jmp(&true_result); @@ -7732,9 +7777,47 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { __ push(rcx); } switch (op_) { - case Token::ADD: + case Token::ADD: { + // Test for string arguments before calling runtime. + Label not_strings, both_strings, not_string1, string1; + Condition is_smi; + Result answer; + __ movq(rdx, Operand(rsp, 2 * kPointerSize)); // First argument. + __ movq(rax, Operand(rsp, 1 * kPointerSize)); // Second argument. + is_smi = masm->CheckSmi(rdx); + __ j(is_smi, ¬_string1); + __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rdx); + __ j(above_equal, ¬_string1); + + // First argument is a a string, test second. + is_smi = masm->CheckSmi(rax); + __ j(is_smi, &string1); + __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, rax); + __ j(above_equal, &string1); + + // First and second argument are strings. + Runtime::Function* f = Runtime::FunctionForId(Runtime::kStringAdd); + __ TailCallRuntime(ExternalReference(f), 2, f->result_size); + + // Only first argument is a string. + __ bind(&string1); + __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_FUNCTION); + + // First argument was not a string, test second. + __ bind(¬_string1); + is_smi = masm->CheckSmi(rax); + __ j(is_smi, ¬_strings); + __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, rax); + __ j(above_equal, ¬_strings); + + // Only second argument is a string. + __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_FUNCTION); + + __ bind(¬_strings); + // Neither argument is a string. __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION); break; + } case Token::SUB: __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION); break; diff --git a/deps/v8/src/x64/codegen-x64.h b/deps/v8/src/x64/codegen-x64.h index 20df41a50..8539884aa 100644 --- a/deps/v8/src/x64/codegen-x64.h +++ b/deps/v8/src/x64/codegen-x64.h @@ -544,6 +544,9 @@ class CodeGenerator: public AstVisitor { inline void GenerateMathSin(ZoneList<Expression*>* args); inline void GenerateMathCos(ZoneList<Expression*>* args); + // Fast support for StringAdd. + void GenerateStringAdd(ZoneList<Expression*>* args); + // Simple condition analysis. enum ConditionAnalysis { ALWAYS_TRUE, diff --git a/deps/v8/src/x64/debug-x64.cc b/deps/v8/src/x64/debug-x64.cc index 49240b407..bc88d4668 100644 --- a/deps/v8/src/x64/debug-x64.cc +++ b/deps/v8/src/x64/debug-x64.cc @@ -181,7 +181,7 @@ void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) { void BreakLocationIterator::ClearDebugBreakAtReturn() { rinfo()->PatchCode(original_rinfo()->pc(), - Debug::kX64JSReturnSequenceLength); + Assembler::kJSReturnSequenceLength); } @@ -191,9 +191,10 @@ bool BreakLocationIterator::IsDebugBreakAtReturn() { void BreakLocationIterator::SetDebugBreakAtReturn() { - ASSERT(Debug::kX64JSReturnSequenceLength >= Debug::kX64CallInstructionLength); + ASSERT(Assembler::kJSReturnSequenceLength >= + Assembler::kCallInstructionLength); rinfo()->PatchCodeWithCall(Debug::debug_break_return()->entry(), - Debug::kX64JSReturnSequenceLength - Debug::kX64CallInstructionLength); + Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength); } #endif // ENABLE_DEBUGGER_SUPPORT diff --git a/deps/v8/src/x64/fast-codegen-x64.cc b/deps/v8/src/x64/fast-codegen-x64.cc index bb85ef5d6..f73f2b90f 100644 --- a/deps/v8/src/x64/fast-codegen-x64.cc +++ b/deps/v8/src/x64/fast-codegen-x64.cc @@ -76,11 +76,43 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) { bool function_in_register = true; + // Possibly allocate a local context. + if (fun->scope()->num_heap_slots() > 0) { + Comment cmnt(masm_, "[ Allocate local context"); + // Argument to NewContext is the function, which is still in rdi. + __ push(rdi); + __ CallRuntime(Runtime::kNewContext, 1); + function_in_register = false; + // 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); + + // Copy any necessary parameters into the context. + int num_parameters = fun->scope()->num_parameters(); + for (int i = 0; i < num_parameters; i++) { + Slot* slot = fun->scope()->parameter(i)->slot(); + if (slot != NULL && slot->type() == Slot::CONTEXT) { + int parameter_offset = StandardFrameConstants::kCallerSPOffset + + (num_parameters - 1 - i) * kPointerSize; + // Load parameter from stack. + __ movq(rax, Operand(rbp, parameter_offset)); + // Store it in the context + __ movq(Operand(rsi, Context::SlotOffset(slot->index())), rax); + } + } + } + + // Possibly allocate an arguments object. Variable* arguments = fun->scope()->arguments()->AsVariable(); if (arguments != NULL) { - // Function uses arguments object. + // Arguments object must be allocated after the context object, in + // case the "arguments" or ".arguments" variables are in the context. Comment cmnt(masm_, "[ Allocate arguments object"); - __ push(rdi); + if (function_in_register) { + __ push(rdi); + } else { + __ push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); + } // The receiver is just before the parameters on the caller's stack. __ lea(rdx, Operand(rbp, StandardFrameConstants::kCallerSPOffset + fun->num_parameters() * kPointerSize)); @@ -93,34 +125,11 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) { ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); __ CallStub(&stub); // Store new arguments object in both "arguments" and ".arguments" slots. - __ movq(Operand(rbp, SlotOffset(arguments->slot())), rax); + __ movq(rcx, rax); + Move(arguments->slot(), rax, rbx, rdx); Slot* dot_arguments_slot = fun->scope()->arguments_shadow()->AsVariable()->slot(); - __ movq(Operand(rbp, SlotOffset(dot_arguments_slot)), rax); - function_in_register = false; - } - - // Possibly allocate a local context. - if (fun->scope()->num_heap_slots() > 0) { - Comment cmnt(masm_, "[ Allocate local context"); - if (function_in_register) { - // Argument to NewContext is the function, still in rdi. - __ push(rdi); - } else { - // Argument to NewContext is the function, no longer in rdi. - __ push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); - } - __ CallRuntime(Runtime::kNewContext, 1); - // 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); -#ifdef DEBUG - // Assert we do not have to copy any parameters into the context. - for (int i = 0, len = fun->scope()->num_parameters(); i < len; i++) { - Slot* slot = fun->scope()->parameter(i)->slot(); - ASSERT(slot != NULL && slot->type() != Slot::CONTEXT); - } -#endif + Move(dot_arguments_slot, rcx, rbx, rdx); } { Comment cmnt(masm_, "[ Stack check"); @@ -180,13 +189,13 @@ void FastCodeGenerator::EmitReturnSequence(int position) { // Add padding that will be overwritten by a debugger breakpoint. We // have just generated "movq rsp, rbp; pop rbp; ret k" with length 7 // (3 + 1 + 3). - const int kPadding = Debug::kX64JSReturnSequenceLength - 7; + const int kPadding = Assembler::kJSReturnSequenceLength - 7; for (int i = 0; i < kPadding; ++i) { masm_->int3(); } // Check that the size of the code used for returning matches what is // expected by the debugger. - ASSERT_EQ(Debug::kX64JSReturnSequenceLength, + ASSERT_EQ(Assembler::kJSReturnSequenceLength, masm_->SizeOfCodeGeneratedSince(&check_exit_codesize)); #endif } @@ -227,20 +236,54 @@ void FastCodeGenerator::Move(Expression::Context context, Register source) { } -void FastCodeGenerator::Move(Expression::Context context, Slot* source) { +template <> +Operand FastCodeGenerator::CreateSlotOperand<Operand>(Slot* source, + Register scratch) { + switch (source->type()) { + case Slot::PARAMETER: + case Slot::LOCAL: + return Operand(rbp, SlotOffset(source)); + case Slot::CONTEXT: { + int context_chain_length = + function_->scope()->ContextChainLength(source->var()->scope()); + __ LoadContext(scratch, context_chain_length); + return CodeGenerator::ContextOperand(scratch, source->index()); + break; + } + case Slot::LOOKUP: + UNIMPLEMENTED(); + // Fall-through. + default: + UNREACHABLE(); + return Operand(rax, 0); // Dead code to make the compiler happy. + } +} + + +void FastCodeGenerator::Move(Register dst, Slot* source) { + Operand location = CreateSlotOperand<Operand>(source, dst); + __ movq(dst, location); +} + + +void FastCodeGenerator::Move(Expression::Context context, + Slot* source, + Register scratch) { switch (context) { case Expression::kUninitialized: UNREACHABLE(); case Expression::kEffect: break; - case Expression::kValue: - __ push(Operand(rbp, SlotOffset(source))); + case Expression::kValue: { + Operand location = CreateSlotOperand<Operand>(source, scratch); + __ push(location); break; + } case Expression::kTest: // Fall through. case Expression::kValueTest: // Fall through. case Expression::kTestValue: - __ movq(rax, Operand(rbp, SlotOffset(source))); - Move(context, rax); + Move(scratch, source); + Move(context, scratch); break; } } @@ -265,24 +308,61 @@ void FastCodeGenerator::Move(Expression::Context context, Literal* expr) { } +void FastCodeGenerator::Move(Slot* dst, + Register src, + Register scratch1, + Register scratch2) { + switch (dst->type()) { + case Slot::PARAMETER: + case Slot::LOCAL: + __ movq(Operand(rbp, SlotOffset(dst)), src); + break; + case Slot::CONTEXT: { + ASSERT(!src.is(scratch1)); + ASSERT(!src.is(scratch2)); + ASSERT(!scratch1.is(scratch2)); + int context_chain_length = + function_->scope()->ContextChainLength(dst->var()->scope()); + __ LoadContext(scratch1, context_chain_length); + __ movq(Operand(scratch1, Context::SlotOffset(dst->index())), src); + int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize; + __ RecordWrite(scratch1, offset, src, scratch2); + break; + } + case Slot::LOOKUP: + UNIMPLEMENTED(); + default: + UNREACHABLE(); + } +} + + void FastCodeGenerator::DropAndMove(Expression::Context context, - Register source) { + Register source, + int drop_count) { + ASSERT(drop_count > 0); switch (context) { case Expression::kUninitialized: UNREACHABLE(); case Expression::kEffect: - __ addq(rsp, Immediate(kPointerSize)); + __ addq(rsp, Immediate(drop_count * kPointerSize)); break; case Expression::kValue: + if (drop_count > 1) { + __ addq(rsp, Immediate((drop_count - 1) * kPointerSize)); + } __ movq(Operand(rsp, 0), source); break; case Expression::kTest: ASSERT(!source.is(rsp)); - __ addq(rsp, Immediate(kPointerSize)); + __ addq(rsp, Immediate(drop_count * kPointerSize)); TestAndBranch(source, true_label_, false_label_); break; case Expression::kValueTest: { Label discard; + if (drop_count > 1) { + __ addq(rsp, Immediate((drop_count - 1) * kPointerSize)); + } __ movq(Operand(rsp, 0), source); TestAndBranch(source, true_label_, &discard); __ bind(&discard); @@ -382,26 +462,26 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { __ Move(rax, Factory::the_hole_value()); if (FLAG_debug_code) { // Check if we have the correct context pointer. - __ movq(rbx, CodeGenerator::ContextOperand( - rsi, Context::FCONTEXT_INDEX)); + __ movq(rbx, CodeGenerator::ContextOperand(rsi, + Context::FCONTEXT_INDEX)); __ cmpq(rbx, rsi); __ Check(equal, "Unexpected declaration in current context."); } __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), rax); // No write barrier since the_hole_value is in old space. - ASSERT(Heap::InNewSpace(*Factory::the_hole_value())); + ASSERT(!Heap::InNewSpace(*Factory::the_hole_value())); } else if (decl->fun() != NULL) { Visit(decl->fun()); __ pop(rax); if (FLAG_debug_code) { // Check if we have the correct context pointer. - __ movq(rbx, CodeGenerator::ContextOperand( - rsi, Context::FCONTEXT_INDEX)); + __ movq(rbx, CodeGenerator::ContextOperand(rsi, + Context::FCONTEXT_INDEX)); __ cmpq(rbx, rsi); __ Check(equal, "Unexpected declaration in current context."); } __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), rax); - int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; + int offset = Context::SlotOffset(slot->index()); __ RecordWrite(rsi, offset, rax, rcx); } break; @@ -473,53 +553,59 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { DropAndMove(expr->context(), rax); } else if (rewrite->AsSlot() != NULL) { Slot* slot = rewrite->AsSlot(); - switch (slot->type()) { - case Slot::LOCAL: - case Slot::PARAMETER: { - Comment cmnt(masm_, "Stack slot"); - Move(expr->context(), slot); - break; - } - - case Slot::CONTEXT: { - Comment cmnt(masm_, "Context slot"); - int chain_length = - function_->scope()->ContextChainLength(slot->var()->scope()); - if (chain_length > 0) { - // Move up the chain of contexts to the context containing the slot. - __ movq(rax, - Operand(rsi, Context::SlotOffset(Context::CLOSURE_INDEX))); - // Load the function context (which is the incoming, outer context). - __ movq(rax, FieldOperand(rax, JSFunction::kContextOffset)); - for (int i = 1; i < chain_length; i++) { - __ movq(rax, - Operand(rax, Context::SlotOffset(Context::CLOSURE_INDEX))); - __ movq(rax, FieldOperand(rax, JSFunction::kContextOffset)); - } - // The context may be an intermediate context, not a function context. - __ movq(rax, - Operand(rax, Context::SlotOffset(Context::FCONTEXT_INDEX))); - } else { // Slot is in the current function context. - // The context may be an intermediate context, not a function context. - __ movq(rax, - Operand(rsi, Context::SlotOffset(Context::FCONTEXT_INDEX))); + if (FLAG_debug_code) { + switch (slot->type()) { + case Slot::LOCAL: + case Slot::PARAMETER: { + Comment cmnt(masm_, "Stack slot"); + break; } - __ movq(rax, Operand(rax, Context::SlotOffset(slot->index()))); - Move(expr->context(), rax); - break; + case Slot::CONTEXT: { + Comment cmnt(masm_, "Context slot"); + break; + } + case Slot::LOOKUP: + UNIMPLEMENTED(); + break; + default: + UNREACHABLE(); } - - case Slot::LOOKUP: - UNREACHABLE(); - break; } + Move(expr->context(), slot, rax); } else { - // The parameter variable has been rewritten into an explict access to - // the arguments object. + // A variable has been rewritten into an explicit access to + // an object property. Property* property = rewrite->AsProperty(); ASSERT_NOT_NULL(property); - ASSERT_EQ(expr->context(), property->context()); - Visit(property); + + // Currently the only parameter expressions that can occur are + // on the form "slot[literal]". + + // Check that the object is in a slot. + Variable* object = property->obj()->AsVariableProxy()->AsVariable(); + ASSERT_NOT_NULL(object); + Slot* object_slot = object->slot(); + ASSERT_NOT_NULL(object_slot); + + // Load the object. + Move(Expression::kValue, object_slot, rax); + + // Check that the key is a smi. + Literal* key_literal = property->key()->AsLiteral(); + ASSERT_NOT_NULL(key_literal); + ASSERT(key_literal->handle()->IsSmi()); + + // Load the key. + Move(Expression::kValue, key_literal); + + // Do a KEYED property load. + Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + // Notice: We must not have a "test rax, ..." instruction after + // the call. It is treated specially by the LoadIC code. + + // Drop key and object left on the stack by IC, and push the result. + DropAndMove(expr->context(), rax, 2); } } @@ -580,8 +666,9 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ CallRuntime(Runtime::kCloneLiteralBoilerplate, 1); } - // If result_saved == true: the result is saved on top of the stack. - // If result_saved == false: the result is not on the stack, just in rax. + // If result_saved == true: The result is saved on top of the + // stack and in rax. + // If result_saved == false: The result not on the stack, just in rax. bool result_saved = false; for (int i = 0; i < expr->properties()->length(); i++) { @@ -606,6 +693,7 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ call(ic, RelocInfo::CODE_TARGET); // StoreIC leaves the receiver on the stack. + __ movq(rax, Operand(rsp, 0)); // Restore result back into rax. break; } // fall through @@ -781,7 +869,7 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) { // Overwrite the global object on the stack with the result if needed. DropAndMove(expr->context(), rax); - } else { + } else if (var->slot()) { Slot* slot = var->slot(); ASSERT_NOT_NULL(slot); // Variables rewritten as properties not handled. switch (slot->type()) { @@ -873,6 +961,36 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) { UNREACHABLE(); break; } + } else { + Property* property = var->AsProperty(); + ASSERT_NOT_NULL(property); + // A variable has been rewritten into a property on an object. + + // Load object and key onto the stack. + Slot* object_slot = property->obj()->AsSlot(); + ASSERT_NOT_NULL(object_slot); + Move(Expression::kValue, object_slot, rax); + + Literal* key_literal = property->key()->AsLiteral(); + ASSERT_NOT_NULL(key_literal); + Move(Expression::kValue, key_literal); + + // Value to store was pushed before object and key on the stack. + __ movq(rax, Operand(rsp, 2 * kPointerSize)); + + // Arguments to ic is value in rax, object and key on stack. + Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + + if (expr->context() == Expression::kEffect) { + __ addq(rsp, Immediate(3 * kPointerSize)); + } else if (expr->context() == Expression::kValue) { + // Value is still on the stack in rsp[2 * kPointerSize] + __ addq(rsp, Immediate(2 * kPointerSize)); + } else { + __ movq(rax, Operand(rsp, 2 * kPointerSize)); + DropAndMove(expr->context(), rax, 3); + } } } @@ -969,9 +1087,9 @@ void FastCodeGenerator::VisitProperty(Property* expr) { Visit(expr->key()); Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); __ call(ic, RelocInfo::CODE_TARGET); - // By emitting a nop we make sure that we do not have a "test rax,..." - // instruction after the call it is treated specially by the LoadIC code. - __ nop(); + // Notice: We must not have a "test rax, ..." instruction after + // the call. It is treated specially by the LoadIC code. + // Drop key left on the stack by IC. __ addq(rsp, Immediate(kPointerSize)); } @@ -1054,7 +1172,7 @@ void FastCodeGenerator::VisitCall(Call* expr) { SetSourcePosition(prop->position()); Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); __ call(ic, RelocInfo::CODE_TARGET); - // By emitting a nop we make sure that we do not have a "test eax,..." + // By emitting a nop we make sure that we do not have a "test rax,..." // instruction after the call it is treated specially by the LoadIC code. __ nop(); // Drop key left on the stack by IC. @@ -1134,9 +1252,13 @@ void FastCodeGenerator::VisitCallNew(CallNew* expr) { void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Comment cmnt(masm_, "[ CallRuntime"); ZoneList<Expression*>* args = expr->arguments(); - Runtime::Function* function = expr->function(); - ASSERT(function != NULL); + if (expr->is_jsruntime()) { + // Prepare for calling JS runtime function. + __ Push(expr->name()); + __ movq(rax, CodeGenerator::GlobalObject()); + __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset)); + } // Push the arguments ("left-to-right"). int arg_count = args->length(); @@ -1145,8 +1267,19 @@ void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) { ASSERT_EQ(Expression::kValue, args->at(i)->context()); } - __ CallRuntime(function, arg_count); - Move(expr->context(), rax); + if (expr->is_jsruntime()) { + // Call the JS runtime function. + Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, + NOT_IN_LOOP); + __ call(ic, RelocInfo::CODE_TARGET); + // Restore context register. + __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + // Discard the function left on TOS. + DropAndMove(expr->context(), rax); + } else { + __ CallRuntime(expr->function(), arg_count); + Move(expr->context(), rax); + } } void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { diff --git a/deps/v8/src/x64/ic-x64.cc b/deps/v8/src/x64/ic-x64.cc index 1642a0490..ccbc615bd 100644 --- a/deps/v8/src/x64/ic-x64.cc +++ b/deps/v8/src/x64/ic-x64.cc @@ -31,6 +31,7 @@ #include "ic-inl.h" #include "runtime.h" #include "stub-cache.h" +#include "utils.h" namespace v8 { namespace internal { @@ -107,7 +108,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label, StringDictionary::kElementsStartIndex * kPointerSize; for (int i = 0; i < kProbes; i++) { // Compute the masked index: (hash + i + i * i) & mask. - __ movl(r1, FieldOperand(name, String::kLengthOffset)); + __ movl(r1, FieldOperand(name, String::kHashFieldOffset)); __ shrl(r1, Immediate(String::kHashShift)); if (i > 0) { __ addl(r1, Immediate(StringDictionary::GetProbeOffset(i))); @@ -239,18 +240,6 @@ void KeyedLoadIC::Generate(MacroAssembler* masm, } -#ifdef DEBUG -// For use in assert below. -static int TenToThe(int exponent) { - ASSERT(exponent <= 9); - ASSERT(exponent >= 1); - int answer = 10; - for (int i = 1; i < exponent; i++) answer *= 10; - return answer; -} -#endif - - void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rsp[0] : return address @@ -327,7 +316,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, rdx); __ j(above_equal, &slow); // Is the string an array index, with cached numeric value? - __ movl(rbx, FieldOperand(rax, String::kLengthOffset)); + __ movl(rbx, FieldOperand(rax, String::kHashFieldOffset)); __ testl(rbx, Immediate(String::kIsArrayIndexMask)); // If the string is a symbol, do a quick inline probe of the receiver's @@ -342,20 +331,16 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ movq(rax, rcx); __ IncrementCounter(&Counters::keyed_load_generic_symbol, 1); __ ret(0); - // Array index string: If short enough use cache in length/hash field (rbx). - // We assert that there are enough bits in an int32_t after the hash shift - // bits have been subtracted to allow space for the length and the cached - // array index. + // If the hash field contains an array index pick it out. The assert checks + // that the constants for the maximum number of digits for an array index + // cached in the hash field and the number of bits reserved for it does not + // conflict. ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) < - (1 << (String::kShortLengthShift - String::kHashShift))); + (1 << String::kArrayIndexValueBits)); __ bind(&index_string); - const int kLengthFieldLimit = - (String::kMaxCachedArrayIndexLength + 1) << String::kShortLengthShift; - __ cmpl(rbx, Immediate(kLengthFieldLimit)); - __ j(above_equal, &slow); __ movl(rax, rbx); - __ and_(rax, Immediate((1 << String::kShortLengthShift) - 1)); - __ shrl(rax, Immediate(String::kLongLengthShift)); + __ and_(rax, Immediate(String::kArrayIndexHashMask)); + __ shrl(rax, Immediate(String::kHashShift)); __ jmp(&index_int); } @@ -393,7 +378,7 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, // ExternalArray. // rax: index (as a smi) // rcx: JSObject - __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset)); + __ movq(rcx, FieldOperand(rcx, JSObject::kElementsOffset)); __ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset), Heap::RootIndexForExternalArrayType(array_type)); __ j(not_equal, &slow); @@ -413,7 +398,7 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, __ movsxbq(rax, Operand(rcx, rax, times_1, 0)); break; case kExternalUnsignedByteArray: - __ movb(rax, Operand(rcx, rax, times_1, 0)); + __ movzxbq(rax, Operand(rcx, rax, times_1, 0)); break; case kExternalShortArray: __ movsxwq(rax, Operand(rcx, rax, times_2, 0)); diff --git a/deps/v8/src/x64/macro-assembler-x64.cc b/deps/v8/src/x64/macro-assembler-x64.cc index 9dea61671..71157914c 100644 --- a/deps/v8/src/x64/macro-assembler-x64.cc +++ b/deps/v8/src/x64/macro-assembler-x64.cc @@ -67,6 +67,12 @@ void MacroAssembler::CompareRoot(Operand with, Heap::RootListIndex index) { } +void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) { + CompareRoot(rsp, Heap::kStackLimitRootIndex); + j(below, on_stack_overflow); +} + + static void RecordWriteHelper(MacroAssembler* masm, Register object, Register addr, @@ -282,6 +288,9 @@ void MacroAssembler::Abort(const char* msg) { RecordComment(msg); } #endif + // Disable stub call restrictions to always allow calls to abort. + set_allow_stub_calls(true); + push(rax); movq(kScratchRegister, p0, RelocInfo::NONE); push(kScratchRegister); @@ -291,6 +300,7 @@ void MacroAssembler::Abort(const char* msg) { push(kScratchRegister); CallRuntime(Runtime::kAbort, 2); // will not return here + int3(); } @@ -2088,6 +2098,11 @@ void MacroAssembler::LoadAllocationTopHelper(Register result, void MacroAssembler::UpdateAllocationTopHelper(Register result_end, Register scratch) { + if (FLAG_debug_code) { + testq(result_end, Immediate(kObjectAlignmentMask)); + Check(zero, "Unaligned allocation in new space"); + } + ExternalReference new_space_allocation_top = ExternalReference::new_space_allocation_top_address(); @@ -2229,6 +2244,25 @@ void MacroAssembler::AllocateHeapNumber(Register result, } +void MacroAssembler::LoadContext(Register dst, int context_chain_length) { + if (context_chain_length > 0) { + // Move up the chain of contexts to the context containing the slot. + movq(dst, Operand(rsi, Context::SlotOffset(Context::CLOSURE_INDEX))); + // Load the function context (which is the incoming, outer context). + movq(rax, FieldOperand(rax, JSFunction::kContextOffset)); + for (int i = 1; i < context_chain_length; i++) { + movq(dst, Operand(dst, Context::SlotOffset(Context::CLOSURE_INDEX))); + movq(dst, FieldOperand(dst, JSFunction::kContextOffset)); + } + // The context may be an intermediate context, not a function context. + movq(dst, Operand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX))); + } else { // context is the current function context. + // The context may be an intermediate context, not a function context. + movq(dst, Operand(rsi, Context::SlotOffset(Context::FCONTEXT_INDEX))); + } +} + + CodePatcher::CodePatcher(byte* address, int size) : address_(address), size_(size), masm_(address, size + Assembler::kGap) { // Create a new macro assembler pointing to the address of the code to patch. diff --git a/deps/v8/src/x64/macro-assembler-x64.h b/deps/v8/src/x64/macro-assembler-x64.h index 11cdfc3c4..9e7c25c95 100644 --- a/deps/v8/src/x64/macro-assembler-x64.h +++ b/deps/v8/src/x64/macro-assembler-x64.h @@ -98,6 +98,12 @@ class MacroAssembler: public Assembler { #endif // --------------------------------------------------------------------------- + // Stack limit support + + // Do simple test for stack overflow. This doesn't handle an overflow. + void StackLimitCheck(Label* on_stack_limit_hit); + + // --------------------------------------------------------------------------- // Activation frames void EnterInternalFrame() { EnterFrame(StackFrame::INTERNAL); } @@ -542,6 +548,9 @@ class MacroAssembler: public Assembler { // occurred. void IllegalOperation(int num_arguments); + // Find the function context up the context chain. + void LoadContext(Register dst, int context_chain_length); + // --------------------------------------------------------------------------- // Runtime calls diff --git a/deps/v8/src/x64/stub-cache-x64.cc b/deps/v8/src/x64/stub-cache-x64.cc index 584fd2b21..55b0b87cd 100644 --- a/deps/v8/src/x64/stub-cache-x64.cc +++ b/deps/v8/src/x64/stub-cache-x64.cc @@ -173,7 +173,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm, __ JumpIfSmi(receiver, &miss); // Get the map of the receiver and compute the hash. - __ movl(scratch, FieldOperand(name, String::kLengthOffset)); + __ movl(scratch, FieldOperand(name, String::kHashFieldOffset)); // Use only the low 32 bits of the map pointer. __ addl(scratch, FieldOperand(receiver, HeapObject::kMapOffset)); __ xor_(scratch, Immediate(flags)); @@ -183,7 +183,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ProbeTable(masm, flags, kPrimary, name, scratch); // Primary miss: Compute hash for secondary probe. - __ movl(scratch, FieldOperand(name, String::kLengthOffset)); + __ movl(scratch, FieldOperand(name, String::kHashFieldOffset)); __ addl(scratch, FieldOperand(receiver, HeapObject::kMapOffset)); __ xor_(scratch, Immediate(flags)); __ and_(scratch, Immediate((kPrimaryTableSize - 1) << kHeapObjectTagSize)); @@ -323,11 +323,7 @@ void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm, // Load length directly from the string. __ bind(&load_length); - __ and_(scratch, Immediate(kStringSizeMask)); __ movl(rax, FieldOperand(receiver, String::kLengthOffset)); - // rcx is also the receiver. - __ lea(rcx, Operand(scratch, String::kLongLengthShift)); - __ shr_cl(rax); __ Integer32ToSmi(rax, rax); __ ret(0); diff --git a/deps/v8/test/cctest/test-alloc.cc b/deps/v8/test/cctest/test-alloc.cc index 7921d2abe..315a34ed5 100644 --- a/deps/v8/test/cctest/test-alloc.cc +++ b/deps/v8/test/cctest/test-alloc.cc @@ -65,9 +65,9 @@ static Object* AllocateAfterFailures() { // Old data space. OldSpace* old_data_space = Heap::old_data_space(); - static const int kOldDataSpaceFillerSize = SeqAsciiString::SizeFor(0); + static const int kOldDataSpaceFillerSize = ByteArray::SizeFor(0); while (old_data_space->Available() > kOldDataSpaceFillerSize) { - CHECK(!Heap::AllocateRawAsciiString(0, TENURED)->IsFailure()); + CHECK(!Heap::AllocateByteArray(0, TENURED)->IsFailure()); } CHECK(!Heap::AllocateRawAsciiString(100, TENURED)->IsFailure()); diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index 6791685e1..6d6c174fd 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -2670,6 +2670,40 @@ THREADED_TEST(AutoExtensions) { } +static const char* kSyntaxErrorInExtensionSource = + "["; + + +// Test that a syntax error in an extension does not cause a fatal +// error but results in an empty context. +THREADED_TEST(SyntaxErrorExtensions) { + v8::HandleScope handle_scope; + v8::RegisterExtension(new Extension("syntaxerror", + kSyntaxErrorInExtensionSource)); + const char* extension_names[] = { "syntaxerror" }; + v8::ExtensionConfiguration extensions(1, extension_names); + v8::Handle<Context> context = Context::New(&extensions); + CHECK(context.IsEmpty()); +} + + +static const char* kExceptionInExtensionSource = + "throw 42"; + + +// Test that an exception when installing an extension does not cause +// a fatal error but results in an empty context. +THREADED_TEST(ExceptionExtensions) { + v8::HandleScope handle_scope; + v8::RegisterExtension(new Extension("exception", + kExceptionInExtensionSource)); + const char* extension_names[] = { "exception" }; + v8::ExtensionConfiguration extensions(1, extension_names); + v8::Handle<Context> context = Context::New(&extensions); + CHECK(context.IsEmpty()); +} + + static void CheckDependencies(const char* name, const char* expected) { v8::HandleScope handle_scope; v8::ExtensionConfiguration config(1, &name); @@ -7029,27 +7063,17 @@ static void MorphAString(i::String* string, CHECK(i::StringShape(string).IsExternal()); if (string->IsAsciiRepresentation()) { // Check old map is not symbol or long. - CHECK(string->map() == i::Heap::short_external_ascii_string_map() || - string->map() == i::Heap::medium_external_ascii_string_map()); + CHECK(string->map() == i::Heap::external_ascii_string_map()); // Morph external string to be TwoByte string. - if (string->length() <= i::String::kMaxShortSize) { - string->set_map(i::Heap::short_external_string_map()); - } else { - string->set_map(i::Heap::medium_external_string_map()); - } + string->set_map(i::Heap::external_string_map()); i::ExternalTwoByteString* morphed = i::ExternalTwoByteString::cast(string); morphed->set_resource(uc16_resource); } else { // Check old map is not symbol or long. - CHECK(string->map() == i::Heap::short_external_string_map() || - string->map() == i::Heap::medium_external_string_map()); + CHECK(string->map() == i::Heap::external_string_map()); // Morph external string to be ASCII string. - if (string->length() <= i::String::kMaxShortSize) { - string->set_map(i::Heap::short_external_ascii_string_map()); - } else { - string->set_map(i::Heap::medium_external_ascii_string_map()); - } + string->set_map(i::Heap::external_ascii_string_map()); i::ExternalAsciiString* morphed = i::ExternalAsciiString::cast(string); morphed->set_resource(ascii_resource); @@ -8059,6 +8083,85 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, result = CompileRun("ext_array[1] = 23;"); CHECK_EQ(23, result->Int32Value()); + // Test more complex manipulations which cause eax to contain values + // that won't be completely overwritten by loads from the arrays. + // This catches bugs in the instructions used for the KeyedLoadIC + // for byte and word types. + { + const int kXSize = 300; + const int kYSize = 300; + const int kLargeElementCount = kXSize * kYSize * 4; + ElementType* large_array_data = + static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); + i::Handle<ExternalArrayClass> large_array = + i::Handle<ExternalArrayClass>::cast( + i::Factory::NewExternalArray(kLargeElementCount, + array_type, + array_data)); + v8::Handle<v8::Object> large_obj = v8::Object::New(); + // Set the elements to be the external array. + large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, + array_type, + kLargeElementCount); + context->Global()->Set(v8_str("large_array"), large_obj); + // Initialize contents of a few rows. + for (int x = 0; x < 300; x++) { + int row = 0; + int offset = row * 300 * 4; + large_array_data[offset + 4 * x + 0] = (ElementType) 127; + large_array_data[offset + 4 * x + 1] = (ElementType) 0; + large_array_data[offset + 4 * x + 2] = (ElementType) 0; + large_array_data[offset + 4 * x + 3] = (ElementType) 127; + row = 150; + offset = row * 300 * 4; + large_array_data[offset + 4 * x + 0] = (ElementType) 127; + large_array_data[offset + 4 * x + 1] = (ElementType) 0; + large_array_data[offset + 4 * x + 2] = (ElementType) 0; + large_array_data[offset + 4 * x + 3] = (ElementType) 127; + row = 298; + offset = row * 300 * 4; + large_array_data[offset + 4 * x + 0] = (ElementType) 127; + large_array_data[offset + 4 * x + 1] = (ElementType) 0; + large_array_data[offset + 4 * x + 2] = (ElementType) 0; + large_array_data[offset + 4 * x + 3] = (ElementType) 127; + } + // The goal of the code below is to make "offset" large enough + // that the computation of the index (which goes into eax) has + // high bits set which will not be overwritten by a byte or short + // load. + result = CompileRun("var failed = false;" + "var offset = 0;" + "for (var i = 0; i < 300; i++) {" + " if (large_array[4 * i] != 127 ||" + " large_array[4 * i + 1] != 0 ||" + " large_array[4 * i + 2] != 0 ||" + " large_array[4 * i + 3] != 127) {" + " failed = true;" + " }" + "}" + "offset = 150 * 300 * 4;" + "for (var i = 0; i < 300; i++) {" + " if (large_array[offset + 4 * i] != 127 ||" + " large_array[offset + 4 * i + 1] != 0 ||" + " large_array[offset + 4 * i + 2] != 0 ||" + " large_array[offset + 4 * i + 3] != 127) {" + " failed = true;" + " }" + "}" + "offset = 298 * 300 * 4;" + "for (var i = 0; i < 300; i++) {" + " if (large_array[offset + 4 * i] != 127 ||" + " large_array[offset + 4 * i + 1] != 0 ||" + " large_array[offset + 4 * i + 2] != 0 ||" + " large_array[offset + 4 * i + 3] != 127) {" + " failed = true;" + " }" + "}" + "!failed;"); + CHECK_EQ(true, result->BooleanValue()); + free(large_array_data); + } + free(array_data); } @@ -8409,3 +8512,103 @@ THREADED_TEST(SpaghettiStackReThrow) { v8::String::Utf8Value value(try_catch.Exception()); CHECK_EQ(0, strcmp(*value, "Hey!")); } + + +static int GetGlobalObjectsCount() { + int count = 0; + v8::internal::HeapIterator it; + while (it.has_next()) { + v8::internal::HeapObject* object = it.next(); + if (object->IsJSGlobalObject()) count++; + } + return count; +} + + +TEST(Regress528) { + v8::V8::Initialize(); + + v8::HandleScope scope; + v8::Persistent<Context> context; + v8::Persistent<Context> other_context; + int gc_count; + + // Create a context used to keep the code from aging in the compilation + // cache. + other_context = Context::New(); + + // Context-dependent context data creates reference from the compilation + // cache to the global object. + const char* source_simple = "1"; + context = Context::New(); + { + v8::HandleScope scope; + + context->Enter(); + Local<v8::String> obj = v8::String::New(""); + context->SetData(obj); + CompileRun(source_simple); + context->Exit(); + } + context.Dispose(); + for (gc_count = 1; gc_count < 10; gc_count++) { + other_context->Enter(); + CompileRun(source_simple); + other_context->Exit(); + v8::internal::Heap::CollectAllGarbage(false); + if (GetGlobalObjectsCount() == 1) break; + } + CHECK_GE(2, gc_count); + CHECK_EQ(1, GetGlobalObjectsCount()); + + // Eval in a function creates reference from the compilation cache to the + // global object. + const char* source_eval = "function f(){eval('1')}; f()"; + context = Context::New(); + { + v8::HandleScope scope; + + context->Enter(); + CompileRun(source_eval); + context->Exit(); + } + context.Dispose(); + for (gc_count = 1; gc_count < 10; gc_count++) { + other_context->Enter(); + CompileRun(source_eval); + other_context->Exit(); + v8::internal::Heap::CollectAllGarbage(false); + if (GetGlobalObjectsCount() == 1) break; + } + CHECK_GE(2, gc_count); + CHECK_EQ(1, GetGlobalObjectsCount()); + + // Looking up the line number for an exception creates reference from the + // compilation cache to the global object. + const char* source_exception = "function f(){throw 1;} f()"; + context = Context::New(); + { + v8::HandleScope scope; + + context->Enter(); + v8::TryCatch try_catch; + CompileRun(source_exception); + CHECK(try_catch.HasCaught()); + v8::Handle<v8::Message> message = try_catch.Message(); + CHECK(!message.IsEmpty()); + CHECK_EQ(1, message->GetLineNumber()); + context->Exit(); + } + context.Dispose(); + for (gc_count = 1; gc_count < 10; gc_count++) { + other_context->Enter(); + CompileRun(source_exception); + other_context->Exit(); + v8::internal::Heap::CollectAllGarbage(false); + if (GetGlobalObjectsCount() == 1) break; + } + CHECK_GE(2, gc_count); + CHECK_EQ(1, GetGlobalObjectsCount()); + + other_context.Dispose(); +} diff --git a/deps/v8/test/cctest/test-debug.cc b/deps/v8/test/cctest/test-debug.cc index d938174e7..5b7219301 100644 --- a/deps/v8/test/cctest/test-debug.cc +++ b/deps/v8/test/cctest/test-debug.cc @@ -5016,7 +5016,7 @@ TEST(ScriptNameAndData) { v8::ScriptOrigin origin2 = v8::ScriptOrigin(v8::String::New("new name")); v8::Handle<v8::Script> script2 = v8::Script::Compile(script, &origin2); script2->Run(); - script2->SetData(data_obj); + script2->SetData(data_obj->ToString()); f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f"))); f->Call(env->Global(), 0, NULL); CHECK_EQ(3, break_point_hit_count); @@ -5069,8 +5069,8 @@ TEST(ContextData) { CHECK(context_2->GetData()->IsUndefined()); // Set and check different data values. - v8::Handle<v8::Value> data_1 = v8::Number::New(1); - v8::Handle<v8::Value> data_2 = v8::String::New("2"); + v8::Handle<v8::String> data_1 = v8::String::New("1"); + v8::Handle<v8::String> data_2 = v8::String::New("2"); context_1->SetData(data_1); context_2->SetData(data_2); CHECK(context_1->GetData()->StrictEquals(data_1)); @@ -5233,7 +5233,7 @@ static void ExecuteScriptForContextCheck() { CHECK(context_1->GetData()->IsUndefined()); // Set and check a data value. - v8::Handle<v8::Value> data_1 = v8::Number::New(1); + v8::Handle<v8::String> data_1 = v8::String::New("1"); context_1->SetData(data_1); CHECK(context_1->GetData()->StrictEquals(data_1)); diff --git a/deps/v8/test/cctest/test-heap.cc b/deps/v8/test/cctest/test-heap.cc index fb9a48e79..17bee5b06 100644 --- a/deps/v8/test/cctest/test-heap.cc +++ b/deps/v8/test/cctest/test-heap.cc @@ -37,8 +37,7 @@ TEST(HeapMaps) { CheckMap(Heap::meta_map(), MAP_TYPE, Map::kSize); CheckMap(Heap::heap_number_map(), HEAP_NUMBER_TYPE, HeapNumber::kSize); CheckMap(Heap::fixed_array_map(), FIXED_ARRAY_TYPE, FixedArray::kHeaderSize); - CheckMap(Heap::long_string_map(), LONG_STRING_TYPE, - SeqTwoByteString::kAlignedSize); + CheckMap(Heap::string_map(), STRING_TYPE, SeqTwoByteString::kAlignedSize); } diff --git a/deps/v8/test/cctest/test-log.cc b/deps/v8/test/cctest/test-log.cc index de29fe097..85ff331a6 100644 --- a/deps/v8/test/cctest/test-log.cc +++ b/deps/v8/test/cctest/test-log.cc @@ -538,6 +538,81 @@ TEST(LogCallbacks) { } +static v8::Handle<v8::Value> Prop1Getter(v8::Local<v8::String> property, + const v8::AccessorInfo& info) { + return v8::Handle<v8::Value>(); +} + +static void Prop1Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo& info) { +} + +static v8::Handle<v8::Value> Prop2Getter(v8::Local<v8::String> property, + const v8::AccessorInfo& info) { + return v8::Handle<v8::Value>(); +} + +TEST(LogAccessorCallbacks) { + 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<v8::Context> env = v8::Context::New(); + if (need_to_set_up_logger) Logger::Setup(); + env->Enter(); + + // Skip all initially logged stuff. + EmbeddedVector<char, 102400> buffer; + int log_pos = GetLogLines(0, &buffer); + + v8::Persistent<v8::FunctionTemplate> obj = + v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New()); + obj->SetClassName(v8::String::New("Obj")); + v8::Handle<v8::ObjectTemplate> inst = obj->InstanceTemplate(); + inst->SetAccessor(v8::String::New("prop1"), Prop1Getter, Prop1Setter); + inst->SetAccessor(v8::String::New("prop2"), Prop2Getter); + + i::Logger::LogAccessorCallbacks(); + log_pos = GetLogLines(log_pos, &buffer); + CHECK_GT(log_pos, 0); + buffer[log_pos] = 0; + printf("%s", buffer.start()); + + EmbeddedVector<char, 100> prop1_getter_record; + i::OS::SNPrintF(prop1_getter_record, + "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop1\"", + Prop1Getter); + CHECK_NE(NULL, strstr(buffer.start(), prop1_getter_record.start())); + EmbeddedVector<char, 100> prop1_setter_record; + i::OS::SNPrintF(prop1_setter_record, + "code-creation,Callback,0x%" V8PRIxPTR ",1,\"set prop1\"", + Prop1Setter); + CHECK_NE(NULL, strstr(buffer.start(), prop1_setter_record.start())); + EmbeddedVector<char, 100> prop2_getter_record; + i::OS::SNPrintF(prop2_getter_record, + "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop2\"", + Prop2Getter); + CHECK_NE(NULL, strstr(buffer.start(), prop2_getter_record.start())); + + 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-serialize.cc b/deps/v8/test/cctest/test-serialize.cc index 9ed487450..8f4441ac7 100644 --- a/deps/v8/test/cctest/test-serialize.cc +++ b/deps/v8/test/cctest/test-serialize.cc @@ -192,6 +192,15 @@ TEST(Serialize) { } +// Test that heap serialization is non-destructive. +TEST(SerializeTwice) { + Serializer::Enable(); + v8::V8::Initialize(); + Serialize(); + Serialize(); +} + + //---------------------------------------------------------------------------- // Tests that the heap can be deserialized. @@ -218,7 +227,17 @@ DEPENDENT_TEST(Deserialize, Serialize) { Deserialize(); - fflush(stdout); + v8::Persistent<v8::Context> env = v8::Context::New(); + env->Enter(); + + SanityCheck(); +} + + +DEPENDENT_TEST(DeserializeFromSecondSerialization, SerializeTwice) { + v8::HandleScope scope; + + Deserialize(); v8::Persistent<v8::Context> env = v8::Context::New(); env->Enter(); @@ -242,6 +261,22 @@ DEPENDENT_TEST(DeserializeAndRunScript2, Serialize) { } +DEPENDENT_TEST(DeserializeFromSecondSerializationAndRunScript2, + SerializeTwice) { + v8::HandleScope scope; + + Deserialize(); + + v8::Persistent<v8::Context> env = v8::Context::New(); + env->Enter(); + + const char* c_source = "\"1234\".length"; + v8::Local<v8::String> source = v8::String::New(c_source); + v8::Local<v8::Script> script = v8::Script::Compile(source); + CHECK_EQ(4, script->Run()->Int32Value()); +} + + TEST(TestThatAlwaysSucceeds) { } diff --git a/deps/v8/test/cctest/test-strings.cc b/deps/v8/test/cctest/test-strings.cc index 0e9bf7a06..59a40af2a 100644 --- a/deps/v8/test/cctest/test-strings.cc +++ b/deps/v8/test/cctest/test-strings.cc @@ -63,6 +63,21 @@ class Resource: public v8::String::ExternalStringResource, }; +class AsciiResource: public v8::String::ExternalAsciiStringResource, + public ZoneObject { + public: + explicit AsciiResource(Vector<const char> string): data_(string.start()) { + length_ = string.length(); + } + virtual const char* data() const { return data_; } + virtual size_t length() const { return length_; } + + private: + const char* data_; + size_t length_; +}; + + static void InitializeBuildingBlocks( Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]) { // A list of pointers that we don't have any interest in cleaning up. @@ -329,25 +344,89 @@ TEST(Utf8Conversion) { } -class TwoByteResource: public v8::String::ExternalStringResource { - public: - TwoByteResource(const uint16_t* data, size_t length, bool* destructed) - : data_(data), length_(length), destructed_(destructed) { - CHECK_NE(destructed, NULL); - *destructed_ = false; - } +TEST(ExternalShortStringAdd) { + ZoneScope zone(DELETE_ON_EXIT); - virtual ~TwoByteResource() { - CHECK_NE(destructed_, NULL); - CHECK(!*destructed_); - *destructed_ = true; - } + InitializeVM(); + v8::HandleScope handle_scope; - const uint16_t* data() const { return data_; } - size_t length() const { return length_; } + // Make sure we cover all always-flat lengths and at least one above. + static const int kMaxLength = 20; + CHECK_GT(kMaxLength, i::String::kMinNonFlatLength); + + // Allocate two JavaScript arrays for holding short strings. + v8::Handle<v8::Array> ascii_external_strings = + v8::Array::New(kMaxLength + 1); + v8::Handle<v8::Array> non_ascii_external_strings = + v8::Array::New(kMaxLength + 1); + + // Generate short ascii and non-ascii external strings. + for (int i = 0; i <= kMaxLength; i++) { + char* ascii = Zone::NewArray<char>(i + 1); + for (int j = 0; j < i; j++) { + ascii[j] = 'a'; + } + // Terminating '\0' is left out on purpose. It is not required for external + // string data. + AsciiResource* ascii_resource = + new AsciiResource(Vector<const char>(ascii, i)); + v8::Local<v8::String> ascii_external_string = + v8::String::NewExternal(ascii_resource); + + ascii_external_strings->Set(v8::Integer::New(i), ascii_external_string); + uc16* non_ascii = Zone::NewArray<uc16>(i + 1); + for (int j = 0; j < i; j++) { + non_ascii[j] = 0x1234; + } + // Terminating '\0' is left out on purpose. It is not required for external + // string data. + Resource* resource = new Resource(Vector<const uc16>(non_ascii, i)); + v8::Local<v8::String> non_ascii_external_string = + v8::String::NewExternal(resource); + non_ascii_external_strings->Set(v8::Integer::New(i), + non_ascii_external_string); + } - private: - const uint16_t* data_; - size_t length_; - bool* destructed_; -}; + // Add the arrays with the short external strings in the global object. + v8::Handle<v8::Object> global = env->Global(); + global->Set(v8_str("external_ascii"), ascii_external_strings); + global->Set(v8_str("external_non_ascii"), non_ascii_external_strings); + global->Set(v8_str("max_length"), v8::Integer::New(kMaxLength)); + + // Add short external ascii and non-ascii strings checking the result. + static const char* source = + "function test() {" + " var ascii_chars = 'aaaaaaaaaaaaaaaaaaaa';" + " var non_ascii_chars = '\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234\\u1234';" //NOLINT + " if (ascii_chars.length != max_length) return 1;" + " if (non_ascii_chars.length != max_length) return 2;" + " var ascii = Array(max_length + 1);" + " var non_ascii = Array(max_length + 1);" + " for (var i = 0; i <= max_length; i++) {" + " ascii[i] = ascii_chars.substring(0, i);" + " non_ascii[i] = non_ascii_chars.substring(0, i);" + " };" + " for (var i = 0; i <= max_length; i++) {" + " if (ascii[i] != external_ascii[i]) return 3;" + " if (non_ascii[i] != external_non_ascii[i]) return 4;" + " for (var j = 0; j < i; j++) {" + " if (external_ascii[i] !=" + " (external_ascii[j] + external_ascii[i - j])) return 5;" + " if (external_non_ascii[i] !=" + " (external_non_ascii[j] + external_non_ascii[i - j])) return 6;" + " if (non_ascii[i] != (non_ascii[j] + non_ascii[i - j])) return 7;" + " if (ascii[i] != (ascii[j] + ascii[i - j])) return 8;" + " if (ascii[i] != (external_ascii[j] + ascii[i - j])) return 9;" + " if (ascii[i] != (ascii[j] + external_ascii[i - j])) return 10;" + " if (non_ascii[i] !=" + " (external_non_ascii[j] + non_ascii[i - j])) return 11;" + " if (non_ascii[i] !=" + " (non_ascii[j] + external_non_ascii[i - j])) return 12;" + " }" + " }" + " return 0;" + "};" + "test()"; + CHECK_EQ(0, + v8::Script::Compile(v8::String::New(source))->Run()->Int32Value()); +} diff --git a/deps/v8/test/mjsunit/arguments-read-and-assignment.js b/deps/v8/test/mjsunit/arguments-read-and-assignment.js new file mode 100644 index 000000000..c5d34bfa9 --- /dev/null +++ b/deps/v8/test/mjsunit/arguments-read-and-assignment.js @@ -0,0 +1,164 @@ +// 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. +// Testing basic functionality of the arguments object. +// Introduced to ensure that the fast compiler does the right thing. +// The arguments object itself. +assertEquals(42, function(){ return arguments;}(42)[0], + "return arguments value"); +assertEquals(42, function(){ return arguments;}(42)[0], + "arguments in plain value context"); +assertEquals(42, function(){ arguments;return 42}(37), + "arguments in effect context"); +assertEquals(42, function(){ if(arguments)return 42;}(), + "arguments in a boolean context"); +assertEquals(42, function(){ return arguments || true;}(42)[0], + "arguments in a short-circuit boolean context - or"); +assertEquals(true, function(){ return arguments && [true];}(42)[0], + "arguments in a short-circuit boolean context - and"); +assertEquals(42, function(){ arguments = 42; return 42;}(), + "arguments assignment"); +// Properties of the arguments object. +assertEquals(42, function(){ return arguments[0]; }(42), + "args[0] value returned"); +assertEquals(42, function(){ arguments[0]; return 42}(), + "args[0] value ignored"); +assertEquals(42, function(){ if (arguments[0]) return 42; }(37), + "args[0] to boolean"); +assertEquals(42, function(){ return arguments[0] || "no"; }(42), + "args[0] short-circuit boolean or true"); +assertEquals(42, function(){ return arguments[0] || 42; }(0), + "args[0] short-circuit boolean or false"); +assertEquals(37, function(){ return arguments[0] && 37; }(42), + "args[0] short-circuit boolean and true"); +assertEquals(0, function(){ return arguments[0] && 42; }(0), + "args[0] short-circuit boolean and false"); +assertEquals(42, function(){ arguments[0] = 42; return arguments[0]; }(37), + "args[0] assignment"); +// Link between arguments and parameters. +assertEquals(42, function(a) { arguments[0] = 42; return a; }(37), + "assign args[0]->a"); +assertEquals(42, function(a) { a = 42; return arguments[0]; }(37), + "assign a->args[0]"); +assertEquals(54, function(a, b) { arguments[1] = 54; return b; }(42, 37), + "assign args[1]->b:b"); +assertEquals(54, function(a, b) { b = 54; return arguments[1]; }(42, 47), + "assign b->args[1]:b"); +assertEquals(42, function(a, b) { arguments[1] = 54; return a; }(42, 37), + "assign args[1]->b:a"); +assertEquals(42, function(a, b) { b = 54; return arguments[0]; }(42, 47), + "assign b->args[1]:a"); + +// Capture parameters in nested contexts. +assertEquals(33, + function(a,b) { + return a + arguments[0] + + function(b){ return a + b + arguments[0]; }(b); }(7,6), + "captured parameters"); +assertEquals(42, function(a) { + arguments[0] = 42; + return function(b){ return a; }(); + }(37), + "capture value returned"); +assertEquals(42, + function(a) { + arguments[0] = 26; + return function(b){ a; return 42; }(); + }(37), + "capture value ignored"); +assertEquals(42, + function(a) { + arguments[0] = 26; + return function(b){ if (a) return 42; }(); + }(37), + "capture to boolean"); +assertEquals(42, + function(a) { + arguments[0] = 42; + return function(b){ return a || "no"; }(); + }(37), + "capture short-circuit boolean or true"); +assertEquals(0, + function(a) { + arguments[0] = 0; + return function(b){ return a && 42; }(); + }(37), + "capture short-circuit boolean and false"); +// Deeply nested. +assertEquals(42, + function(a,b) { + return arguments[2] + + function(){ + return b + + function() { + return a; + }(); + }(); + }(7,14,21), + "deep nested capture"); + +// Assignment to captured parameters. +assertEquals(42, function(a,b) { + arguments[1] = 11; + return a + function(){ a = b; return a; }() + a; + }(20, 37), "captured assignment"); + +// Inside non-function scopes. +assertEquals(42, + function(a) { + arguments[0] = 20; + with ({ b : 22 }) { return a + b; } + }(37), + "a in with"); +assertEquals(42, + function(a) { + with ({ b : 22 }) { return arguments[0] + b; } + }(20), + "args in with"); +assertEquals(42, + function(a) { + arguments[0] = 20; + with ({ b : 22 }) { + return function() { return a; }() + b; } + }(37), + "captured a in with"); +assertEquals(42, + function(a) { + arguments[0] = 12; + with ({ b : 22 }) { + return function f() { + try { throw 8 } catch(e) { return e + a }; + }() + b; + } + }(37), + "in a catch in a named function captured a in with "); +// Escaping arguments. +function weirdargs(a,b,c) { if (!a) return arguments; + return [b[2],c]; } +var args1 = weirdargs(false, null, 40); +var res = weirdargs(true, args1, 15); +assertEquals(40, res[0], "return old args element"); +assertEquals(15, res[1], "return own args element");
\ No newline at end of file diff --git a/deps/v8/test/mjsunit/compiler/jsnatives.js b/deps/v8/test/mjsunit/compiler/jsnatives.js new file mode 100644 index 000000000..f5d6ac464 --- /dev/null +++ b/deps/v8/test/mjsunit/compiler/jsnatives.js @@ -0,0 +1,33 @@ +// 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. + +// Flags: --allow-natives-syntax + +// Test call of JS runtime functions. + +var a = %GlobalParseInt("21", 16); +assertEquals(33, a); diff --git a/deps/v8/test/mjsunit/compiler/objectliterals.js b/deps/v8/test/mjsunit/compiler/objectliterals.js new file mode 100644 index 000000000..788acb480 --- /dev/null +++ b/deps/v8/test/mjsunit/compiler/objectliterals.js @@ -0,0 +1,57 @@ +// 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. + +// Test object literals with getter, setter and prototype properties. + +var o = { x: 41, get bar() { return {x:42} } }; + +assertEquals(41, o.x); +assertEquals(42, o.bar.x); + +o = { f: function() { return 41 }, + get bar() { return this.x }, + x:0, + set bar(t) { this.x = t }, + g: function() { return 43 } +}; +o.bar = 7; +assertEquals(7, o.bar); +assertEquals(7, o.x); +assertEquals(41, o.f()); +assertEquals(43, o.g()); + +p = {x:42}; +o = {get foo() { return this.x; }, + f: function() { return this.foo + 1 }, + set bar(t) { this.x = t; }, + __proto__: p, +}; +assertEquals(42, o.x); +assertEquals(42, o.foo); +assertEquals(43, o.f()); +o.bar = 44; +assertEquals(44, o.foo); diff --git a/deps/v8/test/mjsunit/regress/regress-526.js b/deps/v8/test/mjsunit/regress/regress-526.js new file mode 100644 index 000000000..0cae97aa2 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-526.js @@ -0,0 +1,32 @@ +// 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. + +// Test object literals with computed property and getter. + +var o = { foo: function() { }, get bar() { return {x:42} } }; + +assertEquals(42, o.bar.x); diff --git a/deps/v8/test/mjsunit/regress/regress-r3391.js b/deps/v8/test/mjsunit/regress/regress-r3391.js new file mode 100644 index 000000000..d55728436 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-r3391.js @@ -0,0 +1,77 @@ +// 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. + +// Check what we do if toLocaleString doesn't return a string when we are +// calling Array.prototype.toLocaleString. The standard is somewhat +// vague on this point. This test is now passed by both V8 and JSC. + +var evil_called = 0; +var evil_locale_called = 0; +var exception_thrown = 0; + +function evil_to_string() { + evil_called++; + return this; +} + +function evil_to_locale_string() { + evil_locale_called++; + return this; +} + +var o = {toString: evil_to_string, toLocaleString: evil_to_locale_string}; + +try { + [o].toLocaleString(); +} catch(e) { + exception_thrown++; +} + +assertEquals(1, evil_called, "evil1"); +assertEquals(1, evil_locale_called, "local1"); +assertEquals(1, exception_thrown, "exception1"); + +try { + [o].toString(); +} catch(e) { + exception_thrown++; +} + +assertEquals(2, evil_called, "evil2"); +assertEquals(1, evil_locale_called, "local2"); +assertEquals(2, exception_thrown, "exception2"); + +try { + [o].join(o); +} catch(e) { + exception_thrown++; +} + +assertEquals(3, evil_called, "evil3"); +assertEquals(1, evil_locale_called, "local3"); +assertEquals(3, exception_thrown, "exception3"); +print("ok"); diff --git a/deps/v8/test/mjsunit/string-add.js b/deps/v8/test/mjsunit/string-add.js index c42cf793b..f226ca18c 100644 --- a/deps/v8/test/mjsunit/string-add.js +++ b/deps/v8/test/mjsunit/string-add.js @@ -173,3 +173,23 @@ assertEquals(0, null + null, "uu"); assertEquals("42strz", reswz, "swwz"); assertEquals(84, resww, "swww"); })(1); + +// Generate ascii and non ascii strings from length 0 to 20. +var ascii = 'aaaaaaaaaaaaaaaaaaaa'; +var non_ascii = '\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234'; +assertEquals(20, ascii.length); +assertEquals(20, non_ascii.length); +var a = Array(21); +var b = Array(21); +for (var i = 0; i <= 20; i++) { + a[i] = ascii.substring(0, i); + b[i] = non_ascii.substring(0, i); +} + +// Add ascii and non-ascii strings generating strings with length from 0 to 20. +for (var i = 0; i <= 20; i++) { + for (var j = 0; j < i; j++) { + assertEquals(a[i], a[j] + a[i - j]) + assertEquals(b[i], b[j] + b[i - j]) + } +} diff --git a/deps/v8/test/mjsunit/typeof.js b/deps/v8/test/mjsunit/typeof.js new file mode 100644 index 000000000..b460fbba9 --- /dev/null +++ b/deps/v8/test/mjsunit/typeof.js @@ -0,0 +1,40 @@ +// Copyright 2008 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: --nofast-compiler + +// The type of a regular expression should be 'function', including in +// the context of string equality comparisons. + +var r = new RegExp; +assertEquals('function', typeof r); +assertTrue(typeof r == 'function'); + +function test(x, y) { return x == y; } +assertFalse(test('object', typeof r)); + +assertFalse(typeof r == 'object'); diff --git a/deps/v8/tools/codemap.js b/deps/v8/tools/codemap.js index 404127f23..af511f642 100644 --- a/deps/v8/tools/codemap.js +++ b/deps/v8/tools/codemap.js @@ -244,7 +244,7 @@ devtools.profiler.CodeMap.CodeEntry.prototype.toString = function() { devtools.profiler.CodeMap.NameGenerator = function() { - this.knownNames_ = []; + this.knownNames_ = {}; }; diff --git a/deps/v8/tools/gyp/v8.gyp b/deps/v8/tools/gyp/v8.gyp index d7ee73f6e..ba7224b4a 100644 --- a/deps/v8/tools/gyp/v8.gyp +++ b/deps/v8/tools/gyp/v8.gyp @@ -506,6 +506,17 @@ ], } ], + ['OS=="openbsd"', { + 'link_settings': { + 'libraries': [ + '-L/usr/local/lib -lexecinfo', + ]}, + 'sources': [ + '../../src/platform-openbsd.cc', + '../../src/platform-posix.cc' + ], + } + ], ['OS=="mac"', { 'sources': [ '../../src/platform-macos.cc', diff --git a/deps/v8/tools/presubmit.py b/deps/v8/tools/presubmit.py index 5a99c2add..3f27c001a 100755 --- a/deps/v8/tools/presubmit.py +++ b/deps/v8/tools/presubmit.py @@ -28,9 +28,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import md5 import optparse import os from os.path import abspath, join, dirname, basename, exists +import pickle import re import sys import subprocess @@ -93,6 +95,50 @@ whitespace/todo """.split() +class FileContentsCache(object): + + def __init__(self, sums_file_name): + self.sums = {} + self.sums_file_name = sums_file_name + + def Load(self): + try: + sums_file = None + try: + sums_file = open(self.sums_file_name, 'r') + self.sums = pickle.load(sums_file) + except IOError: + # File might not exist, this is OK. + pass + finally: + if sums_file: + sums_file.close() + + def Save(self): + try: + sums_file = open(self.sums_file_name, 'w') + pickle.dump(self.sums, sums_file) + finally: + sums_file.close() + + def FilterUnchangedFiles(self, files): + changed_or_new = [] + for file in files: + try: + handle = open(file, "r") + file_sum = md5.new(handle.read()).digest() + if not file in self.sums or self.sums[file] != file_sum: + changed_or_new.append(file) + self.sums[file] = file_sum + finally: + handle.close() + return changed_or_new + + def RemoveFile(self, file): + if file in self.sums: + self.sums.pop(file) + + class SourceFileProcessor(object): """ Utility class that can run through a directory structure, find all relevant @@ -137,7 +183,7 @@ class CppLintProcessor(SourceFileProcessor): or (name == 'third_party')) IGNORE_LINT = ['flag-definitions.h'] - + def IgnoreFile(self, name): return (super(CppLintProcessor, self).IgnoreFile(name) or (name in CppLintProcessor.IGNORE_LINT)) @@ -146,13 +192,32 @@ class CppLintProcessor(SourceFileProcessor): return ['src', 'public', 'samples', join('test', 'cctest')] def ProcessFiles(self, files, path): + good_files_cache = FileContentsCache('.cpplint-cache') + good_files_cache.Load() + files = good_files_cache.FilterUnchangedFiles(files) + if len(files) == 0: + print 'No changes in files detected. Skipping cpplint check.' + return True + filt = '-,' + ",".join(['+' + n for n in ENABLED_LINT_RULES]) command = ['cpplint.py', '--filter', filt] + join(files) local_cpplint = join(path, "tools", "cpplint.py") if exists(local_cpplint): command = ['python', local_cpplint, '--filter', filt] + join(files) - process = subprocess.Popen(command) - return process.wait() == 0 + + process = subprocess.Popen(command, stderr=subprocess.PIPE) + LINT_ERROR_PATTERN = re.compile(r'^(.+)[:(]\d+[:)]') + while True: + out_line = process.stderr.readline() + if out_line == '' and process.poll() != None: + break + sys.stderr.write(out_line) + m = LINT_ERROR_PATTERN.match(out_line) + if m: + good_files_cache.RemoveFile(m.group(1)) + + good_files_cache.Save() + return process.returncode == 0 COPYRIGHT_HEADER_PATTERN = re.compile( diff --git a/deps/v8/tools/utils.py b/deps/v8/tools/utils.py index 78d1e0d62..196bb0555 100644 --- a/deps/v8/tools/utils.py +++ b/deps/v8/tools/utils.py @@ -55,6 +55,8 @@ def GuessOS(): return 'win32' elif id == 'FreeBSD': return 'freebsd' + elif id == 'OpenBSD': + return 'openbsd' else: return None diff --git a/deps/v8/tools/v8.xcodeproj/project.pbxproj b/deps/v8/tools/v8.xcodeproj/project.pbxproj index d2af6262b..3ffd18299 100644 --- a/deps/v8/tools/v8.xcodeproj/project.pbxproj +++ b/deps/v8/tools/v8.xcodeproj/project.pbxproj @@ -214,6 +214,10 @@ 9F4B7B8A0FCC877A00DC4117 /* log-utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F4B7B870FCC877A00DC4117 /* log-utils.cc */; }; 9F92FAA90F8F28AD0089F02C /* func-name-inferrer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */; }; 9F92FAAA0F8F28AD0089F02C /* func-name-inferrer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */; }; + 9FBE03DE10BD409900F8BFBA /* fast-codegen.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FBE03DC10BD409900F8BFBA /* fast-codegen.cc */; }; + 9FBE03DF10BD409900F8BFBA /* fast-codegen.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FBE03DC10BD409900F8BFBA /* fast-codegen.cc */; }; + 9FBE03E210BD40EA00F8BFBA /* fast-codegen-ia32.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FBE03E110BD40EA00F8BFBA /* fast-codegen-ia32.cc */; }; + 9FBE03E510BD412600F8BFBA /* fast-codegen-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FBE03E410BD412600F8BFBA /* fast-codegen-arm.cc */; }; 9FC86ABD0F5FEDAC00F22668 /* oprofile-agent.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */; }; 9FC86ABE0F5FEDAC00F22668 /* oprofile-agent.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */; }; /* End PBXBuildFile section */ @@ -550,6 +554,10 @@ 9F4B7B880FCC877A00DC4117 /* log-utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "log-utils.h"; sourceTree = "<group>"; }; 9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "func-name-inferrer.cc"; sourceTree = "<group>"; }; 9F92FAA80F8F28AD0089F02C /* func-name-inferrer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "func-name-inferrer.h"; sourceTree = "<group>"; }; + 9FBE03DC10BD409900F8BFBA /* fast-codegen.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "fast-codegen.cc"; sourceTree = "<group>"; }; + 9FBE03DD10BD409900F8BFBA /* fast-codegen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "fast-codegen.h"; sourceTree = "<group>"; }; + 9FBE03E110BD40EA00F8BFBA /* fast-codegen-ia32.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "fast-codegen-ia32.cc"; path = "ia32/fast-codegen-ia32.cc"; sourceTree = "<group>"; }; + 9FBE03E410BD412600F8BFBA /* fast-codegen-arm.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "fast-codegen-arm.cc"; path = "arm/fast-codegen-arm.cc"; sourceTree = "<group>"; }; 9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "oprofile-agent.cc"; sourceTree = "<group>"; }; 9FC86ABC0F5FEDAC00F22668 /* oprofile-agent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "oprofile-agent.h"; sourceTree = "<group>"; }; /* End PBXFileReference section */ @@ -715,6 +723,10 @@ 897FF1310E719B8F00D62E90 /* execution.h */, 897FF1320E719B8F00D62E90 /* factory.cc */, 897FF1330E719B8F00D62E90 /* factory.h */, + 9FBE03DC10BD409900F8BFBA /* fast-codegen.cc */, + 9FBE03DD10BD409900F8BFBA /* fast-codegen.h */, + 9FBE03E410BD412600F8BFBA /* fast-codegen-arm.cc */, + 9FBE03E110BD40EA00F8BFBA /* fast-codegen-ia32.cc */, 89471C7F0EB23EE400B6874B /* flag-definitions.h */, 897FF1350E719B8F00D62E90 /* flags.cc */, 897FF1360E719B8F00D62E90 /* flags.h */, @@ -1225,6 +1237,8 @@ 9F4B7B890FCC877A00DC4117 /* log-utils.cc in Sources */, 8981F6001010501900D1520E /* frame-element.cc in Sources */, 9F11D9A0105AF0A300EBE5B2 /* heap-profiler.cc in Sources */, + 9FBE03DE10BD409900F8BFBA /* fast-codegen.cc in Sources */, + 9FBE03E210BD40EA00F8BFBA /* fast-codegen-ia32.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1332,6 +1346,8 @@ 9F4B7B8A0FCC877A00DC4117 /* log-utils.cc in Sources */, 8981F6011010502800D1520E /* frame-element.cc in Sources */, 9F11D9A1105AF0A300EBE5B2 /* heap-profiler.cc in Sources */, + 9FBE03DF10BD409900F8BFBA /* fast-codegen.cc in Sources */, + 9FBE03E510BD412600F8BFBA /* fast-codegen-arm.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; |