Search in sources :

Example 11 with Method

use of org.objectweb.asm.commons.Method in project cdap by caskdata.

the class AuthEnforceRewriter method rewriteClass.

@Override
public byte[] rewriteClass(String className, InputStream input) throws IOException {
    byte[] classBytes = ByteStreams.toByteArray(input);
    // First pass: Check the class to have a method with AuthEnforce annotation if found store the annotation details
    // and parameters for the method for second pass in which class rewrite will be performed.
    ClassReader cr = new ClassReader(classBytes);
    // SKIP_CODE SKIP_DEBUG and SKIP_FRAMESto make the first pass faster since in the first pass we just want to
    // process annotations to check if the class has any method with AuthEnforce annotation. If such method is found
    // we also store the parameters which has the named annotations as specified in the entities field of the
    // AuthEnforce.
    AuthEnforceAnnotationVisitor classVisitor = new AuthEnforceAnnotationVisitor(className);
    cr.accept(classVisitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
    Map<Method, AnnotationDetail> methodAnnotations = classVisitor.getMethodAnnotations();
    if (methodAnnotations.isEmpty()) {
        // if no AuthEnforce annotation was found then return the original class bytes
        return classBytes;
    }
    // We found some method which has AuthEnforce annotation so we need a second pass in to rewrite the class
    // in second pass we COMPUTE_FRAMES and visit classes with EXPAND_FRAMES
    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
    cr.accept(new AuthEnforceAnnotationRewriter(className, cw, classVisitor.getFieldDetails(), methodAnnotations), ClassReader.EXPAND_FRAMES);
    return cw.toByteArray();
}
Also used : ClassReader(org.objectweb.asm.ClassReader) Method(org.objectweb.asm.commons.Method) ClassWriter(org.objectweb.asm.ClassWriter)

Example 12 with Method

use of org.objectweb.asm.commons.Method in project cdap by caskdata.

the class DatumWriterGenerator method encodeRecord.

/**
   * Generates method body for encoding java class. If the class given is an interface,
   * getter method will be used to access the field values, otherwise, it will assumes
   * fields are public.
   *
   * @param mg
   * @param schema
   * @param outputType
   * @param value
   * @param encoder
   * @param schemaLocal
   * @param seenRefs
   */
private void encodeRecord(GeneratorAdapter mg, Schema schema, TypeToken<?> outputType, int value, int encoder, int schemaLocal, int seenRefs) {
    try {
        Class<?> rawType = outputType.getRawType();
        // Record type might be defined by the user, hence need to preserve class loading of it
        preservedClasses.add(rawType);
        boolean isInterface = rawType.isInterface();
        /*
        Check for circular reference
        if (value != null && !seenRefs.add(value)) {
           throw new IOException(...);
        }
      */
        Label notSeen = mg.newLabel();
        mg.loadArg(value);
        mg.ifNull(notSeen);
        mg.loadArg(seenRefs);
        mg.loadArg(value);
        mg.invokeInterface(Type.getType(Set.class), getMethod(boolean.class, "add", Object.class));
        mg.ifZCmp(GeneratorAdapter.NE, notSeen);
        mg.throwException(Type.getType(IOException.class), "Circular reference not supported.");
        mg.mark(notSeen);
        // Store the list of schema fields.
        mg.loadArg(schemaLocal);
        mg.invokeVirtual(Type.getType(Schema.class), getMethod(List.class, "getFields"));
        int fieldSchemas = mg.newLocal(Type.getType(List.class));
        mg.storeLocal(fieldSchemas);
        // For each field, call the encode method for the field
        List<Schema.Field> fields = schema.getFields();
        for (int i = 0; i < fields.size(); i++) {
            Schema.Field field = fields.get(i);
            TypeToken<?> fieldType;
            // this.encodeFieldMethod(value.fieldName, encoder, fieldSchemas.get(i).getSchema());
            if (isInterface) {
                mg.loadThis();
                mg.loadArg(value);
                Method getter = getGetter(outputType, field.getName());
                fieldType = outputType.resolveType(rawType.getMethod(getter.getName()).getGenericReturnType());
                mg.invokeInterface(Type.getType(rawType), getter);
            } else {
                fieldType = outputType.resolveType(Fields.findField(outputType.getType(), field.getName()).getGenericType());
                fieldAccessorRequests.put(outputType, field.getName());
                mg.loadThis();
                mg.dup();
                mg.getField(classType, getFieldAccessorName(outputType, field.getName()), Type.getType(FieldAccessor.class));
                mg.loadArg(value);
                mg.invokeInterface(Type.getType(FieldAccessor.class), getAccessorMethod(fieldType));
                if (!fieldType.getRawType().isPrimitive()) {
                    doCast(mg, fieldType, field.getSchema());
                }
            }
            mg.loadArg(encoder);
            mg.loadLocal(fieldSchemas);
            mg.push(i);
            mg.invokeInterface(Type.getType(List.class), getMethod(Object.class, "get", int.class));
            mg.checkCast(Type.getType(Schema.Field.class));
            mg.invokeVirtual(Type.getType(Schema.Field.class), getMethod(Schema.class, "getSchema"));
            mg.loadArg(seenRefs);
            mg.invokeVirtual(classType, getEncodeMethod(fieldType, field.getSchema()));
        }
    } catch (Exception e) {
        throw Throwables.propagate(e);
    }
}
Also used : Set(java.util.Set) Schema(co.cask.cdap.api.data.schema.Schema) Label(org.objectweb.asm.Label) IOException(java.io.IOException) Method(org.objectweb.asm.commons.Method) IOException(java.io.IOException) List(java.util.List)

Example 13 with Method

use of org.objectweb.asm.commons.Method in project cdap by caskdata.

the class DatumWriterGenerator method generateEncode.

/**
   * Generates the {@link DatumWriter#encode(Object, co.cask.cdap.common.io.Encoder)} method.
   * @param outputType Type information of the data type for output
   * @param schema Schema to use for output.
   */
private void generateEncode(TypeToken<?> outputType, Schema schema) {
    TypeToken<?> callOutputType = getCallTypeToken(outputType, schema);
    Method encodeMethod = getMethod(void.class, "encode", callOutputType.getRawType(), Encoder.class);
    if (!Object.class.equals(callOutputType.getRawType())) {
        // Generate the synthetic method for the bridging
        Method method = getMethod(void.class, "encode", Object.class, Encoder.class);
        GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC + Opcodes.ACC_BRIDGE + Opcodes.ACC_SYNTHETIC, method, null, new Type[] { Type.getType(IOException.class) }, classWriter);
        mg.loadThis();
        mg.loadArg(0);
        mg.checkCast(Type.getType(callOutputType.getRawType()));
        mg.loadArg(1);
        mg.invokeVirtual(classType, encodeMethod);
        mg.returnValue();
        mg.endMethod();
    }
    // Generate the top level public encode method
    String methodSignature = null;
    if (callOutputType.getType() instanceof ParameterizedType) {
        methodSignature = Signatures.getMethodSignature(encodeMethod, TypeToken.of(void.class), callOutputType, null);
    }
    GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC, encodeMethod, methodSignature, new Type[] { Type.getType(IOException.class) }, classWriter);
    // Delegate to the actual encode method(value, encoder, schema, Sets.newIdentityHashSet());
    mg.loadThis();
    mg.loadArg(0);
    mg.loadArg(1);
    mg.loadThis();
    mg.getField(classType, "schema", Type.getType(Schema.class));
    // seenRefs Set
    mg.invokeStatic(Type.getType(Sets.class), getMethod(Set.class, "newIdentityHashSet"));
    mg.invokeVirtual(classType, getEncodeMethod(outputType, schema));
    mg.returnValue();
    mg.endMethod();
}
Also used : ParameterizedType(java.lang.reflect.ParameterizedType) Set(java.util.Set) Sets(com.google.common.collect.Sets) Schema(co.cask.cdap.api.data.schema.Schema) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter) Method(org.objectweb.asm.commons.Method) IOException(java.io.IOException)

