diff --git a/.gitattributes b/.gitattributes index fa93c6a1..9c0107fc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ * text eol=lf *.bat text eol=crlf *.jar binary +*.wasm binary diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/embedding/Instance.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/embedding/Instance.kt index 66508fe8..e9ff21f2 100644 --- a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/embedding/Instance.kt +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/embedding/Instance.kt @@ -37,7 +37,7 @@ internal fun instance( importMatcher: ImportMatcher, instantiator: ModuleInstantiator, ): ChasmResult { - 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) diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/FunctionImportMatcher.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/FunctionImportMatcher.kt new file mode 100644 index 00000000..88ca0c3a --- /dev/null +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/FunctionImportMatcher.kt @@ -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 diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/FunctionImportMatcherImpl.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/FunctionImportMatcherImpl.kt new file mode 100644 index 00000000..e9e93726 --- /dev/null +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/FunctionImportMatcherImpl.kt @@ -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 = 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 +} diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/GlobalImportMatcher.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/GlobalImportMatcher.kt new file mode 100644 index 00000000..20fa086d --- /dev/null +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/GlobalImportMatcher.kt @@ -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 diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/GlobalImportMatcherImpl.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/GlobalImportMatcherImpl.kt new file mode 100644 index 00000000..a7b5421d --- /dev/null +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/GlobalImportMatcherImpl.kt @@ -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 = binding { + val actualGlobal = store.global(import.address).bind() + val actualGlobalType = actualGlobal.type + + val requiredGlobalType = descriptor.type + + requiredGlobalType == actualGlobalType +} diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportDescriptorMatcher.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportDescriptorMatcher.kt new file mode 100644 index 00000000..4a6ad9e2 --- /dev/null +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportDescriptorMatcher.kt @@ -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 diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportDescriptorMatcherImpl.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportDescriptorMatcherImpl.kt new file mode 100644 index 00000000..a5326c1b --- /dev/null +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportDescriptorMatcherImpl.kt @@ -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 = + 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 = 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 + } +} diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportMatcher.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportMatcher.kt index 83e5ddc7..1506eedf 100644 --- a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportMatcher.kt +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportMatcher.kt @@ -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) -> Result, InstantiationError> +internal typealias ImportMatcher = (Store, Module, List) -> Result, ModuleTrapError> diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportMatcherImpl.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportMatcherImpl.kt index 7224b727..12887444 100644 --- a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportMatcherImpl.kt +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/ImportMatcherImpl.kt @@ -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, -): Result, InstantiationError> { - return module.imports.map { moduleImport -> +): Result, ModuleTrapError> = + ImportMatcherImpl( + store = store, + module = module, + imports = imports, + descriptorMatcher = ::ImportDescriptorMatcherImpl, + ) + +internal fun ImportMatcherImpl( + store: Store, + module: Module, + imports: List, + descriptorMatcher: ImportDescriptorMatcher, +): Result, 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() } } diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/LimitsMatcher.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/LimitsMatcher.kt new file mode 100644 index 00000000..f91359f7 --- /dev/null +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/LimitsMatcher.kt @@ -0,0 +1,5 @@ +package io.github.charlietap.chasm.import + +import io.github.charlietap.chasm.ast.type.Limits + +internal typealias LimitsMatcher = (Limits, Limits) -> Boolean diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/LimitsMatcherImpl.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/LimitsMatcherImpl.kt new file mode 100644 index 00000000..e6ecfd4f --- /dev/null +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/LimitsMatcherImpl.kt @@ -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 + } +} diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/MemoryImportMatcher.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/MemoryImportMatcher.kt new file mode 100644 index 00000000..7471ea32 --- /dev/null +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/MemoryImportMatcher.kt @@ -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 diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/MemoryImportMatcherImpl.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/MemoryImportMatcherImpl.kt new file mode 100644 index 00000000..6395e4e0 --- /dev/null +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/MemoryImportMatcherImpl.kt @@ -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 = + MemoryImportMatcherImpl( + store = store, + descriptor = descriptor, + import = import, + limitsMatcher = ::LimitsMatcherImpl, + ) + +internal fun MemoryImportMatcherImpl( + store: Store, + descriptor: ModuleImport.Descriptor.Memory, + import: ExternalValue.Memory, + limitsMatcher: LimitsMatcher, +): Result = binding { + val actualMemory = store.memory(import.address).bind() + val actualMemoryType = actualMemory.type + + val requiredMemoryType = descriptor.type + + limitsMatcher(actualMemoryType.limits, requiredMemoryType.limits) +} diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/TableImportMatcher.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/TableImportMatcher.kt new file mode 100644 index 00000000..2b5718ee --- /dev/null +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/TableImportMatcher.kt @@ -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 diff --git a/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/TableImportMatcherImpl.kt b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/TableImportMatcherImpl.kt new file mode 100644 index 00000000..d98c15c0 --- /dev/null +++ b/chasm/src/commonMain/kotlin/io/github/charlietap/chasm/import/TableImportMatcherImpl.kt @@ -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 = + TableImportMatcherImpl( + store = store, + descriptor = descriptor, + import = import, + limitsMatcher = ::LimitsMatcherImpl, + ) + +internal fun TableImportMatcherImpl( + store: Store, + descriptor: ModuleImport.Descriptor.Table, + import: ExternalValue.Table, + limitsMatcher: LimitsMatcher, +): Result = 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) +} diff --git a/chasm/src/commonTest/kotlin/io/github/charlietap/chasm/script/command/AssertUnlinkableCommandRunner.kt b/chasm/src/commonTest/kotlin/io/github/charlietap/chasm/script/command/AssertUnlinkableCommandRunner.kt index d4a372ad..c17ba1db 100644 --- a/chasm/src/commonTest/kotlin/io/github/charlietap/chasm/script/command/AssertUnlinkableCommandRunner.kt +++ b/chasm/src/commonTest/kotlin/io/github/charlietap/chasm/script/command/AssertUnlinkableCommandRunner.kt @@ -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 + } } diff --git a/chasm/src/commonTest/kotlin/io/github/charlietap/chasm/script/command/CommandRunner.kt b/chasm/src/commonTest/kotlin/io/github/charlietap/chasm/script/command/CommandRunner.kt index 902eddbd..3afd85cc 100644 --- a/chasm/src/commonTest/kotlin/io/github/charlietap/chasm/script/command/CommandRunner.kt +++ b/chasm/src/commonTest/kotlin/io/github/charlietap/chasm/script/command/CommandRunner.kt @@ -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) } diff --git a/chasm/src/commonTest/resources/script/spectest-host.wasm b/chasm/src/commonTest/resources/script/spectest-host.wasm index 179d627f..e7c22aa9 100644 Binary files a/chasm/src/commonTest/resources/script/spectest-host.wasm and b/chasm/src/commonTest/resources/script/spectest-host.wasm differ diff --git a/chasm/src/commonTest/resources/script/spectest-host.wat b/chasm/src/commonTest/resources/script/spectest-host.wat index 9f649819..af3f3350 100644 --- a/chasm/src/commonTest/resources/script/spectest-host.wat +++ b/chasm/src/commonTest/resources/script/spectest-host.wat @@ -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)