C语言转WebAssembly
Posted 第三眼的思绪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言转WebAssembly相关的知识,希望对你有一定的参考价值。
- 官网:https://webassembly.org/
- EMScripten:https://developer.mozilla.org/en-US/docs/WebAssembly/C_to_wasm (注:这里允许C++语言,但必须以C编译,小心掉坑)
C代码
#include <stdio.h>
#include <string.h>
const char *str = "hello ";
char *show(char *name)
int m = strlen(str);
int n = strlen(name);
char result[m+n];
for (int i = 0; i < m; i++)
result[i] = str[i];
for (int j = 0; j < n; j++)
result[m+j] = name[j];
printf("result printf===>%s\\n", result);
char * tmp = result;
return tmp;
emsdk安装环境变量需确认是否正常
C to wasm
emcc helloworld.c -s "EXPORTED_FUNCTIONS=['_show']" -s "EXPORTED_RUNTIME_METHODS=['cwrap','ccall']" -o hello.html
运行一:在浏览器中运行
Module.ccall("show","string",["string"],["zhangsan"]);
注:JS中加载WASM默认开启同源验证,所以需要同服务访问加载。
运行二:在微信小程序/小游戏中运行
编译出来的JS与微信生态语法存在一定不兼容,需要做一定改造适配。将hello.js
代码改造如下:
// The Module object: Our interface to the outside world. We import
// and export values on it. There are various ways Module can be used:
// 1. Not defined. We create it here
// 2. A function parameter, function(Module) ..generated code..
// 3. pre-run appended it, var Module = ; ..generated code..
// 4. External script tag defines var Module.
// We need to check if Module already exists (e.g. case 3 above).
// Substitution will be replaced with actual code on later stage of the build,
// this way Closure Compiler will not mangle it (e.g. case 4. above).
// Note that if you want to run closure, and also to use Module
// after the generated code, you will need to define var Module = ;
// before the code. Then that object will be used in the code, and you
// can continue to use Module afterwards as well.
var Module = typeof Module != 'undefined' ? Module : ;
var isWechat = typeof wx == 'object';
WebAssembly = isWechat ? WXWebAssembly : WebAssembly;
// See https://caniuse.com/mdn-javascript_builtins_object_assign
// --pre-jses are emitted after the Module integration code, so that they can
// refer to Module (if they choose; they can also define Module)
// PRE_JSES
// Sometimes an existing Module object exists with properties
// meant to overwrite the default module functionality. Here
// we collect those properties and reapply _after_ we configure
// the current environment's defaults to avoid having to be so
// defensive during initialization.
var moduleOverrides = Object.assign(, Module);
var arguments_ = [];
var thisProgram = './this.program';
var quit_ = (status, toThrow) =>
throw toThrow;
;
// Determine the runtime environment we are in. You can customize this by
// setting the ENVIRONMENT setting at compile time (see settings.js).
// Attempt to auto-detect the environment
var ENVIRONMENT_IS_WEB = typeof window == 'object' || isWechat;
var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function';
// N.b. Electron.js environment is simultaneously a NODE-environment, but
// also a web environment.
var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string';
var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
if (Module['ENVIRONMENT'])
throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -s ENVIRONMENT=web or -s ENVIRONMENT=node)');
// `/` should be present at the end if `scriptDirectory` is not empty
var scriptDirectory = '';
// Hooks that are implemented differently in different runtime environments.
var read_,
readAsync,
readBinary,
setWindowTitle;
// Normally we don't log exceptions but instead let them bubble out the top
// level where the embedding environment (e.g. the browser) can handle
// them.
// However under v8 and node we sometimes exit the process direcly in which case
// its up to use us to log the exception before exiting.
// If we fix https://github.com/emscripten-core/emscripten/issues/15080
// this may no longer be needed under node.
function logExceptionOnExit(e)
if (e instanceof ExitStatus) return;
let toLog = e;
if (e && typeof e == 'object' && e.stack)
toLog = [e, e.stack];
err('exiting due to exception: ' + toLog);
var fs;
var nodePath;
var requireNodeFS;
if (ENVIRONMENT_IS_NODE)
if (!(typeof process == 'object' && typeof require == 'function')) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
if (ENVIRONMENT_IS_WORKER)
scriptDirectory = require('path').dirname(scriptDirectory) + '/';
else
scriptDirectory = __dirname + '/';
// include: node_shell_read.js
requireNodeFS = () =>
// Use nodePath as the indicator for these not being initialized,
// since in some environments a global fs may have already been
// created.
if (!nodePath)
fs = require('fs');
nodePath = require('path');
;
read_ = function shell_read(filename, binary)
requireNodeFS();
filename = nodePath['normalize'](filename);
return fs.readFileSync(filename, binary ? undefined : 'utf8');
;
readBinary = (filename) =>
var ret = read_(filename, true);
if (!ret.buffer)
ret = new Uint8Array(ret);
assert(ret.buffer);
return ret;
;
readAsync = (filename, onload, onerror) =>
requireNodeFS();
filename = nodePath['normalize'](filename);
fs.readFile(filename, function(err, data)
if (err) onerror(err);
else onload(data.buffer);
);
;
// end include: node_shell_read.js
if (process['argv'].length > 1)
thisProgram = process['argv'][1].replace(/\\\\/g, '/');
arguments_ = process['argv'].slice(2);
if (typeof module != 'undefined')
module['exports'] = Module;
process['on']('uncaughtException', function(ex)
// suppress ExitStatus exceptions from showing an error
if (!(ex instanceof ExitStatus))
throw ex;
);
// Without this older versions of node (< v15) will log unhandled rejections
// but return 0, which is not normally the desired behaviour. This is
// not be needed with node v15 and about because it is now the default
// behaviour:
// See https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode
process['on']('unhandledRejection', function(reason) throw reason; );
quit_ = (status, toThrow) =>
if (keepRuntimeAlive())
process['exitCode'] = status;
throw toThrow;
logExceptionOnExit(toThrow);
process['exit'](status);
;
Module['inspect'] = function () return '[Emscripten Module object]'; ;
else
if (ENVIRONMENT_IS_SHELL)
if ((typeof process == 'object' && typeof require === 'function') || typeof window == 'object' || typeof importScripts == 'function') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
if (typeof read != 'undefined')
read_ = function shell_read(f)
return read(f);
;
readBinary = function readBinary(f)
let data;
if (typeof readbuffer == 'function')
return new Uint8Array(readbuffer(f));
data = read(f, 'binary');
assert(typeof data == 'object');
return data;
;
readAsync = function readAsync(f, onload, onerror)
setTimeout(() => onload(readBinary(f)), 0);
;
if (typeof scriptArgs != 'undefined')
arguments_ = scriptArgs;
else if (typeof arguments != 'undefined')
arguments_ = arguments;
if (typeof quit == 'function')
quit_ = (status, toThrow) =>
logExceptionOnExit(toThrow);
quit(status);
;
if (typeof print != 'undefined')
// Prefer to use print/printErr where they exist, as they usually work better.
if (typeof console == 'undefined') console = /** @type!Console */();
console.log = /** @type!function(this:Console, ...*): undefined */ (print);
console.warn = console.error = /** @type!function(this:Console, ...*): undefined */ (typeof printErr != 'undefined' ? printErr : print);
else
// Note that this includes Node.js workers when relevant (pthreads is enabled).
// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and
// ENVIRONMENT_IS_NODE.
if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)
if (ENVIRONMENT_IS_WORKER) // Check worker, not web, since window could be polyfilled
scriptDirectory = self.location.href;
else if (typeof document != 'undefined' && document.currentScript) // web
scriptDirectory = document.currentScript.src;
// blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them.
// otherwise, slice off the final part of the url to find the script directory.
// if scriptDirectory does not contain a slash, lastIndexOf will return -1,
// and scriptDirectory will correctly be replaced with an empty string.
// If scriptDirectory contains a query (starting with ?) or a fragment (starting with #),
// they are removed because they could contain a slash.
if (scriptDirectory.indexOf('blob:') !== 0)
scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf('/')+1);
else
scriptDirectory = '';
if (!(typeof window == 'object' || typeof importScripts == 'function' || isWechat)) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
// Differentiate the Web Worker from the Node Worker case, as reading must
// be done differently.
// include: web_or_worker_shell_read.js
read_ = (url) =>
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.send(null);
return xhr.responseText;
if (ENVIRONMENT_IS_WORKER)
readBinary = (url) =>
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.responseType = 'arraybuffer';
xhr.send(null);
return new Uint8Array(/** @type!ArrayBuffer */(xhr.response));
;
readAsync = (url, onload, onerror) =>
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = () =>
if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) // file URLs can return 0
onload(xhr.response);
return;
onerror();
;
xhr.onerror = onerror;
xhr.send(null);
// end include: web_or_worker_shell_read.js
setWindowTitle = (title) => document.title = title;
else
throw new Error('environment detection error');
var out = Module['print'] || console.log.bind(console);
var err = Module['printErr'] || console.warn.bind(console);
// Merge back in the overrides
Object.assign(Module, moduleOverrides);
// Free the object hierarchy contained in the overrides, this lets the GC
// reclaim data used e.g. in memoryInitializerRequest, which is a large typed array.
moduleOverrides = null;
checkIncomingModuleAPI();
// Emit code to handle expected values on the Module object. This applies Module.x
// to the proper local x. This has two benefits: first, we only emit it if it is
// expected to arrive, and second, by using a local everywhere else that can be
// minified.
if (Module['arguments']) arguments_ = Module['arguments'];legacyModuleProp('arguments', 'arguments_');
if (Module['thisProgram']) thisProgram = Module['thisProgram'];legacyModuleProp('thisProgram', 'thisProgram');
if (Module['quit']) quit_ = Module['quit'];legacyModuleProp('quit', 'quit_');
// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message
// Assertions on removed incoming Module JS APIs.
assert(typeof Module['memoryInitializerPrefixURL'] == 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead');
assert(typeof Module['pthreadMainPrefixURL'] == 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead');
assert(typeof Module['cdInitializerPrefixURL'] == 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead');
assert(typeof Module['filePackagePrefixURL'] == 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead');
assert(typeof Module['read'] == 'undefined', 'Module.read option was removed (modify read_ in JS)');
assert(typeof Module['readAsync'] == 以上是关于C语言转WebAssembly的主要内容,如果未能解决你的问题,请参考以下文章