LCOV - code coverage report
Current view: top level - src - string-stream.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 175 238 73.5 %
Date: 2019-03-21 Functions: 20 23 87.0 %

          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

Generated by: LCOV version 1.10