refactor bcel lib

This commit is contained in:
FranzHaidnor
2023-10-25 10:17:12 +08:00
parent 5776c8f8ef
commit 84c1f7f535
601 changed files with 43626 additions and 9053 deletions

View File

@@ -1,91 +0,0 @@
package haidnor.jvm;
import org.apache.commons.cli.*;
import haidnor.jvm.classloader.ClassLoader;
import haidnor.jvm.core.JavaExecutionEngine;
import haidnor.jvm.rtda.Klass;
import haidnor.jvm.rtda.KlassMethod;
import haidnor.jvm.rtda.Metaspace;
import haidnor.jvm.runtime.JVMThread;
import haidnor.jvm.util.JavaClassUtil;
import haidnor.jvm.util.JvmThreadHolder;
import lombok.SneakyThrows;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* @author wang xiang
*/
public class Main {
@SneakyThrows
public static void main(String[] args) {
String banner = """
██╗ ██╗ █████╗ ██╗██████╗ ███╗ ██╗ ██████╗ ██████╗ ██╗██╗ ██╗███╗ ███╗
██║ ██║██╔══██╗██║██╔══██╗████╗ ██║██╔═══██╗██╔══██╗ ██║██║ ██║████╗ ████║
███████║███████║██║██║ ██║██╔██╗ ██║██║ ██║██████╔╝ ██║██║ ██║██╔████╔██║
██╔══██║██╔══██║██║██║ ██║██║╚██╗██║██║ ██║██╔══██╗ ██ ██║╚██╗ ██╔╝██║╚██╔╝██║
██║ ██║██║ ██║██║██████╔╝██║ ╚████║╚██████╔╝██║ ██║ ╚█████╔╝ ╚████╔╝ ██║ ╚═╝ ██║
╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚════╝ ╚═══╝ ╚═╝ ╚═╝
""";
System.out.println(banner);
CommandLine cmd = initCommandLine(args);
// 指定从 .jar 文件运行
if (cmd.hasOption("jar")) {
String jarFilePath = cmd.getOptionValue("jar");
try (JarFile jarFile = new JarFile(jarFilePath)) {
ClassLoader bootClassLoader = new ClassLoader(jarFile, "ApplicationClassLoader");
String mainClass = jarFile.getManifest().getMainAttributes().getValue("Main-Class");
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
String className = entry.getName().replace('/', '.').substring(0, entry.getName().length() - 6);
if (className.equals(mainClass)) {
JvmThreadHolder.set(new JVMThread());
Klass mainMeteKlass = bootClassLoader.loadClass(jarFile, entry);
KlassMethod mainKlassMethod = JavaClassUtil.getMainMethod(mainMeteKlass);
Metaspace.registerJavaClass(mainMeteKlass);
JavaExecutionEngine.callMainMethod(mainKlassMethod);
return;
}
}
}
}
}
// 指定从 .class 文件运行
if (cmd.hasOption("class")) {
JvmThreadHolder.set(new JVMThread());
String path = cmd.getOptionValue("class");
ClassLoader bootClassLoader = new ClassLoader("ApplicationClassLoader");
Klass mainMeteKlass = bootClassLoader.loadClassWithAbsolutePath(path);
KlassMethod mainKlassMethod = JavaClassUtil.getMainMethod(mainMeteKlass);
Metaspace.registerJavaClass(mainMeteKlass);
JavaExecutionEngine.callMainMethod(mainKlassMethod);
}
}
private static CommandLine initCommandLine(String[] args) throws ParseException {
Option jarOption = new Option("jar", true, "运行 jar 程序");
Option classOption = new Option("class", true, "运行 .class 字节码文件");
OptionGroup optionGroup = new OptionGroup();
optionGroup.addOption(jarOption).addOption(classOption);
Options options = new Options();
options.addOptionGroup(optionGroup);
CommandLineParser parser = new DefaultParser();
return parser.parse(options, args);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,128 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel;
import org.apache.commons.lang3.ArrayUtils;
/**
* Exception constants.
*
* @since 6.0 (intended to replace the InstructionConstant interface)
*/
public final class ExceptionConst {
/**
* Enum corresponding to the various Exception Class arrays, used by
* {@link ExceptionConst#createExceptions(EXCS, Class...)}
*/
public enum EXCS {
EXCS_CLASS_AND_INTERFACE_RESOLUTION, EXCS_FIELD_AND_METHOD_RESOLUTION, EXCS_INTERFACE_METHOD_RESOLUTION, EXCS_STRING_RESOLUTION, EXCS_ARRAY_EXCEPTION,
}
/**
* The mother of all exceptions
*/
public static final Class<Throwable> THROWABLE = Throwable.class;
/**
* Super class of any run-time exception
*/
public static final Class<RuntimeException> RUNTIME_EXCEPTION = RuntimeException.class;
/**
* Super class of any linking exception (aka Linkage Error)
*/
public static final Class<LinkageError> LINKING_EXCEPTION = LinkageError.class;
/**
* Linking Exceptions
*/
public static final Class<ClassCircularityError> CLASS_CIRCULARITY_ERROR = ClassCircularityError.class;
public static final Class<ClassFormatError> CLASS_FORMAT_ERROR = ClassFormatError.class;
public static final Class<ExceptionInInitializerError> EXCEPTION_IN_INITIALIZER_ERROR = ExceptionInInitializerError.class;
public static final Class<IncompatibleClassChangeError> INCOMPATIBLE_CLASS_CHANGE_ERROR = IncompatibleClassChangeError.class;
public static final Class<AbstractMethodError> ABSTRACT_METHOD_ERROR = AbstractMethodError.class;
public static final Class<IllegalAccessError> ILLEGAL_ACCESS_ERROR = IllegalAccessError.class;
public static final Class<InstantiationError> INSTANTIATION_ERROR = InstantiationError.class;
public static final Class<NoSuchFieldError> NO_SUCH_FIELD_ERROR = NoSuchFieldError.class;
public static final Class<NoSuchMethodError> NO_SUCH_METHOD_ERROR = NoSuchMethodError.class;
public static final Class<NoClassDefFoundError> NO_CLASS_DEF_FOUND_ERROR = NoClassDefFoundError.class;
public static final Class<UnsatisfiedLinkError> UNSATISFIED_LINK_ERROR = UnsatisfiedLinkError.class;
public static final Class<VerifyError> VERIFY_ERROR = VerifyError.class;
/* UnsupportedClassVersionError is new in JDK 1.2 */
// public static final Class UnsupportedClassVersionError = UnsupportedClassVersionError.class;
/**
* Run-Time Exceptions
*/
public static final Class<NullPointerException> NULL_POINTER_EXCEPTION = NullPointerException.class;
public static final Class<ArrayIndexOutOfBoundsException> ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION = ArrayIndexOutOfBoundsException.class;
public static final Class<ArithmeticException> ARITHMETIC_EXCEPTION = ArithmeticException.class;
public static final Class<NegativeArraySizeException> NEGATIVE_ARRAY_SIZE_EXCEPTION = NegativeArraySizeException.class;
public static final Class<ClassCastException> CLASS_CAST_EXCEPTION = ClassCastException.class;
public static final Class<IllegalMonitorStateException> ILLEGAL_MONITOR_STATE = IllegalMonitorStateException.class;
/**
* Pre-defined exception arrays according to chapters 5.1-5.4 of the Java Virtual Machine Specification
*/
private static final Class<?>[] EXCS_CLASS_AND_INTERFACE_RESOLUTION = {NO_CLASS_DEF_FOUND_ERROR, CLASS_FORMAT_ERROR, VERIFY_ERROR, ABSTRACT_METHOD_ERROR,
EXCEPTION_IN_INITIALIZER_ERROR, ILLEGAL_ACCESS_ERROR}; // Chapter 5.1
private static final Class<?>[] EXCS_FIELD_AND_METHOD_RESOLUTION = {NO_SUCH_FIELD_ERROR, ILLEGAL_ACCESS_ERROR, NO_SUCH_METHOD_ERROR}; // Chapter 5.2
/**
* Empty array.
*/
private static final Class<?>[] EXCS_INTERFACE_METHOD_RESOLUTION = new Class[0]; // Chapter 5.3 (as below)
/**
* Empty array.
*/
private static final Class<?>[] EXCS_STRING_RESOLUTION = new Class[0];
// Chapter 5.4 (no errors but the ones that _always_ could happen! How stupid.)
private static final Class<?>[] EXCS_ARRAY_EXCEPTION = {NULL_POINTER_EXCEPTION, ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION};
/**
* Creates a copy of the specified Exception Class array combined with any additional Exception classes.
*
* @param type the basic array type
* @param extraClasses additional classes, if any
* @return the merged array
*/
public static Class<?>[] createExceptions(final EXCS type, final Class<?>... extraClasses) {
switch (type) {
case EXCS_CLASS_AND_INTERFACE_RESOLUTION:
return mergeExceptions(EXCS_CLASS_AND_INTERFACE_RESOLUTION, extraClasses);
case EXCS_ARRAY_EXCEPTION:
return mergeExceptions(EXCS_ARRAY_EXCEPTION, extraClasses);
case EXCS_FIELD_AND_METHOD_RESOLUTION:
return mergeExceptions(EXCS_FIELD_AND_METHOD_RESOLUTION, extraClasses);
case EXCS_INTERFACE_METHOD_RESOLUTION:
return mergeExceptions(EXCS_INTERFACE_METHOD_RESOLUTION, extraClasses);
case EXCS_STRING_RESOLUTION:
return mergeExceptions(EXCS_STRING_RESOLUTION, extraClasses);
default:
throw new AssertionError("Cannot happen; unexpected enum value: " + type);
}
}
// helper method to merge exception class arrays
private static Class<?>[] mergeExceptions(final Class<?>[] input, final Class<?>... extraClasses) {
return ArrayUtils.addAll(input, extraClasses);
}
}

View File

@@ -0,0 +1,215 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel;
import haidnor.jvm.bcel.classfile.JavaClass;
import haidnor.jvm.bcel.util.ClassPath;
import haidnor.jvm.bcel.util.SyntheticRepository;
import java.io.IOException;
/**
* The repository maintains informations about class interdependencies, e.g., whether a class is a sub-class of another.
* Delegates actual class loading to SyntheticRepository with current class path by default.
*
* @see haidnor.jvm.bcel.util.Repository
* @see SyntheticRepository
*/
public abstract class Repository {
private static haidnor.jvm.bcel.util.Repository repository = SyntheticRepository.getInstance();
/**
* Adds clazz to repository if there isn't an equally named class already in there.
*
* @return old entry in repository
*/
public static JavaClass addClass(final JavaClass clazz) {
final JavaClass old = repository.findClass(clazz.getClassName());
repository.storeClass(clazz);
return old;
}
/**
* Clears the repository.
*/
public static void clearCache() {
repository.clear();
}
/**
* @return all interfaces implemented by class and its super classes and the interfaces that those interfaces extend,
* and so on. (Some people call this a transitive hull).
* @throws ClassNotFoundException if any of the class's superclasses or superinterfaces can't be found
*/
public static JavaClass[] getInterfaces(final JavaClass clazz) throws ClassNotFoundException {
return clazz.getAllInterfaces();
}
/**
* @return all interfaces implemented by class and its super classes and the interfaces that extend those interfaces,
* and so on
* @throws ClassNotFoundException if the named class can't be found, or if any of its superclasses or superinterfaces
* can't be found
*/
public static JavaClass[] getInterfaces(final String className) throws ClassNotFoundException {
return getInterfaces(lookupClass(className));
}
/**
* @return currently used repository instance
*/
public static haidnor.jvm.bcel.util.Repository getRepository() {
return repository;
}
/**
* @return list of super classes of clazz in ascending order, i.e., Object is always the last element
* @throws ClassNotFoundException if any of the superclasses can't be found
*/
public static JavaClass[] getSuperClasses(final JavaClass clazz) throws ClassNotFoundException {
return clazz.getSuperClasses();
}
/**
* @return list of super classes of clazz in ascending order, i.e., Object is always the last element.
* @throws ClassNotFoundException if the named class or any of its superclasses can't be found
*/
public static JavaClass[] getSuperClasses(final String className) throws ClassNotFoundException {
return getSuperClasses(lookupClass(className));
}
/**
* @return true, if clazz is an implementation of interface inter
* @throws ClassNotFoundException if any superclasses or superinterfaces of clazz can't be found
*/
public static boolean implementationOf(final JavaClass clazz, final JavaClass inter) throws ClassNotFoundException {
return clazz.implementationOf(inter);
}
/**
* @return true, if clazz is an implementation of interface inter
* @throws ClassNotFoundException if inter or any superclasses or superinterfaces of clazz can't be found
*/
public static boolean implementationOf(final JavaClass clazz, final String inter) throws ClassNotFoundException {
return implementationOf(clazz, lookupClass(inter));
}
/**
* @return true, if clazz is an implementation of interface inter
* @throws ClassNotFoundException if clazz or any superclasses or superinterfaces of clazz can't be found
*/
public static boolean implementationOf(final String clazz, final JavaClass inter) throws ClassNotFoundException {
return implementationOf(lookupClass(clazz), inter);
}
/**
* @return true, if clazz is an implementation of interface inter
* @throws ClassNotFoundException if clazz, inter, or any superclasses or superinterfaces of clazz can't be found
*/
public static boolean implementationOf(final String clazz, final String inter) throws ClassNotFoundException {
return implementationOf(lookupClass(clazz), lookupClass(inter));
}
/**
* Equivalent to runtime "instanceof" operator.
*
* @return true, if clazz is an instance of superclass
* @throws ClassNotFoundException if any superclasses or superinterfaces of clazz can't be found
*/
public static boolean instanceOf(final JavaClass clazz, final JavaClass superclass) throws ClassNotFoundException {
return clazz.instanceOf(superclass);
}
/**
* @return true, if clazz is an instance of superclass
* @throws ClassNotFoundException if superclass can't be found
*/
public static boolean instanceOf(final JavaClass clazz, final String superclass) throws ClassNotFoundException {
return instanceOf(clazz, lookupClass(superclass));
}
/**
* @return true, if clazz is an instance of superclass
* @throws ClassNotFoundException if clazz can't be found
*/
public static boolean instanceOf(final String clazz, final JavaClass superclass) throws ClassNotFoundException {
return instanceOf(lookupClass(clazz), superclass);
}
/**
* @return true, if clazz is an instance of superclass
* @throws ClassNotFoundException if either clazz or superclass can't be found
*/
public static boolean instanceOf(final String clazz, final String superclass) throws ClassNotFoundException {
return instanceOf(lookupClass(clazz), lookupClass(superclass));
}
/**
* Tries to find class source using the internal repository instance.
*
* @see Class
* @return JavaClass object for given runtime class
* @throws ClassNotFoundException if the class could not be found or parsed correctly
*/
public static JavaClass lookupClass(final Class<?> clazz) throws ClassNotFoundException {
return repository.loadClass(clazz);
}
/**
* Lookups class somewhere found on your CLASSPATH, or whereever the repository instance looks for it.
*
* @return class object for given fully qualified class name
* @throws ClassNotFoundException if the class could not be found or parsed correctly
*/
public static JavaClass lookupClass(final String className) throws ClassNotFoundException {
return repository.loadClass(className);
}
/**
* @return class file object for given Java class by looking on the system class path; returns null if the class file
* can't be found
*/
public static ClassPath.ClassFile lookupClassFile(final String className) {
try (ClassPath path = repository.getClassPath()) {
return path == null ? null : path.getClassFile(className);
} catch (final IOException e) {
return null;
}
}
/**
* Removes given class from repository.
*/
public static void removeClass(final JavaClass clazz) {
repository.removeClass(clazz);
}
/**
* Removes class with given (fully qualified) name from repository.
*/
public static void removeClass(final String clazz) {
repository.removeClass(repository.findClass(clazz));
}
/**
* Sets repository instance to be used for class loading
*/
public static void setRepository(final haidnor.jvm.bcel.util.Repository rep) {
repository = rep;
}
}

View File

@@ -0,0 +1,211 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
/**
* Super class for all objects that have modifiers like private, final, ... I.e. classes, fields, and methods.
*/
public abstract class AccessFlags {
/**
* @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
*/
@java.lang.Deprecated
protected int access_flags; // TODO not used externally at present
public AccessFlags() {
}
/**
* @param a initial access flags
*/
public AccessFlags(final int a) {
access_flags = a;
}
/**
* @return Access flags of the object aka. "modifiers".
*/
public final int getAccessFlags() {
return access_flags;
}
/**
* @return Access flags of the object aka. "modifiers".
*/
public final int getModifiers() {
return access_flags;
}
public final boolean isAbstract() {
return (access_flags & Const.ACC_ABSTRACT) != 0;
}
public final void isAbstract(final boolean flag) {
setFlag(Const.ACC_ABSTRACT, flag);
}
public final boolean isAnnotation() {
return (access_flags & Const.ACC_ANNOTATION) != 0;
}
public final void isAnnotation(final boolean flag) {
setFlag(Const.ACC_ANNOTATION, flag);
}
public final boolean isEnum() {
return (access_flags & Const.ACC_ENUM) != 0;
}
public final void isEnum(final boolean flag) {
setFlag(Const.ACC_ENUM, flag);
}
public final boolean isFinal() {
return (access_flags & Const.ACC_FINAL) != 0;
}
public final void isFinal(final boolean flag) {
setFlag(Const.ACC_FINAL, flag);
}
public final boolean isInterface() {
return (access_flags & Const.ACC_INTERFACE) != 0;
}
public final void isInterface(final boolean flag) {
setFlag(Const.ACC_INTERFACE, flag);
}
public final boolean isNative() {
return (access_flags & Const.ACC_NATIVE) != 0;
}
public final void isNative(final boolean flag) {
setFlag(Const.ACC_NATIVE, flag);
}
public final boolean isPrivate() {
return (access_flags & Const.ACC_PRIVATE) != 0;
}
public final void isPrivate(final boolean flag) {
setFlag(Const.ACC_PRIVATE, flag);
}
public final boolean isProtected() {
return (access_flags & Const.ACC_PROTECTED) != 0;
}
public final void isProtected(final boolean flag) {
setFlag(Const.ACC_PROTECTED, flag);
}
public final boolean isPublic() {
return (access_flags & Const.ACC_PUBLIC) != 0;
}
public final void isPublic(final boolean flag) {
setFlag(Const.ACC_PUBLIC, flag);
}
public final boolean isStatic() {
return (access_flags & Const.ACC_STATIC) != 0;
}
public final void isStatic(final boolean flag) {
setFlag(Const.ACC_STATIC, flag);
}
public final boolean isStrictfp() {
return (access_flags & Const.ACC_STRICT) != 0;
}
public final void isStrictfp(final boolean flag) {
setFlag(Const.ACC_STRICT, flag);
}
public final boolean isSynchronized() {
return (access_flags & Const.ACC_SYNCHRONIZED) != 0;
}
public final void isSynchronized(final boolean flag) {
setFlag(Const.ACC_SYNCHRONIZED, flag);
}
public final boolean isSynthetic() {
return (access_flags & Const.ACC_SYNTHETIC) != 0;
}
public final void isSynthetic(final boolean flag) {
setFlag(Const.ACC_SYNTHETIC, flag);
}
public final boolean isTransient() {
return (access_flags & Const.ACC_TRANSIENT) != 0;
}
public final void isTransient(final boolean flag) {
setFlag(Const.ACC_TRANSIENT, flag);
}
public final boolean isVarArgs() {
return (access_flags & Const.ACC_VARARGS) != 0;
}
public final void isVarArgs(final boolean flag) {
setFlag(Const.ACC_VARARGS, flag);
}
public final boolean isVolatile() {
return (access_flags & Const.ACC_VOLATILE) != 0;
}
public final void isVolatile(final boolean flag) {
setFlag(Const.ACC_VOLATILE, flag);
}
/**
* Set access flags aka "modifiers".
*
* @param accessFlags Access flags of the object.
*/
public final void setAccessFlags(final int accessFlags) {
this.access_flags = accessFlags;
}
private void setFlag(final int flag, final boolean set) {
if ((access_flags & flag) != 0) { // Flag is set already
if (!set) {
access_flags ^= flag;
}
} else if (set) {
access_flags |= flag;
}
}
/**
* Set access flags aka "modifiers".
*
* @param accessFlags Access flags of the object.
*/
public final void setModifiers(final int accessFlags) {
setAccessFlags(accessFlags);
}
}

View File

@@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Represents the default value of a annotation for a method info.
*
* @since 6.0
*/
public class AnnotationDefault extends Attribute {
private ElementValue defaultValue;
/**
* @param nameIndex Index pointing to the name <em>Code</em>
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
*/
AnnotationDefault(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, (ElementValue) null, constantPool);
defaultValue = ElementValue.readElementValue(input, constantPool);
}
/**
* @param nameIndex Index pointing to the name <em>Code</em>
* @param length Content length in bytes
* @param defaultValue the annotation's default value
* @param constantPool Array of constants
*/
public AnnotationDefault(final int nameIndex, final int length, final ElementValue defaultValue, final ConstantPool constantPool) {
super(Const.ATTR_ANNOTATION_DEFAULT, nameIndex, length, constantPool);
this.defaultValue = defaultValue;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitAnnotationDefault(this);
}
@Override
public Attribute copy(final ConstantPool constantPool) {
return (Attribute) clone();
}
@Override
public final void dump(final DataOutputStream dos) throws IOException {
super.dump(dos);
defaultValue.dump(dos);
}
/**
* @return the default value
*/
public final ElementValue getDefaultValue() {
return defaultValue;
}
/**
* @param defaultValue the default value of this methodinfo's annotation
*/
public final void setDefaultValue(final ElementValue defaultValue) {
this.defaultValue = defaultValue;
}
}

View File

@@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* @since 6.0
*/
public class AnnotationElementValue extends ElementValue {
// For annotation element values, this is the annotation
private final AnnotationEntry annotationEntry;
public AnnotationElementValue(final int type, final AnnotationEntry annotationEntry, final ConstantPool cpool) {
super(type, cpool);
if (type != ANNOTATION) {
throw new ClassFormatException("Only element values of type annotation can be built with this ctor - type specified: " + type);
}
this.annotationEntry = annotationEntry;
}
@Override
public void dump(final DataOutputStream dos) throws IOException {
dos.writeByte(super.getType()); // u1 type of value (ANNOTATION == '@')
annotationEntry.dump(dos);
}
public AnnotationEntry getAnnotationEntry() {
return annotationEntry;
}
@Override
public String stringifyValue() {
return annotationEntry.toString();
}
@Override
public String toString() {
return stringifyValue();
}
}

View File

@@ -0,0 +1,162 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
* Represents one annotation in the annotation table
*
* @since 6.0
*/
public class AnnotationEntry implements Node {
public static final AnnotationEntry[] EMPTY_ARRAY = {};
public static AnnotationEntry[] createAnnotationEntries(final Attribute[] attrs) {
// Find attributes that contain annotation data
return Stream.of(attrs).filter(Annotations.class::isInstance).flatMap(e -> Stream.of(((Annotations) e).getAnnotationEntries()))
.toArray(AnnotationEntry[]::new);
}
/**
* Factory method to create an AnnotionEntry from a DataInput
*
* @param input
* @param constantPool
* @param isRuntimeVisible
* @return the entry
* @throws IOException if an I/O error occurs.
*/
public static AnnotationEntry read(final DataInput input, final ConstantPool constantPool, final boolean isRuntimeVisible) throws IOException {
final AnnotationEntry annotationEntry = new AnnotationEntry(input.readUnsignedShort(), constantPool, isRuntimeVisible);
final int numElementValuePairs = input.readUnsignedShort();
annotationEntry.elementValuePairs = new ArrayList<>();
for (int i = 0; i < numElementValuePairs; i++) {
annotationEntry.elementValuePairs
.add(new ElementValuePair(input.readUnsignedShort(), ElementValue.readElementValue(input, constantPool), constantPool));
}
return annotationEntry;
}
private final int typeIndex;
private final ConstantPool constantPool;
private final boolean isRuntimeVisible;
private List<ElementValuePair> elementValuePairs;
public AnnotationEntry(final int typeIndex, final ConstantPool constantPool, final boolean isRuntimeVisible) {
this.typeIndex = typeIndex;
this.constantPool = constantPool;
this.isRuntimeVisible = isRuntimeVisible;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitAnnotationEntry(this);
}
public void addElementNameValuePair(final ElementValuePair elementNameValuePair) {
elementValuePairs.add(elementNameValuePair);
}
public void dump(final DataOutputStream dos) throws IOException {
dos.writeShort(typeIndex); // u2 index of type name in cpool
dos.writeShort(elementValuePairs.size()); // u2 element_value pair
// count
for (final ElementValuePair envp : elementValuePairs) {
envp.dump(dos);
}
}
/**
* @return the annotation type name
*/
public String getAnnotationType() {
return constantPool.getConstantUtf8(typeIndex).getBytes();
}
/**
* @return the annotation type index
*/
public int getAnnotationTypeIndex() {
return typeIndex;
}
public ConstantPool getConstantPool() {
return constantPool;
}
/**
* @return the element value pairs in this annotation entry
*/
public ElementValuePair[] getElementValuePairs() {
// TODO return List
return elementValuePairs.toArray(ElementValuePair.EMPTY_ARRAY);
}
/**
* @return the number of element value pairs in this annotation entry
*/
public final int getNumElementValuePairs() {
return elementValuePairs.size();
}
public int getTypeIndex() {
return typeIndex;
}
public boolean isRuntimeVisible() {
return isRuntimeVisible;
}
public String toShortString() {
final StringBuilder result = new StringBuilder();
result.append("@");
result.append(getAnnotationType());
final ElementValuePair[] evPairs = getElementValuePairs();
if (evPairs.length > 0) {
result.append("(");
for (final ElementValuePair element : evPairs) {
result.append(element.toShortString());
result.append(", ");
}
// remove last ", "
result.setLength(result.length() - 2);
result.append(")");
}
return result.toString();
}
@Override
public String toString() {
return toShortString();
}
}

View File

@@ -0,0 +1,158 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.stream.Stream;
/**
* base class for annotations
*
* @since 6.0
*/
public abstract class Annotations extends Attribute implements Iterable<AnnotationEntry> {
private AnnotationEntry[] annotationTable;
private final boolean isRuntimeVisible;
/**
* Constructs an instance.
*
* @param annotationType the subclass type of the annotation
* @param nameIndex Index pointing to the name <em>Code</em>
* @param length Content length in bytes
* @param annotationTable the actual annotations
* @param constantPool Array of constants
* @param isRuntimeVisible whether this Annotation visible at runtime
*/
public Annotations(final byte annotationType, final int nameIndex, final int length, final AnnotationEntry[] annotationTable,
final ConstantPool constantPool, final boolean isRuntimeVisible) {
super(annotationType, nameIndex, length, constantPool);
this.annotationTable = annotationTable;
this.isRuntimeVisible = isRuntimeVisible;
}
/**
* Constructs an instance.
*
* @param annotationType the subclass type of the annotation
* @param nameIndex Index pointing to the name <em>Code</em>
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @param isRuntimeVisible whether this Annotation visible at runtime
* @throws IOException if an I/O error occurs.
*/
Annotations(final byte annotationType, final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool,
final boolean isRuntimeVisible) throws IOException {
this(annotationType, nameIndex, length, (AnnotationEntry[]) null, constantPool, isRuntimeVisible);
final int annotationTableLength = input.readUnsignedShort();
annotationTable = new AnnotationEntry[annotationTableLength];
for (int i = 0; i < annotationTableLength; i++) {
annotationTable[i] = AnnotationEntry.read(input, constantPool, isRuntimeVisible);
}
}
/**
* Called by objects that are traversing the nodes of the tree implicitly
* defined by the contents of a Java class. I.e., the hierarchy of methods,
* fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitAnnotation(this);
}
@Override
public Attribute copy(final ConstantPool constantPool) {
// TODO Auto-generated method stub
return null;
}
/**
* Gets the array of annotation entries in this annotation
*/
public AnnotationEntry[] getAnnotationEntries() {
return annotationTable;
}
/**
* Gets the number of annotation entries in this annotation.
*
* @return the number of annotation entries in this annotation
*/
public final int getNumAnnotations() {
if (annotationTable == null) {
return 0;
}
return annotationTable.length;
}
public boolean isRuntimeVisible() {
return isRuntimeVisible;
}
@Override
public Iterator<AnnotationEntry> iterator() {
return Stream.of(annotationTable).iterator();
}
/**
* Sets the entries to set in this annotation.
*
* @param annotationTable the entries to set in this annotation
*/
public final void setAnnotationTable(final AnnotationEntry[] annotationTable) {
this.annotationTable = annotationTable;
}
/**
* Converts to a String representation.
*
* @return String representation
*/
@Override
public final String toString() {
final StringBuilder buf = new StringBuilder(Const.getAttributeName(getTag()));
buf.append(":\n");
for (int i = 0; i < annotationTable.length; i++) {
buf.append(" ").append(annotationTable[i]);
if (i < annotationTable.length - 1) {
buf.append('\n');
}
}
return buf.toString();
}
protected void writeAnnotations(final DataOutputStream dos) throws IOException {
if (annotationTable == null) {
return;
}
dos.writeShort(annotationTable.length);
for (final AnnotationEntry element : annotationTable) {
element.dump(dos);
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* @since 6.0
*/
public class ArrayElementValue extends ElementValue {
// For array types, this is the array
private final ElementValue[] elementValues;
public ArrayElementValue(final int type, final ElementValue[] datums, final ConstantPool cpool) {
super(type, cpool);
if (type != ARRAY) {
throw new ClassFormatException("Only element values of type array can be built with this ctor - type specified: " + type);
}
this.elementValues = datums;
}
@Override
public void dump(final DataOutputStream dos) throws IOException {
dos.writeByte(super.getType()); // u1 type of value (ARRAY == '[')
dos.writeShort(elementValues.length);
for (final ElementValue evalue : elementValues) {
evalue.dump(dos);
}
}
public ElementValue[] getElementValuesArray() {
return elementValues;
}
public int getElementValuesArraySize() {
return elementValues.length;
}
@Override
public String stringifyValue() {
final StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < elementValues.length; i++) {
sb.append(elementValues[i].stringifyValue());
if (i + 1 < elementValues.length) {
sb.append(",");
}
}
sb.append("]");
return sb.toString();
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("{");
for (int i = 0; i < elementValues.length; i++) {
sb.append(elementValues[i]);
if (i + 1 < elementValues.length) {
sb.append(",");
}
}
sb.append("}");
return sb.toString();
}
}

View File

@@ -0,0 +1,367 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Abstract super class for <em>Attribute</em> objects. Currently the <em>ConstantValue</em>, <em>SourceFile</em>, <em>Code</em>, <em>Exceptiontable</em>,
* <em>LineNumberTable</em>, <em>LocalVariableTable</em>, <em>InnerClasses</em> and <em>Synthetic</em> attributes are supported. The <em>Unknown</em> attribute
* stands for non-standard-attributes.
*
* <pre>
* attribute_info {
* u2 attribute_name_index;
* u4 attribute_length;
* u1 info[attribute_length];
* }
* </pre>
*
* @see ConstantValue
* @see SourceFile
* @see Code
* @see Unknown
* @see ExceptionTable
* @see LineNumberTable
* @see LocalVariableTable
* @see InnerClasses
* @see Synthetic
* @see Deprecated
* @see Signature
*/
public abstract class Attribute implements Cloneable, Node {
private static final boolean debug = Boolean.getBoolean(Attribute.class.getCanonicalName() + ".debug"); // Debugging on/off
private static final Map<String, Object> READERS = new HashMap<>();
/**
* Empty array.
*
* @since 6.6.0
*/
public static final Attribute[] EMPTY_ARRAY = {};
/**
* Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the
* standard attributes such as "LineNumberTable", because those are handled internally.
*
* @param name the name of the attribute as stored in the class file
* @param unknownAttributeReader the reader object
*/
public static void addAttributeReader(final String name, final UnknownAttributeReader unknownAttributeReader) {
READERS.put(name, unknownAttributeReader);
}
protected static void println(final String msg) {
if (debug) {
System.err.println(msg);
}
}
/**
* Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
* is called by the Field and Method constructor methods.
*
* @see JavaField
* @see JavaMethod
*
* @param dataInput Input stream
* @param constantPool Array of constants
* @return Attribute
* @throws IOException if an I/O error occurs.
* @since 6.0
*/
public static Attribute readAttribute(final DataInput dataInput, final ConstantPool constantPool) throws IOException {
byte tag = Const.ATTR_UNKNOWN; // Unknown attribute
// Get class name from constant pool via 'name_index' indirection
final int nameIndex = dataInput.readUnsignedShort();
final String name = constantPool.getConstantUtf8(nameIndex).getBytes();
// Length of data in bytes
final int length = dataInput.readInt();
// Compare strings to find known attribute
for (byte i = 0; i < Const.KNOWN_ATTRIBUTES; i++) {
if (name.equals(Const.getAttributeName(i))) {
tag = i; // found!
break;
}
}
// Call proper constructor, depending on 'tag'
switch (tag) {
case Const.ATTR_UNKNOWN:
final Object r = READERS.get(name);
if (r instanceof UnknownAttributeReader) {
return ((UnknownAttributeReader) r).createAttribute(nameIndex, length, dataInput, constantPool);
}
return new Unknown(nameIndex, length, dataInput, constantPool);
case Const.ATTR_CONSTANT_VALUE:
return new ConstantValue(nameIndex, length, dataInput, constantPool);
case Const.ATTR_SOURCE_FILE:
return new SourceFile(nameIndex, length, dataInput, constantPool);
case Const.ATTR_CODE:
return new Code(nameIndex, length, dataInput, constantPool);
case Const.ATTR_EXCEPTIONS:
return new ExceptionTable(nameIndex, length, dataInput, constantPool);
case Const.ATTR_LINE_NUMBER_TABLE:
return new LineNumberTable(nameIndex, length, dataInput, constantPool);
case Const.ATTR_LOCAL_VARIABLE_TABLE:
return new LocalVariableTable(nameIndex, length, dataInput, constantPool);
case Const.ATTR_INNER_CLASSES:
return new InnerClasses(nameIndex, length, dataInput, constantPool);
case Const.ATTR_SYNTHETIC:
return new Synthetic(nameIndex, length, dataInput, constantPool);
case Const.ATTR_DEPRECATED:
return new Deprecated(nameIndex, length, dataInput, constantPool);
case Const.ATTR_PMG:
return new PMGClass(nameIndex, length, dataInput, constantPool);
case Const.ATTR_SIGNATURE:
return new Signature(nameIndex, length, dataInput, constantPool);
case Const.ATTR_STACK_MAP:
// old style stack map: unneeded for JDK5 and below;
// illegal(?) for JDK6 and above. So just delete with a warning.
println("Warning: Obsolete StackMap attribute ignored.");
return new Unknown(nameIndex, length, dataInput, constantPool);
case Const.ATTR_RUNTIME_VISIBLE_ANNOTATIONS:
return new RuntimeVisibleAnnotations(nameIndex, length, dataInput, constantPool);
case Const.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS:
return new RuntimeInvisibleAnnotations(nameIndex, length, dataInput, constantPool);
case Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS:
return new RuntimeVisibleParameterAnnotations(nameIndex, length, dataInput, constantPool);
case Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS:
return new RuntimeInvisibleParameterAnnotations(nameIndex, length, dataInput, constantPool);
case Const.ATTR_ANNOTATION_DEFAULT:
return new AnnotationDefault(nameIndex, length, dataInput, constantPool);
case Const.ATTR_LOCAL_VARIABLE_TYPE_TABLE:
return new LocalVariableTypeTable(nameIndex, length, dataInput, constantPool);
case Const.ATTR_ENCLOSING_METHOD:
return new EnclosingMethod(nameIndex, length, dataInput, constantPool);
case Const.ATTR_STACK_MAP_TABLE:
// read new style stack map: StackMapTable. The rest of the code
// calls this a StackMap for historical reasons.
return new StackMap(nameIndex, length, dataInput, constantPool);
case Const.ATTR_BOOTSTRAP_METHODS:
return new BootstrapMethods(nameIndex, length, dataInput, constantPool);
case Const.ATTR_METHOD_PARAMETERS:
return new MethodParameters(nameIndex, length, dataInput, constantPool);
case Const.ATTR_MODULE:
return new Module(nameIndex, length, dataInput, constantPool);
case Const.ATTR_MODULE_PACKAGES:
return new ModulePackages(nameIndex, length, dataInput, constantPool);
case Const.ATTR_MODULE_MAIN_CLASS:
return new ModuleMainClass(nameIndex, length, dataInput, constantPool);
case Const.ATTR_NEST_HOST:
return new NestHost(nameIndex, length, dataInput, constantPool);
case Const.ATTR_NEST_MEMBERS:
return new NestMembers(nameIndex, length, dataInput, constantPool);
default:
// Never reached
throw new IllegalStateException("Unrecognized attribute type tag parsed: " + tag);
}
}
/**
* Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
* is called by the Field and Method constructor methods.
*
* @see JavaField
* @see JavaMethod
*
* @param dataInputStream Input stream
* @param constantPool Array of constants
* @return Attribute
* @throws IOException if an I/O error occurs.
*/
public static Attribute readAttribute(final DataInputStream dataInputStream, final ConstantPool constantPool) throws IOException {
return readAttribute((DataInput) dataInputStream, constantPool);
}
/**
* Remove attribute reader
*
* @param name the name of the attribute as stored in the class file
*/
public static void removeAttributeReader(final String name) {
READERS.remove(name);
}
/**
* @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
*/
@java.lang.Deprecated
protected int name_index; // Points to attribute name in constant pool TODO make private (has getter & setter)
/**
* @deprecated (since 6.0) (since 6.0) will be made private; do not access directly, use getter/setter
*/
@java.lang.Deprecated
protected int length; // Content length of attribute field TODO make private (has getter & setter)
/**
* @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
*/
@java.lang.Deprecated
protected byte tag; // Tag to distinguish subclasses TODO make private & final; supposed to be immutable
/**
* @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
*/
@java.lang.Deprecated
protected ConstantPool constant_pool; // TODO make private (has getter & setter)
/**
* Constructs an instance.
*
* <pre>
* attribute_info {
* u2 attribute_name_index;
* u4 attribute_length;
* u1 info[attribute_length];
* }
* </pre>
*
* @param tag tag.
* @param nameIndex u2 name index.
* @param length u4 length.
* @param constantPool constant pool.
*/
protected Attribute(final byte tag, final int nameIndex, final int length, final ConstantPool constantPool) {
this.tag = tag;
this.name_index = Args.requireU2(nameIndex, 0, constantPool.getLength(), getClass().getSimpleName() + " name index");
this.length = Args.requireU4(length, getClass().getSimpleName() + " attribute length");
this.constant_pool = constantPool;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public abstract void accept(Visitor v);
/**
* Use copy() if you want to have a deep copy(), i.e., with all references copied correctly.
*
* @return shallow copy of this attribute
*/
@Override
public Object clone() {
Attribute attr = null;
try {
attr = (Attribute) super.clone();
} catch (final CloneNotSupportedException e) {
throw new Error("Clone Not Supported"); // never happens
}
return attr;
}
/**
* @param constantPool constant pool to save.
* @return deep copy of this attribute.
*/
public abstract Attribute copy(ConstantPool constantPool);
/**
* Dumps attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
public void dump(final DataOutputStream file) throws IOException {
file.writeShort(name_index);
file.writeInt(length);
}
/**
* @return Constant pool used by this object.
* @see ConstantPool
*/
public final ConstantPool getConstantPool() {
return constant_pool;
}
/**
* @return Length of attribute field in bytes.
*/
public final int getLength() {
return length;
}
/**
* @return Name of attribute
* @since 6.0
*/
public String getName() {
return constant_pool.getConstantUtf8(name_index).getBytes();
}
/**
* @return Name index in constant pool of attribute name.
*/
public final int getNameIndex() {
return name_index;
}
/**
* @return Tag of attribute, i.e., its type. Value may not be altered, thus there is no setTag() method.
*/
public final byte getTag() {
return tag;
}
/**
* @param constantPool Constant pool to be used for this object.
* @see ConstantPool
*/
public final void setConstantPool(final ConstantPool constantPool) {
this.constant_pool = constantPool;
}
/**
* @param length length in bytes.
*/
public final void setLength(final int length) {
this.length = length;
}
/**
* @param nameIndex of attribute.
*/
public final void setNameIndex(final int nameIndex) {
this.name_index = nameIndex;
}
/**
* @return attribute name.
*/
@Override
public String toString() {
return Const.getAttributeName(tag);
}
}

View File

@@ -0,0 +1,166 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
/**
* This class represents a bootstrap method attribute, i.e., the bootstrap method ref, the number of bootstrap arguments
* and an array of the bootstrap arguments.
*
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.23"> The class File Format :
* The BootstrapMethods Attribute</a>
* @since 6.0
*/
public class BootstrapMethod implements Cloneable {
/** Index of the CONSTANT_MethodHandle_info structure in the constant_pool table */
private int bootstrapMethodRef;
/** Array of references to the constant_pool table */
private int[] bootstrapArguments;
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public BootstrapMethod(final BootstrapMethod c) {
this(c.getBootstrapMethodRef(), c.getBootstrapArguments());
}
/**
* Construct object from input stream.
*
* @param input Input stream
* @throws IOException if an I/O error occurs.
*/
BootstrapMethod(final DataInput input) throws IOException {
this(input.readUnsignedShort(), input.readUnsignedShort());
for (int i = 0; i < bootstrapArguments.length; i++) {
bootstrapArguments[i] = input.readUnsignedShort();
}
}
// helper method
private BootstrapMethod(final int bootstrapMethodRef, final int numBootstrapArguments) {
this(bootstrapMethodRef, new int[numBootstrapArguments]);
}
/**
* @param bootstrapMethodRef int index into constant_pool of CONSTANT_MethodHandle
* @param bootstrapArguments int[] indices into constant_pool of CONSTANT_[type]_info
*/
public BootstrapMethod(final int bootstrapMethodRef, final int[] bootstrapArguments) {
this.bootstrapMethodRef = bootstrapMethodRef;
this.bootstrapArguments = bootstrapArguments;
}
/**
* @return deep copy of this object
*/
public BootstrapMethod copy() {
try {
return (BootstrapMethod) clone();
} catch (final CloneNotSupportedException e) {
// TODO should this throw?
}
return null;
}
/**
* Dump object to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
public final void dump(final DataOutputStream file) throws IOException {
file.writeShort(bootstrapMethodRef);
file.writeShort(bootstrapArguments.length);
for (final int bootstrapArgument : bootstrapArguments) {
file.writeShort(bootstrapArgument);
}
}
/**
* @return int[] of bootstrap_method indices into constant_pool of CONSTANT_[type]_info
*/
public int[] getBootstrapArguments() {
return bootstrapArguments;
}
/**
* @return index into constant_pool of bootstrap_method
*/
public int getBootstrapMethodRef() {
return bootstrapMethodRef;
}
/**
* @return count of number of boostrap arguments
*/
public int getNumBootstrapArguments() {
return bootstrapArguments.length;
}
/**
* @param bootstrapArguments int[] indices into constant_pool of CONSTANT_[type]_info
*/
public void setBootstrapArguments(final int[] bootstrapArguments) {
this.bootstrapArguments = bootstrapArguments;
}
/**
* @param bootstrapMethodRef int index into constant_pool of CONSTANT_MethodHandle
*/
public void setBootstrapMethodRef(final int bootstrapMethodRef) {
this.bootstrapMethodRef = bootstrapMethodRef;
}
/**
* @return String representation.
*/
@Override
public final String toString() {
return "BootstrapMethod(" + bootstrapMethodRef + ", " + bootstrapArguments.length + ", " + Arrays.toString(bootstrapArguments) + ")";
}
/**
* @return Resolved string representation
*/
public final String toString(final ConstantPool constantPool) {
final StringBuilder buf = new StringBuilder();
final String bootstrapMethodName = constantPool.constantToString(bootstrapMethodRef, Const.CONSTANT_MethodHandle);
buf.append(Utility.compactClassName(bootstrapMethodName, false));
final int bootstrapArgumentsLen = bootstrapArguments.length;
if (bootstrapArgumentsLen > 0) {
buf.append("\nMethod Arguments:");
for (int i = 0; i < bootstrapArgumentsLen; i++) {
buf.append("\n ").append(i).append(": ");
buf.append(constantPool.constantToString(constantPool.getConstant(bootstrapArguments[i])));
}
}
return buf.toString();
}
}

View File

@@ -0,0 +1,159 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.stream.Stream;
/**
* This class represents a BootstrapMethods attribute.
*
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.23"> The class File Format :
* The BootstrapMethods Attribute</a>
* @since 6.0
*/
public class BootstrapMethods extends Attribute implements Iterable<BootstrapMethod> {
private BootstrapMethod[] bootstrapMethods; // TODO this could be made final (setter is not used)
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
* physical copy.
*
* @param c Source to copy.
*/
public BootstrapMethods(final BootstrapMethods c) {
this(c.getNameIndex(), c.getLength(), c.getBootstrapMethods(), c.getConstantPool());
}
/**
* @param nameIndex Index in constant pool to CONSTANT_Utf8
* @param length Content length in bytes
* @param bootstrapMethods array of bootstrap methods
* @param constantPool Array of constants
*/
public BootstrapMethods(final int nameIndex, final int length, final BootstrapMethod[] bootstrapMethods, final ConstantPool constantPool) {
super(Const.ATTR_BOOTSTRAP_METHODS, nameIndex, length, constantPool);
this.bootstrapMethods = bootstrapMethods;
}
/**
* Construct object from Input stream.
*
* @param nameIndex Index in constant pool to CONSTANT_Utf8
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
BootstrapMethods(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, (BootstrapMethod[]) null, constantPool);
final int numBootstrapMethods = input.readUnsignedShort();
bootstrapMethods = new BootstrapMethod[numBootstrapMethods];
for (int i = 0; i < numBootstrapMethods; i++) {
bootstrapMethods[i] = new BootstrapMethod(input);
}
}
/**
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitBootstrapMethods(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public BootstrapMethods copy(final ConstantPool constantPool) {
final BootstrapMethods c = (BootstrapMethods) clone();
c.bootstrapMethods = new BootstrapMethod[bootstrapMethods.length];
for (int i = 0; i < bootstrapMethods.length; i++) {
c.bootstrapMethods[i] = bootstrapMethods[i].copy();
}
c.setConstantPool(constantPool);
return c;
}
/**
* Dump bootstrap methods attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public final void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(bootstrapMethods.length);
for (final BootstrapMethod bootstrapMethod : bootstrapMethods) {
bootstrapMethod.dump(file);
}
}
/**
* @return array of bootstrap method "records"
*/
public final BootstrapMethod[] getBootstrapMethods() {
return bootstrapMethods;
}
@Override
public Iterator<BootstrapMethod> iterator() {
return Stream.of(bootstrapMethods).iterator();
}
/**
* @param bootstrapMethods the array of bootstrap methods
*/
public final void setBootstrapMethods(final BootstrapMethod[] bootstrapMethods) {
this.bootstrapMethods = bootstrapMethods;
}
/**
* @return String representation.
*/
@Override
public final String toString() {
final StringBuilder buf = new StringBuilder();
buf.append("BootstrapMethods(");
buf.append(bootstrapMethods.length);
buf.append("):");
for (int i = 0; i < bootstrapMethods.length; i++) {
buf.append("\n");
final int start = buf.length();
buf.append(" ").append(i).append(": ");
final int indentCount = buf.length() - start;
final String[] lines = bootstrapMethods[i].toString(super.getConstantPool()).split("\\r?\\n");
buf.append(lines[0]);
for (int j = 1; j < lines.length; j++) {
buf.append("\n").append(" ", 0, indentCount).append(lines[j]);
}
}
return buf.toString();
}
}

View File

@@ -0,0 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* @since 6.0
*/
public class ClassElementValue extends ElementValue {
// For primitive types and string type, this points to the value entry in
// the cpool
// For 'class' this points to the class entry in the cpool
private final int idx;
public ClassElementValue(final int type, final int idx, final ConstantPool cpool) {
super(type, cpool);
this.idx = idx;
}
@Override
public void dump(final DataOutputStream dos) throws IOException {
dos.writeByte(super.getType()); // u1 kind of value
dos.writeShort(idx);
}
public String getClassString() {
return super.getConstantPool().getConstantUtf8(idx).getBytes();
}
public int getIndex() {
return idx;
}
@Override
public String stringifyValue() {
return super.getConstantPool().getConstantUtf8(idx).getBytes();
}
}

View File

@@ -0,0 +1,68 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
/**
* Thrown when the BCEL attempts to read a class file and determines that a class is malformed or otherwise cannot be interpreted as a class file.
*/
public class ClassFormatException extends RuntimeException {
private static final long serialVersionUID = -3569097343160139969L;
/**
* Constructs a new instance with {@code null} as its detail message. The cause is not initialized, and may subsequently be initialized by a call to
* {@link #initCause}.
*/
public ClassFormatException() {
}
/**
* Constructs a new instance with the specified detail message. The cause is not initialized, and may subsequently be initialized by a call to
* {@link #initCause}.
*
* @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()} method.
*/
public ClassFormatException(final String message) {
super(message);
}
/**
* Constructs a new instance with the specified detail message and cause.
* <p>
* Note that the detail message associated with {@code cause} is <i>not</i> automatically incorporated in this runtime exception's detail message.
*
* @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). (A {@code null} value is permitted, and indicates that
* the cause is nonexistent or unknown.)
* @since 6.0
*/
public ClassFormatException(final String message, final Throwable cause) {
super(message, cause);
}
/**
* Constructs a new instance with the specified cause and a detail message of {@code (cause==null ? null : cause.toString())} (which typically contains the
* class and detail message of {@code cause}). This constructor is useful for runtime exceptions that are little more than wrappers for other throwables.
*
* @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). (A {@code null} value is permitted, and indicates that the
* cause is nonexistent or unknown.)
* @since 6.7.0
*/
public ClassFormatException(final Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,288 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Wrapper class that parses a given Java .class file. The method <a href ="#parse">parse</a> returns a
* <a href ="JavaClass.html"> JavaClass</a> object on success. When an I/O error or an inconsistency occurs an
* appropriate exception is propagated back to the caller.
*
* The structure and the names comply, except for a few conveniences, exactly with the
* <a href="http://docs.oracle.com/javase/specs/"> JVM specification 1.0</a>. See this paper for further details about
* the structure of a bytecode file.
*/
public final class ClassParser {
private static final int BUFSIZE = 8192;
private DataInputStream dataInputStream;
private final boolean fileOwned;
private final String fileName;
private String zipFile;
private int classNameIndex;
private int superclassNameIndex;
private int major; // Compiler version
private int minor; // Compiler version
private int accessFlags; // Access rights of parsed class
private int[] interfaces; // Names of implemented interfaces
private ConstantPool constantPool; // collection of constants
private JavaField[] fields; // class fields, i.e., its variables
private JavaMethod[] methods; // methods defined in the class
private Attribute[] attributes; // attributes defined in the class
private final boolean isZip; // Loaded from zip file
/**
* Parses class from the given stream.
*
* @param inputStream Input stream
* @param fileName File name
*/
public ClassParser(final InputStream inputStream, final String fileName) {
this.fileName = fileName;
this.fileOwned = false;
final String clazz = inputStream.getClass().getName(); // Not a very clean solution ...
this.isZip = clazz.startsWith("java.util.zip.") || clazz.startsWith("java.util.jar.");
if (inputStream instanceof DataInputStream) {
this.dataInputStream = (DataInputStream) inputStream;
} else {
this.dataInputStream = new DataInputStream(new BufferedInputStream(inputStream, BUFSIZE));
}
}
/**
* Parses class from given .class file.
*
* @param fileName file name
*/
public ClassParser(final String fileName) {
this.isZip = false;
this.fileName = fileName;
this.fileOwned = true;
}
/**
* Parses class from given .class file in a ZIP-archive
*
* @param zipFile zip file name
* @param fileName file name
*/
public ClassParser(final String zipFile, final String fileName) {
this.isZip = true;
this.fileOwned = true;
this.zipFile = zipFile;
this.fileName = fileName;
}
/**
* Parses the given Java class file and return an object that represents the contained data, i.e., constants, methods,
* fields and commands. A <em>ClassFormatException</em> is raised, if the file is not a valid .class file. (This does
* not include verification of the byte code as it is performed by the java interpreter).
*
* @return Class object representing the parsed class file
* @throws IOException if an I/O error occurs.
* @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
*/
public JavaClass parse() throws IOException, ClassFormatException {
ZipFile zip = null;
try {
if (fileOwned) {
if (isZip) {
zip = new ZipFile(zipFile);
final ZipEntry entry = zip.getEntry(fileName);
if (entry == null) {
throw new IOException("File " + fileName + " not found");
}
dataInputStream = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry), BUFSIZE));
} else {
dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(fileName), BUFSIZE));
}
}
/****************** Read headers ********************************/
// Check magic tag of class file
readID();
// Get compiler version
readVersion();
/****************** Read constant pool and related **************/
// Read constant pool entries
readConstantPool();
// Get class information
readClassInfo();
// Get interface information, i.e., implemented interfaces
readInterfaces();
/****************** Read class fields and methods ***************/
// Read class fields, i.e., the variables of the class
readFields();
// Read class methods, i.e., the functions in the class
readMethods();
// Read class attributes
readAttributes();
// Check for unknown variables
// Unknown[] u = Unknown.getUnknownAttributes();
// for (int i=0; i < u.length; i++)
// System.err.println("WARNING: " + u[i]);
// Everything should have been read now
// if(file.available() > 0) {
// int bytes = file.available();
// byte[] buf = new byte[bytes];
// file.read(buf);
// if(!(isZip && (buf.length == 1))) {
// System.err.println("WARNING: Trailing garbage at end of " + fileName);
// System.err.println(bytes + " extra bytes: " + Utility.toHexString(buf));
// }
// }
} finally {
// Read everything of interest, so close the file
if (fileOwned) {
try {
if (dataInputStream != null) {
dataInputStream.close();
}
} catch (final IOException ignored) {
// ignore close exceptions
}
}
try {
if (zip != null) {
zip.close();
}
} catch (final IOException ignored) {
// ignore close exceptions
}
}
// Return the information we have gathered in a new object
return new JavaClass(classNameIndex, superclassNameIndex, fileName, major, minor, accessFlags, constantPool, interfaces, fields, methods, attributes,
isZip ? JavaClass.ZIP : JavaClass.FILE);
}
/**
* Reads information about the attributes of the class.
*
* @throws IOException if an I/O error occurs.
* @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
*/
private void readAttributes() throws IOException, ClassFormatException {
final int attributesCount = dataInputStream.readUnsignedShort();
attributes = new Attribute[attributesCount];
for (int i = 0; i < attributesCount; i++) {
attributes[i] = Attribute.readAttribute(dataInputStream, constantPool);
}
}
/**
* Reads information about the class and its super class.
*
* @throws IOException if an I/O error occurs.
* @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
*/
private void readClassInfo() throws IOException, ClassFormatException {
accessFlags = dataInputStream.readUnsignedShort();
/*
* Interfaces are implicitly abstract, the flag should be set according to the JVM specification.
*/
if ((accessFlags & Const.ACC_INTERFACE) != 0) {
accessFlags |= Const.ACC_ABSTRACT;
}
if ((accessFlags & Const.ACC_ABSTRACT) != 0 && (accessFlags & Const.ACC_FINAL) != 0) {
throw new ClassFormatException("Class " + fileName + " can't be both final and abstract");
}
classNameIndex = dataInputStream.readUnsignedShort();
superclassNameIndex = dataInputStream.readUnsignedShort();
}
/**
* Reads constant pool entries.
*
* @throws IOException if an I/O error occurs.
* @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
*/
private void readConstantPool() throws IOException, ClassFormatException {
constantPool = new ConstantPool(dataInputStream);
}
/**
* Reads information about the fields of the class, i.e., its variables.
*
* @throws IOException if an I/O error occurs.
* @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
*/
private void readFields() throws IOException, ClassFormatException {
final int fieldsCount = dataInputStream.readUnsignedShort();
fields = new JavaField[fieldsCount];
for (int i = 0; i < fieldsCount; i++) {
fields[i] = new JavaField(dataInputStream, constantPool);
}
}
/******************** Private utility methods **********************/
/**
* Checks whether the header of the file is ok. Of course, this has to be the first action on successive file reads.
*
* @throws IOException if an I/O error occurs.
* @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
*/
private void readID() throws IOException, ClassFormatException {
if (dataInputStream.readInt() != Const.JVM_CLASSFILE_MAGIC) {
throw new ClassFormatException(fileName + " is not a Java .class file");
}
}
/**
* Reads information about the interfaces implemented by this class.
*
* @throws IOException if an I/O error occurs.
* @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
*/
private void readInterfaces() throws IOException, ClassFormatException {
final int interfacesCount = dataInputStream.readUnsignedShort();
interfaces = new int[interfacesCount];
for (int i = 0; i < interfacesCount; i++) {
interfaces[i] = dataInputStream.readUnsignedShort();
}
}
/**
* Reads information about the methods of the class.
*
* @throws IOException if an I/O error occurs.
* @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
*/
private void readMethods() throws IOException {
final int methodsCount = dataInputStream.readUnsignedShort();
methods = new JavaMethod[methodsCount];
for (int i = 0; i < methodsCount; i++) {
methods[i] = new JavaMethod(dataInputStream, constantPool);
}
}
/**
* Reads major and minor version of compiler which created the file.
*
* @throws IOException if an I/O error occurs.
* @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
*/
private void readVersion() throws IOException, ClassFormatException {
minor = dataInputStream.readUnsignedShort();
major = dataInputStream.readUnsignedShort();
}
}

View File

@@ -0,0 +1,347 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import org.apache.commons.lang3.ArrayUtils;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
/**
* This class represents a chunk of Java byte code contained in a method. It is instantiated by the
* <em>Attribute.readAttribute()</em> method. A <em>Code</em> attribute contains informations about operand stack, local
* variables, byte code and the exceptions handled within this method.
*
* This attribute has attributes itself, namely <em>LineNumberTable</em> which is used for debugging purposes and
* <em>LocalVariableTable</em> which contains information about the local variables.
*
* <pre>
* Code_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 max_stack;
* u2 max_locals;
* u4 code_length;
* u1 code[code_length];
* u2 exception_table_length;
* {
* u2 start_pc;
* u2 end_pc;
* u2 handler_pc;
* u2 catch_type;
* } exception_table[exception_table_length];
* u2 attributes_count;
* attribute_info attributes[attributes_count];
* }
* </pre>
* @see Attribute
* @see CodeException
* @see LineNumberTable
* @see LocalVariableTable
*/
public final class Code extends Attribute {
private int maxStack; // Maximum size of stack used by this method // TODO this could be made final (setter is not used)
private int maxLocals; // Number of local variables // TODO this could be made final (setter is not used)
private byte[] code; // Actual byte code
private CodeException[] exceptionTable; // Table of handled exceptions
private Attribute[] attributes; // or LocalVariable
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a
* physical copy.
*
* @param code The source Code.
*/
public Code(final Code code) {
this(code.getNameIndex(), code.getLength(), code.getMaxStack(), code.getMaxLocals(), code.getCode(), code.getExceptionTable(), code.getAttributes(),
code.getConstantPool());
}
/**
* @param nameIndex Index pointing to the name <em>Code</em>
* @param length Content length in bytes
* @param file Input stream
* @param constantPool Array of constants
*/
Code(final int nameIndex, final int length, final DataInput file, final ConstantPool constantPool) throws IOException {
// Initialize with some default values which will be overwritten later
this(nameIndex, length, file.readUnsignedShort(), file.readUnsignedShort(), (byte[]) null, (CodeException[]) null, (Attribute[]) null, constantPool);
final int codeLength = Args.requireU4(file.readInt(), 1, "Code length attribute");
code = new byte[codeLength]; // Read byte code
file.readFully(code);
/*
* Read exception table that contains all regions where an exception handler is active, i.e., a try { ... } catch()
* block.
*/
final int exceptionTableLength = file.readUnsignedShort();
exceptionTable = new CodeException[exceptionTableLength];
for (int i = 0; i < exceptionTableLength; i++) {
exceptionTable[i] = new CodeException(file);
}
/*
* Read all attributes, currently 'LineNumberTable' and 'LocalVariableTable'
*/
final int attributesCount = file.readUnsignedShort();
attributes = new Attribute[attributesCount];
for (int i = 0; i < attributesCount; i++) {
attributes[i] = Attribute.readAttribute(file, constantPool);
}
/*
* Adjust length, because of setAttributes in this(), s.b. length is incorrect, because it didn't take the internal
* attributes into account yet! Very subtle bug, fixed in 3.1.1.
*/
super.setLength(length);
}
/**
* @param nameIndex Index pointing to the name <em>Code</em>
* @param length Content length in bytes
* @param maxStack Maximum size of stack
* @param maxLocals Number of local variables
* @param code Actual byte code
* @param exceptionTable of handled exceptions
* @param attributes Attributes of code: LineNumber or LocalVariable
* @param constantPool Array of constants
*/
public Code(final int nameIndex, final int length, final int maxStack, final int maxLocals, final byte[] code, final CodeException[] exceptionTable,
final Attribute[] attributes, final ConstantPool constantPool) {
super(Const.ATTR_CODE, nameIndex, length, constantPool);
this.maxStack = Args.requireU2(maxStack, "maxStack");
this.maxLocals = Args.requireU2(maxLocals, "maxLocals");
this.code = code != null ? code : ArrayUtils.EMPTY_BYTE_ARRAY;
this.exceptionTable = exceptionTable != null ? exceptionTable : CodeException.EMPTY_CODE_EXCEPTION_ARRAY;
Args.requireU2(this.exceptionTable.length, "exceptionTable.length");
this.attributes = attributes != null ? attributes : EMPTY_ARRAY;
super.setLength(calculateLength()); // Adjust length
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitCode(this);
}
/**
* @return the full size of this code attribute, minus its first 6 bytes, including the size of all its contained
* attributes
*/
private int calculateLength() {
int len = 0;
if (attributes != null) {
for (final Attribute attribute : attributes) {
len += attribute.getLength() + 6 /* attribute header size */;
}
}
return len + getInternalLength();
}
/**
* @return deep copy of this attribute
*
* @param constantPool the constant pool to duplicate
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final Code c = (Code) clone();
if (code != null) {
c.code = code.clone();
}
c.setConstantPool(constantPool);
c.exceptionTable = new CodeException[exceptionTable.length];
Arrays.setAll(c.exceptionTable, i -> exceptionTable[i].copy());
c.attributes = new Attribute[attributes.length];
Arrays.setAll(c.attributes, i -> attributes[i].copy(constantPool));
return c;
}
/**
* Dump code attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(maxStack);
file.writeShort(maxLocals);
file.writeInt(code.length);
file.write(code, 0, code.length);
file.writeShort(exceptionTable.length);
for (final CodeException exception : exceptionTable) {
exception.dump(file);
}
file.writeShort(attributes.length);
for (final Attribute attribute : attributes) {
attribute.dump(file);
}
}
/**
* @return Collection of code attributes.
* @see Attribute
*/
public Attribute[] getAttributes() {
return attributes;
}
/**
* @return Actual byte code of the method.
*/
public byte[] getCode() {
return code;
}
/**
* @return Table of handled exceptions.
* @see CodeException
*/
public CodeException[] getExceptionTable() {
return exceptionTable;
}
/**
* @return the internal length of this code attribute (minus the first 6 bytes) and excluding all its attributes
*/
private int getInternalLength() {
return 2 /* maxStack */ + 2 /* maxLocals */ + 4 /* code length */
+ code.length /* byte-code */
+ 2 /* exception-table length */
+ 8 * (exceptionTable == null ? 0 : exceptionTable.length) /* exception table */
+ 2 /* attributes count */;
}
/**
* @return LineNumberTable of Code, if it has one
*/
public LineNumberTable getLineNumberTable() {
for (final Attribute attribute : attributes) {
if (attribute instanceof LineNumberTable) {
return (LineNumberTable) attribute;
}
}
return null;
}
/**
* @return LocalVariableTable of Code, if it has one
*/
public LocalVariableTable getLocalVariableTable() {
for (final Attribute attribute : attributes) {
if (attribute instanceof LocalVariableTable) {
return (LocalVariableTable) attribute;
}
}
return null;
}
/**
* @return Number of local variables.
*/
public int getMaxLocals() {
return maxLocals;
}
/**
* @return Maximum size of stack used by this method.
*/
public int getMaxStack() {
return maxStack;
}
/**
* @param attributes the attributes to set for this Code
*/
public void setAttributes(final Attribute[] attributes) {
this.attributes = attributes != null ? attributes : EMPTY_ARRAY;
super.setLength(calculateLength()); // Adjust length
}
/**
* @param code byte code
*/
public void setCode(final byte[] code) {
this.code = code != null ? code : ArrayUtils.EMPTY_BYTE_ARRAY;
super.setLength(calculateLength()); // Adjust length
}
/**
* @param exceptionTable exception table
*/
public void setExceptionTable(final CodeException[] exceptionTable) {
this.exceptionTable = exceptionTable != null ? exceptionTable : CodeException.EMPTY_CODE_EXCEPTION_ARRAY;
super.setLength(calculateLength()); // Adjust length
}
/**
* @param maxLocals maximum number of local variables
*/
public void setMaxLocals(final int maxLocals) {
this.maxLocals = maxLocals;
}
/**
* @param maxStack maximum stack size
*/
public void setMaxStack(final int maxStack) {
this.maxStack = maxStack;
}
/**
* @return String representation of code chunk.
*/
@Override
public String toString() {
return toString(true);
}
/**
* Converts this object to a String.
*
* @param verbose Provides verbose output when true.
* @return String representation of code chunk.
*/
public String toString(final boolean verbose) {
final StringBuilder buf = new StringBuilder(100); // CHECKSTYLE IGNORE MagicNumber
buf.append("Code(maxStack = ").append(maxStack).append(", maxLocals = ").append(maxLocals).append(", code_length = ").append(code.length).append(")\n")
.append(Utility.codeToString(code, super.getConstantPool(), 0, -1, verbose));
if (exceptionTable.length > 0) {
buf.append("\nException handler(s) = \n").append("From\tTo\tHandler\tType\n");
for (final CodeException exception : exceptionTable) {
buf.append(exception.toString(super.getConstantPool(), verbose)).append("\n");
}
}
if (attributes.length > 0) {
buf.append("\nAttribute(s) = ");
for (final Attribute attribute : attributes) {
buf.append("\n").append(attribute.getName()).append(":");
buf.append("\n").append(attribute);
}
}
return buf.toString();
}
}

View File

@@ -0,0 +1,232 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class represents an entry in the exception table of the <em>Code</em> attribute and is used only there. It
* contains a range in which a particular exception handler is active.
*
* <pre>
* Code_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 max_stack;
* u2 max_locals;
* u4 code_length;
* u1 code[code_length];
* u2 exception_table_length;
* {
* u2 start_pc;
* u2 end_pc;
* u2 handler_pc;
* u2 catch_type;
* } exception_table[exception_table_length];
* u2 attributes_count;
* attribute_info attributes[attributes_count];
* }
* </pre>
*
* @see Code
*/
public final class CodeException implements Cloneable, Node {
/**
* Empty array.
*/
static final CodeException[] EMPTY_CODE_EXCEPTION_ARRAY = {};
/**
* Range in the code the exception handler.
*/
private int startPc;
/**
* active. startPc is inclusive, endPc exclusive.
*/
private int endPc;
/**
* Starting address of exception handler, i.e., an offset from start of code.
*/
private int handlerPc;
/*
* If this is zero the handler catches any exception, otherwise it points to the exception class which is to be caught.
*/
private int catchType;
/**
* Constructs a new instance from another instance.
*
* @param c Source for copying.
*/
public CodeException(final CodeException c) {
this(c.getStartPC(), c.getEndPC(), c.getHandlerPC(), c.getCatchType());
}
/**
* Constructs a new instance from a DataInput.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
CodeException(final DataInput file) throws IOException {
this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort());
}
/**
* Constructs a new instance.
*
* @param startPc Range in the code the exception handler is active, startPc is inclusive while
* @param endPc is exclusive
* @param handlerPc Starting address of exception handler, i.e., an offset from start of code.
* @param catchType If zero the handler catches any exception, otherwise it points to the exception class which is to be
* caught.
*/
public CodeException(final int startPc, final int endPc, final int handlerPc, final int catchType) {
this.startPc = Args.requireU2(startPc, "startPc");
this.endPc = Args.requireU2(endPc, "endPc");
this.handlerPc = Args.requireU2(handlerPc, "handlerPc");
this.catchType = Args.requireU2(catchType, "catchType");
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitCodeException(this);
}
/**
* @return deep copy of this object
*/
public CodeException copy() {
try {
return (CodeException) clone();
} catch (final CloneNotSupportedException e) {
// TODO should this throw?
}
return null;
}
/**
* Dumps code exception to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
public void dump(final DataOutputStream file) throws IOException {
file.writeShort(startPc);
file.writeShort(endPc);
file.writeShort(handlerPc);
file.writeShort(catchType);
}
/**
* @return 0, if the handler catches any exception, otherwise it points to the exception class which is to be caught.
*/
public int getCatchType() {
return catchType;
}
/**
* @return Exclusive end index of the region where the handler is active.
*/
public int getEndPC() {
return endPc;
}
/**
* @return Starting address of exception handler, relative to the code.
*/
public int getHandlerPC() {
return handlerPc;
}
/**
* @return Inclusive start index of the region where the handler is active.
*/
public int getStartPC() {
return startPc;
}
/**
* @param catchType the type of exception that is caught
*/
public void setCatchType(final int catchType) {
this.catchType = catchType;
}
/**
* @param endPc end of handled block
*/
public void setEndPC(final int endPc) {
this.endPc = endPc;
}
/**
* @param handlerPc where the actual code is
*/
public void setHandlerPC(final int handlerPc) { // TODO unused
this.handlerPc = handlerPc;
}
/**
* @param startPc start of handled block
*/
public void setStartPC(final int startPc) { // TODO unused
this.startPc = startPc;
}
/**
* @return String representation.
*/
@Override
public String toString() {
return "CodeException(startPc = " + startPc + ", endPc = " + endPc + ", handlerPc = " + handlerPc + ", catchType = " + catchType + ")";
}
public String toString(final ConstantPool cp) {
return toString(cp, true);
}
/**
* @param cp constant pool source.
* @param verbose Output more if true.
* @return String representation.
*/
public String toString(final ConstantPool cp, final boolean verbose) {
String str;
if (catchType == 0) {
str = "<Any exception>(0)";
} else {
str = Utility.compactClassName(cp.getConstantString(catchType, Const.CONSTANT_Class), false) + (verbose ? "(" + catchType + ")" : "");
}
return startPc + "\t" + endPc + "\t" + handlerPc + "\t" + str;
}
}

View File

