All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mockit.external.asm.ClassWriter Maven / Gradle / Ivy

Go to download

JMockit is a Java toolkit for automated developer testing. It contains APIs for the creation of the objects to be tested, for mocking dependencies, and for faking external APIs; JUnit (4 & 5) and TestNG test runners are supported. It also contains an advanced code coverage tool.

There is a newer version: 1.49
Show newest version
/*
 * ASM: a very small and fast Java bytecode manipulation framework
 * Copyright (c) 2000-2011 INRIA, France Telecom
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
package mockit.external.asm;

import java.util.*;

import javax.annotation.*;

import mockit.internal.util.*;

/**
 * A {@link ClassVisitor} that generates classes in bytecode form. More precisely this visitor generates a byte array
 * conforming to the Java class file format. It can be used alone, to generate a Java class "from scratch", or with one
 * or more {@link ClassReader} and adapter class visitor to generate a modified class from one or more existing Java
 * classes.
 */
public final class ClassWriter extends ClassVisitor
{
   /**
    * The class reader from which this class writer was constructed.
    */
   @Nonnull final ClassReader cr;

   /**
    * Minor and major version numbers of the class to be generated.
    */
   private int version;

   /**
    * The access flags of this class.
    */
   private int access;

   /**
    * The constant pool item that contains the internal name of this class.
    */
   private int name;

   /**
    * The internal name of this class.
    */
   String thisName;

   /**
    * The constant pool item that contains the signature of this class.
    */
   private int signature;

   /**
    * The constant pool item that contains the internal name of the super class of this class.
    */
   private int superName;

   @Nullable private Interfaces interfaces;
   @Nonnull private final SourceInfo sourceInfo;
   @Nullable private OuterClass outerClass;
   @Nullable private InnerClasses innerClasses;
   @Nonnull final BootstrapMethods bootstrapMethods;

   /**
    * The fields of this class.
    */
   @Nonnull private final List fields;

   /**
    * The methods of this class.
    */
   @Nonnull private final List methods;

