From 122867c45c7f41b82a550a9665d34b7dda1c3ffa Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Sep 2018 16:01:51 +0100 Subject: WebAssembly support! Quite a lot going on in this PR: * Updates to support recent emscripten, switching to WASM which is now the default * Use emscripten's MODULARIZE option rather than wrapping it ourself, since doing so in pre-post js doesn't work anymore. * Most changes are moving the emscripten runtime functions to top-level calls rather than in the Module object. * Get rid of duplicated NULL_BYTE_PADDING_LENGTH * Fix ciphertext_length used without being declared * Fix things that caused the closure compiler to error, eg. using OLM_OPTIONS without a declaration. * Wait until module is inited to do OLM_ERROR = olm_error() The main BREAKING CHANGE here is that the module now needs to initialise asyncronously (because it has to load the wasm file). require()ing olm now gives a function which needs to be called to create an instance. The resulting object has a promise-like then() method that can be used to detect when the module is ready. (We could use MODULARIZE_INSTANCE to return the module directly as before, rather than the function, but then we don't get the .then() method). --- javascript/olm_post.js | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) (limited to 'javascript/olm_post.js') diff --git a/javascript/olm_post.js b/javascript/olm_post.js index 7a1d284..071021c 100644 --- a/javascript/olm_post.js +++ b/javascript/olm_post.js @@ -1,27 +1,17 @@ -var runtime = Module['Runtime']; var malloc = Module['_malloc']; var free = Module['_free']; -var Pointer_stringify = Module['Pointer_stringify']; -var OLM_ERROR = Module['_olm_error'](); - -/* The 'length' argument to Pointer_stringify doesn't work if the input - * includes characters >= 128, which makes Pointer_stringify unreliable. We - * could use it on strings which are known to be ascii, but that seems - * dangerous. Instead we add a NULL character to all of our strings and just - * use UTF8ToString. - */ -var NULL_BYTE_PADDING_LENGTH = 1; +var OLM_ERROR; /* allocate a number of bytes of storage on the stack. * * If size_or_array is a Number, allocates that number of zero-initialised bytes. */ function stack(size_or_array) { - return Module['allocate'](size_or_array, 'i8', Module['ALLOC_STACK']); + return allocate(size_or_array, 'i8', Module['ALLOC_STACK']); } function array_from_string(string) { - return Module['intArrayFromString'](string, true); + return intArrayFromString(string, true); } function random_stack(size) { @@ -33,11 +23,11 @@ function random_stack(size) { function restore_stack(wrapped) { return function() { - var sp = runtime.stackSave(); + var sp = stackSave(); try { return wrapped.apply(this, arguments); } finally { - runtime.stackRestore(sp); + stackRestore(sp); } } } @@ -315,7 +305,7 @@ Session.prototype['encrypt'] = restore_stack(function( Module['_olm_encrypt_message_type'] )(this.ptr); - plaintext_length = Module['lengthBytesUTF8'](plaintext); + plaintext_length = lengthBytesUTF8(plaintext); var message_length = session_method( Module['_olm_encrypt_message_length'] )(this.ptr, plaintext_length); @@ -325,7 +315,7 @@ Session.prototype['encrypt'] = restore_stack(function( // need to allow space for the terminator (which stringToUTF8 always // writes), hence + 1. plaintext_buffer = malloc(plaintext_length + 1); - Module['stringToUTF8'](plaintext, plaintext_buffer, plaintext_length + 1); + stringToUTF8(plaintext, plaintext_buffer, plaintext_length + 1); message_buffer = malloc(message_length + NULL_BYTE_PADDING_LENGTH); @@ -338,14 +328,14 @@ Session.prototype['encrypt'] = restore_stack(function( // UTF8ToString requires a null-terminated argument, so add the // null terminator. - Module['setValue']( + setValue( message_buffer+message_length, 0, "i8" ); return { "type": message_type, - "body": Module['UTF8ToString'](message_buffer), + "body": UTF8ToString(message_buffer), }; } finally { if (plaintext_buffer !== undefined) { @@ -366,14 +356,14 @@ Session.prototype['decrypt'] = restore_stack(function( try { message_buffer = malloc(message.length); - Module['writeAsciiToMemory'](message, message_buffer, true); + writeAsciiToMemory(message, message_buffer, true); max_plaintext_length = session_method( Module['_olm_decrypt_max_plaintext_length'] )(this.ptr, message_type, message_buffer, message.length); // caculating the length destroys the input buffer, so we need to re-copy it. - Module['writeAsciiToMemory'](message, message_buffer, true); + writeAsciiToMemory(message, message_buffer, true); plaintext_buffer = malloc(max_plaintext_length + NULL_BYTE_PADDING_LENGTH); @@ -385,7 +375,7 @@ Session.prototype['decrypt'] = restore_stack(function( // UTF8ToString requires a null-terminated argument, so add the // null terminator. - Module['setValue']( + setValue( plaintext_buffer+plaintext_length, 0, "i8" ); @@ -474,8 +464,6 @@ olm_exports["get_library_version"] = restore_stack(function() { ]; }); -})(); - // export the olm functions into the environment. // // make sure that we do this *after* populating olm_exports, so that we don't @@ -483,7 +471,11 @@ olm_exports["get_library_version"] = restore_stack(function() { if (typeof module !== 'undefined' && module.exports) { // node / browserify - module.exports = olm_exports; + for (var olm_export in olm_exports) { + if (olm_exports.hasOwnProperty(olm_export)) { + Module[olm_export] = olm_exports[olm_export]; + } + } } if (typeof(window) !== 'undefined') { @@ -492,3 +484,7 @@ if (typeof(window) !== 'undefined') { // Olm in the global scope for browserified and webpacked apps.) window["Olm"] = olm_exports; } + +Module.then(function() { + OLM_ERROR = Module['_olm_error'](); +}); -- cgit v1.2.3 From 263b94428a24caaa5b899ed7f73b896620e6cdf4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 25 Sep 2018 17:13:29 +0100 Subject: Another day, another interface Change the interface again, hopefully this time a bit more normal. Now we wrap the emscripten module completely and just expose the high level objects. The olm library export is now imported as normal (ie. returns a module rather than a function returning a module) but has an `init` method which *must* be called. This returns a promise which resolves when the module is ready. It also rejects if the module failed to set up, unlike before (and unlike the promise-not-a-promise that emscripten returns). Generally catch failures to init the module. --- javascript/olm_post.js | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) (limited to 'javascript/olm_post.js') diff --git a/javascript/olm_post.js b/javascript/olm_post.js index 071021c..9e0294a 100644 --- a/javascript/olm_post.js +++ b/javascript/olm_post.js @@ -464,27 +464,11 @@ olm_exports["get_library_version"] = restore_stack(function() { ]; }); -// export the olm functions into the environment. -// -// make sure that we do this *after* populating olm_exports, so that we don't -// get a half-built window.Olm if there is an exception. - -if (typeof module !== 'undefined' && module.exports) { - // node / browserify - for (var olm_export in olm_exports) { - if (olm_exports.hasOwnProperty(olm_export)) { - Module[olm_export] = olm_exports[olm_export]; - } - } -} - -if (typeof(window) !== 'undefined') { - // We've been imported directly into a browser. Define the global 'Olm' object. - // (we do this even if module.exports was defined, because it's useful to have - // Olm in the global scope for browserified and webpacked apps.) - window["Olm"] = olm_exports; -} - -Module.then(function() { +Module['onRuntimeInitialized'] = function() { OLM_ERROR = Module['_olm_error'](); -}); + if (onInitSuccess) onInitSuccess(); +}; + +Module['onAbort'] = function(err) { + if (onInitFail) onInitFail(err); +}; -- cgit v1.2.3