Home | History | Annotate | Download | only in platform
      1 // Copyright 2012 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 // Platform-specific code for POSIX goes here. This is not a platform on its
      6 // own, but contains the parts which are the same across the POSIX platforms
      7 // Linux, MacOS, FreeBSD, OpenBSD, NetBSD and QNX.
      8 
      9 #include <errno.h>
     10 #include <limits.h>
     11 #include <pthread.h>
     12 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
     13 #include <pthread_np.h>  // for pthread_set_name_np
     14 #endif
     15 #include <sched.h>  // for sched_yield
     16 #include <time.h>
     17 #include <unistd.h>
     18 
     19 #include <sys/mman.h>
     20 #include <sys/resource.h>
     21 #include <sys/stat.h>
     22 #include <sys/time.h>
     23 #include <sys/types.h>
     24 #if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
     25     defined(__NetBSD__) || defined(__OpenBSD__)
     26 #include <sys/sysctl.h>  // NOLINT, for sysctl
     27 #endif
     28 
     29 #undef MAP_TYPE
     30 
     31 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
     32 #define LOG_TAG "v8"
     33 #include <android/log.h>  // NOLINT
     34 #endif
     35 
     36 #include <cmath>
     37 #include <cstdlib>
     38 
     39 #include "src/base/lazy-instance.h"
     40 #include "src/base/macros.h"
     41 #include "src/base/platform/platform.h"
     42 #include "src/base/platform/time.h"
     43 #include "src/base/utils/random-number-generator.h"
     44 
     45 #ifdef V8_FAST_TLS_SUPPORTED
     46 #include "src/base/atomicops.h"
     47 #endif
     48 
     49 #if V8_OS_MACOSX
     50 #include <dlfcn.h>
     51 #endif
     52 
     53 #if V8_OS_LINUX
     54 #include <sys/prctl.h>  // NOLINT, for prctl
     55 #endif
     56 
     57 #if !V8_OS_NACL
     58 #include <sys/syscall.h>
     59 #endif
     60 
     61 namespace v8 {
     62 namespace base {
     63 
     64 namespace {
     65 
     66 // 0 is never a valid thread id.
     67 const pthread_t kNoThread = (pthread_t) 0;
     68 
     69 bool g_hard_abort = false;
     70 
     71 const char* g_gc_fake_mmap = NULL;
     72 
     73 }  // namespace
     74 
     75 
     76 int OS::ActivationFrameAlignment() {
     77 #if V8_TARGET_ARCH_ARM
     78   // On EABI ARM targets this is required for fp correctness in the
     79   // runtime system.
     80   return 8;
     81 #elif V8_TARGET_ARCH_MIPS
     82   return 8;
     83 #else
     84   // Otherwise we just assume 16 byte alignment, i.e.:
     85   // - With gcc 4.4 the tree vectorization optimizer can generate code
     86   //   that requires 16 byte alignment such as movdqa on x86.
     87   // - Mac OS X and Solaris (64-bit) activation frames must be 16 byte-aligned;
     88   //   see "Mac OS X ABI Function Call Guide"
     89   return 16;
     90 #endif
     91 }
     92 
     93 
     94 intptr_t OS::CommitPageSize() {
     95   static intptr_t page_size = getpagesize();
     96   return page_size;
     97 }
     98 
     99 
    100 void OS::Free(void* address, const size_t size) {
    101   // TODO(1240712): munmap has a return value which is ignored here.
    102   int result = munmap(address, size);
    103   USE(result);
    104   DCHECK(result == 0);
    105 }
    106 
    107 
    108 // Get rid of writable permission on code allocations.
    109 void OS::ProtectCode(void* address, const size_t size) {
    110 #if V8_OS_CYGWIN
    111   DWORD old_protect;
    112   VirtualProtect(address, size, PAGE_EXECUTE_READ, &old_protect);
    113 #elif V8_OS_NACL
    114   // The Native Client port of V8 uses an interpreter, so
    115   // code pages don't need PROT_EXEC.
    116   mprotect(address, size, PROT_READ);
    117 #else
    118   mprotect(address, size, PROT_READ | PROT_EXEC);
    119 #endif
    120 }
    121 
    122 
    123 // Create guard pages.
    124 void OS::Guard(void* address, const size_t size) {
    125 #if V8_OS_CYGWIN
    126   DWORD oldprotect;
    127   VirtualProtect(address, size, PAGE_NOACCESS, &oldprotect);
    128 #else
    129   mprotect(address, size, PROT_NONE);
    130 #endif
    131 }
    132 
    133 
    134 static LazyInstance<RandomNumberGenerator>::type
    135     platform_random_number_generator = LAZY_INSTANCE_INITIALIZER;
    136 
    137 
    138 void OS::Initialize(int64_t random_seed, bool hard_abort,
    139                     const char* const gc_fake_mmap) {
    140   if (random_seed) {
    141     platform_random_number_generator.Pointer()->SetSeed(random_seed);
    142   }
    143   g_hard_abort = hard_abort;
    144   g_gc_fake_mmap = gc_fake_mmap;
    145 }
    146 
    147 
    148 const char* OS::GetGCFakeMMapFile() {
    149   return g_gc_fake_mmap;
    150 }
    151 
    152 
    153 void* OS::GetRandomMmapAddr() {
    154 #if V8_OS_NACL
    155   // TODO(bradchen): restore randomization once Native Client gets
    156   // smarter about using mmap address hints.
    157   // See http://code.google.com/p/nativeclient/issues/3341
    158   return NULL;
    159 #endif
    160 #if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
    161     defined(THREAD_SANITIZER)
    162   // Dynamic tools do not support custom mmap addresses.
    163   return NULL;
    164 #endif
    165   uintptr_t raw_addr;
    166   platform_random_number_generator.Pointer()->NextBytes(&raw_addr,
    167                                                         sizeof(raw_addr));
    168 #if V8_TARGET_ARCH_X64
    169   // Currently available CPUs have 48 bits of virtual addressing.  Truncate
    170   // the hint address to 46 bits to give the kernel a fighting chance of
    171   // fulfilling our placement request.
    172   raw_addr &= V8_UINT64_C(0x3ffffffff000);
    173 #else
    174   raw_addr &= 0x3ffff000;
    175 
    176 # ifdef __sun
    177   // For our Solaris/illumos mmap hint, we pick a random address in the bottom
    178   // half of the top half of the address space (that is, the third quarter).
    179   // Because we do not MAP_FIXED, this will be treated only as a hint -- the
    180   // system will not fail to mmap() because something else happens to already
    181   // be mapped at our random address. We deliberately set the hint high enough
    182   // to get well above the system's break (that is, the heap); Solaris and
    183   // illumos will try the hint and if that fails allocate as if there were
    184   // no hint at all. The high hint prevents the break from getting hemmed in
    185   // at low values, ceding half of the address space to the system heap.
    186   raw_addr += 0x80000000;
    187 # else
    188   // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
    189   // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macos
    190   // 10.6 and 10.7.
    191   raw_addr += 0x20000000;
    192 # endif
    193 #endif
    194   return reinterpret_cast<void*>(raw_addr);
    195 }
    196 
    197 
    198 size_t OS::AllocateAlignment() {
    199   return static_cast<size_t>(sysconf(_SC_PAGESIZE));
    200 }
    201 
    202 
    203 void OS::Sleep(int milliseconds) {
    204   useconds_t ms = static_cast<useconds_t>(milliseconds);
    205   usleep(1000 * ms);
    206 }
    207 
    208 
    209 void OS::Abort() {
    210   if (g_hard_abort) {
    211     V8_IMMEDIATE_CRASH();
    212   }
    213   // Redirect to std abort to signal abnormal program termination.
    214   abort();
    215 }
    216 
    217 
    218 void OS::DebugBreak() {
    219 #if V8_HOST_ARCH_ARM
    220   asm("bkpt 0");
    221 #elif V8_HOST_ARCH_ARM64
    222   asm("brk 0");
    223 #elif V8_HOST_ARCH_MIPS
    224   asm("break");
    225 #elif V8_HOST_ARCH_MIPS64
    226   asm("break");
    227 #elif V8_HOST_ARCH_IA32
    228 #if V8_OS_NACL
    229   asm("hlt");
    230 #else
    231   asm("int $3");
    232 #endif  // V8_OS_NACL
    233 #elif V8_HOST_ARCH_X64
    234   asm("int $3");
    235 #else
    236 #error Unsupported host architecture.
    237 #endif
    238 }
    239 
    240 
    241 // ----------------------------------------------------------------------------
    242 // Math functions
    243 
    244 double OS::nan_value() {
    245   // NAN from math.h is defined in C99 and not in POSIX.
    246   return NAN;
    247 }
    248 
    249 
    250 int OS::GetCurrentProcessId() {
    251   return static_cast<int>(getpid());
    252 }
    253 
    254 
    255 int OS::GetCurrentThreadId() {
    256 #if defined(ANDROID)
    257   return static_cast<int>(syscall(__NR_gettid));
    258 #elif defined(SYS_gettid)
    259   return static_cast<int>(syscall(SYS_gettid));
    260 #else
    261   // PNaCL doesn't have a way to get an integral thread ID, but it doesn't
    262   // really matter, because we only need it in PerfJitLogger::LogRecordedBuffer.
    263   return 0;
    264 #endif
    265 }
    266 
    267 
    268 // ----------------------------------------------------------------------------
    269 // POSIX date/time support.
    270 //
    271 
    272 int OS::GetUserTime(uint32_t* secs,  uint32_t* usecs) {
    273 #if V8_OS_NACL
    274   // Optionally used in Logger::ResourceEvent.
    275   return -1;
    276 #else
    277   struct rusage usage;
    278 
    279   if (getrusage(RUSAGE_SELF, &usage) < 0) return -1;
    280   *secs = usage.ru_utime.tv_sec;
    281   *usecs = usage.ru_utime.tv_usec;
    282   return 0;
    283 #endif
    284 }
    285 
    286 
    287 double OS::TimeCurrentMillis() {
    288   return Time::Now().ToJsTime();
    289 }
    290 
    291 
    292 class TimezoneCache {};
    293 
    294 
    295 TimezoneCache* OS::CreateTimezoneCache() {
    296   return NULL;
    297 }
    298 
    299 
    300 void OS::DisposeTimezoneCache(TimezoneCache* cache) {
    301   DCHECK(cache == NULL);
    302 }
    303 
    304 
    305 void OS::ClearTimezoneCache(TimezoneCache* cache) {
    306   DCHECK(cache == NULL);
    307 }
    308 
    309 
    310 double OS::DaylightSavingsOffset(double time, TimezoneCache*) {
    311   if (std::isnan(time)) return nan_value();
    312   time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
    313   struct tm* t = localtime(&tv);
    314   if (NULL == t) return nan_value();
    315   return t->tm_isdst > 0 ? 3600 * msPerSecond : 0;
    316 }
    317 
    318 
    319 int OS::GetLastError() {
    320   return errno;
    321 }
    322 
    323 
    324 // ----------------------------------------------------------------------------
    325 // POSIX stdio support.
    326 //
    327 
    328 FILE* OS::FOpen(const char* path, const char* mode) {
    329   FILE* file = fopen(path, mode);
    330   if (file == NULL) return NULL;
    331   struct stat file_stat;
    332   if (fstat(fileno(file), &file_stat) != 0) return NULL;
    333   bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
    334   if (is_regular_file) return file;
    335   fclose(file);
    336   return NULL;
    337 }
    338 
    339 
    340 bool OS::Remove(const char* path) {
    341   return (remove(path) == 0);
    342 }
    343 
    344 
    345 FILE* OS::OpenTemporaryFile() {
    346   return tmpfile();
    347 }
    348 
    349 
    350 const char* const OS::LogFileOpenMode = "w";
    351 
    352 
    353 void OS::Print(const char* format, ...) {
    354   va_list args;
    355   va_start(args, format);
    356   VPrint(format, args);
    357   va_end(args);
    358 }
    359 
    360 
    361 void OS::VPrint(const char* format, va_list args) {
    362 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
    363   __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
    364 #else
    365   vprintf(format, args);
    366 #endif
    367 }
    368 
    369 
    370 void OS::FPrint(FILE* out, const char* format, ...) {
    371   va_list args;
    372   va_start(args, format);
    373   VFPrint(out, format, args);
    374   va_end(args);
    375 }
    376 
    377 
    378 void OS::VFPrint(FILE* out, const char* format, va_list args) {
    379 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
    380   __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
    381 #else
    382   vfprintf(out, format, args);
    383 #endif
    384 }
    385 
    386 
    387 void OS::PrintError(const char* format, ...) {
    388   va_list args;
    389   va_start(args, format);
    390   VPrintError(format, args);
    391   va_end(args);
    392 }
    393 
    394 
    395 void OS::VPrintError(const char* format, va_list args) {
    396 #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
    397   __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, format, args);
    398 #else
    399   vfprintf(stderr, format, args);
    400 #endif
    401 }
    402 
    403 
    404 int OS::SNPrintF(char* str, int length, const char* format, ...) {
    405   va_list args;
    406   va_start(args, format);
    407   int result = VSNPrintF(str, length, format, args);
    408   va_end(args);
    409   return result;
    410 }
    411 
    412 
    413 int OS::VSNPrintF(char* str,
    414                   int length,
    415                   const char* format,
    416                   va_list args) {
    417   int n = vsnprintf(str, length, format, args);
    418   if (n < 0 || n >= length) {
    419     // If the length is zero, the assignment fails.
    420     if (length > 0)
    421       str[length - 1] = '\0';
    422     return -1;
    423   } else {
    424     return n;
    425   }
    426 }
    427 
    428 
    429 // ----------------------------------------------------------------------------
    430 // POSIX string support.
    431 //
    432 
    433 char* OS::StrChr(char* str, int c) {
    434   return strchr(str, c);
    435 }
    436 
    437 
    438 void OS::StrNCpy(char* dest, int length, const char* src, size_t n) {
    439   strncpy(dest, src, n);
    440 }
    441 
    442 
    443 // ----------------------------------------------------------------------------
    444 // POSIX thread support.
    445 //
    446 
    447 class Thread::PlatformData {
    448  public:
    449   PlatformData() : thread_(kNoThread) {}
    450   pthread_t thread_;  // Thread handle for pthread.
    451   // Synchronizes thread creation
    452   Mutex thread_creation_mutex_;
    453 };
    454 
    455 Thread::Thread(const Options& options)
    456     : data_(new PlatformData),
    457       stack_size_(options.stack_size()),
    458       start_semaphore_(NULL) {
    459   if (stack_size_ > 0 && static_cast<size_t>(stack_size_) < PTHREAD_STACK_MIN) {
    460     stack_size_ = PTHREAD_STACK_MIN;
    461   }
    462   set_name(options.name());
    463 }
    464 
    465 
    466 Thread::~Thread() {
    467   delete data_;
    468 }
    469 
    470 
    471 static void SetThreadName(const char* name) {
    472 #if V8_OS_DRAGONFLYBSD || V8_OS_FREEBSD || V8_OS_OPENBSD
    473   pthread_set_name_np(pthread_self(), name);
    474 #elif V8_OS_NETBSD
    475   STATIC_ASSERT(Thread::kMaxThreadNameLength <= PTHREAD_MAX_NAMELEN_NP);
    476   pthread_setname_np(pthread_self(), "%s", name);
    477 #elif V8_OS_MACOSX
    478   // pthread_setname_np is only available in 10.6 or later, so test
    479   // for it at runtime.
    480   int (*dynamic_pthread_setname_np)(const char*);
    481   *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
    482     dlsym(RTLD_DEFAULT, "pthread_setname_np");
    483   if (dynamic_pthread_setname_np == NULL)
    484     return;
    485 
    486   // Mac OS X does not expose the length limit of the name, so hardcode it.
    487   static const int kMaxNameLength = 63;
    488   STATIC_ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
    489   dynamic_pthread_setname_np(name);
    490 #elif defined(PR_SET_NAME)
    491   prctl(PR_SET_NAME,
    492         reinterpret_cast<unsigned long>(name),  // NOLINT
    493         0, 0, 0);
    494 #endif
    495 }
    496 
    497 
    498 static void* ThreadEntry(void* arg) {
    499   Thread* thread = reinterpret_cast<Thread*>(arg);
    500   // We take the lock here to make sure that pthread_create finished first since
    501   // we don't know which thread will run first (the original thread or the new
    502   // one).
    503   { LockGuard<Mutex> lock_guard(&thread->data()->thread_creation_mutex_); }
    504   SetThreadName(thread->name());
    505   DCHECK(thread->data()->thread_ != kNoThread);
    506   thread->NotifyStartedAndRun();
    507   return NULL;
    508 }
    509 
    510 
    511 void Thread::set_name(const char* name) {
    512   strncpy(name_, name, sizeof(name_));
    513   name_[sizeof(name_) - 1] = '\0';
    514 }
    515 
    516 
    517 void Thread::Start() {
    518   int result;
    519   pthread_attr_t attr;
    520   memset(&attr, 0, sizeof(attr));
    521   result = pthread_attr_init(&attr);
    522   DCHECK_EQ(0, result);
    523   // Native client uses default stack size.
    524 #if !V8_OS_NACL
    525   if (stack_size_ > 0) {
    526     result = pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
    527     DCHECK_EQ(0, result);
    528   }
    529 #endif
    530   {
    531     LockGuard<Mutex> lock_guard(&data_->thread_creation_mutex_);
    532     result = pthread_create(&data_->thread_, &attr, ThreadEntry, this);
    533   }
    534   DCHECK_EQ(0, result);
    535   result = pthread_attr_destroy(&attr);
    536   DCHECK_EQ(0, result);
    537   DCHECK(data_->thread_ != kNoThread);
    538   USE(result);
    539 }
    540 
    541 
    542 void Thread::Join() {
    543   pthread_join(data_->thread_, NULL);
    544 }
    545 
    546 
    547 void Thread::YieldCPU() {
    548   int result = sched_yield();
    549   DCHECK_EQ(0, result);
    550   USE(result);
    551 }
    552 
    553 
    554 static Thread::LocalStorageKey PthreadKeyToLocalKey(pthread_key_t pthread_key) {
    555 #if V8_OS_CYGWIN
    556   // We need to cast pthread_key_t to Thread::LocalStorageKey in two steps
    557   // because pthread_key_t is a pointer type on Cygwin. This will probably not
    558   // work on 64-bit platforms, but Cygwin doesn't support 64-bit anyway.
    559   STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
    560   intptr_t ptr_key = reinterpret_cast<intptr_t>(pthread_key);
    561   return static_cast<Thread::LocalStorageKey>(ptr_key);
    562 #else
    563   return static_cast<Thread::LocalStorageKey>(pthread_key);
    564 #endif
    565 }
    566 
    567 
    568 static pthread_key_t LocalKeyToPthreadKey(Thread::LocalStorageKey local_key) {
    569 #if V8_OS_CYGWIN
    570   STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
    571   intptr_t ptr_key = static_cast<intptr_t>(local_key);
    572   return reinterpret_cast<pthread_key_t>(ptr_key);
    573 #else
    574   return static_cast<pthread_key_t>(local_key);
    575 #endif
    576 }
    577 
    578 
    579 #ifdef V8_FAST_TLS_SUPPORTED
    580 
    581 static Atomic32 tls_base_offset_initialized = 0;
    582 intptr_t kMacTlsBaseOffset = 0;
    583 
    584 // It's safe to do the initialization more that once, but it has to be
    585 // done at least once.
    586 static void InitializeTlsBaseOffset() {
    587   const size_t kBufferSize = 128;
    588   char buffer[kBufferSize];
    589   size_t buffer_size = kBufferSize;
    590   int ctl_name[] = { CTL_KERN , KERN_OSRELEASE };
    591   if (sysctl(ctl_name, 2, buffer, &buffer_size, NULL, 0) != 0) {
    592     V8_Fatal(__FILE__, __LINE__, "V8 failed to get kernel version");
    593   }
    594   // The buffer now contains a string of the form XX.YY.ZZ, where
    595   // XX is the major kernel version component.
    596   // Make sure the buffer is 0-terminated.
    597   buffer[kBufferSize - 1] = '\0';
    598   char* period_pos = strchr(buffer, '.');
    599   *period_pos = '\0';
    600   int kernel_version_major =
    601       static_cast<int>(strtol(buffer, NULL, 10));  // NOLINT
    602   // The constants below are taken from pthreads.s from the XNU kernel
    603   // sources archive at www.opensource.apple.com.
    604   if (kernel_version_major < 11) {
    605     // 8.x.x (Tiger), 9.x.x (Leopard), 10.x.x (Snow Leopard) have the
    606     // same offsets.
    607 #if V8_HOST_ARCH_IA32
    608     kMacTlsBaseOffset = 0x48;
    609 #else
    610     kMacTlsBaseOffset = 0x60;
    611 #endif
    612   } else {
    613     // 11.x.x (Lion) changed the offset.
    614     kMacTlsBaseOffset = 0;
    615   }
    616 
    617   Release_Store(&tls_base_offset_initialized, 1);
    618 }
    619 
    620 
    621 static void CheckFastTls(Thread::LocalStorageKey key) {
    622   void* expected = reinterpret_cast<void*>(0x1234CAFE);
    623   Thread::SetThreadLocal(key, expected);
    624   void* actual = Thread::GetExistingThreadLocal(key);
    625   if (expected != actual) {
    626     V8_Fatal(__FILE__, __LINE__,
    627              "V8 failed to initialize fast TLS on current kernel");
    628   }
    629   Thread::SetThreadLocal(key, NULL);
    630 }
    631 
    632 #endif  // V8_FAST_TLS_SUPPORTED
    633 
    634 
    635 Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
    636 #ifdef V8_FAST_TLS_SUPPORTED
    637   bool check_fast_tls = false;
    638   if (tls_base_offset_initialized == 0) {
    639     check_fast_tls = true;
    640     InitializeTlsBaseOffset();
    641   }
    642 #endif
    643   pthread_key_t key;
    644   int result = pthread_key_create(&key, NULL);
    645   DCHECK_EQ(0, result);
    646   USE(result);
    647   LocalStorageKey local_key = PthreadKeyToLocalKey(key);
    648 #ifdef V8_FAST_TLS_SUPPORTED
    649   // If we just initialized fast TLS support, make sure it works.
    650   if (check_fast_tls) CheckFastTls(local_key);
    651 #endif
    652   return local_key;
    653 }
    654 
    655 
    656 void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
    657   pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
    658   int result = pthread_key_delete(pthread_key);
    659   DCHECK_EQ(0, result);
    660   USE(result);
    661 }
    662 
    663 
    664 void* Thread::GetThreadLocal(LocalStorageKey key) {
    665   pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
    666   return pthread_getspecific(pthread_key);
    667 }
    668 
    669 
    670 void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
    671   pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
    672   int result = pthread_setspecific(pthread_key, value);
    673   DCHECK_EQ(0, result);
    674   USE(result);
    675 }
    676 
    677 
    678 } }  // namespace v8::base
    679