在v8中运行相同代码两次的正确方法(数组越界在第二次运行时失败 - deoptimizer)

时间:2014-07-16 01:59:23

标签: v8 embedded-v8

以下程序基于v8 Getting Started page中的示例。我做了三处更改来证明我遇到的问题:

  • 我创建了一个空数组,将其放入全局上下文中。
  • 正在运行的脚本引用数组中的第0个元素,该元素应返回undefined。
  • 我运行编译过的脚本两次。

第一次运行正常。第二个失败:v8在Deoptimizer :: DoComputeCompiledStubFrame()中调用V8_Fatal(),因为descriptor-> register_param_count_ == -1。

我在这里做错了吗?我该如何解决?

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;

注意:

  • 这是main()的整个主体。
  • JavaScript代码的其他片段运行两次没有问题。不知何故,这与超出范围的数组引用有关,这可能会引发去优化。
  • 我不想每次都从头开始重新编译脚本,因为我通常会运行这些脚本数千次,有时甚至数百万次。
  • 我尝试将脚本编译为UnboundScript,然后为每次执行绑定它,但结果是一样的。
  • 我已将此报告为v8 issue,但没有人回复,所以我希望StackOverflow社区可以提供帮助。
  • 我在VS2012 Update 4上看到了这一点,但我也在VS2008,x64和x86以及Debug和Release版本中看到了它。

1 个答案:

答案 0 :(得分:4)

好的,找到了。问题是字典加载的未初始化代码存根 - 您的用例会将此触发为失败,因为存根未通过其他方式初始化,例如编译。

下面是针对v8主干版本22629的补丁,它修复了我的问题,在Windows上使用VS 2010和Linux使用g ++ 4.9进行了测试。请告诉我你是怎么做的:

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);
   }

作为一种解决方法,如果您现在不想编译自己的V8,则可以在运行代码之前直接使用Isolate的每个KeyedLoadDictionaryElementStub执行一些代码 - - 这应该初始化存根。以下内容适用于我:

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