From e0697299b694d9c29bc5b092d26c789e1e1951d7 Mon Sep 17 00:00:00 2001 From: c-basalt <117849907+c-basalt@users.noreply.github.com> Date: Thu, 2 Jan 2025 00:17:10 -0500 Subject: [PATCH] test wasm --- test/test_jsi_external.py | 64 +++-- test/testdata/jsi_external/hello_wasm.js | 234 ++++++++++++++++++ test/testdata/jsi_external/hello_wasm_bg.wasm | Bin 0 -> 16666 bytes yt_dlp/jsinterp/_helper.py | 23 ++ 4 files changed, 303 insertions(+), 18 deletions(-) create mode 100644 test/testdata/jsi_external/hello_wasm.js create mode 100644 test/testdata/jsi_external/hello_wasm_bg.wasm diff --git a/test/test_jsi_external.py b/test/test_jsi_external.py index 1d52e3fb3..450b7ca21 100644 --- a/test/test_jsi_external.py +++ b/test/test_jsi_external.py @@ -16,10 +16,14 @@ from test.helper import ( FakeYDL, ) +from yt_dlp.utils import ( + variadic, +) from yt_dlp.cookies import YoutubeDLCookieJar -from yt_dlp.jsinterp.common import ExternalJSI +from yt_dlp.jsinterp.common import ExternalJSI, _ALL_FEATURES from yt_dlp.jsinterp._deno import DenoJSI, DenoJITlessJSI, DenoJSDomJSI from yt_dlp.jsinterp._phantomjs import PhantomJSJSI +from yt_dlp.jsinterp._helper import prepare_wasm_jsmodule @dataclasses.dataclass @@ -49,9 +53,26 @@ def __eq__(self, other: NetscapeFields | http.cookiejar.Cookie): return all(getattr(self, attr) == getattr(other, attr) for attr in ['name', 'value', 'domain', 'path', 'secure', 'expires']) +covered_features = set() + + +def requires_feature(features): + covered_features.update(variadic(features)) + + def outer(func): + def wrapper(self, *args, **kwargs): + if not self.jsi._SUPPORTED_FEATURES.issuperset(variadic(features)): + print(f'{self._JSI_CLASS.__name__} does not support {features!r}, skipping') + self.skipTest(f'{"&".join(variadic(features))} not supported') + return func(self, *args, **kwargs) + return wrapper + return outer + + class Base: class TestExternalJSI(unittest.TestCase): _JSI_CLASS: type[ExternalJSI] = None + _TESTDATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'testdata', 'jsi_external') maxDiff = 2000 def setUp(self): @@ -77,28 +98,21 @@ def test_user_agent(self): jsi = self._JSI_CLASS(self.ydl, self.url_param, 10, {}, user_agent='test/ua') self.assertEqual(jsi.execute('console.log(navigator.userAgent);'), 'test/ua') + @requires_feature('location') def test_location(self): - if 'location' not in self._JSI_CLASS._SUPPORTED_FEATURES: - print(f'{self._JSI_CLASS.__name__} does not support location, skipping') - self.skipTest('Location not supported') self.url_param = 'https://example.com/123/456' self.assertEqual(self.jsi.execute('console.log(JSON.stringify([location.href, location.hostname]));'), '["https://example.com/123/456","example.com"]') + @requires_feature('dom') def test_execute_dom_parse(self): - if 'dom' not in self.jsi._SUPPORTED_FEATURES: - print(f'{self._JSI_CLASS.__name__} does not support DOM, skipping') - self.skipTest('DOM not supported') self.assertEqual(self.jsi.execute( 'console.log(document.getElementById("test-div").innerHTML);', html='
Hello, world!
'), 'Hello, world!') + @requires_feature('dom') def test_execute_dom_script(self): - if 'dom' not in self.jsi._SUPPORTED_FEATURES: - print(f'{self._JSI_CLASS.__name__} does not support DOM, skipping') - self.skipTest('DOM not supported') - self.assertEqual(self.jsi.execute( 'console.log(document.getElementById("test-div").innerHTML);', html='''Hello, world! @@ -112,11 +126,8 @@ def test_execute_dom_script(self): '''), 'Hello, world!') + @requires_feature(['dom', 'location']) def test_dom_location(self): - if not self._JSI_CLASS._SUPPORTED_FEATURES.issuperset({'dom', 'location'}): - print(f'{self._JSI_CLASS.__name__} does not support both DOM and location, skipping') - self.skipTest('DOM or location not supported') - self.url_param = 'https://example.com/123/456' self.assertEqual(self.jsi.execute( 'console.log(document.getElementById("test-div").innerHTML);', @@ -125,10 +136,8 @@ def test_dom_location(self):
Hello, world!
'''), 'example.com') + @requires_feature('cookies') def test_execute_cookiejar(self): - if 'cookies' not in self.jsi._SUPPORTED_FEATURES: - print(f'{self._JSI_CLASS.__name__} does not support cookies, skipping') - self.skipTest('Cookies not supported') cookiejar = YoutubeDLCookieJar() ref_cookiejar = YoutubeDLCookieJar() @@ -176,6 +185,22 @@ def _assert_expected_execute(cookie_str, ref_cookie_str): cookiejar=cookiejar), 'test1=new1; test2=new2; test3=test3; test5=test5') + @requires_feature('wasm') + def test_wasm(self): + with open(os.path.join(self._TESTDATA_DIR, 'hello_wasm.js')) as f: + js_mod = f.read() + with open(os.path.join(self._TESTDATA_DIR, 'hello_wasm_bg.wasm'), 'rb') as f: + wasm = f.read() + + js_base = prepare_wasm_jsmodule(js_mod, wasm) + + js_code = js_base + '''; + console.log(add(1, 2)); + greet('world'); + ''' + + self.assertEqual(self.jsi.execute(js_code), '3\nHello, world!') + class TestDeno(Base.TestExternalJSI): _JSI_CLASS = DenoJSI @@ -193,5 +218,8 @@ class TestPhantomJS(Base.TestExternalJSI): _JSI_CLASS = PhantomJSJSI +expect_covered_features = set(_ALL_FEATURES) - {'js'} +assert covered_features.issuperset(expect_covered_features), f'Missing tests for features: {expect_covered_features - covered_features}' + if __name__ == '__main__': unittest.main() diff --git a/test/testdata/jsi_external/hello_wasm.js b/test/testdata/jsi_external/hello_wasm.js new file mode 100644 index 000000000..1a3a31c46 --- /dev/null +++ b/test/testdata/jsi_external/hello_wasm.js @@ -0,0 +1,234 @@ +// wasm-pack build --target web +/* lib.rs +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + pub fn eval(s: &str); +} + +#[wasm_bindgen] +pub fn greet(name: &str) { + eval(&format!("console.log('Hello, {}!')", name)); +} + +#[wasm_bindgen] +pub fn add(left: i32, right: i32) -> i32 { + left + right +} +*/ + +let wasm; + +const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); + +if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; + +let cachedUint8ArrayMemory0 = null; + +function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; +} + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); +} + +let WASM_VECTOR_LEN = 0; + +const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); + +const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); +} + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; +}); + +function passStringToWasm0(arg, malloc, realloc) { + + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8ArrayMemory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} +/** + * @param {string} name + */ +export function greet(name) { + const ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.greet(ptr0, len0); +} + +/** + * @param {number} left + * @param {number} right + * @returns {number} + */ +export function add(left, right) { + const ret = wasm.add(left, right); + return ret; +} + +async function __wbg_load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + + } catch (e) { + if (module.headers.get('Content-Type') != 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { + throw e; + } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + + } else { + return instance; + } + } +} + +function __wbg_get_imports() { + const imports = {}; + imports.wbg = {}; + imports.wbg.__wbg_eval_d1c6d8ede79fdfce = function(arg0, arg1) { + eval(getStringFromWasm0(arg0, arg1)); + }; + imports.wbg.__wbindgen_init_externref_table = function() { + const table = wasm.__wbindgen_export_0; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + ; + }; + + return imports; +} + +function __wbg_init_memory(imports, memory) { + +} + +function __wbg_finalize_init(instance, module) { + wasm = instance.exports; + __wbg_init.__wbindgen_wasm_module = module; + cachedUint8ArrayMemory0 = null; + + + wasm.__wbindgen_start(); + return wasm; +} + +function initSync(module) { + if (wasm !== undefined) return wasm; + + + if (typeof module !== 'undefined') { + if (Object.getPrototypeOf(module) === Object.prototype) { + ({module} = module) + } else { + console.warn('using deprecated parameters for `initSync()`; pass a single object instead') + } + } + + const imports = __wbg_get_imports(); + + __wbg_init_memory(imports); + + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + + const instance = new WebAssembly.Instance(module, imports); + + return __wbg_finalize_init(instance, module); +} + +async function __wbg_init(module_or_path) { + if (wasm !== undefined) return wasm; + + + if (typeof module_or_path !== 'undefined') { + if (Object.getPrototypeOf(module_or_path) === Object.prototype) { + ({module_or_path} = module_or_path) + } else { + console.warn('using deprecated parameters for the initialization function; pass a single object instead') + } + } + + if (typeof module_or_path === 'undefined') { + module_or_path = new URL('hello_wasm_bg.wasm', import.meta.url); + } + const imports = __wbg_get_imports(); + + if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { + module_or_path = fetch(module_or_path); + } + + __wbg_init_memory(imports); + + const { instance, module } = await __wbg_load(await module_or_path, imports); + + return __wbg_finalize_init(instance, module); +} + +export { initSync }; +export default __wbg_init; diff --git a/test/testdata/jsi_external/hello_wasm_bg.wasm b/test/testdata/jsi_external/hello_wasm_bg.wasm new file mode 100644 index 0000000000000000000000000000000000000000..d8f32c44c4980c8eaca07c6528b2dffa85d56fee GIT binary patch literal 16666 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 literal 0 HcmV?d00001 diff --git a/yt_dlp/jsinterp/_helper.py b/yt_dlp/jsinterp/_helper.py index 9a86c73ad..389204f9e 100644 --- a/yt_dlp/jsinterp/_helper.py +++ b/yt_dlp/jsinterp/_helper.py @@ -109,3 +109,26 @@ def extract_script_tags(html: str) -> tuple[str, list[str]]: html = html[:start] + html[end:] return html, inline_scripts + + +def prepare_wasm_jsmodule(js_mod: str, wasm: bytes) -> str: + """ + Prepare wasm init for js wrapper module generated by rust wasm-pack + removes export and import.meta and inlines wasm binary as Uint8Array + See test/test_data/jsi_external/hello_wasm.js for example + + @param {str} js_mod: js wrapper module generated by rust wasm-pack + @param {bytes} wasm: wasm binary + """ + + js_mod = re.sub(r'export(?:\s+default)?([\s{])', r'\1', js_mod) + js_mod = js_mod.replace('import.meta', '{}') + + return js_mod + '''; + await (async () => { + const t = __wbg_get_imports(); + __wbg_init_memory(t); + const {module, instance} = await WebAssembly.instantiate(Uint8Array.from(%s), t); + __wbg_finalize_init(instance, module); + })(); + ''' % list(wasm)