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
16namespace v8 {
17namespace internal {
18
19static const int kMentionedObjectCacheMaxSize = 256;
20
21char* HeapStringAllocator::allocate(unsigned bytes) {
22  space_ = NewArray<char>(bytes);
23  return space_;
24}
25
26char* FixedStringAllocator::allocate(unsigned bytes) {
27  CHECK_LE(bytes, length_);
28  return buffer_;
29}
30
31char* FixedStringAllocator::grow(unsigned* old) {
32  *old = length_;
33  return buffer_;
34}
35
36bool 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.
68static 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
88void 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
194void 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
224std::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
231void StringStream::Log(Isolate* isolate) {
232  LOG(isolate, StringEvent("StackDump", buffer_));
233}
234
235void 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
250Handle<String> StringStream::ToString(Isolate* isolate) {
251  return isolate->factory()
252      ->NewStringFromUtf8(base::Vector<const char>(buffer_, length_))
253      .ToHandleChecked();
254}
255
256void 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
265bool StringStream::IsMentionedObjectCacheClear(Isolate* isolate) {
266  return object_print_mode_ == kPrintObjectConcise ||
267         isolate->string_stream_debug_object_cache()->size() == 0;
268}
269#endif
270
271bool StringStream::Put(String str) { return Put(str, 0, str.length()); }
272
273bool 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
287void 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
300void 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
328void 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
343void 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
364void 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
400void 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
409void StringStream::PrintFunction(JSFunction fun, Object receiver, Code* code) {
410  PrintPrototype(fun, receiver);
411  *code = FromCodeT(fun.code());
412}
413
414void 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
454char* 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