c4057d4645
This CL moves all methods related to scripts and eval origin (HasScript, GetScript, IsEval, GetEvalOrigin) from JSStackFrame to StackFrameBase, because it also applies to WasmFrames. This makes the AppendFileLocation method append the same information to WasmStackFrames and AsmJsWasmStackFrames than to JSStackFrames. R=titzer@chromium.org, mstarzinger@chromium.org BUG=v8:4203 Review-Url: https://codereview.chromium.org/2557923005 Cr-Commit-Position: refs/heads/master@{#41642}
1224 lines
42 KiB
C++
1224 lines
42 KiB
C++
// Copyright 2011 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/messages.h"
|
|
|
|
#include <memory>
|
|
|
|
#include "src/api.h"
|
|
#include "src/execution.h"
|
|
#include "src/isolate-inl.h"
|
|
#include "src/keys.h"
|
|
#include "src/string-builder.h"
|
|
#include "src/wasm/wasm-module.h"
|
|
#include "src/wasm/wasm-objects.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
MessageLocation::MessageLocation(Handle<Script> script, int start_pos,
|
|
int end_pos)
|
|
: script_(script), start_pos_(start_pos), end_pos_(end_pos) {}
|
|
MessageLocation::MessageLocation(Handle<Script> script, int start_pos,
|
|
int end_pos, Handle<JSFunction> function)
|
|
: script_(script),
|
|
start_pos_(start_pos),
|
|
end_pos_(end_pos),
|
|
function_(function) {}
|
|
MessageLocation::MessageLocation() : start_pos_(-1), end_pos_(-1) {}
|
|
|
|
// If no message listeners have been registered this one is called
|
|
// by default.
|
|
void MessageHandler::DefaultMessageReport(Isolate* isolate,
|
|
const MessageLocation* loc,
|
|
Handle<Object> message_obj) {
|
|
std::unique_ptr<char[]> str = GetLocalizedMessage(isolate, message_obj);
|
|
if (loc == NULL) {
|
|
PrintF("%s\n", str.get());
|
|
} else {
|
|
HandleScope scope(isolate);
|
|
Handle<Object> data(loc->script()->name(), isolate);
|
|
std::unique_ptr<char[]> data_str;
|
|
if (data->IsString())
|
|
data_str = Handle<String>::cast(data)->ToCString(DISALLOW_NULLS);
|
|
PrintF("%s:%i: %s\n", data_str.get() ? data_str.get() : "<unknown>",
|
|
loc->start_pos(), str.get());
|
|
}
|
|
}
|
|
|
|
|
|
Handle<JSMessageObject> MessageHandler::MakeMessageObject(
|
|
Isolate* isolate, MessageTemplate::Template message,
|
|
MessageLocation* location, Handle<Object> argument,
|
|
Handle<JSArray> stack_frames) {
|
|
Factory* factory = isolate->factory();
|
|
|
|
int start = -1;
|
|
int end = -1;
|
|
Handle<Object> script_handle = factory->undefined_value();
|
|
if (location != NULL) {
|
|
start = location->start_pos();
|
|
end = location->end_pos();
|
|
script_handle = Script::GetWrapper(location->script());
|
|
} else {
|
|
script_handle = Script::GetWrapper(isolate->factory()->empty_script());
|
|
}
|
|
|
|
Handle<Object> stack_frames_handle = stack_frames.is_null()
|
|
? Handle<Object>::cast(factory->undefined_value())
|
|
: Handle<Object>::cast(stack_frames);
|
|
|
|
Handle<JSMessageObject> message_obj = factory->NewJSMessageObject(
|
|
message, argument, start, end, script_handle, stack_frames_handle);
|
|
|
|
return message_obj;
|
|
}
|
|
|
|
|
|
void MessageHandler::ReportMessage(Isolate* isolate, MessageLocation* loc,
|
|
Handle<JSMessageObject> message) {
|
|
// We are calling into embedder's code which can throw exceptions.
|
|
// Thus we need to save current exception state, reset it to the clean one
|
|
// and ignore scheduled exceptions callbacks can throw.
|
|
|
|
// We pass the exception object into the message handler callback though.
|
|
Object* exception_object = isolate->heap()->undefined_value();
|
|
if (isolate->has_pending_exception()) {
|
|
exception_object = isolate->pending_exception();
|
|
}
|
|
Handle<Object> exception(exception_object, isolate);
|
|
|
|
Isolate::ExceptionScope exception_scope(isolate);
|
|
isolate->clear_pending_exception();
|
|
isolate->set_external_caught_exception(false);
|
|
|
|
// Turn the exception on the message into a string if it is an object.
|
|
if (message->argument()->IsJSObject()) {
|
|
HandleScope scope(isolate);
|
|
Handle<Object> argument(message->argument(), isolate);
|
|
|
|
MaybeHandle<Object> maybe_stringified;
|
|
Handle<Object> stringified;
|
|
// Make sure we don't leak uncaught internally generated Error objects.
|
|
if (argument->IsJSError()) {
|
|
maybe_stringified = Object::NoSideEffectsToString(isolate, argument);
|
|
} else {
|
|
v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
|
|
catcher.SetVerbose(false);
|
|
catcher.SetCaptureMessage(false);
|
|
|
|
maybe_stringified = Object::ToString(isolate, argument);
|
|
}
|
|
|
|
if (!maybe_stringified.ToHandle(&stringified)) {
|
|
stringified = isolate->factory()->NewStringFromAsciiChecked("exception");
|
|
}
|
|
message->set_argument(*stringified);
|
|
}
|
|
|
|
v8::Local<v8::Message> api_message_obj = v8::Utils::MessageToLocal(message);
|
|
v8::Local<v8::Value> api_exception_obj = v8::Utils::ToLocal(exception);
|
|
|
|
Handle<TemplateList> global_listeners =
|
|
isolate->factory()->message_listeners();
|
|
int global_length = global_listeners->length();
|
|
if (global_length == 0) {
|
|
DefaultMessageReport(isolate, loc, message);
|
|
if (isolate->has_scheduled_exception()) {
|
|
isolate->clear_scheduled_exception();
|
|
}
|
|
} else {
|
|
for (int i = 0; i < global_length; i++) {
|
|
HandleScope scope(isolate);
|
|
if (global_listeners->get(i)->IsUndefined(isolate)) continue;
|
|
FixedArray* listener = FixedArray::cast(global_listeners->get(i));
|
|
Foreign* callback_obj = Foreign::cast(listener->get(0));
|
|
v8::MessageCallback callback =
|
|
FUNCTION_CAST<v8::MessageCallback>(callback_obj->foreign_address());
|
|
Handle<Object> callback_data(listener->get(1), isolate);
|
|
{
|
|
// Do not allow exceptions to propagate.
|
|
v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
|
|
callback(api_message_obj, callback_data->IsUndefined(isolate)
|
|
? api_exception_obj
|
|
: v8::Utils::ToLocal(callback_data));
|
|
}
|
|
if (isolate->has_scheduled_exception()) {
|
|
isolate->clear_scheduled_exception();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Handle<String> MessageHandler::GetMessage(Isolate* isolate,
|
|
Handle<Object> data) {
|
|
Handle<JSMessageObject> message = Handle<JSMessageObject>::cast(data);
|
|
Handle<Object> arg = Handle<Object>(message->argument(), isolate);
|
|
return MessageTemplate::FormatMessage(isolate, message->type(), arg);
|
|
}
|
|
|
|
std::unique_ptr<char[]> MessageHandler::GetLocalizedMessage(
|
|
Isolate* isolate, Handle<Object> data) {
|
|
HandleScope scope(isolate);
|
|
return GetMessage(isolate, data)->ToCString(DISALLOW_NULLS);
|
|
}
|
|
|
|
namespace {
|
|
|
|
Object* EvalFromFunctionName(Isolate* isolate, Handle<Script> script) {
|
|
if (script->eval_from_shared()->IsUndefined(isolate))
|
|
return isolate->heap()->undefined_value();
|
|
|
|
Handle<SharedFunctionInfo> shared(
|
|
SharedFunctionInfo::cast(script->eval_from_shared()));
|
|
// Find the name of the function calling eval.
|
|
if (shared->name()->BooleanValue()) {
|
|
return shared->name();
|
|
}
|
|
|
|
return shared->inferred_name();
|
|
}
|
|
|
|
Object* EvalFromScript(Isolate* isolate, Handle<Script> script) {
|
|
if (script->eval_from_shared()->IsUndefined(isolate))
|
|
return isolate->heap()->undefined_value();
|
|
|
|
Handle<SharedFunctionInfo> eval_from_shared(
|
|
SharedFunctionInfo::cast(script->eval_from_shared()));
|
|
return eval_from_shared->script()->IsScript()
|
|
? eval_from_shared->script()
|
|
: isolate->heap()->undefined_value();
|
|
}
|
|
|
|
MaybeHandle<String> FormatEvalOrigin(Isolate* isolate, Handle<Script> script) {
|
|
Handle<Object> sourceURL(script->GetNameOrSourceURL(), isolate);
|
|
if (!sourceURL->IsUndefined(isolate)) {
|
|
DCHECK(sourceURL->IsString());
|
|
return Handle<String>::cast(sourceURL);
|
|
}
|
|
|
|
IncrementalStringBuilder builder(isolate);
|
|
builder.AppendCString("eval at ");
|
|
|
|
Handle<Object> eval_from_function_name =
|
|
handle(EvalFromFunctionName(isolate, script), isolate);
|
|
if (eval_from_function_name->BooleanValue()) {
|
|
Handle<String> str;
|
|
ASSIGN_RETURN_ON_EXCEPTION(
|
|
isolate, str, Object::ToString(isolate, eval_from_function_name),
|
|
String);
|
|
builder.AppendString(str);
|
|
} else {
|
|
builder.AppendCString("<anonymous>");
|
|
}
|
|
|
|
Handle<Object> eval_from_script_obj =
|
|
handle(EvalFromScript(isolate, script), isolate);
|
|
if (eval_from_script_obj->IsScript()) {
|
|
Handle<Script> eval_from_script =
|
|
Handle<Script>::cast(eval_from_script_obj);
|
|
builder.AppendCString(" (");
|
|
if (eval_from_script->compilation_type() == Script::COMPILATION_TYPE_EVAL) {
|
|
// Eval script originated from another eval.
|
|
Handle<String> str;
|
|
ASSIGN_RETURN_ON_EXCEPTION(
|
|
isolate, str, FormatEvalOrigin(isolate, eval_from_script), String);
|
|
builder.AppendString(str);
|
|
} else {
|
|
DCHECK(eval_from_script->compilation_type() !=
|
|
Script::COMPILATION_TYPE_EVAL);
|
|
// eval script originated from "real" source.
|
|
Handle<Object> name_obj = handle(eval_from_script->name(), isolate);
|
|
if (eval_from_script->name()->IsString()) {
|
|
builder.AppendString(Handle<String>::cast(name_obj));
|
|
|
|
Script::PositionInfo info;
|
|
if (Script::GetPositionInfo(eval_from_script, script->GetEvalPosition(),
|
|
&info, Script::NO_OFFSET)) {
|
|
builder.AppendCString(":");
|
|
|
|
Handle<String> str = isolate->factory()->NumberToString(
|
|
handle(Smi::FromInt(info.line + 1), isolate));
|
|
builder.AppendString(str);
|
|
|
|
builder.AppendCString(":");
|
|
|
|
str = isolate->factory()->NumberToString(
|
|
handle(Smi::FromInt(info.column + 1), isolate));
|
|
builder.AppendString(str);
|
|
}
|
|
} else {
|
|
DCHECK(!eval_from_script->name()->IsString());
|
|
builder.AppendCString("unknown source");
|
|
}
|
|
}
|
|
builder.AppendCString(")");
|
|
}
|
|
|
|
Handle<String> result;
|
|
ASSIGN_RETURN_ON_EXCEPTION(isolate, result, builder.Finish(), String);
|
|
return result;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Handle<Object> StackFrameBase::GetEvalOrigin() {
|
|
if (!HasScript()) return isolate_->factory()->undefined_value();
|
|
return FormatEvalOrigin(isolate_, GetScript()).ToHandleChecked();
|
|
}
|
|
|
|
bool StackFrameBase::IsEval() {
|
|
return HasScript() &&
|
|
GetScript()->compilation_type() == Script::COMPILATION_TYPE_EVAL;
|
|
}
|
|
|
|
void JSStackFrame::FromFrameArray(Isolate* isolate, Handle<FrameArray> array,
|
|
int frame_ix) {
|
|
DCHECK(!array->IsWasmFrame(frame_ix));
|
|
isolate_ = isolate;
|
|
receiver_ = handle(array->Receiver(frame_ix), isolate);
|
|
function_ = handle(array->Function(frame_ix), isolate);
|
|
code_ = handle(array->Code(frame_ix), isolate);
|
|
offset_ = array->Offset(frame_ix)->value();
|
|
|
|
const int flags = array->Flags(frame_ix)->value();
|
|
force_constructor_ = (flags & FrameArray::kForceConstructor) != 0;
|
|
is_strict_ = (flags & FrameArray::kIsStrict) != 0;
|
|
}
|
|
|
|
JSStackFrame::JSStackFrame() {}
|
|
|
|
JSStackFrame::JSStackFrame(Isolate* isolate, Handle<Object> receiver,
|
|
Handle<JSFunction> function,
|
|
Handle<AbstractCode> code, int offset)
|
|
: StackFrameBase(isolate),
|
|
receiver_(receiver),
|
|
function_(function),
|
|
code_(code),
|
|
offset_(offset),
|
|
force_constructor_(false),
|
|
is_strict_(false) {}
|
|
|
|
Handle<Object> JSStackFrame::GetFunction() const {
|
|
return Handle<Object>::cast(function_);
|
|
}
|
|
|
|
Handle<Object> JSStackFrame::GetFileName() {
|
|
if (!HasScript()) return isolate_->factory()->null_value();
|
|
return handle(GetScript()->name(), isolate_);
|
|
}
|
|
|
|
Handle<Object> JSStackFrame::GetFunctionName() {
|
|
Handle<String> result = JSFunction::GetName(function_);
|
|
if (result->length() != 0) return result;
|
|
|
|
if (HasScript() &&
|
|
GetScript()->compilation_type() == Script::COMPILATION_TYPE_EVAL) {
|
|
return isolate_->factory()->eval_string();
|
|
}
|
|
return isolate_->factory()->null_value();
|
|
}
|
|
|
|
namespace {
|
|
|
|
bool CheckMethodName(Isolate* isolate, Handle<JSObject> obj, Handle<Name> name,
|
|
Handle<JSFunction> fun,
|
|
LookupIterator::Configuration config) {
|
|
LookupIterator iter =
|
|
LookupIterator::PropertyOrElement(isolate, obj, name, config);
|
|
if (iter.state() == LookupIterator::DATA) {
|
|
return iter.GetDataValue().is_identical_to(fun);
|
|
} else if (iter.state() == LookupIterator::ACCESSOR) {
|
|
Handle<Object> accessors = iter.GetAccessors();
|
|
if (accessors->IsAccessorPair()) {
|
|
Handle<AccessorPair> pair = Handle<AccessorPair>::cast(accessors);
|
|
return pair->getter() == *fun || pair->setter() == *fun;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Handle<Object> ScriptNameOrSourceUrl(Handle<Script> script, Isolate* isolate) {
|
|
Object* name_or_url = script->source_url();
|
|
if (!name_or_url->IsString()) name_or_url = script->name();
|
|
return handle(name_or_url, isolate);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Handle<Object> JSStackFrame::GetScriptNameOrSourceUrl() {
|
|
if (!HasScript()) return isolate_->factory()->null_value();
|
|
return ScriptNameOrSourceUrl(GetScript(), isolate_);
|
|
}
|
|
|
|
Handle<Object> JSStackFrame::GetMethodName() {
|
|
if (receiver_->IsNull(isolate_) || receiver_->IsUndefined(isolate_)) {
|
|
return isolate_->factory()->null_value();
|
|
}
|
|
|
|
Handle<JSReceiver> receiver =
|
|
Object::ToObject(isolate_, receiver_).ToHandleChecked();
|
|
if (!receiver->IsJSObject()) {
|
|
return isolate_->factory()->null_value();
|
|
}
|
|
|
|
Handle<JSObject> obj = Handle<JSObject>::cast(receiver);
|
|
Handle<Object> function_name(function_->shared()->name(), isolate_);
|
|
if (function_name->IsString()) {
|
|
Handle<String> name = Handle<String>::cast(function_name);
|
|
// ES2015 gives getters and setters name prefixes which must
|
|
// be stripped to find the property name.
|
|
if (name->IsUtf8EqualTo(CStrVector("get "), true) ||
|
|
name->IsUtf8EqualTo(CStrVector("set "), true)) {
|
|
name = isolate_->factory()->NewProperSubString(name, 4, name->length());
|
|
}
|
|
if (CheckMethodName(isolate_, obj, name, function_,
|
|
LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR)) {
|
|
return name;
|
|
}
|
|
}
|
|
|
|
HandleScope outer_scope(isolate_);
|
|
Handle<Object> result;
|
|
for (PrototypeIterator iter(isolate_, obj, kStartAtReceiver); !iter.IsAtEnd();
|
|
iter.Advance()) {
|
|
Handle<Object> current = PrototypeIterator::GetCurrent(iter);
|
|
if (!current->IsJSObject()) break;
|
|
Handle<JSObject> current_obj = Handle<JSObject>::cast(current);
|
|
if (current_obj->IsAccessCheckNeeded()) break;
|
|
Handle<FixedArray> keys =
|
|
KeyAccumulator::GetOwnEnumPropertyKeys(isolate_, current_obj);
|
|
for (int i = 0; i < keys->length(); i++) {
|
|
HandleScope inner_scope(isolate_);
|
|
if (!keys->get(i)->IsName()) continue;
|
|
Handle<Name> name_key(Name::cast(keys->get(i)), isolate_);
|
|
if (!CheckMethodName(isolate_, current_obj, name_key, function_,
|
|
LookupIterator::OWN_SKIP_INTERCEPTOR))
|
|
continue;
|
|
// Return null in case of duplicates to avoid confusion.
|
|
if (!result.is_null()) return isolate_->factory()->null_value();
|
|
result = inner_scope.CloseAndEscape(name_key);
|
|
}
|
|
}
|
|
|
|
if (!result.is_null()) return outer_scope.CloseAndEscape(result);
|
|
return isolate_->factory()->null_value();
|
|
}
|
|
|
|
Handle<Object> JSStackFrame::GetTypeName() {
|
|
// TODO(jgruber): Check for strict/constructor here as in
|
|
// CallSitePrototypeGetThis.
|
|
|
|
if (receiver_->IsNull(isolate_) || receiver_->IsUndefined(isolate_))
|
|
return isolate_->factory()->null_value();
|
|
|
|
if (receiver_->IsJSProxy()) return isolate_->factory()->Proxy_string();
|
|
|
|
Handle<JSReceiver> receiver_object =
|
|
Object::ToObject(isolate_, receiver_).ToHandleChecked();
|
|
return JSReceiver::GetConstructorName(receiver_object);
|
|
}
|
|
|
|
int JSStackFrame::GetLineNumber() {
|
|
DCHECK_LE(0, GetPosition());
|
|
if (HasScript()) return Script::GetLineNumber(GetScript(), GetPosition()) + 1;
|
|
return -1;
|
|
}
|
|
|
|
int JSStackFrame::GetColumnNumber() {
|
|
DCHECK_LE(0, GetPosition());
|
|
if (HasScript()) {
|
|
return Script::GetColumnNumber(GetScript(), GetPosition()) + 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool JSStackFrame::IsNative() {
|
|
return HasScript() && GetScript()->type() == Script::TYPE_NATIVE;
|
|
}
|
|
|
|
bool JSStackFrame::IsToplevel() {
|
|
return receiver_->IsJSGlobalProxy() || receiver_->IsNull(isolate_) ||
|
|
receiver_->IsUndefined(isolate_);
|
|
}
|
|
|
|
bool JSStackFrame::IsConstructor() {
|
|
if (force_constructor_) return true;
|
|
if (!receiver_->IsJSObject()) return false;
|
|
Handle<Object> constructor =
|
|
JSReceiver::GetDataProperty(Handle<JSObject>::cast(receiver_),
|
|
isolate_->factory()->constructor_string());
|
|
return constructor.is_identical_to(function_);
|
|
}
|
|
|
|
namespace {
|
|
|
|
bool IsNonEmptyString(Handle<Object> object) {
|
|
return (object->IsString() && String::cast(*object)->length() > 0);
|
|
}
|
|
|
|
void AppendFileLocation(Isolate* isolate, StackFrameBase* call_site,
|
|
IncrementalStringBuilder* builder) {
|
|
if (call_site->IsNative()) {
|
|
builder->AppendCString("native");
|
|
return;
|
|
}
|
|
|
|
Handle<Object> file_name = call_site->GetScriptNameOrSourceUrl();
|
|
if (!file_name->IsString() && call_site->IsEval()) {
|
|
Handle<Object> eval_origin = call_site->GetEvalOrigin();
|
|
DCHECK(eval_origin->IsString());
|
|
builder->AppendString(Handle<String>::cast(eval_origin));
|
|
builder->AppendCString(", "); // Expecting source position to follow.
|
|
}
|
|
|
|
if (IsNonEmptyString(file_name)) {
|
|
builder->AppendString(Handle<String>::cast(file_name));
|
|
} else {
|
|
// Source code does not originate from a file and is not native, but we
|
|
// can still get the source position inside the source string, e.g. in
|
|
// an eval string.
|
|
builder->AppendCString("<anonymous>");
|
|
}
|
|
|
|
int line_number = call_site->GetLineNumber();
|
|
if (line_number != -1) {
|
|
builder->AppendCharacter(':');
|
|
Handle<String> line_string = isolate->factory()->NumberToString(
|
|
handle(Smi::FromInt(line_number), isolate), isolate);
|
|
builder->AppendString(line_string);
|
|
|
|
int column_number = call_site->GetColumnNumber();
|
|
if (column_number != -1) {
|
|
builder->AppendCharacter(':');
|
|
Handle<String> column_string = isolate->factory()->NumberToString(
|
|
handle(Smi::FromInt(column_number), isolate), isolate);
|
|
builder->AppendString(column_string);
|
|
}
|
|
}
|
|
}
|
|
|
|
int StringIndexOf(Isolate* isolate, Handle<String> subject,
|
|
Handle<String> pattern) {
|
|
if (pattern->length() > subject->length()) return -1;
|
|
return String::IndexOf(isolate, subject, pattern, 0);
|
|
}
|
|
|
|
// Returns true iff
|
|
// 1. the subject ends with '.' + pattern, or
|
|
// 2. subject == pattern.
|
|
bool StringEndsWithMethodName(Isolate* isolate, Handle<String> subject,
|
|
Handle<String> pattern) {
|
|
if (String::Equals(subject, pattern)) return true;
|
|
|
|
FlatStringReader subject_reader(isolate, String::Flatten(subject));
|
|
FlatStringReader pattern_reader(isolate, String::Flatten(pattern));
|
|
|
|
int pattern_index = pattern_reader.length() - 1;
|
|
int subject_index = subject_reader.length() - 1;
|
|
for (int i = 0; i <= pattern_reader.length(); i++) { // Iterate over len + 1.
|
|
if (subject_index < 0) {
|
|
return false;
|
|
}
|
|
|
|
const uc32 subject_char = subject_reader.Get(subject_index);
|
|
if (i == pattern_reader.length()) {
|
|
if (subject_char != '.') return false;
|
|
} else if (subject_char != pattern_reader.Get(pattern_index)) {
|
|
return false;
|
|
}
|
|
|
|
pattern_index--;
|
|
subject_index--;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void AppendMethodCall(Isolate* isolate, JSStackFrame* call_site,
|
|
IncrementalStringBuilder* builder) {
|
|
Handle<Object> type_name = call_site->GetTypeName();
|
|
Handle<Object> method_name = call_site->GetMethodName();
|
|
Handle<Object> function_name = call_site->GetFunctionName();
|
|
|
|
if (IsNonEmptyString(function_name)) {
|
|
Handle<String> function_string = Handle<String>::cast(function_name);
|
|
if (IsNonEmptyString(type_name)) {
|
|
Handle<String> type_string = Handle<String>::cast(type_name);
|
|
bool starts_with_type_name =
|
|
(StringIndexOf(isolate, function_string, type_string) == 0);
|
|
if (!starts_with_type_name) {
|
|
builder->AppendString(type_string);
|
|
builder->AppendCharacter('.');
|
|
}
|
|
}
|
|
builder->AppendString(function_string);
|
|
|
|
if (IsNonEmptyString(method_name)) {
|
|
Handle<String> method_string = Handle<String>::cast(method_name);
|
|
if (!StringEndsWithMethodName(isolate, function_string, method_string)) {
|
|
builder->AppendCString(" [as ");
|
|
builder->AppendString(method_string);
|
|
builder->AppendCharacter(']');
|
|
}
|
|
}
|
|
} else {
|
|
builder->AppendString(Handle<String>::cast(type_name));
|
|
builder->AppendCharacter('.');
|
|
if (IsNonEmptyString(method_name)) {
|
|
builder->AppendString(Handle<String>::cast(method_name));
|
|
} else {
|
|
builder->AppendCString("<anonymous>");
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
MaybeHandle<String> JSStackFrame::ToString() {
|
|
IncrementalStringBuilder builder(isolate_);
|
|
|
|
Handle<Object> function_name = GetFunctionName();
|
|
|
|
const bool is_toplevel = IsToplevel();
|
|
const bool is_constructor = IsConstructor();
|
|
const bool is_method_call = !(is_toplevel || is_constructor);
|
|
|
|
if (is_method_call) {
|
|
AppendMethodCall(isolate_, this, &builder);
|
|
} else if (is_constructor) {
|
|
builder.AppendCString("new ");
|
|
if (IsNonEmptyString(function_name)) {
|
|
builder.AppendString(Handle<String>::cast(function_name));
|
|
} else {
|
|
builder.AppendCString("<anonymous>");
|
|
}
|
|
} else if (IsNonEmptyString(function_name)) {
|
|
builder.AppendString(Handle<String>::cast(function_name));
|
|
} else {
|
|
AppendFileLocation(isolate_, this, &builder);
|
|
return builder.Finish();
|
|
}
|
|
|
|
builder.AppendCString(" (");
|
|
AppendFileLocation(isolate_, this, &builder);
|
|
builder.AppendCString(")");
|
|
|
|
return builder.Finish();
|
|
}
|
|
|
|
int JSStackFrame::GetPosition() const { return code_->SourcePosition(offset_); }
|
|
|
|
bool JSStackFrame::HasScript() const {
|
|
return function_->shared()->script()->IsScript();
|
|
}
|
|
|
|
Handle<Script> JSStackFrame::GetScript() const {
|
|
return handle(Script::cast(function_->shared()->script()), isolate_);
|
|
}
|
|
|
|
WasmStackFrame::WasmStackFrame() {}
|
|
|
|
void WasmStackFrame::FromFrameArray(Isolate* isolate, Handle<FrameArray> array,
|
|
int frame_ix) {
|
|
// This function is called for both wasm and asm.js->wasm frames.
|
|
DCHECK(array->IsWasmFrame(frame_ix) || array->IsAsmJsWasmFrame(frame_ix));
|
|
isolate_ = isolate;
|
|
wasm_instance_ = handle(array->WasmInstance(frame_ix), isolate);
|
|
wasm_func_index_ = array->WasmFunctionIndex(frame_ix)->value();
|
|
code_ = handle(array->Code(frame_ix), isolate);
|
|
offset_ = array->Offset(frame_ix)->value();
|
|
}
|
|
|
|
Handle<Object> WasmStackFrame::GetFunction() const {
|
|
Handle<Object> obj(Smi::FromInt(wasm_func_index_), isolate_);
|
|
return obj;
|
|
}
|
|
|
|
Handle<Object> WasmStackFrame::GetFunctionName() {
|
|
Handle<Object> name;
|
|
Handle<WasmCompiledModule> compiled_module(
|
|
Handle<WasmInstanceObject>::cast(wasm_instance_)->get_compiled_module(),
|
|
isolate_);
|
|
if (!WasmCompiledModule::GetFunctionName(compiled_module, wasm_func_index_)
|
|
.ToHandle(&name)) {
|
|
name = isolate_->factory()->null_value();
|
|
}
|
|
return name;
|
|
}
|
|
|
|
MaybeHandle<String> WasmStackFrame::ToString() {
|
|
IncrementalStringBuilder builder(isolate_);
|
|
|
|
Handle<Object> name = GetFunctionName();
|
|
if (name->IsNull(isolate_)) {
|
|
builder.AppendCString("<WASM UNNAMED>");
|
|
} else {
|
|
DCHECK(name->IsString());
|
|
builder.AppendString(Handle<String>::cast(name));
|
|
}
|
|
|
|
builder.AppendCString(" (<WASM>[");
|
|
|
|
Handle<Smi> ix(Smi::FromInt(wasm_func_index_), isolate_);
|
|
builder.AppendString(isolate_->factory()->NumberToString(ix));
|
|
|
|
builder.AppendCString("]+");
|
|
|
|
Handle<Object> pos(Smi::FromInt(GetPosition()), isolate_);
|
|
builder.AppendString(isolate_->factory()->NumberToString(pos));
|
|
builder.AppendCString(")");
|
|
|
|
return builder.Finish();
|
|
}
|
|
|
|
int WasmStackFrame::GetPosition() const {
|
|
// TODO(wasm): Clean this up (bug 5007).
|
|
return (offset_ < 0) ? (-1 - offset_) : code_->SourcePosition(offset_);
|
|
}
|
|
|
|
Handle<Object> WasmStackFrame::Null() const {
|
|
return isolate_->factory()->null_value();
|
|
}
|
|
|
|
bool WasmStackFrame::HasScript() const { return true; }
|
|
|
|
Handle<Script> WasmStackFrame::GetScript() const {
|
|
return WasmInstanceObject::cast(*wasm_instance_)
|
|
->get_compiled_module()
|
|
->script();
|
|
}
|
|
|
|
AsmJsWasmStackFrame::AsmJsWasmStackFrame() {}
|
|
|
|
void AsmJsWasmStackFrame::FromFrameArray(Isolate* isolate,
|
|
Handle<FrameArray> array,
|
|
int frame_ix) {
|
|
DCHECK(array->IsAsmJsWasmFrame(frame_ix));
|
|
WasmStackFrame::FromFrameArray(isolate, array, frame_ix);
|
|
is_at_number_conversion_ =
|
|
array->Flags(frame_ix)->value() & FrameArray::kAsmJsAtNumberConversion;
|
|
}
|
|
|
|
Handle<Object> AsmJsWasmStackFrame::GetReceiver() const {
|
|
return isolate_->global_proxy();
|
|
}
|
|
|
|
Handle<Object> AsmJsWasmStackFrame::GetFunction() const {
|
|
// TODO(clemensh): Return lazily created JSFunction.
|
|
return Null();
|
|
}
|
|
|
|
Handle<Object> AsmJsWasmStackFrame::GetFileName() {
|
|
Handle<Script> script =
|
|
wasm::GetScript(Handle<JSObject>::cast(wasm_instance_));
|
|
DCHECK_EQ(Script::TYPE_NORMAL, script->type());
|
|
return handle(script->name(), isolate_);
|
|
}
|
|
|
|
Handle<Object> AsmJsWasmStackFrame::GetScriptNameOrSourceUrl() {
|
|
Handle<Script> script =
|
|
wasm::GetScript(Handle<JSObject>::cast(wasm_instance_));
|
|
DCHECK_EQ(Script::TYPE_NORMAL, script->type());
|
|
return ScriptNameOrSourceUrl(script, isolate_);
|
|
}
|
|
|
|
int AsmJsWasmStackFrame::GetPosition() const {
|
|
DCHECK_LE(0, offset_);
|
|
int byte_offset = code_->SourcePosition(offset_);
|
|
Handle<WasmCompiledModule> compiled_module(
|
|
WasmInstanceObject::cast(*wasm_instance_)->get_compiled_module(),
|
|
isolate_);
|
|
DCHECK_LE(0, byte_offset);
|
|
return WasmCompiledModule::GetAsmJsSourcePosition(
|
|
compiled_module, wasm_func_index_, static_cast<uint32_t>(byte_offset),
|
|
is_at_number_conversion_);
|
|
}
|
|
|
|
int AsmJsWasmStackFrame::GetLineNumber() {
|
|
DCHECK_LE(0, GetPosition());
|
|
Handle<Script> script =
|
|
wasm::GetScript(Handle<JSObject>::cast(wasm_instance_));
|
|
DCHECK_EQ(Script::TYPE_NORMAL, script->type());
|
|
return Script::GetLineNumber(script, GetPosition()) + 1;
|
|
}
|
|
|
|
int AsmJsWasmStackFrame::GetColumnNumber() {
|
|
DCHECK_LE(0, GetPosition());
|
|
Handle<Script> script =
|
|
wasm::GetScript(Handle<JSObject>::cast(wasm_instance_));
|
|
DCHECK_EQ(Script::TYPE_NORMAL, script->type());
|
|
return Script::GetColumnNumber(script, GetPosition()) + 1;
|
|
}
|
|
|
|
MaybeHandle<String> AsmJsWasmStackFrame::ToString() {
|
|
// The string should look exactly as the respective javascript frame string.
|
|
// Keep this method in line to JSStackFrame::ToString().
|
|
|
|
IncrementalStringBuilder builder(isolate_);
|
|
|
|
Handle<Object> function_name = GetFunctionName();
|
|
|
|
if (IsNonEmptyString(function_name)) {
|
|
builder.AppendString(Handle<String>::cast(function_name));
|
|
builder.AppendCString(" (");
|
|
}
|
|
|
|
AppendFileLocation(isolate_, this, &builder);
|
|
|
|
if (IsNonEmptyString(function_name)) builder.AppendCString(")");
|
|
|
|
return builder.Finish();
|
|
}
|
|
|
|
FrameArrayIterator::FrameArrayIterator(Isolate* isolate,
|
|
Handle<FrameArray> array, int frame_ix)
|
|
: isolate_(isolate), array_(array), next_frame_ix_(frame_ix) {}
|
|
|
|
bool FrameArrayIterator::HasNext() const {
|
|
return (next_frame_ix_ < array_->FrameCount());
|
|
}
|
|
|
|
void FrameArrayIterator::Next() { next_frame_ix_++; }
|
|
|
|
StackFrameBase* FrameArrayIterator::Frame() {
|
|
DCHECK(HasNext());
|
|
const int flags = array_->Flags(next_frame_ix_)->value();
|
|
switch (flags & (FrameArray::kIsWasmFrame | FrameArray::kIsAsmJsWasmFrame)) {
|
|
case 0:
|
|
// JavaScript Frame.
|
|
js_frame_.FromFrameArray(isolate_, array_, next_frame_ix_);
|
|
return &js_frame_;
|
|
case FrameArray::kIsWasmFrame:
|
|
// Wasm Frame;
|
|
wasm_frame_.FromFrameArray(isolate_, array_, next_frame_ix_);
|
|
return &wasm_frame_;
|
|
case FrameArray::kIsAsmJsWasmFrame:
|
|
// Asm.js Wasm Frame:
|
|
asm_wasm_frame_.FromFrameArray(isolate_, array_, next_frame_ix_);
|
|
return &asm_wasm_frame_;
|
|
default:
|
|
UNREACHABLE();
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
MaybeHandle<Object> ConstructCallSite(Isolate* isolate,
|
|
Handle<FrameArray> frame_array,
|
|
int frame_index) {
|
|
Handle<JSFunction> target =
|
|
handle(isolate->native_context()->callsite_function(), isolate);
|
|
|
|
Handle<JSObject> obj;
|
|
ASSIGN_RETURN_ON_EXCEPTION(isolate, obj, JSObject::New(target, target),
|
|
Object);
|
|
|
|
Handle<Symbol> key = isolate->factory()->call_site_frame_array_symbol();
|
|
RETURN_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
|
obj, key, frame_array, DONT_ENUM),
|
|
Object);
|
|
|
|
key = isolate->factory()->call_site_frame_index_symbol();
|
|
Handle<Object> value(Smi::FromInt(frame_index), isolate);
|
|
RETURN_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
|
obj, key, value, DONT_ENUM),
|
|
Object);
|
|
|
|
return obj;
|
|
}
|
|
|
|
// Convert the raw frames as written by Isolate::CaptureSimpleStackTrace into
|
|
// a JSArray of JSCallSite objects.
|
|
MaybeHandle<JSArray> GetStackFrames(Isolate* isolate,
|
|
Handle<FrameArray> elems) {
|
|
const int frame_count = elems->FrameCount();
|
|
|
|
Handle<FixedArray> frames = isolate->factory()->NewFixedArray(frame_count);
|
|
for (int i = 0; i < frame_count; i++) {
|
|
Handle<Object> site;
|
|
ASSIGN_RETURN_ON_EXCEPTION(isolate, site,
|
|
ConstructCallSite(isolate, elems, i), JSArray);
|
|
frames->set(i, *site);
|
|
}
|
|
|
|
return isolate->factory()->NewJSArrayWithElements(frames);
|
|
}
|
|
|
|
MaybeHandle<Object> AppendErrorString(Isolate* isolate, Handle<Object> error,
|
|
IncrementalStringBuilder* builder) {
|
|
MaybeHandle<String> err_str =
|
|
ErrorUtils::ToString(isolate, Handle<Object>::cast(error));
|
|
if (err_str.is_null()) {
|
|
// Error.toString threw. Try to return a string representation of the thrown
|
|
// exception instead.
|
|
|
|
DCHECK(isolate->has_pending_exception());
|
|
Handle<Object> pending_exception =
|
|
handle(isolate->pending_exception(), isolate);
|
|
isolate->clear_pending_exception();
|
|
|
|
err_str = ErrorUtils::ToString(isolate, pending_exception);
|
|
if (err_str.is_null()) {
|
|
// Formatting the thrown exception threw again, give up.
|
|
DCHECK(isolate->has_pending_exception());
|
|
isolate->clear_pending_exception();
|
|
|
|
builder->AppendCString("<error>");
|
|
} else {
|
|
// Formatted thrown exception successfully, append it.
|
|
builder->AppendCString("<error: ");
|
|
builder->AppendString(err_str.ToHandleChecked());
|
|
builder->AppendCharacter('>');
|
|
}
|
|
} else {
|
|
builder->AppendString(err_str.ToHandleChecked());
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
class PrepareStackTraceScope {
|
|
public:
|
|
explicit PrepareStackTraceScope(Isolate* isolate) : isolate_(isolate) {
|
|
DCHECK(!isolate_->formatting_stack_trace());
|
|
isolate_->set_formatting_stack_trace(true);
|
|
}
|
|
|
|
~PrepareStackTraceScope() { isolate_->set_formatting_stack_trace(false); }
|
|
|
|
private:
|
|
Isolate* isolate_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(PrepareStackTraceScope);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// static
|
|
MaybeHandle<Object> ErrorUtils::FormatStackTrace(Isolate* isolate,
|
|
Handle<JSObject> error,
|
|
Handle<Object> raw_stack) {
|
|
DCHECK(raw_stack->IsJSArray());
|
|
Handle<JSArray> raw_stack_array = Handle<JSArray>::cast(raw_stack);
|
|
|
|
DCHECK(raw_stack_array->elements()->IsFixedArray());
|
|
Handle<FrameArray> elems(FrameArray::cast(raw_stack_array->elements()));
|
|
|
|
// If there's a user-specified "prepareStackFrames" function, call it on the
|
|
// frames and use its result.
|
|
|
|
Handle<JSFunction> global_error = isolate->error_function();
|
|
Handle<Object> prepare_stack_trace;
|
|
ASSIGN_RETURN_ON_EXCEPTION(
|
|
isolate, prepare_stack_trace,
|
|
JSFunction::GetProperty(isolate, global_error, "prepareStackTrace"),
|
|
Object);
|
|
|
|
const bool in_recursion = isolate->formatting_stack_trace();
|
|
if (prepare_stack_trace->IsJSFunction() && !in_recursion) {
|
|
PrepareStackTraceScope scope(isolate);
|
|
|
|
Handle<JSArray> sites;
|
|
ASSIGN_RETURN_ON_EXCEPTION(isolate, sites, GetStackFrames(isolate, elems),
|
|
Object);
|
|
|
|
const int argc = 2;
|
|
ScopedVector<Handle<Object>> argv(argc);
|
|
|
|
argv[0] = error;
|
|
argv[1] = sites;
|
|
|
|
Handle<Object> result;
|
|
ASSIGN_RETURN_ON_EXCEPTION(
|
|
isolate, result, Execution::Call(isolate, prepare_stack_trace,
|
|
global_error, argc, argv.start()),
|
|
Object);
|
|
|
|
return result;
|
|
}
|
|
|
|
// Otherwise, run our internal formatting logic.
|
|
|
|
IncrementalStringBuilder builder(isolate);
|
|
|
|
RETURN_ON_EXCEPTION(isolate, AppendErrorString(isolate, error, &builder),
|
|
Object);
|
|
|
|
for (FrameArrayIterator it(isolate, elems); it.HasNext(); it.Next()) {
|
|
builder.AppendCString("\n at ");
|
|
|
|
StackFrameBase* frame = it.Frame();
|
|
MaybeHandle<String> maybe_frame_string = frame->ToString();
|
|
if (maybe_frame_string.is_null()) {
|
|
// CallSite.toString threw. Try to return a string representation of the
|
|
// thrown exception instead.
|
|
|
|
DCHECK(isolate->has_pending_exception());
|
|
Handle<Object> pending_exception =
|
|
handle(isolate->pending_exception(), isolate);
|
|
isolate->clear_pending_exception();
|
|
|
|
maybe_frame_string = ErrorUtils::ToString(isolate, pending_exception);
|
|
if (maybe_frame_string.is_null()) {
|
|
// Formatting the thrown exception threw again, give up.
|
|
|
|
builder.AppendCString("<error>");
|
|
} else {
|
|
// Formatted thrown exception successfully, append it.
|
|
builder.AppendCString("<error: ");
|
|
builder.AppendString(maybe_frame_string.ToHandleChecked());
|
|
builder.AppendCString("<error>");
|
|
}
|
|
} else {
|
|
// CallSite.toString completed without throwing.
|
|
builder.AppendString(maybe_frame_string.ToHandleChecked());
|
|
}
|
|
}
|
|
|
|
return builder.Finish();
|
|
}
|
|
|
|
Handle<String> MessageTemplate::FormatMessage(Isolate* isolate,
|
|
int template_index,
|
|
Handle<Object> arg) {
|
|
Factory* factory = isolate->factory();
|
|
Handle<String> result_string = Object::NoSideEffectsToString(isolate, arg);
|
|
MaybeHandle<String> maybe_result_string = MessageTemplate::FormatMessage(
|
|
template_index, result_string, factory->empty_string(),
|
|
factory->empty_string());
|
|
if (!maybe_result_string.ToHandle(&result_string)) {
|
|
return factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("<error>"));
|
|
}
|
|
// A string that has been obtained from JS code in this way is
|
|
// likely to be a complicated ConsString of some sort. We flatten it
|
|
// here to improve the efficiency of converting it to a C string and
|
|
// other operations that are likely to take place (see GetLocalizedMessage
|
|
// for example).
|
|
return String::Flatten(result_string);
|
|
}
|
|
|
|
|
|
const char* MessageTemplate::TemplateString(int template_index) {
|
|
switch (template_index) {
|
|
#define CASE(NAME, STRING) \
|
|
case k##NAME: \
|
|
return STRING;
|
|
MESSAGE_TEMPLATES(CASE)
|
|
#undef CASE
|
|
case kLastMessage:
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
MaybeHandle<String> MessageTemplate::FormatMessage(int template_index,
|
|
Handle<String> arg0,
|
|
Handle<String> arg1,
|
|
Handle<String> arg2) {
|
|
Isolate* isolate = arg0->GetIsolate();
|
|
const char* template_string = TemplateString(template_index);
|
|
if (template_string == NULL) {
|
|
isolate->ThrowIllegalOperation();
|
|
return MaybeHandle<String>();
|
|
}
|
|
|
|
IncrementalStringBuilder builder(isolate);
|
|
|
|
unsigned int i = 0;
|
|
Handle<String> args[] = {arg0, arg1, arg2};
|
|
for (const char* c = template_string; *c != '\0'; c++) {
|
|
if (*c == '%') {
|
|
// %% results in verbatim %.
|
|
if (*(c + 1) == '%') {
|
|
c++;
|
|
builder.AppendCharacter('%');
|
|
} else {
|
|
DCHECK(i < arraysize(args));
|
|
Handle<String> arg = args[i++];
|
|
builder.AppendString(arg);
|
|
}
|
|
} else {
|
|
builder.AppendCharacter(*c);
|
|
}
|
|
}
|
|
|
|
return builder.Finish();
|
|
}
|
|
|
|
MaybeHandle<Object> ErrorUtils::Construct(
|
|
Isolate* isolate, Handle<JSFunction> target, Handle<Object> new_target,
|
|
Handle<Object> message, FrameSkipMode mode, Handle<Object> caller,
|
|
bool suppress_detailed_trace) {
|
|
// 1. If NewTarget is undefined, let newTarget be the active function object,
|
|
// else let newTarget be NewTarget.
|
|
|
|
Handle<JSReceiver> new_target_recv =
|
|
new_target->IsJSReceiver() ? Handle<JSReceiver>::cast(new_target)
|
|
: Handle<JSReceiver>::cast(target);
|
|
|
|
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%ErrorPrototype%",
|
|
// « [[ErrorData]] »).
|
|
Handle<JSObject> err;
|
|
ASSIGN_RETURN_ON_EXCEPTION(isolate, err,
|
|
JSObject::New(target, new_target_recv), Object);
|
|
|
|
// 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.
|
|
|
|
if (!message->IsUndefined(isolate)) {
|
|
Handle<String> msg_string;
|
|
ASSIGN_RETURN_ON_EXCEPTION(isolate, msg_string,
|
|
Object::ToString(isolate, message), Object);
|
|
RETURN_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
|
err, isolate->factory()->message_string(),
|
|
msg_string, DONT_ENUM),
|
|
Object);
|
|
}
|
|
|
|
// Optionally capture a more detailed stack trace for the message.
|
|
if (!suppress_detailed_trace) {
|
|
RETURN_ON_EXCEPTION(isolate, isolate->CaptureAndSetDetailedStackTrace(err),
|
|
Object);
|
|
}
|
|
|
|
// Capture a simple stack trace for the stack property.
|
|
RETURN_ON_EXCEPTION(isolate,
|
|
isolate->CaptureAndSetSimpleStackTrace(err, mode, caller),
|
|
Object);
|
|
|
|
return err;
|
|
}
|
|
|
|
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 ( )
|
|
MaybeHandle<String> ErrorUtils::ToString(Isolate* isolate,
|
|
Handle<Object> receiver) {
|
|
// 1. Let O be the this value.
|
|
// 2. If Type(O) is not Object, throw a TypeError exception.
|
|
if (!receiver->IsJSReceiver()) {
|
|
return isolate->Throw<String>(isolate->factory()->NewTypeError(
|
|
MessageTemplate::kIncompatibleMethodReceiver,
|
|
isolate->factory()->NewStringFromAsciiChecked(
|
|
"Error.prototype.toString"),
|
|
receiver));
|
|
}
|
|
Handle<JSReceiver> recv = Handle<JSReceiver>::cast(receiver);
|
|
|
|
// 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_ON_EXCEPTION(
|
|
isolate, name,
|
|
GetStringPropertyOrDefault(isolate, recv, name_key, name_default),
|
|
String);
|
|
|
|
// 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_ON_EXCEPTION(
|
|
isolate, msg,
|
|
GetStringPropertyOrDefault(isolate, recv, msg_key, msg_default), String);
|
|
|
|
// 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);
|
|
|
|
Handle<String> result;
|
|
ASSIGN_RETURN_ON_EXCEPTION(isolate, result, builder.Finish(), String);
|
|
return result;
|
|
}
|
|
|
|
namespace {
|
|
|
|
Handle<String> FormatMessage(Isolate* isolate, int template_index,
|
|
Handle<Object> arg0, Handle<Object> arg1,
|
|
Handle<Object> arg2) {
|
|
Handle<String> arg0_str = Object::NoSideEffectsToString(isolate, arg0);
|
|
Handle<String> arg1_str = Object::NoSideEffectsToString(isolate, arg1);
|
|
Handle<String> arg2_str = Object::NoSideEffectsToString(isolate, arg2);
|
|
|
|
isolate->native_context()->IncrementErrorsThrown();
|
|
|
|
Handle<String> msg;
|
|
if (!MessageTemplate::FormatMessage(template_index, arg0_str, arg1_str,
|
|
arg2_str)
|
|
.ToHandle(&msg)) {
|
|
DCHECK(isolate->has_pending_exception());
|
|
isolate->clear_pending_exception();
|
|
return isolate->factory()->NewStringFromAsciiChecked("<error>");
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// static
|
|
MaybeHandle<Object> ErrorUtils::MakeGenericError(
|
|
Isolate* isolate, Handle<JSFunction> constructor, int template_index,
|
|
Handle<Object> arg0, Handle<Object> arg1, Handle<Object> arg2,
|
|
FrameSkipMode mode) {
|
|
if (FLAG_clear_exceptions_on_js_entry) {
|
|
// This function used to be implemented in JavaScript, and JSEntryStub
|
|
// clears
|
|
// any pending exceptions - so whenever we'd call this from C++, pending
|
|
// exceptions would be cleared. Preserve this behavior.
|
|
isolate->clear_pending_exception();
|
|
}
|
|
|
|
DCHECK(mode != SKIP_UNTIL_SEEN);
|
|
|
|
Handle<Object> no_caller;
|
|
Handle<String> msg = FormatMessage(isolate, template_index, arg0, arg1, arg2);
|
|
return ErrorUtils::Construct(isolate, constructor, constructor, msg, mode,
|
|
no_caller, false);
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|