Code/Wasm/Interpret/WasmFunctionDefinition.cs
using System;
using System.Collections.Generic;
namespace WasmBox.Wasm.Interpret {
/// <summary>
/// Represents a WebAssembly function definition.
/// </summary>
public sealed class WasmFunctionDefinition : FunctionDefinition {
/// <summary>
/// Creates a WebAssembly function definition from the given signature,
/// function body and declaring module.
/// </summary>
/// <param name="signature">The function's signature.</param>
/// <param name="body">The function's body.</param>
/// <param name="module">The declaring module.</param>
public WasmFunctionDefinition(
FunctionType signature,
FunctionBody body,
ModuleInstance module) {
this.Signature = signature;
this.body = body;
this.Module = module;
}
/// <summary>
/// Gets the function's signature.
/// </summary>
/// <returns>The function's signature.</returns>
public FunctionType Signature { get; private set; }
/// <summary>
/// The function's body.
/// </summary>
private FunctionBody body;
/// <summary>
/// Gets the module that owns this function definition.
/// </summary>
/// <returns>The declaring module.</returns>
public ModuleInstance Module { get; private set; }
/// <inheritdoc/>
public override IReadOnlyList<WasmValueType> ParameterTypes => Signature.ParameterTypes;
/// <inheritdoc/>
public override IReadOnlyList<WasmValueType> ReturnTypes => Signature.ReturnTypes;
/// <inheritdoc/>
public override IReadOnlyList<object> Invoke(IReadOnlyList<object> arguments, InterpreterContext outerContext) {
var locals = new List<Variable>();
// Check argument types and create parameter variables.
if (Signature.ParameterTypes.Count != arguments.Count) {
throw new WasmException(
"Function arity mismatch: function has " + Signature.ParameterTypes.Count +
" parameters and is given " + arguments.Count + " arguments.");
}
// Turn each argument into a variable.
for (int i = 0; i < Signature.ParameterTypes.Count; i++) {
locals.Add(Variable.Create( Signature.ParameterTypes[i], true, arguments[i]));
}
// Turn each local into a variable.
foreach (var localEntry in body.Locals) {
for (int i = 0; i < localEntry.LocalCount; i++) {
locals.Add(Variable.CreateDefault(localEntry.LocalType, true));
}
}
// Interpret the function body.
var context = InterpretBody(outerContext?.CallStackDepth ?? 0, locals);
// Check return types.
var retVals = context.ReturnValues;
if (retVals.Count != Signature.ReturnTypes.Count) {
throw new WasmException(
"Return value arity mismatch: function expects " + Signature.ReturnTypes.Count +
" return values but is given " + retVals.Count + " return values.");
}
for (int i = 0; i < retVals.Count; i++) {
if (!Variable.IsInstanceOf( retVals[i], Signature.ReturnTypes[i])) {
throw new WasmException(
"Return type mismatch: function has return type '" +
Signature.ReturnTypes[i].ToString() +
" but is given a return value of type '" +
retVals[i].GetType().Name + "'.");
}
}
return retVals;
}
private InterpreterContext InterpretBody(uint callStackDepth, List<Variable> locals) {
if (Module.Policy.TranslateExceptions) {
try {
return InterpretBodyImpl(callStackDepth, locals);
}
catch (DivideByZeroException ex) {
throw new TrapException(ex.Message, TrapException.SpecMessages.IntegerDivideByZero);
}
catch (OverflowException ex) {
throw new TrapException(ex.Message, TrapException.SpecMessages.IntegerOverflow);
}
}
else {
return InterpretBodyImpl(callStackDepth, locals);
}
}
private InterpreterContext InterpretBodyImpl(uint callStackDepth, List<Variable> locals) {
var context = new InterpreterContext(Module, ReturnTypes, locals, Module.Policy, callStackDepth + 1);
var interpreter = Module.Interpreter;
foreach (var instruction in body.BodyInstructions) {
interpreter.Interpret(instruction, context);
if (context.BreakRequested) {
// Functions can use a break to return. This acts exactly like
// a regular return.
OperatorImpls.Return(context);
break;
}
}
context.Return();
return context;
}
}
}