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/keys.h"
     11 #include "src/string-builder.h"
     12 #include "src/wasm/wasm-module.h"
     13 
     14 namespace v8 {
     15 namespace internal {
     16 
     17 MessageLocation::MessageLocation(Handle<Script> script, int start_pos,
     18                                  int end_pos)
     19     : script_(script), start_pos_(start_pos), end_pos_(end_pos) {}
     20 MessageLocation::MessageLocation(Handle<Script> script, int start_pos,
     21                                  int end_pos, Handle<JSFunction> function)
     22     : script_(script),
     23       start_pos_(start_pos),
     24       end_pos_(end_pos),
     25       function_(function) {}
     26 MessageLocation::MessageLocation() : start_pos_(-1), end_pos_(-1) {}
     27 
     28 // If no message listeners have been registered this one is called
     29 // by default.
     30 void MessageHandler::DefaultMessageReport(Isolate* isolate,
     31                                           const MessageLocation* loc,
     32                                           Handle<Object> message_obj) {
     33   base::SmartArrayPointer<char> str = GetLocalizedMessage(isolate, message_obj);
     34   if (loc == NULL) {
     35     PrintF("%s\n", str.get());
     36   } else {
     37     HandleScope scope(isolate);
     38     Handle<Object> data(loc->script()->name(), isolate);
     39     base::SmartArrayPointer<char> data_str;
     40     if (data->IsString())
     41       data_str = Handle<String>::cast(data)->ToCString(DISALLOW_NULLS);
     42     PrintF("%s:%i: %s\n", data_str.get() ? data_str.get() : "<unknown>",
     43            loc->start_pos(), str.get());
     44   }
     45 }
     46 
     47 
     48 Handle<JSMessageObject> MessageHandler::MakeMessageObject(
     49     Isolate* isolate, MessageTemplate::Template message,
     50     MessageLocation* location, Handle<Object> argument,
     51     Handle<JSArray> stack_frames) {
     52   Factory* factory = isolate->factory();
     53 
     54   int start = -1;
     55   int end = -1;
     56   Handle<Object> script_handle = factory->undefined_value();
     57   if (location != NULL) {
     58     start = location->start_pos();
     59     end = location->end_pos();
     60     script_handle = Script::GetWrapper(location->script());
     61   } else {
     62     script_handle = Script::GetWrapper(isolate->factory()->empty_script());
     63   }
     64 
     65   Handle<Object> stack_frames_handle = stack_frames.is_null()
     66       ? Handle<Object>::cast(factory->undefined_value())
     67       : Handle<Object>::cast(stack_frames);
     68 
     69   Handle<JSMessageObject> message_obj = factory->NewJSMessageObject(
     70       message, argument, start, end, script_handle, stack_frames_handle);
     71 
     72   return message_obj;
     73 }
     74 
     75 
     76 void MessageHandler::ReportMessage(Isolate* isolate, MessageLocation* loc,
     77                                    Handle<JSMessageObject> message) {
     78   // We are calling into embedder's code which can throw exceptions.
     79   // Thus we need to save current exception state, reset it to the clean one
     80   // and ignore scheduled exceptions callbacks can throw.
     81 
     82   // We pass the exception object into the message handler callback though.
     83   Object* exception_object = isolate->heap()->undefined_value();
     84   if (isolate->has_pending_exception()) {
     85     exception_object = isolate->pending_exception();
     86   }
     87   Handle<Object> exception(exception_object, isolate);
     88 
     89   Isolate::ExceptionScope exception_scope(isolate);
     90   isolate->clear_pending_exception();
     91   isolate->set_external_caught_exception(false);
     92 
     93   // Turn the exception on the message into a string if it is an object.
     94   if (message->argument()->IsJSObject()) {
     95     HandleScope scope(isolate);
     96     Handle<Object> argument(message->argument(), isolate);
     97 
     98     MaybeHandle<Object> maybe_stringified;
     99     Handle<Object> stringified;
    100     // Make sure we don't leak uncaught internally generated Error objects.
    101     if (argument->IsJSError()) {
    102       Handle<Object> args[] = {argument};
    103       maybe_stringified = Execution::TryCall(
    104           isolate, isolate->no_side_effects_to_string_fun(),
    105           isolate->factory()->undefined_value(), arraysize(args), args);
    106     } else {
    107       v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
    108       catcher.SetVerbose(false);
    109       catcher.SetCaptureMessage(false);
    110 
    111       maybe_stringified = Object::ToString(isolate, argument);
    112     }
    113 
    114     if (!maybe_stringified.ToHandle(&stringified)) {
    115       stringified = isolate->factory()->NewStringFromAsciiChecked("exception");
    116     }
    117     message->set_argument(*stringified);
    118   }
    119 
    120   v8::Local<v8::Message> api_message_obj = v8::Utils::MessageToLocal(message);
    121   v8::Local<v8::Value> api_exception_obj = v8::Utils::ToLocal(exception);
    122 
    123   v8::NeanderArray global_listeners(isolate->factory()->message_listeners());
    124   int global_length = global_listeners.length();
    125   if (global_length == 0) {
    126     DefaultMessageReport(isolate, loc, message);
    127     if (isolate->has_scheduled_exception()) {
    128       isolate->clear_scheduled_exception();
    129     }
    130   } else {
    131     for (int i = 0; i < global_length; i++) {
    132       HandleScope scope(isolate);
    133       if (global_listeners.get(i)->IsUndefined(isolate)) continue;
    134       v8::NeanderObject listener(JSObject::cast(global_listeners.get(i)));
    135       Handle<Foreign> callback_obj(Foreign::cast(listener.get(0)));
    136       v8::MessageCallback callback =
    137           FUNCTION_CAST<v8::MessageCallback>(callback_obj->foreign_address());
    138       Handle<Object> callback_data(listener.get(1), isolate);
    139       {
    140         // Do not allow exceptions to propagate.
    141         v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
    142         callback(api_message_obj, callback_data->IsUndefined(isolate)
    143                                       ? api_exception_obj
    144                                       : v8::Utils::ToLocal(callback_data));
    145       }
    146       if (isolate->has_scheduled_exception()) {
    147         isolate->clear_scheduled_exception();
    148       }
    149     }
    150   }
    151 }
    152 
    153 
    154 Handle<String> MessageHandler::GetMessage(Isolate* isolate,
    155                                           Handle<Object> data) {
    156   Handle<JSMessageObject> message = Handle<JSMessageObject>::cast(data);
    157   Handle<Object> arg = Handle<Object>(message->argument(), isolate);
    158   return MessageTemplate::FormatMessage(isolate, message->type(), arg);
    159 }
    160 
    161 
    162 base::SmartArrayPointer<char> MessageHandler::GetLocalizedMessage(
    163     Isolate* isolate, Handle<Object> data) {
    164   HandleScope scope(isolate);
    165   return GetMessage(isolate, data)->ToCString(DISALLOW_NULLS);
    166 }
    167 
    168 
    169 CallSite::CallSite(Isolate* isolate, Handle<JSObject> call_site_obj)
    170     : isolate_(isolate) {
    171   Handle<Object> maybe_function = JSObject::GetDataProperty(
    172       call_site_obj, isolate->factory()->call_site_function_symbol());
    173   if (maybe_function->IsJSFunction()) {
    174     // javascript
    175     fun_ = Handle<JSFunction>::cast(maybe_function);
    176     receiver_ = JSObject::GetDataProperty(
    177         call_site_obj, isolate->factory()->call_site_receiver_symbol());
    178   } else {
    179     Handle<Object> maybe_wasm_func_index = JSObject::GetDataProperty(
    180         call_site_obj, isolate->factory()->call_site_wasm_func_index_symbol());
    181     if (!maybe_wasm_func_index->IsSmi()) {
    182       // invalid: neither javascript nor wasm
    183       return;
    184     }
    185     // wasm
    186     wasm_obj_ = Handle<JSObject>::cast(JSObject::GetDataProperty(
    187         call_site_obj, isolate->factory()->call_site_wasm_obj_symbol()));
    188     wasm_func_index_ = Smi::cast(*maybe_wasm_func_index)->value();
    189     DCHECK(static_cast<int>(wasm_func_index_) >= 0);
    190   }
    191 
    192   CHECK(JSObject::GetDataProperty(
    193             call_site_obj, isolate->factory()->call_site_position_symbol())
    194             ->ToInt32(&pos_));
    195 }
    196 
    197 
    198 Handle<Object> CallSite::GetFileName() {
    199   if (!IsJavaScript()) return isolate_->factory()->null_value();
    200   Object* script = fun_->shared()->script();
    201   if (!script->IsScript()) return isolate_->factory()->null_value();
    202   return Handle<Object>(Script::cast(script)->name(), isolate_);
    203 }
    204 
    205 
    206 Handle<Object> CallSite::GetFunctionName() {
    207   if (IsWasm()) {
    208     return wasm::GetWasmFunctionNameOrNull(isolate_, wasm_obj_,
    209                                            wasm_func_index_);
    210   }
    211   Handle<String> result = JSFunction::GetName(fun_);
    212   if (result->length() != 0) return result;
    213 
    214   Handle<Object> script(fun_->shared()->script(), isolate_);
    215   if (script->IsScript() &&
    216       Handle<Script>::cast(script)->compilation_type() ==
    217           Script::COMPILATION_TYPE_EVAL) {
    218     return isolate_->factory()->eval_string();
    219   }
    220   return isolate_->factory()->null_value();
    221 }
    222 
    223 Handle<Object> CallSite::GetScriptNameOrSourceUrl() {
    224   if (!IsJavaScript()) return isolate_->factory()->null_value();
    225   Object* script_obj = fun_->shared()->script();
    226   if (!script_obj->IsScript()) return isolate_->factory()->null_value();
    227   Handle<Script> script(Script::cast(script_obj), isolate_);
    228   Object* source_url = script->source_url();
    229   if (source_url->IsString()) return Handle<Object>(source_url, isolate_);
    230   return Handle<Object>(script->name(), isolate_);
    231 }
    232 
    233 bool CheckMethodName(Isolate* isolate, Handle<JSObject> obj, Handle<Name> name,
    234                      Handle<JSFunction> fun,
    235                      LookupIterator::Configuration config) {
    236   LookupIterator iter =
    237       LookupIterator::PropertyOrElement(isolate, obj, name, config);
    238   if (iter.state() == LookupIterator::DATA) {
    239     return iter.GetDataValue().is_identical_to(fun);
    240   } else if (iter.state() == LookupIterator::ACCESSOR) {
    241     Handle<Object> accessors = iter.GetAccessors();
    242     if (accessors->IsAccessorPair()) {
    243       Handle<AccessorPair> pair = Handle<AccessorPair>::cast(accessors);
    244       return pair->getter() == *fun || pair->setter() == *fun;
    245     }
    246   }
    247   return false;
    248 }
    249 
    250 
    251 Handle<Object> CallSite::GetMethodName() {
    252   if (!IsJavaScript() || receiver_->IsNull(isolate_) ||
    253       receiver_->IsUndefined(isolate_)) {
    254     return isolate_->factory()->null_value();
    255   }
    256   Handle<JSReceiver> receiver =
    257       Object::ToObject(isolate_, receiver_).ToHandleChecked();
    258   if (!receiver->IsJSObject()) {
    259     return isolate_->factory()->null_value();
    260   }
    261 
    262   Handle<JSObject> obj = Handle<JSObject>::cast(receiver);
    263   Handle<Object> function_name(fun_->shared()->name(), isolate_);
    264   if (function_name->IsName()) {
    265     Handle<Name> name = Handle<Name>::cast(function_name);
    266     // ES2015 gives getters and setters name prefixes which must
    267     // be stripped to find the property name.
    268     Handle<String> name_string = Handle<String>::cast(name);
    269     if (name_string->IsUtf8EqualTo(CStrVector("get "), true) ||
    270         name_string->IsUtf8EqualTo(CStrVector("set "), true)) {
    271       name = isolate_->factory()->NewProperSubString(name_string, 4,
    272                                                      name_string->length());
    273     }
    274     if (CheckMethodName(isolate_, obj, name, fun_,
    275                         LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR)) {
    276       return name;
    277     }
    278   }
    279 
    280   HandleScope outer_scope(isolate_);
    281   Handle<Object> result;
    282   for (PrototypeIterator iter(isolate_, obj, kStartAtReceiver); !iter.IsAtEnd();
    283        iter.Advance()) {
    284     Handle<Object> current = PrototypeIterator::GetCurrent(iter);
    285     if (!current->IsJSObject()) break;
    286     Handle<JSObject> current_obj = Handle<JSObject>::cast(current);
    287     if (current_obj->IsAccessCheckNeeded()) break;
    288     Handle<FixedArray> keys =
    289         KeyAccumulator::GetEnumPropertyKeys(isolate_, current_obj);
    290     for (int i = 0; i < keys->length(); i++) {
    291       HandleScope inner_scope(isolate_);
    292       if (!keys->get(i)->IsName()) continue;
    293       Handle<Name> name_key(Name::cast(keys->get(i)), isolate_);
    294       if (!CheckMethodName(isolate_, current_obj, name_key, fun_,
    295                            LookupIterator::OWN_SKIP_INTERCEPTOR))
    296         continue;
    297       // Return null in case of duplicates to avoid confusion.
    298       if (!result.is_null()) return isolate_->factory()->null_value();
    299       result = inner_scope.CloseAndEscape(name_key);
    300     }
    301   }
    302 
    303   if (!result.is_null()) return outer_scope.CloseAndEscape(result);
    304   return isolate_->factory()->null_value();
    305 }
    306 
    307 
    308 int CallSite::GetLineNumber() {
    309   if (pos_ >= 0 && IsJavaScript()) {
    310     Handle<Object> script_obj(fun_->shared()->script(), isolate_);
    311     if (script_obj->IsScript()) {
    312       Handle<Script> script = Handle<Script>::cast(script_obj);
    313       return Script::GetLineNumber(script, pos_) + 1;
    314     }
    315   }
    316   return -1;
    317 }
    318 
    319 
    320 int CallSite::GetColumnNumber() {
    321   if (pos_ >= 0 && IsJavaScript()) {
    322     Handle<Object> script_obj(fun_->shared()->script(), isolate_);
    323     if (script_obj->IsScript()) {
    324       Handle<Script> script = Handle<Script>::cast(script_obj);
    325       return Script::GetColumnNumber(script, pos_) + 1;
    326     }
    327   }
    328   return -1;
    329 }
    330 
    331 
    332 bool CallSite::IsNative() {
    333   if (!IsJavaScript()) return false;
    334   Handle<Object> script(fun_->shared()->script(), isolate_);
    335   return script->IsScript() &&
    336          Handle<Script>::cast(script)->type() == Script::TYPE_NATIVE;
    337 }
    338 
    339 
    340 bool CallSite::IsToplevel() {
    341   if (IsWasm()) return false;
    342   return receiver_->IsJSGlobalProxy() || receiver_->IsNull(isolate_) ||
    343          receiver_->IsUndefined(isolate_);
    344 }
    345 
    346 
    347 bool CallSite::IsEval() {
    348   if (!IsJavaScript()) return false;
    349   Handle<Object> script(fun_->shared()->script(), isolate_);
    350   return script->IsScript() &&
    351          Handle<Script>::cast(script)->compilation_type() ==
    352              Script::COMPILATION_TYPE_EVAL;
    353 }
    354 
    355 
    356 bool CallSite::IsConstructor() {
    357   if (!IsJavaScript() || !receiver_->IsJSObject()) return false;
    358   Handle<Object> constructor =
    359       JSReceiver::GetDataProperty(Handle<JSObject>::cast(receiver_),
    360                                   isolate_->factory()->constructor_string());
    361   return constructor.is_identical_to(fun_);
    362 }
    363 
    364 
    365 Handle<String> MessageTemplate::FormatMessage(Isolate* isolate,
    366                                               int template_index,
    367                                               Handle<Object> arg) {
    368   Factory* factory = isolate->factory();
    369   Handle<String> result_string;
    370   if (arg->IsString()) {
    371     result_string = Handle<String>::cast(arg);
    372   } else {
    373     Handle<JSFunction> fun = isolate->no_side_effects_to_string_fun();
    374 
    375     MaybeHandle<Object> maybe_result =
    376         Execution::TryCall(isolate, fun, factory->undefined_value(), 1, &arg);
    377     Handle<Object> result;
    378     if (!maybe_result.ToHandle(&result) || !result->IsString()) {
    379       return factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("<error>"));
    380     }
    381     result_string = Handle<String>::cast(result);
    382   }
    383   MaybeHandle<String> maybe_result_string = MessageTemplate::FormatMessage(
    384       template_index, result_string, factory->empty_string(),
    385       factory->empty_string());
    386   if (!maybe_result_string.ToHandle(&result_string)) {
    387     return factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("<error>"));
    388   }
    389   // A string that has been obtained from JS code in this way is
    390   // likely to be a complicated ConsString of some sort.  We flatten it
    391   // here to improve the efficiency of converting it to a C string and
    392   // other operations that are likely to take place (see GetLocalizedMessage
    393   // for example).
    394   return String::Flatten(result_string);
    395 }
    396 
    397 
    398 const char* MessageTemplate::TemplateString(int template_index) {
    399   switch (template_index) {
    400 #define CASE(NAME, STRING) \
    401   case k##NAME:            \
    402     return STRING;
    403     MESSAGE_TEMPLATES(CASE)
    404 #undef CASE
    405     case kLastMessage:
    406     default:
    407       return NULL;
    408   }
    409 }
    410 
    411 
    412 MaybeHandle<String> MessageTemplate::FormatMessage(int template_index,
    413                                                    Handle<String> arg0,
    414                                                    Handle<String> arg1,
    415                                                    Handle<String> arg2) {
    416   Isolate* isolate = arg0->GetIsolate();
    417   const char* template_string = TemplateString(template_index);
    418   if (template_string == NULL) {
    419     isolate->ThrowIllegalOperation();
    420     return MaybeHandle<String>();
    421   }
    422 
    423   IncrementalStringBuilder builder(isolate);
    424 
    425   unsigned int i = 0;
    426   Handle<String> args[] = {arg0, arg1, arg2};
    427   for (const char* c = template_string; *c != '\0'; c++) {
    428     if (*c == '%') {
    429       // %% results in verbatim %.
    430       if (*(c + 1) == '%') {
    431         c++;
    432         builder.AppendCharacter('%');
    433       } else {
    434         DCHECK(i < arraysize(args));
    435         Handle<String> arg = args[i++];
    436         builder.AppendString(arg);
    437       }
    438     } else {
    439       builder.AppendCharacter(*c);
    440     }
    441   }
    442 
    443   return builder.Finish();
    444 }
    445 
    446 
    447 }  // namespace internal
    448 }  // namespace v8
    449