Line data Source code
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 :
16 : namespace v8 {
17 : namespace internal {
18 :
19 : static const int kMentionedObjectCacheMaxSize = 256;
20 :
21 89433 : char* HeapStringAllocator::allocate(unsigned bytes) {
22 89433 : space_ = NewArray<char>(bytes);
23 89433 : return space_;
24 : }
25 :
26 :
27 15 : char* FixedStringAllocator::allocate(unsigned bytes) {
28 15 : CHECK_LE(bytes, length_);
29 15 : return buffer_;
30 : }
31 :
32 :
33 0 : char* FixedStringAllocator::grow(unsigned* old) {
34 0 : *old = length_;
35 0 : return buffer_;
36 : }
37 :
38 :
39 5227899 : bool StringStream::Put(char c) {
40 5227899 : 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 5227899 : if (length_ == capacity_ - 2) {
46 205892 : unsigned new_capacity = capacity_;
47 205892 : char* new_buffer = allocator_->grow(&new_capacity);
48 205892 : if (new_capacity > capacity_) {
49 205892 : capacity_ = new_capacity;
50 205892 : buffer_ = new_buffer;
51 : } else {
52 : // Reached the end of the available buffer.
53 : DCHECK_GE(capacity_, 5);
54 0 : length_ = capacity_ - 1; // Indicate fullness of the stream.
55 0 : buffer_[length_ - 4] = '.';
56 0 : buffer_[length_ - 3] = '.';
57 0 : buffer_[length_ - 2] = '.';
58 0 : buffer_[length_ - 1] = '\n';
59 0 : buffer_[length_] = '\0';
60 0 : return false;
61 : }
62 : }
63 5227899 : buffer_[length_] = c;
64 5227899 : buffer_[length_ + 1] = '\0';
65 5227899 : length_++;
66 5227899 : 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.
72 : static 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 :
83 888615 : void StringStream::Add(Vector<const char> format, Vector<FmtElm> elms) {
84 : // If we already ran out of space then return immediately.
85 888615 : if (full()) return;
86 : int offset = 0;
87 : int elm = 0;
88 6232775 : while (offset < format.length()) {
89 11400933 : if (format[offset] != '%' || elm == elms.length()) {
90 4631555 : Put(format[offset]);
91 4631555 : offset++;
92 4631555 : 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 712605 : temp[format_length++] = format[offset++];
100 3668859 : while (offset < format.length() && IsControlChar(format[offset]))
101 2243649 : temp[format_length++] = format[offset++];
102 712605 : if (offset >= format.length())
103 0 : return;
104 1425210 : char type = format[offset];
105 1425210 : temp[format_length++] = type;
106 1425210 : temp[format_length] = '\0';
107 712605 : offset++;
108 1425210 : FmtElm current = elms[elm++];
109 712605 : switch (type) {
110 : case 's': {
111 : DCHECK_EQ(FmtElm::C_STR, current.type_);
112 210714 : const char* value = current.data_.u_c_str_;
113 210714 : Add(value);
114 210714 : break;
115 : }
116 : case 'w': {
117 : DCHECK_EQ(FmtElm::LC_STR, current.type_);
118 0 : Vector<const uc16> value = *current.data_.u_lc_str_;
119 0 : for (int i = 0; i < value.length(); i++)
120 0 : Put(static_cast<char>(value[i]));
121 : break;
122 : }
123 : case 'o': {
124 : DCHECK_EQ(FmtElm::OBJ, current.type_);
125 556 : Object obj(current.data_.u_obj_);
126 556 : PrintObject(obj);
127 : break;
128 : }
129 : case 'k': {
130 : DCHECK_EQ(FmtElm::INT, current.type_);
131 0 : int value = current.data_.u_int_;
132 0 : if (0x20 <= value && value <= 0x7F) {
133 0 : Put(value);
134 0 : } else if (value <= 0xFF) {
135 0 : Add("\\x%02x", value);
136 : } else {
137 0 : Add("\\u%04x", value);
138 : }
139 : break;
140 : }
141 : case 'i': case 'd': case 'u': case 'x': case 'c': case 'X': {
142 468427 : int value = current.data_.u_int_;
143 : EmbeddedVector<char, 24> formatted;
144 468427 : int length = SNPrintF(formatted, temp.start(), value);
145 936854 : Add(Vector<const char>(formatted.start(), length));
146 : break;
147 : }
148 : case 'f': case 'g': case 'G': case 'e': case 'E': {
149 0 : double value = current.data_.u_double_;
150 0 : int inf = std::isinf(value);
151 0 : if (inf == -1) {
152 0 : Add("-inf");
153 0 : } else if (inf == 1) {
154 0 : Add("inf");
155 0 : } else if (std::isnan(value)) {
156 0 : Add("nan");
157 : } else {
158 : EmbeddedVector<char, 28> formatted;
159 0 : SNPrintF(formatted, temp.start(), value);
160 0 : Add(formatted.start());
161 : }
162 : break;
163 : }
164 : case 'p': {
165 32908 : void* value = current.data_.u_pointer_;
166 : EmbeddedVector<char, 20> formatted;
167 32908 : SNPrintF(formatted, temp.start(), value);
168 32908 : Add(formatted.start());
169 : break;
170 : }
171 : default:
172 0 : UNREACHABLE();
173 : break;
174 : }
175 : }
176 :
177 : // Verify that the buffer is 0-terminated
178 : DCHECK_EQ(buffer_[length_], '\0');
179 : }
180 :
181 556 : void StringStream::PrintObject(Object o) {
182 556 : o->ShortPrint(this);
183 556 : if (o->IsString()) {
184 49 : if (String::cast(o)->length() <= String::kMaxShortPrintLength) {
185 : return;
186 : }
187 886 : } else if (o->IsNumber() || o->IsOddball()) {
188 : return;
189 : }
190 344 : 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 7890 : for (size_t i = 0; i < debug_object_cache->size(); i++) {
197 3864 : if (*(*debug_object_cache)[i] == o) {
198 91 : Add("#%d#", static_cast<int>(i));
199 91 : return;
200 : }
201 : }
202 253 : if (debug_object_cache->size() < kMentionedObjectCacheMaxSize) {
203 253 : Add("#%d#", static_cast<int>(debug_object_cache->size()));
204 506 : debug_object_cache->push_back(handle(HeapObject::cast(o), isolate));
205 : } else {
206 0 : Add("@%p", o);
207 : }
208 : }
209 : }
210 :
211 89416 : std::unique_ptr<char[]> StringStream::ToCString() const {
212 89416 : char* str = NewArray<char>(length_ + 1);
213 89416 : MemCopy(str, buffer_, length_);
214 89416 : str[length_] = '\0';
215 89416 : return std::unique_ptr<char[]>(str);
216 : }
217 :
218 :
219 17 : void StringStream::Log(Isolate* isolate) {
220 17 : LOG(isolate, StringEvent("StackDump", buffer_));
221 17 : }
222 :
223 :
224 17 : 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 75 : for (unsigned next; (next = position + 2048) < length_; position = next) {
231 29 : char save = buffer_[next];
232 29 : buffer_[next] = '\0';
233 29 : internal::PrintF(out, "%s", &buffer_[position]);
234 29 : buffer_[next] = save;
235 : }
236 17 : internal::PrintF(out, "%s", &buffer_[position]);
237 17 : }
238 :
239 :
240 0 : Handle<String> StringStream::ToString(Isolate* isolate) {
241 0 : return isolate->factory()->NewStringFromUtf8(
242 0 : Vector<const char>(buffer_, length_)).ToHandleChecked();
243 : }
244 :
245 :
246 23 : void StringStream::ClearMentionedObjectCache(Isolate* isolate) {
247 : isolate->set_string_stream_current_security_token(Object());
248 23 : if (isolate->string_stream_debug_object_cache() == nullptr) {
249 15 : isolate->set_string_stream_debug_object_cache(new DebugObjectCache());
250 : }
251 : isolate->string_stream_debug_object_cache()->clear();
252 23 : }
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 55862 : bool StringStream::Put(String str) { return Put(str, 0, str->length()); }
263 :
264 28114 : bool StringStream::Put(String str, int start, int end) {
265 : StringCharacterStream stream(str, start);
266 403364 : for (int i = start; i < end && stream.HasMore(); i++) {
267 187625 : uint16_t c = stream.GetNext();
268 187625 : if (c >= 127 || c < 32) {
269 : c = '?';
270 : }
271 187625 : if (!Put(static_cast<char>(c))) {
272 : return false; // Output was truncated.
273 : }
274 : }
275 : return true;
276 : }
277 :
278 139 : void StringStream::PrintName(Object name) {
279 139 : if (name->IsString()) {
280 : String str = String::cast(name);
281 139 : if (str->length() > 0) {
282 : Put(str);
283 : } else {
284 25 : Add("/* anonymous */");
285 : }
286 : } else {
287 0 : Add("%o", name);
288 : }
289 139 : }
290 :
291 212 : void StringStream::PrintUsingMap(JSObject js_object) {
292 : Map map = js_object->map();
293 : int real_size = map->NumberOfOwnDescriptors();
294 212 : DescriptorArray descs = map->instance_descriptors();
295 1108 : for (int i = 0; i < real_size; i++) {
296 448 : PropertyDetails details = descs->GetDetails(i);
297 448 : if (details.location() == kField) {
298 : DCHECK_EQ(kData, details.kind());
299 252 : Object key = descs->GetKey(i);
300 252 : if (key->IsString() || key->IsNumber()) {
301 : int len = 3;
302 252 : if (key->IsString()) {
303 : len = String::cast(key)->length();
304 : }
305 5264 : for (; len < 18; len++)
306 2506 : Put(' ');
307 252 : if (key->IsString()) {
308 : Put(String::cast(key));
309 : } else {
310 0 : key->ShortPrint();
311 : }
312 252 : Add(": ");
313 252 : 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 252 : Object value = js_object->RawFastPropertyAt(index);
319 252 : Add("%o\n", value);
320 : }
321 : }
322 : }
323 : }
324 212 : }
325 :
326 42 : void StringStream::PrintFixedArray(FixedArray array, unsigned int limit) {
327 : ReadOnlyRoots roots = array->GetReadOnlyRoots();
328 182 : for (unsigned int i = 0; i < 10 && i < limit; i++) {
329 70 : Object element = array->get(i);
330 70 : if (element->IsTheHole(roots)) continue;
331 2450 : for (int len = 1; len < 18; len++) {
332 1190 : Put(' ');
333 : }
334 70 : Add("%d: %o\n", i, array->get(i));
335 : }
336 42 : if (limit >= 10) {
337 0 : Add(" ...\n");
338 : }
339 42 : }
340 :
341 0 : void StringStream::PrintByteArray(ByteArray byte_array) {
342 0 : unsigned int limit = byte_array->length();
343 0 : for (unsigned int i = 0; i < 10 && i < limit; i++) {
344 0 : byte b = byte_array->get(i);
345 0 : Add(" %d: %3d 0x%02x", i, b, b);
346 0 : if (b >= ' ' && b <= '~') {
347 0 : Add(" '%c'", b);
348 0 : } else if (b == '\n') {
349 0 : Add(" '\n'");
350 0 : } else if (b == '\r') {
351 0 : Add(" '\r'");
352 0 : } else if (b >= 1 && b <= 26) {
353 0 : Add(" ^%c", b + 'A' - 1);
354 : }
355 0 : Add("\n");
356 : }
357 0 : if (limit >= 10) {
358 0 : Add(" ...\n");
359 : }
360 0 : }
361 :
362 8 : void StringStream::PrintMentionedObjectCache(Isolate* isolate) {
363 8 : if (object_print_mode_ == kPrintObjectConcise) return;
364 : DebugObjectCache* debug_object_cache =
365 : isolate->string_stream_debug_object_cache();
366 8 : Add("==== Key ============================================\n\n");
367 460 : for (size_t i = 0; i < debug_object_cache->size(); i++) {
368 226 : HeapObject printee = *(*debug_object_cache)[i];
369 226 : Add(" #%d# %p: ", static_cast<int>(i),
370 226 : reinterpret_cast<void*>(printee->ptr()));
371 226 : printee->ShortPrint(this);
372 226 : Add("\n");
373 226 : if (printee->IsJSObject()) {
374 212 : if (printee->IsJSValue()) {
375 0 : Add(" value(): %o\n", JSValue::cast(printee)->value());
376 : }
377 212 : PrintUsingMap(JSObject::cast(printee));
378 212 : if (printee->IsJSArray()) {
379 112 : JSArray array = JSArray::cast(printee);
380 112 : if (array->HasObjectElements()) {
381 28 : unsigned int limit = FixedArray::cast(array->elements())->length();
382 : unsigned int length =
383 28 : static_cast<uint32_t>(JSArray::cast(array)->length()->Number());
384 28 : if (length < limit) limit = length;
385 28 : PrintFixedArray(FixedArray::cast(array->elements()), limit);
386 : }
387 : }
388 14 : } else if (printee->IsByteArray()) {
389 0 : PrintByteArray(ByteArray::cast(printee));
390 14 : } else if (printee->IsFixedArray()) {
391 14 : unsigned int limit = FixedArray::cast(printee)->length();
392 14 : PrintFixedArray(FixedArray::cast(printee), limit);
393 : }
394 : }
395 : }
396 :
397 66 : void StringStream::PrintSecurityTokenIfChanged(JSFunction fun) {
398 66 : Object token = fun->native_context()->security_token();
399 : Isolate* isolate = fun->GetIsolate();
400 66 : if (token != isolate->string_stream_current_security_token()) {
401 17 : Add("Security context: %o\n", token);
402 : isolate->set_string_stream_current_security_token(token);
403 : }
404 66 : }
405 :
406 66 : void StringStream::PrintFunction(JSFunction fun, Object receiver, Code* code) {
407 66 : PrintPrototype(fun, receiver);
408 66 : *code = fun->code();
409 66 : }
410 :
411 66 : void StringStream::PrintPrototype(JSFunction fun, Object receiver) {
412 66 : Object name = fun->shared()->Name();
413 : bool print_name = false;
414 : Isolate* isolate = fun->GetIsolate();
415 198 : if (receiver->IsNullOrUndefined(isolate) || receiver->IsTheHole(isolate) ||
416 : receiver->IsJSProxy()) {
417 : print_name = true;
418 66 : } else if (!isolate->context().is_null()) {
419 66 : if (!receiver->IsJSObject()) {
420 0 : receiver = receiver->GetPrototypeChainRootMap(isolate)->prototype();
421 : }
422 :
423 522 : for (PrototypeIterator iter(isolate, JSObject::cast(receiver),
424 : kStartAtReceiver);
425 228 : !iter.IsAtEnd(); iter.Advance()) {
426 237 : if (iter.GetCurrent()->IsJSProxy()) break;
427 237 : Object key = iter.GetCurrent<JSObject>()->SlowReverseLookup(fun);
428 237 : if (!key->IsUndefined(isolate)) {
429 18 : if (!name->IsString() ||
430 18 : !key->IsString() ||
431 27 : !String::cast(name)->Equals(String::cast(key))) {
432 : print_name = true;
433 : }
434 18 : if (name->IsString() && String::cast(name)->length() == 0) {
435 : print_name = false;
436 : }
437 : name = key;
438 : break;
439 : }
440 : }
441 : }
442 66 : 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 66 : if (print_name) {
446 0 : Add("(aka ");
447 0 : PrintName(fun->shared()->Name());
448 0 : Put(')');
449 : }
450 66 : }
451 :
452 99062 : char* HeapStringAllocator::grow(unsigned* bytes) {
453 99062 : unsigned new_bytes = *bytes * 2;
454 : // Check for overflow.
455 99062 : if (new_bytes <= *bytes) {
456 0 : return space_;
457 : }
458 99062 : char* new_space = NewArray<char>(new_bytes);
459 99062 : if (new_space == nullptr) {
460 0 : return space_;
461 : }
462 99062 : MemCopy(new_space, space_, *bytes);
463 99062 : *bytes = new_bytes;
464 99062 : DeleteArray(space_);
465 99062 : space_ = new_space;
466 99062 : return new_space;
467 : }
468 :
469 :
470 : } // namespace internal
471 120216 : } // namespace v8
|