Example 14 with Method

use of org.objectweb.asm.commons.Method in project cdap by caskdata.

the class DatumWriterGenerator method generateConstructor.

/**
   * Generates the constructor. The constructor generated has signature {@code (Schema, FieldAccessorFactory)}.
   */
private void generateConstructor() {
    Method constructor = getMethod(void.class, "<init>", Schema.class, FieldAccessorFactory.class);
    // Constructor(Schema schema, FieldAccessorFactory accessorFactory)
    GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC, constructor, null, null, classWriter);
    // super(); // Calling Object constructor
    mg.loadThis();
    mg.invokeConstructor(Type.getType(Object.class), getMethod(void.class, "<init>"));
    // if (!SCHEMA_HASH.equals(schema.getSchemaHash().toString())) { throw IllegalArgumentException }
    mg.getStatic(classType, "SCHEMA_HASH", Type.getType(String.class));
    mg.loadArg(0);
    mg.invokeVirtual(Type.getType(Schema.class), getMethod(SchemaHash.class, "getSchemaHash"));
    mg.invokeVirtual(Type.getType(SchemaHash.class), getMethod(String.class, "toString"));
    mg.invokeVirtual(Type.getType(String.class), getMethod(boolean.class, "equals", Object.class));
    Label hashEquals = mg.newLabel();
    mg.ifZCmp(GeneratorAdapter.NE, hashEquals);
    mg.throwException(Type.getType(IllegalArgumentException.class), "Schema not match.");
    mg.mark(hashEquals);
    // this.schema = schema;
    mg.loadThis();
    mg.loadArg(0);
    mg.putField(classType, "schema", Type.getType(Schema.class));
    // For each record field that needs an accessor, get the accessor and store it in field.
    for (Map.Entry<TypeToken<?>, String> entry : fieldAccessorRequests.entries()) {
        String fieldAccessorName = getFieldAccessorName(entry.getKey(), entry.getValue());
        classWriter.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL, fieldAccessorName, Type.getDescriptor(FieldAccessor.class), null, null);
        // this.fieldAccessorName
        //  = accessorFactory.getFieldAccessor(TypeToken.of(Class.forName("className")), "fieldName");
        mg.loadThis();
        mg.loadArg(1);
        mg.push(entry.getKey().getRawType().getName());
        mg.invokeStatic(Type.getType(Class.class), getMethod(Class.class, "forName", String.class));
        mg.invokeStatic(Type.getType(TypeToken.class), getMethod(TypeToken.class, "of", Class.class));
        mg.push(entry.getValue());
        mg.invokeInterface(Type.getType(FieldAccessorFactory.class), getMethod(FieldAccessor.class, "getFieldAccessor", TypeToken.class, String.class));
        mg.putField(classType, fieldAccessorName, Type.getType(FieldAccessor.class));
    }
    mg.returnValue();
    mg.endMethod();
}
Also used : SchemaHash(co.cask.cdap.api.data.schema.SchemaHash) Schema(co.cask.cdap.api.data.schema.Schema) Label(org.objectweb.asm.Label) Method(org.objectweb.asm.commons.Method) TypeToken(com.google.common.reflect.TypeToken) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter) Map(java.util.Map)

