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