Home | History | Annotate | Download | only in src
      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 
     12 namespace v8 {
     13 namespace internal {
     14 
     15 
     16 // If no message listeners have been registered this one is called
     17 // by default.
     18 void 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 
     36 Handle<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 
     64 void 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 
    142 Handle<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 
    150 base::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 
    157 CallSite::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 
    172 Handle<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 
    181 Handle<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 
    195 Handle<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 
    207 bool 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 
    225 Handle<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 
    269 int 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 
    281 int 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 
    293 bool 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 
    300 bool CallSite::IsToplevel() {
    301   return receiver_->IsJSGlobalProxy() || receiver_->IsNull() ||
    302          receiver_->IsUndefined();
    303 }
    304 
    305 
    306 bool 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 
    314 bool 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 
    323 Handle<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 
    356 const 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 
    370 MaybeHandle<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