Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

webassemblyjs

Package Overview
Dependencies
Maintainers
1
Versions
96
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

webassemblyjs - npm Package Compare versions

Comparing version 1.2.3 to 1.2.4

86

lib/compiler/compile/module.js

@@ -9,4 +9,2 @@ "use strict";

var _ast = require("@webassemblyjs/ast");
var _wastIdentifierToIndex = require("@webassemblyjs/ast/lib/transform/wast-identifier-to-index");

@@ -20,2 +18,4 @@

var t = require("@webassemblyjs/ast");
var _require = require("../../errors"),

@@ -27,3 +27,2 @@ CompileError = _require.CompileError;

(0, _validation.default)(ast);
this._ast = ast;

@@ -44,3 +43,4 @@ this._start = start;

(0, _wastIdentifierToIndex.transform)(ast);
(0, _ast.traverse)(ast, {
(0, _validation.default)(ast);
t.traverse(ast, {
ModuleExport: function (_ModuleExport) {

@@ -86,3 +86,81 @@ function ModuleExport(_x) {

});
/**
* Adds missing end instructions
*/
t.traverse(ast, {
Func: function (_Func) {
function Func(_x3) {
return _Func.apply(this, arguments);
}
Func.toString = function () {
return _Func.toString();
};
return Func;
}(function (_ref3) {
var node = _ref3.node;
node.body.push(t.instruction("end"));
}),
Global: function (_Global) {
function Global(_x4) {
return _Global.apply(this, arguments);
}
Global.toString = function () {
return _Global.toString();
};
return Global;
}(function (_ref4) {
var node = _ref4.node;
node.init.push(t.instruction("end"));
}),
IfInstruction: function (_IfInstruction) {
function IfInstruction(_x5) {
return _IfInstruction.apply(this, arguments);
}
IfInstruction.toString = function () {
return _IfInstruction.toString();
};
return IfInstruction;
}(function (_ref5) {
var node = _ref5.node;
node.test.push(t.instruction("end"));
node.consequent.push(t.instruction("end"));
node.alternate.push(t.instruction("end"));
}),
BlockInstruction: function (_BlockInstruction) {
function BlockInstruction(_x6) {
return _BlockInstruction.apply(this, arguments);
}
BlockInstruction.toString = function () {
return _BlockInstruction.toString();
};
return BlockInstruction;
}(function (_ref6) {
var node = _ref6.node;
node.instr.push(t.instruction("end"));
}),
LoopInstruction: function (_LoopInstruction) {
function LoopInstruction(_x7) {
return _LoopInstruction.apply(this, arguments);
}
LoopInstruction.toString = function () {
return _LoopInstruction.toString();
};
return LoopInstruction;
}(function (_ref7) {
var node = _ref7.node;
node.instr.push(t.instruction("end"));
})
});
return new Module(ast, exports, imports, start);
}

@@ -33,2 +33,8 @@ "use strict";

return true;
} // FIXME(sven): this shoudln't be needed, we need to inject our end
// instructions after the validations
if (instr.id === "end") {
return true;
}

@@ -35,0 +41,0 @@

@@ -28,6 +28,13 @@ "use strict";

return;
}
} // FIXME(sven): this shoudln't be needed, we need to inject our end
// instructions after the validations
var last = instrs[instrs.length - 1]; // It's a ObjectInstruction
var last = instrs[instrs.length - 1];
if (last.id === "end") {
last = instrs[instrs.length - 2];
} // It's a ObjectInstruction
if (typeof last.object === "string") {

@@ -34,0 +41,0 @@ // u32 are in fact i32

5

lib/interpreter/host-func.js

@@ -86,3 +86,3 @@ "use strict";

id: t.identifier(exportinst.name)
}); // function trace(depth, blockpc, i, frame) {
}); // function trace(depth, pc, i, frame) {
// function ident() {

@@ -97,6 +97,5 @@ // let out = "";

// ident(),
// `-------------- blockpc: ${blockpc} - depth: ${depth} --------------`
// `-------------- pc: ${pc} - depth: ${depth} --------------`
// );
// console.log(ident(), "instruction:", i.id);
// console.log(ident(), "unwind reason:", frame._unwindReason);
// console.log(ident(), "locals:");

@@ -103,0 +102,0 @@ // frame.locals.forEach((stackLocal: StackLocal) => {

@@ -12,3 +12,3 @@ "use strict";

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); }

@@ -21,2 +21,6 @@ function _sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var t = require("@webassemblyjs/ast");
var _require = require("./instruction/binop"),

@@ -50,9 +54,8 @@ binopi32 = _require.binopi32,

