canto/Assets/Scripts/Scripting/Runtime.cs
2025-08-14 19:11:32 -04:00

144 lines
4.4 KiB
C#

using System;
using System.Collections.Generic;
using KitsuneCafe.Sys;
using UnityEngine;
using Wacs.Core.Runtime;
using Wacs.Core.Runtime.Types;
namespace KitsuneCafe.Scripting
{
public class RuntimeInstance
{
public const string DefaultStartFunction = "main";
public readonly WasmRuntime Runtime;
public readonly Runtime Asset;
public readonly IDictionary<BindingPath, IFunctionState> State;
public RuntimeInstance(WasmRuntime runtime, Runtime asset, IDictionary<BindingPath, IFunctionState> state)
{
Runtime = runtime;
Asset = asset;
State = state;
}
public IResult<Unit, Exception> Invoke(FuncAddr addr, InvokerOptions options = null)
{
var caller = Runtime.CreateInvokerAction(addr, options);
try
{
caller();
return Result.Ok<Unit, Exception>(default);
}
catch (Exception e)
{
return Result.Err<Unit, Exception>(e);
}
}
public IResult<T, Exception> Invoke<T>(FuncAddr addr, InvokerOptions options = null)
{
var caller = Runtime.CreateInvokerFunc<T>(addr, options);
try
{
T value = caller();
return Result.Ok<T, Exception>(default);
}
catch (Exception e)
{
return Result.Err<T, Exception>(e);
}
}
public IResult<Unit, Exception> Invoke(BindingPath path, InvokerOptions options = null)
{
if (Runtime.TryGetExportedFunction(path, out var addr))
{
return Invoke(addr, options);
}
return Result.Err<Unit, Exception>(new ArgumentException($"No such function \"{path}\""));
}
public IResult<T, Exception> Invoke<T>(BindingPath path, InvokerOptions options = null)
{
if (Runtime.TryGetExportedFunction(path, out var addr))
{
return Invoke<T>(addr, options);
}
return Result.Err<T, Exception>(new ArgumentException($"No such function \"{path}\""));
}
public IResult<Unit, Exception> Start(ModuleInstance module, InvokerOptions options = null)
{
return Invoke(module.StartFunc, options);
}
public IResult<Unit, Exception> Start(string moduleName, InvokerOptions options = null)
{
return Start(Runtime.GetModule(moduleName), options);
}
public IResult<Unit, Exception> Start(Module module, InvokerOptions options = null)
{
return Start(module.Name, options);
}
public IResult<Unit, Exception> Start(InvokerOptions options = null)
{
if (Asset.Modules.Count != 1)
{
return Result.Err<Unit, Exception>(new ArgumentException("Runtime has more than one module registered. Specify the module to start."));
}
return Start(Asset.Modules[0], options);
}
}
[CreateAssetMenu(menuName = KitsuneCafeMenu.Module + "Runtime")]
public class Runtime : ScriptableObject
{
[SerializeField]
private RuntimeAttributes attributes = new();
public RuntimeAttributes Attributes => attributes;
[SerializeField]
private List<Environment> environments = new();
public List<Environment> Environments => environments;
[SerializeField]
private List<Module> modules = new();
public List<Module> Modules => modules;
public RuntimeInstance Instantiate()
{
var runtime = new WasmRuntime(attributes);
var state = BindEnvironments(runtime);
RegisterModules(runtime);
return new RuntimeInstance(runtime, this, state);
}
private RuntimeState BindEnvironments(WasmRuntime runtime)
{
var state = new RuntimeState();
foreach (var env in environments)
{
env.Bind(runtime, state);
}
return state;
}
private void RegisterModules(WasmRuntime runtime)
{
foreach (var module in modules)
{
var instance = module.Instantiate(runtime);
runtime.RegisterModule(module.Name, instance);
}
}
}
}