v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
string-stream.cc
Go to the documentation of this file.
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
6
7#include <memory>
8
9#include "src/base/vector.h"
11#include "src/logging/log.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;
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.
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
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
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 Tagged<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_;
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 {
173 SNPrintF(formatted, temp.begin(), value);
174 Add(formatted.begin());
175 }
176 break;
177 }
178 case 'p': {
179 void* value = current.data_.u_pointer_;
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
195 ShortPrint(o, this);
196 if (IsString(o)) {
198 return;
199 }
200 } else if (IsNumber(o) || IsOddball(o)) {
201 return;
202 }
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(Cast<HeapObject>(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
232 LOG(isolate, StringEvent("StackDump", buffer_));
233}
234
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
251 return isolate->factory()
252 ->NewStringFromUtf8(base::Vector<const char>(buffer_, length_))
253 .ToHandleChecked();
254}
255
257 isolate->set_string_stream_current_security_token(Tagged<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) {
267 isolate->string_stream_debug_object_cache()->size() == 0;
268}
269#endif
270
272 return Put(str, 0, str->length());
273}
274
276 StringCharacterStream stream(str, start);
277 for (int i = start; i < end && stream.HasMore(); i++) {
278 uint16_t c = stream.GetNext();
279 if (c >= 127 || c < 32) {
280 c = '?';
281 }
282 if (!Put(static_cast<char>(c))) {
283 return false; // Output was truncated.
284 }
285 }
286 return true;
287}
288
290 if (IsString(name)) {
291 Tagged<String> str = Cast<String>(name);
292 if (str->length() > 0) {
293 Put(str);
294 } else {
295 Add("/* anonymous */");
296 }
297 } else {
298 Add("%o", name);
299 }
300}
301
303 Tagged<Map> map = js_object->map();
304 Tagged<DescriptorArray> descs = map->instance_descriptors(isolate);
305 for (InternalIndex i : map->IterateOwnDescriptors()) {
306 PropertyDetails details = descs->GetDetails(i);
307 if (details.location() == PropertyLocation::kField) {
309 Tagged<Object> key = descs->GetKey(i);
310 if (IsString(key) || IsNumber(key)) {
311 int len = 3;
312 if (IsString(key)) {
313 len = Cast<String>(key)->length();
314 }
315 for (; len < 18; len++) Put(' ');
316 if (IsString(key)) {
318 } else {
320 }
321 Add(": ");
323 Tagged<Object> value = js_object->RawFastPropertyAt(index);
324 Add("%o\n", value);
325 }
326 }
327 }
328}
329
331 unsigned int limit) {
333 for (unsigned int i = 0; i < 10 && i < limit; i++) {
334 Tagged<Object> element = array->get(i);
335 if (IsTheHole(element, roots)) continue;
336 for (int len = 1; len < 18; len++) {
337 Put(' ');
338 }
339 Add("%d: %o\n", i, array->get(i));
340 }
341 if (limit >= 10) {
342 Add(" ...\n");
343 }
344}
345
347 unsigned int limit = byte_array->length();
348 for (unsigned int i = 0; i < 10 && i < limit; i++) {
349 uint8_t b = byte_array->get(i);
350 Add(" %d: %3d 0x%02x", i, b, b);
351 if (b >= ' ' && b <= '~') {
352 Add(" '%c'", b);
353 } else if (b == '\n') {
354 Add(" '\n'");
355 } else if (b == '\r') {
356 Add(" '\r'");
357 } else if (b >= 1 && b <= 26) {
358 Add(" ^%c", b + 'A' - 1);
359 }
360 Add("\n");
361 }
362 if (limit >= 10) {
363 Add(" ...\n");
364 }
365}
366
369 DebugObjectCache* debug_object_cache =
370 isolate->string_stream_debug_object_cache();
371 Add("-- ObjectCacheKey --\n\n");
372 for (size_t i = 0; i < debug_object_cache->size(); i++) {
373 Tagged<HeapObject> printee = *(*debug_object_cache)[i];
374 Add(" #%d# %p: ", static_cast<int>(i),
375 reinterpret_cast<void*>(printee.ptr()));
376 ShortPrint(printee, this);
377 Add("\n");
378 if (IsJSObject(printee)) {
379 if (IsJSPrimitiveWrapper(printee)) {
380 Add(" value(): %o\n",
381 Cast<JSPrimitiveWrapper>(printee)->value());
382 }
383 PrintUsingMap(isolate, Cast<JSObject>(printee));
384 if (IsJSArray(printee)) {
385 Tagged<JSArray> array = Cast<JSArray>(printee);
386 if (array->HasObjectElements()) {
387 unsigned int limit = Cast<FixedArray>(array->elements())->length();
388 unsigned int length = static_cast<uint32_t>(
390 if (length < limit) limit = length;
391 PrintFixedArray(Cast<FixedArray>(array->elements()), limit);
392 }
393 }
394 } else if (IsByteArray(printee)) {
396 } else if (IsFixedArray(printee)) {
397 unsigned int limit = Cast<FixedArray>(printee)->length();
398 PrintFixedArray(Cast<FixedArray>(printee), limit);
399 }
400 }
401}
402
404 Tagged<JSFunction> fun) {
405 Tagged<Object> token = fun->native_context()->security_token();
406 // Use SafeEquals because the cached token might be a stale pointer.
407 if (token.SafeEquals(isolate->string_stream_current_security_token())) {
408 Add("Security context: %o\n", token);
409 isolate->set_string_stream_current_security_token(token);
410 }
411}
412
417
420 Tagged<Object> name = fun->shared()->Name();
421 bool print_name = false;
422 if (IsNullOrUndefined(receiver, isolate) || IsTheHole(receiver, isolate) ||
423 IsJSProxy(receiver) || IsWasmObject(receiver)) {
424 print_name = true;
425 } else if (!isolate->context().is_null()) {
426 if (!IsJSObject(receiver)) {
427 receiver =
428 Object::GetPrototypeChainRootMap(receiver, isolate)->prototype();
429 }
430
431 for (PrototypeIterator iter(isolate, Cast<JSObject>(receiver),
433 !iter.IsAtEnd(); iter.Advance()) {
434 if (!IsJSObject(iter.GetCurrent())) break;
435 Tagged<Object> key = iter.GetCurrent<JSObject>()->SlowReverseLookup(fun);
436 if (!IsUndefined(key, isolate)) {
437 if (!IsString(name) || !IsString(key) ||
438 !Cast<String>(name)->Equals(Cast<String>(key))) {
439 print_name = true;
440 }
441 if (IsString(name) && Cast<String>(name)->length() == 0) {
442 print_name = false;
443 }
444 name = key;
445 break;
446 }
447 }
448 }
449 PrintName(name);
450 // Also known as - if the name in the function doesn't match the name under
451 // which it was looked up.
452 if (print_name) {
453 Add("(aka ");
454 PrintName(fun->shared()->Name());
455 Put(')');
456 }
457}
458
459char* HeapStringAllocator::grow(unsigned* bytes) {
460 unsigned new_bytes = *bytes * 2;
461 // Check for overflow.
462 if (new_bytes <= *bytes) {
463 return space_;
464 }
465 char* new_space = NewArray<char>(new_bytes);
466 if (new_space == nullptr) {
467 return space_;
468 }
469 MemCopy(new_space, space_, *bytes);
470 *bytes = new_bytes;
472 space_ = new_space;
473 return new_space;
474}
475
476} // namespace internal
477} // namespace v8
int length() const
Definition vector.h:64
constexpr T * begin() const
Definition vector.h:96
static FieldIndex ForDescriptor(Tagged< Map > map, InternalIndex descriptor_index)
char * allocate(unsigned bytes) override
char * grow(unsigned *bytes) override
char * allocate(unsigned bytes) override
char * grow(unsigned *bytes) override
static V8_INLINE Isolate * Current()
Definition isolate-inl.h:35
static Tagged< Map > GetPrototypeChainRootMap(Tagged< Object > obj, Isolate *isolate)
Definition objects.cc:1683
static double NumberValue(Tagged< Number > obj)
PropertyLocation location() const
virtual char * grow(unsigned *bytes)=0
void PrintObject(Tagged< Object > obj)
void PrintFunction(Isolate *isolate, Tagged< JSFunction > function, Tagged< Object > receiver)
void PrintByteArray(Tagged< ByteArray > ba)
static V8_EXPORT_PRIVATE void ClearMentionedObjectCache(Isolate *isolate)
DirectHandle< String > ToString(Isolate *isolate)
void PrintPrototype(Isolate *isolate, Tagged< JSFunction > fun, Tagged< Object > receiver)
void PrintMentionedObjectCache(Isolate *isolate)
void PrintSecurityTokenIfChanged(Isolate *isolate, Tagged< JSFunction > function)
std::unique_ptr< char[]> ToCString() const
void PrintFixedArray(Tagged< FixedArray > array, unsigned int limit)
void Add(const char *format)
void PrintName(Tagged< Object > o)
void PrintUsingMap(Isolate *isolate, Tagged< JSObject > js_object)
void Log(Isolate *isolate)
StringAllocator * allocator_
ObjectPrintMode object_print_mode_
static const uint32_t kMaxShortPrintLength
Definition string.h:518
V8_INLINE constexpr StorageType ptr() const
constexpr bool SafeEquals(TaggedImpl< kOtherRefType, StorageType > other) const
Definition tagged-impl.h:93
int start
int end
int32_t offset
TNode< Object > receiver
icu::number::FormattedNumber formatted
int position
Definition liveedit.cc:290
#define LOG(isolate, Call)
Definition log.h:78
void DeleteArray(T *array)
Definition allocation.h:63
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
static const int kMentionedObjectCacheMaxSize
bool IsNumber(Tagged< Object > obj)
ReadOnlyRoots GetReadOnlyRoots()
Definition roots-inl.h:86
void PrintF(const char *format,...)
Definition utils.cc:39
bool IsNullOrUndefined(Tagged< Object > obj, Isolate *isolate)
void ShortPrint(Tagged< Object > obj, FILE *out)
Definition objects.cc:1865
static bool IsControlChar(char c)
V8_INLINE constexpr bool IsHeapObject(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:669
V8_INLINE bool IsWasmObject(T obj, Isolate *=nullptr)
Definition objects.h:725
return value
Definition map-inl.h:893
std::vector< Handle< HeapObject > > DebugObjectCache
Definition isolate.h:511
void MemCopy(void *dest, const void *src, size_t size)
Definition memcopy.h:124
T * NewArray(size_t size)
Definition allocation.h:43
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
#define CHECK_LE(lhs, rhs)
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
wasm::ValueType type