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