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