Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/main/java/com/amazon/ion/bytecode/BytecodeEmitter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,19 @@ internal object BytecodeEmitter {
fun emitShortTimestampReference(destination: BytecodeBuffer, opcode: Int, dataPosition: Int) {
destination.add2(Instructions.I_SHORT_TIMESTAMP_REF.packInstructionData(opcode), dataPosition)
}

/**
* Writes a list to the destination, automatically handling the calculation of the generated bytecode
* length and reserving the space in the buffer for the list start instruction.
*
* @param contentWriter Callback function that should write children's bytecode to `destination`
*/
@JvmStatic
inline fun emitList(destination: BytecodeBuffer, contentWriter: () -> Unit) {
val containerStartIndex = destination.reserve()
contentWriter()
destination.add(Instructions.I_END_CONTAINER)
val containerBytecodeSize = destination.size() - containerStartIndex - 1 // excludes the container start instruction
destination[containerStartIndex] = Instructions.I_LIST_START.packInstructionData(containerBytecodeSize)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,20 @@ internal class ByteArrayBytecodeGenerator11(
while (currentPosition < source.size && !isSystemValue(opcode)) {
opcode = source[currentPosition++].unsignedToInt()
val handler = OpcodeHandlerTable.handler(opcode)
currentPosition += handler.convertOpcodeToBytecode(
opcode,
source,
currentPosition,
destination,
constantPool,
macroSrc,
macroIndices,
symTab
)
try {
currentPosition += handler.convertOpcodeToBytecode(
opcode,
source,
currentPosition,
destination,
constantPool,
macroSrc,
macroIndices,
symTab
)
} catch (e: StackOverflowError) {
throw IonException("Ion data nested too deeply", e)
}
Comment thread
austnwil marked this conversation as resolved.
}

if (currentPosition >= source.size) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazon.ion.bytecode.bin11.bytearray

import com.amazon.ion.bytecode.ir.Instructions
import com.amazon.ion.bytecode.ir.Instructions.packInstructionData
import com.amazon.ion.bytecode.util.AppendableConstantPoolView
import com.amazon.ion.bytecode.util.BytecodeBuffer

/**
* Writes an annotation with symbol address to the bytecode buffer. Handles opcode `0x58`.
*/
internal object AnnotationSIDOpcodeHandler : OpcodeToBytecodeHandler {
@OptIn(ExperimentalStdlibApi::class)
override fun convertOpcodeToBytecode(
opcode: Int,
source: ByteArray,
position: Int,
destination: BytecodeBuffer,
constantPool: AppendableConstantPoolView,
macroSrc: IntArray,
macroIndices: IntArray,
symbolTable: Array<String?>
): Int {
assert(opcode == 0x58) { "Handler cannot compile opcode ${opcode.toHexString()}" }
Comment thread
austnwil marked this conversation as resolved.
Outdated

val sidValueAndLength = PrimitiveDecoder.readFlexUIntValueAndLength(source, position)
val sid = sidValueAndLength.toInt()
val length = sidValueAndLength.shr(Int.SIZE_BITS).toInt()
destination.add(Instructions.I_ANNOTATION_SID.packInstructionData(sid))
return length
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import com.amazon.ion.bytecode.bin11.bytearray.PrimitiveDecoder.readFixedInt24As
import com.amazon.ion.bytecode.bin11.bytearray.PrimitiveDecoder.readFixedInt32
import com.amazon.ion.bytecode.bin11.bytearray.PrimitiveDecoder.readFixedInt8AsShort
import com.amazon.ion.bytecode.bin11.bytearray.PrimitiveDecoder.readFixedIntAsLong
import com.amazon.ion.bytecode.ir.Instructions
import com.amazon.ion.bytecode.ir.Instructions.packInstructionData
import com.amazon.ion.bytecode.util.AppendableConstantPoolView
import com.amazon.ion.bytecode.util.BytecodeBuffer

Expand Down Expand Up @@ -143,3 +145,32 @@ internal object LongIntOpcodeHandler : OpcodeToBytecodeHandler {
return fixedIntLength
}
}

/** Writes a variable-length integer in a tagless context to the bytecode buffer. Handles tagless opcode `0x60`.
* For simplicity this only ever emits `I_INT_I32`, `I_INT_I64`, and `I_INT_CP` bytecode, even if the integer could fit
* in `I_INT_I16`.
* */
@OptIn(ExperimentalStdlibApi::class)
internal val TAGLESS_FLEX_INT = OpcodeToBytecodeHandler { opcode, src, pos, dest, cp, _, _, _ ->
assert(opcode == 0x60) { "Handler cannot compile opcode ${opcode.toHexString()}" }

val flexIntLength = PrimitiveDecoder.lengthOfFlexIntOrUIntAt(src, pos)
when (flexIntLength) {
Comment thread
austnwil marked this conversation as resolved.
1, 2, 3, 4 -> {
val valueAndLength = PrimitiveDecoder.readFlexIntValueAndLength(src, pos)
val value = valueAndLength.toInt()
dest.add2(Instructions.I_INT_I32, value)
}
5, 6, 7, 8, 9 -> {
val longValue = PrimitiveDecoder.readFlexIntAsLong(src, pos)
BytecodeEmitter.emitInt64Value(dest, longValue)
}
else -> {
val bigInt = PrimitiveDecoder.readFlexIntAsBigInteger(src, pos)
val cpIndex = cp.size
cp.add(bigInt)
dest.add(Instructions.I_INT_CP.packInstructionData(cpIndex))
}
}
flexIntLength
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazon.ion.bytecode.bin11.bytearray

import com.amazon.ion.bytecode.BytecodeEmitter
import com.amazon.ion.bytecode.bin11.OpCode
import com.amazon.ion.bytecode.util.AppendableConstantPoolView
import com.amazon.ion.bytecode.util.BytecodeBuffer
import com.amazon.ion.bytecode.util.unsignedToInt

// TODO: much of the logic here is shared between lists and sexps. It might be worthwhile to do something like
// "SequenceOpcodeHandlers" and pass the start instruction (Instructions.I_LIST_START vs .I_SEXP_START) to a helper
// BytecodeEmitter.emitSequence() or similar so this logic is not duplicated in a set of `*SexpOpcodeHandler`s.

/**
* Writes a length prefixed list to the bytecode buffer. Handles opcode `0xB0`-`0xBF`.
*/
internal object ShortLengthPrefixedListOpcodeHandler : OpcodeToBytecodeHandler {
@OptIn(ExperimentalStdlibApi::class)
override fun convertOpcodeToBytecode(
opcode: Int,
source: ByteArray,
position: Int,
destination: BytecodeBuffer,
constantPool: AppendableConstantPoolView,
macroSrc: IntArray,
macroIndices: IntArray,
symbolTable: Array<String?>
): Int {
assert(opcode in 0xb0..0xbf) { "Handler cannot compile opcode ${opcode.toHexString()}" }

val length = opcode and 0xF
BytecodeEmitter.emitList(destination) {
var p = position
val end = p + length
while (p < end) {
val opcode = source[p++].unsignedToInt()
p += OpcodeHandlerTable.handler(opcode).convertOpcodeToBytecode(
opcode,
source,
p,
destination,
constantPool,
macroSrc,
macroIndices,
symbolTable,
)
}
}
return length
}
}

/**
* Writes a length prefixed list to the bytecode buffer. Handles opcode `0xFA`.
*/
internal object LongLengthPrefixedListOpcodeHandler : OpcodeToBytecodeHandler {
@OptIn(ExperimentalStdlibApi::class)
override fun convertOpcodeToBytecode(
opcode: Int,
source: ByteArray,
position: Int,
destination: BytecodeBuffer,
constantPool: AppendableConstantPoolView,
macroSrc: IntArray,
macroIndices: IntArray,
symbolTable: Array<String?>
): Int {
assert(opcode == 0xfa) { "Handler cannot compile opcode ${opcode.toHexString()}" }

val containerSizeUIntValueAndLength = PrimitiveDecoder.readFlexUIntValueAndLength(source, position)
val containerLength = containerSizeUIntValueAndLength.toInt()
val prefixLength = containerSizeUIntValueAndLength.shr(Int.SIZE_BITS).toInt()
BytecodeEmitter.emitList(destination) {
var p = position + prefixLength
val end = p + containerLength
while (p < end) {
val opcode = source[p++].unsignedToInt()
p += OpcodeHandlerTable.handler(opcode).convertOpcodeToBytecode(
opcode,
source,
p,
destination,
constantPool,
macroSrc,
macroIndices,
symbolTable,
)
}
}
return containerLength + prefixLength
}
}

/**
* Writes a delimited list to the bytecode buffer. Handles opcode `0xF0`.
*/
internal object DelimitedListOpcodeHandler : OpcodeToBytecodeHandler {
@OptIn(ExperimentalStdlibApi::class)
override fun convertOpcodeToBytecode(
opcode: Int,
source: ByteArray,
position: Int,
destination: BytecodeBuffer,
constantPool: AppendableConstantPoolView,
macroSrc: IntArray,
macroIndices: IntArray,
symbolTable: Array<String?>
): Int {
assert(opcode == 0xf0) { "Handler cannot compile opcode ${opcode.toHexString()}" }

var p = position
BytecodeEmitter.emitList(destination) {
while (true) {
val opcode = source[p++].unsignedToInt()
if (opcode == OpCode.DELIMITED_CONTAINER_END) {
break
}
p += OpcodeHandlerTable.handler(opcode).convertOpcodeToBytecode(
opcode,
source,
p,
destination,
constantPool,
macroSrc,
macroIndices,
symbolTable,
)
}
}
val bytesRead = p - position
return bytesRead
}
}

/**
* Writes a tagless-element list to the bytecode buffer. Handles opcode `0x5B`.
*/
internal object TaglessElementListOpcodeHandler : OpcodeToBytecodeHandler {
@OptIn(ExperimentalStdlibApi::class)
override fun convertOpcodeToBytecode(
opcode: Int,
source: ByteArray,
position: Int,
destination: BytecodeBuffer,
constantPool: AppendableConstantPoolView,
macroSrc: IntArray,
macroIndices: IntArray,
symbolTable: Array<String?>
): Int {
assert(opcode == 0x5b) { "Handler cannot compile opcode ${opcode.toHexString()}" }

var p = position
val childOpcode = source[p++].unsignedToInt()
val macroAddress = when (childOpcode) {
in 0x00..0x47 -> childOpcode
in 0x48..0x4f, 0xf4 -> {
Comment thread
austnwil marked this conversation as resolved.
Outdated
val addressValueAndLength = PrimitiveDecoder.readFlexUIntValueAndLength(source, p)
val addressValue = addressValueAndLength.toInt()
val addressLength = addressValueAndLength.shr(Int.SIZE_BITS).toInt()
p += addressLength
addressValue
}
else -> -1
}

val containerSizeValueAndLength = PrimitiveDecoder.readFlexUIntValueAndLength(source, p)
val containerLength = containerSizeValueAndLength.toInt()
val prefixLength = containerSizeValueAndLength.shr(Int.SIZE_BITS).toInt()
p += prefixLength
Comment thread
austnwil marked this conversation as resolved.
Outdated

// If macroAddress > -1, then it is the address of the macro-shaped values,
// and childOpcode should be ignored.
// If macroAddress is -1, then childOpcode is the opcode of the values.
if (macroAddress < 0) {
val handler = TaglessOpcodeHandlerTable.handler(childOpcode)
BytecodeEmitter.emitList(destination) {
for (i in 0 until containerLength) {
p += handler.convertOpcodeToBytecode(
childOpcode,
source,
p,
destination,
constantPool,
macroSrc,
macroIndices,
symbolTable,
)
}
}
} else {
TODO("Macro evaluation not yet implemented")
}

return p - position
}
}
Loading
Loading