• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/strings/string-stream.h"
6 
7 #include <memory>
8 
9 #include "src/base/vector.h"
10 #include "src/handles/handles-inl.h"
11 #include "src/logging/log.h"
12 #include "src/objects/js-array-inl.h"
13 #include "src/objects/objects-inl.h"
14 #include "src/objects/prototype.h"
15 
16 namespace v8 {
17 namespace internal {
18 
19 static const int kMentionedObjectCacheMaxSize = 256;
20 
allocate(unsigned bytes)21 char* HeapStringAllocator::allocate(unsigned bytes) {
22   space_ = NewArray<char>(bytes);
23   return space_;
24 }
25 
allocate(unsigned bytes)26 char* FixedStringAllocator::allocate(unsigned bytes) {
27   CHECK_LE(bytes, length_);
28   return buffer_;
29 }
30 
grow(unsigned * old)31 char* FixedStringAllocator::grow(unsigned* old) {
32   *old = length_;
33   return buffer_;
34 }
35 
Put(char c)36 bool StringStream::Put(char c) {
37   if (full()) return false;
38   DCHECK(length_ < capacity_);
39   // Since the trailing '\0' is not accounted for in length_ fullness is
40   // indicated by a difference of 1 between length_ and capacity_. Thus when
41   // reaching a difference of 2 we need to grow the buffer.
42   if (length_ == capacity_ - 2) {
43     unsigned new_capacity = capacity_;
44     char* new_buffer = allocator_->grow(&new_capacity);
45     if (new_capacity > capacity_) {
46       capacity_ = new_capacity;
47       buffer_ = new_buffer;
48     } else {
49       // Reached the end of the available buffer.
50       DCHECK_GE(capacity_, 5);
51       length_ = capacity_ - 1;  // Indicate fullness of the stream.
52       buffer_[length_ - 4] = '.';
53       buffer_[length_ - 3] = '.';
54       buffer_[length_ - 2] = '.';
55       buffer_[length_ - 1] = '\n';
56       buffer_[length_] = '\0';
57       return false;
58     }
59   }
60   buffer_[length_] = c;
61   buffer_[length_ + 1] = '\0';
62   length_++;
63   return true;
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':
71     case '1':
72     case '2':
73     case '3':
74     case '4':
75     case '5':
76     case '6':
77     case '7':
78     case '8':
79     case '9':
80     case '.':
81     case '-':
82       return true;
83     default:
84       return false;
85   }
86 }
87 
Add(base::Vector<const char> format,base::Vector<FmtElm> elms)88 void StringStream::Add(base::Vector<const char> format,
89                        base::Vector<FmtElm> elms) {
90   // If we already ran out of space then return immediately.
91   if (full()) return;
92   int offset = 0;
93   int elm = 0;
94   while (offset < format.length()) {
95     if (format[offset] != '%' || elm == elms.length()) {
96       Put(format[offset]);
97       offset++;
98       continue;
99     }
100     // Read this formatting directive into a temporary buffer
101     base::EmbeddedVector<char, 24> temp;
102     int format_length = 0;
103     // Skip over the whole control character sequence until the
104     // format element type
105     temp[format_length++] = format[offset++];
106     while (offset < format.length() && IsControlChar(format[offset]))
107       temp[format_length++] = format[offset++];
108     if (offset >= format.length()) return;
109     char type = format[offset];
110     temp[format_length++] = type;
111     temp[format_length] = '\0';
112     offset++;
113     FmtElm current = elms[elm++];
114     switch (type) {
115       case 's': {
116         DCHECK_EQ(FmtElm::C_STR, current.type_);
117         const char* value = current.data_.u_c_str_;
118         Add(value);
119         break;
120       }
121       case 'w': {
122         DCHECK_EQ(FmtElm::LC_STR, current.type_);
123         base::Vector<const base::uc16> value = *current.data_.u_lc_str_;
124         for (int i = 0; i < value.length(); i++)
125           Put(static_cast<char>(value[i]));
126         break;
127       }
128       case 'o': {
129         DCHECK_EQ(FmtElm::OBJ, current.type_);
130         Object obj(current.data_.u_obj_);
131         PrintObject(obj);
132         break;
133       }
134       case 'k': {
135         DCHECK_EQ(FmtElm::INT, current.type_);
136         int value = current.data_.u_int_;
137         if (0x20 <= value && value <= 0x7F) {
138           Put(value);
139         } else if (value <= 0xFF) {
140           Add("\\x%02x", value);
141         } else {
142           Add("\\u%04x", value);
143         }
144         break;
145       }
146       case 'i':
147       case 'd':
148       case 'u':
149       case 'x':
150       case 'c':
151       case 'X': {
152         int value = current.data_.u_int_;
153         base::EmbeddedVector<char, 24> formatted;
154         int length = SNPrintF(formatted, temp.begin(), value);
155         Add(base::Vector<const char>(formatted.begin(), length));
156         break;
157       }
158       case 'f':
159       case 'g':
160       case 'G':
161       case 'e':
162       case 'E': {
163         double value = current.data_.u_double_;
164         int inf = std::isinf(value);
165         if (inf == -1) {
166           Add("-inf");
167         } else if (inf == 1) {
168           Add("inf");
169         } else if (std::isnan(value)) {
170           Add("nan");
171         } else {
172           base::EmbeddedVector<char, 28> formatted;
173           SNPrintF(formatted, temp.begin(), value);
174           Add(formatted.begin());
175         }
176         break;
177       }
178       case 'p': {
179         void* value = current.data_.u_pointer_;
180         base::EmbeddedVector<char, 20> formatted;
181         SNPrintF(formatted, temp.begin(), value);
182         Add(formatted.begin());
183         break;
184       }
185       default:
186         UNREACHABLE();
187     }
188   }
189 
190   // Verify that the buffer is 0-terminated
191   DCHECK_EQ(buffer_[length_], '\0');
192 }
193 
PrintObject(Object o)194 void StringStream::PrintObject(Object o) {
195   o.ShortPrint(this);
196   if (o.IsString()) {
197     if (String::cast(o).length() <= String::kMaxShortPrintLength) {
198       return;
199     }
200   } else if (o.IsNumber() || o.IsOddball()) {
201     return;
202   }
203   if (o.IsHeapObject() && object_print_mode_ == kPrintObjectVerbose) {
204     // TODO(delphick): Consider whether we can get the isolate without using
205     // TLS.
206     Isolate* isolate = Isolate::Current();
207     DebugObjectCache* debug_object_cache =
208         isolate->string_stream_debug_object_cache();
209     for (size_t i = 0; i < debug_object_cache->size(); i++) {
210       if (*(*debug_object_cache)[i] == o) {
211         Add("#%d#", static_cast<int>(i));
212         return;
213       }
214     }
215     if (debug_object_cache->size() < kMentionedObjectCacheMaxSize) {
216       Add("#%d#", static_cast<int>(debug_object_cache->size()));
217       debug_object_cache->push_back(handle(HeapObject::cast(o), isolate));
218     } else {
219       Add("@%p", o);
220     }
221   }
222 }
223 
ToCString() const224 std::unique_ptr<char[]> StringStream::ToCString() const {
225   char* str = NewArray<char>(length_ + 1);
226   MemCopy(str, buffer_, length_);
227   str[length_] = '\0';
228   return std::unique_ptr<char[]>(str);
229 }
230 
Log(Isolate * isolate)231 void StringStream::Log(Isolate* isolate) {
232   LOG(isolate, StringEvent("StackDump", buffer_));
233 }
234 
OutputToFile(FILE * out)235 void StringStream::OutputToFile(FILE* out) {
236   // Dump the output to stdout, but make sure to break it up into
237   // manageable chunks to avoid losing parts of the output in the OS
238   // printing code. This is a problem on Windows in particular; see
239   // the VPrint() function implementations in platform-win32.cc.
240   unsigned position = 0;
241   for (unsigned next; (next = position + 2048) < length_; position = next) {
242     char save = buffer_[next];
243     buffer_[next] = '\0';
244     internal::PrintF(out, "%s", &buffer_[position]);
245     buffer_[next] = save;
246   }
247   internal::PrintF(out, "%s", &buffer_[position]);
248 }
249 
ToString(Isolate * isolate)250 Handle<String> StringStream::ToString(Isolate* isolate) {
251   return isolate->factory()
252       ->NewStringFromUtf8(base::Vector<const char>(buffer_, length_))
253       .ToHandleChecked();
254 }
255 
ClearMentionedObjectCache(Isolate * isolate)256 void StringStream::ClearMentionedObjectCache(Isolate* isolate) {
257   isolate->set_string_stream_current_security_token(Object());
258   if (isolate->string_stream_debug_object_cache() == nullptr) {
259     isolate->set_string_stream_debug_object_cache(new DebugObjectCache());
260   }
261   isolate->string_stream_debug_object_cache()->clear();
262 }
263 
264 #ifdef DEBUG
IsMentionedObjectCacheClear(Isolate * isolate)265 bool StringStream::IsMentionedObjectCacheClear(Isolate* isolate) {
266   return object_print_mode_ == kPrintObjectConcise ||
267          isolate->string_stream_debug_object_cache()->size() == 0;
268 }
269 #endif
270 
Put(String str)271 bool StringStream::Put(String str) { return Put(str, 0, str.length()); }
272 
Put(String str,int start,int end)273 bool StringStream::Put(String str, int start, int end) {
274   StringCharacterStream stream(str, start);
275   for (int i = start; i < end && stream.HasMore(); i++) {
276     uint16_t c = stream.GetNext();
277     if (c >= 127 || c < 32) {
278       c = '?';
279     }
280     if (!Put(static_cast<char>(c))) {
281       return false;  // Output was truncated.
282     }
283   }
284   return true;
285 }
286 
PrintName(Object name)287 void StringStream::PrintName(Object name) {
288   if (name.IsString()) {
289     String str = String::cast(name);
290     if (str.length() > 0) {
291       Put(str);
292     } else {
293       Add("/* anonymous */");
294     }
295   } else {
296     Add("%o", name);
297   }
298 }
299 
PrintUsingMap(JSObject js_object)300 void StringStream::PrintUsingMap(JSObject js_object) {
301   Map map = js_object.map();
302   DescriptorArray descs = map.instance_descriptors(js_object.GetIsolate());
303   for (InternalIndex i : map.IterateOwnDescriptors()) {
304     PropertyDetails details = descs.GetDetails(i);
305     if (details.location() == PropertyLocation::kField) {
306       DCHECK_EQ(PropertyKind::kData, details.kind());
307       Object key = descs.GetKey(i);
308       if (key.IsString() || key.IsNumber()) {
309         int len = 3;
310         if (key.IsString()) {
311           len = String::cast(key).length();
312         }
313         for (; len < 18; len++) Put(' ');
314         if (key.IsString()) {
315           Put(String::cast(key));
316         } else {
317           key.ShortPrint();
318         }
319         Add(": ");
320         FieldIndex index = FieldIndex::ForDescriptor(map, i);
321         Object value = js_object.RawFastPropertyAt(index);
322         Add("%o\n", value);
323       }
324     }
325   }
326 }
327 
PrintFixedArray(FixedArray array,unsigned int limit)328 void StringStream::PrintFixedArray(FixedArray array, unsigned int limit) {
329   ReadOnlyRoots roots = array.GetReadOnlyRoots();
330   for (unsigned int i = 0; i < 10 && i < limit; i++) {
331     Object element = array.get(i);
332     if (element.IsTheHole(roots)) continue;
333     for (int len = 1; len < 18; len++) {
334       Put(' ');
335     }
336     Add("%d: %o\n", i, array.get(i));
337   }
338   if (limit >= 10) {
339     Add("                  ...\n");
340   }
341 }
342 
PrintByteArray(ByteArray byte_array)343 void StringStream::PrintByteArray(ByteArray byte_array) {
344   unsigned int limit = byte_array.length();
345   for (unsigned int i = 0; i < 10 && i < limit; i++) {
346     byte b = byte_array.get(i);
347     Add("             %d: %3d 0x%02x", i, b, b);
348     if (b >= ' ' && b <= '~') {
349       Add(" '%c'", b);
350     } else if (b == '\n') {
351       Add(" '\n'");
352     } else if (b == '\r') {
353       Add(" '\r'");
354     } else if (b >= 1 && b <= 26) {
355       Add(" ^%c", b + 'A' - 1);
356     }
357     Add("\n");
358   }
359   if (limit >= 10) {
360     Add("                  ...\n");
361   }
362 }
363 
PrintMentionedObjectCache(Isolate * isolate)364 void StringStream::PrintMentionedObjectCache(Isolate* isolate) {
365   if (object_print_mode_ == kPrintObjectConcise) return;
366   DebugObjectCache* debug_object_cache =
367       isolate->string_stream_debug_object_cache();
368   Add("-- ObjectCacheKey --\n\n");
369   for (size_t i = 0; i < debug_object_cache->size(); i++) {
370     HeapObject printee = *(*debug_object_cache)[i];
371     Add(" #%d# %p: ", static_cast<int>(i),
372         reinterpret_cast<void*>(printee.ptr()));
373     printee.ShortPrint(this);
374     Add("\n");
375     if (printee.IsJSObject()) {
376       if (printee.IsJSPrimitiveWrapper()) {
377         Add("           value(): %o\n",
378             JSPrimitiveWrapper::cast(printee).value());
379       }
380       PrintUsingMap(JSObject::cast(printee));
381       if (printee.IsJSArray()) {
382         JSArray array = JSArray::cast(printee);
383         if (array.HasObjectElements()) {
384           unsigned int limit = FixedArray::cast(array.elements()).length();
385           unsigned int length =
386               static_cast<uint32_t>(JSArray::cast(array).length().Number());
387           if (length < limit) limit = length;
388           PrintFixedArray(FixedArray::cast(array.elements()), limit);
389         }
390       }
391     } else if (printee.IsByteArray()) {
392       PrintByteArray(ByteArray::cast(printee));
393     } else if (printee.IsFixedArray()) {
394       unsigned int limit = FixedArray::cast(printee).length();
395       PrintFixedArray(FixedArray::cast(printee), limit);
396     }
397   }
398 }
399 
PrintSecurityTokenIfChanged(JSFunction fun)400 void StringStream::PrintSecurityTokenIfChanged(JSFunction fun) {
401   Object token = fun.native_context().security_token();
402   Isolate* isolate = fun.GetIsolate();
403   if (token != isolate->string_stream_current_security_token()) {
404     Add("Security context: %o\n", token);
405     isolate->set_string_stream_current_security_token(token);
406   }
407 }
408 
PrintFunction(JSFunction fun,Object receiver,Code * code)409 void StringStream::PrintFunction(JSFunction fun, Object receiver, Code* code) {
410   PrintPrototype(fun, receiver);
411   *code = FromCodeT(fun.code());
412 }
413 
PrintPrototype(JSFunction fun,Object receiver)414 void StringStream::PrintPrototype(JSFunction fun, Object receiver) {
415   Object name = fun.shared().Name();
416   bool print_name = false;
417   Isolate* isolate = fun.GetIsolate();
418   if (receiver.IsNullOrUndefined(isolate) || receiver.IsTheHole(isolate) ||
419       receiver.IsJSProxy()) {
420     print_name = true;
421   } else if (!isolate->context().is_null()) {
422     if (!receiver.IsJSObject()) {
423       receiver = receiver.GetPrototypeChainRootMap(isolate).prototype();
424     }
425 
426     for (PrototypeIterator iter(isolate, JSObject::cast(receiver),
427                                 kStartAtReceiver);
428          !iter.IsAtEnd(); iter.Advance()) {
429       if (iter.GetCurrent().IsJSProxy()) break;
430       Object key = iter.GetCurrent<JSObject>().SlowReverseLookup(fun);
431       if (!key.IsUndefined(isolate)) {
432         if (!name.IsString() || !key.IsString() ||
433             !String::cast(name).Equals(String::cast(key))) {
434           print_name = true;
435         }
436         if (name.IsString() && String::cast(name).length() == 0) {
437           print_name = false;
438         }
439         name = key;
440         break;
441       }
442     }
443   }
444   PrintName(name);
445   // Also known as - if the name in the function doesn't match the name under
446   // which it was looked up.
447   if (print_name) {
448     Add("(aka ");
449     PrintName(fun.shared().Name());
450     Put(')');
451   }
452 }
453 
grow(unsigned * bytes)454 char* HeapStringAllocator::grow(unsigned* bytes) {
455   unsigned new_bytes = *bytes * 2;
456   // Check for overflow.
457   if (new_bytes <= *bytes) {
458     return space_;
459   }
460   char* new_space = NewArray<char>(new_bytes);
461   if (new_space == nullptr) {
462     return space_;
463   }
464   MemCopy(new_space, space_, *bytes);
465   *bytes = new_bytes;
466   DeleteArray(space_);
467   space_ = new_space;
468   return new_space;
469 }
470 
471 }  // namespace internal
472 }  // namespace v8
473