mirror of
https://github.com/fluencelabs/asmble
synced 2025-04-24 22:32:19 +00:00
Completion of exports
This commit is contained in:
parent
864eb40561
commit
3d1fc86c20
@ -373,6 +373,11 @@ open class AstToAsm {
|
||||
}
|
||||
|
||||
fun addExports(ctx: ClsContext) {
|
||||
// Make sure there are no dupes
|
||||
ctx.mod.exports.fold(emptySet<String>()) { prev, exp ->
|
||||
if (prev.contains(exp.field)) throw CompileErr.DuplicateExport(exp.field)
|
||||
prev + exp.field
|
||||
}
|
||||
// Export all functions as named methods that delegate
|
||||
ctx.mod.exports.forEach {
|
||||
when (it.kind) {
|
||||
@ -459,7 +464,7 @@ open class AstToAsm {
|
||||
|
||||
fun addExportMemory(ctx: ClsContext, export: Node.Export) {
|
||||
// Create simple getter for the memory
|
||||
require(export.index == 0) { "Only memory at index 0 supported" }
|
||||
if (export.index != 0) throw CompileErr.UnknownMemory(export.index)
|
||||
val method = MethodNode(Opcodes.ACC_PUBLIC, "get" + export.field.javaIdent.capitalize(),
|
||||
"()" + ctx.mem.memType.asmDesc, null, null)
|
||||
method.addInsns(
|
||||
@ -472,7 +477,7 @@ open class AstToAsm {
|
||||
|
||||
fun addExportTable(ctx: ClsContext, export: Node.Export) {
|
||||
// Create simple getter for the table
|
||||
require(export.index == 0) { "Only table at index 0 supported" }
|
||||
if (export.index != 0) throw CompileErr.UnknownTable(export.index)
|
||||
val method = MethodNode(Opcodes.ACC_PUBLIC, "get" + export.field.javaIdent.capitalize(),
|
||||
"()" + Array<MethodHandle>::class.ref.asmDesc, null, null)
|
||||
method.addInsns(
|
||||
|
@ -36,7 +36,7 @@ data class ClsContext(
|
||||
mod.tables.isNotEmpty() || mod.imports.any { it.kind is Node.Import.Kind.Table }
|
||||
}
|
||||
|
||||
fun assertHasMemory() { if (!hasMemory) throw CompileErr.UnknownMemory() }
|
||||
fun assertHasMemory() { if (!hasMemory) throw CompileErr.UnknownMemory(0) }
|
||||
|
||||
fun typeAtIndex(index: Int) = mod.types.getOrNull(index) ?: throw CompileErr.UnknownType(index)
|
||||
|
||||
|
@ -82,11 +82,11 @@ sealed class CompileErr(message: String, cause: Throwable? = null) : RuntimeExce
|
||||
override val asmErrString get() = "unknown global"
|
||||
}
|
||||
|
||||
class UnknownMemory : CompileErr("No memory present") {
|
||||
class UnknownMemory(val index: Int) : CompileErr("No memory present at index $index") {
|
||||
override val asmErrString get() = "unknown memory"
|
||||
}
|
||||
|
||||
class UnknownTable : CompileErr("No table present") {
|
||||
class UnknownTable(val index: Int) : CompileErr("No table present at index $index") {
|
||||
override val asmErrString get() = "unknown table"
|
||||
}
|
||||
|
||||
@ -127,4 +127,8 @@ sealed class CompileErr(message: String, cause: Throwable? = null) : RuntimeExce
|
||||
) : CompileErr("Start function at $index must take no params and return nothing") {
|
||||
override val asmErrString get() = "start function"
|
||||
}
|
||||
|
||||
class DuplicateExport(val name: String) : CompileErr("Duplicate export '$name'") {
|
||||
override val asmErrString get() = "duplicate export name"
|
||||
}
|
||||
}
|
@ -65,6 +65,9 @@ data class Func(
|
||||
if (isStackEmptyForBlock(currBlock)) {
|
||||
// Just fake it if dead
|
||||
if (currBlock.unreachable) return this to TypeRef.Unknown
|
||||
if (currBlock.insn is Node.Instr.If && !currBlock.hasElse && currBlock.unreachableInIf)
|
||||
return this to TypeRef.Unknown
|
||||
if (currBlock.hasElse && currBlock.unreachableInElse) return this to TypeRef.Unknown
|
||||
throw CompileErr.StackMismatch(emptyArray(), null)
|
||||
}
|
||||
return copy(stack = stack.dropLast(1)) to stack.last()
|
||||
|
@ -1,6 +1,8 @@
|
||||
package asmble.compile.jvm
|
||||
|
||||
import asmble.ast.Node
|
||||
import asmble.io.AstToSExpr
|
||||
import asmble.io.SExprToStr
|
||||
import asmble.util.Either
|
||||
import asmble.util.add
|
||||
import org.objectweb.asm.Handle
|
||||
@ -16,6 +18,7 @@ open class FuncBuilder {
|
||||
// TODO: validate local size?
|
||||
// TODO: initialize non-param locals?
|
||||
ctx.debug { "Building function ${ctx.funcName(index)}" }
|
||||
ctx.trace { "Function ast:\n${SExprToStr.fromSExpr(AstToSExpr.fromFunc(f))}" }
|
||||
var func = Func(
|
||||
access = Opcodes.ACC_PRIVATE,
|
||||
name = ctx.funcName(index),
|
||||
@ -582,7 +585,7 @@ open class FuncBuilder {
|
||||
if (block.endTypes.isNotEmpty() && !block.hasElse)
|
||||
throw CompileErr.IfThenValueWithoutElse()
|
||||
// If the block was an if/then w/ a stack but the else doesn't match it
|
||||
if (block.hasElse && block.thenStackOnIf != fn.stack)
|
||||
if (block.hasElse && !block.unreachableInIf && !block.unreachableInElse && block.thenStackOnIf != fn.stack)
|
||||
throw CompileErr.BlockEndMismatch(block.thenStackOnIf, fn.stack)
|
||||
}
|
||||
// Put the stack where it should be
|
||||
@ -1182,7 +1185,7 @@ open class FuncBuilder {
|
||||
}
|
||||
|
||||
fun applyCallIndirectInsn(ctx: FuncContext, fn: Func, index: Int): Func {
|
||||
if (!ctx.cls.hasTable) throw CompileErr.UnknownTable()
|
||||
if (!ctx.cls.hasTable) throw CompileErr.UnknownTable(0)
|
||||
// For this we do an invokedynamic which calls the bootstrap method. The
|
||||
// bootstrap method is a synthetic method embedded into this module. The
|
||||
// resulting method handle accepts all method params THEN "this" THEN
|
||||
@ -1213,7 +1216,9 @@ open class FuncBuilder {
|
||||
}
|
||||
|
||||
fun applyReturnInsn(ctx: FuncContext, fn: Func): Func {
|
||||
val block = fn.blockStack.first()
|
||||
// If the current stakc is unreachable, we consider that our block since it
|
||||
// will pop properly.
|
||||
val block = if (fn.currentBlock.unreachable) fn.currentBlock else fn.blockStack.first()
|
||||
popForBlockEscape(ctx, fn, block).let { fn ->
|
||||
return when (ctx.node.type.ret) {
|
||||
null ->
|
||||
|
@ -180,7 +180,11 @@ data class ScriptContext(
|
||||
}
|
||||
|
||||
fun doGet(cmd: Script.Cmd.Action.Get): Pair<Node.Type.Value, Any> {
|
||||
TODO()
|
||||
// Grab last module or named one
|
||||
val module = if (cmd.name == null) modules.last() else modules.first { it.name == cmd.name }
|
||||
// Just call the getter
|
||||
val getter = module.cls.getDeclaredMethod("get" + cmd.string.javaIdent.capitalize())
|
||||
return getter.returnType.valueType!! to getter.invoke(module.instance)
|
||||
}
|
||||
|
||||
fun doInvoke(cmd: Script.Cmd.Action.Invoke): Pair<Node.Type.Value?, Any?> {
|
||||
|
@ -41,16 +41,14 @@ class SpecTestUnit(val name: String, val wast: String, val expectedOutput: Strin
|
||||
|
||||
/*
|
||||
TODO: We are going down in order. One's we have not yet handled:
|
||||
- br.wast - Not handling tables yet
|
||||
- br_table.wast - Not handling tables yet
|
||||
- call_indirect.wast - Not handling tables yet
|
||||
- exports.wast - Not handling tables yet
|
||||
- br_table.wast - Table issues on jumps
|
||||
- func.wast - Not handling tables yet
|
||||
- func_ptrs.wast - Not handling tables yet
|
||||
- imports.wast - No memory exports yet
|
||||
- left-to-right.wast - Not handling tables yet
|
||||
- linking.wast - Not handling tables yet
|
||||
- return.wast - Not handling tables yet
|
||||
- switch.wast - Table issues on jumps (ref "argument switch")
|
||||
- typecheck.wast - Not handling tables yet
|
||||
- unreachable.wast - Not handling tables yet
|
||||
*/
|
||||
@ -62,6 +60,7 @@ class SpecTestUnit(val name: String, val wast: String, val expectedOutput: Strin
|
||||
"block.wast",
|
||||
"block-end-label-mismatch.fail.wast",
|
||||
"block-end-label-superfluous.wast",
|
||||
"br.wast",
|
||||
"br_if.wast",
|
||||
"break-drop.wast",
|
||||
"call.wast",
|
||||
@ -70,6 +69,7 @@ class SpecTestUnit(val name: String, val wast: String, val expectedOutput: Strin
|
||||
"conversions.wast",
|
||||
"custom_section.wast",
|
||||
"endianness.wast",
|
||||
"exports.wast",
|
||||
"f32.load32.fail.wast",
|
||||
"f32.load64.fail.wast",
|
||||
"f32.store32.fail.wast",
|
||||
@ -146,7 +146,6 @@ class SpecTestUnit(val name: String, val wast: String, val expectedOutput: Strin
|
||||
"store-align-0.fail.wast",
|
||||
"store-align-odd.fail.wast",
|
||||
"store_retval.wast",
|
||||
// "switch.wast" TODO: we are in trouble here on the "argument switch"
|
||||
"tee_local.wast",
|
||||
"traps.wast",
|
||||
"unreached-invalid.wast",
|
||||
@ -159,11 +158,12 @@ class SpecTestUnit(val name: String, val wast: String, val expectedOutput: Strin
|
||||
"float_exprs" to this::isNanMismatch
|
||||
)
|
||||
|
||||
fun isNanMismatch(t: Throwable) =
|
||||
(((t as? ScriptAssertionError)?.
|
||||
assertion as? Script.Cmd.Assertion.Return)?.
|
||||
action as? Script.Cmd.Action.Invoke)?.
|
||||
string?.contains("nan") ?: false
|
||||
fun isNanMismatch(t: Throwable) = t is ScriptAssertionError && (
|
||||
t.assertion is Script.Cmd.Assertion.ReturnNan ||
|
||||
(t.assertion is Script.Cmd.Assertion.Return && (t.assertion as Script.Cmd.Assertion.Return).let {
|
||||
(it.action as? Script.Cmd.Action.Invoke)?.string?.contains("nan") ?: false
|
||||
})
|
||||
)
|
||||
|
||||
val unitsPath = "/spec/test/core"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user