Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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,21 @@ 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) {
// TODO: implement recursion limit instead of catching StackOverflowError
throw IonException("Ion data nested too deeply", e)
}
}

if (currentPosition >= source.size) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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 {
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,31 @@ 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, _, _, _ ->
val flexIntLength = PrimitiveDecoder.lengthOfFlexIntOrUIntAt(src, pos)
when (flexIntLength) {
// TODO(perf): See if there's any performance benefit to having a separate case for length=1|2 and using INT_I16 instruction
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 {
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 {
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 {
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 {
var p = position
val childOpcode = source[p++].unsignedToInt()
val macroAddress = when (childOpcode) {
in 0x00..0x47 -> childOpcode
in 0x48..0x4f -> {
val flexUIntValueAndLength = PrimitiveDecoder.readFlexUIntValueAndLength(source, p)
val addressLength = flexUIntValueAndLength.shr(Int.SIZE_BITS).toInt()
p += addressLength
val lsb = childOpcode - 0x48
val msb = flexUIntValueAndLength.toInt() * 8
msb + lsb + 72
}
0xf4 -> {
val addressValueAndLength = PrimitiveDecoder.readFlexUIntValueAndLength(source, p)
val addressValue = addressValueAndLength.toInt()
val addressLength = addressValueAndLength.shr(Int.SIZE_BITS).toInt()
p += addressLength
addressValue
}
else -> -1
}

val childCountValueAndLength = PrimitiveDecoder.readFlexUIntValueAndLength(source, p)
val childCount = childCountValueAndLength.toInt()
val prefixSize = childCountValueAndLength.shr(Int.SIZE_BITS).toInt()
p += prefixSize

// 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 childCount) {
p += handler.convertOpcodeToBytecode(
childOpcode,
source,
p,
destination,
constantPool,
macroSrc,
macroIndices,
symbolTable,
)
}
}
} else {
TODO("Macro evaluation not yet implemented")
}

return p - position
}
}
Loading
Loading