Package org.jboss.classfilewriter

Source Code of org.jboss.classfilewriter.ClassFile

/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2012 Red Hat, 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 org.jboss.classfilewriter;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.jboss.classfilewriter.annotations.AnnotationBuilder;
import org.jboss.classfilewriter.annotations.AnnotationsAttribute;
import org.jboss.classfilewriter.attributes.Attribute;
import org.jboss.classfilewriter.constpool.ConstPool;
import org.jboss.classfilewriter.util.ByteArrayDataOutputStream;
import org.jboss.classfilewriter.util.DescriptorUtils;

/**
*
* @author Stuart Douglas
*
*/
public class ClassFile implements WritableEntry {

    private final String name;

    private final String superclass;

    private final int accessFlags;

    private final int version;

    private final ConstPool constPool = new ConstPool();

    private final List<String> interfaces = new ArrayList<String>();

    private final Set<ClassField> fields = new HashSet<ClassField>();

    private final Set<ClassMethod> methods = new HashSet<ClassMethod>();

    private byte[] bytecode;

    private final List<Attribute> attributes = new ArrayList<Attribute>();

    private final AnnotationsAttribute runtimeVisibleAnnotationsAttribute;

    public ClassFile(String name, String superclass, String... interfaces) {
        this.version = JavaVersions.JAVA_6;
        this.name = name.replace('/', '.'); // store the name in . form
        this.superclass = superclass;
        this.accessFlags = AccessFlag.of(AccessFlag.SUPER, AccessFlag.PUBLIC);
        this.interfaces.addAll(Arrays.asList(interfaces));
        runtimeVisibleAnnotationsAttribute = new AnnotationsAttribute(AnnotationsAttribute.Type.RUNTIME_VISIBLE, constPool);
        this.attributes.add(runtimeVisibleAnnotationsAttribute);
    }

    public void addInterface(String iface) {
        this.interfaces.add(iface);
    }

    // fields
    /**
     * Adds a field with the given name and descriptor.
     *
     */
    public ClassField addField(int accessFlags, String name, String descriptor) {
        return addField(accessFlags, name, descriptor, null);
    }

    public ClassField addField(int accessFlags, String name, String descriptor, String signature) {
        ClassField field = new ClassField((short) accessFlags, name, descriptor, this, constPool);
        if (fields.contains(field)) {
            throw new DuplicateMemberException("Field  already exists. Field: " + name + " Descriptor:" + signature);
        }
        fields.add(field);
        field.setSignature(signature);
        return field;
    }

    public ClassField addField(int accessFlags, String name, Class<?> type) {
        return addField(accessFlags, name, DescriptorUtils.makeDescriptor(type));
    }

    public ClassField addField(int accessFlags, String name, Class<?> type, String genericSignature) {
        return addField(accessFlags, name, DescriptorUtils.makeDescriptor(type), genericSignature);
    }

    public ClassField addField(Field field) {
        ClassField classField = addField((short) field.getModifiers(), field.getName(), field.getType(), null);
        for (Annotation annotation : field.getDeclaredAnnotations()) {
            classField.getRuntimeVisibleAnnotationsAttribute().addAnnotation(
                    AnnotationBuilder.createAnnotation(constPool, annotation));
        }
        return classField;

    }

    // methods

    public ClassMethod addMethod(int accessFlags, String name, String returnType, String... parameters) {
        ClassMethod method = new ClassMethod(name, returnType, parameters, accessFlags, this);
        if (methods.contains(method)) {
            throw new DuplicateMemberException("Method  already exists. Method: " + name + " Parameters:"
                    + Arrays.toString(parameters) + " Return Type: " + returnType);
        }
        methods.add(method);
        return method;
    }

