Version 3.26.22 (based on bleeding_edge revision r20902)
Disable field type tracking by default (Chromium issue 365172).
Performance and stability improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@20903 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index e32ecc7..ce43b07 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2014-04-23: Version 3.26.22
+
+ Disable field type tracking by default (Chromium issue 365172).
+
+ Performance and stability improvements on all platforms.
+
+
2014-04-23: Version 3.26.21
Context-allocate all parameters in generators (issue 3280).
diff --git a/src/factory.cc b/src/factory.cc
index 389789f..3aacc59 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -1851,6 +1851,7 @@
share->set_debug_info(*undefined_value(), SKIP_WRITE_BARRIER);
share->set_inferred_name(*empty_string(), SKIP_WRITE_BARRIER);
share->set_initial_map(*undefined_value(), SKIP_WRITE_BARRIER);
+ share->set_profiler_ticks(0);
share->set_ast_node_count(0);
share->set_counters(0);
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 989e285..429ecbd 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -233,7 +233,7 @@
DEFINE_implication(track_double_fields, track_fields)
DEFINE_implication(track_heap_object_fields, track_fields)
DEFINE_implication(track_computed_fields, track_fields)
-DEFINE_bool(track_field_types, true, "track field types")
+DEFINE_bool(track_field_types, false, "track field types")
DEFINE_implication(track_field_types, track_fields)
DEFINE_implication(track_field_types, track_heap_object_fields)
DEFINE_bool(smi_binop, true, "support smi representation in binary operations")
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index 7ac1d5e..843be65 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -6962,13 +6962,36 @@
}
+class FunctionSorter {
+ public:
+ FunctionSorter(int index = 0, int ticks = 0, int size = 0)
+ : index_(index), ticks_(ticks), size_(size) { }
+
+ int index() const { return index_; }
+ int ticks() const { return ticks_; }
+ int size() const { return size_; }
+
+ private:
+ int index_;
+ int ticks_;
+ int size_;
+};
+
+
+inline bool operator<(const FunctionSorter& lhs, const FunctionSorter& rhs) {
+ int diff = lhs.ticks() - rhs.ticks();
+ if (diff != 0) return diff > 0;
+ return lhs.size() < rhs.size();
+}
+
+
void HOptimizedGraphBuilder::HandlePolymorphicCallNamed(
Call* expr,
HValue* receiver,
SmallMapList* types,
Handle<String> name) {
int argument_count = expr->arguments()->length() + 1; // Includes receiver.
- int order[kMaxCallPolymorphism];
+ FunctionSorter order[kMaxCallPolymorphism];
bool handle_smi = false;
bool handled_string = false;
@@ -6990,17 +7013,20 @@
handle_smi = true;
}
expr->set_target(target);
- order[ordered_functions++] = i;
+ order[ordered_functions++] = FunctionSorter(
+ i, target->shared()->profiler_ticks(), InliningAstSize(target));
}
}
+ std::sort(order, order + ordered_functions);
+
HBasicBlock* number_block = NULL;
HBasicBlock* join = NULL;
handled_string = false;
int count = 0;
for (int fn = 0; fn < ordered_functions; ++fn) {
- int i = order[fn];
+ int i = order[fn].index();
PropertyAccessInfo info(this, LOAD, ToType(types->at(i)), name);
if (info.type()->Is(Type::String())) {
if (handled_string) continue;
diff --git a/src/json-stringifier.h b/src/json-stringifier.h
index 872e301..947a396 100644
--- a/src/json-stringifier.h
+++ b/src/json-stringifier.h
@@ -142,7 +142,7 @@
INLINE(Result SerializeJSArray(Handle<JSArray> object));
INLINE(Result SerializeJSObject(Handle<JSObject> object));
- Result SerializeJSArraySlow(Handle<JSArray> object, int length);
+ Result SerializeJSArraySlow(Handle<JSArray> object, uint32_t length);
void SerializeString(Handle<String> object);
@@ -569,13 +569,14 @@
HandleScope handle_scope(isolate_);
Result stack_push = StackPush(object);
if (stack_push != SUCCESS) return stack_push;
- int length = Smi::cast(object->length())->value();
+ uint32_t length = 0;
+ CHECK(object->length()->ToArrayIndex(&length));
Append('[');
switch (object->GetElementsKind()) {
case FAST_SMI_ELEMENTS: {
Handle<FixedArray> elements(
FixedArray::cast(object->elements()), isolate_);
- for (int i = 0; i < length; i++) {
+ for (uint32_t i = 0; i < length; i++) {
if (i > 0) Append(',');
SerializeSmi(Smi::cast(elements->get(i)));
}
@@ -584,7 +585,7 @@
case FAST_DOUBLE_ELEMENTS: {
Handle<FixedDoubleArray> elements(
FixedDoubleArray::cast(object->elements()), isolate_);
- for (int i = 0; i < length; i++) {
+ for (uint32_t i = 0; i < length; i++) {
if (i > 0) Append(',');
SerializeDouble(elements->get_scalar(i));
}
@@ -593,7 +594,7 @@
case FAST_ELEMENTS: {
Handle<FixedArray> elements(
FixedArray::cast(object->elements()), isolate_);
- for (int i = 0; i < length; i++) {
+ for (uint32_t i = 0; i < length; i++) {
if (i > 0) Append(',');
Result result =
SerializeElement(isolate_,
@@ -625,8 +626,8 @@
BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArraySlow(
- Handle<JSArray> object, int length) {
- for (int i = 0; i < length; i++) {
+ Handle<JSArray> object, uint32_t length) {
+ for (uint32_t i = 0; i < length; i++) {
if (i > 0) Append(',');
Handle<Object> element;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
diff --git a/src/mips/code-stubs-mips.cc b/src/mips/code-stubs-mips.cc
index 57f75bb..1d94e70 100644
--- a/src/mips/code-stubs-mips.cc
+++ b/src/mips/code-stubs-mips.cc
@@ -1610,35 +1610,35 @@
}
-void CEntryStub::GenerateCore(MacroAssembler* masm,
- Label* throw_normal_exception,
- Label* throw_termination_exception,
- bool do_gc,
- bool always_allocate) {
- // v0: result parameter for PerformGC, if any
- // s0: number of arguments including receiver (C callee-saved)
- // s1: pointer to the first argument (C callee-saved)
- // s2: pointer to builtin function (C callee-saved)
+void CEntryStub::Generate(MacroAssembler* masm) {
+ // Called from JavaScript; parameters are on stack as if calling JS function
+ // s0: number of arguments including receiver
+ // s1: size of arguments excluding receiver
+ // s2: pointer to builtin function
+ // fp: frame pointer (restored after C call)
+ // sp: stack pointer (restored as callee's sp after C call)
+ // cp: current context (C callee-saved)
+
+ ProfileEntryHookStub::MaybeCallEntryHook(masm);
+
+ // NOTE: s0-s2 hold the arguments of this function instead of a0-a2.
+ // The reason for this is that these arguments would need to be saved anyway
+ // so it's faster to set them up directly.
+ // See MacroAssembler::PrepareCEntryArgs and PrepareCEntryFunction.
+
+ // Compute the argv pointer in a callee-saved register.
+ __ Addu(s1, sp, s1);
+
+ // Enter the exit frame that transitions from JavaScript to C++.
+ FrameScope scope(masm, StackFrame::MANUAL);
+ __ EnterExitFrame(save_doubles_);
+
+ // s0: number of arguments including receiver (C callee-saved)
+ // s1: pointer to first argument (C callee-saved)
+ // s2: pointer to builtin function (C callee-saved)
Isolate* isolate = masm->isolate();
- if (do_gc) {
- // Move result passed in v0 into a0 to call PerformGC.
- __ mov(a0, v0);
- __ PrepareCallCFunction(2, 0, a1);
- __ li(a1, Operand(ExternalReference::isolate_address(masm->isolate())));
- __ CallCFunction(ExternalReference::perform_gc_function(isolate), 2, 0);
- }
-
- ExternalReference scope_depth =
- ExternalReference::heap_always_allocate_scope_depth(isolate);
- if (always_allocate) {
- __ li(a0, Operand(scope_depth));
- __ lw(a1, MemOperand(a0));
- __ Addu(a1, a1, Operand(1));
- __ sw(a1, MemOperand(a0));
- }
-
// Prepare arguments for C routine.
// a0 = argc
__ mov(a0, s0);
@@ -1684,130 +1684,67 @@
masm->InstructionsGeneratedSince(&find_ra));
}
- if (always_allocate) {
- // It's okay to clobber a2 and a3 here. v0 & v1 contain result.
- __ li(a2, Operand(scope_depth));
- __ lw(a3, MemOperand(a2));
- __ Subu(a3, a3, Operand(1));
- __ sw(a3, MemOperand(a2));
+
+ // Runtime functions should not return 'the hole'. Allowing it to escape may
+ // lead to crashes in the IC code later.
+ if (FLAG_debug_code) {
+ Label okay;
+ __ LoadRoot(t0, Heap::kTheHoleValueRootIndex);
+ __ Branch(&okay, ne, v0, Operand(t0));
+ __ stop("The hole escaped");
+ __ bind(&okay);
}
- // Check for failure result.
- Label failure_returned;
- STATIC_ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
- __ addiu(a2, v0, 1);
- __ andi(t0, a2, kFailureTagMask);
- __ Branch(USE_DELAY_SLOT, &failure_returned, eq, t0, Operand(zero_reg));
- // Restore stack (remove arg slots) in branch delay slot.
- __ addiu(sp, sp, kCArgsSlotsSize);
+ // Check result for exception sentinel.
+ Label exception_returned;
+ __ LoadRoot(t0, Heap::kExceptionRootIndex);
+ __ Branch(&exception_returned, eq, t0, Operand(v0));
+ ExternalReference pending_exception_address(
+ Isolate::kPendingExceptionAddress, isolate);
+
+ // Check that there is no pending exception, otherwise we
+ // should have returned the exception sentinel.
+ if (FLAG_debug_code) {
+ Label okay;
+ __ li(a2, Operand(pending_exception_address));
+ __ lw(a2, MemOperand(a2));
+ __ LoadRoot(t0, Heap::kTheHoleValueRootIndex);
+ // Cannot use check here as it attempts to generate call into runtime.
+ __ Branch(&okay, eq, t0, Operand(a2));
+ __ stop("Unexpected pending exception");
+ __ bind(&okay);
+ }
// Exit C frame and return.
// v0:v1: result
// sp: stack pointer
// fp: frame pointer
+ // s0: still holds argc (callee-saved).
__ LeaveExitFrame(save_doubles_, s0, true, EMIT_RETURN);
- // Check if we should retry or throw exception.
- Label retry;
- __ bind(&failure_returned);
- STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
- __ andi(t0, v0, ((1 << kFailureTypeTagSize) - 1) << kFailureTagSize);
- __ Branch(&retry, eq, t0, Operand(zero_reg));
+ // Handling of exception.
+ __ bind(&exception_returned);
// Retrieve the pending exception.
- __ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
- isolate)));
- __ lw(v0, MemOperand(t0));
+ __ li(a2, Operand(pending_exception_address));
+ __ lw(v0, MemOperand(a2));
// Clear the pending exception.
__ li(a3, Operand(isolate->factory()->the_hole_value()));
- __ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
- isolate)));
- __ sw(a3, MemOperand(t0));
+ __ sw(a3, MemOperand(a2));
// Special handling of termination exceptions which are uncatchable
// by javascript code.
+ Label throw_termination_exception;
__ LoadRoot(t0, Heap::kTerminationExceptionRootIndex);
- __ Branch(throw_termination_exception, eq, v0, Operand(t0));
+ __ Branch(&throw_termination_exception, eq, v0, Operand(t0));
// Handle normal exception.
- __ jmp(throw_normal_exception);
-
- __ bind(&retry);
- // Last failure (v0) will be moved to (a0) for parameter when retrying.
-}
-
-
-void CEntryStub::Generate(MacroAssembler* masm) {
- // Called from JavaScript; parameters are on stack as if calling JS function
- // s0: number of arguments including receiver
- // s1: size of arguments excluding receiver
- // s2: pointer to builtin function
- // fp: frame pointer (restored after C call)
- // sp: stack pointer (restored as callee's sp after C call)
- // cp: current context (C callee-saved)
-
- ProfileEntryHookStub::MaybeCallEntryHook(masm);
-
- // NOTE: Invocations of builtins may return failure objects
- // instead of a proper result. The builtin entry handles
- // this by performing a garbage collection and retrying the
- // builtin once.
-
- // NOTE: s0-s2 hold the arguments of this function instead of a0-a2.
- // The reason for this is that these arguments would need to be saved anyway
- // so it's faster to set them up directly.
- // See MacroAssembler::PrepareCEntryArgs and PrepareCEntryFunction.
-
- // Compute the argv pointer in a callee-saved register.
- __ Addu(s1, sp, s1);
-
- // Enter the exit frame that transitions from JavaScript to C++.
- FrameScope scope(masm, StackFrame::MANUAL);
- __ EnterExitFrame(save_doubles_);
-
- // s0: number of arguments (C callee-saved)
- // s1: pointer to first argument (C callee-saved)
- // s2: pointer to builtin function (C callee-saved)
-
- Label throw_normal_exception;
- Label throw_termination_exception;
-
- // Call into the runtime system.
- GenerateCore(masm,
- &throw_normal_exception,
- &throw_termination_exception,
- false,
- false);
-
- // Do space-specific GC and retry runtime call.
- GenerateCore(masm,
- &throw_normal_exception,
- &throw_termination_exception,
- true,
- false);
-
- // Do full GC and retry runtime call one final time.
- Failure* failure = Failure::InternalError();
- __ li(v0, Operand(reinterpret_cast<int32_t>(failure)));
- GenerateCore(masm,
- &throw_normal_exception,
- &throw_termination_exception,
- true,
- true);
-
- { FrameScope scope(masm, StackFrame::MANUAL);
- __ PrepareCallCFunction(0, v0);
- __ CallCFunction(
- ExternalReference::out_of_memory_function(masm->isolate()), 0);
- }
+ __ Throw(v0);
__ bind(&throw_termination_exception);
__ ThrowUncatchable(v0);
-
- __ bind(&throw_normal_exception);
- __ Throw(v0);
}
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 83afd32..68bd159 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -5199,7 +5199,6 @@
ACCESSORS(SharedFunctionInfo, script, Object, kScriptOffset)
ACCESSORS(SharedFunctionInfo, debug_info, Object, kDebugInfoOffset)
ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset)
-SMI_ACCESSORS(SharedFunctionInfo, ast_node_count, kAstNodeCountOffset)
SMI_ACCESSORS(FunctionTemplateInfo, length, kLengthOffset)
@@ -5254,6 +5253,8 @@
SMI_ACCESSORS(SharedFunctionInfo, opt_count_and_bailout_reason,
kOptCountAndBailoutReasonOffset)
SMI_ACCESSORS(SharedFunctionInfo, counters, kCountersOffset)
+SMI_ACCESSORS(SharedFunctionInfo, ast_node_count, kAstNodeCountOffset)
+SMI_ACCESSORS(SharedFunctionInfo, profiler_ticks, kProfilerTicksOffset)
#else
@@ -5304,9 +5305,15 @@
PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo,
opt_count_and_bailout_reason,
kOptCountAndBailoutReasonOffset)
-
PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, counters, kCountersOffset)
+PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo,
+ ast_node_count,
+ kAstNodeCountOffset)
+PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo,
+ profiler_ticks,
+ kProfilerTicksOffset)
+
#endif
@@ -5350,12 +5357,6 @@
}
-int SharedFunctionInfo::profiler_ticks() {
- if (code()->kind() != Code::FUNCTION) return 0;
- return code()->profiler_ticks();
-}
-
-
StrictMode SharedFunctionInfo::strict_mode() {
return BooleanBit::get(compiler_hints(), kStrictModeFunction)
? STRICT : SLOPPY;
diff --git a/src/objects.cc b/src/objects.cc
index 48f9625..6a79315 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -63,8 +63,8 @@
Handle<HeapType> Object::OptimalType(Isolate* isolate,
Representation representation) {
+ if (representation.IsNone()) return HeapType::None(isolate);
if (FLAG_track_field_types) {
- if (representation.IsNone()) return HeapType::None(isolate);
if (representation.IsHeapObject() && IsHeapObject()) {
// We can track only JavaScript objects with stable maps.
Handle<Map> map(HeapObject::cast(this)->map(), isolate);
@@ -5816,7 +5816,7 @@
if (transition_index != TransitionArray::kNotFound) {
new_map = handle(old_map->GetTransition(transition_index), isolate);
ASSERT(new_map->is_observed());
- } else if (old_map->CanHaveMoreTransitions()) {
+ } else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) {
new_map = Map::CopyForObserved(old_map);
} else {
new_map = Map::Copy(old_map);
diff --git a/src/objects.h b/src/objects.h
index a0b4982..71eeaa2 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -4663,6 +4663,10 @@
// Return the initialization flag of the given context local.
InitializationFlag ContextLocalInitFlag(int var);
+ // Return true if this local was introduced by the compiler, and should not be
+ // exposed to the user in a debugger.
+ bool LocalIsSynthetic(int var);
+
// Lookup support for serialized scope info. Returns the
// the stack slot index for a given slot name if the slot is
// present; otherwise returns a value < 0. The name must be an internalized
@@ -7240,6 +7244,7 @@
inline void set_ast_node_count(int count);
inline int profiler_ticks();
+ inline void set_profiler_ticks(int ticks);
// Inline cache age is used to infer whether the function survived a context
// disposal or not. In the former case we reset the opt_count.
@@ -7413,14 +7418,10 @@
static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize;
static const int kInitialMapOffset =
kInferredNameOffset + kPointerSize;
- // ast_node_count is a Smi field. It could be grouped with another Smi field
- // into a PSEUDO_SMI_ACCESSORS pair (on x64), if one becomes available.
- static const int kAstNodeCountOffset =
- kInitialMapOffset + kPointerSize;
#if V8_HOST_ARCH_32_BIT
// Smi fields.
static const int kLengthOffset =
- kAstNodeCountOffset + kPointerSize;
+ kInitialMapOffset + kPointerSize;
static const int kFormalParameterCountOffset = kLengthOffset + kPointerSize;
static const int kExpectedNofPropertiesOffset =
kFormalParameterCountOffset + kPointerSize;
@@ -7438,9 +7439,13 @@
kCompilerHintsOffset + kPointerSize;
static const int kCountersOffset =
kOptCountAndBailoutReasonOffset + kPointerSize;
+ static const int kAstNodeCountOffset =
+ kCountersOffset + kPointerSize;
+ static const int kProfilerTicksOffset =
+ kAstNodeCountOffset + kPointerSize;
// Total size.
- static const int kSize = kCountersOffset + kPointerSize;
+ static const int kSize = kProfilerTicksOffset + kPointerSize;
#else
// The only reason to use smi fields instead of int fields
// is to allow iteration without maps decoding during
@@ -7452,7 +7457,7 @@
// word is not set and thus this word cannot be treated as pointer
// to HeapObject during old space traversal.
static const int kLengthOffset =
- kAstNodeCountOffset + kPointerSize;
+ kInitialMapOffset + kPointerSize;
static const int kFormalParameterCountOffset =
kLengthOffset + kIntSize;
@@ -7473,12 +7478,16 @@
static const int kOptCountAndBailoutReasonOffset =
kCompilerHintsOffset + kIntSize;
-
static const int kCountersOffset =
kOptCountAndBailoutReasonOffset + kIntSize;
+ static const int kAstNodeCountOffset =
+ kCountersOffset + kIntSize;
+ static const int kProfilerTicksOffset =
+ kAstNodeCountOffset + kIntSize;
+
// Total size.
- static const int kSize = kCountersOffset + kIntSize;
+ static const int kSize = kProfilerTicksOffset + kIntSize;
#endif
diff --git a/src/runtime-profiler.cc b/src/runtime-profiler.cc
index 5784e4d..0c9b9e2 100644
--- a/src/runtime-profiler.cc
+++ b/src/runtime-profiler.cc
@@ -185,6 +185,16 @@
SharedFunctionInfo* shared = function->shared();
Code* shared_code = shared->code();
+ List<JSFunction*> functions(4);
+ frame->GetFunctions(&functions);
+ for (int i = functions.length(); --i >= 0; ) {
+ SharedFunctionInfo* shared_function_info = functions[i]->shared();
+ int ticks = shared_function_info->profiler_ticks();
+ if (ticks < Smi::kMaxValue) {
+ shared_function_info->set_profiler_ticks(ticks + 1);
+ }
+ }
+
if (shared_code->kind() != Code::FUNCTION) continue;
if (function->IsInOptimizationQueue()) continue;
diff --git a/src/runtime.cc b/src/runtime.cc
index 1fa6da8..c508b4a 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -11200,33 +11200,44 @@
ASSERT(*scope_info != ScopeInfo::Empty(isolate));
// Get the locals names and values into a temporary array.
- //
- // TODO(1240907): Hide compiler-introduced stack variables
- // (e.g. .result)? For users of the debugger, they will probably be
- // confusing.
+ int local_count = scope_info->LocalCount();
+ for (int slot = 0; slot < scope_info->LocalCount(); ++slot) {
+ // Hide compiler-introduced temporary variables, whether on the stack or on
+ // the context.
+ if (scope_info->LocalIsSynthetic(slot))
+ local_count--;
+ }
+
Handle<FixedArray> locals =
- isolate->factory()->NewFixedArray(scope_info->LocalCount() * 2);
+ isolate->factory()->NewFixedArray(local_count * 2);
// Fill in the values of the locals.
+ int local = 0;
int i = 0;
for (; i < scope_info->StackLocalCount(); ++i) {
// Use the value from the stack.
- locals->set(i * 2, scope_info->LocalName(i));
- locals->set(i * 2 + 1, frame_inspector.GetExpression(i));
+ if (scope_info->LocalIsSynthetic(i))
+ continue;
+ locals->set(local * 2, scope_info->LocalName(i));
+ locals->set(local * 2 + 1, frame_inspector.GetExpression(i));
+ local++;
}
- if (i < scope_info->LocalCount()) {
+ if (local < local_count) {
// Get the context containing declarations.
Handle<Context> context(
Context::cast(it.frame()->context())->declaration_context());
for (; i < scope_info->LocalCount(); ++i) {
+ if (scope_info->LocalIsSynthetic(i))
+ continue;
Handle<String> name(scope_info->LocalName(i));
VariableMode mode;
InitializationFlag init_flag;
- locals->set(i * 2, *name);
+ locals->set(local * 2, *name);
int context_slot_index =
scope_info->ContextSlotIndex(*name, &mode, &init_flag);
Object* value = context->get(context_slot_index);
- locals->set(i * 2 + 1, value);
+ locals->set(local * 2 + 1, value);
+ local++;
}
}
@@ -11287,7 +11298,7 @@
// Calculate the size of the result.
int details_size = kFrameDetailsFirstDynamicIndex +
- 2 * (argument_count + scope_info->LocalCount()) +
+ 2 * (argument_count + local_count) +
(at_return ? 1 : 0);
Handle<FixedArray> details = isolate->factory()->NewFixedArray(details_size);
@@ -11302,7 +11313,7 @@
// Add the locals count
details->set(kFrameDetailsLocalCountIndex,
- Smi::FromInt(scope_info->LocalCount()));
+ Smi::FromInt(local_count));
// Add the source position.
if (position != RelocInfo::kNoPosition) {
@@ -11353,7 +11364,7 @@
}
// Add locals name and value from the temporary copy from the function frame.
- for (int i = 0; i < scope_info->LocalCount() * 2; i++) {
+ for (int i = 0; i < local_count * 2; i++) {
details->set(details_index++, locals->get(i));
}
@@ -11434,6 +11445,7 @@
// Second fill all stack locals.
for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
+ if (scope_info->LocalIsSynthetic(i)) continue;
Handle<String> name(scope_info->StackLocalName(i));
Handle<Object> value(frame_inspector->GetExpression(i), isolate);
if (value->IsTheHole()) continue;
@@ -11478,6 +11490,7 @@
// Stack locals.
for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
+ if (scope_info->LocalIsSynthetic(i)) continue;
if (frame->GetExpression(i)->IsTheHole()) continue;
HandleScope scope(isolate);
Handle<Object> value = Object::GetPropertyOrElement(
diff --git a/src/scopeinfo.cc b/src/scopeinfo.cc
index 157cdfa..564565f 100644
--- a/src/scopeinfo.cc
+++ b/src/scopeinfo.cc
@@ -278,6 +278,17 @@
}
+bool ScopeInfo::LocalIsSynthetic(int var) {
+ ASSERT(0 <= var && var < LocalCount());
+ // There's currently no flag stored on the ScopeInfo to indicate that a
+ // variable is a compiler-introduced temporary. However, to avoid conflict
+ // with user declarations, the current temporaries like .generator_object and
+ // .result start with a dot, so we can use that as a flag. It's a hack!
+ Handle<String> name(LocalName(var));
+ return name->length() > 0 && name->Get(0) == '.';
+}
+
+
int ScopeInfo::StackSlotIndex(String* name) {
ASSERT(name->IsInternalizedString());
if (length() > 0) {
@@ -368,16 +379,17 @@
int local_count = scope_info->ContextLocalCount();
if (local_count == 0) return true;
// Fill all context locals to the context extension.
+ int first_context_var = scope_info->StackLocalCount();
int start = scope_info->ContextLocalNameEntriesIndex();
- int end = start + local_count;
- for (int i = start; i < end; ++i) {
- int context_index = Context::MIN_CONTEXT_SLOTS + i - start;
+ for (int i = 0; i < local_count; ++i) {
+ if (scope_info->LocalIsSynthetic(first_context_var + i)) continue;
+ int context_index = Context::MIN_CONTEXT_SLOTS + i;
RETURN_ON_EXCEPTION_VALUE(
isolate,
Runtime::SetObjectProperty(
isolate,
scope_object,
- Handle<String>(String::cast(scope_info->get(i))),
+ Handle<String>(String::cast(scope_info->get(i + start))),
Handle<Object>(context->get(context_index), isolate),
::NONE,
SLOPPY),
diff --git a/src/version.cc b/src/version.cc
index 83ee66e..3f38536 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
// system so their names cannot be changed without changing the scripts.
#define MAJOR_VERSION 3
#define MINOR_VERSION 26
-#define BUILD_NUMBER 21
+#define BUILD_NUMBER 22
#define PATCH_LEVEL 0
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
diff --git a/test/mjsunit/bugs/harmony/debug-blockscopes.js b/test/mjsunit/bugs/harmony/debug-blockscopes.js
index fda32eb..9ef8efb 100644
--- a/test/mjsunit/bugs/harmony/debug-blockscopes.js
+++ b/test/mjsunit/bugs/harmony/debug-blockscopes.js
@@ -144,18 +144,10 @@
if (!scope.scopeObject().property('arguments').isUndefined()) {
scope_size--;
}
- // Also ignore synthetic variable from catch block.
- if (!scope.scopeObject().property('.catch-var').isUndefined()) {
- scope_size--;
- }
// Skip property with empty name.
if (!scope.scopeObject().property('').isUndefined()) {
scope_size--;
}
- // Also ignore synthetic variable from block scopes.
- if (!scope.scopeObject().property('.block').isUndefined()) {
- scope_size--;
- }
if (count != scope_size) {
print('Names found in scope:');
diff --git a/test/mjsunit/debug-scopes.js b/test/mjsunit/debug-scopes.js
index f5b5ec9..ce37d24 100644
--- a/test/mjsunit/debug-scopes.js
+++ b/test/mjsunit/debug-scopes.js
@@ -166,10 +166,6 @@
if (!scope.scopeObject().property('arguments').isUndefined()) {
scope_size--;
}
- // Also ignore synthetic variable from catch block.
- if (!scope.scopeObject().property('.catch-var').isUndefined()) {
- scope_size--;
- }
// Skip property with empty name.
if (!scope.scopeObject().property('').isUndefined()) {
scope_size--;
diff --git a/test/mjsunit/harmony/debug-blockscopes.js b/test/mjsunit/harmony/debug-blockscopes.js
index ca2ab9e..f56a306 100644
--- a/test/mjsunit/harmony/debug-blockscopes.js
+++ b/test/mjsunit/harmony/debug-blockscopes.js
@@ -147,18 +147,10 @@
if (!scope.scopeObject().property('arguments').isUndefined()) {
scope_size--;
}
- // Also ignore synthetic variable from catch block.
- if (!scope.scopeObject().property('.catch-var').isUndefined()) {
- scope_size--;
- }
// Skip property with empty name.
if (!scope.scopeObject().property('').isUndefined()) {
scope_size--;
}
- // Also ignore synthetic variable from block scopes.
- if (!scope.scopeObject().property('.block').isUndefined()) {
- scope_size--;
- }
if (count != scope_size) {
print('Names found in scope:');
@@ -375,8 +367,9 @@
debug.ScopeType.Local,
debug.ScopeType.Global], exec_state);
CheckScopeContent({x:'y'}, 0, exec_state);
- // The function scope contains a temporary iteration variable.
- CheckScopeContent({'.for.x':'y'}, 1, exec_state);
+ // The function scope contains a temporary iteration variable, but it is
+ // hidden to the debugger.
+ CheckScopeContent({}, 1, exec_state);
};
for_loop_1();
EndTest();
@@ -400,8 +393,9 @@
debug.ScopeType.Global], exec_state);
CheckScopeContent({x:3}, 0, exec_state);
CheckScopeContent({x:'y'}, 1, exec_state);
- // The function scope contains a temporary iteration variable.
- CheckScopeContent({'.for.x':'y'}, 2, exec_state);
+ // The function scope contains a temporary iteration variable, hidden to the
+ // debugger.
+ CheckScopeContent({}, 2, exec_state);
};
for_loop_2();
EndTest();
diff --git a/test/mjsunit/regress/regress-observe-map-cache.js b/test/mjsunit/regress/regress-observe-map-cache.js
new file mode 100644
index 0000000..4c7a7e3
--- /dev/null
+++ b/test/mjsunit/regress/regress-observe-map-cache.js
@@ -0,0 +1,14 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax --enable-slow-asserts
+
+function f() {
+ var x = new Array(0);
+ x[-1] = -1;
+ Object.observe(x, function() { });
+}
+
+f();
+f();