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