Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Right way to run the same code twice in v8 (array out-of-bounds fails on second run - deoptimizer)

Tags:

v8

embedded-v8

The following program is based on the example in the v8 Getting Started page. I have made three changes to demonstrate a problem I am encountering:

  • I create an empty array put it into the global context.
  • The script being run references the zeroth element in the array, which should return undefined.
  • I run the compiled script twice.

The first run works fine. The second fails: v8 calls V8_Fatal() in Deoptimizer::DoComputeCompiledStubFrame() because descriptor->register_param_count_ == -1.

Am I doing something wrong here? How can I fix it?

Isolate* isolate = Isolate::New();
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
Local<Context> context = Context::New(isolate);
Context::Scope context_scope(context);
Local<Array> a = Array::New(isolate);
context->Global()->Set(String::NewFromUtf8(isolate, "a"), a);
Local<String> source = String::NewFromUtf8(isolate, "a[0];");
Local<Script> script = Script::Compile(source);
Local<Value> result = script->Run();
Local<Value> result2 = script->Run();
return 0;

NOTES:

  • This is the entire body of main().
  • Other fragments of JavaScript code run twice without a problem. Somehow this relates to the out-of-bound array reference, which is perhaps triggering deoptimization.
  • I do not want to recompile the script from scratch each time because I am typically running these scripts thousands of times, and sometimes millions of times.
  • I have tried compiling the script as an UnboundScript and then binding it for each execution, but the result is the same.
  • I have reported this as a v8 issue, but nobody has responded so I'm hoping that the StackOverflow community can help.
  • I am seeing this on VS2012 Update 4, but I also see it on VS2008, and in both x64 and x86 and in both Debug and Release builds.
like image 308
Oliver Bock Avatar asked Jul 16 '14 01:07

Oliver Bock


1 Answers

OK, found it. The problem is an uninitialized code stub for dictionary loads - your use case triggers this as a failure as the stub isn't initialized through other means, eg compilation.

Below is a patch against v8 trunk revision 22629 that fixes the problem for me, tested on Windows with VS 2010 and Linux with g++ 4.9. Please let me know how you go with this:

Index: src/code-stubs.cc
===================================================================
--- src/code-stubs.cc   (revision 22629)
+++ src/code-stubs.cc   (working copy)
@@ -236,6 +236,8 @@
     CODE_STUB_LIST(DEF_CASE)
 #undef DEF_CASE
     case UninitializedMajorKey: return "<UninitializedMajorKey>Stub";
+    case NoCache:
+        return "<NoCache>Stub";
     default:
       if (!allow_unknown_keys) {
         UNREACHABLE();
@@ -939,6 +941,13 @@


 // static
+void KeyedLoadDictionaryElementStub::InstallDescriptors(Isolate* isolate) {
+  KeyedLoadDictionaryElementStub stub(isolate);
+  InstallDescriptor(isolate, &stub);
+}
+
+
+// static
 void KeyedLoadGenericElementStub::InstallDescriptors(Isolate* isolate) {
   KeyedLoadGenericElementStub stub(isolate);
   InstallDescriptor(isolate, &stub);
Index: src/code-stubs.h
===================================================================
--- src/code-stubs.h    (revision 22629)
+++ src/code-stubs.h    (working copy)
@@ -1862,6 +1862,8 @@
   virtual void InitializeInterfaceDescriptor(
       CodeStubInterfaceDescriptor* descriptor) V8_OVERRIDE;

+  static void InstallDescriptors(Isolate* isolate);
+
  private:
   Major MajorKey() const { return KeyedLoadElement; }
   int NotMissMinorKey() const { return DICTIONARY_ELEMENTS; }
Index: src/isolate.cc
===================================================================
--- src/isolate.cc  (revision 22629)
+++ src/isolate.cc  (working copy)
@@ -2000,6 +2000,7 @@
     NumberToStringStub::InstallDescriptors(this);
     StringAddStub::InstallDescriptors(this);
     RegExpConstructResultStub::InstallDescriptors(this);
+    KeyedLoadDictionaryElementStub::InstallDescriptors(this);
     KeyedLoadGenericElementStub::InstallDescriptors(this);
   }

As a workaround if you don't want to compile your own V8 for now, you could execute some code on each Isolate that uses the KeyedLoadDictionaryElementStub directly, prior to running your code --- this should initialize the stub. Something like the following works for me:

Isolate* isolate = Isolate::New();
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
Local<Context> context = Context::New(isolate);
Context::Scope context_scope(context);
Local<Array> a = Array::New(isolate);
context->Global()->Set(String::NewFromUtf8(isolate, "a"), a);

// Workaround code for initializing KeyedLoadDictionaryElementStub 
Local<String> workaround_source = String::NewFromUtf8(isolate, "Math.random()");
Local<Script> workaround_script = Script::Compile(workaround_source);
Local<Value>  workaround_value  = workaround_script->Run();
// End workaround

Local<String> source = String::NewFromUtf8(isolate, "a[0]");
Local<Script> script = Script::Compile(source);

// ...and so on
like image 173
clstrfsck Avatar answered Nov 08 '22 20:11

clstrfsck