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