|  | // 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(bool, silent_warnings, false, "Silence warnings."); | 
|  | DEFINE_FLAG(bool, warning_as_error, false, "Treat warnings as errors."); | 
|  |  | 
|  | StringPtr Report::PrependSnippet(Kind kind, | 
|  | const Script& script, | 
|  | TokenPosition token_pos, | 
|  | bool report_after_token, | 
|  | const String& message) { | 
|  | const char* message_header; | 
|  | switch (kind) { | 
|  | case kWarning: | 
|  | message_header = "warning"; | 
|  | break; | 
|  | case kError: | 
|  | message_header = "error"; | 
|  | break; | 
|  | case kBailout: | 
|  | message_header = "bailout"; | 
|  | break; | 
|  | default: | 
|  | message_header = ""; | 
|  | UNREACHABLE(); | 
|  | } | 
|  | String& result = String::Handle(); | 
|  | if (!script.IsNull() && script.HasSource()) { | 
|  | const String& script_url = String::Handle(script.url()); | 
|  | intptr_t line, column; | 
|  | if (script.GetTokenLocation(token_pos, &line, &column)) { | 
|  | const intptr_t token_len = script.GetTokenLength(token_pos); | 
|  | if (report_after_token) { | 
|  | column += token_len < 0 ? 1 : token_len; | 
|  | } | 
|  | // Allocate formatted strings in old space as they may be created during | 
|  | // optimizing compilation. Those strings are created rarely and should not | 
|  | // pollute old space. | 
|  | result = String::NewFormatted( | 
|  | Heap::kOld, "'%s': %s: line %" Pd " pos %" Pd ": ", | 
|  | script_url.ToCString(), message_header, line, column); | 
|  | // Append the formatted error or warning message. | 
|  | const Array& strs = Array::Handle(Array::New(6, Heap::kOld)); | 
|  | strs.SetAt(0, result); | 
|  | strs.SetAt(1, message); | 
|  | // Append the source line. | 
|  | const String& script_line = | 
|  | String::Handle(script.GetLine(line, Heap::kOld)); | 
|  | ASSERT(!script_line.IsNull()); | 
|  | strs.SetAt(2, Symbols::NewLine()); | 
|  | strs.SetAt(3, script_line); | 
|  | strs.SetAt(4, Symbols::NewLine()); | 
|  | // Append the column marker. | 
|  | const String& column_line = String::Handle(String::NewFormatted( | 
|  | Heap::kOld, "%*s\n", static_cast<int>(column), "^")); | 
|  | strs.SetAt(5, column_line); | 
|  | result = String::ConcatAll(strs, Heap::kOld); | 
|  | } 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.ptr(); | 
|  | } | 
|  |  | 
|  | 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, | 
|  | TokenPosition 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, | 
|  | TokenPosition token_pos, | 
|  | const char* format, | 
|  | va_list args) { | 
|  | // If an isolate is being killed a [UnwindError] will be propagated up the | 
|  | // stack. In such a case we cannot wrap the unwind error in a new | 
|  | // [LanguageError]. Instead we simply continue propagating the [UnwindError] | 
|  | // upwards. | 
|  | if (prev_error.IsUnwindError()) { | 
|  | LongJump(prev_error); | 
|  | UNREACHABLE(); | 
|  | } | 
|  | const Error& error = Error::Handle(LanguageError::NewFormattedV( | 
|  | prev_error, script, token_pos, Report::AtLocation, kError, Heap::kOld, | 
|  | format, args)); | 
|  | LongJump(error); | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | void Report::MessageF(Kind kind, | 
|  | const Script& script, | 
|  | TokenPosition token_pos, | 
|  | bool report_after_token, | 
|  | const char* format, | 
|  | ...) { | 
|  | va_list args; | 
|  | va_start(args, format); | 
|  | MessageV(kind, script, token_pos, report_after_token, format, args); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | void Report::MessageV(Kind kind, | 
|  | const Script& script, | 
|  | TokenPosition token_pos, | 
|  | bool report_after_token, | 
|  | 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, report_after_token, msg)); | 
|  | OS::PrintErr("%s", snippet_msg.ToCString()); | 
|  | return; | 
|  | } | 
|  | } | 
|  | // Reporting an error (or a warning as error). | 
|  | const Error& error = Error::Handle(LanguageError::NewFormattedV( | 
|  | Error::Handle(),  // No previous error. | 
|  | script, token_pos, report_after_token, kind, Heap::kOld, format, args)); | 
|  | LongJump(error); | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | }  // namespace dart |