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