From a66c05ad4aff70e2ec52d4b6353ff11cbf1f76a9 Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Thu, 26 Jul 2018 00:05:18 -0500 Subject: [PATCH] Support large data sections. Fixes #18 --- .../main/kotlin/asmble/compile/jvm/AsmExt.kt | 4 ++ .../asmble/compile/jvm/ByteBufferMem.kt | 27 +++++++----- .../asmble/compile/jvm/LargeDataTest.kt | 43 +++++++++++++++++++ 3 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 compiler/src/test/kotlin/asmble/compile/jvm/LargeDataTest.kt diff --git a/compiler/src/main/kotlin/asmble/compile/jvm/AsmExt.kt b/compiler/src/main/kotlin/asmble/compile/jvm/AsmExt.kt index d17385a..3c3586a 100644 --- a/compiler/src/main/kotlin/asmble/compile/jvm/AsmExt.kt +++ b/compiler/src/main/kotlin/asmble/compile/jvm/AsmExt.kt @@ -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) +} \ No newline at end of file diff --git a/compiler/src/main/kotlin/asmble/compile/jvm/ByteBufferMem.kt b/compiler/src/main/kotlin/asmble/compile/jvm/ByteBufferMem.kt index ec04248..87cfa02 100644 --- a/compiler/src/main/kotlin/asmble/compile/jvm/ByteBufferMem.kt +++ b/compiler/src/main/kotlin/asmble/compile/jvm/ByteBufferMem.kt @@ -46,16 +46,23 @@ open class ByteBufferMem(val direct: Boolean = true) : Mem { let(buildOffset).popExpecting(Int::class.ref). addInsns( forceFnType 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>(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>(ByteBuffer::put).invokeVirtual() + ) + }.toList() + ).addInsns( InsnNode(Opcodes.POP) ) diff --git a/compiler/src/test/kotlin/asmble/compile/jvm/LargeDataTest.kt b/compiler/src/test/kotlin/asmble/compile/jvm/LargeDataTest.kt new file mode 100644 index 0000000..0c189df --- /dev/null +++ b/compiler/src/test/kotlin/asmble/compile/jvm/LargeDataTest.kt @@ -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) + } + } +} \ No newline at end of file