Что такое (можете спросить вы) js-ctypes? Скажем, пишете вы расширение на джаваскрипте, и ему нужно обратиться к местному коду (native code). Например, weave-crypto приходится обращаться к библиотеке NSS. А ваше расширение может пожелать, например, вызвать напрямую NSPR, libc, или функции Win32. Прямо сейчас есть два выхода: либо использовать скриптуемые XPCOM-интерфейсы (обеспечиваемые libxul), либо написать и реализовать собственные XPCOM-интерфейсы, то есть поставлять двоичный код в своём расширении. Если первый вариант не годится, остаётся только второй, но тогда поставлять расширение становится заметно сложнее: приходится отдельно компилировать двоичный код для каждой из поддерживаемых платформ, чтобы упаковать его внутрь вашего кросс-платформенного xpi.
Ответом на эту трудность поэтому станет библиотека js-ctypes: она позволяет джаваскрипту вызывать местный код (написанный на Си) и манипулировать сишными типами данных, без использования XPCOM, и нет необходимости компилировать ни одну строку кода. Это означает, что вам не придётся определять XPCOM-интерфейсы, и что можно будет использовать разделяемые библиотеки (shared libraries), подобные libc, напрямую. Есть и побочный положительный эффект: мы большей частью устраняем потери на преобразование типов данных, свойственные XPConnect, так что выполнение кода может становиться быстрее. (Сравнение скорости я приведу в одной из последующих блогозаписей.) Библиотека js-ctypes будет поставляться с Gecko 1.9.3, а эта платформа (если номера версий не переменятся) станет фундаментом для Firefox 3.7.
Вы можете спросить: «Но как...?». И вот примеры (они проверялись на 32-битных Линуксах x86 и содержатне кросс-платформенные части):
1) Открытие библиотеки.
2) Определение и использование структурных типов (struct) и массивов.
3) Создание Си-подобных указателей на функции, указывающих на джаваскриптовые функции.
(Заметим, что эта часть библиотеки ещё не готова, но над патчем идёт работа.)
Итак, если вы автор расширения,или пишете код для браузера, имейте в виду js-ctypes — и дайте нам знать, как пойдут дела!
Ответом на эту трудность поэтому станет библиотека js-ctypes: она позволяет джаваскрипту вызывать местный код (написанный на Си) и манипулировать сишными типами данных, без использования XPCOM, и нет необходимости компилировать ни одну строку кода. Это означает, что вам не придётся определять XPCOM-интерфейсы, и что можно будет использовать разделяемые библиотеки (shared libraries), подобные libc, напрямую. Есть и побочный положительный эффект: мы большей частью устраняем потери на преобразование типов данных, свойственные XPConnect, так что выполнение кода может становиться быстрее. (Сравнение скорости я приведу в одной из последующих блогозаписей.) Библиотека js-ctypes будет поставляться с Gecko 1.9.3, а эта платформа (если номера версий не переменятся) станет фундаментом для Firefox 3.7.
Вы можете спросить: «Но как...?». И вот примеры (они проверялись на 32-битных Линуксах x86 и содержат
1) Открытие библиотеки.
// Сперва импортируем модуль ctypes.
Components.utils.import("resource://gre/modules/ctypes.jsm");
// Открываем libc.
let library = ctypes.open("libc.so.6");
// Определяем функцию fopen, имеющую следующий Си-прототип:
// FILE* fopen(const char* name, const char* mode);
let fopen = library.declare("fopen", // имя символа
ctypes.default_abi, // конвенция вызова cdecl
ctypes.PointerType("FILE*"), // возвращаемый функцией тип (FILE*)
ctypes.char.ptr, // первый аргумент (const char*)
ctypes.char.ptr); // второй аргумент (const char*)
// Вызываем функцию, она возвращает объект указателя FILE*.
let file = fopen("hello world.txt", "w");
// Что такое 'file'?
file.toString();
// выдаст "ctypes.PointerType("FILE*")(ctypes.UInt64("0x9781b38"))" (значение указателя)
// ... далее пишем данные в файл ...
* This source code was highlighted with Source Code Highlighter.
2) Определение и использование структурных типов (struct) и массивов.
// Определяем struct по имени 'hostent', содержащий пять полей,
// каждое из которых имеет свой тип.
// Вот соответствующее определение на Си:
// struct hostent {
// char* h_name; // имя хоста
// char** h_aliases; // массив строк, содержащих псевдонимы хоста
// int h_addrtype; // используется ли адрес IPv4 или IPv6
// int h_length; // длина (в байтах) IP-адреса
// char** h_addr_list; // массив IP-адресов (от нэймсервера)
// };
let hostent = ctypes.StructType("hostent",
[{ h_name : ctypes.char.ptr },
{ h_aliases : ctypes.char.ptr.ptr },
{ h_addrtype : ctypes.int },
{ h_length : ctypes.int },
{ h_addr_list : ctypes.uint8_t.array(4).ptr.ptr }]);
// Определяем функцию 'gethostbyname', имеющую нижеследующий Си-прототип:
// struct hostent* gethostbyname(const char* name);
let gethostbyname = library.declare("gethostbyname",
ctypes.default_abi,
hostent.ptr, // используем наш тип 'hostent'
ctypes.char.ptr);
// Просим нашу функцию обработать имя хоста.
let google = gethostbyname("mail.google.com");
// По полученному указателю получим struct типа 'hostent',
// обращаемся к его полю 'h_name', и преобразуем к джаваскриптовой
// строке. Наши действия примерно подобны вот этому оператору Си:
// printf("%s", google->h_name);
google.contents.h_name.readString();
// выдаст "googlemail.l.google.com"
// Дважды следуя по указателю из поля 'h_addr_list',
// получим первый элемент массива, который и сам является
// массивом из четырёх байтов IPv4-адреса хоста.
// Вот примерный Си-эквивалент:
// printf("%u.%u.%u.%u", (int) h_addr_list[0][0], (int) h_addr_list[0][1],
// (int) h_addr_list[0][2], (int) h_addr_list[0][3]);
google.contents.h_addr_list.contents.contents.toString();
// выдаст "ctypes.uint8_t.array(4)([74, 125, 19, 17])", то есть 74.125.19.17
* This source code was highlighted with Source Code Highlighter.
3) Создание Си-подобных указателей на функции, указывающих на джаваскриптовые функции.
(Заметим, что эта часть библиотеки ещё не готова, но над патчем идёт работа.)
// Определим тип функции сравнения, которая принимает два указателя
// на элементы, а затем возвращает:
// -1, если i < j;
// 0, если i == j;
// 1, если i > j.
// Эквивалентный Си-код таков:
// typedef int (comparator_t*)(const int8_t* i, const int8_t* j);
let comparator_t = ctypes.FunctionType(ctypes.default_abi, ctypes.int,
ctypes.int8_t.ptr, ctypes.int8_t.ptr);
// Каков Си-тип 'comparator_t'?
comparator_t.name;
// выдаст "int (*)(int8_t*,int8_t*)"
// Определим функцию 'qsort', которая принимает массив элементов,
// и функцию сравнения, и сортирует массив.
// void qsort(void* array, size_t length, size_t elemsize, comparator_t comp);
let qsort = library.declare("qsort", ctypes.default_abi, ctypes.void_t,
ctypes.voidptr_t, ctypes.size_t, ctypes.size_t, comparator_t);
// Реализуем джваскриптовую функцию, точно соответствующую вышеприведённому 'comparator_t'.
function reverse(i, j) { return j.contents - i.contents; }
// Создадим сишный указатель на функцию, указывающий на нашу джаваскриптовую функцию.
let reverse_ptr = comparator_t(reverse);
// Что такое 'reverse_ptr'?
reverse_ptr.toString();
// выдаст "ctypes.FunctionType(ctypes.default_abi, ctypes.int, ctypes.int8_t.ptr,
// ctypes.int8_t.ptr)(ctypes.UInt64("0x81a430cb"))"
// Создадим массив значений и вызовем 'qsort'.
let array_t = ctypes.int8_t.array();
let ints = array_t([3, 1, 5, 6, 4, 2]);
qsort(ints.address(), ints.length, array_t.elementType.size, reverse_ptr);
// Вуаля!
ints.toString();
// выдаст "ctypes.int8_t.array(6)([6, 5, 4, 3, 2, 1])"
* This source code was highlighted with Source Code Highlighter.
Итак, если вы автор расширения,