Commit 083b1191 authored by Lubomir Bulej's avatar Lubomir Bulej

- Simplify implementation of classes derived from ShadowClass by moving as

  much common code to ShadowClass. Also avoid duplication information that
  can be easily extracted from ASM Type or ClassNode.
- Add test that test the basic functionality of the three concrete
  subclasses of ShadowClass.
- Modify build.xml to pick up the new tests.
parent d0a6a0e0
......@@ -803,6 +803,7 @@
<include name="**/junit/*Test.java"/>
<include name="**/disl/*Test.java"/>
<include name="**/disl/scope/*Test.java"/>
<include name="**/dislreserver/shadow/*Test.java"/>
</fileset>
</ac:else>
</ac:if>
......
......@@ -2,7 +2,7 @@ package ch.usi.dag.dislreserver.shadow;
import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.stream.Stream;
import org.objectweb.asm.Type;
......@@ -26,7 +26,7 @@ final class ArrayShadowClass extends ShadowClass {
__componentClass = componentClass;
}
//
//
public int getDimensionCount () {
return _type ().getDimensions ();
......@@ -80,13 +80,17 @@ final class ArrayShadowClass extends ShadowClass {
return false;
}
//
//
@Override
public int getModifiers () {
// Array classes are ABSTRACT and FINAL, privacy depends on the
// privacy of the component type. Until we get valid component type,
// we will make the array classes public.
//
// Array classes are ABSTRACT and FINAL, but the access modifier is
// derived from the component type. We make the array classes public
// until we have a valid component type.
//
// FIXME Return access modifier based on the component type.
//
return Modifier.ABSTRACT | Modifier.FINAL | Modifier.PUBLIC;
}
......@@ -115,70 +119,16 @@ final class ArrayShadowClass extends ShadowClass {
//
@Override
public FieldInfo getField (final String fieldName) throws NoSuchFieldException {
throw new NoSuchFieldException (getCanonicalName () + "." + fieldName);
}
@Override
public FieldInfo [] getFields () {
return new FieldInfo [0];
}
//
@Override
public FieldInfo getDeclaredField (final String fieldName)
throws NoSuchFieldException {
throw new NoSuchFieldException (getCanonicalName () + "." + fieldName);
}
@Override
public FieldInfo [] getDeclaredFields () {
return new FieldInfo [0];
}
//
@Override
public MethodInfo getMethod (final String methodName, final String [] argumentNames)
throws NoSuchMethodException {
for (final MethodInfo methodInfo : __superClass.getMethods ()) {
if (methodName.equals (methodInfo.getName ())
&& Arrays.equals (argumentNames, methodInfo.getParameterDescriptors ())
) {
return methodInfo;
}
}
throw new NoSuchMethodException (
getCanonicalName () + "." + methodName + _descriptorsToString (argumentNames)
);
}
@Override
public MethodInfo [] getMethods () {
return getSuperclass ().getMethods ();
}
//
@Override
public MethodInfo getDeclaredMethod (final String methodName,
final String [] argumentNames)
throws NoSuchMethodException {
throw new NoSuchMethodException (
getCanonicalName () + "." + methodName + _descriptorsToString (argumentNames)
);
protected Stream <FieldInfo> _declaredFields () {
// Array types have no declared fields.
return Stream.empty ();
}
@Override
public MethodInfo [] getDeclaredMethods () {
return new MethodInfo [0];
protected Stream <MethodInfo> _declaredMethods () {
// Array types have no declared methods.
return Stream.empty ();
}
}
......@@ -20,6 +20,39 @@ public final class MethodInfo {
//
@Override
public int hashCode () {
return
(this.getName ().hashCode () & 0xFFFF0000)
|
(this.getDescriptor ().hashCode () & 0x0000FFFF);
}
@Override
public boolean equals (final Object object) {
if (object instanceof MethodInfo) {
final MethodInfo other = (MethodInfo) object;
return
this.getName ().equals (other.getName ())
&&
this.getDescriptor ().equals (other.getDescriptor ());
}
return false;
}
//
boolean matches (final String name, final String [] paramDescriptors) {
return
this.getName ().equals (name)
&&
Arrays.equals (paramDescriptors, this.getParameterDescriptors ());
}
//
public String getName () {
return __methodNode.name;
}
......@@ -58,4 +91,14 @@ public final class MethodInfo {
return Modifier.isPublic (getModifiers ());
}
public boolean isConstructor () {
return "<init>".equals (__methodNode.name);
}
public boolean isClassInitializer () {
return "<clinit>".equals (__methodNode.name);
}
}
package ch.usi.dag.dislreserver.shadow;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.lang.reflect.Modifier;
import java.util.stream.Stream;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.MethodNode;
import ch.usi.dag.dislreserver.DiSLREServerFatalException;
final class ObjectShadowClass extends ShadowClass {
// TODO ! is this implementation of methods really working ??
private final ShadowClass __superClass;
private ClassNode __classNode;
private String __name;
private final ClassNode __classNode;
//
ObjectShadowClass (
final long netReference, final Type type,
final ShadowObject classLoader, final ShadowClass superClass,
final byte [] classCode
final ClassNode classNode
) {
super (netReference, type, classLoader);
__superClass = superClass;
if (classCode == null || classCode.length == 0) {
throw new DiSLREServerFatalException (
"Creating class info for "+ type + " with no code provided"
);
}
initializeClassInfo (classCode);
}
private List <MethodInfo> methods;
private List <MethodInfo> public_methods;
private List <FieldInfo> fields;
private List <FieldInfo> public_fields;
private List <String> innerclasses;
private void initializeClassInfo (final byte [] classCode) {
final ClassReader classReader = new ClassReader (classCode);
__classNode = new ClassNode (Opcodes.ASM4);
classReader.accept (__classNode, ClassReader.SKIP_DEBUG | ClassReader.EXPAND_FRAMES);
__name = __classNode.name.replace ('/', '.');
methods = new ArrayList <MethodInfo> (__classNode.methods.size ());
public_methods = new LinkedList <MethodInfo> ();
for (final MethodNode methodNode : __classNode.methods) {
final MethodInfo methodInfo = new MethodInfo (methodNode);
methods.add (methodInfo);
if (methodInfo.isPublic ()) {
public_methods.add (methodInfo);
}
}
fields = new ArrayList <FieldInfo> (__classNode.fields.size ());
public_fields = new LinkedList <FieldInfo> ();
for (final FieldNode fieldNode : __classNode.fields) {
final FieldInfo fieldInfo = new FieldInfo (fieldNode);
fields.add (fieldInfo);
if (fieldInfo.isPublic ()) {
public_fields.add (fieldInfo);
}
}
if (getSuperclass () != null) {
for (final MethodInfo methodInfo : getSuperclass ().getMethods ()) {
public_methods.add (methodInfo);
}
for (final FieldInfo fieldInfo : getSuperclass ().getFields ()) {
public_fields.add (fieldInfo);
}
}
innerclasses = new ArrayList <String> (__classNode.innerClasses.size ());
for (final InnerClassNode innerClassNode : __classNode.innerClasses) {
innerclasses.add (innerClassNode.name);
}
__classNode = classNode;
}
//
//
/**
* @see Class#isInstance(Object)
......@@ -132,14 +55,15 @@ final class ObjectShadowClass extends ShadowClass {
throw new UnsupportedOperationException ("not yet implemented");
}
//
//
@Override
public int getModifiers () {
return __classNode.access;
// Strip modifiers that are not valid for a class.
return __classNode.access & Modifier.classModifiers ();
}
//
//
/**
* @see Class#getSuperclass()
......@@ -158,109 +82,35 @@ final class ObjectShadowClass extends ShadowClass {
@Override
public String [] getInterfaceDescriptors () {
final List <String> interfaces = __classNode.interfaces;
return interfaces.toArray (new String [interfaces.size ()]);
}
//
@Override
public FieldInfo [] getFields () {
// to have "checked" array :(
return public_fields.toArray (new FieldInfo [0]);
return __typeDescriptors (__classNode.interfaces.stream ());
}
//
@Override
public FieldInfo getField (final String fieldName) throws NoSuchFieldException {
for (final FieldInfo fieldInfo : fields) {
if (fieldInfo.isPublic () && fieldInfo.getName ().equals (fieldName)) {
return fieldInfo;
}
}
if (getSuperclass () == null) {
throw new NoSuchFieldException (__name + "." + fieldName);
}
return getSuperclass ().getField (fieldName);
protected Stream <FieldInfo> _declaredFields () {
return __classNode.fields.stream ().map (FieldInfo::new);
}
@Override
public MethodInfo [] getMethods () {
// to have "checked" array :(
return public_methods.toArray (new MethodInfo [0]);
protected Stream <MethodInfo> _declaredMethods () {
return __classNode.methods.stream ().map (MethodInfo::new);
}
//
@Override
public MethodInfo getMethod (
final String methodName, final String [] argumentNames
) throws NoSuchMethodException {
for (final MethodInfo methodInfo : public_methods) {
if (methodName.equals (methodInfo.getName ()) &&
Arrays.equals (argumentNames, methodInfo.getParameterDescriptors ())
) {
return methodInfo;
}
}
throw new NoSuchMethodException (
__name + "." + methodName + _descriptorsToString (argumentNames)
public String [] getDeclaredClassDescriptors () {
return __typeDescriptors (
__classNode.innerClasses.stream ().map (icn -> icn.name)
);
}
//
@Override
public FieldInfo [] getDeclaredFields () {
return fields.toArray (new FieldInfo [0]);
}
@Override
public FieldInfo getDeclaredField (final String fieldName)
throws NoSuchFieldException {
for (final FieldInfo fieldInfo : fields) {
if (fieldInfo.getName ().equals (fieldName)) {
return fieldInfo;
}
}
throw new NoSuchFieldException (__name + "." + fieldName);
}
@Override
public MethodInfo [] getDeclaredMethods () {
return methods.toArray (new MethodInfo [methods.size ()]);
}
@Override
public MethodInfo getDeclaredMethod (
final String methodName, final String [] argumentNames
) throws NoSuchMethodException {
for (final MethodInfo methodInfo : methods) {
if (methodName.equals (methodInfo.getName ()) &&
Arrays.equals (argumentNames, methodInfo.getParameterDescriptors ())
) {
return methodInfo;
}
}
throw new NoSuchMethodException (
__name + "." + methodName + _descriptorsToString (argumentNames)
);
private static String [] __typeDescriptors (final Stream <String> names) {
return names.map (n -> Type.getObjectType (n).getDescriptor ()).toArray (String []::new);
}
//
@Override
public String [] getDeclaredClassDescriptors () {
return __classNode.innerClasses.stream ().unordered ()
.map (icn -> Type.getObjectType (icn.name).getDescriptor ())
.toArray (String []::new);
}
}
package ch.usi.dag.dislreserver.shadow;
import java.lang.reflect.Modifier;
import java.util.stream.Stream;
import org.objectweb.asm.Type;
......@@ -18,7 +19,9 @@ final class PrimitiveShadowClass extends ShadowClass {
@Override
public int getModifiers () {
//
// Primitive type classes are ABSTRACT, FINAL, and PUBLIC.
//
return Modifier.ABSTRACT | Modifier.FINAL | Modifier.PUBLIC;
}
......@@ -52,7 +55,7 @@ final class PrimitiveShadowClass extends ShadowClass {
return getCanonicalName ();
}
//
//
/**
* @see Class#getSuperclass()
......@@ -77,61 +80,16 @@ final class PrimitiveShadowClass extends ShadowClass {
//
@Override
public FieldInfo getField (final String fieldName) throws NoSuchFieldException {
throw new NoSuchFieldException (getCanonicalName () + "." + fieldName);
}
@Override
public FieldInfo [] getFields () {
return new FieldInfo [0];
}
//
@Override
public FieldInfo getDeclaredField (final String fieldName) throws NoSuchFieldException {
throw new NoSuchFieldException (getCanonicalName () + "." + fieldName);
}
@Override
public FieldInfo [] getDeclaredFields () {
return new FieldInfo [0];
}
//
@Override
public MethodInfo [] getMethods () {
return new MethodInfo [0];
}
@Override
public MethodInfo getMethod (
final String methodName, final String [] argumentNames
) throws NoSuchMethodException {
throw new NoSuchMethodException (
getCanonicalName () + "." + methodName + _descriptorsToString (argumentNames)
);
}
//
@Override
public MethodInfo [] getDeclaredMethods () {
return new MethodInfo [0];
protected Stream <FieldInfo> _declaredFields () {
// Primitive types have no declared fields.
return Stream.empty ();
}
@Override
public MethodInfo getDeclaredMethod (
final String methodName, final String [] argumentNames
) throws NoSuchMethodException {
throw new NoSuchMethodException (
getCanonicalName () + "." + methodName + _descriptorsToString (argumentNames)
);
protected Stream <MethodInfo> _declaredMethods () {
// Primitive types have no declared methods.
return Stream.empty ();
}
}
package ch.usi.dag.dislreserver.shadow;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Formatter;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
......@@ -14,6 +22,10 @@ public abstract class ShadowClass extends ShadowObject {
*/
private final Type __type;
/**
* The class loader that loaded the class represented by this shadow class.
* Will be {@code null} for primitive types (including {@code void}).
*/
private final ShadowObject __classLoader;
//
......@@ -44,16 +56,26 @@ public abstract class ShadowClass extends ShadowObject {
@Override
public boolean equals (final Object object) {
if (object instanceof ShadowClass) {
return __equals ((ShadowClass) object);
}
return false;
}
private boolean __equals (final ShadowClass other) {
//
// Two shadow classes are considered equal if they represent the
// same class and have been loaded by the same class loader.
// same type and have been loaded by the same class loader.
//
if (object instanceof ShadowClass) {
final ShadowClass that = (ShadowClass) object;
if (this.getName ().equals (that.getName ())) {
return this.getShadowClassLoader ().equals (
that.getShadowClassLoader ()
);
if (this.__type.equals (other.__type)) {
final ShadowObject thisClassLoader = this.getShadowClassLoader ();
final ShadowObject otherClassLoader = other.getShadowClassLoader ();
if (thisClassLoader != null) {
return thisClassLoader.equals (otherClassLoader);
} else {
return thisClassLoader == otherClassLoader;
}
}
......@@ -74,7 +96,7 @@ public abstract class ShadowClass extends ShadowObject {
public final ShadowObject getShadowClassLoader () {
//
// Should return null for primitive types or for classes
// loaded by the bootstrap classloader.
// loaded by the bootstrap class loader.
//
return __classLoader;
}
......@@ -87,7 +109,7 @@ public abstract class ShadowClass extends ShadowObject {
public String getSimpleName () {
return __simpleName (__type.getClassName ());
return __simpleName (getCanonicalName ());
}
......@@ -98,7 +120,7 @@ public abstract class ShadowClass extends ShadowObject {
public String getCanonicalName () {
return __type.getClassName ();
return __type.getClassName ().replace ('$', '.');
}
......@@ -186,51 +208,176 @@ public abstract class ShadowClass extends ShadowObject {
//
public abstract FieldInfo getField (String fieldName)
throws NoSuchFieldException;
public FieldInfo getDeclaredField (final String name) throws NoSuchFieldException {
// Look among declared fields and throw an exception if it's not there.
return __findField (name, _declaredFields ().unordered ()).orElseThrow (