Встраиваем JavaScript в Си приложение

Не так давно вышла свежая версия JS движка Duktape.
И наверное многим стоит о ней узнать. Библиотека компактна! Всего три файла. Но эти три файла позволят Вам встроить полноценный интерпретатор JavaScript в Ваше приложение на Си.

Для начала работы Вам нужно добавить в проект 3 файла: duktape.c, duktape.h и duk_config.h.

Библиотека действительно легковесна.

x86 default x86 lowmem x86 full lowmem
Code 170kB 150kB 200kB
Startup RAM 68kB 35kB 4kB

Простейшее приложение

#include "duktape.h"

int main()
{
        duk_context *ctx = duk_create_heap_default();
        if (ctx) {
                duk_eval_string(ctx, "print('Hello world from Javascript!', 5);");
                duk_destroy_heap(ctx);
        }
        return 0;
}

При выполнении Вы увидите сообщение.

Загрузка JavaScript файла

Но не всегда удобно держать JavaScript в си строке. Ведь мы скорее всего хотим делать дополнения или систему скриптования для нашего приложения.
На этот случай в Duktape есть возможность загрузить файл.

Создадим файл process.js в который поместим функцию process, которую потом вызовем из Си кода.

// process.js
function process(arg, arg2) {
    print(arg, arg2);
    return "Hi, there";
}

Как видно эта функция принимает 2 параметра, выводит их в консоль и возвращает строку «Hi, there».

#include "duktape.h"

int main()
{
        duk_context *ctx = duk_create_heap_default();

        if (duk_peval_file(ctx, "process.js") != 0) {
                printf("Error: %s\n", duk_safe_to_string(ctx, -1));
                duk_destroy_heap(ctx);
                return 1;
        }
        duk_pop(ctx);

        duk_push_global_object(ctx);


        //get js function, set it parameter and call
        duk_get_prop_string(ctx, -1 /*index*/, "process");

        duk_push_number(ctx, 123);
        duk_push_string(ctx, "foo");

        if (duk_pcall(ctx, 2 /*nargs*/) != 0) {
                printf("Error: %s\n", duk_safe_to_string(ctx, -1));
        }
        else {
                //print result
                printf("%s\n", duk_safe_to_string(ctx, -1));
        }
        duk_pop(ctx);  /* pop result/error */
        duk_destroy_heap(ctx);
    return 0;
}

Как и в предыдущем примере здесь мы создаем кучу для js движка, затем загружаем содержимое файлы process.js и создаем глобальный контекст.
Вся работа с Js строится на использовании стека значений. Таким образмо нам нужно выбрать функцию, которую мы хотим вызвать, а затем протолкнуть в стек параметры для неё. Когда это сделано вызываем фунцию при помощи duk_pcall. Эта функция принимает в качестве второго аргумента — количество параметров вызываемой функции.
Если все прошло успешно, мы можем получить возвращенный результат при помощи вызова duk_safe_to_string.

Си функции в JavaScript

Также имеется возможность выполнять nativ функции из JavaScript.
Создадим функцию, которая возвращает квадрат числа, которое передано ей как параметр.

duk_ret_t my_native_func(duk_context *ctx) {
        double arg = duk_require_number(ctx, 0 /*index*/);
        duk_push_number(ctx, arg * arg);
        return 1;
}

Теперь нам нужно зарегистрировать её.

duk_push_c_function(ctx, my_native_func, 1);
duk_put_prop_string(ctx, -2, "nativ_func");

Теперь функцию nativ_func можно вызывать из js кода.
Например так:

print("Native function returned: ", nativ_func(5))

Результат:

Скачать тестовый проект.

 

Похожий код:

Фото аватара
Алексей Петров

Программист, разработчик с 5 летним опытом работы. Учусь на разработчика игр на Unity и разработчика VR&AR реальности (виртуальной реальности). Основные языки программирования: C#, C++.

Оцените автора
Бла, бла код
Добавить комментарий