1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-07-19 11:18:42 +02:00

Add support for enabling WASM code paths in NodeJS package

See `test.js` for reference on how to enable WASM code
paths (which are disabled by default).
This commit is contained in:
Raymond Hill 2021-07-29 16:54:51 -04:00
parent b02970f5ee
commit 98fc66bb1b
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
10 changed files with 123 additions and 87 deletions

View File

@ -36,7 +36,7 @@ vAPI.cantWebsocket =
vAPI.canWASM = vAPI.webextFlavor.soup.has('chromium') === false; vAPI.canWASM = vAPI.webextFlavor.soup.has('chromium') === false;
if ( vAPI.canWASM === false ) { if ( vAPI.canWASM === false ) {
const csp = manifest.content_security_policy; const csp = manifest.content_security_policy;
vAPI.canWASM = csp !== undefined && csp.indexOf("'wasm-eval'") !== -1; vAPI.canWASM = csp !== undefined && csp.indexOf("'unsafe-eval'") !== -1;
} }
vAPI.supportsUserStylesheets = vAPI.webextFlavor.soup.has('user_stylesheet'); vAPI.supportsUserStylesheets = vAPI.webextFlavor.soup.has('user_stylesheet');

View File

@ -80,11 +80,35 @@ function applyList(name, raw) {
snfe.fromCompiled(reader); snfe.fromCompiled(reader);
} }
function enableWASM(path) { async function enableWASM() {
return Promise.all([ const wasmModuleFetcher = async function(path) {
globals.publicSuffixList.enableWASM(`${path}/lib/publicsuffixlist`), return new Promise(async (resolve, reject) => {
snfe.enableWASM(`${path}/js`), const require = createRequire(import.meta.url); // jshint ignore:line
]); const fs = require('fs');
fs.readFile(`${path}.wasm`, null, (err, data) => {
if ( err ) { return reject(err); }
return globals.WebAssembly.compile(data).then(module => {
resolve(module);
});
});
});
};
try {
const results = await Promise.all([
globals.publicSuffixList.enableWASM(
wasmModuleFetcher,
'./lib/publicsuffixlist/wasm/'
),
snfe.enableWASM(
wasmModuleFetcher,
'./js/wasm/'
),
]);
return results.every(a => a === true);
} catch(reason) {
console.info(reason);
}
return false;
} }
function pslInit(raw) { function pslInit(raw) {

View File

@ -28,6 +28,7 @@
import { createRequire } from 'module'; import { createRequire } from 'module';
import { import {
enableWASM,
FilteringContext, FilteringContext,
pslInit, pslInit,
restart, restart,
@ -43,13 +44,14 @@ function fetch(listName) {
} }
(async ( ) => { (async ( ) => {
/* try {
* WASM require fetch(), not present in Node const result = await enableWASM();
try { if ( result !== true ) {
await enableWASM('//ublock/dist/build/uBlock0.nodejs'); console.log('Failed to enable all WASM code paths');
} catch(ex) {
} }
*/ } catch(ex) {
console.log(ex);
}
await pslInit(); await pslInit();

View File

@ -692,10 +692,10 @@ const BidiTrieContainer = class {
return -1; return -1;
} }
async enableWASM(modulePath) { async enableWASM(wasmModuleFetcher, path) {
if ( typeof WebAssembly !== 'object' ) { return false; } if ( typeof WebAssembly !== 'object' ) { return false; }
if ( this.wasmMemory instanceof WebAssembly.Memory ) { return true; } if ( this.wasmMemory instanceof WebAssembly.Memory ) { return true; }
const module = await getWasmModule(modulePath); const module = await getWasmModule(wasmModuleFetcher, path);
if ( module instanceof WebAssembly.Module === false ) { return false; } if ( module instanceof WebAssembly.Module === false ) { return false; }
const memory = new WebAssembly.Memory({ const memory = new WebAssembly.Memory({
initial: roundToPageSize(this.buf8.length) >>> 16 initial: roundToPageSize(this.buf8.length) >>> 16
@ -925,17 +925,12 @@ BidiTrieContainer.prototype.STrieRef = class {
const getWasmModule = (( ) => { const getWasmModule = (( ) => {
let wasmModulePromise; let wasmModulePromise;
return async function(modulePath) { return async function(wasmModuleFetcher, path) {
if ( wasmModulePromise instanceof Promise ) { if ( wasmModulePromise instanceof Promise ) {
return wasmModulePromise; return wasmModulePromise;
} }
if ( if ( typeof WebAssembly !== 'object' ) { return; }
typeof WebAssembly !== 'object' ||
typeof WebAssembly.compileStreaming !== 'function'
) {
return;
}
// Soft-dependency on vAPI so that the code here can be used outside of // Soft-dependency on vAPI so that the code here can be used outside of
// uBO (i.e. tests, benchmarks) // uBO (i.e. tests, benchmarks)
@ -948,12 +943,7 @@ const getWasmModule = (( ) => {
uint32s[0] = 1; uint32s[0] = 1;
if ( uint8s[0] !== 1 ) { return; } if ( uint8s[0] !== 1 ) { return; }
wasmModulePromise = fetch( wasmModulePromise = wasmModuleFetcher(`${path}biditrie`).catch(reason => {
`${modulePath}/wasm/biditrie.wasm`,
{ mode: 'same-origin' }
).then(
WebAssembly.compileStreaming
).catch(reason => {
console.info(reason); console.info(reason);
}); });

View File

@ -456,10 +456,10 @@ const HNTrieContainer = class {
return n === hr || hn.charCodeAt(hl-1) === 0x2E /* '.' */; return n === hr || hn.charCodeAt(hl-1) === 0x2E /* '.' */;
} }
async enableWASM(modulePath) { async enableWASM(wasmModuleFetcher, path) {
if ( typeof WebAssembly !== 'object' ) { return false; } if ( typeof WebAssembly !== 'object' ) { return false; }
if ( this.wasmMemory instanceof WebAssembly.Memory ) { return true; } if ( this.wasmMemory instanceof WebAssembly.Memory ) { return true; }
const module = await getWasmModule(modulePath); const module = await getWasmModule(wasmModuleFetcher, path);
if ( module instanceof WebAssembly.Module === false ) { return false; } if ( module instanceof WebAssembly.Module === false ) { return false; }
const memory = new WebAssembly.Memory({ initial: 2 }); const memory = new WebAssembly.Memory({ initial: 2 });
const instance = await WebAssembly.instantiate(module, { const instance = await WebAssembly.instantiate(module, {
@ -483,6 +483,7 @@ const HNTrieContainer = class {
this.buf32 = new Uint32Array(this.buf.buffer); this.buf32 = new Uint32Array(this.buf.buffer);
this.matches = this.matchesWASM = instance.exports.matches; this.matches = this.matchesWASM = instance.exports.matches;
this.add = this.addWASM = instance.exports.add; this.add = this.addWASM = instance.exports.add;
return true;
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
@ -767,17 +768,12 @@ HNTrieContainer.prototype.HNTrieRef.prototype.needle = '';
const getWasmModule = (( ) => { const getWasmModule = (( ) => {
let wasmModulePromise; let wasmModulePromise;
return async function(modulePath) { return async function(wasmModuleFetcher, path) {
if ( wasmModulePromise instanceof Promise ) { if ( wasmModulePromise instanceof Promise ) {
return wasmModulePromise; return wasmModulePromise;
} }
if ( if ( typeof WebAssembly !== 'object' ) { return; }
typeof WebAssembly !== 'object' ||
typeof WebAssembly.compileStreaming !== 'function'
) {
return;
}
// Soft-dependency on vAPI so that the code here can be used outside of // Soft-dependency on vAPI so that the code here can be used outside of
// uBO (i.e. tests, benchmarks) // uBO (i.e. tests, benchmarks)
@ -790,12 +786,7 @@ const getWasmModule = (( ) => {
uint32s[0] = 1; uint32s[0] = 1;
if ( uint8s[0] !== 1 ) { return; } if ( uint8s[0] !== 1 ) { return; }
wasmModulePromise = fetch( wasmModulePromise = wasmModuleFetcher(`${path}hntrie`).catch(reason => {
`${modulePath}/wasm/hntrie.wasm`,
{ mode: 'same-origin' }
).then(
WebAssembly.compileStreaming
).catch(reason => {
console.info(reason); console.info(reason);
}); });

View File

@ -25,6 +25,7 @@
import cacheStorage from './cachestorage.js'; import cacheStorage from './cachestorage.js';
import contextMenu from './contextmenu.js'; import contextMenu from './contextmenu.js';
import globals from './globals.js';
import io from './assets.js'; import io from './assets.js';
import lz4Codec from './lz4.js'; import lz4Codec from './lz4.js';
import staticExtFilteringEngine from './static-ext-filtering.js'; import staticExtFilteringEngine from './static-ext-filtering.js';
@ -343,7 +344,15 @@ try {
} }
if ( µb.hiddenSettings.disableWebAssembly !== true ) { if ( µb.hiddenSettings.disableWebAssembly !== true ) {
staticNetFilteringEngine.enableWASM('/js').then(( ) => { const wasmModuleFetcher = async function(path) {
return fetch(`${path}.wasm`, { mode: 'same-origin' }).then(
globals.WebAssembly.compileStreaming
).catch(reason => {
ubolog(reason);
});
};
staticNetFilteringEngine.enableWASM(wasmModuleFetcher, './js/wasm/').then(result => {
if ( result !== true ) { return; }
ubolog(`WASM modules ready ${Date.now()-vAPI.T0} ms after launch`); ubolog(`WASM modules ready ${Date.now()-vAPI.T0} ms after launch`);
}); });
} }

View File

@ -4448,12 +4448,14 @@ FilterContainer.prototype.getFilterCount = function() {
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.enableWASM = function(modulePath) { FilterContainer.prototype.enableWASM = function(wasmModuleFetcher, path) {
return Promise.all([ return Promise.all([
bidiTrie.enableWASM(modulePath), bidiTrie.enableWASM(wasmModuleFetcher, path),
filterOrigin.trieContainer.enableWASM(modulePath), filterOrigin.trieContainer.enableWASM(wasmModuleFetcher, path),
FilterHostnameDict.trieContainer.enableWASM(modulePath), FilterHostnameDict.trieContainer.enableWASM(wasmModuleFetcher, path),
]); ]).then(results => {
return results.every(a => a === true);
});
}; };
/******************************************************************************/ /******************************************************************************/

View File

@ -1194,17 +1194,36 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
µb.loadPublicSuffixList = async function() { µb.loadPublicSuffixList = async function() {
const psl = globals.publicSuffixList; const psl = globals.publicSuffixList;
// WASM is nice but not critical
if ( this.hiddenSettings.disableWebAssembly !== true ) { if ( this.hiddenSettings.disableWebAssembly !== true ) {
psl.enableWASM('/lib/publicsuffixlist'); const wasmModuleFetcher = function(path) {
return fetch( `${path}.wasm`, {
mode: 'same-origin'
}).then(
globals.WebAssembly.compileStreaming
).catch(reason => {
ubolog(reason);
});
};
let result = false;
try {
result = await psl.enableWASM(wasmModuleFetcher,
'./lib/publicsuffixlist/wasm/'
);
} catch(reason) {
ubolog(reason);
}
if ( result ) {
ubolog(`WASM PSL ready ${Date.now()-vAPI.T0} ms after launch`);
}
} }
try { try {
const result = await io.get(`compiled/${this.pslAssetKey}`); const result = await io.get(`compiled/${this.pslAssetKey}`);
if ( psl.fromSelfie(result.content, sparseBase64) ) { if ( psl.fromSelfie(result.content, sparseBase64) ) { return; }
return; } catch (reason) {
} ubolog(reason);
} catch (ex) {
ubolog(ex);
} }
const result = await io.get(this.pslAssetKey); const result = await io.get(this.pslAssetKey);

View File

@ -13,8 +13,7 @@
/*! Home: https://github.com/gorhill/publicsuffixlist.js -- GPLv3 APLv2 */ /*! Home: https://github.com/gorhill/publicsuffixlist.js -- GPLv3 APLv2 */
/* jshint browser:true, esversion:6, laxbreak:true, undef:true, unused:true */ /* globals WebAssembly, exports:true, module */
/* globals WebAssembly, console, exports:true, module */
'use strict'; 'use strict';
@ -528,43 +527,33 @@ const fromSelfie = function(selfie, decoder) {
// The WASM module is entirely optional, the JS implementation will be // The WASM module is entirely optional, the JS implementation will be
// used should the WASM module be unavailable for whatever reason. // used should the WASM module be unavailable for whatever reason.
const enableWASM = (function() { const enableWASM = (( ) => {
let memory; let wasmPromise;
return function(modulePath) {
if ( getPublicSuffixPosWASM instanceof Function ) {
return Promise.resolve(true);
}
if (
typeof WebAssembly !== 'object' ||
typeof WebAssembly.instantiateStreaming !== 'function'
) {
return Promise.resolve(false);
}
const getWasmInstance = async function(wasmModuleFetcher, path) {
if ( typeof WebAssembly !== 'object' ) { return false; }
// The wasm code will work only if CPU is natively little-endian, // The wasm code will work only if CPU is natively little-endian,
// as we use native uint32 array in our js code. // as we use native uint32 array in our js code.
const uint32s = new Uint32Array(1); const uint32s = new Uint32Array(1);
const uint8s = new Uint8Array(uint32s.buffer); const uint8s = new Uint8Array(uint32s.buffer);
uint32s[0] = 1; uint32s[0] = 1;
if ( uint8s[0] !== 1 ) { if ( uint8s[0] !== 1 ) { return false; }
return Promise.resolve(false);
}
return fetch( try {
`${modulePath}/wasm/publicsuffixlist.wasm`, const module = await wasmModuleFetcher(`${path}publicsuffixlist`);
{ mode: 'same-origin' } if ( module instanceof WebAssembly.Module === false ) {
).then(response => { return false;
}
const pageCount = pslBuffer8 !== undefined const pageCount = pslBuffer8 !== undefined
? pslBuffer8.byteLength + 0xFFFF >>> 16 ? pslBuffer8.byteLength + 0xFFFF >>> 16
: 1; : 1;
memory = new WebAssembly.Memory({ initial: pageCount }); const memory = new WebAssembly.Memory({ initial: pageCount });
return WebAssembly.instantiateStreaming( const instance = await WebAssembly.instantiate(module, {
response, imports: { memory }
{ imports: { memory: memory } } });
); if ( instance instanceof WebAssembly.Instance === false ) {
}).then(({ instance }) => { return false;
}
const curPageCount = memory.buffer.byteLength >>> 16; const curPageCount = memory.buffer.byteLength >>> 16;
const newPageCount = pslBuffer8 !== undefined const newPageCount = pslBuffer8 !== undefined
? pslBuffer8.byteLength + 0xFFFF >>> 16 ? pslBuffer8.byteLength + 0xFFFF >>> 16
@ -582,12 +571,19 @@ const enableWASM = (function() {
wasmMemory = memory; wasmMemory = memory;
getPublicSuffixPosWASM = instance.exports.getPublicSuffixPos; getPublicSuffixPosWASM = instance.exports.getPublicSuffixPos;
getPublicSuffixPos = getPublicSuffixPosWASM; getPublicSuffixPos = getPublicSuffixPosWASM;
memory = undefined;
return true; return true;
}).catch(reason => { } catch(reason) {
console.info(reason); console.info(reason);
return false; }
}); return false;
};
return async function(wasmModuleFetcher, path) {
if ( getPublicSuffixPosWASM instanceof Function ) { return true; }
if ( wasmPromise instanceof Promise === false ) {
wasmPromise = getWasmInstance(wasmModuleFetcher, path);
}
return wasmPromise;
}; };
})(); })();

View File

@ -16,6 +16,9 @@ cp src/js/static-filtering-io.js $DES/js
cp src/js/text-iterators.js $DES/js cp src/js/text-iterators.js $DES/js
cp src/js/uri-utils.js $DES/js cp src/js/uri-utils.js $DES/js
mkdir -p $DES/js/wasm
cp src/js/wasm/* $DES/js/wasm/
mkdir -p $DES/lib mkdir -p $DES/lib
cp -R src/lib/punycode.js $DES/lib/ cp -R src/lib/punycode.js $DES/lib/
cp -R src/lib/publicsuffixlist $DES/lib/ cp -R src/lib/publicsuffixlist $DES/lib/