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.
DefaultMessageReport(Isolate * isolate,const MessageLocation * loc,Handle<Object> message_obj)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 
MakeMessageObject(Isolate * isolate,MessageTemplate::Template message,MessageLocation * location,Handle<Object> argument,Handle<JSArray> stack_frames)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 
ReportMessage(Isolate * isolate,MessageLocation * loc,Handle<JSMessageObject> message)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 
GetMessage(Isolate * isolate,Handle<Object> data)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 
GetLocalizedMessage(Isolate * isolate,Handle<Object> data)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 
CallSite(Isolate * isolate,Handle<JSObject> call_site_obj)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 
GetFileName()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 
GetFunctionName()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 
GetScriptNameOrSourceUrl()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 
CheckMethodName(Isolate * isolate,Handle<JSObject> obj,Handle<Name> name,Handle<JSFunction> fun,LookupIterator::Configuration config)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 
GetMethodName()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 
GetLineNumber()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 
GetColumnNumber()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 
IsNative()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 
IsToplevel()300 bool CallSite::IsToplevel() {
301   return receiver_->IsJSGlobalProxy() || receiver_->IsNull() ||
302          receiver_->IsUndefined();
303 }
304 
305 
IsEval()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 
IsConstructor()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 
FormatMessage(Isolate * isolate,int template_index,Handle<Object> arg)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 
TemplateString(int template_index)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 
FormatMessage(int template_index,Handle<String> arg0,Handle<String> arg1,Handle<String> arg2)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