package org.simpleflatmapper.csv.impl.asm;
import org.simpleflatmapper.ow2asm.ClassWriter;
import org.simpleflatmapper.ow2asm.FieldVisitor;
import org.simpleflatmapper.ow2asm.Label;
import org.simpleflatmapper.ow2asm.MethodVisitor;
import org.simpleflatmapper.ow2asm.Opcodes;
import org.simpleflatmapper.reflect.asm.AsmUtils;
import org.simpleflatmapper.reflect.asm.ShardingHelper;
import org.simpleflatmapper.csv.CsvColumnKey;
import org.simpleflatmapper.csv.ParsingContext;
import org.simpleflatmapper.csv.ParsingContextFactory;
import org.simpleflatmapper.map.FieldMapperErrorHandler;
import org.simpleflatmapper.reflect.Instantiator;
import org.simpleflatmapper.csv.mapper.CellSetter;
import org.simpleflatmapper.csv.mapper.CsvMapperCellHandler;
import org.simpleflatmapper.csv.mapper.CsvMapperCellHandlerFactory;
import org.simpleflatmapper.csv.mapper.DelayedCellSetter;
import org.simpleflatmapper.csv.mapper.DelayedCellSetterFactory;
import java.lang.reflect.Type;
@SuppressWarnings("SpellCheckingInspection")
public class CsvMapperCellHandlerBuilder {
public static final String DELAYED_CELL_SETTER_TYPE = AsmUtils.toAsmType(DelayedCellSetter.class);
public static final String CELL_SETTER_TYPE = AsmUtils.toAsmType(CellSetter.class);
public static final String CSV_CELL_MAPPER_TYPE = AsmUtils.toAsmType(CsvMapperCellHandler.class);
public static final String CELL_HANDLER_FACTORY_TYPE = AsmUtils.toAsmType(CsvMapperCellHandlerFactory.class);
public static <T> byte[] createTargetSetterClass(String className,
DelayedCellSetterFactory<T, ?>[] delayedCellSetters,
CellSetter<T>[] setters, Type type, boolean ignoreException, int maxMethodSize) throws Exception {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
FieldVisitor fv;
MethodVisitor mv;
final String targetType = AsmUtils.toAsmType(type);
final String classType = AsmUtils.toAsmType(className);
cw.visit(Opcodes.V1_6, Opcodes.ACC_FINAL + Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, classType,
"L" + CSV_CELL_MAPPER_TYPE + "<L" + targetType + ";>;", CSV_CELL_MAPPER_TYPE, null);
// declare fields
for(int i = 0; i < delayedCellSetters.length; i++) {
if (delayedCellSetters[i] != null) {
fv = cw.visitField(Opcodes.ACC_PROTECTED + Opcodes.ACC_FINAL, "delayedCellSetter" + i,
AsmUtils.toTargetTypeDeclaration(DELAYED_CELL_SETTER_TYPE),
"L" + DELAYED_CELL_SETTER_TYPE + "<L" + targetType + ";*>;", null);
fv.visitEnd();
}
}
for(int i = 0; i < setters.length; i++) {
if (setters[i] != null) {
fv = cw.visitField(Opcodes.ACC_PROTECTED + Opcodes.ACC_FINAL, "setter" + i,
AsmUtils.toTargetTypeDeclaration(CELL_SETTER_TYPE),
"L" + CELL_SETTER_TYPE + "<L" + targetType + ";>;", null);
fv.visitEnd();
}
}
appendInit(delayedCellSetters, setters, cw, targetType, classType, maxMethodSize);
appendDelayedCellValue(delayedCellSetters, ignoreException, cw, classType);
append_delayedCellValue(delayedCellSetters, cw, classType, maxMethodSize);
appendCellValue(setters, ignoreException, cw, classType);
append_cellValue(delayedCellSetters, setters, cw, classType, maxMethodSize);
appendApplyDelayedSetter(delayedCellSetters, ignoreException, cw, classType, maxMethodSize);
appendApplyDelayedCellSetterN(delayedCellSetters, cw, classType);
appendGetDelayedCellSetter(delayedCellSetters, cw, targetType, classType, maxMethodSize);
appendPeekDelayedCellSetterValue(delayedCellSetters, cw, classType, maxMethodSize);
cw.visitEnd();
return AsmUtils.writeClassToFile(className, cw.toByteArray());
}
private static <T> void append_cellValue(final DelayedCellSetterFactory<T, ?>[] delayedCellSetters, final CellSetter<T>[] setters, ClassWriter cw, final String classType, final int maxMethodSize) {
ShardingHelper.shard(setters.length, maxMethodSize, new AbstractMethodDispatchShardCallBack<T>(cw, classType, maxMethodSize) {
@Override
protected void appendLeafSwitch(MethodVisitor mv, int start, int end) {
Label defaultLabel = new Label();
Label[] labels = newLabels(end - start);
mv.visitTableSwitchInsn(delayedCellSetters.length + start, delayedCellSetters.length + end - 1, defaultLabel, labels);
for (int i = start; i < end; i++) {
mv.visitLabel(labels[i - start]);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
if (setters[i] != null) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, classType, "setter" + i, "L" + CELL_SETTER_TYPE + ";");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, classType, "currentInstance", "Ljava/lang/Object;");
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitVarInsn(Opcodes.ILOAD, 2);
mv.visitVarInsn(Opcodes.ILOAD, 3);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, classType, "parsingContext", AsmUtils.toTargetTypeDeclaration(ParsingContext.class));
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, CELL_SETTER_TYPE, "set", "(Ljava/lang/Object;[CIIL" + AsmUtils.toAsmType(ParsingContext.class) + ";)V", true);
}
if (i < (end - 1)) {
mv.visitJumpInsn(Opcodes.GOTO, defaultLabel);
}
}
mv.visitLabel(defaultLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
@Override
protected int maxArgIndex() {
return 4;
}
@Override
protected void loadArguments(MethodVisitor mv) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitVarInsn(Opcodes.ILOAD, 2);
mv.visitVarInsn(Opcodes.ILOAD, 3);
mv.visitVarInsn(Opcodes.ILOAD, 4);
}
@Override
protected int argIndex() {
return 4;
}
@Override
protected String name() {
return "_cellValue";
}
@Override
protected String signature() {
return "([CIII)V";
}
@Override
protected int leafStart() {
return delayedCellSetters.length;
}
});
}
private static <T> void appendPeekDelayedCellSetterValue(final DelayedCellSetterFactory<T, ?>[] delayedCellSetters, final ClassWriter cw, final String classType, final int maxMethodSize) {
ShardingHelper.shard(delayedCellSetters.length, maxMethodSize, new AbstractMethodDispatchShardCallBack<T>(cw, classType, maxMethodSize ) {
@Override
protected void appendLeafSwitch(MethodVisitor mv, int start, int end) {
Label defaultLabel = new Label();
Label[] labels = newLabels(end - start);
mv.visitTableSwitchInsn(start, end - 1, defaultLabel, labels);
for (int i = start; i < end; i++) {
mv.visitLabel(labels[i -start]);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
if (delayedCellSetters[i] != null) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, classType, "delayedCellSetter" + i, "L" + DELAYED_CELL_SETTER_TYPE + ";");
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, DELAYED_CELL_SETTER_TYPE, "peekValue", "()Ljava/lang/Object;", true);
mv.visitInsn(Opcodes.ARETURN);
} else if (i < (delayedCellSetters.length - 1)) {
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitInsn(Opcodes.ARETURN);
}
}
mv.visitLabel(defaultLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
@Override
protected int maxArgIndex() {
return 1;
}
@Override
protected void loadArguments(MethodVisitor mv) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, 1);
}
@Override
protected int argIndex() {
return 1;
}
@Override
protected String name() {
return "_peekDelayedCellSetterValue";
}
@Override
protected String signature() {
return "(I)Ljava/lang/Object;";
}
});
MethodVisitor mv;
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "peekDelayedCellSetterValue" , "(L" + AsmUtils.toAsmType(CsvColumnKey.class) + ";)Ljava/lang/Object;", null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, AsmUtils.toAsmType(CsvColumnKey.class), "getIndex", "()I", false);
mv.visitVarInsn(Opcodes.ISTORE, 2);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, 2);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, classType, "_peekDelayedCellSetterValue", "(I)Ljava/lang/Object;", false);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(1, 2);
mv.visitEnd();
}
private static <T> void append_delayedCellValue(final DelayedCellSetterFactory<T, ?>[] delayedCellSetters, final ClassWriter cw, final String classType, final int maxMethodSize) {
ShardingHelper.shard(delayedCellSetters.length, maxMethodSize, new AbstractMethodDispatchShardCallBack<T>(cw, classType, maxMethodSize ) {
@Override
protected void appendLeafSwitch(MethodVisitor mv, int start, int end) {
Label defaultLabel = new Label();
Label[] labels = newLabels(end - start);
mv.visitTableSwitchInsn(start, end - 1, defaultLabel, labels);
for (int i = start; i < end; i++) {
mv.visitLabel(labels[i - start]);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
if (delayedCellSetters[i] != null) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, classType, "delayedCellSetter" + i, "L" + DELAYED_CELL_SETTER_TYPE + ";");
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitVarInsn(Opcodes.ILOAD, 2);
mv.visitVarInsn(Opcodes.ILOAD, 3);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, classType, "parsingContext", AsmUtils.toTargetTypeDeclaration(ParsingContext.class));
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, DELAYED_CELL_SETTER_TYPE, "set", "([CIIL" + AsmUtils.toAsmType(ParsingContext.class) + ";)V", true);
}
if (i < (end - 1)) {
mv.visitJumpInsn(Opcodes.GOTO, defaultLabel);
}
}
mv.visitLabel(defaultLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); }
@Override
protected void loadArguments(MethodVisitor mv) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitVarInsn(Opcodes.ILOAD, 2);
mv.visitVarInsn(Opcodes.ILOAD, 3);
mv.visitVarInsn(Opcodes.ILOAD, 4);
}
@Override
protected int maxArgIndex() {
return 4;
}
@Override
protected int argIndex() {
return 4;
}
@Override
protected String name() {
return "_delayedCellValue";
}
@Override
protected String signature() {
return "([CIII)V";
}
});
}
private static <T> void appendApplyDelayedSetter(final DelayedCellSetterFactory<T, ?>[] delayedCellSetters, final boolean ignoreException, final ClassWriter cw, final String classType, final int maxMethodSize) {
ShardingHelper.shard(delayedCellSetters.length, maxMethodSize, new ShardingHelper.ShardCallBack() {
@Override
public void leafDispatch(String suffix, int start, int end) {
MethodVisitor mv;
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "applyDelayedSetters" + suffix, "()V", null, null);
mv.visitCode();
if (end - start > 0) {
if (!ignoreException) {
Label[] labels = newLabels((3 * getNbNonNullSettersWithSetters(delayedCellSetters, start, end)) + 1);
for (int i = start, j = 0; i < end; i++) {
if (delayedCellSetters[i] != null && delayedCellSetters[i].hasSetter()) {
mv.visitTryCatchBlock(labels[j], labels[j + 1], labels[j + 2], "java/lang/Exception");
j += 3;
}
}
for (int i = start, j = 0; i < end; i++) {
if (delayedCellSetters[i] != null && delayedCellSetters[i].hasSetter()) {
mv.visitLabel(labels[j]);
if (j > 0) {
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, classType, "applyDelayedCellSetter" + i, "()V", false);
mv.visitLabel(labels[j + 1]);
mv.visitJumpInsn(Opcodes.GOTO, labels[j + 3]);
mv.visitLabel(labels[j + 2]);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/Exception"});
mv.visitVarInsn(Opcodes.ASTORE, 1);
mv.visitVarInsn(Opcodes.ALOAD, 0);
AsmUtils.addIndex(mv, i);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, classType, "fieldError", "(ILjava/lang/Exception;)V", false);
j += 3;
}
}
mv.visitLabel(labels[labels.length - 1]);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
} else {
for (int i = start; i < end; i++) {
if (delayedCellSetters[i] != null && delayedCellSetters[i].hasSetter()) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, classType, "applyDelayedCellSetter" + i, "()V", false);
}
}
}
}
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(3, 2);
mv.visitEnd();
}
@Override
public void nodeDispatch(String suffix, int divide, int start, int end) {
MethodVisitor mv;
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "applyDelayedSetters" + suffix, "()V", null, new String[]{"java/lang/Exception"});
mv.visitCode();
final int powerOfTwo = Integer.numberOfTrailingZeros(divide);
int sStart = start >> powerOfTwo;
int sEnd = end >> powerOfTwo;
int left = end - (sEnd << powerOfTwo);
if (left > 0) {
sEnd ++;
}
for (int i = sStart; i < sEnd; i++) {
int estart = i * divide;
int eend = Math.min(end, (i+1) * divide);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, classType, "applyDelayedSetters"+ (divide/maxMethodSize) + "n" + estart + "t" + eend, "()V" , false);
}
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(6, 5);
mv.visitEnd();
}
});
}
private static Label[] newLabels(int ll) {
Label[] labels = new Label[ll];
for (int i = 0; i < labels.length; i++) {
labels[i] = new Label();
}
return labels;
}
private static <T> void appendApplyDelayedCellSetterN(DelayedCellSetterFactory<T, ?>[] delayedCellSetters, ClassWriter cw, String classType) {
MethodVisitor mv;
for(int i = 0; i < delayedCellSetters.length; i++) {
if (delayedCellSetters[i] != null) {
mv = cw.visitMethod(Opcodes.ACC_PRIVATE, "applyDelayedCellSetter" + i, "()V", null, new String[]{"java/lang/Exception"});
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, classType, "delayedCellSetter" + i, "L" + DELAYED_CELL_SETTER_TYPE + ";");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, classType, "currentInstance", "Ljava/lang/Object;");
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, DELAYED_CELL_SETTER_TYPE, "set", "(Ljava/lang/Object;)V", true);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
}
}
}
private static <T> void appendCellValue(CellSetter<T>[] setters, boolean ignoreException, ClassWriter cw, String classType) {
MethodVisitor mv;
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "cellValue", "([CIII)V", null, null);
mv.visitCode();
if (setters.length != 0) {
if (ignoreException) {
callCellValue(mv, classType);
} else {
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");
mv.visitLabel(l0);
callCellValue(mv, classType);
mv.visitLabel(l1);
Label l3 = new Label();
mv.visitJumpInsn(Opcodes.GOTO, l3);
mv.visitLabel(l2);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/Exception"});
mv.visitVarInsn(Opcodes.ASTORE, 5);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, 4);
mv.visitVarInsn(Opcodes.ALOAD, 5);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, classType, "fieldError", "(ILjava/lang/Exception;)V", false);
mv.visitLabel(l3);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
}
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(5, 6);
mv.visitEnd();
}
private static <T> void appendDelayedCellValue(DelayedCellSetterFactory<T, ?>[] delayedCellSetters, boolean ignoreException, ClassWriter cw, String classType) {
MethodVisitor mv;
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "delayedCellValue", "([CIII)V", null, null);
mv.visitCode();
if (delayedCellSetters.length != 0) {
if (ignoreException) {
callDelayedCellValue(mv, classType);
} else {
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");
mv.visitLabel(l0);
callDelayedCellValue(mv, classType);
mv.visitLabel(l1);
Label l3 = new Label();
mv.visitJumpInsn(Opcodes.GOTO, l3);
mv.visitLabel(l2);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/Exception"});
mv.visitVarInsn(Opcodes.ASTORE, 5);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, 4);
mv.visitVarInsn(Opcodes.ALOAD, 5);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, classType, "fieldError", "(ILjava/lang/Exception;)V", false);
mv.visitLabel(l3);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
}
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(5, 6);
mv.visitEnd();
}
private static <T> void appendInit(DelayedCellSetterFactory<T, ?>[] delayedCellSetters, CellSetter<T>[] setters, ClassWriter cw, String targetType, String classType, int maxSize) {
MethodVisitor mv;// constructor
{
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "(" +
AsmUtils.toTargetTypeDeclaration(Instantiator.class) +
AsmUtils.toTargetTypeDeclaration(DelayedCellSetter[].class) +
AsmUtils.toTargetTypeDeclaration(CellSetter[].class) +
AsmUtils.toTargetTypeDeclaration(CsvColumnKey[].class) +
AsmUtils.toTargetTypeDeclaration(ParsingContext.class) +
AsmUtils.toTargetTypeDeclaration(FieldMapperErrorHandler.class) +
")V",
"(" +
"L" + AsmUtils.toAsmType(Instantiator.class) +"<L" + AsmUtils.toAsmType(CsvMapperCellHandler.class)+ "<L" + targetType + ";>;L"+ targetType + ";>;" +
"[L" + DELAYED_CELL_SETTER_TYPE + "<L" + targetType + ";*>;" +
"[L" + CELL_SETTER_TYPE + "<L" + targetType + ";>;" +
AsmUtils.toTargetTypeDeclaration(CsvColumnKey[].class) +
AsmUtils.toTargetTypeDeclaration(ParsingContext.class) +
"L" + AsmUtils.toAsmType(FieldMapperErrorHandler.class) + "<L" + AsmUtils.toAsmType(CsvColumnKey.class) + ";>;" +
")V", null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitVarInsn(Opcodes.ALOAD, 4);
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitInsn(Opcodes.ARRAYLENGTH);
mv.visitVarInsn(Opcodes.ALOAD, 3);
mv.visitInsn(Opcodes.ARRAYLENGTH);
mv.visitVarInsn(Opcodes.ALOAD, 5);
mv.visitVarInsn(Opcodes.ALOAD, 6);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, CSV_CELL_MAPPER_TYPE, "<init>",
"(" +
AsmUtils.toTargetTypeDeclaration(Instantiator.class) +
AsmUtils.toTargetTypeDeclaration(CsvColumnKey[].class) +
"I" +
"I" +
AsmUtils.toTargetTypeDeclaration(ParsingContext.class) +
AsmUtils.toTargetTypeDeclaration(FieldMapperErrorHandler.class) +
")V", false);
ShardingHelper.shard(delayedCellSetters.length, maxSize, new ShardingHelper.ShardCallBack() {
@Override
public void leafDispatch(String suffix, int start, int end) {
}
@Override
public void nodeDispatch(String suffix, int divide, int start, int end) {
}
});
for(int i = 0; i < delayedCellSetters.length; i++) {
if (delayedCellSetters[i] != null) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 2);
AsmUtils.addIndex(mv, i);
mv.visitInsn(Opcodes.AALOAD);
mv.visitFieldInsn(Opcodes.PUTFIELD,
classType,
"delayedCellSetter" + i,
AsmUtils.toTargetTypeDeclaration(DELAYED_CELL_SETTER_TYPE));
}
}
for(int i = 0; i < setters.length; i++) {
if (setters[i] != null) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 3);
AsmUtils.addIndex(mv, i);
mv.visitInsn(Opcodes.AALOAD);
mv.visitFieldInsn(Opcodes.PUTFIELD,
classType,
"setter" + i ,
AsmUtils.toTargetTypeDeclaration(CELL_SETTER_TYPE));
}
}
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(7, 7);
mv.visitEnd();
}
}
private static <T> void appendGetDelayedCellSetter(DelayedCellSetterFactory<T, ?>[] delayedCellSetters, ClassWriter cw, String targetType, String classType, int maxMethodSize) {
MethodVisitor mv;
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "getDelayedCellSetter", "(I)L" + DELAYED_CELL_SETTER_TYPE + ";",
"(I)L" + DELAYED_CELL_SETTER_TYPE + "<L" + targetType + ";*>;", null);
mv.visitCode();
if (delayedCellSetters.length != 0) {
final int switchStart = 0;
final int switchEnd = delayedCellSetters.length;
appendGetDelayedCellSetterSwitch(delayedCellSetters, classType, mv, switchStart, switchEnd);
}
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(1, 2);
mv.visitEnd();
}
private static <T> void appendGetDelayedCellSetterSwitch(DelayedCellSetterFactory<T, ?>[] delayedCellSetters, String classType, MethodVisitor mv, int switchStart, int switchEnd) {
mv.visitVarInsn(Opcodes.ILOAD, 1);
Label defaultLabel = new Label();
Label[] labels = newLabels(switchEnd - switchStart);
mv.visitTableSwitchInsn(switchStart, switchEnd - 1, defaultLabel, labels);
for (int i = switchStart; i < switchEnd; i++) {
mv.visitLabel(labels[i - switchStart]);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
if (delayedCellSetters != null) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, classType, "delayedCellSetter" + i, "L" + DELAYED_CELL_SETTER_TYPE + ";");
} else {
mv.visitInsn(Opcodes.ACONST_NULL);
}
mv.visitInsn(Opcodes.ARETURN);
}
mv.visitLabel(defaultLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
private static void callCellValue(MethodVisitor mv, String classType) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitVarInsn(Opcodes.ILOAD, 2);
mv.visitVarInsn(Opcodes.ILOAD, 3);
mv.visitVarInsn(Opcodes.ILOAD, 4);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, classType, "_cellValue", "([CIII)V", false);
}
private static void callDelayedCellValue(MethodVisitor mv, String classType) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitVarInsn(Opcodes.ILOAD, 2);
mv.visitVarInsn(Opcodes.ILOAD, 3);
mv.visitVarInsn(Opcodes.ILOAD, 4);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, classType, "_delayedCellValue", "([CIII)V", false);
}
private static <T> int getNbNonNullSettersWithSetters(DelayedCellSetterFactory<T, ?>[] delayedCellSetters, int start, int end) {
int n = 0;
for(int i = start; i < end; i++) {
if (delayedCellSetters[i] != null && delayedCellSetters[i].hasSetter()) n++;
}
return n;
}
public static byte[] createTargetSetterFactory(String factoryName, String className, Type target) throws Exception {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
MethodVisitor mv;
String factoryType = AsmUtils.toAsmType(factoryName);
String classType = AsmUtils.toAsmType(className);
String targetType = AsmUtils.toAsmType(target);
cw.visit(Opcodes.V1_6, Opcodes.ACC_FINAL + Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER,
factoryType,
"L" + CELL_HANDLER_FACTORY_TYPE + "<L" + targetType + ";>;",
CELL_HANDLER_FACTORY_TYPE, null);
{
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "(" +
AsmUtils.toTargetTypeDeclaration(Instantiator.class) +
AsmUtils.toTargetTypeDeclaration(CsvColumnKey[].class) +
AsmUtils.toTargetTypeDeclaration(ParsingContextFactory.class) +
AsmUtils.toTargetTypeDeclaration(FieldMapperErrorHandler.class) +
")V",
"(" +
"L"+ AsmUtils.toAsmType(Instantiator.class) +
"<L" + AsmUtils.toAsmType(CsvMapperCellHandler.class) + "<L"+ targetType + ";>;L" + targetType + ";>;" +
AsmUtils.toTargetTypeDeclaration(CsvColumnKey[].class) +
AsmUtils.toTargetTypeDeclaration(ParsingContextFactory.class) +
"L" + AsmUtils.toAsmType(FieldMapperErrorHandler.class) + "<L" + AsmUtils.toAsmType(CsvColumnKey.class) + ";>;" +
")V", null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitVarInsn(Opcodes.ALOAD, 3);
mv.visitVarInsn(Opcodes.ALOAD, 4);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, CELL_HANDLER_FACTORY_TYPE, "<init>",
"(" +
AsmUtils.toTargetTypeDeclaration(Instantiator.class) +
AsmUtils.toTargetTypeDeclaration(CsvColumnKey[].class) +
AsmUtils.toTargetTypeDeclaration(ParsingContextFactory.class) +
AsmUtils.toTargetTypeDeclaration(FieldMapperErrorHandler.class) +
")V", false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(5, 5);
mv.visitEnd();
}
{
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "newInstance", "(" +
AsmUtils.toTargetTypeDeclaration(DelayedCellSetter[].class) +
AsmUtils.toTargetTypeDeclaration(CellSetter[].class) +
")" + AsmUtils.toTargetTypeDeclaration(CsvMapperCellHandler.class),
"(" +
"[L" + DELAYED_CELL_SETTER_TYPE + "<L" + targetType + ";*>;" +
"[L" + CELL_SETTER_TYPE + "<L" + targetType + ";>;" +
")" +
"L" + AsmUtils.toAsmType(CsvMapperCellHandler.class) + "<L" + targetType + ";>;", null);
mv.visitCode();
mv.visitTypeInsn(Opcodes.NEW, classType);
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, factoryType, "instantiator", AsmUtils.toTargetTypeDeclaration(Instantiator.class));
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, factoryType, "keys", AsmUtils.toTargetTypeDeclaration(CsvColumnKey[].class));
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, factoryType, "parsingContextFactory", AsmUtils.toTargetTypeDeclaration(ParsingContextFactory.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, AsmUtils.toAsmType(ParsingContextFactory.class), "newContext", "()" + AsmUtils.toTargetTypeDeclaration(ParsingContext.class), false);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, factoryType, "fieldErrorHandler", AsmUtils.toTargetTypeDeclaration(FieldMapperErrorHandler.class));
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, classType, "<init>",
"(" +
AsmUtils.toTargetTypeDeclaration(Instantiator.class) +
AsmUtils.toTargetTypeDeclaration(DelayedCellSetter[].class) +
AsmUtils.toTargetTypeDeclaration(CellSetter[].class) +
AsmUtils.toTargetTypeDeclaration(CsvColumnKey[].class) +
AsmUtils.toTargetTypeDeclaration(ParsingContext.class) +
AsmUtils.toTargetTypeDeclaration(FieldMapperErrorHandler.class) +
")V", false);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(8, 3);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
private static abstract class AbstractMethodDispatchShardCallBack<T> implements ShardingHelper.ShardCallBack {
private final ClassWriter cw;
private final String classType;
private final int maxMethodSize;
public AbstractMethodDispatchShardCallBack(ClassWriter cw, String classType, int maxMethodSize) {
this.cw = cw;
this.classType = classType;
this.maxMethodSize = maxMethodSize;
}
@Override
public void leafDispatch(String suffix, int start, int end) {
MethodVisitor mv;
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, name() + suffix, signature(), null, null);
mv.visitCode();
if (end - start > 0) {
mv.visitVarInsn(Opcodes.ILOAD, argIndex());
appendLeafSwitch(mv, start, end);
}
if (isVoid()) {
mv.visitInsn(Opcodes.RETURN);
} else {
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitInsn(Opcodes.ARETURN);
}
mv.visitMaxs(1, 2);
mv.visitEnd();
}
protected abstract void appendLeafSwitch(MethodVisitor mv, int start, int end);
@Override
public void nodeDispatch(String suffix, int divide, int start, int end) {
MethodVisitor mv;
mv = cw.visitMethod(Opcodes.ACC_PRIVATE, name() + suffix, signature(), null, null);
mv.visitCode();
Label startLabel = new Label();
mv.visitLabel(startLabel);
final int powerOfTwo = Integer.numberOfTrailingZeros(divide);
int sStart = start >> powerOfTwo;
int sEnd = end >> powerOfTwo;
int left = end - (sEnd << powerOfTwo);
if (left > 0) {
sEnd ++;
}
Label[] labels = newLabels(sEnd - sStart);
Label defaultLabel = new Label();
mv.visitVarInsn(Opcodes.ILOAD, argIndex());
int sub = leafStart();
if (sub != 0) {
AsmUtils.addIndex(mv, sub);
mv.visitInsn(Opcodes.ISUB);
}
AsmUtils.addIndex(mv, powerOfTwo);
mv.visitInsn(Opcodes.ISHR);
mv.visitVarInsn(Opcodes.ISTORE, maxArgIndex() + 1);
mv.visitVarInsn(Opcodes.ILOAD, maxArgIndex() + 1);
mv.visitTableSwitchInsn(sStart, sEnd - 1, defaultLabel, labels);
for (int i = sStart; i < sEnd; i++) {
int estart = i << powerOfTwo;
int eend = Math.min(end, (i+1) << powerOfTwo);
mv.visitLabel(labels[i - sStart]);
if ( i == start) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null);
} else {
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
loadArguments(mv);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, classType, name() + (divide / maxMethodSize) + "n" + estart + "t" + eend, signature(), false);
if (isVoid()) {
if (i < (sEnd - 1)) {
mv.visitJumpInsn(Opcodes.GOTO, defaultLabel);
}
} else {
mv.visitInsn(Opcodes.ARETURN);
}
}
mv.visitLabel(defaultLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
if (isVoid()) {
mv.visitInsn(Opcodes.RETURN);
} else {
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitInsn(Opcodes.ARETURN);
}
Label endLabel = new Label();
mv.visitLabel(endLabel);
appendDebugInfo(mv, startLabel, endLabel);
mv.visitMaxs(6, 5);
mv.visitEnd();
}
protected int leafStart() {
return 0;
}
protected abstract int maxArgIndex();
protected void appendDebugInfo(MethodVisitor mv, Label startLabel, Label endLabel) {
}
protected abstract void loadArguments(MethodVisitor mv);
protected abstract int argIndex();
private boolean isVoid() {
return signature().endsWith("V");
}
protected abstract String name();
protected abstract String signature();
}
}