    /**
     * Adds a method with the same signiture as the given method, including exception types
     * <p>
     * The new method will have the same modifier as the original method, except that the abstract and native flags will be
     * stripped.
     * <p>
     * TODO: annotations and signiture attribute
     */
    public ClassMethod addMethod(Method method) {
        ClassMethod classMethod = addMethod(method.getModifiers() & (~AccessFlag.ABSTRACT) & (~AccessFlag.NATIVE), method
                .getName(), DescriptorUtils.makeDescriptor(method.getReturnType()), DescriptorUtils.parameterDescriptors(method
                .getParameterTypes()));
        for (Class<?> e : method.getExceptionTypes()) {
            classMethod.addCheckedExceptions((Class<? extends Exception>) e);
        }
        for (Annotation annotation : method.getDeclaredAnnotations()) {
            classMethod.getRuntimeVisibleAnnotationsAttribute().addAnnotation(
                    AnnotationBuilder.createAnnotation(constPool, annotation));
        }
        int count = 0;
        for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
            for (Annotation annotation : parameterAnnotations) {
                classMethod.getRuntimeVisibleParameterAnnotationsAttribute().addAnnotation(count,
                        AnnotationBuilder.createAnnotation(constPool, annotation));
            }
            count++;
        }
        return classMethod;
    }

    /**
     * Adds a constructor with the same signiture as the given constrcutor, including exception types
     * <p>
     * TODO: annotations and signiture attribute
     */
    public ClassMethod addConstructor(Constructor<?> method) {
        ClassMethod classMethod = addMethod(method.getModifiers(), "<init>", "V", DescriptorUtils.parameterDescriptors(method
                .getParameterTypes()));
        for (Class<?> e : method.getExceptionTypes()) {
            classMethod.addCheckedExceptions((Class<? extends Exception>) e);
        }
        for (Annotation annotation : method.getDeclaredAnnotations()) {
            classMethod.getRuntimeVisibleAnnotationsAttribute().addAnnotation(
                    AnnotationBuilder.createAnnotation(constPool, annotation));
        }
        int count = 0;
        for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
            for (Annotation annotation : parameterAnnotations) {
                classMethod.getRuntimeVisibleParameterAnnotationsAttribute().addAnnotation(count,
                        AnnotationBuilder.createAnnotation(constPool, annotation));
            }
            count++;
        }
        return classMethod;
    }

    public void write(ByteArrayDataOutputStream stream) throws IOException {
        // first make sure everything we need is in the const pool
        short nameIndex = constPool.addClassEntry(name);
        short superClassIndex = constPool.addClassEntry(superclass);

        List<Short> interfaceIndexes = new ArrayList<Short>(interfaces.size());
        for (String i : interfaces) {
            interfaceIndexes.add(constPool.addClassEntry(i));
        }

        stream.writeInt(0xCAFEBABE);// magic
        stream.writeInt(version);
        constPool.write(stream);
        stream.writeShort(accessFlags);
        stream.writeShort(nameIndex);
        stream.writeShort(superClassIndex);
        stream.writeShort(interfaceIndexes.size()); // interface count
        for (short i : interfaceIndexes) {
            stream.writeShort(i);
        }
        stream.writeShort(fields.size()); // field count
        for (ClassField field : fields) {
            field.write(stream);
        }
        stream.writeShort(methods.size()); // method count
        for (ClassMethod method : methods) {
            method.write(stream);
        }
        stream.writeShort(attributes.size()); // attribute count
        for (Attribute attribute : attributes) {
            attribute.write(stream);
        }
    }

    private static java.lang.reflect.Method defineClass1, defineClass2;

    static {
        try {
            AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                public Object run() throws Exception {
                    Class<?> cl = Class.forName("java.lang.ClassLoader", false, null);
                    defineClass1 = cl.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class,
                            int.class });
                    defineClass1.setAccessible(true);
                    defineClass2 = cl.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class,
                            int.class, ProtectionDomain.class });
                    defineClass2.setAccessible(true);
                    return null;
                }
            });
        } catch (PrivilegedActionException pae) {
            throw new RuntimeException("cannot initialize ClassFile", pae.getException());
        }
    }

    public Class<?> define(ClassLoader loader) {
        return define(loader, null);
    }

    /**
     * Definines the class using the given ClassLoader and ProtectionDomain
     */
    public Class<?> define(ClassLoader loader, ProtectionDomain domain) {
        try {
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                final int index = name.lastIndexOf('.');
                final String packageName;
                if(index == -1 ) {
                    packageName = "";
                } else {
                    packageName = name.substring(0, index);
                }
                RuntimePermission permission = new RuntimePermission("defineClassInPackage." + packageName);
                sm.checkPermission(permission);
            }
            byte[] b = toBytecode();
            java.lang.reflect.Method method;
            Object[] args;
            if (domain == null) {
                method = defineClass1;
                args = new Object[] { name.replace('/', '.'), b, new Integer(0), new Integer(b.length) };
            } else {
                method = defineClass2;
                args = new Object[] { name.replace('/', '.'), b, new Integer(0), new Integer(b.length), domain };
            }
            Class<?> clazz = (Class<?>) method.invoke(loader, args);
            return clazz;
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public byte[] toBytecode() {
        // TODO: throw illegal state exception if the class file is modified after writing
        if (bytecode == null) {
            try {
                ByteArrayDataOutputStream out = new ByteArrayDataOutputStream();
                write(out);
                bytecode = out.getBytes();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return bytecode;
    }

    public ConstPool getConstPool() {
        return constPool;
    }

    /**
     * returns the type descriptor for the class
     *
     * @return
     */
    public String getDescriptor() {
        return 'L' + name + ';';
    }

    public AnnotationsAttribute getRuntimeVisibleAnnotationsAttribute() {
        return runtimeVisibleAnnotationsAttribute;
    }

    /**
     * Returns the generated class name
     *
     * @return The generated class name
     */
    public String getName() {
        return name;
    }

    /**
     *
     * @return The generated superclass name
     */
    public String getSuperclass() {
        return superclass;
    }

    /**
     *
     * @return The interfaces implemented by this class
     */
    public List<String> getInterfaces() {
        return Collections.unmodifiableList(interfaces);
    }

    /**
     *
     * @return This class's fields
     */
    public Set<ClassField> getFields() {
        return Collections.unmodifiableSet(fields);
    }

    /**
     *
     * @return This classes methods
     */
    public Set<ClassMethod> getMethods() {
        return Collections.unmodifiableSet(methods);
    }

}
TOP

Related Classes of org.jboss.classfilewriter.ClassFile

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.