Support large data sections. Fixes #18

This commit is contained in:
Chad Retz
2018-07-26 00:05:18 -05:00
parent 6786350f53
commit a66c05ad4a
3 changed files with 64 additions and 10 deletions

View File

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

View File

@ -46,8 +46,12 @@ 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
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...
@ -55,7 +59,10 @@ open class ByteBufferMem(val direct: Boolean = true) : Mem {
"getBytes", "(Ljava/lang/String;)[B", false),
0.const,
bytes.size.const,
forceFnType<ByteBuffer.(ByteArray, Int, Int) -> ByteBuffer>(ByteBuffer::put).invokeVirtual(),
forceFnType<ByteBuffer.(ByteArray, Int, Int) -> ByteBuffer>(ByteBuffer::put).invokeVirtual()
)
}.toList()
).addInsns(
InsnNode(Opcodes.POP)
)

View File

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