Example 15 with Method

use of org.objectweb.asm.commons.Method in project cdap by caskdata.

the class DatumWriterGenerator method getEncodeMethod.

/**
   * Returns the encode method for the given type and schema. The same method will be returned if the same
   * type and schema has been passed to the method before.
   *
   * @param outputType Type information of the data type for output
   * @param schema Schema to use for output.
   * @return A method for encoding the given output type and schema.
   */
private Method getEncodeMethod(TypeToken<?> outputType, Schema schema) {
    String key = String.format("%s%s", normalizeTypeName(outputType), schema.getSchemaHash());
    Method method = encodeMethods.get(key);
    if (method != null) {
        return method;
    }
    // Generate the encode method (value, encoder, schema, set)
    TypeToken<?> callOutputType = getCallTypeToken(outputType, schema);
    String methodName = String.format("encode%s", key);
    method = getMethod(void.class, methodName, callOutputType.getRawType(), Encoder.class, Schema.class, Set.class);
    // Put the method into map first before generating the body in order to support recursive data type.
    encodeMethods.put(key, method);
    String methodSignature = Signatures.getMethodSignature(method, TypeToken.of(void.class), callOutputType, null, null, new TypeToken<Set<Object>>() {
    });
    GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PRIVATE, method, methodSignature, new Type[] { Type.getType(IOException.class) }, classWriter);
    generateEncodeBody(mg, schema, outputType, 0, 1, 2, 3);
    mg.returnValue();
    mg.endMethod();
    return method;
}
Also used : Set(java.util.Set) Encoder(co.cask.cdap.common.io.Encoder) Schema(co.cask.cdap.api.data.schema.Schema) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter) Method(org.objectweb.asm.commons.Method) IOException(java.io.IOException)

Aggregations

Method (org.objectweb.asm.commons.Method)32 GeneratorAdapter (org.objectweb.asm.commons.GeneratorAdapter)24 Type (org.objectweb.asm.Type)16 Label (org.objectweb.asm.Label)8 ClassWriter (org.objectweb.asm.ClassWriter)7 MethodVisitor (org.objectweb.asm.MethodVisitor)7 IOException (java.io.IOException)6 ClassReader (org.objectweb.asm.ClassReader)5 Schema (co.cask.cdap.api.data.schema.Schema)4 Set (java.util.Set)3 ClassVisitor (org.objectweb.asm.ClassVisitor)3 TypeToken (com.google.common.reflect.TypeToken)2 InvocationHandler (java.lang.reflect.InvocationHandler)2 SchemaHash (co.cask.cdap.api.data.schema.SchemaHash)1 MetricsContext (co.cask.cdap.api.metrics.MetricsContext)1 Encoder (co.cask.cdap.common.io.Encoder)1 Sets (com.google.common.collect.Sets)1 JClassType (com.google.gwt.core.ext.typeinfo.JClassType)1 InterceptorType (com.navercorp.pinpoint.profiler.instrument.interceptor.InterceptorType)1 File (java.io.File)1