blob: e65391d5a528053b0e28f2f9c40a203ff3a58711 [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/report.h"
#include "vm/code_patcher.h"
#include "vm/exceptions.h"
#include "vm/flags.h"
#include "vm/longjump.h"
#include "vm/object.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
namespace dart {
DEFINE_FLAG(int, stacktrace_depth_on_warning, 5,
"Maximal number of stack frames to print after a runtime warning.");
DEFINE_FLAG(bool, silent_warnings, false, "Silence warnings.");
DEFINE_FLAG(bool, warn_on_javascript_compatibility, false,
"Warn on incompatibilities between vm and dart2js.");
DEFINE_FLAG(bool, warning_as_error, false, "Treat warnings as errors.");
DECLARE_FLAG(bool, always_megamorphic_calls);
RawString* Report::PrependSnippet(Kind kind,
const Script& script,
intptr_t token_pos,
const String& message) {
const char* message_header;
switch (kind) {
case kWarning: message_header = "warning"; break;
case kJSWarning: message_header = "javascript compatibility warning"; break;
case kError: message_header = "error"; break;
case kMalformedType: message_header = "malformed type"; break;
case kMalboundedType: message_header = "malbounded type"; break;
case kBailout: message_header = "bailout"; break;
default: message_header = ""; UNREACHABLE();
}
String& result = String::Handle();
if (!script.IsNull()) {
const String& script_url = String::Handle(script.url());
if (token_pos >= 0) {
intptr_t line, column;
script.GetTokenLocation(token_pos, &line, &column);
// Only report the line position if we have the original source. We still
// need to get a valid column so that we can report the ^ mark below the
// snippet.
// Allocate formatted strings in old sapce as they may be created during
// optimizing compilation. Those strings are created rarely and should not
// polute old space.
if (script.HasSource()) {
result = String::NewFormatted(Heap::kOld,
"'%s': %s: line %" Pd " pos %" Pd ": ",
script_url.ToCString(),
message_header,
line,
column);
} else {
result = String::NewFormatted(Heap::kOld,
"'%s': %s: line %" Pd ": ",
script_url.ToCString(),
message_header,
line);
}
// Append the formatted error or warning message.
GrowableHandlePtrArray<const String> strs(Thread::Current()->zone(), 5);
strs.Add(result);
strs.Add(message);
// Append the source line.
const String& script_line = String::Handle(
script.GetLine(line, Heap::kOld));
ASSERT(!script_line.IsNull());
strs.Add(Symbols::NewLine());
strs.Add(script_line);
strs.Add(Symbols::NewLine());
// Append the column marker.
const String& column_line = String::Handle(
String::NewFormatted(Heap::kOld,
"%*s\n", static_cast<int>(column), "^"));
strs.Add(column_line);
// TODO(srdjan): Use Strings::FromConcatAll in old space, once
// implemented.
result = Symbols::FromConcatAll(strs);
} else {
// Token position is unknown.
result = String::NewFormatted(Heap::kOld, "'%s': %s: ",
script_url.ToCString(),
message_header);
result = String::Concat(result, message, Heap::kOld);
}
} else {
// Script is unknown.
// Append the formatted error or warning message.
result = String::NewFormatted(Heap::kOld, "%s: ", message_header);
result = String::Concat(result, message, Heap::kOld);
}
return result.raw();
}
void Report::LongJump(const Error& error) {
Thread::Current()->long_jump_base()->Jump(1, error);
UNREACHABLE();
}
void Report::LongJumpF(const Error& prev_error,
const Script& script, intptr_t token_pos,
const char* format, ...) {
va_list args;
va_start(args, format);
LongJumpV(prev_error, script, token_pos, format, args);
va_end(args);
UNREACHABLE();
}
void Report::LongJumpV(const Error& prev_error,
const Script& script, intptr_t token_pos,
const char* format, va_list args) {
const Error& error = Error::Handle(LanguageError::NewFormattedV(
prev_error, script, token_pos,
kError, Heap::kNew,
format, args));
LongJump(error);
UNREACHABLE();
}
void Report::MessageF(Kind kind, const Script& script, intptr_t token_pos,
const char* format, ...) {
va_list args;
va_start(args, format);
MessageV(kind, script, token_pos, format, args);
va_end(args);
}
void Report::MessageV(Kind kind, const Script& script, intptr_t token_pos,
const char* format, va_list args) {
if (kind < kError) {
// Reporting a warning.
if (FLAG_silent_warnings) {
return;
}
if (!FLAG_warning_as_error) {
const String& msg = String::Handle(String::NewFormattedV(format, args));
const String& snippet_msg = String::Handle(
PrependSnippet(kind, script, token_pos, msg));
OS::Print("%s", snippet_msg.ToCString());
if (kind == kJSWarning) {
TraceJSWarning(script, token_pos, msg);
// Do not print stacktrace if we have not executed Dart code yet.
if (Isolate::Current()->top_exit_frame_info() != 0) {
const Stacktrace& stacktrace =
Stacktrace::Handle(Exceptions::CurrentStacktrace());
intptr_t idx = 0;
OS::Print("%s", stacktrace.ToCStringInternal(
&idx, FLAG_stacktrace_depth_on_warning));
}
}
return;
}
}
// Reporting an error (or a warning as error).
const Error& error = Error::Handle(
LanguageError::NewFormattedV(Error::Handle(), // No previous error.
script, token_pos,
kind, Heap::kNew,
format, args));
if (kind == kJSWarning) {
Exceptions::ThrowJavascriptCompatibilityError(error.ToErrorCString());
UNREACHABLE();
}
LongJump(error);
UNREACHABLE();
}
void Report::JSWarningFromNative(bool is_static_native, const char* msg) {
DartFrameIterator iterator;
iterator.NextFrame(); // Skip native call.
StackFrame* caller_frame = iterator.NextFrame();
ASSERT(caller_frame != NULL);
const Code& caller_code = Code::Handle(caller_frame->LookupDartCode());
ASSERT(!caller_code.IsNull());
const uword caller_pc = caller_frame->pc();
ICData& ic_data = ICData::Handle();
if (is_static_native) {
// Assume an unoptimized static call. Optimization was prevented.
CodePatcher::GetUnoptimizedStaticCallAt(caller_pc, caller_code, &ic_data);
} else {
if (FLAG_always_megamorphic_calls) {
Report::JSWarningFromFrame(caller_frame, msg);
return;
} else {
// Assume an instance call.
CodePatcher::GetInstanceCallAt(caller_pc, caller_code, &ic_data);
}
}
ASSERT(!ic_data.IsNull());
// Report warning only if not already reported at this location.
if (!ic_data.IssuedJSWarning()) {
ic_data.SetIssuedJSWarning();
Report::JSWarningFromFrame(caller_frame, msg);
}
}
void Report::JSWarningFromIC(const ICData& ic_data, const char* msg) {
DartFrameIterator iterator;
StackFrame* caller_frame = iterator.NextFrame();
ASSERT(caller_frame != NULL);
// Report warning only if not already reported at this location.
if (!ic_data.IssuedJSWarning()) {
ic_data.SetIssuedJSWarning();
JSWarningFromFrame(caller_frame, msg);
}
}
void Report::JSWarningFromFrame(StackFrame* caller_frame, const char* msg) {
ASSERT(caller_frame != NULL);
ASSERT(FLAG_warn_on_javascript_compatibility);
if (FLAG_silent_warnings) return;
Zone* zone = Thread::Current()->zone();
const Code& caller_code = Code::Handle(zone,
caller_frame->LookupDartCode());
ASSERT(!caller_code.IsNull());
const uword caller_pc = caller_frame->pc();
const intptr_t token_pos = caller_code.GetTokenIndexOfPC(caller_pc);
const Function& caller = Function::Handle(zone, caller_code.function());
const Script& script = Script::Handle(zone, caller.script());
MessageF(kJSWarning, script, token_pos, "%s", msg);
}
void Report::TraceJSWarning(const Script& script,
intptr_t token_pos,
const String& message) {
const int64_t micros = OS::GetCurrentTimeMicros();
Isolate* isolate = Isolate::Current();
TraceBuffer* trace_buffer = isolate->trace_buffer();
if (trace_buffer == NULL) {
TraceBuffer::Init(isolate);
trace_buffer = isolate->trace_buffer();
}
JSONStream js;
{
JSONObject trace_warning(&js);
trace_warning.AddProperty("type", "JSCompatibilityWarning");
trace_warning.AddProperty("script", script);
trace_warning.AddProperty("tokenPos", token_pos);
trace_warning.AddProperty("message", message);
}
trace_buffer->Trace(micros, js.ToCString(), true); // Already escaped.
}
} // namespace dart