• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MessageLocation(Handle<Script> script,int start_pos,int end_pos)17 MessageLocation::MessageLocation(Handle<Script> script, int start_pos,
18                                  int end_pos)
19     : script_(script), start_pos_(start_pos), end_pos_(end_pos) {}
MessageLocation(Handle<Script> script,int start_pos,int end_pos,Handle<JSFunction> function)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) {}
MessageLocation()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.
DefaultMessageReport(Isolate * isolate,const MessageLocation * loc,Handle<Object> message_obj)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 
MakeMessageObject(Isolate * isolate,MessageTemplate::Template message,MessageLocation * location,Handle<Object> argument,Handle<JSArray> stack_frames)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 
ReportMessage(Isolate * isolate,MessageLocation * loc,Handle<JSMessageObject> message)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 
GetMessage(Isolate * isolate,Handle<Object> data)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 
GetLocalizedMessage(Isolate * isolate,Handle<Object> data)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 
CallSite(Isolate * isolate,Handle<JSObject> call_site_obj)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 
GetFileName()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 
GetFunctionName()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 
GetScriptNameOrSourceUrl()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 
CheckMethodName(Isolate * isolate,Handle<JSObject> obj,Handle<Name> name,Handle<JSFunction> fun,LookupIterator::Configuration config)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 
GetMethodName()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 
GetLineNumber()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 
GetColumnNumber()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 
IsNative()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 
IsToplevel()340 bool CallSite::IsToplevel() {
341   if (IsWasm()) return false;
342   return receiver_->IsJSGlobalProxy() || receiver_->IsNull(isolate_) ||
343          receiver_->IsUndefined(isolate_);
344 }
345 
346 
IsEval()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 
IsConstructor()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 
FormatMessage(Isolate * isolate,int template_index,Handle<Object> arg)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 
TemplateString(int template_index)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 
FormatMessage(int template_index,Handle<String> arg0,Handle<String> arg1,Handle<String> arg2)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