Skip to content
This repository has been archived by the owner on Nov 5, 2019. It is now read-only.

Commit

Permalink
Fix #28 for 2.3.3
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Apr 9, 2014
1 parent 861cbc0 commit c213798
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 66 deletions.
3 changes: 2 additions & 1 deletion release-notes/VERSION
Original file line number Diff line number Diff line change
@@ -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: ===
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, superClass, "<init>", "()V");
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
// then single-arg constructor
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "("+stdValueInstDesc+")V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKESPECIAL, superClass, "<init>", "("+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, "<init>", "("+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, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, superClass, "<init>", "()V");
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
// then single-arg constructor
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "("+stdValueInstDesc+")V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKESPECIAL, superClass, "<init>", "("+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, "<init>", "("+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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}

}

0 comments on commit c213798

Please sign in to comment.