Optimized double-to-integer conversions in bit operations by using SSE3 instructions if available.
Optimized initialization sequences that store to multiple properties of the same object.
Changed the D8 debugger frontend to use JSON messages.
Force garbage collections when disposing contexts.
Align code objects at 32-byte boundaries.
git-svn-id: http://v8.googlecode.com/svn/trunk@1388 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index 0a41053..5d963d0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2009-02-27: Version 1.0.3
+
+ Optimized double-to-integer conversions in bit operations by using
+ SSE3 instructions if available.
+
+ Optimized initialization sequences that store to multiple
+ properties of the same object.
+
+ Changed the D8 debugger frontend to use JSON messages.
+
+ Force garbage collections when disposing contexts.
+
+ Align code objects at 32-byte boundaries.
+
+
2009-02-25: Version 1.0.2
Improved profiling support by performing simple call stack
diff --git a/src/accessors.cc b/src/accessors.cc
index bbf60df..901dc07 100644
--- a/src/accessors.cc
+++ b/src/accessors.cc
@@ -387,8 +387,8 @@
if (frame->function() != *function) continue;
// If there is an arguments variable in the stack, we return that.
- int index = ScopeInfo<>::StackSlotIndex(frame->FindCode(),
- Heap::arguments_symbol());
+ int index = ScopeInfo<>::StackSlotIndex(frame->code(),
+ Heap::arguments_symbol());
if (index >= 0) return frame->GetExpression(index);
// If there isn't an arguments variable in the stack, we need to
diff --git a/src/api.cc b/src/api.cc
index 12526b0..2930ec6 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -376,7 +376,9 @@
void V8::DisposeGlobal(void** obj) {
LOG_API("DisposeGlobal");
if (has_shut_down) return;
- i::GlobalHandles::Destroy(reinterpret_cast<i::Object**>(obj));
+ i::Object** ptr = reinterpret_cast<i::Object**>(obj);
+ if ((*ptr)->IsGlobalContext()) i::Heap::NotifyContextDisposed();
+ i::GlobalHandles::Destroy(ptr);
}
// --- H a n d l e s ---
@@ -2175,7 +2177,6 @@
if (i::V8::HasBeenSetup()) return true;
HandleScope scope;
if (i::Snapshot::Initialize()) {
- i::Serializer::disable();
return true;
} else {
return i::V8::Initialize(NULL);
@@ -2184,7 +2185,7 @@
const char* v8::V8::GetVersion() {
- return "1.0.2";
+ return "1.0.3";
}
@@ -2208,6 +2209,9 @@
LOG_API("Context::New");
ON_BAILOUT("v8::Context::New()", return Persistent<Context>());
+ // Give the heap a chance to cleanup if we've disposed contexts.
+ i::Heap::CollectAllGarbageIfContextDisposed();
+
v8::Handle<ObjectTemplate> proxy_template = global_template;
i::Handle<i::FunctionTemplateInfo> proxy_constructor;
i::Handle<i::FunctionTemplateInfo> global_constructor;
diff --git a/src/assembler-ia32.cc b/src/assembler-ia32.cc
index 3597fc2..f57229f 100644
--- a/src/assembler-ia32.cc
+++ b/src/assembler-ia32.cc
@@ -69,27 +69,27 @@
// Implementation of CpuFeatures
// Safe default is no features.
-uint32_t CpuFeatures::supported_ = 0;
-uint32_t CpuFeatures::enabled_ = 0;
+uint64_t CpuFeatures::supported_ = 0;
+uint64_t CpuFeatures::enabled_ = 0;
-typedef int (*F0)();
-
// The Probe method needs executable memory, so it uses Heap::CreateCode.
// Allocation failure is silent and leads to safe default.
void CpuFeatures::Probe() {
- supported_ = 0;
+ ASSERT(Heap::HasBeenSetup());
+ ASSERT(supported_ == 0);
if (Serializer::enabled()) return; // No features if we might serialize.
+
Assembler assm(NULL, 0);
- Label done;
+ Label cpuid, done;
#define __ assm.
// Save old esp, since we are going to modify the stack.
__ push(ebp);
__ pushfd();
__ push(ecx);
- __ push(edx);
__ push(ebx);
__ mov(ebp, Operand(esp));
+
// If we can modify bit 21 of the EFLAGS register, then CPUID is supported.
__ pushfd();
__ pop(eax);
@@ -100,34 +100,48 @@
__ pushfd();
__ pop(eax);
__ xor_(eax, Operand(edx)); // Different if CPUID is supported.
- __ j(zero, &done);
- // Invoke CPUID with 1 in eax to get feature information in edx.
+ __ j(not_zero, &cpuid);
+
+ // CPUID not supported. Clear the supported features in edx:eax.
+ __ xor_(eax, Operand(eax));
+ __ xor_(edx, Operand(edx));
+ __ jmp(&done);
+
+ // Invoke CPUID with 1 in eax to get feature information in
+ // ecx:edx. Temporarily enable CPUID support because we know it's
+ // safe here.
+ __ bind(&cpuid);
__ mov(eax, 1);
- // Temporarily force CPUID support, since we know it is safe here.
supported_ = (1 << CPUID);
{ Scope fscope(CPUID);
__ cpuid();
}
supported_ = 0;
- // Return result in eax.
+
+ // Move the result from ecx:edx to edx:eax and make sure to mark the
+ // CPUID feature as supported.
__ mov(eax, Operand(edx));
+ __ or_(eax, 1 << CPUID);
+ __ mov(edx, Operand(ecx));
+
+ // Done.
__ bind(&done);
__ mov(esp, Operand(ebp));
__ pop(ebx);
- __ pop(edx);
__ pop(ecx);
__ popfd();
__ pop(ebp);
__ ret(0);
#undef __
+
CodeDesc desc;
assm.GetCode(&desc);
Object* code =
Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB), NULL);
if (!code->IsCode()) return;
- F0 f = FUNCTION_CAST<F0>(Code::cast(code)->entry());
- uint32_t res = f();
- supported_ = (res | (1 << CPUID));
+ typedef uint64_t (*F0)();
+ F0 probe = FUNCTION_CAST<F0>(Code::cast(code)->entry());
+ supported_ = probe();
}
@@ -1614,6 +1628,15 @@
}
+void Assembler::fisttp_s(const Operand& adr) {
+ ASSERT(CpuFeatures::IsEnabled(CpuFeatures::SSE3));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xDB);
+ emit_operand(ecx, adr);
+}
+
+
void Assembler::fist_s(const Operand& adr) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -1809,6 +1832,14 @@
}
+void Assembler::fnclex() {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xDB);
+ EMIT(0xE2);
+}
+
+
void Assembler::sahf() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
diff --git a/src/assembler-ia32.h b/src/assembler-ia32.h
index 4219212..4a88b9d 100644
--- a/src/assembler-ia32.h
+++ b/src/assembler-ia32.h
@@ -347,14 +347,18 @@
// Feature flags bit positions. They are mostly based on the CPUID spec.
// (We assign CPUID itself to one of the currently reserved bits --
// feel free to change this if needed.)
- enum Feature { SSE2 = 26, CMOV = 15, RDTSC = 4, CPUID = 10 };
+ enum Feature { SSE3 = 32, SSE2 = 26, CMOV = 15, RDTSC = 4, CPUID = 10 };
// Detect features of the target CPU. Set safe defaults if the serializer
// is enabled (snapshots must be portable).
static void Probe();
// Check whether a feature is supported by the target CPU.
- static bool IsSupported(Feature f) { return supported_ & (1 << f); }
+ static bool IsSupported(Feature f) {
+ return (supported_ & (static_cast<uint64_t>(1) << f)) != 0;
+ }
// Check whether a feature is currently enabled.
- static bool IsEnabled(Feature f) { return enabled_ & (1 << f); }
+ static bool IsEnabled(Feature f) {
+ return (enabled_ & (static_cast<uint64_t>(1) << f)) != 0;
+ }
// Enable a specified feature within a scope.
class Scope BASE_EMBEDDED {
#ifdef DEBUG
@@ -362,19 +366,19 @@
explicit Scope(Feature f) {
ASSERT(CpuFeatures::IsSupported(f));
old_enabled_ = CpuFeatures::enabled_;
- CpuFeatures::enabled_ |= (1 << f);
+ CpuFeatures::enabled_ |= (static_cast<uint64_t>(1) << f);
}
~Scope() { CpuFeatures::enabled_ = old_enabled_; }
private:
- uint32_t old_enabled_;
+ uint64_t old_enabled_;
#else
public:
explicit Scope(Feature f) {}
#endif
};
private:
- static uint32_t supported_;
- static uint32_t enabled_;
+ static uint64_t supported_;
+ static uint64_t enabled_;
};
@@ -635,6 +639,8 @@
void fistp_s(const Operand& adr);
void fistp_d(const Operand& adr);
+ void fisttp_s(const Operand& adr);
+
void fabs();
void fchs();
@@ -663,6 +669,7 @@
void fcompp();
void fnstsw_ax();
void fwait();
+ void fnclex();
void frndint();
diff --git a/src/ast.h b/src/ast.h
index f8272f8..c0fa414 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -1106,7 +1106,8 @@
class Assignment: public Expression {
public:
Assignment(Token::Value op, Expression* target, Expression* value, int pos)
- : op_(op), target_(target), value_(value), pos_(pos) {
+ : op_(op), target_(target), value_(value), pos_(pos),
+ block_start_(false), block_end_(false) {
ASSERT(Token::IsAssignmentOp(op));
}
@@ -1120,11 +1121,22 @@
Expression* value() const { return value_; }
int position() { return pos_; }
+ // An initialization block is a series of statments of the form
+ // x.y.z.a = ...; x.y.z.b = ...; etc. The parser marks the beginning and
+ // ending of these blocks to allow for optimizations of initialization
+ // blocks.
+ bool starts_initialization_block() { return block_start_; }
+ bool ends_initialization_block() { return block_end_; }
+ void mark_block_start() { block_start_ = true; }
+ void mark_block_end() { block_end_ = true; }
+
private:
Token::Value op_;
Expression* target_;
Expression* value_;
int pos_;
+ bool block_start_;
+ bool block_end_;
};
diff --git a/src/builtins.cc b/src/builtins.cc
index c905489..c4991c3 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -688,7 +688,7 @@
// During startup it's OK to always allocate and defer GC to later.
// This simplifies things because we don't need to retry.
AlwaysAllocateScope __scope__;
- code = Heap::CreateCode(desc, NULL, flags, NULL);
+ code = Heap::CreateCode(desc, NULL, flags, masm.CodeObject());
if (code->IsFailure()) {
v8::internal::V8::FatalProcessOutOfMemory("CreateCode");
}
diff --git a/src/code-stubs.cc b/src/code-stubs.cc
index 2ead1b3..417806f 100644
--- a/src/code-stubs.cc
+++ b/src/code-stubs.cc
@@ -59,7 +59,7 @@
// Copy the generated code into a heap object, and store the major key.
Code::Flags flags = Code::ComputeFlags(Code::STUB);
- Handle<Code> code = Factory::NewCode(desc, NULL, flags);
+ Handle<Code> code = Factory::NewCode(desc, NULL, flags, masm.CodeObject());
code->set_major_key(MajorKey());
// Add unresolved entries in the code to the fixup list.
diff --git a/src/codegen-ia32.cc b/src/codegen-ia32.cc
index e095541..5bdeb00 100644
--- a/src/codegen-ia32.cc
+++ b/src/codegen-ia32.cc
@@ -2758,6 +2758,15 @@
Reference target(this, node->target());
if (target.is_illegal()) return;
+ if (node->starts_initialization_block()) {
+ ASSERT(target.type() == Reference::NAMED ||
+ target.type() == Reference::KEYED);
+ // Change to slow case in the beginning of an initialization block
+ // to avoid the quadratic behavior of repeatedly adding fast properties.
+ int stack_position = (target.type() == Reference::NAMED) ? 0 : 1;
+ frame_->Push(Operand(esp, stack_position * kPointerSize));
+ __ CallRuntime(Runtime::kToSlowProperties, 1);
+ }
if (node->op() == Token::ASSIGN ||
node->op() == Token::INIT_VAR ||
node->op() == Token::INIT_CONST) {
@@ -2789,6 +2798,14 @@
target.SetValue(CONST_INIT);
} else {
target.SetValue(NOT_CONST_INIT);
+ if (node->ends_initialization_block()) {
+ ASSERT(target.type() == Reference::NAMED ||
+ target.type() == Reference::KEYED);
+ // End of initialization block. Revert to fast case.
+ int stack_position = (target.type() == Reference::NAMED) ? 1 : 2;
+ frame_->Push(Operand(esp, stack_position * kPointerSize));
+ __ CallRuntime(Runtime::kToFastProperties, 1);
+ }
}
}
}
@@ -4507,31 +4524,44 @@
FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
FloatingPointHelper::LoadFloatOperands(masm, ecx);
- Label non_int32_operands, non_smi_result, skip_allocation;
+ Label skip_allocation, non_smi_result, operand_conversion_failure;
+
// Reserve space for converted numbers.
__ sub(Operand(esp), Immediate(2 * kPointerSize));
- // Check if right operand is int32.
- __ fist_s(Operand(esp, 1 * kPointerSize));
- __ fild_s(Operand(esp, 1 * kPointerSize));
- __ fucompp();
- __ fnstsw_ax();
- __ sahf();
- __ j(not_zero, &non_int32_operands);
- __ j(parity_even, &non_int32_operands);
+ bool use_sse3 = CpuFeatures::IsSupported(CpuFeatures::SSE3);
+ if (use_sse3) {
+ // Truncate the operands to 32-bit integers and check for
+ // exceptions in doing so.
+ CpuFeatures::Scope scope(CpuFeatures::SSE3);
+ __ fisttp_s(Operand(esp, 0 * kPointerSize));
+ __ fisttp_s(Operand(esp, 1 * kPointerSize));
+ __ fnstsw_ax();
+ __ test(eax, Immediate(1));
+ __ j(not_zero, &operand_conversion_failure);
+ } else {
+ // Check if right operand is int32.
+ __ fist_s(Operand(esp, 0 * kPointerSize));
+ __ fild_s(Operand(esp, 0 * kPointerSize));
+ __ fucompp();
+ __ fnstsw_ax();
+ __ sahf();
+ __ j(not_zero, &operand_conversion_failure);
+ __ j(parity_even, &operand_conversion_failure);
- // Check if left operand is int32.
- __ fist_s(Operand(esp, 0 * kPointerSize));
- __ fild_s(Operand(esp, 0 * kPointerSize));
- __ fucompp();
- __ fnstsw_ax();
- __ sahf();
- __ j(not_zero, &non_int32_operands);
- __ j(parity_even, &non_int32_operands);
+ // Check if left operand is int32.
+ __ fist_s(Operand(esp, 1 * kPointerSize));
+ __ fild_s(Operand(esp, 1 * kPointerSize));
+ __ fucompp();
+ __ fnstsw_ax();
+ __ sahf();
+ __ j(not_zero, &operand_conversion_failure);
+ __ j(parity_even, &operand_conversion_failure);
+ }
// Get int32 operands and perform bitop.
- __ pop(eax);
__ pop(ecx);
+ __ pop(eax);
switch (op_) {
case Token::BIT_OR: __ or_(eax, Operand(ecx)); break;
case Token::BIT_AND: __ and_(eax, Operand(ecx)); break;
@@ -4579,10 +4609,22 @@
__ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
__ ret(2 * kPointerSize);
}
- __ bind(&non_int32_operands);
- // Restore stacks and operands before calling runtime.
- __ ffree(0);
+
+ // Clear the FPU exception flag and reset the stack before calling
+ // the runtime system.
+ __ bind(&operand_conversion_failure);
__ add(Operand(esp), Immediate(2 * kPointerSize));
+ if (use_sse3) {
+ // If we've used the SSE3 instructions for truncating the
+ // floating point values to integers and it failed, we have a
+ // pending #IA exception. Clear it.
+ __ fnclex();
+ } else {
+ // The non-SSE3 variant does early bailout if the right
+ // operand isn't a 32-bit integer, so we may have a single
+ // value on the FPU stack we need to get rid of.
+ __ ffree(0);
+ }
// SHR should return uint32 - go to runtime for non-smi/negative result.
if (op_ == Token::SHR) __ bind(&non_smi_result);
diff --git a/src/codegen.cc b/src/codegen.cc
index 81c2af8..271f571 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -130,7 +130,10 @@
cgen.masm()->GetCode(&desc);
ScopeInfo<> sinfo(flit->scope());
Code::Flags flags = Code::ComputeFlags(Code::FUNCTION);
- Handle<Code> code = Factory::NewCode(desc, &sinfo, flags);
+ Handle<Code> code = Factory::NewCode(desc,
+ &sinfo,
+ flags,
+ cgen.masm()->CodeObject());
// Add unresolved entries in the code to the fixup list.
Bootstrapper::AddFixup(*code, cgen.masm());
diff --git a/src/cpu-ia32.cc b/src/cpu-ia32.cc
index 030dbde..9cd6b10 100644
--- a/src/cpu-ia32.cc
+++ b/src/cpu-ia32.cc
@@ -30,11 +30,12 @@
#include "v8.h"
#include "cpu.h"
+#include "macro-assembler.h"
namespace v8 { namespace internal {
void CPU::Setup() {
- // Nothing to do.
+ CpuFeatures::Probe();
}
diff --git a/src/d8-debug.cc b/src/d8-debug.cc
index 0e30a86..cfe69ca 100644
--- a/src/d8-debug.cc
+++ b/src/d8-debug.cc
@@ -46,8 +46,19 @@
TryCatch try_catch;
+ // Get the toJSONProtocol function on the event and get the JSON format.
+ Local<String> to_json_fun_name = String::New("toJSONProtocol");
+ Local<Function> to_json_fun =
+ Function::Cast(*event_data->Get(to_json_fun_name));
+ Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
+ if (try_catch.HasCaught()) {
+ Shell::ReportException(&try_catch);
+ return;
+ }
+
// Print the event details.
- Handle<String> details = Shell::DebugEventToText(event_data);
+ Handle<String> details =
+ Shell::DebugEventToText(Handle<String>::Cast(event_json));
if (details->Length() == 0) {
// Empty string is used to signal not to process this event.
return;
diff --git a/src/d8.cc b/src/d8.cc
index 8e415ca..a049430 100644
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -232,7 +232,7 @@
}
-Handle<String> Shell::DebugEventToText(Handle<Object> event) {
+Handle<String> Shell::DebugEventToText(Handle<String> event) {
HandleScope handle_scope;
Context::Scope context_scope(utility_context_);
Handle<Object> global = utility_context_->Global();
diff --git a/src/d8.h b/src/d8.h
index 4614378..726b369 100644
--- a/src/d8.h
+++ b/src/d8.h
@@ -88,7 +88,7 @@
static int Main(int argc, char* argv[]);
static Handle<Array> GetCompletions(Handle<String> text,
Handle<String> full);
- static Handle<String> DebugEventToText(Handle<Object> event);
+ static Handle<String> DebugEventToText(Handle<String> event);
static Handle<Value> DebugCommandToJSONRequest(Handle<String> command);
static Handle<Object> DebugResponseDetails(Handle<String> response);
diff --git a/src/d8.js b/src/d8.js
index b298a5f..25896ba 100644
--- a/src/d8.js
+++ b/src/d8.js
@@ -103,66 +103,54 @@
function DebugEventToText(event) {
- switch (event.eventType()) {
- case Debug.DebugEvent.Break:
- // Build the break details.
- var details = '';
- if (event.breakPointsHit()) {
+ // Convert the JSON string to an object.
+ var response = new ProtocolPackage(event);
+
+ // Build the text.
+ var body = response.body();
+ var details = '';
+ switch (response.event()) {
+ case 'break':
+ if (body.breakpoints) {
details += 'breakpoint';
- if (event.breakPointsHit().length > 1) {
+ if (body.breakpoints.length > 1) {
details += 's';
}
details += ' #';
- for (var i = 0; i < event.breakPointsHit().length; i++) {
+ for (var i = 0; i < body.breakpoints.length; i++) {
if (i > 0) {
details += ', #';
}
- // Find the break point number. For break points originating from a
- // script break point display the script break point number.
- var break_point = event.breakPointsHit()[i];
- var script_break_point = break_point.script_break_point();
- if (script_break_point) {
- details += script_break_point.number();
- } else {
- details += break_point.number();
- }
+ details += body.breakpoints[i];
}
} else {
details += 'break';
}
details += ' in ';
- details += event.executionState().frame(0).invocationText();
- details += ' at ';
- details += event.executionState().frame(0).sourceAndPositionText();
- details += '\n'
- if (event.func().script()) {
- details += FrameSourceUnderline(event.executionState().frame(0));
- }
- Debug.State.currentSourceLine =
- event.executionState().frame(0).sourceLine();
+ details += body.invocationText;
+ details += ', ';
+ details += SourceInfo(body);
+ details += '\n';
+ details += SourceUnderline(body.sourceLineText, body.sourceColumn);
+ Debug.State.currentSourceLine = body.sourceLine;
Debug.State.currentFrame = 0;
return details;
-
- case Debug.DebugEvent.Exception:
- var details = '';
- if (event.uncaught_) {
+
+ case 'exception':
+ if (body.uncaught) {
details += 'Uncaught: ';
} else {
details += 'Exception: ';
}
-
details += '"';
- details += event.exception();
+ details += body.exception.text;
details += '"';
- if (event.executionState().frameCount() > 0) {
- details += '"';
- details += event.exception();
- details += ' at ';
- details += event.executionState().frame(0).sourceAndPositionText();
+ if (body.sourceLine >= 0) {
+ details += ', ';
+ details += SourceInfo(body);
details += '\n';
- details += FrameSourceUnderline(event.executionState().frame(0));
- Debug.State.currentSourceLine =
- event.executionState().frame(0).sourceLine();
+ details += SourceUnderline(body.sourceLineText, body.sourceColumn);
+ Debug.State.currentSourceLine = body.sourceLine;
Debug.State.currentFrame = 0;
} else {
details += ' (empty stack)';
@@ -170,11 +158,18 @@
Debug.State.currentFrame = kNoFrame;
}
return details;
-
- case Debug.DebugEvent.AfterCompile:
+
+ case 'exception':
+ if (trace_compile) {
+ details = 'Source ' + body.script.name + ' compiled:\n'
+ } else {
+ return '';
+ }
+
+ case 'afterCompile':
if (trace_compile) {
details = 'Source ' + event.script().name() + ' compiled:\n'
- var source = event.script().source();
+ var source = body.script.source;
if (!(source[source.length - 1] == '\n')) {
details += source;
} else {
@@ -185,11 +180,29 @@
return '';
}
}
-
- return 'Unknown debug event ' + event.eventType();
+ return 'Unknown debug event ' + response.event();
};
+function SourceInfo(body) {
+ var result = '';
+
+ if (body.script) {
+ if (body.script.name) {
+ result += body.script.name;
+ } else {
+ result += '[unnamed]';
+ }
+ }
+ result += ' line ';
+ result += body.sourceLine + 1;
+ result += ' column ';
+ result += body.sourceColumn + 1;
+
+ return result;
+}
+
+
function SourceUnderline(source_text, position) {
if (!source_text) {
return;
@@ -213,15 +226,6 @@
};
-function FrameSourceUnderline(frame) {
- var location = frame.sourceLocation();
- if (location) {
- return SourceUnderline(location.sourceText(),
- location.position - location.start);
- }
-};
-
-
// Converts a text command to a JSON request.
function DebugCommandToJSONRequest(cmd_line) {
return new DebugRequest(cmd_line).JSONRequest();
diff --git a/src/debug-delay.js b/src/debug-delay.js
index 45e3c6e..cfd1c7d 100644
--- a/src/debug-delay.js
+++ b/src/debug-delay.js
@@ -871,28 +871,32 @@
ExceptionEvent.prototype.toJSONProtocol = function() {
- var o = { seq: next_response_seq++,
- type: "event",
- event: "exception",
- body: { uncaught: this.uncaught_,
- exception: MakeMirror(this.exception_),
- sourceLine: this.sourceLine(),
- sourceColumn: this.sourceColumn(),
- sourceLineText: this.sourceLineText(),
- }
- }
+ var o = new ProtocolMessage();
+ o.event = "exception";
+ o.body = { uncaught: this.uncaught_,
+ exception: MakeMirror(this.exception_)
+ }
+
+ // Exceptions might happen whithout any JavaScript frames.
+ if (this.exec_state_.frameCount() > 0) {
+ o.body.sourceLine = this.sourceLine();
+ o.body.sourceColumn = this.sourceColumn();
+ o.body.sourceLineText = this.sourceLineText();
- // Add script information to the event if available.
- var script = this.func().script();
- if (script) {
- o.body.script = { name: script.name(),
- lineOffset: script.lineOffset(),
- columnOffset: script.columnOffset(),
- lineCount: script.lineCount()
- };
+ // Add script information to the event if available.
+ var script = this.func().script();
+ if (script) {
+ o.body.script = { name: script.name(),
+ lineOffset: script.lineOffset(),
+ columnOffset: script.columnOffset(),
+ lineCount: script.lineCount()
+ };
+ }
+ } else {
+ o.body.sourceLine = -1;
}
- return SimpleObjectToJSON_(o);
+ return o.toJSONProtocol();
};
@@ -927,6 +931,25 @@
};
+CompileEvent.prototype.toJSONProtocol = function() {
+ var o = new ProtocolMessage();
+ if (this.before_) {
+ o.event = "beforeCompile";
+ } else {
+ o.event = "afterCompile";
+ }
+ o.body = {};
+ o.body.script = { name: this.script_.name(),
+ lineOffset: this.script_.lineOffset(),
+ columnOffset: this.script_.columnOffset(),
+ lineCount: this.script_.lineCount(),
+ source: this.script_.source()
+ };
+
+ return o.toJSONProtocol();
+}
+
+
function MakeNewFunctionEvent(func) {
return new NewFunctionEvent(func);
}
@@ -963,24 +986,32 @@
}
-function ResponsePacket(request) {
- // Build the initial response from the request.
+function ProtocolMessage(request) {
+ // Update sequence number.
this.seq = next_response_seq++;
- this.type = 'response';
- if (request) this.request_seq = request.seq;
- if (request) this.command = request.command;
+
+ if (request) {
+ // If message is based on a request this is a response. Fill the initial
+ // response from the request.
+ this.type = 'response';
+ this.request_seq = request.seq;
+ this.command = request.command;
+ } else {
+ // If message is not based on a request it is a dabugger generated event.
+ this.type = 'event';
+ }
this.success = true;
this.running = false;
}
-ResponsePacket.prototype.failed = function(message) {
+ProtocolMessage.prototype.failed = function(message) {
this.success = false;
this.message = message;
}
-ResponsePacket.prototype.toJSONProtocol = function() {
+ProtocolMessage.prototype.toJSONProtocol = function() {
// Encode the protocol header.
var json = '{';
json += '"seq":' + this.seq;
@@ -988,6 +1019,9 @@
json += ',"request_seq":' + this.request_seq;
}
json += ',"type":"' + this.type + '"';
+ if (this.event) {
+ json += ',"event":' + StringToJSON_(this.event);
+ }
if (this.command) {
json += ',"command":' + StringToJSON_(this.command);
}
@@ -1033,7 +1067,7 @@
DebugCommandProcessor.prototype.createResponse = function(request) {
- return new ResponsePacket(request);
+ return new ProtocolMessage(request);
};
@@ -1652,7 +1686,9 @@
var property_value_json;
switch (typeof property_value) {
case 'object':
- if (typeof property_value.toJSONProtocol == 'function') {
+ if (property_value instanceof Mirror) {
+ property_value_json = mirror_serializer.serializeValue(property_value);
+ } else if (typeof property_value.toJSONProtocol == 'function') {
property_value_json = property_value.toJSONProtocol(true)
} else if (IS_ARRAY(property_value)){
property_value_json = SimpleArrayToJSON_(property_value, mirror_serializer);
diff --git a/src/debug.cc b/src/debug.cc
index dba09e2..f36bf2d 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -1266,7 +1266,7 @@
Handle<Code> original_code(debug_info->original_code());
#ifdef DEBUG
// Get the code which is actually executing.
- Handle<Code> frame_code(frame->FindCode());
+ Handle<Code> frame_code(frame->code());
ASSERT(frame_code.is_identical_to(code));
#endif
diff --git a/src/factory.cc b/src/factory.cc
index 35d8c51..ec52520 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -487,13 +487,7 @@
Handle<Code> Factory::NewCode(const CodeDesc& desc, ScopeInfo<>* sinfo,
Code::Flags flags, Handle<Object> self_ref) {
- CALL_HEAP_FUNCTION(Heap::CreateCode(
- desc, sinfo, flags, reinterpret_cast<Code**>(self_ref.location())), Code);
-}
-
-Handle<Code> Factory::NewCode(const CodeDesc& desc, ScopeInfo<>* sinfo,
- Code::Flags flags) {
- CALL_HEAP_FUNCTION(Heap::CreateCode(desc, sinfo, flags, NULL), Code);
+ CALL_HEAP_FUNCTION(Heap::CreateCode(desc, sinfo, flags, self_ref), Code);
}
diff --git a/src/factory.h b/src/factory.h
index c5c3384..f282896 100644
--- a/src/factory.h
+++ b/src/factory.h
@@ -206,9 +206,6 @@
static Handle<Code> NewCode(const CodeDesc& desc, ScopeInfo<>* sinfo,
Code::Flags flags, Handle<Object> self_reference);
- static Handle<Code> NewCode(const CodeDesc& desc, ScopeInfo<>* sinfo,
- Code::Flags flags);
-
static Handle<Code> CopyCode(Handle<Code> code);
static Handle<Object> ToObject(Handle<Object> object,
diff --git a/src/frames-arm.cc b/src/frames-arm.cc
index f7e7452..fe850a8 100644
--- a/src/frames-arm.cc
+++ b/src/frames-arm.cc
@@ -114,10 +114,4 @@
}
-Code* JavaScriptFrame::FindCode() const {
- JSFunction* function = JSFunction::cast(this->function());
- return function->shared()->code();
-}
-
-
} } // namespace v8::internal
diff --git a/src/frames-ia32.cc b/src/frames-ia32.cc
index ddf4bfd..2b24777 100644
--- a/src/frames-ia32.cc
+++ b/src/frames-ia32.cc
@@ -112,10 +112,4 @@
}
-Code* JavaScriptFrame::FindCode() const {
- JSFunction* function = JSFunction::cast(this->function());
- return function->shared()->code();
-}
-
-
} } // namespace v8::internal
diff --git a/src/frames.cc b/src/frames.cc
index 09c6a02..20a7149 100644
--- a/src/frames.cc
+++ b/src/frames.cc
@@ -28,6 +28,7 @@
#include "v8.h"
#include "frames-inl.h"
+#include "mark-compact.h"
#include "scopeinfo.h"
#include "string-stream.h"
#include "top.h"
@@ -162,12 +163,14 @@
void StackHandler::Cook(Code* code) {
+ ASSERT(MarkCompactCollector::IsCompacting());
ASSERT(code->contains(pc()));
set_pc(AddressFrom<Address>(pc() - code->instruction_start()));
}
void StackHandler::Uncook(Code* code) {
+ ASSERT(MarkCompactCollector::IsCompacting());
set_pc(code->instruction_start() + OffsetFrom(pc()));
ASSERT(code->contains(pc()));
}
@@ -183,6 +186,9 @@
void StackFrame::CookFramesForThread(ThreadLocalTop* thread) {
+ // Only cooking frames when the collector is compacting and thus moving code
+ // around.
+ ASSERT(MarkCompactCollector::IsCompacting());
ASSERT(!thread->stack_is_cooked());
for (StackFrameIterator it(thread); !it.done(); it.Advance()) {
it.frame()->Cook();
@@ -192,6 +198,9 @@
void StackFrame::UncookFramesForThread(ThreadLocalTop* thread) {
+ // Only uncooking frames when the collector is compacting and thus moving code
+ // around.
+ ASSERT(MarkCompactCollector::IsCompacting());
ASSERT(thread->stack_is_cooked());
for (StackFrameIterator it(thread); !it.done(); it.Advance()) {
it.frame()->Uncook();
@@ -201,7 +210,7 @@
void StackFrame::Cook() {
- Code* code = FindCode();
+ Code* code = this->code();
for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
it.handler()->Cook(code);
}
@@ -211,7 +220,7 @@
void StackFrame::Uncook() {
- Code* code = FindCode();
+ Code* code = this->code();
for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
it.handler()->Uncook(code);
}
@@ -220,7 +229,7 @@
}
-Code* EntryFrame::FindCode() const {
+Code* EntryFrame::code() const {
return Heap::js_entry_code();
}
@@ -232,12 +241,12 @@
}
-Code* EntryConstructFrame::FindCode() const {
+Code* EntryConstructFrame::code() const {
return Heap::js_construct_entry_code();
}
-Code* ExitFrame::FindCode() const {
+Code* ExitFrame::code() const {
return Heap::c_entry_code();
}
@@ -257,7 +266,7 @@
}
-Code* ExitDebugFrame::FindCode() const {
+Code* ExitDebugFrame::code() const {
return Heap::c_entry_debug_break_code();
}
@@ -320,20 +329,20 @@
}
-Code* ArgumentsAdaptorFrame::FindCode() const {
+Code* JavaScriptFrame::code() const {
+ JSFunction* function = JSFunction::cast(this->function());
+ return function->shared()->code();
+}
+
+
+Code* ArgumentsAdaptorFrame::code() const {
return Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline);
}
-Code* InternalFrame::FindCode() const {
+Code* InternalFrame::code() const {
const int offset = InternalFrameConstants::kCodeOffset;
Object* code = Memory::Object_at(fp() + offset);
- if (code == NULL) {
- // The code object isn't set; find it and set it.
- code = Heap::FindCodeObject(pc());
- ASSERT(!code->IsFailure());
- Memory::Object_at(fp() + offset) = code;
- }
ASSERT(code != NULL);
return Code::cast(code);
}
diff --git a/src/frames.h b/src/frames.h
index ea82cc6..c18cb74 100644
--- a/src/frames.h
+++ b/src/frames.h
@@ -148,7 +148,7 @@
virtual Type type() const = 0;
// Get the code associated with this frame.
- virtual Code* FindCode() const = 0;
+ virtual Code* code() const = 0;
// Garbage collection support.
static void CookFramesForThread(ThreadLocalTop* thread);
@@ -209,7 +209,7 @@
public:
virtual Type type() const { return ENTRY; }
- virtual Code* FindCode() const;
+ virtual Code* code() const;
// Garbage collection support.
virtual void Iterate(ObjectVisitor* v) const;
@@ -238,7 +238,7 @@
public:
virtual Type type() const { return ENTRY_CONSTRUCT; }
- virtual Code* FindCode() const;
+ virtual Code* code() const;
static EntryConstructFrame* cast(StackFrame* frame) {
ASSERT(frame->is_entry_construct());
@@ -259,7 +259,7 @@
public:
virtual Type type() const { return EXIT; }
- virtual Code* FindCode() const;
+ virtual Code* code() const;
// Garbage collection support.
virtual void Iterate(ObjectVisitor* v) const;
@@ -290,7 +290,7 @@
public:
virtual Type type() const { return EXIT_DEBUG; }
- virtual Code* FindCode() const;
+ virtual Code* code() const;
static ExitDebugFrame* cast(StackFrame* frame) {
ASSERT(frame->is_exit_debug());
@@ -399,7 +399,7 @@
int index) const;
// Determine the code for the frame.
- virtual Code* FindCode() const;
+ virtual Code* code() const;
static JavaScriptFrame* cast(StackFrame* frame) {
ASSERT(frame->is_java_script());
@@ -433,7 +433,7 @@
virtual Type type() const { return ARGUMENTS_ADAPTOR; }
// Determine the code for the frame.
- virtual Code* FindCode() const;
+ virtual Code* code() const;
static ArgumentsAdaptorFrame* cast(StackFrame* frame) {
ASSERT(frame->is_arguments_adaptor());
@@ -463,7 +463,7 @@
virtual void Iterate(ObjectVisitor* v) const;
// Determine the code for the frame.
- virtual Code* FindCode() const;
+ virtual Code* code() const;
static InternalFrame* cast(StackFrame* frame) {
ASSERT(frame->is_internal());
diff --git a/src/heap.cc b/src/heap.cc
index 72f427e..d088463 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -97,6 +97,7 @@
int Heap::gc_count_ = 0;
int Heap::always_allocate_scope_depth_ = 0;
+bool Heap::context_disposed_pending_ = false;
#ifdef DEBUG
bool Heap::allocation_allowed_ = true;
@@ -293,6 +294,20 @@
}
+void Heap::CollectAllGarbageIfContextDisposed() {
+ if (context_disposed_pending_) {
+ StatsRateScope scope(&Counters::gc_context);
+ CollectAllGarbage();
+ context_disposed_pending_ = false;
+ }
+}
+
+
+void Heap::NotifyContextDisposed() {
+ context_disposed_pending_ = true;
+}
+
+
bool Heap::CollectGarbage(int requested_size, AllocationSpace space) {
// The VM is in the GC state until exiting this function.
VMState state(GC);
@@ -419,11 +434,15 @@
tracer->set_full_gc_count(mc_count_);
LOG(ResourceEvent("markcompact", "begin"));
- MarkCompactPrologue();
+ MarkCompactCollector::Prepare(tracer);
- MarkCompactCollector::CollectGarbage(tracer);
+ bool is_compacting = MarkCompactCollector::IsCompacting();
- MarkCompactEpilogue();
+ MarkCompactPrologue(is_compacting);
+
+ MarkCompactCollector::CollectGarbage();
+
+ MarkCompactEpilogue(is_compacting);
LOG(ResourceEvent("markcompact", "end"));
@@ -432,21 +451,26 @@
Shrink();
Counters::objs_since_last_full.Set(0);
+ context_disposed_pending_ = false;
}
-void Heap::MarkCompactPrologue() {
+void Heap::MarkCompactPrologue(bool is_compacting) {
+ // At any old GC clear the keyed lookup cache to enable collection of unused
+ // maps.
ClearKeyedLookupCache();
+
CompilationCache::MarkCompactPrologue();
RegExpImpl::OldSpaceCollectionPrologue();
- Top::MarkCompactPrologue();
- ThreadManager::MarkCompactPrologue();
+
+ Top::MarkCompactPrologue(is_compacting);
+ ThreadManager::MarkCompactPrologue(is_compacting);
}
-void Heap::MarkCompactEpilogue() {
- Top::MarkCompactEpilogue();
- ThreadManager::MarkCompactEpilogue();
+void Heap::MarkCompactEpilogue(bool is_compacting) {
+ Top::MarkCompactEpilogue(is_compacting);
+ ThreadManager::MarkCompactEpilogue(is_compacting);
}
@@ -1601,12 +1625,13 @@
Object* Heap::CreateCode(const CodeDesc& desc,
ScopeInfo<>* sinfo,
Code::Flags flags,
- Code** self_reference) {
+ Handle<Object> self_reference) {
// Compute size
int body_size = RoundUp(desc.instr_size + desc.reloc_size, kObjectAlignment);
int sinfo_size = 0;
if (sinfo != NULL) sinfo_size = sinfo->Serialize(NULL);
int obj_size = Code::SizeFor(body_size, sinfo_size);
+ ASSERT(IsAligned(obj_size, Code::kCodeAlignment));
Object* result;
if (obj_size > MaxHeapObjectSize()) {
result = lo_space_->AllocateRawCode(obj_size);
@@ -1624,9 +1649,10 @@
code->set_sinfo_size(sinfo_size);
code->set_flags(flags);
code->set_ic_flag(Code::IC_TARGET_IS_ADDRESS);
- // Allow self references to created code object.
- if (self_reference != NULL) {
- *self_reference = code;
+ // Allow self references to created code object by patching the handle to
+ // point to the newly allocated Code object.
+ if (!self_reference.is_null()) {
+ *(self_reference.location()) = code;
}
// Migrate generated code.
// The generated code can contain Object** values (typically from handles)
diff --git a/src/heap.h b/src/heap.h
index f1cb8ee..712efb9 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -567,7 +567,7 @@
static Object* CreateCode(const CodeDesc& desc,
ScopeInfo<>* sinfo,
Code::Flags flags,
- Code** self_reference = NULL);
+ Handle<Object> self_reference);
static Object* CopyCode(Code* code);
// Finds the symbol for string in the symbol table.
@@ -606,6 +606,13 @@
// Performs a full garbage collection.
static void CollectAllGarbage();
+ // Performs a full garbage collection if a context has been disposed
+ // since the last time the check was performed.
+ static void CollectAllGarbageIfContextDisposed();
+
+ // Notify the heap that a context has been disposed.
+ static void NotifyContextDisposed();
+
// Utility to invoke the scavenger. This is needed in test code to
// ensure correct callback for weak global handles.
static void PerformScavenge();
@@ -808,6 +815,7 @@
static int scavenge_count_;
static int always_allocate_scope_depth_;
+ static bool context_disposed_pending_;
static const int kMaxMapSpaceSize = 8*MB;
@@ -923,8 +931,8 @@
static void MarkCompact(GCTracer* tracer);
// Code to be run before and after mark-compact.
- static void MarkCompactPrologue();
- static void MarkCompactEpilogue();
+ static void MarkCompactPrologue(bool is_compacting);
+ static void MarkCompactEpilogue(bool is_compacting);
// Helper function used by CopyObject to copy a source object to an
// allocated target object and update the forwarding pointer in the source
diff --git a/src/jsregexp.cc b/src/jsregexp.cc
index 324d0f9..12572d3 100644
--- a/src/jsregexp.cc
+++ b/src/jsregexp.cc
@@ -256,12 +256,14 @@
Handle<Object> RegExpImpl::Exec(Handle<JSRegExp> regexp,
Handle<String> subject,
- Handle<Object> index) {
+ int index,
+ Handle<JSArray> last_match_info) {
switch (regexp->TypeTag()) {
case JSRegExp::ATOM:
- return AtomExec(regexp, subject, index);
+ return AtomExec(regexp, subject, index, last_match_info);
case JSRegExp::IRREGEXP: {
- Handle<Object> result = IrregexpExec(regexp, subject, index);
+ Handle<Object> result =
+ IrregexpExec(regexp, subject, index, last_match_info);
ASSERT(!result.is_null() || Top::has_pending_exception());
return result;
}
@@ -273,12 +275,14 @@
Handle<Object> RegExpImpl::ExecGlobal(Handle<JSRegExp> regexp,
- Handle<String> subject) {
+ Handle<String> subject,
+ Handle<JSArray> last_match_info) {
switch (regexp->TypeTag()) {
case JSRegExp::ATOM:
- return AtomExecGlobal(regexp, subject);
+ return AtomExecGlobal(regexp, subject, last_match_info);
case JSRegExp::IRREGEXP: {
- Handle<Object> result = IrregexpExecGlobal(regexp, subject);
+ Handle<Object> result =
+ IrregexpExecGlobal(regexp, subject, last_match_info);
ASSERT(!result.is_null() || Top::has_pending_exception());
return result;
}
@@ -301,51 +305,79 @@
}
+static void SetAtomLastCapture(FixedArray* array,
+ String* subject,
+ int from,
+ int to) {
+ RegExpImpl::SetLastCaptureCount(array, 2);
+ RegExpImpl::SetLastSubject(array, subject);
+ RegExpImpl::SetLastInput(array, subject);
+ RegExpImpl::SetCapture(array, 0, from);
+ RegExpImpl::SetCapture(array, 1, to);
+}
+
+
Handle<Object> RegExpImpl::AtomExec(Handle<JSRegExp> re,
Handle<String> subject,
- Handle<Object> index) {
+ int index,
+ Handle<JSArray> last_match_info) {
Handle<String> needle(String::cast(re->DataAt(JSRegExp::kAtomPatternIndex)));
- uint32_t start_index;
- if (!Array::IndexFromObject(*index, &start_index)) {
- return Handle<Smi>(Smi::FromInt(-1));
- }
+ uint32_t start_index = index;
int value = Runtime::StringMatch(subject, needle, start_index);
if (value == -1) return Factory::null_value();
+ ASSERT(last_match_info->HasFastElements());
- Handle<FixedArray> array = Factory::NewFixedArray(2);
- array->set(0, Smi::FromInt(value));
- array->set(1, Smi::FromInt(value + needle->length()));
- return Factory::NewJSArrayWithElements(array);
+ Handle<FixedArray> array(last_match_info->elements());
+ SetAtomLastCapture(*array, *subject, value, value + needle->length());
+ return last_match_info;
}
Handle<Object> RegExpImpl::AtomExecGlobal(Handle<JSRegExp> re,
- Handle<String> subject) {
+ Handle<String> subject,
+ Handle<JSArray> last_match_info) {
Handle<String> needle(String::cast(re->DataAt(JSRegExp::kAtomPatternIndex)));
+ ASSERT(last_match_info->HasFastElements());
Handle<JSArray> result = Factory::NewJSArray(1);
int index = 0;
int match_count = 0;
int subject_length = subject->length();
int needle_length = needle->length();
+ int last_value = -1;
while (true) {
+ HandleScope scope;
int value = -1;
if (index + needle_length <= subject_length) {
value = Runtime::StringMatch(subject, needle, index);
}
- if (value == -1) break;
- HandleScope scope;
+ if (value == -1) {
+ if (last_value != -1) {
+ Handle<FixedArray> array(last_match_info->elements());
+ SetAtomLastCapture(*array,
+ *subject,
+ last_value,
+ last_value + needle->length());
+ }
+ break;
+ }
+
int end = value + needle_length;
- Handle<FixedArray> array = Factory::NewFixedArray(2);
- array->set(0, Smi::FromInt(value));
- array->set(1, Smi::FromInt(end));
+ // Create an array that looks like the static last_match_info array
+ // that is attached to the global RegExp object. We will be returning
+ // an array of these.
+ Handle<FixedArray> array = Factory::NewFixedArray(kFirstCapture + 2);
+ SetCapture(*array, 0, value);
+ SetCapture(*array, 1, end);
+ SetLastCaptureCount(*array, 2);
Handle<JSArray> pair = Factory::NewJSArrayWithElements(array);
SetElement(result, match_count, pair);
match_count++;
index = end;
if (needle_length == 0) index++;
+ last_value = value;
}
return result;
}
@@ -445,7 +477,8 @@
Handle<Object> RegExpImpl::IrregexpExec(Handle<JSRegExp> regexp,
Handle<String> subject,
- Handle<Object> index) {
+ int index,
+ Handle<JSArray> last_match_info) {
ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
ASSERT(regexp->DataAt(JSRegExp::kIrregexpDataIndex)->IsFixedArray());
@@ -457,12 +490,11 @@
}
// Prepare space for the return values.
- int number_of_registers = IrregexpNumberOfRegisters(irregexp);
- OffsetsVector offsets(number_of_registers);
+ int number_of_capture_registers =
+ (IrregexpNumberOfCaptures(irregexp) + 1) * 2;
+ OffsetsVector offsets(number_of_capture_registers);
- int num_captures = IrregexpNumberOfCaptures(irregexp);
-
- int previous_index = static_cast<int>(DoubleToInteger(index->Number()));
+ int previous_index = index;
#ifdef DEBUG
if (FLAG_trace_regexp_bytecodes) {
@@ -476,8 +508,11 @@
FlattenString(subject);
}
+ last_match_info->EnsureSize(number_of_capture_registers + kLastMatchOverhead);
+
return IrregexpExecOnce(irregexp,
- num_captures,
+ number_of_capture_registers,
+ last_match_info,
subject,
previous_index,
offsets.vector(),
@@ -486,7 +521,8 @@
Handle<Object> RegExpImpl::IrregexpExecGlobal(Handle<JSRegExp> regexp,
- Handle<String> subject) {
+ Handle<String> subject,
+ Handle<JSArray> last_match_info) {
ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
bool is_ascii = StringShape(*subject).IsAsciiRepresentation();
@@ -496,19 +532,22 @@
}
// Prepare space for the return values.
- int number_of_registers = IrregexpNumberOfRegisters(irregexp);
- OffsetsVector offsets(number_of_registers);
+ int number_of_capture_registers =
+ (IrregexpNumberOfCaptures(irregexp) + 1) * 2;
+ OffsetsVector offsets(number_of_capture_registers);
int previous_index = 0;
Handle<JSArray> result = Factory::NewJSArray(0);
- int i = 0;
+ int result_length = 0;
Handle<Object> matches;
if (!subject->IsFlat(StringShape(*subject))) {
FlattenString(subject);
}
+ last_match_info->EnsureSize(number_of_capture_registers + kLastMatchOverhead);
+
while (true) {
if (previous_index > subject->length() || previous_index < 0) {
// Per ECMA-262 15.10.6.2, if the previous index is greater than the
@@ -523,8 +562,10 @@
PrintF("\n\nSubject string: '%s'\n\n", *(subject->ToCString()));
}
#endif
+ HandleScope scope;
matches = IrregexpExecOnce(irregexp,
- IrregexpNumberOfCaptures(irregexp),
+ number_of_capture_registers,
+ last_match_info,
subject,
previous_index,
offsets.vector(),
@@ -536,12 +577,25 @@
}
if (matches->IsJSArray()) {
- SetElement(result, i, matches);
- i++;
- previous_index = offsets.vector()[1];
- if (offsets.vector()[0] == offsets.vector()[1]) {
- previous_index++;
+ // Create an array that looks like the static last_match_info array
+ // that is attached to the global RegExp object. We will be returning
+ // an array of these.
+ Handle<FixedArray> matches_array(JSArray::cast(*matches)->elements());
+ Handle<JSArray> latest_match =
+ Factory::NewJSArray(kFirstCapture + number_of_capture_registers);
+ Handle<FixedArray> latest_match_array(latest_match->elements());
+
+ for (int i = 0; i < number_of_capture_registers; i++) {
+ SetCapture(*latest_match_array, i, GetCapture(*matches_array, i));
}
+ SetLastCaptureCount(*latest_match_array, number_of_capture_registers);
+
+ SetElement(result, result_length, latest_match);
+ result_length++;
+ previous_index = GetCapture(*matches_array, 1);
+ if (GetCapture(*matches_array, 0) == previous_index)
+ previous_index++;
+
} else {
ASSERT(matches->IsNull());
return result;
@@ -552,7 +606,8 @@
Handle<Object> RegExpImpl::IrregexpExecOnce(Handle<FixedArray> irregexp,
- int num_captures,
+ int number_of_capture_registers,
+ Handle<JSArray> last_match_info,
Handle<String> subject,
int previous_index,
int* offsets_vector,
@@ -642,7 +697,7 @@
#endif
}
case RegExpMacroAssembler::kBytecodeImplementation: {
- for (int i = (num_captures + 1) * 2 - 1; i >= 0; i--) {
+ for (int i = number_of_capture_registers - 1; i >= 0; i--) {
offsets_vector[i] = -1;
}
Handle<ByteArray> byte_codes = IrregexpByteCode(irregexp);
@@ -664,13 +719,16 @@
return Factory::null_value();
}
- Handle<FixedArray> array = Factory::NewFixedArray(2 * (num_captures+1));
+ Handle<FixedArray> array(last_match_info->elements());
// The captures come in (start, end+1) pairs.
- for (int i = 0; i < 2 * (num_captures + 1); i += 2) {
- array->set(i, Smi::FromInt(offsets_vector[i]));
- array->set(i + 1, Smi::FromInt(offsets_vector[i + 1]));
+ for (int i = 0; i < number_of_capture_registers; i += 2) {
+ SetCapture(*array, i, offsets_vector[i]);
+ SetCapture(*array, i + 1, offsets_vector[i + 1]);
}
- return Factory::NewJSArrayWithElements(array);
+ SetLastCaptureCount(*array, number_of_capture_registers);
+ SetLastSubject(*array, *subject);
+ SetLastInput(*array, *subject);
+ return last_match_info;
}
@@ -3723,9 +3781,6 @@
// |
// [if r >= f] \----> ...
//
- //
- // TODO(someone): clear captures on repetition and handle empty
- // matches.
// 15.10.2.5 RepeatMatcher algorithm.
// The parser has already eliminated the case where max is 0. In the case
diff --git a/src/jsregexp.h b/src/jsregexp.h
index fbacff3..84e5b95 100644
--- a/src/jsregexp.h
+++ b/src/jsregexp.h
@@ -59,13 +59,15 @@
// This function calls the garbage collector if necessary.
static Handle<Object> Exec(Handle<JSRegExp> regexp,
Handle<String> subject,
- Handle<Object> index);
+ int index,
+ Handle<JSArray> lastMatchInfo);
// Call RegExp.prototyp.exec(string) in a loop.
// Used by String.prototype.match and String.prototype.replace.
// This function calls the garbage collector if necessary.
static Handle<Object> ExecGlobal(Handle<JSRegExp> regexp,
- Handle<String> subject);
+ Handle<String> subject,
+ Handle<JSArray> lastMatchInfo);
// Prepares a JSRegExp object with Irregexp-specific data.
static Handle<Object> IrregexpPrepare(Handle<JSRegExp> re,
@@ -79,18 +81,22 @@
Handle<String> match_pattern);
static Handle<Object> AtomExec(Handle<JSRegExp> regexp,
Handle<String> subject,
- Handle<Object> index);
+ int index,
+ Handle<JSArray> lastMatchInfo);
static Handle<Object> AtomExecGlobal(Handle<JSRegExp> regexp,
- Handle<String> subject);
+ Handle<String> subject,
+ Handle<JSArray> lastMatchInfo);
// Execute an Irregexp bytecode pattern.
static Handle<Object> IrregexpExec(Handle<JSRegExp> regexp,
Handle<String> subject,
- Handle<Object> index);
+ int index,
+ Handle<JSArray> lastMatchInfo);
static Handle<Object> IrregexpExecGlobal(Handle<JSRegExp> regexp,
- Handle<String> subject);
+ Handle<String> subject,
+ Handle<JSArray> lastMatchInfo);
static void NewSpaceCollectionPrologue();
static void OldSpaceCollectionPrologue();
@@ -107,6 +113,30 @@
static const int kIrregexpCodeIndex = 3;
static const int kIrregexpDataLength = 4;
+ // Offsets in the lastMatchInfo array.
+ static const int kLastCaptureCount = 0;
+ static const int kLastSubject = 1;
+ static const int kLastInput = 2;
+ static const int kFirstCapture = 1;
+ static const int kLastMatchOverhead = 3;
+ static int GetCapture(FixedArray* array, int index) {
+ return Smi::cast(array->get(index + kFirstCapture))->value();
+ }
+ static void SetLastCaptureCount(FixedArray* array, int to) {
+ array->set(kLastCaptureCount, Smi::FromInt(to));
+ }
+ static void SetLastSubject(FixedArray* array, String* to) {
+ int capture_count = GetLastCaptureCount(array);
+ array->set(capture_count + kLastSubject, to);
+ }
+ static void SetLastInput(FixedArray* array, String* to) {
+ int capture_count = GetLastCaptureCount(array);
+ array->set(capture_count + kLastInput, to);
+ }
+ static void SetCapture(FixedArray* array, int index, int to) {
+ array->set(index + kFirstCapture, Smi::FromInt(to));
+ }
+
private:
static String* last_ascii_string_;
static String* two_byte_cached_string_;
@@ -121,6 +151,7 @@
// Returns an empty handle in case of an exception.
static Handle<Object> IrregexpExecOnce(Handle<FixedArray> regexp,
int num_captures,
+ Handle<JSArray> lastMatchInfo,
Handle<String> subject16,
int previous_index,
int* ovector,
@@ -134,6 +165,10 @@
int character_position,
int utf8_position);
+ // Used to access the lastMatchInfo array.
+ static int GetLastCaptureCount(FixedArray* array) {
+ return Smi::cast(array->get(kLastCaptureCount))->value();
+ }
// A one element cache of the last utf8_subject string and its length. The
// subject JS String object is cached in the heap. We also cache a
// translation between position and utf8 position.
diff --git a/src/log.cc b/src/log.cc
index 74b9289..ea81b81 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -134,6 +134,24 @@
//
+// StackTracer implementation
+//
+void StackTracer::Trace(TickSample* sample) {
+ // Assuming that stack grows from lower addresses
+ if (sample->state != GC
+ && (sample->sp < sample->fp && sample->fp < low_stack_bound_)) {
+ sample->InitStack(1);
+ sample->stack[0] = Memory::Address_at(
+ (Address)(sample->fp + StandardFrameConstants::kCallerPCOffset));
+ } else {
+ // GC runs or FP seems to be in some intermediate state,
+ // better discard this sample
+ sample->InitStack(0);
+ }
+}
+
+
+//
// Ticker used to provide ticks to the profiler and the sliding state
// window.
//
@@ -141,12 +159,12 @@
public:
explicit Ticker(int interval, unsigned int low_stack_bound):
Sampler(interval, FLAG_prof), window_(NULL), profiler_(NULL),
- low_stack_bound_(low_stack_bound) {}
+ stack_tracer_(low_stack_bound) {}
~Ticker() { if (IsActive()) Stop(); }
void Tick(TickSample* sample) {
- if (IsProfiling()) SampleStack(sample);
+ if (IsProfiling()) stack_tracer_.Trace(sample);
if (profiler_) profiler_->Insert(sample);
if (window_) window_->AddState(sample->state);
}
@@ -172,21 +190,9 @@
}
private:
- void SampleStack(TickSample* sample) {
- // Assuming that stack grows from lower addresses
- if (sample->sp < sample->fp && sample->fp < low_stack_bound_) {
- sample->InitStack(1);
- sample->stack[0] = Memory::Address_at(
- (Address)(sample->fp + StandardFrameConstants::kCallerPCOffset));
- } else {
- // FP seems to be in some intermediate state, better discard this sample
- sample->InitStack(0);
- }
- }
-
SlidingStateWindow* window_;
Profiler* profiler_;
- unsigned int low_stack_bound_;
+ StackTracer stack_tracer_;
};
diff --git a/src/log.h b/src/log.h
index 7d17567..aceb282 100644
--- a/src/log.h
+++ b/src/log.h
@@ -272,6 +272,17 @@
};
+// Class that extracts stack trace, used for profiling
+class StackTracer BASE_EMBEDDED {
+ public:
+ explicit StackTracer(unsigned int low_stack_bound)
+ : low_stack_bound_(low_stack_bound) { }
+ void Trace(TickSample* sample);
+ private:
+ unsigned int low_stack_bound_;
+};
+
+
} } // namespace v8::internal
#endif // V8_LOG_H_
diff --git a/src/macro-assembler-arm.cc b/src/macro-assembler-arm.cc
index dc6d40f..88a300b 100644
--- a/src/macro-assembler-arm.cc
+++ b/src/macro-assembler-arm.cc
@@ -43,7 +43,8 @@
: Assembler(buffer, size),
unresolved_(0),
generating_stub_(false),
- allow_stub_calls_(true) {
+ allow_stub_calls_(true),
+ code_object_(Heap::undefined_value()) {
}
@@ -270,8 +271,8 @@
stm(db_w, sp, cp.bit() | fp.bit() | lr.bit());
mov(ip, Operand(Smi::FromInt(type)));
push(ip);
- mov(ip, Operand(0));
- push(ip); // Push an empty code cache slot.
+ mov(ip, Operand(CodeObject()));
+ push(ip);
add(fp, sp, Operand(3 * kPointerSize)); // Adjust FP to point to saved FP.
}
diff --git a/src/macro-assembler-arm.h b/src/macro-assembler-arm.h
index b2edf3e..4b999fd 100644
--- a/src/macro-assembler-arm.h
+++ b/src/macro-assembler-arm.h
@@ -230,6 +230,8 @@
};
List<Unresolved>* unresolved() { return &unresolved_; }
+ Handle<Object> CodeObject() { return code_object_; }
+
// ---------------------------------------------------------------------------
// StatsCounter support
@@ -265,6 +267,8 @@
List<Unresolved> unresolved_;
bool generating_stub_;
bool allow_stub_calls_;
+ Handle<Object> code_object_; // This handle will be patched with the code
+ // object on installation.
// Helper functions for generating invokes.
void InvokePrologue(const ParameterCount& expected,
diff --git a/src/macro-assembler-ia32.cc b/src/macro-assembler-ia32.cc
index 80e4d3e..506f890 100644
--- a/src/macro-assembler-ia32.cc
+++ b/src/macro-assembler-ia32.cc
@@ -39,7 +39,8 @@
: Assembler(buffer, size),
unresolved_(0),
generating_stub_(false),
- allow_stub_calls_(true) {
+ allow_stub_calls_(true),
+ code_object_(Heap::undefined_value()) {
}
@@ -317,7 +318,11 @@
mov(ebp, Operand(esp));
push(esi);
push(Immediate(Smi::FromInt(type)));
- push(Immediate(0)); // Push an empty code cache slot.
+ push(Immediate(CodeObject()));
+ if (FLAG_debug_code) {
+ cmp(Operand(esp, 0), Immediate(Factory::undefined_value()));
+ Check(not_equal, "code object not properly patched");
+ }
}
diff --git a/src/macro-assembler-ia32.h b/src/macro-assembler-ia32.h
index b8fb3b9..b389df2 100644
--- a/src/macro-assembler-ia32.h
+++ b/src/macro-assembler-ia32.h
@@ -235,6 +235,8 @@
};
List<Unresolved>* unresolved() { return &unresolved_; }
+ Handle<Object> CodeObject() { return code_object_; }
+
// ---------------------------------------------------------------------------
// StatsCounter support
@@ -267,6 +269,8 @@
List<Unresolved> unresolved_;
bool generating_stub_;
bool allow_stub_calls_;
+ Handle<Object> code_object_; // This handle will be patched with the code
+ // code object on installation.
// Helper functions for generating invokes.
void InvokePrologue(const ParameterCount& expected,
diff --git a/src/macros.py b/src/macros.py
index b036c63..a3db5f9 100644
--- a/src/macros.py
+++ b/src/macros.py
@@ -1,4 +1,4 @@
-# Copyright 2006-2008 the V8 project authors. All rights reserved.
+# Copyright 2006-2009 the V8 project authors. All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
@@ -99,3 +99,22 @@
# Accessors for original global properties that ensure they have been loaded.
const ORIGINAL_REGEXP = (global.RegExp, $RegExp);
const ORIGINAL_DATE = (global.Date, $Date);
+
+# Constants used on an array to implement the properties of the RegExp object.
+const REGEXP_NUMBER_OF_CAPTURES = 0;
+const REGEXP_FIRST_CAPTURE = 1;
+
+# We can't put macros in macros so we use constants here.
+# REGEXP_NUMBER_OF_CAPTURES
+macro NUMBER_OF_CAPTURES(array) = ((array)[0]);
+
+# Last input and last subject are after the captures so we can omit them on
+# results returned from global searches. Beware - these evaluate their
+# arguments twice.
+macro LAST_SUBJECT(array) = ((array)[(array)[0] + 1]);
+macro LAST_INPUT(array) = ((array)[(array)[0] + 2]);
+
+# REGEXP_FIRST_CAPTURE
+macro CAPTURE(index) = (1 + (index));
+const CAPTURE0 = 1;
+const CAPTURE1 = 2;
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index 4ab4cfe..a747ca3 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -58,11 +58,11 @@
int MarkCompactCollector::live_lo_objects_ = 0;
#endif
-void MarkCompactCollector::CollectGarbage(GCTracer* tracer) {
- // Rather than passing the tracer around we stash it in a static member
- // variable.
- tracer_ = tracer;
- Prepare();
+void MarkCompactCollector::CollectGarbage() {
+ // Make sure that Prepare() has been called. The individual steps below will
+ // update the state as they proceed.
+ ASSERT(state_ == PREPARE_GC);
+
// Prepare has selected whether to compact the old generation or not.
// Tell the tracer.
if (IsCompacting()) tracer_->set_is_compacting();
@@ -96,7 +96,11 @@
}
-void MarkCompactCollector::Prepare() {
+void MarkCompactCollector::Prepare(GCTracer* tracer) {
+ // Rather than passing the tracer around we stash it in a static member
+ // variable.
+ tracer_ = tracer;
+
static const int kFragmentationLimit = 50; // Percent.
#ifdef DEBUG
ASSERT(state_ == IDLE);
@@ -241,7 +245,6 @@
// Helper class for marking pointers in HeapObjects.
class MarkingVisitor : public ObjectVisitor {
public:
-
void VisitPointer(Object** p) {
MarkObjectByPointer(p);
}
diff --git a/src/mark-compact.h b/src/mark-compact.h
index 746aead..9a92ade 100644
--- a/src/mark-compact.h
+++ b/src/mark-compact.h
@@ -74,8 +74,12 @@
// Type of functions to process non-live objects.
typedef void (*ProcessNonLiveFunction)(HeapObject* object);
+ // Prepares for GC by resetting relocation info in old and map spaces and
+ // choosing spaces to compact.
+ static void Prepare(GCTracer* tracer);
+
// Performs a global garbage collection.
- static void CollectGarbage(GCTracer* tracer);
+ static void CollectGarbage();
// True if the last full GC performed heap compaction.
static bool HasCompacted() { return compacting_collection_; }
@@ -123,10 +127,6 @@
// collection (NULL before and after).
static GCTracer* tracer_;
- // Prepares for GC by resetting relocation info in old and map spaces and
- // choosing spaces to compact.
- static void Prepare();
-
// Finishes GC, performs heap verification if enabled.
static void Finish();
diff --git a/src/mksnapshot.cc b/src/mksnapshot.cc
index a1c9850..40a9019 100644
--- a/src/mksnapshot.cc
+++ b/src/mksnapshot.cc
@@ -162,6 +162,8 @@
const int kExtensionCount = 1;
const char* extension_list[kExtensionCount] = { "v8/gc" };
v8::ExtensionConfiguration extensions(kExtensionCount, extension_list);
+
+ i::Serializer::Enable();
v8::Context::New(&extensions);
// Make sure all builtin scripts are cached.
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index 7b7afc4..0362a15 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -665,6 +665,8 @@
void Code::CodeVerify() {
CHECK(ic_flag() == IC_TARGET_IS_ADDRESS);
+ CHECK(IsAligned(reinterpret_cast<intptr_t>(instruction_start()),
+ static_cast<intptr_t>(kCodeAlignment)));
Address last_gc_pc = NULL;
for (RelocIterator it(this); !it.done(); it.next()) {
it.rinfo()->Verify();
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 66ca97e..9705b75 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -2281,7 +2281,7 @@
byte* Code::relocation_start() {
- return FIELD_ADDR(this, CodeSize() - sinfo_size() - relocation_size());
+ return FIELD_ADDR(this, kHeaderSize + instruction_size());
}
@@ -2297,7 +2297,7 @@
byte* Code::sinfo_start() {
- return FIELD_ADDR(this, CodeSize() - sinfo_size());
+ return FIELD_ADDR(this, kHeaderSize + body_size());
}
diff --git a/src/objects.cc b/src/objects.cc
index dbecdc9..14d9778 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -4879,6 +4879,20 @@
}
+void JSArray::EnsureSize(int required_size) {
+ ASSERT(HasFastElements());
+ if (elements()->length() >= required_size) return;
+ Handle<FixedArray> old_backing(elements());
+ int old_size = old_backing->length();
+ // Doubling in size would be overkill, but leave some slack to avoid
+ // constantly growing.
+ int new_size = required_size + (required_size >> 3);
+ Handle<FixedArray> new_backing = Factory::NewFixedArray(new_size);
+ for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
+ SetContent(*new_backing);
+}
+
+
// Computes the new capacity when expanding the elements of a JSObject.
static int NewElementsCapacity(int old_capacity) {
// (old_capacity + 50%) + 16
diff --git a/src/objects.h b/src/objects.h
index fcad01a..65dba63 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -2255,7 +2255,7 @@
static int SizeFor(int body_size, int sinfo_size) {
ASSERT_SIZE_TAG_ALIGNED(body_size);
ASSERT_SIZE_TAG_ALIGNED(sinfo_size);
- return kHeaderSize + body_size + sinfo_size;
+ return RoundUp(kHeaderSize + body_size + sinfo_size, kCodeAlignment);
}
// Locating source position.
@@ -2279,7 +2279,14 @@
static const int kSInfoSizeOffset = kRelocationSizeOffset + kIntSize;
static const int kFlagsOffset = kSInfoSizeOffset + kIntSize;
static const int kKindSpecificFlagsOffset = kFlagsOffset + kIntSize;
- static const int kHeaderSize = kKindSpecificFlagsOffset + kIntSize;
+ // Add filler objects to align the instruction start following right after
+ // the Code object header.
+ static const int kFiller6Offset = kKindSpecificFlagsOffset + kIntSize;
+ static const int kFiller7Offset = kFiller6Offset + kIntSize;
+ static const int kHeaderSize = kFiller7Offset + kIntSize;
+
+ // Code entry points are aligned to 32 bytes.
+ static const int kCodeAlignment = 32;
// Byte offsets within kKindSpecificFlagsOffset.
static const int kICFlagOffset = kKindSpecificFlagsOffset + 0;
@@ -3779,6 +3786,10 @@
// Casting.
static inline JSArray* cast(Object* obj);
+ // Uses handles. Ensures that the fixed array backing the JSArray has at
+ // least the stated size.
+ void EnsureSize(int minimum_size_of_backing_fixed_array);
+
// Dispatched behavior.
#ifdef DEBUG
void JSArrayPrint();
diff --git a/src/parser.cc b/src/parser.cc
index 0a5fa31..611f93a 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -69,6 +69,11 @@
Handle<String> name,
int start_position, bool is_expression);
+ // The minimum number of contiguous assignment that will
+ // be treated as an initialization block. Benchmarks show that
+ // the overhead exceeds the savings below this limit.
+ static const int kMinInitializationBlock = 3;
+
protected:
enum Mode {
@@ -1215,6 +1220,110 @@
}
+// An InitializationBlockFinder finds and marks sequences of statements of the
+// form x.y.z.a = ...; x.y.z.b = ...; etc.
+class InitializationBlockFinder {
+ public:
+ InitializationBlockFinder()
+ : first_in_block_(NULL), last_in_block_(NULL), block_size_(0) {}
+
+ ~InitializationBlockFinder() {
+ if (InBlock()) EndBlock();
+ }
+
+ void Update(Statement* stat) {
+ Assignment* assignment = AsAssignment(stat);
+ if (InBlock()) {
+ if (BlockContinues(assignment)) {
+ UpdateBlock(assignment);
+ } else {
+ EndBlock();
+ }
+ }
+ if (!InBlock() && (assignment != NULL) &&
+ (assignment->op() == Token::ASSIGN)) {
+ StartBlock(assignment);
+ }
+ }
+
+ private:
+ static Assignment* AsAssignment(Statement* stat) {
+ if (stat == NULL) return NULL;
+ ExpressionStatement* exp_stat = stat->AsExpressionStatement();
+ if (exp_stat == NULL) return NULL;
+ return exp_stat->expression()->AsAssignment();
+ }
+
+ // Returns true if the expressions appear to denote the same object.
+ // In the context of initialization blocks, we only consider expressions
+ // of the form 'x.y.z'.
+ static bool SameObject(Expression* e1, Expression* e2) {
+ VariableProxy* v1 = e1->AsVariableProxy();
+ VariableProxy* v2 = e2->AsVariableProxy();
+ if (v1 != NULL && v2 != NULL) {
+ return v1->name()->Equals(*v2->name());
+ }
+ Property* p1 = e1->AsProperty();
+ Property* p2 = e2->AsProperty();
+ if ((p1 == NULL) || (p2 == NULL)) return false;
+ Literal* key1 = p1->key()->AsLiteral();
+ Literal* key2 = p2->key()->AsLiteral();
+ if ((key1 == NULL) || (key2 == NULL)) return false;
+ if (!key1->handle()->IsString() || !key2->handle()->IsString()) {
+ return false;
+ }
+ String* name1 = String::cast(*key1->handle());
+ String* name2 = String::cast(*key2->handle());
+ if (!name1->Equals(name2)) return false;
+ return SameObject(p1->obj(), p2->obj());
+ }
+
+ // Returns true if the expressions appear to denote different properties
+ // of the same object.
+ static bool PropertyOfSameObject(Expression* e1, Expression* e2) {
+ Property* p1 = e1->AsProperty();
+ Property* p2 = e2->AsProperty();
+ if ((p1 == NULL) || (p2 == NULL)) return false;
+ return SameObject(p1->obj(), p2->obj());
+ }
+
+ bool BlockContinues(Assignment* assignment) {
+ if ((assignment == NULL) || (first_in_block_ == NULL)) return false;
+ if (assignment->op() != Token::ASSIGN) return false;
+ return PropertyOfSameObject(first_in_block_->target(),
+ assignment->target());
+ }
+
+ void StartBlock(Assignment* assignment) {
+ first_in_block_ = assignment;
+ last_in_block_ = assignment;
+ block_size_ = 1;
+ }
+
+ void UpdateBlock(Assignment* assignment) {
+ last_in_block_ = assignment;
+ ++block_size_;
+ }
+
+ void EndBlock() {
+ if (block_size_ >= Parser::kMinInitializationBlock) {
+ first_in_block_->mark_block_start();
+ last_in_block_->mark_block_end();
+ }
+ last_in_block_ = first_in_block_ = NULL;
+ block_size_ = 0;
+ }
+
+ bool InBlock() { return first_in_block_ != NULL; }
+
+ Assignment* first_in_block_;
+ Assignment* last_in_block_;
+ int block_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(InitializationBlockFinder);
+};
+
+
void* Parser::ParseSourceElements(ZoneListWrapper<Statement>* processor,
int end_token,
bool* ok) {
@@ -1228,9 +1337,15 @@
TargetScope scope(this);
ASSERT(processor != NULL);
+ InitializationBlockFinder block_finder;
while (peek() != end_token) {
Statement* stat = ParseStatement(NULL, CHECK_OK);
- if (stat && !stat->IsEmpty()) processor->Add(stat);
+ if (stat == NULL || stat->IsEmpty()) continue;
+ // We find and mark the initialization blocks on top level code only.
+ // This is because the optimization prevents reuse of the map transitions,
+ // so it should be used only for code that will only be run once.
+ if (top_scope_->is_global_scope()) block_finder.Update(stat);
+ processor->Add(stat);
}
return 0;
}
diff --git a/src/regexp-delay.js b/src/regexp-delay.js
index f1ded9d..8b075c6 100644
--- a/src/regexp-delay.js
+++ b/src/regexp-delay.js
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2006-2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -52,7 +52,7 @@
var multiline = false;
for (var i = 0; i < flags.length; i++) {
- var c = flags.charAt(i);
+ var c = StringCharAt.call(flags, i);
switch (c) {
case 'g':
// Allow duplicate flags to be consistent with JSC and others.
@@ -117,15 +117,15 @@
// Deprecated RegExp.prototype.compile method. We behave like the constructor
// were called again. In SpiderMonkey, this method returns the regexp object.
-// In KJS, it returns undefined. For compatibility with KJS, we match their
+// In JSC, it returns undefined. For compatibility with JSC, we match their
// behavior.
function CompileRegExp(pattern, flags) {
- // Both KJS and SpiderMonkey treat a missing pattern argument as the
+ // Both JSC and SpiderMonkey treat a missing pattern argument as the
// empty subject string, and an actual undefined value passed as the
- // patter as the string 'undefined'. Note that KJS is inconsistent
+ // pattern as the string 'undefined'. Note that JSC is inconsistent
// here, treating undefined values differently in
// RegExp.prototype.compile and in the constructor, where they are
- // the empty string. For compatibility with KJS, we match their
+ // the empty string. For compatibility with JSC, we match their
// behavior.
if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) {
DoConstructRegExp(this, 'undefined', flags, false);
@@ -135,32 +135,20 @@
}
-// DoRegExpExec and DoRegExpExecGlobal are wrappers around the runtime
-// %RegExp and %RegExpGlobal functions that ensure that the static
-// properties of the RegExp constructor are set.
function DoRegExpExec(regexp, string, index) {
- var matchIndices = %RegExpExec(regexp, string, index);
- if (!IS_NULL(matchIndices)) {
- regExpCaptures = matchIndices;
- regExpSubject = regExpInput = string;
- }
- return matchIndices;
+ return %RegExpExec(regexp, string, index, lastMatchInfo);
}
function DoRegExpExecGlobal(regexp, string) {
- // Here, matchIndices is an array of arrays of substring indices.
- var matchIndices = %RegExpExecGlobal(regexp, string);
- if (matchIndices.length != 0) {
- regExpCaptures = matchIndices[matchIndices.length - 1];
- regExpSubject = regExpInput = string;
- }
- return matchIndices;
+ // Returns an array of arrays of substring indices.
+ return %RegExpExecGlobal(regexp, string, lastMatchInfo);
}
function RegExpExec(string) {
if (%_ArgumentsLength() == 0) {
+ var regExpInput = LAST_INPUT(lastMatchInfo);
if (IS_UNDEFINED(regExpInput)) {
throw MakeError('no_input_to_regexp', [this]);
}
@@ -177,23 +165,21 @@
}
%_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
- // matchIndices is an array of integers with length of captures*2,
- // each pair of integers specified the start and the end of index
- // in the string.
- var matchIndices = DoRegExpExec(this, s, i);
+ // matchIndices is either null or the lastMatchInfo array.
+ var matchIndices = %RegExpExec(this, s, i, lastMatchInfo);
if (matchIndices == null) {
if (this.global) this.lastIndex = 0;
return matchIndices; // no match
}
- var numResults = matchIndices.length >> 1;
+ var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
var result = new $Array(numResults);
for (var i = 0; i < numResults; i++) {
- var matchStart = matchIndices[2*i];
- var matchEnd = matchIndices[2*i + 1];
+ var matchStart = lastMatchInfo[CAPTURE(i << 1)];
+ var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)];
if (matchStart != -1 && matchEnd != -1) {
- result[i] = s.slice(matchStart, matchEnd);
+ result[i] = SubString(s, matchStart, matchEnd);
} else {
// Make sure the element is present. Avoid reading the undefined
// property from the global object since this may change.
@@ -202,13 +188,15 @@
}
if (this.global)
- this.lastIndex = matchIndices[1];
- result.index = matchIndices[0];
+ this.lastIndex = lastMatchInfo[CAPTURE1];
+ result.index = lastMatchInfo[CAPTURE0];
result.input = s;
return result;
}
+// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
+// that test is defined in terms of String.prototype.exec even if it changes.
function RegExpTest(string) {
var result = (%_ArgumentsLength() == 0) ? this.exec() : this.exec(string);
return result != null;
@@ -236,56 +224,69 @@
// on the captures array of the last successful match and the subject string
// of the last successful match.
function RegExpGetLastMatch() {
- return regExpSubject.slice(regExpCaptures[0], regExpCaptures[1]);
+ var regExpSubject = LAST_SUBJECT(lastMatchInfo);
+ return SubString(regExpSubject,
+ lastMatchInfo[CAPTURE0],
+ lastMatchInfo[CAPTURE1]);
}
function RegExpGetLastParen() {
- var length = regExpCaptures.length;
- if (length <= 2) return ''; // There were no captures.
+ var length = NUMBER_OF_CAPTURES(lastMatchInfo);
+ if (length <= 2) return ''; // There were no captures.
// We match the SpiderMonkey behavior: return the substring defined by the
// last pair (after the first pair) of elements of the capture array even if
// it is empty.
- return regExpSubject.slice(regExpCaptures[length - 2],
- regExpCaptures[length - 1]);
+ var regExpSubject = LAST_SUBJECT(lastMatchInfo);
+ return SubString(regExpSubject,
+ lastMatchInfo[CAPTURE(length - 2)],
+ lastMatchInfo[CAPTURE(length - 1)]);
}
function RegExpGetLeftContext() {
- return regExpSubject.slice(0, regExpCaptures[0]);
+ return SubString(LAST_SUBJECT(lastMatchInfo),
+ 0,
+ lastMatchInfo[CAPTURE0]);
}
function RegExpGetRightContext() {
- return regExpSubject.slice(regExpCaptures[1], regExpSubject.length);
+ var subject = LAST_SUBJECT(lastMatchInfo);
+ return SubString(subject,
+ lastMatchInfo[CAPTURE1],
+ subject.length);
}
// The properties $1..$9 are the first nine capturing substrings of the last
// successful match, or ''. The function RegExpMakeCaptureGetter will be
-// called with an index greater than or equal to 1 but it actually works for
-// any non-negative index.
+// called with indeces from 1 to 9.
function RegExpMakeCaptureGetter(n) {
return function() {
var index = n * 2;
- if (index >= regExpCaptures.length) return '';
- var matchStart = regExpCaptures[index];
- var matchEnd = regExpCaptures[index + 1];
+ if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return '';
+ var matchStart = lastMatchInfo[CAPTURE(index)];
+ var matchEnd = lastMatchInfo[CAPTURE(index + 1)];
if (matchStart == -1 || matchEnd == -1) return '';
- return regExpSubject.slice(matchStart, matchEnd);
+ return SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd);
};
}
-// Properties of the builtins object for recording the result of the last
-// regexp match. The property regExpCaptures is the matchIndices array of the
-// last successful regexp match (an array of start/end index pairs for the
-// match and all the captured substrings), the invariant is that there is at
-// least two elements. The property regExpSubject is the subject string for
-// the last successful match.
-var regExpCaptures = [0, 0];
-var regExpSubject = '';
-var regExpInput;
+// Property of the builtins object for recording the result of the last
+// regexp match. The property lastMatchInfo includes the matchIndices
+// array of the last successful regexp match (an array of start/end index
+// pairs for the match and all the captured substrings), the invariant is
+// that there are at least two capture indeces. The array also contains
+// the subject string for the last successful match.
+var lastMatchInfo = [
+ 2, // REGEXP_NUMBER_OF_CAPTURES
+ 0, // REGEXP_FIRST_CAPTURE + 0
+ 0, // REGEXP_FIRST_CAPTURE + 1
+ "", // Last subject.
+ void 0, // Last input - settable with RegExpSetInput.
+];
// -------------------------------------------------------------------
@@ -303,19 +304,22 @@
));
// The spec says nothing about the length of exec and test, but
- // SpiderMonkey and KJS have length equal to 0.
+ // SpiderMonkey and JSC have length equal to 0.
%FunctionSetLength($RegExp.prototype.exec, 0);
%FunctionSetLength($RegExp.prototype.test, 0);
// The length of compile is 1 in SpiderMonkey.
%FunctionSetLength($RegExp.prototype.compile, 1);
// The properties input, $input, and $_ are aliases for each other. When this
- // value is set the value it is set to is coerced to a string.
+ // value is set the value it is set to is coerced to a string.
// Getter and setter for the input.
function RegExpGetInput() {
+ var regExpInput = LAST_INPUT(lastMatchInfo);
return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
}
- function RegExpSetInput(string) { regExpInput = ToString(string); }
+ function RegExpSetInput(string) {
+ lastMatchInfo[lastMatchInfo[REGEXP_NUMBER_OF_CAPTURES] + 2] = string;
+ };
%DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE);
%DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE);
diff --git a/src/regexp-macro-assembler-ia32.cc b/src/regexp-macro-assembler-ia32.cc
index 0e96b9d..3483460 100644
--- a/src/regexp-macro-assembler-ia32.cc
+++ b/src/regexp-macro-assembler-ia32.cc
@@ -99,8 +99,7 @@
start_label_(),
success_label_(),
backtrack_label_(),
- exit_label_(),
- self_(Heap::undefined_value()) {
+ exit_label_() {
__ jmp(&entry_label_); // We'll write the entry code later.
__ bind(&start_label_); // And then continue from here.
}
@@ -145,7 +144,7 @@
CheckPreemption();
// Pop Code* offset from backtrack stack, add Code* and jump to location.
Pop(ebx);
- __ add(Operand(ebx), Immediate(self_));
+ __ add(Operand(ebx), Immediate(masm_->CodeObject()));
__ jmp(Operand(ebx));
}
@@ -661,7 +660,7 @@
__ bind(&stack_limit_hit);
int num_arguments = 2;
FrameAlign(num_arguments, ebx);
- __ mov(Operand(esp, 1 * kPointerSize), Immediate(self_));
+ __ mov(Operand(esp, 1 * kPointerSize), Immediate(masm_->CodeObject()));
__ lea(eax, Operand(esp, -kPointerSize));
__ mov(Operand(esp, 0 * kPointerSize), eax);
CallCFunction(FUNCTION_ADDR(&CheckStackGuardState), num_arguments);
@@ -784,7 +783,7 @@
__ bind(&retry);
int num_arguments = 2;
FrameAlign(num_arguments, ebx);
- __ mov(Operand(esp, 1 * kPointerSize), Immediate(self_));
+ __ mov(Operand(esp, 1 * kPointerSize), Immediate(masm_->CodeObject()));
__ lea(eax, Operand(esp, -kPointerSize));
__ mov(Operand(esp, 0 * kPointerSize), eax);
CallCFunction(FUNCTION_ADDR(&CheckStackGuardState), num_arguments);
@@ -849,7 +848,7 @@
Handle<Code> code = Factory::NewCode(code_desc,
NULL,
Code::ComputeFlags(Code::REGEXP),
- self_);
+ masm_->CodeObject());
LOG(CodeCreateEvent("RegExp", *code, *(source->ToCString())));
return Handle<Object>::cast(code);
}
@@ -1139,7 +1138,7 @@
void RegExpMacroAssemblerIA32::SafeReturn() {
__ pop(ebx);
- __ add(Operand(ebx), Immediate(self_));
+ __ add(Operand(ebx), Immediate(masm_->CodeObject()));
__ jmp(Operand(ebx));
}
diff --git a/src/regexp-macro-assembler-ia32.h b/src/regexp-macro-assembler-ia32.h
index a9297f5..d6f11ad 100644
--- a/src/regexp-macro-assembler-ia32.h
+++ b/src/regexp-macro-assembler-ia32.h
@@ -253,9 +253,6 @@
Label exit_label_;
Label check_preempt_label_;
Label stack_overflow_label_;
-
- // Handle used to represent the generated code object itself.
- Handle<Object> self_;
};
}} // namespace v8::internal
diff --git a/src/runtime.cc b/src/runtime.cc
index b687057..5f74d72 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -858,14 +858,21 @@
static Object* Runtime_RegExpExec(Arguments args) {
HandleScope scope;
- ASSERT(args.length() == 3);
+ ASSERT(args.length() == 4);
CONVERT_CHECKED(JSRegExp, raw_regexp, args[0]);
Handle<JSRegExp> regexp(raw_regexp);
CONVERT_CHECKED(String, raw_subject, args[1]);
Handle<String> subject(raw_subject);
- Handle<Object> index(args[2]);
- ASSERT(index->IsNumber());
- Handle<Object> result = RegExpImpl::Exec(regexp, subject, index);
+ // Due to the way the JS files are constructed this must be less than the
+ // length of a string, i.e. it is always a Smi. We check anyway for security.
+ CONVERT_CHECKED(Smi, index, args[2]);
+ CONVERT_CHECKED(JSArray, raw_last_match_info, args[3]);
+ Handle<JSArray> last_match_info(raw_last_match_info);
+ CHECK(last_match_info->HasFastElements());
+ Handle<Object> result = RegExpImpl::Exec(regexp,
+ subject,
+ index->value(),
+ last_match_info);
if (result.is_null()) return Failure::Exception();
return *result;
}
@@ -873,12 +880,16 @@
static Object* Runtime_RegExpExecGlobal(Arguments args) {
HandleScope scope;
- ASSERT(args.length() == 2);
+ ASSERT(args.length() == 3);
CONVERT_CHECKED(JSRegExp, raw_regexp, args[0]);
Handle<JSRegExp> regexp(raw_regexp);
CONVERT_CHECKED(String, raw_subject, args[1]);
Handle<String> subject(raw_subject);
- Handle<Object> result = RegExpImpl::ExecGlobal(regexp, subject);
+ CONVERT_CHECKED(JSArray, raw_last_match_info, args[2]);
+ Handle<JSArray> last_match_info(raw_last_match_info);
+ CHECK(last_match_info->HasFastElements());
+ Handle<Object> result =
+ RegExpImpl::ExecGlobal(regexp, subject, last_match_info);
if (result.is_null()) return Failure::Exception();
return *result;
}
@@ -2151,6 +2162,22 @@
}
+static Object* Runtime_ToFastProperties(Arguments args) {
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSObject, object, 0);
+ object->TransformToFastProperties(0);
+ return *object;
+}
+
+
+static Object* Runtime_ToSlowProperties(Arguments args) {
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSObject, object, 0);
+ object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
+ return *object;
+}
+
+
static Object* Runtime_ToBool(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -5015,13 +5042,13 @@
Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
// Find source position.
- int position = it.frame()->FindCode()->SourcePosition(it.frame()->pc());
+ int position = it.frame()->code()->SourcePosition(it.frame()->pc());
// Check for constructor frame.
bool constructor = it.frame()->IsConstructor();
// Get code and read scope info from it for local variable information.
- Handle<Code> code(it.frame()->FindCode());
+ Handle<Code> code(it.frame()->code());
ScopeInfo<> info(*code);
// Get the context.
diff --git a/src/runtime.h b/src/runtime.h
index ecb9a10..5377e42 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -49,6 +49,8 @@
F(GetPropertyNames, 1) \
F(GetPropertyNamesFast, 1) \
F(GetArgumentsProperty, 1) \
+ F(ToFastProperties, 1) \
+ F(ToSlowProperties, 1) \
\
F(IsInPrototypeChain, 2) \
\
@@ -135,8 +137,8 @@
\
/* Regular expressions */ \
F(RegExpCompile, 3) \
- F(RegExpExec, 3) \
- F(RegExpExecGlobal, 2) \
+ F(RegExpExec, 4) \
+ F(RegExpExecGlobal, 3) \
\
/* Strings */ \
F(StringCharCodeAt, 2) \
diff --git a/src/serialize.cc b/src/serialize.cc
index 1090433..00b734a 100644
--- a/src/serialize.cc
+++ b/src/serialize.cc
@@ -930,7 +930,7 @@
}
-bool Serializer::serialization_enabled_ = true;
+bool Serializer::serialization_enabled_ = false;
#ifdef DEBUG
@@ -993,7 +993,7 @@
Heap::IterateRoots(this);
PutLog();
PutContextStack();
- disable();
+ Disable();
}
diff --git a/src/serialize.h b/src/serialize.h
index 18d36f3..ce7d947 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -147,7 +147,8 @@
static bool enabled() { return serialization_enabled_; }
- static void disable() { serialization_enabled_ = false; }
+ static void Enable() { serialization_enabled_ = true; }
+ static void Disable() { serialization_enabled_ = false; }
private:
friend class ReferenceUpdater;
diff --git a/src/spaces.cc b/src/spaces.cc
index 63add7c..b10139e 100644
--- a/src/spaces.cc
+++ b/src/spaces.cc
@@ -538,11 +538,9 @@
Object* PagedSpace::FindObject(Address addr) {
-#ifdef DEBUG
// Note: this function can only be called before or after mark-compact GC
// because it accesses map pointers.
ASSERT(!MarkCompactCollector::in_use());
-#endif
if (!Contains(addr)) return Failure::Exception();
diff --git a/src/string.js b/src/string.js
index 18d47ca..57735f0 100644
--- a/src/string.js
+++ b/src/string.js
@@ -165,8 +165,9 @@
// Build the result array.
var result = new $Array(match_string);
for (var i = 0; i < matches.length; ++i) {
- var match = matches[i];
- var match_string = subject.slice(match[0], match[1]);
+ var matchInfo = matches[i];
+ var match_string = subject.slice(matchInfo[CAPTURE0],
+ matchInfo[CAPTURE1]);
result[i] = match_string;
}
@@ -218,7 +219,9 @@
if (IS_FUNCTION(replace)) {
builder.add(replace.call(null, search, start, subject));
} else {
- ExpandReplacement(ToString(replace), subject, [ start, end ], builder);
+ reusableMatchInfo[CAPTURE0] = start;
+ reusableMatchInfo[CAPTURE1] = end;
+ ExpandReplacement(ToString(replace), subject, reusableMatchInfo, builder);
}
// suffix
@@ -228,6 +231,15 @@
}
+// This has the same size as the lastMatchInfo array, and can be used for
+// functions that expect that structure to be returned. It is used when the
+// needle is a string rather than a regexp. In this case we can't update
+// lastMatchArray without erroneously affecting the properties on the global
+// RegExp object.
+var reusableMatchInfo = [2, -1, -1, "", ""];
+var reusableMatchArray = [ void 0 ];
+
+
// Helper function for regular expressions in String.prototype.replace.
function StringReplaceRegExp(subject, regexp, replace) {
// Compute an array of matches; each match is really a list of
@@ -237,9 +249,10 @@
matches = DoRegExpExecGlobal(regexp, subject);
if (matches.length == 0) return subject;
} else {
- var captures = DoRegExpExec(regexp, subject, 0);
- if (IS_NULL(captures)) return subject;
- matches = [ captures ];
+ var lastMatchInfo = DoRegExpExec(regexp, subject, 0);
+ if (IS_NULL(lastMatchInfo)) return subject;
+ reusableMatchArray[0] = lastMatchInfo;
+ matches = reusableMatchArray;
}
// Determine the number of matches.
@@ -253,17 +266,17 @@
replace = ToString(replace);
if (%StringIndexOf(replace, "$", 0) < 0) {
for (var i = 0; i < length; i++) {
- var captures = matches[i];
- result.addSpecialSlice(previous, captures[0]);
+ var matchInfo = matches[i];
+ result.addSpecialSlice(previous, matchInfo[CAPTURE0]);
result.add(replace);
- previous = captures[1]; // continue after match
+ previous = matchInfo[CAPTURE1]; // continue after match
}
} else {
for (var i = 0; i < length; i++) {
- var captures = matches[i];
- result.addSpecialSlice(previous, captures[0]);
- ExpandReplacement(replace, subject, captures, result);
- previous = captures[1]; // continue after match
+ var matchInfo = matches[i];
+ result.addSpecialSlice(previous, matchInfo[CAPTURE0]);
+ ExpandReplacement(replace, subject, matchInfo, result);
+ previous = matchInfo[CAPTURE1]; // continue after match
}
}
result.addSpecialSlice(previous, subject.length);
@@ -273,7 +286,7 @@
// Expand the $-expressions in the string and return a new string with
// the result.
-function ExpandReplacement(string, subject, captures, builder) {
+function ExpandReplacement(string, subject, matchInfo, builder) {
var next = %StringIndexOf(string, '$', 0);
if (next < 0) {
builder.add(string);
@@ -281,11 +294,12 @@
}
// Compute the number of captures; see ECMA-262, 15.5.4.11, p. 102.
- var m = captures.length >> 1; // includes the match
+ var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; // Includes the match.
if (next > 0) builder.add(SubString(string, 0, next));
var length = string.length;
+
while (true) {
var expansion = '$';
var position = next + 1;
@@ -299,13 +313,14 @@
builder.add('$');
} else if (peek == 38) { // $& - match
++position;
- builder.addSpecialSlice(captures[0], captures[1]);
+ builder.addSpecialSlice(matchInfo[CAPTURE0],
+ matchInfo[CAPTURE1]);
} else if (peek == 96) { // $` - prefix
++position;
- builder.addSpecialSlice(0, captures[0]);
+ builder.addSpecialSlice(0, matchInfo[CAPTURE0]);
} else if (peek == 39) { // $' - suffix
++position;
- builder.addSpecialSlice(captures[1], subject.length);
+ builder.addSpecialSlice(matchInfo[CAPTURE1], subject.length);
} else if (peek >= 48 && peek <= 57) { // $n, 0 <= n <= 9
++position;
var n = peek - 48;
@@ -329,7 +344,7 @@
}
}
if (0 < n && n < m) {
- addCaptureString(builder, captures, n);
+ addCaptureString(builder, matchInfo, n);
} else {
// Because of the captures range check in the parsing of two
// digit capture references, we can only enter here when a
@@ -361,26 +376,27 @@
};
-// Compute the string of a given PCRE capture.
-function CaptureString(string, captures, index) {
+// Compute the string of a given regular expression capture.
+function CaptureString(string, lastCaptureInfo, index) {
// Scale the index.
var scaled = index << 1;
// Compute start and end.
- var start = captures[scaled];
- var end = captures[scaled + 1];
+ var start = lastCaptureInfo[CAPTURE(scaled)];
+ var end = lastCaptureInfo[CAPTURE(scaled + 1)];
// If either start or end is missing return undefined.
if (start < 0 || end < 0) return;
return SubString(string, start, end);
};
-// Add the string of a given PCRE capture to the ReplaceResultBuilder
-function addCaptureString(builder, captures, index) {
+// Add the string of a given regular expression capture to the
+// ReplaceResultBuilder
+function addCaptureString(builder, matchInfo, index) {
// Scale the index.
var scaled = index << 1;
// Compute start and end.
- var start = captures[scaled];
- var end = captures[scaled + 1];
+ var start = matchInfo[CAPTURE(scaled)];
+ var end = matchInfo[CAPTURE(scaled + 1)];
// If either start or end is missing return.
if (start < 0 || end <= start) return;
builder.addSpecialSlice(start, end);
@@ -396,10 +412,8 @@
// should be 'abcd' and not 'dddd' (or anything else).
function StringReplaceRegExpWithFunction(subject, regexp, replace) {
var result = new ReplaceResultBuilder(subject);
- // Captures is an array of pairs of (start, end) indices for the match and
- // any captured substrings.
- var captures = DoRegExpExec(regexp, subject, 0);
- if (IS_NULL(captures)) return subject;
+ var lastMatchInfo = DoRegExpExec(regexp, subject, 0);
+ if (IS_NULL(lastMatchInfo)) return subject;
// There's at least one match. If the regexp is global, we have to loop
// over all matches. The loop is not in C++ code here like the one in
@@ -409,13 +423,16 @@
if (regexp.global) {
var previous = 0;
do {
- result.addSpecialSlice(previous, captures[0]);
- result.add(ApplyReplacementFunction(replace, captures, subject));
+ result.addSpecialSlice(previous, lastMatchInfo[CAPTURE0]);
+ var startOfMatch = lastMatchInfo[CAPTURE0];
+ previous = lastMatchInfo[CAPTURE1];
+ result.add(ApplyReplacementFunction(replace, lastMatchInfo, subject));
+ // Can't use lastMatchInfo any more from here, since the function could
+ // overwrite it.
// Continue with the next match.
- previous = captures[1];
// Increment previous if we matched an empty string, as per ECMA-262
// 15.5.4.10.
- if (previous == captures[0]) {
+ if (previous == startOfMatch) {
// Add the skipped character to the output, if any.
if (previous < subject.length) {
result.addSpecialSlice(previous, previous + 1);
@@ -425,19 +442,22 @@
// Per ECMA-262 15.10.6.2, if the previous index is greater than the
// string length, there is no match
- captures = (previous > subject.length)
+ lastMatchInfo = (previous > subject.length)
? null
: DoRegExpExec(regexp, subject, previous);
- } while (!IS_NULL(captures));
+ } while (!IS_NULL(lastMatchInfo));
// Tack on the final right substring after the last match, if necessary.
if (previous < subject.length) {
result.addSpecialSlice(previous, subject.length);
}
} else { // Not a global regexp, no need to loop.
- result.addSpecialSlice(0, captures[0]);
- result.add(ApplyReplacementFunction(replace, captures, subject));
- result.addSpecialSlice(captures[1], subject.length);
+ result.addSpecialSlice(0, lastMatchInfo[CAPTURE0]);
+ var endOfMatch = lastMatchInfo[CAPTURE1];
+ result.add(ApplyReplacementFunction(replace, lastMatchInfo, subject));
+ // Can't use lastMatchInfo any more from here, since the function could
+ // overwrite it.
+ result.addSpecialSlice(endOfMatch, subject.length);
}
return result.generate();
@@ -445,20 +465,20 @@
// Helper function to apply a string replacement function once.
-function ApplyReplacementFunction(replace, captures, subject) {
+function ApplyReplacementFunction(replace, lastMatchInfo, subject) {
// Compute the parameter list consisting of the match, captures, index,
// and subject for the replace function invocation.
- var index = captures[0];
+ var index = lastMatchInfo[CAPTURE0];
// The number of captures plus one for the match.
- var m = captures.length >> 1;
+ var m = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
if (m == 1) {
- var s = CaptureString(subject, captures, 0);
+ var s = CaptureString(subject, lastMatchInfo, 0);
// Don't call directly to avoid exposing the built-in global object.
return ToString(replace.call(null, s, index, subject));
}
var parameters = $Array(m + 2);
for (var j = 0; j < m; j++) {
- parameters[j] = CaptureString(subject, captures, j);
+ parameters[j] = CaptureString(subject, lastMatchInfo, j);
}
parameters[j] = index;
parameters[j + 1] = subject;
@@ -559,14 +579,14 @@
return result;
}
- var match = splitMatch(sep, subject, currentIndex, startIndex);
+ var lastMatchInfo = splitMatch(sep, subject, currentIndex, startIndex);
- if (IS_NULL(match)) {
+ if (IS_NULL(lastMatchInfo)) {
result[result.length] = subject.slice(currentIndex, length);
return result;
}
- var endIndex = match[0];
+ var endIndex = lastMatchInfo[CAPTURE1];
// We ignore a zero-length match at the currentIndex.
if (startIndex === endIndex && endIndex === currentIndex) {
@@ -574,11 +594,20 @@
continue;
}
- result[result.length] = match[1];
+ result[result.length] =
+ SubString(subject, currentIndex, lastMatchInfo[CAPTURE0]);
if (result.length === lim) return result;
- for (var i = 2; i < match.length; i++) {
- result[result.length] = match[i];
+ for (var i = 2; i < NUMBER_OF_CAPTURES(lastMatchInfo); i += 2) {
+ var start = lastMatchInfo[CAPTURE(i)];
+ var end = lastMatchInfo[CAPTURE(i + 1)];
+ if (start != -1 && end != -1) {
+ result[result.length] = SubString(subject,
+ lastMatchInfo[CAPTURE(i)],
+ lastMatchInfo[CAPTURE(i + 1)]);
+ } else {
+ result[result.length] = void 0;
+ }
if (result.length === lim) return result;
}
@@ -588,32 +617,24 @@
// ECMA-262 section 15.5.4.14
-// Helper function used by split.
+// Helper function used by split. This version returns the lastMatchInfo
+// instead of allocating a new array with basically the same information.
function splitMatch(separator, subject, current_index, start_index) {
if (IS_REGEXP(separator)) {
- var ovector = DoRegExpExec(separator, subject, start_index);
- if (ovector == null) return null;
- var nof_results = ovector.length >> 1;
- var result = new $Array(nof_results + 1);
+ var lastMatchInfo = DoRegExpExec(separator, subject, start_index);
+ if (lastMatchInfo == null) return null;
// Section 15.5.4.14 paragraph two says that we do not allow zero length
// matches at the end of the string.
- if (ovector[0] === subject.length) return null;
- result[0] = ovector[1];
- result[1] = subject.slice(current_index, ovector[0]);
- for (var i = 1; i < nof_results; i++) {
- var matching_start = ovector[2*i];
- var matching_end = ovector[2*i + 1];
- if (matching_start != -1 && matching_end != -1) {
- result[i + 1] = subject.slice(matching_start, matching_end);
- }
- }
- return result;
+ if (lastMatchInfo[CAPTURE0] === subject.length) return null;
+ return lastMatchInfo;
}
var separatorIndex = subject.indexOf(separator, start_index);
if (separatorIndex === -1) return null;
- return [ separatorIndex + separator.length, subject.slice(current_index, separatorIndex) ];
+ reusableMatchInfo[CAPTURE0] = separatorIndex;
+ reusableMatchInfo[CAPTURE1] = separatorIndex + separator.length;
+ return reusableMatchInfo;
};
diff --git a/src/stub-cache-arm.cc b/src/stub-cache-arm.cc
index 732d292..45fa1c9 100644
--- a/src/stub-cache-arm.cc
+++ b/src/stub-cache-arm.cc
@@ -466,8 +466,6 @@
// -- lr: return address
// -----------------------------------
- HandleScope scope;
-
// Enter an internal frame.
__ EnterInternalFrame();
@@ -500,8 +498,6 @@
// ----------- S t a t e -------------
// -- lr: return address
// -----------------------------------
-
- HandleScope scope;
Label miss;
const int argc = arguments().immediate();
@@ -553,8 +549,6 @@
// ----------- S t a t e -------------
// -- lr: return address
// -----------------------------------
-
- HandleScope scope;
Label miss;
// Get the receiver from the stack
@@ -675,8 +669,6 @@
// ----------- S t a t e -------------
// -- lr: return address
// -----------------------------------
-
- HandleScope scope;
Label miss;
// TODO(1224669): Implement.
@@ -701,8 +693,6 @@
// -- lr : return address
// -- [sp] : receiver
// -----------------------------------
-
- HandleScope scope;
Label miss;
// Get the receiver from the stack.
@@ -735,8 +725,6 @@
// -- lr : return address
// -- [sp] : receiver
// -----------------------------------
-
- HandleScope scope;
Label miss;
// Get the object from the stack.
@@ -791,8 +779,6 @@
// -- lr : return address
// -- [sp] : receiver
// -----------------------------------
-
- HandleScope scope;
Label miss;
// Get the object from the stack.
@@ -845,8 +831,6 @@
// -- lr : return address
// -- [sp] : receiver
// -----------------------------------
-
- HandleScope scope;
Label miss;
__ ldr(r0, MemOperand(sp, 0));
@@ -868,8 +852,6 @@
// -- lr : return address
// -- [sp] : receiver
// -----------------------------------
-
- HandleScope scope;
Label miss;
__ ldr(r0, MemOperand(sp, 0));
@@ -890,8 +872,6 @@
// -- lr : return address
// -- [sp] : receiver
// -----------------------------------
-
- HandleScope scope;
Label miss;
__ ldr(r0, MemOperand(sp, 0));
@@ -913,8 +893,6 @@
// -- lr : return address
// -- [sp] : receiver
// -----------------------------------
-
- HandleScope scope;
Label miss;
__ ldr(r0, MemOperand(sp, 0));
@@ -939,7 +917,6 @@
// -- sp[0] : key
// -- sp[4] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
__ ldr(r2, MemOperand(sp, 0));
@@ -965,7 +942,6 @@
// -- sp[0] : key
// -- sp[4] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
__ ldr(r2, MemOperand(sp, 0));
@@ -992,7 +968,6 @@
// -- sp[0] : key
// -- sp[4] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
// Check the key is the cached one
@@ -1019,7 +994,6 @@
// -- sp[0] : key
// -- sp[4] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
// Check the key is the cached one
@@ -1043,7 +1017,6 @@
// -- sp[0] : key
// -- sp[4] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
// Check the key is the cached one
@@ -1067,8 +1040,6 @@
// -- sp[0] : key
// -- sp[4] : receiver
// -----------------------------------
- HandleScope scope;
-
Label miss;
__ IncrementCounter(&Counters::keyed_load_string_length, 1, r1, r3);
@@ -1095,7 +1066,6 @@
// -- sp[0] : key
// -- sp[4] : receiver
// -----------------------------------
- HandleScope scope;
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
return GetCode(CALLBACKS);
@@ -1112,7 +1082,6 @@
// -- lr : return address
// -- [sp] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
__ IncrementCounter(&Counters::keyed_store_field, 1, r1, r3);
diff --git a/src/stub-cache-ia32.cc b/src/stub-cache-ia32.cc
index 5acc1df..ad1eb4c 100644
--- a/src/stub-cache-ia32.cc
+++ b/src/stub-cache-ia32.cc
@@ -448,8 +448,6 @@
// TODO(1241006): Avoid having lazy compile stubs specialized by the
// number of arguments. It is not needed anymore.
Object* StubCompiler::CompileLazyCompile(Code::Flags flags) {
- HandleScope scope;
-
// Enter an internal frame.
__ EnterInternalFrame();
@@ -476,8 +474,6 @@
int index) {
// ----------- S t a t e -------------
// -----------------------------------
-
- HandleScope scope;
Label miss;
// Get the receiver from the stack.
@@ -528,8 +524,6 @@
CheckType check) {
// ----------- S t a t e -------------
// -----------------------------------
-
- HandleScope scope;
Label miss;
// Get the receiver from the stack.
@@ -649,8 +643,6 @@
String* name) {
// ----------- S t a t e -------------
// -----------------------------------
-
- HandleScope scope;
Label miss;
// Get the number of arguments.
@@ -729,8 +721,6 @@
// -- esp[0] : return address
// -- esp[4] : receiver
// -----------------------------------
-
- HandleScope scope;
Label miss;
// Get the object from the stack.
@@ -765,8 +755,6 @@
// -- esp[0] : return address
// -- esp[4] : receiver
// -----------------------------------
-
- HandleScope scope;
Label miss;
// Get the object from the stack.
@@ -821,8 +809,6 @@
// -- esp[0] : return address
// -- esp[4] : receiver
// -----------------------------------
-
- HandleScope scope;
Label miss;
// Get the object from the stack.
@@ -878,7 +864,6 @@
// -- esp[4] : key
// -- esp[8] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
__ IncrementCounter(&Counters::keyed_store_field, 1);
@@ -920,8 +905,6 @@
// -- esp[0] : return address
// -- esp[4] : receiver
// -----------------------------------
-
- HandleScope scope;
Label miss;
__ mov(eax, (Operand(esp, kPointerSize)));
@@ -942,8 +925,6 @@
// -- esp[0] : return address
// -- esp[4] : receiver
// -----------------------------------
-
- HandleScope scope;
Label miss;
__ mov(eax, (Operand(esp, kPointerSize)));
@@ -965,8 +946,6 @@
// -- esp[0] : return address
// -- esp[4] : receiver
// -----------------------------------
-
- HandleScope scope;
Label miss;
__ mov(eax, (Operand(esp, kPointerSize)));
@@ -987,7 +966,6 @@
// -- esp[0] : return address
// -- esp[4] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
__ mov(eax, (Operand(esp, kPointerSize)));
@@ -1009,7 +987,6 @@
// -- esp[4] : name
// -- esp[8] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
__ mov(eax, (Operand(esp, kPointerSize)));
@@ -1039,7 +1016,6 @@
// -- esp[4] : name
// -- esp[8] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
__ mov(eax, (Operand(esp, kPointerSize)));
@@ -1070,7 +1046,6 @@
// -- esp[4] : name
// -- esp[8] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
__ mov(eax, (Operand(esp, kPointerSize)));
@@ -1099,7 +1074,6 @@
// -- esp[4] : name
// -- esp[8] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
__ mov(eax, (Operand(esp, kPointerSize)));
@@ -1128,7 +1102,6 @@
// -- esp[4] : name
// -- esp[8] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
__ mov(eax, (Operand(esp, kPointerSize)));
@@ -1155,7 +1128,6 @@
// -- esp[4] : name
// -- esp[8] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
__ mov(eax, (Operand(esp, kPointerSize)));
@@ -1182,7 +1154,6 @@
// -- esp[4] : name
// -- esp[8] : receiver
// -----------------------------------
- HandleScope scope;
Label miss;
__ mov(eax, (Operand(esp, kPointerSize)));
diff --git a/src/stub-cache.cc b/src/stub-cache.cc
index 3daf357..35b5be3 100644
--- a/src/stub-cache.cc
+++ b/src/stub-cache.cc
@@ -892,7 +892,7 @@
Object* StubCompiler::GetCodeWithFlags(Code::Flags flags) {
CodeDesc desc;
masm_.GetCode(&desc);
- Object* result = Heap::CreateCode(desc, NULL, flags, NULL);
+ Object* result = Heap::CreateCode(desc, NULL, flags, masm_.CodeObject());
#ifdef DEBUG
if (FLAG_print_code_stubs && !result->IsFailure()) {
Code::cast(result)->Print();
diff --git a/src/stub-cache.h b/src/stub-cache.h
index 498a695..ec93c80 100644
--- a/src/stub-cache.h
+++ b/src/stub-cache.h
@@ -281,7 +281,7 @@
JSARRAY_HAS_FAST_ELEMENTS_CHECK
};
- StubCompiler() : masm_(NULL, 256) { }
+ StubCompiler() : scope_(), masm_(NULL, 256) { }
Object* CompileCallInitialize(Code::Flags flags);
Object* CompileCallPreMonomorphic(Code::Flags flags);
@@ -367,6 +367,7 @@
MacroAssembler* masm() { return &masm_; }
private:
+ HandleScope scope_;
MacroAssembler masm_;
};
diff --git a/src/top.cc b/src/top.cc
index a35d266..aa0c58e 100644
--- a/src/top.cc
+++ b/src/top.cc
@@ -333,33 +333,37 @@
}
-void Top::MarkCompactPrologue() {
- MarkCompactPrologue(&thread_local_);
+void Top::MarkCompactPrologue(bool is_compacting) {
+ MarkCompactPrologue(is_compacting, &thread_local_);
}
-void Top::MarkCompactPrologue(char* data) {
- MarkCompactPrologue(reinterpret_cast<ThreadLocalTop*>(data));
+void Top::MarkCompactPrologue(bool is_compacting, char* data) {
+ MarkCompactPrologue(is_compacting, reinterpret_cast<ThreadLocalTop*>(data));
}
-void Top::MarkCompactPrologue(ThreadLocalTop* thread) {
- StackFrame::CookFramesForThread(thread);
+void Top::MarkCompactPrologue(bool is_compacting, ThreadLocalTop* thread) {
+ if (is_compacting) {
+ StackFrame::CookFramesForThread(thread);
+ }
}
-void Top::MarkCompactEpilogue(char* data) {
- MarkCompactEpilogue(reinterpret_cast<ThreadLocalTop*>(data));
+void Top::MarkCompactEpilogue(bool is_compacting, char* data) {
+ MarkCompactEpilogue(is_compacting, reinterpret_cast<ThreadLocalTop*>(data));
}
-void Top::MarkCompactEpilogue() {
- MarkCompactEpilogue(&thread_local_);
+void Top::MarkCompactEpilogue(bool is_compacting) {
+ MarkCompactEpilogue(is_compacting, &thread_local_);
}
-void Top::MarkCompactEpilogue(ThreadLocalTop* thread) {
- StackFrame::UncookFramesForThread(thread);
+void Top::MarkCompactEpilogue(bool is_compacting, ThreadLocalTop* thread) {
+ if (is_compacting) {
+ StackFrame::UncookFramesForThread(thread);
+ }
}
@@ -690,7 +694,7 @@
HandleScope scope;
// Find code position if recorded in relocation info.
JavaScriptFrame* frame = it.frame();
- int pos = frame->FindCode()->SourcePosition(frame->pc());
+ int pos = frame->code()->SourcePosition(frame->pc());
Handle<Object> pos_obj(Smi::FromInt(pos));
// Fetch function and receiver.
Handle<JSFunction> fun(JSFunction::cast(frame->function()));
@@ -721,7 +725,7 @@
Object* script = fun->shared()->script();
if (script->IsScript() &&
!(Script::cast(script)->source()->IsUndefined())) {
- int pos = frame->FindCode()->SourcePosition(frame->pc());
+ int pos = frame->code()->SourcePosition(frame->pc());
// Compute the location from the function and the reloc info.
Handle<Script> casted_script(Script::cast(script));
*target = MessageLocation(casted_script, pos, pos + 1);
diff --git a/src/top.h b/src/top.h
index 0a3bec3..26151bd 100644
--- a/src/top.h
+++ b/src/top.h
@@ -189,10 +189,12 @@
static StackFrame::Id break_frame_id();
static int break_id();
- static void MarkCompactPrologue();
- static void MarkCompactEpilogue();
- static void MarkCompactPrologue(char* archived_thread_data);
- static void MarkCompactEpilogue(char* archived_thread_data);
+ static void MarkCompactPrologue(bool is_compacting);
+ static void MarkCompactEpilogue(bool is_compacting);
+ static void MarkCompactPrologue(bool is_compacting,
+ char* archived_thread_data);
+ static void MarkCompactEpilogue(bool is_compacting,
+ char* archived_thread_data);
static void PrintCurrentStackTrace(FILE* out);
static void PrintStackTrace(FILE* out, char* thread_data);
static void PrintStack(StringStream* accumulator);
@@ -293,8 +295,10 @@
static ThreadLocalTop thread_local_;
static void InitializeThreadLocal();
static void PrintStackTrace(FILE* out, ThreadLocalTop* thread);
- static void MarkCompactPrologue(ThreadLocalTop* archived_thread_data);
- static void MarkCompactEpilogue(ThreadLocalTop* archived_thread_data);
+ static void MarkCompactPrologue(bool is_compacting,
+ ThreadLocalTop* archived_thread_data);
+ static void MarkCompactEpilogue(bool is_compacting,
+ ThreadLocalTop* archived_thread_data);
// Debug.
// Mutex for serializing access to break control structures.
diff --git a/src/v8-counters.h b/src/v8-counters.h
index 93e8970..acd3b23 100644
--- a/src/v8-counters.h
+++ b/src/v8-counters.h
@@ -32,14 +32,15 @@
namespace v8 { namespace internal {
-#define STATS_RATE_LIST(SR) \
- SR(gc_compactor, V8.GCCompactor) /* GC Compactor time */ \
- SR(gc_scavenger, V8.GCScavenger) /* GC Scavenger time */ \
- SR(compile, V8.Compile) /* Compile time*/ \
- SR(compile_eval, V8.CompileEval) /* Eval compile time */ \
- SR(compile_lazy, V8.CompileLazy) /* Lazy compile time */ \
- SR(parse, V8.Parse) /* Parse time */ \
- SR(parse_lazy, V8.ParseLazy) /* Lazy parse time */ \
+#define STATS_RATE_LIST(SR) \
+ SR(gc_compactor, V8.GCCompactor) /* GC Compactor time */ \
+ SR(gc_scavenger, V8.GCScavenger) /* GC Scavenger time */ \
+ SR(gc_context, V8.GCContext) /* GC context cleanup time */ \
+ SR(compile, V8.Compile) /* Compile time*/ \
+ SR(compile_eval, V8.CompileEval) /* Eval compile time */ \
+ SR(compile_lazy, V8.CompileLazy) /* Lazy compile time */ \
+ SR(parse, V8.Parse) /* Parse time */ \
+ SR(parse_lazy, V8.ParseLazy) /* Lazy parse time */ \
SR(pre_parse, V8.PreParse) /* Pre-parse time */
// WARNING: STATS_COUNTER_LIST_* is a very large macro that is causing MSVC
diff --git a/src/v8.cc b/src/v8.cc
index 29013fd..cf78c0d 100644
--- a/src/v8.cc
+++ b/src/v8.cc
@@ -51,9 +51,6 @@
Logger::Setup();
if (des) des->GetLog();
- // Setup the CPU support.
- CPU::Setup();
-
// Setup the platform OS support.
OS::Setup();
@@ -83,6 +80,11 @@
StubCache::Clear();
}
+ // Setup the CPU support. Must be done after heap setup and after
+ // any deserialization because we have to have the initial heap
+ // objects in place for creating the code object used for probing.
+ CPU::Setup();
+
return true;
}
diff --git a/src/v8threads.cc b/src/v8threads.cc
index df0095d..f4a7f72 100644
--- a/src/v8threads.cc
+++ b/src/v8threads.cc
@@ -268,24 +268,24 @@
}
-void ThreadManager::MarkCompactPrologue() {
+void ThreadManager::MarkCompactPrologue(bool is_compacting) {
for (ThreadState* state = ThreadState::FirstInUse();
state != NULL;
state = state->Next()) {
char* data = state->data();
data += HandleScopeImplementer::ArchiveSpacePerThread();
- Top::MarkCompactPrologue(data);
+ Top::MarkCompactPrologue(is_compacting, data);
}
}
-void ThreadManager::MarkCompactEpilogue() {
+void ThreadManager::MarkCompactEpilogue(bool is_compacting) {
for (ThreadState* state = ThreadState::FirstInUse();
state != NULL;
state = state->Next()) {
char* data = state->data();
data += HandleScopeImplementer::ArchiveSpacePerThread();
- Top::MarkCompactEpilogue(data);
+ Top::MarkCompactEpilogue(is_compacting, data);
}
}
diff --git a/src/v8threads.h b/src/v8threads.h
index b86df73..35db9ad 100644
--- a/src/v8threads.h
+++ b/src/v8threads.h
@@ -74,8 +74,8 @@
static bool RestoreThread();
static void Iterate(ObjectVisitor* v);
- static void MarkCompactPrologue();
- static void MarkCompactEpilogue();
+ static void MarkCompactPrologue(bool is_compacting);
+ static void MarkCompactEpilogue(bool is_compacting);
static bool IsLockedByCurrentThread() { return mutex_owner_.IsSelf(); }
private:
static void EagerlyArchiveThread();
diff --git a/test/cctest/SConscript b/test/cctest/SConscript
index a2d1964..c2b6748 100644
--- a/test/cctest/SConscript
+++ b/test/cctest/SConscript
@@ -42,7 +42,7 @@
'test-sockets.cc'
],
'arch:arm': ['test-assembler-arm.cc', 'test-disasm-arm.cc'],
- 'arch:ia32': ['test-assembler-ia32.cc', 'test-disasm-ia32.cc'],
+ 'arch:ia32': ['test-assembler-ia32.cc', 'test-disasm-ia32.cc', 'test-log-ia32.cc'],
'os:linux': ['test-platform-linux.cc'],
'os:macos': ['test-platform-macos.cc'],
'os:nullos': ['test-platform-nullos.cc'],
diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status
index 2be0fd0..aa81dd1 100644
--- a/test/cctest/cctest.status
+++ b/test/cctest/cctest.status
@@ -37,3 +37,5 @@
# BUG(113): Test seems flaky on ARM.
test-spaces/LargeObjectSpace: PASS || FAIL
+# BUG(240): Test seems flaky on ARM.
+test-api/RegExpInterruption: SKIP
diff --git a/test/cctest/test-assembler-arm.cc b/test/cctest/test-assembler-arm.cc
index f8a952c..bd75a04 100644
--- a/test/cctest/test-assembler-arm.cc
+++ b/test/cctest/test-assembler-arm.cc
@@ -72,7 +72,10 @@
CodeDesc desc;
assm.GetCode(&desc);
- Object* code = Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB));
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
CHECK(code->IsCode());
#ifdef DEBUG
Code::cast(code)->Print();
@@ -106,7 +109,10 @@
CodeDesc desc;
assm.GetCode(&desc);
- Object* code = Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB));
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
CHECK(code->IsCode());
#ifdef DEBUG
Code::cast(code)->Print();
@@ -149,7 +155,10 @@
CodeDesc desc;
assm.GetCode(&desc);
- Object* code = Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB));
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
CHECK(code->IsCode());
#ifdef DEBUG
Code::cast(code)->Print();
@@ -194,7 +203,10 @@
CodeDesc desc;
assm.GetCode(&desc);
- Object* code = Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB));
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
CHECK(code->IsCode());
#ifdef DEBUG
Code::cast(code)->Print();
diff --git a/test/cctest/test-assembler-ia32.cc b/test/cctest/test-assembler-ia32.cc
index 2b68300..9ad7c76 100644
--- a/test/cctest/test-assembler-ia32.cc
+++ b/test/cctest/test-assembler-ia32.cc
@@ -69,7 +69,10 @@
CodeDesc desc;
assm.GetCode(&desc);
- Object* code = Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB));
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
CHECK(code->IsCode());
#ifdef DEBUG
Code::cast(code)->Print();
@@ -104,7 +107,10 @@
CodeDesc desc;
assm.GetCode(&desc);
- Object* code = Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB));
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
CHECK(code->IsCode());
#ifdef DEBUG
Code::cast(code)->Print();
@@ -143,7 +149,10 @@
CodeDesc desc;
assm.GetCode(&desc);
- Object* code = Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB));
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
CHECK(code->IsCode());
#ifdef DEBUG
Code::cast(code)->Print();
@@ -164,8 +173,6 @@
v8::internal::byte buffer[256];
Assembler assm(buffer, sizeof buffer);
- Serializer::disable(); // Needed for Probe when running without snapshot.
- CpuFeatures::Probe();
CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
{ CpuFeatures::Scope fscope(CpuFeatures::SSE2);
__ cvttss2si(eax, Operand(esp, 4));
@@ -175,7 +182,10 @@
CodeDesc desc;
assm.GetCode(&desc);
Code* code =
- Code::cast(Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB)));
+ Code::cast(Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value())));
// don't print the code - our disassembler can't handle cvttss2si
// instead print bytes
Disassembler::Dump(stdout,
@@ -197,8 +207,6 @@
v8::internal::byte buffer[256];
Assembler assm(buffer, sizeof buffer);
- Serializer::disable(); // Needed for Probe when running without snapshot.
- CpuFeatures::Probe();
CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
CpuFeatures::Scope fscope(CpuFeatures::SSE2);
__ cvttsd2si(eax, Operand(esp, 4));
@@ -207,7 +215,10 @@
CodeDesc desc;
assm.GetCode(&desc);
Code* code =
- Code::cast(Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB)));
+ Code::cast(Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value())));
// don't print the code - our disassembler can't handle cvttsd2si
// instead print bytes
Disassembler::Dump(stdout,
@@ -234,7 +245,10 @@
CodeDesc desc;
assm.GetCode(&desc);
Code* code =
- Code::cast(Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB)));
+ Code::cast(Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value())));
F0 f = FUNCTION_CAST<F0>(code->entry());
int res = f();
CHECK_EQ(42, res);
@@ -246,8 +260,6 @@
TEST(AssemblerIa326) {
InitializeVM();
v8::HandleScope scope;
- Serializer::disable(); // Needed for Probe when running without snapshot.
- CpuFeatures::Probe();
CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
CpuFeatures::Scope fscope(CpuFeatures::SSE2);
v8::internal::byte buffer[256];
@@ -269,7 +281,10 @@
CodeDesc desc;
assm.GetCode(&desc);
Code* code =
- Code::cast(Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB)));
+ Code::cast(Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value())));
#ifdef DEBUG
::printf("\n---\n");
// don't print the code - our disassembler can't handle SSE instructions
@@ -290,8 +305,6 @@
TEST(AssemblerIa328) {
InitializeVM();
v8::HandleScope scope;
- Serializer::disable(); // Needed for Probe when running without snapshot.
- CpuFeatures::Probe();
CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
CpuFeatures::Scope fscope(CpuFeatures::SSE2);
v8::internal::byte buffer[256];
@@ -307,7 +320,10 @@
CodeDesc desc;
assm.GetCode(&desc);
Code* code =
- Code::cast(Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB)));
+ Code::cast(Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value())));
CHECK(code->IsCode());
#ifdef DEBUG
Code::cast(code)->Print();
@@ -360,7 +376,10 @@
CodeDesc desc;
assm.GetCode(&desc);
Code* code =
- Code::cast(Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB)));
+ Code::cast(Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value())));
CHECK(code->IsCode());
#ifdef DEBUG
Code::cast(code)->Print();
diff --git a/test/cctest/test-disasm-arm.cc b/test/cctest/test-disasm-arm.cc
index 316b93a..1cca17d 100644
--- a/test/cctest/test-disasm-arm.cc
+++ b/test/cctest/test-disasm-arm.cc
@@ -74,7 +74,6 @@
// in the rest of the macros.
#define SETUP() \
InitializeVM(); \
- Serializer::disable(); \
v8::HandleScope scope; \
byte *buffer = reinterpret_cast<byte*>(malloc(4*1024)); \
Assembler assm(buffer, 4*1024); \
diff --git a/test/cctest/test-disasm-ia32.cc b/test/cctest/test-disasm-ia32.cc
index 35a39b3..af9fb97 100644
--- a/test/cctest/test-disasm-ia32.cc
+++ b/test/cctest/test-disasm-ia32.cc
@@ -56,8 +56,6 @@
TEST(DisasmIa320) {
InitializeVM();
- Serializer::disable(); // Needed for Probe when running without snapshot.
- CpuFeatures::Probe();
v8::HandleScope scope;
v8::internal::byte buffer[1024];
Assembler assm(buffer, sizeof buffer);
@@ -370,7 +368,10 @@
CodeDesc desc;
assm.GetCode(&desc);
- Object* code = Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB));
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
CHECK(code->IsCode());
#ifdef DEBUG
Code::cast(code)->Print();
diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc
index 4ac1ae4..a146c4c 100644
--- a/test/cctest/test-heap.cc
+++ b/test/cctest/test-heap.cc
@@ -77,7 +77,10 @@
CodeDesc desc;
assm.GetCode(&desc);
- Object* code = Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB));
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
CHECK(code->IsCode());
HeapObject* obj = HeapObject::cast(code);
@@ -88,7 +91,10 @@
CHECK_EQ(code, found);
}
- Object* copy = Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB));
+ Object* copy = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
CHECK(copy->IsCode());
HeapObject* obj_copy = HeapObject::cast(copy);
Object* not_right = Heap::FindCodeObject(obj_copy->address() +
diff --git a/test/cctest/test-log-ia32.cc b/test/cctest/test-log-ia32.cc
new file mode 100644
index 0000000..9e2d2f4
--- /dev/null
+++ b/test/cctest/test-log-ia32.cc
@@ -0,0 +1,231 @@
+// Copyright 2006-2009 the V8 project authors. All rights reserved.
+//
+// Tests of profiler-related functions from log.h
+
+#ifdef ENABLE_LOGGING_AND_PROFILING
+
+#include <stdlib.h>
+
+#include "v8.h"
+
+#include "log.h"
+#include "cctest.h"
+
+using v8::Function;
+using v8::Local;
+using v8::Object;
+using v8::Script;
+using v8::String;
+using v8::Value;
+
+using v8::internal::byte;
+using v8::internal::Handle;
+using v8::internal::JSFunction;
+using v8::internal::StackTracer;
+using v8::internal::TickSample;
+
+
+static v8::Persistent<v8::Context> env;
+
+
+static struct {
+ StackTracer* tracer;
+ TickSample* sample;
+} trace_env;
+
+
+static void InitTraceEnv(StackTracer* tracer, TickSample* sample) {
+ trace_env.tracer = tracer;
+ trace_env.sample = sample;
+}
+
+
+static void DoTrace(unsigned int fp) {
+ trace_env.sample->fp = fp;
+ // something that is less than fp
+ trace_env.sample->sp = trace_env.sample->fp - sizeof(unsigned int);
+ trace_env.tracer->Trace(trace_env.sample);
+}
+
+
+static void CFuncDoTrace() {
+ unsigned int fp;
+#ifdef __GNUC__
+ fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0));
+#elif defined _MSC_VER
+ __asm mov [fp], ebp // NOLINT
+#endif
+ DoTrace(fp);
+}
+
+
+static void CFunc(int i) {
+ for (int j = i; j >= 0; --j) {
+ CFuncDoTrace();
+ }
+}
+
+
+static void CheckRetAddrIsInFunction(unsigned int ret_addr,
+ unsigned int func_start_addr,
+ unsigned int func_len) {
+ printf("CheckRetAddrIsInFunction: %08x %08x %08x\n",
+ func_start_addr, ret_addr, func_start_addr + func_len);
+ CHECK_GE(ret_addr, func_start_addr);
+ CHECK_GE(func_start_addr + func_len, ret_addr);
+}
+
+
+#ifdef DEBUG
+static const int kMaxCFuncLen = 0x40; // seems enough for a small C function
+
+static void CheckRetAddrIsInCFunction(unsigned int ret_addr,
+ unsigned int func_start_addr) {
+ CheckRetAddrIsInFunction(ret_addr, func_start_addr, kMaxCFuncLen);
+}
+#endif
+
+
+TEST(PureCStackTrace) {
+ TickSample sample;
+ StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
+ InitTraceEnv(&tracer, &sample);
+ CFunc(0);
+#ifdef DEBUG
+ // C stack trace works only in debug mode, in release mode EBP is
+ // usually treated as a general-purpose register
+ CheckRetAddrIsInCFunction(reinterpret_cast<unsigned int>(sample.stack[0]),
+ reinterpret_cast<unsigned int>(&CFunc));
+ CHECK_EQ(0, sample.stack[1]);
+#endif
+}
+
+
+// --- T r a c e E x t e n s i o n ---
+
+class TraceExtension : public v8::Extension {
+ public:
+ TraceExtension() : v8::Extension("v8/trace", kSource) { }
+ virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
+ v8::Handle<v8::String> name);
+ static v8::Handle<v8::Value> Trace(const v8::Arguments& args);
+ private:
+ static const char* kSource;
+};
+
+
+const char* TraceExtension::kSource = "native function trace();";
+
+
+v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction(
+ v8::Handle<v8::String> str) {
+ return v8::FunctionTemplate::New(TraceExtension::Trace);
+}
+
+
+v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) {
+ CHECK_EQ(1, args.Length());
+ unsigned int fp = args[0]->Int32Value() << 2;
+ printf("Trace: %08x\n", fp);
+ DoTrace(fp);
+ return v8::Undefined();
+}
+
+
+static TraceExtension kTraceExtension;
+v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension);
+
+
+static void InitializeVM() {
+ if (env.IsEmpty()) {
+ v8::HandleScope scope;
+ const char* extensions[] = { "v8/trace" };
+ v8::ExtensionConfiguration config(1, extensions);
+ env = v8::Context::New(&config);
+ }
+ v8::HandleScope scope;
+ env->Enter();
+}
+
+
+static Handle<JSFunction> CompileFunction(const char* source) {
+ return v8::Utils::OpenHandle(*Script::Compile(String::New(source)));
+}
+
+
+static void CompileRun(const char* source) {
+ Script::Compile(String::New(source))->Run();
+}
+
+
+static Local<Value> GetGlobalProperty(const char* name) {
+ return env->Global()->Get(String::New(name));
+}
+
+
+static void SetGlobalProperty(const char* name, Local<Value> value) {
+ env->Global()->Set(String::New(name), value);
+}
+
+
+static bool Patch(byte* from,
+ size_t num,
+ byte* original,
+ byte* patch,
+ size_t patch_len) {
+ byte* to = from + num;
+ do {
+ from = static_cast<byte*>(memchr(from, *original, to - from));
+ CHECK(from != NULL);
+ if (memcmp(original, from, patch_len) == 0) {
+ memcpy(from, patch, patch_len);
+ return true;
+ } else {
+ from++;
+ }
+ } while (to - from > 0);
+ return false;
+}
+
+
+TEST(PureJSStackTrace) {
+ TickSample sample;
+ StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
+ InitTraceEnv(&tracer, &sample);
+
+ InitializeVM();
+ v8::HandleScope scope;
+ Handle<JSFunction> call_trace = CompileFunction("trace(0x6666);");
+ CHECK(!call_trace.is_null());
+ v8::internal::Code* call_trace_code = call_trace->code();
+ CHECK(call_trace_code->IsCode());
+
+ // push 0xcccc (= 0x6666 << 1)
+ byte original[] = { 0x68, 0xcc, 0xcc, 0x00, 0x00 };
+ // mov eax,ebp; shr eax; push eax;
+ byte patch[] = { 0x89, 0xe8, 0xd1, 0xe8, 0x50 };
+ // Patch generated code to replace pushing of a constant with
+ // pushing of ebp contents in a Smi
+ CHECK(Patch(call_trace_code->instruction_start(),
+ call_trace_code->instruction_size(),
+ original, patch, sizeof(patch)));
+
+ SetGlobalProperty("JSFuncDoTrace", v8::ToApi<Value>(call_trace));
+
+ CompileRun(
+ "function JSTrace() {"
+ " JSFuncDoTrace();"
+ "};\n"
+ "JSTrace();");
+ Handle<JSFunction> js_trace(JSFunction::cast(*(v8::Utils::OpenHandle(
+ *GetGlobalProperty("JSTrace")))));
+ v8::internal::Code* js_trace_code = js_trace->code();
+ CheckRetAddrIsInFunction(
+ reinterpret_cast<unsigned int>(sample.stack[0]),
+ reinterpret_cast<unsigned int>(js_trace_code->instruction_start()),
+ js_trace_code->instruction_size());
+ CHECK_EQ(0, sample.stack[1]);
+}
+
+#endif // ENABLE_LOGGING_AND_PROFILING
+
diff --git a/test/cctest/test-serialize.cc b/test/cctest/test-serialize.cc
index 3b7fa90..56727dc 100644
--- a/test/cctest/test-serialize.cc
+++ b/test/cctest/test-serialize.cc
@@ -157,6 +157,7 @@
const int kExtensionCount = 1;
const char* extension_list[kExtensionCount] = { "v8/gc" };
v8::ExtensionConfiguration extensions(kExtensionCount, extension_list);
+ Serializer::Enable();
v8::Persistent<v8::Context> env = v8::Context::New(&extensions);
env->Enter();
@@ -187,6 +188,7 @@
if (Snapshot::IsEnabled()) return;
StatsTable::SetCounterFunction(counter_function);
v8::HandleScope scope;
+ Serializer::Enable();
v8::Persistent<v8::Context> env = v8::Context::New();
v8::Context::Scope context_scope(env);
Serializer().Serialize();
diff --git a/test/mjsunit/regexp-string-methods.js b/test/mjsunit/regexp-string-methods.js
new file mode 100644
index 0000000..274e64b
--- /dev/null
+++ b/test/mjsunit/regexp-string-methods.js
@@ -0,0 +1,49 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Regexp shouldn't use String.prototype.slice()
+var s = new String("foo");
+assertEquals("f", s.slice(0,1));
+String.prototype.slice = function() { return "x"; }
+assertEquals("x", s.slice(0,1));
+assertEquals("g", /g/.exec("gg"));
+
+// Regexp shouldn't use String.prototype.charAt()
+var f1 = new RegExp("f", "i");
+assertEquals("F", f1.exec("F"));
+assertEquals("f", "foo".charAt(0));
+String.prototype.charAt = function(idx) { return 'g'; };
+assertEquals("g", "foo".charAt(0));
+var f2 = new RegExp("[g]", "i");
+assertEquals("G", f2.exec("G"));
+assertTrue(f2.ignoreCase);
+
+// On the other hand test is defined in a semi-coherent way as a call to exec.
+// 15.10.6.3
+// SpiderMonkey fails this one.
+RegExp.prototype.exec = function(string) { return 'x'; }
+assertTrue(/f/.test('x'));
diff --git a/test/mjsunit/top-level-assignments.js b/test/mjsunit/top-level-assignments.js
new file mode 100644
index 0000000..7ab6e25
--- /dev/null
+++ b/test/mjsunit/top-level-assignments.js
@@ -0,0 +1,96 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Testing that optimization of top-level object initialization doesn't
+// break V8.
+
+var x = new Object();
+x.a = 7;
+x.b = function() { return 42; };
+x.c = 88;
+x.d = "A Man Called Horse";
+
+assertEquals(7, x.a);
+assertEquals(42, x.b());
+assertEquals(88, x.c);
+assertEquals("A Man Called Horse", x.d);
+
+var y = new Object();
+y.a = 7;
+y.b = function() { return 42; };
+y.c = 12 * y.a;
+y.d = "A Man Called Horse";
+
+assertEquals(84, y.c);
+
+var z = new Object();
+z.a = 3;
+function forty_two() { return 42; };
+z.a += 4;
+z.b = forty_two;
+z.c = 12;
+z.c *= z.a;
+z.d = "A Man Called Horse";
+
+assertEquals(84, z.c);
+
+var x1 = new Object();
+var x2 = new Object();
+x1.a = 7;
+x1.b = function() { return 42; };
+x2.a = 7;
+x2.b = function() { return 42; };
+x1.c = 88;
+x1.d = "A Man Called Horse";
+x2.c = 88;
+x2.d = "A Man Called Horse";
+
+assertEquals(7, x1.a);
+assertEquals(42, x1.b());
+assertEquals(88, x1.c);
+assertEquals("A Man Called Horse", x1.d);
+
+assertEquals(7, x2.a);
+assertEquals(42, x2.b());
+assertEquals(88, x2.c);
+assertEquals("A Man Called Horse", x2.d);
+
+function Calculator(x, y) {
+ this.x = x;
+ this.y = y;
+}
+
+Calculator.prototype.sum = function() { return this.x + this.y; };
+Calculator.prototype.difference = function() { return this.x - this.y; };
+Calculator.prototype.product = function() { return this.x * this.y; };
+Calculator.prototype.quotient = function() { return this.x / this.y; };
+
+var calc = new Calculator(20, 10);
+assertEquals(30, calc.sum());
+assertEquals(10, calc.difference());
+assertEquals(200, calc.product());
+assertEquals(2, calc.quotient());
diff --git a/tools/js2c.py b/tools/js2c.py
index 0ae1ad9..52fe35c 100755
--- a/tools/js2c.py
+++ b/tools/js2c.py
@@ -104,7 +104,7 @@
def ExpandMacros(lines, macros):
for name, macro in macros.items():
- start = lines.find(name, 0)
+ start = lines.find(name + '(', 0)
while start != -1:
# Scan over the arguments
assert lines[start + len(name)] == '('
@@ -132,7 +132,7 @@
result = macro.expand(mapping)
# Replace the occurrence of the macro with the expansion
lines = lines[:start] + result + lines[end:]
- start = lines.find(name, end)
+ start = lines.find(name + '(', end)
return lines
class TextMacro:
diff --git a/tools/tickprocessor.py b/tools/tickprocessor.py
index bce3027..127e808 100644
--- a/tools/tickprocessor.py
+++ b/tools/tickprocessor.py
@@ -147,6 +147,9 @@
self.regions = []
+VMStates = { 'JS': 0, 'GC': 1, 'COMPILER': 2, 'OTHER': 3 }
+
+
class TickProcessor(object):
def __init__(self):
@@ -164,6 +167,7 @@
self.number_of_library_ticks = 0
self.unaccounted_number_of_ticks = 0
self.excluded_number_of_ticks = 0
+ self.number_of_gc_ticks = 0
# Flag indicating whether to ignore unaccounted ticks in the report
self.ignore_unknown = False
@@ -287,6 +291,8 @@
return result
def ProcessTick(self, pc, sp, state, stack):
+ if state == VMStates['GC']:
+ self.number_of_gc_ticks += 1
if not self.IncludeTick(pc, sp, state):
self.excluded_number_of_ticks += 1;
return
@@ -320,11 +326,7 @@
# Print the unknown ticks percentage if they are not ignored.
if not self.ignore_unknown and self.unaccounted_number_of_ticks > 0:
self.PrintHeader('Unknown')
- unknown_percentage = self.unaccounted_number_of_ticks * 100.0 / self.total_number_of_ticks
- print(' %(ticks)5d %(total)5.1f%%' % {
- 'ticks' : self.unaccounted_number_of_ticks,
- 'total' : unknown_percentage,
- })
+ self.PrintCounter(self.unaccounted_number_of_ticks)
# Print the library ticks.
self.PrintHeader('Shared libraries')
self.PrintEntries(cpp_entries, lambda e:e.IsSharedLibraryEntry())
@@ -334,6 +336,9 @@
# Print the C++ ticks.
self.PrintHeader('C++')
self.PrintEntries(cpp_entries, lambda e:not e.IsSharedLibraryEntry())
+ # Print the GC ticks.
+ self.PrintHeader('GC')
+ self.PrintCounter(self.number_of_gc_ticks)
# Print call profile.
print('\n [Call profile]:')
print(' total call path')
@@ -344,6 +349,13 @@
print('\n [%s]:' % header_title)
print(' ticks total nonlib name')
+ def PrintCounter(self, ticks_count):
+ percentage = ticks_count * 100.0 / self.total_number_of_ticks
+ print(' %(ticks)5d %(total)5.1f%%' % {
+ 'ticks' : ticks_count,
+ 'total' : percentage,
+ })
+
def PrintEntries(self, entries, condition):
# If ignoring unaccounted ticks don't include these in percentage
# calculations
@@ -418,13 +430,13 @@
self.PrintUsageAndExit()
for key, value in opts:
if key in ("-j", "--js"):
- self.state = 0
+ self.state = VMStates['JS']
if key in ("-g", "--gc"):
- self.state = 1
+ self.state = VMStates['GC']
if key in ("-c", "--compiler"):
- self.state = 2
+ self.state = VMStates['COMPILER']
if key in ("-o", "--other"):
- self.state = 3
+ self.state = VMStates['OTHER']
if key in ("--ignore-unknown"):
self.ignore_unknown = True
if key in ("--separate-ic"):
diff --git a/tools/visual_studio/v8_cctest.vcproj b/tools/visual_studio/v8_cctest.vcproj
index d870f1a..859e445 100644
--- a/tools/visual_studio/v8_cctest.vcproj
+++ b/tools/visual_studio/v8_cctest.vcproj
@@ -198,6 +198,10 @@
>
</File>
<File
+ RelativePath="..\..\test\cctest\test-log-ia32.cc"
+ >
+ </File>
+ <File
RelativePath="..\..\test\cctest\test-mark-compact.cc"
>
</File>