当前位置: 首页 > Web前端 > HTML

emscripten学习

时间:2023-03-27 23:55:22 HTML

whatemscripten是一个c/cpp编译器,可以用LLVM将c/cpp代码编译成WebAssembly。其中emcc就是他的cli,类似于常用的makefile的功能。编译出来的产品可选性比较强,htmljswasm可以自由组合js产品源码解释使用emcc编译一个c文件会产生一个js文件和一个wasm文件。js文件中的源码完全兼容node环境和web环境。本节只讨论网络环境。if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){//检查worker,而不是web,因为窗口可以填充scriptDirectory=self.location.href;}elseif(typeofdocument!=='undefined'&&document.currentScript){//webscriptDirectory=document.currentScript.src;}//bloburl看起来像blob:http://site.com/etc/etc,我们无法从中推断出任何信息。//否则,切掉url的最后部分以找到脚本目录。//如果scriptDirectory不包含斜杠,lastIndexOf将返回-1,//并且scriptDirectory将正确替换为空字符串。if(scriptDirectory.indexOf('blob:')!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf('/')+1);}else{scriptDirectory='';}//区分WebWorker和NodeWorker的情况,因为读取必须//以不同的方式完成。{//包括:web_or_worker_shell_read.js重新ad_=functionshell_read(url){varxhr=newXMLHttpRequest();xhr.open('GET',url,false);xhr.发送(空);返回xhr.responseText;};if(ENVIRONMENT_IS_WORKER){readBinary=functionreadBinary(url){varxhr=newXMLHttpRequest();xhr.open('GET',url,false);xhr.responseType='arraybuffer';xhr.发送(空);返回新的Uint8Array(/**@type{!ArrayBuffer}*/(xhr.response));};}readAsync=functionreadAsync(url,onload,onerror){varxhr=newXMLHttpRequest();xhr.open('GET',url,true);xhr.responseType='arraybuffer';xhr.onload=functionxhr_onload(){if(xhr.status==200||(xhr.status==0&&xhr.response)){//文件URL可以返回0onload(xhr.response);返回;}onerror();};xhr.onerror=onerror;xhr.发送(空);};//结束包括:web_or_worker_shell_read.js}setWindowTitle=function(title){document.title=title};}else{thrownewError('environmentdetectionerror');}以上就是为em构建的web中文件系统的改造方法。Em将采用全局Module对象并支持Module对象。这样,em中一些常用的em函数就可以这样重写了。常用的工具函数都是在js中定义的,比如如何通过方法名访问c模块或者c函数。c和js中数据类型的转换其实就是https://emscripten.org/docs/a的内容...emjsfunctionpreRun(){if(Module['preRun']){if(typeofModule['preRun']=='function')Module['preRun']=[Module['preRun']];while(Module['preRun'].length){addOnPreRun(Module['preRun'].shift());}}callRuntimeCallbacks(__ATPRERUN__);}functioninitRuntime(){checkStackCookie();断言(!runtimeInitialized);运行时初始化=真;如果(!Module["noFSInit"]&&!FS.init.initialized)FS.init();TTY.init();callRuntimeCallbacks(__ATINIT__);}functionpreMain(){checkStackCookie();FS.ignorePermissions=false;callRuntimeCallbacks(__ATMAIN__);??}functionexitRuntime(){checkStackCookie();核心函数createWasmfunctioncreateWasm(){//准备导入varinfo={'env':asmLibraryArg,'wasi_snapshot_preview1':asmLibraryArg,};//加载wasm模块并创建一个在JS引擎中使用原生支持的实例。//处理生成的wasm实例,接收它的导出并//执行其他必要的设置/**@param{WebAssembly.Module=}module*/functionreceiveInstance(instance,module){varexports=instance.exports;模块['asm']=导出;wasmMemory=Module['asm']['memory'];assert(wasmMemory,"在wasm导出中找不到内存");//当emscripten在--post-link//模式下运行时,此断言不成立。//TODO(sbc):以后链接模式从wasm文件中读取INITIAL_MEMORY。//断言(wasmMemory.buffer.byteLength===16777216);updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module['asm']['__indirect_function_table'];assert(wasmTable,"在wasmexp中找不到表orts");removeRunDependency('wasm-instantiate');}//我们还不能运行(除了在pthread中,我们有一个自定义的同步实例化器)addRunDependency('wasm-instantiate');//异步编译可以当页面上的错误覆盖Module//时会造成混淆(例如,如果元素顺序错误,定义Module的在//后面),因此我们保存Module稍后检查。vartrueModule=Module;functionreceiveInstantiatedSource(output){//'output'是一个WebAssemblyInstantiatedSource对象,它同时具有模块和实例。//receiveInstance()将交换导出(到Module.asm),因此它们可以被称为assert(Module===trueModule,'在异步编译期间不应替换Module对象-也许HTML元素的顺序是错误的?');trueModule=null;//TODO:由于闭包回归https://github.com/google/closure-compiler/issues/3193,上面一行不再优化out到下一行。//当回归修复后,可以恢复上面启用USE_PTHREADS的路径。接收实例(输出['实例']);}functioninstantiateArrayBuffer(receiver){returngetBinaryPromise().then(function(binary){returnWebAssembly.instantiate(binary,info);}).then(receiver,function(reason){err('无法异步准备wasm:'+原因);中止(原因);});}//如果可用,首选流式实例化。functioninstantiateAsync(){if(!wasmBinary&&typeofWebAssembly.instantiateStreaming==='function'&&!isDataURI(wasmBinaryFile)&&//不要对file://在webview中传递的对象使用流,同步获取它们。!isFileURI(wasmBinaryFile)&&typeoffetch==='function'){returnfetch(wasmBinaryFile,{credentials:'same-origin'}).then(function(response){var结果=WebAssembly.instantiateStreaming(响应,信息);returnresult.then(receiveInstantiatedSource,function(reason){//我们预计最常见的失败原因是二进制文件的错误MIME类型,//在这种情况下回退到ArrayBuffer实例化应该可行。err('wasmstreamingcompilefailed:'+reason);err('回到ArrayBuffer实例化');returninstantiateArrayBuffer(receiveInstantiatedSource);});});}else{returninstantiateArrayBuffer(receiveInstantiatedSource);}}//用户shell页面可以编写自己的Module.instantiateWasm=function(imports,successCallback)回调//自己手动实例化Wasm模块。这允许页面并行运行实例化//它们正在执行的任何其他异步启动操作。if(Module['instantiateWasm']){try{varexports=Module['instantiateWasm'](信息,接收实例);退货出口;}catch(e){err('Module.instantiateWasm回调失败,错误:'+e);返回假;}}实例化异步();返回{};//还没有导出;后面我们再填}加载wasm文件,这里主要是初始化的声明,包括内存空间的声明和依赖的注入,这里是em自动管理的关注点。wasm的加载查看源码中的wasm文件都是远程加载的。这个时候我们需要把我们的wasm文件替换到CDN。我们需要添加Module['locateFile']以确保我们的wasm可以指向我们需要的地址。varwasmBinaryFile='xxx.wasm';if(!isDataURI(wasmBinaryFile)){//dataurl判断只要不是base64就为truewasmBinaryFile=locateFile(wasmBinaryFile);}//`/`应该是如果`scriptDirectory`不为空则出现在最后}returnscriptDirectory+path;}if(ENVIRONMENT_IS_WORKER){//检查worker,而不是web,因为窗口可以填充scriptDirectory=self.location.href;}elseif(typeofdocument!=='undefined'&&document.currentScript){//网络脚本目录=document.currentScript.src;}//scriptDirectory是脚本的src,不过这个不重要。Module['locateFile']可以从入参createWasm中得到执行完成数,我们得到一个promise源码中还有一个关键函数hookModule.instantiateWasm。我们可以在这里获取wasm的实例。至此,整个wasm初始化后的output模块就可以得到main函数的运行了。这是c中的主要功能。如果是的话其实模块输出不需要main函数。他对比了模块中的run函数,默认加载后会直接运行。我们可以使用noInitialRun让main函数不执行,然后使用模块的run函数运行,支持参数的传递,非常适合wasm模块初始化场景的工程设计。编写自定义模块对象。必须重写的函数主要是生命周期函数和'locateFile'来梳理c/cpp模块的边界,哪些是js完成的,哪些是c完成的,确认输入输出。这里,我们可以利用组件化的思想来解决编译c代码的问题,输出为js和wasm对导出的模块进行二次包装,梳理主要功能,处理入参类型完成组件生命周期管控和核心功能联调