@@ -0,0 +1,199 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.BCELComparator;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Objects;
/**
* Abstract superclass for classes to represent the different constant types in the constant pool of a class file. The
* classes keep closely to the JVM specification.
*/
public abstract class Constant implements Cloneable, Node {
private static BCELComparator bcelComparator = new BCELComparator() {
@Override
public boolean equals(final Object o1, final Object o2) {
final Constant THIS = (Constant) o1;
final Constant THAT = (Constant) o2;
return Objects.equals(THIS.toString(), THAT.toString());
}
@Override
public int hashCode(final Object o) {
final Constant THIS = (Constant) o;
return THIS.toString().hashCode();
}
};
/**
* @return Comparison strategy object
*/
public static BCELComparator getComparator() {
return bcelComparator;
}
/**
* Reads one constant from the given input, the type depends on a tag byte.
*
* @param dataInput Input stream
* @return Constant object
* @throws IOException if an I/O error occurs reading from the given {@code dataInput}.
* @throws ClassFormatException if the next byte is not recognized
* @since 6.0 made public
*/
public static Constant readConstant(final DataInput dataInput) throws IOException, ClassFormatException {
final byte b = dataInput.readByte(); // Read tag byte
switch (b) {
case Const.CONSTANT_Class:
return new ConstantClass(dataInput);
case Const.CONSTANT_Fieldref:
return new ConstantFieldref(dataInput);
case Const.CONSTANT_Methodref:
return new ConstantMethodref(dataInput);
case Const.CONSTANT_InterfaceMethodref:
return new ConstantInterfaceMethodref(dataInput);
case Const.CONSTANT_String:
return new ConstantString(dataInput);
case Const.CONSTANT_Integer:
return new ConstantInteger(dataInput);
case Const.CONSTANT_Float:
return new ConstantFloat(dataInput);
case Const.CONSTANT_Long:
return new ConstantLong(dataInput);
case Const.CONSTANT_Double:
return new ConstantDouble(dataInput);
case Const.CONSTANT_NameAndType:
return new ConstantNameAndType(dataInput);
case Const.CONSTANT_Utf8:
return ConstantUtf8.getInstance(dataInput);
case Const.CONSTANT_MethodHandle:
return new ConstantMethodHandle(dataInput);
case Const.CONSTANT_MethodType:
return new ConstantMethodType(dataInput);
case Const.CONSTANT_Dynamic:
return new ConstantDynamic(dataInput);
case Const.CONSTANT_InvokeDynamic:
return new ConstantInvokeDynamic(dataInput);
case Const.CONSTANT_Module:
return new ConstantModule(dataInput);
case Const.CONSTANT_Package:
return new ConstantPackage(dataInput);
default:
throw new ClassFormatException("Invalid byte tag in constant pool: " + b);
}
}
/**
* @param comparator Comparison strategy object
*/
public static void setComparator(final BCELComparator comparator) {
bcelComparator = comparator;
}
/*
* In fact this tag is redundant since we can distinguish different 'Constant' objects by their type, i.e., via
* 'instanceof'. In some places we will use the tag for switch()es anyway.
*
* First, we want match the specification as closely as possible. Second we need the tag as an index to select the
* corresponding class name from the 'CONSTANT_NAMES' array.
*/
/**
* @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
*/
@java.lang.Deprecated
protected byte tag; // TODO should be private & final
Constant(final byte tag) {
this.tag = tag;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public abstract void accept(Visitor v);
@Override
public Object clone() {
try {
return super.clone();
} catch (final CloneNotSupportedException e) {
throw new Error("Clone Not Supported"); // never happens
}
}
/**
* @return deep copy of this constant
*/
public Constant copy() {
try {
return (Constant) super.clone();
} catch (final CloneNotSupportedException e) {
// TODO should this throw?
}
return null;
}
public abstract void dump(DataOutputStream file) throws IOException;
/**
* Returns value as defined by given BCELComparator strategy. By default two Constant objects are said to be equal when
* the result of toString() is equal.
*
* @see Object#equals(Object)
*/
@Override
public boolean equals(final Object obj) {
return bcelComparator.equals(this, obj);
}
/**
* @return Tag of constant, i.e., its type. No setTag() method to avoid confusion.
*/
public final byte getTag() {
return tag;
}
/**
* Returns value as defined by given BCELComparator strategy. By default return the hashcode of the result of
* toString().
*
* @see Object#hashCode()
*/
@Override
public int hashCode() {
return bcelComparator.hashCode(this);
}
/**
* @return String representation.
*/
@Override
public String toString() {
return Const.getConstantName(tag) + "[" + tag + "]";
}
}

View File

@@ -0,0 +1,140 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Abstract super class for Fieldref, Methodref, InterfaceMethodref and InvokeDynamic constants.
*
* @see ConstantFieldref
* @see ConstantMethodref
* @see ConstantInterfaceMethodref
* @see ConstantInvokeDynamic
*/
public abstract class ConstantCP extends Constant {
/**
* References to the constants containing the class and the field signature
*/
// Note that this field is used to store the
// bootstrap_method_attr_index of a ConstantInvokeDynamic.
/**
* @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
*/
@java.lang.Deprecated
protected int class_index; // TODO make private (has getter & setter)
// This field has the same meaning for all subclasses.
/**
* @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
*/
@java.lang.Deprecated
protected int name_and_type_index; // TODO make private (has getter & setter)
/**
* Initialize instance from file data.
*
* @param tag Constant type tag
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantCP(final byte tag, final DataInput file) throws IOException {
this(tag, file.readUnsignedShort(), file.readUnsignedShort());
}
/**
* @param classIndex Reference to the class containing the field
* @param nameAndTypeIndex and the field signature
*/
protected ConstantCP(final byte tag, final int classIndex, final int nameAndTypeIndex) {
super(tag);
this.class_index = classIndex;
this.name_and_type_index = nameAndTypeIndex;
}
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantCP(final ConstantCP c) {
this(c.getTag(), c.getClassIndex(), c.getNameAndTypeIndex());
}
/**
* Dump constant field reference to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public final void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
file.writeShort(class_index);
file.writeShort(name_and_type_index);
}
/**
* @return Class this field belongs to.
*/
public String getClass(final ConstantPool cp) {
return cp.constantToString(class_index, Const.CONSTANT_Class);
}
/**
* @return Reference (index) to class this constant refers to.
*/
public final int getClassIndex() {
return class_index;
}
/**
* @return Reference (index) to signature of the field.
*/
public final int getNameAndTypeIndex() {
return name_and_type_index;
}
/**
* @param classIndex points to Constant_class
*/
public final void setClassIndex(final int classIndex) {
this.class_index = classIndex;
}
/**
* @param nameAndTypeIndex points to Constant_NameAndType
*/
public final void setNameAndTypeIndex(final int nameAndTypeIndex) {
this.name_and_type_index = nameAndTypeIndex;
}
/**
* @return String representation.
*
* not final as ConstantInvokeDynamic needs to modify
*/
@Override
public String toString() {
return super.toString() + "(class_index = " + class_index + ", name_and_type_index = " + name_and_type_index + ")";
}
}

View File

@@ -0,0 +1,120 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from the abstract {@link Constant} and represents a reference to a (external) class.
*
* @see Constant
*/
public final class ConstantClass extends Constant implements ConstantObject {
private int nameIndex; // Identical to ConstantString except for the name
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantClass(final ConstantClass c) {
this(c.getNameIndex());
}
/**
* Constructs an instance from file data.
*
* @param dataInput Input stream
* @throws IOException if an I/O error occurs reading from the given {@code dataInput}.
*/
ConstantClass(final DataInput dataInput) throws IOException {
this(dataInput.readUnsignedShort());
}
/**
* @param nameIndex Name index in constant pool. Should refer to a ConstantUtf8.
*/
public ConstantClass(final int nameIndex) {
super(Const.CONSTANT_Class);
this.nameIndex = nameIndex;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantClass(this);
}
/**
* Dumps constant class to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs writing to the DataOutputStream.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
file.writeShort(nameIndex);
}
/**
* @return dereferenced string
*/
public String getBytes(final ConstantPool cp) {
return (String) getConstantValue(cp);
}
/**
* @return String object
*/
@Override
public Object getConstantValue(final ConstantPool cp) {
return cp.getConstantUtf8(nameIndex).getBytes();
}
/**
* @return Name index in constant pool of class name.
*/
public int getNameIndex() {
return nameIndex;
}
/**
* @param nameIndex the name index in the constant pool of this Constant Class
*/
public void setNameIndex(final int nameIndex) {
this.nameIndex = nameIndex;
}
/**
* @return String representation.
*/
@Override
public String toString() {
return super.toString() + "(nameIndex = " + nameIndex + ")";
}
}

View File

@@ -0,0 +1,113 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from the abstract {@link Constant} and represents a reference to a Double object.
*
* @see Constant
*/
public final class ConstantDouble extends Constant implements ConstantObject {
private double bytes;
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantDouble(final ConstantDouble c) {
this(c.getBytes());
}
/**
* Initialize instance from file data.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantDouble(final DataInput file) throws IOException {
this(file.readDouble());
}
/**
* @param bytes Data
*/
public ConstantDouble(final double bytes) {
super(Const.CONSTANT_Double);
this.bytes = bytes;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantDouble(this);
}
/**
* Dump constant double to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
file.writeDouble(bytes);
}
/**
* @return data, i.e., 8 bytes.
*/
public double getBytes() {
return bytes;
}
/**
* @return Double object
*/
@Override
public Object getConstantValue(final ConstantPool cp) {
return Double.valueOf(bytes);
}
/**
* @param bytes the raw bytes that represent the double value
*/
public void setBytes(final double bytes) {
this.bytes = bytes;
}
/**
* @return String representation.
*/
@Override
public String toString() {
return super.toString() + "(bytes = " + bytes + ")";
}
}

View File

@@ -0,0 +1,86 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.IOException;
/**
* This class is derived from the abstract {@link Constant} and represents a reference to a dynamically computed
* constant.
*
* @see Constant
* @see <a href="https://bugs.openjdk.java.net/secure/attachment/74618/constant-dynamic.html"> Change request for JEP
* 309</a>
* @since 6.3
*/
public final class ConstantDynamic extends ConstantCP {
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantDynamic(final ConstantDynamic c) {
this(c.getBootstrapMethodAttrIndex(), c.getNameAndTypeIndex());
}
/**
* Initialize instance from file data.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantDynamic(final DataInput file) throws IOException {
this(file.readShort(), file.readShort());
}
public ConstantDynamic(final int bootstrapMethodAttrIndex, final int nameAndTypeIndex) {
super(Const.CONSTANT_Dynamic, bootstrapMethodAttrIndex, nameAndTypeIndex);
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e.,
* the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantDynamic(this);
}
/**
* @return Reference (index) to bootstrap method this constant refers to.
*
* Note that this method is a functional duplicate of getClassIndex for use by ConstantInvokeDynamic.
* @since 6.0
*/
public int getBootstrapMethodAttrIndex() {
return super.getClassIndex(); // AKA bootstrap_method_attr_index
}
/**
* @return String representation
*/
@Override
public String toString() {
return super.toString().replace("class_index", "bootstrap_method_attr_index");
}
}

View File

@@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.IOException;
/**
* This class represents a constant pool reference to a field.
*/
public final class ConstantFieldref extends ConstantCP {
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantFieldref(final ConstantFieldref c) {
super(Const.CONSTANT_Fieldref, c.getClassIndex(), c.getNameAndTypeIndex());
}
/**
* Initialize instance from input data.
*
* @param input input stream
* @throws IOException if an I/O error occurs.
*/
ConstantFieldref(final DataInput input) throws IOException {
super(Const.CONSTANT_Fieldref, input);
}
/**
* @param classIndex Reference to the class containing the Field
* @param nameAndTypeIndex and the Field signature
*/
public ConstantFieldref(final int classIndex, final int nameAndTypeIndex) {
super(Const.CONSTANT_Fieldref, classIndex, nameAndTypeIndex);
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of Fields, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantFieldref(this);
}
}

View File

@@ -0,0 +1,114 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from the abstract {@link Constant} and represents a reference to a float object.
*
* @see Constant
*/
public final class ConstantFloat extends Constant implements ConstantObject {
private float bytes;
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
* physical copy.
*
* @param c Source to copy.
*/
public ConstantFloat(final ConstantFloat c) {
this(c.getBytes());
}
/**
* Initialize instance from file data.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantFloat(final DataInput file) throws IOException {
this(file.readFloat());
}
/**
* @param bytes Data
*/
public ConstantFloat(final float bytes) {
super(Const.CONSTANT_Float);
this.bytes = bytes;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantFloat(this);
}
/**
* Dump constant float to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
file.writeFloat(bytes);
}
/**
* @return data, i.e., 4 bytes.
*/
public float getBytes() {
return bytes;
}
/**
* @return Float object
*/
@Override
public Object getConstantValue(final ConstantPool cp) {
return Float.valueOf(bytes);
}
/**
* @param bytes the raw bytes that represent this float
*/
public void setBytes(final float bytes) {
this.bytes = bytes;
}
/**
* @return String representation.
*/
@Override
public String toString() {
return super.toString() + "(bytes = " + bytes + ")";
}
}

View File

@@ -0,0 +1,113 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from the abstract {@link Constant} and represents a reference to an int object.
*
* @see Constant
*/
public final class ConstantInteger extends Constant implements ConstantObject {
private int bytes;
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantInteger(final ConstantInteger c) {
this(c.getBytes());
}
/**
* Initialize instance from file data.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantInteger(final DataInput file) throws IOException {
this(file.readInt());
}
/**
* @param bytes Data
*/
public ConstantInteger(final int bytes) {
super(Const.CONSTANT_Integer);
this.bytes = bytes;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantInteger(this);
}
/**
* Dump constant integer to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
file.writeInt(bytes);
}
/**
* @return data, i.e., 4 bytes.
*/
public int getBytes() {
return bytes;
}
/**
* @return Integer object
*/
@Override
public Object getConstantValue(final ConstantPool cp) {
return Integer.valueOf(bytes);
}
/**
* @param bytes the raw bytes that represent this integer
*/
public void setBytes(final int bytes) {
this.bytes = bytes;
}
/**
* @return String representation.
*/
@Override
public String toString() {
return super.toString() + "(bytes = " + bytes + ")";
}
}

View File

@@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.IOException;
/**
* This class represents a constant pool reference to an interface method.
*/
public final class ConstantInterfaceMethodref extends ConstantCP {
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantInterfaceMethodref(final ConstantInterfaceMethodref c) {
super(Const.CONSTANT_InterfaceMethodref, c.getClassIndex(), c.getNameAndTypeIndex());
}
/**
* Initialize instance from input data.
*
* @param input input stream
* @throws IOException if an I/O error occurs.
*/
ConstantInterfaceMethodref(final DataInput input) throws IOException {
super(Const.CONSTANT_InterfaceMethodref, input);
}
/**
* @param classIndex Reference to the class containing the method
* @param nameAndTypeIndex and the method signature
*/
public ConstantInterfaceMethodref(final int classIndex, final int nameAndTypeIndex) {
super(Const.CONSTANT_InterfaceMethodref, classIndex, nameAndTypeIndex);
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantInterfaceMethodref(this);
}
}

View File

@@ -0,0 +1,85 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.IOException;
/**
* This class is derived from the abstract {@link Constant} and represents a reference to a invoke dynamic.
*
* @see Constant
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.10"> The
* CONSTANT_InvokeDynamic_info Structure in The Java Virtual Machine Specification</a>
* @since 6.0
*/
public final class ConstantInvokeDynamic extends ConstantCP {
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantInvokeDynamic(final ConstantInvokeDynamic c) {
this(c.getBootstrapMethodAttrIndex(), c.getNameAndTypeIndex());
}
/**
* Initialize instance from file data.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantInvokeDynamic(final DataInput file) throws IOException {
this(file.readUnsignedShort(), file.readUnsignedShort());
}
public ConstantInvokeDynamic(final int bootstrapMethodAttrIndex, final int nameAndTypeIndex) {
super(Const.CONSTANT_InvokeDynamic, bootstrapMethodAttrIndex, nameAndTypeIndex);
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e.,
* the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantInvokeDynamic(this);
}
/**
* @return Reference (index) to bootstrap method this constant refers to.
*
* Note that this method is a functional duplicate of getClassIndex for use by ConstantInvokeDynamic.
* @since 6.0
*/
public int getBootstrapMethodAttrIndex() {
return super.getClassIndex(); // AKA bootstrap_method_attr_index
}
/**
* @return String representation
*/
@Override
public String toString() {
return super.toString().replace("class_index", "bootstrap_method_attr_index");
}
}

View File

@@ -0,0 +1,113 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from the abstract {@link Constant} and represents a reference to a long object.
*
* @see Constant
*/
public final class ConstantLong extends Constant implements ConstantObject {
private long bytes;
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantLong(final ConstantLong c) {
this(c.getBytes());
}
/**
* Initialize instance from file data.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantLong(final DataInput file) throws IOException {
this(file.readLong());
}
/**
* @param bytes Data
*/
public ConstantLong(final long bytes) {
super(Const.CONSTANT_Long);
this.bytes = bytes;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantLong(this);
}
/**
* Dump constant long to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
file.writeLong(bytes);
}
/**
* @return data, i.e., 8 bytes.
*/
public long getBytes() {
return bytes;
}
/**
* @return Long object
*/
@Override
public Object getConstantValue(final ConstantPool cp) {
return Long.valueOf(bytes);
}
/**
* @param bytes the raw bytes that represent this long
*/
public void setBytes(final long bytes) {
this.bytes = bytes;
}
/**
* @return String representation.
*/
@Override
public String toString() {
return super.toString() + "(bytes = " + bytes + ")";
}
}

View File

@@ -0,0 +1,108 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from the abstract {@link Constant} and represents a reference to a method handle.
*
* @see Constant
* @since 6.0
*/
public final class ConstantMethodHandle extends Constant {
private int referenceKind;
private int referenceIndex;
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantMethodHandle(final ConstantMethodHandle c) {
this(c.getReferenceKind(), c.getReferenceIndex());
}
/**
* Initialize instance from file data.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantMethodHandle(final DataInput file) throws IOException {
this(file.readUnsignedByte(), file.readUnsignedShort());
}
public ConstantMethodHandle(final int referenceKind, final int referenceIndex) {
super(Const.CONSTANT_MethodHandle);
this.referenceKind = referenceKind;
this.referenceIndex = referenceIndex;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e.,
* the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantMethodHandle(this);
}
/**
* Dump method kind and index to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
file.writeByte(referenceKind);
file.writeShort(referenceIndex);
}
public int getReferenceIndex() {
return referenceIndex;
}
public int getReferenceKind() {
return referenceKind;
}
public void setReferenceIndex(final int referenceIndex) {
this.referenceIndex = referenceIndex;
}
public void setReferenceKind(final int referenceKind) {
this.referenceKind = referenceKind;
}
/**
* @return String representation
*/
@Override
public String toString() {
return super.toString() + "(referenceKind = " + referenceKind + ", referenceIndex = " + referenceIndex + ")";
}
}

View File

@@ -0,0 +1,97 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from the abstract {@link Constant} and represents a reference to a method type.
*
* @see Constant
* @since 6.0
*/
public final class ConstantMethodType extends Constant {
private int descriptorIndex;
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantMethodType(final ConstantMethodType c) {
this(c.getDescriptorIndex());
}
/**
* Initialize instance from file data.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantMethodType(final DataInput file) throws IOException {
this(file.readUnsignedShort());
}
public ConstantMethodType(final int descriptorIndex) {
super(Const.CONSTANT_MethodType);
this.descriptorIndex = descriptorIndex;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e.,
* the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantMethodType(this);
}
/**
* Dump name and signature index to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
file.writeShort(descriptorIndex);
}
public int getDescriptorIndex() {
return descriptorIndex;
}
public void setDescriptorIndex(final int descriptorIndex) {
this.descriptorIndex = descriptorIndex;
}
/**
* @return String representation
*/
@Override
public String toString() {
return super.toString() + "(descriptorIndex = " + descriptorIndex + ")";
}
}

View File

@@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.IOException;
/**
* This class represents a constant pool reference to a method.
*/
public final class ConstantMethodref extends ConstantCP {
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantMethodref(final ConstantMethodref c) {
super(Const.CONSTANT_Methodref, c.getClassIndex(), c.getNameAndTypeIndex());
}
/**
* Initialize instance from input data.
*
* @param input input stream
* @throws IOException if an I/O error occurs.
*/
ConstantMethodref(final DataInput input) throws IOException {
super(Const.CONSTANT_Methodref, input);
}
/**
* @param classIndex Reference to the class containing the method
* @param nameAndTypeIndex and the method signature
*/
public ConstantMethodref(final int classIndex, final int nameAndTypeIndex) {
super(Const.CONSTANT_Methodref, classIndex, nameAndTypeIndex);
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantMethodref(this);
}
}

View File

@@ -0,0 +1,125 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from the abstract {@link Constant} and represents a reference to a module.
*
* <p>
* Note: Early access Java 9 support- currently subject to change
* </p>
*
* @see Constant
* @since 6.1
*/
public final class ConstantModule extends Constant implements ConstantObject {
private int nameIndex;
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantModule(final ConstantModule c) {
this(c.getNameIndex());
}
/**
* Initialize instance from file data.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantModule(final DataInput file) throws IOException {
this(file.readUnsignedShort());
}
/**
* @param nameIndex Name index in constant pool. Should refer to a ConstantUtf8.
*/
public ConstantModule(final int nameIndex) {
super(Const.CONSTANT_Module);
this.nameIndex = nameIndex;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e.,
* the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantModule(this);
}
/**
* Dump constant module to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
file.writeShort(nameIndex);
}
/**
* @return dereferenced string
*/
public String getBytes(final ConstantPool cp) {
return (String) getConstantValue(cp);
}
/**
* @return String object
*/
@Override
public Object getConstantValue(final ConstantPool cp) {
return cp.getConstantUtf8(nameIndex).getBytes();
}
/**
* @return Name index in constant pool of module name.
*/
public int getNameIndex() {
return nameIndex;
}
/**
* @param nameIndex the name index in the constant pool of this Constant Module
*/
public void setNameIndex(final int nameIndex) {
this.nameIndex = nameIndex;
}
/**
* @return String representation.
*/
@Override
public String toString() {
return super.toString() + "(nameIndex = " + nameIndex + ")";
}
}

View File

@@ -0,0 +1,138 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from the abstract {@link Constant} and represents a reference to the name and signature of a
* field or method.
*
* @see Constant
*/
public final class ConstantNameAndType extends Constant {
private int nameIndex; // Name of field/method
private int signatureIndex; // and its signature.
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantNameAndType(final ConstantNameAndType c) {
this(c.getNameIndex(), c.getSignatureIndex());
}
/**
* Initialize instance from file data.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantNameAndType(final DataInput file) throws IOException {
this(file.readUnsignedShort(), file.readUnsignedShort());
}
/**
* @param nameIndex Name of field/method
* @param signatureIndex and its signature
*/
public ConstantNameAndType(final int nameIndex, final int signatureIndex) {
super(Const.CONSTANT_NameAndType);
this.nameIndex = nameIndex;
this.signatureIndex = signatureIndex;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantNameAndType(this);
}
/**
* Dump name and signature index to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
file.writeShort(nameIndex);
file.writeShort(signatureIndex);
}
/**
* @return name
*/
public String getName(final ConstantPool cp) {
return cp.constantToString(getNameIndex(), Const.CONSTANT_Utf8);
}
/**
* @return Name index in constant pool of field/method name.
*/
public int getNameIndex() {
return nameIndex;
}
/**
* @return signature
*/
public String getSignature(final ConstantPool cp) {
return cp.constantToString(getSignatureIndex(), Const.CONSTANT_Utf8);
}
/**
* @return Index in constant pool of field/method signature.
*/
public int getSignatureIndex() {
return signatureIndex;
}
/**
* @param nameIndex the name index of this constant
*/
public void setNameIndex(final int nameIndex) {
this.nameIndex = nameIndex;
}
/**
* @param signatureIndex the signature index in the constant pool of this type
*/
public void setSignatureIndex(final int signatureIndex) {
this.signatureIndex = signatureIndex;
}
/**
* @return String representation
*/
@Override
public String toString() {
return super.toString() + "(nameIndex = " + nameIndex + ", signatureIndex = " + signatureIndex + ")";
}
}

View File

@@ -0,0 +1,30 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
/**
* This interface denotes those constants that have a "natural" value, such as ConstantLong, ConstantString, etc..
*
* @see Constant
*/
public interface ConstantObject {
/**
* @return object representing the constant, e.g., Long for ConstantLong
*/
Object getConstantValue(ConstantPool cp);
}

View File

@@ -0,0 +1,125 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from the abstract {@link Constant} and represents a reference to a package.
*
* <p>
* Note: Early access Java 9 support- currently subject to change
* </p>
*
* @see Constant
* @since 6.1
*/
public final class ConstantPackage extends Constant implements ConstantObject {
private int nameIndex;
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantPackage(final ConstantPackage c) {
this(c.getNameIndex());
}
/**
* Initialize instance from file data.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantPackage(final DataInput file) throws IOException {
this(file.readUnsignedShort());
}
/**
* @param nameIndex Name index in constant pool. Should refer to a ConstantUtf8.
*/
public ConstantPackage(final int nameIndex) {
super(Const.CONSTANT_Package);
this.nameIndex = nameIndex;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e.,
* the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantPackage(this);
}
/**
* Dump constant package to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
file.writeShort(nameIndex);
}
/**
* @return dereferenced string
*/
public String getBytes(final ConstantPool cp) {
return (String) getConstantValue(cp);
}
/**
* @return String object
*/
@Override
public Object getConstantValue(final ConstantPool cp) {
return cp.getConstantUtf8(nameIndex).getBytes();
}
/**
* @return Name index in constant pool of package name.
*/
public int getNameIndex() {
return nameIndex;
}
/**
* @param nameIndex the name index in the constant pool of this Constant Package
*/
public void setNameIndex(final int nameIndex) {
this.nameIndex = nameIndex;
}
/**
* @return String representation.
*/
@Override
public String toString() {
return super.toString() + "(nameIndex = " + nameIndex + ")";
}
}

View File

@@ -0,0 +1,433 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.generic.ConstantPoolGen;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
/**
* This class represents the constant pool, i.e., a table of constants, of a parsed classfile. It may contain null references, due to the JVM specification that
* skips an entry after an 8-byte constant (double, long) entry. Those interested in generating constant pools programmatically should see
* <a href="../generic/ConstantPoolGen.html"> ConstantPoolGen</a>.
*
* @see Constant
* @see ConstantPoolGen
*/
public class ConstantPool implements Cloneable, Node, Iterable<Constant> {
private static String escape(final String str) {
final int len = str.length();
final StringBuilder buf = new StringBuilder(len + 5);
final char[] ch = str.toCharArray();
for (int i = 0; i < len; i++) {
switch (ch[i]) {
case '\n':
buf.append("\\n");
break;
case '\r':
buf.append("\\r");
break;
case '\t':
buf.append("\\t");
break;
case '\b':
buf.append("\\b");
break;
case '"':
buf.append("\\\"");
break;
default:
buf.append(ch[i]);
}
}
return buf.toString();
}
private Constant[] constantPool;
/**
* @param constantPool Array of constants
*/
public ConstantPool(final Constant[] constantPool) {
this.constantPool = constantPool;
}
/**
* Reads constants from given input stream.
*
* @param input Input stream
* @throws IOException if problem in readUnsignedShort or readConstant
*/
public ConstantPool(final DataInput input) throws IOException {
byte tag;
final int constantPoolCount = input.readUnsignedShort();
constantPool = new Constant[constantPoolCount];
/*
* constantPool[0] is unused by the compiler and may be used freely by the implementation.
*/
for (int i = 1; i < constantPoolCount; i++) {
constantPool[i] = Constant.readConstant(input);
/*
* Quote from the JVM specification: "All eight byte constants take up two spots in the constant pool. If this is the n'th byte in the constant
* pool, then the next item will be numbered n+2"
*
* Thus we have to increment the index counter.
*/
tag = constantPool[i].getTag();
if (tag == Const.CONSTANT_Double || tag == Const.CONSTANT_Long) {
i++;
}
}
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e., the hierarchy of methods, fields,
* attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantPool(this);
}
/**
* Resolves constant to a string representation.
*
* @param c Constant to be printed
* @return String representation
* @throws IllegalArgumentException if c is unknown constant type
*/
public String constantToString(Constant c) throws IllegalArgumentException {
String str;
int i;
final byte tag = c.getTag();
switch (tag) {
case Const.CONSTANT_Class:
i = ((ConstantClass) c).getNameIndex();
c = getConstantUtf8(i);
str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
break;
case Const.CONSTANT_String:
i = ((ConstantString) c).getStringIndex();
c = getConstantUtf8(i);
str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\"";
break;
case Const.CONSTANT_Utf8:
str = ((ConstantUtf8) c).getBytes();
break;
case Const.CONSTANT_Double:
str = String.valueOf(((ConstantDouble) c).getBytes());
break;
case Const.CONSTANT_Float:
str = String.valueOf(((ConstantFloat) c).getBytes());
break;
case Const.CONSTANT_Long:
str = String.valueOf(((ConstantLong) c).getBytes());
break;
case Const.CONSTANT_Integer:
str = String.valueOf(((ConstantInteger) c).getBytes());
break;
case Const.CONSTANT_NameAndType:
str = constantToString(((ConstantNameAndType) c).getNameIndex(), Const.CONSTANT_Utf8) + " "
+ constantToString(((ConstantNameAndType) c).getSignatureIndex(), Const.CONSTANT_Utf8);
break;
case Const.CONSTANT_InterfaceMethodref:
case Const.CONSTANT_Methodref:
case Const.CONSTANT_Fieldref:
str = constantToString(((ConstantCP) c).getClassIndex(), Const.CONSTANT_Class) + "."
+ constantToString(((ConstantCP) c).getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
break;
case Const.CONSTANT_MethodHandle:
// Note that the ReferenceIndex may point to a Fieldref, Methodref or
// InterfaceMethodref - so we need to peek ahead to get the actual type.
final ConstantMethodHandle cmh = (ConstantMethodHandle) c;
str = Const.getMethodHandleName(cmh.getReferenceKind()) + " "
+ constantToString(cmh.getReferenceIndex(), getConstant(cmh.getReferenceIndex()).getTag());
break;
case Const.CONSTANT_MethodType:
final ConstantMethodType cmt = (ConstantMethodType) c;
str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8);
break;
case Const.CONSTANT_InvokeDynamic:
final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c;
str = cid.getBootstrapMethodAttrIndex() + ":" + constantToString(cid.getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
break;
case Const.CONSTANT_Dynamic:
final ConstantDynamic cd = (ConstantDynamic) c;
str = cd.getBootstrapMethodAttrIndex() + ":" + constantToString(cd.getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
break;
case Const.CONSTANT_Module:
i = ((ConstantModule) c).getNameIndex();
c = getConstantUtf8(i);
str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
break;
case Const.CONSTANT_Package:
i = ((ConstantPackage) c).getNameIndex();
c = getConstantUtf8(i);
str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
break;
default: // Never reached
throw new IllegalArgumentException("Unknown constant type " + tag);
}
return str;
}
/**
* Retrieves constant at 'index' from constant pool and resolve it to a string representation.
*
* @param index of constant in constant pool
* @param tag expected type
* @return String representation
*/
public String constantToString(final int index, final byte tag) {
return constantToString(getConstant(index, tag));
}
/**
* @return deep copy of this constant pool
*/
public ConstantPool copy() {
ConstantPool c = null;
try {
c = (ConstantPool) clone();
c.constantPool = new Constant[constantPool.length];
for (int i = 1; i < constantPool.length; i++) {
if (constantPool[i] != null) {
c.constantPool[i] = constantPool[i].copy();
}
}
} catch (final CloneNotSupportedException e) {
// TODO should this throw?
}
return c;
}
/**
* Dump constant pool to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if problem in writeShort or dump
*/
public void dump(final DataOutputStream file) throws IOException {
/*
* Constants over the size of the constant pool shall not be written out. This is a redundant measure as the ConstantPoolGen should have already
* reported an error back in the situation.
*/
final int size = Math.min(constantPool.length, Const.MAX_CP_ENTRIES);
file.writeShort(size);
for (int i = 1; i < size; i++) {
if (constantPool[i] != null) {
constantPool[i].dump(file);
}
}
}
/**
* Gets constant from constant pool.
*
* @param index Index in constant pool
* @return Constant value
* @see Constant
* @throws ClassFormatException if index is invalid
*/
@SuppressWarnings("unchecked")
public <T extends Constant> T getConstant(final int index) throws ClassFormatException {
return (T) getConstant(index, Constant.class);
}
/**
* Gets constant from constant pool and check whether it has the expected type.
*
* @param index Index in constant pool
* @param tag Tag of expected constant, i.e., its type
* @return Constant value
* @see Constant
* @throws ClassFormatException if constant type does not match tag
*/
@SuppressWarnings("unchecked")
public <T extends Constant> T getConstant(final int index, final byte tag) throws ClassFormatException {
return (T) getConstant(index, tag, Constant.class);
}
/**
* Gets constant from constant pool and check whether it has the expected type.
*
* @param index Index in constant pool
* @param tag Tag of expected constant, i.e., its type
* @return Constant value
* @see Constant
* @throws ClassFormatException if constant type does not match tag
* @since 6.6.0
*/
public <T extends Constant> T getConstant(final int index, final byte tag, final Class<T> castTo) throws ClassFormatException {
final T c = getConstant(index);
if (c.getTag() != tag) {
throw new ClassFormatException("Expected class '" + Const.getConstantName(tag) + "' at index " + index + " and got " + c);
}
return c;
}
/**
* Gets constant from constant pool.
*
* @param <T> A {@link Constant} subclass
* @param index Index in constant pool
* @param castTo The {@link Constant} subclass to cast to.
* @return Constant value
* @see Constant
* @throws ClassFormatException if index is invalid
* @since 6.6.0
*/
public <T extends Constant> T getConstant(final int index, final Class<T> castTo) throws ClassFormatException {
if (index >= constantPool.length || index < 0) {
throw new ClassFormatException("Invalid constant pool reference using index: " + index + ". Constant pool size is: " + constantPool.length);
}
if (constantPool[index] != null && !castTo.isAssignableFrom(constantPool[index].getClass())) {
throw new ClassFormatException("Invalid constant pool reference at index: " + index +
". Expected " + castTo + " but was " + constantPool[index].getClass());
}
// Previous check ensures this won't throw a ClassCastException
final T c = castTo.cast(constantPool[index]);
if (c == null
// the 0th element is always null
&& index != 0) {
final Constant prev = constantPool[index - 1];
if (prev == null || prev.getTag() != Const.CONSTANT_Double && prev.getTag() != Const.CONSTANT_Long) {
throw new ClassFormatException("Constant pool at index " + index + " is null.");
}
}
return c;
}
/**
* Gets constant from constant pool and check whether it has the expected type.
*
* @param index Index in constant pool
* @return ConstantInteger value
* @see ConstantInteger
* @throws ClassFormatException if constant type does not match tag
*/
public ConstantInteger getConstantInteger(final int index) {
return getConstant(index, Const.CONSTANT_Integer, ConstantInteger.class);
}
/**
* @return Array of constants.
* @see Constant
*/
public Constant[] getConstantPool() {
return constantPool;
}
/**
* Gets string from constant pool and bypass the indirection of 'ConstantClass' and 'ConstantString' objects. I.e. these classes have an index field that
* points to another entry of the constant pool of type 'ConstantUtf8' which contains the real data.
*
* @param index Index in constant pool
* @param tag Tag of expected constant, either ConstantClass or ConstantString
* @return Contents of string reference
* @see ConstantClass
* @see ConstantString
* @throws IllegalArgumentException if tag is invalid
*/
public String getConstantString(final int index, final byte tag) throws IllegalArgumentException {
int i;
/*
* This switch() is not that elegant, since the four classes have the same contents, they just differ in the name of the index field variable. But we
* want to stick to the JVM naming conventions closely though we could have solved these more elegantly by using the same variable name or by
* subclassing.
*/
switch (tag) {
case Const.CONSTANT_Class:
i = getConstant(index, ConstantClass.class).getNameIndex();
break;
case Const.CONSTANT_String:
i = getConstant(index, ConstantString.class).getStringIndex();
break;
case Const.CONSTANT_Module:
i = getConstant(index, ConstantModule.class).getNameIndex();
break;
case Const.CONSTANT_Package:
i = getConstant(index, ConstantPackage.class).getNameIndex();
break;
case Const.CONSTANT_Utf8:
return getConstantUtf8(index).getBytes();
default:
throw new IllegalArgumentException("getConstantString called with illegal tag " + tag);
}
// Finally get the string from the constant pool
return getConstantUtf8(i).getBytes();
}
/**
* Gets constant from constant pool and check whether it has the expected type.
*
* @param index Index in constant pool
* @return ConstantUtf8 value
* @see ConstantUtf8
* @throws ClassFormatException if constant type does not match tag
*/
public ConstantUtf8 getConstantUtf8(final int index) throws ClassFormatException {
return getConstant(index, Const.CONSTANT_Utf8, ConstantUtf8.class);
}
/**
* @return Length of constant pool.
*/
public int getLength() {
return constantPool == null ? 0 : constantPool.length;
}
@Override
public Iterator<Constant> iterator() {
return Arrays.stream(constantPool).iterator();
}
/**
* @param constant Constant to set
*/
public void setConstant(final int index, final Constant constant) {
constantPool[index] = constant;
}
/**
* @param constantPool
*/
public void setConstantPool(final Constant[] constantPool) {
this.constantPool = constantPool;
}
/**
* @return String representation.
*/
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
for (int i = 1; i < constantPool.length; i++) {
buf.append(i).append(")").append(constantPool[i]).append("\n");
}
return buf.toString();
}
}

View File

@@ -0,0 +1,120 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from the abstract {@link Constant} and represents a reference to a String object.
*
* @see Constant
*/
public final class ConstantString extends Constant implements ConstantObject {
private int stringIndex; // Identical to ConstantClass except for this name
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public ConstantString(final ConstantString c) {
this(c.getStringIndex());
}
/**
* Initialize instance from file data.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantString(final DataInput file) throws IOException {
this(file.readUnsignedShort());
}
/**
* @param stringIndex Index of Constant_Utf8 in constant pool
*/
public ConstantString(final int stringIndex) {
super(Const.CONSTANT_String);
this.stringIndex = stringIndex;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantString(this);
}
/**
* Dump constant field reference to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
file.writeShort(stringIndex);
}
/**
* @return dereferenced string
*/
public String getBytes(final ConstantPool cp) {
return (String) getConstantValue(cp);
}
/**
* @return String object
*/
@Override
public Object getConstantValue(final ConstantPool cp) {
return cp.getConstantUtf8(stringIndex).getBytes();
}
/**
* @return Index in constant pool of the string (ConstantUtf8).
*/
public int getStringIndex() {
return stringIndex;
}
/**
* @param stringIndex the index into the constant of the string value
*/
public void setStringIndex(final int stringIndex) {
this.stringIndex = stringIndex;
}
/**
* @return String representation.
*/
@Override
public String toString() {
return super.toString() + "(stringIndex = " + stringIndex + ")";
}
}

View File

@@ -0,0 +1,259 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
/**
* Extends the abstract {@link Constant} to represent a reference to a UTF-8 encoded string.
* <p>
* The following system properties govern caching this class performs.
* </p>
* <ul>
* <li>{@value #SYS_PROP_CACHE_MAX_ENTRIES} (since 6.4): The size of the cache, by default 0, meaning caching is
* disabled.</li>
* <li>{@value #SYS_PROP_CACHE_MAX_ENTRY_SIZE} (since 6.0): The maximum size of the values to cache, by default 200, 0
* disables caching. Values larger than this are <em>not</em> cached.</li>
* <li>{@value #SYS_PROP_STATISTICS} (since 6.0): Prints statistics on the console when the JVM exits.</li>
* </ul>
* <p>
* Here is a sample Maven invocation with caching disabled:
* </p>
*
* <pre>
* mvn test -Dbcel.statistics=true -Dbcel.maxcached.size=0 -Dbcel.maxcached=0
* </pre>
* <p>
* Here is a sample Maven invocation with caching enabled:
* </p>
*
* <pre>
* mvn test -Dbcel.statistics=true -Dbcel.maxcached.size=100000 -Dbcel.maxcached=5000000
* </pre>
*
* @see Constant
*/
public final class ConstantUtf8 extends Constant {
private static class Cache {
private static final boolean BCEL_STATISTICS = Boolean.getBoolean(SYS_PROP_STATISTICS);
private static final int MAX_ENTRIES = Integer.getInteger(SYS_PROP_CACHE_MAX_ENTRIES, 0).intValue();
private static final int INITIAL_CAPACITY = (int) (MAX_ENTRIES / 0.75);
private static final HashMap<String, ConstantUtf8> CACHE = new LinkedHashMap<String, ConstantUtf8>(INITIAL_CAPACITY, 0.75f, true) {
private static final long serialVersionUID = -8506975356158971766L;
@Override
protected boolean removeEldestEntry(final Map.Entry<String, ConstantUtf8> eldest) {
return size() > MAX_ENTRIES;
}
};
// Set the size to 0 or below to skip caching entirely
private static final int MAX_ENTRY_SIZE = Integer.getInteger(SYS_PROP_CACHE_MAX_ENTRY_SIZE, 200).intValue();
static boolean isEnabled() {
return Cache.MAX_ENTRIES > 0 && MAX_ENTRY_SIZE > 0;
}
}
// TODO these should perhaps be AtomicInt?
private static volatile int considered;
private static volatile int created;
private static volatile int hits;
private static volatile int skipped;
private static final String SYS_PROP_CACHE_MAX_ENTRIES = "bcel.maxcached";
private static final String SYS_PROP_CACHE_MAX_ENTRY_SIZE = "bcel.maxcached.size";
private static final String SYS_PROP_STATISTICS = "bcel.statistics";
static {
if (Cache.BCEL_STATISTICS) {
Runtime.getRuntime().addShutdownHook(new Thread(ConstantUtf8::printStats));
}
}
/**
* Clears the cache.
*
* @since 6.4.0
*/
public static synchronized void clearCache() {
Cache.CACHE.clear();
}
// for access by test code
static synchronized void clearStats() {
hits = considered = skipped = created = 0;
}
/**
* Gets a new or cached instance of the given value.
* <p>
* See {@link ConstantUtf8} class Javadoc for details.
* </p>
*
* @param value the value.
* @return a new or cached instance of the given value.
* @since 6.0
*/
public static ConstantUtf8 getCachedInstance(final String value) {
if (value.length() > Cache.MAX_ENTRY_SIZE) {
skipped++;
return new ConstantUtf8(value);
}
considered++;
synchronized (ConstantUtf8.class) { // might be better with a specific lock object
ConstantUtf8 result = Cache.CACHE.get(value);
if (result != null) {
hits++;
return result;
}
result = new ConstantUtf8(value);
Cache.CACHE.put(value, result);
return result;
}
}
/**
* Gets a new or cached instance of the given value.
* <p>
* See {@link ConstantUtf8} class Javadoc for details.
* </p>
*
* @param dataInput the value.
* @return a new or cached instance of the given value.
* @throws IOException if an I/O error occurs.
* @since 6.0
*/
public static ConstantUtf8 getInstance(final DataInput dataInput) throws IOException {
return getInstance(dataInput.readUTF());
}
/**
* Gets a new or cached instance of the given value.
* <p>
* See {@link ConstantUtf8} class Javadoc for details.
* </p>
*
* @param value the value.
* @return a new or cached instance of the given value.
* @since 6.0
*/
public static ConstantUtf8 getInstance(final String value) {
return Cache.isEnabled() ? getCachedInstance(value) : new ConstantUtf8(value);
}
// for access by test code
static void printStats() {
final String prefix = "[Apache Commons BCEL]";
System.err.printf("%s Cache hit %,d/%,d, %d skipped.%n", prefix, hits, considered, skipped);
System.err.printf("%s Total of %,d ConstantUtf8 objects created.%n", prefix, created);
System.err.printf("%s Configuration: %s=%,d, %s=%,d.%n", prefix, SYS_PROP_CACHE_MAX_ENTRIES, Cache.MAX_ENTRIES, SYS_PROP_CACHE_MAX_ENTRY_SIZE,
Cache.MAX_ENTRY_SIZE);
}
private final String value;
/**
* Initializes from another object.
*
* @param constantUtf8 the value.
*/
public ConstantUtf8(final ConstantUtf8 constantUtf8) {
this(constantUtf8.getBytes());
}
/**
* Initializes instance from file data.
*
* @param dataInput Input stream
* @throws IOException if an I/O error occurs.
*/
ConstantUtf8(final DataInput dataInput) throws IOException {
super(Const.CONSTANT_Utf8);
value = dataInput.readUTF();
created++;
}
/**
* @param value Data
*/
public ConstantUtf8(final String value) {
super(Const.CONSTANT_Utf8);
this.value = Objects.requireNonNull(value, "value");
created++;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantUtf8(this);
}
/**
* Dumps String in Utf8 format to file stream.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
file.writeUTF(value);
}
/**
* @return Data converted to string.
*/
public String getBytes() {
return value;
}
/**
* @param bytes the raw bytes of this UTF-8
* @deprecated (since 6.0)
*/
@java.lang.Deprecated
public void setBytes(final String bytes) {
throw new UnsupportedOperationException();
}
/**
* @return String representation
*/
@Override
public String toString() {
return super.toString() + "(\"" + Utility.replace(value, "\n", "\\n") + "\")";
}
}

View File

@@ -0,0 +1,156 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from <em>Attribute</em> and represents a constant value, i.e., a default value for initializing
* a class field. This class is instantiated by the <em>Attribute.readAttribute()</em> method.
*
* <pre>
* ConstantValue_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 constantvalue_index;
* }
* </pre>
* @see Attribute
*/
public final class ConstantValue extends Attribute {
private int constantValueIndex;
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
* physical copy.
*
* @param c Source to copy.
*/
public ConstantValue(final ConstantValue c) {
this(c.getNameIndex(), c.getLength(), c.getConstantValueIndex(), c.getConstantPool());
}
/**
* Construct object from input stream.
*
* @param nameIndex Name index in constant pool
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
ConstantValue(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, input.readUnsignedShort(), constantPool);
}
/**
* @param nameIndex Name index in constant pool
* @param length Content length in bytes
* @param constantValueIndex Index in constant pool
* @param constantPool Array of constants
*/
public ConstantValue(final int nameIndex, final int length, final int constantValueIndex, final ConstantPool constantPool) {
super(Const.ATTR_CONSTANT_VALUE, nameIndex, Args.require(length, 2, "ConstantValue attribute length"), constantPool);
this.constantValueIndex = constantValueIndex;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitConstantValue(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final ConstantValue c = (ConstantValue) clone();
c.setConstantPool(constantPool);
return c;
}
/**
* Dump constant value attribute to file stream on binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(constantValueIndex);
}
/**
* @return Index in constant pool of constant value.
*/
public int getConstantValueIndex() {
return constantValueIndex;
}
/**
* @param constantValueIndex the index info the constant pool of this constant value
*/
public void setConstantValueIndex(final int constantValueIndex) {
this.constantValueIndex = constantValueIndex;
}
/**
* @return String representation of constant value.
*/
@Override
public String toString() {
Constant c = super.getConstantPool().getConstant(constantValueIndex);
String buf;
int i;
// Print constant to string depending on its type
switch (c.getTag()) {
case Const.CONSTANT_Long:
buf = String.valueOf(((ConstantLong) c).getBytes());
break;
case Const.CONSTANT_Float:
buf = String.valueOf(((ConstantFloat) c).getBytes());
break;
case Const.CONSTANT_Double:
buf = String.valueOf(((ConstantDouble) c).getBytes());
break;
case Const.CONSTANT_Integer:
buf = String.valueOf(((ConstantInteger) c).getBytes());
break;
case Const.CONSTANT_String:
i = ((ConstantString) c).getStringIndex();
c = super.getConstantPool().getConstantUtf8(i);
buf = "\"" + Utility.convertString(((ConstantUtf8) c).getBytes()) + "\"";
break;
default:
throw new IllegalStateException("Type of ConstValue invalid: " + c);
}
return buf;
}
}

View File

@@ -0,0 +1,134 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from <em>Attribute</em> and denotes that this is a deprecated method. It is instantiated from
* the <em>Attribute.readAttribute()</em> method.
*
* @see Attribute
*/
public final class Deprecated extends Attribute {
private byte[] bytes;
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
* physical copy.
*
* @param c Source to copy.
*/
public Deprecated(final Deprecated c) {
this(c.getNameIndex(), c.getLength(), c.getBytes(), c.getConstantPool());
}
/**
* @param nameIndex Index in constant pool to CONSTANT_Utf8
* @param length Content length in bytes
* @param bytes Attribute contents
* @param constantPool Array of constants
*/
public Deprecated(final int nameIndex, final int length, final byte[] bytes, final ConstantPool constantPool) {
super(Const.ATTR_DEPRECATED, nameIndex, Args.require0(length, "Deprecated attribute length"), constantPool);
this.bytes = bytes;
}
/**
* Construct object from input stream.
*
* @param nameIndex Index in constant pool to CONSTANT_Utf8
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
Deprecated(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, (byte[]) null, constantPool);
if (length > 0) {
bytes = new byte[length];
input.readFully(bytes);
println("Deprecated attribute with length > 0");
}
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitDeprecated(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final Deprecated c = (Deprecated) clone();
if (bytes != null) {
c.bytes = bytes.clone();
}
c.setConstantPool(constantPool);
return c;
}
/**
* Dump source file attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
if (super.getLength() > 0) {
file.write(bytes, 0, super.getLength());
}
}
/**
* @return data bytes.
*/
public byte[] getBytes() {
return bytes;
}
/**
* @param bytes the raw bytes that represents this byte array
*/
public void setBytes(final byte[] bytes) {
this.bytes = bytes;
}
/**
* @return attribute name
*/
@Override
public String toString() {
return Const.getAttributeName(Const.ATTR_DEPRECATED) + ": true";
}
}

View File

@@ -0,0 +1,547 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import java.util.Objects;
import java.util.Stack;
import java.util.stream.Stream;
/**
* Traverses a JavaClass with another Visitor object 'piggy-backed' that is applied to all components of a JavaClass
* object. I.e. this class supplies the traversal strategy, other classes can make use of it.
*/
public class DescendingVisitor implements Visitor {
private final JavaClass clazz;
private final Visitor visitor;
private final Stack<Object> stack = new Stack<>();
/**
* @param clazz Class to traverse
* @param visitor visitor object to apply to all components
*/
public DescendingVisitor(final JavaClass clazz, final Visitor visitor) {
this.clazz = clazz;
this.visitor = visitor;
}
private <E extends Node> void accept(final E[] node) {
Stream.of(node).forEach(e -> e.accept(this));
}
/**
* @return current object
*/
public Object current() {
return stack.peek();
}
/**
* @return container of current entitity, i.e., predecessor during traversal
*/
public Object predecessor() {
return predecessor(0);
}
/**
* @param level nesting level, i.e., 0 returns the direct predecessor
* @return container of current entitity, i.e., predecessor during traversal
*/
public Object predecessor(final int level) {
final int size = stack.size();
if (size < 2 || level < 0) {
return null;
}
return stack.elementAt(size - (level + 2)); // size - 1 == current
}
/**
* Start traversal.
*/
public void visit() {
clazz.accept(this);
}
/**
* @since 6.0
*/
@Override
public void visitAnnotation(final Annotations annotation) {
stack.push(annotation);
annotation.accept(visitor);
accept(annotation.getAnnotationEntries());
stack.pop();
}
/**
* @since 6.0
*/
@Override
public void visitAnnotationDefault(final AnnotationDefault obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
/**
* @since 6.0
*/
@Override
public void visitAnnotationEntry(final AnnotationEntry annotationEntry) {
stack.push(annotationEntry);
annotationEntry.accept(visitor);
stack.pop();
}
/**
* @since 6.0
*/
@Override
public void visitBootstrapMethods(final BootstrapMethods bm) {
stack.push(bm);
bm.accept(visitor);
// BootstrapMethod[] bms = bm.getBootstrapMethods();
// for (int i = 0; i < bms.length; i++)
// {
// bms[i].accept(this);
// }
stack.pop();
}
@Override
public void visitCode(final Code code) {
stack.push(code);
code.accept(visitor);
accept(code.getExceptionTable());
accept(code.getAttributes());
stack.pop();
}
@Override
public void visitCodeException(final CodeException ce) {
stack.push(ce);
ce.accept(visitor);
stack.pop();
}
@Override
public void visitConstantClass(final ConstantClass constant) {
stack.push(constant);
constant.accept(visitor);
stack.pop();
}
@Override
public void visitConstantDouble(final ConstantDouble constant) {
stack.push(constant);
constant.accept(visitor);
stack.pop();
}
/** @since 6.3 */
@Override
public void visitConstantDynamic(final ConstantDynamic obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
@Override
public void visitConstantFieldref(final ConstantFieldref constant) {
stack.push(constant);
constant.accept(visitor);
stack.pop();
}
@Override
public void visitConstantFloat(final ConstantFloat constant) {
stack.push(constant);
constant.accept(visitor);
stack.pop();
}
@Override
public void visitConstantInteger(final ConstantInteger constant) {
stack.push(constant);
constant.accept(visitor);
stack.pop();
}
@Override
public void visitConstantInterfaceMethodref(final ConstantInterfaceMethodref constant) {
stack.push(constant);
constant.accept(visitor);
stack.pop();
}
/**
* @since 6.0
*/
@Override
public void visitConstantInvokeDynamic(final ConstantInvokeDynamic constant) {
stack.push(constant);
constant.accept(visitor);
stack.pop();
}
@Override
public void visitConstantLong(final ConstantLong constant) {
stack.push(constant);
constant.accept(visitor);
stack.pop();
}
/** @since 6.0 */
@Override
public void visitConstantMethodHandle(final ConstantMethodHandle obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
@Override
public void visitConstantMethodref(final ConstantMethodref constant) {
stack.push(constant);
constant.accept(visitor);
stack.pop();
}
/** @since 6.0 */
@Override
public void visitConstantMethodType(final ConstantMethodType obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
/** @since 6.1 */
@Override
public void visitConstantModule(final ConstantModule obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
@Override
public void visitConstantNameAndType(final ConstantNameAndType constant) {
stack.push(constant);
constant.accept(visitor);
stack.pop();
}
/** @since 6.1 */
@Override
public void visitConstantPackage(final ConstantPackage obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
@Override
public void visitConstantPool(final ConstantPool cp) {
stack.push(cp);
cp.accept(visitor);
Stream.of(cp.getConstantPool()).filter(Objects::nonNull).forEach(e -> e.accept(this));
stack.pop();
}
@Override
public void visitConstantString(final ConstantString constant) {
stack.push(constant);
constant.accept(visitor);
stack.pop();
}
@Override
public void visitConstantUtf8(final ConstantUtf8 constant) {
stack.push(constant);
constant.accept(visitor);
stack.pop();
}
@Override
public void visitConstantValue(final ConstantValue cv) {
stack.push(cv);
cv.accept(visitor);
stack.pop();
}
@Override
public void visitDeprecated(final Deprecated attribute) {
stack.push(attribute);
attribute.accept(visitor);
stack.pop();
}
/**
* @since 6.0
*/
@Override
public void visitEnclosingMethod(final EnclosingMethod obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
@Override
public void visitExceptionTable(final ExceptionTable table) {
stack.push(table);
table.accept(visitor);
stack.pop();
}
@Override
public void visitField(final JavaField field) {
stack.push(field);
field.accept(visitor);
accept(field.getAttributes());
stack.pop();
}
@Override
public void visitInnerClass(final InnerClass inner) {
stack.push(inner);
inner.accept(visitor);
stack.pop();
}
@Override
public void visitInnerClasses(final InnerClasses ic) {
stack.push(ic);
ic.accept(visitor);
accept(ic.getInnerClasses());
stack.pop();
}
@Override
public void visitJavaClass(final JavaClass clazz) {
stack.push(clazz);
clazz.accept(visitor);
accept(clazz.getFields());
accept(clazz.getMethods());
accept(clazz.getAttributes());
clazz.getConstantPool().accept(this);
stack.pop();
}
@Override
public void visitLineNumber(final LineNumber number) {
stack.push(number);
number.accept(visitor);
stack.pop();
}
@Override
public void visitLineNumberTable(final LineNumberTable table) {
stack.push(table);
table.accept(visitor);
accept(table.getLineNumberTable());
stack.pop();
}
@Override
public void visitLocalVariable(final LocalVariable var) {
stack.push(var);
var.accept(visitor);
stack.pop();
}
@Override
public void visitLocalVariableTable(final LocalVariableTable table) {
stack.push(table);
table.accept(visitor);
accept(table.getLocalVariableTable());
stack.pop();
}
/**
* @since 6.0
*/
@Override
public void visitLocalVariableTypeTable(final LocalVariableTypeTable obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
@Override
public void visitMethod(final JavaMethod method) {
stack.push(method);
method.accept(visitor);
accept(method.getAttributes());
stack.pop();
}
/**
* @since 6.4.0
*/
@Override
public void visitMethodParameter(final MethodParameter obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
/**
* @since 6.0
*/
@Override
public void visitMethodParameters(final MethodParameters obj) {
stack.push(obj);
obj.accept(visitor);
Stream.of(obj.getParameters()).forEach(e -> e.accept(this));
stack.pop();
}
/** @since 6.4.0 */
@Override
public void visitModule(final Module obj) {
stack.push(obj);
obj.accept(visitor);
accept(obj.getRequiresTable());
accept(obj.getExportsTable());
accept(obj.getOpensTable());
accept(obj.getProvidesTable());
stack.pop();
}
/** @since 6.4.0 */
@Override
public void visitModuleExports(final ModuleExports obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
/** @since 6.4.0 */
@Override
public void visitModuleMainClass(final ModuleMainClass obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
/** @since 6.4.0 */
@Override
public void visitModuleOpens(final ModuleOpens obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
/** @since 6.4.0 */
@Override
public void visitModulePackages(final ModulePackages obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
/** @since 6.4.0 */
@Override
public void visitModuleProvides(final ModuleProvides obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
/** @since 6.4.0 */
@Override
public void visitModuleRequires(final ModuleRequires obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
/** @since 6.4.0 */
@Override
public void visitNestHost(final NestHost obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
/** @since 6.4.0 */
@Override
public void visitNestMembers(final NestMembers obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
/**
* @since 6.0
*/
@Override
public void visitParameterAnnotation(final ParameterAnnotations obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
/** @since 6.0 */
@Override
public void visitParameterAnnotationEntry(final ParameterAnnotationEntry obj) {
stack.push(obj);
obj.accept(visitor);
stack.pop();
}
@Override
public void visitSignature(final Signature attribute) {
stack.push(attribute);
attribute.accept(visitor);
stack.pop();
}
@Override
public void visitSourceFile(final SourceFile attribute) {
stack.push(attribute);
attribute.accept(visitor);
stack.pop();
}
@Override
public void visitStackMap(final StackMap table) {
stack.push(table);
table.accept(visitor);
accept(table.getStackMap());
stack.pop();
}
@Override
public void visitStackMapEntry(final StackMapEntry var) {
stack.push(var);
var.accept(visitor);
stack.pop();
}
@Override
public void visitSynthetic(final Synthetic attribute) {
stack.push(attribute);
attribute.accept(visitor);
stack.pop();
}
@Override
public void visitUnknown(final Unknown attribute) {
stack.push(attribute);
attribute.accept(visitor);
stack.pop();
}
}

View File

@@ -0,0 +1,174 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* The element_value structure is documented at https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7.16.1
*
* <pre>
* element_value {
* u1 tag;
* union {
* u2 const_value_index;
*
* { u2 type_name_index;
* u2 const_name_index;
* } enum_const_value;
*
* u2 class_info_index;
*
* annotation annotation_value;
*
* { u2 num_values;
* element_value values[num_values];
* } array_value;
* } value;
*}
*</pre>
* @since 6.0
*/
public abstract class ElementValue {
public static final byte STRING = 's';
public static final byte ENUM_CONSTANT = 'e';
public static final byte CLASS = 'c';
public static final byte ANNOTATION = '@';
public static final byte ARRAY = '[';
public static final byte PRIMITIVE_INT = 'I';
public static final byte PRIMITIVE_BYTE = 'B';
public static final byte PRIMITIVE_CHAR = 'C';
public static final byte PRIMITIVE_DOUBLE = 'D';
public static final byte PRIMITIVE_FLOAT = 'F';
public static final byte PRIMITIVE_LONG = 'J';
public static final byte PRIMITIVE_SHORT = 'S';
public static final byte PRIMITIVE_BOOLEAN = 'Z';
/**
* Reads an {@code element_value} as an {@code ElementValue}.
*
* @param input Raw data input.
* @param cpool Constant pool.
* @return a new ElementValue.
* @throws IOException if an I/O error occurs.
*/
public static ElementValue readElementValue(final DataInput input, final ConstantPool cpool) throws IOException {
return readElementValue(input, cpool, 0);
}
/**
* Reads an {@code element_value} as an {@code ElementValue}.
*
* @param input Raw data input.
* @param cpool Constant pool.
* @param arrayNesting level of current array nesting.
* @return a new ElementValue.
* @throws IOException if an I/O error occurs.
* @since 6.7.0
*/
public static ElementValue readElementValue(final DataInput input, final ConstantPool cpool, int arrayNesting)
throws IOException {
final byte tag = input.readByte();
switch (tag) {
case PRIMITIVE_BYTE:
case PRIMITIVE_CHAR:
case PRIMITIVE_DOUBLE:
case PRIMITIVE_FLOAT:
case PRIMITIVE_INT:
case PRIMITIVE_LONG:
case PRIMITIVE_SHORT:
case PRIMITIVE_BOOLEAN:
case STRING:
return new SimpleElementValue(tag, input.readUnsignedShort(), cpool);
case ENUM_CONSTANT:
return new EnumElementValue(ENUM_CONSTANT, input.readUnsignedShort(), input.readUnsignedShort(), cpool);
case CLASS:
return new ClassElementValue(CLASS, input.readUnsignedShort(), cpool);
case ANNOTATION:
// TODO isRuntimeVisible
return new AnnotationElementValue(ANNOTATION, AnnotationEntry.read(input, cpool, false), cpool);
case ARRAY:
arrayNesting++;
if (arrayNesting > Const.MAX_ARRAY_DIMENSIONS) {
// JVM spec 4.4.1
throw new ClassFormatException(String.format("Arrays are only valid if they represent %,d or fewer dimensions.", Const.MAX_ARRAY_DIMENSIONS));
}
final int numArrayVals = input.readUnsignedShort();
final ElementValue[] evalues = new ElementValue[numArrayVals];
for (int j = 0; j < numArrayVals; j++) {
evalues[j] = ElementValue.readElementValue(input, cpool, arrayNesting);
}
return new ArrayElementValue(ARRAY, evalues, cpool);
default:
throw new ClassFormatException("Unexpected element value tag in annotation: " + tag);
}
}
/**
* @deprecated (since 6.0) will be made private and final; do not access directly, use getter
*/
@java.lang.Deprecated
protected int type; // TODO should be final
/**
* @deprecated (since 6.0) will be made private and final; do not access directly, use getter
*/
@java.lang.Deprecated
protected ConstantPool cpool; // TODO should be final
protected ElementValue(final int type, final ConstantPool cpool) {
this.type = type;
this.cpool = cpool;
}
public abstract void dump(DataOutputStream dos) throws IOException;
/** @since 6.0 */
final ConstantPool getConstantPool() {
return cpool;
}
public int getElementValueType() {
return type;
}
/** @since 6.0 */
final int getType() {
return type;
}
public abstract String stringifyValue();
public String toShortString() {
return stringifyValue();
}
@Override
public String toString() {
return stringifyValue();
}
}

View File

@@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* An annotation's element value pair.
*
* @since 6.0
*/
public class ElementValuePair {
static final ElementValuePair[] EMPTY_ARRAY = {};
private final ElementValue elementValue;
private final ConstantPool constantPool;
private final int elementNameIndex;
public ElementValuePair(final int elementNameIndex, final ElementValue elementValue, final ConstantPool constantPool) {
this.elementValue = elementValue;
this.elementNameIndex = elementNameIndex;
this.constantPool = constantPool;
}
protected void dump(final DataOutputStream dos) throws IOException {
dos.writeShort(elementNameIndex); // u2 name of the element
elementValue.dump(dos);
}
public int getNameIndex() {
return elementNameIndex;
}
public String getNameString() {
return constantPool.getConstantUtf8(elementNameIndex).getBytes();
}
public final ElementValue getValue() {
return elementValue;
}
public String toShortString() {
final StringBuilder result = new StringBuilder();
result.append(getNameString()).append("=").append(getValue().toShortString());
return result.toString();
}
}

View File

@@ -0,0 +1,320 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
/**
* Visitor with empty method bodies, can be extended and used in conjunction with the DescendingVisitor class, e.g. By
* courtesy of David Spencer.
*
* @see DescendingVisitor
*/
public class EmptyVisitor implements Visitor {
protected EmptyVisitor() {
}
/**
* @since 6.0
*/
@Override
public void visitAnnotation(final Annotations obj) {
}
/**
* @since 6.0
*/
@Override
public void visitAnnotationDefault(final AnnotationDefault obj) {
}
/**
* @since 6.0
*/
@Override
public void visitAnnotationEntry(final AnnotationEntry obj) {
}
/**
* @since 6.0
*/
@Override
public void visitBootstrapMethods(final BootstrapMethods obj) {
}
@Override
public void visitCode(final Code obj) {
}
@Override
public void visitCodeException(final CodeException obj) {
}
@Override
public void visitConstantClass(final ConstantClass obj) {
}
@Override
public void visitConstantDouble(final ConstantDouble obj) {
}
/**
* @since 6.3
*/
@Override
public void visitConstantDynamic(final ConstantDynamic obj) {
}
@Override
public void visitConstantFieldref(final ConstantFieldref obj) {
}
@Override
public void visitConstantFloat(final ConstantFloat obj) {
}
@Override
public void visitConstantInteger(final ConstantInteger obj) {
}
@Override
public void visitConstantInterfaceMethodref(final ConstantInterfaceMethodref obj) {
}
@Override
public void visitConstantInvokeDynamic(final ConstantInvokeDynamic obj) {
}
@Override
public void visitConstantLong(final ConstantLong obj) {
}
/**
* @since 6.0
*/
@Override
public void visitConstantMethodHandle(final ConstantMethodHandle constantMethodHandle) {
}
@Override
public void visitConstantMethodref(final ConstantMethodref obj) {
}
/**
* @since 6.0
*/
@Override
public void visitConstantMethodType(final ConstantMethodType obj) {
}
/**
* @since 6.1
*/
@Override
public void visitConstantModule(final ConstantModule constantModule) {
}
@Override
public void visitConstantNameAndType(final ConstantNameAndType obj) {
}
/**
* @since 6.1
*/
@Override
public void visitConstantPackage(final ConstantPackage constantPackage) {
}
@Override
public void visitConstantPool(final ConstantPool obj) {
}
@Override
public void visitConstantString(final ConstantString obj) {
}
@Override
public void visitConstantUtf8(final ConstantUtf8 obj) {
}
@Override
public void visitConstantValue(final ConstantValue obj) {
}
@Override
public void visitDeprecated(final Deprecated obj) {
}
/**
* @since 6.0
*/
@Override
public void visitEnclosingMethod(final EnclosingMethod obj) {
}
@Override
public void visitExceptionTable(final ExceptionTable obj) {
}
@Override
public void visitField(final JavaField obj) {
}
@Override
public void visitInnerClass(final InnerClass obj) {
}
@Override
public void visitInnerClasses(final InnerClasses obj) {
}
@Override
public void visitJavaClass(final JavaClass obj) {
}
@Override
public void visitLineNumber(final LineNumber obj) {
}
@Override
public void visitLineNumberTable(final LineNumberTable obj) {
}
@Override
public void visitLocalVariable(final LocalVariable obj) {
}
@Override
public void visitLocalVariableTable(final LocalVariableTable obj) {
}
/**
* @since 6.0
*/
@Override
public void visitLocalVariableTypeTable(final LocalVariableTypeTable obj) {
}
@Override
public void visitMethod(final JavaMethod obj) {
}
/**
* @since 6.0
* @Override public void visitStackMapTable(StackMapTable obj) { }
*/
/**
* @since 6.0
* @Override public void visitStackMapTableEntry(StackMapTableEntry obj) { }
*/
/**
* @since 6.4.0
*/
@Override
public void visitMethodParameter(final MethodParameter obj) {
}
/**
* @since 6.0
*/
@Override
public void visitMethodParameters(final MethodParameters obj) {
}
/** @since 6.4.0 */
@Override
public void visitModule(final Module obj) {
}
/** @since 6.4.0 */
@Override
public void visitModuleExports(final ModuleExports obj) {
}
/** @since 6.4.0 */
@Override
public void visitModuleMainClass(final ModuleMainClass obj) {
}
/** @since 6.4.0 */
@Override
public void visitModuleOpens(final ModuleOpens obj) {
}
/** @since 6.4.0 */
@Override
public void visitModulePackages(final ModulePackages obj) {
}
/** @since 6.4.0 */
@Override
public void visitModuleProvides(final ModuleProvides obj) {
}
/** @since 6.4.0 */
@Override
public void visitModuleRequires(final ModuleRequires obj) {
}
/** @since 6.4.0 */
@Override
public void visitNestHost(final NestHost obj) {
}
/** @since 6.4.0 */
@Override
public void visitNestMembers(final NestMembers obj) {
}
/**
* @since 6.0
*/
@Override
public void visitParameterAnnotation(final ParameterAnnotations obj) {
}
/**
* @since 6.0
*/
@Override
public void visitParameterAnnotationEntry(final ParameterAnnotationEntry parameterAnnotationEntry) {
}
@Override
public void visitSignature(final Signature obj) {
}
@Override
public void visitSourceFile(final SourceFile obj) {
}
@Override
public void visitStackMap(final StackMap obj) {
}
@Override
public void visitStackMapEntry(final StackMapEntry obj) {
}
@Override
public void visitSynthetic(final Synthetic obj) {
}
@Override
public void visitUnknown(final Unknown obj) {
}
}

View File

@@ -0,0 +1,102 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This attribute exists for local or anonymous classes and ... there can be only one.
*
* @since 6.0
*/
public class EnclosingMethod extends Attribute {
// Pointer to the CONSTANT_Class_info structure representing the
// innermost class that encloses the declaration of the current class.
private int classIndex;
// If the current class is not immediately enclosed by a method or
// constructor, then the value of the method_index item must be zero.
// Otherwise, the value of the method_index item must point to a
// CONSTANT_NameAndType_info structure representing the name and the
// type of a method in the class referenced by the class we point
// to in the class_index. *It is the compiler responsibility* to
// ensure that the method identified by this index is the closest
// lexically enclosing method that includes the local/anonymous class.
private int methodIndex;
// Ctors - and code to read an attribute in.
EnclosingMethod(final int nameIndex, final int len, final DataInput input, final ConstantPool cpool) throws IOException {
this(nameIndex, len, input.readUnsignedShort(), input.readUnsignedShort(), cpool);
}
private EnclosingMethod(final int nameIndex, final int len, final int classIndex, final int methodIndex, final ConstantPool cpool) {
super(Const.ATTR_ENCLOSING_METHOD, nameIndex, Args.require(len, 4, "EnclosingMethod attribute length"), cpool);
this.classIndex = Args.requireU2(classIndex, 0, cpool.getLength(), "EnclosingMethod class index");
this.methodIndex = Args.requireU2(methodIndex, "EnclosingMethod method index");
}
@Override
public void accept(final Visitor v) {
v.visitEnclosingMethod(this);
}
@Override
public Attribute copy(final ConstantPool constantPool) {
return (Attribute) clone();
}
@Override
public final void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(classIndex);
file.writeShort(methodIndex);
}
public final ConstantClass getEnclosingClass() {
return super.getConstantPool().getConstant(classIndex, Const.CONSTANT_Class, ConstantClass.class);
}
// Accessors
public final int getEnclosingClassIndex() {
return classIndex;
}
public final ConstantNameAndType getEnclosingMethod() {
if (methodIndex == 0) {
return null;
}
return super.getConstantPool().getConstant(methodIndex, Const.CONSTANT_NameAndType, ConstantNameAndType.class);
}
public final int getEnclosingMethodIndex() {
return methodIndex;
}
public final void setEnclosingClassIndex(final int idx) {
classIndex = idx;
}
public final void setEnclosingMethodIndex(final int idx) {
methodIndex = idx;
}
}

View File

@@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* @since 6.0
*/
public class EnumElementValue extends ElementValue {
// For enum types, these two indices point to the type and value
private final int typeIdx;
private final int valueIdx;
public EnumElementValue(final int type, final int typeIdx, final int valueIdx, final ConstantPool cpool) {
super(type, cpool);
if (type != ENUM_CONSTANT) {
throw new ClassFormatException("Only element values of type enum can be built with this ctor - type specified: " + type);
}
this.typeIdx = typeIdx;
this.valueIdx = valueIdx;
}
@Override
public void dump(final DataOutputStream dos) throws IOException {
dos.writeByte(super.getType()); // u1 type of value (ENUM_CONSTANT == 'e')
dos.writeShort(typeIdx); // u2
dos.writeShort(valueIdx); // u2
}
public String getEnumTypeString() {
return super.getConstantPool().getConstantUtf8(typeIdx).getBytes();
}
public String getEnumValueString() {
return super.getConstantPool().getConstantUtf8(valueIdx).getBytes();
}
public int getTypeIndex() {
return typeIdx;
}
public int getValueIndex() {
return valueIdx;
}
@Override
public String stringifyValue() {
return super.getConstantPool().getConstantUtf8(valueIdx).getBytes();
}
}

View File

@@ -0,0 +1,176 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import org.apache.commons.lang3.ArrayUtils;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
/**
* This class represents the table of exceptions that are thrown by a method. This attribute may be used once per
* method. The name of this class is <em>ExceptionTable</em> for historical reasons; The Java Virtual Machine
* Specification, Second Edition defines this attribute using the name <em>Exceptions</em> (which is inconsistent with
* the other classes).
*
* <pre>
* Exceptions_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 number_of_exceptions;
* u2 exception_index_table[number_of_exceptions];
* }
* </pre>
* @see Code
*/
public final class ExceptionTable extends Attribute {
private int[] exceptionIndexTable; // constant pool
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a
* physical copy.
*
* @param c Source to copy.
*/
public ExceptionTable(final ExceptionTable c) {
this(c.getNameIndex(), c.getLength(), c.getExceptionIndexTable(), c.getConstantPool());
}
/**
* Construct object from input stream.
*
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
ExceptionTable(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, (int[]) null, constantPool);
final int exceptionCount = input.readUnsignedShort();
exceptionIndexTable = new int[exceptionCount];
for (int i = 0; i < exceptionCount; i++) {
exceptionIndexTable[i] = input.readUnsignedShort();
}
}
/**
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param exceptionIndexTable Table of indices in constant pool
* @param constantPool Array of constants
*/
public ExceptionTable(final int nameIndex, final int length, final int[] exceptionIndexTable, final ConstantPool constantPool) {
super(Const.ATTR_EXCEPTIONS, nameIndex, length, constantPool);
this.exceptionIndexTable = exceptionIndexTable != null ? exceptionIndexTable : ArrayUtils.EMPTY_INT_ARRAY;
Args.requireU2(this.exceptionIndexTable.length, "exceptionIndexTable.length");
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitExceptionTable(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final ExceptionTable c = (ExceptionTable) clone();
if (exceptionIndexTable != null) {
c.exceptionIndexTable = exceptionIndexTable.clone();
}
c.setConstantPool(constantPool);
return c;
}
/**
* Dump exceptions attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(exceptionIndexTable.length);
for (final int index : exceptionIndexTable) {
file.writeShort(index);
}
}
/**
* @return Array of indices into constant pool of thrown exceptions.
*/
public int[] getExceptionIndexTable() {
return exceptionIndexTable;
}
/**
* @return class names of thrown exceptions
*/
public String[] getExceptionNames() {
final String[] names = new String[exceptionIndexTable.length];
Arrays.setAll(names, i -> Utility.pathToPackage(super.getConstantPool().getConstantString(exceptionIndexTable[i], Const.CONSTANT_Class)));
return names;
}
/**
* @return Length of exception table.
*/
public int getNumberOfExceptions() {
return exceptionIndexTable == null ? 0 : exceptionIndexTable.length;
}
/**
* @param exceptionIndexTable the list of exception indexes Also redefines number_of_exceptions according to table
* length.
*/
public void setExceptionIndexTable(final int[] exceptionIndexTable) {
this.exceptionIndexTable = exceptionIndexTable != null ? exceptionIndexTable : ArrayUtils.EMPTY_INT_ARRAY;
}
/**
* @return String representation, i.e., a list of thrown exceptions.
*/
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
String str;
buf.append("Exceptions: ");
for (int i = 0; i < exceptionIndexTable.length; i++) {
str = super.getConstantPool().getConstantString(exceptionIndexTable[i], Const.CONSTANT_Class);
buf.append(Utility.compactClassName(str, false));
if (i < exceptionIndexTable.length - 1) {
buf.append(", ");
}
}
return buf.toString();
}
}

View File

@@ -0,0 +1,260 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
/**
* Abstract super class for fields and methods.
*/
public abstract class FieldOrMethod extends AccessFlags implements Cloneable, Node {
/**
* @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
*/
@java.lang.Deprecated
protected int name_index; // Points to field name in constant pool
/**
* @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
*/
@java.lang.Deprecated
protected int signature_index; // Points to encoded signature
/**
* @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
*/
@java.lang.Deprecated
protected Attribute[] attributes; // Collection of attributes
/**
* @deprecated (since 6.0) will be removed (not needed)
*/
@java.lang.Deprecated
protected int attributes_count; // No. of attributes
// @since 6.0
private AnnotationEntry[] annotationEntries; // annotations defined on the field or method
/**
* @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
*/
@java.lang.Deprecated
protected ConstantPool constant_pool;
private String signatureAttributeString;
private boolean searchedForSignatureAttribute;
FieldOrMethod() {
}
/**
* Construct object from file stream.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
protected FieldOrMethod(final DataInput file, final ConstantPool constantPool) throws IOException {
this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), null, constantPool);
final int attributesCount = file.readUnsignedShort();
attributes = new Attribute[attributesCount];
for (int i = 0; i < attributesCount; i++) {
attributes[i] = Attribute.readAttribute(file, constantPool);
}
this.attributes_count = attributesCount; // init deprecated field
}
/**
* Construct object from file stream.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
* @deprecated (6.0) Use {@link #FieldOrMethod(DataInput, ConstantPool)} instead.
*/
@java.lang.Deprecated
protected FieldOrMethod(final DataInputStream file, final ConstantPool constantPool) throws IOException {
this((DataInput) file, constantPool);
}
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
* physical copy.
*
* @param c Source to copy.
*/
protected FieldOrMethod(final FieldOrMethod c) {
this(c.getAccessFlags(), c.getNameIndex(), c.getSignatureIndex(), c.getAttributes(), c.getConstantPool());
}
/**
* @param accessFlags Access rights of method
* @param nameIndex Points to field name in constant pool
* @param signatureIndex Points to encoded signature
* @param attributes Collection of attributes
* @param constantPool Array of constants
*/
protected FieldOrMethod(final int accessFlags, final int nameIndex, final int signatureIndex, final Attribute[] attributes,
final ConstantPool constantPool) {
super(accessFlags);
this.name_index = nameIndex;
this.signature_index = signatureIndex;
this.constant_pool = constantPool;
setAttributes(attributes);
}
/**
* @return deep copy of this field
*/
protected FieldOrMethod copy_(final ConstantPool constantPool) {
try {
final FieldOrMethod c = (FieldOrMethod) clone();
c.constant_pool = constantPool;
c.attributes = new Attribute[attributes.length];
c.attributes_count = attributes_count; // init deprecated field
Arrays.setAll(c.attributes, i -> attributes[i].copy(constantPool));
return c;
} catch (final CloneNotSupportedException e) {
throw new IllegalStateException(e);
}
}
/**
* Dump object to file stream on binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
public final void dump(final DataOutputStream file) throws IOException {
file.writeShort(super.getAccessFlags());
file.writeShort(name_index);
file.writeShort(signature_index);
file.writeShort(attributes_count);
if (attributes != null) {
for (final Attribute attribute : attributes) {
attribute.dump(file);
}
}
}
/**
* @return Annotations on the field or method
* @since 6.0
*/
public AnnotationEntry[] getAnnotationEntries() {
if (annotationEntries == null) {
annotationEntries = AnnotationEntry.createAnnotationEntries(getAttributes());
}
return annotationEntries;
}
/**
* @return Collection of object attributes.
*/
public final Attribute[] getAttributes() {
return attributes;
}
/**
* @return Constant pool used by this object.
*/
public final ConstantPool getConstantPool() {
return constant_pool;
}
/**
* Hunts for a signature attribute on the member and returns its contents. So where the 'regular' signature may be
* (Ljava/util/Vector;)V the signature attribute may in fact say 'Ljava/lang/Vector&lt;Ljava/lang/String&gt;;' Coded for
* performance - searches for the attribute only when requested - only searches for it once.
*
* @since 6.0
*/
public final String getGenericSignature() {
if (!searchedForSignatureAttribute) {
boolean found = false;
for (int i = 0; !found && i < attributes.length; i++) {
if (attributes[i] instanceof Signature) {
signatureAttributeString = ((Signature) attributes[i]).getSignature();
found = true;
}
}
searchedForSignatureAttribute = true;
}
return signatureAttributeString;
}
/**
* @return Name of object, i.e., method name or field name
*/
public final String getName() {
return constant_pool.getConstantUtf8(name_index).getBytes();
}
/**
* @return Index in constant pool of object's name.
*/
public final int getNameIndex() {
return name_index;
}
/**
* @return String representation of object's type signature (java style)
*/
public final String getSignature() {
return constant_pool.getConstantUtf8(signature_index).getBytes();
}
/**
* @return Index in constant pool of field signature.
*/
public final int getSignatureIndex() {
return signature_index;
}
/**
* @param attributes Collection of object attributes.
*/
public final void setAttributes(final Attribute[] attributes) {
this.attributes = attributes;
this.attributes_count = attributes != null ? attributes.length : 0; // init deprecated field
}
/**
* @param constantPool Constant pool to be used for this object.
*/
public final void setConstantPool(final ConstantPool constantPool) {
this.constant_pool = constantPool;
}
/**
* @param nameIndex Index in constant pool of object's name.
*/
public final void setNameIndex(final int nameIndex) {
this.name_index = nameIndex;
}
/**
* @param signatureIndex Index in constant pool of field signature.
*/
public final void setSignatureIndex(final int signatureIndex) {
this.signature_index = signatureIndex;
}
}

View File

@@ -0,0 +1,193 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class represents a inner class attribute, i.e., the class indices of the inner and outer classes, the name and
* the attributes of the inner class.
*
* @see InnerClasses
*/
public final class InnerClass implements Cloneable, Node {
private int innerClassIndex;
private int outerClassIndex;
private int innerNameIndex;
private int innerAccessFlags;
/**
* Construct object from file stream.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
InnerClass(final DataInput file) throws IOException {
this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort());
}
/**
* Initialize from another object.
*
* @param c Source to copy.
*/
public InnerClass(final InnerClass c) {
this(c.getInnerClassIndex(), c.getOuterClassIndex(), c.getInnerNameIndex(), c.getInnerAccessFlags());
}
/**
* @param innerClassIndex Class index in constant pool of inner class
* @param outerClassIndex Class index in constant pool of outer class
* @param innerNameIndex Name index in constant pool of inner class
* @param innerAccessFlags Access flags of inner class
*/
public InnerClass(final int innerClassIndex, final int outerClassIndex, final int innerNameIndex, final int innerAccessFlags) {
this.innerClassIndex = innerClassIndex;
this.outerClassIndex = outerClassIndex;
this.innerNameIndex = innerNameIndex;
this.innerAccessFlags = innerAccessFlags;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitInnerClass(this);
}
/**
* @return deep copy of this object
*/
public InnerClass copy() {
try {
return (InnerClass) clone();
} catch (final CloneNotSupportedException e) {
// TODO should this throw?
}
return null;
}
/**
* Dump inner class attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
public void dump(final DataOutputStream file) throws IOException {
file.writeShort(innerClassIndex);
file.writeShort(outerClassIndex);
file.writeShort(innerNameIndex);
file.writeShort(innerAccessFlags);
}
/**
* @return access flags of inner class.
*/
public int getInnerAccessFlags() {
return innerAccessFlags;
}
/**
* @return class index of inner class.
*/
public int getInnerClassIndex() {
return innerClassIndex;
}
/**
* @return name index of inner class.
*/
public int getInnerNameIndex() {
return innerNameIndex;
}
/**
* @return class index of outer class.
*/
public int getOuterClassIndex() {
return outerClassIndex;
}
/**
* @param innerAccessFlags access flags for this inner class
*/
public void setInnerAccessFlags(final int innerAccessFlags) {
this.innerAccessFlags = innerAccessFlags;
}
/**
* @param innerClassIndex index into the constant pool for this class
*/
public void setInnerClassIndex(final int innerClassIndex) {
this.innerClassIndex = innerClassIndex;
}
/**
* @param innerNameIndex index into the constant pool for this class's name
*/
public void setInnerNameIndex(final int innerNameIndex) { // TODO unused
this.innerNameIndex = innerNameIndex;
}
/**
* @param outerClassIndex index into the constant pool for the owning class
*/
public void setOuterClassIndex(final int outerClassIndex) { // TODO unused
this.outerClassIndex = outerClassIndex;
}
/**
* @return String representation.
*/
@Override
public String toString() {
return "InnerClass(" + innerClassIndex + ", " + outerClassIndex + ", " + innerNameIndex + ", " + innerAccessFlags + ")";
}
/**
* @return Resolved string representation
*/
public String toString(final ConstantPool constantPool) {
String outerClassName;
String innerName;
String innerClassName = constantPool.getConstantString(innerClassIndex, Const.CONSTANT_Class);
innerClassName = Utility.compactClassName(innerClassName, false);
if (outerClassIndex != 0) {
outerClassName = constantPool.getConstantString(outerClassIndex, Const.CONSTANT_Class);
outerClassName = " of class " + Utility.compactClassName(outerClassName, false);
} else {
outerClassName = "";
}
if (innerNameIndex != 0) {
innerName = constantPool.getConstantUtf8(innerNameIndex).getBytes();
} else {
innerName = "(anonymous)";
}
String access = Utility.accessToString(innerAccessFlags, true);
access = access.isEmpty() ? "" : access + " ";
return " " + access + innerName + "=class " + innerClassName + outerClassName;
}
}

View File

@@ -0,0 +1,156 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.stream.Stream;
/**
* This class is derived from <em>Attribute</em> and denotes that this class is an Inner class of another. to the source
* file of this class. It is instantiated from the <em>Attribute.readAttribute()</em> method.
*
* @see Attribute
*/
public final class InnerClasses extends Attribute implements Iterable<InnerClass> {
/**
* Empty array.
*/
private static final InnerClass[] EMPTY_INNER_CLASSE_ARRAY = {};
private InnerClass[] innerClasses;
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
* physical copy.
*
* @param c Source to copy.
*/
public InnerClasses(final InnerClasses c) {
this(c.getNameIndex(), c.getLength(), c.getInnerClasses(), c.getConstantPool());
}
/**
* Construct object from input stream.
*
* @param nameIndex Index in constant pool to CONSTANT_Utf8
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
InnerClasses(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, (InnerClass[]) null, constantPool);
final int classCount = input.readUnsignedShort();
innerClasses = new InnerClass[classCount];
for (int i = 0; i < classCount; i++) {
innerClasses[i] = new InnerClass(input);
}
}
/**
* @param nameIndex Index in constant pool to CONSTANT_Utf8
* @param length Content length in bytes
* @param innerClasses array of inner classes attributes
* @param constantPool Array of constants
*/
public InnerClasses(final int nameIndex, final int length, final InnerClass[] innerClasses, final ConstantPool constantPool) {
super(Const.ATTR_INNER_CLASSES, nameIndex, length, constantPool);
this.innerClasses = innerClasses != null ? innerClasses : EMPTY_INNER_CLASSE_ARRAY;
Args.requireU2(this.innerClasses.length, "innerClasses.length");
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitInnerClasses(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
// TODO this could be recoded to use a lower level constructor after creating a copy of the inner classes
final InnerClasses c = (InnerClasses) clone();
c.innerClasses = new InnerClass[innerClasses.length];
Arrays.setAll(c.innerClasses, i -> innerClasses[i].copy());
c.setConstantPool(constantPool);
return c;
}
/**
* Dump source file attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(innerClasses.length);
for (final InnerClass innerClass : innerClasses) {
innerClass.dump(file);
}
}
/**
* @return array of inner class "records"
*/
public InnerClass[] getInnerClasses() {
return innerClasses;
}
@Override
public Iterator<InnerClass> iterator() {
return Stream.of(innerClasses).iterator();
}
/**
* @param innerClasses the array of inner classes
*/
public void setInnerClasses(final InnerClass[] innerClasses) {
this.innerClasses = innerClasses != null ? innerClasses : EMPTY_INNER_CLASSE_ARRAY;
}
/**
* @return String representation.
*/
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
buf.append("InnerClasses(");
buf.append(innerClasses.length);
buf.append("):\n");
for (final InnerClass innerClass : innerClasses) {
buf.append(innerClass.toString(super.getConstantPool())).append("\n");
}
return buf.substring(0, buf.length() - 1); // remove the last newline
}
}

View File

@@ -0,0 +1,862 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.generic.ClassGen;
import haidnor.jvm.bcel.generic.Type;
import haidnor.jvm.bcel.util.BCELComparator;
import haidnor.jvm.bcel.util.ClassQueue;
import haidnor.jvm.bcel.util.Repository;
import haidnor.jvm.bcel.util.SyntheticRepository;
import org.apache.commons.lang3.ArrayUtils;
import java.io.*;
import java.util.*;
/**
* Represents a Java class, i.e., the data structures, constant pool, fields, methods and commands contained in a Java
* .class file. See <a href="https://docs.oracle.com/javase/specs/">JVM specification</a> for details. The intent of
* this class is to represent a parsed or otherwise existing class file. Those interested in programmatically generating
* classes should see the <a href="../generic/ClassGen.html">ClassGen</a> class.
*
* @see ClassGen
*/
public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> {
/**
* The standard class file extension.
*
* @since 6.7.0
*/
public static final String EXTENSION = ".class";
/**
* Empty array.
*
* @since 6.6.0
*/
public static final JavaClass[] EMPTY_ARRAY = {};
public static final byte HEAP = 1;
public static final byte FILE = 2;
public static final byte ZIP = 3;
private static final boolean debug = Boolean.getBoolean("JavaClass.debug"); // Debugging on/off
private static BCELComparator bcelComparator = new BCELComparator() {
@Override
public boolean equals(final Object o1, final Object o2) {
final JavaClass THIS = (JavaClass) o1;
final JavaClass THAT = (JavaClass) o2;
return Objects.equals(THIS.getClassName(), THAT.getClassName());
}
@Override
public int hashCode(final Object o) {
final JavaClass THIS = (JavaClass) o;
return THIS.getClassName().hashCode();
}
};
/*
* Print debug information depending on 'JavaClass.debug'
*/
static void Debug(final String str) {
if (debug) {
System.out.println(str);
}
}
/**
* @return Comparison strategy object
*/
public static BCELComparator getComparator() {
return bcelComparator;
}
private static String indent(final Object obj) {
final StringTokenizer tokenizer = new StringTokenizer(obj.toString(), "\n");
final StringBuilder buf = new StringBuilder();
while (tokenizer.hasMoreTokens()) {
buf.append("\t").append(tokenizer.nextToken()).append("\n");
}
return buf.toString();
}
/**
* @param comparator Comparison strategy object
*/
public static void setComparator(final BCELComparator comparator) {
bcelComparator = comparator;
}
private String fileName;
private final String packageName;
private String sourceFileName = "<Unknown>";
private int classNameIndex;
private int superclassNameIndex;
private String className;
private String superclassName;
private int major;
private int minor; // Compiler version
private ConstantPool constantPool; // Constant pool
private int[] interfaces; // implemented interfaces
private String[] interfaceNames;
private JavaField[] fields; // Fields, i.e., variables of class
private JavaMethod[] methods; // methods defined in the class
private Attribute[] attributes; // attributes defined in the class
private AnnotationEntry[] annotations; // annotations defined on the class
private byte source = HEAP; // Generated in memory
private boolean isAnonymous;
private boolean isNested;
private boolean computedNestedTypeStatus;
/**
* In cases where we go ahead and create something, use the default SyntheticRepository, because we don't know any
* better.
*/
private transient Repository repository = SyntheticRepository.getInstance();
/**
* Constructor gets all contents as arguments.
*
* @param classNameIndex Class name
* @param superclassNameIndex Superclass name
* @param fileName File name
* @param major Major compiler version
* @param minor Minor compiler version
* @param accessFlags Access rights defined by bit flags
* @param constantPool Array of constants
* @param interfaces Implemented interfaces
* @param fields Class fields
* @param methods Class methods
* @param attributes Class attributes
*/
public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags,
final ConstantPool constantPool, final int[] interfaces, final JavaField[] fields, final JavaMethod[] methods, final Attribute[] attributes) {
this(classNameIndex, superclassNameIndex, fileName, major, minor, accessFlags, constantPool, interfaces, fields, methods, attributes, HEAP);
}
/**
* Constructor gets all contents as arguments.
*
* @param classNameIndex Index into constant pool referencing a ConstantClass that represents this class.
* @param superclassNameIndex Index into constant pool referencing a ConstantClass that represents this class's
* superclass.
* @param fileName File name
* @param major Major compiler version
* @param minor Minor compiler version
* @param accessFlags Access rights defined by bit flags
* @param constantPool Array of constants
* @param interfaces Implemented interfaces
* @param fields Class fields
* @param methods Class methods
* @param attributes Class attributes
* @param source Read from file or generated in memory?
*/
public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags,
final ConstantPool constantPool, int[] interfaces, JavaField[] fields, JavaMethod[] methods, Attribute[] attributes, final byte source) {
super(accessFlags);
if (interfaces == null) {
interfaces = ArrayUtils.EMPTY_INT_ARRAY;
}
if (attributes == null) {
attributes = Attribute.EMPTY_ARRAY;
}
if (fields == null) {
fields = JavaField.EMPTY_FIELD_ARRAY;
}
if (methods == null) {
methods = JavaMethod.EMPTY_METHOD_ARRAY;
}
this.classNameIndex = classNameIndex;
this.superclassNameIndex = superclassNameIndex;
this.fileName = fileName;
this.major = major;
this.minor = minor;
this.constantPool = constantPool;
this.interfaces = interfaces;
this.fields = fields;
this.methods = methods;
this.attributes = attributes;
this.source = source;
// Get source file name if available
for (final Attribute attribute : attributes) {
if (attribute instanceof SourceFile) {
sourceFileName = ((SourceFile) attribute).getSourceFileName();
break;
}
}
/*
* According to the specification the following entries must be of type 'ConstantClass' but we check that anyway via the
* 'ConstPool.getConstant' method.
*/
className = constantPool.getConstantString(classNameIndex, Const.CONSTANT_Class);
className = Utility.compactClassName(className, false);
final int index = className.lastIndexOf('.');
if (index < 0) {
packageName = "";
} else {
packageName = className.substring(0, index);
}
if (superclassNameIndex > 0) {
// May be zero -> class is java.lang.Object
superclassName = constantPool.getConstantString(superclassNameIndex, Const.CONSTANT_Class);
superclassName = Utility.compactClassName(superclassName, false);
} else {
superclassName = "java.lang.Object";
}
interfaceNames = new String[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
final String str = constantPool.getConstantString(interfaces[i], Const.CONSTANT_Class);
interfaceNames[i] = Utility.compactClassName(str, false);
}
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitJavaClass(this);
}
/**
* Return the natural ordering of two JavaClasses. This ordering is based on the class name
*
* @since 6.0
*/
@Override
public int compareTo(final JavaClass obj) {
return getClassName().compareTo(obj.getClassName());
}
private void computeNestedTypeStatus() {
if (computedNestedTypeStatus) {
return;
}
for (final Attribute attribute : this.attributes) {
if (attribute instanceof InnerClasses) {
((InnerClasses) attribute).forEach(innerClass -> {
boolean innerClassAttributeRefersToMe = false;
String innerClassName = constantPool.getConstantString(innerClass.getInnerClassIndex(), Const.CONSTANT_Class);
innerClassName = Utility.compactClassName(innerClassName, false);
if (innerClassName.equals(getClassName())) {
innerClassAttributeRefersToMe = true;
}
if (innerClassAttributeRefersToMe) {
this.isNested = true;
if (innerClass.getInnerNameIndex() == 0) {
this.isAnonymous = true;
}
}
});
}
}
this.computedNestedTypeStatus = true;
}
/**
* @return deep copy of this class
*/
public JavaClass copy() {
try {
final JavaClass c = (JavaClass) clone();
c.constantPool = constantPool.copy();
c.interfaces = interfaces.clone();
c.interfaceNames = interfaceNames.clone();
c.fields = new JavaField[fields.length];
Arrays.setAll(c.fields, i -> fields[i].copy(c.constantPool));
c.methods = new JavaMethod[methods.length];
Arrays.setAll(c.methods, i -> methods[i].copy(c.constantPool));
c.attributes = new Attribute[attributes.length];
Arrays.setAll(c.attributes, i -> attributes[i].copy(c.constantPool));
return c;
} catch (final CloneNotSupportedException e) {
return null;
}
}
/**
* Dump Java class to output stream in binary format.
*
* @param file Output stream
* @throws IOException if an I/O error occurs.
*/
public void dump(final DataOutputStream file) throws IOException {
file.writeInt(Const.JVM_CLASSFILE_MAGIC);
file.writeShort(minor);
file.writeShort(major);
constantPool.dump(file);
file.writeShort(super.getAccessFlags());
file.writeShort(classNameIndex);
file.writeShort(superclassNameIndex);
file.writeShort(interfaces.length);
for (final int interface1 : interfaces) {
file.writeShort(interface1);
}
file.writeShort(fields.length);
for (final JavaField field : fields) {
field.dump(file);
}
file.writeShort(methods.length);
for (final JavaMethod method : methods) {
method.dump(file);
}
if (attributes != null) {
file.writeShort(attributes.length);
for (final Attribute attribute : attributes) {
attribute.dump(file);
}
} else {
file.writeShort(0);
}
file.flush();
}
/**
* Dump class to a file.
*
* @param file Output file
* @throws IOException if an I/O error occurs.
*/
public void dump(final File file) throws IOException {
final String parent = file.getParent();
if (parent != null) {
final File dir = new File(parent);
if (!dir.mkdirs() && !dir.isDirectory()) {
throw new IOException("Could not create the directory " + dir);
}
}
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
dump(dos);
}
}
/**
* Dump Java class to output stream in binary format.
*
* @param file Output stream
* @throws IOException if an I/O error occurs.
*/
public void dump(final OutputStream file) throws IOException {
dump(new DataOutputStream(file));
}
/**
* Dump class to a file named fileName.
*
* @param fileName Output file name
* @throws IOException if an I/O error occurs.
*/
public void dump(final String fileName) throws IOException {
dump(new File(fileName));
}
/**
* Return value as defined by given BCELComparator strategy. By default two JavaClass objects are said to be equal when
* their class names are equal.
*
* @see Object#equals(Object)
*/
@Override
public boolean equals(final Object obj) {
return bcelComparator.equals(this, obj);
}
/**
* Get all interfaces implemented by this JavaClass (transitively).
*
* @throws ClassNotFoundException if any of the class's superclasses or interfaces can't be found.
*/
public JavaClass[] getAllInterfaces() throws ClassNotFoundException {
final ClassQueue queue = new ClassQueue();
final Set<JavaClass> allInterfaces = new TreeSet<>();
queue.enqueue(this);
while (!queue.empty()) {
final JavaClass clazz = queue.dequeue();
final JavaClass souper = clazz.getSuperClass();
final JavaClass[] interfaces = clazz.getInterfaces();
if (clazz.isInterface()) {
allInterfaces.add(clazz);
} else if (souper != null) {
queue.enqueue(souper);
}
for (final JavaClass iface : interfaces) {
queue.enqueue(iface);
}
}
return allInterfaces.toArray(JavaClass.EMPTY_ARRAY);
}
/**
* @return Annotations on the class
* @since 6.0
*/
public AnnotationEntry[] getAnnotationEntries() {
if (annotations == null) {
annotations = AnnotationEntry.createAnnotationEntries(getAttributes());
}
return annotations;
}
/**
* @return Attributes of the class.
*/
public Attribute[] getAttributes() {
return attributes;
}
/**
* @return class in binary format
*/
public byte[] getBytes() {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (DataOutputStream dos = new DataOutputStream(baos)) {
dump(dos);
} catch (final IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
/**
* @return Class name.
*/
public String getClassName() {
return className;
}
/**
* @return Class name index.
*/
public int getClassNameIndex() {
return classNameIndex;
}
/**
* @return Constant pool.
*/
public ConstantPool getConstantPool() {
return constantPool;
}
/**
* @return Fields, i.e., variables of the class. Like the JVM spec mandates for the classfile format, these fields are
* those specific to this class, and not those of the superclass or superinterfaces.
*/
public JavaField[] getFields() {
return fields;
}
/**
* @return File name of class, aka SourceFile attribute value
*/
public String getFileName() {
return fileName;
}
/**
* @return Indices in constant pool of implemented interfaces.
*/
public int[] getInterfaceIndices() {
return interfaces;
}
/**
* @return Names of implemented interfaces.
*/
public String[] getInterfaceNames() {
return interfaceNames;
}
/**
* Get interfaces directly implemented by this JavaClass.
*
* @throws ClassNotFoundException if any of the class's interfaces can't be found.
*/
public JavaClass[] getInterfaces() throws ClassNotFoundException {
final String[] interfaces = getInterfaceNames();
final JavaClass[] classes = new JavaClass[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
classes[i] = repository.loadClass(interfaces[i]);
}
return classes;
}
/**
* @return Major number of class file version.
*/
public int getMajor() {
return major;
}
/**
* @return A {@link JavaMethod} corresponding to java.lang.reflect.Method if any
*/
public JavaMethod getMethod(final java.lang.reflect.Method m) {
for (final JavaMethod method : methods) {
if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) {
return method;
}
}
return null;
}
/**
* @return Methods of the class.
*/
public JavaMethod[] getMethods() {
return methods;
}
/**
* @return Minor number of class file version.
*/
public int getMinor() {
return minor;
}
/**
* @return Package name.
*/
public String getPackageName() {
return packageName;
}
/**
* Gets the ClassRepository which holds its definition. By default this is the same as
* SyntheticRepository.getInstance();
*/
public Repository getRepository() {
return repository;
}
/**
* @return returns either HEAP (generated), FILE, or ZIP
*/
public final byte getSource() {
return source;
}
/**
* @return file name where this class was read from
*/
public String getSourceFileName() {
return sourceFileName;
}
/**
* Gets the source file path including the package path.
*
* @return path to original source file of parsed class, relative to original source directory.
* @since 6.7.0
*/
public String getSourceFilePath() {
final StringBuilder outFileName = new StringBuilder();
if (!packageName.isEmpty()) {
outFileName.append(Utility.packageToPath(packageName));
outFileName.append('/');
}
outFileName.append(sourceFileName);
return outFileName.toString();
}
/**
* @return the superclass for this JavaClass object, or null if this is java.lang.Object
* @throws ClassNotFoundException if the superclass can't be found
*/
public JavaClass getSuperClass() throws ClassNotFoundException {
if ("java.lang.Object".equals(getClassName())) {
return null;
}
return repository.loadClass(getSuperclassName());
}
/**
* @return list of super classes of this class in ascending order, i.e., java.lang.Object is always the last element
* @throws ClassNotFoundException if any of the superclasses can't be found
*/
public JavaClass[] getSuperClasses() throws ClassNotFoundException {
JavaClass clazz = this;
final List<JavaClass> allSuperClasses = new ArrayList<>();
for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) {
allSuperClasses.add(clazz);
}
return allSuperClasses.toArray(JavaClass.EMPTY_ARRAY);
}
/**
* returns the super class name of this class. In the case that this class is java.lang.Object, it will return itself
* (java.lang.Object). This is probably incorrect but isn't fixed at this time to not break existing clients.
*
* @return Superclass name.
*/
public String getSuperclassName() {
return superclassName;
}
/**
* @return Class name index.
*/
public int getSuperclassNameIndex() {
return superclassNameIndex;
}
/**
* Return value as defined by given BCELComparator strategy. By default return the hashcode of the class name.
*
* @see Object#hashCode()
*/
@Override
public int hashCode() {
return bcelComparator.hashCode(this);
}
/**
* @return true, if this class is an implementation of interface inter
* @throws ClassNotFoundException if superclasses or superinterfaces of this class can't be found
*/
public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException {
if (!inter.isInterface()) {
throw new IllegalArgumentException(inter.getClassName() + " is no interface");
}
if (this.equals(inter)) {
return true;
}
final JavaClass[] superInterfaces = getAllInterfaces();
for (final JavaClass superInterface : superInterfaces) {
if (superInterface.equals(inter)) {
return true;
}
}
return false;
}
/**
* Equivalent to runtime "instanceof" operator.
*
* @return true if this JavaClass is derived from the super class
* @throws ClassNotFoundException if superclasses or superinterfaces of this object can't be found
*/
public final boolean instanceOf(final JavaClass superclass) throws ClassNotFoundException {
if (this.equals(superclass)) {
return true;
}
for (final JavaClass clazz : getSuperClasses()) {
if (clazz.equals(superclass)) {
return true;
}
}
if (superclass.isInterface()) {
return implementationOf(superclass);
}
return false;
}
/**
* @since 6.0
*/
public final boolean isAnonymous() {
computeNestedTypeStatus();
return this.isAnonymous;
}
public final boolean isClass() {
return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0;
}
/**
* @since 6.0
*/
public final boolean isNested() {
computeNestedTypeStatus();
return this.isNested;
}
public final boolean isSuper() {
return (super.getAccessFlags() & Const.ACC_SUPER) != 0;
}
/**
* @param attributes .
*/
public void setAttributes(final Attribute[] attributes) {
this.attributes = attributes;
}
/**
* @param className .
*/
public void setClassName(final String className) {
this.className = className;
}
/**
* @param classNameIndex .
*/
public void setClassNameIndex(final int classNameIndex) {
this.classNameIndex = classNameIndex;
}
/**
* @param constantPool .
*/
public void setConstantPool(final ConstantPool constantPool) {
this.constantPool = constantPool;
}
/**
* @param fields .
*/
public void setFields(final JavaField[] fields) {
this.fields = fields;
}
/**
* Set File name of class, aka SourceFile attribute value
*/
public void setFileName(final String fileName) {
this.fileName = fileName;
}
/**
* @param interfaceNames .
*/
public void setInterfaceNames(final String[] interfaceNames) {
this.interfaceNames = interfaceNames;
}
/**
* @param interfaces .
*/
public void setInterfaces(final int[] interfaces) {
this.interfaces = interfaces;
}
/**
* @param major .
*/
public void setMajor(final int major) {
this.major = major;
}
/**
* @param methods .
*/
public void setMethods(final JavaMethod[] methods) {
this.methods = methods;
}
/**
* @param minor .
*/
public void setMinor(final int minor) {
this.minor = minor;
}
/**
* Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done.
*/
public void setRepository(final Repository repository) { // TODO make protected?
this.repository = repository;
}
/**
* Set absolute path to file this class was read from.
*/
public void setSourceFileName(final String sourceFileName) {
this.sourceFileName = sourceFileName;
}
/**
* @param superclassName .
*/
public void setSuperclassName(final String superclassName) {
this.superclassName = superclassName;
}
/**
* @param superclassNameIndex .
*/
public void setSuperclassNameIndex(final int superclassNameIndex) {
this.superclassNameIndex = superclassNameIndex;
}
/**
* @return String representing class contents.
*/
@Override
public String toString() {
String access = Utility.accessToString(super.getAccessFlags(), true);
access = access.isEmpty() ? "" : access + " ";
final StringBuilder buf = new StringBuilder(128);
buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(className).append(" extends ")
.append(Utility.compactClassName(superclassName, false)).append('\n');
final int size = interfaces.length;
if (size > 0) {
buf.append("implements\t\t");
for (int i = 0; i < size; i++) {
buf.append(interfaceNames[i]);
if (i < size - 1) {
buf.append(", ");
}
}
buf.append('\n');
}
buf.append("file name\t\t").append(fileName).append('\n');
buf.append("compiled from\t\t").append(sourceFileName).append('\n');
buf.append("compiler version\t").append(major).append(".").append(minor).append('\n');
buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n');
buf.append("constant pool\t\t").append(constantPool.getLength()).append(" entries\n");
buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n");
if (attributes.length > 0) {
buf.append("\nAttribute(s):\n");
for (final Attribute attribute : attributes) {
buf.append(indent(attribute));
}
}
final AnnotationEntry[] annotations = getAnnotationEntries();
if (annotations != null && annotations.length > 0) {
buf.append("\nAnnotation(s):\n");
for (final AnnotationEntry annotation : annotations) {
buf.append(indent(annotation));
}
}
if (fields.length > 0) {
buf.append("\n").append(fields.length).append(" fields:\n");
for (final JavaField field : fields) {
buf.append("\t").append(field).append('\n');
}
}
if (methods.length > 0) {
buf.append("\n").append(methods.length).append(" methods:\n");
for (final JavaMethod method : methods) {
buf.append("\t").append(method).append('\n');
}
}
return buf.toString();
}
}

View File

@@ -0,0 +1,193 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.generic.Type;
import haidnor.jvm.bcel.util.BCELComparator;
import java.io.DataInput;
import java.io.IOException;
import java.util.Objects;
/**
* This class represents the field info structure, i.e., the representation for a variable in the class. See JVM
* specification for details.
*/
public final class JavaField extends FieldOrMethod {
/**
* Empty array constant.
*
* @since 6.6.0
*/
public static final JavaField[] EMPTY_ARRAY = {};
private static BCELComparator bcelComparator = new BCELComparator() {
@Override
public boolean equals(final Object o1, final Object o2) {
final JavaField THIS = (JavaField) o1;
final JavaField THAT = (JavaField) o2;
return Objects.equals(THIS.getName(), THAT.getName()) && Objects.equals(THIS.getSignature(), THAT.getSignature());
}
@Override
public int hashCode(final Object o) {
final JavaField THIS = (JavaField) o;
return THIS.getSignature().hashCode() ^ THIS.getName().hashCode();
}
};
/**
* Empty array.
*/
static final JavaField[] EMPTY_FIELD_ARRAY = {};
/**
* @return Comparison strategy object
*/
public static BCELComparator getComparator() {
return bcelComparator;
}
/**
* @param comparator Comparison strategy object
*/
public static void setComparator(final BCELComparator comparator) {
bcelComparator = comparator;
}
/**
* Construct object from file stream.
*
* @param file Input stream
*/
JavaField(final DataInput file, final ConstantPool constantPool) throws IOException, ClassFormatException {
super(file, constantPool);
}
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
* physical copy.
*
* @param c Source to copy.
*/
public JavaField(final JavaField c) {
super(c);
}
/**
* @param accessFlags Access rights of field
* @param nameIndex Points to field name in constant pool
* @param signatureIndex Points to encoded signature
* @param attributes Collection of attributes
* @param constantPool Array of constants
*/
public JavaField(final int accessFlags, final int nameIndex, final int signatureIndex, final Attribute[] attributes, final ConstantPool constantPool) {
super(accessFlags, nameIndex, signatureIndex, attributes, constantPool);
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitField(this);
}
/**
* @return deep copy of this field
*/
public JavaField copy(final ConstantPool constantPool) {
return (JavaField) copy_(constantPool);
}
/**
* Return value as defined by given BCELComparator strategy. By default two Field objects are said to be equal when
* their names and signatures are equal.
*
* @see Object#equals(Object)
*/
@Override
public boolean equals(final Object obj) {
return bcelComparator.equals(this, obj);
}
/**
* @return constant value associated with this field (may be null)
*/
public ConstantValue getConstantValue() {
for (final Attribute attribute : super.getAttributes()) {
if (attribute.getTag() == Const.ATTR_CONSTANT_VALUE) {
return (ConstantValue) attribute;
}
}
return null;
}
/**
* @return type of field
*/
public Type getType() {
return Type.getReturnType(getSignature());
}
/**
* Return value as defined by given BCELComparator strategy. By default return the hashcode of the field's name XOR
* signature.
*
* @see Object#hashCode()
*/
@Override
public int hashCode() {
return bcelComparator.hashCode(this);
}
/**
* Return string representation close to declaration format, 'public static final short MAX = 100', e.g..
*
* @return String representation of field, including the signature.
*/
@Override
public String toString() {
String name;
String signature;
String access; // Short cuts to constant pool
// Get names from constant pool
access = Utility.accessToString(super.getAccessFlags());
access = access.isEmpty() ? "" : access + " ";
signature = Utility.signatureToString(getSignature());
name = getName();
final StringBuilder buf = new StringBuilder(64); // CHECKSTYLE IGNORE MagicNumber
buf.append(access).append(signature).append(" ").append(name);
final ConstantValue cv = getConstantValue();
if (cv != null) {
buf.append(" = ").append(cv);
}
for (final Attribute attribute : super.getAttributes()) {
if (!(attribute instanceof ConstantValue)) {
buf.append(" [").append(attribute).append("]");
}
}
return buf.toString();
}
}

View File

@@ -0,0 +1,257 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.generic.Type;
import haidnor.jvm.bcel.util.BCELComparator;
import java.io.DataInput;
import java.io.IOException;
import java.util.Objects;
/**
* This class represents the method info structure, i.e., the representation for a method in the class. See JVM
* specification for details. A method has access flags, a name, a signature and a number of attributes.
*/
public final class JavaMethod extends FieldOrMethod {
/**
* Empty array constant.
*
* @since 6.6.0
*/
public static final JavaMethod[] EMPTY_ARRAY = {};
private static BCELComparator bcelComparator = new BCELComparator() {
@Override
public boolean equals(final Object o1, final Object o2) {
final JavaMethod THIS = (JavaMethod) o1;
final JavaMethod THAT = (JavaMethod) o2;
return Objects.equals(THIS.getName(), THAT.getName()) && Objects.equals(THIS.getSignature(), THAT.getSignature());
}
@Override
public int hashCode(final Object o) {
final JavaMethod THIS = (JavaMethod) o;
return THIS.getSignature().hashCode() ^ THIS.getName().hashCode();
}
};
/**
* Empty array.
*/
static final JavaMethod[] EMPTY_METHOD_ARRAY = {};
/**
* @return Comparison strategy object
*/
public static BCELComparator getComparator() {
return bcelComparator;
}
/**
* @param comparator Comparison strategy object
*/
public static void setComparator(final BCELComparator comparator) {
bcelComparator = comparator;
}
// annotations defined on the parameters of a method
private ParameterAnnotationEntry[] parameterAnnotationEntries;
/**
* Empty constructor, all attributes have to be defined via 'setXXX' methods. Use at your own risk.
*/
public JavaMethod() {
}
/**
* Construct object from file stream.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
* @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
*/
JavaMethod(final DataInput file, final ConstantPool constantPool) throws IOException, ClassFormatException {
super(file, constantPool);
}
/**
* @param accessFlags Access rights of method
* @param nameIndex Points to field name in constant pool
* @param signatureIndex Points to encoded signature
* @param attributes Collection of attributes
* @param constantPool Array of constants
*/
public JavaMethod(final int accessFlags, final int nameIndex, final int signatureIndex, final Attribute[] attributes, final ConstantPool constantPool) {
super(accessFlags, nameIndex, signatureIndex, attributes, constantPool);
}
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
* physical copy.
*
* @param c Source to copy.
*/
public JavaMethod(final JavaMethod c) {
super(c);
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitMethod(this);
}
/**
* @return deep copy of this method
*/
public JavaMethod copy(final ConstantPool constantPool) {
return (JavaMethod) copy_(constantPool);
}
/**
* Return value as defined by given BCELComparator strategy. By default two method objects are said to be equal when
* their names and signatures are equal.
*
* @see Object#equals(Object)
*/
@Override
public boolean equals(final Object obj) {
return bcelComparator.equals(this, obj);
}
/**
* @return array of method argument types
*/
public Type[] getArgumentTypes() {
return Type.getArgumentTypes(getSignature());
}
/**
* @return Code attribute of method, if any
*/
public Code getCode() {
for (final Attribute attribute : super.getAttributes()) {
if (attribute instanceof Code) {
return (Code) attribute;
}
}
return null;
}
/**
* @return ExceptionTable attribute of method, if any, i.e., list all exceptions the method may throw not exception
* handlers!
*/
public ExceptionTable getExceptionTable() {
for (final Attribute attribute : super.getAttributes()) {
if (attribute instanceof ExceptionTable) {
return (ExceptionTable) attribute;
}
}
return null;
}
/**
* @return LineNumberTable of code attribute if any, i.e. the call is forwarded to the Code atribute.
*/
public LineNumberTable getLineNumberTable() {
final Code code = getCode();
if (code == null) {
return null;
}
return code.getLineNumberTable();
}
/**
* @return LocalVariableTable of code attribute if any, i.e. the call is forwarded to the Code atribute.
*/
public LocalVariableTable getLocalVariableTable() {
final Code code = getCode();
if (code == null) {
return null;
}
return code.getLocalVariableTable();
}
/**
* @return Annotations on the parameters of a method
* @since 6.0
*/
public ParameterAnnotationEntry[] getParameterAnnotationEntries() {
if (parameterAnnotationEntries == null) {
parameterAnnotationEntries = ParameterAnnotationEntry.createParameterAnnotationEntries(getAttributes());
}
return parameterAnnotationEntries;
}
/**
* @return return type of method
*/
public Type getReturnType() {
return Type.getReturnType(getSignature());
}
/**
* Return value as defined by given BCELComparator strategy. By default return the hashcode of the method's name XOR
* signature.
*
* @see Object#hashCode()
*/
@Override
public int hashCode() {
return bcelComparator.hashCode(this);
}
/**
* Return string representation close to declaration format, 'public static void main(String[] args) throws
* IOException', e.g.
*
* @return String representation of the method.
*/
@Override
public String toString() {
final String access = Utility.accessToString(super.getAccessFlags());
// Get name and signature from constant pool
ConstantUtf8 c = super.getConstantPool().getConstantUtf8(super.getSignatureIndex());
String signature = c.getBytes();
c = super.getConstantPool().getConstantUtf8(super.getNameIndex());
final String name = c.getBytes();
signature = Utility.methodSignatureToString(signature, name, access, true, getLocalVariableTable());
final StringBuilder buf = new StringBuilder(signature);
for (final Attribute attribute : super.getAttributes()) {
if (!(attribute instanceof Code || attribute instanceof ExceptionTable)) {
buf.append(" [").append(attribute).append("]");
}
}
final ExceptionTable e = getExceptionTable();
if (e != null) {
final String str = e.toString();
if (!str.isEmpty()) {
buf.append("\n\t\tthrows ").append(str);
}
}
return buf.toString();
}
}

View File

@@ -0,0 +1,138 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class represents a (PC offset, line number) pair, i.e., a line number in the source that corresponds to a
* relative address in the byte code. This is used for debugging purposes.
*
* @see LineNumberTable
*/
public final class LineNumber implements Cloneable, Node {
static final LineNumber[] EMPTY_ARRAY = {};
/** Program Counter (PC) corresponds to line */
private int startPc;
/** number in source file */
private int lineNumber;
/**
* Construct object from file stream.
*
* @param file Input stream
* @throws IOException if an I/O Exception occurs in readUnsignedShort
*/
LineNumber(final DataInput file) throws IOException {
this(file.readUnsignedShort(), file.readUnsignedShort());
}
/**
* @param startPc Program Counter (PC) corresponds to
* @param lineNumber line number in source file
*/
public LineNumber(final int startPc, final int lineNumber) {
this.startPc = Args.requireU2(startPc, "startPc");
this.lineNumber = Args.requireU2(lineNumber, "lineNumber");
}
/**
* Initialize from another object.
*
* @param c the object to copy
*/
public LineNumber(final LineNumber c) {
this(c.getStartPC(), c.getLineNumber());
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitLineNumber(this);
}
/**
* @return deep copy of this object
*/
public LineNumber copy() {
try {
return (LineNumber) clone();
} catch (final CloneNotSupportedException e) {
// TODO should this throw?
}
return null;
}
/**
* Dump line number/pc pair to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O Exception occurs in writeShort
*/
public void dump(final DataOutputStream file) throws IOException {
file.writeShort(startPc);
file.writeShort(lineNumber);
}
/**
* @return Corresponding source line
*/
public int getLineNumber() {
return lineNumber & 0xffff;
}
/**
* @return PC in code
*/
public int getStartPC() {
return startPc & 0xffff;
}
/**
* @param lineNumber the source line number
*/
public void setLineNumber(final int lineNumber) {
this.lineNumber = (short) lineNumber;
}
/**
* @param startPc the pc for this line number
*/
public void setStartPC(final int startPc) {
this.startPc = (short) startPc;
}
/**
* @return String representation
*/
@Override
public String toString() {
return "LineNumber(" + getStartPC() + ", " + getLineNumber() + ")";
}
}

View File

@@ -0,0 +1,213 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.stream.Stream;
/**
* This class represents a table of line numbers for debugging purposes. This attribute is used by the <em>Code</em>
* attribute. It contains pairs of PCs and line numbers.
*
* @see Code
* @see LineNumber
*/
public final class LineNumberTable extends Attribute implements Iterable<LineNumber> {
private static final int MAX_LINE_LENGTH = 72;
private LineNumber[] lineNumberTable; // Table of line/numbers pairs
/**
* Construct object from input stream.
*
* @param nameIndex Index of name
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O Exception occurs in readUnsignedShort
*/
LineNumberTable(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, (LineNumber[]) null, constantPool);
final int lineNumberTableLength = input.readUnsignedShort();
lineNumberTable = new LineNumber[lineNumberTableLength];
for (int i = 0; i < lineNumberTableLength; i++) {
lineNumberTable[i] = new LineNumber(input);
}
}
/*
* @param nameIndex Index of name
*
* @param length Content length in bytes
*
* @param lineNumberTable Table of line/numbers pairs
*
* @param constantPool Array of constants
*/
public LineNumberTable(final int nameIndex, final int length, final LineNumber[] lineNumberTable, final ConstantPool constantPool) {
super(Const.ATTR_LINE_NUMBER_TABLE, nameIndex, length, constantPool);
this.lineNumberTable = lineNumberTable != null ? lineNumberTable : LineNumber.EMPTY_ARRAY;
Args.requireU2(this.lineNumberTable.length, "lineNumberTable.length");
}
/*
* Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a
* physical copy.
*/
public LineNumberTable(final LineNumberTable c) {
this(c.getNameIndex(), c.getLength(), c.getLineNumberTable(), c.getConstantPool());
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitLineNumberTable(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
// TODO could use the lower level constructor and thereby allow
// lineNumberTable to be made final
final LineNumberTable c = (LineNumberTable) clone();
c.lineNumberTable = new LineNumber[lineNumberTable.length];
Arrays.setAll(c.lineNumberTable, i -> lineNumberTable[i].copy());
c.setConstantPool(constantPool);
return c;
}
/**
* Dump line number table attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O Exception occurs in writeShort
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(lineNumberTable.length);
for (final LineNumber lineNumber : lineNumberTable) {
lineNumber.dump(file);
}
}
/**
* @return Array of (pc offset, line number) pairs.
*/
public LineNumber[] getLineNumberTable() {
return lineNumberTable;
}
/**
* Map byte code positions to source code lines.
*
* @param pos byte code offset
* @return corresponding line in source code
*/
public int getSourceLine(final int pos) {
int l = 0;
int r = lineNumberTable.length - 1;
if (r < 0) {
return -1;
}
int minIndex = -1;
int min = -1;
/*
* Do a binary search since the array is ordered.
*/
do {
final int i = l + r >>> 1;
final int j = lineNumberTable[i].getStartPC();
if (j == pos) {
return lineNumberTable[i].getLineNumber();
}
if (pos < j) {
r = i - 1;
} else {
l = i + 1;
}
/*
* If exact match can't be found (which is the most common case) return the line number that corresponds to the greatest
* index less than pos.
*/
if (j < pos && j > min) {
min = j;
minIndex = i;
}
} while (l <= r);
/*
* It's possible that we did not find any valid entry for the bytecode offset we were looking for.
*/
if (minIndex < 0) {
return -1;
}
return lineNumberTable[minIndex].getLineNumber();
}
public int getTableLength() {
return lineNumberTable == null ? 0 : lineNumberTable.length;
}
@Override
public Iterator<LineNumber> iterator() {
return Stream.of(lineNumberTable).iterator();
}
/**
* @param lineNumberTable the line number entries for this table
*/
public void setLineNumberTable(final LineNumber[] lineNumberTable) {
this.lineNumberTable = lineNumberTable;
}
/**
* @return String representation.
*/
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
final StringBuilder line = new StringBuilder();
final String newLine = System.getProperty("line.separator", "\n");
for (int i = 0; i < lineNumberTable.length; i++) {
line.append(lineNumberTable[i].toString());
if (i < lineNumberTable.length - 1) {
line.append(", ");
}
if (line.length() > MAX_LINE_LENGTH && i < lineNumberTable.length - 1) {
line.append(newLine);
buf.append(line);
line.setLength(0);
}
}
buf.append(line);
return buf.toString();
}
}

View File

@@ -0,0 +1,282 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class represents a local variable within a method. It contains its scope, name, signature and index on the
* method's frame. It is used both to represent an element of the LocalVariableTable as well as an element of the
* LocalVariableTypeTable. The nomenclature used here may be a bit confusing; while the two items have the same layout
* in a class file, a LocalVariableTable attribute contains a descriptor_index, not a signatureIndex. The
* LocalVariableTypeTable attribute does have a signatureIndex.
*
* @see Utility for more details on the difference.
* @see LocalVariableTable
* @see LocalVariableTypeTable
*/
public final class LocalVariable implements Cloneable, Node {
static final LocalVariable[] EMPTY_ARRAY = {};
/**
* Range in which the variable is valid.
*/
private int startPc;
private int length;
/**
* Index in constant pool of variable name.
*/
private int nameIndex;
/**
* Technically, a decscriptor_index for a local variable table entry and a signatureIndex for a local variable type table entry. Index of variable signature
*/
private int signatureIndex;
/*
* Variable is index'th local variable on this method's frame.
*/
private int index;
private ConstantPool constantPool;
/**
* Never changes; used to match up with LocalVariableTypeTable entries.
*/
private final int origIndex;
/**
* Constructs object from file stream.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
LocalVariable(final DataInput file, final ConstantPool constantPool) throws IOException {
this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), constantPool);
}
/**
* @param startPc Range in which the variable
* @param length ... is valid
* @param nameIndex Index in constant pool of variable name
* @param signatureIndex Index of variable's signature
* @param index Variable is 'index'th local variable on the method's frame
* @param constantPool Array of constants
*/
public LocalVariable(final int startPc, final int length, final int nameIndex, final int signatureIndex, final int index, final ConstantPool constantPool) {
this(startPc, length, nameIndex, signatureIndex, index, constantPool, index);
}
/**
* @param startPc Range in which the variable
* @param length ... is valid
* @param nameIndex Index in constant pool of variable name
* @param signatureIndex Index of variable's signature
* @param index Variable is 'index'th local variable on the method's frame
* @param constantPool Array of constants
* @param origIndex Variable is 'index'th local variable on the method's frame prior to any changes
*/
public LocalVariable(final int startPc, final int length, final int nameIndex, final int signatureIndex, final int index, final ConstantPool constantPool,
final int origIndex) {
this.startPc = Args.requireU2(startPc, "startPc");
this.length = Args.requireU2(length, "length");
this.nameIndex = Args.requireU2(nameIndex, "nameIndex");
this.signatureIndex = Args.requireU2(signatureIndex, "signatureIndex");
this.index = Args.requireU2(index, "index");
this.origIndex = Args.requireU2(origIndex, "origIndex");
this.constantPool = constantPool;
}
/**
* Initializes from another LocalVariable. Note that both objects use the same references (shallow copy). Use copy() for
* a physical copy.
*
* @param localVariable Another LocalVariable.
*/
public LocalVariable(final LocalVariable localVariable) {
this(localVariable.getStartPC(), localVariable.getLength(), localVariable.getNameIndex(), localVariable.getSignatureIndex(), localVariable.getIndex(),
localVariable.getConstantPool());
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitLocalVariable(this);
}
/**
* @return deep copy of this object
*/
public LocalVariable copy() {
try {
return (LocalVariable) clone();
} catch (final CloneNotSupportedException e) {
// TODO should this throw?
}
return null;
}
/**
* Dumps local variable to file stream in binary format.
*
* @param dataOutputStream Output file stream
* @throws IOException if an I/O error occurs.
* @see java.io.FilterOutputStream#out
*/
public void dump(final DataOutputStream dataOutputStream) throws IOException {
dataOutputStream.writeShort(startPc);
dataOutputStream.writeShort(length);
dataOutputStream.writeShort(nameIndex);
dataOutputStream.writeShort(signatureIndex);
dataOutputStream.writeShort(index);
}
/**
* @return Constant pool used by this object.
*/
public ConstantPool getConstantPool() {
return constantPool;
}
/**
* @return index of register where variable is stored
*/
public int getIndex() {
return index;
}
/**
* @return Variable is valid within getStartPC() .. getStartPC()+getLength()
*/
public int getLength() {
return length;
}
/**
* @return Variable name.
*/
public String getName() {
return constantPool.getConstantUtf8(nameIndex).getBytes();
}
/**
* @return Index in constant pool of variable name.
*/
public int getNameIndex() {
return nameIndex;
}
/**
* @return index of register where variable was originally stored
*/
public int getOrigIndex() {
return origIndex;
}
/**
* @return Signature.
*/
public String getSignature() {
return constantPool.getConstantUtf8(signatureIndex).getBytes();
}
/**
* @return Index in constant pool of variable signature.
*/
public int getSignatureIndex() {
return signatureIndex;
}
/**
* @return Start of range where the variable is valid
*/
public int getStartPC() {
return startPc;
}
/**
* @param constantPool Constant pool to be used for this object.
*/
public void setConstantPool(final ConstantPool constantPool) {
this.constantPool = constantPool;
}
/**
* @param index the index in the local variable table of this variable
*/
public void setIndex(final int index) { // TODO unused
this.index = index;
}
/**
* @param length the length of this local variable
*/
public void setLength(final int length) {
this.length = length;
}
/**
* @param nameIndex the index into the constant pool for the name of this variable
*/
public void setNameIndex(final int nameIndex) { // TODO unused
this.nameIndex = nameIndex;
}
/**
* @param signatureIndex the index into the constant pool for the signature of this variable
*/
public void setSignatureIndex(final int signatureIndex) { // TODO unused
this.signatureIndex = signatureIndex;
}
/**
* @param startPc Specify range where the local variable is valid.
*/
public void setStartPC(final int startPc) { // TODO unused
this.startPc = startPc;
}
/**
* @return string representation.
*/
@Override
public String toString() {
return toStringShared(false);
}
/*
* Helper method shared with LocalVariableTypeTable
*/
String toStringShared(final boolean typeTable) {
final String name = getName();
final String signature = Utility.signatureToString(getSignature(), false);
final String label = "LocalVariable" + (typeTable ? "Types" : "");
return label + "(startPc = " + startPc + ", length = " + length + ", index = " + index + ":" + signature + " " + name + ")";
}
}

View File

@@ -0,0 +1,191 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.stream.Stream;
/**
* This class represents colection of local variables in a method. This attribute is contained in the <em>Code</em>
* attribute.
*
* @see Code
* @see LocalVariable
*/
public class LocalVariableTable extends Attribute implements Iterable<LocalVariable> {
private LocalVariable[] localVariableTable; // variables
/**
* Construct object from input stream.
*
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
LocalVariableTable(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, (LocalVariable[]) null, constantPool);
final int localVariableTableLength = input.readUnsignedShort();
localVariableTable = new LocalVariable[localVariableTableLength];
for (int i = 0; i < localVariableTableLength; i++) {
localVariableTable[i] = new LocalVariable(input, constantPool);
}
}
/**
* @param nameIndex Index in constant pool to 'LocalVariableTable'
* @param length Content length in bytes
* @param localVariableTable Table of local variables
* @param constantPool Array of constants
*/
public LocalVariableTable(final int nameIndex, final int length, final LocalVariable[] localVariableTable, final ConstantPool constantPool) {
super(Const.ATTR_LOCAL_VARIABLE_TABLE, nameIndex, length, constantPool);
this.localVariableTable = localVariableTable != null ? localVariableTable : LocalVariable.EMPTY_ARRAY;
Args.requireU2(this.localVariableTable.length, "localVariableTable.length");
}
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a
* physical copy.
*
* @param c Source to copy.
*/
public LocalVariableTable(final LocalVariableTable c) {
this(c.getNameIndex(), c.getLength(), c.getLocalVariableTable(), c.getConstantPool());
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitLocalVariableTable(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final LocalVariableTable c = (LocalVariableTable) clone();
c.localVariableTable = new LocalVariable[localVariableTable.length];
Arrays.setAll(c.localVariableTable, i -> localVariableTable[i].copy());
c.setConstantPool(constantPool);
return c;
}
/**
* Dump local variable table attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public final void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(localVariableTable.length);
for (final LocalVariable variable : localVariableTable) {
variable.dump(file);
}
}
/**
*
* @param index the variable slot
*
* @return the first LocalVariable that matches the slot or null if not found
*
* @deprecated since 5.2 because multiple variables can share the same slot, use getLocalVariable(int index, int pc)
* instead.
*/
@java.lang.Deprecated
public final LocalVariable getLocalVariable(final int index) {
for (final LocalVariable variable : localVariableTable) {
if (variable.getIndex() == index) {
return variable;
}
}
return null;
}
/**
*
* @param index the variable slot
* @param pc the current pc that this variable is alive
*
* @return the LocalVariable that matches or null if not found
*/
public final LocalVariable getLocalVariable(final int index, final int pc) {
for (final LocalVariable variable : localVariableTable) {
if (variable.getIndex() == index) {
final int startPc = variable.getStartPC();
final int endPc = startPc + variable.getLength();
if (pc >= startPc && pc <= endPc) {
return variable;
}
}
}
return null;
}
/**
* @return Array of local variables of method.
*/
public final LocalVariable[] getLocalVariableTable() {
return localVariableTable;
}
public final int getTableLength() {
return localVariableTable == null ? 0 : localVariableTable.length;
}
@Override
public Iterator<LocalVariable> iterator() {
return Stream.of(localVariableTable).iterator();
}
public final void setLocalVariableTable(final LocalVariable[] localVariableTable) {
this.localVariableTable = localVariableTable;
}
/**
* @return String representation.
*/
@Override
public final String toString() {
final StringBuilder buf = new StringBuilder();
for (int i = 0; i < localVariableTable.length; i++) {
buf.append(localVariableTable[i]);
if (i < localVariableTable.length - 1) {
buf.append('\n');
}
}
return buf.toString();
}
}

View File

@@ -0,0 +1,157 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.stream.Stream;
// The new table is used when generic types are about...
//LocalVariableTable_attribute {
// u2 attribute_name_index;
// u4 attribute_length;
// u2 local_variable_table_length;
// { u2 start_pc;
// u2 length;
// u2 name_index;
// u2 descriptor_index;
// u2 index;
// } local_variable_table[local_variable_table_length];
// }
//LocalVariableTypeTable_attribute {
// u2 attribute_name_index;
// u4 attribute_length;
// u2 local_variable_type_table_length;
// {
// u2 start_pc;
// u2 length;
// u2 name_index;
// u2 signature_index;
// u2 index;
// } localVariableTypeTable[local_variable_type_table_length];
// }
// J5TODO: Needs some testing !
/**
* @since 6.0
*/
public class LocalVariableTypeTable extends Attribute implements Iterable<LocalVariable> {
private LocalVariable[] localVariableTypeTable; // variables
LocalVariableTypeTable(final int nameIdx, final int len, final DataInput input, final ConstantPool cpool) throws IOException {
this(nameIdx, len, (LocalVariable[]) null, cpool);
final int localVariableTypeTableLength = input.readUnsignedShort();
localVariableTypeTable = new LocalVariable[localVariableTypeTableLength];
for (int i = 0; i < localVariableTypeTableLength; i++) {
localVariableTypeTable[i] = new LocalVariable(input, cpool);
}
}
public LocalVariableTypeTable(final int nameIndex, final int length, final LocalVariable[] localVariableTypeTable, final ConstantPool constantPool) {
super(Const.ATTR_LOCAL_VARIABLE_TYPE_TABLE, nameIndex, length, constantPool);
this.localVariableTypeTable = localVariableTypeTable != null ? localVariableTypeTable : LocalVariable.EMPTY_ARRAY;
Args.requireU2(this.localVariableTypeTable.length, "localVariableTypeTable.length");
}
public LocalVariableTypeTable(final LocalVariableTypeTable c) {
this(c.getNameIndex(), c.getLength(), c.getLocalVariableTypeTable(), c.getConstantPool());
}
@Override
public void accept(final Visitor v) {
v.visitLocalVariableTypeTable(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final LocalVariableTypeTable c = (LocalVariableTypeTable) clone();
c.localVariableTypeTable = new LocalVariable[localVariableTypeTable.length];
Arrays.setAll(c.localVariableTypeTable, i -> localVariableTypeTable[i].copy());
c.setConstantPool(constantPool);
return c;
}
@Override
public final void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(localVariableTypeTable.length);
for (final LocalVariable variable : localVariableTypeTable) {
variable.dump(file);
}
}
public final LocalVariable getLocalVariable(final int index) {
for (final LocalVariable variable : localVariableTypeTable) {
if (variable.getIndex() == index) {
return variable;
}
}
return null;
}
public final LocalVariable[] getLocalVariableTypeTable() {
return localVariableTypeTable;
}
public final int getTableLength() {
return localVariableTypeTable == null ? 0 : localVariableTypeTable.length;
}
@Override
public Iterator<LocalVariable> iterator() {
return Stream.of(localVariableTypeTable).iterator();
}
public final void setLocalVariableTable(final LocalVariable[] localVariableTable) {
this.localVariableTypeTable = localVariableTable;
}
/**
* @return String representation.
*/
@Override
public final String toString() {
final StringBuilder buf = new StringBuilder();
for (int i = 0; i < localVariableTypeTable.length; i++) {
buf.append(localVariableTypeTable[i].toStringShared(true));
if (i < localVariableTypeTable.length - 1) {
buf.append('\n');
}
}
return buf.toString();
}
}

View File

@@ -0,0 +1,121 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Entry of the parameters table.
*
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24"> The class File Format :
* The MethodParameters Attribute</a>
* @since 6.0
*/
public class MethodParameter implements Cloneable, Node {
/** Index of the CONSTANT_Utf8_info structure in the constant_pool table representing the name of the parameter */
private int nameIndex;
/** The access flags */
private int accessFlags;
public MethodParameter() {
}
/**
* Construct object from input stream.
*
* @param input Input stream
* @throws IOException if an I/O error occurs.
* @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
*/
MethodParameter(final DataInput input) throws IOException {
nameIndex = input.readUnsignedShort();
accessFlags = input.readUnsignedShort();
}
@Override
public void accept(final Visitor v) {
v.visitMethodParameter(this);
}
/**
* @return deep copy of this object
*/
public MethodParameter copy() {
try {
return (MethodParameter) clone();
} catch (final CloneNotSupportedException e) {
// TODO should this throw?
}
return null;
}
/**
* Dump object to file stream on binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
public final void dump(final DataOutputStream file) throws IOException {
file.writeShort(nameIndex);
file.writeShort(accessFlags);
}
public int getAccessFlags() {
return accessFlags;
}
public int getNameIndex() {
return nameIndex;
}
/**
* Returns the name of the parameter.
*/
public String getParameterName(final ConstantPool constantPool) {
if (nameIndex == 0) {
return null;
}
return constantPool.getConstantUtf8(nameIndex).getBytes();
}
public boolean isFinal() {
return (accessFlags & Const.ACC_FINAL) != 0;
}
public boolean isMandated() {
return (accessFlags & Const.ACC_MANDATED) != 0;
}
public boolean isSynthetic() {
return (accessFlags & Const.ACC_SYNTHETIC) != 0;
}
public void setAccessFlags(final int accessFlags) {
this.accessFlags = accessFlags;
}
public void setNameIndex(final int nameIndex) {
this.nameIndex = nameIndex;
}
}

View File

@@ -0,0 +1,97 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.stream.Stream;
/**
* This class represents a MethodParameters attribute.
*
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24"> The class File Format :
* The MethodParameters Attribute</a>
* @since 6.0
*/
public class MethodParameters extends Attribute implements Iterable<MethodParameter> {
/**
* Empty array.
*/
private static final MethodParameter[] EMPTY_METHOD_PARAMETER_ARRAY = {};
private MethodParameter[] parameters = EMPTY_METHOD_PARAMETER_ARRAY;
MethodParameters(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
super(Const.ATTR_METHOD_PARAMETERS, nameIndex, length, constantPool);
final int parameterCount = input.readUnsignedByte();
parameters = new MethodParameter[parameterCount];
for (int i = 0; i < parameterCount; i++) {
parameters[i] = new MethodParameter(input);
}
}
@Override
public void accept(final Visitor v) {
v.visitMethodParameters(this);
}
@Override
public Attribute copy(final ConstantPool constantPool) {
final MethodParameters c = (MethodParameters) clone();
c.parameters = new MethodParameter[parameters.length];
Arrays.setAll(c.parameters, i -> parameters[i].copy());
c.setConstantPool(constantPool);
return c;
}
/**
* Dump method parameters attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeByte(parameters.length);
for (final MethodParameter parameter : parameters) {
parameter.dump(file);
}
}
public MethodParameter[] getParameters() {
return parameters;
}
@Override
public Iterator<MethodParameter> iterator() {
return Stream.of(parameters).iterator();
}
public void setParameters(final MethodParameter[] parameters) {
this.parameters = parameters;
}
}

View File

@@ -0,0 +1,250 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
/**
* This class is derived from <em>Attribute</em> and represents the list of modules required, exported, opened or
* provided by a module. There may be at most one Module attribute in a ClassFile structure.
*
* @see Attribute
* @since 6.4.0
*/
public final class Module extends Attribute {
/**
* The module file name extension.
*
* @since 6.7.0
*/
public static final String EXTENSION = ".jmod";
private final int moduleNameIndex;
private final int moduleFlags;
private final int moduleVersionIndex;
private ModuleRequires[] requiresTable;
private ModuleExports[] exportsTable;
private ModuleOpens[] opensTable;
private final int usesCount;
private final int[] usesIndex;
private ModuleProvides[] providesTable;
/**
* Construct object from input stream.
*
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
Module(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
super(Const.ATTR_MODULE, nameIndex, length, constantPool);
moduleNameIndex = input.readUnsignedShort();
moduleFlags = input.readUnsignedShort();
moduleVersionIndex = input.readUnsignedShort();
final int requiresCount = input.readUnsignedShort();
requiresTable = new ModuleRequires[requiresCount];
for (int i = 0; i < requiresCount; i++) {
requiresTable[i] = new ModuleRequires(input);
}
final int exportsCount = input.readUnsignedShort();
exportsTable = new ModuleExports[exportsCount];
for (int i = 0; i < exportsCount; i++) {
exportsTable[i] = new ModuleExports(input);
}
final int opensCount = input.readUnsignedShort();
opensTable = new ModuleOpens[opensCount];
for (int i = 0; i < opensCount; i++) {
opensTable[i] = new ModuleOpens(input);
}
usesCount = input.readUnsignedShort();
usesIndex = new int[usesCount];
for (int i = 0; i < usesCount; i++) {
usesIndex[i] = input.readUnsignedShort();
}
final int providesCount = input.readUnsignedShort();
providesTable = new ModuleProvides[providesCount];
for (int i = 0; i < providesCount; i++) {
providesTable[i] = new ModuleProvides(input);
}
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitModule(this);
}
// TODO add more getters and setters?
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final Module c = (Module) clone();
c.requiresTable = new ModuleRequires[requiresTable.length];
Arrays.setAll(c.requiresTable, i -> requiresTable[i].copy());
c.exportsTable = new ModuleExports[exportsTable.length];
Arrays.setAll(c.exportsTable, i -> exportsTable[i].copy());
c.opensTable = new ModuleOpens[opensTable.length];
Arrays.setAll(c.opensTable, i -> opensTable[i].copy());
c.providesTable = new ModuleProvides[providesTable.length];
Arrays.setAll(c.providesTable, i -> providesTable[i].copy());
c.setConstantPool(constantPool);
return c;
}
/**
* Dump Module attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(moduleNameIndex);
file.writeShort(moduleFlags);
file.writeShort(moduleVersionIndex);
file.writeShort(requiresTable.length);
for (final ModuleRequires entry : requiresTable) {
entry.dump(file);
}
file.writeShort(exportsTable.length);
for (final ModuleExports entry : exportsTable) {
entry.dump(file);
}
file.writeShort(opensTable.length);
for (final ModuleOpens entry : opensTable) {
entry.dump(file);
}
file.writeShort(usesIndex.length);
for (final int entry : usesIndex) {
file.writeShort(entry);
}
file.writeShort(providesTable.length);
for (final ModuleProvides entry : providesTable) {
entry.dump(file);
}
}
/**
* @return table of exported interfaces
* @see ModuleExports
*/
public ModuleExports[] getExportsTable() {
return exportsTable;
}
/**
* @return table of provided interfaces
* @see ModuleOpens
*/
public ModuleOpens[] getOpensTable() {
return opensTable;
}
/**
* @return table of provided interfaces
* @see ModuleProvides
*/
public ModuleProvides[] getProvidesTable() {
return providesTable;
}
/**
* @return table of required modules
* @see ModuleRequires
*/
public ModuleRequires[] getRequiresTable() {
return requiresTable;
}
/**
* @return String representation, i.e., a list of packages.
*/
@Override
public String toString() {
final ConstantPool cp = super.getConstantPool();
final StringBuilder buf = new StringBuilder();
buf.append("Module:\n");
buf.append(" name: ").append(Utility.pathToPackage(cp.getConstantString(moduleNameIndex, Const.CONSTANT_Module))).append("\n");
buf.append(" flags: ").append(String.format("%04x", moduleFlags)).append("\n");
final String version = moduleVersionIndex == 0 ? "0" : cp.getConstantString(moduleVersionIndex, Const.CONSTANT_Utf8);
buf.append(" version: ").append(version).append("\n");
buf.append(" requires(").append(requiresTable.length).append("):\n");
for (final ModuleRequires module : requiresTable) {
buf.append(" ").append(module.toString(cp)).append("\n");
}
buf.append(" exports(").append(exportsTable.length).append("):\n");
for (final ModuleExports module : exportsTable) {
buf.append(" ").append(module.toString(cp)).append("\n");
}
buf.append(" opens(").append(opensTable.length).append("):\n");
for (final ModuleOpens module : opensTable) {
buf.append(" ").append(module.toString(cp)).append("\n");
}
buf.append(" uses(").append(usesIndex.length).append("):\n");
for (final int index : usesIndex) {
final String className = cp.getConstantString(index, Const.CONSTANT_Class);
buf.append(" ").append(Utility.compactClassName(className, false)).append("\n");
}
buf.append(" provides(").append(providesTable.length).append("):\n");
for (final ModuleProvides module : providesTable) {
buf.append(" ").append(module.toString(cp)).append("\n");
}
return buf.substring(0, buf.length() - 1); // remove the last newline
}
}

View File

@@ -0,0 +1,119 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class represents an entry in the exports table of the Module attribute. Each entry describes a package which may
* open the parent module.
*
* @see Module
* @since 6.4.0
*/
public final class ModuleExports implements Cloneable, Node {
private final int exportsIndex; // points to CONSTANT_Package_info
private final int exportsFlags;
private final int exportsToCount;
private final int[] exportsToIndex; // points to CONSTANT_Module_info
/**
* Construct object from file stream.
*
* @param file Input stream
* @throws IOException if an I/O Exception occurs in readUnsignedShort
*/
ModuleExports(final DataInput file) throws IOException {
exportsIndex = file.readUnsignedShort();
exportsFlags = file.readUnsignedShort();
exportsToCount = file.readUnsignedShort();
exportsToIndex = new int[exportsToCount];
for (int i = 0; i < exportsToCount; i++) {
exportsToIndex[i] = file.readUnsignedShort();
}
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitModuleExports(this);
}
// TODO add more getters and setters?
/**
* @return deep copy of this object
*/
public ModuleExports copy() {
try {
return (ModuleExports) clone();
} catch (final CloneNotSupportedException e) {
// TODO should this throw?
}
return null;
}
/**
* Dump table entry to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O Exception occurs in writeShort
*/
public void dump(final DataOutputStream file) throws IOException {
file.writeShort(exportsIndex);
file.writeShort(exportsFlags);
file.writeShort(exportsToCount);
for (final int entry : exportsToIndex) {
file.writeShort(entry);
}
}
/**
* @return String representation
*/
@Override
public String toString() {
return "exports(" + exportsIndex + ", " + exportsFlags + ", " + exportsToCount + ", ...)";
}
/**
* @return Resolved string representation
*/
public String toString(final ConstantPool constantPool) {
final StringBuilder buf = new StringBuilder();
final String packageName = constantPool.constantToString(exportsIndex, Const.CONSTANT_Package);
buf.append(Utility.compactClassName(packageName, false));
buf.append(", ").append(String.format("%04x", exportsFlags));
buf.append(", to(").append(exportsToCount).append("):\n");
for (final int index : exportsToIndex) {
final String moduleName = constantPool.getConstantString(index, Const.CONSTANT_Module);
buf.append(" ").append(Utility.compactClassName(moduleName, false)).append("\n");
}
return buf.substring(0, buf.length() - 1); // remove the last newline
}
}

View File

@@ -0,0 +1,130 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from <em>Attribute</em> and indicates the main class of a module. There may be at most one
* ModuleMainClass attribute in a ClassFile structure.
*
* @see Attribute
*/
public final class ModuleMainClass extends Attribute {
private int mainClassIndex;
/**
* Construct object from input stream.
*
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
ModuleMainClass(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, 0, constantPool);
mainClassIndex = input.readUnsignedShort();
}
/**
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param mainClassIndex Host class index
* @param constantPool Array of constants
*/
public ModuleMainClass(final int nameIndex, final int length, final int mainClassIndex, final ConstantPool constantPool) {
super(Const.ATTR_NEST_MEMBERS, nameIndex, length, constantPool);
this.mainClassIndex = Args.requireU2(mainClassIndex, "mainClassIndex");
}
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a
* physical copy.
*
* @param c Source to copy.
*/
public ModuleMainClass(final ModuleMainClass c) {
this(c.getNameIndex(), c.getLength(), c.getHostClassIndex(), c.getConstantPool());
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e.,
* the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitModuleMainClass(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final ModuleMainClass c = (ModuleMainClass) clone();
c.setConstantPool(constantPool);
return c;
}
/**
* Dump ModuleMainClass attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(mainClassIndex);
}
/**
* @return index into constant pool of host class name.
*/
public int getHostClassIndex() {
return mainClassIndex;
}
/**
* @param mainClassIndex the host class index
*/
public void setHostClassIndex(final int mainClassIndex) {
this.mainClassIndex = mainClassIndex;
}
/**
* @return String representation
*/
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
buf.append("ModuleMainClass: ");
final String className = super.getConstantPool().getConstantString(mainClassIndex, Const.CONSTANT_Class);
buf.append(Utility.compactClassName(className, false));
return buf.toString();
}
}

View File

@@ -0,0 +1,119 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class represents an entry in the opens table of the Module attribute. Each entry describes a package which the
* parent module opens.
*
* @see Module
* @since 6.4.0
*/
public final class ModuleOpens implements Cloneable, Node {
private final int opensIndex; // points to CONSTANT_Package_info
private final int opensFlags;
private final int opensToCount;
private final int[] opensToIndex; // points to CONSTANT_Module_info
/**
* Construct object from file stream.
*
* @param file Input stream
* @throws IOException if an I/O Exception occurs in readUnsignedShort
*/
ModuleOpens(final DataInput file) throws IOException {
opensIndex = file.readUnsignedShort();
opensFlags = file.readUnsignedShort();
opensToCount = file.readUnsignedShort();
opensToIndex = new int[opensToCount];
for (int i = 0; i < opensToCount; i++) {
opensToIndex[i] = file.readUnsignedShort();
}
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitModuleOpens(this);
}
// TODO add more getters and setters?
/**
* @return deep copy of this object
*/
public ModuleOpens copy() {
try {
return (ModuleOpens) clone();
} catch (final CloneNotSupportedException e) {
// TODO should this throw?
}
return null;
}
/**
* Dump table entry to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O Exception occurs in writeShort
*/
public void dump(final DataOutputStream file) throws IOException {
file.writeShort(opensIndex);
file.writeShort(opensFlags);
file.writeShort(opensToCount);
for (final int entry : opensToIndex) {
file.writeShort(entry);
}
}
/**
* @return String representation
*/
@Override
public String toString() {
return "opens(" + opensIndex + ", " + opensFlags + ", " + opensToCount + ", ...)";
}
/**
* @return Resolved string representation
*/
public String toString(final ConstantPool constantPool) {
final StringBuilder buf = new StringBuilder();
final String packageName = constantPool.constantToString(opensIndex, Const.CONSTANT_Package);
buf.append(Utility.compactClassName(packageName, false));
buf.append(", ").append(String.format("%04x", opensFlags));
buf.append(", to(").append(opensToCount).append("):\n");
for (final int index : opensToIndex) {
final String moduleName = constantPool.getConstantString(index, Const.CONSTANT_Module);
buf.append(" ").append(Utility.compactClassName(moduleName, false)).append("\n");
}
return buf.substring(0, buf.length() - 1); // remove the last newline
}
}

View File

@@ -0,0 +1,163 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import org.apache.commons.lang3.ArrayUtils;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
/**
* This class is derived from <em>Attribute</em> and represents the list of packages that are exported or opened by the
* Module attribute. There may be at most one ModulePackages attribute in a ClassFile structure.
*
* @see Attribute
*/
public final class ModulePackages extends Attribute {
private int[] packageIndexTable;
/**
* Construct object from input stream.
*
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
ModulePackages(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, (int[]) null, constantPool);
final int packageCount = input.readUnsignedShort();
packageIndexTable = new int[packageCount];
for (int i = 0; i < packageCount; i++) {
packageIndexTable[i] = input.readUnsignedShort();
}
}
/**
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param packageIndexTable Table of indices in constant pool
* @param constantPool Array of constants
*/
public ModulePackages(final int nameIndex, final int length, final int[] packageIndexTable, final ConstantPool constantPool) {
super(Const.ATTR_MODULE_PACKAGES, nameIndex, length, constantPool);
this.packageIndexTable = packageIndexTable != null ? packageIndexTable : ArrayUtils.EMPTY_INT_ARRAY;
Args.requireU2(this.packageIndexTable.length, "packageIndexTable.length");
}
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a
* physical copy.
*
* @param c Source to copy.
*/
public ModulePackages(final ModulePackages c) {
this(c.getNameIndex(), c.getLength(), c.getPackageIndexTable(), c.getConstantPool());
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitModulePackages(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final ModulePackages c = (ModulePackages) clone();
if (packageIndexTable != null) {
c.packageIndexTable = packageIndexTable.clone();
}
c.setConstantPool(constantPool);
return c;
}
/**
* Dump ModulePackages attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(packageIndexTable.length);
for (final int index : packageIndexTable) {
file.writeShort(index);
}
}
/**
* @return Length of package table.
*/
public int getNumberOfPackages() {
return packageIndexTable == null ? 0 : packageIndexTable.length;
}
/**
* @return array of indices into constant pool of package names.
*/
public int[] getPackageIndexTable() {
return packageIndexTable;
}
/**
* @return string array of package names
*/
public String[] getPackageNames() {
final String[] names = new String[packageIndexTable.length];
Arrays.setAll(names, i -> Utility.pathToPackage(super.getConstantPool().getConstantString(packageIndexTable[i], Const.CONSTANT_Package)));
return names;
}
/**
* @param packageIndexTable the list of package indexes Also redefines number_of_packages according to table length.
*/
public void setPackageIndexTable(final int[] packageIndexTable) {
this.packageIndexTable = packageIndexTable != null ? packageIndexTable : ArrayUtils.EMPTY_INT_ARRAY;
}
/**
* @return String representation, i.e., a list of packages.
*/
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
buf.append("ModulePackages(");
buf.append(packageIndexTable.length);
buf.append("):\n");
for (final int index : packageIndexTable) {
final String packageName = super.getConstantPool().getConstantString(index, Const.CONSTANT_Package);
buf.append(" ").append(Utility.compactClassName(packageName, false)).append("\n");
}
return buf.substring(0, buf.length() - 1); // remove the last newline
}
}

View File

@@ -0,0 +1,115 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class represents an entry in the provides table of the Module attribute. Each entry describes a service
* implementation that the parent module provides.
*
* @see Module
* @since 6.4.0
*/
public final class ModuleProvides implements Cloneable, Node {
private final int providesIndex; // points to CONSTANT_Class_info
private final int providesWithCount;
private final int[] providesWithIndex; // points to CONSTANT_Class_info
/**
* Construct object from file stream.
*
* @param file Input stream
* @throws IOException if an I/O Exception occurs in readUnsignedShort
*/
ModuleProvides(final DataInput file) throws IOException {
providesIndex = file.readUnsignedShort();
providesWithCount = file.readUnsignedShort();
providesWithIndex = new int[providesWithCount];
for (int i = 0; i < providesWithCount; i++) {
providesWithIndex[i] = file.readUnsignedShort();
}
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitModuleProvides(this);
}
// TODO add more getters and setters?
/**
* @return deep copy of this object
*/
public ModuleProvides copy() {
try {
return (ModuleProvides) clone();
} catch (final CloneNotSupportedException e) {
// TODO should this throw?
}
return null;
}
/**
* Dump table entry to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O Exception occurs in writeShort
*/
public void dump(final DataOutputStream file) throws IOException {
file.writeShort(providesIndex);
file.writeShort(providesWithCount);
for (final int entry : providesWithIndex) {
file.writeShort(entry);
}
}
/**
* @return String representation
*/
@Override
public String toString() {
return "provides(" + providesIndex + ", " + providesWithCount + ", ...)";
}
/**
* @return Resolved string representation
*/
public String toString(final ConstantPool constantPool) {
final StringBuilder buf = new StringBuilder();
final String interfaceName = constantPool.constantToString(providesIndex, Const.CONSTANT_Class);
buf.append(Utility.compactClassName(interfaceName, false));
buf.append(", with(").append(providesWithCount).append("):\n");
for (final int index : providesWithIndex) {
final String className = constantPool.getConstantString(index, Const.CONSTANT_Class);
buf.append(" ").append(Utility.compactClassName(className, false)).append("\n");
}
return buf.substring(0, buf.length() - 1); // remove the last newline
}
}

View File

@@ -0,0 +1,108 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class represents an entry in the requires table of the Module attribute. Each entry describes a module on which
* the parent module depends.
*
* @see Module
* @since 6.4.0
*/
public final class ModuleRequires implements Cloneable, Node {
private final int requiresIndex; // points to CONSTANT_Module_info
private final int requiresFlags;
private final int requiresVersionIndex; // either 0 or points to CONSTANT_Utf8_info
/**
* Construct object from file stream.
*
* @param file Input stream
* @throws IOException if an I/O Exception occurs in readUnsignedShort
*/
ModuleRequires(final DataInput file) throws IOException {
requiresIndex = file.readUnsignedShort();
requiresFlags = file.readUnsignedShort();
requiresVersionIndex = file.readUnsignedShort();
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitModuleRequires(this);
}
// TODO add more getters and setters?
/**
* @return deep copy of this object
*/
public ModuleRequires copy() {
try {
return (ModuleRequires) clone();
} catch (final CloneNotSupportedException e) {
// TODO should this throw?
}
return null;
}
/**
* Dump table entry to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O Exception occurs in writeShort
*/
public void dump(final DataOutputStream file) throws IOException {
file.writeShort(requiresIndex);
file.writeShort(requiresFlags);
file.writeShort(requiresVersionIndex);
}
/**
* @return String representation
*/
@Override
public String toString() {
return "requires(" + requiresIndex + ", " + String.format("%04x", requiresFlags) + ", " + requiresVersionIndex + ")";
}
/**
* @return Resolved string representation
*/
public String toString(final ConstantPool constantPool) {
final StringBuilder buf = new StringBuilder();
final String moduleName = constantPool.constantToString(requiresIndex, Const.CONSTANT_Module);
buf.append(Utility.compactClassName(moduleName, false));
buf.append(", ").append(String.format("%04x", requiresFlags));
final String version = requiresVersionIndex == 0 ? "0" : constantPool.getConstantString(requiresVersionIndex, Const.CONSTANT_Utf8);
buf.append(", ").append(version);
return buf.toString();
}
}

View File

@@ -0,0 +1,130 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from <em>Attribute</em> and records the nest host of the nest to which the current class or
* interface claims to belong. There may be at most one NestHost attribute in a ClassFile structure.
*
* @see Attribute
*/
public final class NestHost extends Attribute {
private int hostClassIndex;
/**
* Constructs object from input stream.
*
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
NestHost(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, 0, constantPool);
hostClassIndex = input.readUnsignedShort();
}
/**
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param hostClassIndex Host class index
* @param constantPool Array of constants
*/
public NestHost(final int nameIndex, final int length, final int hostClassIndex, final ConstantPool constantPool) {
super(Const.ATTR_NEST_MEMBERS, nameIndex, length, constantPool);
this.hostClassIndex = Args.requireU2(hostClassIndex, "hostClassIndex");
}
/**
* Initializes from another object. Note that both objects use the same references (shallow copy). Use copy() for a
* physical copy.
*
* @param c Source to copy.
*/
public NestHost(final NestHost c) {
this(c.getNameIndex(), c.getLength(), c.getHostClassIndex(), c.getConstantPool());
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitNestHost(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final NestHost c = (NestHost) clone();
c.setConstantPool(constantPool);
return c;
}
/**
* Dumps NestHost attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(hostClassIndex);
}
/**
* @return index into constant pool of host class name.
*/
public int getHostClassIndex() {
return hostClassIndex;
}
/**
* @param hostClassIndex the host class index
*/
public void setHostClassIndex(final int hostClassIndex) {
this.hostClassIndex = hostClassIndex;
}
/**
* @return String representation
*/
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
buf.append("NestHost: ");
final String className = super.getConstantPool().getConstantString(hostClassIndex, Const.CONSTANT_Class);
buf.append(Utility.compactClassName(className, false));
return buf.toString();
}
}

View File

@@ -0,0 +1,164 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import org.apache.commons.lang3.ArrayUtils;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
/**
* This class is derived from <em>Attribute</em> and records the classes and interfaces that are authorized to claim
* membership in the nest hosted by the current class or interface. There may be at most one NestMembers attribute in a
* ClassFile structure.
*
* @see Attribute
*/
public final class NestMembers extends Attribute {
private int[] classes;
/**
* Construct object from input stream.
*
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
NestMembers(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, (int[]) null, constantPool);
final int classCount = input.readUnsignedShort();
classes = new int[classCount];
for (int i = 0; i < classCount; i++) {
classes[i] = input.readUnsignedShort();
}
}
/**
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param classes Table of indices in constant pool
* @param constantPool Array of constants
*/
public NestMembers(final int nameIndex, final int length, final int[] classes, final ConstantPool constantPool) {
super(Const.ATTR_NEST_MEMBERS, nameIndex, length, constantPool);
this.classes = classes != null ? classes : ArrayUtils.EMPTY_INT_ARRAY;
Args.requireU2(this.classes.length, "classes.length");
}
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a
* physical copy.
*
* @param c Source to copy.
*/
public NestMembers(final NestMembers c) {
this(c.getNameIndex(), c.getLength(), c.getClasses(), c.getConstantPool());
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitNestMembers(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final NestMembers c = (NestMembers) clone();
if (classes.length > 0) {
c.classes = classes.clone();
}
c.setConstantPool(constantPool);
return c;
}
/**
* Dump NestMembers attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(classes.length);
for (final int index : classes) {
file.writeShort(index);
}
}
/**
* @return array of indices into constant pool of class names.
*/
public int[] getClasses() {
return classes;
}
/**
* @return string array of class names
*/
public String[] getClassNames() {
final String[] names = new String[classes.length];
Arrays.setAll(names, i -> Utility.pathToPackage(super.getConstantPool().getConstantString(classes[i], Const.CONSTANT_Class)));
return names;
}
/**
* @return Length of classes table.
*/
public int getNumberClasses() {
return classes.length;
}
/**
* @param classes the list of class indexes Also redefines number_of_classes according to table length.
*/
public void setClasses(final int[] classes) {
this.classes = classes != null ? classes : ArrayUtils.EMPTY_INT_ARRAY;
}
/**
* @return String representation, i.e., a list of classes.
*/
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
buf.append("NestMembers(");
buf.append(classes.length);
buf.append("):\n");
for (final int index : classes) {
final String className = super.getConstantPool().getConstantString(index, Const.CONSTANT_Class);
buf.append(" ").append(Utility.compactClassName(className, false)).append("\n");
}
return buf.substring(0, buf.length() - 1); // remove the last newline
}
}

View File

@@ -0,0 +1,25 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
/**
* Denote class to have an accept method();
*/
public interface Node {
void accept(Visitor obj);
}

View File

@@ -0,0 +1,152 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from <em>Attribute</em> and represents a reference to a PMG attribute.
*
* @see Attribute
*/
public final class PMGClass extends Attribute {
private int pmgClassIndex;
private int pmgIndex;
/**
* Construct object from input stream.
*
* @param nameIndex Index in constant pool to CONSTANT_Utf8
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
PMGClass(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, input.readUnsignedShort(), input.readUnsignedShort(), constantPool);
}
/**
* @param nameIndex Index in constant pool to CONSTANT_Utf8
* @param length Content length in bytes
* @param pmgIndex index in constant pool for source file name
* @param pmgClassIndex Index in constant pool to CONSTANT_Utf8
* @param constantPool Array of constants
*/
public PMGClass(final int nameIndex, final int length, final int pmgIndex, final int pmgClassIndex, final ConstantPool constantPool) {
super(Const.ATTR_PMG, nameIndex, length, constantPool);
this.pmgIndex = pmgIndex;
this.pmgClassIndex = pmgClassIndex;
}
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a
* physical copy.
*
* @param pgmClass Source to copy.
*/
public PMGClass(final PMGClass pgmClass) {
this(pgmClass.getNameIndex(), pgmClass.getLength(), pgmClass.getPMGIndex(), pgmClass.getPMGClassIndex(), pgmClass.getConstantPool());
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
println("Visiting non-standard PMGClass object");
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
return (Attribute) clone();
}
/**
* Dump source file attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(pmgIndex);
file.writeShort(pmgClassIndex);
}
/**
* @return Index in constant pool of source file name.
*/
public int getPMGClassIndex() {
return pmgClassIndex;
}
/**
* @return PMG class name.
*/
public String getPMGClassName() {
return super.getConstantPool().getConstantUtf8(pmgClassIndex).getBytes();
}
/**
* @return Index in constant pool of source file name.
*/
public int getPMGIndex() {
return pmgIndex;
}
/**
* @return PMG name.
*/
public String getPMGName() {
return super.getConstantPool().getConstantUtf8(pmgIndex).getBytes();
}
/**
* @param pmgClassIndex
*/
public void setPMGClassIndex(final int pmgClassIndex) {
this.pmgClassIndex = pmgClassIndex;
}
/**
* @param pmgIndex
*/
public void setPMGIndex(final int pmgIndex) {
this.pmgIndex = pmgIndex;
}
/**
* @return String representation
*/
@Override
public String toString() {
return "PMGClass(" + getPMGName() + ", " + getPMGClassName() + ")";
}
}

View File

@@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* represents one parameter annotation in the parameter annotation table
*
* @since 6.0
*/
public class ParameterAnnotationEntry implements Node {
static final ParameterAnnotationEntry[] EMPTY_ARRAY = {};
public static ParameterAnnotationEntry[] createParameterAnnotationEntries(final Attribute[] attrs) {
// Find attributes that contain parameter annotation data
final List<ParameterAnnotationEntry> accumulatedAnnotations = new ArrayList<>(attrs.length);
for (final Attribute attribute : attrs) {
if (attribute instanceof ParameterAnnotations) {
final ParameterAnnotations runtimeAnnotations = (ParameterAnnotations) attribute;
Collections.addAll(accumulatedAnnotations, runtimeAnnotations.getParameterAnnotationEntries());
}
}
return accumulatedAnnotations.toArray(ParameterAnnotationEntry.EMPTY_ARRAY);
}
private final AnnotationEntry[] annotationTable;
/**
* Construct object from input stream.
*
* @param input Input stream
* @throws IOException if an I/O error occurs.
*/
ParameterAnnotationEntry(final DataInput input, final ConstantPool constantPool) throws IOException {
final int annotationTableLength = input.readUnsignedShort();
annotationTable = new AnnotationEntry[annotationTableLength];
for (int i = 0; i < annotationTableLength; i++) {
// TODO isRuntimeVisible
annotationTable[i] = AnnotationEntry.read(input, constantPool, false);
}
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitParameterAnnotationEntry(this);
}
public void dump(final DataOutputStream dos) throws IOException {
dos.writeShort(annotationTable.length);
for (final AnnotationEntry entry : annotationTable) {
entry.dump(dos);
}
}
/**
* returns the array of annotation entries in this annotation
*/
public AnnotationEntry[] getAnnotationEntries() {
return annotationTable;
}
}

View File

@@ -0,0 +1,120 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.stream.Stream;
/**
* base class for parameter annotations
*
* @since 6.0
*/
public abstract class ParameterAnnotations extends Attribute implements Iterable<ParameterAnnotationEntry> {
/** Table of parameter annotations */
private ParameterAnnotationEntry[] parameterAnnotationTable;
/**
* @param parameterAnnotationType the subclass type of the parameter annotation
* @param nameIndex Index pointing to the name <em>Code</em>
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
*/
ParameterAnnotations(final byte parameterAnnotationType, final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool)
throws IOException {
this(parameterAnnotationType, nameIndex, length, (ParameterAnnotationEntry[]) null, constantPool);
final int numParameters = input.readUnsignedByte();
parameterAnnotationTable = new ParameterAnnotationEntry[numParameters];
for (int i = 0; i < numParameters; i++) {
parameterAnnotationTable[i] = new ParameterAnnotationEntry(input, constantPool);
}
}
/**
* @param parameterAnnotationType the subclass type of the parameter annotation
* @param nameIndex Index pointing to the name <em>Code</em>
* @param length Content length in bytes
* @param parameterAnnotationTable the actual parameter annotations
* @param constantPool Array of constants
*/
public ParameterAnnotations(final byte parameterAnnotationType, final int nameIndex, final int length,
final ParameterAnnotationEntry[] parameterAnnotationTable, final ConstantPool constantPool) {
super(parameterAnnotationType, nameIndex, length, constantPool);
this.parameterAnnotationTable = parameterAnnotationTable;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitParameterAnnotation(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
return (Attribute) clone();
}
@Override
public void dump(final DataOutputStream dos) throws IOException {
super.dump(dos);
dos.writeByte(parameterAnnotationTable.length);
for (final ParameterAnnotationEntry element : parameterAnnotationTable) {
element.dump(dos);
}
}
/**
* returns the array of parameter annotation entries in this parameter annotation
*/
public ParameterAnnotationEntry[] getParameterAnnotationEntries() {
return parameterAnnotationTable;
}
/**
* @return the parameter annotation entry table
*/
public final ParameterAnnotationEntry[] getParameterAnnotationTable() {
return parameterAnnotationTable;
}
@Override
public Iterator<ParameterAnnotationEntry> iterator() {
return Stream.of(parameterAnnotationTable).iterator();
}
/**
* @param parameterAnnotationTable the entries to set in this parameter annotation
*/
public final void setParameterAnnotationTable(final ParameterAnnotationEntry[] parameterAnnotationTable) {
this.parameterAnnotationTable = parameterAnnotationTable;
}
}

View File

@@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* represents an annotation that is represented in the class file but is not provided to the JVM.
*
* @since 6.0
*/
public class RuntimeInvisibleAnnotations extends Annotations {
/**
* @param nameIndex Index pointing to the name <em>Code</em>
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException Thrown when an I/O exception of some sort has occurred.
*/
public RuntimeInvisibleAnnotations(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
super(Const.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS, nameIndex, length, input, constantPool, false);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
return (Attribute) clone();
}
@Override
public final void dump(final DataOutputStream dos) throws IOException {
super.dump(dos);
writeAnnotations(dos);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.IOException;
/**
* Represents a parameter annotation that is represented in the class file but is not provided to the JVM.
*
* @since 6.0
*/
public class RuntimeInvisibleParameterAnnotations extends ParameterAnnotations {
/**
* @param nameIndex Index pointing to the name <em>Code</em>
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException Thrown when an I/O exception of some sort has occurred.
*/
public RuntimeInvisibleParameterAnnotations(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool)
throws IOException {
super(Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS, nameIndex, length, input, constantPool);
}
}

View File

@@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* represents an annotation that is represented in the class file and is provided to the JVM.
*
* @since 6.0
*/
public class RuntimeVisibleAnnotations extends Annotations {
/**
* @param nameIndex Index pointing to the name <em>Code</em>
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException Thrown when an I/O exception of some sort has occurred.
*/
public RuntimeVisibleAnnotations(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
super(Const.ATTR_RUNTIME_VISIBLE_ANNOTATIONS, nameIndex, length, input, constantPool, true);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
return (Attribute) clone();
}
@Override
public final void dump(final DataOutputStream dos) throws IOException {
super.dump(dos);
writeAnnotations(dos);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.IOException;
/**
* Represents a parameter annotation that is represented in the class file and is provided to the JVM.
*
* @since 6.0
*/
public class RuntimeVisibleParameterAnnotations extends ParameterAnnotations {
/**
* @param nameIndex Index pointing to the name <em>Code</em>
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException Thrown when an I/O exception of some sort has occurred.
*/
public RuntimeVisibleParameterAnnotations(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool)
throws IOException {
super(Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, nameIndex, length, input, constantPool);
}
}

View File

@@ -0,0 +1,253 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
* This class is derived from <em>Attribute</em> and represents a reference to a GJ attribute.
*
* @see Attribute
*/
public final class Signature extends Attribute {
/**
* Extends ByteArrayInputStream to make 'unreading' chars possible.
*/
private static final class MyByteArrayInputStream extends ByteArrayInputStream {
MyByteArrayInputStream(final String data) {
super(data.getBytes(StandardCharsets.UTF_8));
}
String getData() {
return new String(buf, StandardCharsets.UTF_8);
}
void unread() {
if (pos > 0) {
pos--;
}
}
}
private static boolean identStart(final int ch) {
return ch == 'T' || ch == 'L';
}
// @since 6.0 is no longer final
public static boolean isActualParameterList(final String s) {
return s.startsWith("L") && s.endsWith(">;");
}
// @since 6.0 is no longer final
public static boolean isFormalParameterList(final String s) {
return s.startsWith("<") && s.indexOf(':') > 0;
}
private static void matchGJIdent(final MyByteArrayInputStream in, final StringBuilder buf) {
int ch;
matchIdent(in, buf);
ch = in.read();
if (ch == '<' || ch == '(') { // Parameterized or method
// System.out.println("Enter <");
buf.append((char) ch);
matchGJIdent(in, buf);
while ((ch = in.read()) != '>' && ch != ')') { // List of parameters
if (ch == -1) {
throw new IllegalArgumentException("Illegal signature: " + in.getData() + " reaching EOF");
}
// System.out.println("Still no >");
buf.append(", ");
in.unread();
matchGJIdent(in, buf); // Recursive call
}
// System.out.println("Exit >");
buf.append((char) ch);
} else {
in.unread();
}
ch = in.read();
if (identStart(ch)) {
in.unread();
matchGJIdent(in, buf);
} else if (ch == ')') {
in.unread();
} else if (ch != ';') {
throw new IllegalArgumentException("Illegal signature: " + in.getData() + " read " + (char) ch);
}
}
private static void matchIdent(final MyByteArrayInputStream in, final StringBuilder buf) {
int ch;
if ((ch = in.read()) == -1) {
throw new IllegalArgumentException("Illegal signature: " + in.getData() + " no ident, reaching EOF");
}
// System.out.println("return from ident:" + (char)ch);
if (!identStart(ch)) {
final StringBuilder buf2 = new StringBuilder();
int count = 1;
while (Character.isJavaIdentifierPart((char) ch)) {
buf2.append((char) ch);
count++;
ch = in.read();
}
if (ch == ':') { // Ok, formal parameter
final int skipExpected = "Ljava/lang/Object".length();
final long skipActual = in.skip(skipExpected);
if (skipActual != skipExpected) {
throw new IllegalStateException(String.format("Unexpected skip: expected=%,d, actual=%,d", skipExpected, skipActual));
}
buf.append(buf2);
ch = in.read();
in.unread();
// System.out.println("so far:" + buf2 + ":next:" +(char)ch);
} else {
for (int i = 0; i < count; i++) {
in.unread();
}
}
return;
}
final StringBuilder buf2 = new StringBuilder();
ch = in.read();
do {
buf2.append((char) ch);
ch = in.read();
// System.out.println("within ident:"+ (char)ch);
} while (ch != -1 && (Character.isJavaIdentifierPart((char) ch) || ch == '/'));
buf.append(Utility.pathToPackage(buf2.toString()));
// System.out.println("regular return ident:"+ (char)ch + ":" + buf2);
if (ch != -1) {
in.unread();
}
}
public static String translate(final String s) {
// System.out.println("Sig:" + s);
final StringBuilder buf = new StringBuilder();
matchGJIdent(new MyByteArrayInputStream(s), buf);
return buf.toString();
}
private int signatureIndex;
/**
* Construct object from file stream.
*
* @param nameIndex Index in constant pool to CONSTANT_Utf8
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
Signature(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, input.readUnsignedShort(), constantPool);
}
/**
* @param nameIndex Index in constant pool to CONSTANT_Utf8
* @param length Content length in bytes
* @param signatureIndex Index in constant pool to CONSTANT_Utf8
* @param constantPool Array of constants
*/
public Signature(final int nameIndex, final int length, final int signatureIndex, final ConstantPool constantPool) {
super(Const.ATTR_SIGNATURE, nameIndex, Args.require(length, 2, "Signature length attribute"), constantPool);
this.signatureIndex = signatureIndex;
// validate:
Objects.requireNonNull(constantPool.getConstantUtf8(signatureIndex), "constantPool.getConstantUtf8(signatureIndex)");
}
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
* physical copy.
*
* @param c Source to copy.
*/
public Signature(final Signature c) {
this(c.getNameIndex(), c.getLength(), c.getSignatureIndex(), c.getConstantPool());
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
// System.err.println("Visiting non-standard Signature object");
v.visitSignature(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
return (Attribute) clone();
}
/**
* Dump source file attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(signatureIndex);
}
/**
* @return GJ signature.
*/
public String getSignature() {
return super.getConstantPool().getConstantUtf8(signatureIndex).getBytes();
}
/**
* @return Index in constant pool of source file name.
*/
public int getSignatureIndex() {
return signatureIndex;
}
/**
* @param signatureIndex the index info the constant pool of this signature
*/
public void setSignatureIndex(final int signatureIndex) {
this.signatureIndex = signatureIndex;
}
/**
* @return String representation
*/
@Override
public String toString() {
return "Signature: " + getSignature();
}
}

View File

@@ -0,0 +1,178 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* @since 6.0
*/
public class SimpleElementValue extends ElementValue {
private int index;
public SimpleElementValue(final int type, final int index, final ConstantPool cpool) {
super(type, cpool);
this.index = index;
}
@Override
public void dump(final DataOutputStream dos) throws IOException {
final int type = super.getType();
dos.writeByte(type); // u1 kind of value
switch (type) {
case PRIMITIVE_INT:
case PRIMITIVE_BYTE:
case PRIMITIVE_CHAR:
case PRIMITIVE_FLOAT:
case PRIMITIVE_LONG:
case PRIMITIVE_BOOLEAN:
case PRIMITIVE_SHORT:
case PRIMITIVE_DOUBLE:
case STRING:
dos.writeShort(getIndex());
break;
default:
throw new ClassFormatException("SimpleElementValue doesnt know how to write out type " + type);
}
}
/**
* @return Value entry index in the cpool
*/
public int getIndex() {
return index;
}
public boolean getValueBoolean() {
if (super.getType() != PRIMITIVE_BOOLEAN) {
throw new IllegalStateException("Dont call getValueBoolean() on a non BOOLEAN ElementValue");
}
final ConstantInteger bo = (ConstantInteger) super.getConstantPool().getConstant(getIndex());
return bo.getBytes() != 0;
}
public byte getValueByte() {
if (super.getType() != PRIMITIVE_BYTE) {
throw new IllegalStateException("Dont call getValueByte() on a non BYTE ElementValue");
}
return (byte) super.getConstantPool().getConstantInteger(getIndex()).getBytes();
}
public char getValueChar() {
if (super.getType() != PRIMITIVE_CHAR) {
throw new IllegalStateException("Dont call getValueChar() on a non CHAR ElementValue");
}
return (char) super.getConstantPool().getConstantInteger(getIndex()).getBytes();
}
public double getValueDouble() {
if (super.getType() != PRIMITIVE_DOUBLE) {
throw new IllegalStateException("Dont call getValueDouble() on a non DOUBLE ElementValue");
}
final ConstantDouble d = (ConstantDouble) super.getConstantPool().getConstant(getIndex());
return d.getBytes();
}
public float getValueFloat() {
if (super.getType() != PRIMITIVE_FLOAT) {
throw new IllegalStateException("Dont call getValueFloat() on a non FLOAT ElementValue");
}
final ConstantFloat f = (ConstantFloat) super.getConstantPool().getConstant(getIndex());
return f.getBytes();
}
public int getValueInt() {
if (super.getType() != PRIMITIVE_INT) {
throw new IllegalStateException("Dont call getValueInt() on a non INT ElementValue");
}
return super.getConstantPool().getConstantInteger(getIndex()).getBytes();
}
public long getValueLong() {
if (super.getType() != PRIMITIVE_LONG) {
throw new IllegalStateException("Dont call getValueLong() on a non LONG ElementValue");
}
final ConstantLong j = (ConstantLong) super.getConstantPool().getConstant(getIndex());
return j.getBytes();
}
public short getValueShort() {
if (super.getType() != PRIMITIVE_SHORT) {
throw new IllegalStateException("Dont call getValueShort() on a non SHORT ElementValue");
}
final ConstantInteger s = (ConstantInteger) super.getConstantPool().getConstant(getIndex());
return (short) s.getBytes();
}
public String getValueString() {
if (super.getType() != STRING) {
throw new IllegalStateException("Dont call getValueString() on a non STRING ElementValue");
}
return super.getConstantPool().getConstantUtf8(getIndex()).getBytes();
}
public void setIndex(final int index) {
this.index = index;
}
// Whatever kind of value it is, return it as a string
@Override
public String stringifyValue() {
final ConstantPool cpool = super.getConstantPool();
final int type = super.getType();
switch (type) {
case PRIMITIVE_INT:
return Integer.toString(cpool.getConstantInteger(getIndex()).getBytes());
case PRIMITIVE_LONG:
final ConstantLong j = cpool.getConstant(getIndex(), Const.CONSTANT_Long, ConstantLong.class);
return Long.toString(j.getBytes());
case PRIMITIVE_DOUBLE:
final ConstantDouble d = cpool.getConstant(getIndex(), Const.CONSTANT_Double, ConstantDouble.class);
return Double.toString(d.getBytes());
case PRIMITIVE_FLOAT:
final ConstantFloat f = cpool.getConstant(getIndex(), Const.CONSTANT_Float, ConstantFloat.class);
return Float.toString(f.getBytes());
case PRIMITIVE_SHORT:
final ConstantInteger s = cpool.getConstantInteger(getIndex());
return Integer.toString(s.getBytes());
case PRIMITIVE_BYTE:
final ConstantInteger b = cpool.getConstantInteger(getIndex());
return Integer.toString(b.getBytes());
case PRIMITIVE_CHAR:
final ConstantInteger ch = cpool.getConstantInteger(getIndex());
return String.valueOf((char) ch.getBytes());
case PRIMITIVE_BOOLEAN:
final ConstantInteger bo = cpool.getConstantInteger(getIndex());
if (bo.getBytes() == 0) {
return "false";
}
return "true";
case STRING:
return cpool.getConstantUtf8(getIndex()).getBytes();
default:
throw new IllegalStateException("SimpleElementValue class does not know how to stringify type " + type);
}
}
@Override
public String toString() {
return stringifyValue();
}
}

View File

@@ -0,0 +1,133 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from <em>Attribute</em> and represents a reference to the source file of this class. At most
* one SourceFile attribute should appear per classfile. The intention of this class is that it is instantiated from the
* <em>Attribute.readAttribute()</em> method.
*
* @see Attribute
*/
public final class SourceFile extends Attribute {
private int sourceFileIndex;
/**
* Construct object from input stream.
*
* @param nameIndex Index in constant pool to CONSTANT_Utf8
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
SourceFile(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, input.readUnsignedShort(), constantPool);
}
/**
* @param nameIndex Index in constant pool to CONSTANT_Utf8, which should represent the string "SourceFile".
* @param length Content length in bytes, the value should be 2.
* @param constantPool The constant pool that this attribute is associated with.
* @param sourceFileIndex Index in constant pool to CONSTANT_Utf8. This string will be interpreted as the name of the
* file from which this class was compiled. It will not be interpreted as indicating the name of the directory
* contqining the file or an absolute path; this information has to be supplied the consumer of this attribute -
* in many cases, the JVM.
*/
public SourceFile(final int nameIndex, final int length, final int sourceFileIndex, final ConstantPool constantPool) {
super(Const.ATTR_SOURCE_FILE, nameIndex, Args.require(length, 2, "SourceFile length attribute"), constantPool);
this.sourceFileIndex = Args.requireU2(sourceFileIndex, 0, constantPool.getLength(), "SourceFile source file index");
}
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
* physical copy.
*
* @param c Source to copy.
*/
public SourceFile(final SourceFile c) {
this(c.getNameIndex(), c.getLength(), c.getSourceFileIndex(), c.getConstantPool());
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitSourceFile(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
return (Attribute) clone();
}
/**
* Dump source file attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(sourceFileIndex);
}
/**
* @return Index in constant pool of source file name.
*/
public int getSourceFileIndex() {
return sourceFileIndex;
}
/**
* @return Source file name.
*/
public String getSourceFileName() {
return super.getConstantPool().getConstantUtf8(sourceFileIndex).getBytes();
}
/**
* @param sourceFileIndex
*/
public void setSourceFileIndex(final int sourceFileIndex) {
this.sourceFileIndex = sourceFileIndex;
}
/**
* @return String representation
*/
@Override
public String toString() {
return "SourceFile: " + getSourceFileName();
}
}

View File

@@ -0,0 +1,161 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
/**
* This class represents a stack map attribute used for preverification of Java classes for the
* <a href="http://java.sun.com/j2me/"> Java 2 Micro Edition</a> (J2ME). This attribute is used by the
* <a href="http://java.sun.com/products/cldc/">KVM</a> and contained within the Code attribute of a method. See CLDC
* specification <20>5.3.1.2
*
* <pre>
* StackMapTable_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 number_of_entries;
* stack_map_frame entries[number_of_entries];
* }
* </pre>
*
* @see Code
* @see StackMapEntry
* @see StackMapType
*/
public final class StackMap extends Attribute {
private StackMapEntry[] table; // Table of stack map entries
/**
* Construct object from input stream.
*
* @param nameIndex Index of name
* @param length Content length in bytes
* @param dataInput Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
StackMap(final int nameIndex, final int length, final DataInput dataInput, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, (StackMapEntry[]) null, constantPool);
final int mapLength = dataInput.readUnsignedShort();
table = new StackMapEntry[mapLength];
for (int i = 0; i < mapLength; i++) {
table[i] = new StackMapEntry(dataInput, constantPool);
}
}
/*
* @param nameIndex Index of name
*
* @param length Content length in bytes
*
* @param map Table of stack map entries
*
* @param constantPool Array of constants
*/
public StackMap(final int nameIndex, final int length, final StackMapEntry[] table, final ConstantPool constantPool) {
super(Const.ATTR_STACK_MAP, nameIndex, length, constantPool);
this.table = table != null ? table : StackMapEntry.EMPTY_ARRAY;
Args.requireU2(this.table.length, "table.length");
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitStackMap(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final StackMap c = (StackMap) clone();
c.table = new StackMapEntry[table.length];
Arrays.setAll(c.table, i -> table[i].copy());
c.setConstantPool(constantPool);
return c;
}
/**
* Dump stack map table attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
file.writeShort(table.length);
for (final StackMapEntry entry : table) {
entry.dump(file);
}
}
public int getMapLength() {
return table.length;
}
/**
* @return Array of stack map entries
*/
public StackMapEntry[] getStackMap() {
return table;
}
/**
* @param table Array of stack map entries
*/
public void setStackMap(final StackMapEntry[] table) {
this.table = table != null ? table : StackMapEntry.EMPTY_ARRAY;
int len = 2; // Length of 'number_of_entries' field prior to the array of stack maps
for (final StackMapEntry element : this.table) {
len += element.getMapEntrySize();
}
setLength(len);
}
/**
* @return String representation.
*/
@Override
public String toString() {
final StringBuilder buf = new StringBuilder("StackMap(");
int runningOffset = -1; // no +1 on first entry
for (int i = 0; i < table.length; i++) {
runningOffset = table[i].getByteCodeOffset() + runningOffset + 1;
buf.append(String.format("%n@%03d %s", runningOffset, table[i]));
if (i < table.length - 1) {
buf.append(", ");
}
}
buf.append(')');
return buf.toString();
}
}

View File

@@ -0,0 +1,415 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
/**
* This class represents a stack map entry recording the types of local variables and the of stack items at a given
* byte code offset. See CLDC specification 5.3.1.2.
*
* See also https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.4
*
* <pre>
* union stack_map_frame {
* same_frame;
* same_locals_1_stack_item_frame;
* same_locals_1_stack_item_frame_extended;
* chop_frame;
* same_frame_extended;
* append_frame;
* full_frame;
* }
* </pre>
* @see StackMap
* @see StackMapType
*/
public final class StackMapEntry implements Node, Cloneable {
static final StackMapEntry[] EMPTY_ARRAY = {};
private int frameType;
private int byteCodeOffset;
private StackMapType[] typesOfLocals;
private StackMapType[] typesOfStackItems;
private ConstantPool constantPool;
/**
* Construct object from input stream.
*
* @param dataInput Input stream
* @throws IOException if an I/O error occurs.
*/
StackMapEntry(final DataInput dataInput, final ConstantPool constantPool) throws IOException {
this(dataInput.readByte() & 0xFF, -1, null, null, constantPool);
if (frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX) {
byteCodeOffset = frameType - Const.SAME_FRAME;
} else if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) {
byteCodeOffset = frameType - Const.SAME_LOCALS_1_STACK_ITEM_FRAME;
typesOfStackItems = new StackMapType[] { new StackMapType(dataInput, constantPool) };
} else if (frameType == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
byteCodeOffset = dataInput.readUnsignedShort();
typesOfStackItems = new StackMapType[] { new StackMapType(dataInput, constantPool) };
} else if (frameType >= Const.CHOP_FRAME && frameType <= Const.CHOP_FRAME_MAX) {
byteCodeOffset = dataInput.readUnsignedShort();
} else if (frameType == Const.SAME_FRAME_EXTENDED) {
byteCodeOffset = dataInput.readUnsignedShort();
} else if (frameType >= Const.APPEND_FRAME && frameType <= Const.APPEND_FRAME_MAX) {
byteCodeOffset = dataInput.readUnsignedShort();
final int numberOfLocals = frameType - 251;
typesOfLocals = new StackMapType[numberOfLocals];
for (int i = 0; i < numberOfLocals; i++) {
typesOfLocals[i] = new StackMapType(dataInput, constantPool);
}
} else if (frameType == Const.FULL_FRAME) {
byteCodeOffset = dataInput.readUnsignedShort();
final int numberOfLocals = dataInput.readUnsignedShort();
typesOfLocals = new StackMapType[numberOfLocals];
for (int i = 0; i < numberOfLocals; i++) {
typesOfLocals[i] = new StackMapType(dataInput, constantPool);
}
final int numberOfStackItems = dataInput.readUnsignedShort();
typesOfStackItems = new StackMapType[numberOfStackItems];
for (int i = 0; i < numberOfStackItems; i++) {
typesOfStackItems[i] = new StackMapType(dataInput, constantPool);
}
} else {
/* Can't happen */
throw new ClassFormatException("Invalid frame type found while parsing stack map table: " + frameType);
}
}
/**
* DO NOT USE
*
* @param byteCodeOffset
* @param numberOfLocals NOT USED
* @param typesOfLocals array of {@link StackMapType}s of locals
* @param numberOfStackItems NOT USED
* @param typesOfStackItems array ot {@link StackMapType}s of stack items
* @param constantPool the constant pool
* @deprecated Since 6.0, use {@link #StackMapEntry(int, int, StackMapType[], StackMapType[], ConstantPool)} instead
*/
@java.lang.Deprecated
public StackMapEntry(final int byteCodeOffset, final int numberOfLocals, final StackMapType[] typesOfLocals, final int numberOfStackItems,
final StackMapType[] typesOfStackItems, final ConstantPool constantPool) {
this.byteCodeOffset = byteCodeOffset;
this.typesOfLocals = typesOfLocals != null ? typesOfLocals : StackMapType.EMPTY_ARRAY;
this.typesOfStackItems = typesOfStackItems != null ? typesOfStackItems : StackMapType.EMPTY_ARRAY;
this.constantPool = constantPool;
if (numberOfLocals < 0) {
throw new IllegalArgumentException("numberOfLocals < 0");
}
if (numberOfStackItems < 0) {
throw new IllegalArgumentException("numberOfStackItems < 0");
}
}
/**
* Create an instance
*
* @param tag the frameType to use
* @param byteCodeOffset
* @param typesOfLocals array of {@link StackMapType}s of locals
* @param typesOfStackItems array ot {@link StackMapType}s of stack items
* @param constantPool the constant pool
*/
public StackMapEntry(final int tag, final int byteCodeOffset, final StackMapType[] typesOfLocals, final StackMapType[] typesOfStackItems,
final ConstantPool constantPool) {
this.frameType = tag;
this.byteCodeOffset = byteCodeOffset;
this.typesOfLocals = typesOfLocals != null ? typesOfLocals : StackMapType.EMPTY_ARRAY;
this.typesOfStackItems = typesOfStackItems != null ? typesOfStackItems : StackMapType.EMPTY_ARRAY;
this.constantPool = constantPool;
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitStackMapEntry(this);
}
/**
* @return deep copy of this object
*/
public StackMapEntry copy() {
StackMapEntry e;
try {
e = (StackMapEntry) clone();
} catch (final CloneNotSupportedException ex) {
throw new Error("Clone Not Supported");
}
e.typesOfLocals = new StackMapType[typesOfLocals.length];
Arrays.setAll(e.typesOfLocals, i -> typesOfLocals[i].copy());
e.typesOfStackItems = new StackMapType[typesOfStackItems.length];
Arrays.setAll(e.typesOfStackItems, i -> typesOfStackItems[i].copy());
return e;
}
/**
* Dump stack map entry
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
public void dump(final DataOutputStream file) throws IOException {
file.write(frameType);
if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) {
typesOfStackItems[0].dump(file);
} else if (frameType == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
file.writeShort(byteCodeOffset);
typesOfStackItems[0].dump(file);
} else if (frameType >= Const.CHOP_FRAME && frameType <= Const.CHOP_FRAME_MAX) {
file.writeShort(byteCodeOffset);
} else if (frameType == Const.SAME_FRAME_EXTENDED) {
file.writeShort(byteCodeOffset);
} else if (frameType >= Const.APPEND_FRAME && frameType <= Const.APPEND_FRAME_MAX) {
file.writeShort(byteCodeOffset);
for (final StackMapType type : typesOfLocals) {
type.dump(file);
}
} else if (frameType == Const.FULL_FRAME) {
file.writeShort(byteCodeOffset);
file.writeShort(typesOfLocals.length);
for (final StackMapType type : typesOfLocals) {
type.dump(file);
}
file.writeShort(typesOfStackItems.length);
for (final StackMapType type : typesOfStackItems) {
type.dump(file);
}
} else if (!(frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX)) {
/* Can't happen */
throw new ClassFormatException("Invalid Stack map table tag: " + frameType);
}
}
public int getByteCodeOffset() {
return byteCodeOffset;
}
/**
* @return Constant pool used by this object.
*/
public ConstantPool getConstantPool() {
return constantPool;
}
public int getFrameType() {
return frameType;
}
/**
* Calculate stack map entry size
*
*/
int getMapEntrySize() {
if (frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX) {
return 1;
}
if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) {
return 1 + (typesOfStackItems[0].hasIndex() ? 3 : 1);
}
if (frameType == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
return 3 + (typesOfStackItems[0].hasIndex() ? 3 : 1);
}
if (frameType >= Const.CHOP_FRAME && frameType <= Const.CHOP_FRAME_MAX || frameType == Const.SAME_FRAME_EXTENDED) {
return 3;
}
if (frameType >= Const.APPEND_FRAME && frameType <= Const.APPEND_FRAME_MAX) {
int len = 3;
for (final StackMapType typesOfLocal : typesOfLocals) {
len += typesOfLocal.hasIndex() ? 3 : 1;
}
return len;
}
if (frameType != Const.FULL_FRAME) {
throw new IllegalStateException("Invalid StackMap frameType: " + frameType);
}
int len = 7;
for (final StackMapType typesOfLocal : typesOfLocals) {
len += typesOfLocal.hasIndex() ? 3 : 1;
}
for (final StackMapType typesOfStackItem : typesOfStackItems) {
len += typesOfStackItem.hasIndex() ? 3 : 1;
}
return len;
}
public int getNumberOfLocals() {
return typesOfLocals.length;
}
public int getNumberOfStackItems() {
return typesOfStackItems.length;
}
public StackMapType[] getTypesOfLocals() {
return typesOfLocals;
}
public StackMapType[] getTypesOfStackItems() {
return typesOfStackItems;
}
private boolean invalidFrameType(final int f) {
// @formatter:off
return f != Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED
&& !(f >= Const.CHOP_FRAME && f <= Const.CHOP_FRAME_MAX)
&& f != Const.SAME_FRAME_EXTENDED
&& !(f >= Const.APPEND_FRAME && f <= Const.APPEND_FRAME_MAX)
&& f != Const.FULL_FRAME;
// @formatter:on
}
public void setByteCodeOffset(final int newOffset) {
if (newOffset < 0 || newOffset > 32767) {
throw new IllegalArgumentException("Invalid StackMap offset: " + newOffset);
}
if (frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX) {
if (newOffset > Const.SAME_FRAME_MAX) {
frameType = Const.SAME_FRAME_EXTENDED;
} else {
frameType = newOffset;
}
} else if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) {
if (newOffset > Const.SAME_FRAME_MAX) {
frameType = Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
} else {
frameType = Const.SAME_LOCALS_1_STACK_ITEM_FRAME + newOffset;
}
} else if (invalidFrameType(frameType)) {
throw new IllegalStateException("Invalid StackMap frameType: " + frameType);
}
byteCodeOffset = newOffset;
}
/**
* @param constantPool Constant pool to be used for this object.
*/
public void setConstantPool(final ConstantPool constantPool) {
this.constantPool = constantPool;
}
public void setFrameType(final int ft) {
if (ft >= Const.SAME_FRAME && ft <= Const.SAME_FRAME_MAX) {
byteCodeOffset = ft - Const.SAME_FRAME;
} else if (ft >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && ft <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) {
byteCodeOffset = ft - Const.SAME_LOCALS_1_STACK_ITEM_FRAME;
} else if (invalidFrameType(ft)) {
throw new IllegalArgumentException("Invalid StackMap frameType");
}
frameType = ft;
}
/**
*
* @deprecated since 6.0
*/
@java.lang.Deprecated
public void setNumberOfLocals(final int n) { // TODO unused
}
/**
*
* @deprecated since 6.0
*/
@java.lang.Deprecated
public void setNumberOfStackItems(final int n) { // TODO unused
}
public void setTypesOfLocals(final StackMapType[] types) {
typesOfLocals = types != null ? types : StackMapType.EMPTY_ARRAY;
}
public void setTypesOfStackItems(final StackMapType[] types) {
typesOfStackItems = types != null ? types : StackMapType.EMPTY_ARRAY;
}
/**
* @return String representation.
*/
@Override
public String toString() {
final StringBuilder buf = new StringBuilder(64);
buf.append("(");
if (frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX) {
buf.append("SAME");
} else if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) {
buf.append("SAME_LOCALS_1_STACK");
} else if (frameType == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
buf.append("SAME_LOCALS_1_STACK_EXTENDED");
} else if (frameType >= Const.CHOP_FRAME && frameType <= Const.CHOP_FRAME_MAX) {
buf.append("CHOP ").append(String.valueOf(251 - frameType));
} else if (frameType == Const.SAME_FRAME_EXTENDED) {
buf.append("SAME_EXTENDED");
} else if (frameType >= Const.APPEND_FRAME && frameType <= Const.APPEND_FRAME_MAX) {
buf.append("APPEND ").append(String.valueOf(frameType - 251));
} else if (frameType == Const.FULL_FRAME) {
buf.append("FULL");
} else {
buf.append("UNKNOWN (").append(frameType).append(")");
}
buf.append(", offset delta=").append(byteCodeOffset);
if (typesOfLocals.length > 0) {
buf.append(", locals={");
for (int i = 0; i < typesOfLocals.length; i++) {
buf.append(typesOfLocals[i]);
if (i < typesOfLocals.length - 1) {
buf.append(", ");
}
}
buf.append("}");
}
if (typesOfStackItems.length > 0) {
buf.append(", stack items={");
for (int i = 0; i < typesOfStackItems.length; i++) {
buf.append(typesOfStackItems[i]);
if (i < typesOfStackItems.length - 1) {
buf.append(", ");
}
}
buf.append("}");
}
buf.append(")");
return buf.toString();
}
/**
* Update the distance (as an offset delta) from this StackMap entry to the next. Note that this might cause the
* frame type to change. Note also that delta may be negative.
*
* @param delta offset delta
*/
public void updateByteCodeOffset(final int delta) {
setByteCodeOffset(byteCodeOffset + delta);
}
}

View File

@@ -0,0 +1,157 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class represents the type of a local variable or item on stack used in the StackMap entries.
*
* @see StackMapEntry
* @see StackMap
* @see Const
*/
public final class StackMapType implements Cloneable {
public static final StackMapType[] EMPTY_ARRAY = {}; // must be public because BCELifier code generator writes calls to it
private byte type;
private int index = -1; // Index to CONSTANT_Class or offset
private ConstantPool constantPool;
/**
* @param type type tag as defined in the Constants interface
* @param index index to constant pool, or byte code offset
*/
public StackMapType(final byte type, final int index, final ConstantPool constantPool) {
this.type = checkType(type);
this.index = index;
this.constantPool = constantPool;
}
/**
* Construct object from file stream.
*
* @param file Input stream
* @throws IOException if an I/O error occurs.
*/
StackMapType(final DataInput file, final ConstantPool constantPool) throws IOException {
this(file.readByte(), -1, constantPool);
if (hasIndex()) {
this.index = file.readUnsignedShort();
}
this.constantPool = constantPool;
}
private byte checkType(final byte type) {
if (type < Const.ITEM_Bogus || type > Const.ITEM_NewObject) {
throw new ClassFormatException("Illegal type for StackMapType: " + type);
}
return type;
}
/**
* @return deep copy of this object
*/
public StackMapType copy() {
try {
return (StackMapType) clone();
} catch (final CloneNotSupportedException e) {
// TODO should this throw?
}
return null;
}
/**
* Dump type entries to file.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
public void dump(final DataOutputStream file) throws IOException {
file.writeByte(type);
if (hasIndex()) {
file.writeShort(getIndex());
}
}
/**
* @return Constant pool used by this object.
*/
public ConstantPool getConstantPool() {
return constantPool;
}
/**
* @return index to constant pool if type == ITEM_Object, or offset in byte code, if type == ITEM_NewObject, and -1
* otherwise
*/
public int getIndex() {
return index;
}
public byte getType() {
return type;
}
/**
* @return true, if type is either ITEM_Object or ITEM_NewObject
*/
public boolean hasIndex() {
return type == Const.ITEM_Object || type == Const.ITEM_NewObject;
}
private String printIndex() {
if (type == Const.ITEM_Object) {
if (index < 0) {
return ", class=<unknown>";
}
return ", class=" + constantPool.constantToString(index, Const.CONSTANT_Class);
}
if (type == Const.ITEM_NewObject) {
return ", offset=" + index;
}
return "";
}
/**
* @param constantPool Constant pool to be used for this object.
*/
public void setConstantPool(final ConstantPool constantPool) {
this.constantPool = constantPool;
}
public void setIndex(final int index) {
this.index = index;
}
public void setType(final byte type) {
this.type = checkType(type);
}
/**
* @return String representation
*/
@Override
public String toString() {
return "(type=" + Const.getItemName(type) + printIndex() + ")";
}
}

View File

@@ -0,0 +1,140 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.util.Args;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is derived from <em>Attribute</em> and declares this class as 'synthetic', i.e., it needs special
* handling. The JVM specification states "A class member that does not appear in the source code must be marked using a
* Synthetic attribute." It may appear in the ClassFile attribute table, a field_info table or a method_info table. This
* class is intended to be instantiated from the <em>Attribute.readAttribute()</em> method.
*
* @see Attribute
*/
public final class Synthetic extends Attribute {
private byte[] bytes;
/**
* @param nameIndex Index in constant pool to CONSTANT_Utf8, which should represent the string "Synthetic".
* @param length Content length in bytes - should be zero.
* @param bytes Attribute contents
* @param constantPool The constant pool this attribute is associated with.
*/
public Synthetic(final int nameIndex, final int length, final byte[] bytes, final ConstantPool constantPool) {
super(Const.ATTR_SYNTHETIC, nameIndex, Args.require0(length, "Synthetic attribute length"), constantPool);
this.bytes = bytes;
}
/**
* Construct object from input stream.
*
* @param nameIndex Index in constant pool to CONSTANT_Utf8
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
Synthetic(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, (byte[]) null, constantPool);
if (length > 0) {
bytes = new byte[length];
input.readFully(bytes);
println("Synthetic attribute with length > 0");
}
}
/**
* Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a
* physical copy.
*
* @param c Source to copy.
*/
public Synthetic(final Synthetic c) {
this(c.getNameIndex(), c.getLength(), c.getBytes(), c.getConstantPool());
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitSynthetic(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final Synthetic c = (Synthetic) clone();
if (bytes != null) {
c.bytes = bytes.clone();
}
c.setConstantPool(constantPool);
return c;
}
/**
* Dump source file attribute to file stream in binary format.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
if (super.getLength() > 0) {
file.write(bytes, 0, super.getLength());
}
}
/**
* @return data bytes.
*/
public byte[] getBytes() {
return bytes;
}
/**
* @param bytes
*/
public void setBytes(final byte[] bytes) {
this.bytes = bytes;
}
/**
* @return String representation.
*/
@Override
public String toString() {
final StringBuilder buf = new StringBuilder("Synthetic");
if (super.getLength() > 0) {
buf.append(" ").append(Utility.toHexString(bytes));
}
return buf.toString();
}
}

View File

@@ -0,0 +1,159 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
import haidnor.jvm.bcel.Const;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
/**
* This class represents a reference to an unknown (i.e., application-specific) attribute of a class. It is instantiated
* from the {@link Attribute#readAttribute(DataInput, ConstantPool)} method. Applications that need to read in
* application-specific attributes should create an {@link UnknownAttributeReader} implementation and attach it via
* {@link Attribute#addAttributeReader(String, UnknownAttributeReader)}.
*
* @see Attribute
* @see UnknownAttributeReader
*/
public final class Unknown extends Attribute {
private byte[] bytes;
private final String name;
/**
* Constructs a new instance for a non-standard attribute.
*
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param bytes Attribute contents
* @param constantPool Array of constants
*/
public Unknown(final int nameIndex, final int length, final byte[] bytes, final ConstantPool constantPool) {
super(Const.ATTR_UNKNOWN, nameIndex, length, constantPool);
this.bytes = bytes;
this.name = constantPool.getConstantUtf8(nameIndex).getBytes();
}
/**
* Constructs a new instance from an input stream.
*
* @param nameIndex Index in constant pool
* @param length Content length in bytes
* @param input Input stream
* @param constantPool Array of constants
* @throws IOException if an I/O error occurs.
*/
Unknown(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, (byte[]) null, constantPool);
if (length > 0) {
bytes = new byte[length];
input.readFully(bytes);
}
}
/**
* Constructs a new instance from another instance. Note that both objects use the same references (shallow copy). Use clone() for a physical copy.
*
* @param unknown Source.
*/
public Unknown(final Unknown unknown) {
this(unknown.getNameIndex(), unknown.getLength(), unknown.getBytes(), unknown.getConstantPool());
}
/**
* Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
* I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitUnknown(this);
}
/**
* @return deep copy of this attribute
*/
@Override
public Attribute copy(final ConstantPool constantPool) {
final Unknown c = (Unknown) clone();
if (bytes != null) {
c.bytes = bytes.clone();
}
c.setConstantPool(constantPool);
return c;
}
/**
* Dumps unknown bytes to file stream.
*
* @param file Output file stream
* @throws IOException if an I/O error occurs.
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
super.dump(file);
if (super.getLength() > 0) {
file.write(bytes, 0, super.getLength());
}
}
/**
* @return data bytes.
*/
public byte[] getBytes() {
return bytes;
}
/**
* @return name of attribute.
*/
@Override
public String getName() {
return name;
}
/**
* @param bytes the bytes to set
*/
public void setBytes(final byte[] bytes) {
this.bytes = bytes;
}
/**
* @return String representation.
*/
@Override
public String toString() {
if (super.getLength() == 0 || bytes == null) {
return "(Unknown attribute " + name + ")";
}
String hex;
final int limit = 10;
if (super.getLength() > limit) {
final byte[] tmp = Arrays.copyOf(bytes, limit);
hex = Utility.toHexString(tmp) + "... (truncated)";
} else {
hex = Utility.toHexString(bytes);
}
return "(Unknown attribute " + name + ": " + hex + ")";
}
}

View File

@@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
/**
* Unknown (non-standard) attributes may be read via user-defined factory objects that can be registered with the
* Attribute.addAttributeReader method. These factory objects should implement this interface.
*
* @see Attribute
* @since 6.0
*/
public interface UnknownAttributeReader {
/**
* When this attribute reader is added via the static method Attribute.addAttributeReader, an attribute name is
* associated with it. As the class file parser parses attributes, it will call various AttributeReaders based on the
* name of the attributes it is constructing.
*
* @param nameIndex An index into the constant pool, indexing a ConstantUtf8 that represents the name of the attribute.
* @param length The length of the data contained in the attribute. This is written into the constant pool and should
* agree with what the factory expects the length to be.
* @param file This is the data input that the factory needs to read its data from.
* @param constantPool This is the constant pool associated with the Attribute that we are constructing.
*
* @return The user-defined AttributeReader should take this data and use it to construct an attribute. In the case of
* errors, a null can be returned which will cause the parsing of the class file to fail.
*
* @see Attribute#addAttributeReader(String, UnknownAttributeReader)
*/
Attribute createAttribute(int nameIndex, int length, java.io.DataInput file, ConstantPool constantPool);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,231 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.classfile;
/**
* Interface to make use of the Visitor pattern programming style. I.e. a class that implements this interface can
* traverse the contents of a Java class just by calling the 'accept' method which all classes have.
*/
public interface Visitor {
/**
* @since 6.0
*/
void visitAnnotation(Annotations obj);
/**
* @since 6.0
*/
void visitAnnotationDefault(AnnotationDefault obj);
/**
* @since 6.0
*/
void visitAnnotationEntry(AnnotationEntry obj);
/**
* @since 6.0
*/
void visitBootstrapMethods(BootstrapMethods obj);
void visitCode(Code obj);
void visitCodeException(CodeException obj);
void visitConstantClass(ConstantClass obj);
void visitConstantDouble(ConstantDouble obj);
/**
* @since 6.3
*/
default void visitConstantDynamic(final ConstantDynamic constantDynamic) {
// empty
}
void visitConstantFieldref(ConstantFieldref obj);
void visitConstantFloat(ConstantFloat obj);
void visitConstantInteger(ConstantInteger obj);
void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj);
void visitConstantInvokeDynamic(ConstantInvokeDynamic obj);
void visitConstantLong(ConstantLong obj);
/**
* @since 6.0
*/
void visitConstantMethodHandle(ConstantMethodHandle obj);
void visitConstantMethodref(ConstantMethodref obj);
/**
* @since 6.0
*/
void visitConstantMethodType(ConstantMethodType obj);
/**
* @since 6.1
*/
void visitConstantModule(ConstantModule constantModule);
void visitConstantNameAndType(ConstantNameAndType obj);
/**
* @since 6.1
*/
void visitConstantPackage(ConstantPackage constantPackage);
void visitConstantPool(ConstantPool obj);
void visitConstantString(ConstantString obj);
void visitConstantUtf8(ConstantUtf8 obj);
void visitConstantValue(ConstantValue obj);
void visitDeprecated(Deprecated obj);
/**
* @since 6.0
*/
void visitEnclosingMethod(EnclosingMethod obj);
void visitExceptionTable(ExceptionTable obj);
void visitField(JavaField obj);
void visitInnerClass(InnerClass obj);
void visitInnerClasses(InnerClasses obj);
void visitJavaClass(JavaClass obj);
void visitLineNumber(LineNumber obj);
void visitLineNumberTable(LineNumberTable obj);
void visitLocalVariable(LocalVariable obj);
void visitLocalVariableTable(LocalVariableTable obj);
/**
* @since 6.0
*/
void visitLocalVariableTypeTable(LocalVariableTypeTable obj);
void visitMethod(JavaMethod obj);
/**
* @since 6.4.0
*/
default void visitMethodParameter(final MethodParameter obj) {
// empty
}
/**
* @since 6.0
*/
void visitMethodParameters(MethodParameters obj);
/**
* @since 6.4.0
*/
default void visitModule(final Module constantModule) {
// empty
}
/**
* @since 6.4.0
*/
default void visitModuleExports(final ModuleExports constantModule) {
// empty
}
/**
* @since 6.4.0
*/
default void visitModuleMainClass(final ModuleMainClass obj) {
// empty
}
/**
* @since 6.4.0
*/
default void visitModuleOpens(final ModuleOpens constantModule) {
// empty
}
/**
* @since 6.4.0
*/
default void visitModulePackages(final ModulePackages constantModule) {
// empty
}
/**
* @since 6.4.0
*/
default void visitModuleProvides(final ModuleProvides constantModule) {
// empty
}
/**
* @since 6.4.0
*/
default void visitModuleRequires(final ModuleRequires constantModule) {
// empty
}
/**
* @since 6.4.0
*/
default void visitNestHost(final NestHost obj) {
// empty
}
/**
* @since 6.4.0
*/
default void visitNestMembers(final NestMembers obj) {
// empty
}
/**
* @since 6.0
*/
void visitParameterAnnotation(ParameterAnnotations obj);
/**
* @since 6.0
*/
void visitParameterAnnotationEntry(ParameterAnnotationEntry obj);
void visitSignature(Signature obj);
void visitSourceFile(SourceFile obj);
void visitStackMap(StackMap obj);
void visitStackMapEntry(StackMapEntry obj);
void visitSynthetic(Synthetic obj);
void visitUnknown(Unknown obj);
}

View File

@@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.generic;
import haidnor.jvm.bcel.Const;
/**
* AALOAD - Load reference from array
*
* <PRE>
* Stack: ..., arrayref, index -&gt; value
* </PRE>
*/
public class AALOAD extends ArrayInstruction implements StackProducer {
/**
* Load reference from array
*/
public AALOAD() {
super(Const.AALOAD);
}
/**
* Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call
* methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitStackProducer(this);
v.visitExceptionThrower(this);
v.visitTypedInstruction(this);
v.visitArrayInstruction(this);
v.visitAALOAD(this);
}
}

View File

@@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.generic;
import haidnor.jvm.bcel.Const;
/**
* AASTORE - Store into reference array
*
* <PRE>
* Stack: ..., arrayref, index, value -&gt; ...
* </PRE>
*/
public class AASTORE extends ArrayInstruction implements StackConsumer {
/**
* Store into reference array
*/
public AASTORE() {
super(Const.AASTORE);
}
/**
* Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call
* methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitStackConsumer(this);
v.visitExceptionThrower(this);
v.visitTypedInstruction(this);
v.visitArrayInstruction(this);
v.visitAASTORE(this);
}
}

View File

@@ -0,0 +1,58 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.generic;
import haidnor.jvm.bcel.Const;
/**
* ACONST_NULL - Push null reference
*
* <PRE>
* Stack: ... -&gt; ..., null
* </PRE>
*/
public class ACONST_NULL extends Instruction implements PushInstruction, TypedInstruction {
/**
* Push null reference
*/
public ACONST_NULL() {
super(Const.ACONST_NULL, (short) 1);
}
/**
* Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call
* methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitStackProducer(this);
v.visitPushInstruction(this);
v.visitTypedInstruction(this);
v.visitACONST_NULL(this);
}
/**
* @return Type.NULL
*/
@Override
public Type getType(final ConstantPoolGen cp) {
return Type.NULL;
}
}

View File

@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.generic;
import haidnor.jvm.bcel.Const;
/**
* ALOAD - Load reference from local variable
*
* <PRE>
* Stack: ... -&gt; ..., objectref
* </PRE>
*/
public class ALOAD extends LoadInstruction {
/**
* Empty constructor needed for Instruction.readInstruction. Not to be used otherwise.
*/
ALOAD() {
super(Const.ALOAD, Const.ALOAD_0);
}
/**
* Load reference from local variable
*
* @param n index of local variable
*/
public ALOAD(final int n) {
super(Const.ALOAD, Const.ALOAD_0, n);
}
/**
* Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call
* methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
super.accept(v);
v.visitALOAD(this);
}
}

View File

@@ -0,0 +1,71 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.generic;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.ExceptionConst;
/**
* ANEWARRAY - Create new array of references
*
* <PRE>
* Stack: ..., count -&gt; ..., arrayref
* </PRE>
*/
public class ANEWARRAY extends CPInstruction implements LoadClass, AllocationInstruction, ExceptionThrower, StackConsumer, StackProducer {
/**
* Empty constructor needed for Instruction.readInstruction. Not to be used otherwise.
*/
ANEWARRAY() {
}
public ANEWARRAY(final int index) {
super(Const.ANEWARRAY, index);
}
/**
* Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call
* methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitLoadClass(this);
v.visitAllocationInstruction(this);
v.visitExceptionThrower(this);
v.visitStackProducer(this);
v.visitTypedInstruction(this);
v.visitCPInstruction(this);
v.visitANEWARRAY(this);
}
@Override
public Class<?>[] getExceptions() {
return ExceptionConst.createExceptions(ExceptionConst.EXCS.EXCS_CLASS_AND_INTERFACE_RESOLUTION, ExceptionConst.NEGATIVE_ARRAY_SIZE_EXCEPTION);
}
@Override
public ObjectType getLoadClassType(final ConstantPoolGen cpg) {
Type t = getType(cpg);
if (t instanceof ArrayType) {
t = ((ArrayType) t).getBasicType();
}
return t instanceof ObjectType ? (ObjectType) t : null;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.generic;
import haidnor.jvm.bcel.Const;
/**
* ARETURN - Return reference from method
*
* <PRE>
* Stack: ..., objectref -&gt; &lt;empty&gt;
* </PRE>
*/
public class ARETURN extends ReturnInstruction {
/**
* Return reference from method
*/
public ARETURN() {
super(Const.ARETURN);
}
/**
* Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call
* methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitExceptionThrower(this);
v.visitTypedInstruction(this);
v.visitStackConsumer(this);
v.visitReturnInstruction(this);
v.visitARETURN(this);
}
}

View File

@@ -0,0 +1,58 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.generic;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.ExceptionConst;
/**
* ARRAYLENGTH - Get length of array
*
* <PRE>
* Stack: ..., arrayref -&gt; ..., length
* </PRE>
*/
public class ARRAYLENGTH extends Instruction implements ExceptionThrower, StackProducer, StackConsumer /* since 6.0 */ {
/**
* Get length of array
*/
public ARRAYLENGTH() {
super(Const.ARRAYLENGTH, (short) 1);
}
/**
* Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call
* methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitExceptionThrower(this);
v.visitStackProducer(this);
v.visitARRAYLENGTH(this);
}
/**
* @return exceptions this instruction may cause
*/
@Override
public Class<?>[] getExceptions() {
return new Class[] {ExceptionConst.NULL_POINTER_EXCEPTION};
}
}

View File

@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.generic;
import haidnor.jvm.bcel.Const;
/**
* ASTORE - Store reference into local variable
*
* <PRE>
* Stack ..., objectref -&gt; ...
* </PRE>
*/
public class ASTORE extends StoreInstruction {
/**
* Empty constructor needed for Instruction.readInstruction. Not to be used otherwise.
*/
ASTORE() {
super(Const.ASTORE, Const.ASTORE_0);
}
/**
* Store reference into local variable
*
* @param n index of local variable
*/
public ASTORE(final int n) {
super(Const.ASTORE, Const.ASTORE_0, n);
}
/**
* Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call
* methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
super.accept(v);
v.visitASTORE(this);
}
}

View File

@@ -0,0 +1,58 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.generic;
import haidnor.jvm.bcel.Const;
import haidnor.jvm.bcel.ExceptionConst;
/**
* ATHROW - Throw exception
*
* <PRE>
* Stack: ..., objectref -&gt; objectref
* </PRE>
*/
public class ATHROW extends Instruction implements UnconditionalBranch, ExceptionThrower {
/**
* Throw exception
*/
public ATHROW() {
super(Const.ATHROW, (short) 1);
}
/**
* Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call
* methods according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last.
*
* @param v Visitor object
*/
@Override
public void accept(final Visitor v) {
v.visitUnconditionalBranch(this);
v.visitExceptionThrower(this);
v.visitATHROW(this);
}
/**
* @return exceptions this instruction may cause
*/
@Override
public Class<?>[] getExceptions() {
return new Class[] {ExceptionConst.THROWABLE};
}
}

View File

@@ -0,0 +1,23 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.generic;
/**
* Denote family of instructions that allocates space in the heap.
*/
public interface AllocationInstruction {
}

View File

@@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 haidnor.jvm.bcel.generic;
import haidnor.jvm.bcel.classfile.AnnotationElementValue;
import haidnor.jvm.bcel.classfile.ElementValue;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* @since 6.0
*/
public class AnnotationElementValueGen extends ElementValueGen {
// For annotation element values, this is the annotation
private final AnnotationEntryGen a;
public AnnotationElementValueGen(final AnnotationElementValue value, final ConstantPoolGen cpool, final boolean copyPoolEntries) {
super(ANNOTATION, cpool);
a = new AnnotationEntryGen(value.getAnnotationEntry(), cpool, copyPoolEntries);
}
public AnnotationElementValueGen(final AnnotationEntryGen a, final ConstantPoolGen cpool) {
super(ANNOTATION, cpool);
this.a = a;
}
public AnnotationElementValueGen(final int type, final AnnotationEntryGen annotation, final ConstantPoolGen cpool) {
super(type, cpool);
if (type != ANNOTATION) {
throw new IllegalArgumentException("Only element values of type annotation can be built with this ctor - type specified: " + type);
}
this.a = annotation;
}
@Override
public void dump(final DataOutputStream dos) throws IOException {
dos.writeByte(super.getElementValueType()); // u1 type of value (ANNOTATION == '@')
a.dump(dos);
}
public AnnotationEntryGen getAnnotation() {
return a;
}
/**
* Return immutable variant of this AnnotationElementValueGen
*/
@Override
public ElementValue getElementValue() {
return new AnnotationElementValue(super.getElementValueType(), a.getAnnotation(), getConstantPool().getConstantPool());
}
@Override
public String stringifyValue() {
throw new UnsupportedOperationException("Not implemented yet");
}
}

Some files were not shown because too many files have changed in this diff Show More