Update to account for nullability awareness in the carpenter

This commit is contained in:
Katelyn Baker 2017-07-11 14:02:22 +01:00
parent c29673a4a4
commit 5ab26dd275
7 changed files with 198 additions and 178 deletions

View File

@ -17,7 +17,9 @@ import java.util.*
import net.corda.core.serialization.carpenter.CarpenterSchemas
import net.corda.core.serialization.carpenter.Schema as CarpenterSchema
import net.corda.core.serialization.carpenter.Field as CarpenterField
import net.corda.core.serialization.carpenter.CarpenterSchemaFactory
import net.corda.core.serialization.carpenter.FieldFactory
// TODO: get an assigned number as per AMQP spec
val DESCRIPTOR_TOP_32BITS: Long = 0xc0da0000
@ -350,11 +352,11 @@ data class CompositeType(override val name: String, override val label: String?,
}
}
val m : MutableMap<String, Class<out Any?>> = mutableMapOf()
val m : MutableMap<String, CarpenterField> = mutableMapOf()
fields.forEach {
try {
m[it.name] = it.getTypeAsClass(classLoaders)
m[it.name] = FieldFactory.newInstance(it.mandatory, it.name, it.getTypeAsClass(classLoaders))
}
catch (e: ClassNotFoundException) {
carpenterSchemas.addDepPair(this, name, it.typeAsString())

View File

@ -3,17 +3,14 @@ package net.corda.core.serialization.carpenter
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.Type
import java.lang.Character.isJavaIdentifierPart
import java.lang.Character.isJavaIdentifierStart
import net.corda.core.serialization.carpenter.Schema
import net.corda.core.serialization.carpenter.ClassSchema
import net.corda.core.serialization.carpenter.InterfaceSchema
import java.util.*
/**********************************************************************************************************************/
/**
* Any object that implements this interface is expected to expose its own fields via the [get] method, exactly
* as if `this.class.getMethod("get" + name.capitalize()).invoke(this)` had been called. It is intended as a more
@ -23,6 +20,13 @@ interface SimpleFieldAccess {
operator fun get(name: String): Any?
}
/**********************************************************************************************************************/
class CarpenterClassLoader : ClassLoader(Thread.currentThread().contextClassLoader) {
fun load(name: String, bytes: ByteArray) = defineClass(name, bytes, 0, bytes.size)
}
/**********************************************************************************************************************/
/**
* A class carpenter generates JVM bytecodes for a class given a schema and then loads it into a sub-classloader.
@ -68,15 +72,6 @@ interface SimpleFieldAccess {
*
* Equals/hashCode methods are not yet supported.
*/
/**********************************************************************************************************************/
class CarpenterClassLoader : ClassLoader(Thread.currentThread().contextClassLoader) {
fun load(name: String, bytes: ByteArray) = defineClass(name, bytes, 0, bytes.size)
}
/**********************************************************************************************************************/
class ClassCarpenter {
// TODO: Generics.
// TODO: Sandbox the generated code when a security manager is in use.
@ -84,94 +79,6 @@ class ClassCarpenter {
// TODO: Support annotations.
// TODO: isFoo getter patterns for booleans (this is what Kotlin generates)
class DuplicateNameException : RuntimeException("An attempt was made to register two classes with the same name within the same ClassCarpenter namespace.")
class InterfaceMismatchException(msg: String) : RuntimeException(msg)
class NullablePrimitiveException(msg: String) : RuntimeException(msg)
abstract class Field(val field: Class<out Any?>) {
companion object {
const val unsetName = "Unset"
}
var name: String = unsetName
abstract val nullabilityAnnotation: String
val descriptor: String
get() = Type.getDescriptor(this.field)
val type: String
get() = if (this.field.isPrimitive) this.descriptor else "Ljava/lang/Object;"
fun generateField(cw: ClassWriter) {
val fieldVisitor = cw.visitField(ACC_PROTECTED + ACC_FINAL, name, descriptor, null, null)
fieldVisitor.visitAnnotation(nullabilityAnnotation, true).visitEnd()
fieldVisitor.visitEnd()
}
fun addNullabilityAnnotation(mv: MethodVisitor) {
mv.visitAnnotation(nullabilityAnnotation, true).visitEnd()
}
fun visitParameter(mv: MethodVisitor, idx: Int) {
with(mv) {
visitParameter(name, 0)
if (!field.isPrimitive) {
visitParameterAnnotation(idx, nullabilityAnnotation, true).visitEnd()
}
}
}
abstract fun copy(name: String, field: Class<out Any?>): Field
abstract fun nullTest(mv: MethodVisitor, slot: Int)
}
class NonNullableField(field: Class<out Any?>) : Field(field) {
override val nullabilityAnnotation = "Ljavax/annotation/Nonnull;"
constructor(name: String, field: Class<out Any?>) : this(field) {
this.name = name
}
override fun copy(name: String, field: Class<out Any?>) = NonNullableField(name, field)
override fun nullTest(mv: MethodVisitor, slot: Int) {
assert(name != unsetName)
if (!field.isPrimitive) {
with(mv) {
visitVarInsn(ALOAD, 0) // load this
visitVarInsn(ALOAD, slot) // load parameter
visitLdcInsn("param \"$name\" cannot be null")
visitMethodInsn(INVOKESTATIC,
"java/util/Objects",
"requireNonNull",
"(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", false)
visitInsn(POP)
}
}
}
}
class NullableField(field: Class<out Any?>) : Field(field) {
override val nullabilityAnnotation = "Ljavax/annotation/Nullable;"
constructor(name: String, field: Class<out Any?>) : this(field) {
if (field.isPrimitive) {
throw NullablePrimitiveException (
"Field $name is primitive type ${Type.getDescriptor(field)} and thus cannot be nullable")
}
this.name = name
}
override fun copy(name: String, field: Class<out Any?>) = NullableField(name, field)
override fun nullTest(mv: MethodVisitor, slot: Int) {
assert(name != unsetName)
}
}
val classloader = CarpenterClassLoader()
private val _loaded = HashMap<String, Class<*>>()

View File

@ -0,0 +1,9 @@
package net.corda.core.serialization.carpenter
class DuplicateNameException : RuntimeException (
"An attempt was made to register two classes with the same name within the same ClassCarpenter namespace.")
class InterfaceMismatchException(msg: String) : RuntimeException(msg)
class NullablePrimitiveException(msg: String) : RuntimeException(msg)

View File

@ -1,5 +1,10 @@
package net.corda.core.serialization.carpenter
import jdk.internal.org.objectweb.asm.Opcodes.*
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Type
import java.util.LinkedHashMap
@ -14,7 +19,7 @@ abstract class Schema(
val superclass: Schema? = null,
val interfaces: List<Class<*>> = emptyList())
{
private fun Map<String, ClassCarpenter.Field>.descriptors() =
private fun Map<String, Field>.descriptors() =
LinkedHashMap(this.mapValues { it.value.descriptor })
/* Fix the order up front if the user didn't, inject the name into the field as it's
@ -35,7 +40,7 @@ abstract class Schema(
class ClassSchema(
name: String,
fields: Map<String, Class<out Any?>>,
fields: Map<String, Field>,
superclass: Schema? = null,
interfaces: List<Class<*>> = emptyList()
) : Schema (name, fields, superclass, interfaces)
@ -44,7 +49,7 @@ class ClassSchema(
class InterfaceSchema(
name: String,
fields: Map<String, Class<out Any?>>,
fields: Map<String, Field>,
superclass: Schema? = null,
interfaces: List<Class<*>> = emptyList()
) : Schema (name, fields, superclass, interfaces)
@ -54,7 +59,7 @@ class InterfaceSchema(
object CarpenterSchemaFactory {
fun newInstance (
name: String,
fields: Map<String, Class<out Any?>>,
fields: Map<String, Field>,
superclass: Schema? = null,
interfaces: List<Class<*>> = emptyList(),
isInterface: Boolean = false
@ -64,3 +69,100 @@ object CarpenterSchemaFactory {
}
/**********************************************************************************************************************/
abstract class Field(val field: Class<out Any?>) {
companion object {
const val unsetName = "Unset"
}
var name: String = unsetName
abstract val nullabilityAnnotation: String
val descriptor: String
get() = Type.getDescriptor(this.field)
val type: String
get() = if (this.field.isPrimitive) this.descriptor else "Ljava/lang/Object;"
fun generateField(cw: ClassWriter) {
val fieldVisitor = cw.visitField(ACC_PROTECTED + ACC_FINAL, name, descriptor, null, null)
fieldVisitor.visitAnnotation(nullabilityAnnotation, true).visitEnd()
fieldVisitor.visitEnd()
}
fun addNullabilityAnnotation(mv: MethodVisitor) {
mv.visitAnnotation(nullabilityAnnotation, true).visitEnd()
}
fun visitParameter(mv: MethodVisitor, idx: Int) {
with(mv) {
visitParameter(name, 0)
if (!field.isPrimitive) {
visitParameterAnnotation(idx, nullabilityAnnotation, true).visitEnd()
}
}
}
abstract fun copy(name: String, field: Class<out Any?>): Field
abstract fun nullTest(mv: MethodVisitor, slot: Int)
}
/**********************************************************************************************************************/
class NonNullableField(field: Class<out Any?>) : Field(field) {
override val nullabilityAnnotation = "Ljavax/annotation/Nonnull;"
constructor(name: String, field: Class<out Any?>) : this(field) {
this.name = name
}
override fun copy(name: String, field: Class<out Any?>) = NonNullableField(name, field)
override fun nullTest(mv: MethodVisitor, slot: Int) {
assert(name != unsetName)
if (!field.isPrimitive) {
with(mv) {
visitVarInsn(ALOAD, 0) // load this
visitVarInsn(ALOAD, slot) // load parameter
visitLdcInsn("param \"$name\" cannot be null")
visitMethodInsn(INVOKESTATIC,
"java/util/Objects",
"requireNonNull",
"(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", false)
visitInsn(POP)
}
}
}
}
/**********************************************************************************************************************/
class NullableField(field: Class<out Any?>) : Field(field) {
override val nullabilityAnnotation = "Ljavax/annotation/Nullable;"
constructor(name: String, field: Class<out Any?>) : this(field) {
if (field.isPrimitive) {
throw NullablePrimitiveException (
"Field $name is primitive type ${Type.getDescriptor(field)} and thus cannot be nullable")
}
this.name = name
}
override fun copy(name: String, field: Class<out Any?>) = NullableField(name, field)
override fun nullTest(mv: MethodVisitor, slot: Int) {
assert(name != unsetName)
}
}
/**********************************************************************************************************************/
object FieldFactory {
fun newInstance (mandatory: Boolean, name: String, field: Class<out Any?>) =
if (mandatory) NonNullableField (name, field) else NullableField (name, field)
}
/**********************************************************************************************************************/

View File

@ -8,7 +8,7 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.EmptyWhitelist
import net.corda.core.serialization.KryoAMQPSerializer
import net.corda.core.serialization.amqp.SerializerFactory.Companion.isPrimitive
import net.corda.core.utilities.CordaRuntimeException
import net.corda.core.CordaRuntimeException
import net.corda.nodeapi.RPCException
import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_PUBKEY

View File

@ -31,7 +31,7 @@ class ClassCarpenterTest {
@Test
fun prims() {
val clazz = cc.build(ClassCarpenter.ClassSchema(
val clazz = cc.build(ClassSchema(
"gen.Prims",
mapOf(
"anIntField" to Int::class.javaPrimitiveType!!,
@ -42,7 +42,7 @@ class ClassCarpenterTest {
"floatMyBoat" to Float::class.javaPrimitiveType!!,
"byteMe" to Byte::class.javaPrimitiveType!!,
"booleanField" to Boolean::class.javaPrimitiveType!!).mapValues {
ClassCarpenter.NonNullableField (it.value)
NonNullableField (it.value)
}))
assertEquals(8, clazz.nonSyntheticFields.size)
assertEquals(10, clazz.nonSyntheticMethods.size)
@ -72,7 +72,7 @@ class ClassCarpenterTest {
val clazz = cc.build(ClassSchema("gen.Person", mapOf(
"age" to Int::class.javaPrimitiveType!!,
"name" to String::class.java
).mapValues { ClassCarpenter.NonNullableField (it.value) } ))
).mapValues { NonNullableField (it.value) } ))
val i = clazz.constructors[0].newInstance(32, "Mike")
return Pair(clazz, i)
}
@ -90,7 +90,7 @@ class ClassCarpenterTest {
assertEquals("Person{age=32, name=Mike}", i.toString())
}
@Test(expected = ClassCarpenter.DuplicateNameException::class)
@Test(expected = DuplicateNameException::class)
fun duplicates() {
cc.build(ClassSchema("gen.EmptyClass", emptyMap(), null))
cc.build(ClassSchema("gen.EmptyClass", emptyMap(), null))
@ -99,8 +99,8 @@ class ClassCarpenterTest {
@Test
fun `can refer to each other`() {
val (clazz1, i) = genPerson()
val clazz2 = cc.build(ClassCarpenter.ClassSchema("gen.Referee", mapOf(
"ref" to ClassCarpenter.NonNullableField (clazz1)
val clazz2 = cc.build(ClassSchema("gen.Referee", mapOf(
"ref" to NonNullableField (clazz1)
)))
val i2 = clazz2.constructors[0].newInstance(i)
assertEquals(i, (i2 as SimpleFieldAccess)["ref"])
@ -108,13 +108,13 @@ class ClassCarpenterTest {
@Test
fun superclasses() {
val schema1 = ClassCarpenter.ClassSchema(
val schema1 = ClassSchema(
"gen.A",
mapOf("a" to ClassCarpenter.NonNullableField (String::class.java)))
mapOf("a" to NonNullableField (String::class.java)))
val schema2 = ClassCarpenter.ClassSchema(
val schema2 = ClassSchema(
"gen.B",
mapOf("b" to ClassCarpenter.NonNullableField (String::class.java)),
mapOf("b" to NonNullableField (String::class.java)),
schema1)
val clazz = cc.build(schema2)
@ -126,12 +126,12 @@ class ClassCarpenterTest {
@Test
fun interfaces() {
val schema1 = ClassCarpenter.ClassSchema(
val schema1 = ClassSchema(
"gen.A",
mapOf("a" to ClassCarpenter.NonNullableField(String::class.java)))
mapOf("a" to NonNullableField(String::class.java)))
val schema2 = ClassCarpenter.ClassSchema("gen.B",
mapOf("b" to ClassCarpenter.NonNullableField(Int::class.java)),
val schema2 = ClassSchema("gen.B",
mapOf("b" to NonNullableField(Int::class.java)),
schema1,
interfaces = listOf(DummyInterface::class.java))
@ -141,15 +141,15 @@ class ClassCarpenterTest {
assertEquals(1, i.b)
}
@Test(expected = ClassCarpenter.InterfaceMismatchException::class)
@Test(expected = InterfaceMismatchException::class)
fun `mismatched interface`() {
val schema1 = ClassCarpenter.ClassSchema(
val schema1 = ClassSchema(
"gen.A",
mapOf("a" to ClassCarpenter.NonNullableField(String::class.java)))
mapOf("a" to NonNullableField(String::class.java)))
val schema2 = ClassCarpenter.ClassSchema(
val schema2 = ClassSchema(
"gen.B",
mapOf("c" to ClassCarpenter.NonNullableField(Int::class.java)),
mapOf("c" to NonNullableField(Int::class.java)),
schema1,
interfaces = listOf(DummyInterface::class.java))
@ -160,9 +160,9 @@ class ClassCarpenterTest {
@Test
fun `generate interface`() {
val schema1 = ClassCarpenter.InterfaceSchema(
val schema1 = InterfaceSchema(
"gen.Interface",
mapOf("a" to ClassCarpenter.NonNullableField (Int::class.java)))
mapOf("a" to NonNullableField (Int::class.java)))
val iface = cc.build(schema1)
@ -173,7 +173,7 @@ class ClassCarpenterTest {
val schema2 = ClassSchema(
"gen.Derived",
mapOf("a" to ClassCarpenter.NonNullableField (Int::class.java)),
mapOf("a" to NonNullableField (Int::class.java)),
interfaces = listOf(iface))
val clazz = cc.build(schema2)
@ -185,25 +185,25 @@ class ClassCarpenterTest {
@Test
fun `generate multiple interfaces`() {
val iFace1 = ClassCarpenter.InterfaceSchema(
val iFace1 = InterfaceSchema(
"gen.Interface1",
mapOf(
"a" to ClassCarpenter.NonNullableField(Int::class.java),
"b" to ClassCarpenter.NonNullableField(String::class.java)))
"a" to NonNullableField(Int::class.java),
"b" to NonNullableField(String::class.java)))
val iFace2 = ClassCarpenter.InterfaceSchema(
val iFace2 = InterfaceSchema(
"gen.Interface2",
mapOf(
"c" to ClassCarpenter.NonNullableField(Int::class.java),
"d" to ClassCarpenter.NonNullableField(String::class.java)))
"c" to NonNullableField(Int::class.java),
"d" to NonNullableField(String::class.java)))
val class1 = ClassSchema(
"gen.Derived",
mapOf(
"a" to ClassCarpenter.NonNullableField(Int::class.java),
"b" to ClassCarpenter.NonNullableField(String::class.java),
"c" to ClassCarpenter.NonNullableField(Int::class.java),
"d" to ClassCarpenter.NonNullableField(String::class.java)),
"a" to NonNullableField(Int::class.java),
"b" to NonNullableField(String::class.java),
"c" to NonNullableField(Int::class.java),
"d" to NonNullableField(String::class.java)),
interfaces = listOf(cc.build(iFace1), cc.build(iFace2)))
val clazz = cc.build(class1)
@ -224,23 +224,23 @@ class ClassCarpenterTest {
val iFace1 = InterfaceSchema(
"gen.Interface1",
mapOf(
"a" to ClassCarpenter.NonNullableField (Int::class.java),
"b" to ClassCarpenter.NonNullableField(String::class.java)))
"a" to NonNullableField (Int::class.java),
"b" to NonNullableField(String::class.java)))
val iFace2 = InterfaceSchema(
"gen.Interface2",
mapOf(
"c" to ClassCarpenter.NonNullableField(Int::class.java),
"d" to ClassCarpenter.NonNullableField(String::class.java)),
"c" to NonNullableField(Int::class.java),
"d" to NonNullableField(String::class.java)),
interfaces = listOf(cc.build(iFace1)))
val class1 = ClassSchema(
"gen.Derived",
mapOf(
"a" to ClassCarpenter.NonNullableField(Int::class.java),
"b" to ClassCarpenter.NonNullableField(String::class.java),
"c" to ClassCarpenter.NonNullableField(Int::class.java),
"d" to ClassCarpenter.NonNullableField(String::class.java)),
"a" to NonNullableField(Int::class.java),
"b" to NonNullableField(String::class.java),
"c" to NonNullableField(Int::class.java),
"d" to NonNullableField(String::class.java)),
interfaces = listOf(cc.build(iFace2)))
val clazz = cc.build(class1)
@ -259,9 +259,9 @@ class ClassCarpenterTest {
@Test(expected = java.lang.IllegalArgumentException::class)
fun `null parameter small int`() {
val className = "iEnjoySwede"
val schema = ClassCarpenter.ClassSchema(
val schema = ClassSchema(
"gen.$className",
mapOf("a" to ClassCarpenter.NonNullableField (Int::class.java)))
mapOf("a" to NonNullableField (Int::class.java)))
val clazz = cc.build(schema)
@ -269,12 +269,12 @@ class ClassCarpenterTest {
clazz.constructors[0].newInstance(a)
}
@Test(expected = ClassCarpenter.NullablePrimitiveException::class)
@Test(expected = NullablePrimitiveException::class)
fun `nullable parameter small int`() {
val className = "iEnjoySwede"
val schema = ClassCarpenter.ClassSchema(
val schema = ClassSchema(
"gen.$className",
mapOf("a" to ClassCarpenter.NullableField (Int::class.java)))
mapOf("a" to NullableField (Int::class.java)))
cc.build(schema)
}
@ -282,9 +282,9 @@ class ClassCarpenterTest {
@Test
fun `nullable parameter integer`() {
val className = "iEnjoyWibble"
val schema = ClassCarpenter.ClassSchema(
val schema = ClassSchema(
"gen.$className",
mapOf("a" to ClassCarpenter.NullableField (Integer::class.java)))
mapOf("a" to NullableField (Integer::class.java)))
val clazz = cc.build(schema)
val a1 : Int? = null
@ -297,9 +297,9 @@ class ClassCarpenterTest {
@Test
fun `non nullable parameter integer with non null`() {
val className = "iEnjoyWibble"
val schema = ClassCarpenter.ClassSchema(
val schema = ClassSchema(
"gen.$className",
mapOf("a" to ClassCarpenter.NonNullableField (Integer::class.java)))
mapOf("a" to NonNullableField (Integer::class.java)))
val clazz = cc.build(schema)
@ -310,9 +310,9 @@ class ClassCarpenterTest {
@Test(expected = java.lang.reflect.InvocationTargetException::class)
fun `non nullable parameter integer with null`() {
val className = "iEnjoyWibble"
val schema = ClassCarpenter.ClassSchema(
val schema = ClassSchema(
"gen.$className",
mapOf("a" to ClassCarpenter.NonNullableField (Integer::class.java)))
mapOf("a" to NonNullableField (Integer::class.java)))
val clazz = cc.build(schema)
@ -324,9 +324,9 @@ class ClassCarpenterTest {
@Suppress("UNCHECKED_CAST")
fun `int array`() {
val className = "iEnjoyPotato"
val schema = ClassCarpenter.ClassSchema(
val schema = ClassSchema(
"gen.$className",
mapOf("a" to ClassCarpenter.NonNullableField(IntArray::class.java)))
mapOf("a" to NonNullableField(IntArray::class.java)))
val clazz = cc.build(schema)
@ -343,9 +343,9 @@ class ClassCarpenterTest {
@Test(expected = java.lang.reflect.InvocationTargetException::class)
fun `nullable int array throws`() {
val className = "iEnjoySwede"
val schema = ClassCarpenter.ClassSchema(
val schema = ClassSchema(
"gen.$className",
mapOf("a" to ClassCarpenter.NonNullableField(IntArray::class.java)))
mapOf("a" to NonNullableField(IntArray::class.java)))
val clazz = cc.build(schema)
@ -357,9 +357,9 @@ class ClassCarpenterTest {
@Suppress("UNCHECKED_CAST")
fun `integer array`() {
val className = "iEnjoyFlan"
val schema = ClassCarpenter.ClassSchema(
val schema = ClassSchema(
"gen.$className",
mapOf("a" to ClassCarpenter.NonNullableField(Array<Int>::class.java)))
mapOf("a" to NonNullableField(Array<Int>::class.java)))
val clazz = cc.build(schema)
@ -377,11 +377,11 @@ class ClassCarpenterTest {
@Suppress("UNCHECKED_CAST")
fun `int array with ints`() {
val className = "iEnjoyCrumble"
val schema = ClassCarpenter.ClassSchema(
val schema = ClassSchema(
"gen.$className", mapOf(
"a" to Int::class.java,
"b" to IntArray::class.java,
"c" to Int::class.java).mapValues { ClassCarpenter.NonNullableField(it.value) })
"c" to Int::class.java).mapValues { NonNullableField(it.value) })
val clazz = cc.build(schema)
@ -399,11 +399,11 @@ class ClassCarpenterTest {
@Suppress("UNCHECKED_CAST")
fun `multiple int arrays`() {
val className = "iEnjoyJam"
val schema = ClassCarpenter.ClassSchema(
val schema = ClassSchema(
"gen.$className", mapOf(
"a" to IntArray::class.java,
"b" to Int::class.java,
"c" to IntArray::class.java).mapValues { ClassCarpenter.NonNullableField(it.value) })
"c" to IntArray::class.java).mapValues { NonNullableField(it.value) })
val clazz = cc.build(schema)
val i = clazz.constructors[0].newInstance(intArrayOf(1, 2), 3, intArrayOf(4, 5, 6))
@ -422,9 +422,9 @@ class ClassCarpenterTest {
@Suppress("UNCHECKED_CAST")
fun `string array`() {
val className = "iEnjoyToast"
val schema = ClassCarpenter.ClassSchema(
val schema = ClassSchema(
"gen.$className",
mapOf("a" to ClassCarpenter.NullableField(Array<String>::class.java)))
mapOf("a" to NullableField(Array<String>::class.java)))
val clazz = cc.build(schema)
@ -440,12 +440,12 @@ class ClassCarpenterTest {
@Suppress("UNCHECKED_CAST")
fun `string arrays`() {
val className = "iEnjoyToast"
val schema = ClassCarpenter.ClassSchema(
val schema = ClassSchema(
"gen.$className",
mapOf(
"a" to Array<String>::class.java,
"b" to String::class.java,
"c" to Array<String>::class.java).mapValues { ClassCarpenter.NullableField (it.value) })
"c" to Array<String>::class.java).mapValues { NullableField (it.value) })
val clazz = cc.build(schema)
@ -469,10 +469,10 @@ class ClassCarpenterTest {
@Test
fun `nullable sets annotations`() {
val className = "iEnjoyJam"
val schema = ClassCarpenter.ClassSchema(
val schema = ClassSchema(
"gen.$className",
mapOf("a" to ClassCarpenter.NullableField(String::class.java),
"b" to ClassCarpenter.NonNullableField(String::class.java)))
mapOf("a" to NullableField(String::class.java),
"b" to NonNullableField(String::class.java)))
val clazz = cc.build(schema)

View File

@ -145,7 +145,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
}
@Test
fun singleDouble() {
fun singleDouble() {
val test = 10.0
@CordaSerializable