mirror of
https://github.com/fluencelabs/asmble
synced 2025-04-25 14:52:21 +00:00
Update spec and two related changes (detailed)
* Added LEB128 validation (ref: https://github.com/WebAssembly/spec/pull/750) * Rename memory instructions (ref: https://github.com/WebAssembly/spec/pull/720)
This commit is contained in:
parent
368ab300fa
commit
f24342959d
@ -223,8 +223,8 @@ sealed class Node {
|
||||
data class I64Store8(override val align: Int, override val offset: Long) : Instr(), Args.AlignOffset
|
||||
data class I64Store16(override val align: Int, override val offset: Long) : Instr(), Args.AlignOffset
|
||||
data class I64Store32(override val align: Int, override val offset: Long) : Instr(), Args.AlignOffset
|
||||
data class CurrentMemory(override val reserved: Boolean) : Instr(), Args.Reserved
|
||||
data class GrowMemory(override val reserved: Boolean) : Instr(), Args.Reserved
|
||||
data class MemorySize(override val reserved: Boolean) : Instr(), Args.Reserved
|
||||
data class MemoryGrow(override val reserved: Boolean) : Instr(), Args.Reserved
|
||||
|
||||
// Constants
|
||||
data class I32Const(override val value: Int) : Instr(), Args.Const<Int>
|
||||
@ -511,8 +511,8 @@ sealed class Node {
|
||||
opMapEntry("i64.store8", 0x3c, ::MemOpAlignOffsetArg, Instr::I64Store8, Instr.I64Store8::class)
|
||||
opMapEntry("i64.store16", 0x3d, ::MemOpAlignOffsetArg, Instr::I64Store16, Instr.I64Store16::class)
|
||||
opMapEntry("i64.store32", 0x3e, ::MemOpAlignOffsetArg, Instr::I64Store32, Instr.I64Store32::class)
|
||||
opMapEntry("current_memory", 0x3f, ::MemOpReservedArg, Instr::CurrentMemory, Instr.CurrentMemory::class)
|
||||
opMapEntry("grow_memory", 0x40, ::MemOpReservedArg, Instr::GrowMemory, Instr.GrowMemory::class)
|
||||
opMapEntry("memory.size", 0x3f, ::MemOpReservedArg, Instr::MemorySize, Instr.MemorySize::class)
|
||||
opMapEntry("memory.grow", 0x40, ::MemOpReservedArg, Instr::MemoryGrow, Instr.MemoryGrow::class)
|
||||
|
||||
opMapEntry("i32.const", 0x41, ::ConstOpIntArg, Instr::I32Const, Instr.I32Const::class)
|
||||
opMapEntry("i64.const", 0x42, ::ConstOpLongArg, Instr::I64Const, Instr.I64Const::class)
|
||||
|
@ -148,10 +148,10 @@ open class FuncBuilder {
|
||||
is Node.Instr.I32Store8, is Node.Instr.I32Store16, is Node.Instr.I64Store8, is Node.Instr.I64Store16,
|
||||
is Node.Instr.I64Store32 ->
|
||||
applyStoreOp(ctx, fn, i as Node.Instr.Args.AlignOffset, index)
|
||||
is Node.Instr.CurrentMemory ->
|
||||
applyCurrentMemory(ctx, fn)
|
||||
is Node.Instr.GrowMemory ->
|
||||
applyGrowMemory(ctx, fn)
|
||||
is Node.Instr.MemorySize ->
|
||||
applyMemorySize(ctx, fn)
|
||||
is Node.Instr.MemoryGrow ->
|
||||
applyMemoryGrow(ctx, fn)
|
||||
is Node.Instr.I32Const ->
|
||||
fn.addInsns(i.value.const).push(Int::class.ref)
|
||||
is Node.Instr.I64Const ->
|
||||
@ -1062,14 +1062,14 @@ open class FuncBuilder {
|
||||
).push(Int::class.ref)
|
||||
}
|
||||
|
||||
fun applyGrowMemory(ctx: FuncContext, fn: Func) =
|
||||
fun applyMemoryGrow(ctx: FuncContext, fn: Func) =
|
||||
// Grow mem is a special case where the memory ref is already pre-injected on
|
||||
// the stack before this call. Result is an int.
|
||||
ctx.cls.assertHasMemory().let {
|
||||
ctx.cls.mem.growMemory(ctx, fn)
|
||||
}
|
||||
|
||||
fun applyCurrentMemory(ctx: FuncContext, fn: Func) =
|
||||
fun applyMemorySize(ctx: FuncContext, fn: Func) =
|
||||
// Curr mem is not specially injected, so we have to put the memory on the
|
||||
// stack since we need it
|
||||
ctx.cls.assertHasMemory().let {
|
||||
|
@ -194,7 +194,7 @@ open class InsnReworker {
|
||||
is Node.Instr.I64Store32 ->
|
||||
injectBeforeLastStackCount(Insn.MemNeededOnStack, 2)
|
||||
// Grow memory requires "mem" before the single param
|
||||
is Node.Instr.GrowMemory ->
|
||||
is Node.Instr.MemoryGrow ->
|
||||
injectBeforeLastStackCount(Insn.MemNeededOnStack, 1)
|
||||
else -> { }
|
||||
}
|
||||
@ -239,8 +239,8 @@ open class InsnReworker {
|
||||
is Node.Instr.I32Store, is Node.Instr.I64Store, is Node.Instr.F32Store, is Node.Instr.F64Store,
|
||||
is Node.Instr.I32Store8, is Node.Instr.I32Store16, is Node.Instr.I64Store8, is Node.Instr.I64Store16,
|
||||
is Node.Instr.I64Store32 -> POP_PARAM
|
||||
is Node.Instr.CurrentMemory -> PUSH_RESULT
|
||||
is Node.Instr.GrowMemory -> POP_PARAM + PUSH_RESULT
|
||||
is Node.Instr.MemorySize -> PUSH_RESULT
|
||||
is Node.Instr.MemoryGrow -> POP_PARAM + PUSH_RESULT
|
||||
is Node.Instr.I32Const, is Node.Instr.I64Const,
|
||||
is Node.Instr.F32Const, is Node.Instr.F64Const -> PUSH_RESULT
|
||||
is Node.Instr.I32Add, is Node.Instr.I32Sub, is Node.Instr.I32Mul, is Node.Instr.I32DivS,
|
||||
@ -288,12 +288,12 @@ open class InsnReworker {
|
||||
val inc =
|
||||
if (lastCouldHaveMem) 0
|
||||
else if (insn == Insn.MemNeededOnStack) 1
|
||||
else if (insn is Insn.Node && insn.insn is Node.Instr.CurrentMemory) 1
|
||||
else if (insn is Insn.Node && insn.insn is Node.Instr.MemorySize) 1
|
||||
else 0
|
||||
val couldSetMemNext = if (insn !is Insn.Node) false else when (insn.insn) {
|
||||
is Node.Instr.I32Store, is Node.Instr.I64Store, is Node.Instr.F32Store, is Node.Instr.F64Store,
|
||||
is Node.Instr.I32Store8, is Node.Instr.I32Store16, is Node.Instr.I64Store8, is Node.Instr.I64Store16,
|
||||
is Node.Instr.I64Store32, is Node.Instr.GrowMemory -> true
|
||||
is Node.Instr.I64Store32, is Node.Instr.MemoryGrow -> true
|
||||
else -> false
|
||||
}
|
||||
(count + inc) to couldSetMemNext
|
||||
|
@ -1,12 +1,12 @@
|
||||
package asmble.io
|
||||
|
||||
import asmble.util.toIntExact
|
||||
import asmble.util.toUnsignedBigInt
|
||||
import asmble.util.toUnsignedLong
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.math.BigInteger
|
||||
|
||||
|
||||
abstract class ByteReader {
|
||||
abstract val isEof: Boolean
|
||||
|
||||
@ -34,27 +34,30 @@ abstract class ByteReader {
|
||||
}
|
||||
|
||||
fun readVarInt7() = readSignedLeb128().let {
|
||||
require(it >= Byte.MIN_VALUE.toLong() && it <= Byte.MAX_VALUE.toLong())
|
||||
if (it < Byte.MIN_VALUE.toLong() || it > Byte.MAX_VALUE.toLong()) throw IoErr.InvalidLeb128Number()
|
||||
it.toByte()
|
||||
}
|
||||
|
||||
fun readVarInt32() = readSignedLeb128().toIntExact()
|
||||
fun readVarInt32() = readSignedLeb128().let {
|
||||
if (it < Int.MIN_VALUE.toLong() || it > Int.MAX_VALUE.toLong()) throw IoErr.InvalidLeb128Number()
|
||||
it.toInt()
|
||||
}
|
||||
|
||||
fun readVarInt64() = readSignedLeb128()
|
||||
fun readVarInt64() = readSignedLeb128(9)
|
||||
|
||||
fun readVarUInt1() = readUnsignedLeb128().let {
|
||||
require(it == 1 || it == 0)
|
||||
if (it != 1 && it != 0) throw IoErr.InvalidLeb128Number()
|
||||
it == 1
|
||||
}
|
||||
|
||||
fun readVarUInt7() = readUnsignedLeb128().let {
|
||||
require(it <= 255)
|
||||
if (it > 255) throw IoErr.InvalidLeb128Number()
|
||||
it.toShort()
|
||||
}
|
||||
|
||||
fun readVarUInt32() = readUnsignedLeb128().toUnsignedLong()
|
||||
|
||||
protected fun readUnsignedLeb128(): Int {
|
||||
protected fun readUnsignedLeb128(maxCount: Int = 4): Int {
|
||||
// Taken from Android source, Apache licensed
|
||||
var result = 0
|
||||
var cur: Int
|
||||
@ -63,12 +66,12 @@ abstract class ByteReader {
|
||||
cur = readByte().toInt() and 0xff
|
||||
result = result or ((cur and 0x7f) shl (count * 7))
|
||||
count++
|
||||
} while (cur and 0x80 == 0x80 && count < 5)
|
||||
if (cur and 0x80 == 0x80) throw NumberFormatException()
|
||||
} while (cur and 0x80 == 0x80 && count <= maxCount)
|
||||
if (cur and 0x80 == 0x80) throw IoErr.InvalidLeb128Number()
|
||||
return result
|
||||
}
|
||||
|
||||
private fun readSignedLeb128(): Long {
|
||||
private fun readSignedLeb128(maxCount: Int = 4): Long {
|
||||
// Taken from Android source, Apache licensed
|
||||
var result = 0L
|
||||
var cur: Int
|
||||
@ -79,8 +82,20 @@ abstract class ByteReader {
|
||||
result = result or ((cur and 0x7f).toLong() shl (count * 7))
|
||||
signBits = signBits shl 7
|
||||
count++
|
||||
} while (cur and 0x80 == 0x80 && count < 10)
|
||||
if (cur and 0x80 == 0x80) throw NumberFormatException()
|
||||
} while (cur and 0x80 == 0x80 && count <= maxCount)
|
||||
if (cur and 0x80 == 0x80) throw IoErr.InvalidLeb128Number()
|
||||
|
||||
// Check for 64 bit invalid, taken from Apache/MIT licensed:
|
||||
// https://github.com/paritytech/parity-wasm/blob/2650fc14c458c6a252c9dc43dd8e0b14b6d264ff/src/elements/primitives.rs#L351
|
||||
// TODO: probably need 32 bit checks too, but meh, not in the suite
|
||||
if (count > maxCount && maxCount == 9) {
|
||||
if (cur and 0b0100_0000 == 0b0100_0000) {
|
||||
if ((cur or 0b1000_0000).toByte() != (-1).toByte()) throw IoErr.InvalidLeb128Number()
|
||||
} else if (cur != 0) {
|
||||
throw IoErr.InvalidLeb128Number()
|
||||
}
|
||||
}
|
||||
|
||||
if ((signBits shr 1) and result != 0L) result = result or signBits
|
||||
return result
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package asmble.io
|
||||
|
||||
import asmble.AsmErr
|
||||
import java.math.BigInteger
|
||||
|
||||
sealed class IoErr(message: String, cause: Throwable? = null) : RuntimeException(message, cause), AsmErr {
|
||||
class UnexpectedEnd : IoErr("Unexpected EOF") {
|
||||
@ -119,4 +118,9 @@ sealed class IoErr(message: String, cause: Throwable? = null) : RuntimeException
|
||||
class InvalidUtf8Encoding : IoErr("Some byte sequence was not UTF-8 compatible") {
|
||||
override val asmErrString get() = "invalid UTF-8 encoding"
|
||||
}
|
||||
|
||||
class InvalidLeb128Number : IoErr("Invalid LEB128 number") {
|
||||
override val asmErrString get() = "integer representation too long"
|
||||
override val asmErrStrings get() = listOf(asmErrString, "integer too large")
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
Subproject commit 98b90e2ab22053559ded143768d6559d2ba8d2fd
|
||||
Subproject commit 1f00d57d009ec4098b60fb6ca138e8e2787accef
|
Loading…
x
Reference in New Issue
Block a user