Skip to content

Commit

Permalink
add host function exception and handler that allows host function to …
Browse files Browse the repository at this point in the history
…stop the vm gracefully
  • Loading branch information
CharlieTap committed Dec 15, 2024
1 parent 104302b commit ae25d17
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 1 deletion.
1 change: 1 addition & 0 deletions chasm/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ kotlin {
sourceSets {
commonMain {
dependencies {
api(projects.host)
api(projects.stream)

implementation(projects.ast)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.github.charlietap.chasm.integration

import io.github.charlietap.chasm.embedding.error.ChasmError
import io.github.charlietap.chasm.embedding.fixture.publicFunctionType
import io.github.charlietap.chasm.embedding.fixture.publicImport
import io.github.charlietap.chasm.embedding.fixture.publicStore
import io.github.charlietap.chasm.embedding.function
import io.github.charlietap.chasm.embedding.shapes.ChasmResult
import io.github.charlietap.chasm.embedding.shapes.HostFunction
import io.github.charlietap.chasm.embedding.shapes.ValueType
import io.github.charlietap.chasm.executor.runtime.error.InvocationError
import io.github.charlietap.chasm.fixture.executor.runtime.store
import io.github.charlietap.chasm.host.HostFunctionException
import kotlin.test.Test
import kotlin.test.assertEquals

class HostFunctionExceptionTest {

@Test
fun `can run a host function that throws an exception and return a chasm error`() {

val store = publicStore(store())

val functionType = publicFunctionType(
emptyList(),
listOf(ValueType.Number.I32),
)
val reason = "Fail gracefully"
val exception = HostFunctionException(reason)
val hostFunction: HostFunction = {
throw exception
}
val functionExternal = function(store, functionType, hostFunction)
val functionImport = publicImport(
"env",
"host_function",
functionExternal,
)
val result = testRunner(
fileName = "host_function.wasm",
fileDirectory = FILE_DIR,
functionName = "call_host_function",
store = store,
imports = listOf(
functionImport,
),
)

val expected = ChasmError.ExecutionError(
InvocationError.HostFunctionError(reason).toString(),
)

assertEquals(ChasmResult.Error(expected), result)
}

companion object {
private const val FILE_DIR = "src/commonTest/resources/integration/"
}
}
Binary file not shown.
5 changes: 5 additions & 0 deletions chasm/src/commonTest/resources/integration/host_function.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(module
(import "env" "host_function" (func $host_function (result i32)))
(func $call_host_function (result i32) call $host_function)
(export "call_host_function" (func $call_host_function))
)
1 change: 1 addition & 0 deletions executor/invoker/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ kotlin {
api(libs.result)

implementation(projects.executor.memory)
implementation(projects.host)
implementation(projects.libs.sse2)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import io.github.charlietap.chasm.executor.runtime.value.ExecutionValue
import io.github.charlietap.chasm.executor.runtime.value.NumberValue
import io.github.charlietap.chasm.executor.runtime.value.ReferenceValue
import io.github.charlietap.chasm.executor.runtime.value.VectorValue
import io.github.charlietap.chasm.host.HostFunctionException

internal typealias HostFunctionCall = (ExecutionContext, FunctionInstance.HostFunction) -> Result<Unit, InvocationError>

Expand All @@ -39,7 +40,11 @@ internal fun HostFunctionCall(
store,
frame.instance,
)
val results = function.function.invoke(functionContext, params)
val results = try {
function.function.invoke(functionContext, params)
} catch (e: HostFunctionException) {
Err(InvocationError.HostFunctionError(e.reason)).bind()
}

type.results.types.forEachIndexed { index, valueType ->
val result = results.getOrNull(index)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ sealed interface InvocationError : ModuleTrapError {
val actualValue: ExecutionValue?,
) : InvocationError

@JvmInline
value class HostFunctionError(val error: String) : InvocationError

data object ProgramFinishedInconsistentState : InvocationError

sealed interface Trap : InvocationError {
Expand Down
21 changes: 21 additions & 0 deletions host/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
alias(libs.plugins.kotlin.multiplatform)

alias(libs.plugins.conventions.kmp)
alias(libs.plugins.conventions.linting)
alias(libs.plugins.conventions.publishing)
}

configure<PublishingConventionsExtension> {
name = "host"
description = "host system interface"
}

tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
jvmTarget = JvmTarget.fromTarget(libs.versions.java.bytecode.version.get())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package io.github.charlietap.chasm.host

class HostFunctionException(val reason: String) : Exception()
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ include(":executor:invoker")
include(":executor:memory")
include(":executor:runtime")

include(":host")

include(":libs:sse2")
include(":libs:stack")

Expand Down

0 comments on commit ae25d17

Please sign in to comment.