Skip to content

Commit

Permalink
Support large data sections. Fixes #18
Browse files Browse the repository at this point in the history
  • Loading branch information
cretz committed Jul 26, 2018
1 parent 6786350 commit a66c05a
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 10 deletions.
4 changes: 4 additions & 0 deletions compiler/src/main/kotlin/asmble/compile/jvm/AsmExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,7 @@ fun ByteArray.asClassNode(): ClassNode {
ClassReader(this).accept(newNode, 0)
return newNode
}

fun ByteArray.chunked(v: Int) = (0 until size step v).asSequence().map {
copyOfRange(it, (it + v).takeIf { it < size } ?: size)
}
27 changes: 17 additions & 10 deletions compiler/src/main/kotlin/asmble/compile/jvm/ByteBufferMem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,23 @@ open class ByteBufferMem(val direct: Boolean = true) : Mem {
let(buildOffset).popExpecting(Int::class.ref).
addInsns(
forceFnType<ByteBuffer.(Int) -> Buffer>(ByteBuffer::position).invokeVirtual(),
TypeInsnNode(Opcodes.CHECKCAST, memType.asmName),
// We're going to do this as an LDC string in ISO-8859 and read it back at runtime
LdcInsnNode(bytes.toString(Charsets.ISO_8859_1)),
LdcInsnNode("ISO-8859-1"),
// Ug, can't do func refs on native types here...
MethodInsnNode(Opcodes.INVOKEVIRTUAL, String::class.ref.asmName,
"getBytes", "(Ljava/lang/String;)[B", false),
0.const,
bytes.size.const,
forceFnType<ByteBuffer.(ByteArray, Int, Int) -> ByteBuffer>(ByteBuffer::put).invokeVirtual(),
TypeInsnNode(Opcodes.CHECKCAST, memType.asmName)
).addInsns(
// We're going to do this as an LDC string in ISO-8859 and read it back at runtime. However,
// due to JVM limits, we can't have a string > 65536 chars, so I'll chunk it every 65500 chars.
bytes.chunked(65500).flatMap { bytes ->
sequenceOf(
LdcInsnNode(bytes.toString(Charsets.ISO_8859_1)),
LdcInsnNode("ISO-8859-1"),
// Ug, can't do func refs on native types here...
MethodInsnNode(Opcodes.INVOKEVIRTUAL, String::class.ref.asmName,
"getBytes", "(Ljava/lang/String;)[B", false),
0.const,
bytes.size.const,
forceFnType<ByteBuffer.(ByteArray, Int, Int) -> ByteBuffer>(ByteBuffer::put).invokeVirtual()
)
}.toList()
).addInsns(
InsnNode(Opcodes.POP)
)

Expand Down
43 changes: 43 additions & 0 deletions compiler/src/test/kotlin/asmble/compile/jvm/LargeDataTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package asmble.compile.jvm

import asmble.TestBase
import asmble.ast.Node
import asmble.run.jvm.ScriptContext
import asmble.util.get
import org.junit.Test
import java.nio.ByteBuffer
import java.util.*
import kotlin.test.assertEquals

class LargeDataTest : TestBase() {
@Test
fun testLargeData() {
// This previously failed because string constants can't be longer than 65536 chars
val mod = Node.Module(
memories = listOf(Node.Type.Memory(
limits = Node.ResizableLimits(initial = 2, maximum = 2)
)),
data = listOf(Node.Data(
index = 0,
offset = listOf(Node.Instr.I32Const(0)),
data = ByteArray(70000) { 'a'.toByte() }
))
)
val ctx = ClsContext(
packageName = "test",
className = "Temp" + UUID.randomUUID().toString().replace("-", ""),
mod = mod,
logger = logger
)
AstToAsm.fromModule(ctx)
val cls = ScriptContext.SimpleClassLoader(javaClass.classLoader, logger).fromBuiltContext(ctx)
// Instantiate it, get the memory out, and check it
val field = cls.getDeclaredField("memory").apply { isAccessible = true }
val buf = field[cls.newInstance()] as ByteBuffer
// Grab all + 1 and check values
val bytes = ByteArray(70001).also { buf.get(0, it) }
bytes.forEachIndexed { index, byte ->
assertEquals(if (index == 70000) 0.toByte() else 'a'.toByte(), byte)
}
}
}

0 comments on commit a66c05a

Please sign in to comment.