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