1 // Copyright 2014 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/string-stream.h"
6
7 #include <memory>
8
9 #include "src/handles-inl.h"
10 #include "src/prototype.h"
11
12 namespace v8 {
13 namespace internal {
14
15 static const int kMentionedObjectCacheMaxSize = 256;
16
allocate(unsigned bytes)17 char* HeapStringAllocator::allocate(unsigned bytes) {
18 space_ = NewArray<char>(bytes);
19 return space_;
20 }
21
22
allocate(unsigned bytes)23 char* FixedStringAllocator::allocate(unsigned bytes) {
24 CHECK_LE(bytes, length_);
25 return buffer_;
26 }
27
28
grow(unsigned * old)29 char* FixedStringAllocator::grow(unsigned* old) {
30 *old = length_;
31 return buffer_;
32 }
33
34
Put(char c)35 bool StringStream::Put(char c) {
36 if (full()) return false;
37 DCHECK(length_ < capacity_);
38 // Since the trailing '\0' is not accounted for in length_ fullness is
39 // indicated by a difference of 1 between length_ and capacity_. Thus when
40 // reaching a difference of 2 we need to grow the buffer.
41 if (length_ == capacity_ - 2) {
42 unsigned new_capacity = capacity_;
43 char* new_buffer = allocator_->grow(&new_capacity);
44 if (new_capacity > capacity_) {
45 capacity_ = new_capacity;
46 buffer_ = new_buffer;
47 } else {
48 // Reached the end of the available buffer.
49 DCHECK(capacity_ >= 5);
50 length_ = capacity_ - 1; // Indicate fullness of the stream.
51 buffer_[length_ - 4] = '.';
52 buffer_[length_ - 3] = '.';
53 buffer_[length_ - 2] = '.';
54 buffer_[length_ - 1] = '\n';
55 buffer_[length_] = '\0';
56 return false;
57 }
58 }
59 buffer_[length_] = c;
60 buffer_[length_ + 1] = '\0';
61 length_++;
62 return true;
63 }
64
65
66 // A control character is one that configures a format element. For
67 // instance, in %.5s, .5 are control characters.
IsControlChar(char c)68 static bool IsControlChar(char c) {
69 switch (c) {
70 case '0': case '1': case '2': case '3': case '4': case '5':
71 case '6': case '7': case '8': case '9': case '.': case '-':
72 return true;
73 default:
74 return false;
75 }
76 }
77
78
Add(Vector<const char> format,Vector<FmtElm> elms)79 void StringStream::Add(Vector<const char> format, Vector<FmtElm> elms) {
80 // If we already ran out of space then return immediately.
81 if (full()) return;
82 int offset = 0;
83 int elm = 0;
84 while (offset < format.length()) {
85 if (format[offset] != '%' || elm == elms.length()) {
86 Put(format[offset]);
87 offset++;
88 continue;
89 }
90 // Read this formatting directive into a temporary buffer
91 EmbeddedVector<char, 24> temp;
92 int format_length = 0;
93 // Skip over the whole control character sequence until the
94 // format element type
95 temp[format_length++] = format[offset++];
96 while (offset < format.length() && IsControlChar(format[offset]))
97 temp[format_length++] = format[offset++];
98 if (offset >= format.length())
99 return;
100 char type = format[offset];
101 temp[format_length++] = type;
102 temp[format_length] = '\0';
103 offset++;
104 FmtElm current = elms[elm++];
105 switch (type) {
106 case 's': {
107 DCHECK_EQ(FmtElm::C_STR, current.type_);
108 const char* value = current.data_.u_c_str_;
109 Add(value);
110 break;
111 }
112 case 'w': {
113 DCHECK_EQ(FmtElm::LC_STR, current.type_);
114 Vector<const uc16> value = *current.data_.u_lc_str_;
115 for (int i = 0; i < value.length(); i++)
116 Put(static_cast<char>(value[i]));
117 break;
118 }
119 case 'o': {
120 DCHECK_EQ(FmtElm::OBJ, current.type_);
121 Object* obj = current.data_.u_obj_;
122 PrintObject(obj);
123 break;
124 }
125 case 'k': {
126 DCHECK_EQ(FmtElm::INT, current.type_);
127 int value = current.data_.u_int_;
128 if (0x20 <= value && value <= 0x7F) {
129 Put(value);
130 } else if (value <= 0xff) {
131 Add("\\x%02x", value);
132 } else {
133 Add("\\u%04x", value);
134 }
135 break;
136 }
137 case 'i': case 'd': case 'u': case 'x': case 'c': case 'X': {
138 int value = current.data_.u_int_;
139 EmbeddedVector<char, 24> formatted;
140 int length = SNPrintF(formatted, temp.start(), value);
141 Add(Vector<const char>(formatted.start(), length));
142 break;
143 }
144 case 'f': case 'g': case 'G': case 'e': case 'E': {
145 double value = current.data_.u_double_;
146 int inf = std::isinf(value);
147 if (inf == -1) {
148 Add("-inf");
149 } else if (inf == 1) {
150 Add("inf");
151 } else if (std::isnan(value)) {
152 Add("nan");
153 } else {
154 EmbeddedVector<char, 28> formatted;
155 SNPrintF(formatted, temp.start(), value);
156 Add(formatted.start());
157 }
158 break;
159 }
160 case 'p': {
161 void* value = current.data_.u_pointer_;
162 EmbeddedVector<char, 20> formatted;
163 SNPrintF(formatted, temp.start(), value);
164 Add(formatted.start());
165 break;
166 }
167 default:
168 UNREACHABLE();
169 break;
170 }
171 }
172
173 // Verify that the buffer is 0-terminated
174 DCHECK(buffer_[length_] == '\0');
175 }
176
177
PrintObject(Object * o)178 void StringStream::PrintObject(Object* o) {
179 o->ShortPrint(this);
180 if (o->IsString()) {
181 if (String::cast(o)->length() <= String::kMaxShortPrintLength) {
182 return;
183 }
184 } else if (o->IsNumber() || o->IsOddball()) {
185 return;
186 }
187 if (o->IsHeapObject() && object_print_mode_ == kPrintObjectVerbose) {
188 HeapObject* ho = HeapObject::cast(o);
189 DebugObjectCache* debug_object_cache = ho->GetIsolate()->
190 string_stream_debug_object_cache();
191 for (int i = 0; i < debug_object_cache->length(); i++) {
192 if ((*debug_object_cache)[i] == o) {
193 Add("#%d#", i);
194 return;
195 }
196 }
197 if (debug_object_cache->length() < kMentionedObjectCacheMaxSize) {
198 Add("#%d#", debug_object_cache->length());
199 debug_object_cache->Add(HeapObject::cast(o));
200 } else {
201 Add("@%p", o);
202 }
203 }
204 }
205
206
Add(const char * format)207 void StringStream::Add(const char* format) {
208 Add(CStrVector(format));
209 }
210
211
Add(Vector<const char> format)212 void StringStream::Add(Vector<const char> format) {
213 Add(format, Vector<FmtElm>::empty());
214 }
215
216
Add(const char * format,FmtElm arg0)217 void StringStream::Add(const char* format, FmtElm arg0) {
218 const char argc = 1;
219 FmtElm argv[argc] = { arg0 };
220 Add(CStrVector(format), Vector<FmtElm>(argv, argc));
221 }
222
223
Add(const char * format,FmtElm arg0,FmtElm arg1)224 void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1) {
225 const char argc = 2;
226 FmtElm argv[argc] = { arg0, arg1 };
227 Add(CStrVector(format), Vector<FmtElm>(argv, argc));
228 }
229
230
Add(const char * format,FmtElm arg0,FmtElm arg1,FmtElm arg2)231 void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1,
232 FmtElm arg2) {
233 const char argc = 3;
234 FmtElm argv[argc] = { arg0, arg1, arg2 };
235 Add(CStrVector(format), Vector<FmtElm>(argv, argc));
236 }
237
238
Add(const char * format,FmtElm arg0,FmtElm arg1,FmtElm arg2,FmtElm arg3)239 void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1,
240 FmtElm arg2, FmtElm arg3) {
241 const char argc = 4;
242 FmtElm argv[argc] = { arg0, arg1, arg2, arg3 };
243 Add(CStrVector(format), Vector<FmtElm>(argv, argc));
244 }
245
246
Add(const char * format,FmtElm arg0,FmtElm arg1,FmtElm arg2,FmtElm arg3,FmtElm arg4)247 void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1,
248 FmtElm arg2, FmtElm arg3, FmtElm arg4) {
249 const char argc = 5;
250 FmtElm argv[argc] = { arg0, arg1, arg2, arg3, arg4 };
251 Add(CStrVector(format), Vector<FmtElm>(argv, argc));
252 }
253
ToCString() const254 std::unique_ptr<char[]> StringStream::ToCString() const {
255 char* str = NewArray<char>(length_ + 1);
256 MemCopy(str, buffer_, length_);
257 str[length_] = '\0';
258 return std::unique_ptr<char[]>(str);
259 }
260
261
Log(Isolate * isolate)262 void StringStream::Log(Isolate* isolate) {
263 LOG(isolate, StringEvent("StackDump", buffer_));
264 }
265
266
OutputToFile(FILE * out)267 void StringStream::OutputToFile(FILE* out) {
268 // Dump the output to stdout, but make sure to break it up into
269 // manageable chunks to avoid losing parts of the output in the OS
270 // printing code. This is a problem on Windows in particular; see
271 // the VPrint() function implementations in platform-win32.cc.
272 unsigned position = 0;
273 for (unsigned next; (next = position + 2048) < length_; position = next) {
274 char save = buffer_[next];
275 buffer_[next] = '\0';
276 internal::PrintF(out, "%s", &buffer_[position]);
277 buffer_[next] = save;
278 }
279 internal::PrintF(out, "%s", &buffer_[position]);
280 }
281
282
ToString(Isolate * isolate)283 Handle<String> StringStream::ToString(Isolate* isolate) {
284 return isolate->factory()->NewStringFromUtf8(
285 Vector<const char>(buffer_, length_)).ToHandleChecked();
286 }
287
288
ClearMentionedObjectCache(Isolate * isolate)289 void StringStream::ClearMentionedObjectCache(Isolate* isolate) {
290 isolate->set_string_stream_current_security_token(NULL);
291 if (isolate->string_stream_debug_object_cache() == NULL) {
292 isolate->set_string_stream_debug_object_cache(new DebugObjectCache(0));
293 }
294 isolate->string_stream_debug_object_cache()->Clear();
295 }
296
297
298 #ifdef DEBUG
IsMentionedObjectCacheClear(Isolate * isolate)299 bool StringStream::IsMentionedObjectCacheClear(Isolate* isolate) {
300 return object_print_mode_ == kPrintObjectConcise ||
301 isolate->string_stream_debug_object_cache()->length() == 0;
302 }
303 #endif
304
305
Put(String * str)306 bool StringStream::Put(String* str) {
307 return Put(str, 0, str->length());
308 }
309
310
Put(String * str,int start,int end)311 bool StringStream::Put(String* str, int start, int end) {
312 StringCharacterStream stream(str, start);
313 for (int i = start; i < end && stream.HasMore(); i++) {
314 uint16_t c = stream.GetNext();
315 if (c >= 127 || c < 32) {
316 c = '?';
317 }
318 if (!Put(static_cast<char>(c))) {
319 return false; // Output was truncated.
320 }
321 }
322 return true;
323 }
324
325
PrintName(Object * name)326 void StringStream::PrintName(Object* name) {
327 if (name->IsString()) {
328 String* str = String::cast(name);
329 if (str->length() > 0) {
330 Put(str);
331 } else {
332 Add("/* anonymous */");
333 }
334 } else {
335 Add("%o", name);
336 }
337 }
338
339
PrintUsingMap(JSObject * js_object)340 void StringStream::PrintUsingMap(JSObject* js_object) {
341 Map* map = js_object->map();
342 if (!js_object->GetHeap()->Contains(map) ||
343 !map->IsHeapObject() ||
344 !map->IsMap()) {
345 Add("<Invalid map>\n");
346 return;
347 }
348 int real_size = map->NumberOfOwnDescriptors();
349 DescriptorArray* descs = map->instance_descriptors();
350 for (int i = 0; i < real_size; i++) {
351 PropertyDetails details = descs->GetDetails(i);
352 if (details.type() == DATA) {
353 Object* key = descs->GetKey(i);
354 if (key->IsString() || key->IsNumber()) {
355 int len = 3;
356 if (key->IsString()) {
357 len = String::cast(key)->length();
358 }
359 for (; len < 18; len++)
360 Put(' ');
361 if (key->IsString()) {
362 Put(String::cast(key));
363 } else {
364 key->ShortPrint();
365 }
366 Add(": ");
367 FieldIndex index = FieldIndex::ForDescriptor(map, i);
368 if (js_object->IsUnboxedDoubleField(index)) {
369 double value = js_object->RawFastDoublePropertyAt(index);
370 Add("<unboxed double> %.16g\n", FmtElm(value));
371 } else {
372 Object* value = js_object->RawFastPropertyAt(index);
373 Add("%o\n", value);
374 }
375 }
376 }
377 }
378 }
379
380
PrintFixedArray(FixedArray * array,unsigned int limit)381 void StringStream::PrintFixedArray(FixedArray* array, unsigned int limit) {
382 Isolate* isolate = array->GetIsolate();
383 for (unsigned int i = 0; i < 10 && i < limit; i++) {
384 Object* element = array->get(i);
385 if (element->IsTheHole(isolate)) continue;
386 for (int len = 1; len < 18; len++) {
387 Put(' ');
388 }
389 Add("%d: %o\n", i, array->get(i));
390 }
391 if (limit >= 10) {
392 Add(" ...\n");
393 }
394 }
395
396
PrintByteArray(ByteArray * byte_array)397 void StringStream::PrintByteArray(ByteArray* byte_array) {
398 unsigned int limit = byte_array->length();
399 for (unsigned int i = 0; i < 10 && i < limit; i++) {
400 byte b = byte_array->get(i);
401 Add(" %d: %3d 0x%02x", i, b, b);
402 if (b >= ' ' && b <= '~') {
403 Add(" '%c'", b);
404 } else if (b == '\n') {
405 Add(" '\n'");
406 } else if (b == '\r') {
407 Add(" '\r'");
408 } else if (b >= 1 && b <= 26) {
409 Add(" ^%c", b + 'A' - 1);
410 }
411 Add("\n");
412 }
413 if (limit >= 10) {
414 Add(" ...\n");
415 }
416 }
417
418
PrintMentionedObjectCache(Isolate * isolate)419 void StringStream::PrintMentionedObjectCache(Isolate* isolate) {
420 if (object_print_mode_ == kPrintObjectConcise) return;
421 DebugObjectCache* debug_object_cache =
422 isolate->string_stream_debug_object_cache();
423 Add("==== Key ============================================\n\n");
424 for (int i = 0; i < debug_object_cache->length(); i++) {
425 HeapObject* printee = (*debug_object_cache)[i];
426 Add(" #%d# %p: ", i, printee);
427 printee->ShortPrint(this);
428 Add("\n");
429 if (printee->IsJSObject()) {
430 if (printee->IsJSValue()) {
431 Add(" value(): %o\n", JSValue::cast(printee)->value());
432 }
433 PrintUsingMap(JSObject::cast(printee));
434 if (printee->IsJSArray()) {
435 JSArray* array = JSArray::cast(printee);
436 if (array->HasFastObjectElements()) {
437 unsigned int limit = FixedArray::cast(array->elements())->length();
438 unsigned int length =
439 static_cast<uint32_t>(JSArray::cast(array)->length()->Number());
440 if (length < limit) limit = length;
441 PrintFixedArray(FixedArray::cast(array->elements()), limit);
442 }
443 }
444 } else if (printee->IsByteArray()) {
445 PrintByteArray(ByteArray::cast(printee));
446 } else if (printee->IsFixedArray()) {
447 unsigned int limit = FixedArray::cast(printee)->length();
448 PrintFixedArray(FixedArray::cast(printee), limit);
449 }
450 }
451 }
452
453
PrintSecurityTokenIfChanged(Object * f)454 void StringStream::PrintSecurityTokenIfChanged(Object* f) {
455 if (!f->IsHeapObject()) return;
456 HeapObject* obj = HeapObject::cast(f);
457 Isolate* isolate = obj->GetIsolate();
458 Heap* heap = isolate->heap();
459 if (!heap->Contains(obj)) return;
460 Map* map = obj->map();
461 if (!map->IsHeapObject() ||
462 !heap->Contains(map) ||
463 !map->IsMap() ||
464 !f->IsJSFunction()) {
465 return;
466 }
467
468 JSFunction* fun = JSFunction::cast(f);
469 Object* perhaps_context = fun->context();
470 if (perhaps_context->IsHeapObject() &&
471 heap->Contains(HeapObject::cast(perhaps_context)) &&
472 perhaps_context->IsContext()) {
473 Context* context = fun->context();
474 if (!heap->Contains(context)) {
475 Add("(Function context is outside heap)\n");
476 return;
477 }
478 Object* token = context->native_context()->security_token();
479 if (token != isolate->string_stream_current_security_token()) {
480 Add("Security context: %o\n", token);
481 isolate->set_string_stream_current_security_token(token);
482 }
483 } else {
484 Add("(Function context is corrupt)\n");
485 }
486 }
487
488
PrintFunction(Object * f,Object * receiver,Code ** code)489 void StringStream::PrintFunction(Object* f, Object* receiver, Code** code) {
490 if (!f->IsHeapObject()) {
491 Add("/* warning: 'function' was not a heap object */ ");
492 return;
493 }
494 Heap* heap = HeapObject::cast(f)->GetHeap();
495 if (!heap->Contains(HeapObject::cast(f))) {
496 Add("/* warning: 'function' was not on the heap */ ");
497 return;
498 }
499 if (!heap->Contains(HeapObject::cast(f)->map())) {
500 Add("/* warning: function's map was not on the heap */ ");
501 return;
502 }
503 if (!HeapObject::cast(f)->map()->IsMap()) {
504 Add("/* warning: function's map was not a valid map */ ");
505 return;
506 }
507 if (f->IsJSFunction()) {
508 JSFunction* fun = JSFunction::cast(f);
509 // Common case: on-stack function present and resolved.
510 PrintPrototype(fun, receiver);
511 *code = fun->code();
512 } else if (f->IsInternalizedString()) {
513 // Unresolved and megamorphic calls: Instead of the function
514 // we have the function name on the stack.
515 PrintName(f);
516 Add("/* unresolved */ ");
517 } else {
518 // Unless this is the frame of a built-in function, we should always have
519 // the callee function or name on the stack. If we don't, we have a
520 // problem or a change of the stack frame layout.
521 Add("%o", f);
522 Add("/* warning: no JSFunction object or function name found */ ");
523 }
524 }
525
526
PrintPrototype(JSFunction * fun,Object * receiver)527 void StringStream::PrintPrototype(JSFunction* fun, Object* receiver) {
528 Object* name = fun->shared()->name();
529 bool print_name = false;
530 Isolate* isolate = fun->GetIsolate();
531 if (receiver->IsNull(isolate) || receiver->IsUndefined(isolate) ||
532 receiver->IsTheHole(isolate) || receiver->IsJSProxy()) {
533 print_name = true;
534 } else if (isolate->context() != nullptr) {
535 if (!receiver->IsJSObject()) {
536 receiver = receiver->GetPrototypeChainRootMap(isolate)->prototype();
537 }
538
539 for (PrototypeIterator iter(isolate, JSObject::cast(receiver),
540 kStartAtReceiver);
541 !iter.IsAtEnd(); iter.Advance()) {
542 if (iter.GetCurrent()->IsJSProxy()) break;
543 Object* key = iter.GetCurrent<JSObject>()->SlowReverseLookup(fun);
544 if (!key->IsUndefined(isolate)) {
545 if (!name->IsString() ||
546 !key->IsString() ||
547 !String::cast(name)->Equals(String::cast(key))) {
548 print_name = true;
549 }
550 if (name->IsString() && String::cast(name)->length() == 0) {
551 print_name = false;
552 }
553 name = key;
554 break;
555 }
556 }
557 }
558 PrintName(name);
559 // Also known as - if the name in the function doesn't match the name under
560 // which it was looked up.
561 if (print_name) {
562 Add("(aka ");
563 PrintName(fun->shared()->name());
564 Put(')');
565 }
566 }
567
568
grow(unsigned * bytes)569 char* HeapStringAllocator::grow(unsigned* bytes) {
570 unsigned new_bytes = *bytes * 2;
571 // Check for overflow.
572 if (new_bytes <= *bytes) {
573 return space_;
574 }
575 char* new_space = NewArray<char>(new_bytes);
576 if (new_space == NULL) {
577 return space_;
578 }
579 MemCopy(new_space, space_, *bytes);
580 *bytes = new_bytes;
581 DeleteArray(space_);
582 space_ = new_space;
583 return new_space;
584 }
585
586
587 } // namespace internal
588 } // namespace v8
589