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#include "src/vector.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
26
27char* FixedStringAllocator::allocate(unsigned bytes) {
28 CHECK_LE(bytes, length_);
29 return buffer_;
30}
31
32
33char* FixedStringAllocator::grow(unsigned* old) {
34 *old = length_;
35 return buffer_;
36}
37
38
39bool StringStream::Put(char c) {
40 if (full()) return false;
41 DCHECK(length_ < capacity_);
42 // Since the trailing '\0' is not accounted for in length_ fullness is
43 // indicated by a difference of 1 between length_ and capacity_. Thus when
44 // reaching a difference of 2 we need to grow the buffer.
45 if (length_ == capacity_ - 2) {
46 unsigned new_capacity = capacity_;
47 char* new_buffer = allocator_->grow(&new_capacity);
48 if (new_capacity > capacity_) {
49 capacity_ = new_capacity;
50 buffer_ = new_buffer;
51 } else {
52 // Reached the end of the available buffer.
53 DCHECK_GE(capacity_, 5);
54 length_ = capacity_ - 1; // Indicate fullness of the stream.
55 buffer_[length_ - 4] = '.';
56 buffer_[length_ - 3] = '.';
57 buffer_[length_ - 2] = '.';
58 buffer_[length_ - 1] = '\n';
59 buffer_[length_] = '\0';
60 return false;
61 }
62 }
63 buffer_[length_] = c;
64 buffer_[length_ + 1] = '\0';
65 length_++;
66 return true;
67}
68
69
70// A control character is one that configures a format element. For
71// instance, in %.5s, .5 are control characters.
72static bool IsControlChar(char c) {
73 switch (c) {
74 case '0': case '1': case '2': case '3': case '4': case '5':
75 case '6': case '7': case '8': case '9': case '.': case '-':
76 return true;
77 default:
78 return false;
79 }
80}
81
82
83void StringStream::Add(Vector<const char> format, Vector<FmtElm> elms) {
84 // If we already ran out of space then return immediately.
85 if (full()) return;
86 int offset = 0;
87 int elm = 0;
88 while (offset < format.length()) {
89 if (format[offset] != '%' || elm == elms.length()) {
90 Put(format[offset]);
91 offset++;
92 continue;
93 }
94 // Read this formatting directive into a temporary buffer
95 EmbeddedVector<char, 24> temp;
96 int format_length = 0;
97 // Skip over the whole control character sequence until the
98 // format element type
99 temp[format_length++] = format[offset++];
100 while (offset < format.length() && IsControlChar(format[offset]))
101 temp[format_length++] = format[offset++];
102 if (offset >= format.length())
103 return;
104 char type = format[offset];
105 temp[format_length++] = type;
106 temp[format_length] = '\0';
107 offset++;
108 FmtElm current = elms[elm++];
109 switch (type) {
110 case 's': {
111 DCHECK_EQ(FmtElm::C_STR, current.type_);
112 const char* value = current.data_.u_c_str_;
113 Add(value);
114 break;
115 }
116 case 'w': {
117 DCHECK_EQ(FmtElm::LC_STR, current.type_);
118 Vector<const uc16> value = *current.data_.u_lc_str_;
119 for (int i = 0; i < value.length(); i++)
120 Put(static_cast<char>(value[i]));
121 break;
122 }
123 case 'o': {
124 DCHECK_EQ(FmtElm::OBJ, current.type_);
125 Object obj(current.data_.u_obj_);
126 PrintObject(obj);
127 break;
128 }
129 case 'k': {
130 DCHECK_EQ(FmtElm::INT, current.type_);
131 int value = current.data_.u_int_;
132 if (0x20 <= value && value <= 0x7F) {
133 Put(value);
134 } else if (value <= 0xFF) {
135 Add("\\x%02x", value);
136 } else {
137 Add("\\u%04x", value);
138 }
139 break;
140 }
141 case 'i': case 'd': case 'u': case 'x': case 'c': case 'X': {
142 int value = current.data_.u_int_;
143 EmbeddedVector<char, 24> formatted;
144 int length = SNPrintF(formatted, temp.start(), value);
145 Add(Vector<const char>(formatted.start(), length));
146 break;
147 }
148 case 'f': case 'g': case 'G': case 'e': case 'E': {
149 double value = current.data_.u_double_;
150 int inf = std::isinf(value);
151 if (inf == -1) {
152 Add("-inf");
153 } else if (inf == 1) {
154 Add("inf");
155 } else if (std::isnan(value)) {
156 Add("nan");
157 } else {
158 EmbeddedVector<char, 28> formatted;
159 SNPrintF(formatted, temp.start(), value);
160 Add(formatted.start());
161 }
162 break;
163 }
164 case 'p': {
165 void* value = current.data_.u_pointer_;
166 EmbeddedVector<char, 20> formatted;
167 SNPrintF(formatted, temp.start(), value);
168 Add(formatted.start());
169 break;
170 }
171 default:
172 UNREACHABLE();
173 break;
174 }
175 }
176
177 // Verify that the buffer is 0-terminated
178 DCHECK_EQ(buffer_[length_], '\0');
179}
180
181void 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 Isolate* isolate = Isolate::Current();
194 DebugObjectCache* debug_object_cache =
195 isolate->string_stream_debug_object_cache();
196 for (size_t i = 0; i < debug_object_cache->size(); i++) {
197 if (*(*debug_object_cache)[i] == o) {
198 Add("#%d#", static_cast<int>(i));
199 return;
200 }
201 }
202 if (debug_object_cache->size() < kMentionedObjectCacheMaxSize) {
203 Add("#%d#", static_cast<int>(debug_object_cache->size()));
204 debug_object_cache->push_back(handle(HeapObject::cast(o), isolate));
205 } else {
206 Add("@%p", o);
207 }
208 }
209}
210
211std::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
219void StringStream::Log(Isolate* isolate) {
220 LOG(isolate, StringEvent("StackDump", buffer_));
221}
222
223
224void 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
240Handle<String> StringStream::ToString(Isolate* isolate) {
241 return isolate->factory()->NewStringFromUtf8(
242 Vector<const char>(buffer_, length_)).ToHandleChecked();
243}
244
245
246void StringStream::ClearMentionedObjectCache(Isolate* isolate) {
247 isolate->set_string_stream_current_security_token(Object());
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
256bool StringStream::IsMentionedObjectCacheClear(Isolate* isolate) {
257 return object_print_mode_ == kPrintObjectConcise ||
258 isolate->string_stream_debug_object_cache()->size() == 0;
259}
260#endif
261
262bool StringStream::Put(String str) { return Put(str, 0, str->length()); }
263
264bool 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
278void 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
291void StringStream::PrintUsingMap(JSObject js_object) {
292 Map map = js_object->map();
293 int real_size = map->NumberOfOwnDescriptors();
294 DescriptorArray descs = map->instance_descriptors();
295 for (int i = 0; i < real_size; i++) {
296 PropertyDetails details = descs->GetDetails(i);
297 if (details.location() == kField) {
298 DCHECK_EQ(kData, details.kind());
299 Object key = descs->GetKey(i);
300 if (key->IsString() || key->IsNumber()) {
301 int len = 3;
302 if (key->IsString()) {
303 len = String::cast(key)->length();
304 }
305 for (; len < 18; len++)
306 Put(' ');
307 if (key->IsString()) {
308 Put(String::cast(key));
309 } else {
310 key->ShortPrint();
311 }
312 Add(": ");
313 FieldIndex index = FieldIndex::ForDescriptor(map, i);
314 if (js_object->IsUnboxedDoubleField(index)) {
315 double value = js_object->RawFastDoublePropertyAt(index);
316 Add("<unboxed double> %.16g\n", FmtElm(value));
317 } else {
318 Object value = js_object->RawFastPropertyAt(index);
319 Add("%o\n", value);
320 }
321 }
322 }
323 }
324}
325
326void StringStream::PrintFixedArray(FixedArray array, unsigned int limit) {
327 ReadOnlyRoots roots = array->GetReadOnlyRoots();
328 for (unsigned int i = 0; i < 10 && i < limit; i++) {
329 Object element = array->get(i);
330 if (element->IsTheHole(roots)) continue;
331 for (int len = 1; len < 18; len++) {
332 Put(' ');
333 }
334 Add("%d: %o\n", i, array->get(i));
335 }
336 if (limit >= 10) {
337 Add(" ...\n");
338 }
339}
340
341void StringStream::PrintByteArray(ByteArray byte_array) {
342 unsigned int limit = byte_array->length();
343 for (unsigned int i = 0; i < 10 && i < limit; i++) {
344 byte b = byte_array->get(i);
345 Add(" %d: %3d 0x%02x", i, b, b);
346 if (b >= ' ' && b <= '~') {
347 Add(" '%c'", b);
348 } else if (b == '\n') {
349 Add(" '\n'");
350 } else if (b == '\r') {
351 Add(" '\r'");
352 } else if (b >= 1 && b <= 26) {
353 Add(" ^%c", b + 'A' - 1);
354 }
355 Add("\n");
356 }
357 if (limit >= 10) {
358 Add(" ...\n");
359 }
360}
361
362void StringStream::PrintMentionedObjectCache(Isolate* isolate) {
363 if (object_print_mode_ == kPrintObjectConcise) return;
364 DebugObjectCache* debug_object_cache =
365 isolate->string_stream_debug_object_cache();
366 Add("==== Key ============================================\n\n");
367 for (size_t i = 0; i < debug_object_cache->size(); i++) {
368 HeapObject printee = *(*debug_object_cache)[i];
369 Add(" #%d# %p: ", static_cast<int>(i),
370 reinterpret_cast<void*>(printee->ptr()));
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
397void 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
406void StringStream::PrintFunction(JSFunction fun, Object receiver, Code* code) {
407 PrintPrototype(fun, receiver);
408 *code = fun->code();
409}
410
411void StringStream::PrintPrototype(JSFunction fun, Object receiver) {
412 Object name = fun->shared()->Name();
413 bool print_name = false;
414 Isolate* isolate = fun->GetIsolate();
415 if (receiver->IsNullOrUndefined(isolate) || receiver->IsTheHole(isolate) ||
416 receiver->IsJSProxy()) {
417 print_name = true;
418 } else if (!isolate->context().is_null()) {
419 if (!receiver->IsJSObject()) {
420 receiver = receiver->GetPrototypeChainRootMap(isolate)->prototype();
421 }
422
423 for (PrototypeIterator iter(isolate, JSObject::cast(receiver),
424 kStartAtReceiver);
425 !iter.IsAtEnd(); iter.Advance()) {
426 if (iter.GetCurrent()->IsJSProxy()) break;
427 Object key = iter.GetCurrent<JSObject>()->SlowReverseLookup(fun);
428 if (!key->IsUndefined(isolate)) {
429 if (!name->IsString() ||
430 !key->IsString() ||
431 !String::cast(name)->Equals(String::cast(key))) {
432 print_name = true;
433 }
434 if (name->IsString() && String::cast(name)->length() == 0) {
435 print_name = false;
436 }
437 name = key;
438 break;
439 }
440 }
441 }
442 PrintName(name);
443 // Also known as - if the name in the function doesn't match the name under
444 // which it was looked up.
445 if (print_name) {
446 Add("(aka ");
447 PrintName(fun->shared()->Name());
448 Put(')');
449 }
450}
451
452char* HeapStringAllocator::grow(unsigned* bytes) {
453 unsigned new_bytes = *bytes * 2;
454 // Check for overflow.
455 if (new_bytes <= *bytes) {
456 return space_;
457 }
458 char* new_space = NewArray<char>(new_bytes);
459 if (new_space == nullptr) {
460 return space_;
461 }
462 MemCopy(new_space, space_, *bytes);
463 *bytes = new_bytes;
464 DeleteArray(space_);
465 space_ = new_space;
466 return new_space;
467}
468
469
470} // namespace internal
471} // namespace v8
472