Version 3.0.0.
Improved performance by (partially) addressing issue 957 on IA-32. Still needs more work for the other architectures.
git-svn-id: http://v8.googlecode.com/svn/trunk@5932 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/runtime.cc b/src/runtime.cc
index c43a1ab..efdb508 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Copyright 2010 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:
@@ -33,16 +33,19 @@
#include "api.h"
#include "arguments.h"
#include "codegen.h"
+#include "compilation-cache.h"
#include "compiler.h"
#include "cpu.h"
#include "dateparser-inl.h"
#include "debug.h"
+#include "deoptimizer.h"
#include "execution.h"
#include "jsregexp.h"
#include "liveedit.h"
#include "parser.h"
#include "platform.h"
#include "runtime.h"
+#include "runtime-profiler.h"
#include "scopeinfo.h"
#include "smart-pointer.h"
#include "stub-cache.h"
@@ -611,6 +614,22 @@
}
+// Sets the magic number that identifies a function as one of the special
+// math functions that can be inlined.
+static MaybeObject* Runtime_SetMathFunctionId(Arguments args) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 2);
+ CONVERT_CHECKED(JSFunction, function, args[0]);
+ CONVERT_CHECKED(Smi, id, args[1]);
+ RUNTIME_ASSERT(id->value() >= 0);
+ RUNTIME_ASSERT(id->value() < SharedFunctionInfo::max_math_id_number());
+
+ function->shared()->set_math_function_id(id->value());
+
+ return Heap::undefined_value();
+}
+
+
static MaybeObject* Runtime_IsConstructCall(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 0);
@@ -1640,14 +1659,13 @@
static MaybeObject* Runtime_FunctionGetPositionForOffset(Arguments args) {
ASSERT(args.length() == 2);
- CONVERT_CHECKED(JSFunction, fun, args[0]);
+ CONVERT_CHECKED(Code, code, args[0]);
CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
- Code* code = fun->code();
RUNTIME_ASSERT(0 <= offset && offset < code->Size());
Address pc = code->address() + offset;
- return Smi::FromInt(fun->code()->SourcePosition(pc));
+ return Smi::FromInt(code->SourcePosition(pc));
}
@@ -1724,10 +1742,14 @@
if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
return Failure::Exception();
}
+ // Since we don't store the source for this we should never
+ // optimize this.
+ shared->code()->set_optimizable(false);
+
// Set the code, scope info, formal parameter count,
// and the length of the target function.
target->shared()->set_code(shared->code());
- target->set_code(shared->code());
+ target->ReplaceCode(shared->code());
target->shared()->set_scope_info(shared->scope_info());
target->shared()->set_length(shared->length());
target->shared()->set_formal_parameter_count(
@@ -1757,6 +1779,7 @@
// It's okay to skip the write barrier here because the literals
// are guaranteed to be in old space.
target->set_literals(*literals, SKIP_WRITE_BARRIER);
+ target->set_next_function_link(Heap::undefined_value());
}
target->set_context(*context);
@@ -2019,10 +2042,7 @@
}
Handle<JSArray> GetParts() {
- Handle<JSArray> result =
- Factory::NewJSArrayWithElements(array_builder_.array());
- result->set_length(Smi::FromInt(array_builder_.length()));
- return result;
+ return array_builder_.ToJSArray();
}
private:
@@ -2597,7 +2617,7 @@
// Perform string match of pattern on subject, starting at start index.
// Caller must ensure that 0 <= start_index <= sub->length(),
-// and should check that pat->length() + start_index <= sub->length()
+// and should check that pat->length() + start_index <= sub->length().
int Runtime::StringMatch(Handle<String> sub,
Handle<String> pat,
int start_index) {
@@ -3196,7 +3216,7 @@
if (regexp->TypeTag() == JSRegExp::ATOM) {
Handle<String> pattern(
String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
- if (!pattern->IsFlat()) FlattenString(pattern);
+ ASSERT(pattern->IsFlat());
if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
return *builder.ToJSArray(result_array);
}
@@ -4524,6 +4544,170 @@
}
+static const unsigned int kQuoteTableLength = 128u;
+
+static const char* const JsonQuotes[kQuoteTableLength] = {
+ "\\u0000", "\\u0001", "\\u0002", "\\u0003",
+ "\\u0004", "\\u0005", "\\u0006", "\\u0007",
+ "\\b", "\\t", "\\n", "\\u000b",
+ "\\f", "\\r", "\\u000e", "\\u000f",
+ "\\u0010", "\\u0011", "\\u0012", "\\u0013",
+ "\\u0014", "\\u0015", "\\u0016", "\\u0017",
+ "\\u0018", "\\u0019", "\\u001a", "\\u001b",
+ "\\u001c", "\\u001d", "\\u001e", "\\u001f",
+ NULL, NULL, "\\\"", NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ "\\\\", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+};
+
+
+static const byte JsonQuoteLengths[kQuoteTableLength] = {
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 6, 2, 2, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 1, 1, 2, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+
+template <typename Char>
+Char* WriteString(Char* dst, const char* src_string) {
+ char c;
+ for (c = *src_string; c; c = *src_string) {
+ *dst = c;
+ dst++;
+ src_string++;
+ }
+ return dst;
+}
+
+
+template <typename StringType>
+MaybeObject* AllocateRawString(int length);
+
+
+template <>
+MaybeObject* AllocateRawString<SeqTwoByteString>(int length) {
+ return Heap::AllocateRawTwoByteString(length);
+}
+
+
+template <>
+MaybeObject* AllocateRawString<SeqAsciiString>(int length) {
+ return Heap::AllocateRawAsciiString(length);
+}
+
+
+template <typename Char, typename StringType>
+static MaybeObject* QuoteJsonString(Vector<const Char> characters) {
+ int length = characters.length();
+ int quoted_length = 0;
+ for (int i = 0; i < length; i++) {
+ unsigned int c = characters[i];
+ if (sizeof(Char) > 1u) {
+ quoted_length += (c >= kQuoteTableLength) ? 1 : JsonQuoteLengths[c];
+ } else {
+ quoted_length += JsonQuoteLengths[c];
+ }
+ }
+ Counters::quote_json_char_count.Increment(length);
+
+ // Add space for quotes.
+ quoted_length += 2;
+
+ MaybeObject* new_alloc = AllocateRawString<StringType>(quoted_length);
+ Object* new_object;
+ if (!new_alloc->ToObject(&new_object)) {
+ Counters::quote_json_char_recount.Increment(length);
+ return new_alloc;
+ }
+ StringType* new_string = StringType::cast(new_object);
+
+
+ STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize);
+ Char* write_cursor = reinterpret_cast<Char*>(
+ new_string->address() + SeqAsciiString::kHeaderSize);
+ *(write_cursor++) = '"';
+ const Char* read_cursor = characters.start();
+ if (quoted_length == length + 2) {
+ CopyChars(write_cursor, read_cursor, length);
+ write_cursor += length;
+ } else {
+ const Char* end = read_cursor + length;
+ while (read_cursor < end) {
+ Char c = *(read_cursor++);
+ if (sizeof(Char) > 1u && static_cast<unsigned>(c) >= kQuoteTableLength) {
+ *(write_cursor++) = c;
+ } else {
+ const char* replacement = JsonQuotes[static_cast<unsigned>(c)];
+ if (!replacement) {
+ *(write_cursor++) = c;
+ } else {
+ write_cursor = WriteString(write_cursor, replacement);
+ }
+ }
+ }
+ }
+ *(write_cursor++) = '"';
+ ASSERT_EQ(SeqAsciiString::kHeaderSize + quoted_length * sizeof(Char),
+ reinterpret_cast<Address>(write_cursor) - new_string->address());
+ return new_string;
+}
+
+
+static MaybeObject* Runtime_QuoteJSONString(Arguments args) {
+ NoHandleAllocation ha;
+ CONVERT_CHECKED(String, str, args[0]);
+ if (!str->IsFlat()) {
+ MaybeObject* try_flatten = str->TryFlatten();
+ Object* flat;
+ if (!try_flatten->ToObject(&flat)) {
+ return try_flatten;
+ }
+ str = String::cast(flat);
+ ASSERT(str->IsFlat());
+ }
+ if (str->IsTwoByteRepresentation()) {
+ return QuoteJsonString<uc16, SeqTwoByteString>(str->ToUC16Vector());
+ } else {
+ return QuoteJsonString<char, SeqAsciiString>(str->ToAsciiVector());
+ }
+}
+
+
+
static MaybeObject* Runtime_StringParseInt(Arguments args) {
NoHandleAllocation ha;
@@ -5178,6 +5362,13 @@
}
+static MaybeObject* Runtime_AllocateHeapNumber(Arguments args) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 0);
+ return Heap::AllocateHeapNumber(0);
+}
+
+
static MaybeObject* Runtime_NumberAdd(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6450,9 +6641,12 @@
}
}
- // The function should be compiled for the optimization hints to be available.
+ // The function should be compiled for the optimization hints to be
+ // available. We cannot use EnsureCompiled because that forces a
+ // compilation through the shared function info which makes it
+ // impossible for us to optimize.
Handle<SharedFunctionInfo> shared(function->shared());
- EnsureCompiled(shared, CLEAR_EXCEPTION);
+ if (!function->is_compiled()) CompileLazy(function, CLEAR_EXCEPTION);
if (!function->has_initial_map() &&
shared->IsInobjectSlackTrackingInProgress()) {
@@ -6496,7 +6690,7 @@
#ifdef DEBUG
if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
PrintF("[lazy: ");
- function->shared()->name()->Print();
+ function->PrintName();
PrintF("]\n");
}
#endif
@@ -6513,10 +6707,236 @@
return Failure::Exception();
}
+ // All done. Return the compiled code.
+ ASSERT(function->is_compiled());
return function->code();
}
+static MaybeObject* Runtime_LazyRecompile(Arguments args) {
+ HandleScope scope;
+ ASSERT(args.length() == 1);
+ Handle<JSFunction> function = args.at<JSFunction>(0);
+ // If the function is not optimizable or debugger is active continue using the
+ // code from the full compiler.
+ if (!function->shared()->code()->optimizable() ||
+ Debug::has_break_points()) {
+ function->ReplaceCode(function->shared()->code());
+ return function->code();
+ }
+ if (CompileOptimized(function, AstNode::kNoNumber)) {
+ return function->code();
+ }
+ function->ReplaceCode(function->shared()->code());
+ return Failure::Exception();
+}
+
+
+static MaybeObject* Runtime_NotifyDeoptimized(Arguments args) {
+ HandleScope scope;
+ ASSERT(args.length() == 1);
+ RUNTIME_ASSERT(args[0]->IsSmi());
+ Deoptimizer::BailoutType type =
+ static_cast<Deoptimizer::BailoutType>(Smi::cast(args[0])->value());
+ Deoptimizer* deoptimizer = Deoptimizer::Grab();
+ ASSERT(Heap::IsAllocationAllowed());
+ int frames = deoptimizer->output_count();
+
+ JavaScriptFrameIterator it;
+ JavaScriptFrame* frame = NULL;
+ for (int i = 0; i < frames; i++) {
+ if (i != 0) it.Advance();
+ frame = it.frame();
+ deoptimizer->InsertHeapNumberValues(frames - i - 1, frame);
+ }
+ delete deoptimizer;
+
+ RUNTIME_ASSERT(frame->function()->IsJSFunction());
+ Handle<JSFunction> function(JSFunction::cast(frame->function()));
+ Handle<Object> arguments;
+ for (int i = frame->ComputeExpressionsCount() - 1; i >= 0; --i) {
+ if (frame->GetExpression(i) == Heap::the_hole_value()) {
+ if (arguments.is_null()) {
+ // FunctionGetArguments can't throw an exception, so cast away the
+ // doubt with an assert.
+ arguments = Handle<Object>(
+ Accessors::FunctionGetArguments(*function,
+ NULL)->ToObjectUnchecked());
+ ASSERT(*arguments != Heap::null_value());
+ ASSERT(*arguments != Heap::undefined_value());
+ }
+ frame->SetExpression(i, *arguments);
+ }
+ }
+
+ CompilationCache::MarkForLazyOptimizing(function);
+ if (type == Deoptimizer::EAGER) {
+ RUNTIME_ASSERT(function->IsOptimized());
+ } else {
+ RUNTIME_ASSERT(!function->IsOptimized());
+ }
+
+ // Avoid doing too much work when running with --always-opt and keep
+ // the optimized code around.
+ if (FLAG_always_opt || type == Deoptimizer::LAZY) {
+ return Heap::undefined_value();
+ }
+
+ // Count the number of optimized activations of the function.
+ int activations = 0;
+ while (!it.done()) {
+ JavaScriptFrame* frame = it.frame();
+ if (frame->is_optimized() && frame->function() == *function) {
+ activations++;
+ }
+ it.Advance();
+ }
+
+ // TODO(kasperl): For now, we cannot support removing the optimized
+ // code when we have recursive invocations of the same function.
+ if (activations == 0) {
+ if (FLAG_trace_deopt) {
+ PrintF("[removing optimized code for: ");
+ function->PrintName();
+ PrintF("]\n");
+ }
+ function->ReplaceCode(function->shared()->code());
+ }
+ return Heap::undefined_value();
+}
+
+
+static MaybeObject* Runtime_NotifyOSR(Arguments args) {
+ Deoptimizer* deoptimizer = Deoptimizer::Grab();
+ delete deoptimizer;
+ return Heap::undefined_value();
+}
+
+
+static MaybeObject* Runtime_DeoptimizeFunction(Arguments args) {
+ HandleScope scope;
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSFunction, function, 0);
+ if (!function->IsOptimized()) return Heap::undefined_value();
+
+ Deoptimizer::DeoptimizeFunction(*function);
+
+ return Heap::undefined_value();
+}
+
+
+static MaybeObject* Runtime_CompileForOnStackReplacement(Arguments args) {
+ HandleScope scope;
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSFunction, function, 0);
+
+ // We're not prepared to handle a function with arguments object.
+ ASSERT(!function->shared()->scope_info()->HasArgumentsShadow());
+
+ // We have hit a back edge in an unoptimized frame for a function that was
+ // selected for on-stack replacement. Find the unoptimized code object.
+ Handle<Code> unoptimized(function->shared()->code());
+ // Keep track of whether we've succeeded in optimizing.
+ bool succeeded = unoptimized->optimizable();
+ if (succeeded) {
+ // If we are trying to do OSR when there are already optimized
+ // activations of the function, it means (a) the function is directly or
+ // indirectly recursive and (b) an optimized invocation has been
+ // deoptimized so that we are currently in an unoptimized activation.
+ // Check for optimized activations of this function.
+ JavaScriptFrameIterator it;
+ while (succeeded && !it.done()) {
+ JavaScriptFrame* frame = it.frame();
+ succeeded = !frame->is_optimized() || frame->function() != *function;
+ it.Advance();
+ }
+ }
+
+ int ast_id = AstNode::kNoNumber;
+ if (succeeded) {
+ // The top JS function is this one, the PC is somewhere in the
+ // unoptimized code.
+ JavaScriptFrameIterator it;
+ JavaScriptFrame* frame = it.frame();
+ ASSERT(frame->function() == *function);
+ ASSERT(frame->code() == *unoptimized);
+ ASSERT(unoptimized->contains(frame->pc()));
+
+ // Use linear search of the unoptimized code's stack check table to find
+ // the AST id matching the PC.
+ Address start = unoptimized->instruction_start();
+ unsigned target_pc_offset = static_cast<unsigned>(frame->pc() - start);
+ Address table_cursor = start + unoptimized->stack_check_table_start();
+ uint32_t table_length = Memory::uint32_at(table_cursor);
+ table_cursor += kIntSize;
+ for (unsigned i = 0; i < table_length; ++i) {
+ // Table entries are (AST id, pc offset) pairs.
+ uint32_t pc_offset = Memory::uint32_at(table_cursor + kIntSize);
+ if (pc_offset == target_pc_offset) {
+ ast_id = static_cast<int>(Memory::uint32_at(table_cursor));
+ break;
+ }
+ table_cursor += 2 * kIntSize;
+ }
+ ASSERT(ast_id != AstNode::kNoNumber);
+ if (FLAG_trace_osr) {
+ PrintF("[replacing on-stack at AST id %d in ", ast_id);
+ function->PrintName();
+ PrintF("]\n");
+ }
+
+ // Try to compile the optimized code. A true return value from
+ // CompileOptimized means that compilation succeeded, not necessarily
+ // that optimization succeeded.
+ if (CompileOptimized(function, ast_id) && function->IsOptimized()) {
+ DeoptimizationInputData* data = DeoptimizationInputData::cast(
+ function->code()->deoptimization_data());
+ if (FLAG_trace_osr) {
+ PrintF("[on-stack replacement offset %d in optimized code]\n",
+ data->OsrPcOffset()->value());
+ }
+ ASSERT(data->OsrAstId()->value() == ast_id);
+ ASSERT(data->OsrPcOffset()->value() >= 0);
+ } else {
+ succeeded = false;
+ }
+ }
+
+ // Revert to the original stack checks in the original unoptimized code.
+ if (FLAG_trace_osr) {
+ PrintF("[restoring original stack checks in ");
+ function->PrintName();
+ PrintF("]\n");
+ }
+ StackCheckStub check_stub;
+ Handle<Code> check_code = check_stub.GetCode();
+ Handle<Code> replacement_code(
+ Builtins::builtin(Builtins::OnStackReplacement));
+ // Iterate the unoptimized code and revert all the patched stack checks.
+ for (RelocIterator it(*unoptimized, RelocInfo::kCodeTargetMask);
+ !it.done();
+ it.next()) {
+ RelocInfo* rinfo = it.rinfo();
+ if (rinfo->target_address() == replacement_code->entry()) {
+ Deoptimizer::RevertStackCheckCode(rinfo, *check_code);
+ }
+ }
+
+ // Allow OSR only at nesting level zero again.
+ unoptimized->set_allow_osr_at_loop_nesting_level(0);
+
+ // If the optimization attempt succeeded, return the AST id tagged as a
+ // smi. This tells the builtin that we need to translate the unoptimized
+ // frame to an optimized one.
+ if (succeeded) {
+ ASSERT(function->code()->kind() == Code::OPTIMIZED_FUNCTION);
+ return Smi::FromInt(ast_id);
+ } else {
+ return Smi::FromInt(-1);
+ }
+}
+
+
static MaybeObject* Runtime_GetFunctionDelegate(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 1);
@@ -7794,7 +8214,7 @@
int keys_length = keys->length();
for (int i = 0; i < keys_length; i++) {
Object* key = keys->get(i);
- uint32_t index;
+ uint32_t index = 0;
if (!key->ToArrayIndex(&index) || index >= length) {
// Zap invalid keys.
keys->set_undefined(i);
@@ -7921,6 +8341,7 @@
MaybeObject* maybe_value = receiver->GetPropertyWithCallback(
receiver, structure, name, result->holder());
if (!maybe_value->ToObject(&value)) {
+ if (maybe_value->IsRetryAfterGC()) return maybe_value;
ASSERT(maybe_value->IsException());
maybe_value = Top::pending_exception();
Top::clear_pending_exception();
@@ -8221,6 +8642,9 @@
}
if (it.done()) return Heap::undefined_value();
+ bool is_optimized_frame =
+ it.frame()->code()->kind() == Code::OPTIMIZED_FUNCTION;
+
// Traverse the saved contexts chain to find the active context for the
// selected frame.
SaveContext* save = Top::save_context();
@@ -8252,18 +8676,28 @@
// (e.g. .result)? For users of the debugger, they will probably be
// confusing.
Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
- for (int i = 0; i < info.NumberOfLocals(); i++) {
- // Name of the local.
- locals->set(i * 2, *info.LocalName(i));
- // Fetch the value of the local - either from the stack or from a
- // heap-allocated context.
- if (i < info.number_of_stack_slots()) {
+ // Fill in the names of the locals.
+ for (int i = 0; i < info.NumberOfLocals(); i++) {
+ locals->set(i * 2, *info.LocalName(i));
+ }
+
+ // Fill in the values of the locals.
+ for (int i = 0; i < info.NumberOfLocals(); i++) {
+ if (is_optimized_frame) {
+ // If we are inspecting an optimized frame use undefined as the
+ // value for all locals.
+ //
+ // TODO(3141533): We should be able to get the correct values
+ // for locals in optimized frames.
+ locals->set(i * 2 + 1, Heap::undefined_value());
+ } else if (i < info.number_of_stack_slots()) {
+ // Get the value from the stack.
locals->set(i * 2 + 1, it.frame()->GetExpression(i));
} else {
- Handle<String> name = info.LocalName(i);
// Traverse the context chain to the function context as all local
// variables stored in the context will be on the function context.
+ Handle<String> name = info.LocalName(i);
while (!context->is_function_context()) {
context = Handle<Context>(context->previous());
}
@@ -8273,8 +8707,12 @@
}
}
- // Check whether this frame is positioned at return.
- int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
+ // Check whether this frame is positioned at return. If not top
+ // frame or if the frame is optimized it cannot be at a return.
+ bool at_return = false;
+ if (!is_optimized_frame && index == 0) {
+ at_return = Debug::IsBreakAtReturn(it.frame());
+ }
// If positioned just before return find the value to be returned and add it
// to the frame information.
@@ -8368,8 +8806,13 @@
details->set(details_index++, Heap::undefined_value());
}
- // Parameter value.
- if (i < it.frame()->GetProvidedParametersCount()) {
+ // Parameter value. If we are inspecting an optimized frame, use
+ // undefined as the value.
+ //
+ // TODO(3141533): We should be able to get the actual parameter
+ // value for optimized frames.
+ if (!is_optimized_frame &&
+ (i < it.frame()->GetProvidedParametersCount())) {
details->set(details_index++, it.frame()->GetParameter(i));
} else {
details->set(details_index++, Heap::undefined_value());
@@ -8963,7 +9406,7 @@
// Iterate the heap looking for SharedFunctionInfo generated from the
// script. The inner most SharedFunctionInfo containing the source position
// for the requested break point is found.
- // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
+ // NOTE: This might require several heap iterations. If the SharedFunctionInfo
// which is found is not compiled it is compiled and the heap is iterated
// again as the compilation might create inner functions from the newly
// compiled function and the actual requested break point might be in one of
@@ -9785,6 +10228,15 @@
}
}
+
+static MaybeObject* Runtime_LiveEditFunctionSourceUpdated(Arguments args) {
+ ASSERT(args.length() == 1);
+ HandleScope scope;
+ CONVERT_ARG_CHECKED(JSArray, shared_info, 0);
+ return LiveEdit::FunctionSourceUpdated(shared_info);
+}
+
+
// Replaces code of SharedFunctionInfo with a new one.
static MaybeObject* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
ASSERT(args.length() == 2);
@@ -9887,7 +10339,12 @@
Handle<Code> code(function->code());
- RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
+ if (code->kind() != Code::FUNCTION &&
+ code->kind() != Code::OPTIMIZED_FUNCTION) {
+ return Heap::undefined_value();
+ }
+
+ RelocIterator it(*code, RelocInfo::ModeMask(RelocInfo::STATEMENT_POSITION));
int closest_pc = 0;
int distance = kMaxInt;
while (!it.done()) {
@@ -10041,9 +10498,9 @@
}
-// Collect the raw data for a stack trace. Returns an array of three
-// element segments each containing a receiver, function and native
-// code offset.
+// Collect the raw data for a stack trace. Returns an array of 4
+// element segments each containing a receiver, function, code and
+// native code offset.
static MaybeObject* Runtime_CollectStackTrace(Arguments args) {
ASSERT_EQ(args.length(), 2);
Handle<Object> caller = args.at<Object>(0);
@@ -10053,7 +10510,7 @@
limit = Max(limit, 0); // Ensure that limit is not negative.
int initial_size = Min(limit, 10);
- Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
+ Handle<JSArray> result = Factory::NewJSArray(initial_size * 4);
StackFrameIterator iter;
// If the caller parameter is a function we skip frames until we're
@@ -10066,23 +10523,25 @@
if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
frames_seen++;
JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
- Object* recv = frame->receiver();
- Object* fun = frame->function();
- Address pc = frame->pc();
- Address start = frame->code()->address();
- Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
- FixedArray* elements = FixedArray::cast(result->elements());
- if (cursor + 2 < elements->length()) {
- elements->set(cursor++, recv);
- elements->set(cursor++, fun);
- elements->set(cursor++, offset);
- } else {
- HandleScope scope;
- Handle<Object> recv_handle(recv);
- Handle<Object> fun_handle(fun);
- SetElement(result, cursor++, recv_handle);
- SetElement(result, cursor++, fun_handle);
- SetElement(result, cursor++, Handle<Smi>(offset));
+ List<FrameSummary> frames(3); // Max 2 levels of inlining.
+ frame->Summarize(&frames);
+ for (int i = frames.length() - 1; i >= 0; i--) {
+ Handle<Object> recv = frames[i].receiver();
+ Handle<JSFunction> fun = frames[i].function();
+ Handle<Code> code = frames[i].code();
+ Handle<Smi> offset(Smi::FromInt(frames[i].offset()));
+ FixedArray* elements = FixedArray::cast(result->elements());
+ if (cursor + 3 < elements->length()) {
+ elements->set(cursor++, *recv);
+ elements->set(cursor++, *fun);
+ elements->set(cursor++, *code);
+ elements->set(cursor++, *offset);
+ } else {
+ SetElement(result, cursor++, recv);
+ SetElement(result, cursor++, fun);
+ SetElement(result, cursor++, code);
+ SetElement(result, cursor++, offset);
+ }
}
}
iter.Advance();