+2012-01-26: Version 3.8.9
+
+ Flush number string cache on GC (issue 1605).
+
+ Provide access to function inferred name with
+ v8::Function::GetInferredName in V8 public API.
+
+ Fix building with Clang (issue 1912).
+
+ Reduce the space used by the stack for the profiling thread.
+
+ Fix misleading documentation of v8::Locker (issue 542).
+
+ Introduce readbinary function in d8 to read binary files.
+
+ Performance and stability improvements on all platforms.
+
+
2012-01-23: Version 3.8.8
Limited number of loop iterations in Heap::ReserveSpace
'-O3',
],
'conditions': [
- [ 'gcc_version==44', {
+ [ 'gcc_version==44 and clang==0', {
'cflags': [
# Avoid crashes with gcc 4.4 in the v8 test suite.
'-fno-tree-vrp',
V8EXPORT Handle<Value> GetName() const;
/**
+ * Name inferred from variable or property assignment of this function.
+ * Used to facilitate debugging and profiling of JavaScript code written
+ * in an OO style, where many functions are anonymous but are assigned
+ * to object properties.
+ */
+ V8EXPORT Handle<Value> GetInferredName() const;
+
+ /**
* Returns zero based line number of function body and
* kLineOffsetNotFound if no information available.
*/
* default isolate is implicitly created and entered. The embedder
* can create additional isolates and use them in parallel in multiple
* threads. An isolate can be entered by at most one thread at any
- * given time. The Locker/Unlocker API can be used to synchronize.
+ * given time. The Locker/Unlocker API must be used to synchronize.
*/
class V8EXPORT Isolate {
public:
* accessing handles or holding onto object pointers obtained
* from V8 handles while in the particular V8 isolate. It is up
* to the user of V8 to ensure (perhaps with locking) that this
- * constraint is not violated.
+ * constraint is not violated. In addition to any other synchronization
+ * mechanism that may be used, the v8::Locker and v8::Unlocker classes
+ * must be used to signal thead switches to V8.
*
* v8::Locker is a scoped lock object. While it's
* active (i.e. between its construction and destruction) the current thread is
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
#ifndef V8STDINT_H_
#define V8STDINT_H_
+#include <stddef.h>
#include <stdio.h>
#if defined(_WIN32) && !defined(__MINGW32__)
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "v8.h"
-
#include "accessors.h"
-#include "ast.h"
+
+#include "contexts.h"
#include "deoptimizer.h"
#include "execution.h"
#include "factory.h"
+#include "frames-inl.h"
+#include "isolate.h"
#include "list-inl.h"
-#include "safepoint-table.h"
-#include "scopeinfo.h"
+#include "property-details.h"
namespace v8 {
namespace internal {
Handle<JSFunction> inlined_function,
int inlined_frame_index) {
Factory* factory = Isolate::Current()->factory();
- int args_count = inlined_function->shared()->formal_parameter_count();
- ScopedVector<SlotRef> args_slots(args_count);
- SlotRef::ComputeSlotMappingForArguments(frame,
- inlined_frame_index,
- &args_slots);
+ Vector<SlotRef> args_slots =
+ SlotRef::ComputeSlotMappingForArguments(
+ frame,
+ inlined_frame_index,
+ inlined_function->shared()->formal_parameter_count());
+ int args_count = args_slots.length();
Handle<JSObject> arguments =
factory->NewArgumentsObject(inlined_function, args_count);
Handle<FixedArray> array = factory->NewFixedArray(args_count);
array->set(i, *value);
}
arguments->set_elements(*array);
+ args_slots.Dispose();
// Return the freshly allocated arguments object.
return *arguments;
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
#define V8_ACCESSORS_H_
#include "allocation.h"
+#include "v8globals.h"
namespace v8 {
namespace internal {
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include "../include/v8stdint.h"
-#include "globals.h"
-#include "checks.h"
#include "allocation.h"
+
+#include <stdlib.h> // For free, malloc.
+#include <string.h> // For memcpy.
+#include "checks.h"
#include "utils.h"
namespace v8 {
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
#ifndef V8_ALLOCATION_H_
#define V8_ALLOCATION_H_
-#include "checks.h"
#include "globals.h"
namespace v8 {
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include "v8.h"
-
#include "api.h"
-#include "arguments.h"
+#include <math.h> // For isnan.
+#include <string.h> // For memcpy, strlen.
+#include "../include/v8-debug.h"
+#include "../include/v8-profiler.h"
+#include "../include/v8-testing.h"
#include "bootstrapper.h"
#include "compiler.h"
+#include "conversions-inl.h"
+#include "counters.h"
#include "debug.h"
#include "deoptimizer.h"
#include "execution.h"
-#include "flags.h"
#include "global-handles.h"
#include "heap-profiler.h"
#include "messages.h"
-#include "natives.h"
#include "parser.h"
#include "platform.h"
#include "profile-generator-inl.h"
+#include "property-details.h"
+#include "property.h"
#include "runtime-profiler.h"
#include "scanner-character-streams.h"
-#include "serialize.h"
#include "snapshot.h"
+#include "unicode-inl.h"
#include "v8threads.h"
#include "version.h"
#include "vm-state-inl.h"
-#include "../include/v8-profiler.h"
-#include "../include/v8-testing.h"
#define LOG_API(isolate, expr) LOG(isolate, ApiEntryCall(expr))
}
+Handle<Value> Function::GetInferredName() const {
+ i::Handle<i::JSFunction> func = Utils::OpenHandle(this);
+ return Utils::ToLocal(i::Handle<i::Object>(func->shared()->inferred_name()));
+}
+
+
ScriptOrigin Function::GetScriptOrigin() const {
i::Handle<i::JSFunction> func = Utils::OpenHandle(this);
if (func->shared()->script()->IsScript()) {
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
#ifndef V8_API_H_
#define V8_API_H_
-#include "apiutils.h"
-#include "factory.h"
+#include "v8.h"
#include "../include/v8-testing.h"
+#include "apiutils.h"
+#include "contexts.h"
+#include "factory.h"
+#include "isolate.h"
+#include "list-inl.h"
namespace v8 {
#define V8_ARM_ASSEMBLER_ARM_INL_H_
#include "arm/assembler-arm.h"
+
#include "cpu.h"
#include "debug.h"
const DwVfpRegister d14 = { 14 };
const DwVfpRegister d15 = { 15 };
-// Aliases for double registers.
-static const DwVfpRegister& kFirstCalleeSavedDoubleReg = d8;
-static const DwVfpRegister& kLastCalleeSavedDoubleReg = d15;
-static const DwVfpRegister& kDoubleRegZero = d14;
-static const DwVfpRegister& kScratchDoubleReg = d15;
+// Aliases for double registers. Defined using #define instead of
+// "static const DwVfpRegister&" because Clang complains otherwise when a
+// compilation unit that includes this header doesn't use the variables.
+#define kFirstCalleeSavedDoubleReg d8
+#define kLastCalleeSavedDoubleReg d15
+#define kDoubleRegZero d14
+#define kScratchDoubleReg d15
// Coprocessor register
__ bind(&invoke);
__ Call(r3);
+ masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
// Exit frame and return.
LeaveArgumentsAdaptorFrame(masm);
__ Jump(lr);
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
ASSERT(Translation::BEGIN == opcode);
USE(opcode);
int count = iterator.Next();
+ iterator.Skip(1); // Drop JS frame count.
ASSERT(count == 1);
USE(count);
opcode = static_cast<Translation::Opcode>(iterator.Next());
USE(opcode);
- ASSERT(Translation::FRAME == opcode);
+ ASSERT(Translation::JS_FRAME == opcode);
unsigned node_id = iterator.Next();
USE(node_id);
ASSERT(node_id == ast_id);
output_ = new FrameDescription*[1];
output_[0] = new(output_frame_size) FrameDescription(
output_frame_size, function_);
-#ifdef DEBUG
- output_[0]->SetKind(Code::OPTIMIZED_FUNCTION);
-#endif
+ output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT);
// Clear the incoming parameters in the optimized frame to avoid
// confusing the garbage collector.
}
+void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
+ int frame_index) {
+ JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
+ unsigned height = iterator->Next();
+ unsigned height_in_bytes = height * kPointerSize;
+ if (FLAG_trace_deopt) {
+ PrintF(" translating arguments adaptor => height=%d\n", height_in_bytes);
+ }
+
+ unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize;
+ unsigned input_frame_size = input_->GetFrameSize();
+ unsigned output_frame_size = height_in_bytes + fixed_frame_size;
+
+ // Allocate and store the output frame description.
+ FrameDescription* output_frame =
+ new(output_frame_size) FrameDescription(output_frame_size, function);
+ output_frame->SetFrameType(StackFrame::ARGUMENTS_ADAPTOR);
+
+ // Arguments adaptor can not be topmost or bottommost.
+ ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
+ ASSERT(output_[frame_index] == NULL);
+ output_[frame_index] = output_frame;
+
+ // The top address of the frame is computed from the previous
+ // frame's top and this frame's size.
+ uint32_t top_address;
+ top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
+ output_frame->SetTop(top_address);
+
+ // Compute the incoming parameter translation.
+ int parameter_count = height;
+ unsigned output_offset = output_frame_size;
+ unsigned input_offset = input_frame_size;
+ for (int i = 0; i < parameter_count; ++i) {
+ output_offset -= kPointerSize;
+ DoTranslateCommand(iterator, frame_index, output_offset);
+ }
+ input_offset -= (parameter_count * kPointerSize);
+
+ // Read caller's PC from the previous frame.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ intptr_t callers_pc = output_[frame_index - 1]->GetPc();
+ output_frame->SetFrameSlot(output_offset, callers_pc);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's pc\n",
+ top_address + output_offset, output_offset, callers_pc);
+ }
+
+ // Read caller's FP from the previous frame, and set this frame's FP.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ intptr_t value = output_[frame_index - 1]->GetFp();
+ output_frame->SetFrameSlot(output_offset, value);
+ intptr_t fp_value = top_address + output_offset;
+ output_frame->SetFp(fp_value);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's fp\n",
+ fp_value, output_offset, value);
+ }
+
+ // A marker value is used in place of the context.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ intptr_t context = reinterpret_cast<intptr_t>(
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+ output_frame->SetFrameSlot(output_offset, context);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context (adaptor sentinel)\n",
+ top_address + output_offset, output_offset, context);
+ }
+
+ // The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ value = reinterpret_cast<intptr_t>(function);
+ output_frame->SetFrameSlot(output_offset, value);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function\n",
+ top_address + output_offset, output_offset, value);
+ }
+
+ // Number of incoming arguments.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ value = reinterpret_cast<uint32_t>(Smi::FromInt(height - 1));
+ output_frame->SetFrameSlot(output_offset, value);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08x: [top + %d] <- 0x%08x ; argc (%d)\n",
+ top_address + output_offset, output_offset, value, height - 1);
+ }
+
+ ASSERT(0 == output_offset);
+
+ Builtins* builtins = isolate_->builtins();
+ Code* adaptor_trampoline =
+ builtins->builtin(Builtins::kArgumentsAdaptorTrampoline);
+ uint32_t pc = reinterpret_cast<uint32_t>(
+ adaptor_trampoline->instruction_start() +
+ isolate_->heap()->arguments_adaptor_deopt_pc_offset()->value());
+ output_frame->SetPc(pc);
+}
+
+
// This code is very similar to ia32 code, but relies on register names (fp, sp)
// and how the frame is laid out.
-void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
- int frame_index) {
+void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
+ int frame_index) {
// Read the ast node id, function, and frame height for this output frame.
- Translation::Opcode opcode =
- static_cast<Translation::Opcode>(iterator->Next());
- USE(opcode);
- ASSERT(Translation::FRAME == opcode);
int node_id = iterator->Next();
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
unsigned height = iterator->Next();
// Allocate and store the output frame description.
FrameDescription* output_frame =
new(output_frame_size) FrameDescription(output_frame_size, function);
-#ifdef DEBUG
- output_frame->SetKind(Code::FUNCTION);
-#endif
+ output_frame->SetFrameType(StackFrame::JAVA_SCRIPT);
bool is_bottommost = (0 == frame_index);
bool is_topmost = (output_count_ - 1 == frame_index);
}
+// The disassembler may end up decoding data inlined in the code. We do not want
+// it to crash if the data does not ressemble any known instruction.
+#define VERIFY(condition) \
+if(!(condition)) { \
+ Unknown(instr); \
+ return; \
+}
+
+
// For currently unimplemented decodings the disassembler calls Unknown(instr)
// which will just print "unknown" of the instruction bits.
void Decoder::Unknown(Instruction* instr) {
void Decoder::DecodeType3(Instruction* instr) {
switch (instr->PUField()) {
case da_x: {
- ASSERT(!instr->HasW());
+ VERIFY(!instr->HasW());
Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm");
break;
}
case ia_x: {
if (instr->HasW()) {
- ASSERT(instr->Bits(5, 4) == 0x1);
+ VERIFY(instr->Bits(5, 4) == 0x1);
if (instr->Bit(22) == 0x1) {
Format(instr, "usat 'rd, #'imm05@16, 'rm'shift_sat");
} else {
// vmsr
// Dd = vsqrt(Dm)
void Decoder::DecodeTypeVFP(Instruction* instr) {
- ASSERT((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0) );
- ASSERT(instr->Bits(11, 9) == 0x5);
+ VERIFY((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0) );
+ VERIFY(instr->Bits(11, 9) == 0x5);
if (instr->Bit(4) == 0) {
if (instr->Opc1Value() == 0x7) {
void Decoder::DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(
Instruction* instr) {
- ASSERT((instr->Bit(4) == 1) && (instr->VCValue() == 0x0) &&
+ VERIFY((instr->Bit(4) == 1) && (instr->VCValue() == 0x0) &&
(instr->VAValue() == 0x0));
bool to_arm_register = (instr->VLValue() == 0x1);
void Decoder::DecodeVCMP(Instruction* instr) {
- ASSERT((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7));
- ASSERT(((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) &&
+ VERIFY((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7));
+ VERIFY(((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) &&
(instr->Opc3Value() & 0x1));
// Comparison.
void Decoder::DecodeVCVTBetweenDoubleAndSingle(Instruction* instr) {
- ASSERT((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7));
- ASSERT((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3));
+ VERIFY((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7));
+ VERIFY((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3));
bool double_to_single = (instr->SzValue() == 1);
void Decoder::DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr) {
- ASSERT((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7));
- ASSERT(((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) ||
+ VERIFY((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7));
+ VERIFY(((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) ||
(((instr->Opc2Value() >> 1) == 0x6) && (instr->Opc3Value() & 0x1)));
bool to_integer = (instr->Bit(18) == 1);
// Ddst = MEM(Rbase + 4*offset).
// MEM(Rbase + 4*offset) = Dsrc.
void Decoder::DecodeType6CoprocessorIns(Instruction* instr) {
- ASSERT(instr->TypeValue() == 6);
+ VERIFY(instr->TypeValue() == 6);
if (instr->CoprocessorValue() == 0xA) {
switch (instr->OpcodeValue()) {
}
}
+#undef VERIFIY
bool Decoder::IsConstantPoolAt(byte* instr_ptr) {
int instruction_bits = *(reinterpret_cast<int*>(instr_ptr));
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
class StandardFrameConstants : public AllStatic {
public:
+ // Fixed part of the frame consists of return address, caller fp,
+ // context and function.
+ static const int kFixedFrameSize = 4 * kPointerSize;
static const int kExpressionsOffset = -3 * kPointerSize;
static const int kMarkerOffset = -2 * kPointerSize;
static const int kContextOffset = -1 * kPointerSize;
class ArgumentsAdaptorFrameConstants : public AllStatic {
public:
static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + kPointerSize;
};
// Load the key (consisting of map and symbol) from the cache and
// check for match.
- Label try_second_entry, hit_on_first_entry, load_in_object_property;
+ Label load_in_object_property;
+ static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket;
+ Label hit_on_nth_entry[kEntriesPerBucket];
ExternalReference cache_keys =
ExternalReference::keyed_lookup_cache_keys(isolate);
+
__ mov(r4, Operand(cache_keys));
__ add(r4, r4, Operand(r3, LSL, kPointerSizeLog2 + 1));
- // Move r4 to second entry.
- __ ldr(r5, MemOperand(r4, kPointerSize * 2, PostIndex));
- __ cmp(r2, r5);
- __ b(ne, &try_second_entry);
- __ ldr(r5, MemOperand(r4, -kPointerSize)); // Load symbol
- __ cmp(r0, r5);
- __ b(eq, &hit_on_first_entry);
- __ bind(&try_second_entry);
- __ ldr(r5, MemOperand(r4, kPointerSize, PostIndex)); // Move r4 to symbol.
+ for (int i = 0; i < kEntriesPerBucket - 1; i++) {
+ Label try_next_entry;
+ // Load map and move r4 to next entry.
+ __ ldr(r5, MemOperand(r4, kPointerSize * 2, PostIndex));
+ __ cmp(r2, r5);
+ __ b(ne, &try_next_entry);
+ __ ldr(r5, MemOperand(r4, -kPointerSize)); // Load symbol
+ __ cmp(r0, r5);
+ __ b(eq, &hit_on_nth_entry[i]);
+ __ bind(&try_next_entry);
+ }
+
+ // Last entry: Load map and move r4 to symbol.
+ __ ldr(r5, MemOperand(r4, kPointerSize, PostIndex));
__ cmp(r2, r5);
__ b(ne, &slow);
__ ldr(r5, MemOperand(r4));
ExternalReference cache_field_offsets =
ExternalReference::keyed_lookup_cache_field_offsets(isolate);
- // Hit on second entry.
- __ mov(r4, Operand(cache_field_offsets));
- __ add(r3, r3, Operand(1));
- __ ldr(r5, MemOperand(r4, r3, LSL, kPointerSizeLog2));
- __ ldrb(r6, FieldMemOperand(r2, Map::kInObjectPropertiesOffset));
- __ sub(r5, r5, r6, SetCC);
- __ b(ge, &property_array_property);
- __ jmp(&load_in_object_property);
-
- // Hit on first entry.
- __ bind(&hit_on_first_entry);
- __ mov(r4, Operand(cache_field_offsets));
- __ ldr(r5, MemOperand(r4, r3, LSL, kPointerSizeLog2));
- __ ldrb(r6, FieldMemOperand(r2, Map::kInObjectPropertiesOffset));
- __ sub(r5, r5, r6, SetCC);
- __ b(ge, &property_array_property);
+ // Hit on nth entry.
+ for (int i = kEntriesPerBucket - 1; i >= 0; i--) {
+ __ bind(&hit_on_nth_entry[i]);
+ __ mov(r4, Operand(cache_field_offsets));
+ if (i != 0) {
+ __ add(r3, r3, Operand(i));
+ }
+ __ ldr(r5, MemOperand(r4, r3, LSL, kPointerSizeLog2));
+ __ ldrb(r6, FieldMemOperand(r2, Map::kInObjectPropertiesOffset));
+ __ sub(r5, r5, r6, SetCC);
+ __ b(ge, &property_array_property);
+ if (i != 0) {
+ __ jmp(&load_in_object_property);
+ }
+ }
// Load in-object property.
__ bind(&load_in_object_property);
LEnvironment* outer =
CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator);
int ast_id = hydrogen_env->ast_id();
- ASSERT(ast_id != AstNode::kNoNumber);
+ ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor());
int value_count = hydrogen_env->length();
LEnvironment* result = new LEnvironment(hydrogen_env->closure(),
+ hydrogen_env->is_arguments_adaptor(),
ast_id,
hydrogen_env->parameter_count(),
argument_count_,
value_count,
outer);
+ int argument_index = *argument_index_accumulator;
for (int i = 0; i < value_count; ++i) {
if (hydrogen_env->is_special_index(i)) continue;
if (value->IsArgumentsObject()) {
op = NULL;
} else if (value->IsPushArgument()) {
- op = new LArgument((*argument_index_accumulator)++);
+ op = new LArgument(argument_index++);
} else {
op = UseAny(value);
}
result->AddValue(op, value->representation());
}
+ if (!hydrogen_env->is_arguments_adaptor()) {
+ *argument_index_accumulator = argument_index;
+ }
+
return result;
}
LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement(
HLoadKeyedSpecializedArrayElement* instr) {
ElementsKind elements_kind = instr->elements_kind();
- Representation representation(instr->representation());
ASSERT(
- (representation.IsInteger32() &&
+ (instr->representation().IsInteger32() &&
(elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
(elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
- (representation.IsDouble() &&
+ (instr->representation().IsDouble() &&
((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
(elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
ASSERT(instr->key()->representation().IsInteger32());
LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement(
HStoreKeyedSpecializedArrayElement* instr) {
- Representation representation(instr->value()->representation());
ElementsKind elements_kind = instr->elements_kind();
ASSERT(
- (representation.IsInteger32() &&
+ (instr->value()->representation().IsInteger32() &&
(elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
(elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
- (representation.IsDouble() &&
+ (instr->value()->representation().IsDouble() &&
((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
(elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
ASSERT(instr->external_pointer()->representation().IsExternal());
HEnvironment* outer = current_block_->last_environment();
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
+ instr->arguments_count(),
instr->function(),
undefined,
instr->call_kind());
LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
- HEnvironment* outer = current_block_->last_environment()->outer();
+ HEnvironment* outer = current_block_->last_environment()->
+ DiscardInlined(false);
current_block_->UpdateEnvironment(outer);
return NULL;
}
WriteTranslation(environment->outer(), translation);
int closure_id = DefineDeoptimizationLiteral(environment->closure());
- translation->BeginFrame(environment->ast_id(), closure_id, height);
+ if (environment->is_arguments_adaptor()) {
+ translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
+ } else {
+ translation->BeginJSFrame(environment->ast_id(), closure_id, height);
+ }
for (int i = 0; i < translation_size; ++i) {
LOperand* value = environment->values()->at(i);
// spilled_registers_ and spilled_double_registers_ are either
// |>------------ translation_size ------------<|
int frame_count = 0;
+ int jsframe_count = 0;
for (LEnvironment* e = environment; e != NULL; e = e->outer()) {
++frame_count;
+ if (!e->is_arguments_adaptor()) {
+ ++jsframe_count;
+ }
}
- Translation translation(&translations_, frame_count);
+ Translation translation(&translations_, frame_count, jsframe_count);
WriteTranslation(environment, &translation);
int deoptimization_index = deoptimizations_.length();
int pc_offset = masm()->pc_offset();
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
-#include "v8.h"
+#include "assembler.h"
-#include "arguments.h"
+#include <math.h> // For cos, log, pow, sin, tan, etc.
+#include "api.h"
+#include "builtins.h"
+#include "counters.h"
+#include "cpu.h"
+#include "debug.h"
#include "deoptimizer.h"
#include "execution.h"
-#include "ic-inl.h"
-#include "incremental-marking.h"
-#include "factory.h"
+#include "ic.h"
+#include "isolate.h"
+#include "jsregexp.h"
+#include "platform.h"
+#include "regexp-macro-assembler.h"
+#include "regexp-stack.h"
#include "runtime.h"
-#include "runtime-profiler.h"
#include "serialize.h"
+#include "store-buffer-inl.h"
#include "stub-cache.h"
-#include "regexp-stack.h"
-#include "ast.h"
-#include "regexp-macro-assembler.h"
-#include "platform.h"
-#include "store-buffer.h"
+#include "token.h"
+
+#if V8_TARGET_ARCH_IA32
+#include "ia32/assembler-ia32-inl.h"
+#elif V8_TARGET_ARCH_X64
+#include "x64/assembler-x64-inl.h"
+#elif V8_TARGET_ARCH_ARM
+#include "arm/assembler-arm-inl.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/assembler-mips-inl.h"
+#else
+#error "Unknown architecture."
+#endif
+
// Include native regexp-macro-assembler.
#ifndef V8_INTERPRETED_REGEXP
#if V8_TARGET_ARCH_IA32
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
#ifndef V8_ASSEMBLER_H_
#define V8_ASSEMBLER_H_
+#include "v8.h"
+
#include "allocation.h"
+#include "builtins.h"
#include "gdb-jit.h"
+#include "isolate.h"
#include "runtime.h"
#include "token.h"
namespace v8 {
+
+class ApiFunction;
+
namespace internal {
+struct StatsCounter;
const unsigned kNoASTId = -1;
// -----------------------------------------------------------------------------
// Platform independent assembler base class.
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include "v8.h"
-
#include "ast.h"
+
+#include <math.h> // For isfinite.
+#include "builtins.h"
+#include "conversions.h"
+#include "hashmap.h"
#include "parser.h"
+#include "property-details.h"
+#include "property.h"
#include "scopes.h"
#include "string-stream.h"
#include "type-info.h"
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
#ifndef V8_AST_H_
#define V8_AST_H_
-#include "allocation.h"
-#include "execution.h"
+#include "v8.h"
+
+#include "assembler.h"
#include "factory.h"
+#include "isolate.h"
#include "jsregexp.h"
+#include "list-inl.h"
#include "runtime.h"
#include "small-pointer-list.h"
+#include "smart-array-pointer.h"
#include "token.h"
#include "variables.h"
+#include "zone-inl.h"
namespace v8 {
namespace internal {
EXPRESSION_NODE_LIST(V)
// Forward declarations
-class BitVector;
-class DefinitionInfo;
+class AstVisitor;
+class BreakableStatement;
+class Expression;
+class IterationStatement;
class MaterializedLiteral;
+class Statement;
class TargetCollector;
class TypeFeedbackOracle;
+class RegExpAlternative;
+class RegExpAssertion;
+class RegExpAtom;
+class RegExpBackReference;
+class RegExpCapture;
+class RegExpCharacterClass;
+class RegExpCompiler;
+class RegExpDisjunction;
+class RegExpEmpty;
+class RegExpLookahead;
+class RegExpQuantifier;
+class RegExpText;
+
#define DEF_FORWARD_DECLARATION(type) class type;
AST_NODE_LIST(DEF_FORWARD_DECLARATION)
#undef DEF_FORWARD_DECLARATION
typedef ZoneList<Handle<Object> > ZoneObjectList;
-#define DECLARE_NODE_TYPE(type) \
- virtual void Accept(AstVisitor* v); \
- virtual AstNode::Type node_type() const { return AstNode::k##type; } \
-
-
class AstNode: public ZoneObject {
public:
#define DECLARE_TYPE_ENUM(type) k##type,
};
+#define DECLARE_NODE_TYPE(type) \
+ virtual void Accept(AstVisitor* v); \
+ virtual AstNode::Type node_type() const { return AstNode::k##type; } \
+
+
class Statement: public AstNode {
public:
Statement() : statement_pos_(RelocInfo::kNoPosition) {}
namespace v8 {
namespace internal {
-static const int kEventsBufferSize = 256*KB;
-static const int kTickSamplesBufferChunkSize = 64*KB;
+static const int kEventsBufferSize = 256 * KB;
+static const int kTickSamplesBufferChunkSize = 64 * KB;
static const int kTickSamplesBufferChunksCount = 16;
+static const int kProfilerStackSize = 32 * KB;
ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator)
- : Thread("v8:ProfEvntProc"),
+ : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)),
generator_(generator),
running_(true),
ticks_buffer_(sizeof(TickSampleEventRecord),
const char* Shell::kPrompt = "d8> ";
+const int MB = 1024 * 1024;
+
+
#ifndef V8_SHARED
bool CounterMap::Match(void* key1, void* key2) {
const char* name1 = reinterpret_cast<const char*>(key1);
global_template->Set(String::New("print"), FunctionTemplate::New(Print));
global_template->Set(String::New("write"), FunctionTemplate::New(Write));
global_template->Set(String::New("read"), FunctionTemplate::New(Read));
+ global_template->Set(String::New("readbinary"),
+ FunctionTemplate::New(ReadBinary));
global_template->Set(String::New("readline"),
FunctionTemplate::New(ReadLine));
global_template->Set(String::New("load"), FunctionTemplate::New(Load));
}
+Handle<Value> Shell::ReadBinary(const Arguments& args) {
+ String::Utf8Value filename(args[0]);
+ int size;
+ if (*filename == NULL) {
+ return ThrowException(String::New("Error loading file"));
+ }
+ char* chars = ReadChars(*filename, &size);
+ if (chars == NULL) {
+ return ThrowException(String::New("Error reading file"));
+ }
+ // We skip checking the string for UTF8 characters and use it raw as
+ // backing store for the external string with 8-bit characters.
+ BinaryResource* resource = new BinaryResource(chars, size);
+ return String::NewExternal(resource);
+}
+
+
#ifndef V8_SHARED
static char* ReadToken(char* data, char token) {
char* next = i::OS::StrChr(data, token);
#ifndef V8_SHARED
i::Thread::Options SourceGroup::GetThreadOptions() {
- i::Thread::Options options;
- options.name = "IsolateThread";
// On some systems (OSX 10.6) the stack size default is 0.5Mb or less
// which is not enough to parse the big literal expressions used in tests.
// The stack size should be at least StackGuard::kLimitSize + some
- // OS-specific padding for thread startup code.
- options.stack_size = 2 << 20; // 2 Mb seems to be enough
- return options;
+ // OS-specific padding for thread startup code. 2Mbytes seems to be enough.
+ return i::Thread::Options("IsolateThread", 2 * MB);
}
};
+class BinaryResource : public v8::String::ExternalAsciiStringResource {
+ public:
+ BinaryResource(const char* string, int length)
+ : data_(string),
+ length_(length) { }
+
+ ~BinaryResource() {
+ delete[] data_;
+ data_ = NULL;
+ length_ = 0;
+ }
+
+ virtual const char* data() const { return data_; }
+ virtual size_t length() const { return length_; }
+
+ private:
+ const char* data_;
+ size_t length_;
+};
+
+
class ShellOptions {
public:
ShellOptions() :
static Handle<Value> EnableProfiler(const Arguments& args);
static Handle<Value> DisableProfiler(const Arguments& args);
static Handle<Value> Read(const Arguments& args);
+ static Handle<Value> ReadBinary(const Arguments& args);
static Handle<String> ReadFromStdin();
static Handle<Value> ReadLine(const Arguments& args) {
return ReadFromStdin();
}
+static void CollectActiveFunctionsFromThread(
+ Isolate* isolate,
+ ThreadLocalTop* top,
+ List<Handle<JSFunction> >* active_functions,
+ Object* active_code_marker) {
+ // Find all non-optimized code functions with activation frames
+ // on the stack. This includes functions which have optimized
+ // activations (including inlined functions) on the stack as the
+ // non-optimized code is needed for the lazy deoptimization.
+ for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
+ JavaScriptFrame* frame = it.frame();
+ if (frame->is_optimized()) {
+ List<JSFunction*> functions(Compiler::kMaxInliningLevels + 1);
+ frame->GetFunctions(&functions);
+ for (int i = 0; i < functions.length(); i++) {
+ JSFunction* function = functions[i];
+ active_functions->Add(Handle<JSFunction>(function));
+ function->shared()->code()->set_gc_metadata(active_code_marker);
+ }
+ } else if (frame->function()->IsJSFunction()) {
+ JSFunction* function = JSFunction::cast(frame->function());
+ ASSERT(frame->LookupCode()->kind() == Code::FUNCTION);
+ active_functions->Add(Handle<JSFunction>(function));
+ function->shared()->code()->set_gc_metadata(active_code_marker);
+ }
+ }
+}
+
+
+static void RedirectActivationsToRecompiledCodeOnThread(
+ Isolate* isolate,
+ ThreadLocalTop* top) {
+ for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
+ JavaScriptFrame* frame = it.frame();
+
+ if (frame->is_optimized() || !frame->function()->IsJSFunction()) continue;
+
+ JSFunction* function = JSFunction::cast(frame->function());
+
+ ASSERT(frame->LookupCode()->kind() == Code::FUNCTION);
+
+ Handle<Code> frame_code(frame->LookupCode());
+ if (frame_code->has_debug_break_slots()) continue;
+
+ Handle<Code> new_code(function->shared()->code());
+ if (new_code->kind() != Code::FUNCTION ||
+ !new_code->has_debug_break_slots()) {
+ continue;
+ }
+
+ intptr_t delta = frame->pc() - frame_code->instruction_start();
+ int debug_break_slot_count = 0;
+ int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT);
+ for (RelocIterator it(*new_code, mask); !it.done(); it.next()) {
+ // Check if the pc in the new code with debug break
+ // slots is before this slot.
+ RelocInfo* info = it.rinfo();
+ int debug_break_slot_bytes =
+ debug_break_slot_count * Assembler::kDebugBreakSlotLength;
+ intptr_t new_delta =
+ info->pc() -
+ new_code->instruction_start() -
+ debug_break_slot_bytes;
+ if (new_delta > delta) {
+ break;
+ }
+
+ // Passed a debug break slot in the full code with debug
+ // break slots.
+ debug_break_slot_count++;
+ }
+ int debug_break_slot_bytes =
+ debug_break_slot_count * Assembler::kDebugBreakSlotLength;
+ if (FLAG_trace_deopt) {
+ PrintF("Replacing code %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) "
+ "with %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) "
+ "for debugging, "
+ "changing pc from %08" V8PRIxPTR " to %08" V8PRIxPTR "\n",
+ reinterpret_cast<intptr_t>(
+ frame_code->instruction_start()),
+ reinterpret_cast<intptr_t>(
+ frame_code->instruction_start()) +
+ frame_code->instruction_size(),
+ frame_code->instruction_size(),
+ reinterpret_cast<intptr_t>(new_code->instruction_start()),
+ reinterpret_cast<intptr_t>(new_code->instruction_start()) +
+ new_code->instruction_size(),
+ new_code->instruction_size(),
+ reinterpret_cast<intptr_t>(frame->pc()),
+ reinterpret_cast<intptr_t>(new_code->instruction_start()) +
+ delta + debug_break_slot_bytes);
+ }
+
+ // Patch the return address to return into the code with
+ // debug break slots.
+ frame->set_pc(
+ new_code->instruction_start() + delta + debug_break_slot_bytes);
+ }
+}
+
+
+class ActiveFunctionsCollector : public ThreadVisitor {
+ public:
+ explicit ActiveFunctionsCollector(List<Handle<JSFunction> >* active_functions,
+ Object* active_code_marker)
+ : active_functions_(active_functions),
+ active_code_marker_(active_code_marker) { }
+
+ void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
+ CollectActiveFunctionsFromThread(isolate,
+ top,
+ active_functions_,
+ active_code_marker_);
+ }
+
+ private:
+ List<Handle<JSFunction> >* active_functions_;
+ Object* active_code_marker_;
+};
+
+
+class ActiveFunctionsRedirector : public ThreadVisitor {
+ public:
+ void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
+ RedirectActivationsToRecompiledCodeOnThread(isolate, top);
+ }
+};
+
+
void Debug::PrepareForBreakPoints() {
// If preparing for the first break point make sure to deoptimize all
// functions as debugging does not work with optimized code.
// debug break slots.
isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask);
- // Ensure no GC in this scope as we are comparing raw pointer
- // values and performing a heap iteration.
+ // Ensure no GC in this scope as we are going to use gc_metadata
+ // field in the Code object to mark active functions.
AssertNoAllocation no_allocation;
- // Find all non-optimized code functions with activation frames
- // on the stack. This includes functions which have optimized
- // activations (including inlined functions) on the stack as the
- // non-optimized code is needed for the lazy deoptimization.
- for (JavaScriptFrameIterator it(isolate_); !it.done(); it.Advance()) {
- JavaScriptFrame* frame = it.frame();
- if (frame->is_optimized()) {
- List<JSFunction*> functions(Compiler::kMaxInliningLevels + 1);
- frame->GetFunctions(&functions);
- for (int i = 0; i < functions.length(); i++) {
- if (!functions[i]->shared()->code()->has_debug_break_slots()) {
- active_functions.Add(Handle<JSFunction>(functions[i]));
- }
- }
- } else if (frame->function()->IsJSFunction()) {
- JSFunction* function = JSFunction::cast(frame->function());
- ASSERT(frame->LookupCode()->kind() == Code::FUNCTION);
- if (!frame->LookupCode()->has_debug_break_slots() ||
- !function->shared()->code()->has_debug_break_slots()) {
- active_functions.Add(Handle<JSFunction>(function));
- }
- }
- }
+ Object* active_code_marker = isolate_->heap()->the_hole_value();
- // Sort the functions on the object pointer value to prepare for
- // the binary search below.
- active_functions.Sort(HandleObjectPointerCompare<JSFunction>);
+ CollectActiveFunctionsFromThread(isolate_,
+ isolate_->thread_local_top(),
+ &active_functions,
+ active_code_marker);
+ ActiveFunctionsCollector active_functions_collector(&active_functions,
+ active_code_marker);
+ isolate_->thread_manager()->IterateArchivedThreads(
+ &active_functions_collector);
- // Scan the heap for all non-optimized functions which has no
- // debug break slots.
+ // Scan the heap for all non-optimized functions which have no
+ // debug break slots and are not active or inlined into an active
+ // function and mark them for lazy compilation.
HeapIterator iterator;
HeapObject* obj = NULL;
while (((obj = iterator.next()) != NULL)) {
if (obj->IsJSFunction()) {
JSFunction* function = JSFunction::cast(obj);
- if (function->shared()->allows_lazy_compilation() &&
- function->shared()->script()->IsScript() &&
+ SharedFunctionInfo* shared = function->shared();
+ if (shared->allows_lazy_compilation() &&
+ shared->script()->IsScript() &&
function->code()->kind() == Code::FUNCTION &&
- !function->code()->has_debug_break_slots()) {
- bool has_activation =
- SortedListBSearch<Handle<JSFunction> >(
- active_functions,
- Handle<JSFunction>(function),
- HandleObjectPointerCompare<JSFunction>) != -1;
- if (!has_activation) {
- function->set_code(*lazy_compile);
- function->shared()->set_code(*lazy_compile);
- }
+ !function->code()->has_debug_break_slots() &&
+ shared->code()->gc_metadata() != active_code_marker) {
+ function->set_code(*lazy_compile);
+ function->shared()->set_code(*lazy_compile);
}
}
}
- }
- // Now the non-GC scope is left, and the sorting of the functions
- // in active_function is not ensured any more. The code below does
- // not rely on it.
+ // Clear gc_metadata field.
+ for (int i = 0; i < active_functions.length(); i++) {
+ Handle<JSFunction> function = active_functions[i];
+ function->shared()->code()->set_gc_metadata(Smi::FromInt(0));
+ }
+ }
// Now recompile all functions with activation frames and and
// patch the return address to run in the new compiled code.
for (int i = 0; i < active_functions.length(); i++) {
Handle<JSFunction> function = active_functions[i];
+
+ if (function->code()->kind() == Code::FUNCTION &&
+ function->code()->has_debug_break_slots()) {
+ // Nothing to do. Function code already had debug break slots.
+ continue;
+ }
+
Handle<SharedFunctionInfo> shared(function->shared());
// If recompilation is not possible just skip it.
if (shared->is_toplevel() ||
// Make sure that the shared full code is compiled with debug
// break slots.
- if (function->code() == *lazy_compile) {
- function->set_code(shared->code());
- }
if (!shared->code()->has_debug_break_slots()) {
// Try to compile the full code with debug break slots. If it
// fails just keep the current code.
continue;
}
}
- Handle<Code> new_code(shared->code());
-
- // Find the function and patch the return address.
- for (JavaScriptFrameIterator it(isolate_); !it.done(); it.Advance()) {
- JavaScriptFrame* frame = it.frame();
- // If the current frame is for this function in its
- // non-optimized form rewrite the return address to continue
- // in the newly compiled full code with debug break slots.
- if (!frame->is_optimized() &&
- frame->function()->IsJSFunction() &&
- frame->function() == *function) {
- ASSERT(frame->LookupCode()->kind() == Code::FUNCTION);
- Handle<Code> frame_code(frame->LookupCode());
- if (frame_code->has_debug_break_slots()) continue;
- intptr_t delta = frame->pc() - frame_code->instruction_start();
- int debug_break_slot_count = 0;
- int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT);
- for (RelocIterator it(*new_code, mask); !it.done(); it.next()) {
- // Check if the pc in the new code with debug break
- // slots is before this slot.
- RelocInfo* info = it.rinfo();
- int debug_break_slot_bytes =
- debug_break_slot_count * Assembler::kDebugBreakSlotLength;
- intptr_t new_delta =
- info->pc() -
- new_code->instruction_start() -
- debug_break_slot_bytes;
- if (new_delta > delta) {
- break;
- }
-
- // Passed a debug break slot in the full code with debug
- // break slots.
- debug_break_slot_count++;
- }
- int debug_break_slot_bytes =
- debug_break_slot_count * Assembler::kDebugBreakSlotLength;
- if (FLAG_trace_deopt) {
- PrintF("Replacing code %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) "
- "with %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) "
- "for debugging, "
- "changing pc from %08" V8PRIxPTR " to %08" V8PRIxPTR "\n",
- reinterpret_cast<intptr_t>(
- frame_code->instruction_start()),
- reinterpret_cast<intptr_t>(
- frame_code->instruction_start()) +
- frame_code->instruction_size(),
- frame_code->instruction_size(),
- reinterpret_cast<intptr_t>(new_code->instruction_start()),
- reinterpret_cast<intptr_t>(new_code->instruction_start()) +
- new_code->instruction_size(),
- new_code->instruction_size(),
- reinterpret_cast<intptr_t>(frame->pc()),
- reinterpret_cast<intptr_t>(new_code->instruction_start()) +
- delta + debug_break_slot_bytes);
- }
- // Patch the return address to return into the code with
- // debug break slots.
- frame->set_pc(
- new_code->instruction_start() + delta + debug_break_slot_bytes);
- }
- }
+ // Keep function code in sync with shared function info.
+ function->set_code(shared->code());
}
+
+ RedirectActivationsToRecompiledCodeOnThread(isolate_,
+ isolate_->thread_local_top());
+
+ ActiveFunctionsRedirector active_functions_redirector;
+ isolate_->thread_manager()->IterateArchivedThreads(
+ &active_functions_redirector);
}
}
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
return result;
}
+
+int Deoptimizer::ConvertJSFrameIndexToFrameIndex(int jsframe_index) {
+ if (jsframe_index == 0) return 0;
+
+ int frame_index = 0;
+ while (jsframe_index >= 0) {
+ FrameDescription* frame = output_[frame_index];
+ if (frame->GetFrameType() == StackFrame::JAVA_SCRIPT) {
+ jsframe_index--;
+ }
+ frame_index++;
+ }
+
+ return frame_index - 1;
+}
+
+
#ifdef ENABLE_DEBUGGER_SUPPORT
DeoptimizedFrameInfo* Deoptimizer::DebuggerInspectableFrame(
JavaScriptFrame* frame,
- int frame_index,
+ int jsframe_index,
Isolate* isolate) {
ASSERT(isolate == Isolate::Current());
ASSERT(frame->is_optimized());
// Create the GC safe output frame information and register it for GC
// handling.
- ASSERT_LT(frame_index, deoptimizer->output_count());
+ ASSERT_LT(jsframe_index, deoptimizer->jsframe_count());
+
+ // Convert JS frame index into frame index.
+ int frame_index = deoptimizer->ConvertJSFrameIndexToFrameIndex(jsframe_index);
+
+ bool has_arguments_adaptor =
+ frame_index > 0 &&
+ deoptimizer->output_[frame_index - 1]->GetFrameType() ==
+ StackFrame::ARGUMENTS_ADAPTOR;
+
DeoptimizedFrameInfo* info =
- new DeoptimizedFrameInfo(deoptimizer, frame_index);
+ new DeoptimizedFrameInfo(deoptimizer, frame_index, has_arguments_adaptor);
isolate->deoptimizer_data()->deoptimized_frame_info_ = info;
// Get the "simulated" top and size for the requested frame.
- Address top =
- reinterpret_cast<Address>(deoptimizer->output_[frame_index]->GetTop());
- uint32_t size = deoptimizer->output_[frame_index]->GetFrameSize();
+ FrameDescription* parameters_frame =
+ deoptimizer->output_[
+ has_arguments_adaptor ? (frame_index - 1) : frame_index];
+
+ uint32_t parameters_size = (info->parameters_count() + 1) * kPointerSize;
+ Address parameters_top = reinterpret_cast<Address>(
+ parameters_frame->GetTop() + (parameters_frame->GetFrameSize() -
+ parameters_size));
+
+ uint32_t expressions_size = info->expression_count() * kPointerSize;
+ Address expressions_top = reinterpret_cast<Address>(
+ deoptimizer->output_[frame_index]->GetTop());
// Done with the GC-unsafe frame descriptions. This re-enables allocation.
deoptimizer->DeleteFrameDescriptions();
// Allocate a heap number for the doubles belonging to this frame.
deoptimizer->MaterializeHeapNumbersForDebuggerInspectableFrame(
- top, size, info);
+ parameters_top, parameters_size, expressions_top, expressions_size, info);
// Finished using the deoptimizer instance.
delete deoptimizer;
fp_to_sp_delta_(fp_to_sp_delta),
input_(NULL),
output_count_(0),
+ jsframe_count_(0),
output_(NULL),
frame_alignment_marker_(isolate->heap()->frame_alignment_marker()),
has_alignment_padding_(0),
ASSERT(HEAP->allow_allocation(false));
unsigned size = ComputeInputFrameSize();
input_ = new(size) FrameDescription(size, function);
-#ifdef DEBUG
- input_->SetKind(Code::OPTIMIZED_FUNCTION);
-#endif
+ input_->SetFrameType(StackFrame::JAVA_SCRIPT);
}
// Read the number of output frames and allocate an array for their
// descriptions.
int count = iterator.Next();
+ iterator.Next(); // Drop JS frames count.
ASSERT(output_ == NULL);
output_ = new FrameDescription*[count];
for (int i = 0; i < count; ++i) {
// Translate each output frame.
for (int i = 0; i < count; ++i) {
- DoComputeFrame(&iterator, i);
+ // Read the ast node id, function, and frame height for this output frame.
+ Translation::Opcode opcode =
+ static_cast<Translation::Opcode>(iterator.Next());
+ switch (opcode) {
+ case Translation::JS_FRAME:
+ DoComputeJSFrame(&iterator, i);
+ jsframe_count_++;
+ break;
+ case Translation::ARGUMENTS_ADAPTOR_FRAME:
+ DoComputeArgumentsAdaptorFrame(&iterator, i);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
}
// Print some helpful diagnostic information.
#ifdef ENABLE_DEBUGGER_SUPPORT
void Deoptimizer::MaterializeHeapNumbersForDebuggerInspectableFrame(
- Address top, uint32_t size, DeoptimizedFrameInfo* info) {
+ Address parameters_top,
+ uint32_t parameters_size,
+ Address expressions_top,
+ uint32_t expressions_size,
+ DeoptimizedFrameInfo* info) {
ASSERT_EQ(DEBUGGER, bailout_type_);
+ Address parameters_bottom = parameters_top + parameters_size;
+ Address expressions_bottom = expressions_top + expressions_size;
for (int i = 0; i < deferred_heap_numbers_.length(); i++) {
HeapNumberMaterializationDescriptor d = deferred_heap_numbers_[i];
// Check of the heap number to materialize actually belong to the frame
// being extracted.
Address slot = d.slot_address();
- if (top <= slot && slot < top + size) {
+ if (parameters_top <= slot && slot < parameters_bottom) {
Handle<Object> num = isolate_->factory()->NewNumber(d.value());
- // Calculate the index with the botton of the expression stack
- // at index 0, and the fixed part (including incoming arguments)
- // at negative indexes.
- int index = static_cast<int>(
- info->expression_count_ - (slot - top) / kPointerSize - 1);
+
+ int index = (info->parameters_count() - 1) -
+ static_cast<int>(slot - parameters_top) / kPointerSize;
+
if (FLAG_trace_deopt) {
PrintF("Materializing a new heap number %p [%e] in slot %p"
- "for stack index %d\n",
+ "for parameter slot #%d\n",
reinterpret_cast<void*>(*num),
d.value(),
d.slot_address(),
index);
}
- if (index >=0) {
- info->SetExpression(index, *num);
- } else {
- // Calculate parameter index subtracting one for the receiver.
- int parameter_index =
- index +
- static_cast<int>(size) / kPointerSize -
- info->expression_count_ - 1;
- info->SetParameter(parameter_index, *num);
+
+ info->SetParameter(index, *num);
+ } else if (expressions_top <= slot && slot < expressions_bottom) {
+ Handle<Object> num = isolate_->factory()->NewNumber(d.value());
+
+ int index = info->expression_count() - 1 -
+ static_cast<int>(slot - expressions_top) / kPointerSize;
+
+ if (FLAG_trace_deopt) {
+ PrintF("Materializing a new heap number %p [%e] in slot %p"
+ "for expression slot #%d\n",
+ reinterpret_cast<void*>(*num),
+ d.value(),
+ d.slot_address(),
+ index);
}
+
+ info->SetExpression(index, *num);
}
}
}
switch (opcode) {
case Translation::BEGIN:
- case Translation::FRAME:
+ case Translation::JS_FRAME:
+ case Translation::ARGUMENTS_ADAPTOR_FRAME:
case Translation::DUPLICATE:
UNREACHABLE();
return;
case Translation::STACK_SLOT: {
int input_slot_index = iterator->Next();
unsigned input_offset =
- input_->GetOffsetFromSlotIndex(this, input_slot_index);
+ input_->GetOffsetFromSlotIndex(input_slot_index);
intptr_t input_value = input_->GetFrameSlot(input_offset);
if (FLAG_trace_deopt) {
PrintF(" 0x%08" V8PRIxPTR ": ",
case Translation::INT32_STACK_SLOT: {
int input_slot_index = iterator->Next();
unsigned input_offset =
- input_->GetOffsetFromSlotIndex(this, input_slot_index);
+ input_->GetOffsetFromSlotIndex(input_slot_index);
intptr_t value = input_->GetFrameSlot(input_offset);
bool is_smi = Smi::IsValid(value);
if (FLAG_trace_deopt) {
case Translation::DOUBLE_STACK_SLOT: {
int input_slot_index = iterator->Next();
unsigned input_offset =
- input_->GetOffsetFromSlotIndex(this, input_slot_index);
+ input_->GetOffsetFromSlotIndex(input_slot_index);
double value = input_->GetDoubleFrameSlot(input_offset);
if (FLAG_trace_deopt) {
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- %e ; [esp + %d]\n",
switch (opcode) {
case Translation::BEGIN:
- case Translation::FRAME:
+ case Translation::JS_FRAME:
+ case Translation::ARGUMENTS_ADAPTOR_FRAME:
case Translation::DUPLICATE:
UNREACHABLE(); // Malformed input.
return false;
case Translation::STACK_SLOT: {
int output_index = iterator->Next();
unsigned output_offset =
- output->GetOffsetFromSlotIndex(this, output_index);
+ output->GetOffsetFromSlotIndex(output_index);
if (FLAG_trace_osr) {
PrintF(" [sp + %d] <- 0x%08" V8PRIxPTR " ; [sp + %d] ",
output_offset,
int output_index = iterator->Next();
unsigned output_offset =
- output->GetOffsetFromSlotIndex(this, output_index);
+ output->GetOffsetFromSlotIndex(output_index);
int int32_value = input_object->IsSmi()
? Smi::cast(input_object)->value()
: DoubleToInt32(input_object->Number());
int output_index = iterator->Next();
unsigned output_offset =
- output->GetOffsetFromSlotIndex(this, output_index);
+ output->GetOffsetFromSlotIndex(output_index);
double double_value = input_object->Number();
uint64_t int_value = BitCast<uint64_t, double>(double_value);
int32_t lower = static_cast<int32_t>(int_value);
unsigned Deoptimizer::ComputeFixedSize(JSFunction* function) const {
// The fixed part of the frame consists of the return address, frame
// pointer, function, context, and all the incoming arguments.
- static const unsigned kFixedSlotSize = 4 * kPointerSize;
- return ComputeIncomingArgumentSize(function) + kFixedSlotSize;
+ return ComputeIncomingArgumentSize(function) +
+ StandardFrameConstants::kFixedFrameSize;
}
}
-unsigned FrameDescription::GetOffsetFromSlotIndex(Deoptimizer* deoptimizer,
- int slot_index) {
+int FrameDescription::ComputeFixedSize() {
+ return StandardFrameConstants::kFixedFrameSize +
+ (ComputeParametersCount() + 1) * kPointerSize;
+}
+
+
+unsigned FrameDescription::GetOffsetFromSlotIndex(int slot_index) {
if (slot_index >= 0) {
// Local or spill slots. Skip the fixed part of the frame
// including all arguments.
- unsigned base =
- GetFrameSize() - deoptimizer->ComputeFixedSize(GetFunction());
+ unsigned base = GetFrameSize() - ComputeFixedSize();
return base - ((slot_index + 1) * kPointerSize);
} else {
// Incoming parameter.
- unsigned base = GetFrameSize() -
- deoptimizer->ComputeIncomingArgumentSize(GetFunction());
+ int arg_size = (ComputeParametersCount() + 1) * kPointerSize;
+ unsigned base = GetFrameSize() - arg_size;
return base - ((slot_index + 1) * kPointerSize);
}
}
int FrameDescription::ComputeParametersCount() {
- return function_->shared()->formal_parameter_count();
+ switch (type_) {
+ case StackFrame::JAVA_SCRIPT:
+ return function_->shared()->formal_parameter_count();
+ case StackFrame::ARGUMENTS_ADAPTOR: {
+ // Last slot contains number of incomming arguments as a smi.
+ // Can't use GetExpression(0) because it would cause infinite recursion.
+ return reinterpret_cast<Smi*>(*GetFrameSlotPointer(0))->value();
+ }
+ default:
+ UNREACHABLE();
+ return 0;
+ }
}
-Object* FrameDescription::GetParameter(Deoptimizer* deoptimizer, int index) {
- ASSERT_EQ(Code::FUNCTION, kind_);
+Object* FrameDescription::GetParameter(int index) {
ASSERT(index >= 0);
ASSERT(index < ComputeParametersCount());
// The slot indexes for incoming arguments are negative.
- unsigned offset = GetOffsetFromSlotIndex(deoptimizer,
- index - ComputeParametersCount());
+ unsigned offset = GetOffsetFromSlotIndex(index - ComputeParametersCount());
return reinterpret_cast<Object*>(*GetFrameSlotPointer(offset));
}
-unsigned FrameDescription::GetExpressionCount(Deoptimizer* deoptimizer) {
- ASSERT_EQ(Code::FUNCTION, kind_);
- unsigned size = GetFrameSize() - deoptimizer->ComputeFixedSize(GetFunction());
+unsigned FrameDescription::GetExpressionCount() {
+ ASSERT_EQ(StackFrame::JAVA_SCRIPT, type_);
+ unsigned size = GetFrameSize() - ComputeFixedSize();
return size / kPointerSize;
}
-Object* FrameDescription::GetExpression(Deoptimizer* deoptimizer, int index) {
- ASSERT_EQ(Code::FUNCTION, kind_);
- unsigned offset = GetOffsetFromSlotIndex(deoptimizer, index);
+Object* FrameDescription::GetExpression(int index) {
+ ASSERT_EQ(StackFrame::JAVA_SCRIPT, type_);
+ unsigned offset = GetOffsetFromSlotIndex(index);
return reinterpret_cast<Object*>(*GetFrameSlotPointer(offset));
}
}
-void Translation::BeginFrame(int node_id, int literal_id, unsigned height) {
- buffer_->Add(FRAME);
+void Translation::BeginArgumentsAdaptorFrame(int literal_id, unsigned height) {
+ buffer_->Add(ARGUMENTS_ADAPTOR_FRAME);
+ buffer_->Add(literal_id);
+ buffer_->Add(height);
+}
+
+
+void Translation::BeginJSFrame(int node_id, int literal_id, unsigned height) {
+ buffer_->Add(JS_FRAME);
buffer_->Add(node_id);
buffer_->Add(literal_id);
buffer_->Add(height);
case ARGUMENTS_OBJECT:
case DUPLICATE:
return 0;
- case BEGIN:
case REGISTER:
case INT32_REGISTER:
case DOUBLE_REGISTER:
case DOUBLE_STACK_SLOT:
case LITERAL:
return 1;
- case FRAME:
+ case BEGIN:
+ case ARGUMENTS_ADAPTOR_FRAME:
+ return 2;
+ case JS_FRAME:
return 3;
}
UNREACHABLE();
switch (opcode) {
case BEGIN:
return "BEGIN";
- case FRAME:
- return "FRAME";
+ case JS_FRAME:
+ return "JS_FRAME";
+ case ARGUMENTS_ADAPTOR_FRAME:
+ return "ARGUMENTS_ADAPTOR_FRAME";
case REGISTER:
return "REGISTER";
case INT32_REGISTER:
switch (opcode) {
case Translation::BEGIN:
- case Translation::FRAME:
+ case Translation::JS_FRAME:
+ case Translation::ARGUMENTS_ADAPTOR_FRAME:
// Peeled off before getting here.
break;
}
-void SlotRef::ComputeSlotMappingForArguments(JavaScriptFrame* frame,
- int inlined_frame_index,
- Vector<SlotRef>* args_slots) {
+void SlotRef::ComputeSlotsForArguments(Vector<SlotRef>* args_slots,
+ TranslationIterator* it,
+ DeoptimizationInputData* data,
+ JavaScriptFrame* frame) {
+ // Process the translation commands for the arguments.
+
+ // Skip the translation command for the receiver.
+ it->Skip(Translation::NumberOfOperandsFor(
+ static_cast<Translation::Opcode>(it->Next())));
+
+ // Compute slots for arguments.
+ for (int i = 0; i < args_slots->length(); ++i) {
+ (*args_slots)[i] = ComputeSlotForNextArgument(it, data, frame);
+ }
+}
+
+
+Vector<SlotRef> SlotRef::ComputeSlotMappingForArguments(
+ JavaScriptFrame* frame,
+ int inlined_jsframe_index,
+ int formal_parameter_count) {
AssertNoAllocation no_gc;
int deopt_index = AstNode::kNoNumber;
DeoptimizationInputData* data =
data->TranslationIndex(deopt_index)->value());
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
ASSERT(opcode == Translation::BEGIN);
- int frame_count = it.Next();
- USE(frame_count);
- ASSERT(frame_count > inlined_frame_index);
- int frames_to_skip = inlined_frame_index;
+ it.Next(); // Drop frame count.
+ int jsframe_count = it.Next();
+ USE(jsframe_count);
+ ASSERT(jsframe_count > inlined_jsframe_index);
+ int jsframes_to_skip = inlined_jsframe_index;
while (true) {
opcode = static_cast<Translation::Opcode>(it.Next());
- // Skip over operands to advance to the next opcode.
- it.Skip(Translation::NumberOfOperandsFor(opcode));
- if (opcode == Translation::FRAME) {
- if (frames_to_skip == 0) {
+ if (opcode == Translation::ARGUMENTS_ADAPTOR_FRAME) {
+ if (jsframes_to_skip == 0) {
+ ASSERT(Translation::NumberOfOperandsFor(opcode) == 2);
+
+ it.Skip(1); // literal id
+ int height = it.Next();
+
+ // We reached the arguments adaptor frame corresponding to the
+ // inlined function in question. Number of arguments is height - 1.
+ Vector<SlotRef> args_slots =
+ Vector<SlotRef>::New(height - 1); // Minus receiver.
+ ComputeSlotsForArguments(&args_slots, &it, data, frame);
+ return args_slots;
+ }
+ } else if (opcode == Translation::JS_FRAME) {
+ if (jsframes_to_skip == 0) {
+ // Skip over operands to advance to the next opcode.
+ it.Skip(Translation::NumberOfOperandsFor(opcode));
+
// We reached the frame corresponding to the inlined function
// in question. Process the translation commands for the
- // arguments.
- //
- // Skip the translation command for the receiver.
- it.Skip(Translation::NumberOfOperandsFor(
- static_cast<Translation::Opcode>(it.Next())));
- // Compute slots for arguments.
- for (int i = 0; i < args_slots->length(); ++i) {
- (*args_slots)[i] = ComputeSlotForNextArgument(&it, data, frame);
- }
- return;
+ // arguments. Number of arguments is equal to the number of
+ // format parameter count.
+ Vector<SlotRef> args_slots =
+ Vector<SlotRef>::New(formal_parameter_count);
+ ComputeSlotsForArguments(&args_slots, &it, data, frame);
+ return args_slots;
}
- frames_to_skip--;
+ jsframes_to_skip--;
}
+
+ // Skip over operands to advance to the next opcode.
+ it.Skip(Translation::NumberOfOperandsFor(opcode));
}
UNREACHABLE();
+ return Vector<SlotRef>();
}
#ifdef ENABLE_DEBUGGER_SUPPORT
DeoptimizedFrameInfo::DeoptimizedFrameInfo(
- Deoptimizer* deoptimizer, int frame_index) {
+ Deoptimizer* deoptimizer, int frame_index, bool has_arguments_adaptor) {
FrameDescription* output_frame = deoptimizer->output_[frame_index];
SetFunction(output_frame->GetFunction());
- expression_count_ = output_frame->GetExpressionCount(deoptimizer);
+ expression_count_ = output_frame->GetExpressionCount();
+ expression_stack_ = new Object*[expression_count_];
+ for (int i = 0; i < expression_count_; i++) {
+ SetExpression(i, output_frame->GetExpression(i));
+ }
+
+ if (has_arguments_adaptor) {
+ output_frame = deoptimizer->output_[frame_index - 1];
+ ASSERT(output_frame->GetFrameType() == StackFrame::ARGUMENTS_ADAPTOR);
+ }
+
parameters_count_ = output_frame->ComputeParametersCount();
parameters_ = new Object*[parameters_count_];
for (int i = 0; i < parameters_count_; i++) {
- SetParameter(i, output_frame->GetParameter(deoptimizer, i));
- }
- expression_stack_ = new Object*[expression_count_];
- for (int i = 0; i < expression_count_; i++) {
- SetExpression(i, output_frame->GetExpression(deoptimizer, i));
+ SetParameter(i, output_frame->GetParameter(i));
}
}
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
int output_count() const { return output_count_; }
+ // Number of created JS frames. Not all created frames are necessarily JS.
+ int jsframe_count() const { return jsframe_count_; }
+
static Deoptimizer* New(JSFunction* function,
BailoutType type,
unsigned bailout_id,
// The returned object with information on the optimized frame needs to be
// freed before another one can be generated.
static DeoptimizedFrameInfo* DebuggerInspectableFrame(JavaScriptFrame* frame,
- int frame_index,
+ int jsframe_index,
Isolate* isolate);
static void DeleteDebuggerInspectableFrame(DeoptimizedFrameInfo* info,
Isolate* isolate);
void MaterializeHeapNumbers();
#ifdef ENABLE_DEBUGGER_SUPPORT
void MaterializeHeapNumbersForDebuggerInspectableFrame(
- Address top, uint32_t size, DeoptimizedFrameInfo* info);
+ Address parameters_top,
+ uint32_t parameters_size,
+ Address expressions_top,
+ uint32_t expressions_size,
+ DeoptimizedFrameInfo* info);
#endif
static void ComputeOutputFrames(Deoptimizer* deoptimizer);
int count_;
};
+ int ConvertJSFrameIndexToFrameIndex(int jsframe_index);
+
private:
- static const int kNumberOfEntries = 4096;
+ static const int kNumberOfEntries = 8192;
Deoptimizer(Isolate* isolate,
JSFunction* function,
void DoComputeOutputFrames();
void DoComputeOsrOutputFrame();
- void DoComputeFrame(TranslationIterator* iterator, int frame_index);
+ void DoComputeJSFrame(TranslationIterator* iterator, int frame_index);
+ void DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
+ int frame_index);
void DoTranslateCommand(TranslationIterator* iterator,
int frame_index,
unsigned output_offset);
FrameDescription* input_;
// Number of output frames.
int output_count_;
+ // Number of output js frames.
+ int jsframe_count_;
// Array of output frame descriptions.
FrameDescription** output_;
JSFunction* GetFunction() const { return function_; }
- unsigned GetOffsetFromSlotIndex(Deoptimizer* deoptimizer, int slot_index);
+ unsigned GetOffsetFromSlotIndex(int slot_index);
intptr_t GetFrameSlot(unsigned offset) {
return *GetFrameSlotPointer(offset);
void SetContinuation(intptr_t pc) { continuation_ = pc; }
-#ifdef DEBUG
- Code::Kind GetKind() const { return kind_; }
- void SetKind(Code::Kind kind) { kind_ = kind; }
-#endif
+ StackFrame::Type GetFrameType() const { return type_; }
+ void SetFrameType(StackFrame::Type type) { type_ = type; }
// Get the incoming arguments count.
int ComputeParametersCount();
// Get a parameter value for an unoptimized frame.
- Object* GetParameter(Deoptimizer* deoptimizer, int index);
+ Object* GetParameter(int index);
// Get the expression stack height for a unoptimized frame.
- unsigned GetExpressionCount(Deoptimizer* deoptimizer);
+ unsigned GetExpressionCount();
// Get the expression stack value for an unoptimized frame.
- Object* GetExpression(Deoptimizer* deoptimizer, int index);
+ Object* GetExpression(int index);
static int registers_offset() {
return OFFSET_OF(FrameDescription, registers_);
intptr_t top_;
intptr_t pc_;
intptr_t fp_;
+ StackFrame::Type type_;
Smi* state_;
#ifdef DEBUG
Code::Kind kind_;
return reinterpret_cast<intptr_t*>(
reinterpret_cast<Address>(this) + frame_content_offset() + offset);
}
+
+ int ComputeFixedSize();
};
public:
enum Opcode {
BEGIN,
- FRAME,
+ JS_FRAME,
+ ARGUMENTS_ADAPTOR_FRAME,
REGISTER,
INT32_REGISTER,
DOUBLE_REGISTER,
DUPLICATE
};
- Translation(TranslationBuffer* buffer, int frame_count)
+ Translation(TranslationBuffer* buffer, int frame_count, int jsframe_count)
: buffer_(buffer),
index_(buffer->CurrentIndex()) {
buffer_->Add(BEGIN);
buffer_->Add(frame_count);
+ buffer_->Add(jsframe_count);
}
int index() const { return index_; }
// Commands.
- void BeginFrame(int node_id, int literal_id, unsigned height);
+ void BeginJSFrame(int node_id, int literal_id, unsigned height);
+ void BeginArgumentsAdaptorFrame(int literal_id, unsigned height);
void StoreRegister(Register reg);
void StoreInt32Register(Register reg);
void StoreDoubleRegister(DoubleRegister reg);
}
}
- static void ComputeSlotMappingForArguments(JavaScriptFrame* frame,
- int inlined_frame_index,
- Vector<SlotRef>* args_slots);
+ static Vector<SlotRef> ComputeSlotMappingForArguments(
+ JavaScriptFrame* frame,
+ int inlined_frame_index,
+ int formal_parameter_count);
private:
Address addr_;
static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator,
DeoptimizationInputData* data,
JavaScriptFrame* frame);
+
+ static void ComputeSlotsForArguments(
+ Vector<SlotRef>* args_slots,
+ TranslationIterator* iterator,
+ DeoptimizationInputData* data,
+ JavaScriptFrame* frame);
};
// needs to inspect a frame that is part of an optimized frame. The
// internally used FrameDescription objects are not GC safe so for use
// by the debugger frame information is copied to an object of this type.
+// Represents parameters in unadapted form so their number might mismatch
+// formal parameter count.
class DeoptimizedFrameInfo : public Malloced {
public:
- DeoptimizedFrameInfo(Deoptimizer* deoptimizer, int frame_index);
+ DeoptimizedFrameInfo(Deoptimizer* deoptimizer,
+ int frame_index,
+ bool has_arguments_adaptor);
virtual ~DeoptimizedFrameInfo();
// GC support.
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
data->TranslationIndex(deopt_index)->value());
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
ASSERT(opcode == Translation::BEGIN);
- int frame_count = it.Next();
+ it.Next(); // Drop frame count.
+ int jsframe_count = it.Next();
// We create the summary in reverse order because the frames
// in the deoptimization translation are ordered bottom-to-top.
- int i = frame_count;
+ int i = jsframe_count;
while (i > 0) {
opcode = static_cast<Translation::Opcode>(it.Next());
- if (opcode == Translation::FRAME) {
+ if (opcode == Translation::JS_FRAME) {
// We don't inline constructor calls, so only the first, outermost
// frame can be a constructor frame in case of inlining.
- bool is_constructor = (i == frame_count) && IsConstructor();
+ bool is_constructor = (i == jsframe_count) && IsConstructor();
i--;
int ast_id = it.Next();
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
ASSERT(opcode == Translation::BEGIN);
USE(opcode);
- int frame_count = it.Next();
- return frame_count;
+ it.Next(); // Drop frame count.
+ int jsframe_count = it.Next();
+ return jsframe_count;
}
data->TranslationIndex(deopt_index)->value());
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
ASSERT(opcode == Translation::BEGIN);
- int frame_count = it.Next();
+ it.Next(); // Drop frame count.
+ int jsframe_count = it.Next();
// We insert the frames in reverse order because the frames
// in the deoptimization translation are ordered bottom-to-top.
- while (frame_count > 0) {
+ while (jsframe_count > 0) {
opcode = static_cast<Translation::Opcode>(it.Next());
- if (opcode == Translation::FRAME) {
- frame_count--;
+ if (opcode == Translation::JS_FRAME) {
+ jsframe_count--;
it.Next(); // Skip ast id.
int function_id = it.Next();
it.Next(); // Skip height.
StateField::encode(state) | PcField::encode(masm_->pc_offset());
BailoutEntry entry = { id, pc_and_state };
#ifdef DEBUG
- // Assert that we don't have multiple bailout entries for the same node.
- for (int i = 0; i < bailout_entries_.length(); i++) {
- if (bailout_entries_.at(i).id == entry.id) {
- AstPrinter printer;
- PrintF("%s", printer.PrintProgram(info_->function()));
- UNREACHABLE();
+ if (FLAG_enable_slow_asserts) {
+ // Assert that we don't have multiple bailout entries for the same node.
+ for (int i = 0; i < bailout_entries_.length(); i++) {
+ if (bailout_entries_.at(i).id == entry.id) {
+ AstPrinter printer;
+ PrintF("%s", printer.PrintProgram(info_->function()));
+ UNREACHABLE();
+ }
}
}
#endif // DEBUG
#define GC_GREEDY_CHECK() { }
#endif
-
// Calls the FUNCTION_CALL function and retries it up to three times
// to guarantee that any allocations performed during the call will
// succeed if there's enough memory.
(static_cast<double>(young_survivors_after_last_gc_) * 100) /
start_new_space_size;
- if (survival_rate > kYoungSurvivalRateThreshold) {
+ if (survival_rate > kYoungSurvivalRateHighThreshold) {
high_survival_rate_period_length_++;
} else {
high_survival_rate_period_length_ = 0;
}
+ if (survival_rate < kYoungSurvivalRateLowThreshold) {
+ low_survival_rate_period_length_++;
+ } else {
+ low_survival_rate_period_length_ = 0;
+ }
+
double survival_rate_diff = survival_rate_ - survival_rate;
if (survival_rate_diff > kYoungSurvivalRateAllowedDeviation) {
UpdateSurvivalRateTrend(start_new_space_size);
- if (!new_space_high_promotion_mode_active_ &&
- new_space_.Capacity() == new_space_.MaximumCapacity() &&
- IsStableOrIncreasingSurvivalTrend() &&
- IsHighSurvivalRate()) {
- // Stable high survival rates even though young generation is at
- // maximum capacity indicates that most objects will be promoted.
- // To decrease scavenger pauses and final mark-sweep pauses, we
- // have to limit maximal capacity of the young generation.
- new_space_high_promotion_mode_active_ = true;
- if (FLAG_trace_gc) {
- PrintF("Limited new space size due to high promotion rate: %d MB\n",
- new_space_.InitialCapacity() / MB);
- }
- } else if (new_space_high_promotion_mode_active_ &&
- IsDecreasingSurvivalTrend() &&
- !IsHighSurvivalRate()) {
- // Decreasing low survival rates might indicate that the above high
- // promotion mode is over and we should allow the young generation
- // to grow again.
- new_space_high_promotion_mode_active_ = false;
- if (FLAG_trace_gc) {
- PrintF("Unlimited new space size due to low promotion rate: %d MB\n",
- new_space_.MaximumCapacity() / MB);
- }
- }
-
size_of_old_gen_at_last_old_space_gc_ = PromotedSpaceSize();
if (high_survival_rate_during_scavenges &&
UpdateSurvivalRateTrend(start_new_space_size);
}
+ if (!new_space_high_promotion_mode_active_ &&
+ new_space_.Capacity() == new_space_.MaximumCapacity() &&
+ IsStableOrIncreasingSurvivalTrend() &&
+ IsHighSurvivalRate()) {
+ // Stable high survival rates even though young generation is at
+ // maximum capacity indicates that most objects will be promoted.
+ // To decrease scavenger pauses and final mark-sweep pauses, we
+ // have to limit maximal capacity of the young generation.
+ new_space_high_promotion_mode_active_ = true;
+ if (FLAG_trace_gc) {
+ PrintF("Limited new space size due to high promotion rate: %d MB\n",
+ new_space_.InitialCapacity() / MB);
+ }
+ } else if (new_space_high_promotion_mode_active_ &&
+ IsStableOrDecreasingSurvivalTrend() &&
+ IsLowSurvivalRate()) {
+ // Decreasing low survival rates might indicate that the above high
+ // promotion mode is over and we should allow the young generation
+ // to grow again.
+ new_space_high_promotion_mode_active_ = false;
+ if (FLAG_trace_gc) {
+ PrintF("Unlimited new space size due to low promotion rate: %d MB\n",
+ new_space_.MaximumCapacity() / MB);
+ }
+ }
+
if (new_space_high_promotion_mode_active_ &&
new_space_.Capacity() > new_space_.InitialCapacity()) {
new_space_.Shrink();
isolate_->descriptor_lookup_cache()->Clear();
// Used for updating survived_since_last_expansion_ at function end.
- intptr_t survived_watermark = PromotedSpaceSize();
+ intptr_t survived_watermark = PromotedSpaceSizeOfObjects();
CheckNewSpaceExpansionCriteria();
// Update how much has survived scavenge.
IncrementYoungSurvivorsCounter(static_cast<int>(
- (PromotedSpaceSize() - survived_watermark) + new_space_.Size()));
+ (PromotedSpaceSizeOfObjects() - survived_watermark) + new_space_.Size()));
LOG(isolate_, ResourceEvent("scavenge", "end"));
}
code->set_deoptimization_data(empty_fixed_array(), SKIP_WRITE_BARRIER);
code->set_handler_table(empty_fixed_array(), SKIP_WRITE_BARRIER);
- code->set_next_code_flushing_candidate(undefined_value());
+ code->set_gc_metadata(Smi::FromInt(0));
// Allow self references to created code object by patching the handle to
// point to the newly allocated Code object.
if (!self_reference.is_null()) {
}
+intptr_t Heap::PromotedSpaceSizeOfObjects() {
+ return old_pointer_space_->SizeOfObjects()
+ + old_data_space_->SizeOfObjects()
+ + code_space_->SizeOfObjects()
+ + map_space_->SizeOfObjects()
+ + cell_space_->SizeOfObjects()
+ + lo_space_->SizeOfObjects();
+}
+
+
int Heap::PromotedExternalMemorySize() {
if (amount_of_external_allocated_memory_
<= amount_of_external_allocated_memory_at_last_global_gc_) return 0;
int KeyedLookupCache::Lookup(Map* map, String* name) {
int index = (Hash(map, name) & kHashMask);
- Key& key = keys_[index];
- if ((key.map == map) && key.name->Equals(name)) {
- return field_offsets_[index];
- }
- ASSERT(kEntriesPerBucket == 2); // There are two entries to check.
- // First entry in the bucket missed, check the second.
- Key& key2 = keys_[index + 1];
- if ((key2.map == map) && key2.name->Equals(name)) {
- return field_offsets_[index + 1];
+ for (int i = 0; i < kEntriesPerBucket; i++) {
+ Key& key = keys_[index + i];
+ if ((key.map == map) && key.name->Equals(name)) {
+ return field_offsets_[index + i];
+ }
}
return kNotFound;
}
String* symbol;
if (HEAP->LookupSymbolIfExists(name, &symbol)) {
int index = (Hash(map, symbol) & kHashMask);
- Key& key = keys_[index];
- Key& key2 = keys_[index + 1]; // Second entry in the bucket.
- // Demote the first entry to the second in the bucket.
- key2.map = key.map;
- key2.name = key.name;
- field_offsets_[index + 1] = field_offsets_[index];
+ // After a GC there will be free slots, so we use them in order (this may
+ // help to get the most frequently used one in position 0).
+ for (int i = 0; i< kEntriesPerBucket; i++) {
+ Key& key = keys_[index];
+ Object* free_entry_indicator = NULL;
+ if (key.map == free_entry_indicator) {
+ key.map = map;
+ key.name = symbol;
+ field_offsets_[index + i] = field_offset;
+ return;
+ }
+ }
+ // No free entry found in this bucket, so we move them all down one and
+ // put the new entry at position zero.
+ for (int i = kEntriesPerBucket - 1; i > 0; i--) {
+ Key& key = keys_[index + i];
+ Key& key2 = keys_[index + i - 1];
+ key = key2;
+ field_offsets_[index + i] = field_offsets_[index + i - 1];
+ }
+
// Write the new first entry.
+ Key& key = keys_[index];
key.map = map;
key.name = symbol;
field_offsets_[index] = field_offset;
V(Script, empty_script, EmptyScript) \
V(Smi, real_stack_limit, RealStackLimit) \
V(StringDictionary, intrinsic_function_names, IntrinsicFunctionNames) \
+ V(Smi, arguments_adaptor_deopt_pc_offset, ArgumentsAdaptorDeoptPCOffset)
#define ROOT_LIST(V) \
STRONG_ROOT_LIST(V) \
// Heap root getters. We have versions with and without type::cast() here.
// You can't use type::cast during GC because the assert fails.
// TODO(1490): Try removing the unchecked accessors, now that GC marking does
- // not corrupt the stack.
+ // not corrupt the map.
#define ROOT_ACCESSOR(type, name, camel_name) \
type* name() { \
return type::cast(roots_[k##camel_name##RootIndex]); \
void CheckNewSpaceExpansionCriteria();
inline void IncrementYoungSurvivorsCounter(int survived) {
+ ASSERT(survived >= 0);
young_survivors_after_last_gc_ = survived;
survived_since_last_expansion_ += survived;
}
// Returns the size of objects residing in non new spaces.
intptr_t PromotedSpaceSize();
+ intptr_t PromotedSpaceSizeOfObjects();
double total_regexp_code_generated() { return total_regexp_code_generated_; }
void IncreaseTotalRegexpCodeGenerated(int size) {
return seed;
}
+ void SetArgumentsAdaptorDeoptPCOffset(int pc_offset) {
+ ASSERT(arguments_adaptor_deopt_pc_offset() == Smi::FromInt(0));
+ set_arguments_adaptor_deopt_pc_offset(Smi::FromInt(pc_offset));
+ }
+
private:
Heap();
enum SurvivalRateTrend { INCREASING, STABLE, DECREASING, FLUCTUATING };
- static const int kYoungSurvivalRateThreshold = 90;
+ static const int kYoungSurvivalRateHighThreshold = 90;
+ static const int kYoungSurvivalRateLowThreshold = 10;
static const int kYoungSurvivalRateAllowedDeviation = 15;
int young_survivors_after_last_gc_;
int high_survival_rate_period_length_;
+ int low_survival_rate_period_length_;
double survival_rate_;
SurvivalRateTrend previous_survival_rate_trend_;
SurvivalRateTrend survival_rate_trend_;
}
}
- bool IsIncreasingSurvivalTrend() {
- return survival_rate_trend() == INCREASING;
+ bool IsStableOrDecreasingSurvivalTrend() {
+ switch (survival_rate_trend()) {
+ case STABLE:
+ case DECREASING:
+ return true;
+ default:
+ return false;
+ }
}
- bool IsDecreasingSurvivalTrend() {
- return survival_rate_trend() == DECREASING;
+ bool IsIncreasingSurvivalTrend() {
+ return survival_rate_trend() == INCREASING;
}
bool IsHighSurvivalRate() {
return high_survival_rate_period_length_ > 0;
}
+ bool IsLowSurvivalRate() {
+ return low_survival_rate_period_length_ > 0;
+ }
+
void SelectScavengingVisitorsTable();
void StartIdleRound() {
// Clear the cache.
void Clear();
- static const int kLength = 128;
+ static const int kLength = 256;
static const int kCapacityMask = kLength - 1;
static const int kMapHashShift = 5;
- static const int kHashMask = -2; // Zero the last bit.
- static const int kEntriesPerBucket = 2;
+ static const int kHashMask = -4; // Zero the last two bits.
+ static const int kEntriesPerBucket = 4;
static const int kNotFound = -1;
+ // kEntriesPerBucket should be a power of 2.
+ STATIC_ASSERT((kEntriesPerBucket & (kEntriesPerBucket - 1)) == 0);
+ STATIC_ASSERT(kEntriesPerBucket == -kHashMask);
+
private:
KeyedLookupCache() {
for (int i = 0; i < kLength; ++i) {
}
+Range* HLoadKeyedSpecializedArrayElement::InferRange() {
+ switch (elements_kind()) {
+ case EXTERNAL_PIXEL_ELEMENTS:
+ return new Range(0, 255);
+ case EXTERNAL_BYTE_ELEMENTS:
+ return new Range(-128, 127);
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ return new Range(0, 255);
+ case EXTERNAL_SHORT_ELEMENTS:
+ return new Range(-32768, 32767);
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ return new Range(0, 65535);
+ default:
+ return HValue::InferRange();
+ }
+}
+
void HCompareGeneric::PrintDataTo(StringStream* stream) {
stream->Add(Token::Name(token()));
int flags_;
GVNFlagSet gvn_flags_;
+ private:
DISALLOW_COPY_AND_ASSIGN(HValue);
};
class HEnterInlined: public HTemplateInstruction<0> {
public:
HEnterInlined(Handle<JSFunction> closure,
+ int arguments_count,
FunctionLiteral* function,
CallKind call_kind)
: closure_(closure),
+ arguments_count_(arguments_count),
function_(function),
call_kind_(call_kind) {
}
virtual void PrintDataTo(StringStream* stream);
Handle<JSFunction> closure() const { return closure_; }
+ int arguments_count() const { return arguments_count_; }
FunctionLiteral* function() const { return function_; }
CallKind call_kind() const { return call_kind_; }
private:
Handle<JSFunction> closure_;
+ int arguments_count_;
FunctionLiteral* function_;
CallKind call_kind_;
};
HValue* key() { return OperandAt(1); }
ElementsKind elements_kind() const { return elements_kind_; }
+ virtual Range* InferRange();
+
DECLARE_CONCRETE_INSTRUCTION(LoadKeyedSpecializedArrayElement)
protected:
void HBasicBlock::Goto(HBasicBlock* block, bool drop_extra) {
if (block->IsInlineReturnTarget()) {
AddInstruction(new(zone()) HLeaveInlined);
- last_environment_ = last_environment()->outer();
- if (drop_extra) last_environment_->Drop(1);
+ last_environment_ = last_environment()->DiscardInlined(drop_extra);
}
AddSimulate(AstNode::kNoNumber);
HGoto* instr = new(zone()) HGoto(block);
ASSERT(target->IsInlineReturnTarget());
ASSERT(return_value != NULL);
AddInstruction(new(zone()) HLeaveInlined);
- last_environment_ = last_environment()->outer();
- if (drop_extra) last_environment_->Drop(1);
+ last_environment_ = last_environment()->DiscardInlined(drop_extra);
last_environment()->Push(return_value);
AddSimulate(AstNode::kNoNumber);
HGoto* instr = new(zone()) HGoto(target);
for_typeof_(false) {
owner->set_ast_context(this); // Push.
#ifdef DEBUG
+ ASSERT(!owner->environment()->is_arguments_adaptor());
original_length_ = owner->environment()->length();
#endif
}
EffectContext::~EffectContext() {
ASSERT(owner()->HasStackOverflow() ||
owner()->current_block() == NULL ||
- owner()->environment()->length() == original_length_);
+ (owner()->environment()->length() == original_length_ &&
+ !owner()->environment()->is_arguments_adaptor()));
}
ValueContext::~ValueContext() {
ASSERT(owner()->HasStackOverflow() ||
owner()->current_block() == NULL ||
- owner()->environment()->length() == original_length_ + 1);
+ (owner()->environment()->length() == original_length_ + 1 &&
+ !owner()->environment()->is_arguments_adaptor()));
}
TraceInline(target, caller, "inline depth limit reached");
return false;
}
- current_level++;
+ if (!env->outer()->is_arguments_adaptor()) {
+ current_level++;
+ }
env = env->outer();
}
return false;
}
- // Don't inline functions that uses the arguments object or that
- // have a mismatching number of parameters.
- int arity = expr->arguments()->length();
- if (function->scope()->arguments() != NULL ||
- arity != target_shared->formal_parameter_count()) {
+ // Don't inline functions that uses the arguments object.
+ if (function->scope()->arguments() != NULL) {
TraceInline(target, caller, "target requires special argument handling");
return false;
}
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner_env =
environment()->CopyForInlining(target,
+ expr->arguments()->length(),
function,
undefined,
call_kind);
body_entry->SetJoinId(expr->ReturnId());
set_current_block(body_entry);
AddInstruction(new(zone()) HEnterInlined(target,
+ expr->arguments()->length(),
function,
call_kind));
VisitDeclarations(target_info.scope()->declarations());
outer_(outer),
pop_count_(0),
push_count_(0),
- ast_id_(AstNode::kNoNumber) {
+ ast_id_(AstNode::kNoNumber),
+ arguments_adaptor_(false) {
Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
}
outer_(NULL),
pop_count_(0),
push_count_(0),
- ast_id_(other->ast_id()) {
+ ast_id_(other->ast_id()),
+ arguments_adaptor_(false) {
Initialize(other);
}
+HEnvironment::HEnvironment(HEnvironment* outer,
+ Handle<JSFunction> closure,
+ int arguments)
+ : closure_(closure),
+ values_(arguments),
+ assigned_variables_(0),
+ parameter_count_(arguments),
+ local_count_(0),
+ outer_(outer),
+ pop_count_(0),
+ push_count_(0),
+ ast_id_(AstNode::kNoNumber),
+ arguments_adaptor_(true) {
+}
+
+
void HEnvironment::Initialize(int parameter_count,
int local_count,
int stack_height) {
pop_count_ = other->pop_count_;
push_count_ = other->push_count_;
ast_id_ = other->ast_id_;
+ arguments_adaptor_ = other->arguments_adaptor_;
}
HEnvironment* HEnvironment::CopyForInlining(
Handle<JSFunction> target,
+ int arguments,
FunctionLiteral* function,
HConstant* undefined,
CallKind call_kind) const {
+ ASSERT(!is_arguments_adaptor());
+
+ Zone* zone = closure()->GetIsolate()->zone();
+
// Outer environment is a copy of this one without the arguments.
int arity = function->scope()->num_parameters();
+
HEnvironment* outer = Copy();
- outer->Drop(arity + 1); // Including receiver.
+ outer->Drop(arguments + 1); // Including receiver.
outer->ClearHistory();
- Zone* zone = closure()->GetIsolate()->zone();
+
+ if (arity != arguments) {
+ // Create artificial arguments adaptation environment.
+ outer = new(zone) HEnvironment(outer, target, arguments + 1);
+ for (int i = 0; i <= arguments; ++i) { // Include receiver.
+ outer->Push(ExpressionStackAt(arguments - i));
+ }
+ outer->ClearHistory();
+ }
+
HEnvironment* inner =
new(zone) HEnvironment(outer, function->scope(), target);
// Get the argument values from the original environment.
for (int i = 0; i <= arity; ++i) { // Include receiver.
- HValue* push = ExpressionStackAt(arity - i);
+ HValue* push = (i <= arguments) ?
+ ExpressionStackAt(arguments - i) : undefined;
inner->SetValueAt(i, push);
}
// If the function we are inlining is a strict mode function or a
call_kind == CALL_AS_FUNCTION) {
inner->SetValueAt(0, undefined);
}
- inner->SetValueAt(arity + 1, outer->LookupContext());
+ inner->SetValueAt(arity + 1, LookupContext());
for (int i = arity + 2; i < inner->length(); ++i) {
inner->SetValueAt(i, undefined);
}
if (i == parameter_count()) stream->Add("specials\n");
if (i == parameter_count() + specials_count()) stream->Add("locals\n");
if (i == parameter_count() + specials_count() + local_count()) {
- stream->Add("expressions");
+ stream->Add("expressions\n");
}
HValue* val = values_.at(i);
stream->Add("%d: ", i);
}
stream->Add("\n");
}
+ PrintF("\n");
}
Scope* scope,
Handle<JSFunction> closure);
+ bool is_arguments_adaptor() const {
+ return arguments_adaptor_;
+ }
+
+ HEnvironment* DiscardInlined(bool drop_extra) {
+ HEnvironment* outer = outer_->is_arguments_adaptor() ?
+ outer_->outer_ : outer_;
+ if (drop_extra) outer->Drop(1);
+ return outer;
+ }
+
// Simple accessors.
Handle<JSFunction> closure() const { return closure_; }
const ZoneList<HValue*>* values() const { return &values_; }
// environment is the outer environment but the top expression stack
// elements are moved to an inner environment as parameters.
HEnvironment* CopyForInlining(Handle<JSFunction> target,
+ int arguments,
FunctionLiteral* function,
HConstant* undefined,
CallKind call_kind) const;
private:
explicit HEnvironment(const HEnvironment* other);
+ // Create an argument adaptor environment.
+ HEnvironment(HEnvironment* outer, Handle<JSFunction> closure, int arguments);
+
+
// True if index is included in the expression stack part of the environment.
bool HasExpressionAt(int index) const;
int pop_count_;
int push_count_;
int ast_id_;
+ bool arguments_adaptor_;
};
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// A light-weight IA32 Assembler.
#ifndef V8_IA32_ASSEMBLER_IA32_INL_H_
#define V8_IA32_ASSEMBLER_IA32_INL_H_
+#include "ia32/assembler-ia32.h"
+
#include "cpu.h"
#include "debug.h"
{
FrameScope scope(masm, StackFrame::INTERNAL);
- // Pass the function and deoptimization type to the runtime system.
+ // Pass deoptimization type to the runtime system.
__ push(Immediate(Smi::FromInt(static_cast<int>(type))));
__ CallRuntime(Runtime::kNotifyDeoptimized, 1);
__ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
__ call(edx);
+ masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
// Leave frame and return.
LeaveArgumentsAdaptorFrame(masm);
__ ret(0);
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
ASSERT(Translation::BEGIN == opcode);
USE(opcode);
int count = iterator.Next();
+ iterator.Next(); // Drop JS frames count.
ASSERT(count == 1);
USE(count);
opcode = static_cast<Translation::Opcode>(iterator.Next());
USE(opcode);
- ASSERT(Translation::FRAME == opcode);
+ ASSERT(Translation::JS_FRAME == opcode);
unsigned node_id = iterator.Next();
USE(node_id);
ASSERT(node_id == ast_id);
output_ = new FrameDescription*[1];
output_[0] = new(output_frame_size) FrameDescription(
output_frame_size, function_);
-#ifdef DEBUG
- output_[0]->SetKind(Code::OPTIMIZED_FUNCTION);
-#endif
+ output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT);
// Clear the incoming parameters in the optimized frame to avoid
// confusing the garbage collector.
}
-void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
- int frame_index) {
- // Read the ast node id, function, and frame height for this output frame.
- Translation::Opcode opcode =
- static_cast<Translation::Opcode>(iterator->Next());
- USE(opcode);
- ASSERT(Translation::FRAME == opcode);
+void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
+ int frame_index) {
+ JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
+ unsigned height = iterator->Next();
+ unsigned height_in_bytes = height * kPointerSize;
+ if (FLAG_trace_deopt) {
+ PrintF(" translating arguments adaptor => height=%d\n", height_in_bytes);
+ }
+
+ unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize;
+ unsigned input_frame_size = input_->GetFrameSize();
+ unsigned output_frame_size = height_in_bytes + fixed_frame_size;
+
+ // Allocate and store the output frame description.
+ FrameDescription* output_frame =
+ new(output_frame_size) FrameDescription(output_frame_size, function);
+ output_frame->SetFrameType(StackFrame::ARGUMENTS_ADAPTOR);
+
+ // Arguments adaptor can not be topmost or bottommost.
+ ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
+ ASSERT(output_[frame_index] == NULL);
+ output_[frame_index] = output_frame;
+
+ // The top address of the frame is computed from the previous
+ // frame's top and this frame's size.
+ uint32_t top_address;
+ top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
+ output_frame->SetTop(top_address);
+
+ // Compute the incoming parameter translation.
+ int parameter_count = height;
+ unsigned output_offset = output_frame_size;
+ unsigned input_offset = input_frame_size;
+ for (int i = 0; i < parameter_count; ++i) {
+ output_offset -= kPointerSize;
+ DoTranslateCommand(iterator, frame_index, output_offset);
+ }
+ input_offset -= (parameter_count * kPointerSize);
+
+ // Read caller's PC from the previous frame.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ intptr_t callers_pc = output_[frame_index - 1]->GetPc();
+ output_frame->SetFrameSlot(output_offset, callers_pc);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's pc\n",
+ top_address + output_offset, output_offset, callers_pc);
+ }
+
+ // Read caller's FP from the previous frame, and set this frame's FP.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ intptr_t value = output_[frame_index - 1]->GetFp();
+ output_frame->SetFrameSlot(output_offset, value);
+ intptr_t fp_value = top_address + output_offset;
+ output_frame->SetFp(fp_value);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's fp\n",
+ fp_value, output_offset, value);
+ }
+
+ // A marker value is used in place of the context.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ intptr_t context = reinterpret_cast<intptr_t>(
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+ output_frame->SetFrameSlot(output_offset, context);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context (adaptor sentinel)\n",
+ top_address + output_offset, output_offset, context);
+ }
+
+ // The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ value = reinterpret_cast<intptr_t>(function);
+ output_frame->SetFrameSlot(output_offset, value);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function\n",
+ top_address + output_offset, output_offset, value);
+ }
+
+ // Number of incoming arguments.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ value = reinterpret_cast<uint32_t>(Smi::FromInt(height - 1));
+ output_frame->SetFrameSlot(output_offset, value);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08x: [top + %d] <- 0x%08x ; argc (%d)\n",
+ top_address + output_offset, output_offset, value, height - 1);
+ }
+
+ ASSERT(0 == output_offset);
+
+ Builtins* builtins = isolate_->builtins();
+ Code* adaptor_trampoline =
+ builtins->builtin(Builtins::kArgumentsAdaptorTrampoline);
+ uint32_t pc = reinterpret_cast<uint32_t>(
+ adaptor_trampoline->instruction_start() +
+ isolate_->heap()->arguments_adaptor_deopt_pc_offset()->value());
+ output_frame->SetPc(pc);
+}
+
+
+void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
+ int frame_index) {
int node_id = iterator->Next();
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
unsigned height = iterator->Next();
// Allocate and store the output frame description.
FrameDescription* output_frame =
new(output_frame_size) FrameDescription(output_frame_size, function);
-#ifdef DEBUG
- output_frame->SetKind(Code::FUNCTION);
-#endif
+ output_frame->SetFrameType(StackFrame::JAVA_SCRIPT);
bool is_bottommost = (0 == frame_index);
bool is_topmost = (output_count_ - 1 == frame_index);
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
class StandardFrameConstants : public AllStatic {
public:
+ // Fixed part of the frame consists of return address, caller fp,
+ // context and function.
// StandardFrame::IterateExpressions assumes that kContextOffset is the last
// object pointer.
- static const int kFixedFrameSize = 4; // Currently unused.
+ static const int kFixedFrameSize = 4 * kPointerSize;
static const int kExpressionsOffset = -3 * kPointerSize;
static const int kMarkerOffset = -2 * kPointerSize;
static const int kContextOffset = -1 * kPointerSize;
class ArgumentsAdaptorFrameConstants : public AllStatic {
public:
static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + kPointerSize;
};
// Load the key (consisting of map and symbol) from the cache and
// check for match.
- Label try_second_entry, hit_on_first_entry, load_in_object_property;
+ Label load_in_object_property;
+ static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket;
+ Label hit_on_nth_entry[kEntriesPerBucket];
ExternalReference cache_keys =
ExternalReference::keyed_lookup_cache_keys(masm->isolate());
- __ mov(edi, ecx);
- __ shl(edi, kPointerSizeLog2 + 1);
- __ cmp(ebx, Operand::StaticArray(edi, times_1, cache_keys));
- __ j(not_equal, &try_second_entry);
- __ add(edi, Immediate(kPointerSize));
- __ cmp(eax, Operand::StaticArray(edi, times_1, cache_keys));
- __ j(equal, &hit_on_first_entry);
- __ bind(&try_second_entry);
+ for (int i = 0; i < kEntriesPerBucket - 1; i++) {
+ Label try_next_entry;
+ __ mov(edi, ecx);
+ __ shl(edi, kPointerSizeLog2 + 1);
+ if (i != 0) {
+ __ add(edi, Immediate(kPointerSize * i * 2));
+ }
+ __ cmp(ebx, Operand::StaticArray(edi, times_1, cache_keys));
+ __ j(not_equal, &try_next_entry);
+ __ add(edi, Immediate(kPointerSize));
+ __ cmp(eax, Operand::StaticArray(edi, times_1, cache_keys));
+ __ j(equal, &hit_on_nth_entry[i]);
+ __ bind(&try_next_entry);
+ }
+
__ lea(edi, Operand(ecx, 1));
__ shl(edi, kPointerSizeLog2 + 1);
+ __ add(edi, Immediate(kPointerSize * (kEntriesPerBucket - 1) * 2));
__ cmp(ebx, Operand::StaticArray(edi, times_1, cache_keys));
__ j(not_equal, &slow);
__ add(edi, Immediate(kPointerSize));
ExternalReference cache_field_offsets =
ExternalReference::keyed_lookup_cache_field_offsets(masm->isolate());
- // Hit on second entry.
- __ add(ecx, Immediate(1));
- __ mov(edi,
- Operand::StaticArray(ecx, times_pointer_size, cache_field_offsets));
- __ movzx_b(ecx, FieldOperand(ebx, Map::kInObjectPropertiesOffset));
- __ sub(edi, ecx);
- __ j(above_equal, &property_array_property);
- __ jmp(&load_in_object_property);
-
- // Hit on first entry.
- __ bind(&hit_on_first_entry);
- __ mov(edi,
- Operand::StaticArray(ecx, times_pointer_size, cache_field_offsets));
- __ movzx_b(ecx, FieldOperand(ebx, Map::kInObjectPropertiesOffset));
- __ sub(edi, ecx);
- __ j(above_equal, &property_array_property);
+ // Hit on nth entry.
+ for (int i = kEntriesPerBucket - 1; i >= 0; i--) {
+ __ bind(&hit_on_nth_entry[i]);
+ if (i != 0) {
+ __ add(ecx, Immediate(i));
+ }
+ __ mov(edi,
+ Operand::StaticArray(ecx, times_pointer_size, cache_field_offsets));
+ __ movzx_b(ecx, FieldOperand(ebx, Map::kInObjectPropertiesOffset));
+ __ sub(edi, ecx);
+ __ j(above_equal, &property_array_property);
+ if (i != 0) {
+ __ jmp(&load_in_object_property);
+ }
+ }
// Load in-object property.
__ bind(&load_in_object_property);
WriteTranslation(environment->outer(), translation);
int closure_id = DefineDeoptimizationLiteral(environment->closure());
- translation->BeginFrame(environment->ast_id(), closure_id, height);
+ if (environment->is_arguments_adaptor()) {
+ translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
+ } else {
+ translation->BeginJSFrame(environment->ast_id(), closure_id, height);
+ }
for (int i = 0; i < translation_size; ++i) {
LOperand* value = environment->values()->at(i);
// spilled_registers_ and spilled_double_registers_ are either
// |>------------ translation_size ------------<|
int frame_count = 0;
+ int jsframe_count = 0;
for (LEnvironment* e = environment; e != NULL; e = e->outer()) {
++frame_count;
+ if (!e->is_arguments_adaptor()) {
+ ++jsframe_count;
+ }
}
- Translation translation(&translations_, frame_count);
+ Translation translation(&translations_, frame_count, jsframe_count);
WriteTranslation(environment, &translation);
int deoptimization_index = deoptimizations_.length();
int pc_offset = masm()->pc_offset();
LEnvironment* outer =
CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator);
int ast_id = hydrogen_env->ast_id();
- ASSERT(ast_id != AstNode::kNoNumber);
+ ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor());
int value_count = hydrogen_env->length();
LEnvironment* result =
new(zone()) LEnvironment(hydrogen_env->closure(),
+ hydrogen_env->is_arguments_adaptor(),
ast_id,
hydrogen_env->parameter_count(),
argument_count_,
value_count,
outer);
+ int argument_index = *argument_index_accumulator;
for (int i = 0; i < value_count; ++i) {
if (hydrogen_env->is_special_index(i)) continue;
if (value->IsArgumentsObject()) {
op = NULL;
} else if (value->IsPushArgument()) {
- op = new(zone()) LArgument((*argument_index_accumulator)++);
+ op = new(zone()) LArgument(argument_index++);
} else {
op = UseAny(value);
}
result->AddValue(op, value->representation());
}
+ if (!hydrogen_env->is_arguments_adaptor()) {
+ *argument_index_accumulator = argument_index;
+ }
+
return result;
}
LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement(
HLoadKeyedSpecializedArrayElement* instr) {
ElementsKind elements_kind = instr->elements_kind();
- Representation representation(instr->representation());
ASSERT(
- (representation.IsInteger32() &&
+ (instr->representation().IsInteger32() &&
(elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
(elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
- (representation.IsDouble() &&
+ (instr->representation().IsDouble() &&
((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
(elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
ASSERT(instr->key()->representation().IsInteger32());
LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement(
HStoreKeyedSpecializedArrayElement* instr) {
- Representation representation(instr->value()->representation());
ElementsKind elements_kind = instr->elements_kind();
ASSERT(
- (representation.IsInteger32() &&
+ (instr->value()->representation().IsInteger32() &&
(elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
(elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
- (representation.IsDouble() &&
+ (instr->value()->representation().IsDouble() &&
((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
(elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
ASSERT(instr->external_pointer()->representation().IsExternal());
HEnvironment* outer = current_block_->last_environment();
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
+ instr->arguments_count(),
instr->function(),
undefined,
instr->call_kind());
LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
- HEnvironment* outer = current_block_->last_environment()->outer();
+ HEnvironment* outer = current_block_->last_environment()->
+ DiscardInlined(false);
current_block_->UpdateEnvironment(outer);
return NULL;
}
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
namespace v8 {
namespace internal {
-
+class NodeVisitor;
+class RegExpCompiler;
class RegExpMacroAssembler;
-
+class RegExpNode;
+class RegExpTree;
class RegExpImpl {
public:
next_(NULL),
current_interval_(NULL),
last_processed_use_(NULL),
- spill_start_index_(kMaxInt) {
- spill_operand_ = new LUnallocated(LUnallocated::IGNORE);
-}
+ spill_operand_(new LOperand()),
+ spill_start_index_(kMaxInt) { }
void LiveRange::set_assigned_register(int reg, RegisterKind register_kind) {
bool LiveRange::HasAllocatedSpillOperand() const {
- return spill_operand_ != NULL && !spill_operand_->IsUnallocated();
+ ASSERT(spill_operand_ != NULL);
+ return !spill_operand_->IsIgnored();
}
void LiveRange::SetSpillOperand(LOperand* operand) {
ASSERT(!operand->IsUnallocated());
ASSERT(spill_operand_ != NULL);
- ASSERT(spill_operand_->IsUnallocated());
+ ASSERT(spill_operand_->IsIgnored());
spill_operand_->ConvertTo(operand->kind(), operand->index());
}
int LAllocator::max_initial_value_ids() {
- return LUnallocated::kMaxVirtualRegisters / 32;
+ return LUnallocated::kMaxVirtualRegisters / 16;
}
LUnallocated* unalloc = NULL;
switch (kind()) {
case INVALID:
+ stream->Add("(0)");
break;
case UNALLOCATED:
unalloc = LUnallocated::cast(this);
case LUnallocated::ANY:
stream->Add("(-)");
break;
- case LUnallocated::IGNORE:
- stream->Add("(0)");
- break;
}
break;
case CONSTANT_OPERAND:
bool IsDoubleRegister() const { return kind() == DOUBLE_REGISTER; }
bool IsArgument() const { return kind() == ARGUMENT; }
bool IsUnallocated() const { return kind() == UNALLOCATED; }
+ bool IsIgnored() const { return kind() == INVALID; }
bool Equals(LOperand* other) const { return value_ == other->value_; }
int VirtualRegister();
FIXED_SLOT,
MUST_HAVE_REGISTER,
WRITABLE_REGISTER,
- SAME_AS_FIRST_INPUT,
- IGNORE
+ SAME_AS_FIRST_INPUT
};
// Lifetime of operand inside the instruction.
// The superclass has a KindField. Some policies have a signed fixed
// index in the upper bits.
- static const int kPolicyWidth = 4;
+ static const int kPolicyWidth = 3;
static const int kLifetimeWidth = 1;
- static const int kVirtualRegisterWidth = 17;
+ static const int kVirtualRegisterWidth = 18;
static const int kPolicyShift = kKindFieldWidth;
static const int kLifetimeShift = kPolicyShift + kPolicyWidth;
kVirtualRegisterWidth> {
};
- static const int kMaxVirtualRegisters = 1 << (kVirtualRegisterWidth + 1);
+ static const int kMaxVirtualRegisters = 1 << kVirtualRegisterWidth;
static const int kMaxFixedIndex = 63;
static const int kMinFixedIndex = -64;
- bool HasIgnorePolicy() const { return policy() == IGNORE; }
- bool HasNoPolicy() const { return policy() == NONE; }
bool HasAnyPolicy() const {
return policy() == ANY;
}
}
bool IsIgnored() const {
- return destination_ != NULL &&
- destination_->IsUnallocated() &&
- LUnallocated::cast(destination_)->HasIgnorePolicy();
+ return destination_ != NULL && destination_->IsIgnored();
}
// We clear both operands to indicate move that's been eliminated.
class LEnvironment: public ZoneObject {
public:
LEnvironment(Handle<JSFunction> closure,
+ bool is_arguments_adaptor,
int ast_id,
int parameter_count,
int argument_count,
int value_count,
LEnvironment* outer)
: closure_(closure),
+ is_arguments_adaptor_(is_arguments_adaptor),
arguments_stack_height_(argument_count),
deoptimization_index_(Safepoint::kNoDeoptimizationIndex),
translation_index_(-1),
void PrintTo(StringStream* stream);
+ bool is_arguments_adaptor() const { return is_arguments_adaptor_; }
+
private:
Handle<JSFunction> closure_;
+ bool is_arguments_adaptor_;
int arguments_stack_height_;
int deoptimization_index_;
int translation_index_;
}
+bool MarkCompactCollector::MarkObjectWithoutPush(HeapObject* object) {
+ MarkBit mark = Marking::MarkBitFrom(object);
+ bool old_mark = mark.Get();
+ if (!old_mark) SetMark(object, mark);
+ return old_mark;
+}
+
+
+void MarkCompactCollector::MarkObjectAndPush(HeapObject* object) {
+ if (!MarkObjectWithoutPush(object)) marking_deque_.PushBlack(object);
+}
+
+
void MarkCompactCollector::SetMark(HeapObject* obj, MarkBit mark_bit) {
ASSERT(!mark_bit.Get());
ASSERT(Marking::MarkBitFrom(obj) == mark_bit);
SharedFunctionInfo* candidate) {
Code* code = candidate->code();
return reinterpret_cast<SharedFunctionInfo**>(
- code->address() + Code::kNextCodeFlushingCandidateOffset);
+ code->address() + Code::kGCMetadataOffset);
}
static SharedFunctionInfo* GetNextCandidate(SharedFunctionInfo* candidate) {
- return *GetNextCandidateField(candidate);
+ return reinterpret_cast<SharedFunctionInfo*>(
+ candidate->code()->gc_metadata());
}
static void SetNextCandidate(SharedFunctionInfo* candidate,
SharedFunctionInfo* next_candidate) {
- *GetNextCandidateField(candidate) = next_candidate;
+ candidate->code()->set_gc_metadata(next_candidate);
}
Isolate* isolate_;
}
+void MarkCompactCollector::MarkAccessorPairSlot(HeapObject* accessors,
+ int offset) {
+ Object** slot = HeapObject::RawField(accessors, offset);
+ HeapObject* accessor = HeapObject::cast(*slot);
+ if (accessor->IsMap()) return;
+ RecordSlot(slot, slot, accessor);
+ MarkObjectAndPush(accessor);
+}
+
+
void MarkCompactCollector::MarkDescriptorArray(
DescriptorArray* descriptors) {
MarkBit descriptors_mark = Marking::MarkBitFrom(descriptors);
PropertyDetails details(Smi::cast(contents->get(i + 1)));
Object** slot = contents->data_start() + i;
- Object* value = *slot;
- if (!value->IsHeapObject()) continue;
+ if (!(*slot)->IsHeapObject()) continue;
+ HeapObject* value = HeapObject::cast(*slot);
RecordSlot(slot, slot, *slot);
- if (details.IsProperty()) {
- HeapObject* object = HeapObject::cast(value);
- MarkBit mark = Marking::MarkBitFrom(HeapObject::cast(object));
- if (!mark.Get()) {
- SetMark(HeapObject::cast(object), mark);
- marking_deque_.PushBlack(object);
- }
- } else if (details.type() == ELEMENTS_TRANSITION && value->IsFixedArray()) {
- // For maps with multiple elements transitions, the transition maps are
- // stored in a FixedArray. Keep the fixed array alive but not the maps
- // that it refers to.
- HeapObject* object = HeapObject::cast(value);
- MarkBit mark = Marking::MarkBitFrom(HeapObject::cast(object));
- if (!mark.Get()) {
- SetMark(HeapObject::cast(object), mark);
- }
+ switch (details.type()) {
+ case NORMAL:
+ case FIELD:
+ case CONSTANT_FUNCTION:
+ case HANDLER:
+ case INTERCEPTOR:
+ MarkObjectAndPush(value);
+ break;
+ case CALLBACKS:
+ if (!value->IsAccessorPair()) {
+ MarkObjectAndPush(value);
+ } else if (!MarkObjectWithoutPush(value)) {
+ MarkAccessorPairSlot(value, AccessorPair::kGetterOffset);
+ MarkAccessorPairSlot(value, AccessorPair::kSetterOffset);
+ }
+ break;
+ case ELEMENTS_TRANSITION:
+ // For maps with multiple elements transitions, the transition maps are
+ // stored in a FixedArray. Keep the fixed array alive but not the maps
+ // that it refers to.
+ if (value->IsFixedArray()) MarkObjectWithoutPush(value);
+ break;
+ case MAP_TRANSITION:
+ case CONSTANT_TRANSITION:
+ case NULL_DESCRIPTOR:
+ break;
}
}
// The DescriptorArray descriptors contains a pointer to its contents array,
map->unchecked_constructor()->unchecked_shared()->AttachInitialMap(map);
}
- // Clear dead prototype transitions.
- int number_of_transitions = map->NumberOfProtoTransitions();
- FixedArray* prototype_transitions = map->prototype_transitions();
-
- int new_number_of_transitions = 0;
- const int header = Map::kProtoTransitionHeaderSize;
- const int proto_offset =
- header + Map::kProtoTransitionPrototypeOffset;
- const int map_offset = header + Map::kProtoTransitionMapOffset;
- const int step = Map::kProtoTransitionElementsPerEntry;
- for (int i = 0; i < number_of_transitions; i++) {
- Object* prototype = prototype_transitions->get(proto_offset + i * step);
- Object* cached_map = prototype_transitions->get(map_offset + i * step);
- if (IsMarked(prototype) && IsMarked(cached_map)) {
- int proto_index = proto_offset + new_number_of_transitions * step;
- int map_index = map_offset + new_number_of_transitions * step;
- if (new_number_of_transitions != i) {
- prototype_transitions->set_unchecked(
- heap_,
- proto_index,
- prototype,
- UPDATE_WRITE_BARRIER);
- prototype_transitions->set_unchecked(
- heap_,
- map_index,
- cached_map,
- SKIP_WRITE_BARRIER);
- }
- Object** slot =
- HeapObject::RawField(prototype_transitions,
- FixedArray::OffsetOfElementAt(proto_index));
- RecordSlot(slot, slot, prototype);
- new_number_of_transitions++;
+ ClearNonLivePrototypeTransitions(map);
+ ClearNonLiveMapTransitions(map, map_mark);
+ }
+}
+
+
+void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) {
+ int number_of_transitions = map->NumberOfProtoTransitions();
+ FixedArray* prototype_transitions = map->prototype_transitions();
+
+ int new_number_of_transitions = 0;
+ const int header = Map::kProtoTransitionHeaderSize;
+ const int proto_offset = header + Map::kProtoTransitionPrototypeOffset;
+ const int map_offset = header + Map::kProtoTransitionMapOffset;
+ const int step = Map::kProtoTransitionElementsPerEntry;
+ for (int i = 0; i < number_of_transitions; i++) {
+ Object* prototype = prototype_transitions->get(proto_offset + i * step);
+ Object* cached_map = prototype_transitions->get(map_offset + i * step);
+ if (IsMarked(prototype) && IsMarked(cached_map)) {
+ int proto_index = proto_offset + new_number_of_transitions * step;
+ int map_index = map_offset + new_number_of_transitions * step;
+ if (new_number_of_transitions != i) {
+ prototype_transitions->set_unchecked(
+ heap_,
+ proto_index,
+ prototype,
+ UPDATE_WRITE_BARRIER);
+ prototype_transitions->set_unchecked(
+ heap_,
+ map_index,
+ cached_map,
+ SKIP_WRITE_BARRIER);
}
+ Object** slot =
+ HeapObject::RawField(prototype_transitions,
+ FixedArray::OffsetOfElementAt(proto_index));
+ RecordSlot(slot, slot, prototype);
+ new_number_of_transitions++;
}
+ }
- if (new_number_of_transitions != number_of_transitions) {
- map->SetNumberOfProtoTransitions(new_number_of_transitions);
- }
+ if (new_number_of_transitions != number_of_transitions) {
+ map->SetNumberOfProtoTransitions(new_number_of_transitions);
+ }
- // Fill slots that became free with undefined value.
- for (int i = new_number_of_transitions * step;
- i < number_of_transitions * step;
- i++) {
- prototype_transitions->set_undefined(heap_, header + i);
- }
+ // Fill slots that became free with undefined value.
+ for (int i = new_number_of_transitions * step;
+ i < number_of_transitions * step;
+ i++) {
+ prototype_transitions->set_undefined(heap_, header + i);
+ }
+}
- // Follow the chain of back pointers to find the prototype.
- Map* current = map;
- while (current->IsMap()) {
- current = reinterpret_cast<Map*>(current->prototype());
- ASSERT(current->IsHeapObject());
- }
- Object* real_prototype = current;
- // Follow back pointers, setting them to prototype,
- // clearing map transitions when necessary.
- current = map;
- bool on_dead_path = !map_mark.Get();
- Object* next;
- while (current->IsMap()) {
- next = current->prototype();
- // There should never be a dead map above a live map.
- MarkBit current_mark = Marking::MarkBitFrom(current);
- bool is_alive = current_mark.Get();
- ASSERT(on_dead_path || is_alive);
+void MarkCompactCollector::ClearNonLiveMapTransitions(Map* map,
+ MarkBit map_mark) {
+ // Follow the chain of back pointers to find the prototype.
+ Map* real_prototype = map;
+ while (real_prototype->IsMap()) {
+ real_prototype = reinterpret_cast<Map*>(real_prototype->prototype());
+ ASSERT(real_prototype->IsHeapObject());
+ }
- // A live map above a dead map indicates a dead transition.
- // This test will always be false on the first iteration.
- if (on_dead_path && is_alive) {
- on_dead_path = false;
- current->ClearNonLiveTransitions(heap(), real_prototype);
- }
- *HeapObject::RawField(current, Map::kPrototypeOffset) =
- real_prototype;
+ // Follow back pointers, setting them to prototype, clearing map transitions
+ // when necessary.
+ Map* current = map;
+ bool current_is_alive = map_mark.Get();
+ bool on_dead_path = !current_is_alive;
+ while (current->IsMap()) {
+ Object* next = current->prototype();
+ // There should never be a dead map above a live map.
+ ASSERT(on_dead_path || current_is_alive);
- if (is_alive) {
- Object** slot = HeapObject::RawField(current, Map::kPrototypeOffset);
- RecordSlot(slot, slot, real_prototype);
- }
- current = reinterpret_cast<Map*>(next);
+ // A live map above a dead map indicates a dead transition. This test will
+ // always be false on the first iteration.
+ if (on_dead_path && current_is_alive) {
+ on_dead_path = false;
+ current->ClearNonLiveTransitions(heap(), real_prototype);
}
+
+ Object** slot = HeapObject::RawField(current, Map::kPrototypeOffset);
+ *slot = real_prototype;
+ if (current_is_alive) RecordSlot(slot, slot, real_prototype);
+
+ current = reinterpret_cast<Map*>(next);
+ current_is_alive = Marking::MarkBitFrom(current).Get();
}
}
// This is for non-incremental marking.
INLINE(void MarkObject(HeapObject* obj, MarkBit mark_bit));
+ INLINE(bool MarkObjectWithoutPush(HeapObject* object));
+ INLINE(void MarkObjectAndPush(HeapObject* value));
+
// Marks the object black. This is for non-incremental marking.
INLINE(void SetMark(HeapObject* obj, MarkBit mark_bit));
// Mark a Map and its DescriptorArray together, skipping transitions.
void MarkMapContents(Map* map);
+ void MarkAccessorPairSlot(HeapObject* accessors, int offset);
void MarkDescriptorArray(DescriptorArray* descriptors);
// Mark the heap roots and all objects reachable from them.
// Map transitions from a live map to a dead map must be killed.
// We replace them with a null descriptor, with the same key.
void ClearNonLiveTransitions();
+ void ClearNonLivePrototypeTransitions(Map* map);
+ void ClearNonLiveMapTransitions(Map* map, MarkBit map_mark);
// Marking detaches initial maps from SharedFunctionInfo objects
// to make this reference weak. We need to reattach initial maps
#define V8_MIPS_ASSEMBLER_MIPS_INL_H_
#include "mips/assembler-mips.h"
+
#include "cpu.h"
#include "debug.h"
// Load the key (consisting of map and symbol) from the cache and
// check for match.
- Label try_second_entry, hit_on_first_entry, load_in_object_property;
+ Label load_in_object_property;
+ static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket;
+ Label hit_on_nth_entry[kEntriesPerBucket];
ExternalReference cache_keys =
ExternalReference::keyed_lookup_cache_keys(isolate);
__ li(t0, Operand(cache_keys));
__ sll(at, a3, kPointerSizeLog2 + 1);
__ addu(t0, t0, at);
- __ lw(t1, MemOperand(t0));
- __ Branch(&try_second_entry, ne, a2, Operand(t1));
- __ lw(t1, MemOperand(t0, kPointerSize));
- __ Branch(&hit_on_first_entry, eq, a0, Operand(t1));
- __ bind(&try_second_entry);
- __ lw(t1, MemOperand(t0, kPointerSize * 2));
+ for (int i = 0; i < kEntriesPerBucket - 1; i++) {
+ Label try_next_entry;
+ __ lw(t1, MemOperand(t0, kPointerSize * i * 2));
+ __ Branch(&try_next_entry, ne, a2, Operand(t1));
+ __ lw(t1, MemOperand(t0, kPointerSize * (i * 2 + 1)));
+ __ Branch(&hit_on_nth_entry[i], eq, a0, Operand(t1));
+ __ bind(&try_next_entry);
+ }
+
+ __ lw(t1, MemOperand(t0, kPointerSize * (kEntriesPerBucket - 1) * 2));
__ Branch(&slow, ne, a2, Operand(t1));
- __ lw(t1, MemOperand(t0, kPointerSize * 3));
+ __ lw(t1, MemOperand(t0, kPointerSize * ((kEntriesPerBucket - 1) * 2 + 1)));
__ Branch(&slow, ne, a0, Operand(t1));
// Get field offset.
ExternalReference cache_field_offsets =
ExternalReference::keyed_lookup_cache_field_offsets(isolate);
- // Hit on second entry.
- __ li(t0, Operand(cache_field_offsets));
- __ sll(at, a3, kPointerSizeLog2);
- __ addu(at, t0, at);
- __ lw(t1, MemOperand(at, kPointerSize));
- __ lbu(t2, FieldMemOperand(a2, Map::kInObjectPropertiesOffset));
- __ Subu(t1, t1, t2);
- __ Branch(&property_array_property, ge, t1, Operand(zero_reg));
- __ Branch(&load_in_object_property);
-
- // Hit on first entry.
- __ bind(&hit_on_first_entry);
- __ li(t0, Operand(cache_field_offsets));
- __ sll(at, a3, kPointerSizeLog2);
- __ addu(at, t0, at);
- __ lw(t1, MemOperand(at));
- __ lbu(t2, FieldMemOperand(a2, Map::kInObjectPropertiesOffset));
- __ Subu(t1, t1, t2);
- __ Branch(&property_array_property, ge, t1, Operand(zero_reg));
+ // Hit on nth entry.
+ for (int i = kEntriesPerBucket - 1; i >= 0; i--) {
+ __ bind(&hit_on_nth_entry[i]);
+ __ li(t0, Operand(cache_field_offsets));
+ __ sll(at, a3, kPointerSizeLog2);
+ __ addu(at, t0, at);
+ __ lw(t1, MemOperand(at, kPointerSize * i));
+ __ lbu(t2, FieldMemOperand(a2, Map::kInObjectPropertiesOffset));
+ __ Subu(t1, t1, t2);
+ __ Branch(&property_array_property, ge, t1, Operand(zero_reg));
+ if (i != 0) {
+ __ Branch(&load_in_object_property);
+ }
+ }
// Load in-object property.
__ bind(&load_in_object_property);
ACCESSORS(Code, relocation_info, ByteArray, kRelocationInfoOffset)
ACCESSORS(Code, handler_table, FixedArray, kHandlerTableOffset)
ACCESSORS(Code, deoptimization_data, FixedArray, kDeoptimizationDataOffset)
-ACCESSORS(Code, next_code_flushing_candidate,
- Object, kNextCodeFlushingCandidateOffset)
+ACCESSORS(Code, gc_metadata, Object, kGCMetadataOffset)
byte* Code::instruction_start() {
Map* Next() {
ASSERT(IsIterating());
FixedArray* contents = ContentArray();
+ // Attention, tricky index manipulation ahead: Every entry in the contents
+ // array consists of a value/details pair, so the index is typically even.
+ // An exception is made for CALLBACKS entries: An even index means we look
+ // at its getter, and an odd index means we look at its setter.
int index = Smi::cast(*ContentHeader())->value();
while (index < contents->length()) {
- int next_index = index + 2;
- PropertyDetails details(Smi::cast(contents->get(index + 1)));
- if (details.IsTransition()) {
- *ContentHeader() = Smi::FromInt(next_index);
- return static_cast<Map*>(contents->get(index));
+ PropertyDetails details(Smi::cast(contents->get(index | 1)));
+ switch (details.type()) {
+ case MAP_TRANSITION:
+ case CONSTANT_TRANSITION:
+ case ELEMENTS_TRANSITION:
+ // We definitely have a map transition.
+ *ContentHeader() = Smi::FromInt(index + 2);
+ return static_cast<Map*>(contents->get(index));
+ case CALLBACKS: {
+ // We might have a map transition in a getter or in a setter.
+ AccessorPair* accessors =
+ static_cast<AccessorPair*>(contents->get(index & ~1));
+ Object* accessor =
+ ((index & 1) == 0) ? accessors->getter() : accessors->setter();
+ index++;
+ if (accessor->IsMap()) {
+ *ContentHeader() = Smi::FromInt(index);
+ return static_cast<Map*>(accessor);
+ }
+ break;
+ }
+ case NORMAL:
+ case FIELD:
+ case CONSTANT_FUNCTION:
+ case HANDLER:
+ case INTERCEPTOR:
+ case NULL_DESCRIPTOR:
+ // We definitely have no map transition.
+ index += 2;
+ break;
}
- index = next_index;
}
*ContentHeader() = descriptor_array_->GetHeap()->fixed_array_map();
return NULL;
static_cast<Translation::Opcode>(iterator.Next());
ASSERT(Translation::BEGIN == opcode);
int frame_count = iterator.Next();
- PrintF(out, " %s {count=%d}\n", Translation::StringFor(opcode),
- frame_count);
+ int jsframe_count = iterator.Next();
+ PrintF(out, " %s {frame count=%d, js frame count=%d}\n",
+ Translation::StringFor(opcode),
+ frame_count,
+ jsframe_count);
while (iterator.HasNext() &&
Translation::BEGIN !=
UNREACHABLE();
break;
- case Translation::FRAME: {
+ case Translation::JS_FRAME: {
int ast_id = iterator.Next();
int function_id = iterator.Next();
JSFunction* function =
break;
}
+ case Translation::ARGUMENTS_ADAPTOR_FRAME: {
+ unsigned height = iterator.Next();
+ PrintF(out, "{arguments adaptor, height=%d}", height);
+ break;
+ }
+
case Translation::DUPLICATE:
break;
// [deoptimization_data]: Array containing data for deopt.
DECL_ACCESSORS(deoptimization_data, FixedArray)
- // [code_flushing_candidate]: Field only used during garbage
- // collection to hold code flushing candidates. The contents of this
+ // [gc_metadata]: Field used to hold GC related metadata. The contents of this
// field does not have to be traced during garbage collection since
// it is only used by the garbage collector itself.
- DECL_ACCESSORS(next_code_flushing_candidate, Object)
+ DECL_ACCESSORS(gc_metadata, Object)
// Unchecked accessors to be used during GC.
inline ByteArray* unchecked_relocation_info();
static const int kHandlerTableOffset = kRelocationInfoOffset + kPointerSize;
static const int kDeoptimizationDataOffset =
kHandlerTableOffset + kPointerSize;
- static const int kNextCodeFlushingCandidateOffset =
- kDeoptimizationDataOffset + kPointerSize;
- static const int kFlagsOffset =
- kNextCodeFlushingCandidateOffset + kPointerSize;
+ static const int kGCMetadataOffset = kDeoptimizationDataOffset + kPointerSize;
+ static const int kFlagsOffset = kGCMetadataOffset + kPointerSize;
static const int kKindSpecificFlagsOffset = kFlagsOffset + kIntSize;
static const int kKindSpecificFlagsSize = 2 * kIntSize;
Thread::Thread(const Options& options)
: data_(new PlatformData),
- stack_size_(options.stack_size) {
- set_name(options.name);
-}
-
-
-Thread::Thread(const char* name)
- : data_(new PlatformData),
- stack_size_(0) {
- set_name(name);
+ stack_size_(options.stack_size()) {
+ set_name(options.name());
}
FULL_INTERVAL
};
+ static const int kSignalSenderStackSize = 32 * KB;
+
explicit SignalSender(int interval)
- : Thread("SignalSender"),
+ : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)),
interval_(interval) {}
static void AddActiveSampler(Sampler* sampler) {
Thread::Thread(const Options& options)
: data_(new PlatformData()),
- stack_size_(options.stack_size) {
- set_name(options.name);
-}
-
-
-Thread::Thread(const char* name)
- : data_(new PlatformData()),
- stack_size_(0) {
- set_name(name);
+ stack_size_(options.stack_size()) {
+ set_name(options.name());
}
FULL_INTERVAL
};
+ static const int kSignalSenderStackSize = 32 * KB;
+
explicit SignalSender(int interval)
- : Thread("SignalSender"),
+ : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)),
vm_tgid_(getpid()),
interval_(interval) {}
pthread_t thread_; // Thread handle for pthread.
};
-Thread::Thread(const Options& options)
- : data_(new PlatformData),
- stack_size_(options.stack_size) {
- set_name(options.name);
-}
-
-Thread::Thread(const char* name)
+Thread::Thread(const Options& options)
: data_(new PlatformData),
- stack_size_(0) {
- set_name(name);
+ stack_size_(options.stack_size()) {
+ set_name(options.name());
}
thread_act_t profiled_thread_;
};
+
class SamplerThread : public Thread {
public:
+ static const int kSamplerThreadStackSize = 32 * KB;
+
explicit SamplerThread(int interval)
- : Thread("SamplerThread"),
+ : Thread(Thread::Options("SamplerThread", kSamplerThreadStackSize)),
interval_(interval) {}
static void AddActiveSampler(Sampler* sampler) {
Thread::Thread(const Options& options)
: data_(new PlatformData()),
- stack_size_(options.stack_size) {
- set_name(options.name);
-}
-
-
-Thread::Thread(const char* name)
- : data_(new PlatformData()),
- stack_size_(0) {
- set_name(name);
+ stack_size_(options.stack_size()) {
+ set_name(options.name());
}
FULL_INTERVAL
};
+ static const int kSignalSenderStackSize = 32 * KB;
+
explicit SignalSender(int interval)
- : Thread("SignalSender"),
+ : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)),
vm_tgid_(getpid()),
interval_(interval) {}
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
#include "v8.h"
#include "platform.h"
-#include "vm-state-inl.h"
#include "v8threads.h"
+#include "vm-state-inl.h"
// It seems there is a bug in some Solaris distributions (experienced in
static const pthread_t kNoThread = (pthread_t) 0;
-static void* GetRandomMmapAddr() {
- Isolate* isolate = Isolate::UncheckedCurrent();
- // Note that the current isolate isn't set up in a call path via
- // CpuFeatures::Probe. We don't care about randomization in this case because
- // the code page is immediately freed.
- if (isolate != NULL) {
-#ifdef V8_TARGET_ARCH_X64
- uint64_t rnd1 = V8::RandomPrivate(isolate);
- uint64_t rnd2 = V8::RandomPrivate(isolate);
- uint64_t raw_addr = (rnd1 << 32) ^ rnd2;
- // Currently available CPUs have 48 bits of virtual addressing. Truncate
- // the hint address to 46 bits to give the kernel a fighting chance of
- // fulfilling our placement request.
- raw_addr &= V8_UINT64_C(0x3ffffffff000);
-#else
- uint32_t raw_addr = V8::RandomPrivate(isolate);
- // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
- // variety of ASLR modes (PAE kernel, NX compat mode, etc).
- raw_addr &= 0x3ffff000;
- raw_addr += 0x20000000;
-#endif
- return reinterpret_cast<void*>(raw_addr);
- }
- return NULL;
-}
-
-
double ceiling(double x) {
return ceil(x);
}
// 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
+// and verification). The estimate is conservative, i.e., 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);
ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment())));
size_t request_size = RoundUp(size + alignment,
static_cast<intptr_t>(OS::AllocateAlignment()));
- void* reservation = mmap(GetRandomMmapAddr(),
+ void* reservation = mmap(OS::GetRandomMmapAddr(),
request_size,
PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
void* VirtualMemory::ReserveRegion(size_t size) {
- void* result = mmap(GetRandomMmapAddr(),
+ void* result = mmap(OS::GetRandomMmapAddr(),
size,
PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
pthread_t thread_; // Thread handle for pthread.
};
-Thread::Thread(const Options& options)
- : data_(new PlatformData()),
- stack_size_(options.stack_size) {
- set_name(options.name);
-}
-
-Thread::Thread(const char* name)
+Thread::Thread(const Options& options)
: data_(new PlatformData()),
- stack_size_(0) {
- set_name(name);
+ stack_size_(options.stack_size()) {
+ set_name(options.name());
}
FULL_INTERVAL
};
+ static const int kSignalSenderStackSize = 32 * KB;
+
explicit SignalSender(int interval)
- : Thread("SignalSender"),
+ : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)),
interval_(interval) {}
static void InstallSignalHandler() {
static bool signal_handler_installed_;
static struct sigaction old_signal_handler_;
+ private:
DISALLOW_COPY_AND_ASSIGN(SignalSender);
};
// handle until it is started.
Thread::Thread(const Options& options)
- : stack_size_(options.stack_size) {
+ : stack_size_(options.stack_size()) {
data_ = new PlatformData(kNoThread);
- set_name(options.name);
-}
-
-
-Thread::Thread(const char* name)
- : stack_size_(0) {
- data_ = new PlatformData(kNoThread);
- set_name(name);
+ set_name(options.name());
}
class SamplerThread : public Thread {
public:
+ static const int kSamplerThreadStackSize = 32 * KB;
+
explicit SamplerThread(int interval)
- : Thread("SamplerThread"),
+ : Thread(Thread::Options("SamplerThread", kSamplerThreadStackSize)),
interval_(interval) {}
static void AddActiveSampler(Sampler* sampler) {
LOCAL_STORAGE_KEY_MAX_VALUE = kMaxInt
};
- struct Options {
- Options() : name("v8:<unknown>"), stack_size(0) {}
+ class Options {
+ public:
+ Options() : name_("v8:<unknown>"), stack_size_(0) {}
+ Options(const char* name, int stack_size = 0)
+ : name_(name), stack_size_(stack_size) {}
+
+ const char* name() const { return name_; }
+ int stack_size() const { return stack_size_; }
- const char* name;
- int stack_size;
+ private:
+ const char* name_;
+ int stack_size_;
};
// Create new thread.
explicit Thread(const Options& options);
- explicit Thread(const char* name);
virtual ~Thread();
// Start new thread by calling the Run() method in the new thread.
List<JSFunction*> functions(2);
frame->GetFunctions(&functions);
if (functions.length() > 1) {
- int inlined_frame_index = functions.length() - 1;
- JSFunction* inlined_function = functions[inlined_frame_index];
- int args_count = inlined_function->shared()->formal_parameter_count();
- ScopedVector<SlotRef> args_slots(args_count);
- SlotRef::ComputeSlotMappingForArguments(frame,
- inlined_frame_index,
- &args_slots);
+ int inlined_jsframe_index = functions.length() - 1;
+ JSFunction* inlined_function = functions[inlined_jsframe_index];
+ Vector<SlotRef> args_slots =
+ SlotRef::ComputeSlotMappingForArguments(
+ frame,
+ inlined_jsframe_index,
+ inlined_function->shared()->formal_parameter_count());
+
+ int args_count = args_slots.length();
*total_argc = prefix_argc + args_count;
SmartArrayPointer<Handle<Object> > param_data(
Handle<Object> val = args_slots[i].GetValue();
param_data[prefix_argc + i] = val;
}
+
+ args_slots.Dispose();
+
return param_data;
} else {
it.AdvanceToArgumentsFrame();
static_cast<Deoptimizer::BailoutType>(args.smi_at(0));
Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
ASSERT(isolate->heap()->IsAllocationAllowed());
- int frames = deoptimizer->output_count();
+ int jsframes = deoptimizer->jsframe_count();
deoptimizer->MaterializeHeapNumbers();
delete deoptimizer;
JavaScriptFrameIterator it(isolate);
JavaScriptFrame* frame = NULL;
- for (int i = 0; i < frames - 1; i++) it.Advance();
+ for (int i = 0; i < jsframes - 1; i++) it.Advance();
frame = it.frame();
RUNTIME_ASSERT(frame->function()->IsJSFunction());
class FrameInspector {
public:
FrameInspector(JavaScriptFrame* frame,
- int inlined_frame_index,
+ int inlined_jsframe_index,
Isolate* isolate)
: frame_(frame), deoptimized_frame_(NULL), isolate_(isolate) {
// Calculate the deoptimized frame.
if (frame->is_optimized()) {
deoptimized_frame_ = Deoptimizer::DebuggerInspectableFrame(
- frame, inlined_frame_index, isolate);
+ frame, inlined_jsframe_index, isolate);
}
has_adapted_arguments_ = frame_->has_adapted_arguments();
is_optimized_ = frame_->is_optimized();
return heap->undefined_value();
}
- int inlined_frame_index = 0; // Inlined frame index in optimized frame.
-
int count = 0;
JavaScriptFrameIterator it(isolate, id);
for (; !it.done(); it.Advance()) {
}
if (it.done()) return heap->undefined_value();
- if (it.frame()->is_optimized()) {
- inlined_frame_index =
+ bool is_optimized = it.frame()->is_optimized();
+
+ int inlined_jsframe_index = 0; // Inlined frame index in optimized frame.
+ if (is_optimized) {
+ inlined_jsframe_index =
it.frame()->GetInlineCount() - (index - count) - 1;
}
- FrameInspector frame_inspector(it.frame(), inlined_frame_index, isolate);
+ FrameInspector frame_inspector(it.frame(), inlined_jsframe_index, isolate);
// Traverse the saved contexts chain to find the active context for the
// selected frame.
it.frame()->LookupCode()->SourcePosition(it.frame()->pc());
// Check for constructor frame. Inlined frames cannot be construct calls.
- bool inlined_frame =
- it.frame()->is_optimized() && inlined_frame_index != 0;
+ bool inlined_frame = is_optimized && inlined_jsframe_index != 0;
bool constructor = !inlined_frame && it.frame()->IsConstructor();
// Get scope info and read from it for local variable information.
- Handle<JSFunction> function(JSFunction::cast(it.frame()->function()));
+ Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction()));
Handle<SharedFunctionInfo> shared(function->shared());
Handle<ScopeInfo> scope_info(shared->scope_info());
ASSERT(*scope_info != ScopeInfo::Empty());
// Check whether this frame is positioned at return. If not top
// frame or if the frame is optimized it cannot be at a return.
bool at_return = false;
- if (!it.frame()->is_optimized() && index == 0) {
+ if (!is_optimized && index == 0) {
at_return = isolate->debug()->IsBreakAtReturn(it.frame());
}
// the provided parameters whereas the function frame always have the number
// of arguments matching the functions parameters. The rest of the
// information (except for what is collected above) is the same.
- if (it.frame()->has_adapted_arguments()) {
+ if ((inlined_jsframe_index == 0) && it.frame()->has_adapted_arguments()) {
it.AdvanceToArgumentsFrame();
frame_inspector.SetArgumentsFrame(it.frame());
}
if (argument_count < frame_inspector.GetParametersCount()) {
argument_count = frame_inspector.GetParametersCount();
}
-#ifdef DEBUG
- if (it.frame()->is_optimized()) {
- ASSERT_EQ(argument_count, frame_inspector.GetParametersCount());
- }
-#endif
// Calculate the size of the result.
int details_size = kFrameDetailsFirstDynamicIndex +
if (*save->context() == *isolate->debug()->debug_context()) {
flags |= 1 << 0;
}
- if (it.frame()->is_optimized()) {
+ if (is_optimized) {
flags |= 1 << 1;
- flags |= inlined_frame_index << 2;
+ flags |= inlined_jsframe_index << 2;
}
details->set(kFrameDetailsFlagsIndex, Smi::FromInt(flags));
}
// Parameter value.
- if (i < it.frame()->ComputeParametersCount()) {
+ if (i < frame_inspector.GetParametersCount()) {
// Get the value from the stack.
details->set(details_index++, frame_inspector.GetParameter(i));
} else {
// Create a plain JSObject which materializes the local scope for the specified
// frame.
-static Handle<JSObject> MaterializeLocalScope(
+static Handle<JSObject> MaterializeLocalScopeWithFrameInspector(
Isolate* isolate,
JavaScriptFrame* frame,
- int inlined_frame_index) {
- Handle<JSFunction> function(JSFunction::cast(frame->function()));
+ FrameInspector* frame_inspector) {
+ Handle<JSFunction> function(JSFunction::cast(frame_inspector->GetFunction()));
Handle<SharedFunctionInfo> shared(function->shared());
Handle<ScopeInfo> scope_info(shared->scope_info());
- FrameInspector frame_inspector(frame, inlined_frame_index, isolate);
// Allocate and initialize a JSObject with all the arguments, stack locals
// heap locals and extension properties of the debugged function.
// First fill all parameters.
for (int i = 0; i < scope_info->ParameterCount(); ++i) {
+ Handle<Object> value(
+ i < frame_inspector->GetParametersCount() ?
+ frame_inspector->GetParameter(i) : isolate->heap()->undefined_value());
+
RETURN_IF_EMPTY_HANDLE_VALUE(
isolate,
SetProperty(local_scope,
Handle<String>(scope_info->ParameterName(i)),
- Handle<Object>(frame_inspector.GetParameter(i)),
+ value,
NONE,
kNonStrictMode),
Handle<JSObject>());
isolate,
SetProperty(local_scope,
Handle<String>(scope_info->StackLocalName(i)),
- Handle<Object>(frame_inspector.GetExpression(i)),
+ Handle<Object>(frame_inspector->GetExpression(i)),
NONE,
kNonStrictMode),
Handle<JSObject>());
}
+static Handle<JSObject> MaterializeLocalScope(
+ Isolate* isolate,
+ JavaScriptFrame* frame,
+ int inlined_jsframe_index) {
+ FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
+ return MaterializeLocalScopeWithFrameInspector(isolate,
+ frame,
+ &frame_inspector);
+}
+
+
// Create a plain JSObject which materializes the closure content for the
// context.
static Handle<JSObject> MaterializeClosure(Isolate* isolate,
ScopeIterator(Isolate* isolate,
JavaScriptFrame* frame,
- int inlined_frame_index)
+ int inlined_jsframe_index)
: isolate_(isolate),
frame_(frame),
- inlined_frame_index_(inlined_frame_index),
+ inlined_jsframe_index_(inlined_jsframe_index),
function_(JSFunction::cast(frame->function())),
context_(Context::cast(frame->context())),
nested_scope_chain_(4) {
case ScopeIterator::ScopeTypeLocal:
// Materialize the content of the local scope into a JSObject.
ASSERT(nested_scope_chain_.length() == 1);
- return MaterializeLocalScope(isolate_, frame_, inlined_frame_index_);
+ return MaterializeLocalScope(isolate_, frame_, inlined_jsframe_index_);
case ScopeIterator::ScopeTypeWith:
// Return the with object.
return Handle<JSObject>(JSObject::cast(CurrentContext()->extension()));
private:
Isolate* isolate_;
JavaScriptFrame* frame_;
- int inlined_frame_index_;
+ int inlined_jsframe_index_;
Handle<JSFunction> function_;
Handle<Context> context_;
List<Handle<ScopeInfo> > nested_scope_chain_;
if (!maybe_check->ToObject(&check)) return maybe_check;
}
CONVERT_CHECKED(Smi, wrapped_id, args[1]);
- CONVERT_NUMBER_CHECKED(int, inlined_frame_index, Int32, args[2]);
+ CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
CONVERT_NUMBER_CHECKED(int, index, Int32, args[3]);
// Get the frame where the debugging is performed.
// Find the requested scope.
int n = 0;
- ScopeIterator it(isolate, frame, inlined_frame_index);
+ ScopeIterator it(isolate, frame, inlined_jsframe_index);
for (; !it.Done() && n < index; it.Next()) {
n++;
}
Handle<JSFunction> function,
Handle<Context> base,
JavaScriptFrame* frame,
- int inlined_frame_index) {
+ int inlined_jsframe_index) {
HandleScope scope(isolate);
List<Handle<ScopeInfo> > scope_chain;
List<Handle<Context> > context_chain;
- ScopeIterator it(isolate, frame, inlined_frame_index);
+ ScopeIterator it(isolate, frame, inlined_jsframe_index);
for (; it.Type() != ScopeIterator::ScopeTypeGlobal &&
it.Type() != ScopeIterator::ScopeTypeLocal ; it.Next()) {
ASSERT(!it.Done());
// Runtime_DebugEvaluate.
static Handle<Object> GetArgumentsObject(Isolate* isolate,
JavaScriptFrame* frame,
- int inlined_frame_index,
- Handle<JSFunction> function,
+ FrameInspector* frame_inspector,
Handle<ScopeInfo> scope_info,
Handle<Context> function_context) {
// Try to find the value of 'arguments' to pass as parameter. If it is not
}
}
- FrameInspector frame_inspector(frame, inlined_frame_index, isolate);
-
- int length = frame_inspector.GetParametersCount();
+ Handle<JSFunction> function(JSFunction::cast(frame_inspector->GetFunction()));
+ int length = frame_inspector->GetParametersCount();
Handle<JSObject> arguments =
isolate->factory()->NewArgumentsObject(function, length);
Handle<FixedArray> array = isolate->factory()->NewFixedArray(length);
AssertNoAllocation no_gc;
WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
for (int i = 0; i < length; i++) {
- array->set(i, frame_inspector.GetParameter(i), mode);
+ array->set(i, frame_inspector->GetParameter(i), mode);
}
arguments->set_elements(*array);
return arguments;
}
}
CONVERT_CHECKED(Smi, wrapped_id, args[1]);
- CONVERT_NUMBER_CHECKED(int, inlined_frame_index, Int32, args[2]);
+ CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
CONVERT_ARG_CHECKED(String, source, 3);
CONVERT_BOOLEAN_CHECKED(disable_break, args[4]);
Handle<Object> additional_context(args[5]);
StackFrame::Id id = UnwrapFrameId(wrapped_id);
JavaScriptFrameIterator it(isolate, id);
JavaScriptFrame* frame = it.frame();
- Handle<JSFunction> function(JSFunction::cast(frame->function()));
+ FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
+ Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction()));
Handle<ScopeInfo> scope_info(function->shared()->scope_info());
// Traverse the saved contexts chain to find the active context for the
#endif
// Materialize the content of the local scope into a JSObject.
- Handle<JSObject> local_scope = MaterializeLocalScope(
- isolate, frame, inlined_frame_index);
+ Handle<JSObject> local_scope = MaterializeLocalScopeWithFrameInspector(
+ isolate, frame, &frame_inspector);
RETURN_IF_EMPTY_HANDLE(isolate, local_scope);
// Allocate a new context for the debug evaluation and set the extension
go_between,
context,
frame,
- inlined_frame_index);
+ inlined_jsframe_index);
if (additional_context->IsJSObject()) {
Handle<JSObject> extension = Handle<JSObject>::cast(additional_context);
Handle<Object> arguments = GetArgumentsObject(isolate,
frame,
- inlined_frame_index,
- function,
+ &frame_inspector,
scope_info,
function_context);
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
PutSection(static_cast<int>(integer & 0x7f), "IntLastPart");
}
-#ifdef DEBUG
-
-void Deserializer::Synchronize(const char* tag) {
- int data = source_->Get();
- // If this assert fails then that indicates that you have a mismatch between
- // the number of GC roots when serializing and deserializing.
- ASSERT_EQ(kSynchronize, data);
- do {
- int character = source_->Get();
- if (character == 0) break;
- if (FLAG_debug_serialization) {
- PrintF("%c", character);
- }
- } while (true);
- if (FLAG_debug_serialization) {
- PrintF("\n");
- }
-}
-
-
-void Serializer::Synchronize(const char* tag) {
- sink_->Put(kSynchronize, tag);
- int character;
- do {
- character = *tag++;
- sink_->PutSection(character, "TagCharacter");
- } while (character != 0);
-}
-
-#endif
Serializer::Serializer(SnapshotByteSink* sink)
: sink_(sink),
// Deserialize a single object and the objects reachable from it.
void DeserializePartial(Object** root);
-#ifdef DEBUG
- virtual void Synchronize(const char* tag);
-#endif
-
private:
virtual void VisitPointers(Object** start, Object** end);
SerializationAddressMapper* address_mapper() { return &address_mapper_; }
void PutRoot(
int index, HeapObject* object, HowToCode how, WhereToPoint where);
-#ifdef DEBUG
- virtual void Synchronize(const char* tag);
-#endif
protected:
static const int kInvalidRootIndex = -1;
2 * heap()->ReservedSemiSpaceSize());
ASSERT(IsAddressAligned(chunk_base_, 2 * reserved_semispace_capacity, 0));
- if (!to_space_.SetUp(chunk_base_,
- initial_semispace_capacity,
- maximum_semispace_capacity)) {
- return false;
- }
- if (!from_space_.SetUp(chunk_base_ + reserved_semispace_capacity,
- initial_semispace_capacity,
- maximum_semispace_capacity)) {
+ to_space_.SetUp(chunk_base_,
+ initial_semispace_capacity,
+ maximum_semispace_capacity);
+ from_space_.SetUp(chunk_base_ + reserved_semispace_capacity,
+ initial_semispace_capacity,
+ maximum_semispace_capacity);
+ if (!to_space_.Commit()) {
return false;
}
// -----------------------------------------------------------------------------
// SemiSpace implementation
-bool SemiSpace::SetUp(Address start,
+void SemiSpace::SetUp(Address start,
int initial_capacity,
int maximum_capacity) {
// Creates a space in the young generation. The constructor does not
object_mask_ = address_mask_ | kHeapObjectTagMask;
object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag;
age_mark_ = start_;
-
- return Commit();
}
bool SemiSpace::GrowTo(int new_capacity) {
+ if (!is_committed()) {
+ if (!Commit()) return false;
+ }
ASSERT((new_capacity & Page::kPageAlignmentMask) == 0);
ASSERT(new_capacity <= maximum_capacity_);
ASSERT(new_capacity > capacity_);
current_page_(NULL) { }
// Sets up the semispace using the given chunk.
- bool SetUp(Address start, int initial_capacity, int maximum_capacity);
+ void SetUp(Address start, int initial_capacity, int maximum_capacity);
// Tear down the space. Heap memory was not allocated by the space, so it
// is not deallocated here.
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
class CompareOperation;
class CompilationInfo;
class CountOperation;
+class Expression;
class Property;
class SmallMapList;
class UnaryOperation;
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// -----------------------------------------------------------------------------
// Forward declarations for frequently used classes
-// (sorted alphabetically)
class AccessorInfo;
class Allocation;
class Arguments;
class Assembler;
class AssertNoAllocation;
-class BreakableStatement;
class Code;
class CodeGenerator;
class CodeStub;
class DebugInfo;
class Descriptor;
class DescriptorArray;
-class Expression;
class ExternalReference;
class FixedArray;
-class FunctionLiteral;
class FunctionTemplateInfo;
class MemoryChunk;
class SeededNumberDictionary;
class HeapObject;
class IC;
class InterceptorInfo;
-class IterationStatement;
class JSArray;
class JSFunction;
class JSObject;
class MapSpace;
class MarkCompactCollector;
class NewSpace;
-class NodeVisitor;
class Object;
class MaybeObject;
class OldSpace;
-class Property;
class Foreign;
-class RegExpNode;
-struct RegExpCompileData;
-class RegExpTree;
-class RegExpCompiler;
-class RegExpVisitor;
class Scope;
class ScopeInfo;
class Script;
-class Slot;
class Smi;
template <typename Config, class Allocator = FreeStoreAllocationPolicy>
class SplayTree;
-class Statement;
class String;
class Struct;
-class SwitchStatement;
-class AstVisitor;
class Variable;
-class VariableProxy;
class RelocInfo;
class Deserializer;
class MessageLocation;
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 3
#define MINOR_VERSION 8
-#define BUILD_NUMBER 8
+#define BUILD_NUMBER 9
#define PATCH_LEVEL 0
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
#ifndef V8_X64_ASSEMBLER_X64_INL_H_
#define V8_X64_ASSEMBLER_X64_INL_H_
+#include "x64/assembler-x64.h"
+
#include "cpu.h"
#include "debug.h"
#include "v8memory.h"
__ bind(&invoke);
__ call(rdx);
+ masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
// Leave frame and return.
LeaveArgumentsAdaptorFrame(masm);
__ ret(0);
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
ASSERT(Translation::BEGIN == opcode);
USE(opcode);
int count = iterator.Next();
+ iterator.Skip(1); // Drop JS frame count.
ASSERT(count == 1);
USE(count);
opcode = static_cast<Translation::Opcode>(iterator.Next());
USE(opcode);
- ASSERT(Translation::FRAME == opcode);
+ ASSERT(Translation::JS_FRAME == opcode);
unsigned node_id = iterator.Next();
USE(node_id);
ASSERT(node_id == ast_id);
output_ = new FrameDescription*[1];
output_[0] = new(output_frame_size) FrameDescription(
output_frame_size, function_);
-#ifdef DEBUG
- output_[0]->SetKind(Code::OPTIMIZED_FUNCTION);
-#endif
+ output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT);
// Clear the incoming parameters in the optimized frame to avoid
// confusing the garbage collector.
}
-void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
- int frame_index) {
- // Read the ast node id, function, and frame height for this output frame.
- Translation::Opcode opcode =
- static_cast<Translation::Opcode>(iterator->Next());
- USE(opcode);
- ASSERT(Translation::FRAME == opcode);
+void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
+ int frame_index) {
+ JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
+ unsigned height = iterator->Next();
+ unsigned height_in_bytes = height * kPointerSize;
+ if (FLAG_trace_deopt) {
+ PrintF(" translating arguments adaptor => height=%d\n", height_in_bytes);
+ }
+
+ unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize;
+ unsigned input_frame_size = input_->GetFrameSize();
+ unsigned output_frame_size = height_in_bytes + fixed_frame_size;
+
+ // Allocate and store the output frame description.
+ FrameDescription* output_frame =
+ new(output_frame_size) FrameDescription(output_frame_size, function);
+ output_frame->SetFrameType(StackFrame::ARGUMENTS_ADAPTOR);
+
+ // Arguments adaptor can not be topmost or bottommost.
+ ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
+ ASSERT(output_[frame_index] == NULL);
+ output_[frame_index] = output_frame;
+
+ // The top address of the frame is computed from the previous
+ // frame's top and this frame's size.
+ intptr_t top_address;
+ top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
+ output_frame->SetTop(top_address);
+
+ // Compute the incoming parameter translation.
+ int parameter_count = height;
+ unsigned output_offset = output_frame_size;
+ unsigned input_offset = input_frame_size;
+ for (int i = 0; i < parameter_count; ++i) {
+ output_offset -= kPointerSize;
+ DoTranslateCommand(iterator, frame_index, output_offset);
+ }
+ input_offset -= (parameter_count * kPointerSize);
+
+ // Read caller's PC from the previous frame.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ intptr_t callers_pc = output_[frame_index - 1]->GetPc();
+ output_frame->SetFrameSlot(output_offset, callers_pc);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; caller's pc\n",
+ top_address + output_offset, output_offset, callers_pc);
+ }
+
+ // Read caller's FP from the previous frame, and set this frame's FP.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ intptr_t value = output_[frame_index - 1]->GetFp();
+ output_frame->SetFrameSlot(output_offset, value);
+ intptr_t fp_value = top_address + output_offset;
+ output_frame->SetFp(fp_value);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; caller's fp\n",
+ fp_value, output_offset, value);
+ }
+
+ // A marker value is used in place of the context.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ intptr_t context = reinterpret_cast<intptr_t>(
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+ output_frame->SetFrameSlot(output_offset, context);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; context (adaptor sentinel)\n",
+ top_address + output_offset, output_offset, context);
+ }
+
+ // The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ value = reinterpret_cast<intptr_t>(function);
+ output_frame->SetFrameSlot(output_offset, value);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; function\n",
+ top_address + output_offset, output_offset, value);
+ }
+
+ // Number of incoming arguments.
+ output_offset -= kPointerSize;
+ input_offset -= kPointerSize;
+ value = reinterpret_cast<intptr_t>(Smi::FromInt(height - 1));
+ output_frame->SetFrameSlot(output_offset, value);
+ if (FLAG_trace_deopt) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; argc (%d)\n",
+ top_address + output_offset, output_offset, value, height - 1);
+ }
+
+ ASSERT(0 == output_offset);
+
+ Builtins* builtins = isolate_->builtins();
+ Code* adaptor_trampoline =
+ builtins->builtin(Builtins::kArgumentsAdaptorTrampoline);
+ intptr_t pc_value = reinterpret_cast<intptr_t>(
+ adaptor_trampoline->instruction_start() +
+ isolate_->heap()->arguments_adaptor_deopt_pc_offset()->value());
+ output_frame->SetPc(pc_value);
+}
+
+
+void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
+ int frame_index) {
int node_id = iterator->Next();
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
unsigned height = iterator->Next();
// Allocate and store the output frame description.
FrameDescription* output_frame =
new(output_frame_size) FrameDescription(output_frame_size, function);
-#ifdef DEBUG
- output_frame->SetKind(Code::FUNCTION);
-#endif
+ output_frame->SetFrameType(StackFrame::JAVA_SCRIPT);
bool is_bottommost = (0 == frame_index);
bool is_topmost = (output_count_ - 1 == frame_index);
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
class StandardFrameConstants : public AllStatic {
public:
+ // Fixed part of the frame consists of return address, caller fp,
+ // context and function.
+ static const int kFixedFrameSize = 4 * kPointerSize;
static const int kExpressionsOffset = -3 * kPointerSize;
static const int kMarkerOffset = -2 * kPointerSize;
static const int kContextOffset = -1 * kPointerSize;
class ArgumentsAdaptorFrameConstants : public AllStatic {
public:
static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + kPointerSize;
};
// Load the key (consisting of map and symbol) from the cache and
// check for match.
- Label try_second_entry, hit_on_first_entry, load_in_object_property;
+ Label load_in_object_property;
+ static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket;
+ Label hit_on_nth_entry[kEntriesPerBucket];
ExternalReference cache_keys
= ExternalReference::keyed_lookup_cache_keys(masm->isolate());
- __ movq(rdi, rcx);
- __ shl(rdi, Immediate(kPointerSizeLog2 + 1));
- __ LoadAddress(kScratchRegister, cache_keys);
- __ cmpq(rbx, Operand(kScratchRegister, rdi, times_1, 0));
- __ j(not_equal, &try_second_entry);
- __ cmpq(rax, Operand(kScratchRegister, rdi, times_1, kPointerSize));
- __ j(equal, &hit_on_first_entry);
-
- __ bind(&try_second_entry);
- __ cmpq(rbx, Operand(kScratchRegister, rdi, times_1, kPointerSize * 2));
+
+ for (int i = 0; i < kEntriesPerBucket - 1; i++) {
+ Label try_next_entry;
+ __ movq(rdi, rcx);
+ __ shl(rdi, Immediate(kPointerSizeLog2 + 1));
+ __ LoadAddress(kScratchRegister, cache_keys);
+ int off = kPointerSize * i * 2;
+ __ cmpq(rbx, Operand(kScratchRegister, rdi, times_1, off));
+ __ j(not_equal, &try_next_entry);
+ __ cmpq(rax, Operand(kScratchRegister, rdi, times_1, off + kPointerSize));
+ __ j(equal, &hit_on_nth_entry[i]);
+ __ bind(&try_next_entry);
+ }
+
+ int off = kPointerSize * (kEntriesPerBucket - 1) * 2;
+ __ cmpq(rbx, Operand(kScratchRegister, rdi, times_1, off));
__ j(not_equal, &slow);
- __ cmpq(rax, Operand(kScratchRegister, rdi, times_1, kPointerSize * 3));
+ __ cmpq(rax, Operand(kScratchRegister, rdi, times_1, off + kPointerSize));
__ j(not_equal, &slow);
// Get field offset, which is a 32-bit integer.
ExternalReference cache_field_offsets
= ExternalReference::keyed_lookup_cache_field_offsets(masm->isolate());
- // Hit on second entry.
- __ LoadAddress(kScratchRegister, cache_field_offsets);
- __ addl(rcx, Immediate(1));
- __ movl(rdi, Operand(kScratchRegister, rcx, times_4, 0));
- __ movzxbq(rcx, FieldOperand(rbx, Map::kInObjectPropertiesOffset));
- __ subq(rdi, rcx);
- __ j(above_equal, &property_array_property);
- __ jmp(&load_in_object_property);
-
- // Hit on first entry.
- __ bind(&hit_on_first_entry);
- __ LoadAddress(kScratchRegister, cache_field_offsets);
- __ movl(rdi, Operand(kScratchRegister, rcx, times_4, 0));
- __ movzxbq(rcx, FieldOperand(rbx, Map::kInObjectPropertiesOffset));
- __ subq(rdi, rcx);
- __ j(above_equal, &property_array_property);
+ // Hit on nth entry.
+ for (int i = kEntriesPerBucket - 1; i >= 0; i--) {
+ __ bind(&hit_on_nth_entry[i]);
+ if (i != 0) {
+ __ addl(rcx, Immediate(i));
+ }
+ __ LoadAddress(kScratchRegister, cache_field_offsets);
+ __ movl(rdi, Operand(kScratchRegister, rcx, times_4, 0));
+ __ movzxbq(rcx, FieldOperand(rbx, Map::kInObjectPropertiesOffset));
+ __ subq(rdi, rcx);
+ __ j(above_equal, &property_array_property);
+ if (i != 0) {
+ __ jmp(&load_in_object_property);
+ }
+ }
// Load in-object property.
__ bind(&load_in_object_property);
WriteTranslation(environment->outer(), translation);
int closure_id = DefineDeoptimizationLiteral(environment->closure());
- translation->BeginFrame(environment->ast_id(), closure_id, height);
+ if (environment->is_arguments_adaptor()) {
+ translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
+ } else {
+ translation->BeginJSFrame(environment->ast_id(), closure_id, height);
+ }
for (int i = 0; i < translation_size; ++i) {
LOperand* value = environment->values()->at(i);
// spilled_registers_ and spilled_double_registers_ are either
// |>------------ translation_size ------------<|
int frame_count = 0;
+ int jsframe_count = 0;
for (LEnvironment* e = environment; e != NULL; e = e->outer()) {
++frame_count;
+ if (!e->is_arguments_adaptor()) {
+ ++jsframe_count;
+ }
}
- Translation translation(&translations_, frame_count);
+ Translation translation(&translations_, frame_count, jsframe_count);
WriteTranslation(environment, &translation);
int deoptimization_index = deoptimizations_.length();
int pc_offset = masm()->pc_offset();
LEnvironment* outer =
CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator);
int ast_id = hydrogen_env->ast_id();
- ASSERT(ast_id != AstNode::kNoNumber);
+ ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor());
int value_count = hydrogen_env->length();
LEnvironment* result = new LEnvironment(hydrogen_env->closure(),
+ hydrogen_env->is_arguments_adaptor(),
ast_id,
hydrogen_env->parameter_count(),
argument_count_,
value_count,
outer);
+ int argument_index = *argument_index_accumulator;
for (int i = 0; i < value_count; ++i) {
if (hydrogen_env->is_special_index(i)) continue;
if (value->IsArgumentsObject()) {
op = NULL;
} else if (value->IsPushArgument()) {
- op = new LArgument((*argument_index_accumulator)++);
+ op = new LArgument(argument_index++);
} else {
op = UseAny(value);
}
result->AddValue(op, value->representation());
}
+ if (!hydrogen_env->is_arguments_adaptor()) {
+ *argument_index_accumulator = argument_index;
+ }
+
return result;
}
LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement(
HLoadKeyedSpecializedArrayElement* instr) {
ElementsKind elements_kind = instr->elements_kind();
- Representation representation(instr->representation());
ASSERT(
- (representation.IsInteger32() &&
+ (instr->representation().IsInteger32() &&
(elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
(elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
- (representation.IsDouble() &&
+ (instr->representation().IsDouble() &&
((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
(elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
ASSERT(instr->key()->representation().IsInteger32());
LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement(
HStoreKeyedSpecializedArrayElement* instr) {
- Representation representation(instr->value()->representation());
ElementsKind elements_kind = instr->elements_kind();
ASSERT(
- (representation.IsInteger32() &&
+ (instr->value()->representation().IsInteger32() &&
(elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
(elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
- (representation.IsDouble() &&
+ (instr->value()->representation().IsDouble() &&
((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
(elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
ASSERT(instr->external_pointer()->representation().IsExternal());
HEnvironment* outer = current_block_->last_environment();
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
+ instr->arguments_count(),
instr->function(),
undefined,
instr->call_kind());
LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
- HEnvironment* outer = current_block_->last_environment()->outer();
+ HEnvironment* outer = current_block_->last_environment()->
+ DiscardInlined(false);
current_block_->UpdateEnvironment(outer);
return NULL;
}
templ->Set("x", v8_num(200));
templ->SetAccessor(v8_str("m"), GetM);
LocalContext env(0, templ);
- v8::Handle<v8::Object> obj(env->Global());
v8::Handle<Script> script(v8_compile("dummy()"));
v8::Handle<Value> result(script->Run());
CHECK_EQ(13.4, result->NumberValue());
env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
call_recursively_script = v8_compile("callScriptRecursively()");
- v8::Handle<Value> result(call_recursively_script->Run());
+ call_recursively_script->Run();
call_recursively_script = v8::Handle<Script>();
env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
source = v8_str("undetectable.y = 2000;");
script = Script::Compile(source);
- Local<Value> result(script->Run());
+ script->Run();
ExpectBoolean("undetectable.y == undefined", true);
}
const char* extension_names[] = { name };
v8::ExtensionConfiguration extensions(1, extension_names);
v8::Handle<Context> context(Context::New(&extensions));
- ASSERT(context.IsEmpty());
+ CHECK(context.IsEmpty());
}
+
THREADED_TEST(NativeFunctionDeclarationErrorEscape) {
v8::HandleScope handle_scope;
const char* name = "nativedeclerresc";
const char* extension_names[] = { name };
v8::ExtensionConfiguration extensions(1, extension_names);
v8::Handle<Context> context(Context::New(&extensions));
- ASSERT(context.IsEmpty());
+ CHECK(context.IsEmpty());
}
Local<Script> script =
Script::Compile(String::New(js_code_causing_huge_string_flattening));
last_location = NULL;
- Local<Value> result(script->Run());
+ script->Run();
CHECK(false); // Should not return.
}
v8::Handle<String> message = v8_str("message");
v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
CHECK(range_error->IsObject());
- v8::Handle<v8::Object> range_obj(range_error.As<v8::Object>());
CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
CHECK(reference_error->IsObject());
// Create new environment reusing the global object.
LocalContext env(NULL, instance_template, global_object);
env->Global()->Set(v8_str("foo"), foo);
- Local<Value> value(Script::Compile(v8_str("foo()"))->Run());
+ Script::Compile(v8_str("foo()"))->Run();
}
}
context->Global()->Set(v8_str("Fun"), cons);
Local<v8::Object> inst = cons->NewInstance();
i::Handle<i::JSObject> obj(v8::Utils::OpenHandle(*inst));
+ CHECK(obj->IsJSObject());
Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
CHECK(value->BooleanValue());
}
{ Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
Local<ObjectTemplate> instance_template(t->InstanceTemplate());
+ USE(instance_template);
Local<v8::Object> instance = t->GetFunction()->NewInstance();
context->Global()->Set(v8_str("obj2"), instance);
v8::TryCatch try_catch;
0, 0, 0, v8_str("data"));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
- v8::Handle<Value> value(CompileRun(
- "for (var i = 0; i < 1000; i++) {"
- " o.x = 42;"
- "}"));
+ CompileRun(
+ "for (var i = 0; i < 1000; i++) {"
+ " o.x = 42;"
+ "}");
}
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"var result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = o.method(41);"
- "}"));
+ "}");
CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
CHECK_EQ(100, interceptor_call_count);
}
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
"var result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = receiver.method(41);"
- "}"));
+ "}");
CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
CHECK_EQ(100, interceptor_call_count);
}
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
" saved_result = result;"
" receiver = {method: function(x) { return x - 1 }};"
" }"
- "}"));
+ "}");
CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
CHECK_GE(interceptor_call_count, 50);
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
" saved_result = result;"
" o.method = function(x) { return x - 1 };"
" }"
- "}"));
+ "}");
CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
CHECK_GE(interceptor_call_count, 50);
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
v8::TryCatch try_catch;
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
" saved_result = result;"
" receiver = 333;"
" }"
- "}"));
+ "}");
CHECK(try_catch.HasCaught());
CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
try_catch.Exception()->ToString());
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
v8::TryCatch try_catch;
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
" saved_result = result;"
" receiver = {method: receiver.method};"
" }"
- "}"));
+ "}");
CHECK(try_catch.HasCaught());
CHECK_EQ(v8_str("TypeError: Illegal invocation"),
try_catch.Exception()->ToString());
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
+ USE(templ);
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"var result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = o.method(41);"
- "}"));
+ "}");
CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
}
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
+ CHECK(!templ.IsEmpty());
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
"var result = 0;"
"for (var i = 0; i < 100; i++) {"
" result = receiver.method(41);"
- "}"));
+ "}");
CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
}
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
+ CHECK(!templ.IsEmpty());
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
" saved_result = result;"
" receiver = {method: function(x) { return x - 1 }};"
" }"
- "}"));
+ "}");
CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
}
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
+ CHECK(!templ.IsEmpty());
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
context->Global()->Set(v8_str("o"), fun->NewInstance());
v8::TryCatch try_catch;
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"o.foo = 17;"
"var receiver = {};"
"receiver.__proto__ = o;"
" saved_result = result;"
" receiver = 333;"
" }"
- "}"));
+ "}");
CHECK(try_catch.HasCaught());
CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
try_catch.Exception()->ToString());
templ->SetNamedPropertyHandler(NoBlockGetterX);
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"proto = new Object();"
"proto.y = function(x) { return x + 1; };"
"proto.z = function(x) { return x - 1; };"
"for (var i = 0; i < 10; i++) {"
" if (i == 5) { method = 'z'; };"
" result += o[method](41);"
- "}"));
+ "}");
CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
}
context->Global()->Set(v8_str("proto1"), templ->NewInstance());
keyed_call_ic_function =
v8_compile("function f(x) { return x - 1; }; f")->Run();
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"o = new Object();"
"proto2 = new Object();"
"o.y = function(x) { return x + 1; };"
"for (var i = 0; i < 10; i++) {"
" if (i == 5) { method = 'y'; };"
" result += o[method](41);"
- "}"));
+ "}");
CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
}
templ->SetNamedPropertyHandler(NoBlockGetterX);
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"function inc(x) { return x + 1; };"
"inc(1);"
"function dec(x) { return x - 1; };"
"for (var i = 0; i < 10; i++) {"
" if (i == 5) { method = 'y'; };"
" result += o[method](41);"
- "}"));
+ "}");
CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
}
LocalContext context;
context->Global()->Set(v8_str("o"), templ_o->NewInstance());
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"function len(x) { return x.length; };"
"o.__proto__ = this;"
"var m = 'parseFloat';"
" saved_result = result;"
" };"
" result = o[m]('239');"
- "}"));
+ "}");
CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
}
LocalContext context;
context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"var o = new Object();"
"o.__proto__ = proto;"
"o.method = function(x) { return x + 1; };"
"for (var i = 0; i < 10; i++) {"
" if (i == 5) { o.method = function(x) { return x - 1; }; };"
" result += o[m](41);"
- "}"));
+ "}");
CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
}
LocalContext context;
context->Global()->Set(v8_str("o"), templ_o->NewInstance());
- v8::Handle<Value> value(CompileRun(
+ CompileRun(
"var proto = new Object();"
"o.__proto__ = proto;"
"proto.method = function(x) { return x + 1; };"
"for (var i = 0; i < 10; i++) {"
" if (i == 5) { proto.method = function(x) { return x - 1; }; };"
" result += o[m](41);"
- "}"));
+ "}");
CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
}
env->Enter();
v8::Handle<Value> value = NestedScope(env);
v8::Handle<String> str(value->ToString());
+ CHECK(!str.IsEmpty());
env->Exit();
env.Dispose();
}
THREADED_TEST(ExternalAllocatedMemory) {
v8::HandleScope outer;
v8::Persistent<Context> env(Context::New());
+ CHECK(!env.IsEmpty());
const int kSize = 1024*1024;
CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
i::FunctionTemplateInfo::cast(internal_template->constructor()));
CHECK(!constructor->access_check_info()->IsUndefined());
v8::Persistent<Context> context0(Context::New(NULL, global_template));
+ CHECK(!context0.IsEmpty());
CHECK(!constructor->access_check_info()->IsUndefined());
}
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(
- 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,
v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
v8::Handle<Value> overview_result(
v8::Script::New(overview_src, origin)->Run());
- ASSERT(!overview_result.IsEmpty());
- ASSERT(overview_result->IsObject());
+ CHECK(!overview_result.IsEmpty());
+ CHECK(overview_result->IsObject());
// Test getting DETAILED information.
const char *detailed_source =
v8::Handle<v8::Script> detailed_script(
v8::Script::New(detailed_src, &detailed_origin));
v8::Handle<Value> detailed_result(detailed_script->Run());
- ASSERT(!detailed_result.IsEmpty());
- ASSERT(detailed_result->IsObject());
+ CHECK(!detailed_result.IsEmpty());
+ CHECK(detailed_result->IsObject());
}
v8::HandleScope scope;
v8::TryCatch tc;
v8::Handle<v8::String> str(args[0]->ToString());
+ USE(str);
if (tc.HasCaught())
return tc.ReThrow();
return v8::Undefined();
CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
}
+THREADED_TEST(FunctionGetInferredName) {
+ v8::HandleScope scope;
+ LocalContext env;
+ v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
+ v8::Handle<v8::String> script = v8::String::New(
+ "var foo = { bar : { baz : function() {}}}; var f = foo.bar.baz;");
+ v8::Script::Compile(script, &origin)->Run();
+ v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
+ env->Global()->Get(v8::String::New("f")));
+ CHECK_EQ("foo.bar.baz", *v8::String::AsciiValue(f->GetInferredName()));
+}
THREADED_TEST(ScriptLineNumber) {
v8::HandleScope scope;
// RegExps are objects on which you can set properties.
re->Set(v8_str("property"), v8::Integer::New(32));
v8::Handle<v8::Value> value(CompileRun("re.property"));
- ASSERT_EQ(32, value->Int32Value());
+ CHECK_EQ(32, value->Int32Value());
v8::TryCatch try_catch;
re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone);
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
CHECK(!fun.is_null());
bool has_pending_exception;
Handle<JSObject> global(Isolate::Current()->context()->global());
- Handle<Object> result(
- Execution::Call(fun, global, 0, NULL, &has_pending_exception));
+ Execution::Call(fun, global, 0, NULL, &has_pending_exception);
CHECK(has_pending_exception);
CHECK_EQ(42.0, Isolate::Current()->pending_exception()->
ToObjectChecked()->Number());
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
if (event == v8::Break) {
break_point_hit_count++;
- v8::Handle<v8::Function> fun(v8::Handle<v8::Function>::Cast(data));
+ CHECK(data->IsFunction());
ClearBreakPoint(debug_event_remove_break_point);
}
}
// Test IC store break point with garbage collection.
{
- v8::Local<v8::Function> bar(
- CompileFunction(&env, "function foo(){}", "foo"));
+ CompileFunction(&env, "function foo(){}", "foo");
foo = CompileFunction(&env, "function foo(){bar=0;}", "foo");
SetBreakPoint(foo, 0);
}
// Test IC load break point with garbage collection.
{
- v8::Local<v8::Function> bar(
- CompileFunction(&env, "function foo(){}", "foo"));
+ CompileFunction(&env, "function foo(){}", "foo");
foo = CompileFunction(&env, "bar=1;function foo(){var x=bar;}", "foo");
SetBreakPoint(foo, 0);
}
// Test IC call break point with garbage collection.
{
- v8::Local<v8::Function> bar(
- CompileFunction(&env, "function foo(){}", "foo"));
+ CompileFunction(&env, "function foo(){}", "foo");
foo = CompileFunction(&env,
"function bar(){};function foo(){bar();}",
"foo");
// Test return break point with garbage collection.
{
- v8::Local<v8::Function> bar(
- CompileFunction(&env, "function foo(){}", "foo"));
+ CompileFunction(&env, "function foo(){}", "foo");
foo = CompileFunction(&env, "function foo(){}", "foo");
SetBreakPoint(foo, 0);
}
// Test non IC break point with garbage collection.
{
- v8::Local<v8::Function> bar(
- CompileFunction(&env, "function foo(){}", "foo"));
+ CompileFunction(&env, "function foo(){}", "foo");
foo = CompileFunction(&env, "function foo(){var bar=0;}", "foo");
SetBreakPoint(foo, 0);
}
v8::internal::Isolate::Current()->TraceException(false);
// Create functions for testing break on exception.
- v8::Local<v8::Function> throws(
- CompileFunction(&env, "function throws(){throw 1;}", "throws"));
+ CompileFunction(&env, "function throws(){throw 1;}", "throws");
v8::Local<v8::Function> caught =
CompileFunction(&env,
"function caught(){try {throws();} catch(e) {};}",
// Get the test functions again.
v8::Local<v8::Function> foo(v8::Local<v8::Function>::Cast(
env->Global()->Get(v8::String::New("foo"))));
- v8::Local<v8::Function> bar(v8::Local<v8::Function>::Cast(
- env->Global()->Get(v8::String::New("foo"))));
foo->Call(env->Global(), 0, NULL);
CHECK_EQ(0, break_point_hit_count);
EmptyExternalStringResource source_ext_str;
v8::Local<v8::String> source = v8::String::NewExternal(&source_ext_str);
v8::Handle<v8::Script> evil_script(v8::Script::Compile(source));
+ // "use" evil_script to make the compiler happy.
+ (void) evil_script;
Handle<i::ExternalTwoByteString> i_source(
i::ExternalTwoByteString::cast(*v8::Utils::OpenHandle(*source)));
// This situation can happen if source was an external string disposed
break_point_hit_count++;
v8::HandleScope scope;
- v8::Handle<v8::String> json(message.GetJSON());
+ message.GetJSON();
SendContinueCommand();
} else if (message.IsEvent() && message.GetEvent() == v8::AfterCompile) {
isolate->stack_guard()->DebugBreak();
// Force serialization to trigger some internal JS execution.
- v8::Handle<v8::String> json(message.GetJSON());
+ message.GetJSON();
// Restore previous state.
if (is_debug_break) {
-// Copyright 2007-2010 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
v8::Local<v8::Function> fun =
v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
- Handle<v8::internal::JSFunction> f(v8::Utils::OpenHandle(*fun));
+ CHECK(!fun.IsEmpty());
}
TEST(BootUpMemoryUse) {
intptr_t initial_memory = MemoryInUse();
+ FLAG_crankshaft = false; // Avoid flakiness.
// Only Linux has the proc filesystem and only if it is mapped. If it's not
// there we just skip the test.
if (initial_memory >= 0) {
InitializeVM();
intptr_t booted_memory = MemoryInUse();
- CHECK_LE(booted_memory - initial_memory, 16 * 1024 * 1024);
+ if (sizeof(initial_memory) == 8) {
+ if (v8::internal::Snapshot::IsEnabled()) {
+ CHECK_LE(booted_memory - initial_memory, 6654 * 1024); // 6444.
+ } else {
+ CHECK_LE(booted_memory - initial_memory, 6777 * 1024); // 6596.
+ }
+ } else {
+ if (v8::internal::Snapshot::IsEnabled()) {
+ CHECK_LE(booted_memory - initial_memory, 6500 * 1024); // 6365.
+ } else {
+ CHECK_LE(booted_memory - initial_memory, 6654 * 1024); // 6424
+ }
+ }
}
}
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
CHECK_EQ(11, error_location.end_pos);
// Should not crash.
const char* message = pre_impl->BuildMessage();
- i::Vector<const char*> args(pre_impl->BuildArgs());
+ pre_impl->BuildArgs();
CHECK_GT(strlen(message), 0);
}
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Test function.arguments.
function A() {}
return o.g(x, "z");
}
-function stress() {
- for (var i=0; i<5000000; i++) o.g(i, "g");
- for (var j=0; j<5000000; j++) hej(j);
+function opt() {
+ for (var k=0; k<2; k++) {
+ for (var i=0; i<5; i++) o.g(i, "g");
+ for (var j=0; j<5; j++) hej(j);
+ }
+ %OptimizeFunctionOnNextCall(o.g);
+ %OptimizeFunctionOnNextCall(hej);
}
-stress();
-
+opt();
assertArrayEquals([0, "g"], o.g(0, "g"));
assertArrayEquals([1, "f"], o.g(1, "g"));
assertArrayEquals([0, "h"], hej(0));
o = new B();
-stress();
-
+opt();
assertArrayEquals([0, "f"], o.g(0, "g"));
assertArrayEquals([1, "g"], o.g(1, "g"));
assertArrayEquals([0, "f"], hej(0));
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
var testingConstructCall = false;
+var input = [
+ {a: 1, b: 2},
+ {a: 3, b: 4},
+ {a: 5, b: 6},
+ {a: 7, b: 8},
+ {a: 9, b: 10}
+];
+
+var expected = [
+ { locals: {a0: 1.01, b0: 2.02}, args: { names: ["i", "x0", "y0"], values: [0, 3.03, 4.04] } },
+ { locals: {a1: 3.03, b1: 4.04}, args: { names: ["i", "x1", "y1"], values: [1, 5.05, 6.06] } },
+ { locals: {a2: 5.05, b2: 6.06}, args: { names: ["i"], values: [2] } },
+ { locals: {a3: 7.07, b3: 8.08}, args: { names: ["i", "x3", "y3", "z3"],
+ values: [3, 9.09, 10.10, undefined] }
+ },
+ { locals: {a4: 9.09, b4: 10.10}, args: { names: ["i", "x4", "y4"], values: [4, 11.11, 12.12] } }
+];
+
+function arraySum(arr) {
+ return arr.reduce(function (a, b) { return a + b; }, 0);
+}
function listener(event, exec_state, event_data, data) {
try {
for (var i = 0; i < exec_state.frameCount(); i++) {
var frame = exec_state.frame(i);
if (i < exec_state.frameCount() - 1) {
- var expected_a = i * 2 + 1 + (i * 2 + 1) / 100;
- var expected_b = i * 2 + 2 + (i * 2 + 2) / 100;
- var expected_x = (i + 1) * 2 + 1 + ((i + 1) * 2 + 1) / 100;
- var expected_y = (i + 1) * 2 + 2 + ((i + 1) * 2 + 2) / 100;
-
- // All frames except the bottom one has normal variables a and b.
- var a = ('a' === frame.localName(0)) ? 0 : 1;
- var b = 1 - a;
- assertEquals('a', frame.localName(a));
- assertEquals('b', frame.localName(b));
- assertEquals(expected_a, frame.localValue(a).value());
- assertEquals(expected_b, frame.localValue(b).value());
-
- // All frames except the bottom one has arguments variables x and y.
- assertEquals('x', frame.argumentName(0));
- assertEquals('y', frame.argumentName(1));
- assertEquals(expected_x, frame.argumentValue(0).value());
- assertEquals(expected_y, frame.argumentValue(1).value());
+ var expected_args = expected[i].args;
+ var expected_locals = expected[i].locals;
+
+ // All frames except the bottom one have expected locals.
+ var locals = {};
+ for (var j = 0; j < frame.localCount(); j++) {
+ locals[frame.localName(j)] = frame.localValue(j).value();
+ }
+ assertPropertiesEqual(expected_locals, locals);
+
+ // All frames except the bottom one have expected arguments.
+ for (var j = 0; j < expected_args.names.length; j++) {
+ assertEquals(expected_args.names[j], frame.argumentName(j));
+ assertEquals(expected_args.values[j], frame.argumentValue(j).value());
+ }
// All frames except the bottom one have two scopes.
assertEquals(2, frame.scopeCount());
assertEquals(debug.ScopeType.Local, frame.scope(0).scopeType());
assertEquals(debug.ScopeType.Global, frame.scope(1).scopeType());
- assertEquals(expected_a, frame.scope(0).scopeObject().value()['a']);
- assertEquals(expected_b, frame.scope(0).scopeObject().value()['b']);
- assertEquals(expected_x, frame.scope(0).scopeObject().value()['x']);
- assertEquals(expected_y, frame.scope(0).scopeObject().value()['y']);
+
+ Object.keys(expected_locals).forEach(function (name) {
+ assertEquals(expected_locals[name], frame.scope(0).scopeObject().value()[name]);
+ });
+
+ for (var j = 0; j < expected_args.names.length; j++) {
+ var arg_name = expected_args.names[j];
+ var arg_value = expected_args.values[j];
+ assertEquals(arg_value, frame.scope(0).scopeObject().value()[arg_name]);
+ }
// Evaluate in the inlined frame.
- assertEquals(expected_a, frame.evaluate('a').value());
- assertEquals(expected_x, frame.evaluate('x').value());
- assertEquals(expected_x, frame.evaluate('arguments[0]').value());
- assertEquals(expected_a + expected_b + expected_x + expected_y,
- frame.evaluate('a + b + x + y').value());
- assertEquals(expected_x + expected_y,
- frame.evaluate('arguments[0] + arguments[1]').value());
+ Object.keys(expected_locals).forEach(function (name) {
+ assertEquals(expected_locals[name], frame.evaluate(name).value());
+ });
+
+ for (var j = 0; j < expected_args.names.length; j++) {
+ var arg_name = expected_args.names[j];
+ var arg_value = expected_args.values[j];
+ assertEquals(arg_value, frame.evaluate(arg_name).value());
+ assertEquals(arg_value, frame.evaluate('arguments['+j+']').value());
+ }
+
+ var expected_args_sum = arraySum(expected_args.values);
+ var expected_locals_sum =
+ arraySum(Object.keys(expected_locals).
+ map(function (k) { return expected_locals[k]; }));
+
+ assertEquals(expected_locals_sum + expected_args_sum,
+ frame.evaluate(Object.keys(expected_locals).join('+') + ' + ' +
+ expected_args.names.join('+')).value());
+
+ var arguments_sum = expected_args.names.map(function(_, idx) {
+ return "arguments[" + idx + "]";
+ }).join('+');
+ assertEquals(expected_args_sum,
+ frame.evaluate(arguments_sum).value());
} else {
// The bottom frame only have the global scope.
assertEquals(1, frame.scopeCount());
listenerComplete = true;
}
} catch (e) {
- exception = e
+ exception = e.toString() + e.stack;
};
};
-f();f();f();
+for (var i = 0; i < 4; i++) f(input.length - 1, 11.11, 12.12);
%OptimizeFunctionOnNextCall(f);
-f();
+f(input.length - 1, 11.11, 12.12);
// Add the debug event listener.
Debug.setListener(listener);
-function h(x, y) {
- var a = 1;
- var b = 2;
- a = a + a / 100;
- b = b + b / 100;
+function h(i, x0, y0) {
+ var a0 = input[i].a;
+ var b0 = input[i].b;
+ a0 = a0 + a0 / 100;
+ b0 = b0 + b0 / 100;
debugger; // Breakpoint.
};
-function g3(x, y) {
- var a = 3;
- var b = 4;
- a = a + a / 100;
- b = b + b / 100;
- h(a, b);
- return a+b;
+function g3(i, x1, y1) {
+ var a1 = input[i].a;
+ var b1 = input[i].b;
+ a1 = a1 + a1 / 100;
+ b1 = b1 + b1 / 100;
+ h(i - 1, a1, b1);
+ return a1+b1;
};
-function g2(x, y) {
- var a = 5;
- var b = 6;
- a = a + a / 100;
- b = b + b / 100;
- g3(a, b);
+function g2(i) {
+ var a2 = input[i].a;
+ var b2 = input[i].b;
+ a2 = a2 + a2 / 100;
+ b2 = b2 + b2 / 100;
+ g3(i - 1, a2, b2);
};
-function g1(x, y) {
- var a = 7;
- var b = 8;
- a = a + a / 100;
- b = b + b / 100;
- g2(a, b);
+function g1(i, x3, y3, z3) {
+ var a3 = input[i].a;
+ var b3 = input[i].b;
+ a3 = a3 + a3 / 100;
+ b3 = b3 + b3 / 100;
+ g2(i - 1, a3, b3);
};
-function f(x, y) {
- var a = 9;
- var b = 10;
- a = a + a / 100;
- b = b + b / 100;
- g1(a, b);
+function f(i, x4, y4) {
+ var a4 = input[i].a;
+ var b4 = input[i].b;
+ a4 = a4 + a4 / 100;
+ b4 = b4 + b4 / 100;
+ g1(i - 1, a4, b4);
};
// Test calling f normally and as a constructor.
-f(11.11, 12.12);
+f(input.length - 1, 11.11, 12.12);
+f(input.length - 1, 11.11, 12.12, "");
testingConstructCall = true;
-new f(11.11, 12.12);
+new f(input.length - 1, 11.11, 12.12);
+new f(input.length - 1, 11.11, 12.12, "");
// Make sure that the debug event listener vas invoked.
assertFalse(exception, "exception in listener " + exception)
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
var testingConstructCall = false;
+var expected = [
+ { locals: {a0: 1, b0: 2}, args: { names: ["i", "x0", "y0"], values: [0, 3, 4] } },
+ { locals: {a1: 3, b1: 4}, args: { names: ["i", "x1", "y1"], values: [1, 5, 6] } },
+ { locals: {a2: 5, b2: 6}, args: { names: ["i"], values: [2] } },
+ { locals: {a3: 7, b3: 8}, args: { names: ["i", "x3", "y3", "z3"], values: [3, 9, 10, undefined] } },
+ { locals: {a4: 9, b4: 10}, args: { names: ["i", "x4", "y4"], values: [4, 11, 12] } }
+];
+
+function arraySum(arr) {
+ return arr.reduce(function (a, b) { return a + b; }, 0);
+}
function listener(event, exec_state, event_data, data) {
try {
for (var i = 0; i < exec_state.frameCount(); i++) {
var frame = exec_state.frame(i);
if (i < exec_state.frameCount() - 1) {
- var expected_a = i * 2 + 1;
- var expected_b = i * 2 + 2;
- var expected_x = (i + 1) * 2 + 1;
- var expected_y = (i + 1) * 2 + 2;
-
- // All frames except the bottom one has normal variables a and b.
- var a = ('a' === frame.localName(0)) ? 0 : 1;
- var b = 1 - a;
- assertEquals('a', frame.localName(a));
- assertEquals('b', frame.localName(b));
- assertEquals(expected_a, frame.localValue(a).value());
- assertEquals(expected_b, frame.localValue(b).value());
-
- // All frames except the bottom one has arguments variables x and y.
- assertEquals('x', frame.argumentName(0));
- assertEquals('y', frame.argumentName(1));
- assertEquals(expected_x, frame.argumentValue(0).value());
- assertEquals(expected_y, frame.argumentValue(1).value());
+ var expected_args = expected[i].args;
+ var expected_locals = expected[i].locals;
+
+ // All frames except the bottom one have expected locals.
+ var locals = {};
+ for (var j = 0; j < frame.localCount(); j++) {
+ locals[frame.localName(j)] = frame.localValue(j).value();
+ }
+ assertPropertiesEqual(expected_locals, locals);
+
+ // All frames except the bottom one have expected arguments.
+ for (var j = 0; j < expected_args.names.length; j++) {
+ assertEquals(expected_args.names[j], frame.argumentName(j));
+ assertEquals(expected_args.values[j], frame.argumentValue(j).value());
+ }
// All frames except the bottom one have two scopes.
assertEquals(2, frame.scopeCount());
assertEquals(debug.ScopeType.Local, frame.scope(0).scopeType());
assertEquals(debug.ScopeType.Global, frame.scope(1).scopeType());
- assertEquals(expected_a, frame.scope(0).scopeObject().value()['a']);
- assertEquals(expected_b, frame.scope(0).scopeObject().value()['b']);
- assertEquals(expected_x, frame.scope(0).scopeObject().value()['x']);
- assertEquals(expected_y, frame.scope(0).scopeObject().value()['y']);
+
+ Object.keys(expected_locals).forEach(function (name) {
+ assertEquals(expected_locals[name], frame.scope(0).scopeObject().value()[name]);
+ });
+
+ for (var j = 0; j < expected_args.names.length; j++) {
+ var arg_name = expected_args.names[j];
+ var arg_value = expected_args.values[j];
+ assertEquals(arg_value, frame.scope(0).scopeObject().value()[arg_name]);
+ }
// Evaluate in the inlined frame.
- assertEquals(expected_a, frame.evaluate('a').value());
- assertEquals(expected_x, frame.evaluate('x').value());
- assertEquals(expected_x, frame.evaluate('arguments[0]').value());
- assertEquals(expected_a + expected_b + expected_x + expected_y,
- frame.evaluate('a + b + x + y').value());
- assertEquals(expected_x + expected_y,
- frame.evaluate('arguments[0] + arguments[1]').value());
+ Object.keys(expected_locals).forEach(function (name) {
+ assertEquals(expected_locals[name], frame.evaluate(name).value());
+ });
+
+ for (var j = 0; j < expected_args.names.length; j++) {
+ var arg_name = expected_args.names[j];
+ var arg_value = expected_args.values[j];
+ assertEquals(arg_value, frame.evaluate(arg_name).value());
+ assertEquals(arg_value, frame.evaluate('arguments['+j+']').value());
+ }
+
+ var expected_args_sum = arraySum(expected_args.values);
+ var expected_locals_sum =
+ arraySum(Object.keys(expected_locals).
+ map(function (k) { return expected_locals[k]; }));
+
+ assertEquals(expected_locals_sum + expected_args_sum,
+ frame.evaluate(Object.keys(expected_locals).join('+') + ' + ' +
+ expected_args.names.join('+')).value());
+
+ var arguments_sum = expected_args.names.map(function(_, idx) {
+ return "arguments[" + idx + "]";
+ }).join('+');
+ assertEquals(expected_args_sum,
+ frame.evaluate(arguments_sum).value());
} else {
// The bottom frame only have the global scope.
assertEquals(1, frame.scopeCount());
listenerComplete = true;
}
} catch (e) {
- exception = e.stack;
+ exception = e.toString() + e.stack;
};
};
-f();f();f();
+for (var i = 0; i < 4; i++) f(expected.length - 1, 11, 12);
%OptimizeFunctionOnNextCall(f);
-f();
+f(expected.length - 1, 11, 12);
// Add the debug event listener.
Debug.setListener(listener);
-function h(x, y) {
- var a = 1;
- var b = 2;
+function h(i, x0, y0) {
+ var a0 = expected[i].locals.a0;
+ var b0 = expected[i].locals.b0;
debugger; // Breakpoint.
-};
-
-function g3(x, y) {
- var a = 3;
- var b = 4;
- h(a, b);
-};
-
-function g2(x, y) {
- var a = 5;
- var b = 6;
- g3(a, b);
-};
-
-function g1(x, y) {
- var a = 7;
- var b = 8;
- g2(a, b);
-};
-
-function f(x, y) {
- var a = 9;
- var b = 10;
- g1(a, b);
-};
+}
+
+function g3(i, x1, y1) {
+ var a1 = expected[i].locals.a1;
+ var b1 = expected[i].locals.b1;
+ h(i - 1, a1, b1);
+}
+
+function g2(i) {
+ var a2 = expected[i].locals.a2;
+ var b2 = expected[i].locals.b2;
+ g3(i - 1, a2, b2);
+}
+
+function g1(i, x3, y3, z3) {
+ var a3 = expected[i].locals.a3;
+ var b3 = expected[i].locals.b3;
+ g2(i - 1, a3, b3);
+}
+
+function f(i, x4, y4) {
+ var a4 = expected[i].locals.a4;
+ var b4 = expected[i].locals.b4;
+ g1(i - 1, a4, b4);
+}
// Test calling f normally and as a constructor.
-f(11, 12);
+f(expected.length - 1, 11, 12);
+f(expected.length - 1, 11, 12, 0);
testingConstructCall = true;
-new f(11, 12);
+new f(expected.length - 1, 11, 12);
+new f(expected.length - 1, 11, 12, 0);
// Make sure that the debug event listener vas invoked.
assertFalse(exception, "exception in listener " + exception)
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// Check that %NewObjectFromBound works correctly when called from optimized
// frame.
-function foo(x, y, z) {
+function foo1(x, y, z) {
assertEquals(1, x);
assertEquals(2, y);
assertEquals(3, z);
}
-var foob = foo.bind({}, 1);
+function foo2(x, y, z) {
+ assertEquals(1, x);
+ assertEquals(2, y);
+ assertEquals(undefined, z);
+}
-function f(y, z) {
- return %NewObjectFromBound(foob);
+function foo3(x, y, z) {
+ assertEquals(1, x);
+ assertEquals(2, y);
+ assertEquals(3, z);
+}
+
+
+var foob1 = foo1.bind({}, 1);
+var foob2 = foo2.bind({}, 1);
+var foob3 = foo3.bind({}, 1);
+
+
+function f1(y, z) {
+ return %NewObjectFromBound(foob1);
+}
+
+function f2(y, z) {
+ return %NewObjectFromBound(foob2);
+}
+
+function f3(y, z) {
+ return %NewObjectFromBound(foob3);
}
// Check that %NewObjectFromBound looks at correct frame for inlined function.
-function g(z, y) {
- return f(y, z); /* f should be inlined into g, note rotated arguments */
+function g1(z, y) {
+ return f1(y, z); /* f should be inlined into g, note rotated arguments */
+}
+
+function g2(z, y, x) {
+ return f2(y); /* f should be inlined into g, note argument count mismatch */
+}
+
+function g3(z, y, x) {
+ return f3(x, y, z); /* f should be inlined into g, note argument count mismatch */
}
// Check that %NewObjectFromBound looks at correct frame for inlined function.
function ff(x) { }
-function h(z2, y2) {
+function h1(z2, y2) {
var local_z = z2 >> 1;
ff(local_z);
var local_y = y2 >> 1;
ff(local_y);
- return f(local_y, local_z); /* f should be inlined into h */
+ return f1(local_y, local_z); /* f should be inlined into h */
}
-for (var i = 0; i < 5; i++) f(2, 3);
-%OptimizeFunctionOnNextCall(f);
-f(2, 3);
+function h2(z2, y2, x2) {
+ var local_z = z2 >> 1;
+ ff(local_z);
+ var local_y = y2 >> 1;
+ ff(local_y);
+ return f2(local_y); /* f should be inlined into h */
+}
+
+function h3(z2, y2, x2) {
+ var local_z = z2 >> 1;
+ ff(local_z);
+ var local_y = y2 >> 1;
+ ff(local_y);
+ var local_x = x2 >> 1;
+ ff(local_x);
+ return f3(local_x, local_y, local_z); /* f should be inlined into h */
+}
-for (var i = 0; i < 5; i++) g(3, 2);
-%OptimizeFunctionOnNextCall(g);
-g(3, 2);
-for (var i = 0; i < 5; i++) h(6, 4);
-%OptimizeFunctionOnNextCall(h);
-h(6, 4);
+function invoke(f, args) {
+ for (var i = 0; i < 5; i++) f.apply(this, args);
+ %OptimizeFunctionOnNextCall(f);
+ f.apply(this, args);
+}
+
+invoke(f1, [2, 3]);
+invoke(f2, [2]);
+invoke(f3, [2, 3, 4]);
+invoke(g1, [3, 2]);
+invoke(g2, [3, 2, 4]);
+invoke(g3, [4, 3, 2]);
+invoke(h1, [6, 4]);
+invoke(h2, [6, 4, 8]);
+invoke(h3, [8, 6, 4]);
// Check that %_IsConstructCall returns correct value when inlined
var NON_CONSTRUCT_MARKER = {};
var CONSTRUCT_MARKER = {};
-function baz() {
+function baz(x) {
return (!%_IsConstructCall()) ? NON_CONSTRUCT_MARKER : CONSTRUCT_MARKER;
}
function bar(x, y, z) {
+ var non_construct = baz(0); /* baz should be inlined */
+ assertEquals(non_construct, NON_CONSTRUCT_MARKER);
var non_construct = baz(); /* baz should be inlined */
assertEquals(non_construct, NON_CONSTRUCT_MARKER);
- var construct = new baz();
+ var non_construct = baz(0, 0); /* baz should be inlined */
+ assertEquals(non_construct, NON_CONSTRUCT_MARKER);
+ var construct = new baz(0);
assertEquals(construct, CONSTRUCT_MARKER);
}
-for (var i = 0; i < 5; i++) new bar(1, 2, 3);
-%OptimizeFunctionOnNextCall(bar);
-bar(1, 2, 3);
+invoke(bar, [1, 2, 3]);
end = pattern_match.end()
assert lines[end - 1] == '('
last_match = end
- arg_index = 0
+ arg_index = [0] # Wrap state into array, to work around Python "scoping"
mapping = { }
def add_arg(str):
# Remember to expand recursively in the arguments
replacement = ExpandMacros(str.strip(), macros)
- mapping[macro.args[arg_index]] = replacement
+ mapping[macro.args[arg_index[0]]] = replacement
+ arg_index[0] += 1
while end < len(lines) and height > 0:
# We don't count commas at higher nesting levels.
if lines[end] == ',' and height == 1:
dest="suppress_dialogs", default=True, action="store_true")
result.add_option("--no-suppress-dialogs", help="Display Windows dialogs for crashing tests",
dest="suppress_dialogs", action="store_false")
+ result.add_option("--mips-arch-variant", help="mips architecture variant: mips32r1/mips32r2", default="mips32r2");
result.add_option("--shell", help="Path to V8 shell", default="d8")
result.add_option("--isolates", help="Whether to test isolates", default=False, action="store_true")
result.add_option("--store-unexpected-output",
if options.snapshot:
options.scons_flags.append("snapshot=on")
global VARIANT_FLAGS
+ if options.mips_arch_variant:
+ options.scons_flags.append("mips_arch_variant=" + options.mips_arch_variant)
+
if options.stress_only:
VARIANT_FLAGS = [['--stress-opt', '--always-opt']]
if options.nostress: