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/messages.h"
6
7#include "src/api.h"
8#include "src/execution.h"
9#include "src/isolate-inl.h"
10#include "src/string-builder.h"
11
12namespace v8 {
13namespace internal {
14
15
16// If no message listeners have been registered this one is called
17// by default.
18void MessageHandler::DefaultMessageReport(Isolate* isolate,
19                                          const MessageLocation* loc,
20                                          Handle<Object> message_obj) {
21  base::SmartArrayPointer<char> str = GetLocalizedMessage(isolate, message_obj);
22  if (loc == NULL) {
23    PrintF("%s\n", str.get());
24  } else {
25    HandleScope scope(isolate);
26    Handle<Object> data(loc->script()->name(), isolate);
27    base::SmartArrayPointer<char> data_str;
28    if (data->IsString())
29      data_str = Handle<String>::cast(data)->ToCString(DISALLOW_NULLS);
30    PrintF("%s:%i: %s\n", data_str.get() ? data_str.get() : "<unknown>",
31           loc->start_pos(), str.get());
32  }
33}
34
35
36Handle<JSMessageObject> MessageHandler::MakeMessageObject(
37    Isolate* isolate, MessageTemplate::Template message,
38    MessageLocation* location, Handle<Object> argument,
39    Handle<JSArray> stack_frames) {
40  Factory* factory = isolate->factory();
41
42  int start = -1;
43  int end = -1;
44  Handle<Object> script_handle = factory->undefined_value();
45  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());
51  }
52
53  Handle<Object> stack_frames_handle = stack_frames.is_null()
54      ? Handle<Object>::cast(factory->undefined_value())
55      : Handle<Object>::cast(stack_frames);
56
57  Handle<JSMessageObject> message_obj = factory->NewJSMessageObject(
58      message, argument, start, end, script_handle, stack_frames_handle);
59
60  return message_obj;
61}
62
63
64void MessageHandler::ReportMessage(Isolate* isolate, MessageLocation* loc,
65                                   Handle<JSMessageObject> message) {
66  // 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.
69
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  }
75  Handle<Object> exception(exception_object, isolate);
76
77  Isolate::ExceptionScope exception_scope(isolate);
78  isolate->clear_pending_exception();
79  isolate->set_external_caught_exception(false);
80
81  // 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
108  v8::Local<v8::Message> api_message_obj = v8::Utils::MessageToLocal(message);
109  v8::Local<v8::Value> api_exception_obj = v8::Utils::ToLocal(exception);
110
111  v8::NeanderArray global_listeners(isolate->factory()->message_listeners());
112  int global_length = global_listeners.length();
113  if (global_length == 0) {
114    DefaultMessageReport(isolate, loc, message);
115    if (isolate->has_scheduled_exception()) {
116      isolate->clear_scheduled_exception();
117    }
118  } else {
119    for (int i = 0; i < global_length; i++) {
120      HandleScope scope(isolate);
121      if (global_listeners.get(i)->IsUndefined()) continue;
122      v8::NeanderObject listener(JSObject::cast(global_listeners.get(i)));
123      Handle<Foreign> callback_obj(Foreign::cast(listener.get(0)));
124      v8::MessageCallback callback =
125          FUNCTION_CAST<v8::MessageCallback>(callback_obj->foreign_address());
126      Handle<Object> callback_data(listener.get(1), isolate);
127      {
128        // Do not allow exceptions to propagate.
129        v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
130        callback(api_message_obj, callback_data->IsUndefined()
131                                      ? api_exception_obj
132                                      : v8::Utils::ToLocal(callback_data));
133      }
134      if (isolate->has_scheduled_exception()) {
135        isolate->clear_scheduled_exception();
136      }
137    }
138  }
139}
140
141
142Handle<String> MessageHandler::GetMessage(Isolate* isolate,
143                                          Handle<Object> data) {
144  Handle<JSMessageObject> message = Handle<JSMessageObject>::cast(data);
145  Handle<Object> arg = Handle<Object>(message->argument(), isolate);
146  return MessageTemplate::FormatMessage(isolate, message->type(), arg);
147}
148
149
150base::SmartArrayPointer<char> MessageHandler::GetLocalizedMessage(
151    Isolate* isolate, Handle<Object> data) {
152  HandleScope scope(isolate);
153  return GetMessage(isolate, data)->ToCString(DISALLOW_NULLS);
154}
155
156
157CallSite::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() {
226  MaybeHandle<JSReceiver> maybe = Object::ToObject(isolate_, receiver_);
227  Handle<JSReceiver> receiver;
228  if (!maybe.ToHandle(&receiver) || !receiver->IsJSObject()) {
229    return isolate_->factory()->null_value();
230  }
231
232  Handle<JSObject> obj = Handle<JSObject>::cast(receiver);
233  Handle<Object> function_name(fun_->shared()->name(), isolate_);
234  if (function_name->IsName()) {
235    Handle<Name> name = Handle<Name>::cast(function_name);
236    if (CheckMethodName(isolate_, obj, name, fun_,
237                        LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR))
238      return name;
239  }
240
241  HandleScope outer_scope(isolate_);
242  Handle<Object> result;
243  for (PrototypeIterator iter(isolate_, obj,
244                              PrototypeIterator::START_AT_RECEIVER);
245       !iter.IsAtEnd(); iter.Advance()) {
246    Handle<Object> current = PrototypeIterator::GetCurrent(iter);
247    if (!current->IsJSObject()) break;
248    Handle<JSObject> current_obj = Handle<JSObject>::cast(current);
249    if (current_obj->IsAccessCheckNeeded()) break;
250    Handle<FixedArray> keys = JSObject::GetEnumPropertyKeys(current_obj, false);
251    for (int i = 0; i < keys->length(); i++) {
252      HandleScope inner_scope(isolate_);
253      if (!keys->get(i)->IsName()) continue;
254      Handle<Name> name_key(Name::cast(keys->get(i)), isolate_);
255      if (!CheckMethodName(isolate_, current_obj, name_key, fun_,
256                           LookupIterator::OWN_SKIP_INTERCEPTOR))
257        continue;
258      // Return null in case of duplicates to avoid confusion.
259      if (!result.is_null()) return isolate_->factory()->null_value();
260      result = inner_scope.CloseAndEscape(name_key);
261    }
262  }
263
264  if (!result.is_null()) return outer_scope.CloseAndEscape(result);
265  return isolate_->factory()->null_value();
266}
267
268
269int CallSite::GetLineNumber() {
270  if (pos_ >= 0) {
271    Handle<Object> script_obj(fun_->shared()->script(), isolate_);
272    if (script_obj->IsScript()) {
273      Handle<Script> script = Handle<Script>::cast(script_obj);
274      return Script::GetLineNumber(script, pos_) + 1;
275    }
276  }
277  return -1;
278}
279
280
281int CallSite::GetColumnNumber() {
282  if (pos_ >= 0) {
283    Handle<Object> script_obj(fun_->shared()->script(), isolate_);
284    if (script_obj->IsScript()) {
285      Handle<Script> script = Handle<Script>::cast(script_obj);
286      return Script::GetColumnNumber(script, pos_) + 1;
287    }
288  }
289  return -1;
290}
291
292
293bool CallSite::IsNative() {
294  Handle<Object> script(fun_->shared()->script(), isolate_);
295  return script->IsScript() &&
296         Handle<Script>::cast(script)->type() == Script::TYPE_NATIVE;
297}
298
299
300bool CallSite::IsToplevel() {
301  return receiver_->IsJSGlobalProxy() || receiver_->IsNull() ||
302         receiver_->IsUndefined();
303}
304
305
306bool CallSite::IsEval() {
307  Handle<Object> script(fun_->shared()->script(), isolate_);
308  return script->IsScript() &&
309         Handle<Script>::cast(script)->compilation_type() ==
310             Script::COMPILATION_TYPE_EVAL;
311}
312
313
314bool CallSite::IsConstructor() {
315  if (!receiver_->IsJSObject()) return false;
316  Handle<Object> constructor =
317      JSReceiver::GetDataProperty(Handle<JSObject>::cast(receiver_),
318                                  isolate_->factory()->constructor_string());
319  return constructor.is_identical_to(fun_);
320}
321
322
323Handle<String> MessageTemplate::FormatMessage(Isolate* isolate,
324                                              int template_index,
325                                              Handle<Object> arg) {
326  Factory* factory = isolate->factory();
327  Handle<String> result_string;
328  if (arg->IsString()) {
329    result_string = Handle<String>::cast(arg);
330  } else {
331    Handle<JSFunction> fun = isolate->no_side_effects_to_string_fun();
332
333    MaybeHandle<Object> maybe_result =
334        Execution::TryCall(isolate, fun, factory->undefined_value(), 1, &arg);
335    Handle<Object> result;
336    if (!maybe_result.ToHandle(&result) || !result->IsString()) {
337      return factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("<error>"));
338    }
339    result_string = Handle<String>::cast(result);
340  }
341  MaybeHandle<String> maybe_result_string = MessageTemplate::FormatMessage(
342      template_index, result_string, factory->empty_string(),
343      factory->empty_string());
344  if (!maybe_result_string.ToHandle(&result_string)) {
345    return factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("<error>"));
346  }
347  // A string that has been obtained from JS code in this way is
348  // likely to be a complicated ConsString of some sort.  We flatten it
349  // here to improve the efficiency of converting it to a C string and
350  // other operations that are likely to take place (see GetLocalizedMessage
351  // for example).
352  return String::Flatten(result_string);
353}
354
355
356const char* MessageTemplate::TemplateString(int template_index) {
357  switch (template_index) {
358#define CASE(NAME, STRING) \
359  case k##NAME:            \
360    return STRING;
361    MESSAGE_TEMPLATES(CASE)
362#undef CASE
363    case kLastMessage:
364    default:
365      return NULL;
366  }
367}
368
369
370MaybeHandle<String> MessageTemplate::FormatMessage(int template_index,
371                                                   Handle<String> arg0,
372                                                   Handle<String> arg1,
373                                                   Handle<String> arg2) {
374  Isolate* isolate = arg0->GetIsolate();
375  const char* template_string = TemplateString(template_index);
376  if (template_string == NULL) {
377    isolate->ThrowIllegalOperation();
378    return MaybeHandle<String>();
379  }
380
381  IncrementalStringBuilder builder(isolate);
382
383  unsigned int i = 0;
384  Handle<String> args[] = {arg0, arg1, arg2};
385  for (const char* c = template_string; *c != '\0'; c++) {
386    if (*c == '%') {
387      // %% results in verbatim %.
388      if (*(c + 1) == '%') {
389        c++;
390        builder.AppendCharacter('%');
391      } else {
392        DCHECK(i < arraysize(args));
393        Handle<String> arg = args[i++];
394        builder.AppendString(arg);
395      }
396    } else {
397      builder.AppendCharacter(*c);
398    }
399  }
400
401  return builder.Finish();
402}
403
404
405}  // namespace internal
406}  // namespace v8
407