blob: 072ac1d5a694a41f7d33a21363cf114ee29ca05e [file] [log] [blame]
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001// Copyright 2011 the V8 project authors. All rights reserved.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Steve Blocka7e24c12009-10-30 11:49:00 +00004
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00005#include "src/messages.h"
Steve Blocka7e24c12009-10-30 11:49:00 +00006
Ben Murdochb8a8cc12014-11-26 15:28:44 +00007#include "src/api.h"
8#include "src/execution.h"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00009#include "src/isolate-inl.h"
10#include "src/string-builder.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000011
12namespace v8 {
13namespace internal {
14
15
16// If no message listeners have been registered this one is called
17// by default.
Ben Murdochb8a8cc12014-11-26 15:28:44 +000018void MessageHandler::DefaultMessageReport(Isolate* isolate,
19 const MessageLocation* loc,
Steve Blocka7e24c12009-10-30 11:49:00 +000020 Handle<Object> message_obj) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000021 base::SmartArrayPointer<char> str = GetLocalizedMessage(isolate, message_obj);
Steve Blocka7e24c12009-10-30 11:49:00 +000022 if (loc == NULL) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000023 PrintF("%s\n", str.get());
Steve Blocka7e24c12009-10-30 11:49:00 +000024 } else {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000025 HandleScope scope(isolate);
26 Handle<Object> data(loc->script()->name(), isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000027 base::SmartArrayPointer<char> data_str;
Steve Blocka7e24c12009-10-30 11:49:00 +000028 if (data->IsString())
29 data_str = Handle<String>::cast(data)->ToCString(DISALLOW_NULLS);
Ben Murdochb8a8cc12014-11-26 15:28:44 +000030 PrintF("%s:%i: %s\n", data_str.get() ? data_str.get() : "<unknown>",
31 loc->start_pos(), str.get());
Steve Blocka7e24c12009-10-30 11:49:00 +000032 }
33}
34
35
Steve Block1e0659c2011-05-24 12:43:12 +010036Handle<JSMessageObject> MessageHandler::MakeMessageObject(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000037 Isolate* isolate, MessageTemplate::Template message,
38 MessageLocation* location, Handle<Object> argument,
Ben Murdoch3bec4d22010-07-22 14:51:16 +010039 Handle<JSArray> stack_frames) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000040 Factory* factory = isolate->factory();
Steve Blocka7e24c12009-10-30 11:49:00 +000041
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000042 int start = -1;
43 int end = -1;
Ben Murdochb8a8cc12014-11-26 15:28:44 +000044 Handle<Object> script_handle = factory->undefined_value();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000045 if (location != NULL) {
46 start = location->start_pos();
47 end = location->end_pos();
48 script_handle = Script::GetWrapper(location->script());
49 } else {
50 script_handle = Script::GetWrapper(isolate->factory()->empty_script());
Steve Blocka7e24c12009-10-30 11:49:00 +000051 }
Steve Blocka7e24c12009-10-30 11:49:00 +000052
Steve Block1e0659c2011-05-24 12:43:12 +010053 Handle<Object> stack_frames_handle = stack_frames.is_null()
Ben Murdochb8a8cc12014-11-26 15:28:44 +000054 ? Handle<Object>::cast(factory->undefined_value())
Steve Block1e0659c2011-05-24 12:43:12 +010055 : Handle<Object>::cast(stack_frames);
Steve Blocka7e24c12009-10-30 11:49:00 +000056
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000057 Handle<JSMessageObject> message_obj = factory->NewJSMessageObject(
58 message, argument, start, end, script_handle, stack_frames_handle);
Steve Blocka7e24c12009-10-30 11:49:00 +000059
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000060 return message_obj;
Steve Blocka7e24c12009-10-30 11:49:00 +000061}
62
63
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000064void MessageHandler::ReportMessage(Isolate* isolate, MessageLocation* loc,
65 Handle<JSMessageObject> message) {
Ben Murdoch8b112d22011-06-08 16:22:53 +010066 // We are calling into embedder's code which can throw exceptions.
67 // Thus we need to save current exception state, reset it to the clean one
68 // and ignore scheduled exceptions callbacks can throw.
Ben Murdochb8a8cc12014-11-26 15:28:44 +000069
70 // We pass the exception object into the message handler callback though.
71 Object* exception_object = isolate->heap()->undefined_value();
72 if (isolate->has_pending_exception()) {
73 exception_object = isolate->pending_exception();
74 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000075 Handle<Object> exception(exception_object, isolate);
Ben Murdochb8a8cc12014-11-26 15:28:44 +000076
Ben Murdoch8b112d22011-06-08 16:22:53 +010077 Isolate::ExceptionScope exception_scope(isolate);
78 isolate->clear_pending_exception();
79 isolate->set_external_caught_exception(false);
80
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000081 // Turn the exception on the message into a string if it is an object.
82 if (message->argument()->IsJSObject()) {
83 HandleScope scope(isolate);
84 Handle<Object> argument(message->argument(), isolate);
85
86 MaybeHandle<Object> maybe_stringified;
87 Handle<Object> stringified;
88 // Make sure we don't leak uncaught internally generated Error objects.
89 if (Object::IsErrorObject(isolate, argument)) {
90 Handle<Object> args[] = {argument};
91 maybe_stringified = Execution::TryCall(
92 isolate, isolate->no_side_effects_to_string_fun(),
93 isolate->factory()->undefined_value(), arraysize(args), args);
94 } else {
95 v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
96 catcher.SetVerbose(false);
97 catcher.SetCaptureMessage(false);
98
99 maybe_stringified = Object::ToString(isolate, argument);
100 }
101
102 if (!maybe_stringified.ToHandle(&stringified)) {
103 stringified = isolate->factory()->NewStringFromAsciiChecked("exception");
104 }
105 message->set_argument(*stringified);
106 }
107
Steve Blocka7e24c12009-10-30 11:49:00 +0000108 v8::Local<v8::Message> api_message_obj = v8::Utils::MessageToLocal(message);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000109 v8::Local<v8::Value> api_exception_obj = v8::Utils::ToLocal(exception);
Steve Blocka7e24c12009-10-30 11:49:00 +0000110
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000111 v8::NeanderArray global_listeners(isolate->factory()->message_listeners());
Steve Blocka7e24c12009-10-30 11:49:00 +0000112 int global_length = global_listeners.length();
113 if (global_length == 0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000114 DefaultMessageReport(isolate, loc, message);
Ben Murdoch8b112d22011-06-08 16:22:53 +0100115 if (isolate->has_scheduled_exception()) {
116 isolate->clear_scheduled_exception();
117 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000118 } else {
119 for (int i = 0; i < global_length; i++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000120 HandleScope scope(isolate);
Steve Blocka7e24c12009-10-30 11:49:00 +0000121 if (global_listeners.get(i)->IsUndefined()) continue;
122 v8::NeanderObject listener(JSObject::cast(global_listeners.get(i)));
Ben Murdoch257744e2011-11-30 15:57:28 +0000123 Handle<Foreign> callback_obj(Foreign::cast(listener.get(0)));
Steve Blocka7e24c12009-10-30 11:49:00 +0000124 v8::MessageCallback callback =
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100125 FUNCTION_CAST<v8::MessageCallback>(callback_obj->foreign_address());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000126 Handle<Object> callback_data(listener.get(1), isolate);
Ben Murdoch8b112d22011-06-08 16:22:53 +0100127 {
128 // Do not allow exceptions to propagate.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000129 v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000130 callback(api_message_obj, callback_data->IsUndefined()
131 ? api_exception_obj
132 : v8::Utils::ToLocal(callback_data));
Ben Murdoch8b112d22011-06-08 16:22:53 +0100133 }
134 if (isolate->has_scheduled_exception()) {
135 isolate->clear_scheduled_exception();
136 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000137 }
138 }
139}
140
141
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000142Handle<String> MessageHandler::GetMessage(Isolate* isolate,
143 Handle<Object> data) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000144 Handle<JSMessageObject> message = Handle<JSMessageObject>::cast(data);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000145 Handle<Object> arg = Handle<Object>(message->argument(), isolate);
146 return MessageTemplate::FormatMessage(isolate, message->type(), arg);
Steve Blocka7e24c12009-10-30 11:49:00 +0000147}
148
149
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000150base::SmartArrayPointer<char> MessageHandler::GetLocalizedMessage(
151 Isolate* isolate, Handle<Object> data) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000152 HandleScope scope(isolate);
153 return GetMessage(isolate, data)->ToCString(DISALLOW_NULLS);
Steve Blocka7e24c12009-10-30 11:49:00 +0000154}
155
156
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000157CallSite::CallSite(Isolate* isolate, Handle<JSObject> call_site_obj)
158 : isolate_(isolate) {
159 Handle<Object> maybe_function = JSObject::GetDataProperty(
160 call_site_obj, isolate->factory()->call_site_function_symbol());
161 if (!maybe_function->IsJSFunction()) return;
162
163 fun_ = Handle<JSFunction>::cast(maybe_function);
164 receiver_ = JSObject::GetDataProperty(
165 call_site_obj, isolate->factory()->call_site_receiver_symbol());
166 CHECK(JSObject::GetDataProperty(
167 call_site_obj, isolate->factory()->call_site_position_symbol())
168 ->ToInt32(&pos_));
169}
170
171
172Handle<Object> CallSite::GetFileName() {
173 Handle<Object> script(fun_->shared()->script(), isolate_);
174 if (script->IsScript()) {
175 return Handle<Object>(Handle<Script>::cast(script)->name(), isolate_);
176 }
177 return isolate_->factory()->null_value();
178}
179
180
181Handle<Object> CallSite::GetFunctionName() {
182 Handle<String> result = JSFunction::GetName(fun_);
183 if (result->length() != 0) return result;
184
185 Handle<Object> script(fun_->shared()->script(), isolate_);
186 if (script->IsScript() &&
187 Handle<Script>::cast(script)->compilation_type() ==
188 Script::COMPILATION_TYPE_EVAL) {
189 return isolate_->factory()->eval_string();
190 }
191 return isolate_->factory()->null_value();
192}
193
194
195Handle<Object> CallSite::GetScriptNameOrSourceUrl() {
196 Handle<Object> script_obj(fun_->shared()->script(), isolate_);
197 if (script_obj->IsScript()) {
198 Handle<Script> script = Handle<Script>::cast(script_obj);
199 Object* source_url = script->source_url();
200 if (source_url->IsString()) return Handle<Object>(source_url, isolate_);
201 return Handle<Object>(script->name(), isolate_);
202 }
203 return isolate_->factory()->null_value();
204}
205
206
207bool CheckMethodName(Isolate* isolate, Handle<JSObject> obj, Handle<Name> name,
208 Handle<JSFunction> fun,
209 LookupIterator::Configuration config) {
210 LookupIterator iter =
211 LookupIterator::PropertyOrElement(isolate, obj, name, config);
212 if (iter.state() == LookupIterator::DATA) {
213 return iter.GetDataValue().is_identical_to(fun);
214 } else if (iter.state() == LookupIterator::ACCESSOR) {
215 Handle<Object> accessors = iter.GetAccessors();
216 if (accessors->IsAccessorPair()) {
217 Handle<AccessorPair> pair = Handle<AccessorPair>::cast(accessors);
218 return pair->getter() == *fun || pair->setter() == *fun;
219 }
220 }
221 return false;
222}
223
224
225Handle<Object> CallSite::GetMethodName() {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100226 if (receiver_->IsNull() || receiver_->IsUndefined()) {
227 return isolate_->factory()->null_value();
228 }
229 Handle<JSReceiver> receiver =
230 Object::ToObject(isolate_, receiver_).ToHandleChecked();
231 if (!receiver->IsJSObject()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000232 return isolate_->factory()->null_value();
233 }
234
235 Handle<JSObject> obj = Handle<JSObject>::cast(receiver);
236 Handle<Object> function_name(fun_->shared()->name(), isolate_);
237 if (function_name->IsName()) {
238 Handle<Name> name = Handle<Name>::cast(function_name);
239 if (CheckMethodName(isolate_, obj, name, fun_,
240 LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR))
241 return name;
242 }
243
244 HandleScope outer_scope(isolate_);
245 Handle<Object> result;
246 for (PrototypeIterator iter(isolate_, obj,
247 PrototypeIterator::START_AT_RECEIVER);
248 !iter.IsAtEnd(); iter.Advance()) {
249 Handle<Object> current = PrototypeIterator::GetCurrent(iter);
250 if (!current->IsJSObject()) break;
251 Handle<JSObject> current_obj = Handle<JSObject>::cast(current);
252 if (current_obj->IsAccessCheckNeeded()) break;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100253 Handle<FixedArray> keys = JSObject::GetEnumPropertyKeys(current_obj);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000254 for (int i = 0; i < keys->length(); i++) {
255 HandleScope inner_scope(isolate_);
256 if (!keys->get(i)->IsName()) continue;
257 Handle<Name> name_key(Name::cast(keys->get(i)), isolate_);
258 if (!CheckMethodName(isolate_, current_obj, name_key, fun_,
259 LookupIterator::OWN_SKIP_INTERCEPTOR))
260 continue;
261 // Return null in case of duplicates to avoid confusion.
262 if (!result.is_null()) return isolate_->factory()->null_value();
263 result = inner_scope.CloseAndEscape(name_key);
264 }
265 }
266
267 if (!result.is_null()) return outer_scope.CloseAndEscape(result);
268 return isolate_->factory()->null_value();
269}
270
271
272int CallSite::GetLineNumber() {
273 if (pos_ >= 0) {
274 Handle<Object> script_obj(fun_->shared()->script(), isolate_);
275 if (script_obj->IsScript()) {
276 Handle<Script> script = Handle<Script>::cast(script_obj);
277 return Script::GetLineNumber(script, pos_) + 1;
278 }
279 }
280 return -1;
281}
282
283
284int CallSite::GetColumnNumber() {
285 if (pos_ >= 0) {
286 Handle<Object> script_obj(fun_->shared()->script(), isolate_);
287 if (script_obj->IsScript()) {
288 Handle<Script> script = Handle<Script>::cast(script_obj);
289 return Script::GetColumnNumber(script, pos_) + 1;
290 }
291 }
292 return -1;
293}
294
295
296bool CallSite::IsNative() {
297 Handle<Object> script(fun_->shared()->script(), isolate_);
298 return script->IsScript() &&
299 Handle<Script>::cast(script)->type() == Script::TYPE_NATIVE;
300}
301
302
303bool CallSite::IsToplevel() {
304 return receiver_->IsJSGlobalProxy() || receiver_->IsNull() ||
305 receiver_->IsUndefined();
306}
307
308
309bool CallSite::IsEval() {
310 Handle<Object> script(fun_->shared()->script(), isolate_);
311 return script->IsScript() &&
312 Handle<Script>::cast(script)->compilation_type() ==
313 Script::COMPILATION_TYPE_EVAL;
314}
315
316
317bool CallSite::IsConstructor() {
318 if (!receiver_->IsJSObject()) return false;
319 Handle<Object> constructor =
320 JSReceiver::GetDataProperty(Handle<JSObject>::cast(receiver_),
321 isolate_->factory()->constructor_string());
322 return constructor.is_identical_to(fun_);
323}
324
325
326Handle<String> MessageTemplate::FormatMessage(Isolate* isolate,
327 int template_index,
328 Handle<Object> arg) {
329 Factory* factory = isolate->factory();
330 Handle<String> result_string;
331 if (arg->IsString()) {
332 result_string = Handle<String>::cast(arg);
333 } else {
334 Handle<JSFunction> fun = isolate->no_side_effects_to_string_fun();
335
336 MaybeHandle<Object> maybe_result =
337 Execution::TryCall(isolate, fun, factory->undefined_value(), 1, &arg);
338 Handle<Object> result;
339 if (!maybe_result.ToHandle(&result) || !result->IsString()) {
340 return factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("<error>"));
341 }
342 result_string = Handle<String>::cast(result);
343 }
344 MaybeHandle<String> maybe_result_string = MessageTemplate::FormatMessage(
345 template_index, result_string, factory->empty_string(),
346 factory->empty_string());
347 if (!maybe_result_string.ToHandle(&result_string)) {
348 return factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("<error>"));
349 }
350 // A string that has been obtained from JS code in this way is
351 // likely to be a complicated ConsString of some sort. We flatten it
352 // here to improve the efficiency of converting it to a C string and
353 // other operations that are likely to take place (see GetLocalizedMessage
354 // for example).
355 return String::Flatten(result_string);
356}
357
358
359const char* MessageTemplate::TemplateString(int template_index) {
360 switch (template_index) {
361#define CASE(NAME, STRING) \
362 case k##NAME: \
363 return STRING;
364 MESSAGE_TEMPLATES(CASE)
365#undef CASE
366 case kLastMessage:
367 default:
368 return NULL;
369 }
370}
371
372
373MaybeHandle<String> MessageTemplate::FormatMessage(int template_index,
374 Handle<String> arg0,
375 Handle<String> arg1,
376 Handle<String> arg2) {
377 Isolate* isolate = arg0->GetIsolate();
378 const char* template_string = TemplateString(template_index);
379 if (template_string == NULL) {
380 isolate->ThrowIllegalOperation();
381 return MaybeHandle<String>();
382 }
383
384 IncrementalStringBuilder builder(isolate);
385
386 unsigned int i = 0;
387 Handle<String> args[] = {arg0, arg1, arg2};
388 for (const char* c = template_string; *c != '\0'; c++) {
389 if (*c == '%') {
390 // %% results in verbatim %.
391 if (*(c + 1) == '%') {
392 c++;
393 builder.AppendCharacter('%');
394 } else {
395 DCHECK(i < arraysize(args));
396 Handle<String> arg = args[i++];
397 builder.AppendString(arg);
398 }
399 } else {
400 builder.AppendCharacter(*c);
401 }
402 }
403
404 return builder.Finish();
405}
406
407
408} // namespace internal
409} // namespace v8