Skip to content

Commit

Permalink
Wasm testsuite linking (#17)
Browse files Browse the repository at this point in the history
* enable linking testsuite tests and tighten typing on linked imports
  • Loading branch information
CharlieTap committed Jun 11, 2024
1 parent a4275d4 commit addbd22
Show file tree
Hide file tree
Showing 20 changed files with 292 additions and 30 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
* text eol=lf
*.bat text eol=crlf
*.jar binary
*.wasm binary
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ internal fun instance(
importMatcher: ImportMatcher,
instantiator: ModuleInstantiator,
): ChasmResult<ModuleInstance, ChasmError.ExecutionError> {
val orderedImports = importMatcher(module, imports).component1()
val orderedImports = importMatcher(store, module, imports).component1()
?: return Error(ChasmError.ExecutionError(InstantiationError.MissingImport))
return instantiator(store, module, orderedImports)
.mapError(ChasmError::ExecutionError)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.github.charlietap.chasm.import

import com.github.michaelbull.result.Result
import io.github.charlietap.chasm.ast.module.Import
import io.github.charlietap.chasm.ast.module.Module
import io.github.charlietap.chasm.executor.runtime.error.ModuleTrapError
import io.github.charlietap.chasm.executor.runtime.instance.ExternalValue
import io.github.charlietap.chasm.executor.runtime.store.Store

internal typealias FunctionImportMatcher = (Store, Module, Import.Descriptor.Function, ExternalValue.Function) -> Result<Boolean, ModuleTrapError>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.github.charlietap.chasm.import

import com.github.michaelbull.result.Result
import com.github.michaelbull.result.binding
import io.github.charlietap.chasm.ast.module.Module
import io.github.charlietap.chasm.executor.runtime.error.ModuleTrapError
import io.github.charlietap.chasm.executor.runtime.ext.function
import io.github.charlietap.chasm.executor.runtime.instance.ExternalValue
import io.github.charlietap.chasm.executor.runtime.store.Store
import io.github.charlietap.chasm.executor.type.ext.definedType
import io.github.charlietap.chasm.executor.type.ext.functionType
import io.github.charlietap.chasm.ast.module.Import as ModuleImport

internal fun FunctionImportMatcherImpl(
store: Store,
module: Module,
descriptor: ModuleImport.Descriptor.Function,
import: ExternalValue.Function,
): Result<Boolean, ModuleTrapError> = binding {
val actualFunction = store.function(import.address).bind()
val actualFunctionType = actualFunction.functionType()

val requiredImportType = module.types[descriptor.typeIndex.idx.toInt()]
val requiredFunctionType = requiredImportType.recursiveType.definedType().functionType()

requiredFunctionType == actualFunctionType
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.charlietap.chasm.import

import com.github.michaelbull.result.Result
import io.github.charlietap.chasm.ast.module.Import
import io.github.charlietap.chasm.executor.runtime.error.ModuleTrapError
import io.github.charlietap.chasm.executor.runtime.instance.ExternalValue
import io.github.charlietap.chasm.executor.runtime.store.Store

internal typealias GlobalImportMatcher = (Store, Import.Descriptor.Global, ExternalValue.Global) -> Result<Boolean, ModuleTrapError>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.github.charlietap.chasm.import

import com.github.michaelbull.result.Result
import com.github.michaelbull.result.binding
import io.github.charlietap.chasm.executor.runtime.error.ModuleTrapError
import io.github.charlietap.chasm.executor.runtime.ext.global
import io.github.charlietap.chasm.executor.runtime.instance.ExternalValue
import io.github.charlietap.chasm.executor.runtime.store.Store
import io.github.charlietap.chasm.ast.module.Import as ModuleImport

internal fun GlobalImportMatcherImpl(
store: Store,
descriptor: ModuleImport.Descriptor.Global,
import: ExternalValue.Global,
): Result<Boolean, ModuleTrapError> = binding {
val actualGlobal = store.global(import.address).bind()
val actualGlobalType = actualGlobal.type

val requiredGlobalType = descriptor.type

requiredGlobalType == actualGlobalType
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.github.charlietap.chasm.import

import com.github.michaelbull.result.Result
import io.github.charlietap.chasm.ast.module.Module
import io.github.charlietap.chasm.executor.runtime.error.ModuleTrapError
import io.github.charlietap.chasm.executor.runtime.instance.ExternalValue
import io.github.charlietap.chasm.executor.runtime.store.Store
import io.github.charlietap.chasm.ast.module.Import as ModuleImport

internal typealias ImportDescriptorMatcher = (Store, Module, ModuleImport.Descriptor, ExternalValue) -> Result<Boolean, ModuleTrapError>
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package io.github.charlietap.chasm.import

import com.github.michaelbull.result.Result
import com.github.michaelbull.result.binding
import io.github.charlietap.chasm.ast.module.Module
import io.github.charlietap.chasm.executor.runtime.error.ModuleTrapError
import io.github.charlietap.chasm.executor.runtime.instance.ExternalValue
import io.github.charlietap.chasm.executor.runtime.store.Store
import io.github.charlietap.chasm.ast.module.Import as ModuleImport

internal fun ImportDescriptorMatcherImpl(
store: Store,
module: Module,
descriptor: ModuleImport.Descriptor,
externalValue: ExternalValue,
): Result<Boolean, ModuleTrapError> =
ImportDescriptorMatcherImpl(
store = store,
module = module,
descriptor = descriptor,
externalValue = externalValue,
functionImportMatcher = ::FunctionImportMatcherImpl,
tableImportMatcher = ::TableImportMatcherImpl,
memoryImportMatcher = ::MemoryImportMatcherImpl,
globalImportMatcher = ::GlobalImportMatcherImpl,
)

internal fun ImportDescriptorMatcherImpl(
store: Store,
module: Module,
descriptor: ModuleImport.Descriptor,
externalValue: ExternalValue,
functionImportMatcher: FunctionImportMatcher,
tableImportMatcher: TableImportMatcher,
memoryImportMatcher: MemoryImportMatcher,
globalImportMatcher: GlobalImportMatcher,
): Result<Boolean, ModuleTrapError> = binding {
when {
descriptor is ModuleImport.Descriptor.Function && externalValue is ExternalValue.Function -> {
functionImportMatcher(store, module, descriptor, externalValue).bind()
}
descriptor is ModuleImport.Descriptor.Table && externalValue is ExternalValue.Table -> {
tableImportMatcher(store, descriptor, externalValue).bind()
}
descriptor is ModuleImport.Descriptor.Memory && externalValue is ExternalValue.Memory -> {
memoryImportMatcher(store, descriptor, externalValue).bind()
}
descriptor is ModuleImport.Descriptor.Global && externalValue is ExternalValue.Global -> {
globalImportMatcher(store, descriptor, externalValue).bind()
}
else -> false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package io.github.charlietap.chasm.import

import com.github.michaelbull.result.Result
import io.github.charlietap.chasm.ast.module.Module
import io.github.charlietap.chasm.executor.runtime.error.InstantiationError
import io.github.charlietap.chasm.executor.runtime.error.ModuleTrapError
import io.github.charlietap.chasm.executor.runtime.instance.ExternalValue
import io.github.charlietap.chasm.executor.runtime.store.Store

internal typealias ImportMatcher = (Module, List<Import>) -> Result<List<ExternalValue>, InstantiationError>
internal typealias ImportMatcher = (Store, Module, List<Import>) -> Result<List<ExternalValue>, ModuleTrapError>
Original file line number Diff line number Diff line change
@@ -1,34 +1,37 @@
package io.github.charlietap.chasm.import

import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.binding
import io.github.charlietap.chasm.ast.module.Module
import io.github.charlietap.chasm.executor.runtime.error.InstantiationError
import io.github.charlietap.chasm.executor.runtime.error.ModuleTrapError
import io.github.charlietap.chasm.executor.runtime.instance.ExternalValue
import io.github.charlietap.chasm.ast.module.Import as ModuleImport
import io.github.charlietap.chasm.executor.runtime.store.Store

internal fun ImportMatcherImpl(
store: Store,
module: Module,
imports: List<Import>,
): Result<List<ExternalValue>, InstantiationError> {
return module.imports.map { moduleImport ->
): Result<List<ExternalValue>, ModuleTrapError> =
ImportMatcherImpl(
store = store,
module = module,
imports = imports,
descriptorMatcher = ::ImportDescriptorMatcherImpl,
)

internal fun ImportMatcherImpl(
store: Store,
module: Module,
imports: List<Import>,
descriptorMatcher: ImportDescriptorMatcher,
): Result<List<ExternalValue>, ModuleTrapError> = binding {
module.imports.map { moduleImport ->
imports.firstOrNull { import ->
moduleImport.moduleName.name == import.moduleName &&
moduleImport.entityName.name == import.entityName &&
descriptorMatcher(moduleImport.descriptor, import.value)
}?.value ?: return Err(InstantiationError.MissingImport)
}.let(::Ok)
}

private fun descriptorMatcher(
descriptor: ModuleImport.Descriptor,
externalValue: ExternalValue,
): Boolean {
return when (descriptor) {
is ModuleImport.Descriptor.Function -> externalValue is ExternalValue.Function
is ModuleImport.Descriptor.Table -> externalValue is ExternalValue.Table
is ModuleImport.Descriptor.Memory -> externalValue is ExternalValue.Memory
is ModuleImport.Descriptor.Global -> externalValue is ExternalValue.Global
descriptorMatcher(store, module, moduleImport.descriptor, import.value).bind()
}?.value ?: Err(InstantiationError.MissingImport).bind()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.github.charlietap.chasm.import

import io.github.charlietap.chasm.ast.type.Limits

internal typealias LimitsMatcher = (Limits, Limits) -> Boolean
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.github.charlietap.chasm.import

import io.github.charlietap.chasm.ast.type.Limits

internal fun LimitsMatcherImpl(
first: Limits,
second: Limits,
): Boolean {
return if (first.min >= second.min) {
if (second.max == null) {
true
} else {
first.max != null && first.max!! <= second.max!!
}
} else {
false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.charlietap.chasm.import

import com.github.michaelbull.result.Result
import io.github.charlietap.chasm.ast.module.Import
import io.github.charlietap.chasm.executor.runtime.error.ModuleTrapError
import io.github.charlietap.chasm.executor.runtime.instance.ExternalValue
import io.github.charlietap.chasm.executor.runtime.store.Store

internal typealias MemoryImportMatcher = (Store, Import.Descriptor.Memory, ExternalValue.Memory) -> Result<Boolean, ModuleTrapError>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.github.charlietap.chasm.import

import com.github.michaelbull.result.Result
import com.github.michaelbull.result.binding
import io.github.charlietap.chasm.executor.runtime.error.ModuleTrapError
import io.github.charlietap.chasm.executor.runtime.ext.memory
import io.github.charlietap.chasm.executor.runtime.instance.ExternalValue
import io.github.charlietap.chasm.executor.runtime.store.Store
import io.github.charlietap.chasm.ast.module.Import as ModuleImport

internal fun MemoryImportMatcherImpl(
store: Store,
descriptor: ModuleImport.Descriptor.Memory,
import: ExternalValue.Memory,
): Result<Boolean, ModuleTrapError> =
MemoryImportMatcherImpl(
store = store,
descriptor = descriptor,
import = import,
limitsMatcher = ::LimitsMatcherImpl,
)

internal fun MemoryImportMatcherImpl(
store: Store,
descriptor: ModuleImport.Descriptor.Memory,
import: ExternalValue.Memory,
limitsMatcher: LimitsMatcher,
): Result<Boolean, ModuleTrapError> = binding {
val actualMemory = store.memory(import.address).bind()
val actualMemoryType = actualMemory.type

val requiredMemoryType = descriptor.type

limitsMatcher(actualMemoryType.limits, requiredMemoryType.limits)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.charlietap.chasm.import

import com.github.michaelbull.result.Result
import io.github.charlietap.chasm.ast.module.Import
import io.github.charlietap.chasm.executor.runtime.error.ModuleTrapError
import io.github.charlietap.chasm.executor.runtime.instance.ExternalValue
import io.github.charlietap.chasm.executor.runtime.store.Store

internal typealias TableImportMatcher = (Store, Import.Descriptor.Table, ExternalValue.Table) -> Result<Boolean, ModuleTrapError>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.github.charlietap.chasm.import

import com.github.michaelbull.result.Result
import com.github.michaelbull.result.binding
import io.github.charlietap.chasm.executor.runtime.error.ModuleTrapError
import io.github.charlietap.chasm.executor.runtime.ext.table
import io.github.charlietap.chasm.executor.runtime.instance.ExternalValue
import io.github.charlietap.chasm.executor.runtime.store.Store
import io.github.charlietap.chasm.ast.module.Import as ModuleImport

internal fun TableImportMatcherImpl(
store: Store,
descriptor: ModuleImport.Descriptor.Table,
import: ExternalValue.Table,
): Result<Boolean, ModuleTrapError> =
TableImportMatcherImpl(
store = store,
descriptor = descriptor,
import = import,
limitsMatcher = ::LimitsMatcherImpl,
)

internal fun TableImportMatcherImpl(
store: Store,
descriptor: ModuleImport.Descriptor.Table,
import: ExternalValue.Table,
limitsMatcher: LimitsMatcher,
): Result<Boolean, ModuleTrapError> = binding {
val actualTable = store.table(import.address).bind()
val actualTableType = actualTable.type

val requiredTableType = descriptor.type

requiredTableType.referenceType == actualTableType.referenceType &&
limitsMatcher(actualTableType.limits, requiredTableType.limits)
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
package io.github.charlietap.chasm.script.command

import io.github.charlietap.chasm.embedding.instance
import io.github.charlietap.chasm.embedding.module
import io.github.charlietap.chasm.flatMap
import io.github.charlietap.chasm.fold
import io.github.charlietap.chasm.script.ScriptContext
import io.github.charlietap.chasm.script.ext.readBytesFromPath
import io.github.charlietap.sweet.lib.command.AssertUnlinkableCommand

typealias AssertUnlinkableCommandRunner = (AssertUnlinkableCommand) -> CommandResult
typealias AssertUnlinkableCommandRunner = (ScriptContext, AssertUnlinkableCommand) -> CommandResult

@Suppress("UNUSED_PARAMETER")
fun AssertUnlinkableCommandRunner(
context: ScriptContext,
command: AssertUnlinkableCommand,
): CommandResult {
println("ignoring AssertUnlinkableCommand")
return CommandResult.Success
val moduleFilePath = context.binaryDirectory + "/" + command.filename
val bytes = moduleFilePath.readBytesFromPath()

return module(bytes).flatMap { module ->
instance(context.store, module, context.imports)
}.fold({ _ ->
CommandResult.Failure(command, "unlinkable module was instantiated when it should have failed")
}) {
CommandResult.Success
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ private fun CommandRunner(
is AssertReturnCommand -> assertReturnCommandRunner(context, command)
is AssertTrapCommand -> assertTrapCommandRunner(context, command)
is AssertUninstantiableCommand -> assertUninstantiableCommandRunner(context, command)
is AssertUnlinkableCommand -> assertUnlinkableCommandRunner(command)
is AssertUnlinkableCommand -> assertUnlinkableCommandRunner(context, command)
is ModuleCommand -> moduleCommandRunner(context, command)
is RegisterCommand -> registerCommandRunner(context, command)
}
Binary file modified chasm/src/commonTest/resources/script/spectest-host.wasm
Binary file not shown.
8 changes: 4 additions & 4 deletions chasm/src/commonTest/resources/script/spectest-host.wat
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
(module
(global $global_i32 (export "global_i32") (mut i32) (i32.const 666))
(global $global_i64 (export "global_i64") (mut i64) (i64.const 666))
(global $global_f32 (export "global_f32") (mut f32) (f32.const 666.6))
(global $global_f64 (export "global_f64") (mut f64) (f64.const 666.6))
(global $global_i32 (export "global_i32") i32 (i32.const 666))
(global $global_i64 (export "global_i64") i64 (i64.const 666))
(global $global_f32 (export "global_f32") f32 (f32.const 666.6))
(global $global_f64 (export "global_f64") f64 (f64.const 666.6))

(table (export "table") 10 20 funcref)

Expand Down

0 comments on commit addbd22

Please sign in to comment.