var _require4 = require("./signals"),
createTrap = _require4.createTrap; // TODO(sven): can remove asserts call at compile to gain perf in prod
createTrap = _require4.createTrap; // Syntactic sugar for the Syntactic sugar
// TODO(sven): do it AOT?
function assert(cond) {
if (!cond) {
throw new _errors.RuntimeError("Assertion error");
}
function addEndInstruction(body) {
body.push(t.instruction("end"));
}

@@ -66,932 +69,1145 @@

function executeStackFrame(frame) {
function executeStackFrame(firstFrame) {
var depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var stack = [firstFrame];
var framepointer = 0; // eax
function createAndExecuteChildStackFrame(instrs) {
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
passCurrentContext = _ref.passCurrentContext;
var returnRegister = null;
var childStackFrame = stackframe.createChildStackFrame(frame, instrs);
function run() {
assertStackDepth(framepointer);
var frame = stack[framepointer];
if (passCurrentContext === true) {
childStackFrame.values = frame.values;
childStackFrame.labels = frame.labels;
}
(function () {
if (!(frame !== undefined)) {
throw new _errors.RuntimeError("Assertion error: " + ("no frame at " + framepointer || "unknown"));
}
})();
var res = executeStackFrame(childStackFrame, depth + 1);
pushResult(res);
}
framepointer++;
function getLocalByIndex(index) {
var local = frame.locals[index];
function getLocalByIndex(index) {
var local = frame.locals[index];
if (typeof local === "undefined") {
throw newRuntimeError("Assertion error: no local value at index " + index);
if (typeof local === "undefined") {
throw newRuntimeError("Assertion error: no local value at index " + index);
}
frame.values.push(local);
}
frame.values.push(local);
}
function setLocalByIndex(index, value) {
(function () {
if (!(typeof index === "number")) {
throw new _errors.RuntimeError("Assertion error: " + (undefined || "unknown"));
}
})();
function setLocalByIndex(index, value) {
assert(typeof index === "number");
frame.locals[index] = value;
}
function pushResult(res) {
if (typeof res === "undefined") {
return;
frame.locals[index] = value;
}
frame.values.push(res);
}
function pushResult(res) {
if (typeof res === "undefined") {
return;
}
function popArrayOfValTypes(types) {
assertNItemsOnStack(frame.values, types.length);
return types.map(function (type) {
return pop1OfType(type);
});
}
frame.values.push(res);
}
function pop1OfType(type) {
assertNItemsOnStack(frame.values, 1);
var v = frame.values.pop();
function popArrayOfValTypes(types) {
(function () {
if (frame.values.length < types.length) {
throw new _errors.RuntimeError("Assertion error: expected " + types.length + " on the stack, found " + frame.values.length);
}
})();
if (typeof type === "string" && v.type !== type) {
throw newRuntimeError("Internal failure: expected value of type " + type + " on top of the stack, type given: " + v.type);
return types.map(function (type) {
return pop1OfType(type);
});
}
return v;
}
function pop1OfType(type) {
(function () {
if (frame.values.length < 1) {
throw new _errors.RuntimeError("Assertion error: expected " + 1 + " on the stack, found " + frame.values.length);
}
})();
function pop1() {
assertNItemsOnStack(frame.values, 1);
return frame.values.pop();
}
var v = frame.values.pop();
function pop2(type1, type2) {
assertNItemsOnStack(frame.values, 2);
var c2 = frame.values.pop();
var c1 = frame.values.pop();
if (typeof type === "string" && v.type !== type) {
throw newRuntimeError("Internal failure: expected value of type " + type + " on top of the stack, type given: " + v.type);
}
if (c2.type !== type2) {
throw newRuntimeError("Internal failure: expected c2 value of type " + type2 + " on top of the stack, give type: " + c2.type);
return v;
}
if (c1.type !== type1) {
throw newRuntimeError("Internal failure: expected c1 value of type " + type1 + " on top of the stack, give type: " + c1.type);
function pop1() {
(function () {
if (frame.values.length < 1) {
throw new _errors.RuntimeError("Assertion error: expected " + 1 + " on the stack, found " + frame.values.length);
}
})();
return frame.values.pop();
}
return [c1, c2];
}
function pop2(type1, type2) {
(function () {
if (frame.values.length < 2) {
throw new _errors.RuntimeError("Assertion error: expected " + 2 + " on the stack, found " + frame.values.length);
}
})();
function getLabel(index) {
var code;
var c2 = frame.values.pop();
var c1 = frame.values.pop();
if (index.type === "NumberLiteral") {
var _label = index; // WASM
if (c2.type !== type2) {
throw newRuntimeError("Internal failure: expected c2 value of type " + type2 + " on top of the stack, give type: " + c2.type);
}
code = frame.labels.find(function (l) {
return l.value.value === _label.value;
});
} else if (index.type === "Identifier") {
var _label2 = index; // WAST
if (c1.type !== type1) {
throw newRuntimeError("Internal failure: expected c1 value of type " + type1 + " on top of the stack, give type: " + c1.type);
}
code = frame.labels.find(function (l) {
if (l.id == null) {
return false;
}
return l.id.value === _label2.value;
});
return [c1, c2];
}
if (typeof code !== "undefined") {
return code.value;
function getLabel(index) {
var code;
if (index.type === "NumberLiteral") {
var _label = index; // WASM
code = frame.labels.find(function (l) {
return l.value.value === _label.value;
});
} else if (index.type === "Identifier") {
var _label2 = index; // WAST
code = frame.labels.find(function (l) {
if (l.id == null) {
return false;
}
return l.id.value === _label2.value;
});
}
if (typeof code !== "undefined") {
return code.value;
}
}
}
function br(label) {
var code = getLabel(label);
function br(label) {
var code = getLabel(label);
if (typeof code === "undefined") {
throw newRuntimeError("Label ".concat(label.value, " doesn't exist"));
} // FIXME(sven): find a more generic way to handle label and its code
// Currently func body and block instr*.
if (typeof code === "undefined") {
throw newRuntimeError("Label ".concat(label.value, " doesn't exist"));
} // FIXME(sven): find a more generic way to handle label and its code
// Currently func body and block instr*.
var childStackFrame = stackframe.createChildStackFrame(frame, code.body || code.instr);
return executeStackFrame(childStackFrame, depth + 1);
}
var childStackFrame = stackframe.createChildStackFrame(frame, code.body || code.instr);
return executeStackFrame(childStackFrame, depth + 1);
}
function getMemoryOffset(instruction) {
if (instruction.namedArgs && instruction.namedArgs.offset) {
var offset = instruction.namedArgs.offset.value;
function getMemoryOffset(instruction) {
if (instruction.namedArgs && instruction.namedArgs.offset) {
var offset = instruction.namedArgs.offset.value;
if (offset < 0) {
throw newRuntimeError("offset must be positive");
if (offset < 0) {
throw newRuntimeError("offset must be positive");
}
if (offset > 0xffffffff) {
throw newRuntimeError("offset must be less than or equal to 0xffffffff");
}
return offset;
} else {
return 0;
}
}
if (offset > 0xffffffff) {
throw newRuntimeError("offset must be less than or equal to 0xffffffff");
function getMemory() {
if (frame.originatingModule.memaddrs.length !== 1) {
throw newRuntimeError("unknown memory");
}
return offset;
} else {
return 0;
var memAddr = frame.originatingModule.memaddrs[0];
return frame.allocator.get(memAddr);
}
}
function getMemory() {
if (frame.originatingModule.memaddrs.length !== 1) {
throw newRuntimeError("unknown memory");
function newRuntimeError(msg) {
return new _errors.RuntimeError(msg);
}
var memAddr = frame.originatingModule.memaddrs[0];
return frame.allocator.get(memAddr);
}
function createAndExecuteChildStackFrame(instrs) {
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
passCurrentContext = _ref.passCurrentContext;
function newRuntimeError(msg) {
return new _errors.RuntimeError(msg);
}
// FIXME(sven): that's wrong
var frame = stack[framepointer - 1];
assertStackDepth(depth);
(function () {
if (!(frame !== undefined)) {
throw new _errors.RuntimeError("Assertion error: " + ("no active frame" || "unknown"));
}
})();
while (frame._pc < frame.code.length) {
var instruction = frame.code[frame._pc];
var nextStackFrame = stackframe.createChildStackFrame(frame, instrs);
switch (instruction.type) {
/**
* Function declaration
*
* FIXME(sven): seems unspecified in the spec but it's required for the `call`
* instruction.
*/
case "Func":
{
var func = instruction;
/**
* Register the function into the stack frame labels
*/
if (passCurrentContext === true) {
nextStackFrame.values = frame.values;
nextStackFrame.labels = frame.labels;
} // Push the frame on top of the stack
if (_typeof(func.name) === "object") {
if (func.name.type === "Identifier") {
frame.labels.push({
value: func,
arity: func.params.length,
id: func.name
});
stack[framepointer] = nextStackFrame; // Jump and execute the next frame
run();
if (returnRegister !== null) {
var _frame$values;
(_frame$values = frame.values).push.apply(_frame$values, _toConsumableArray(returnRegister));
returnRegister = null;
}
}
while (true) {
var instruction = frame.code[frame._pc];
(function () {
if (!(instruction !== undefined)) {
throw new _errors.RuntimeError("Assertion error: " + ("no instruction at pc ".concat(frame._pc, " in frame ").concat(framepointer) || "unknown"));
}
})();
if (typeof frame.trace === "function") {
frame.trace(framepointer, frame._pc, instruction, frame);
}
frame._pc++;
switch (instruction.type) {
/**
* Function declaration
*
* FIXME(sven): seems unspecified in the spec but it's required for the `call`
* instruction.
*/
case "Func":
{
var func = instruction;
/**
* Register the function into the stack frame labels
*/
if (_typeof(func.name) === "object") {
if (func.name.type === "Identifier") {
frame.labels.push({
value: func,
arity: func.params.length,
id: func.name
});
}
}
break;
}
}
break;
}
}
switch (instruction.id) {
case "const":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-const
var n = instruction.args[0];
switch (instruction.id) {
case "const":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-const
var n = instruction.args[0];
if (typeof n === "undefined") {
throw newRuntimeError("const requires one argument, none given.");
}
if (typeof n === "undefined") {
throw newRuntimeError("const requires one argument, none given.");
if (n.type !== "NumberLiteral" && n.type !== "LongNumberLiteral" && n.type !== "FloatLiteral") {
throw newRuntimeError("const: unsupported value of type: " + n.type);
}
pushResult(castIntoStackLocalOfType(instruction.object, n.value));
break;
}
if (n.type !== "NumberLiteral" && n.type !== "LongNumberLiteral" && n.type !== "FloatLiteral") {
throw newRuntimeError("const: unsupported value of type: " + n.type);
/**
* Control Instructions
*
* https://webassembly.github.io/spec/core/exec/instructions.html#control-instructions
*/
case "nop":
{
// Do nothing
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-nop
break;
}
pushResult(castIntoStackLocalOfType(instruction.object, n.value));
break;
}
case "loop":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-loop
var loop = instruction;
/**
* Control Instructions
*
* https://webassembly.github.io/spec/core/exec/instructions.html#control-instructions
*/
(function () {
if (!(_typeof(loop.instr) === "object" && typeof loop.instr.length !== "undefined")) {
throw new _errors.RuntimeError("Assertion error: " + (undefined || "unknown"));
}
})(); // 2. Enter the block instr∗ with label
case "nop":
{
// Do nothing
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-nop
break;
}
case "loop":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-loop
var loop = instruction;
assert(_typeof(loop.instr) === "object" && typeof loop.instr.length !== "undefined"); // 2. Enter the block instr∗ with label
frame.labels.push({
value: loop,
arity: 0,
id: loop.label
});
pushResult(label.createValue(loop.label.value));
frame.labels.push({
value: loop,
arity: 0,
id: loop.label
});
pushResult(label.createValue(loop.label.value));
if (loop.instr.length > 0) {
createAndExecuteChildStackFrame(loop.instr, {
passCurrentContext: true
});
}
if (loop.instr.length > 0) {
createAndExecuteChildStackFrame(loop.instr, {
passCurrentContext: true
});
break;
}
break;
}
case "drop":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-drop
// 1. Assert: due to validation, a value is on the top of the stack.
(function () {
if (frame.values.length < 1) {
throw new _errors.RuntimeError("Assertion error: expected " + 1 + " on the stack, found " + frame.values.length);
}
})(); // 2. Pop the value valval from the stack.
case "drop":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-drop
// 1. Assert: due to validation, a value is on the top of the stack.
assertNItemsOnStack(frame.values, 1); // 2. Pop the value valval from the stack.
pop1();
break;
}
pop1();
break;
}
case "call":
{
// According to the spec call doesn't support an Identifier as argument
// but the Script syntax supports it.
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-call
var call = instruction;
case "call":
{
// According to the spec call doesn't support an Identifier as argument
// but the Script syntax supports it.
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-call
var call = instruction;
if (call.index.type === "Identifier") {
throw newRuntimeError("Internal compiler error: Identifier argument in call must be " + "transformed to a NumberLiteral node");
} // WASM
if (call.index.type === "Identifier") {
throw newRuntimeError("Internal compiler error: Identifier argument in call must be " + "transformed to a NumberLiteral node");
} // WASM
if (call.index.type === "NumberLiteral") {
var index = call.index.value;
assert(typeof frame.originatingModule !== "undefined"); // 2. Assert: due to validation, F.module.funcaddrs[x] exists.
if (call.index.type === "NumberLiteral") {
var index = call.index.value;
var funcaddr = frame.originatingModule.funcaddrs[index];
(function () {
if (!(typeof frame.originatingModule !== "undefined")) {
throw new _errors.RuntimeError("Assertion error: " + (undefined || "unknown"));
}
})(); // 2. Assert: due to validation, F.module.funcaddrs[x] exists.
if (typeof funcaddr === "undefined") {
throw newRuntimeError("No function were found in module at address ".concat(index));
} // 3. Let a be the function address F.module.funcaddrs[x]
var funcaddr = frame.originatingModule.funcaddrs[index];
var subroutine = frame.allocator.get(funcaddr);
if (typeof funcaddr === "undefined") {
throw newRuntimeError("No function were found in module at address ".concat(index));
} // 3. Let a be the function address F.module.funcaddrs[x]
if (_typeof(subroutine) !== "object") {
throw newRuntimeError("Cannot call function at address ".concat(funcaddr, ": not a function"));
} // 4. Invoke the function instance at address a
// FIXME(sven): assert that res has type of resultType
var subroutine = frame.allocator.get(funcaddr);
var _subroutine$type = _slicedToArray(subroutine.type, 2),
argTypes = _subroutine$type[0],
resultType = _subroutine$type[1];
if (_typeof(subroutine) !== "object") {
throw newRuntimeError("Cannot call function at address ".concat(funcaddr, ": not a function"));
} // 4. Invoke the function instance at address a
// FIXME(sven): assert that res has type of resultType
var args = popArrayOfValTypes(argTypes);
if (subroutine.isExternal === false) {
createAndExecuteChildStackFrame(subroutine.code);
} else {
var res = subroutine.code(args.map(function (arg) {
return arg.value;
}));
var _subroutine$type = _slicedToArray(subroutine.type, 2),
argTypes = _subroutine$type[0],
resultType = _subroutine$type[1];
if (typeof res !== "undefined") {
pushResult(castIntoStackLocalOfType(resultType, res));
var args = popArrayOfValTypes(argTypes);
if (subroutine.isExternal === false) {
createAndExecuteChildStackFrame(subroutine.code);
} else {
var res = subroutine.code(args.map(function (arg) {
return arg.value;
}));
if (typeof res !== "undefined") {
pushResult(castIntoStackLocalOfType(resultType, res));
}
}
}
break;
}
break;
}
case "block":
{
var _ret = function () {
// https://webassembly.github.io/spec/core/exec/instructions.html#blocks
var block = instruction;
/**
* Used to keep track of the number of values added on top of the stack
* because we need to remove the label after the execution of this block.
*/
case "block":
{
var _ret = function () {
// https://webassembly.github.io/spec/core/exec/instructions.html#blocks
var block = instruction;
/**
* Used to keep track of the number of values added on top of the stack
* because we need to remove the label after the execution of this block.
*/
var numberOfValuesAddedOnTopOfTheStack = 0; // 2. Enter the block instr∗ with label
var numberOfValuesAddedOnTopOfTheStack = 0; // 2. Enter the block instr∗ with label
frame.labels.push({
value: block,
arity: 0,
id: block.label
});
frame.labels.push({
value: block,
arity: 0,
id: block.label
});
if (block.label.type === "Identifier") {
pushResult(label.createValue(block.label.value));
} else {
throw newRuntimeError("Block has no id");
}
if (block.label.type === "Identifier") {
pushResult(label.createValue(block.label.value));
} else {
throw newRuntimeError("Block has no id");
}
(function () {
if (!(_typeof(block.instr) === "object" && typeof block.instr.length !== "undefined")) {
throw new _errors.RuntimeError("Assertion error: " + (undefined || "unknown"));
}
})();
assert(_typeof(block.instr) === "object" && typeof block.instr.length !== "undefined");
if (block.instr.length > 0) {
var oldStackSize = frame.values.length;
createAndExecuteChildStackFrame(block.instr, {
passCurrentContext: true
});
numberOfValuesAddedOnTopOfTheStack = frame.values.length - oldStackSize;
}
/**
* Wen exiting the block
*
* > Let m be the number of values on the top of the stack
*
* The Stack (values) are seperated by StackFrames and we are running on
* one single thread, there's no need to check if values were added.
*
* We tracked it in numberOfValuesAddedOnTopOfTheStack anyway.
*/
if (block.instr.length > 0) {
var oldStackSize = frame.values.length;
createAndExecuteChildStackFrame(block.instr, {
passCurrentContext: true
var topOfTheStack = frame.values.slice(frame.values.length - numberOfValuesAddedOnTopOfTheStack);
frame.values.splice(frame.values.length - numberOfValuesAddedOnTopOfTheStack); // 3. Assert: due to validation, the label LL is now on the top of the stack.
// 4. Pop the label from the stack.
pop1OfType("label");
frame.values = _toConsumableArray(frame.values).concat(_toConsumableArray(topOfTheStack)); // Remove label
frame.labels = frame.labels.filter(function (x) {
if (x.id == null) {
return true;
}
return x.id.value !== block.label.value;
});
numberOfValuesAddedOnTopOfTheStack = frame.values.length - oldStackSize;
}
/**
* Wen exiting the block
*
* > Let m be the number of values on the top of the stack
*
* The Stack (values) are seperated by StackFrames and we are running on
* one single thread, there's no need to check if values were added.
*
* We tracked it in numberOfValuesAddedOnTopOfTheStack anyway.
*/
return "break";
}();
if (_ret === "break") break;
}
var topOfTheStack = frame.values.slice(frame.values.length - numberOfValuesAddedOnTopOfTheStack);
frame.values.splice(frame.values.length - numberOfValuesAddedOnTopOfTheStack); // 3. Assert: due to validation, the label LL is now on the top of the stack.
// 4. Pop the label from the stack.
case "br":
{
var _ret2 = function () {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-br
var _instruction$args = _toArray(instruction.args),
label = _instruction$args[0],
children = _instruction$args.slice(1);
pop1OfType("label");
frame.values = _toConsumableArray(frame.values).concat(_toConsumableArray(topOfTheStack)); // Remove label
frame.labels = frame.labels.filter(function (x) {
if (x.id == null) {
return true;
if (label.type === "Identifier") {
throw newRuntimeError("Internal compiler error: Identifier argument in br must be " + "transformed to a NumberLiteral node");
}
return x.id.value !== block.label.value;
});
return "break";
}();
var l = label.value; // 1. Assert: due to validation, the stack contains at least l+1 labels.
if (_ret === "break") break;
}
(function () {
if (frame.values.length < l + 1) {
throw new _errors.RuntimeError("Assertion error: expected " + (l + 1) + " on the stack, found " + frame.values.length);
}
})(); // 2. Let L be the l-th label appearing on the stack, starting from the top and counting from zero.
case "br_if":
{
var _instruction$args = _slicedToArray(instruction.args, 1),
_label3 = _instruction$args[0]; // 1. Assert: due to validation, a value of type i32 is on the top of the stack.
// 2. Pop the value ci32.const c from the stack.
var seenLabels = 0;
var labelidx = {
value: "unknown"
}; // for (var i = 0, len = frame.values.length; i < len; i++) {
var c = pop1OfType("i32");
for (var i = frame.values.length; i--;) {
if (frame.values[i].type === "label") {
if (seenLabels === l) {
labelidx = frame.values[i];
break;
}
if (!c.value.eqz().isTrue()) {
// 3. If c is non-zero, then
// 3. a. Execute the instruction (br l).
var _res = br(_label3);
seenLabels++;
}
} // $FlowIgnore
pushResult(_res);
} else {// 4. Else:
// 4. a. Do nothing.
}
break;
}
var L = frame.labels.find(function (x) {
return x.id.value === labelidx.value;
});
case "if":
{
if (instruction.test.length > 0) {
createAndExecuteChildStackFrame(instruction.test);
} // 1. Assert: due to validation, a value of value type i32 is on the top of the stack.
// 2. Pop the value i32.const from the stack.
if (typeof L === "undefined") {
throw newRuntimeError("br: unknown label ".concat(labelidx.value));
} // 3. Let n be the arity of L.
var _c = pop1OfType("i32");
var n = L.arity; // 4. Assert: due to validation, there are at least nn values on the top of the stack.
if (_c.value.eqz().isTrue() === false) {
/**
* Execute consequent
*/
createAndExecuteChildStackFrame(instruction.consequent);
} else if (typeof instruction.alternate !== "undefined" && instruction.alternate.length > 0) {
/**
* Execute alternate
*/
createAndExecuteChildStackFrame(instruction.alternate);
}
(function () {
if (frame.values.length < n) {
throw new _errors.RuntimeError("Assertion error: expected " + n + " on the stack, found " + frame.values.length);
}
})(); // 5. Pop the values valn from the stack
break;
}
/**
* Administrative Instructions
*
* https://webassembly.github.io/spec/core/exec/runtime.html#administrative-instructions
*/
var val = frame.values[n];
var bottomOfTheStack = frame.values.slice(0, n);
var topOfTheStack = frame.values.slice(n + 1);
frame.values = _toConsumableArray(bottomOfTheStack).concat(_toConsumableArray(topOfTheStack)); // 6. Repeat l+1 times:
case "unreachable": // https://webassembly.github.io/spec/core/exec/instructions.html#exec-unreachable
for (var _i2 = 0; _i2 < l + 1; _i2++) {
// a. While the top of the stack is a value, do:
// i. Pop the value from the stack
var value = frame.values[frame.values.length - 1];
case "trap":
{
// signalling abrupt termination
// https://webassembly.github.io/spec/core/exec/runtime.html#syntax-trap
throw createTrap();
}
if (typeof value === "undefined") {
break;
}
case "local":
{
var _instruction$args2 = _slicedToArray(instruction.args, 1),
valtype = _instruction$args2[0];
if (value.type !== "label") {
pop1();
}
} // b. Assert: due to validation, the top of the stack now is a label.
// c. Pop the label from the stack.
var init = castIntoStackLocalOfType(valtype.name, 0);
frame.locals.push(init);
break;
}
/**
* Memory Instructions
*
* https://webassembly.github.io/spec/core/exec/instructions.html#memory-instructions
*/
pop1OfType("label"); // 7. Push the values valn to the stack.
case "get_local":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-get-local
var _index = instruction.args[0];
pushResult(val); // 0 is the current frame, 1 is it's parent.
if (typeof _index === "undefined") {
throw newRuntimeError("get_local requires one argument, none given.");
}
stack = stack.slice(0, -(l + 1));
framepointer -= l + 1; // execute childrens
if (_index.type === "NumberLiteral" || _index.type === "FloatLiteral") {
getLocalByIndex(_index.value);
} else {
throw newRuntimeError("get_local: unsupported index of type: " + _index.type);
addEndInstruction(children);
createAndExecuteChildStackFrame(children, {
passCurrentContext: true
});
return {
v: void 0
};
}();
if (_typeof(_ret2) === "object") return _ret2.v;
}
break;
}
case "br_if":
{
var _instruction$args2 = _toArray(instruction.args),
_label3 = _instruction$args2[0],
children = _instruction$args2.slice(1); // execute childrens
case "set_local":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-set-local
var _index2 = instruction.args[0];
var _init = instruction.args[1];
if (typeof _init !== "undefined" && _init.type === "Instr") {
// WAST
createAndExecuteChildStackFrame([_init], {
addEndInstruction(children);
createAndExecuteChildStackFrame(children, {
passCurrentContext: true
});
}); // 1. Assert: due to validation, a value of type i32 is on the top of the stack.
// 2. Pop the value ci32.const c from the stack.
var _res2 = pop1();
var c = pop1OfType("i32");
setLocalByIndex(_index2.value, _res2);
} else if (_index2.type === "NumberLiteral") {
// WASM
// 4. Pop the value val from the stack
var val = pop1(); // 5. Replace F.locals[x] with the value val
if (!c.value.eqz().isTrue()) {
// 3. If c is non-zero, then
// 3. a. Execute the instruction (br l).
var _res = br(_label3);
setLocalByIndex(_index2.value, val);
} else {
throw newRuntimeError("set_local: unsupported index of type: " + _index2.type);
pushResult(_res);
} else {// 4. Else:
// 4. a. Do nothing.
}
break;
}
break;
}
case "if":
{
if (instruction.test.length > 0) {
createAndExecuteChildStackFrame(instruction.test);
} // 1. Assert: due to validation, a value of value type i32 is on the top of the stack.
// 2. Pop the value i32.const from the stack.
case "tee_local":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-tee-local
var _index3 = instruction.args[0];
var _init2 = instruction.args[1];
if (typeof _init2 !== "undefined" && _init2.type === "Instr") {
// WAST
createAndExecuteChildStackFrame([_init2], {
passCurrentContext: true
});
var _c = pop1OfType("i32");
var _res3 = pop1();
if (_c.value.eqz().isTrue() === false) {
/**
* Execute consequent
*/
createAndExecuteChildStackFrame(instruction.consequent);
} else if (typeof instruction.alternate !== "undefined" && instruction.alternate.length > 0) {
/**
* Execute alternate
*/
createAndExecuteChildStackFrame(instruction.alternate);
}
setLocalByIndex(_index3.value, _res3);
pushResult(_res3);
} else if (_index3.type === "NumberLiteral") {
// WASM
// 1. Assert: due to validation, a value is on the top of the stack.
// 2. Pop the value val from the stack.
var _val = pop1(); // 3. Push the value valval to the stack.
break;
}
/**
* Administrative Instructions
*
* https://webassembly.github.io/spec/core/exec/runtime.html#administrative-instructions
*/
pushResult(_val); // 4. Push the value valval to the stack.
case "unreachable": // https://webassembly.github.io/spec/core/exec/instructions.html#exec-unreachable
pushResult(_val); // 5. Execute the instruction (set_local x).
// 5. 4. Pop the value val from the stack
case "trap":
{
// signalling abrupt termination
// https://webassembly.github.io/spec/core/exec/runtime.html#syntax-trap
throw createTrap();
}
var val2 = pop1(); // 5. 5. Replace F.locals[x] with the value val
case "local":
{
var _instruction$args3 = _slicedToArray(instruction.args, 1),
valtype = _instruction$args3[0];
setLocalByIndex(_index3.value, val2);
} else {
throw newRuntimeError("tee_local: unsupported index of type: " + _index3.type);
var init = castIntoStackLocalOfType(valtype.name, 0);
frame.locals.push(init);
break;
}
break;
}
/**
* Memory Instructions
*
* https://webassembly.github.io/spec/core/exec/instructions.html#memory-instructions
*/
case "set_global":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-set-global
var _instruction$args3 = _slicedToArray(instruction.args, 2),
_index4 = _instruction$args3[0],
right = _instruction$args3[1]; // Interpret right branch first if it's a child instruction
case "get_local":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-get-local
var _index = instruction.args[0];
if (typeof _index === "undefined") {
throw newRuntimeError("get_local requires one argument, none given.");
}
if (typeof right !== "undefined") {
createAndExecuteChildStackFrame([right], {
passCurrentContext: true
});
} // 2. Assert: due to validation, F.module.globaladdrs[x] exists.
if (_index.type === "NumberLiteral" || _index.type === "FloatLiteral") {
getLocalByIndex(_index.value);
} else {
throw newRuntimeError("get_local: unsupported index of type: " + _index.type);
}
break;
}
var globaladdr = frame.originatingModule.globaladdrs[_index4.value];
case "set_local":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-set-local
var _index2 = instruction.args[0];
var _init = instruction.args[1];
if (typeof globaladdr === "undefined") {
throw newRuntimeError("Global address ".concat(_index4.value, " not found"));
} // 4. Assert: due to validation, S.globals[a] exists.
if (typeof _init !== "undefined" && _init.type === "Instr") {
// WAST
var code = [_init];
addEndInstruction(code);
createAndExecuteChildStackFrame(code, {
passCurrentContext: true
});
var _res2 = pop1();
var globalinst = frame.allocator.get(globaladdr);
setLocalByIndex(_index2.value, _res2);
} else if (_index2.type === "NumberLiteral") {
// WASM
// 4. Pop the value val from the stack
var val = pop1(); // 5. Replace F.locals[x] with the value val
if (_typeof(globalinst) !== "object") {
throw newRuntimeError("Unexpected data for global at ".concat(globaladdr));
} // 7. Pop the value val from the stack.
setLocalByIndex(_index2.value, val);
} else {
throw newRuntimeError("set_local: unsupported index of type: " + _index2.type);
}
break;
}
var _val2 = pop1(); // 8. Replace glob.value with the value val.
case "tee_local":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-tee-local
var _index3 = instruction.args[0];
var _init2 = instruction.args[1];
if (typeof _init2 !== "undefined" && _init2.type === "Instr") {
// WAST
var _code = [_init2];
addEndInstruction(_code);
createAndExecuteChildStackFrame(_code, {
passCurrentContext: true
});
globalinst.value = _val2.value;
frame.allocator.set(globaladdr, globalinst);
break;
}
var _res3 = pop1();
case "get_global":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-get-global
var _index5 = instruction.args[0]; // 2. Assert: due to validation, F.module.globaladdrs[x] exists.
setLocalByIndex(_index3.value, _res3);
pushResult(_res3);
} else if (_index3.type === "NumberLiteral") {
// WASM
// 1. Assert: due to validation, a value is on the top of the stack.
// 2. Pop the value val from the stack.
var _val = pop1(); // 3. Push the value valval to the stack.
var _globaladdr = frame.originatingModule.globaladdrs[_index5.value];
if (typeof _globaladdr === "undefined") {
throw newRuntimeError("Unknown global at index: ".concat(_index5.value));
} // 4. Assert: due to validation, S.globals[a] exists.
pushResult(_val); // 4. Push the value valval to the stack.
pushResult(_val); // 5. Execute the instruction (set_local x).
// 5. 4. Pop the value val from the stack
var _globalinst = frame.allocator.get(_globaladdr);
var val2 = pop1(); // 5. 5. Replace F.locals[x] with the value val
if (_typeof(_globalinst) !== "object") {
throw newRuntimeError("Unexpected data for global at ".concat(_globaladdr));
} // 7. Pop the value val from the stack.
setLocalByIndex(_index3.value, val2);
} else {
throw newRuntimeError("tee_local: unsupported index of type: " + _index3.type);
}
break;
}
pushResult(_globalinst);
break;
}
case "set_global":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-set-global
var _instruction$args4 = _slicedToArray(instruction.args, 2),
_index4 = _instruction$args4[0],
right = _instruction$args4[1]; // Interpret right branch first if it's a child instruction
case "return":
{
var _args = instruction.args;
if (_args.length > 0) {
createAndExecuteChildStackFrame(_args, {
passCurrentContext: true
});
} // Abort execution and return the first item on the stack
if (typeof right !== "undefined") {
var _code2 = [right];
addEndInstruction(_code2);
createAndExecuteChildStackFrame(_code2, {
passCurrentContext: true
});
} // 2. Assert: due to validation, F.module.globaladdrs[x] exists.
return pop1();
}
var globaladdr = frame.originatingModule.globaladdrs[_index4.value];
/**
* Memory operations
*/
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-storen
if (typeof globaladdr === "undefined") {
throw newRuntimeError("Global address ".concat(_index4.value, " not found"));
} // 4. Assert: due to validation, S.globals[a] exists.
case "store":
case "store8":
case "store16":
case "store32":
{
var id = instruction.id,
object = instruction.object,
_args2 = instruction.args; // Interpret children first
// only WAST
if (typeof _args2 !== "undefined" && _args2.length > 0) {
createAndExecuteChildStackFrame(_args2, {
passCurrentContext: true
});
var globalinst = frame.allocator.get(globaladdr);
if (_typeof(globalinst) !== "object") {
throw newRuntimeError("Unexpected data for global at ".concat(globaladdr));
} // 7. Pop the value val from the stack.
var _val2 = pop1(); // 8. Replace glob.value with the value val.
globalinst.value = _val2.value;
frame.allocator.set(globaladdr, globalinst);
break;
}
var memory = getMemory();
case "get_global":
{
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-get-global
var _index5 = instruction.args[0]; // 2. Assert: due to validation, F.module.globaladdrs[x] exists.
var _pop = pop2("i32", object),
_pop2 = _slicedToArray(_pop, 2),
c1 = _pop2[0],
c2 = _pop2[1];
var _globaladdr = frame.originatingModule.globaladdrs[_index5.value];
var ptr = c1.value.toNumber() + getMemoryOffset(instruction);
var valueBuffer = c2.value.toByteArray();
if (typeof _globaladdr === "undefined") {
throw newRuntimeError("Unknown global at index: ".concat(_index5.value));
} // 4. Assert: due to validation, S.globals[a] exists.
switch (id) {
case "store8":
valueBuffer = valueBuffer.slice(0, 1);
break;
case "store16":
valueBuffer = valueBuffer.slice(0, 2);
break;
var _globalinst = frame.allocator.get(_globaladdr);
case "store32":
valueBuffer = valueBuffer.slice(0, 4);
break;
}
if (_typeof(_globalinst) !== "object") {
throw newRuntimeError("Unexpected data for global at ".concat(_globaladdr));
} // 7. Pop the value val from the stack.
if (ptr + valueBuffer.length > memory.buffer.byteLength) {
throw newRuntimeError("memory access out of bounds");
pushResult(_globalinst);
break;
}
var memoryBuffer = new Uint8Array(memory.buffer); // load / store use little-endian order
case "return":
{
var _args = instruction.args;
for (var ptrOffset = 0; ptrOffset < valueBuffer.length; ptrOffset++) {
memoryBuffer[ptr + ptrOffset] = valueBuffer[ptrOffset];
if (_args.length > 0) {
addEndInstruction(_args);
createAndExecuteChildStackFrame(_args, {
passCurrentContext: true
});
} // Abort execution and return the first item on the stack
returnRegister = [pop1()];
return;
}
break;
}
// https://webassembly.github.io/spec/core/exec/instructions.html#and
/**
* Memory operations
*/
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-storen
case "load":
case "load16_s":
case "load16_u":
case "load8_s":
case "load8_u":
case "load32_s":
case "load32_u":
{
var _id = instruction.id,
_object = instruction.object,
_args3 = instruction.args; // Interpret children first
// only WAST
case "store":
case "store8":
case "store16":
case "store32":
{
var id = instruction.id,
object = instruction.object,
_args2 = instruction.args; // Interpret children first
// only WAST
if (typeof _args3 !== "undefined" && _args3.length > 0) {
createAndExecuteChildStackFrame(_args3, {
passCurrentContext: true
});
}
if (typeof _args2 !== "undefined" && _args2.length > 0) {
addEndInstruction(_args2);
createAndExecuteChildStackFrame(_args2, {
passCurrentContext: true
});
}
var _memory = getMemory();
var memory = getMemory();
var _ptr = pop1OfType("i32").value.toNumber() + getMemoryOffset(instruction); // for i32 / i64 ops, handle extended load
var _pop = pop2("i32", object),
_pop2 = _slicedToArray(_pop, 2),
c1 = _pop2[0],
c2 = _pop2[1];
var ptr = c1.value.toNumber() + getMemoryOffset(instruction);
var valueBuffer = c2.value.toByteArray();
var extend = 0; // for i64 values, increase the bitshift by 4 bytes
switch (id) {
case "store8":
valueBuffer = valueBuffer.slice(0, 1);
break;
var extendOffset = _object === "i32" ? 0 : 32;
var signed = false;
case "store16":
valueBuffer = valueBuffer.slice(0, 2);
break;
switch (_id) {
case "load16_s":
extend = 16 + extendOffset;
signed = true;
break;
case "store32":
valueBuffer = valueBuffer.slice(0, 4);
break;
}
case "load16_u":
extend = 16 + extendOffset;
signed = false;
break;
if (ptr + valueBuffer.length > memory.buffer.byteLength) {
throw newRuntimeError("memory access out of bounds");
}
case "load8_s":
extend = 24 + extendOffset;
signed = true;
break;
var memoryBuffer = new Uint8Array(memory.buffer); // load / store use little-endian order
case "load8_u":
extend = 24 + extendOffset;
signed = false;
break;
for (var ptrOffset = 0; ptrOffset < valueBuffer.length; ptrOffset++) {
memoryBuffer[ptr + ptrOffset] = valueBuffer[ptrOffset];
}
case "load32_u":
extend = 0 + extendOffset;
signed = false;
break;
break;
}
// https://webassembly.github.io/spec/core/exec/instructions.html#and
case "load32_s":
extend = 0 + extendOffset;
signed = true;
break;
} // check for memory access out of bounds
case "load":
case "load16_s":
case "load16_u":
case "load8_s":
case "load8_u":
case "load32_s":
case "load32_u":
{
var _id = instruction.id,
_object = instruction.object,
_args3 = instruction.args; // Interpret children first
// only WAST
if (typeof _args3 !== "undefined" && _args3.length > 0) {
addEndInstruction(_args3);
createAndExecuteChildStackFrame(_args3, {
passCurrentContext: true
});
}
switch (_object) {
case "i32":
case "f32":
if (_ptr + 4 > _memory.buffer.byteLength) {
throw newRuntimeError("memory access out of bounds");
}
var _memory = getMemory();
break;
var _ptr = pop1OfType("i32").value.toNumber() + getMemoryOffset(instruction); // for i32 / i64 ops, handle extended load
case "i64":
case "f64":
if (_ptr + 8 > _memory.buffer.byteLength) {
throw newRuntimeError("memory access out of bounds");
}
break;
}
var extend = 0; // for i64 values, increase the bitshift by 4 bytes
switch (_object) {
case "i32":
pushResult(i32.createValueFromArrayBuffer(_memory.buffer, _ptr, extend, signed));
break;
var extendOffset = _object === "i32" ? 0 : 32;
var signed = false;
case "i64":
pushResult(i64.createValueFromArrayBuffer(_memory.buffer, _ptr, extend, signed));
break;
switch (_id) {
case "load16_s":
extend = 16 + extendOffset;
signed = true;
break;
case "f32":
pushResult(f32.createValueFromArrayBuffer(_memory.buffer, _ptr));
break;
case "load16_u":
extend = 16 + extendOffset;
signed = false;
break;
case "f64":
pushResult(f64.createValueFromArrayBuffer(_memory.buffer, _ptr));
break;
}
case "load8_s":
extend = 24 + extendOffset;
signed = true;
break;
break;
}
case "load8_u":
extend = 24 + extendOffset;
signed = false;
break;
/**
* Binary operations
*/
case "load32_u":
extend = 0 + extendOffset;
signed = false;
break;
case "add":
case "mul":
case "sub":
/**
* There are two seperated operation for both signed and unsigned integer,
* but since the host environment will handle that, we don't have too :)
*/
case "load32_s":
extend = 0 + extendOffset;
signed = true;
break;
} // check for memory access out of bounds
case "div_s":
case "div_u":
case "rem_s":
case "rem_u":
case "shl":
case "shr_s":
case "shr_u":
case "rotl":
case "rotr":
case "div":
case "min":
case "max":
case "copysign":
case "or":
case "xor":
case "and":
case "eq":
case "ne":
case "lt_s":
case "lt_u":
case "le_s":
case "le_u":
case "gt":
case "gt_s":
case "gt_u":
case "ge_s":
case "ge_u":
{
var binopFn = void 0;
switch (instruction.object) {
case "i32":
binopFn = binopi32;
break;
switch (_object) {
case "i32":
case "f32":
if (_ptr + 4 > _memory.buffer.byteLength) {
throw newRuntimeError("memory access out of bounds");
}
case "i64":
binopFn = binopi64;
break;
break;
case "f32":
binopFn = binopf32;
break;
case "i64":
case "f64":
if (_ptr + 8 > _memory.buffer.byteLength) {
throw newRuntimeError("memory access out of bounds");
}
case "f64":
binopFn = binopf64;
break;
break;
}
default:
throw createTrap("Unsupported operation " + instruction.id + " on " + instruction.object);
switch (_object) {
case "i32":
pushResult(i32.createValueFromArrayBuffer(_memory.buffer, _ptr, extend, signed));
break;
case "i64":
pushResult(i64.createValueFromArrayBuffer(_memory.buffer, _ptr, extend, signed));
break;
case "f32":
pushResult(f32.createValueFromArrayBuffer(_memory.buffer, _ptr));
break;
case "f64":
pushResult(f64.createValueFromArrayBuffer(_memory.buffer, _ptr));
break;
}
break;
}
var _instruction$args4 = _slicedToArray(instruction.args, 2),
left = _instruction$args4[0],
_right = _instruction$args4[1]; // Interpret left branch first if it's a child instruction
/**
* Binary operations
*/
case "add":
case "mul":
case "sub":
/**
* There are two seperated operation for both signed and unsigned integer,
* but since the host environment will handle that, we don't have too :)
*/
if (typeof left !== "undefined") {
createAndExecuteChildStackFrame([left], {
passCurrentContext: true
});
} // Interpret right branch first if it's a child instruction
case "div_s":
case "div_u":
case "rem_s":
case "rem_u":
case "shl":
case "shr_s":
case "shr_u":
case "rotl":
case "rotr":
case "div":
case "min":
case "max":
case "copysign":
case "or":
case "xor":
case "and":
case "eq":
case "ne":
case "lt_s":
case "lt_u":
case "le_s":
case "le_u":
case "gt":
case "gt_s":
case "gt_u":
case "ge_s":
case "ge_u":
{
var binopFn = void 0;
switch (instruction.object) {
case "i32":
binopFn = binopi32;
break;
if (typeof _right !== "undefined") {
createAndExecuteChildStackFrame([_right], {
passCurrentContext: true
});
}
case "i64":
binopFn = binopi64;
break;
var _pop3 = pop2(instruction.object, instruction.object),
_pop4 = _slicedToArray(_pop3, 2),
_c2 = _pop4[0],
_c3 = _pop4[1];
case "f32":
binopFn = binopf32;
break;
pushResult(binopFn(_c2, _c3, instruction.id));
break;
}
case "f64":
binopFn = binopf64;
break;
/**
* Unary operations
*/
default:
throw createTrap("Unsupported operation " + instruction.id + " on " + instruction.object);
}
case "abs":
case "neg":
case "clz":
case "ctz":
case "popcnt":
case "eqz":
case "reinterpret/f32":
case "reinterpret/f64":
{
var unopFn = void 0; // for conversion operations, the operand type appears after the forward-slash
// e.g. with i32.reinterpret/f32, the oprand is f32, and the resultant is i32
var _instruction$args5 = _slicedToArray(instruction.args, 2),
left = _instruction$args5[0],
_right = _instruction$args5[1]; // Interpret left branch first if it's a child instruction
var opType = instruction.id.indexOf("/") !== -1 ? instruction.id.split("/")[1] : instruction.object;
switch (opType) {
case "i32":
unopFn = unopi32;
break;
if (typeof left !== "undefined") {
var _code3 = [left];
addEndInstruction(_code3);
createAndExecuteChildStackFrame(_code3, {
passCurrentContext: true
});
} // Interpret right branch first if it's a child instruction
case "i64":
unopFn = unopi64;
break;
case "f32":
unopFn = unopf32;
break;
if (typeof _right !== "undefined") {
var _code4 = [_right];
addEndInstruction(_code4);
createAndExecuteChildStackFrame(_code4, {
passCurrentContext: true
});
}
case "f64":
unopFn = unopf64;
break;
var _pop3 = pop2(instruction.object, instruction.object),
_pop4 = _slicedToArray(_pop3, 2),
_c2 = _pop4[0],
_c3 = _pop4[1];
default:
throw createTrap("Unsupported operation " + instruction.id + " on " + opType);
pushResult(binopFn(_c2, _c3, instruction.id));
break;
}
var _instruction$args5 = _slicedToArray(instruction.args, 1),
operand = _instruction$args5[0]; // Interpret argument first if it's a child instruction
/**
* Unary operations
*/
case "abs":
case "neg":
case "clz":
case "ctz":
case "popcnt":
case "eqz":
case "reinterpret/f32":
case "reinterpret/f64":
{
var unopFn = void 0; // for conversion operations, the operand type appears after the forward-slash
// e.g. with i32.reinterpret/f32, the oprand is f32, and the resultant is i32
if (typeof operand !== "undefined") {
createAndExecuteChildStackFrame([operand], {
passCurrentContext: true
});
}
var opType = instruction.id.indexOf("/") !== -1 ? instruction.id.split("/")[1] : instruction.object;
var _c4 = pop1OfType(opType);
switch (opType) {
case "i32":
unopFn = unopi32;
break;
pushResult(unopFn(_c4, instruction.id));
break;
}
}
case "i64":
unopFn = unopi64;
break;
if (typeof frame.trace === "function") {
frame.trace(depth, frame._pc, instruction, frame);
}
case "f32":
unopFn = unopf32;
break;
frame._pc++;
} // Return the item on top of the values/stack;
case "f64":
unopFn = unopf64;
break;
default:
throw createTrap("Unsupported operation " + instruction.id + " on " + opType);
}
if (frame.values.length > 0) {
var _res4 = pop1();
var _instruction$args6 = _slicedToArray(instruction.args, 1),
operand = _instruction$args6[0]; // Interpret argument first if it's a child instruction
if (_res4.type !== "label") {
return _res4;
} else {
// Push label back
pushResult(_res4);
if (typeof operand !== "undefined") {
var _code5 = [operand];
addEndInstruction(_code5);
createAndExecuteChildStackFrame(_code5, {
passCurrentContext: true
});
}
var _c4 = pop1OfType(opType);
pushResult(unopFn(_c4, instruction.id));
break;
}
case "end":
{
// Pop active frame from the stack
stack.pop();
framepointer--; // Return the item on top of the values/stack;
if (frame.values.length > 0) {
var _res4 = pop1();
if (_res4.type !== "label") {
returnRegister = [_res4];
} else {
// Push label back
pushResult(_res4);
}
}
return;
}
}
}
}
}
function assertNItemsOnStack(stack, numberOfItem) {
if (stack.length < numberOfItem) {
throw new _errors.RuntimeError("Assertion error: expected " + numberOfItem + " on the stack, found " + stack.length);
run();
if (returnRegister !== null) {
// FIXME(sven): handle multiple results in hostfunc
return returnRegister[0];
}
}

@@ -28,5 +28,6 @@ "use strict";

// TODO(sven): find a specification reference for that
// FIXME(sven): +1 because of the implicit end, change the order of validations
if (node.init.length > 1 || node.init.length === 0) {
if (node.init.length > 2 || node.init.length === 1) {
throw new CompileError("type mismatch");

@@ -33,0 +34,0 @@ } // Validate the type

{
"name": "webassemblyjs",
"version": "1.2.3",
"version": "1.2.4",
"keywords": [

@@ -21,9 +21,9 @@ "webassembly",

"dependencies": {
"@webassemblyjs/ast": "1.2.3",
"@webassemblyjs/wasm-parser": "1.2.3",
"@webassemblyjs/wast-parser": "1.2.3",
"@webassemblyjs/ast": "1.2.4",
"@webassemblyjs/wasm-parser": "1.2.4",
"@webassemblyjs/wast-parser": "1.2.4",
"long": "^3.2.0"
},
"devDependencies": {
"@webassemblyjs/floating-point-hex-parser": "1.2.3"
"@webassemblyjs/floating-point-hex-parser": "1.2.4"
},

@@ -30,0 +30,0 @@ "repository": {

// @flow
import { traverse } from "@webassemblyjs/ast";
import { transform } from "@webassemblyjs/ast/lib/transform/wast-identifier-to-index";
const t = require("@webassemblyjs/ast");
import validateAST from "../validation";

@@ -22,4 +23,2 @@ const { CompileError } = require("../../errors");

) {
validateAST(ast);
this._ast = ast;

@@ -43,3 +42,5 @@ this._start = start;

traverse(ast, {
validateAST(ast);
t.traverse(ast, {
ModuleExport({ node }: NodePath<ModuleExport>) {

@@ -63,3 +64,30 @@ if (node.descr.type === "Func") {

/**
* Adds missing end instructions
*/
t.traverse(ast, {
Func({ node }: NodePath<Func>) {
node.body.push(t.instruction("end"));
},
Global({ node }: NodePath<Global>) {
node.init.push(t.instruction("end"));
},
IfInstruction({ node }: NodePath<IfInstruction>) {
node.test.push(t.instruction("end"));
node.consequent.push(t.instruction("end"));
node.alternate.push(t.instruction("end"));
},
BlockInstruction({ node }: NodePath<BlockInstruction>) {
node.instr.push(t.instruction("end"));
},
LoopInstruction({ node }: NodePath<LoopInstruction>) {
node.instr.push(t.instruction("end"));
}
});
return new Module(ast, exports, imports, start);
}

@@ -30,4 +30,10 @@ // @flow

// FIXME(sven): this shoudln't be needed, we need to inject our end
// instructions after the validations
if (instr.id === "end") {
return true;
}
return false;
}, true);
}

@@ -24,4 +24,10 @@ // @flow

const last = instrs[instrs.length - 1];
// FIXME(sven): this shoudln't be needed, we need to inject our end
// instructions after the validations
let last = instrs[instrs.length - 1];
if (last.id === "end") {
last = instrs[instrs.length - 2];
}
// It's a ObjectInstruction

@@ -28,0 +34,0 @@ if (typeof last.object === "string") {

@@ -99,3 +99,3 @@ // @flow

// function trace(depth, blockpc, i, frame) {
// function trace(depth, pc, i, frame) {
// function ident() {

@@ -113,7 +113,6 @@ // let out = "";

// ident(),
// `-------------- blockpc: ${blockpc} - depth: ${depth} --------------`
// `-------------- pc: ${pc} - depth: ${depth} --------------`
// );
// console.log(ident(), "instruction:", i.id);
// console.log(ident(), "unwind reason:", frame._unwindReason);

@@ -120,0 +119,0 @@ // console.log(ident(), "locals:");

// @flow
type AssertArgs = {|
MSG?: string,
COND: boolean
|};
declare function assert(AssertArgs): void;
type AssertNItemsOnStackArgs = {|
N: number
|};
declare function assertNItemsOnStack(AssertNItemsOnStackArgs): void;
import { Memory } from "../runtime/values/memory";
import { RuntimeError } from "../../errors";
const t = require("@webassemblyjs/ast");
MACRO(
assert,
`if (!COND) {
throw new RuntimeError("Assertion error: " + (MSG || "unknown"));
}`
);
MACRO(
assertNItemsOnStack,
`if (frame.values.length < N) {
throw new RuntimeError("Assertion error: expected " + N + " on the stack, found " + frame.values.length);
}`
);
const {

@@ -23,7 +50,6 @@ binopi32,

// TODO(sven): can remove asserts call at compile to gain perf in prod
function assert(cond) {
if (!cond) {
throw new RuntimeError("Assertion error");
}
// Syntactic sugar for the Syntactic sugar
// TODO(sven): do it AOT?
function addEndInstruction(body) {
body.push(t.instruction("end"));
}

@@ -43,960 +69,1145 @@

export function executeStackFrame(
frame: StackFrame,
firstFrame: StackFrame,
depth: number = 0
): ?StackLocal {
function createAndExecuteChildStackFrame(
instrs: Array<Instruction>,
{ passCurrentContext }: createChildStackFrameOptions = {}
): ?StackLocal {
const childStackFrame = stackframe.createChildStackFrame(frame, instrs);
let stack: Array<StackFrame> = [firstFrame];
let framepointer = 0;
if (passCurrentContext === true) {
childStackFrame.values = frame.values;
childStackFrame.labels = frame.labels;
}
// eax
let returnRegister = null;
const res = executeStackFrame(childStackFrame, depth + 1);
pushResult(res);
}
function run() {
assertStackDepth(framepointer);
function getLocalByIndex(index: number) {
const local = frame.locals[index];
const frame = stack[framepointer];
if (typeof local === "undefined") {
throw newRuntimeError(
"Assertion error: no local value at index " + index
);
assert({
COND: frame !== undefined,
MSG: "no frame at " + framepointer
});
framepointer++;
function getLocalByIndex(index: number) {
const local = frame.locals[index];
if (typeof local === "undefined") {
throw newRuntimeError(
"Assertion error: no local value at index " + index
);
}
frame.values.push(local);
}
frame.values.push(local);
}
function setLocalByIndex(index: number, value: StackLocal) {
assert({ COND: typeof index === "number" });
function setLocalByIndex(index: number, value: StackLocal) {
assert(typeof index === "number");
frame.locals[index] = value;
}
frame.locals[index] = value;
}
function pushResult(res: ?StackLocal) {
if (typeof res === "undefined") {
return;
}
function pushResult(res: ?StackLocal) {
if (typeof res === "undefined") {
return;
frame.values.push(res);
}
frame.values.push(res);
}
function popArrayOfValTypes(types: Array<Valtype>): any {
assertNItemsOnStack({ N: types.length });
function popArrayOfValTypes(types: Array<Valtype>): any {
assertNItemsOnStack(frame.values, types.length);
return types.map(type => {
return pop1OfType(type);
});
}
return types.map(type => {
return pop1OfType(type);
});
}
function pop1OfType(type: Valtype): any {
assertNItemsOnStack({ N: 1 });
function pop1OfType(type: Valtype): any {
assertNItemsOnStack(frame.values, 1);
const v = frame.values.pop();
const v = frame.values.pop();
if (typeof type === "string" && v.type !== type) {
throw newRuntimeError(
"Internal failure: expected value of type " +
type +
" on top of the stack, type given: " +
v.type
);
}
if (typeof type === "string" && v.type !== type) {
throw newRuntimeError(
"Internal failure: expected value of type " +
type +
" on top of the stack, type given: " +
v.type
);
return v;
}
return v;
}
function pop1(): any {
assertNItemsOnStack({ N: 1 });
function pop1(): any {
assertNItemsOnStack(frame.values, 1);
return frame.values.pop();
}
return frame.values.pop();
}
function pop2(type1: Valtype, type2: Valtype): [any, any] {
assertNItemsOnStack({ N: 2 });
function pop2(type1: Valtype, type2: Valtype): [any, any] {
assertNItemsOnStack(frame.values, 2);
const c2 = frame.values.pop();
const c1 = frame.values.pop();
const c2 = frame.values.pop();
const c1 = frame.values.pop();
if (c2.type !== type2) {
throw newRuntimeError(
"Internal failure: expected c2 value of type " +
type2 +
" on top of the stack, give type: " +
c2.type
);
}
if (c2.type !== type2) {
throw newRuntimeError(
"Internal failure: expected c2 value of type " +
type2 +
" on top of the stack, give type: " +
c2.type
);
if (c1.type !== type1) {
throw newRuntimeError(
"Internal failure: expected c1 value of type " +
type1 +
" on top of the stack, give type: " +
c1.type
);
}
return [c1, c2];
}
if (c1.type !== type1) {
throw newRuntimeError(
"Internal failure: expected c1 value of type " +
type1 +
" on top of the stack, give type: " +
c1.type
);
function getLabel(index: Index): any {
let code;
if (index.type === "NumberLiteral") {
const label: NumberLiteral = index;
// WASM
code = frame.labels.find(l => l.value.value === label.value);
} else if (index.type === "Identifier") {
const label: Identifier = index;
// WAST
code = frame.labels.find(l => {
if (l.id == null) {
return false;
}
return l.id.value === label.value;
});
}
if (typeof code !== "undefined") {
return code.value;
}
}
return [c1, c2];
}
function br(label: Index) {
const code = getLabel(label);
function getLabel(index: Index): any {
let code;
if (typeof code === "undefined") {
throw newRuntimeError(`Label ${label.value} doesn't exist`);
}
if (index.type === "NumberLiteral") {
const label: NumberLiteral = index;
// FIXME(sven): find a more generic way to handle label and its code
// Currently func body and block instr*.
const childStackFrame = stackframe.createChildStackFrame(
frame,
code.body || code.instr
);
// WASM
code = frame.labels.find(l => l.value.value === label.value);
} else if (index.type === "Identifier") {
const label: Identifier = index;
return executeStackFrame(childStackFrame, depth + 1);
}
// WAST
code = frame.labels.find(l => {
if (l.id == null) {
return false;
function getMemoryOffset(instruction) {
if (instruction.namedArgs && instruction.namedArgs.offset) {
const offset = instruction.namedArgs.offset.value;
if (offset < 0) {
throw newRuntimeError("offset must be positive");
}
if (offset > 0xffffffff) {
throw newRuntimeError(
"offset must be less than or equal to 0xffffffff"
);
}
return offset;
} else {
return 0;
}
}
return l.id.value === label.value;
});
function getMemory(): Memory {
if (frame.originatingModule.memaddrs.length !== 1) {
throw newRuntimeError("unknown memory");
}
const memAddr = frame.originatingModule.memaddrs[0];
return frame.allocator.get(memAddr);
}
if (typeof code !== "undefined") {
return code.value;
function newRuntimeError(msg) {
return new RuntimeError(msg);
}
}
function br(label: Index) {
const code = getLabel(label);
function createAndExecuteChildStackFrame(
instrs: Array<Instruction>,
{ passCurrentContext }: createChildStackFrameOptions = {}
): ?StackLocal {
// FIXME(sven): that's wrong
const frame = stack[framepointer - 1];
if (typeof code === "undefined") {
throw newRuntimeError(`Label ${label.value} doesn't exist`);
assert({
COND: frame !== undefined,
MSG: "no active frame"
});
const nextStackFrame = stackframe.createChildStackFrame(frame, instrs);
if (passCurrentContext === true) {
nextStackFrame.values = frame.values;
nextStackFrame.labels = frame.labels;
}
// Push the frame on top of the stack
stack[framepointer] = nextStackFrame;
// Jump and execute the next frame
run();
if (returnRegister !== null) {
frame.values.push(...returnRegister);
returnRegister = null;
}
}
// FIXME(sven): find a more generic way to handle label and its code
// Currently func body and block instr*.
const childStackFrame = stackframe.createChildStackFrame(
frame,
code.body || code.instr
);
while (true) {
const instruction = frame.code[frame._pc];
return executeStackFrame(childStackFrame, depth + 1);
}
assert({
COND: instruction !== undefined,
MSG: `no instruction at pc ${frame._pc} in frame ${framepointer}`
});
function getMemoryOffset(instruction) {
if (instruction.namedArgs && instruction.namedArgs.offset) {
const offset = instruction.namedArgs.offset.value;
if (offset < 0) {
throw newRuntimeError("offset must be positive");
if (typeof frame.trace === "function") {
frame.trace(framepointer, frame._pc, instruction, frame);
}
if (offset > 0xffffffff) {
throw newRuntimeError(
"offset must be less than or equal to 0xffffffff"
);
frame._pc++;
switch (instruction.type) {
/**
* Function declaration
*
* FIXME(sven): seems unspecified in the spec but it's required for the `call`
* instruction.
*/
case "Func": {
const func = instruction;
/**
* Register the function into the stack frame labels
*/
if (typeof func.name === "object") {
if (func.name.type === "Identifier") {
frame.labels.push({
value: func,
arity: func.params.length,
id: func.name
});
}
}
break;
}
}
return offset;
} else {
return 0;
}
}
function getMemory(): Memory {
if (frame.originatingModule.memaddrs.length !== 1) {
throw newRuntimeError("unknown memory");
}
switch (instruction.id) {
case "const": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-const
const memAddr = frame.originatingModule.memaddrs[0];
return frame.allocator.get(memAddr);
}
const n = instruction.args[0];
function newRuntimeError(msg) {
return new RuntimeError(msg);
}
if (typeof n === "undefined") {
throw newRuntimeError("const requires one argument, none given.");
}
assertStackDepth(depth);
if (
n.type !== "NumberLiteral" &&
n.type !== "LongNumberLiteral" &&
n.type !== "FloatLiteral"
) {
throw newRuntimeError(
"const: unsupported value of type: " + n.type
);
}
while (frame._pc < frame.code.length) {
const instruction = frame.code[frame._pc];
pushResult(castIntoStackLocalOfType(instruction.object, n.value));
switch (instruction.type) {
/**
* Function declaration
*
* FIXME(sven): seems unspecified in the spec but it's required for the `call`
* instruction.
*/
case "Func": {
const func = instruction;
break;
}
/**
* Register the function into the stack frame labels
* Control Instructions
*
* https://webassembly.github.io/spec/core/exec/instructions.html#control-instructions
*/
if (typeof func.name === "object") {
if (func.name.type === "Identifier") {
frame.labels.push({
value: func,
arity: func.params.length,
id: func.name
case "nop": {
// Do nothing
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-nop
break;
}
case "loop": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-loop
const loop = instruction;
assert({
COND:
typeof loop.instr === "object" &&
typeof loop.instr.length !== "undefined"
});
// 2. Enter the block instr∗ with label
frame.labels.push({
value: loop,
arity: 0,
id: loop.label
});
pushResult(label.createValue(loop.label.value));
if (loop.instr.length > 0) {
createAndExecuteChildStackFrame(loop.instr, {
passCurrentContext: true
});
}
break;
}
break;
}
}
case "drop": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-drop
switch (instruction.id) {
case "const": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-const
// 1. Assert: due to validation, a value is on the top of the stack.
assertNItemsOnStack({ N: 1 });
const n = instruction.args[0];
// 2. Pop the value valval from the stack.
pop1();
if (typeof n === "undefined") {
throw newRuntimeError("const requires one argument, none given.");
break;
}
if (
n.type !== "NumberLiteral" &&
n.type !== "LongNumberLiteral" &&
n.type !== "FloatLiteral"
) {
throw newRuntimeError("const: unsupported value of type: " + n.type);
}
case "call": {
// According to the spec call doesn't support an Identifier as argument
// but the Script syntax supports it.
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-call
pushResult(castIntoStackLocalOfType(instruction.object, n.value));
const call = instruction;
break;
}
if (call.index.type === "Identifier") {
throw newRuntimeError(
"Internal compiler error: Identifier argument in call must be " +
"transformed to a NumberLiteral node"
);
}
/**
* Control Instructions
*
* https://webassembly.github.io/spec/core/exec/instructions.html#control-instructions
*/
case "nop": {
// Do nothing
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-nop
break;
}
// WASM
if (call.index.type === "NumberLiteral") {
const index = call.index.value;
case "loop": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-loop
const loop = instruction;
assert({
COND: typeof frame.originatingModule !== "undefined"
});
assert(
typeof loop.instr === "object" &&
typeof loop.instr.length !== "undefined"
);
// 2. Assert: due to validation, F.module.funcaddrs[x] exists.
const funcaddr = frame.originatingModule.funcaddrs[index];
// 2. Enter the block instr∗ with label
frame.labels.push({
value: loop,
arity: 0,
id: loop.label
});
if (typeof funcaddr === "undefined") {
throw newRuntimeError(
`No function were found in module at address ${index}`
);
}
pushResult(label.createValue(loop.label.value));
// 3. Let a be the function address F.module.funcaddrs[x]
if (loop.instr.length > 0) {
createAndExecuteChildStackFrame(loop.instr, {
passCurrentContext: true
});
const subroutine = frame.allocator.get(funcaddr);
if (typeof subroutine !== "object") {
throw newRuntimeError(
`Cannot call function at address ${funcaddr}: not a function`
);
}
// 4. Invoke the function instance at address a
// FIXME(sven): assert that res has type of resultType
const [argTypes, resultType] = subroutine.type;
const args = popArrayOfValTypes(argTypes);
if (subroutine.isExternal === false) {
createAndExecuteChildStackFrame(subroutine.code);
} else {
const res = subroutine.code(args.map(arg => arg.value));
if (typeof res !== "undefined") {
pushResult(castIntoStackLocalOfType(resultType, res));
}
}
}
break;
}
break;
}
case "block": {
// https://webassembly.github.io/spec/core/exec/instructions.html#blocks
case "drop": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-drop
const block = instruction;
// 1. Assert: due to validation, a value is on the top of the stack.
assertNItemsOnStack(frame.values, 1);
/**
* Used to keep track of the number of values added on top of the stack
* because we need to remove the label after the execution of this block.
*/
let numberOfValuesAddedOnTopOfTheStack = 0;
// 2. Pop the value valval from the stack.
pop1();
// 2. Enter the block instr∗ with label
frame.labels.push({
value: block,
arity: 0,
id: block.label
});
break;
}
if (block.label.type === "Identifier") {
pushResult(label.createValue(block.label.value));
} else {
throw newRuntimeError("Block has no id");
}
case "call": {
// According to the spec call doesn't support an Identifier as argument
// but the Script syntax supports it.
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-call
assert({
COND:
typeof block.instr === "object" &&
typeof block.instr.length !== "undefined"
});
const call = instruction;
if (block.instr.length > 0) {
const oldStackSize = frame.values.length;
if (call.index.type === "Identifier") {
throw newRuntimeError(
"Internal compiler error: Identifier argument in call must be " +
"transformed to a NumberLiteral node"
createAndExecuteChildStackFrame(block.instr, {
passCurrentContext: true
});
numberOfValuesAddedOnTopOfTheStack =
frame.values.length - oldStackSize;
}
/**
* Wen exiting the block
*
* > Let m be the number of values on the top of the stack
*
* The Stack (values) are seperated by StackFrames and we are running on
* one single thread, there's no need to check if values were added.
*
* We tracked it in numberOfValuesAddedOnTopOfTheStack anyway.
*/
const topOfTheStack = frame.values.slice(
frame.values.length - numberOfValuesAddedOnTopOfTheStack
);
}
// WASM
if (call.index.type === "NumberLiteral") {
const index = call.index.value;
frame.values.splice(
frame.values.length - numberOfValuesAddedOnTopOfTheStack
);
assert(typeof frame.originatingModule !== "undefined");
// 3. Assert: due to validation, the label LL is now on the top of the stack.
// 4. Pop the label from the stack.
pop1OfType("label");
// 2. Assert: due to validation, F.module.funcaddrs[x] exists.
const funcaddr = frame.originatingModule.funcaddrs[index];
frame.values = [...frame.values, ...topOfTheStack];
if (typeof funcaddr === "undefined") {
throw newRuntimeError(
`No function were found in module at address ${index}`
);
}
// Remove label
frame.labels = frame.labels.filter(x => {
if (x.id == null) {
return true;
}
// 3. Let a be the function address F.module.funcaddrs[x]
return x.id.value !== block.label.value;
});
const subroutine = frame.allocator.get(funcaddr);
break;
}
if (typeof subroutine !== "object") {
case "br": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-br
const [label, ...children] = instruction.args;
if (label.type === "Identifier") {
throw newRuntimeError(
`Cannot call function at address ${funcaddr}: not a function`
"Internal compiler error: Identifier argument in br must be " +
"transformed to a NumberLiteral node"
);
}
// 4. Invoke the function instance at address a
const l = label.value;
// FIXME(sven): assert that res has type of resultType
const [argTypes, resultType] = subroutine.type;
// 1. Assert: due to validation, the stack contains at least l+1 labels.
assertNItemsOnStack({ N: l + 1 });
const args = popArrayOfValTypes(argTypes);
// 2. Let L be the l-th label appearing on the stack, starting from the top and counting from zero.
let seenLabels = 0;
let labelidx = { value: "unknown" };
// for (var i = 0, len = frame.values.length; i < len; i++) {
for (let i = frame.values.length; i--; ) {
if (frame.values[i].type === "label") {
if (seenLabels === l) {
labelidx = frame.values[i];
break;
}
if (subroutine.isExternal === false) {
createAndExecuteChildStackFrame(subroutine.code);
} else {
const res = subroutine.code(args.map(arg => arg.value));
if (typeof res !== "undefined") {
pushResult(castIntoStackLocalOfType(resultType, res));
seenLabels++;
}
}
}
break;
}
// $FlowIgnore
const L = frame.labels.find(x => x.id.value === labelidx.value);
case "block": {
// https://webassembly.github.io/spec/core/exec/instructions.html#blocks
if (typeof L === "undefined") {
throw newRuntimeError(`br: unknown label ${labelidx.value}`);
}
const block = instruction;
// 3. Let n be the arity of L.
const n = L.arity;
/**
* Used to keep track of the number of values added on top of the stack
* because we need to remove the label after the execution of this block.
*/
let numberOfValuesAddedOnTopOfTheStack = 0;
// 4. Assert: due to validation, there are at least nn values on the top of the stack.
assertNItemsOnStack({ N: n });
// 2. Enter the block instr∗ with label
frame.labels.push({
value: block,
arity: 0,
id: block.label
});
// 5. Pop the values valn from the stack
const val = frame.values[n];
if (block.label.type === "Identifier") {
pushResult(label.createValue(block.label.value));
} else {
throw newRuntimeError("Block has no id");
}
const bottomOfTheStack = frame.values.slice(0, n);
const topOfTheStack = frame.values.slice(n + 1);
assert(
typeof block.instr === "object" &&
typeof block.instr.length !== "undefined"
);
frame.values = [...bottomOfTheStack, ...topOfTheStack];
if (block.instr.length > 0) {
const oldStackSize = frame.values.length;
// 6. Repeat l+1 times:
for (let i = 0; i < l + 1; i++) {
// a. While the top of the stack is a value, do:
// i. Pop the value from the stack
const value = frame.values[frame.values.length - 1];
createAndExecuteChildStackFrame(block.instr, {
if (typeof value === "undefined") {
break;
}
if (value.type !== "label") {
pop1();
}
}
// b. Assert: due to validation, the top of the stack now is a label.
// c. Pop the label from the stack.
pop1OfType("label");
// 7. Push the values valn to the stack.
pushResult(val);
// 0 is the current frame, 1 is it's parent.
stack = stack.slice(0, -(l + 1));
framepointer -= l + 1;
// execute childrens
addEndInstruction(children);
createAndExecuteChildStackFrame(children, {
passCurrentContext: true
});
numberOfValuesAddedOnTopOfTheStack =
frame.values.length - oldStackSize;
return;
}
/**
* Wen exiting the block
*
* > Let m be the number of values on the top of the stack
*
* The Stack (values) are seperated by StackFrames and we are running on
* one single thread, there's no need to check if values were added.
*
* We tracked it in numberOfValuesAddedOnTopOfTheStack anyway.
*/
const topOfTheStack = frame.values.slice(
frame.values.length - numberOfValuesAddedOnTopOfTheStack
);
case "br_if": {
const [label, ...children] = instruction.args;
frame.values.splice(
frame.values.length - numberOfValuesAddedOnTopOfTheStack
);
// execute childrens
addEndInstruction(children);
// 3. Assert: due to validation, the label LL is now on the top of the stack.
// 4. Pop the label from the stack.
pop1OfType("label");
createAndExecuteChildStackFrame(children, {
passCurrentContext: true
});
frame.values = [...frame.values, ...topOfTheStack];
// 1. Assert: due to validation, a value of type i32 is on the top of the stack.
// 2. Pop the value ci32.const c from the stack.
const c = pop1OfType("i32");
// Remove label
frame.labels = frame.labels.filter(x => {
if (x.id == null) {
return true;
if (!c.value.eqz().isTrue()) {
// 3. If c is non-zero, then
// 3. a. Execute the instruction (br l).
const res = br(label);
pushResult(res);
} else {
// 4. Else:
// 4. a. Do nothing.
}
return x.id.value !== block.label.value;
});
break;
}
break;
}
case "if": {
if (instruction.test.length > 0) {
createAndExecuteChildStackFrame(instruction.test);
}
case "br_if": {
const [label] = instruction.args;
// 1. Assert: due to validation, a value of value type i32 is on the top of the stack.
// 2. Pop the value i32.const from the stack.
const c = pop1OfType("i32");
// 1. Assert: due to validation, a value of type i32 is on the top of the stack.
// 2. Pop the value ci32.const c from the stack.
const c = pop1OfType("i32");
if (c.value.eqz().isTrue() === false) {
/**
* Execute consequent
*/
createAndExecuteChildStackFrame(instruction.consequent);
} else if (
typeof instruction.alternate !== "undefined" &&
instruction.alternate.length > 0
) {
/**
* Execute alternate
*/
createAndExecuteChildStackFrame(instruction.alternate);
}
if (!c.value.eqz().isTrue()) {
// 3. If c is non-zero, then
// 3. a. Execute the instruction (br l).
const res = br(label);
break;
}
pushResult(res);
} else {
// 4. Else:
// 4. a. Do nothing.
/**
* Administrative Instructions
*
* https://webassembly.github.io/spec/core/exec/runtime.html#administrative-instructions
*/
case "unreachable":
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-unreachable
case "trap": {
// signalling abrupt termination
// https://webassembly.github.io/spec/core/exec/runtime.html#syntax-trap
throw createTrap();
}
break;
}
case "local": {
const [valtype] = instruction.args;
case "if": {
if (instruction.test.length > 0) {
createAndExecuteChildStackFrame(instruction.test);
const init = castIntoStackLocalOfType(valtype.name, 0);
frame.locals.push(init);
break;
}
// 1. Assert: due to validation, a value of value type i32 is on the top of the stack.
// 2. Pop the value i32.const from the stack.
const c = pop1OfType("i32");
/**
* Memory Instructions
*
* https://webassembly.github.io/spec/core/exec/instructions.html#memory-instructions
*/
case "get_local": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-get-local
const index = instruction.args[0];
if (c.value.eqz().isTrue() === false) {
/**
* Execute consequent
*/
createAndExecuteChildStackFrame(instruction.consequent);
} else if (
typeof instruction.alternate !== "undefined" &&
instruction.alternate.length > 0
) {
/**
* Execute alternate
*/
createAndExecuteChildStackFrame(instruction.alternate);
if (typeof index === "undefined") {
throw newRuntimeError(
"get_local requires one argument, none given."
);
}
if (index.type === "NumberLiteral" || index.type === "FloatLiteral") {
getLocalByIndex(index.value);
} else {
throw newRuntimeError(
"get_local: unsupported index of type: " + index.type
);
}
break;
}
break;
}
case "set_local": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-set-local
const index = instruction.args[0];
const init = instruction.args[1];
/**
* Administrative Instructions
*
* https://webassembly.github.io/spec/core/exec/runtime.html#administrative-instructions
*/
case "unreachable":
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-unreachable
case "trap": {
// signalling abrupt termination
// https://webassembly.github.io/spec/core/exec/runtime.html#syntax-trap
throw createTrap();
}
if (typeof init !== "undefined" && init.type === "Instr") {
// WAST
case "local": {
const [valtype] = instruction.args;
const code = [init];
addEndInstruction(code);
const init = castIntoStackLocalOfType(valtype.name, 0);
frame.locals.push(init);
createAndExecuteChildStackFrame(code, {
passCurrentContext: true
});
break;
}
const res = pop1();
setLocalByIndex(index.value, res);
} else if (index.type === "NumberLiteral") {
// WASM
/**
* Memory Instructions
*
* https://webassembly.github.io/spec/core/exec/instructions.html#memory-instructions
*/
case "get_local": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-get-local
const index = instruction.args[0];
// 4. Pop the value val from the stack
const val = pop1();
if (typeof index === "undefined") {
throw newRuntimeError("get_local requires one argument, none given.");
}
// 5. Replace F.locals[x] with the value val
setLocalByIndex(index.value, val);
} else {
throw newRuntimeError(
"set_local: unsupported index of type: " + index.type
);
}
if (index.type === "NumberLiteral" || index.type === "FloatLiteral") {
getLocalByIndex(index.value);
} else {
throw newRuntimeError(
"get_local: unsupported index of type: " + index.type
);
break;
}
break;
}
case "tee_local": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-tee-local
const index = instruction.args[0];
const init = instruction.args[1];
case "set_local": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-set-local
const index = instruction.args[0];
const init = instruction.args[1];
if (typeof init !== "undefined" && init.type === "Instr") {
// WAST
if (typeof init !== "undefined" && init.type === "Instr") {
// WAST
const code = [init];
addEndInstruction(code);
createAndExecuteChildStackFrame([init], { passCurrentContext: true });
createAndExecuteChildStackFrame(code, {
passCurrentContext: true
});
const res = pop1();
setLocalByIndex(index.value, res);
} else if (index.type === "NumberLiteral") {
// WASM
const res = pop1();
setLocalByIndex(index.value, res);
// 4. Pop the value val from the stack
const val = pop1();
pushResult(res);
} else if (index.type === "NumberLiteral") {
// WASM
// 5. Replace F.locals[x] with the value val
setLocalByIndex(index.value, val);
} else {
throw newRuntimeError(
"set_local: unsupported index of type: " + index.type
);
}
// 1. Assert: due to validation, a value is on the top of the stack.
// 2. Pop the value val from the stack.
const val = pop1();
break;
}
// 3. Push the value valval to the stack.
pushResult(val);
case "tee_local": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-tee-local
const index = instruction.args[0];
const init = instruction.args[1];
// 4. Push the value valval to the stack.
pushResult(val);
if (typeof init !== "undefined" && init.type === "Instr") {
// WAST
// 5. Execute the instruction (set_local x).
// 5. 4. Pop the value val from the stack
const val2 = pop1();
createAndExecuteChildStackFrame([init], { passCurrentContext: true });
// 5. 5. Replace F.locals[x] with the value val
setLocalByIndex(index.value, val2);
} else {
throw newRuntimeError(
"tee_local: unsupported index of type: " + index.type
);
}
const res = pop1();
setLocalByIndex(index.value, res);
break;
}
pushResult(res);
} else if (index.type === "NumberLiteral") {
// WASM
case "set_global": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-set-global
const [index, right] = instruction.args;
// 1. Assert: due to validation, a value is on the top of the stack.
// 2. Pop the value val from the stack.
const val = pop1();
// Interpret right branch first if it's a child instruction
if (typeof right !== "undefined") {
const code = [right];
addEndInstruction(code);
// 3. Push the value valval to the stack.
pushResult(val);
createAndExecuteChildStackFrame(code, {
passCurrentContext: true
});
}
// 4. Push the value valval to the stack.
pushResult(val);
// 2. Assert: due to validation, F.module.globaladdrs[x] exists.
const globaladdr = frame.originatingModule.globaladdrs[index.value];
// 5. Execute the instruction (set_local x).
// 5. 4. Pop the value val from the stack
const val2 = pop1();
if (typeof globaladdr === "undefined") {
throw newRuntimeError(`Global address ${index.value} not found`);
}
// 5. 5. Replace F.locals[x] with the value val
setLocalByIndex(index.value, val2);
} else {
throw newRuntimeError(
"tee_local: unsupported index of type: " + index.type
);
}
// 4. Assert: due to validation, S.globals[a] exists.
const globalinst = frame.allocator.get(globaladdr);
break;
}
if (typeof globalinst !== "object") {
throw newRuntimeError(
`Unexpected data for global at ${globaladdr}`
);
}
case "set_global": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-set-global
const [index, right] = instruction.args;
// 7. Pop the value val from the stack.
const val = pop1();
// Interpret right branch first if it's a child instruction
if (typeof right !== "undefined") {
createAndExecuteChildStackFrame([right], {
passCurrentContext: true
});
}
// 8. Replace glob.value with the value val.
globalinst.value = val.value;
// 2. Assert: due to validation, F.module.globaladdrs[x] exists.
const globaladdr = frame.originatingModule.globaladdrs[index.value];
frame.allocator.set(globaladdr, globalinst);
if (typeof globaladdr === "undefined") {
throw newRuntimeError(`Global address ${index.value} not found`);
break;
}
// 4. Assert: due to validation, S.globals[a] exists.
const globalinst = frame.allocator.get(globaladdr);
case "get_global": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-get-global
const index = instruction.args[0];
if (typeof globalinst !== "object") {
throw newRuntimeError(`Unexpected data for global at ${globaladdr}`);
}
// 2. Assert: due to validation, F.module.globaladdrs[x] exists.
const globaladdr = frame.originatingModule.globaladdrs[index.value];
// 7. Pop the value val from the stack.
const val = pop1();
if (typeof globaladdr === "undefined") {
throw newRuntimeError(`Unknown global at index: ${index.value}`);
}
// 8. Replace glob.value with the value val.
globalinst.value = val.value;
// 4. Assert: due to validation, S.globals[a] exists.
const globalinst = frame.allocator.get(globaladdr);
frame.allocator.set(globaladdr, globalinst);
if (typeof globalinst !== "object") {
throw newRuntimeError(
`Unexpected data for global at ${globaladdr}`
);
}
break;
}
// 7. Pop the value val from the stack.
pushResult(globalinst);
case "get_global": {
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-get-global
const index = instruction.args[0];
break;
}
// 2. Assert: due to validation, F.module.globaladdrs[x] exists.
const globaladdr = frame.originatingModule.globaladdrs[index.value];
case "return": {
const { args } = instruction;
if (typeof globaladdr === "undefined") {
throw newRuntimeError(`Unknown global at index: ${index.value}`);
if (args.length > 0) {
addEndInstruction(args);
createAndExecuteChildStackFrame(args, { passCurrentContext: true });
}
// Abort execution and return the first item on the stack
returnRegister = [pop1()];
return;
}
// 4. Assert: due to validation, S.globals[a] exists.
const globalinst = frame.allocator.get(globaladdr);
/**
* Memory operations
*/
if (typeof globalinst !== "object") {
throw newRuntimeError(`Unexpected data for global at ${globaladdr}`);
}
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-storen
case "store":
case "store8":
case "store16":
case "store32": {
const { id, object, args } = instruction;
// 7. Pop the value val from the stack.
pushResult(globalinst);
// Interpret children first
// only WAST
if (typeof args !== "undefined" && args.length > 0) {
addEndInstruction(args);
createAndExecuteChildStackFrame(args, { passCurrentContext: true });
}
break;
}
const memory = getMemory();
case "return": {
const { args } = instruction;
const [c1, c2] = pop2("i32", object);
const ptr = c1.value.toNumber() + getMemoryOffset(instruction);
let valueBuffer = c2.value.toByteArray();
if (args.length > 0) {
createAndExecuteChildStackFrame(args, { passCurrentContext: true });
}
switch (id) {
case "store8":
valueBuffer = valueBuffer.slice(0, 1);
break;
case "store16":
valueBuffer = valueBuffer.slice(0, 2);
break;
case "store32":
valueBuffer = valueBuffer.slice(0, 4);
break;
}
// Abort execution and return the first item on the stack
return pop1();
}
if (ptr + valueBuffer.length > memory.buffer.byteLength) {
throw newRuntimeError("memory access out of bounds");
}
/**
* Memory operations
*/
const memoryBuffer = new Uint8Array(memory.buffer);
// https://webassembly.github.io/spec/core/exec/instructions.html#exec-storen
case "store":
case "store8":
case "store16":
case "store32": {
const { id, object, args } = instruction;
// Interpret children first
// only WAST
if (typeof args !== "undefined" && args.length > 0) {
createAndExecuteChildStackFrame(args, { passCurrentContext: true });
// load / store use little-endian order
for (let ptrOffset = 0; ptrOffset < valueBuffer.length; ptrOffset++) {
memoryBuffer[ptr + ptrOffset] = valueBuffer[ptrOffset];
}
break;
}
const memory = getMemory();
// https://webassembly.github.io/spec/core/exec/instructions.html#and
case "load":
case "load16_s":
case "load16_u":
case "load8_s":
case "load8_u":
case "load32_s":
case "load32_u": {
const { id, object, args } = instruction;
const [c1, c2] = pop2("i32", object);
const ptr = c1.value.toNumber() + getMemoryOffset(instruction);
let valueBuffer = c2.value.toByteArray();
// Interpret children first
// only WAST
if (typeof args !== "undefined" && args.length > 0) {
addEndInstruction(args);
createAndExecuteChildStackFrame(args, { passCurrentContext: true });
}
switch (id) {
case "store8":
valueBuffer = valueBuffer.slice(0, 1);
break;
case "store16":
valueBuffer = valueBuffer.slice(0, 2);
break;
case "store32":
valueBuffer = valueBuffer.slice(0, 4);
break;
}
const memory = getMemory();
if (ptr + valueBuffer.length > memory.buffer.byteLength) {
throw newRuntimeError("memory access out of bounds");
}
const ptr =
pop1OfType("i32").value.toNumber() + getMemoryOffset(instruction);
const memoryBuffer = new Uint8Array(memory.buffer);
// for i32 / i64 ops, handle extended load
let extend = 0;
// for i64 values, increase the bitshift by 4 bytes
const extendOffset = object === "i32" ? 0 : 32;
let signed = false;
switch (id) {
case "load16_s":
extend = 16 + extendOffset;
signed = true;
break;
case "load16_u":
extend = 16 + extendOffset;
signed = false;
break;
case "load8_s":
extend = 24 + extendOffset;
signed = true;
break;
case "load8_u":
extend = 24 + extendOffset;
signed = false;
break;
case "load32_u":
extend = 0 + extendOffset;
signed = false;
break;
case "load32_s":
extend = 0 + extendOffset;
signed = true;
break;
}
// load / store use little-endian order
for (let ptrOffset = 0; ptrOffset < valueBuffer.length; ptrOffset++) {
memoryBuffer[ptr + ptrOffset] = valueBuffer[ptrOffset];
}
break;
}
// check for memory access out of bounds
switch (object) {
case "i32":
case "f32":
if (ptr + 4 > memory.buffer.byteLength) {
throw newRuntimeError("memory access out of bounds");
}
break;
case "i64":
case "f64":
if (ptr + 8 > memory.buffer.byteLength) {
throw newRuntimeError("memory access out of bounds");
}
break;
}
// https://webassembly.github.io/spec/core/exec/instructions.html#and
case "load":
case "load16_s":
case "load16_u":
case "load8_s":
case "load8_u":
case "load32_s":
case "load32_u": {
const { id, object, args } = instruction;
switch (object) {
case "i32":
pushResult(
i32.createValueFromArrayBuffer(
memory.buffer,
ptr,
extend,
signed
)
);
break;
case "i64":
pushResult(
i64.createValueFromArrayBuffer(
memory.buffer,
ptr,
extend,
signed
)
);
break;
case "f32":
pushResult(f32.createValueFromArrayBuffer(memory.buffer, ptr));
break;
case "f64":
pushResult(f64.createValueFromArrayBuffer(memory.buffer, ptr));
break;
}
// Interpret children first
// only WAST
if (typeof args !== "undefined" && args.length > 0) {
createAndExecuteChildStackFrame(args, { passCurrentContext: true });
break;
}
const memory = getMemory();
/**
* Binary operations
*/
case "add":
case "mul":
case "sub":
/**
* There are two seperated operation for both signed and unsigned integer,
* but since the host environment will handle that, we don't have too :)
*/
case "div_s":
case "div_u":
case "rem_s":
case "rem_u":
case "shl":
case "shr_s":
case "shr_u":
case "rotl":
case "rotr":
case "div":
case "min":
case "max":
case "copysign":
case "or":
case "xor":
case "and":
case "eq":
case "ne":
case "lt_s":
case "lt_u":
case "le_s":
case "le_u":
case "gt":
case "gt_s":
case "gt_u":
case "ge_s":
case "ge_u": {
let binopFn;
switch (instruction.object) {
case "i32":
binopFn = binopi32;
break;
case "i64":
binopFn = binopi64;
break;
case "f32":
binopFn = binopf32;
break;
case "f64":
binopFn = binopf64;
break;
default:
throw createTrap(
"Unsupported operation " +
instruction.id +
" on " +
instruction.object
);
}
const ptr =
pop1OfType("i32").value.toNumber() + getMemoryOffset(instruction);
const [left, right] = instruction.args;
// for i32 / i64 ops, handle extended load
let extend = 0;
// for i64 values, increase the bitshift by 4 bytes
const extendOffset = object === "i32" ? 0 : 32;
let signed = false;
switch (id) {
case "load16_s":
extend = 16 + extendOffset;
signed = true;
break;
case "load16_u":
extend = 16 + extendOffset;
signed = false;
break;
case "load8_s":
extend = 24 + extendOffset;
signed = true;
break;
case "load8_u":
extend = 24 + extendOffset;
signed = false;
break;
case "load32_u":
extend = 0 + extendOffset;
signed = false;
break;
case "load32_s":
extend = 0 + extendOffset;
signed = true;
break;
}
// Interpret left branch first if it's a child instruction
if (typeof left !== "undefined") {
const code = [left];
addEndInstruction(code);
// check for memory access out of bounds
switch (object) {
case "i32":
case "f32":
if (ptr + 4 > memory.buffer.byteLength) {
throw newRuntimeError("memory access out of bounds");
}
break;
case "i64":
case "f64":
if (ptr + 8 > memory.buffer.byteLength) {
throw newRuntimeError("memory access out of bounds");
}
break;
}
createAndExecuteChildStackFrame(code, {
passCurrentContext: true
});
}
switch (object) {
case "i32":
pushResult(
i32.createValueFromArrayBuffer(memory.buffer, ptr, extend, signed)
);
break;
case "i64":
pushResult(
i64.createValueFromArrayBuffer(memory.buffer, ptr, extend, signed)
);
break;
case "f32":
pushResult(f32.createValueFromArrayBuffer(memory.buffer, ptr));
break;
case "f64":
pushResult(f64.createValueFromArrayBuffer(memory.buffer, ptr));
break;
}
// Interpret right branch first if it's a child instruction
if (typeof right !== "undefined") {
const code = [right];
addEndInstruction(code);
break;
}
createAndExecuteChildStackFrame(code, {
passCurrentContext: true
});
}
/**
* Binary operations
*/
case "add":
case "mul":
case "sub":
/**
* There are two seperated operation for both signed and unsigned integer,
* but since the host environment will handle that, we don't have too :)
*/
case "div_s":
case "div_u":
case "rem_s":
case "rem_u":
case "shl":
case "shr_s":
case "shr_u":
case "rotl":
case "rotr":
case "div":
case "min":
case "max":
case "copysign":
case "or":
case "xor":
case "and":
case "eq":
case "ne":
case "lt_s":
case "lt_u":
case "le_s":
case "le_u":
case "gt":
case "gt_s":
case "gt_u":
case "ge_s":
case "ge_u": {
let binopFn;
switch (instruction.object) {
case "i32":
binopFn = binopi32;
break;
case "i64":
binopFn = binopi64;
break;
case "f32":
binopFn = binopf32;
break;
case "f64":
binopFn = binopf64;
break;
default:
throw createTrap(
"Unsupported operation " +
instruction.id +
" on " +
instruction.object
);
const [c1, c2] = pop2(instruction.object, instruction.object);
pushResult(binopFn(c1, c2, instruction.id));
break;
}
const [left, right] = instruction.args;
/**
* Unary operations
*/
case "abs":
case "neg":
case "clz":
case "ctz":
case "popcnt":
case "eqz":
case "reinterpret/f32":
case "reinterpret/f64": {
let unopFn;
// Interpret left branch first if it's a child instruction
if (typeof left !== "undefined") {
createAndExecuteChildStackFrame([left], {
passCurrentContext: true
});
}
// for conversion operations, the operand type appears after the forward-slash
// e.g. with i32.reinterpret/f32, the oprand is f32, and the resultant is i32
const opType =
instruction.id.indexOf("/") !== -1
? instruction.id.split("/")[1]
: instruction.object;
// Interpret right branch first if it's a child instruction
if (typeof right !== "undefined") {
createAndExecuteChildStackFrame([right], {
passCurrentContext: true
});
}
switch (opType) {
case "i32":
unopFn = unopi32;
break;
case "i64":
unopFn = unopi64;
break;
case "f32":
unopFn = unopf32;
break;
case "f64":
unopFn = unopf64;
break;
default:
throw createTrap(
"Unsupported operation " + instruction.id + " on " + opType
);
}
const [c1, c2] = pop2(instruction.object, instruction.object);
pushResult(binopFn(c1, c2, instruction.id));
const [operand] = instruction.args;
break;
}
// Interpret argument first if it's a child instruction
if (typeof operand !== "undefined") {
const code = [operand];
addEndInstruction(code);
/**
* Unary operations
*/
case "abs":
case "neg":
case "clz":
case "ctz":
case "popcnt":
case "eqz":
case "reinterpret/f32":
case "reinterpret/f64": {
let unopFn;
createAndExecuteChildStackFrame(code, {
passCurrentContext: true
});
}
// for conversion operations, the operand type appears after the forward-slash
// e.g. with i32.reinterpret/f32, the oprand is f32, and the resultant is i32
const opType =
instruction.id.indexOf("/") !== -1
? instruction.id.split("/")[1]
: instruction.object;
const c = pop1OfType(opType);
switch (opType) {
case "i32":
unopFn = unopi32;
break;
case "i64":
unopFn = unopi64;
break;
case "f32":
unopFn = unopf32;
break;
case "f64":
unopFn = unopf64;
break;
default:
throw createTrap(
"Unsupported operation " + instruction.id + " on " + opType
);
pushResult(unopFn(c, instruction.id));
break;
}
const [operand] = instruction.args;
case "end": {
// Pop active frame from the stack
stack.pop();
framepointer--;
// Interpret argument first if it's a child instruction
if (typeof operand !== "undefined") {
createAndExecuteChildStackFrame([operand], {
passCurrentContext: true
});
}
// Return the item on top of the values/stack;
if (frame.values.length > 0) {
const res = pop1();
const c = pop1OfType(opType);
if (res.type !== "label") {
returnRegister = [res];
} else {
// Push label back
pushResult(res);
}
}
pushResult(unopFn(c, instruction.id));
break;
return;
}
}
}
if (typeof frame.trace === "function") {
frame.trace(depth, frame._pc, instruction, frame);
}
frame._pc++;
}
// Return the item on top of the values/stack;
if (frame.values.length > 0) {
const res = pop1();
run();
if (res.type !== "label") {
return res;
} else {
// Push label back
pushResult(res);
}
if (returnRegister !== null) {
// FIXME(sven): handle multiple results in hostfunc
return returnRegister[0];
}
}
function assertNItemsOnStack(stack: Array<any>, numberOfItem: number) {
if (stack.length < numberOfItem) {
throw new RuntimeError(
"Assertion error: expected " +
numberOfItem +
" on the stack, found " +
stack.length
);
}
}

@@ -22,3 +22,4 @@ // @flow

// TODO(sven): find a specification reference for that
if (node.init.length > 1 || node.init.length === 0) {
// FIXME(sven): +1 because of the implicit end, change the order of validations
if (node.init.length > 2 || node.init.length === 1) {
throw new CompileError("type mismatch");

@@ -25,0 +26,0 @@ }

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc