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}
This commit is contained in:
parent
d6a38645ef
commit
9211dee01a
1
BUILD.gn
1
BUILD.gn
@ -878,6 +878,7 @@ v8_source_set("v8_base") {
|
|||||||
"src/builtins/builtins-boolean.cc",
|
"src/builtins/builtins-boolean.cc",
|
||||||
"src/builtins/builtins-dataview.cc",
|
"src/builtins/builtins-dataview.cc",
|
||||||
"src/builtins/builtins-date.cc",
|
"src/builtins/builtins-date.cc",
|
||||||
|
"src/builtins/builtins-error.cc",
|
||||||
"src/builtins/builtins-function.cc",
|
"src/builtins/builtins-function.cc",
|
||||||
"src/builtins/builtins-global.cc",
|
"src/builtins/builtins-global.cc",
|
||||||
"src/builtins/builtins-internal.cc",
|
"src/builtins/builtins-internal.cc",
|
||||||
|
133
src/accessors.cc
133
src/accessors.cc
@ -1116,6 +1116,139 @@ Handle<AccessorInfo> Accessors::BoundFunctionNameInfo(
|
|||||||
attributes);
|
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 internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -24,6 +24,7 @@ class AccessorInfo;
|
|||||||
V(ArrayLength) \
|
V(ArrayLength) \
|
||||||
V(BoundFunctionLength) \
|
V(BoundFunctionLength) \
|
||||||
V(BoundFunctionName) \
|
V(BoundFunctionName) \
|
||||||
|
V(ErrorStack) \
|
||||||
V(FunctionArguments) \
|
V(FunctionArguments) \
|
||||||
V(FunctionCaller) \
|
V(FunctionCaller) \
|
||||||
V(FunctionName) \
|
V(FunctionName) \
|
||||||
@ -46,9 +47,10 @@ class AccessorInfo;
|
|||||||
V(ScriptIsEmbedderDebugScript) \
|
V(ScriptIsEmbedderDebugScript) \
|
||||||
V(StringLength)
|
V(StringLength)
|
||||||
|
|
||||||
#define ACCESSOR_SETTER_LIST(V) \
|
#define ACCESSOR_SETTER_LIST(V) \
|
||||||
V(ReconfigureToDataProperty) \
|
V(ReconfigureToDataProperty) \
|
||||||
V(ArrayLengthSetter) \
|
V(ArrayLengthSetter) \
|
||||||
|
V(ErrorStackSetter) \
|
||||||
V(FunctionPrototypeSetter)
|
V(FunctionPrototypeSetter)
|
||||||
|
|
||||||
// Accessors contains all predefined proxy accessors.
|
// Accessors contains all predefined proxy accessors.
|
||||||
|
@ -966,6 +966,71 @@ static void InstallWithIntrinsicDefaultProto(Isolate* isolate,
|
|||||||
isolate->native_context()->set(context_index, *function);
|
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
|
// This is only called if we are not using snapshots. The equivalent
|
||||||
// work in the snapshot case is done in HookUpGlobalObject.
|
// 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
|
{ // -- E r r o r
|
||||||
Handle<JSFunction> error_fun = InstallFunction(
|
InstallError(isolate, global, factory->Error_string(),
|
||||||
global, "Error", JS_ERROR_TYPE, JSObject::kHeaderSize,
|
Context::ERROR_FUNCTION_INDEX);
|
||||||
isolate->initial_object_prototype(), Builtins::kIllegal);
|
|
||||||
InstallWithIntrinsicDefaultProto(isolate, error_fun,
|
|
||||||
Context::ERROR_FUNCTION_INDEX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // -- E v a l E r r o r
|
{ // -- E v a l E r r o r
|
||||||
Handle<JSFunction> eval_error_fun = InstallFunction(
|
InstallError(isolate, global, factory->EvalError_string(),
|
||||||
global, "EvalError", JS_ERROR_TYPE, JSObject::kHeaderSize,
|
Context::EVAL_ERROR_FUNCTION_INDEX);
|
||||||
isolate->initial_object_prototype(), Builtins::kIllegal);
|
|
||||||
InstallWithIntrinsicDefaultProto(isolate, eval_error_fun,
|
|
||||||
Context::EVAL_ERROR_FUNCTION_INDEX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // -- R a n g e E r r o r
|
{ // -- R a n g e E r r o r
|
||||||
Handle<JSFunction> range_error_fun = InstallFunction(
|
InstallError(isolate, global, factory->RangeError_string(),
|
||||||
global, "RangeError", JS_ERROR_TYPE, JSObject::kHeaderSize,
|
Context::RANGE_ERROR_FUNCTION_INDEX);
|
||||||
isolate->initial_object_prototype(), Builtins::kIllegal);
|
|
||||||
InstallWithIntrinsicDefaultProto(isolate, range_error_fun,
|
|
||||||
Context::RANGE_ERROR_FUNCTION_INDEX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // -- R e f e r e n c e E r r o r
|
{ // -- R e f e r e n c e E r r o r
|
||||||
Handle<JSFunction> reference_error_fun = InstallFunction(
|
InstallError(isolate, global, factory->ReferenceError_string(),
|
||||||
global, "ReferenceError", JS_ERROR_TYPE, JSObject::kHeaderSize,
|
Context::REFERENCE_ERROR_FUNCTION_INDEX);
|
||||||
isolate->initial_object_prototype(), Builtins::kIllegal);
|
|
||||||
InstallWithIntrinsicDefaultProto(isolate, reference_error_fun,
|
|
||||||
Context::REFERENCE_ERROR_FUNCTION_INDEX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // -- S y n t a x E r r o r
|
{ // -- S y n t a x E r r o r
|
||||||
Handle<JSFunction> syntax_error_fun = InstallFunction(
|
InstallError(isolate, global, factory->SyntaxError_string(),
|
||||||
global, "SyntaxError", JS_ERROR_TYPE, JSObject::kHeaderSize,
|
Context::SYNTAX_ERROR_FUNCTION_INDEX);
|
||||||
isolate->initial_object_prototype(), Builtins::kIllegal);
|
|
||||||
InstallWithIntrinsicDefaultProto(isolate, syntax_error_fun,
|
|
||||||
Context::SYNTAX_ERROR_FUNCTION_INDEX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // -- T y p e E r r o r
|
{ // -- T y p e E r r o r
|
||||||
Handle<JSFunction> type_error_fun = InstallFunction(
|
InstallError(isolate, global, factory->TypeError_string(),
|
||||||
global, "TypeError", JS_ERROR_TYPE, JSObject::kHeaderSize,
|
Context::TYPE_ERROR_FUNCTION_INDEX);
|
||||||
isolate->initial_object_prototype(), Builtins::kIllegal);
|
|
||||||
InstallWithIntrinsicDefaultProto(isolate, type_error_fun,
|
|
||||||
Context::TYPE_ERROR_FUNCTION_INDEX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // -- U R I E r r o r
|
{ // -- U R I E r r o r
|
||||||
Handle<JSFunction> uri_error_fun = InstallFunction(
|
InstallError(isolate, global, factory->URIError_string(),
|
||||||
global, "URIError", JS_ERROR_TYPE, JSObject::kHeaderSize,
|
Context::URI_ERROR_FUNCTION_INDEX);
|
||||||
isolate->initial_object_prototype(), Builtins::kIllegal);
|
|
||||||
InstallWithIntrinsicDefaultProto(isolate, uri_error_fun,
|
|
||||||
Context::URI_ERROR_FUNCTION_INDEX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the embedder data slot.
|
// Initialize the embedder data slot.
|
||||||
|
210
src/builtins/builtins-error.cc
Normal file
210
src/builtins/builtins-error.cc
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
// 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 {
|
namespace {
|
||||||
|
@ -285,6 +285,11 @@ namespace internal {
|
|||||||
CPP(DatePrototypeToJson) \
|
CPP(DatePrototypeToJson) \
|
||||||
CPP(DateUTC) \
|
CPP(DateUTC) \
|
||||||
\
|
\
|
||||||
|
/* Error */ \
|
||||||
|
CPP(ErrorConstructor) \
|
||||||
|
CPP(ErrorCaptureStackTrace) \
|
||||||
|
CPP(ErrorPrototypeToString) \
|
||||||
|
\
|
||||||
/* Function */ \
|
/* Function */ \
|
||||||
CPP(FunctionConstructor) \
|
CPP(FunctionConstructor) \
|
||||||
ASM(FunctionPrototypeApply) \
|
ASM(FunctionPrototypeApply) \
|
||||||
|
@ -101,6 +101,7 @@ enum BindingFlags {
|
|||||||
V(ASYNC_FUNCTION_AWAIT_INDEX, JSFunction, async_function_await) \
|
V(ASYNC_FUNCTION_AWAIT_INDEX, JSFunction, async_function_await) \
|
||||||
V(DERIVED_GET_TRAP_INDEX, JSFunction, derived_get_trap) \
|
V(DERIVED_GET_TRAP_INDEX, JSFunction, derived_get_trap) \
|
||||||
V(ERROR_FUNCTION_INDEX, JSFunction, error_function) \
|
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(EVAL_ERROR_FUNCTION_INDEX, JSFunction, eval_error_function) \
|
||||||
V(GET_STACK_TRACE_LINE_INDEX, JSFunction, get_stack_trace_line_fun) \
|
V(GET_STACK_TRACE_LINE_INDEX, JSFunction, get_stack_trace_line_fun) \
|
||||||
V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
|
V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
V(enumerable_string, "enumerable") \
|
V(enumerable_string, "enumerable") \
|
||||||
V(Error_string, "Error") \
|
V(Error_string, "Error") \
|
||||||
V(eval_string, "eval") \
|
V(eval_string, "eval") \
|
||||||
|
V(EvalError_string, "EvalError") \
|
||||||
V(false_string, "false") \
|
V(false_string, "false") \
|
||||||
V(float32x4_string, "float32x4") \
|
V(float32x4_string, "float32x4") \
|
||||||
V(Float32x4_string, "Float32x4") \
|
V(Float32x4_string, "Float32x4") \
|
||||||
@ -92,6 +93,7 @@
|
|||||||
V(length_string, "length") \
|
V(length_string, "length") \
|
||||||
V(line_string, "line") \
|
V(line_string, "line") \
|
||||||
V(Map_string, "Map") \
|
V(Map_string, "Map") \
|
||||||
|
V(message_string, "message") \
|
||||||
V(minus_infinity_string, "-Infinity") \
|
V(minus_infinity_string, "-Infinity") \
|
||||||
V(minus_zero_string, "-0") \
|
V(minus_zero_string, "-0") \
|
||||||
V(name_string, "name") \
|
V(name_string, "name") \
|
||||||
@ -114,6 +116,8 @@
|
|||||||
V(prototype_string, "prototype") \
|
V(prototype_string, "prototype") \
|
||||||
V(Proxy_string, "Proxy") \
|
V(Proxy_string, "Proxy") \
|
||||||
V(query_colon_string, "(?:)") \
|
V(query_colon_string, "(?:)") \
|
||||||
|
V(RangeError_string, "RangeError") \
|
||||||
|
V(ReferenceError_string, "ReferenceError") \
|
||||||
V(RegExp_string, "RegExp") \
|
V(RegExp_string, "RegExp") \
|
||||||
V(script_string, "script") \
|
V(script_string, "script") \
|
||||||
V(setPrototypeOf_string, "setPrototypeOf") \
|
V(setPrototypeOf_string, "setPrototypeOf") \
|
||||||
@ -129,12 +133,14 @@
|
|||||||
V(String_string, "String") \
|
V(String_string, "String") \
|
||||||
V(symbol_string, "symbol") \
|
V(symbol_string, "symbol") \
|
||||||
V(Symbol_string, "Symbol") \
|
V(Symbol_string, "Symbol") \
|
||||||
|
V(SyntaxError_string, "SyntaxError") \
|
||||||
V(this_string, "this") \
|
V(this_string, "this") \
|
||||||
V(throw_string, "throw") \
|
V(throw_string, "throw") \
|
||||||
V(timed_out, "timed-out") \
|
V(timed_out, "timed-out") \
|
||||||
V(toJSON_string, "toJSON") \
|
V(toJSON_string, "toJSON") \
|
||||||
V(toString_string, "toString") \
|
V(toString_string, "toString") \
|
||||||
V(true_string, "true") \
|
V(true_string, "true") \
|
||||||
|
V(TypeError_string, "TypeError") \
|
||||||
V(uint16x8_string, "uint16x8") \
|
V(uint16x8_string, "uint16x8") \
|
||||||
V(Uint16x8_string, "Uint16x8") \
|
V(Uint16x8_string, "Uint16x8") \
|
||||||
V(uint32x4_string, "uint32x4") \
|
V(uint32x4_string, "uint32x4") \
|
||||||
@ -143,6 +149,7 @@
|
|||||||
V(Uint8x16_string, "Uint8x16") \
|
V(Uint8x16_string, "Uint8x16") \
|
||||||
V(undefined_string, "undefined") \
|
V(undefined_string, "undefined") \
|
||||||
V(undefined_to_string, "[object Undefined]") \
|
V(undefined_to_string, "[object Undefined]") \
|
||||||
|
V(URIError_string, "URIError") \
|
||||||
V(valueOf_string, "valueOf") \
|
V(valueOf_string, "valueOf") \
|
||||||
V(values_string, "values") \
|
V(values_string, "values") \
|
||||||
V(value_string, "value") \
|
V(value_string, "value") \
|
||||||
@ -168,7 +175,6 @@
|
|||||||
V(error_end_pos_symbol) \
|
V(error_end_pos_symbol) \
|
||||||
V(error_script_symbol) \
|
V(error_script_symbol) \
|
||||||
V(error_start_pos_symbol) \
|
V(error_start_pos_symbol) \
|
||||||
V(formatted_stack_trace_symbol) \
|
|
||||||
V(frozen_symbol) \
|
V(frozen_symbol) \
|
||||||
V(hash_code_symbol) \
|
V(hash_code_symbol) \
|
||||||
V(home_object_symbol) \
|
V(home_object_symbol) \
|
||||||
|
@ -329,12 +329,34 @@ static Handle<FixedArray> MaybeGrow(Isolate* isolate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
class StackTraceHelper {
|
class StackTraceHelper {
|
||||||
|
private:
|
||||||
|
enum FrameSkipMode {
|
||||||
|
SKIP_FIRST,
|
||||||
|
SKIP_UNTIL_SEEN,
|
||||||
|
SKIP_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StackTraceHelper(Isolate* isolate, Handle<Object> caller)
|
StackTraceHelper(Isolate* isolate, Handle<Object> caller)
|
||||||
: isolate_(isolate), caller_(caller) {
|
: isolate_(isolate), caller_(caller) {
|
||||||
// If the caller parameter is a function we skip frames until we're
|
// The caller parameter can be used to skip a specific set of frames in the
|
||||||
// under it before starting to collect.
|
// stack trace. It can be:
|
||||||
seen_caller_ = !caller->IsJSFunction();
|
// * 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;
|
encountered_strict_function_ = false;
|
||||||
sloppy_frames_ = 0;
|
sloppy_frames_ = 0;
|
||||||
}
|
}
|
||||||
@ -356,26 +378,34 @@ class StackTraceHelper {
|
|||||||
// Determines whether the given stack frame should be displayed in a stack
|
// Determines whether the given stack frame should be displayed in a stack
|
||||||
// trace.
|
// trace.
|
||||||
bool IsVisibleInStackTrace(JSFunction* fun) {
|
bool IsVisibleInStackTrace(JSFunction* fun) {
|
||||||
return IsAfterCaller(fun) && IsNotInNativeScript(fun) &&
|
return ShouldIncludeFrame(fun) && IsNotInNativeScript(fun) &&
|
||||||
IsInSameSecurityContext(fun);
|
IsInSameSecurityContext(fun);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sloppy_frames() const { return sloppy_frames_; }
|
int sloppy_frames() const { return sloppy_frames_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The caller is the error constructor that asked
|
// This mechanism excludes a number of uninteresting frames from the stack
|
||||||
// for the stack trace to be collected. The first time a construct
|
// trace. This can be be the first frame (which will be a builtin-exit frame
|
||||||
// call to this function is encountered it is skipped. The seen_caller
|
// for the error constructor builtin) or every frame until encountering a
|
||||||
// in/out parameter is used to remember if the caller has been seen
|
// user-specified function.
|
||||||
// yet.
|
bool ShouldIncludeFrame(JSFunction* fun) {
|
||||||
bool IsAfterCaller(JSFunction* fun) {
|
switch (mode_) {
|
||||||
if ((fun == *caller_) && !(seen_caller_)) {
|
case SKIP_NONE:
|
||||||
seen_caller_ = true;
|
return true;
|
||||||
return false;
|
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.
|
UNREACHABLE();
|
||||||
if (!seen_caller_) return false;
|
return false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsNotInNativeScript(JSFunction* fun) {
|
bool IsNotInNativeScript(JSFunction* fun) {
|
||||||
@ -394,9 +424,11 @@ class StackTraceHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Isolate* isolate_;
|
Isolate* isolate_;
|
||||||
Handle<Object> caller_;
|
|
||||||
|
|
||||||
bool seen_caller_;
|
FrameSkipMode mode_;
|
||||||
|
Handle<Object> caller_;
|
||||||
|
bool skip_next_frame_;
|
||||||
|
|
||||||
int sloppy_frames_;
|
int sloppy_frames_;
|
||||||
bool encountered_strict_function_;
|
bool encountered_strict_function_;
|
||||||
};
|
};
|
||||||
@ -531,8 +563,9 @@ MaybeHandle<JSReceiver> Isolate::CaptureAndSetDetailedStackTrace(
|
|||||||
Handle<JSArray> stack_trace = CaptureCurrentStackTrace(
|
Handle<JSArray> stack_trace = CaptureCurrentStackTrace(
|
||||||
stack_trace_for_uncaught_exceptions_frame_limit_,
|
stack_trace_for_uncaught_exceptions_frame_limit_,
|
||||||
stack_trace_for_uncaught_exceptions_options_);
|
stack_trace_for_uncaught_exceptions_options_);
|
||||||
|
// TODO(jgruber): Set back to STRICT once we have eagerly formatted traces.
|
||||||
RETURN_ON_EXCEPTION(
|
RETURN_ON_EXCEPTION(
|
||||||
this, JSReceiver::SetProperty(error_object, key, stack_trace, STRICT),
|
this, JSReceiver::SetProperty(error_object, key, stack_trace, SLOPPY),
|
||||||
JSReceiver);
|
JSReceiver);
|
||||||
}
|
}
|
||||||
return error_object;
|
return error_object;
|
||||||
@ -543,8 +576,9 @@ MaybeHandle<JSReceiver> Isolate::CaptureAndSetSimpleStackTrace(
|
|||||||
// Capture stack trace for simple stack trace string formatting.
|
// Capture stack trace for simple stack trace string formatting.
|
||||||
Handle<Name> key = factory()->stack_trace_symbol();
|
Handle<Name> key = factory()->stack_trace_symbol();
|
||||||
Handle<Object> stack_trace = CaptureSimpleStackTrace(error_object, caller);
|
Handle<Object> stack_trace = CaptureSimpleStackTrace(error_object, caller);
|
||||||
|
// TODO(jgruber): Set back to STRICT once we have eagerly formatted traces.
|
||||||
RETURN_ON_EXCEPTION(
|
RETURN_ON_EXCEPTION(
|
||||||
this, JSReceiver::SetProperty(error_object, key, stack_trace, STRICT),
|
this, JSReceiver::SetProperty(error_object, key, stack_trace, SLOPPY),
|
||||||
JSReceiver);
|
JSReceiver);
|
||||||
return error_object;
|
return error_object;
|
||||||
}
|
}
|
||||||
|
@ -700,7 +700,8 @@ class Isolate {
|
|||||||
MaybeHandle<JSReceiver> CaptureAndSetDetailedStackTrace(
|
MaybeHandle<JSReceiver> CaptureAndSetDetailedStackTrace(
|
||||||
Handle<JSReceiver> error_object);
|
Handle<JSReceiver> error_object);
|
||||||
MaybeHandle<JSReceiver> CaptureAndSetSimpleStackTrace(
|
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);
|
Handle<JSArray> GetDetailedStackTrace(Handle<JSObject> error_object);
|
||||||
|
|
||||||
// Returns if the given context may access the given global object. If
|
// Returns if the given context may access the given global object. If
|
||||||
|
@ -30,9 +30,14 @@ var callSiteWasmObjectSymbol =
|
|||||||
var callSiteWasmFunctionIndexSymbol =
|
var callSiteWasmFunctionIndexSymbol =
|
||||||
utils.ImportNow("call_site_wasm_func_index_symbol");
|
utils.ImportNow("call_site_wasm_func_index_symbol");
|
||||||
var Float32x4ToString;
|
var Float32x4ToString;
|
||||||
var formattedStackTraceSymbol =
|
|
||||||
utils.ImportNow("formatted_stack_trace_symbol");
|
|
||||||
var GlobalObject = global.Object;
|
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 Int16x8ToString;
|
||||||
var Int32x4ToString;
|
var Int32x4ToString;
|
||||||
var Int8x16ToString;
|
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() {
|
function NoSideEffectsObjectToString() {
|
||||||
@ -603,92 +601,6 @@ function GetTypeName(receiver, requireConstructor) {
|
|||||||
return %GetConstructorName(receiver);
|
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() {
|
function ErrorToString() {
|
||||||
if (!IS_RECEIVER(this)) {
|
if (!IS_RECEIVER(this)) {
|
||||||
throw MakeTypeError(kCalledOnNonObject, "Error.prototype.toString");
|
throw MakeTypeError(kCalledOnNonObject, "Error.prototype.toString");
|
||||||
@ -728,21 +640,9 @@ function MakeURIError() {
|
|||||||
// Boilerplate for exceptions for stack overflows. Used from
|
// Boilerplate for exceptions for stack overflows. Used from
|
||||||
// Isolate::StackOverflow().
|
// Isolate::StackOverflow().
|
||||||
var StackOverflowBoilerplate = MakeRangeError(kStackOverflow);
|
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([
|
%InstallToContext([
|
||||||
|
"error_format_stack_trace", FormatStackTrace,
|
||||||
"get_stack_trace_line_fun", GetStackTraceLine,
|
"get_stack_trace_line_fun", GetStackTraceLine,
|
||||||
"make_error_function", MakeGenericError,
|
"make_error_function", MakeGenericError,
|
||||||
"make_range_error", MakeRangeError,
|
"make_range_error", MakeRangeError,
|
||||||
|
@ -365,26 +365,6 @@ RUNTIME_FUNCTION(Runtime_AllocateSeqTwoByteString) {
|
|||||||
return *result;
|
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) {
|
RUNTIME_FUNCTION(Runtime_MessageGetStartPosition) {
|
||||||
SealHandleScope shs(isolate);
|
SealHandleScope shs(isolate);
|
||||||
|
@ -307,7 +307,6 @@ namespace internal {
|
|||||||
F(AllocateInTargetSpace, 2, 1) \
|
F(AllocateInTargetSpace, 2, 1) \
|
||||||
F(AllocateSeqOneByteString, 1, 1) \
|
F(AllocateSeqOneByteString, 1, 1) \
|
||||||
F(AllocateSeqTwoByteString, 1, 1) \
|
F(AllocateSeqTwoByteString, 1, 1) \
|
||||||
F(CollectStackTrace, 2, 1) \
|
|
||||||
F(MessageGetStartPosition, 1, 1) \
|
F(MessageGetStartPosition, 1, 1) \
|
||||||
F(MessageGetScript, 1, 1) \
|
F(MessageGetScript, 1, 1) \
|
||||||
F(FormatMessageString, 4, 1) \
|
F(FormatMessageString, 4, 1) \
|
||||||
|
@ -491,6 +491,7 @@
|
|||||||
'builtins/builtins-boolean.cc',
|
'builtins/builtins-boolean.cc',
|
||||||
'builtins/builtins-dataview.cc',
|
'builtins/builtins-dataview.cc',
|
||||||
'builtins/builtins-date.cc',
|
'builtins/builtins-date.cc',
|
||||||
|
'builtins/builtins-error.cc',
|
||||||
'builtins/builtins-function.cc',
|
'builtins/builtins-function.cc',
|
||||||
'builtins/builtins-global.cc',
|
'builtins/builtins-global.cc',
|
||||||
'builtins/builtins-internal.cc',
|
'builtins/builtins-internal.cc',
|
||||||
|
@ -517,8 +517,7 @@ TEST(ErrorObjectAfterTermination) {
|
|||||||
v8::Context::Scope context_scope(context);
|
v8::Context::Scope context_scope(context);
|
||||||
isolate->TerminateExecution();
|
isolate->TerminateExecution();
|
||||||
v8::Local<v8::Value> error = v8::Exception::Error(v8_str("error"));
|
v8::Local<v8::Value> error = v8::Exception::Error(v8_str("error"));
|
||||||
// TODO(yangguo): crbug/403509. Check for empty handle instead.
|
CHECK(error->IsNativeError());
|
||||||
CHECK(error->IsUndefined());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,18 +33,18 @@ function assertNotIn(thrower, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Realm.eval(realms[1], script);
|
Realm.eval(realms[1], script);
|
||||||
assertSame(3, Realm.shared.error_0.length);
|
assertSame(2, Realm.shared.error_0.length);
|
||||||
assertSame(4, Realm.shared.error_1.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_0);
|
||||||
assertNotIn(Realm.shared.thrower_0, Realm.shared.error_1);
|
assertNotIn(Realm.shared.thrower_0, Realm.shared.error_1);
|
||||||
|
|
||||||
Realm.eval(realms[0], script);
|
Realm.eval(realms[0], script);
|
||||||
assertSame(5, Realm.shared.error_0.length);
|
assertSame(4, Realm.shared.error_0.length);
|
||||||
assertSame(4, Realm.shared.error_1.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_0);
|
||||||
assertNotIn(Realm.shared.thrower_1, Realm.shared.error_1);
|
assertNotIn(Realm.shared.thrower_1, Realm.shared.error_1);
|
||||||
|
|
||||||
|
@ -155,6 +155,3 @@ function CheckNoScopeVisible(f) {
|
|||||||
CheckNoScopeVisible(Number);
|
CheckNoScopeVisible(Number);
|
||||||
|
|
||||||
CheckNoScopeVisible(Function.toString);
|
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
|
// 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 ];
|
var invalid_this = [ "invalid", 23, undefined, null ];
|
||||||
for (var i = 0; i < invalid_this.length; i++) {
|
for (var i = 0; i < invalid_this.length; i++) {
|
||||||
var exception = false;
|
var exception = false;
|
||||||
@ -34,7 +36,7 @@ for (var i = 0; i < invalid_this.length; i++) {
|
|||||||
Error.prototype.toString.call(invalid_this[i]);
|
Error.prototype.toString.call(invalid_this[i]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
exception = true;
|
exception = true;
|
||||||
assertEquals("Error.prototype.toString called on non-object", e.message);
|
assertEquals(msg + invalid_this[i], e.message);
|
||||||
}
|
}
|
||||||
assertTrue(exception);
|
assertTrue(exception);
|
||||||
}
|
}
|
||||||
|
@ -7,5 +7,5 @@ try {
|
|||||||
var p = new Proxy({}, o);
|
var p = new Proxy({}, o);
|
||||||
Error.captureStackTrace(p);
|
Error.captureStackTrace(p);
|
||||||
} catch(e) {
|
} 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) {
|
function testErrorPrototype(prototype) {
|
||||||
var object = {};
|
var object = {};
|
||||||
object.__proto__ = prototype;
|
object.__proto__ = prototype;
|
||||||
object.stack = "123"; // Overwriting stack property fails.
|
object.stack = "123"; // Overwriting stack property succeeds.
|
||||||
assertEquals(prototype.stack, object.stack);
|
assertTrue(prototype.stack != object.stack);
|
||||||
assertTrue("123" != prototype.stack);
|
assertEquals("123", object.stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -366,3 +366,17 @@ my_error = new Error();
|
|||||||
var stolen_getter = Object.getOwnPropertyDescriptor(my_error, 'stack').get;
|
var stolen_getter = Object.getOwnPropertyDescriptor(my_error, 'stack').get;
|
||||||
Object.defineProperty(fake_error, 'stack', { get: stolen_getter });
|
Object.defineProperty(fake_error, 'stack', { get: stolen_getter });
|
||||||
assertEquals(undefined, fake_error.stack);
|
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user