1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
|
function integrateWasmJS(Module) {
// wasm.js has several methods for creating the compiled code module here:
// * 'wasm-s-parser': load s-expression code from a .wast and create wasm
// * 'asm2wasm': load asm.js code and translate to wasm
// * 'just-asm': no wasm, just load the asm.js code and use that (good for testing)
// The method can be set at compile time (BINARYEN_METHOD), or runtime by setting Module['wasmJSMethod'].
var method = Module['wasmJSMethod'] || 'wasm-s-parser';
assert(method == 'asm2wasm' || method == 'wasm-s-parser' || method == 'just-asm');
if (method == 'just-asm') {
eval(Module['read'](Module['asmjsCodeFile']));
return;
}
var asm2wasmImports = { // special asm2wasm imports
"f64-rem": function(x, y) {
return x % y;
},
"f64-to-int": function(x) {
return x | 0;
},
"debugger": function() {
debugger;
},
};
function flatten(obj) {
var ret = {};
for (var x in obj) {
for (var y in obj[x]) {
if (ret[y]) Module['printErr']('warning: flatten dupe: ' + y);
ret[y] = obj[x][y];
}
}
return ret;
}
// wasm lacks globals, so asm2wasm maps them into locations in memory. that information cannot
// be present in the wasm output of asm2wasm, so we store it in a side file. If we load asm2wasm
// output, either generated ahead of time or on the client, we need to apply those mapped
// globals after loading the module.
function applyMappedGlobals() {
var mappedGlobals = JSON.parse(Module['read'](Module['wasmCodeFile'] + '.mappedGlobals'));
for (var name in mappedGlobals) {
var global = mappedGlobals[name];
if (!global.import) continue; // non-imports are initialized to zero in the typed array anyhow, so nothing to do here
var value = wasmJS['lookupImport'](global.module, global.base);
var address = global.address;
switch (global.type) {
case WasmTypes.i32: Module['HEAP32'][address >> 2] = value; break;
case WasmTypes.f32: Module['HEAPF32'][address >> 2] = value; break;
case WasmTypes.f64: Module['HEAPF64'][address >> 3] = value; break;
default: abort();
}
}
}
if (typeof WASM === 'object') {
// Provide an "asm.js function" for the application, called to "link" the asm.js module. We instantiate
// the wasm module at that time, and it receives imports and provides exports and so forth, the app
// doesn't need to care that it is wasm and not asm.
Module['asm'] = function(global, env, providedBuffer) {
// Load the wasm module
var binary = Module['readBinary'](Module['wasmCodeFile']);
// Create an instance of the module using native support in the JS engine.
var instance = WASM.instantiateModule(binary, flatten({ // XXX for now, flatten the imports
"global.Math": global.Math,
"env": env,
"asm2wasm": asm2wasmImports
}));
// The wasm instance creates its memory. But static init code might have written to
// buffer already, and we must copy it over.
// TODO: avoid this copy, by avoiding such static init writes
// TODO: in shorter term, just copy up to the last static init write
var oldBuffer = Module['buffer'];
var newBuffer = instance.memory;
assert(newBuffer.byteLength >= oldBuffer.byteLength, 'we might fail if we allocated more than TOTAL_MEMORY');
// the wasm module does write out the memory initialization, in range STATIC_BASE..STATIC_BUMP, so avoid that
(new Int8Array(newBuffer).subarray(0, STATIC_BASE)).set(new Int8Array(oldBuffer).subarray(0, STATIC_BASE));
(new Int8Array(newBuffer).subarray(STATIC_BASE + STATIC_BUMP)).set(new Int8Array(oldBuffer).subarray(STATIC_BASE + STATIC_BUMP));
updateGlobalBuffer(newBuffer);
updateGlobalBufferViews();
Module['reallocBuffer'] = function(size) {
var old = Module['buffer'];
wasmJS['asmExports']['__growWasmMemory'](size); // tiny wasm method that just does grow_memory
return Module['buffer'] !== old ? Module['buffer'] : null; // if it was reallocated, it changed
};
applyMappedGlobals();
return instance;
};
return;
}
var WasmTypes = {
none: 0,
i32: 1,
i64: 2,
f32: 3,
f64: 4
};
// Use wasm.js to polyfill and execute code in a wasm interpreter.
var wasmJS = WasmJS({});
// XXX don't be confused. Module here is in the outside program. wasmJS is the inner wasm-js.cpp.
wasmJS['outside'] = Module; // Inside wasm-js.cpp, Module['outside'] reaches the outside module.
// Information for the instance of the module.
var info = wasmJS['info'] = {
global: null,
env: null,
asm2wasm: asm2wasmImports,
parent: Module // Module inside wasm-js.cpp refers to wasm-js.cpp; this allows access to the outside program.
};
wasmJS['lookupImport'] = function(mod, base) {
var lookup = info;
if (mod.indexOf('.') < 0) {
lookup = (lookup || {})[mod];
} else {
var parts = mod.split('.');
lookup = (lookup || {})[parts[0]];
lookup = (lookup || {})[parts[1]];
}
lookup = (lookup || {})[base];
if (lookup === undefined) {
abort('bad lookupImport to (' + mod + ').' + base);
}
return lookup;
}
// The asm.js function, called to "link" the asm.js module. At that time, we are provided imports
// and respond with exports, and so forth.
Module['asm'] = function(global, env, providedBuffer) {
assert(providedBuffer === Module['buffer']); // we should not even need to pass it as a 3rd arg for wasm, but that's the asm.js way.
info.global = global;
info.env = env;
// wasm code would create its own buffer, at this time. But static init code might have
// written to the buffer already, and we must copy it over. We could just avoid
// this copy in wasm.js polyfilling, but to be as close as possible to real wasm,
// we do what wasm would do.
// TODO: avoid this copy, by avoiding such static init writes
// TODO: in shorter term, just copy up to the last static init write
var oldBuffer = Module['buffer'];
var newBuffer = new ArrayBuffer(oldBuffer.byteLength);
(new Int8Array(newBuffer)).set(new Int8Array(oldBuffer));
updateGlobalBuffer(newBuffer);
updateGlobalBufferViews();
wasmJS['providedTotalMemory'] = Module['buffer'].byteLength;
Module['reallocBuffer'] = function(size) {
var old = Module['buffer'];
wasmJS['asmExports']['__growWasmMemory'](size); // tiny wasm method that just does grow_memory
return Module['buffer'] !== old ? Module['buffer'] : null; // if it was reallocated, it changed
};
// Prepare to generate wasm, using either asm2wasm or wasm-s-parser
var code = Module['read'](method == 'asm2wasm' ? Module['asmjsCodeFile'] : Module['wasmCodeFile']);
var temp = wasmJS['_malloc'](code.length + 1);
wasmJS['writeAsciiToMemory'](code, temp);
if (method == 'asm2wasm') {
wasmJS['_load_asm2wasm'](temp);
} else {
wasmJS['_load_s_expr2wasm'](temp);
applyMappedGlobals();
}
wasmJS['_free'](temp);
wasmJS['_instantiate'](temp);
return wasmJS['asmExports'];
};
}
|