Правильный способ запуска одного и того же кода дважды в v8 (выход за пределы массива завершается неудачно при втором запуске - деоптимизатор)

Следующая программа основана на примере на странице "Начало работы с v8". Я сделал три изменения, чтобы продемонстрировать проблему, с которой я столкнулся:

  • Я создаю пустой массив и помещаю его в глобальный контекст.
  • Запускаемый скрипт ссылается на нулевой элемент в массиве, который должен возвращать undefined.
  • Я запускаю скомпилированный скрипт дважды.

Первый запуск работает отлично. Второй сбой: v8 вызывает V8_Fatal() в Deoptimizer::DoComputeCompiledStubFrame(), потому что 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, но никто не ответил, поэтому я надеюсь, что сообщество Stackru может помочь.
  • Я вижу это на VS2012 Update 4, но я также вижу на VS2008, и в x64 и x86, и в сборках Debug и Release.

1 ответ

Решение

ОК, нашел это. Проблема заключается в заглушке неинициализированного кода для загрузки словаря - ваш вариант использования вызывает это как сбой, поскольку заглушка не инициализируется другими способами, например, компиляцией.

Ниже приведен патч против версии 22629 транка v8, который исправляет эту проблему для меня, протестирован на 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
Другие вопросы по тегам