LCOV - code coverage report
Current view: top level - src - string-stream.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 164 270 60.7 %
Date: 2017-04-26 Functions: 17 22 77.3 %

          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/prototype.h"
      13             : 
      14             : namespace v8 {
      15             : namespace internal {
      16             : 
      17             : static const int kMentionedObjectCacheMaxSize = 256;
      18             : 
      19         918 : char* HeapStringAllocator::allocate(unsigned bytes) {
      20         918 :   space_ = NewArray<char>(bytes);
      21         918 :   return space_;
      22             : }
      23             : 
      24             : 
      25           0 : char* FixedStringAllocator::allocate(unsigned bytes) {
      26           0 :   CHECK_LE(bytes, length_);
      27           0 :   return buffer_;
      28             : }
      29             : 
      30             : 
      31           0 : char* FixedStringAllocator::grow(unsigned* old) {
      32           0 :   *old = length_;
      33           0 :   return buffer_;
      34             : }
      35             : 
      36             : 
      37       38969 : bool StringStream::Put(char c) {
      38       38969 :   if (full()) return false;
      39             :   DCHECK(length_ < capacity_);
      40             :   // Since the trailing '\0' is not accounted for in length_ fullness is
      41             :   // indicated by a difference of 1 between length_ and capacity_. Thus when
      42             :   // reaching a difference of 2 we need to grow the buffer.
      43       38969 :   if (length_ == capacity_ - 2) {
      44        1119 :     unsigned new_capacity = capacity_;
      45        1119 :     char* new_buffer = allocator_->grow(&new_capacity);
      46        1119 :     if (new_capacity > capacity_) {
      47        1119 :       capacity_ = new_capacity;
      48        1119 :       buffer_ = new_buffer;
      49             :     } else {
      50             :       // Reached the end of the available buffer.
      51             :       DCHECK(capacity_ >= 5);
      52           0 :       length_ = capacity_ - 1;  // Indicate fullness of the stream.
      53           0 :       buffer_[length_ - 4] = '.';
      54           0 :       buffer_[length_ - 3] = '.';
      55           0 :       buffer_[length_ - 2] = '.';
      56           0 :       buffer_[length_ - 1] = '\n';
      57           0 :       buffer_[length_] = '\0';
      58           0 :       return false;
      59             :     }
      60             :   }
      61       38969 :   buffer_[length_] = c;
      62       38969 :   buffer_[length_ + 1] = '\0';
      63       38969 :   length_++;
      64       38969 :   return true;
      65             : }
      66             : 
      67             : 
      68             : // A control character is one that configures a format element.  For
      69             : // instance, in %.5s, .5 are control characters.
      70             : static bool IsControlChar(char c) {
      71             :   switch (c) {
      72             :   case '0': case '1': case '2': case '3': case '4': case '5':
      73             :   case '6': case '7': case '8': case '9': case '.': case '-':
      74             :     return true;
      75             :   default:
      76             :     return false;
      77             :   }
      78             : }
      79             : 
      80             : 
      81        4222 : void StringStream::Add(Vector<const char> format, Vector<FmtElm> elms) {
      82             :   // If we already ran out of space then return immediately.
      83        4222 :   if (full()) return;
      84             :   int offset = 0;
      85             :   int elm = 0;
      86       39182 :   while (offset < format.length()) {
      87       34960 :     if (format[offset] != '%' || elm == elms.length()) {
      88       33109 :       Put(format[offset]);
      89       33109 :       offset++;
      90       33109 :       continue;
      91             :     }
      92             :     // Read this formatting directive into a temporary buffer
      93             :     EmbeddedVector<char, 24> temp;
      94             :     int format_length = 0;
      95             :     // Skip over the whole control character sequence until the
      96             :     // format element type
      97        1851 :     temp[format_length++] = format[offset++];
      98        5595 :     while (offset < format.length() && IsControlChar(format[offset]))
      99          63 :       temp[format_length++] = format[offset++];
     100        1851 :     if (offset >= format.length())
     101           0 :       return;
     102        1851 :     char type = format[offset];
     103        3702 :     temp[format_length++] = type;
     104        3702 :     temp[format_length] = '\0';
     105        1851 :     offset++;
     106        3702 :     FmtElm current = elms[elm++];
     107        1851 :     switch (type) {
     108             :     case 's': {
     109             :       DCHECK_EQ(FmtElm::C_STR, current.type_);
     110         536 :       const char* value = current.data_.u_c_str_;
     111         536 :       Add(value);
     112         536 :       break;
     113             :     }
     114             :     case 'w': {
     115             :       DCHECK_EQ(FmtElm::LC_STR, current.type_);
     116           0 :       Vector<const uc16> value = *current.data_.u_lc_str_;
     117           0 :       for (int i = 0; i < value.length(); i++)
     118           0 :         Put(static_cast<char>(value[i]));
     119             :       break;
     120             :     }
     121             :     case 'o': {
     122             :       DCHECK_EQ(FmtElm::OBJ, current.type_);
     123         196 :       Object* obj = current.data_.u_obj_;
     124         196 :       PrintObject(obj);
     125         196 :       break;
     126             :     }
     127             :     case 'k': {
     128             :       DCHECK_EQ(FmtElm::INT, current.type_);
     129           0 :       int value = current.data_.u_int_;
     130           0 :       if (0x20 <= value && value <= 0x7F) {
     131           0 :         Put(value);
     132           0 :       } else if (value <= 0xff) {
     133           0 :         Add("\\x%02x", value);
     134             :       } else {
     135           0 :         Add("\\u%04x", value);
     136             :       }
     137             :       break;
     138             :     }
     139             :     case 'i': case 'd': case 'u': case 'x': case 'c': case 'X': {
     140         497 :       int value = current.data_.u_int_;
     141             :       EmbeddedVector<char, 24> formatted;
     142         497 :       int length = SNPrintF(formatted, temp.start(), value);
     143         994 :       Add(Vector<const char>(formatted.start(), length));
     144             :       break;
     145             :     }
     146             :     case 'f': case 'g': case 'G': case 'e': case 'E': {
     147           0 :       double value = current.data_.u_double_;
     148           0 :       int inf = std::isinf(value);
     149           0 :       if (inf == -1) {
     150           0 :         Add("-inf");
     151           0 :       } else if (inf == 1) {
     152           0 :         Add("inf");
     153           0 :       } else if (std::isnan(value)) {
     154           0 :         Add("nan");
     155             :       } else {
     156             :         EmbeddedVector<char, 28> formatted;
     157           0 :         SNPrintF(formatted, temp.start(), value);
     158           0 :         Add(formatted.start());
     159             :       }
     160             :       break;
     161             :     }
     162             :     case 'p': {
     163         622 :       void* value = current.data_.u_pointer_;
     164             :       EmbeddedVector<char, 20> formatted;
     165         622 :       SNPrintF(formatted, temp.start(), value);
     166         622 :       Add(formatted.start());
     167             :       break;
     168             :     }
     169             :     default:
     170           0 :       UNREACHABLE();
     171             :       break;
     172             :     }
     173             :   }
     174             : 
     175             :   // Verify that the buffer is 0-terminated
     176             :   DCHECK(buffer_[length_] == '\0');
     177             : }
     178             : 
     179             : 
     180         196 : void StringStream::PrintObject(Object* o) {
     181         196 :   o->ShortPrint(this);
     182         196 :   if (o->IsString()) {
     183           0 :     if (String::cast(o)->length() <= String::kMaxShortPrintLength) {
     184             :       return;
     185             :     }
     186         308 :   } else if (o->IsNumber() || o->IsOddball()) {
     187             :     return;
     188             :   }
     189          84 :   if (o->IsHeapObject() && object_print_mode_ == kPrintObjectVerbose) {
     190             :     HeapObject* ho = HeapObject::cast(o);
     191         329 :     DebugObjectCache* debug_object_cache = ho->GetIsolate()->
     192          84 :         string_stream_debug_object_cache();
     193         378 :     for (int i = 0; i < debug_object_cache->length(); i++) {
     194         140 :       if ((*debug_object_cache)[i] == o) {
     195          35 :         Add("#%d#", i);
     196          35 :         return;
     197             :       }
     198             :     }
     199          49 :     if (debug_object_cache->length() < kMentionedObjectCacheMaxSize) {
     200          49 :       Add("#%d#", debug_object_cache->length());
     201          49 :       debug_object_cache->Add(HeapObject::cast(o));
     202             :     } else {
     203           0 :       Add("@%p", o);
     204             :     }
     205             :   }
     206             : }
     207             : 
     208             : 
     209         904 : std::unique_ptr<char[]> StringStream::ToCString() const {
     210         904 :   char* str = NewArray<char>(length_ + 1);
     211         904 :   MemCopy(str, buffer_, length_);
     212         904 :   str[length_] = '\0';
     213         904 :   return std::unique_ptr<char[]>(str);
     214             : }
     215             : 
     216             : 
     217          14 : void StringStream::Log(Isolate* isolate) {
     218          14 :   LOG(isolate, StringEvent("StackDump", buffer_));
     219          14 : }
     220             : 
     221             : 
     222          14 : void StringStream::OutputToFile(FILE* out) {
     223             :   // Dump the output to stdout, but make sure to break it up into
     224             :   // manageable chunks to avoid losing parts of the output in the OS
     225             :   // printing code. This is a problem on Windows in particular; see
     226             :   // the VPrint() function implementations in platform-win32.cc.
     227             :   unsigned position = 0;
     228          28 :   for (unsigned next; (next = position + 2048) < length_; position = next) {
     229           0 :     char save = buffer_[next];
     230           0 :     buffer_[next] = '\0';
     231           0 :     internal::PrintF(out, "%s", &buffer_[position]);
     232           0 :     buffer_[next] = save;
     233             :   }
     234          14 :   internal::PrintF(out, "%s", &buffer_[position]);
     235          14 : }
     236             : 
     237             : 
     238           0 : Handle<String> StringStream::ToString(Isolate* isolate) {
     239             :   return isolate->factory()->NewStringFromUtf8(
     240           0 :       Vector<const char>(buffer_, length_)).ToHandleChecked();
     241             : }
     242             : 
     243             : 
     244          63 : void StringStream::ClearMentionedObjectCache(Isolate* isolate) {
     245             :   isolate->set_string_stream_current_security_token(NULL);
     246          21 :   if (isolate->string_stream_debug_object_cache() == NULL) {
     247             :     isolate->set_string_stream_debug_object_cache(new DebugObjectCache(0));
     248             :   }
     249             :   isolate->string_stream_debug_object_cache()->Clear();
     250          21 : }
     251             : 
     252             : 
     253             : #ifdef DEBUG
     254             : bool StringStream::IsMentionedObjectCacheClear(Isolate* isolate) {
     255             :   return object_print_mode_ == kPrintObjectConcise ||
     256             :          isolate->string_stream_debug_object_cache()->length() == 0;
     257             : }
     258             : #endif
     259             : 
     260             : 
     261         545 : bool StringStream::Put(String* str) {
     262         601 :   return Put(str, 0, str->length());
     263             : }
     264             : 
     265             : 
     266         601 : bool StringStream::Put(String* str, int start, int end) {
     267             :   StringCharacterStream stream(str, start);
     268        4933 :   for (int i = start; i < end && stream.HasMore(); i++) {
     269        4332 :     uint16_t c = stream.GetNext();
     270        4332 :     if (c >= 127 || c < 32) {
     271             :       c = '?';
     272             :     }
     273        4332 :     if (!Put(static_cast<char>(c))) {
     274             :       return false;  // Output was truncated.
     275             :     }
     276             :   }
     277             :   return true;
     278             : }
     279             : 
     280             : 
     281          70 : void StringStream::PrintName(Object* name) {
     282          70 :   if (name->IsString()) {
     283             :     String* str = String::cast(name);
     284          42 :     if (str->length() > 0) {
     285             :       Put(str);
     286             :     } else {
     287           0 :       Add("/* anonymous */");
     288             :     }
     289             :   } else {
     290          28 :     Add("%o", name);
     291             :   }
     292          70 : }
     293             : 
     294             : 
     295          49 : void StringStream::PrintUsingMap(JSObject* js_object) {
     296             :   Map* map = js_object->map();
     297         147 :   if (!js_object->GetHeap()->Contains(map) ||
     298          98 :       !map->IsHeapObject() ||
     299             :       !map->IsMap()) {
     300           0 :     Add("<Invalid map>\n");
     301          49 :     return;
     302             :   }
     303             :   int real_size = map->NumberOfOwnDescriptors();
     304             :   DescriptorArray* descs = map->instance_descriptors();
     305         133 :   for (int i = 0; i < real_size; i++) {
     306          84 :     PropertyDetails details = descs->GetDetails(i);
     307          84 :     if (details.location() == kField) {
     308             :       DCHECK_EQ(kData, details.kind());
     309             :       Object* key = descs->GetKey(i);
     310          14 :       if (key->IsString() || key->IsNumber()) {
     311             :         int len = 3;
     312          14 :         if (key->IsString()) {
     313             :           len = String::cast(key)->length();
     314             :         }
     315         154 :         for (; len < 18; len++)
     316         154 :           Put(' ');
     317          14 :         if (key->IsString()) {
     318             :           Put(String::cast(key));
     319             :         } else {
     320           0 :           key->ShortPrint();
     321             :         }
     322          14 :         Add(": ");
     323          14 :         FieldIndex index = FieldIndex::ForDescriptor(map, i);
     324          14 :         if (js_object->IsUnboxedDoubleField(index)) {
     325             :           double value = js_object->RawFastDoublePropertyAt(index);
     326           0 :           Add("<unboxed double> %.16g\n", FmtElm(value));
     327             :         } else {
     328          14 :           Object* value = js_object->RawFastPropertyAt(index);
     329          14 :           Add("%o\n", value);
     330             :         }
     331             :       }
     332             :     }
     333             :   }
     334             : }
     335             : 
     336             : 
     337           0 : void StringStream::PrintFixedArray(FixedArray* array, unsigned int limit) {
     338             :   Isolate* isolate = array->GetIsolate();
     339           0 :   for (unsigned int i = 0; i < 10 && i < limit; i++) {
     340           0 :     Object* element = array->get(i);
     341           0 :     if (element->IsTheHole(isolate)) continue;
     342           0 :     for (int len = 1; len < 18; len++) {
     343           0 :       Put(' ');
     344             :     }
     345           0 :     Add("%d: %o\n", i, array->get(i));
     346             :   }
     347           0 :   if (limit >= 10) {
     348           0 :     Add("                  ...\n");
     349             :   }
     350           0 : }
     351             : 
     352             : 
     353           0 : void StringStream::PrintByteArray(ByteArray* byte_array) {
     354           0 :   unsigned int limit = byte_array->length();
     355           0 :   for (unsigned int i = 0; i < 10 && i < limit; i++) {
     356           0 :     byte b = byte_array->get(i);
     357           0 :     Add("             %d: %3d 0x%02x", i, b, b);
     358           0 :     if (b >= ' ' && b <= '~') {
     359           0 :       Add(" '%c'", b);
     360           0 :     } else if (b == '\n') {
     361           0 :       Add(" '\n'");
     362           0 :     } else if (b == '\r') {
     363           0 :       Add(" '\r'");
     364           0 :     } else if (b >= 1 && b <= 26) {
     365           0 :       Add(" ^%c", b + 'A' - 1);
     366             :     }
     367           0 :     Add("\n");
     368             :   }
     369           0 :   if (limit >= 10) {
     370           0 :     Add("                  ...\n");
     371             :   }
     372           0 : }
     373             : 
     374             : 
     375          28 : void StringStream::PrintMentionedObjectCache(Isolate* isolate) {
     376          28 :   if (object_print_mode_ == kPrintObjectConcise) return;
     377         112 :   DebugObjectCache* debug_object_cache =
     378             :       isolate->string_stream_debug_object_cache();
     379          14 :   Add("==== Key         ============================================\n\n");
     380         126 :   for (int i = 0; i < debug_object_cache->length(); i++) {
     381          49 :     HeapObject* printee = (*debug_object_cache)[i];
     382          49 :     Add(" #%d# %p: ", i, printee);
     383          49 :     printee->ShortPrint(this);
     384          49 :     Add("\n");
     385          49 :     if (printee->IsJSObject()) {
     386          49 :       if (printee->IsJSValue()) {
     387           0 :         Add("           value(): %o\n", JSValue::cast(printee)->value());
     388             :       }
     389          49 :       PrintUsingMap(JSObject::cast(printee));
     390          49 :       if (printee->IsJSArray()) {
     391             :         JSArray* array = JSArray::cast(printee);
     392           0 :         if (array->HasFastObjectElements()) {
     393           0 :           unsigned int limit = FixedArray::cast(array->elements())->length();
     394             :           unsigned int length =
     395           0 :             static_cast<uint32_t>(JSArray::cast(array)->length()->Number());
     396           0 :           if (length < limit) limit = length;
     397           0 :           PrintFixedArray(FixedArray::cast(array->elements()), limit);
     398             :         }
     399             :       }
     400           0 :     } else if (printee->IsByteArray()) {
     401           0 :       PrintByteArray(ByteArray::cast(printee));
     402           0 :     } else if (printee->IsFixedArray()) {
     403           0 :       unsigned int limit = FixedArray::cast(printee)->length();
     404           0 :       PrintFixedArray(FixedArray::cast(printee), limit);
     405             :     }
     406             :   }
     407             : }
     408             : 
     409             : 
     410          42 : void StringStream::PrintSecurityTokenIfChanged(Object* f) {
     411          42 :   if (!f->IsHeapObject()) return;
     412             :   HeapObject* obj = HeapObject::cast(f);
     413          42 :   Isolate* isolate = obj->GetIsolate();
     414          42 :   Heap* heap = isolate->heap();
     415          42 :   if (!heap->Contains(obj)) return;
     416             :   Map* map = obj->map();
     417          84 :   if (!map->IsHeapObject() ||
     418          84 :       !heap->Contains(map) ||
     419          84 :       !map->IsMap() ||
     420             :       !f->IsJSFunction()) {
     421             :     return;
     422             :   }
     423             : 
     424             :   JSFunction* fun = JSFunction::cast(f);
     425             :   Object* perhaps_context = fun->context();
     426          84 :   if (perhaps_context->IsHeapObject() &&
     427          84 :       heap->Contains(HeapObject::cast(perhaps_context)) &&
     428             :       perhaps_context->IsContext()) {
     429             :     Context* context = fun->context();
     430          42 :     if (!heap->Contains(context)) {
     431           0 :       Add("(Function context is outside heap)\n");
     432           0 :       return;
     433             :     }
     434             :     Object* token = context->native_context()->security_token();
     435          42 :     if (token != isolate->string_stream_current_security_token()) {
     436          14 :       Add("Security context: %o\n", token);
     437             :       isolate->set_string_stream_current_security_token(token);
     438             :     }
     439             :   } else {
     440           0 :     Add("(Function context is corrupt)\n");
     441             :   }
     442             : }
     443             : 
     444             : 
     445          42 : void StringStream::PrintFunction(Object* f, Object* receiver, Code** code) {
     446          42 :   if (!f->IsHeapObject()) {
     447           0 :     Add("/* warning: 'function' was not a heap object */ ");
     448           0 :     return;
     449             :   }
     450             :   Heap* heap = HeapObject::cast(f)->GetHeap();
     451          42 :   if (!heap->Contains(HeapObject::cast(f))) {
     452           0 :     Add("/* warning: 'function' was not on the heap */ ");
     453           0 :     return;
     454             :   }
     455          42 :   if (!heap->Contains(HeapObject::cast(f)->map())) {
     456           0 :     Add("/* warning: function's map was not on the heap */ ");
     457           0 :     return;
     458             :   }
     459          42 :   if (!HeapObject::cast(f)->map()->IsMap()) {
     460           0 :     Add("/* warning: function's map was not a valid map */ ");
     461           0 :     return;
     462             :   }
     463          42 :   if (f->IsJSFunction()) {
     464             :     JSFunction* fun = JSFunction::cast(f);
     465             :     // Common case: on-stack function present and resolved.
     466          42 :     PrintPrototype(fun, receiver);
     467          42 :     *code = fun->code();
     468           0 :   } else if (f->IsInternalizedString()) {
     469             :     // Unresolved and megamorphic calls: Instead of the function
     470             :     // we have the function name on the stack.
     471           0 :     PrintName(f);
     472           0 :     Add("/* unresolved */ ");
     473             :   } else {
     474             :     // Unless this is the frame of a built-in function, we should always have
     475             :     // the callee function or name on the stack. If we don't, we have a
     476             :     // problem or a change of the stack frame layout.
     477           0 :     Add("%o", f);
     478           0 :     Add("/* warning: no JSFunction object or function name found */ ");
     479             :   }
     480             : }
     481             : 
     482             : 
     483          42 : void StringStream::PrintPrototype(JSFunction* fun, Object* receiver) {
     484             :   Object* name = fun->shared()->name();
     485             :   bool print_name = false;
     486          42 :   Isolate* isolate = fun->GetIsolate();
     487         126 :   if (receiver->IsNullOrUndefined(isolate) || receiver->IsTheHole(isolate) ||
     488             :       receiver->IsJSProxy()) {
     489             :     print_name = true;
     490          42 :   } else if (isolate->context() != nullptr) {
     491          42 :     if (!receiver->IsJSObject()) {
     492           0 :       receiver = receiver->GetPrototypeChainRootMap(isolate)->prototype();
     493             :     }
     494             : 
     495          98 :     for (PrototypeIterator iter(isolate, JSObject::cast(receiver),
     496             :                                 kStartAtReceiver);
     497          56 :          !iter.IsAtEnd(); iter.Advance()) {
     498         168 :       if (iter.GetCurrent()->IsJSProxy()) break;
     499          84 :       Object* key = iter.GetCurrent<JSObject>()->SlowReverseLookup(fun);
     500          84 :       if (!key->IsUndefined(isolate)) {
     501          56 :         if (!name->IsString() ||
     502          56 :             !key->IsString() ||
     503          28 :             !String::cast(name)->Equals(String::cast(key))) {
     504             :           print_name = true;
     505             :         }
     506          56 :         if (name->IsString() && String::cast(name)->length() == 0) {
     507             :           print_name = false;
     508             :         }
     509             :         name = key;
     510          28 :         break;
     511             :       }
     512             :     }
     513             :   }
     514          42 :   PrintName(name);
     515             :   // Also known as - if the name in the function doesn't match the name under
     516             :   // which it was looked up.
     517          42 :   if (print_name) {
     518           0 :     Add("(aka ");
     519           0 :     PrintName(fun->shared()->name());
     520           0 :     Put(')');
     521             :   }
     522          42 : }
     523             : 
     524             : 
     525        1119 : char* HeapStringAllocator::grow(unsigned* bytes) {
     526        1119 :   unsigned new_bytes = *bytes * 2;
     527             :   // Check for overflow.
     528        1119 :   if (new_bytes <= *bytes) {
     529           0 :     return space_;
     530             :   }
     531        1119 :   char* new_space = NewArray<char>(new_bytes);
     532        1119 :   if (new_space == NULL) {
     533           0 :     return space_;
     534             :   }
     535        1119 :   MemCopy(new_space, space_, *bytes);
     536        1119 :   *bytes = new_bytes;
     537        1119 :   DeleteArray(space_);
     538        1119 :   space_ = new_space;
     539        1119 :   return new_space;
     540             : }
     541             : 
     542             : 
     543             : }  // namespace internal
     544             : }  // namespace v8

Generated by: LCOV version 1.10