Не так давно вышла свежая версия 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))
Результат:
Скачать тестовый проект.