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