• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/execution/messages.h"
6 
7 #include <memory>
8 
9 #include "src/api/api-inl.h"
10 #include "src/ast/ast.h"
11 #include "src/ast/prettyprinter.h"
12 #include "src/base/v8-fallthrough.h"
13 #include "src/execution/execution.h"
14 #include "src/execution/frames-inl.h"
15 #include "src/execution/frames.h"
16 #include "src/execution/isolate-inl.h"
17 #include "src/logging/runtime-call-stats-scope.h"
18 #include "src/objects/call-site-info-inl.h"
19 #include "src/objects/foreign-inl.h"
20 #include "src/objects/js-array-inl.h"
21 #include "src/objects/struct-inl.h"
22 #include "src/parsing/parse-info.h"
23 #include "src/parsing/parsing.h"
24 #include "src/roots/roots.h"
25 #include "src/strings/string-builder-inl.h"
26 
27 namespace v8 {
28 namespace internal {
29 
MessageLocation(Handle<Script> script,int start_pos,int end_pos)30 MessageLocation::MessageLocation(Handle<Script> script, int start_pos,
31                                  int end_pos)
32     : script_(script),
33       start_pos_(start_pos),
34       end_pos_(end_pos),
35       bytecode_offset_(-1) {}
36 
MessageLocation(Handle<Script> script,int start_pos,int end_pos,Handle<SharedFunctionInfo> shared)37 MessageLocation::MessageLocation(Handle<Script> script, int start_pos,
38                                  int end_pos, Handle<SharedFunctionInfo> shared)
39     : script_(script),
40       start_pos_(start_pos),
41       end_pos_(end_pos),
42       bytecode_offset_(-1),
43       shared_(shared) {}
44 
MessageLocation(Handle<Script> script,Handle<SharedFunctionInfo> shared,int bytecode_offset)45 MessageLocation::MessageLocation(Handle<Script> script,
46                                  Handle<SharedFunctionInfo> shared,
47                                  int bytecode_offset)
48     : script_(script),
49       start_pos_(-1),
50       end_pos_(-1),
51       bytecode_offset_(bytecode_offset),
52       shared_(shared) {}
53 
MessageLocation()54 MessageLocation::MessageLocation()
55     : start_pos_(-1), end_pos_(-1), bytecode_offset_(-1) {}
56 
57 // If no message listeners have been registered this one is called
58 // by default.
DefaultMessageReport(Isolate * isolate,const MessageLocation * loc,Handle<Object> message_obj)59 void MessageHandler::DefaultMessageReport(Isolate* isolate,
60                                           const MessageLocation* loc,
61                                           Handle<Object> message_obj) {
62   std::unique_ptr<char[]> str = GetLocalizedMessage(isolate, message_obj);
63   if (loc == nullptr) {
64     PrintF("%s\n", str.get());
65   } else {
66     HandleScope scope(isolate);
67     Handle<Object> data(loc->script()->name(), isolate);
68     std::unique_ptr<char[]> data_str;
69     if (data->IsString())
70       data_str = Handle<String>::cast(data)->ToCString(DISALLOW_NULLS);
71     PrintF("%s:%i: %s\n", data_str.get() ? data_str.get() : "<unknown>",
72            loc->start_pos(), str.get());
73   }
74 }
75 
MakeMessageObject(Isolate * isolate,MessageTemplate message,const MessageLocation * location,Handle<Object> argument,Handle<FixedArray> stack_frames)76 Handle<JSMessageObject> MessageHandler::MakeMessageObject(
77     Isolate* isolate, MessageTemplate message, const MessageLocation* location,
78     Handle<Object> argument, Handle<FixedArray> stack_frames) {
79   Factory* factory = isolate->factory();
80 
81   int start = -1;
82   int end = -1;
83   int bytecode_offset = -1;
84   Handle<Script> script_handle = isolate->factory()->empty_script();
85   Handle<SharedFunctionInfo> shared_info;
86   if (location != nullptr && !FLAG_correctness_fuzzer_suppressions) {
87     start = location->start_pos();
88     end = location->end_pos();
89     script_handle = location->script();
90     bytecode_offset = location->bytecode_offset();
91     shared_info = location->shared();
92   }
93 
94   Handle<Object> stack_frames_handle =
95       stack_frames.is_null() ? Handle<Object>::cast(factory->undefined_value())
96                              : Handle<Object>::cast(stack_frames);
97 
98   Handle<JSMessageObject> message_obj = factory->NewJSMessageObject(
99       message, argument, start, end, shared_info, bytecode_offset,
100       script_handle, stack_frames_handle);
101 
102   return message_obj;
103 }
104 
ReportMessage(Isolate * isolate,const MessageLocation * loc,Handle<JSMessageObject> message)105 void MessageHandler::ReportMessage(Isolate* isolate, const MessageLocation* loc,
106                                    Handle<JSMessageObject> message) {
107   v8::Local<v8::Message> api_message_obj = v8::Utils::MessageToLocal(message);
108 
109   if (api_message_obj->ErrorLevel() != v8::Isolate::kMessageError) {
110     ReportMessageNoExceptions(isolate, loc, message, v8::Local<v8::Value>());
111     return;
112   }
113 
114   // We are calling into embedder's code which can throw exceptions.
115   // Thus we need to save current exception state, reset it to the clean one
116   // and ignore scheduled exceptions callbacks can throw.
117 
118   // We pass the exception object into the message handler callback though.
119   Object exception_object = ReadOnlyRoots(isolate).undefined_value();
120   if (isolate->has_pending_exception()) {
121     exception_object = isolate->pending_exception();
122   }
123   Handle<Object> exception(exception_object, isolate);
124 
125   Isolate::ExceptionScope exception_scope(isolate);
126   isolate->clear_pending_exception();
127   isolate->set_external_caught_exception(false);
128 
129   // Turn the exception on the message into a string if it is an object.
130   if (message->argument().IsJSObject()) {
131     HandleScope scope(isolate);
132     Handle<Object> argument(message->argument(), isolate);
133 
134     MaybeHandle<Object> maybe_stringified;
135     Handle<Object> stringified;
136     // Make sure we don't leak uncaught internally generated Error objects.
137     if (argument->IsJSError()) {
138       maybe_stringified = Object::NoSideEffectsToString(isolate, argument);
139     } else {
140       v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
141       catcher.SetVerbose(false);
142       catcher.SetCaptureMessage(false);
143 
144       maybe_stringified = Object::ToString(isolate, argument);
145     }
146 
147     if (!maybe_stringified.ToHandle(&stringified)) {
148       DCHECK(isolate->has_pending_exception());
149       isolate->clear_pending_exception();
150       isolate->set_external_caught_exception(false);
151       stringified = isolate->factory()->exception_string();
152     }
153     message->set_argument(*stringified);
154   }
155 
156   v8::Local<v8::Value> api_exception_obj = v8::Utils::ToLocal(exception);
157   ReportMessageNoExceptions(isolate, loc, message, api_exception_obj);
158 }
159 
ReportMessageNoExceptions(Isolate * isolate,const MessageLocation * loc,Handle<Object> message,v8::Local<v8::Value> api_exception_obj)160 void MessageHandler::ReportMessageNoExceptions(
161     Isolate* isolate, const MessageLocation* loc, Handle<Object> message,
162     v8::Local<v8::Value> api_exception_obj) {
163   v8::Local<v8::Message> api_message_obj = v8::Utils::MessageToLocal(message);
164   int error_level = api_message_obj->ErrorLevel();
165 
166   Handle<TemplateList> global_listeners =
167       isolate->factory()->message_listeners();
168   int global_length = global_listeners->length();
169   if (global_length == 0) {
170     DefaultMessageReport(isolate, loc, message);
171     if (isolate->has_scheduled_exception()) {
172       isolate->clear_scheduled_exception();
173     }
174   } else {
175     for (int i = 0; i < global_length; i++) {
176       HandleScope scope(isolate);
177       if (global_listeners->get(i).IsUndefined(isolate)) continue;
178       FixedArray listener = FixedArray::cast(global_listeners->get(i));
179       Foreign callback_obj = Foreign::cast(listener.get(0));
180       int32_t message_levels =
181           static_cast<int32_t>(Smi::ToInt(listener.get(2)));
182       if (!(message_levels & error_level)) {
183         continue;
184       }
185       v8::MessageCallback callback =
186           FUNCTION_CAST<v8::MessageCallback>(callback_obj.foreign_address());
187       Handle<Object> callback_data(listener.get(1), isolate);
188       {
189         RCS_SCOPE(isolate, RuntimeCallCounterId::kMessageListenerCallback);
190         // Do not allow exceptions to propagate.
191         v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
192         callback(api_message_obj, callback_data->IsUndefined(isolate)
193                                       ? api_exception_obj
194                                       : v8::Utils::ToLocal(callback_data));
195       }
196       if (isolate->has_scheduled_exception()) {
197         isolate->clear_scheduled_exception();
198       }
199     }
200   }
201 }
202 
GetMessage(Isolate * isolate,Handle<Object> data)203 Handle<String> MessageHandler::GetMessage(Isolate* isolate,
204                                           Handle<Object> data) {
205   Handle<JSMessageObject> message = Handle<JSMessageObject>::cast(data);
206   Handle<Object> arg = Handle<Object>(message->argument(), isolate);
207   return MessageFormatter::Format(isolate, message->type(), arg);
208 }
209 
GetLocalizedMessage(Isolate * isolate,Handle<Object> data)210 std::unique_ptr<char[]> MessageHandler::GetLocalizedMessage(
211     Isolate* isolate, Handle<Object> data) {
212   HandleScope scope(isolate);
213   return GetMessage(isolate, data)->ToCString(DISALLOW_NULLS);
214 }
215 
216 namespace {
217 
218 // Convert the raw frames as written by Isolate::CaptureSimpleStackTrace into
219 // a JSArray of JSCallSite objects.
GetStackFrames(Isolate * isolate,Handle<FixedArray> frames)220 MaybeHandle<JSArray> GetStackFrames(Isolate* isolate,
221                                     Handle<FixedArray> frames) {
222   int frame_count = frames->length();
223   Handle<JSFunction> constructor = isolate->callsite_function();
224   Handle<FixedArray> sites = isolate->factory()->NewFixedArray(frame_count);
225   for (int i = 0; i < frame_count; ++i) {
226     Handle<CallSiteInfo> frame(CallSiteInfo::cast(frames->get(i)), isolate);
227     Handle<JSObject> site;
228     ASSIGN_RETURN_ON_EXCEPTION(
229         isolate, site,
230         JSObject::New(constructor, constructor, Handle<AllocationSite>::null()),
231         JSArray);
232     RETURN_ON_EXCEPTION(isolate,
233                         JSObject::SetOwnPropertyIgnoreAttributes(
234                             site, isolate->factory()->call_site_info_symbol(),
235                             frame, DONT_ENUM),
236                         JSArray);
237     sites->set(i, *site);
238   }
239 
240   return isolate->factory()->NewJSArrayWithElements(sites);
241 }
242 
AppendErrorString(Isolate * isolate,Handle<Object> error,IncrementalStringBuilder * builder)243 MaybeHandle<Object> AppendErrorString(Isolate* isolate, Handle<Object> error,
244                                       IncrementalStringBuilder* builder) {
245   MaybeHandle<String> err_str =
246       ErrorUtils::ToString(isolate, Handle<Object>::cast(error));
247   if (err_str.is_null()) {
248     // Error.toString threw. Try to return a string representation of the thrown
249     // exception instead.
250 
251     DCHECK(isolate->has_pending_exception());
252     Handle<Object> pending_exception =
253         handle(isolate->pending_exception(), isolate);
254     isolate->clear_pending_exception();
255     isolate->set_external_caught_exception(false);
256 
257     err_str = ErrorUtils::ToString(isolate, pending_exception);
258     if (err_str.is_null()) {
259       // Formatting the thrown exception threw again, give up.
260       DCHECK(isolate->has_pending_exception());
261       isolate->clear_pending_exception();
262       isolate->set_external_caught_exception(false);
263       builder->AppendCStringLiteral("<error>");
264     } else {
265       // Formatted thrown exception successfully, append it.
266       builder->AppendCStringLiteral("<error: ");
267       builder->AppendString(err_str.ToHandleChecked());
268       builder->AppendCharacter('>');
269     }
270   } else {
271     builder->AppendString(err_str.ToHandleChecked());
272   }
273 
274   return error;
275 }
276 
277 class V8_NODISCARD PrepareStackTraceScope {
278  public:
PrepareStackTraceScope(Isolate * isolate)279   explicit PrepareStackTraceScope(Isolate* isolate) : isolate_(isolate) {
280     DCHECK(!isolate_->formatting_stack_trace());
281     isolate_->set_formatting_stack_trace(true);
282   }
283 
~PrepareStackTraceScope()284   ~PrepareStackTraceScope() { isolate_->set_formatting_stack_trace(false); }
285 
286   PrepareStackTraceScope(const PrepareStackTraceScope&) = delete;
287   PrepareStackTraceScope& operator=(const PrepareStackTraceScope&) = delete;
288 
289  private:
290   Isolate* isolate_;
291 };
292 
293 }  // namespace
294 
295 // static
FormatStackTrace(Isolate * isolate,Handle<JSObject> error,Handle<Object> raw_stack)296 MaybeHandle<Object> ErrorUtils::FormatStackTrace(Isolate* isolate,
297                                                  Handle<JSObject> error,
298                                                  Handle<Object> raw_stack) {
299   if (FLAG_correctness_fuzzer_suppressions) {
300     return isolate->factory()->empty_string();
301   }
302   DCHECK(raw_stack->IsFixedArray());
303   Handle<FixedArray> elems = Handle<FixedArray>::cast(raw_stack);
304 
305   const bool in_recursion = isolate->formatting_stack_trace();
306   const bool has_overflowed = i::StackLimitCheck{isolate}.HasOverflowed();
307   Handle<Context> error_context;
308   if (!in_recursion && !has_overflowed &&
309       error->GetCreationContext().ToHandle(&error_context)) {
310     DCHECK(error_context->IsNativeContext());
311 
312     if (isolate->HasPrepareStackTraceCallback()) {
313       PrepareStackTraceScope scope(isolate);
314 
315       Handle<JSArray> sites;
316       ASSIGN_RETURN_ON_EXCEPTION(isolate, sites, GetStackFrames(isolate, elems),
317                                  Object);
318 
319       Handle<Object> result;
320       ASSIGN_RETURN_ON_EXCEPTION(
321           isolate, result,
322           isolate->RunPrepareStackTraceCallback(error_context, error, sites),
323           Object);
324       return result;
325     } else {
326       Handle<JSFunction> global_error =
327           handle(error_context->error_function(), isolate);
328 
329       // If there's a user-specified "prepareStackTrace" function, call it on
330       // the frames and use its result.
331 
332       Handle<Object> prepare_stack_trace;
333       ASSIGN_RETURN_ON_EXCEPTION(
334           isolate, prepare_stack_trace,
335           JSFunction::GetProperty(isolate, global_error, "prepareStackTrace"),
336           Object);
337 
338       if (prepare_stack_trace->IsJSFunction()) {
339         PrepareStackTraceScope scope(isolate);
340 
341         isolate->CountUsage(v8::Isolate::kErrorPrepareStackTrace);
342 
343         Handle<JSArray> sites;
344         ASSIGN_RETURN_ON_EXCEPTION(isolate, sites,
345                                    GetStackFrames(isolate, elems), Object);
346 
347         const int argc = 2;
348         base::ScopedVector<Handle<Object>> argv(argc);
349         argv[0] = error;
350         argv[1] = sites;
351 
352         Handle<Object> result;
353 
354         ASSIGN_RETURN_ON_EXCEPTION(
355             isolate, result,
356             Execution::Call(isolate, prepare_stack_trace, global_error, argc,
357                             argv.begin()),
358             Object);
359 
360         return result;
361       }
362     }
363   }
364 
365   // Otherwise, run our internal formatting logic.
366   IncrementalStringBuilder builder(isolate);
367 
368   RETURN_ON_EXCEPTION(isolate, AppendErrorString(isolate, error, &builder),
369                       Object);
370 
371   for (int i = 0; i < elems->length(); ++i) {
372     builder.AppendCStringLiteral("\n    at ");
373 
374     Handle<CallSiteInfo> frame(CallSiteInfo::cast(elems->get(i)), isolate);
375     SerializeCallSiteInfo(isolate, frame, &builder);
376 
377     if (isolate->has_pending_exception()) {
378       // CallSite.toString threw. Parts of the current frame might have been
379       // stringified already regardless. Still, try to append a string
380       // representation of the thrown exception.
381 
382       Handle<Object> pending_exception =
383           handle(isolate->pending_exception(), isolate);
384       isolate->clear_pending_exception();
385       isolate->set_external_caught_exception(false);
386 
387       MaybeHandle<String> exception_string =
388           ErrorUtils::ToString(isolate, pending_exception);
389       if (exception_string.is_null()) {
390         // Formatting the thrown exception threw again, give up.
391 
392         builder.AppendCStringLiteral("<error>");
393       } else {
394         // Formatted thrown exception successfully, append it.
395         builder.AppendCStringLiteral("<error: ");
396         builder.AppendString(exception_string.ToHandleChecked());
397         builder.AppendCStringLiteral("<error>");
398       }
399     }
400   }
401 
402   return builder.Finish();
403 }
404 
Format(Isolate * isolate,MessageTemplate index,Handle<Object> arg0,Handle<Object> arg1,Handle<Object> arg2)405 Handle<String> MessageFormatter::Format(Isolate* isolate, MessageTemplate index,
406                                         Handle<Object> arg0,
407                                         Handle<Object> arg1,
408                                         Handle<Object> arg2) {
409   Factory* factory = isolate->factory();
410   Handle<String> arg0_string = factory->empty_string();
411   if (!arg0.is_null()) {
412     arg0_string = Object::NoSideEffectsToString(isolate, arg0);
413   }
414   Handle<String> arg1_string = factory->empty_string();
415   if (!arg1.is_null()) {
416     arg1_string = Object::NoSideEffectsToString(isolate, arg1);
417   }
418   Handle<String> arg2_string = factory->empty_string();
419   if (!arg2.is_null()) {
420     arg2_string = Object::NoSideEffectsToString(isolate, arg2);
421   }
422   MaybeHandle<String> maybe_result_string = MessageFormatter::Format(
423       isolate, index, arg0_string, arg1_string, arg2_string);
424   Handle<String> result_string;
425   if (!maybe_result_string.ToHandle(&result_string)) {
426     DCHECK(isolate->has_pending_exception());
427     isolate->clear_pending_exception();
428     return factory->InternalizeString(base::StaticCharVector("<error>"));
429   }
430   // A string that has been obtained from JS code in this way is
431   // likely to be a complicated ConsString of some sort.  We flatten it
432   // here to improve the efficiency of converting it to a C string and
433   // other operations that are likely to take place (see GetLocalizedMessage
434   // for example).
435   return String::Flatten(isolate, result_string);
436 }
437 
TemplateString(MessageTemplate index)438 const char* MessageFormatter::TemplateString(MessageTemplate index) {
439   switch (index) {
440 #define CASE(NAME, STRING)       \
441   case MessageTemplate::k##NAME: \
442     return STRING;
443     MESSAGE_TEMPLATES(CASE)
444 #undef CASE
445     case MessageTemplate::kMessageCount:
446     default:
447       return nullptr;
448   }
449 }
450 
Format(Isolate * isolate,MessageTemplate index,Handle<String> arg0,Handle<String> arg1,Handle<String> arg2)451 MaybeHandle<String> MessageFormatter::Format(Isolate* isolate,
452                                              MessageTemplate index,
453                                              Handle<String> arg0,
454                                              Handle<String> arg1,
455                                              Handle<String> arg2) {
456   const char* template_string = TemplateString(index);
457   if (template_string == nullptr) {
458     isolate->ThrowIllegalOperation();
459     return MaybeHandle<String>();
460   }
461 
462   IncrementalStringBuilder builder(isolate);
463 
464   unsigned int i = 0;
465   Handle<String> args[] = {arg0, arg1, arg2};
466   for (const char* c = template_string; *c != '\0'; c++) {
467     if (*c == '%') {
468       // %% results in verbatim %.
469       if (*(c + 1) == '%') {
470         c++;
471         builder.AppendCharacter('%');
472       } else {
473         DCHECK(i < arraysize(args));
474         Handle<String> arg = args[i++];
475         builder.AppendString(arg);
476       }
477     } else {
478       builder.AppendCharacter(*c);
479     }
480   }
481 
482   return builder.Finish();
483 }
484 
Construct(Isolate * isolate,Handle<JSFunction> target,Handle<Object> new_target,Handle<Object> message,Handle<Object> options)485 MaybeHandle<JSObject> ErrorUtils::Construct(Isolate* isolate,
486                                             Handle<JSFunction> target,
487                                             Handle<Object> new_target,
488                                             Handle<Object> message,
489                                             Handle<Object> options) {
490   FrameSkipMode mode = SKIP_FIRST;
491   Handle<Object> caller;
492 
493   // When we're passed a JSFunction as new target, we can skip frames until that
494   // specific function is seen instead of unconditionally skipping the first
495   // frame.
496   if (new_target->IsJSFunction()) {
497     mode = SKIP_UNTIL_SEEN;
498     caller = new_target;
499   }
500 
501   return ErrorUtils::Construct(isolate, target, new_target, message, options,
502                                mode, caller,
503                                ErrorUtils::StackTraceCollection::kEnabled);
504 }
505 
Construct(Isolate * isolate,Handle<JSFunction> target,Handle<Object> new_target,Handle<Object> message,Handle<Object> options,FrameSkipMode mode,Handle<Object> caller,StackTraceCollection stack_trace_collection)506 MaybeHandle<JSObject> ErrorUtils::Construct(
507     Isolate* isolate, Handle<JSFunction> target, Handle<Object> new_target,
508     Handle<Object> message, Handle<Object> options, FrameSkipMode mode,
509     Handle<Object> caller, StackTraceCollection stack_trace_collection) {
510   if (FLAG_correctness_fuzzer_suppressions) {
511     // Abort range errors in correctness fuzzing, as their causes differ
512     // accross correctness-fuzzing scenarios.
513     if (target.is_identical_to(isolate->range_error_function())) {
514       FATAL("Aborting on range error");
515     }
516     // Ignore error messages in correctness fuzzing, because the spec leaves
517     // room for undefined behavior.
518     message = isolate->factory()->InternalizeUtf8String(
519         "Message suppressed for fuzzers (--correctness-fuzzer-suppressions)");
520   }
521 
522   // 1. If NewTarget is undefined, let newTarget be the active function object,
523   // else let newTarget be NewTarget.
524   Handle<JSReceiver> new_target_recv =
525       new_target->IsJSReceiver() ? Handle<JSReceiver>::cast(new_target)
526                                  : Handle<JSReceiver>::cast(target);
527 
528   // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%ErrorPrototype%",
529   //    « [[ErrorData]] »).
530   Handle<JSObject> err;
531   ASSIGN_RETURN_ON_EXCEPTION(
532       isolate, err,
533       JSObject::New(target, new_target_recv, Handle<AllocationSite>::null()),
534       JSObject);
535 
536   // 3. If message is not undefined, then
537   //  a. Let msg be ? ToString(message).
538   //  b. Let msgDesc be the PropertyDescriptor{[[Value]]: msg, [[Writable]]:
539   //     true, [[Enumerable]]: false, [[Configurable]]: true}.
540   //  c. Perform ! DefinePropertyOrThrow(O, "message", msgDesc).
541   // 4. Return O.
542   if (!message->IsUndefined(isolate)) {
543     Handle<String> msg_string;
544     ASSIGN_RETURN_ON_EXCEPTION(isolate, msg_string,
545                                Object::ToString(isolate, message), JSObject);
546     RETURN_ON_EXCEPTION(
547         isolate,
548         JSObject::SetOwnPropertyIgnoreAttributes(
549             err, isolate->factory()->message_string(), msg_string, DONT_ENUM),
550         JSObject);
551   }
552 
553   if (FLAG_harmony_error_cause && !options->IsUndefined(isolate)) {
554     // If Type(options) is Object and ? HasProperty(options, "cause") then
555     //   a. Let cause be ? Get(options, "cause").
556     //   b. Perform ! CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause).
557     Handle<Name> cause_string = isolate->factory()->cause_string();
558     if (options->IsJSReceiver()) {
559       Handle<JSReceiver> js_options = Handle<JSReceiver>::cast(options);
560       Maybe<bool> has_cause =
561           JSObject::HasProperty(isolate, js_options, cause_string);
562       if (has_cause.IsNothing()) {
563         DCHECK((isolate)->has_pending_exception());
564         return MaybeHandle<JSObject>();
565       }
566       if (has_cause.ToChecked()) {
567         Handle<Object> cause;
568         ASSIGN_RETURN_ON_EXCEPTION(
569             isolate, cause,
570             JSObject::GetProperty(isolate, js_options, cause_string), JSObject);
571         RETURN_ON_EXCEPTION(isolate,
572                             JSObject::SetOwnPropertyIgnoreAttributes(
573                                 err, cause_string, cause, DONT_ENUM),
574                             JSObject);
575       }
576     }
577   }
578 
579   switch (stack_trace_collection) {
580     case StackTraceCollection::kEnabled:
581       RETURN_ON_EXCEPTION(isolate,
582                           isolate->CaptureAndSetErrorStack(err, mode, caller),
583                           JSObject);
584       break;
585     case StackTraceCollection::kDisabled:
586       break;
587   }
588   return err;
589 }
590 
591 namespace {
592 
GetStringPropertyOrDefault(Isolate * isolate,Handle<JSReceiver> recv,Handle<String> key,Handle<String> default_str)593 MaybeHandle<String> GetStringPropertyOrDefault(Isolate* isolate,
594                                                Handle<JSReceiver> recv,
595                                                Handle<String> key,
596                                                Handle<String> default_str) {
597   Handle<Object> obj;
598   ASSIGN_RETURN_ON_EXCEPTION(isolate, obj,
599                              JSObject::GetProperty(isolate, recv, key), String);
600 
601   Handle<String> str;
602   if (obj->IsUndefined(isolate)) {
603     str = default_str;
604   } else {
605     ASSIGN_RETURN_ON_EXCEPTION(isolate, str, Object::ToString(isolate, obj),
606                                String);
607   }
608 
609   return str;
610 }
611 
612 }  // namespace
613 
614 // ES6 section 19.5.3.4 Error.prototype.toString ( )
ToString(Isolate * isolate,Handle<Object> receiver)615 MaybeHandle<String> ErrorUtils::ToString(Isolate* isolate,
616                                          Handle<Object> receiver) {
617   // 1. Let O be the this value.
618   // 2. If Type(O) is not Object, throw a TypeError exception.
619   if (!receiver->IsJSReceiver()) {
620     return isolate->Throw<String>(isolate->factory()->NewTypeError(
621         MessageTemplate::kIncompatibleMethodReceiver,
622         isolate->factory()->NewStringFromAsciiChecked(
623             "Error.prototype.toString"),
624         receiver));
625   }
626   Handle<JSReceiver> recv = Handle<JSReceiver>::cast(receiver);
627 
628   // 3. Let name be ? Get(O, "name").
629   // 4. If name is undefined, let name be "Error"; otherwise let name be
630   // ? ToString(name).
631   Handle<String> name_key = isolate->factory()->name_string();
632   Handle<String> name_default = isolate->factory()->Error_string();
633   Handle<String> name;
634   ASSIGN_RETURN_ON_EXCEPTION(
635       isolate, name,
636       GetStringPropertyOrDefault(isolate, recv, name_key, name_default),
637       String);
638 
639   // 5. Let msg be ? Get(O, "message").
640   // 6. If msg is undefined, let msg be the empty String; otherwise let msg be
641   // ? ToString(msg).
642   Handle<String> msg_key = isolate->factory()->message_string();
643   Handle<String> msg_default = isolate->factory()->empty_string();
644   Handle<String> msg;
645   ASSIGN_RETURN_ON_EXCEPTION(
646       isolate, msg,
647       GetStringPropertyOrDefault(isolate, recv, msg_key, msg_default), String);
648 
649   // 7. If name is the empty String, return msg.
650   // 8. If msg is the empty String, return name.
651   if (name->length() == 0) return msg;
652   if (msg->length() == 0) return name;
653 
654   // 9. Return the result of concatenating name, the code unit 0x003A (COLON),
655   // the code unit 0x0020 (SPACE), and msg.
656   IncrementalStringBuilder builder(isolate);
657   builder.AppendString(name);
658   builder.AppendCStringLiteral(": ");
659   builder.AppendString(msg);
660 
661   Handle<String> result;
662   ASSIGN_RETURN_ON_EXCEPTION(isolate, result, builder.Finish(), String);
663   return result;
664 }
665 
666 namespace {
667 
DoFormatMessage(Isolate * isolate,MessageTemplate index,Handle<Object> arg0,Handle<Object> arg1,Handle<Object> arg2)668 Handle<String> DoFormatMessage(Isolate* isolate, MessageTemplate index,
669                                Handle<Object> arg0, Handle<Object> arg1,
670                                Handle<Object> arg2) {
671   Handle<String> arg0_str = Object::NoSideEffectsToString(isolate, arg0);
672   Handle<String> arg1_str = Object::NoSideEffectsToString(isolate, arg1);
673   Handle<String> arg2_str = Object::NoSideEffectsToString(isolate, arg2);
674 
675   isolate->native_context()->IncrementErrorsThrown();
676 
677   Handle<String> msg;
678   if (!MessageFormatter::Format(isolate, index, arg0_str, arg1_str, arg2_str)
679            .ToHandle(&msg)) {
680     DCHECK(isolate->has_pending_exception());
681     isolate->clear_pending_exception();
682     isolate->set_external_caught_exception(false);
683     return isolate->factory()->NewStringFromAsciiChecked("<error>");
684   }
685 
686   return msg;
687 }
688 
689 }  // namespace
690 
691 // static
MakeGenericError(Isolate * isolate,Handle<JSFunction> constructor,MessageTemplate index,Handle<Object> arg0,Handle<Object> arg1,Handle<Object> arg2,FrameSkipMode mode)692 Handle<JSObject> ErrorUtils::MakeGenericError(
693     Isolate* isolate, Handle<JSFunction> constructor, MessageTemplate index,
694     Handle<Object> arg0, Handle<Object> arg1, Handle<Object> arg2,
695     FrameSkipMode mode) {
696   if (FLAG_clear_exceptions_on_js_entry) {
697     // This function used to be implemented in JavaScript, and JSEntry
698     // clears any pending exceptions - so whenever we'd call this from C++,
699     // pending exceptions would be cleared. Preserve this behavior.
700     isolate->clear_pending_exception();
701   }
702   Handle<String> msg = DoFormatMessage(isolate, index, arg0, arg1, arg2);
703   Handle<Object> options = isolate->factory()->undefined_value();
704 
705   DCHECK(mode != SKIP_UNTIL_SEEN);
706 
707   Handle<Object> no_caller;
708   // The call below can't fail because constructor is a builtin.
709   DCHECK(constructor->shared().HasBuiltinId());
710   return ErrorUtils::Construct(isolate, constructor, constructor, msg, options,
711                                mode, no_caller, StackTraceCollection::kEnabled)
712       .ToHandleChecked();
713 }
714 
715 namespace {
716 
ComputeLocation(Isolate * isolate,MessageLocation * target)717 bool ComputeLocation(Isolate* isolate, MessageLocation* target) {
718   JavaScriptFrameIterator it(isolate);
719   if (!it.done()) {
720     // Compute the location from the function and the relocation info of the
721     // baseline code. For optimized code this will use the deoptimization
722     // information to get canonical location information.
723     std::vector<FrameSummary> frames;
724     it.frame()->Summarize(&frames);
725     auto& summary = frames.back().AsJavaScript();
726     Handle<SharedFunctionInfo> shared(summary.function()->shared(), isolate);
727     Handle<Object> script(shared->script(), isolate);
728     SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, shared);
729     int pos = summary.abstract_code()->SourcePosition(summary.code_offset());
730     if (script->IsScript() &&
731         !(Handle<Script>::cast(script)->source().IsUndefined(isolate))) {
732       Handle<Script> casted_script = Handle<Script>::cast(script);
733       *target = MessageLocation(casted_script, pos, pos + 1, shared);
734       return true;
735     }
736   }
737   return false;
738 }
739 
BuildDefaultCallSite(Isolate * isolate,Handle<Object> object)740 Handle<String> BuildDefaultCallSite(Isolate* isolate, Handle<Object> object) {
741   IncrementalStringBuilder builder(isolate);
742 
743   builder.AppendString(Object::TypeOf(isolate, object));
744   if (object->IsString()) {
745     builder.AppendCStringLiteral(" \"");
746     Handle<String> string = Handle<String>::cast(object);
747     // This threshold must be sufficiently far below String::kMaxLength that
748     // the {builder}'s result can never exceed that limit.
749     constexpr int kMaxPrintedStringLength = 100;
750     if (string->length() <= kMaxPrintedStringLength) {
751       builder.AppendString(string);
752     } else {
753       string = isolate->factory()->NewProperSubString(string, 0,
754                                                       kMaxPrintedStringLength);
755       builder.AppendString(string);
756       builder.AppendCStringLiteral("<...>");
757     }
758     builder.AppendCStringLiteral("\"");
759   } else if (object->IsNull(isolate)) {
760     builder.AppendCStringLiteral(" null");
761   } else if (object->IsTrue(isolate)) {
762     builder.AppendCStringLiteral(" true");
763   } else if (object->IsFalse(isolate)) {
764     builder.AppendCStringLiteral(" false");
765   } else if (object->IsNumber()) {
766     builder.AppendCharacter(' ');
767     builder.AppendString(isolate->factory()->NumberToString(object));
768   }
769 
770   return builder.Finish().ToHandleChecked();
771 }
772 
RenderCallSite(Isolate * isolate,Handle<Object> object,MessageLocation * location,CallPrinter::ErrorHint * hint)773 Handle<String> RenderCallSite(Isolate* isolate, Handle<Object> object,
774                               MessageLocation* location,
775                               CallPrinter::ErrorHint* hint) {
776   if (ComputeLocation(isolate, location)) {
777     UnoptimizedCompileFlags flags = UnoptimizedCompileFlags::ForFunctionCompile(
778         isolate, *location->shared());
779     UnoptimizedCompileState compile_state;
780     ReusableUnoptimizedCompileState reusable_state(isolate);
781     ParseInfo info(isolate, flags, &compile_state, &reusable_state);
782     if (parsing::ParseAny(&info, location->shared(), isolate,
783                           parsing::ReportStatisticsMode::kNo)) {
784       info.ast_value_factory()->Internalize(isolate);
785       CallPrinter printer(isolate, location->shared()->IsUserJavaScript());
786       Handle<String> str = printer.Print(info.literal(), location->start_pos());
787       *hint = printer.GetErrorHint();
788       if (str->length() > 0) return str;
789     }
790   }
791   return BuildDefaultCallSite(isolate, object);
792 }
793 
UpdateErrorTemplate(CallPrinter::ErrorHint hint,MessageTemplate default_id)794 MessageTemplate UpdateErrorTemplate(CallPrinter::ErrorHint hint,
795                                     MessageTemplate default_id) {
796   switch (hint) {
797     case CallPrinter::ErrorHint::kNormalIterator:
798       return MessageTemplate::kNotIterable;
799 
800     case CallPrinter::ErrorHint::kCallAndNormalIterator:
801       return MessageTemplate::kNotCallableOrIterable;
802 
803     case CallPrinter::ErrorHint::kAsyncIterator:
804       return MessageTemplate::kNotAsyncIterable;
805 
806     case CallPrinter::ErrorHint::kCallAndAsyncIterator:
807       return MessageTemplate::kNotCallableOrAsyncIterable;
808 
809     case CallPrinter::ErrorHint::kNone:
810       return default_id;
811   }
812 }
813 
814 }  // namespace
815 
NewIteratorError(Isolate * isolate,Handle<Object> source)816 Handle<JSObject> ErrorUtils::NewIteratorError(Isolate* isolate,
817                                               Handle<Object> source) {
818   MessageLocation location;
819   CallPrinter::ErrorHint hint = CallPrinter::ErrorHint::kNone;
820   Handle<String> callsite = RenderCallSite(isolate, source, &location, &hint);
821   MessageTemplate id = MessageTemplate::kNotIterableNoSymbolLoad;
822 
823   if (hint == CallPrinter::ErrorHint::kNone) {
824     Handle<Symbol> iterator_symbol = isolate->factory()->iterator_symbol();
825     return isolate->factory()->NewTypeError(id, callsite, iterator_symbol);
826   }
827 
828   id = UpdateErrorTemplate(hint, id);
829   return isolate->factory()->NewTypeError(id, callsite);
830 }
831 
ThrowSpreadArgError(Isolate * isolate,MessageTemplate id,Handle<Object> object)832 Object ErrorUtils::ThrowSpreadArgError(Isolate* isolate, MessageTemplate id,
833                                        Handle<Object> object) {
834   MessageLocation location;
835   Handle<String> callsite;
836   if (ComputeLocation(isolate, &location)) {
837     UnoptimizedCompileFlags flags = UnoptimizedCompileFlags::ForFunctionCompile(
838         isolate, *location.shared());
839     UnoptimizedCompileState compile_state;
840     ReusableUnoptimizedCompileState reusable_state(isolate);
841     ParseInfo info(isolate, flags, &compile_state, &reusable_state);
842     if (parsing::ParseAny(&info, location.shared(), isolate,
843                           parsing::ReportStatisticsMode::kNo)) {
844       info.ast_value_factory()->Internalize(isolate);
845       CallPrinter printer(isolate, location.shared()->IsUserJavaScript(),
846                           CallPrinter::SpreadErrorInArgsHint::kErrorInArgs);
847       Handle<String> str = printer.Print(info.literal(), location.start_pos());
848       callsite =
849           str->length() > 0 ? str : BuildDefaultCallSite(isolate, object);
850 
851       if (printer.spread_arg() != nullptr) {
852         // Change the message location to point at the property name.
853         int pos = printer.spread_arg()->position();
854         location =
855             MessageLocation(location.script(), pos, pos + 1, location.shared());
856       }
857     } else {
858       callsite = BuildDefaultCallSite(isolate, object);
859     }
860   }
861 
862   isolate->ThrowAt(isolate->factory()->NewTypeError(id, callsite, object),
863                    &location);
864   return ReadOnlyRoots(isolate).exception();
865 }
866 
NewCalledNonCallableError(Isolate * isolate,Handle<Object> source)867 Handle<JSObject> ErrorUtils::NewCalledNonCallableError(Isolate* isolate,
868                                                        Handle<Object> source) {
869   MessageLocation location;
870   CallPrinter::ErrorHint hint = CallPrinter::ErrorHint::kNone;
871   Handle<String> callsite = RenderCallSite(isolate, source, &location, &hint);
872   MessageTemplate id = MessageTemplate::kCalledNonCallable;
873   id = UpdateErrorTemplate(hint, id);
874   return isolate->factory()->NewTypeError(id, callsite);
875 }
876 
NewConstructedNonConstructable(Isolate * isolate,Handle<Object> source)877 Handle<JSObject> ErrorUtils::NewConstructedNonConstructable(
878     Isolate* isolate, Handle<Object> source) {
879   MessageLocation location;
880   CallPrinter::ErrorHint hint = CallPrinter::ErrorHint::kNone;
881   Handle<String> callsite = RenderCallSite(isolate, source, &location, &hint);
882   MessageTemplate id = MessageTemplate::kNotConstructor;
883   return isolate->factory()->NewTypeError(id, callsite);
884 }
885 
ThrowLoadFromNullOrUndefined(Isolate * isolate,Handle<Object> object,MaybeHandle<Object> key)886 Object ErrorUtils::ThrowLoadFromNullOrUndefined(Isolate* isolate,
887                                                 Handle<Object> object,
888                                                 MaybeHandle<Object> key) {
889   DCHECK(object->IsNullOrUndefined());
890 
891   MaybeHandle<String> maybe_property_name;
892 
893   // Try to extract the property name from the given key, if any.
894   Handle<Object> key_handle;
895   if (key.ToHandle(&key_handle)) {
896     if (key_handle->IsString()) {
897       maybe_property_name = Handle<String>::cast(key_handle);
898     } else {
899       maybe_property_name =
900           Object::NoSideEffectsToMaybeString(isolate, key_handle);
901     }
902   }
903 
904   Handle<String> callsite;
905 
906   // Inline the RenderCallSite logic here so that we can additonally access the
907   // destructuring property.
908   bool location_computed = false;
909   bool is_destructuring = false;
910   MessageLocation location;
911   if (ComputeLocation(isolate, &location)) {
912     location_computed = true;
913 
914     UnoptimizedCompileFlags flags = UnoptimizedCompileFlags::ForFunctionCompile(
915         isolate, *location.shared());
916     UnoptimizedCompileState compile_state;
917     ReusableUnoptimizedCompileState reusable_state(isolate);
918     ParseInfo info(isolate, flags, &compile_state, &reusable_state);
919     if (parsing::ParseAny(&info, location.shared(), isolate,
920                           parsing::ReportStatisticsMode::kNo)) {
921       info.ast_value_factory()->Internalize(isolate);
922       CallPrinter printer(isolate, location.shared()->IsUserJavaScript());
923       Handle<String> str = printer.Print(info.literal(), location.start_pos());
924 
925       int pos = -1;
926       is_destructuring = printer.destructuring_assignment() != nullptr;
927 
928       if (is_destructuring) {
929         // If we don't have one yet, try to extract the property name from the
930         // destructuring property in the AST.
931         ObjectLiteralProperty* destructuring_prop =
932             printer.destructuring_prop();
933         if (maybe_property_name.is_null() && destructuring_prop != nullptr &&
934             destructuring_prop->key()->IsPropertyName()) {
935           maybe_property_name = destructuring_prop->key()
936                                     ->AsLiteral()
937                                     ->AsRawPropertyName()
938                                     ->string();
939           // Change the message location to point at the property name.
940           pos = destructuring_prop->key()->position();
941         }
942         if (maybe_property_name.is_null()) {
943           // Change the message location to point at the destructured value.
944           pos = printer.destructuring_assignment()->value()->position();
945         }
946 
947         // If we updated the pos to a valid pos, rewrite the location.
948         if (pos != -1) {
949           location = MessageLocation(location.script(), pos, pos + 1,
950                                      location.shared());
951         }
952       }
953 
954       if (str->length() > 0) callsite = str;
955     }
956   }
957 
958   if (callsite.is_null()) {
959     callsite = BuildDefaultCallSite(isolate, object);
960   }
961 
962   Handle<JSObject> error;
963   Handle<String> property_name;
964   if (is_destructuring) {
965     if (maybe_property_name.ToHandle(&property_name)) {
966       error = isolate->factory()->NewTypeError(
967           MessageTemplate::kNonCoercibleWithProperty, property_name, callsite,
968           object);
969     } else {
970       error = isolate->factory()->NewTypeError(MessageTemplate::kNonCoercible,
971                                                callsite, object);
972     }
973   } else {
974     if (!key.ToHandle(&key_handle) ||
975         !maybe_property_name.ToHandle(&property_name)) {
976       error = isolate->factory()->NewTypeError(
977           MessageTemplate::kNonObjectPropertyLoad, object);
978     } else if (*key_handle == ReadOnlyRoots(isolate).iterator_symbol()) {
979       error = NewIteratorError(isolate, object);
980     } else {
981       error = isolate->factory()->NewTypeError(
982           MessageTemplate::kNonObjectPropertyLoadWithProperty, object,
983           property_name);
984     }
985   }
986 
987   if (location_computed) {
988     isolate->ThrowAt(error, &location);
989   } else {
990     isolate->Throw(*error);
991   }
992   return ReadOnlyRoots(isolate).exception();
993 }
994 
995 // static
GetFormattedStack(Isolate * isolate,Handle<JSObject> error_object)996 MaybeHandle<Object> ErrorUtils::GetFormattedStack(
997     Isolate* isolate, Handle<JSObject> error_object) {
998   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.stack_trace"), __func__);
999 
1000   Handle<Object> error_stack = JSReceiver::GetDataProperty(
1001       isolate, error_object, isolate->factory()->error_stack_symbol());
1002   if (error_stack->IsErrorStackData()) {
1003     Handle<ErrorStackData> error_stack_data =
1004         Handle<ErrorStackData>::cast(error_stack);
1005     if (error_stack_data->HasFormattedStack()) {
1006       return handle(error_stack_data->formatted_stack(), isolate);
1007     }
1008     ErrorStackData::EnsureStackFrameInfos(isolate, error_stack_data);
1009     Handle<Object> formatted_stack;
1010     ASSIGN_RETURN_ON_EXCEPTION(
1011         isolate, formatted_stack,
1012         FormatStackTrace(isolate, error_object,
1013                          handle(error_stack_data->call_site_infos(), isolate)),
1014         Object);
1015     error_stack_data->set_formatted_stack(*formatted_stack);
1016     return formatted_stack;
1017   }
1018 
1019   if (error_stack->IsFixedArray()) {
1020     Handle<Object> formatted_stack;
1021     ASSIGN_RETURN_ON_EXCEPTION(
1022         isolate, formatted_stack,
1023         FormatStackTrace(isolate, error_object,
1024                          Handle<FixedArray>::cast(error_stack)),
1025         Object);
1026     RETURN_ON_EXCEPTION(
1027         isolate,
1028         JSObject::SetProperty(isolate, error_object,
1029                               isolate->factory()->error_stack_symbol(),
1030                               formatted_stack, StoreOrigin::kMaybeKeyed,
1031                               Just(ShouldThrow::kThrowOnError)),
1032         Object);
1033     return formatted_stack;
1034   }
1035 
1036   return error_stack;
1037 }
1038 
1039 // static
SetFormattedStack(Isolate * isolate,Handle<JSObject> error_object,Handle<Object> formatted_stack)1040 void ErrorUtils::SetFormattedStack(Isolate* isolate,
1041                                    Handle<JSObject> error_object,
1042                                    Handle<Object> formatted_stack) {
1043   Handle<Object> error_stack = JSReceiver::GetDataProperty(
1044       isolate, error_object, isolate->factory()->error_stack_symbol());
1045   if (error_stack->IsErrorStackData()) {
1046     Handle<ErrorStackData> error_stack_data =
1047         Handle<ErrorStackData>::cast(error_stack);
1048     ErrorStackData::EnsureStackFrameInfos(isolate, error_stack_data);
1049     error_stack_data->set_formatted_stack(*formatted_stack);
1050   } else {
1051     JSObject::SetProperty(isolate, error_object,
1052                           isolate->factory()->error_stack_symbol(),
1053                           formatted_stack, StoreOrigin::kMaybeKeyed,
1054                           Just(ShouldThrow::kThrowOnError))
1055         .Check();
1056   }
1057 }
1058 
1059 }  // namespace internal
1060 }  // namespace v8
1061