Completion of exports

This commit is contained in:
Chad Retz 2017-04-14 15:01:44 -05:00
parent 864eb40561
commit 3d1fc86c20
7 changed files with 40 additions and 19 deletions

View File

@ -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(

View File

@ -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)

View File

@ -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"
}
}

View File

@ -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()

View File

@ -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 ->

View File

@ -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?> {

View File

@ -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"