   /**
    * true if the stack map frames must be recomputed from scratch.
    * 

* If this flag is set, then the stack map frames are recomputed from the methods bytecode. The arguments of the * {@link MethodVisitor#visitMaxStack} method are also ignored and recomputed from the bytecode. In other words, * computeFrames implies computeMaxs. */ private final boolean computeFrames; /** * Constructs a new {@link ClassWriter} object and enables optimizations for "mostly add" bytecode transformations. * These optimizations are the following: *

    *
  • The constant pool from the original class is copied as is in the new class, which saves time. * New constant pool entries will be added at the end if necessary, but unused constant pool entries won't be * removed.
  • *
  • Methods that are not transformed are copied as is in the new class, directly from the original class bytecode * (i.e. without emitting visit events for all the method instructions), which saves a lot of time. * Untransformed methods are detected by the fact that the {@link ClassReader} receives {@link MethodVisitor} * objects that come from a {@link ClassWriter} (and not from any other {@link ClassVisitor} instance).
  • *
* * @param classReader the {@link ClassReader} used to read the original class. It will be used to copy the entire * constant pool from the original class and also to copy other fragments of original bytecode * where applicable. */ public ClassWriter(@Nonnull ClassReader classReader) { cp = new ConstantPoolGeneration(); sourceInfo = new SourceInfo(cp); bootstrapMethods = new BootstrapMethods(cp); version = classReader.getVersion(); computeFrames = version >= ClassVersion.V1_7; cr = classReader; classReader.copyPool(this); fields = new ArrayList(); methods = new ArrayList(); } // ------------------------------------------------------------------------ // Implementation of the ClassVisitor base class // ------------------------------------------------------------------------ @Override public void visit( int version, int access, @Nonnull String name, @Nullable String signature, @Nullable String superName, @Nullable String[] interfaces ) { this.version = version; this.access = access; this.name = cp.newClass(name); thisName = name; if (signature != null) { this.signature = cp.newUTF8(signature); } this.superName = superName == null ? 0 : cp.newClass(superName); if (interfaces != null && interfaces.length > 0) { this.interfaces = new Interfaces(cp, interfaces); } if (superName != null) { ClassLoad.addSuperClass(name, superName); } } @Override public void visitSource(@Nullable String file, @Nullable String debug) { sourceInfo.add(file, debug); } @Override public void visitOuterClass(@Nonnull String owner, @Nullable String name, @Nullable String desc) { outerClass = new OuterClass(cp, owner, name, desc); } @Nonnull @Override public AnnotationVisitor visitAnnotation(@Nonnull String desc) { return addAnnotation(desc); } @Override public void visitInnerClass( @Nonnull String name, @Nullable String outerName, @Nullable String innerName, int access ) { if (innerClasses == null) { innerClasses = new InnerClasses(cp); } innerClasses.add(name, outerName, innerName, access); } @Nonnull @Override public FieldVisitor visitField( int access, @Nonnull String name, @Nonnull String desc, @Nullable String signature, @Nullable Object value ) { FieldWriter field = new FieldWriter(this, access, name, desc, signature, value); fields.add(field); return field; } @Nonnull @Override public MethodWriter visitMethod( int access, @Nonnull String name, @Nonnull String desc, @Nullable String signature, @Nullable String[] exceptions ) { MethodWriter method = new MethodWriter(this, access, name, desc, signature, exceptions, computeFrames); methods.add(method); return method; } // ------------------------------------------------------------------------ // Other public methods // ------------------------------------------------------------------------ /** * Returns the bytecode of the class that was build with this class writer. */ @Nonnull @Override public byte[] toByteArray() { cp.checkConstantPoolMaxSize(); // Computes the real size of the bytecode of this class. int interfaceCount = interfaces == null ? 0 : interfaces.getCount(); int size = 24 + 2 * interfaceCount; size += getFieldsSize(); size += getMethodsSize(); int attributeCount = 0; if (bootstrapMethods.hasMethods()) { // We put it as first attribute in order to improve a bit the performance of copyBootstrapMethods. attributeCount++; size += bootstrapMethods.getSize(); } if (signature != 0) { attributeCount++; size += 8; cp.newUTF8("Signature"); } attributeCount += sourceInfo.getAttributeCount(); size += sourceInfo.getSize(); if (outerClass != null) { attributeCount++; size += outerClass.getSize(); } boolean deprecated = Access.isDeprecated(access); if (deprecated) { attributeCount++; size += 6; cp.newUTF8("Deprecated"); } if (isSynthetic()) { attributeCount++; size += 6; cp.newUTF8("Synthetic"); } if (innerClasses != null) { attributeCount++; size += innerClasses.getSize(); } if (annotations != null) { attributeCount++; size += getAnnotationsSize(); } size += cp.getSize(); // Allocates a byte vector of this size, in order to avoid unnecessary arraycopy operations in the // ByteVector.enlarge() method. ByteVector out = new ByteVector(size); out.putInt(0xCAFEBABE).putInt(version); cp.put(out); int accessFlag = Access.computeFlag(access, 0); out.putShort(accessFlag); out.putShort(name); out.putShort(superName); out.putShort(interfaceCount); if (interfaceCount > 0) { interfaces.put(out); } putFields(out); putMethods(out); out.putShort(attributeCount); bootstrapMethods.put(out); putSignature(out); sourceInfo.put(out); if (outerClass != null) { outerClass.put(out); } if (deprecated) { out.putShort(cp.newUTF8("Deprecated")).putInt(0); } if (isSynthetic()) { out.putShort(cp.newUTF8("Synthetic")).putInt(0); } if (innerClasses != null) { innerClasses.put(out); } putAnnotations(out); return out.data; } @Nonnegative private int getMethodsSize() { int size = 0; for (MethodWriter mb : methods) { size += mb.getSize(); } return size; } @Nonnegative private int getFieldsSize() { int size = 0; for (FieldWriter fb : fields) { size += fb.getSize(); } return size; } private void putFields(@Nonnull ByteVector out) { out.putShort(fields.size()); for (FieldWriter fb : fields) { fb.put(out); } } private void putMethods(@Nonnull ByteVector out) { out.putShort(methods.size()); for (MethodWriter mb : methods) { mb.put(out); } } private void putSignature(@Nonnull ByteVector out) { if (signature != 0) { out.putShort(cp.newUTF8("Signature")).putInt(2).putShort(signature); } } // ------------------------------------------------------------------------ // Utility methods: version, synthetic // ------------------------------------------------------------------------ int getClassVersion() { return version & 0xFFFF; } private boolean isSynthetic() { return isSynthetic(access); } boolean isSynthetic(int access) { return Access.isSynthetic(access) && ((access & Access.SYNTHETIC_ATTRIBUTE) != 0 || getClassVersion() < ClassVersion.V1_5); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy