Запускаем JavaScript-код внутри .NET-приложения, используя ChakraCore

Бибилотеку ChakraCore можно использовать для встраивания в собственные приложения. Благодаря этому ваше приложение научится запускать произвольный Javascript-код.

Код ChakraCore написан на C++. Поэтому не представляет особого труда импортировать эту библиотеку в собственные проекты C++. Можно использовать PInvoke и запускать этот код из среды .NET.

Подготовка проекта

В первую очередь готовим библиотеку ChakraCore.dll — это и есть движок CharkCore. Проект распростарняется в виде исходного кода, поэтому его нужно загрузить себе и собрать.

  1. Загружаем исходный код ChakraCore.
  2. В Visual Studio открываем проект Build\Chakra.Core.sln.
  3. Выбираем платформу, на которой будет запускаться код - x86, x64 или ARM. Если приложение будет запускаться на нескольких платформах, нужно собрать разные собрки.
  4. Собираем.
  5. В папке Build\VcBuild\bin\platform забираем файл ChakraCore.dll - он потребуется нам на следующем шаге.

Исполнение кода JavaScript

ChakraCore.dll — это не .NET-сборка. Поэтому нам потребуются .NET-обертки для запуска кода. К нашему удобству такие обертки доступны среди примеров использования ChakraCore.

  1. Создаем новый .NET-проект.
  2. Добавляем к проекту ChakraCore.dll.
  3. Копируем в проект обертки для ChakraCore.dll.

Для доступа к ChakraCore используется объект JavaScriptRuntime. После завершения работы, этот объект необходимо освободить, поэтому обернем его в using.

JavaScriptRuntime runtime;
Native.JsCreateRuntime(JavaScriptRuntimeAttributes.None, null, out runtime);

using (runtime)
{
    // ...
}

Определяем контекст исполнения JS-кода:

JavaScriptSourceContext currentSourceContext = JavaScriptSourceContext.FromIntPtr(IntPtr.Zero);
JavaScriptContext context;
Native.JsCreateContext(runtime, out context);
Native.JsSetCurrentContext

И запускаем:

JavaScriptValue result;
Native.JsRunScript(script, currentSourceContext++, "", out result);

Не забываем, что мы работаем с нативной библиотекой, поэтому данные нам вернуться в виде указателей на результат. Нехитрыми преобразованиями получаем из них .NET-объект:

IntPtr resultPtr;
UIntPtr stringLength;
Native.JsStringToPointer(resultJSString, out resultPtr, out stringLength);

Native.JsSetCurrentContext(JavaScriptContext.Invalid);

string result = Marshal.PtrToStringUni(resultPtr);

Собираем результат

В моем случае в качестве примера используется консольное приложение. При запуске приложения получаем от пользователя два числа и просим Javascript-функцию сложить их. Результат выводим в консоль.

static void Main(string[] args)
{
    int a = int.Parse(Console.ReadLine());
    int b = int.Parse(Console.ReadLine());

    string script = String.Format(
        "((a, b)=>{" +
        "return a+b;" +
        "})({0}, {1})", a, b);

    string result = RunJavascript(script);

    Console.WriteLine(result);
    Console.Read();
}

private static string RunJavascript(string script)
{
    JavaScriptRuntime runtime;
    Native.JsCreateRuntime(JavaScriptRuntimeAttributes.None, null, out runtime);

    using (runtime)
    {
        JavaScriptSourceContext currentSourceContext = JavaScriptSourceContext.FromIntPtr(IntPtr.Zero);

        JavaScriptContext context;
        Native.JsCreateContext(runtime, out context);

        Native.JsSetCurrentContext(context);

        JavaScriptValue result;
        Native.JsRunScript(script, currentSourceContext++, "", out result);

        JavaScriptValue resultJSString;
        Native.JsConvertValueToString(result, out resultJSString);

        IntPtr resultPtr;
        UIntPtr stringLength;
        Native.JsStringToPointer(resultJSString, out resultPtr, out stringLength);

        Native.JsSetCurrentContext(JavaScriptContext.Invalid);

        return Marshal.PtrToStringUni(resultPtr);
    }
}

Результат: