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) {
|
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
|
// Export all functions as named methods that delegate
|
||||||
ctx.mod.exports.forEach {
|
ctx.mod.exports.forEach {
|
||||||
when (it.kind) {
|
when (it.kind) {
|
||||||
@ -459,7 +464,7 @@ open class AstToAsm {
|
|||||||
|
|
||||||
fun addExportMemory(ctx: ClsContext, export: Node.Export) {
|
fun addExportMemory(ctx: ClsContext, export: Node.Export) {
|
||||||
// Create simple getter for the memory
|
// 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(),
|
val method = MethodNode(Opcodes.ACC_PUBLIC, "get" + export.field.javaIdent.capitalize(),
|
||||||
"()" + ctx.mem.memType.asmDesc, null, null)
|
"()" + ctx.mem.memType.asmDesc, null, null)
|
||||||
method.addInsns(
|
method.addInsns(
|
||||||
@ -472,7 +477,7 @@ open class AstToAsm {
|
|||||||
|
|
||||||
fun addExportTable(ctx: ClsContext, export: Node.Export) {
|
fun addExportTable(ctx: ClsContext, export: Node.Export) {
|
||||||
// Create simple getter for the table
|
// 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(),
|
val method = MethodNode(Opcodes.ACC_PUBLIC, "get" + export.field.javaIdent.capitalize(),
|
||||||
"()" + Array<MethodHandle>::class.ref.asmDesc, null, null)
|
"()" + Array<MethodHandle>::class.ref.asmDesc, null, null)
|
||||||
method.addInsns(
|
method.addInsns(
|
||||||
|
@ -36,7 +36,7 @@ data class ClsContext(
|
|||||||
mod.tables.isNotEmpty() || mod.imports.any { it.kind is Node.Import.Kind.Table }
|
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)
|
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"
|
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"
|
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"
|
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") {
|
) : CompileErr("Start function at $index must take no params and return nothing") {
|
||||||
override val asmErrString get() = "start function"
|
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)) {
|
if (isStackEmptyForBlock(currBlock)) {
|
||||||
// Just fake it if dead
|
// Just fake it if dead
|
||||||
if (currBlock.unreachable) return this to TypeRef.Unknown
|
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)
|
throw CompileErr.StackMismatch(emptyArray(), null)
|
||||||
}
|
}
|
||||||
return copy(stack = stack.dropLast(1)) to stack.last()
|
return copy(stack = stack.dropLast(1)) to stack.last()
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package asmble.compile.jvm
|
package asmble.compile.jvm
|
||||||
|
|
||||||
import asmble.ast.Node
|
import asmble.ast.Node
|
||||||
|
import asmble.io.AstToSExpr
|
||||||
|
import asmble.io.SExprToStr
|
||||||
import asmble.util.Either
|
import asmble.util.Either
|
||||||
import asmble.util.add
|
import asmble.util.add
|
||||||
import org.objectweb.asm.Handle
|
import org.objectweb.asm.Handle
|
||||||
@ -16,6 +18,7 @@ open class FuncBuilder {
|
|||||||
// TODO: validate local size?
|
// TODO: validate local size?
|
||||||
// TODO: initialize non-param locals?
|
// TODO: initialize non-param locals?
|
||||||
ctx.debug { "Building function ${ctx.funcName(index)}" }
|
ctx.debug { "Building function ${ctx.funcName(index)}" }
|
||||||
|
ctx.trace { "Function ast:\n${SExprToStr.fromSExpr(AstToSExpr.fromFunc(f))}" }
|
||||||
var func = Func(
|
var func = Func(
|
||||||
access = Opcodes.ACC_PRIVATE,
|
access = Opcodes.ACC_PRIVATE,
|
||||||
name = ctx.funcName(index),
|
name = ctx.funcName(index),
|
||||||
@ -582,7 +585,7 @@ open class FuncBuilder {
|
|||||||
if (block.endTypes.isNotEmpty() && !block.hasElse)
|
if (block.endTypes.isNotEmpty() && !block.hasElse)
|
||||||
throw CompileErr.IfThenValueWithoutElse()
|
throw CompileErr.IfThenValueWithoutElse()
|
||||||
// If the block was an if/then w/ a stack but the else doesn't match it
|
// 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)
|
throw CompileErr.BlockEndMismatch(block.thenStackOnIf, fn.stack)
|
||||||
}
|
}
|
||||||
// Put the stack where it should be
|
// Put the stack where it should be
|
||||||
@ -1182,7 +1185,7 @@ open class FuncBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun applyCallIndirectInsn(ctx: FuncContext, fn: Func, index: Int): Func {
|
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
|
// For this we do an invokedynamic which calls the bootstrap method. The
|
||||||
// bootstrap method is a synthetic method embedded into this module. The
|
// bootstrap method is a synthetic method embedded into this module. The
|
||||||
// resulting method handle accepts all method params THEN "this" THEN
|
// resulting method handle accepts all method params THEN "this" THEN
|
||||||
@ -1213,7 +1216,9 @@ open class FuncBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun applyReturnInsn(ctx: FuncContext, fn: Func): Func {
|
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 ->
|
popForBlockEscape(ctx, fn, block).let { fn ->
|
||||||
return when (ctx.node.type.ret) {
|
return when (ctx.node.type.ret) {
|
||||||
null ->
|
null ->
|
||||||
|
@ -180,7 +180,11 @@ data class ScriptContext(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun doGet(cmd: Script.Cmd.Action.Get): Pair<Node.Type.Value, Any> {
|
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?> {
|
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:
|
TODO: We are going down in order. One's we have not yet handled:
|
||||||
- br.wast - Not handling tables yet
|
- br_table.wast - Table issues on jumps
|
||||||
- br_table.wast - Not handling tables yet
|
|
||||||
- call_indirect.wast - Not handling tables yet
|
|
||||||
- exports.wast - Not handling tables yet
|
|
||||||
- func.wast - Not handling tables yet
|
- func.wast - Not handling tables yet
|
||||||
- func_ptrs.wast - Not handling tables yet
|
- func_ptrs.wast - Not handling tables yet
|
||||||
- imports.wast - No memory exports yet
|
- imports.wast - No memory exports yet
|
||||||
- left-to-right.wast - Not handling tables yet
|
- left-to-right.wast - Not handling tables yet
|
||||||
- linking.wast - Not handling tables yet
|
- linking.wast - Not handling tables yet
|
||||||
- return.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
|
- typecheck.wast - Not handling tables yet
|
||||||
- unreachable.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.wast",
|
||||||
"block-end-label-mismatch.fail.wast",
|
"block-end-label-mismatch.fail.wast",
|
||||||
"block-end-label-superfluous.wast",
|
"block-end-label-superfluous.wast",
|
||||||
|
"br.wast",
|
||||||
"br_if.wast",
|
"br_if.wast",
|
||||||
"break-drop.wast",
|
"break-drop.wast",
|
||||||
"call.wast",
|
"call.wast",
|
||||||
@ -70,6 +69,7 @@ class SpecTestUnit(val name: String, val wast: String, val expectedOutput: Strin
|
|||||||
"conversions.wast",
|
"conversions.wast",
|
||||||
"custom_section.wast",
|
"custom_section.wast",
|
||||||
"endianness.wast",
|
"endianness.wast",
|
||||||
|
"exports.wast",
|
||||||
"f32.load32.fail.wast",
|
"f32.load32.fail.wast",
|
||||||
"f32.load64.fail.wast",
|
"f32.load64.fail.wast",
|
||||||
"f32.store32.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-0.fail.wast",
|
||||||
"store-align-odd.fail.wast",
|
"store-align-odd.fail.wast",
|
||||||
"store_retval.wast",
|
"store_retval.wast",
|
||||||
// "switch.wast" TODO: we are in trouble here on the "argument switch"
|
|
||||||
"tee_local.wast",
|
"tee_local.wast",
|
||||||
"traps.wast",
|
"traps.wast",
|
||||||
"unreached-invalid.wast",
|
"unreached-invalid.wast",
|
||||||
@ -159,11 +158,12 @@ class SpecTestUnit(val name: String, val wast: String, val expectedOutput: Strin
|
|||||||
"float_exprs" to this::isNanMismatch
|
"float_exprs" to this::isNanMismatch
|
||||||
)
|
)
|
||||||
|
|
||||||
fun isNanMismatch(t: Throwable) =
|
fun isNanMismatch(t: Throwable) = t is ScriptAssertionError && (
|
||||||
(((t as? ScriptAssertionError)?.
|
t.assertion is Script.Cmd.Assertion.ReturnNan ||
|
||||||
assertion as? Script.Cmd.Assertion.Return)?.
|
(t.assertion is Script.Cmd.Assertion.Return && (t.assertion as Script.Cmd.Assertion.Return).let {
|
||||||
action as? Script.Cmd.Action.Invoke)?.
|
(it.action as? Script.Cmd.Action.Invoke)?.string?.contains("nan") ?: false
|
||||||
string?.contains("nan") ?: false
|
})
|
||||||
|
)
|
||||||
|
|
||||||
val unitsPath = "/spec/test/core"
|
val unitsPath = "/spec/test/core"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user