Commit 9211dee0 authored by jgruber's avatar jgruber Committed by Commit bot

Move Error methods to C++

This ports a large portion of Error methods to C++,
including the constructor, stack setter and getter,
and Error.prototype.toString.

BUG=

Committed: https://crrev.com/5742da056a290caa13a0b8717ddb1e43424e0d31
Review-Url: https://codereview.chromium.org/2142933003
Cr-Original-Commit-Position: refs/heads/master@{#37870}
Cr-Commit-Position: refs/heads/master@{#37908}
parent d6a38645
......@@ -878,6 +878,7 @@ v8_source_set("v8_base") {
"src/builtins/builtins-boolean.cc",
"src/builtins/builtins-dataview.cc",
"src/builtins/builtins-date.cc",
"src/builtins/builtins-error.cc",
"src/builtins/builtins-function.cc",
"src/builtins/builtins-global.cc",
"src/builtins/builtins-internal.cc",
......
......@@ -1116,6 +1116,139 @@ Handle<AccessorInfo> Accessors::BoundFunctionNameInfo(
attributes);
}
//
// Accessors::ErrorStack
//
namespace {
MaybeHandle<JSReceiver> ClearInternalStackTrace(Isolate* isolate,
Handle<JSObject> error) {
RETURN_ON_EXCEPTION(
isolate,
JSReceiver::SetProperty(error, isolate->factory()->stack_trace_symbol(),
isolate->factory()->undefined_value(), STRICT),
JSReceiver);
return error;
}
MaybeHandle<Object> FormatStackTrace(Isolate* isolate, Handle<JSObject> error,
Handle<Object> stack_trace) {
// TODO(jgruber): Port FormatStackTrace from JS.
Handle<JSFunction> fun = isolate->error_format_stack_trace();
int argc = 2;
ScopedVector<Handle<Object>> argv(argc);
argv[0] = error;
argv[1] = stack_trace;
Handle<Object> formatted_stack_trace;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, formatted_stack_trace,
Execution::Call(isolate, fun, error, argc, argv.start()), Object);
return formatted_stack_trace;
}
bool IsAccessor(Handle<Object> receiver, Handle<Name> name,
Handle<JSObject> holder) {
LookupIterator it(receiver, name, holder,
LookupIterator::OWN_SKIP_INTERCEPTOR);
// Skip any access checks we might hit. This accessor should never hit in a
// situation where the caller does not have access.
if (it.state() == LookupIterator::ACCESS_CHECK) {
CHECK(it.HasAccess());
it.Next();
}
return (it.state() == LookupIterator::ACCESSOR);
}
} // namespace
void Accessors::ErrorStackGetter(
v8::Local<v8::Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
HandleScope scope(isolate);
Handle<JSObject> holder =
Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder()));
// Retrieve the structured stack trace.
Handle<Object> stack_trace;
Handle<Symbol> stack_trace_symbol = isolate->factory()->stack_trace_symbol();
MaybeHandle<Object> maybe_stack_trace =
JSObject::GetProperty(holder, stack_trace_symbol);
if (!maybe_stack_trace.ToHandle(&stack_trace) ||
stack_trace->IsUndefined(isolate)) {
Handle<Object> result = isolate->factory()->undefined_value();
info.GetReturnValue().Set(Utils::ToLocal(result));
return;
}
// Format it, clear the internal structured trace and reconfigure as a data
// property.
Handle<Object> formatted_stack_trace;
if (!FormatStackTrace(isolate, holder, stack_trace)
.ToHandle(&formatted_stack_trace)) {
isolate->OptionalRescheduleException(false);
return;
}
MaybeHandle<Object> result = ClearInternalStackTrace(isolate, holder);
if (result.is_null()) {
isolate->OptionalRescheduleException(false);
return;
}
// If stack is still an accessor (this could have changed in the meantime
// since FormatStackTrace can execute arbitrary JS), replace it with a data
// property.
Handle<Object> receiver = Utils::OpenHandle(*info.This());
Handle<Name> name = Utils::OpenHandle(*key);
if (IsAccessor(receiver, name, holder)) {
result = ReplaceAccessorWithDataProperty(isolate, receiver, holder, name,
formatted_stack_trace);
if (result.is_null()) {
isolate->OptionalRescheduleException(false);
return;
}
} else {
// The stack property has been modified in the meantime.
if (!JSObject::GetProperty(holder, name).ToHandle(&formatted_stack_trace)) {
isolate->OptionalRescheduleException(false);
return;
}
}
v8::Local<v8::Value> value = Utils::ToLocal(formatted_stack_trace);
info.GetReturnValue().Set(value);
}
void Accessors::ErrorStackSetter(v8::Local<v8::Name> name,
v8::Local<v8::Value> val,
const v8::PropertyCallbackInfo<void>& info) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
HandleScope scope(isolate);
Handle<JSObject> obj =
Handle<JSObject>::cast(Utils::OpenHandle(*info.This()));
// Clear internal properties to avoid memory leaks.
Handle<Symbol> stack_trace_symbol = isolate->factory()->stack_trace_symbol();
if (JSReceiver::HasOwnProperty(obj, stack_trace_symbol).FromMaybe(false)) {
ClearInternalStackTrace(isolate, obj);
}
Accessors::ReconfigureToDataProperty(name, val, info);
}
Handle<AccessorInfo> Accessors::ErrorStackInfo(Isolate* isolate,
PropertyAttributes attributes) {
Handle<AccessorInfo> info =
MakeAccessor(isolate, isolate->factory()->stack_string(),
&ErrorStackGetter, &ErrorStackSetter, attributes);
return info;
}
} // namespace internal
} // namespace v8
......@@ -24,6 +24,7 @@ class AccessorInfo;
V(ArrayLength) \
V(BoundFunctionLength) \
V(BoundFunctionName) \
V(ErrorStack) \
V(FunctionArguments) \
V(FunctionCaller) \
V(FunctionName) \
......@@ -46,9 +47,10 @@ class AccessorInfo;
V(ScriptIsEmbedderDebugScript) \
V(StringLength)
#define ACCESSOR_SETTER_LIST(V) \
V(ReconfigureToDataProperty) \
V(ArrayLengthSetter) \
#define ACCESSOR_SETTER_LIST(V) \
V(ReconfigureToDataProperty) \
V(ArrayLengthSetter) \
V(ErrorStackSetter) \
V(FunctionPrototypeSetter)
// Accessors contains all predefined proxy accessors.
......
......@@ -966,6 +966,71 @@ static void InstallWithIntrinsicDefaultProto(Isolate* isolate,
isolate->native_context()->set(context_index, *function);
}
static void InstallError(Isolate* isolate, Handle<JSObject> global,
Handle<String> name, int context_index) {
Factory* factory = isolate->factory();
Handle<JSFunction> error_fun =
InstallFunction(global, name, JS_ERROR_TYPE, JSObject::kHeaderSize,
isolate->initial_object_prototype(),
Builtins::kErrorConstructor, DONT_ENUM);
error_fun->shared()->set_instance_class_name(*factory->Error_string());
error_fun->shared()->DontAdaptArguments();
error_fun->shared()->set_construct_stub(
*isolate->builtins()->ErrorConstructor());
error_fun->shared()->set_length(1);
error_fun->shared()->set_native(true);
if (context_index == Context::ERROR_FUNCTION_INDEX) {
Handle<JSFunction> capture_stack_trace_fun =
SimpleInstallFunction(error_fun, "captureStackTrace",
Builtins::kErrorCaptureStackTrace, 2, false);
capture_stack_trace_fun->shared()->set_native(true);
}
InstallWithIntrinsicDefaultProto(isolate, error_fun, context_index);
{
Handle<JSObject> prototype =
factory->NewJSObject(isolate->object_function(), TENURED);
JSObject::AddProperty(prototype, factory->name_string(), name, DONT_ENUM);
JSObject::AddProperty(prototype, factory->message_string(),
factory->empty_string(), DONT_ENUM);
JSObject::AddProperty(prototype, factory->constructor_string(), error_fun,
DONT_ENUM);
Handle<JSFunction> to_string_fun =
SimpleInstallFunction(prototype, factory->toString_string(),
Builtins::kErrorPrototypeToString, 0, true);
to_string_fun->shared()->set_native(true);
if (context_index != Context::ERROR_FUNCTION_INDEX) {
Handle<JSFunction> global_error = isolate->error_function();
CHECK(JSReceiver::SetPrototype(error_fun, global_error, false,
Object::THROW_ON_ERROR)
.FromMaybe(false));
CHECK(JSReceiver::SetPrototype(prototype,
handle(global_error->prototype(), isolate),
false, Object::THROW_ON_ERROR)
.FromMaybe(false));
}
Accessors::FunctionSetPrototype(error_fun, prototype).Assert();
}
Handle<Map> initial_map(error_fun->initial_map());
Map::EnsureDescriptorSlack(initial_map, 1);
PropertyAttributes attribs = DONT_ENUM;
Handle<AccessorInfo> error_stack =
Accessors::ErrorStackInfo(isolate, attribs);
{
AccessorConstantDescriptor d(Handle<Name>(Name::cast(error_stack->name())),
error_stack, attribs);
initial_map->AppendDescriptor(&d);
}
}
// This is only called if we are not using snapshots. The equivalent
// work in the snapshot case is done in HookUpGlobalObject.
......@@ -1509,59 +1574,38 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
}
{ // -- E r r o r
Handle<JSFunction> error_fun = InstallFunction(
global, "Error", JS_ERROR_TYPE, JSObject::kHeaderSize,
isolate->initial_object_prototype(), Builtins::kIllegal);
InstallWithIntrinsicDefaultProto(isolate, error_fun,
Context::ERROR_FUNCTION_INDEX);
InstallError(isolate, global, factory->Error_string(),
Context::ERROR_FUNCTION_INDEX);
}
{ // -- E v a l E r r o r
Handle<JSFunction> eval_error_fun = InstallFunction(
global, "EvalError", JS_ERROR_TYPE, JSObject::kHeaderSize,
isolate->initial_object_prototype(), Builtins::kIllegal);
InstallWithIntrinsicDefaultProto(isolate, eval_error_fun,
Context::EVAL_ERROR_FUNCTION_INDEX);
InstallError(isolate, global, factory->EvalError_string(),
Context::EVAL_ERROR_FUNCTION_INDEX);
}
{ // -- R a n g e E r r o r
Handle<JSFunction> range_error_fun = InstallFunction(
global, "RangeError", JS_ERROR_TYPE, JSObject::kHeaderSize,
isolate->initial_object_prototype(), Builtins::kIllegal);
InstallWithIntrinsicDefaultProto(isolate, range_error_fun,
Context::RANGE_ERROR_FUNCTION_INDEX);
InstallError(isolate, global, factory->RangeError_string(),
Context::RANGE_ERROR_FUNCTION_INDEX);
}
{ // -- R e f e r e n c e E r r o r
Handle<JSFunction> reference_error_fun = InstallFunction(
global, "ReferenceError", JS_ERROR_TYPE, JSObject::kHeaderSize,
isolate->initial_object_prototype(), Builtins::kIllegal);
InstallWithIntrinsicDefaultProto(isolate, reference_error_fun,
Context::REFERENCE_ERROR_FUNCTION_INDEX);
InstallError(isolate, global, factory->ReferenceError_string(),
Context::REFERENCE_ERROR_FUNCTION_INDEX);
}
{ // -- S y n t a x E r r o r
Handle<JSFunction> syntax_error_fun = InstallFunction(
global, "SyntaxError", JS_ERROR_TYPE, JSObject::kHeaderSize,
isolate->initial_object_prototype(), Builtins::kIllegal);
InstallWithIntrinsicDefaultProto(isolate, syntax_error_fun,
Context::SYNTAX_ERROR_FUNCTION_INDEX);
InstallError(isolate, global, factory->SyntaxError_string(),
Context::SYNTAX_ERROR_FUNCTION_INDEX);
}
{ // -- T y p e E r r o r
Handle<JSFunction> type_error_fun = InstallFunction(
global, "TypeError", JS_ERROR_TYPE, JSObject::kHeaderSize,
isolate->initial_object_prototype(), Builtins::kIllegal);
InstallWithIntrinsicDefaultProto(isolate, type_error_fun,
Context::TYPE_ERROR_FUNCTION_INDEX);
InstallError(isolate, global, factory->TypeError_string(),
Context::TYPE_ERROR_FUNCTION_INDEX);
}
{ // -- U R I E r r o r
Handle<JSFunction> uri_error_fun = InstallFunction(
global, "URIError", JS_ERROR_TYPE, JSObject::kHeaderSize,
isolate->initial_object_prototype(), Builtins::kIllegal);
InstallWithIntrinsicDefaultProto(isolate, uri_error_fun,
Context::URI_ERROR_FUNCTION_INDEX);
InstallError(isolate, global, factory->URIError_string(),
Context::URI_ERROR_FUNCTION_INDEX);
}
// Initialize the embedder data slot.
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/builtins/builtins.h"
#include "src/builtins/builtins-utils.h"
#include "src/accessors.h"
#include "src/bootstrapper.h"
#include "src/property-descriptor.h"
#include "src/string-builder.h"
namespace v8 {
namespace internal {
// ES6 section 19.5.1.1 Error ( message )
BUILTIN(ErrorConstructor) {
HandleScope scope(isolate);
// 1. If NewTarget is undefined, let newTarget be the active function object,
// else let newTarget be NewTarget.
Handle<JSFunction> target = args.target<JSFunction>();
Handle<JSReceiver> new_target;
if (args.new_target()->IsJSReceiver()) {
new_target = Handle<JSReceiver>::cast(args.new_target());
} else {
new_target = target;
}
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%ErrorPrototype%",
// « [[ErrorData]] »).
Handle<JSObject> err;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, err,
JSObject::New(target, new_target));
// 3. If message is not undefined, then
// a. Let msg be ? ToString(message).
// b. Let msgDesc be the PropertyDescriptor{[[Value]]: msg, [[Writable]]:
// true, [[Enumerable]]: false, [[Configurable]]: true}.
// c. Perform ! DefinePropertyOrThrow(O, "message", msgDesc).
// 4. Return O.
Handle<Object> msg = args.atOrUndefined(isolate, 1);
if (!msg->IsUndefined(isolate)) {
Handle<String> msg_string;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, msg_string,
Object::ToString(isolate, msg));
RETURN_FAILURE_ON_EXCEPTION(
isolate,
JSObject::SetOwnPropertyIgnoreAttributes(
err, isolate->factory()->message_string(), msg_string, DONT_ENUM));
}
// Capture the stack trace unless we're setting up.
if (!isolate->bootstrapper()->IsActive()) {
// Optionally capture a more detailed stack trace for the message.
RETURN_FAILURE_ON_EXCEPTION(isolate,
isolate->CaptureAndSetDetailedStackTrace(err));
// Capture a simple stack trace for the stack property.
RETURN_FAILURE_ON_EXCEPTION(isolate,
isolate->CaptureAndSetSimpleStackTrace(err));
}
return *err;
}
// static
BUILTIN(ErrorCaptureStackTrace) {
HandleScope scope(isolate);
Handle<Object> object_obj = args.atOrUndefined(isolate, 1);
if (!object_obj->IsJSObject()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kInvalidArgument, object_obj));
}
Handle<JSObject> object = Handle<JSObject>::cast(object_obj);
Handle<Object> caller = args.atOrUndefined(isolate, 2);
// TODO(jgruber): Eagerly format the stack trace and remove accessors.h
// include.
// Handle writes to the global object.
if (object->IsJSGlobalProxy()) {
Map* map = object->map();
if (map->has_hidden_prototype()) {
object = handle(JSGlobalObject::cast(map->prototype()), isolate);
}
}
// Check if the stack property is read-only.
bool is_extensible = true;
if (!JSObject::IsExtensible(object)) {
is_extensible = false;
}
PropertyDescriptor desc;
Maybe<bool> owned = JSReceiver::GetOwnPropertyDescriptor(
isolate, object, isolate->factory()->stack_string(), &desc);
if (owned.FromMaybe(false)) {
if (!desc.configurable() || !desc.writable()) {
is_extensible = false;
}
}
if (!is_extensible) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDefineDisallowed,
isolate->factory()->stack_string(), object));
}
// Add stack accessors to the given object
Handle<Map> map(object->map());
PropertyAttributes attribs = DONT_ENUM;
Handle<AccessorInfo> error_stack =
Accessors::ErrorStackInfo(isolate, attribs);
{
AccessorConstantDescriptor d(Handle<Name>(Name::cast(error_stack->name())),
error_stack, attribs);
Handle<DescriptorArray> old_descriptors(map->instance_descriptors());
int index = old_descriptors->SearchWithCache(isolate, *d.GetKey(), *map);
if (index == DescriptorArray::kNotFound) {
// TODO(jgruber): This ensures we do not crash when CaptureStackTrace is
// called on an object with an existing "stack" property. This will be
// removed as soon as we move to eager trace formatting.
Handle<Map> new_map =
Map::CopyInsertDescriptor(map, &d, INSERT_TRANSITION);
JSObject::MigrateToMap(object, new_map, 1);
}
}
// Collect the stack trace.
RETURN_FAILURE_ON_EXCEPTION(isolate,
isolate->CaptureAndSetDetailedStackTrace(object));
RETURN_FAILURE_ON_EXCEPTION(
isolate, isolate->CaptureAndSetSimpleStackTrace(object, caller));
return *isolate->factory()->undefined_value();
}
namespace {
MaybeHandle<String> GetStringPropertyOrDefault(Isolate* isolate,
Handle<JSReceiver> recv,
Handle<String> key,
Handle<String> default_str) {
Handle<Object> obj;
ASSIGN_RETURN_ON_EXCEPTION(isolate, obj, JSObject::GetProperty(recv, key),
String);
Handle<String> str;
if (obj->IsUndefined(isolate)) {
str = default_str;
} else {
ASSIGN_RETURN_ON_EXCEPTION(isolate, str, Object::ToString(isolate, obj),
String);
}
return str;
}
} // namespace
// ES6 section 19.5.3.4 Error.prototype.toString ( )
BUILTIN(ErrorPrototypeToString) {
HandleScope scope(isolate);
// 1. Let O be the this value.
// 2. If Type(O) is not Object, throw a TypeError exception.
CHECK_RECEIVER(JSReceiver, receiver, "Error.prototype.toString");
// 3. Let name be ? Get(O, "name").
// 4. If name is undefined, let name be "Error"; otherwise let name be
// ? ToString(name).
Handle<String> name_key = isolate->factory()->name_string();
Handle<String> name_default = isolate->factory()->Error_string();
Handle<String> name;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, name,
GetStringPropertyOrDefault(isolate, receiver, name_key, name_default));
// 5. Let msg be ? Get(O, "message").
// 6. If msg is undefined, let msg be the empty String; otherwise let msg be
// ? ToString(msg).
Handle<String> msg_key = isolate->factory()->message_string();
Handle<String> msg_default = isolate->factory()->empty_string();
Handle<String> msg;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, msg,
GetStringPropertyOrDefault(isolate, receiver, msg_key, msg_default));
// 7. If name is the empty String, return msg.
// 8. If msg is the empty String, return name.
if (name->length() == 0) return *msg;
if (msg->length() == 0) return *name;
// 9. Return the result of concatenating name, the code unit 0x003A (COLON),
// the code unit 0x0020 (SPACE), and msg.
IncrementalStringBuilder builder(isolate);
builder.AppendString(name);
builder.AppendCString(": ");
builder.AppendString(msg);
RETURN_RESULT_OR_FAILURE(isolate, builder.Finish());
}
} // namespace internal
} // namespace v8
......@@ -142,6 +142,7 @@ void Builtins::Generate_GeneratorPrototypeThrow(CodeStubAssembler* assembler) {
}
// -----------------------------------------------------------------------------
//
namespace {
......
......@@ -285,6 +285,11 @@ namespace internal {
CPP(DatePrototypeToJson) \
CPP(DateUTC) \
\
/* Error */ \
CPP(ErrorConstructor) \
CPP(ErrorCaptureStackTrace) \
CPP(ErrorPrototypeToString) \
\
/* Function */ \
CPP(FunctionConstructor) \
ASM(FunctionPrototypeApply) \
......
......@@ -101,6 +101,7 @@ enum BindingFlags {
V(ASYNC_FUNCTION_AWAIT_INDEX, JSFunction, async_function_await) \
V(DERIVED_GET_TRAP_INDEX, JSFunction, derived_get_trap) \
V(ERROR_FUNCTION_INDEX, JSFunction, error_function) \
V(ERROR_FORMAT_STACK_TRACE_INDEX, JSFunction, error_format_stack_trace) \
V(EVAL_ERROR_FUNCTION_INDEX, JSFunction, eval_error_function) \
V(GET_STACK_TRACE_LINE_INDEX, JSFunction, get_stack_trace_line_fun) \
V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
......
......@@ -59,6 +59,7 @@
V(enumerable_string, "enumerable") \
V(Error_string, "Error") \
V(eval_string, "eval") \
V(EvalError_string, "EvalError") \
V(false_string, "false") \
V(float32x4_string, "float32x4") \
V(Float32x4_string, "Float32x4") \
......@@ -92,6 +93,7 @@
V(length_string, "length") \
V(line_string, "line") \
V(Map_string, "Map") \
V(message_string, "message") \
V(minus_infinity_string, "-Infinity") \
V(minus_zero_string, "-0") \
V(name_string, "name") \
......@@ -114,6 +116,8 @@
V(prototype_string, "prototype") \
V(Proxy_string, "Proxy") \
V(query_colon_string, "(?:)") \
V(RangeError_string, "RangeError") \
V(ReferenceError_string, "ReferenceError") \
V(RegExp_string, "RegExp") \
V(script_string, "script") \
V(setPrototypeOf_string, "setPrototypeOf") \
......@@ -129,12 +133,14 @@
V(String_string, "String") \
V(symbol_string, "symbol") \
V(Symbol_string, "Symbol") \
V(SyntaxError_string, "SyntaxError") \
V(this_string, "this") \
V(throw_string, "throw") \
V(timed_out, "timed-out") \
V(toJSON_string, "toJSON") \
V(toString_string, "toString") \
V(true_string, "true") \
V(TypeError_string, "TypeError") \
V(uint16x8_string, "uint16x8") \
V(Uint16x8_string, "Uint16x8") \
V(uint32x4_string, "uint32x4") \
......@@ -143,6 +149,7 @@
V(Uint8x16_string, "Uint8x16") \
V(undefined_string, "undefined") \
V(undefined_to_string, "[object Undefined]") \
V(URIError_string, "URIError") \
V(valueOf_string, "valueOf") \
V(values_string, "values") \
V(value_string, "value") \
......@@ -168,7 +175,6 @@
V(error_end_pos_symbol) \
V(error_script_symbol) \
V(error_start_pos_symbol) \
V(formatted_stack_trace_symbol) \
V(frozen_symbol) \
V(hash_code_symbol) \
V(home_object_symbol) \
......
......@@ -329,12 +329,34 @@ static Handle<FixedArray> MaybeGrow(Isolate* isolate,
}
class StackTraceHelper {
private:
enum FrameSkipMode {
SKIP_FIRST,
SKIP_UNTIL_SEEN,
SKIP_NONE,
};
public:
StackTraceHelper(Isolate* isolate, Handle<Object> caller)
: isolate_(isolate), caller_(caller) {
// If the caller parameter is a function we skip frames until we're
// under it before starting to collect.
seen_caller_ = !caller->IsJSFunction();
// The caller parameter can be used to skip a specific set of frames in the
// stack trace. It can be:
// * null, when called from a standard error constructor. We unconditionally
// skip the top frame, which is always a builtin-exit frame for the error
// constructor builtin.
// * a JSFunction, when called by the user from Error.captureStackTrace().
// We skip each frame until encountering the caller function.
// * For any other value, all frames are included in the trace.
if (caller_.is_null()) {
mode_ = SKIP_FIRST;
skip_next_frame_ = true;
} else if (caller_->IsJSFunction()) {
mode_ = SKIP_UNTIL_SEEN;
skip_next_frame_ = true;
} else {
mode_ = SKIP_NONE;
skip_next_frame_ = false;
}
encountered_strict_function_ = false;
sloppy_frames_ = 0;
}
......@@ -356,26 +378,34 @@ class StackTraceHelper {
// Determines whether the given stack frame should be displayed in a stack
// trace.
bool IsVisibleInStackTrace(JSFunction* fun) {
return IsAfterCaller(fun) && IsNotInNativeScript(fun) &&
return ShouldIncludeFrame(fun) && IsNotInNativeScript(fun) &&
IsInSameSecurityContext(fun);
}
int sloppy_frames() const { return sloppy_frames_; }
private:
// The caller is the error constructor that asked
// for the stack trace to be collected. The first time a construct
// call to this function is encountered it is skipped. The seen_caller
// in/out parameter is used to remember if the caller has been seen
// yet.
bool IsAfterCaller(JSFunction* fun) {
if ((fun == *caller_) && !(seen_caller_)) {
seen_caller_ = true;
return false;
// This mechanism excludes a number of uninteresting frames from the stack
// trace. This can be be the first frame (which will be a builtin-exit frame
// for the error constructor builtin) or every frame until encountering a
// user-specified function.
bool ShouldIncludeFrame(JSFunction* fun) {
switch (mode_) {
case SKIP_NONE:
return true;
case SKIP_FIRST:
if (!skip_next_frame_) return true;
skip_next_frame_ = false;
return false;
case SKIP_UNTIL_SEEN:
if (skip_next_frame_ && (fun == *caller_)) {
skip_next_frame_ = false;
return false;
}
return !skip_next_frame_;
}
// Skip all frames until we've seen the caller.
if (!seen_caller_) return false;
return true;
UNREACHABLE();
return false;
}
bool IsNotInNativeScript(JSFunction* fun) {
......@@ -394,9 +424,11 @@ class StackTraceHelper {
}
Isolate* isolate_;
FrameSkipMode mode_;
Handle<Object> caller_;
bool skip_next_frame_;
bool seen_caller_;
int sloppy_frames_;
bool encountered_strict_function_;
};
......@@ -531,8 +563,9 @@ MaybeHandle<JSReceiver> Isolate::CaptureAndSetDetailedStackTrace(
Handle<JSArray> stack_trace = CaptureCurrentStackTrace(
stack_trace_for_uncaught_exceptions_frame_limit_,
stack_trace_for_uncaught_exceptions_options_);
// TODO(jgruber): Set back to STRICT once we have eagerly formatted traces.
RETURN_ON_EXCEPTION(
this, JSReceiver::SetProperty(error_object, key, stack_trace, STRICT),
this, JSReceiver::SetProperty(error_object, key, stack_trace, SLOPPY),
JSReceiver);
}
return error_object;
......@@ -543,8 +576,9 @@ MaybeHandle<JSReceiver> Isolate::CaptureAndSetSimpleStackTrace(
// Capture stack trace for simple stack trace string formatting.
Handle<Name> key = factory()->stack_trace_symbol();
Handle<Object> stack_trace = CaptureSimpleStackTrace(error_object, caller);
// TODO(jgruber): Set back to STRICT once we have eagerly formatted traces.
RETURN_ON_EXCEPTION(
this, JSReceiver::SetProperty(error_object, key, stack_trace, STRICT),
this, JSReceiver::SetProperty(error_object, key, stack_trace, SLOPPY),
JSReceiver);
return error_object;
}
......
......@@ -700,7 +700,8 @@ class Isolate {
MaybeHandle<JSReceiver> CaptureAndSetDetailedStackTrace(
Handle<JSReceiver> error_object);
MaybeHandle<JSReceiver> CaptureAndSetSimpleStackTrace(
Handle<JSReceiver> error_object, Handle<Object> caller);
Handle<JSReceiver> error_object,
Handle<Object> caller = Handle<Object>());
Handle<JSArray> GetDetailedStackTrace(Handle<JSObject> error_object);
// Returns if the given context may access the given global object. If
......
......@@ -30,9 +30,14 @@ var callSiteWasmObjectSymbol =
var callSiteWasmFunctionIndexSymbol =
utils.ImportNow("call_site_wasm_func_index_symbol");
var Float32x4ToString;
var formattedStackTraceSymbol =
utils.ImportNow("formatted_stack_trace_symbol");
var GlobalObject = global.Object;
var GlobalError = global.Error;
var GlobalEvalError = global.EvalError;
var GlobalRangeError = global.RangeError;
var GlobalReferenceError = global.ReferenceError;
var GlobalSyntaxError = global.SyntaxError;
var GlobalTypeError = global.TypeError;
var GlobalURIError = global.URIError;
var Int16x8ToString;
var Int32x4ToString;
var Int8x16ToString;
......@@ -66,13 +71,6 @@ utils.Import(function(from) {
// -------------------------------------------------------------------
var GlobalError;
var GlobalTypeError;
var GlobalRangeError;
var GlobalURIError;
var GlobalSyntaxError;
var GlobalReferenceError;
var GlobalEvalError;
function NoSideEffectsObjectToString() {
......@@ -603,92 +601,6 @@ function GetTypeName(receiver, requireConstructor) {
return %GetConstructorName(receiver);
}
// Format the stack trace if not yet done, and return it.
// Cache the formatted stack trace on the holder.
var StackTraceGetter = function() {
var formatted_stack_trace = UNDEFINED;
var holder = this;
while (holder) {
var formatted_stack_trace =
GET_PRIVATE(holder, formattedStackTraceSymbol);
if (IS_UNDEFINED(formatted_stack_trace)) {
// No formatted stack trace available.
var stack_trace = GET_PRIVATE(holder, stackTraceSymbol);
if (IS_UNDEFINED(stack_trace)) {
// Neither formatted nor structured stack trace available.
// Look further up the prototype chain.
holder = %object_get_prototype_of(holder);
continue;
}
formatted_stack_trace = FormatStackTrace(holder, stack_trace);
SET_PRIVATE(holder, stackTraceSymbol, UNDEFINED);
SET_PRIVATE(holder, formattedStackTraceSymbol, formatted_stack_trace);
}
return formatted_stack_trace;
}
return UNDEFINED;
};
// If the receiver equals the holder, set the formatted stack trace that the
// getter returns.
var StackTraceSetter = function(v) {
if (IsErrorObject(this)) {
SET_PRIVATE(this, stackTraceSymbol, UNDEFINED);
SET_PRIVATE(this, formattedStackTraceSymbol, v);
}
};
// Use a dummy function since we do not actually want to capture a stack trace
// when constructing the initial Error prototytpes.
var captureStackTrace = function() {};
// Set up special error type constructors.
function SetUpError(error_function) {
%FunctionSetInstanceClassName(error_function, 'Error');
var name = error_function.name;
var prototype = new GlobalObject();
if (name !== 'Error') {
%InternalSetPrototype(error_function, GlobalError);
%InternalSetPrototype(prototype, GlobalError.prototype);
}
%FunctionSetPrototype(error_function, prototype);
%AddNamedProperty(error_function.prototype, 'name', name, DONT_ENUM);
%AddNamedProperty(error_function.prototype, 'message', '', DONT_ENUM);
%AddNamedProperty(
error_function.prototype, 'constructor', error_function, DONT_ENUM);
%SetCode(error_function, function(m) {
if (IS_UNDEFINED(new.target)) return new error_function(m);
try { captureStackTrace(this, error_function); } catch (e) { }
// Define all the expected properties directly on the error
// object. This avoids going through getters and setters defined
// on prototype objects.
if (!IS_UNDEFINED(m)) {
%AddNamedProperty(this, 'message', TO_STRING(m), DONT_ENUM);
}
});
%SetNativeFlag(error_function);
return error_function;
};
GlobalError = SetUpError(global.Error);
GlobalEvalError = SetUpError(global.EvalError);
GlobalRangeError = SetUpError(global.RangeError);
GlobalReferenceError = SetUpError(global.ReferenceError);
GlobalSyntaxError = SetUpError(global.SyntaxError);
GlobalTypeError = SetUpError(global.TypeError);
GlobalURIError = SetUpError(global.URIError);
utils.InstallFunctions(GlobalError.prototype, DONT_ENUM,
['toString', ErrorToString]);
function ErrorToString() {
if (!IS_RECEIVER(this)) {
throw MakeTypeError(kCalledOnNonObject, "Error.prototype.toString");
......@@ -728,21 +640,9 @@ function MakeURIError() {
// Boilerplate for exceptions for stack overflows. Used from
// Isolate::StackOverflow().
var StackOverflowBoilerplate = MakeRangeError(kStackOverflow);
utils.InstallGetterSetter(StackOverflowBoilerplate, 'stack',
StackTraceGetter, StackTraceSetter)
// Define actual captureStackTrace function after everything has been set up.
captureStackTrace = function captureStackTrace(obj, cons_opt) {
// Define accessors first, as this may fail and throw.
%object_define_property(obj, 'stack', { get: StackTraceGetter,
set: StackTraceSetter,
configurable: true });
%CollectStackTrace(obj, cons_opt ? cons_opt : captureStackTrace);
};
GlobalError.captureStackTrace = captureStackTrace;
%InstallToContext([
"error_format_stack_trace", FormatStackTrace,
"get_stack_trace_line_fun", GetStackTraceLine,
"make_error_function", MakeGenericError,
"make_range_error", MakeRangeError,
......
......@@ -365,26 +365,6 @@ RUNTIME_FUNCTION(Runtime_AllocateSeqTwoByteString) {
return *result;
}
// 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.
RUNTIME_FUNCTION(Runtime_CollectStackTrace) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, error_object, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, caller, 1);
if (!isolate->bootstrapper()->IsActive()) {
// Optionally capture a more detailed stack trace for the message.
RETURN_FAILURE_ON_EXCEPTION(
isolate, isolate->CaptureAndSetDetailedStackTrace(error_object));
// Capture a simple stack trace for the stack property.
RETURN_FAILURE_ON_EXCEPTION(
isolate, isolate->CaptureAndSetSimpleStackTrace(error_object, caller));
}
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_MessageGetStartPosition) {
SealHandleScope shs(isolate);
......
......@@ -307,7 +307,6 @@ namespace internal {
F(AllocateInTargetSpace, 2, 1) \
F(AllocateSeqOneByteString, 1, 1) \
F(AllocateSeqTwoByteString, 1, 1) \
F(CollectStackTrace, 2, 1) \
F(MessageGetStartPosition, 1, 1) \
F(MessageGetScript, 1, 1) \
F(FormatMessageString, 4, 1) \
......
......@@ -491,6 +491,7 @@
'builtins/builtins-boolean.cc',
'builtins/builtins-dataview.cc',
'builtins/builtins-date.cc',
'builtins/builtins-error.cc',
'builtins/builtins-function.cc',
'builtins/builtins-global.cc',
'builtins/builtins-internal.cc',
......
......@@ -517,8 +517,7 @@ TEST(ErrorObjectAfterTermination) {
v8::Context::Scope context_scope(context);
isolate->TerminateExecution();
v8::Local<v8::Value> error = v8::Exception::Error(v8_str("error"));
// TODO(yangguo): crbug/403509. Check for empty handle instead.
CHECK(error->IsUndefined());
CHECK(error->IsNativeError());
}
......
......@@ -33,18 +33,18 @@ function assertNotIn(thrower, error) {
}
Realm.eval(realms[1], script);
assertSame(3, Realm.shared.error_0.length);
assertSame(4, Realm.shared.error_1.length);
assertSame(2, Realm.shared.error_0.length);
assertSame(3, Realm.shared.error_1.length);
assertTrue(Realm.shared.thrower_1 === Realm.shared.error_1[2].getFunction());
assertTrue(Realm.shared.thrower_1 === Realm.shared.error_1[1].getFunction());
assertNotIn(Realm.shared.thrower_0, Realm.shared.error_0);
assertNotIn(Realm.shared.thrower_0, Realm.shared.error_1);
Realm.eval(realms[0], script);
assertSame(5, Realm.shared.error_0.length);
assertSame(4, Realm.shared.error_1.length);
assertSame(4, Realm.shared.error_0.length);
assertSame(3, Realm.shared.error_1.length);
assertTrue(Realm.shared.thrower_0 === Realm.shared.error_0[2].getFunction());
assertTrue(Realm.shared.thrower_0 === Realm.shared.error_0[1].getFunction());
assertNotIn(Realm.shared.thrower_1, Realm.shared.error_0);
assertNotIn(Realm.shared.thrower_1, Realm.shared.error_1);
......
......@@ -155,6 +155,3 @@ function CheckNoScopeVisible(f) {
CheckNoScopeVisible(Number);
CheckNoScopeVisible(Function.toString);
// This getter is known to be implemented as closure.
CheckNoScopeVisible(new Error().__lookupGetter__("stack"));
......@@ -27,6 +27,8 @@
// See: http://code.google.com/p/v8/issues/detail?id=1980
var msg = "Method Error.prototype.toString called on incompatible receiver ";
var invalid_this = [ "invalid", 23, undefined, null ];
for (var i = 0; i < invalid_this.length; i++) {
var exception = false;
......@@ -34,7 +36,7 @@ for (var i = 0; i < invalid_this.length; i++) {
Error.prototype.toString.call(invalid_this[i]);
} catch (e) {
exception = true;
assertEquals("Error.prototype.toString called on non-object", e.message);
assertEquals(msg + invalid_this[i], e.message);
}
assertTrue(exception);
}
......@@ -7,5 +7,5 @@ try {
var p = new Proxy({}, o);
Error.captureStackTrace(p);
} catch(e) {
assertEquals("Cannot pass private property name to proxy trap", e.message);
assertEquals("invalid_argument", e.message);
}
......@@ -75,9 +75,9 @@ try {
function testErrorPrototype(prototype) {
var object = {};
object.__proto__ = prototype;
object.stack = "123"; // Overwriting stack property fails.
assertEquals(prototype.stack, object.stack);
assertTrue("123" != prototype.stack);
object.stack = "123"; // Overwriting stack property succeeds.
assertTrue(prototype.stack != object.stack);
assertEquals("123", object.stack);
}
try {
......
......@@ -366,3 +366,17 @@ my_error = new Error();
var stolen_getter = Object.getOwnPropertyDescriptor(my_error, 'stack').get;
Object.defineProperty(fake_error, 'stack', { get: stolen_getter });
assertEquals(undefined, fake_error.stack);
// Check that overwriting the stack property during stack trace formatting
// does not crash.
error = new Error();
error.__defineGetter__("name", function() { error.stack = "abc"; });
assertEquals("abc", error.stack);
error = new Error();
error.__defineGetter__("name", function() { delete error.stack; });
assertEquals(undefined, error.stack);
// Check that repeated trace collection does not crash.
error = new Error();
Error.captureStackTrace(error);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment