| // Copyright 2009 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.gwtorm.protobuf; |
| |
| import com.google.gwtorm.client.Column; |
| import com.google.gwtorm.schema.ColumnModel; |
| import com.google.gwtorm.schema.Util; |
| import com.google.gwtorm.schema.java.JavaColumnModel; |
| import com.google.gwtorm.server.CodeGenSupport; |
| import com.google.gwtorm.server.GeneratedClassLoader; |
| import com.google.gwtorm.server.OrmException; |
| import com.google.protobuf.ByteString; |
| import com.google.protobuf.CodedInputStream; |
| import com.google.protobuf.CodedOutputStream; |
| import com.google.protobuf.WireFormat; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.TreeSet; |
| import org.objectweb.asm.ClassWriter; |
| import org.objectweb.asm.Label; |
| import org.objectweb.asm.MethodVisitor; |
| import org.objectweb.asm.Opcodes; |
| import org.objectweb.asm.Type; |
| |
| /** Generates {@link ProtobufCodec} implementations. */ |
| class CodecGen<T> implements Opcodes { |
| private static final Type illegalStateException = Type.getType(IllegalStateException.class); |
| private static final Type collection = Type.getType(java.util.Collection.class); |
| private static final Type iterator = Type.getType(java.util.Iterator.class); |
| private static final Type string = Type.getType(String.class); |
| private static final Type enumType = Type.getType(Enum.class); |
| private static final Type byteString = Type.getType(ByteString.class); |
| private static final Type object = Type.getType(Object.class); |
| private static final Type codedOutputStream = Type.getType(CodedOutputStream.class); |
| private static final Type codedInputStream = Type.getType(CodedInputStream.class); |
| private final GeneratedClassLoader classLoader; |
| private final Class<T> pojo; |
| private final Type pojoType; |
| |
| private ClassWriter cw; |
| private JavaColumnModel[] myFields; |
| private String superTypeName; |
| private String implClassName; |
| private String implTypeName; |
| |
| private Map<Class<?>, NestedCodec> nestedCodecs; |
| |
| public CodecGen(final GeneratedClassLoader loader, final Class<T> t) { |
| classLoader = loader; |
| pojo = t; |
| pojoType = Type.getType(pojo); |
| nestedCodecs = new HashMap<>(); |
| } |
| |
| public ProtobufCodec<T> create() throws OrmException { |
| myFields = scanFields(pojo); |
| |
| init(); |
| implementNewInstanceObject(); |
| implementNewInstanceSelf(); |
| |
| implementSizeofObject(); |
| implementSizeofSelf(); |
| |
| implementEncodeObject(); |
| implementEncodeSelf(); |
| |
| implementMergeFromObject(); |
| implementMergeFromSelf(); |
| |
| implementCodecFields(); |
| implementStaticInit(); |
| implementConstructor(); |
| cw.visitEnd(); |
| classLoader.defineClass(implClassName, cw.toByteArray()); |
| |
| try { |
| final Class<?> c = Class.forName(implClassName, true, classLoader); |
| return cast(c.newInstance()); |
| } catch (InstantiationException e) { |
| throw new OrmException("Cannot create new encoder", e); |
| } catch (IllegalAccessException e) { |
| throw new OrmException("Cannot create new encoder", e); |
| } catch (ClassNotFoundException e) { |
| throw new OrmException("Cannot create new encoder", e); |
| } |
| } |
| |
| private static JavaColumnModel[] scanFields(Class<?> in) throws OrmException { |
| final Collection<JavaColumnModel> col = new ArrayList<>(); |
| while (in != null) { |
| for (final Field f : JavaColumnModel.getDeclaredFields(in)) { |
| if (f.getAnnotation(Column.class) != null) { |
| col.add(new JavaColumnModel(f)); |
| } |
| } |
| in = in.getSuperclass(); |
| } |
| if (col.isEmpty()) { |
| throw new OrmException("Cannot create new encoder, no @Column fields found"); |
| } |
| return sort(col); |
| } |
| |
| private static JavaColumnModel[] sort(final Collection<? extends ColumnModel> col) { |
| JavaColumnModel[] out = col.toArray(new JavaColumnModel[col.size()]); |
| Arrays.sort( |
| out, |
| new Comparator<JavaColumnModel>() { |
| @Override |
| public int compare(JavaColumnModel o1, JavaColumnModel o2) { |
| return o1.getColumnID() - o2.getColumnID(); |
| } |
| }); |
| return out; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private static <T> ProtobufCodec<T> cast(final Object c) { |
| return (ProtobufCodec<T>) c; |
| } |
| |
| private void init() { |
| superTypeName = Type.getInternalName(ProtobufCodec.class); |
| implClassName = pojo.getName() + "_protobuf_" + Util.createRandomName(); |
| implTypeName = implClassName.replace('.', '/'); |
| |
| cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); |
| cw.visit( |
| V1_3, |
| ACC_PUBLIC | ACC_FINAL | ACC_SUPER, |
| implTypeName, |
| null, |
| superTypeName, |
| new String[] {}); |
| } |
| |
| private void implementCodecFields() { |
| for (NestedCodec other : nestedCodecs.values()) { |
| cw.visitField( |
| ACC_PRIVATE | ACC_STATIC | ACC_FINAL, |
| other.field, |
| other.codecType.getDescriptor(), |
| null, |
| null) |
| .visitEnd(); |
| } |
| } |
| |
| private void implementStaticInit() { |
| final MethodVisitor mv = |
| cw.visitMethod( |
| ACC_PUBLIC | ACC_STATIC, |
| "<clinit>", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), |
| null, |
| null); |
| mv.visitCode(); |
| |
| for (NestedCodec other : nestedCodecs.values()) { |
| mv.visitTypeInsn(NEW, other.codecType.getInternalName()); |
| mv.visitInsn(DUP); |
| mv.visitMethodInsn( |
| INVOKESPECIAL, |
| other.codecType.getInternalName(), |
| "<init>", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {})); |
| mv.visitFieldInsn(PUTSTATIC, implTypeName, other.field, other.codecType.getDescriptor()); |
| } |
| |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(-1, -1); |
| mv.visitEnd(); |
| } |
| |
| private void implementConstructor() { |
| final String consName = "<init>"; |
| final String consDesc = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}); |
| final MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, consName, consDesc, null, null); |
| mv.visitCode(); |
| |
| mv.visitVarInsn(ALOAD, 0); |
| mv.visitMethodInsn(INVOKESPECIAL, superTypeName, consName, consDesc); |
| |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(-1, -1); |
| mv.visitEnd(); |
| } |
| |
| private void implementNewInstanceObject() { |
| final MethodVisitor mv = |
| cw.visitMethod( |
| ACC_PUBLIC, |
| "newInstance", |
| Type.getMethodDescriptor(object, new Type[] {}), |
| null, |
| new String[] {}); |
| mv.visitCode(); |
| |
| mv.visitTypeInsn(NEW, pojoType.getInternalName()); |
| mv.visitInsn(DUP); |
| mv.visitMethodInsn( |
| INVOKESPECIAL, |
| pojoType.getInternalName(), |
| "<init>", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {})); |
| |
| mv.visitInsn(ARETURN); |
| mv.visitMaxs(-1, -1); |
| mv.visitEnd(); |
| } |
| |
| private void implementNewInstanceSelf() { |
| final MethodVisitor mv = |
| cw.visitMethod( |
| ACC_PUBLIC, |
| "newInstance", |
| Type.getMethodDescriptor(pojoType, new Type[] {}), |
| null, |
| new String[] {}); |
| mv.visitCode(); |
| |
| mv.visitTypeInsn(NEW, pojoType.getInternalName()); |
| mv.visitInsn(DUP); |
| mv.visitMethodInsn( |
| INVOKESPECIAL, |
| pojoType.getInternalName(), |
| "<init>", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {})); |
| |
| mv.visitInsn(ARETURN); |
| mv.visitMaxs(-1, -1); |
| mv.visitEnd(); |
| } |
| |
| private void implementSizeofObject() { |
| final MethodVisitor mv = |
| cw.visitMethod( |
| ACC_PUBLIC, |
| "sizeof", |
| Type.getMethodDescriptor(Type.INT_TYPE, new Type[] {object}), |
| null, |
| new String[] {}); |
| mv.visitCode(); |
| final SizeofCGS cgs = new SizeofCGS(mv); |
| cgs.sizeVar = cgs.newLocal(); |
| cgs.setEntityType(pojoType); |
| |
| mv.visitVarInsn(ALOAD, 0); |
| mv.visitVarInsn(ALOAD, 1); |
| mv.visitTypeInsn(CHECKCAST, pojoType.getInternalName()); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| implTypeName, |
| "sizeof", |
| Type.getMethodDescriptor(Type.INT_TYPE, new Type[] {pojoType})); |
| |
| mv.visitInsn(IRETURN); |
| mv.visitMaxs(-1, -1); |
| mv.visitEnd(); |
| } |
| |
| private void implementSizeofSelf() throws OrmException { |
| final MethodVisitor mv = |
| cw.visitMethod( |
| ACC_PUBLIC, |
| "sizeof", |
| Type.getMethodDescriptor(Type.INT_TYPE, new Type[] {pojoType}), |
| null, |
| new String[] {}); |
| mv.visitCode(); |
| final SizeofCGS cgs = new SizeofCGS(mv); |
| cgs.sizeVar = cgs.newLocal(); |
| cgs.setEntityType(pojoType); |
| |
| cgs.push(0); |
| mv.visitVarInsn(ISTORE, cgs.sizeVar); |
| sizeofMessage(myFields, mv, cgs); |
| |
| mv.visitVarInsn(ILOAD, cgs.sizeVar); |
| mv.visitInsn(IRETURN); |
| mv.visitMaxs(-1, -1); |
| mv.visitEnd(); |
| } |
| |
| private void sizeofMessage( |
| final JavaColumnModel[] myFields, final MethodVisitor mv, final SizeofCGS cgs) |
| throws OrmException { |
| for (final JavaColumnModel f : myFields) { |
| if (f.isNested()) { |
| final NestedCodec n = nestedFor(f); |
| final Label end = new Label(); |
| cgs.setFieldReference(f); |
| cgs.pushFieldValue(); |
| mv.visitJumpInsn(IFNULL, end); |
| |
| final int msgSizeVar = cgs.newLocal(); |
| mv.visitFieldInsn(GETSTATIC, implTypeName, n.field, n.codecType.getDescriptor()); |
| cgs.pushFieldValue(); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| n.codecType.getInternalName(), |
| "sizeof", |
| Type.getMethodDescriptor(Type.INT_TYPE, new Type[] {n.pojoType})); |
| mv.visitVarInsn(ISTORE, msgSizeVar); |
| |
| cgs.preinc(); |
| cgs.push(f.getColumnID()); |
| cgs.doinc("computeTagSize", Type.INT_TYPE); |
| |
| cgs.preinc(); |
| mv.visitVarInsn(ILOAD, msgSizeVar); |
| cgs.doinc("computeRawVarint32Size", Type.INT_TYPE); |
| |
| cgs.preinc(); |
| mv.visitVarInsn(ILOAD, msgSizeVar); |
| cgs.doinc(); |
| |
| cgs.freeLocal(msgSizeVar); |
| mv.visitLabel(end); |
| |
| } else if (f.isCollection()) { |
| sizeofCollection(f, mv, cgs); |
| |
| } else { |
| sizeofScalar(mv, cgs, f); |
| } |
| } |
| } |
| |
| @SuppressWarnings({"unchecked", "rawtypes"}) |
| private NestedCodec nestedFor(JavaColumnModel f) { |
| Class clazz = f.getNestedClass(); |
| NestedCodec n = nestedCodecs.get(clazz); |
| if (n == null) { |
| Class<? extends ProtobufCodec> codec = null; |
| Type type = Type.getType(clazz); |
| if (f.getField() != null) { |
| final CustomCodec cc = f.getField().getAnnotation(CustomCodec.class); |
| if (cc != null) { |
| codec = cc.value(); |
| type = object; |
| } |
| } |
| if (codec == null) { |
| codec = CodecFactory.encoder(clazz).getClass(); |
| } |
| |
| n = new NestedCodec("codec" + f.getColumnID(), codec, type); |
| nestedCodecs.put(clazz, n); |
| } |
| return n; |
| } |
| |
| private void sizeofCollection( |
| final JavaColumnModel f, final MethodVisitor mv, final SizeofCGS cgs) throws OrmException { |
| final int itr = cgs.newLocal(); |
| final int val = cgs.newLocal(); |
| final Class<?> valClazz = (Class<?>) f.getArgumentTypes()[0]; |
| final Type valType = Type.getType(valClazz); |
| final JavaColumnModel col = collectionColumn(f, valClazz); |
| final SizeofCGS ng = |
| new SizeofCGS(mv) { |
| { |
| sizeVar = cgs.sizeVar; |
| setEntityType(valType); |
| } |
| |
| @Override |
| public void pushEntity() { |
| mv.visitVarInsn(ALOAD, val); |
| } |
| |
| @Override |
| protected void appendGetField(final ColumnModel c) { |
| if (c != col) { |
| super.appendGetField(c); |
| } |
| } |
| |
| @Override |
| public int newLocal() { |
| return cgs.newLocal(); |
| } |
| |
| @Override |
| public void freeLocal(int index) { |
| cgs.freeLocal(index); |
| } |
| }; |
| |
| final Label end = new Label(); |
| cgs.setFieldReference(f); |
| cgs.pushFieldValue(); |
| mv.visitJumpInsn(IFNULL, end); |
| |
| cgs.setFieldReference(f); |
| cgs.pushFieldValue(); |
| mv.visitMethodInsn( |
| INVOKEINTERFACE, |
| collection.getInternalName(), |
| "iterator", |
| Type.getMethodDescriptor(iterator, new Type[] {})); |
| mv.visitVarInsn(ASTORE, itr); |
| |
| final Label doloop = new Label(); |
| mv.visitLabel(doloop); |
| mv.visitVarInsn(ALOAD, itr); |
| mv.visitMethodInsn( |
| INVOKEINTERFACE, |
| iterator.getInternalName(), |
| "hasNext", |
| Type.getMethodDescriptor(Type.BOOLEAN_TYPE, new Type[] {})); |
| mv.visitJumpInsn(IFEQ, end); |
| |
| mv.visitVarInsn(ALOAD, itr); |
| mv.visitMethodInsn( |
| INVOKEINTERFACE, |
| iterator.getInternalName(), |
| "next", |
| Type.getMethodDescriptor(object, new Type[] {})); |
| mv.visitTypeInsn(CHECKCAST, valType.getInternalName()); |
| mv.visitVarInsn(ASTORE, val); |
| |
| sizeofMessage(new JavaColumnModel[] {col}, mv, ng); |
| mv.visitJumpInsn(GOTO, doloop); |
| |
| mv.visitLabel(end); |
| cgs.freeLocal(itr); |
| cgs.freeLocal(val); |
| } |
| |
| private JavaColumnModel collectionColumn(final JavaColumnModel f, final Class<?> valClazz) |
| throws OrmException { |
| return new JavaColumnModel( // |
| f.getField(), // |
| f.getPathToFieldName(), // |
| f.getColumnID(), // |
| valClazz); |
| } |
| |
| private void sizeofScalar(final MethodVisitor mv, final SizeofCGS cgs, final JavaColumnModel f) |
| throws OrmException { |
| cgs.setFieldReference(f); |
| |
| switch (Type.getType(f.getPrimitiveType()).getSort()) { |
| case Type.BOOLEAN: |
| cgs.preinc(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| cgs.doinc("computeBoolSize", Type.INT_TYPE, Type.BOOLEAN_TYPE); |
| break; |
| |
| case Type.CHAR: |
| cgs.preinc(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| cgs.doinc("computeUInt32Size", Type.INT_TYPE, Type.INT_TYPE); |
| break; |
| |
| case Type.BYTE: |
| case Type.SHORT: |
| case Type.INT: |
| cgs.preinc(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| cgs.doinc("computeInt32Size", Type.INT_TYPE, Type.INT_TYPE); |
| break; |
| |
| case Type.FLOAT: |
| cgs.preinc(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| cgs.doinc("computeFloatSize", Type.INT_TYPE, Type.FLOAT_TYPE); |
| break; |
| |
| case Type.DOUBLE: |
| cgs.preinc(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| cgs.doinc("computeDoubleSize", Type.INT_TYPE, Type.DOUBLE_TYPE); |
| break; |
| |
| case Type.LONG: |
| cgs.preinc(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| cgs.doinc("computeInt64Size", Type.INT_TYPE, Type.LONG_TYPE); |
| break; |
| |
| case Type.ARRAY: |
| case Type.OBJECT: |
| { |
| final Label end = new Label(); |
| cgs.pushFieldValue(); |
| mv.visitJumpInsn(IFNULL, end); |
| |
| if (f.getPrimitiveType() == byte[].class) { |
| cgs.preinc(); |
| cgs.push(f.getColumnID()); |
| cgs.doinc("computeTagSize", Type.INT_TYPE); |
| |
| cgs.preinc(); |
| cgs.pushFieldValue(); |
| mv.visitInsn(ARRAYLENGTH); |
| cgs.doinc("computeRawVarint32Size", Type.INT_TYPE); |
| |
| cgs.preinc(); |
| cgs.pushFieldValue(); |
| mv.visitInsn(ARRAYLENGTH); |
| cgs.doinc(); |
| |
| } else if (f.getPrimitiveType() == String.class) { |
| cgs.preinc(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| cgs.doinc("computeStringSize", Type.INT_TYPE, string); |
| |
| } else if (f.getPrimitiveType() == java.sql.Timestamp.class |
| || f.getPrimitiveType() == java.util.Date.class |
| || f.getPrimitiveType() == java.sql.Date.class) { |
| cgs.preinc(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| String tsType = Type.getType(f.getPrimitiveType()).getInternalName(); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| tsType, |
| "getTime", |
| Type.getMethodDescriptor(Type.LONG_TYPE, new Type[] {})); |
| cgs.doinc("computeFixed64Size", Type.INT_TYPE, Type.LONG_TYPE); |
| |
| } else if (f.getPrimitiveType().isEnum()) { |
| cgs.preinc(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| enumType.getInternalName(), |
| "ordinal", // |
| Type.getMethodDescriptor(Type.INT_TYPE, new Type[] {})); |
| cgs.doinc("computeEnumSize", Type.INT_TYPE, Type.INT_TYPE); |
| |
| } else { |
| throw new OrmException( |
| "Type " |
| + f.getPrimitiveType() |
| + " not supported for field " |
| + f.getPathToFieldName()); |
| } |
| mv.visitLabel(end); |
| break; |
| } |
| |
| default: |
| throw new OrmException( |
| "Type " + f.getPrimitiveType() + " not supported for field " + f.getPathToFieldName()); |
| } |
| } |
| |
| private void implementEncodeObject() { |
| final MethodVisitor mv = |
| cw.visitMethod( |
| ACC_PUBLIC, |
| "encode", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {object, codedOutputStream}), |
| null, |
| new String[] {}); |
| mv.visitCode(); |
| final EncodeCGS cgs = new EncodeCGS(mv); |
| cgs.setEntityType(pojoType); |
| |
| mv.visitVarInsn(ALOAD, 0); |
| mv.visitVarInsn(ALOAD, 1); |
| mv.visitTypeInsn(CHECKCAST, pojoType.getInternalName()); |
| mv.visitVarInsn(ALOAD, 2); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| implTypeName, |
| "encode", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {pojoType, codedOutputStream})); |
| |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(-1, -1); |
| mv.visitEnd(); |
| } |
| |
| private void implementEncodeSelf() throws OrmException { |
| final MethodVisitor mv = |
| cw.visitMethod( |
| ACC_PUBLIC, |
| "encode", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {pojoType, codedOutputStream}), |
| null, |
| new String[] {}); |
| mv.visitCode(); |
| final EncodeCGS cgs = new EncodeCGS(mv); |
| cgs.setEntityType(pojoType); |
| |
| encodeMessage(myFields, mv, cgs); |
| |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(-1, -1); |
| mv.visitEnd(); |
| } |
| |
| private void encodeMessage( |
| final JavaColumnModel[] myFields, final MethodVisitor mv, final EncodeCGS cgs) |
| throws OrmException { |
| for (final JavaColumnModel f : myFields) { |
| if (f.isNested()) { |
| final NestedCodec n = nestedFor(f); |
| |
| final Label end = new Label(); |
| cgs.setFieldReference(f); |
| cgs.pushFieldValue(); |
| mv.visitJumpInsn(IFNULL, end); |
| |
| final int msgSizeVar = cgs.newLocal(); |
| mv.visitFieldInsn(GETSTATIC, implTypeName, n.field, n.codecType.getDescriptor()); |
| cgs.pushFieldValue(); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| n.codecType.getInternalName(), |
| "sizeof", |
| Type.getMethodDescriptor(Type.INT_TYPE, new Type[] {n.pojoType})); |
| mv.visitVarInsn(ISTORE, msgSizeVar); |
| |
| cgs.pushCodedOutputStream(); |
| cgs.push(f.getColumnID()); |
| cgs.push(WireFormat.FieldType.MESSAGE.getWireType()); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| codedOutputStream.getInternalName(), |
| "writeTag", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type.INT_TYPE, Type.INT_TYPE})); |
| |
| cgs.pushCodedOutputStream(); |
| mv.visitVarInsn(ILOAD, msgSizeVar); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| codedOutputStream.getInternalName(), |
| "writeRawVarint32", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type.INT_TYPE})); |
| |
| mv.visitFieldInsn(GETSTATIC, implTypeName, n.field, n.codecType.getDescriptor()); |
| cgs.pushFieldValue(); |
| cgs.pushCodedOutputStream(); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| n.codecType.getInternalName(), |
| "encode", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {n.pojoType, codedOutputStream})); |
| |
| cgs.freeLocal(msgSizeVar); |
| mv.visitLabel(end); |
| |
| } else if (f.isCollection()) { |
| encodeCollection(f, mv, cgs); |
| |
| } else { |
| encodeScalar(mv, cgs, f); |
| } |
| } |
| } |
| |
| private void encodeCollection( |
| final JavaColumnModel f, final MethodVisitor mv, final EncodeCGS cgs) throws OrmException { |
| final int itr = cgs.newLocal(); |
| final int val = cgs.newLocal(); |
| final Class<?> valClazz = (Class<?>) f.getArgumentTypes()[0]; |
| final Type valType = Type.getType(valClazz); |
| final JavaColumnModel col = collectionColumn(f, valClazz); |
| final EncodeCGS ng = |
| new EncodeCGS(mv) { |
| { |
| sizeVar = cgs.sizeVar; |
| setEntityType(valType); |
| } |
| |
| @Override |
| public void pushEntity() { |
| mv.visitVarInsn(ALOAD, val); |
| } |
| |
| @Override |
| protected void appendGetField(final ColumnModel c) { |
| if (c != col) { |
| super.appendGetField(c); |
| } |
| } |
| |
| @Override |
| public int newLocal() { |
| return cgs.newLocal(); |
| } |
| |
| @Override |
| public void freeLocal(int index) { |
| cgs.freeLocal(index); |
| } |
| }; |
| |
| final Label end = new Label(); |
| cgs.setFieldReference(f); |
| cgs.pushFieldValue(); |
| mv.visitJumpInsn(IFNULL, end); |
| |
| cgs.setFieldReference(f); |
| cgs.pushFieldValue(); |
| mv.visitMethodInsn( |
| INVOKEINTERFACE, |
| collection.getInternalName(), |
| "iterator", |
| Type.getMethodDescriptor(iterator, new Type[] {})); |
| mv.visitVarInsn(ASTORE, itr); |
| |
| final Label doloop = new Label(); |
| mv.visitLabel(doloop); |
| mv.visitVarInsn(ALOAD, itr); |
| mv.visitMethodInsn( |
| INVOKEINTERFACE, |
| iterator.getInternalName(), |
| "hasNext", |
| Type.getMethodDescriptor(Type.BOOLEAN_TYPE, new Type[] {})); |
| mv.visitJumpInsn(IFEQ, end); |
| |
| mv.visitVarInsn(ALOAD, itr); |
| mv.visitMethodInsn( |
| INVOKEINTERFACE, |
| iterator.getInternalName(), |
| "next", |
| Type.getMethodDescriptor(object, new Type[] {})); |
| mv.visitTypeInsn(CHECKCAST, valType.getInternalName()); |
| mv.visitVarInsn(ASTORE, val); |
| |
| encodeMessage(new JavaColumnModel[] {col}, mv, ng); |
| mv.visitJumpInsn(GOTO, doloop); |
| |
| mv.visitLabel(end); |
| cgs.freeLocal(itr); |
| cgs.freeLocal(val); |
| } |
| |
| private void encodeScalar(final MethodVisitor mv, final EncodeCGS cgs, final JavaColumnModel f) |
| throws OrmException { |
| cgs.setFieldReference(f); |
| |
| switch (Type.getType(f.getPrimitiveType()).getSort()) { |
| case Type.BOOLEAN: |
| cgs.pushCodedOutputStream(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| cgs.write("writeBool", Type.BOOLEAN_TYPE); |
| break; |
| |
| case Type.CHAR: |
| cgs.pushCodedOutputStream(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| cgs.write("writeUInt32", Type.INT_TYPE); |
| break; |
| |
| case Type.BYTE: |
| case Type.SHORT: |
| case Type.INT: |
| cgs.pushCodedOutputStream(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| cgs.write("writeInt32", Type.INT_TYPE); |
| break; |
| |
| case Type.FLOAT: |
| cgs.pushCodedOutputStream(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| cgs.write("writeFloat", Type.FLOAT_TYPE); |
| break; |
| |
| case Type.DOUBLE: |
| cgs.pushCodedOutputStream(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| cgs.write("writeDouble", Type.DOUBLE_TYPE); |
| break; |
| |
| case Type.LONG: |
| cgs.pushCodedOutputStream(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| cgs.write("writeInt64", Type.LONG_TYPE); |
| break; |
| |
| case Type.ARRAY: |
| case Type.OBJECT: |
| { |
| final Label end = new Label(); |
| cgs.pushFieldValue(); |
| mv.visitJumpInsn(IFNULL, end); |
| |
| if (f.getPrimitiveType() == byte[].class) { |
| cgs.pushCodedOutputStream(); |
| cgs.push(f.getColumnID()); |
| cgs.push(WireFormat.FieldType.BYTES.getWireType()); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| codedOutputStream.getInternalName(), |
| "writeTag", |
| Type.getMethodDescriptor( |
| Type.VOID_TYPE, new Type[] {Type.INT_TYPE, Type.INT_TYPE})); |
| |
| cgs.pushCodedOutputStream(); |
| cgs.pushFieldValue(); |
| mv.visitInsn(ARRAYLENGTH); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| codedOutputStream.getInternalName(), |
| "writeRawVarint32", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type.INT_TYPE})); |
| |
| cgs.pushCodedOutputStream(); |
| cgs.pushFieldValue(); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| codedOutputStream.getInternalName(), |
| "writeRawBytes", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type.getType(byte[].class)})); |
| |
| } else { |
| cgs.pushCodedOutputStream(); |
| cgs.push(f.getColumnID()); |
| cgs.pushFieldValue(); |
| |
| if (f.getPrimitiveType() == String.class) { |
| cgs.write("writeString", string); |
| |
| } else if (f.getPrimitiveType() == java.sql.Timestamp.class |
| || f.getPrimitiveType() == java.util.Date.class |
| || f.getPrimitiveType() == java.sql.Date.class) { |
| String tsType = Type.getType(f.getPrimitiveType()).getInternalName(); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| tsType, |
| "getTime", |
| Type.getMethodDescriptor(Type.LONG_TYPE, new Type[] {})); |
| cgs.write("writeFixed64", Type.LONG_TYPE); |
| |
| } else if (f.getPrimitiveType().isEnum()) { |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| enumType.getInternalName(), |
| "ordinal", // |
| Type.getMethodDescriptor(Type.INT_TYPE, new Type[] {})); |
| cgs.write("writeEnum", Type.INT_TYPE); |
| |
| } else { |
| throw new OrmException( |
| "Type " |
| + f.getPrimitiveType() |
| + " not supported for field " |
| + f.getPathToFieldName()); |
| } |
| } |
| mv.visitLabel(end); |
| break; |
| } |
| |
| default: |
| throw new OrmException( |
| "Type " + f.getPrimitiveType() + " not supported for field " + f.getPathToFieldName()); |
| } |
| } |
| |
| private void implementMergeFromObject() { |
| final MethodVisitor mv = |
| cw.visitMethod( |
| ACC_PUBLIC, |
| "mergeFrom", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {codedInputStream, object}), |
| null, |
| new String[] {}); |
| mv.visitCode(); |
| |
| mv.visitVarInsn(ALOAD, 0); |
| mv.visitVarInsn(ALOAD, 1); |
| mv.visitVarInsn(ALOAD, 2); |
| mv.visitTypeInsn(CHECKCAST, pojoType.getInternalName()); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| implTypeName, |
| "mergeFrom", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {codedInputStream, pojoType})); |
| |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(-1, -1); |
| mv.visitEnd(); |
| } |
| |
| private void implementMergeFromSelf() throws OrmException { |
| final MethodVisitor mv = |
| cw.visitMethod( |
| ACC_PUBLIC, |
| "mergeFrom", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {codedInputStream, pojoType}), |
| null, |
| new String[] {}); |
| mv.visitCode(); |
| final DecodeCGS cgs = new DecodeCGS(mv); |
| cgs.objVar = 2; |
| cgs.tagVar = cgs.newLocal(); |
| cgs.setEntityType(pojoType); |
| |
| decodeMessage(myFields, mv, cgs); |
| |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(-1, -1); |
| mv.visitEnd(); |
| } |
| |
| private void decodeMessage( |
| final JavaColumnModel[] myFields, final MethodVisitor mv, final DecodeCGS cgs) |
| throws OrmException { |
| final Label nextField = new Label(); |
| final Label end = new Label(); |
| mv.visitLabel(nextField); |
| |
| // while (!ci.isAtEnd) { ... |
| cgs.call("readTag", Type.INT_TYPE); |
| mv.visitInsn(DUP); |
| mv.visitVarInsn(ISTORE, cgs.tagVar); |
| |
| cgs.push(3); |
| mv.visitInsn(IUSHR); |
| |
| final Label badField = new Label(); |
| final int[] caseTags = new int[1 + myFields.length]; |
| final Label[] caseLabels = new Label[caseTags.length]; |
| |
| caseTags[0] = 0; |
| caseLabels[0] = new Label(); |
| |
| int gaps = 0; |
| for (int i = 1; i < caseTags.length; i++) { |
| caseTags[i] = myFields[i - 1].getColumnID(); |
| caseLabels[i] = new Label(); |
| gaps += caseTags[i] - (caseTags[i - 1] + 1); |
| } |
| |
| if (2 * gaps / 3 <= myFields.length) { |
| final int min = 0; |
| final int max = caseTags[caseTags.length - 1]; |
| final Label[] table = new Label[max + 1]; |
| Arrays.fill(table, badField); |
| for (int idx = 0; idx < caseTags.length; idx++) { |
| table[caseTags[idx]] = caseLabels[idx]; |
| } |
| mv.visitTableSwitchInsn(min, max, badField, table); |
| } else { |
| mv.visitLookupSwitchInsn(badField, caseTags, caseLabels); |
| } |
| |
| mv.visitLabel(caseLabels[0]); |
| mv.visitJumpInsn(GOTO, end); |
| |
| for (int idx = 1; idx < caseTags.length; idx++) { |
| final JavaColumnModel f = myFields[idx - 1]; |
| mv.visitLabel(caseLabels[idx]); |
| decodeField(mv, cgs, f); |
| mv.visitJumpInsn(GOTO, nextField); |
| } |
| |
| // default: |
| mv.visitLabel(badField); |
| cgs.pushCodedInputStream(); |
| mv.visitVarInsn(ILOAD, cgs.tagVar); |
| cgs.ncallInt("skipField", Type.BOOLEAN_TYPE); |
| mv.visitInsn(POP); |
| mv.visitJumpInsn(GOTO, nextField); |
| |
| mv.visitLabel(end); |
| cgs.pushCodedInputStream(); |
| cgs.push(0); |
| cgs.ncallInt("checkLastTagWas", Type.VOID_TYPE); |
| } |
| |
| private void decodeField(final MethodVisitor mv, final DecodeCGS cgs, final JavaColumnModel f) |
| throws OrmException { |
| if (f.isNested()) { |
| final NestedCodec n = nestedFor(f); |
| final Label load = new Label(); |
| cgs.setFieldReference(f); |
| cgs.pushFieldValue(); |
| mv.visitJumpInsn(IFNONNULL, load); |
| |
| // Since the field isn't initialized, construct it |
| // |
| cgs.fieldSetBegin(); |
| mv.visitFieldInsn(GETSTATIC, implTypeName, n.field, n.codecType.getDescriptor()); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| n.codecType.getInternalName(), |
| "newInstance", |
| Type.getMethodDescriptor(n.pojoType, new Type[] {})); |
| if (object.equals(n.pojoType)) { |
| mv.visitTypeInsn(CHECKCAST, Type.getType(f.getNestedClass()).getInternalName()); |
| } |
| cgs.fieldSetEnd(); |
| |
| // read the length, set a new limit, decode the message, validate |
| // we stopped at the end of it as expected. |
| // |
| mv.visitLabel(load); |
| final int limitVar = cgs.newLocal(); |
| cgs.pushCodedInputStream(); |
| cgs.call("readRawVarint32", Type.INT_TYPE); |
| cgs.ncallInt("pushLimit", Type.INT_TYPE); |
| mv.visitVarInsn(ISTORE, limitVar); |
| |
| mv.visitFieldInsn(GETSTATIC, implTypeName, n.field, n.codecType.getDescriptor()); |
| cgs.pushCodedInputStream(); |
| cgs.pushFieldValue(); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| n.codecType.getInternalName(), |
| "mergeFrom", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {codedInputStream, n.pojoType})); |
| |
| cgs.pushCodedInputStream(); |
| mv.visitVarInsn(ILOAD, limitVar); |
| cgs.ncallInt("popLimit", Type.VOID_TYPE); |
| cgs.freeLocal(limitVar); |
| |
| } else if (f.isCollection()) { |
| decodeCollection(mv, cgs, f); |
| |
| } else { |
| decodeScalar(mv, cgs, f); |
| } |
| } |
| |
| private void decodeCollection( |
| final MethodVisitor mv, final DecodeCGS cgs, final JavaColumnModel f) throws OrmException { |
| final Class<?> valClazz = (Class<?>) f.getArgumentTypes()[0]; |
| final Type valType = Type.getType(valClazz); |
| final JavaColumnModel col = collectionColumn(f, valClazz); |
| final DecodeCGS ng = |
| new DecodeCGS(mv) { |
| { |
| tagVar = cgs.tagVar; |
| setEntityType(valType); |
| } |
| |
| @Override |
| public int newLocal() { |
| return cgs.newLocal(); |
| } |
| |
| @Override |
| public void freeLocal(int index) { |
| cgs.freeLocal(index); |
| } |
| |
| @Override |
| protected void appendGetField(final ColumnModel c) { |
| if (c != col) { |
| super.appendGetField(c); |
| } |
| } |
| |
| @Override |
| public void fieldSetBegin() { |
| if (col.isNested()) { |
| super.fieldSetBegin(); |
| } else { |
| cgs.pushFieldValue(); |
| } |
| } |
| |
| @Override |
| public void fieldSetEnd() { |
| if (col.isNested()) { |
| super.fieldSetEnd(); |
| } else { |
| mv.visitMethodInsn( |
| INVOKEINTERFACE, |
| collection.getInternalName(), |
| "add", |
| Type.getMethodDescriptor(Type.BOOLEAN_TYPE, new Type[] {object})); |
| mv.visitInsn(POP); |
| } |
| } |
| }; |
| |
| final Label notnull = new Label(); |
| cgs.setFieldReference(f); |
| cgs.pushFieldValue(); |
| mv.visitJumpInsn(IFNONNULL, notnull); |
| |
| // If the field is null, try to initialize it based on its declared type. |
| // If we don't know what that is, we have to throw an exception instead. |
| // |
| final Type concreteType; |
| if (!f.getNestedClass().isInterface() |
| && (f.getNestedClass().getModifiers() & Modifier.ABSTRACT) == 0) { |
| concreteType = Type.getType(f.getNestedClass()); |
| |
| } else if (f.getNestedClass().isAssignableFrom(ArrayList.class)) { |
| concreteType = Type.getType(ArrayList.class); |
| |
| } else if (f.getNestedClass().isAssignableFrom(HashSet.class)) { |
| concreteType = Type.getType(HashSet.class); |
| |
| } else if (f.getNestedClass().isAssignableFrom(TreeSet.class)) { |
| concreteType = Type.getType(TreeSet.class); |
| |
| } else { |
| mv.visitTypeInsn(NEW, illegalStateException.getInternalName()); |
| mv.visitInsn(DUP); |
| mv.visitLdcInsn("Field " + f.getPathToFieldName() + " not initialized"); |
| mv.visitMethodInsn( |
| INVOKESPECIAL, |
| illegalStateException.getInternalName(), |
| "<init>", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {string})); |
| mv.visitInsn(ATHROW); |
| concreteType = null; |
| } |
| if (concreteType != null) { |
| cgs.fieldSetBegin(); |
| mv.visitTypeInsn(NEW, concreteType.getInternalName()); |
| mv.visitInsn(DUP); |
| mv.visitMethodInsn( |
| INVOKESPECIAL, |
| concreteType.getInternalName(), |
| "<init>", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {})); |
| cgs.fieldSetEnd(); |
| } |
| mv.visitLabel(notnull); |
| |
| if (col.isNested()) { |
| // If its nested, we have to build the object instance. |
| // |
| final NestedCodec n = nestedFor(col); |
| ng.objVar = cgs.newLocal(); |
| mv.visitFieldInsn(GETSTATIC, implTypeName, n.field, n.codecType.getDescriptor()); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| n.codecType.getInternalName(), |
| "newInstance", |
| Type.getMethodDescriptor(n.pojoType, new Type[] {})); |
| mv.visitVarInsn(ASTORE, ng.objVar); |
| |
| // read the length, set a new limit, decode the message, validate |
| // we stopped at the end of it as expected. |
| // |
| final int limitVar = cgs.newLocal(); |
| cgs.pushCodedInputStream(); |
| cgs.call("readRawVarint32", Type.INT_TYPE); |
| cgs.ncallInt("pushLimit", Type.INT_TYPE); |
| mv.visitVarInsn(ISTORE, limitVar); |
| |
| mv.visitFieldInsn(GETSTATIC, implTypeName, n.field, n.codecType.getDescriptor()); |
| cgs.pushCodedInputStream(); |
| mv.visitVarInsn(ALOAD, ng.objVar); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| n.codecType.getInternalName(), |
| "mergeFrom", |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {codedInputStream, n.pojoType})); |
| |
| cgs.pushCodedInputStream(); |
| mv.visitVarInsn(ILOAD, limitVar); |
| cgs.ncallInt("popLimit", Type.VOID_TYPE); |
| cgs.freeLocal(limitVar); |
| cgs.pushFieldValue(); |
| |
| mv.visitVarInsn(ALOAD, ng.objVar); |
| mv.visitMethodInsn( |
| INVOKEINTERFACE, |
| collection.getInternalName(), |
| "add", |
| Type.getMethodDescriptor(Type.BOOLEAN_TYPE, new Type[] {object})); |
| mv.visitInsn(POP); |
| cgs.freeLocal(ng.objVar); |
| |
| } else if (col.isCollection()) { |
| throw new OrmException( |
| "Cannot nest collection as member of another" + " collection: " + f.getPathToFieldName()); |
| |
| } else { |
| decodeScalar(mv, ng, col); |
| } |
| } |
| |
| private static void decodeScalar( |
| final MethodVisitor mv, final DecodeCGS cgs, final JavaColumnModel f) throws OrmException { |
| cgs.setFieldReference(f); |
| cgs.fieldSetBegin(); |
| switch (Type.getType(f.getPrimitiveType()).getSort()) { |
| case Type.BOOLEAN: |
| cgs.call("readBool", Type.BOOLEAN_TYPE); |
| break; |
| |
| case Type.CHAR: |
| cgs.call("readUInt32", Type.INT_TYPE); |
| break; |
| |
| case Type.BYTE: |
| case Type.SHORT: |
| case Type.INT: |
| cgs.call("readInt32", Type.INT_TYPE); |
| break; |
| |
| case Type.FLOAT: |
| cgs.call("readFloat", Type.FLOAT_TYPE); |
| break; |
| |
| case Type.DOUBLE: |
| cgs.call("readDouble", Type.DOUBLE_TYPE); |
| break; |
| |
| case Type.LONG: |
| cgs.call("readInt64", Type.LONG_TYPE); |
| break; |
| |
| default: |
| if (f.getPrimitiveType() == byte[].class) { |
| cgs.call("readBytes", byteString); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| byteString.getInternalName(), |
| "toByteArray", |
| Type.getMethodDescriptor(Type.getType(byte[].class), new Type[] {})); |
| |
| } else if (f.getPrimitiveType() == String.class) { |
| cgs.call("readString", string); |
| |
| } else if (f.getPrimitiveType() == java.sql.Timestamp.class |
| || f.getPrimitiveType() == java.util.Date.class |
| || f.getPrimitiveType() == java.sql.Date.class) { |
| String tsType = Type.getType(f.getPrimitiveType()).getInternalName(); |
| mv.visitTypeInsn(NEW, tsType); |
| mv.visitInsn(DUP); |
| cgs.call("readFixed64", Type.LONG_TYPE); |
| mv.visitMethodInsn( |
| INVOKESPECIAL, |
| tsType, |
| "<init>", // |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type.LONG_TYPE})); |
| |
| } else if (f.getPrimitiveType().isEnum()) { |
| Type et = Type.getType(f.getPrimitiveType()); |
| mv.visitMethodInsn( |
| INVOKESTATIC, |
| et.getInternalName(), |
| "values", |
| Type.getMethodDescriptor(Type.getType("[" + et.getDescriptor()), new Type[] {})); |
| cgs.call("readEnum", Type.INT_TYPE); |
| mv.visitInsn(AALOAD); |
| |
| } else { |
| throw new OrmException( |
| "Type " |
| + f.getPrimitiveType() |
| + " not supported for field " |
| + f.getPathToFieldName()); |
| } |
| break; |
| } |
| cgs.fieldSetEnd(); |
| } |
| |
| private static class SizeofCGS extends CodeGenSupport { |
| int sizeVar; |
| |
| SizeofCGS(MethodVisitor method) { |
| super(method); |
| } |
| |
| void doinc(String name, Type... args) { |
| mv.visitMethodInsn( |
| INVOKESTATIC, |
| codedOutputStream.getInternalName(), |
| name, |
| Type.getMethodDescriptor(Type.INT_TYPE, args)); |
| doinc(); |
| } |
| |
| void preinc() { |
| mv.visitVarInsn(ILOAD, sizeVar); |
| } |
| |
| void doinc() { |
| mv.visitInsn(IADD); |
| mv.visitVarInsn(ISTORE, sizeVar); |
| } |
| |
| @Override |
| public void pushEntity() { |
| mv.visitVarInsn(ALOAD, 1); |
| } |
| } |
| |
| private static class EncodeCGS extends SizeofCGS { |
| private EncodeCGS(MethodVisitor method) { |
| super(method); |
| } |
| |
| void pushCodedOutputStream() { |
| mv.visitVarInsn(ALOAD, 2); |
| } |
| |
| void write(String name, Type arg) { |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| codedOutputStream.getInternalName(), |
| name, |
| Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type.INT_TYPE, arg})); |
| } |
| } |
| |
| private static class DecodeCGS extends CodeGenSupport { |
| final int codedInputStreamVar = 1; |
| int objVar; |
| int tagVar; |
| |
| DecodeCGS(MethodVisitor method) { |
| super(method); |
| } |
| |
| void pushCodedInputStream() { |
| mv.visitVarInsn(ALOAD, codedInputStreamVar); |
| } |
| |
| void call(String name, Type ret) { |
| pushCodedInputStream(); |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| codedInputStream.getInternalName(), |
| name, |
| Type.getMethodDescriptor(ret, new Type[] {})); |
| } |
| |
| void ncallInt(String name, Type ret) { |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| codedInputStream.getInternalName(), |
| name, |
| Type.getMethodDescriptor(ret, new Type[] {Type.INT_TYPE})); |
| } |
| |
| @Override |
| public void pushEntity() { |
| mv.visitVarInsn(ALOAD, objVar); |
| } |
| } |
| |
| private static class NestedCodec { |
| final String field; |
| final Type codecType; |
| final Type pojoType; |
| |
| NestedCodec(String field, Class<?> impl, Type pojoType) { |
| this.field = field; |
| this.codecType = Type.getType(impl); |
| this.pojoType = pojoType; |
| } |
| } |
| } |