📜 ⬆️ ⬇️

Extension authors, browser hackers, meet js-ctypes

What is (you may ask) js-ctypes? Let's say you write an extension in javascript, and it needs to refer to the native code. For example, weave-crypto has to access the NSS library. And your extension may wish, for example, to call directly NSPR, libc, or Win32 functions. Right now there are two options: either use scripted XPCOM interfaces (provided by libxul), or write and implement your own XPCOM interfaces, that is, supply binary code in its extension. If the first option is no good, only the second remains, but then it becomes much more difficult to deliver the extension: you have to separately compile the binary code for each of the supported platforms in order to pack it inside your cross-platform xpi.

The answer to this difficulty will therefore be the js-ctypes library: it allows javascript to call local code (written in C) and manipulate sishnyh data types without using XPCOM, and there is no need to compile any line of code. This means that you do not have to define XPCOM interfaces, and that you can use shared libraries, like libc, directly. There is also a side benefit: we mostly eliminate XPConnect data type conversion losses, so code execution can become faster. (I'll compare the speed in one of the subsequent blog posts.) The js-ctypes library will be shipped with Gecko 1.9.3, and this platform (if the version numbers do not change) will become the foundation for Firefox 3.7.

You may ask: "But how ...?". And here are some examples (they were tested on x86 32-bit linux and contain non-cross-platform parts):
')
1) Opening the library.
// 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) Definition and use of structural types (struct) and arrays.
// 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) Creating C-like pointers to functions pointing to javascript functions.

(Note that this part of the library is not ready yet, but we are working on a patch.)
// ,
// , :
// -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 .

So, if you're the author of the extension, or writing code for the browser, keep in mind js-ctypes — and let us know how things are going!

Source: https://habr.com/ru/post/87464/


All Articles