From c213798e7219f3602df9a2c66ba371b4af8d6e2a Mon Sep 17 00:00:00 2001 From: Tatu Date: Wed, 9 Apr 2014 11:08:26 -0700 Subject: [PATCH] Fix #28 for 2.3.3 --- release-notes/VERSION | 3 +- .../afterburner/deser/CreatorOptimizer.java | 128 +++++++++--------- .../afterburner/deser/TestSingleArgCtors.java | 39 ++++++ 3 files changed, 104 insertions(+), 66 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/module/afterburner/deser/TestSingleArgCtors.java diff --git a/release-notes/VERSION b/release-notes/VERSION index 9922c90..e1a97e4 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -1,7 +1,8 @@ Project: jackson-module-afterburner Version: 2.3.3 (xx-xxx-2014) -No changes since 2.3.2. +#28: JsonCreator deser with single-String-argument constructor fails + (reported by Scott M, sgmiller@github) ------------------------------------------------------------------------ === History: === diff --git a/src/main/java/com/fasterxml/jackson/module/afterburner/deser/CreatorOptimizer.java b/src/main/java/com/fasterxml/jackson/module/afterburner/deser/CreatorOptimizer.java index 84544ec..fe38058 100644 --- a/src/main/java/com/fasterxml/jackson/module/afterburner/deser/CreatorOptimizer.java +++ b/src/main/java/com/fasterxml/jackson/module/afterburner/deser/CreatorOptimizer.java @@ -59,21 +59,21 @@ public ValueInstantiator createOptimized() // First things first: as per [Issue#34], can NOT access private ctors or methods Constructor ctor = (Constructor) elem; if (!Modifier.isPrivate(ctor.getModifiers())) { - return createSubclass(ctor, null); + return createSubclass(ctor, null).with(_originalInstantiator); } } else if (elem instanceof Method) { Method m = (Method) elem; int mods = m.getModifiers(); // and as above, can't access private ones if (Modifier.isStatic(mods) && !Modifier.isPrivate(mods)) { - return createSubclass(null, m); + return createSubclass(null, m).with(_originalInstantiator); } } } return null; } - protected ValueInstantiator createSubclass(Constructor ctor, Method factory) + protected OptimizedValueInstantiator createSubclass(Constructor ctor, Method factory) { MyClassLoader loader = (_classLoader == null) ? new MyClassLoader(_valueClass.getClassLoader(), true) : _classLoader; @@ -86,77 +86,75 @@ protected ValueInstantiator createSubclass(Constructor ctor, Method factory) byte[] bytecode = generateOptimized(srcName, ctor, factory); impl = loader.loadAndResolve(srcName, bytecode); } - ValueInstantiator inst; try { - inst = (ValueInstantiator) impl.newInstance(); + return (OptimizedValueInstantiator) impl.newInstance(); } catch (Exception e) { throw new IllegalStateException("Failed to generate accessor class '"+srcName+"': "+e.getMessage(), e); } - return inst; } protected byte[] generateOptimized(String srcName, Constructor ctor, Method factory) { - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); - String superClass = internalClassName(OptimizedValueInstantiator.class.getName()); - String generatedClass = internalClassName(srcName); - - cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, generatedClass, null, superClass, null); - cw.visitSource(srcName + ".java", null); - - // First: must define 2 constructors: - // (a) default constructor, for creating bogus instance (just calls default instance) - // (b) copy-constructor which takes StdValueInstantiator instance, passes to superclass - final String optimizedValueInstDesc = Type.getDescriptor(OptimizedValueInstantiator.class); - final String stdValueInstDesc = Type.getDescriptor(StdValueInstantiator.class); - - // default (no-arg) constructor: - MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); - mv.visitCode(); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, superClass, "", "()V"); - mv.visitInsn(RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - // then single-arg constructor - mv = cw.visitMethod(ACC_PUBLIC, "", "("+stdValueInstDesc+")V", null, null); - mv.visitCode(); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKESPECIAL, superClass, "", "("+stdValueInstDesc+")V"); - mv.visitInsn(RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - - // and then non-static factory method to use second constructor (implements base-class method) - // protected abstract OptimizedValueInstantiator with(StdValueInstantiator src); - mv = cw.visitMethod(ACC_PUBLIC, "with", "(" - +stdValueInstDesc+")"+optimizedValueInstDesc, null, null); - mv.visitCode(); - mv.visitTypeInsn(NEW, generatedClass); - mv.visitInsn(DUP); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKESPECIAL, generatedClass, "", "("+stdValueInstDesc+")V"); - mv.visitInsn(ARETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - - // And then override: public Object createUsingDefault() - mv = cw.visitMethod(ACC_PUBLIC, "createUsingDefault", "(" + - Type.getDescriptor(DeserializationContext.class)+")Ljava/lang/Object;", null, null); - mv.visitCode(); - - if (ctor != null) { - addCreator(mv, ctor); - } else { - addCreator(mv, factory); - } - mv.visitInsn(ARETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + String superClass = internalClassName(OptimizedValueInstantiator.class.getName()); + String generatedClass = internalClassName(srcName); + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, generatedClass, null, superClass, null); + cw.visitSource(srcName + ".java", null); + + // First: must define 2 constructors: + // (a) default constructor, for creating bogus instance (just calls default instance) + // (b) copy-constructor which takes StdValueInstantiator instance, passes to superclass + final String optimizedValueInstDesc = Type.getDescriptor(OptimizedValueInstantiator.class); + final String stdValueInstDesc = Type.getDescriptor(StdValueInstantiator.class); + + // default (no-arg) constructor: + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, superClass, "", "()V"); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + // then single-arg constructor + mv = cw.visitMethod(ACC_PUBLIC, "", "("+stdValueInstDesc+")V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKESPECIAL, superClass, "", "("+stdValueInstDesc+")V"); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + // and then non-static factory method to use second constructor (implements base-class method) + // protected abstract OptimizedValueInstantiator with(StdValueInstantiator src); + mv = cw.visitMethod(ACC_PUBLIC, "with", "(" + +stdValueInstDesc+")"+optimizedValueInstDesc, null, null); + mv.visitCode(); + mv.visitTypeInsn(NEW, generatedClass); + mv.visitInsn(DUP); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKESPECIAL, generatedClass, "", "("+stdValueInstDesc+")V"); + mv.visitInsn(ARETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + // And then override: public Object createUsingDefault() + mv = cw.visitMethod(ACC_PUBLIC, "createUsingDefault", "(" + + Type.getDescriptor(DeserializationContext.class)+")Ljava/lang/Object;", null, null); + mv.visitCode(); + + if (ctor != null) { + addCreator(mv, ctor); + } else { + addCreator(mv, factory); + } + mv.visitInsn(ARETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); - cw.visitEnd(); - return cw.toByteArray(); + cw.visitEnd(); + return cw.toByteArray(); } protected void addCreator(MethodVisitor mv, Constructor ctor) diff --git a/src/test/java/com/fasterxml/jackson/module/afterburner/deser/TestSingleArgCtors.java b/src/test/java/com/fasterxml/jackson/module/afterburner/deser/TestSingleArgCtors.java new file mode 100644 index 0000000..871239c --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/module/afterburner/deser/TestSingleArgCtors.java @@ -0,0 +1,39 @@ +package com.fasterxml.jackson.module.afterburner.deser; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.afterburner.AfterburnerTestBase; + +public class TestSingleArgCtors extends AfterburnerTestBase +{ + static class Node { + public String name; + + public int value; + + public Node() { } + + @JsonCreator + public Node(String n) { + name = n; + value = -1; + } + } + + /* + /********************************************************** + /* Test methods + /********************************************************** + */ + + private final ObjectMapper MAPPER = mapperWithModule(); + + public void testSingleStringArgCtor() throws Exception + { + Node bean = MAPPER.readValue(quote("Foobar"), Node.class); + assertNotNull(bean); + assertEquals(-1, bean.value); + assertEquals("Foobar", bean.name); + } + +}