duktape js — имеет несколько контекстов с собственным глобальным и ссылкой на один общий «синглтон»

#duktape

Вопрос:

Мы находимся в процессе внедрения JS в наше приложение, и мы будем использовать несколько десятков сценариев, каждый из которых назначен для события. Внутри этих сценариев мы предоставляем минимальный api обратного вызова,

 function onevent(value)
{ // user javascript code here
}
 

который вызывается всякий раз, когда происходит это событие. Сценарии должны иметь свой собственный глобальный, так как эта функция всегда имеет одно и то же имя, и мы получаем доступ к ней из кода cpp с помощью

 duk_get_global_string(js_context_duk, "onevent");
duk_push_number(js_context_duk, val);
if (duk_pcall(js_context_duk, 1) != 0)
{
    printf("Duk error: %sn", duk_safe_to_string(js_context_duk, -1));
}
duk_pop(js_context_duk);  /* ignore result */
 

Опять же, мы хотим обеспечить минимальную связь между сценариями, например

Сценарий 1

 var a = 1;
function onevent(val)
{
log(a);
}
 

Сценарий 2

 function onevent(val)
{
 a  ;
}
 

Есть ли способ достичь этого? Может быть, путем введения собственного глобального объекта «ueber», который определяется один раз и на который можно ссылаться везде? Должно быть возможно добавить свойства к этому «глобальному объекту ueber» из любого скрипта, например

Сценарий 1

 function onevent(val)
{
log(ueber.a);
}
 

Сценарий 2

 function onevent(val)
{
 ueber.a=1;
}
 

Ответ №1:

Вместо простых файлов JS вы могли бы использовать модули. duktape поставляется с примером кода для реализации модульной системы (включая ее изоляцию кода), как в Node.js. Имея это на месте, вы можете экспортировать переменные, которые должны быть доступны для совместного использования.

Комментарии:

1. Спасибо вам за ваш ответ! Но я проверил модульный подход, и кэш модуля связан с глобальным объектом контекста через duk_push_global_stash(ctx) в duk_module_node.c, поэтому модули не имеют желаемого «одноэлементного» поведения, или я что-то пропустил? Вот что говорится в примере связанного кода: «После этих шагов require() будет зарегистрирован в глобальном объекте, и система модулей готова к использованию».

Ответ №2:

У нас есть подход, который, похоже, работает и сейчас. После создания нового контекста с

 duk_push_thread_new_globalenv(master_ctx);
new_ctx = duk_require_context(master_ctx, -1);
duk_copy_element_reference(master_ctx, new_ctx, "ueber");
 

мы выполняем эту последовательность вызовов для всех свойств/объектов/функций, созданных в основном контексте:

 void duk_copy_element_reference(duk_context* src, duk_context* dst, const char* element)
{
    duk_get_global_string(src, element);
    duk_require_stack(dst, 1);
    duk_xcopy_top(dst, src, 1);
    duk_put_global_string(dst, element);
}
 

Кажется, это работает (потому что все находится в одной куче и все однопоточные). Может быть, кто-нибудь с более глубоким пониманием duktape может прокомментировать это? Является ли это осуществимым решением без побочных эффектов?

правка: отметьте это как ответ. работает, как и ожидалось, никаких утечек памяти или других проблем.