1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-07-08 12:57:57 +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;
if ( vAPI.canWASM === false ) {
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');

View File

@ -80,11 +80,35 @@ function applyList(name, raw) {
snfe.fromCompiled(reader);
}
function enableWASM(path) {
return Promise.all([
globals.publicSuffixList.enableWASM(`${path}/lib/publicsuffixlist`),
snfe.enableWASM(`${path}/js`),
]);
async function enableWASM() {
const wasmModuleFetcher = async function(path) {
return new Promise(async (resolve, reject) => {
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) {

View File

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

View File

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

View File

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

View File

@ -25,6 +25,7 @@
import cacheStorage from './cachestorage.js';
import contextMenu from './contextmenu.js';
import globals from './globals.js';
import io from './assets.js';
import lz4Codec from './lz4.js';
import staticExtFilteringEngine from './static-ext-filtering.js';
@ -343,7 +344,15 @@ try {
}
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`);
});
}

View File

@ -4448,12 +4448,14 @@ FilterContainer.prototype.getFilterCount = function() {
/******************************************************************************/
FilterContainer.prototype.enableWASM = function(modulePath) {
FilterContainer.prototype.enableWASM = function(wasmModuleFetcher, path) {
return Promise.all([
bidiTrie.enableWASM(modulePath),
filterOrigin.trieContainer.enableWASM(modulePath),
FilterHostnameDict.trieContainer.enableWASM(modulePath),
]);
bidiTrie.enableWASM(wasmModuleFetcher, path),
filterOrigin.trieContainer.enableWASM(wasmModuleFetcher, path),
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() {
const psl = globals.publicSuffixList;
// WASM is nice but not critical
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 {
const result = await io.get(`compiled/${this.pslAssetKey}`);
if ( psl.fromSelfie(result.content, sparseBase64) ) {
return;
}
} catch (ex) {
ubolog(ex);
if ( psl.fromSelfie(result.content, sparseBase64) ) { return; }
} catch (reason) {
ubolog(reason);
}
const result = await io.get(this.pslAssetKey);

View File

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