v8  3.14.5(node0.10.28)
V8 is Google's open source JavaScript engine
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
test-log.cc
Go to the documentation of this file.
1 // Copyright 2006-2009 the V8 project authors. All rights reserved.
2 //
3 // Tests of logging functions from log.h
4 
5 #ifdef __linux__
6 #include <math.h>
7 #include <pthread.h>
8 #include <signal.h>
9 #include <unistd.h>
10 #endif // __linux__
11 
12 #include "v8.h"
13 #include "log.h"
14 #include "cpu-profiler.h"
15 #include "natives.h"
16 #include "v8threads.h"
17 #include "v8utils.h"
18 #include "cctest.h"
19 #include "vm-state-inl.h"
20 
25 
26 namespace {
27 
28 
29 class ScopedLoggerInitializer {
30  public:
31  explicit ScopedLoggerInitializer(bool prof_lazy)
32  : saved_log_(i::FLAG_log),
33  saved_prof_lazy_(i::FLAG_prof_lazy),
34  saved_prof_(i::FLAG_prof),
35  saved_prof_auto_(i::FLAG_prof_auto),
36  temp_file_(NULL),
37  // Need to run this prior to creating the scope.
38  trick_to_run_init_flags_(init_flags_(prof_lazy)),
39  scope_(),
40  env_(v8::Context::New()) {
41  env_->Enter();
42  }
43 
44  ~ScopedLoggerInitializer() {
45  env_->Exit();
46  LOGGER->TearDown();
47  if (temp_file_ != NULL) fclose(temp_file_);
48  i::FLAG_prof_lazy = saved_prof_lazy_;
49  i::FLAG_prof = saved_prof_;
50  i::FLAG_prof_auto = saved_prof_auto_;
51  i::FLAG_log = saved_log_;
52  }
53 
54  v8::Handle<v8::Context>& env() { return env_; }
55 
56  FILE* StopLoggingGetTempFile() {
57  temp_file_ = LOGGER->TearDown();
58  CHECK_NE(NULL, temp_file_);
59  fflush(temp_file_);
60  rewind(temp_file_);
61  return temp_file_;
62  }
63 
64  private:
65  static bool init_flags_(bool prof_lazy) {
66  i::FLAG_log = true;
67  i::FLAG_prof = true;
68  i::FLAG_prof_lazy = prof_lazy;
69  i::FLAG_prof_auto = false;
70  i::FLAG_logfile = i::Log::kLogToTemporaryFile;
71  return prof_lazy;
72  }
73 
74  const bool saved_log_;
75  const bool saved_prof_lazy_;
76  const bool saved_prof_;
77  const bool saved_prof_auto_;
78  FILE* temp_file_;
79  const bool trick_to_run_init_flags_;
80  v8::HandleScope scope_;
82 
83  DISALLOW_COPY_AND_ASSIGN(ScopedLoggerInitializer);
84 };
85 
86 } // namespace
87 
88 
89 static const char* StrNStr(const char* s1, const char* s2, int n) {
90  if (s1[n] == '\0') return strstr(s1, s2);
91  i::ScopedVector<char> str(n + 1);
92  i::OS::StrNCpy(str, s1, static_cast<size_t>(n));
93  str[n] = '\0';
94  char* found = strstr(str.start(), s2);
95  return found != NULL ? s1 + (found - str.start()) : NULL;
96 }
97 
98 
99 TEST(ProfLazyMode) {
100  ScopedLoggerInitializer initialize_logger(true);
101 
102  if (!i::V8::UseCrankshaft()) return;
103 
104  LOGGER->StringEvent("test-start", "");
105  CompileRun("var a = (function(x) { return x + 1; })(10);");
106  LOGGER->StringEvent("test-profiler-start", "");
108  CompileRun(
109  "var b = (function(x) { return x + 2; })(10);\n"
110  "var c = (function(x) { return x + 3; })(10);\n"
111  "var d = (function(x) { return x + 4; })(10);\n"
112  "var e = (function(x) { return x + 5; })(10);");
114  LOGGER->StringEvent("test-profiler-stop", "");
115  CompileRun("var f = (function(x) { return x + 6; })(10);");
116  // Check that profiling can be resumed again.
117  LOGGER->StringEvent("test-profiler-start-2", "");
119  CompileRun(
120  "var g = (function(x) { return x + 7; })(10);\n"
121  "var h = (function(x) { return x + 8; })(10);\n"
122  "var i = (function(x) { return x + 9; })(10);\n"
123  "var j = (function(x) { return x + 10; })(10);");
125  LOGGER->StringEvent("test-profiler-stop-2", "");
126  LOGGER->StringEvent("test-stop", "");
127 
128  bool exists = false;
130  i::ReadFile(initialize_logger.StopLoggingGetTempFile(), &exists, true));
131  CHECK(exists);
132 
133  const char* test_start_position =
134  StrNStr(log.start(), "test-start,", log.length());
135  CHECK_NE(NULL, test_start_position);
136  const char* test_profiler_start_position =
137  StrNStr(log.start(), "test-profiler-start,", log.length());
138  CHECK_NE(NULL, test_profiler_start_position);
139  CHECK_GT(test_profiler_start_position, test_start_position);
140  const char* test_profiler_stop_position =
141  StrNStr(log.start(), "test-profiler-stop,", log.length());
142  CHECK_NE(NULL, test_profiler_stop_position);
143  CHECK_GT(test_profiler_stop_position, test_profiler_start_position);
144  const char* test_profiler_start_2_position =
145  StrNStr(log.start(), "test-profiler-start-2,", log.length());
146  CHECK_NE(NULL, test_profiler_start_2_position);
147  CHECK_GT(test_profiler_start_2_position, test_profiler_stop_position);
148 
149  // Nothing must be logged until profiling is resumed.
150  CHECK_EQ(NULL, StrNStr(test_start_position,
151  "code-creation,",
152  static_cast<int>(test_profiler_start_position -
153  test_start_position)));
154  // Nothing must be logged while profiling is suspended.
155  CHECK_EQ(NULL, StrNStr(test_profiler_stop_position,
156  "code-creation,",
157  static_cast<int>(test_profiler_start_2_position -
158  test_profiler_stop_position)));
159 }
160 
161 
162 // BUG(913). Need to implement support for profiling multiple VM threads.
163 #if 0
164 
165 namespace {
166 
167 class LoopingThread : public v8::internal::Thread {
168  public:
169  explicit LoopingThread(v8::internal::Isolate* isolate)
170  : v8::internal::Thread(isolate),
171  semaphore_(v8::internal::OS::CreateSemaphore(0)),
172  run_(true) {
173  }
174 
175  virtual ~LoopingThread() { delete semaphore_; }
176 
177  void Run() {
178  self_ = pthread_self();
179  RunLoop();
180  }
181 
182  void SendSigProf() { pthread_kill(self_, SIGPROF); }
183 
184  void Stop() { run_ = false; }
185 
186  bool WaitForRunning() { return semaphore_->Wait(1000000); }
187 
188  protected:
189  bool IsRunning() { return run_; }
190 
191  virtual void RunLoop() = 0;
192 
193  void SetV8ThreadId() {
194  v8_thread_id_ = v8::V8::GetCurrentThreadId();
195  }
196 
197  void SignalRunning() { semaphore_->Signal(); }
198 
199  private:
200  v8::internal::Semaphore* semaphore_;
201  bool run_;
202  pthread_t self_;
203  int v8_thread_id_;
204 };
205 
206 
207 class LoopingJsThread : public LoopingThread {
208  public:
209  explicit LoopingJsThread(v8::internal::Isolate* isolate)
210  : LoopingThread(isolate) { }
211  void RunLoop() {
212  v8::Locker locker;
213  CHECK(i::Isolate::Current() != NULL);
214  CHECK_GT(i::Isolate::Current()->thread_manager()->CurrentId(), 0);
215  SetV8ThreadId();
216  while (IsRunning()) {
217  v8::HandleScope scope;
219  CHECK(!context.IsEmpty());
220  {
221  v8::Context::Scope context_scope(context);
222  SignalRunning();
223  CompileRun(
224  "var j; for (var i=0; i<10000; ++i) { j = Math.sin(i); }");
225  }
226  context.Dispose();
227  i::OS::Sleep(1);
228  }
229  }
230 };
231 
232 
233 class LoopingNonJsThread : public LoopingThread {
234  public:
235  explicit LoopingNonJsThread(v8::internal::Isolate* isolate)
236  : LoopingThread(isolate) { }
237  void RunLoop() {
238  v8::Locker locker;
239  v8::Unlocker unlocker;
240  // Now thread has V8's id, but will not run VM code.
241  CHECK(i::Isolate::Current() != NULL);
242  CHECK_GT(i::Isolate::Current()->thread_manager()->CurrentId(), 0);
243  double i = 10;
244  SignalRunning();
245  while (IsRunning()) {
246  i = sin(i);
247  i::OS::Sleep(1);
248  }
249  }
250 };
251 
252 
253 class TestSampler : public v8::internal::Sampler {
254  public:
255  explicit TestSampler(v8::internal::Isolate* isolate)
256  : Sampler(isolate, 0, true, true),
257  semaphore_(v8::internal::OS::CreateSemaphore(0)),
258  was_sample_stack_called_(false) {
259  }
260 
261  ~TestSampler() { delete semaphore_; }
262 
264  was_sample_stack_called_ = true;
265  }
266 
267  void Tick(v8::internal::TickSample*) { semaphore_->Signal(); }
268 
269  bool WaitForTick() { return semaphore_->Wait(1000000); }
270 
271  void Reset() { was_sample_stack_called_ = false; }
272 
273  bool WasSampleStackCalled() { return was_sample_stack_called_; }
274 
275  private:
276  v8::internal::Semaphore* semaphore_;
277  bool was_sample_stack_called_;
278 };
279 
280 
281 } // namespace
282 
283 TEST(ProfMultipleThreads) {
284  TestSampler* sampler = NULL;
285  {
286  v8::Locker locker;
287  sampler = new TestSampler(v8::internal::Isolate::Current());
288  sampler->Start();
289  CHECK(sampler->IsActive());
290  }
291 
292  LoopingJsThread jsThread(v8::internal::Isolate::Current());
293  jsThread.Start();
294  LoopingNonJsThread nonJsThread(v8::internal::Isolate::Current());
295  nonJsThread.Start();
296 
297  CHECK(!sampler->WasSampleStackCalled());
298  jsThread.WaitForRunning();
299  jsThread.SendSigProf();
300  CHECK(sampler->WaitForTick());
301  CHECK(sampler->WasSampleStackCalled());
302  sampler->Reset();
303  CHECK(!sampler->WasSampleStackCalled());
304  nonJsThread.WaitForRunning();
305  nonJsThread.SendSigProf();
306  CHECK(!sampler->WaitForTick());
307  CHECK(!sampler->WasSampleStackCalled());
308  sampler->Stop();
309 
310  jsThread.Stop();
311  nonJsThread.Stop();
312  jsThread.Join();
313  nonJsThread.Join();
314 
315  delete sampler;
316 }
317 
318 #endif // __linux__
319 
320 
321 // Test for issue http://crbug.com/23768 in Chromium.
322 // Heap can contain scripts with already disposed external sources.
323 // We need to verify that LogCompiledFunctions doesn't crash on them.
324 namespace {
325 
326 class SimpleExternalString : public v8::String::ExternalStringResource {
327  public:
328  explicit SimpleExternalString(const char* source)
329  : utf_source_(StrLength(source)) {
330  for (int i = 0; i < utf_source_.length(); ++i)
331  utf_source_[i] = source[i];
332  }
333  virtual ~SimpleExternalString() {}
334  virtual size_t length() const { return utf_source_.length(); }
335  virtual const uint16_t* data() const { return utf_source_.start(); }
336  private:
337  i::ScopedVector<uint16_t> utf_source_;
338 };
339 
340 } // namespace
341 
342 TEST(Issue23768) {
343  v8::HandleScope scope;
345  env->Enter();
346 
347  SimpleExternalString source_ext_str("(function ext() {})();");
348  v8::Local<v8::String> source = v8::String::NewExternal(&source_ext_str);
349  // Script needs to have a name in order to trigger InitLineEnds execution.
350  v8::Handle<v8::String> origin = v8::String::New("issue-23768-test");
351  v8::Handle<v8::Script> evil_script = v8::Script::Compile(source, origin);
352  CHECK(!evil_script.IsEmpty());
353  CHECK(!evil_script->Run().IsEmpty());
355  i::ExternalTwoByteString::cast(*v8::Utils::OpenHandle(*source)));
356  // This situation can happen if source was an external string disposed
357  // by its owner.
358  i_source->set_resource(NULL);
359 
360  // Must not crash.
361  LOGGER->LogCompiledFunctions();
362 }
363 
364 
365 static v8::Handle<v8::Value> ObjMethod1(const v8::Arguments& args) {
366  return v8::Handle<v8::Value>();
367 }
368 
369 TEST(LogCallbacks) {
370  ScopedLoggerInitializer initialize_logger(false);
371 
374  obj->SetClassName(v8_str("Obj"));
377  proto->Set(v8_str("method1"),
378  v8::FunctionTemplate::New(ObjMethod1,
380  signature),
381  static_cast<v8::PropertyAttribute>(v8::DontDelete));
382 
383  initialize_logger.env()->Global()->Set(v8_str("Obj"), obj->GetFunction());
384  CompileRun("Obj.prototype.method1.toString();");
385 
386  LOGGER->LogCompiledFunctions();
387 
388  bool exists = false;
390  i::ReadFile(initialize_logger.StopLoggingGetTempFile(), &exists, true));
391  CHECK(exists);
392 
394  i::OS::SNPrintF(ref_data,
395  "code-creation,Callback,0x%" V8PRIxPTR ",1,\"method1\"\0",
396  ObjMethod1);
397 
398  CHECK_NE(NULL, StrNStr(log.start(), ref_data.start(), log.length()));
399 
400  obj.Dispose();
401 }
402 
403 
404 static v8::Handle<v8::Value> Prop1Getter(v8::Local<v8::String> property,
405  const v8::AccessorInfo& info) {
406  return v8::Handle<v8::Value>();
407 }
408 
409 static void Prop1Setter(v8::Local<v8::String> property,
410  v8::Local<v8::Value> value,
411  const v8::AccessorInfo& info) {
412 }
413 
414 static v8::Handle<v8::Value> Prop2Getter(v8::Local<v8::String> property,
415  const v8::AccessorInfo& info) {
416  return v8::Handle<v8::Value>();
417 }
418 
419 TEST(LogAccessorCallbacks) {
420  ScopedLoggerInitializer initialize_logger(false);
421 
424  obj->SetClassName(v8_str("Obj"));
426  inst->SetAccessor(v8_str("prop1"), Prop1Getter, Prop1Setter);
427  inst->SetAccessor(v8_str("prop2"), Prop2Getter);
428 
429  LOGGER->LogAccessorCallbacks();
430 
431  bool exists = false;
433  i::ReadFile(initialize_logger.StopLoggingGetTempFile(), &exists, true));
434  CHECK(exists);
435 
436  EmbeddedVector<char, 100> prop1_getter_record;
437  i::OS::SNPrintF(prop1_getter_record,
438  "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop1\"",
439  Prop1Getter);
440  CHECK_NE(NULL,
441  StrNStr(log.start(), prop1_getter_record.start(), log.length()));
442 
443  EmbeddedVector<char, 100> prop1_setter_record;
444  i::OS::SNPrintF(prop1_setter_record,
445  "code-creation,Callback,0x%" V8PRIxPTR ",1,\"set prop1\"",
446  Prop1Setter);
447  CHECK_NE(NULL,
448  StrNStr(log.start(), prop1_setter_record.start(), log.length()));
449 
450  EmbeddedVector<char, 100> prop2_getter_record;
451  i::OS::SNPrintF(prop2_getter_record,
452  "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop2\"",
453  Prop2Getter);
454  CHECK_NE(NULL,
455  StrNStr(log.start(), prop2_getter_record.start(), log.length()));
456 
457  obj.Dispose();
458 }
459 
460 
461 TEST(IsLoggingPreserved) {
462  ScopedLoggerInitializer initialize_logger(false);
463 
464  CHECK(LOGGER->is_logging());
465  LOGGER->ResumeProfiler();
466  CHECK(LOGGER->is_logging());
467  LOGGER->PauseProfiler();
468  CHECK(LOGGER->is_logging());
469 }
470 
471 
473 
474 
475 // Test that logging of code create / move events is equivalent to traversal of
476 // a resulting heap.
477 TEST(EquivalenceOfLoggingAndTraversal) {
478  // This test needs to be run on a "clean" V8 to ensure that snapshot log
479  // is loaded. This is always true when running using tools/test.py because
480  // it launches a new cctest instance for every test. To be sure that launching
481  // cctest manually also works, please be sure that no tests below
482  // are using V8.
483  //
484  // P.S. No, V8 can't be re-initialized after disposal, see include/v8.h.
486 
487  // Start with profiling to capture all code events from the beginning.
488  ScopedLoggerInitializer initialize_logger(false);
489 
490  // Compile and run a function that creates other functions.
491  CompileRun(
492  "(function f(obj) {\n"
493  " obj.test =\n"
494  " (function a(j) { return function b() { return j; } })(100);\n"
495  "})(this);");
497  HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask);
498  LOGGER->StringEvent("test-logging-done", "");
499 
500  // Iterate heap to find compiled functions, will write to log.
501  LOGGER->LogCompiledFunctions();
502  LOGGER->StringEvent("test-traversal-done", "");
503 
504  bool exists = false;
506  i::ReadFile(initialize_logger.StopLoggingGetTempFile(), &exists, true));
507  CHECK(exists);
508  v8::Handle<v8::String> log_str = v8::String::New(log.start(), log.length());
509  initialize_logger.env()->Global()->Set(v8_str("_log"), log_str);
510 
513  reinterpret_cast<const char*>(source.start()), source.length());
514  v8::TryCatch try_catch;
515  v8::Handle<v8::Script> script = v8::Script::Compile(source_str, v8_str(""));
516  if (script.IsEmpty()) {
517  v8::String::Utf8Value exception(try_catch.Exception());
518  printf("compile: %s\n", *exception);
519  CHECK(false);
520  }
521  v8::Handle<v8::Value> result = script->Run();
522  if (result.IsEmpty()) {
523  v8::String::Utf8Value exception(try_catch.Exception());
524  printf("run: %s\n", *exception);
525  CHECK(false);
526  }
527  // The result either be a "true" literal or problem description.
528  if (!result->IsTrue()) {
529  v8::Local<v8::String> s = result->ToString();
530  i::ScopedVector<char> data(s->Length() + 1);
531  CHECK_NE(NULL, data.start());
532  s->WriteAscii(data.start());
533  printf("%s\n", data.start());
534  // Make sure that our output is written prior crash due to CHECK failure.
535  fflush(stdout);
536  CHECK(false);
537  }
538 }
void Enter()
Definition: api.cc:742
byte * Address
Definition: globals.h:157
const SwVfpRegister s2
V8EXPORT int Length() const
Definition: api.cc:3741
static Local< Script > Compile(Handle< String > source, ScriptOrigin *origin=NULL, ScriptData *pre_data=NULL, Handle< String > script_data=Handle< String >())
Definition: api.cc:1568
#define CHECK_EQ(expected, value)
Definition: checks.h:219
V8EXPORT bool IsTrue() const
Definition: api.cc:2143
static Local< FunctionTemplate > New(InvocationCallback callback=0, Handle< Value > data=Handle< Value >(), Handle< Signature > signature=Handle< Signature >())
Definition: api.cc:951
#define V8PRIxPTR
Definition: globals.h:189
void Dispose()
Definition: v8.h:4235
Thread(const Options &options)
static bool UseCrankshaft()
Definition: v8.h:86
static const char *const kLogToTemporaryFile
Definition: log-utils.h:62
#define CHECK_GT(a, b)
Definition: checks.h:227
static V8EXPORT Local< String > New(const char *data, int length=-1)
Definition: api.cc:4779
static ExternalTwoByteString * cast(Object *obj)
Local< ObjectTemplate > InstanceTemplate()
Definition: api.cc:1122
static Vector< const byte > GetScriptsSource()
unsigned short uint16_t
Definition: unicode.cc:46
i::NativesCollection< i::TEST > TestSources
Definition: test-log.cc:472
V8EXPORT Local< String > ToString() const
Definition: api.cc:2313
#define CHECK(condition)
Definition: checks.h:56
void Set(Handle< String > name, Handle< Data > value, PropertyAttribute attributes=None)
Definition: api.cc:902
virtual const uint16_t * data() const =0
void SetClassName(Handle< String > name)
Definition: api.cc:1139
virtual size_t length() const =0
TEST(ProfLazyMode)
Definition: test-log.cc:99
T * start() const
Definition: utils.h:390
Vector< const char > ReadFile(const char *filename, bool *exists, bool verbose)
Definition: v8utils.cc:182
void SetAccessor(Handle< String > name, AccessorGetter getter, AccessorSetter setter=0, Handle< Value > data=Handle< Value >(), AccessControl settings=DEFAULT, PropertyAttribute attribute=None, Handle< AccessorSignature > signature=Handle< AccessorSignature >())
Definition: api.cc:1291
static void ResumeProfiler()
Definition: api.cc:5468
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: globals.h:307
int length() const
Definition: utils.h:384
static Persistent< T > New(Handle< T > that)
Definition: v8.h:4206
static const int kMakeHeapIterableMask
Definition: heap.h:1088
activate correct semantics for inheriting readonliness false
#define CHECK_NE(unexpected, value)
Definition: checks.h:223
static void Sleep(const int milliseconds)
int StrLength(const char *string)
Definition: utils.h:234
const SwVfpRegister s1
static int SNPrintF(Vector< char > str, const char *format,...)
Local< Function > GetFunction()
Definition: api.cc:4675
static int GetCurrentThreadId()
Definition: api.cc:5480
void SampleStack(TickSample *sample)
Definition: platform.h:742
Local< ObjectTemplate > PrototypeTemplate()
Definition: api.cc:928
#define HEAP
Definition: isolate.h:1433
static Local< Signature > New(Handle< FunctionTemplate > receiver=Handle< FunctionTemplate >(), int argc=0, Handle< FunctionTemplate > argv[]=0)
Definition: api.cc:978
activate correct semantics for inheriting readonliness enable harmony semantics for typeof enable harmony enable harmony proxies enable all harmony harmony_scoping harmony_proxies harmony_scoping tracks arrays with only smi values automatically unbox arrays of doubles use crankshaft use hydrogen range analysis use hydrogen global value numbering use function inlining maximum number of AST nodes considered for a single inlining loop invariant code motion print statistics for hydrogen trace generated IR for specified phases trace register allocator trace range analysis trace representation types environment for every instruction put a break point before deoptimizing polymorphic inlining perform array bounds checks elimination use dead code elimination trace on stack replacement optimize closures cache optimized code for closures functions with arguments object loop weight for representation inference allow uint32 values on optimize frames if they are used only in safe operations track parallel recompilation enable all profiler experiments number of stack frames inspected by the profiler call recompile stub directly when self optimizing trigger profiler ticks based on counting instead of timing weight back edges by jump distance for interrupt triggering percentage of ICs that must have type info to allow optimization watch_ic_patching retry_self_opt interrupt_at_exit extra verbose compilation tracing generate extra emit comments in code disassembly enable use of SSE3 instructions if available enable use of CMOV instruction if available enable use of SAHF instruction if enable use of VFP3 instructions if available this implies enabling ARMv7 and VFP2 enable use of VFP2 instructions if available enable use of SDIV and UDIV instructions if enable loading bit constant by means of movw movt instruction enable unaligned accesses for enable use of MIPS FPU instructions if NULL
static void StrNCpy(Vector< char > dest, const char *src, size_t n)
V8EXPORT int WriteAscii(char *buffer, int start=0, int length=-1, int options=NO_OPTIONS) const
Definition: api.cc:3984
bool IsEmpty() const
Definition: v8.h:209
Local< Value > Run()
Definition: api.cc:1598
static Persistent< Context > New(ExtensionConfiguration *extensions=NULL, Handle< ObjectTemplate > global_template=Handle< ObjectTemplate >(), Handle< Value > global_object=Handle< Value >())
Definition: api.cc:4411
virtual void Tick(TickSample *sample)=0
static V8EXPORT Local< String > NewExternal(ExternalStringResource *resource)
Definition: api.cc:4871
#define LOGGER
Definition: isolate.h:1436
Definition: v8.h:106
static void PauseProfiler()
Definition: api.cc:5462
static bool IsRunning()
Definition: v8.h:85