/*
* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.annotate;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
import com.sun.max.lang.*;
import com.sun.max.program.*;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.runtime.*;
import com.sun.max.vm.type.*;
/**
* Denotes a class (the "substitutor") that provides an alternative implementation
* (a method annotated by {@link SUBSTITUTE}) for at least one method in another
* class (the "substitutee").
*
* @see SUBSTITUTE
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface METHOD_SUBSTITUTIONS {
/**
* Specifies the substitutee class.
*
* If the default value is specified for this element, then a non-default
* value must be given for the {@link #className()} element.
*/
Class value() default METHOD_SUBSTITUTIONS.class;
/**
* Specifies the substitutee class.
* This method is provided for cases where the substitutee class
* is not accessible (according to Java language access control rules).
*
* If the default value is specified for this element, then a non-default
* value must be given for the {@link #value()} element.
*/
String className() default "";
/**
* Specifies the suffix of the substitutee class name when it is an inner class.
*/
String innerClass() default "";
public static final class Static {
private Static() {
}
/**
* A map from a method that is substituted to the method that substitutes it.
*/
private static final HashMap<ClassMethodActor, ClassMethodActor> originalToSubstitute = new HashMap<ClassMethodActor, ClassMethodActor>();
/**
* The converse mapping.
*/
private static final HashMap<ClassMethodActor, ClassMethodActor> substituteToOriginal = new HashMap<ClassMethodActor, ClassMethodActor>();
/**
* @param substitutee a class that has one or more methods to be substituted
* @param substitutor a class that provides substitute implementations for one or more methods in
* {@code substitutee}
*/
@HOSTED_ONLY
private static void register(Class substitutee, Class substitutor) {
boolean substitutionFound = false;
for (Method substituteMethod : substitutor.getDeclaredMethods()) {
final SUBSTITUTE substituteAnnotation = substituteMethod.getAnnotation(SUBSTITUTE.class);
if (substituteAnnotation != null) {
substitutionFound = true;
String substituteName = substituteAnnotation.value();
if (substituteName.length() == 0) {
substituteName = substituteMethod.getName();
}
boolean conditional = substituteAnnotation.optional();
boolean isConstructor = substituteAnnotation.constructor();
final ClassMethodActor originalMethodActor;
Constructor constructor = null;
Method originalMethod = null;
SignatureDescriptor substituteeSignature;
if (substituteAnnotation.signatureDescriptor().equals("")) {
substituteeSignature = SignatureDescriptor.fromJava(substituteMethod);
} else {
substituteeSignature = SignatureDescriptor.create(substituteAnnotation.signatureDescriptor());
}
if (isConstructor) {
constructor = findConstructor(substitutee, substituteeSignature);
if (constructor == null) {
if (conditional) {
Trace.line(1, "Substitutee for " + substituteMethod + " not found - skipping");
continue;
}
throw ProgramError.unexpected("could not find unconditional substitutee constructor in " + substitutee + " substituted by " + substituteMethod);
}
try {
originalMethodActor = (ClassMethodActor) MethodActor.fromJavaConstructor(constructor);
} catch (NoSuchMethodError e) {
if (conditional) {
continue;
}
throw e;
}
} else {
originalMethod = findMethod(substitutee, substituteName, substituteeSignature);
if (originalMethod == null) {
if (conditional) {
Trace.line(1, "Substitutee for " + substituteMethod + " not found - skipping");
continue;
}
ProgramError.unexpected("could not find unconditional substitutee method in " + substitutee + " substituted by " + substituteMethod);
}
try {
originalMethodActor = ClassMethodActor.fromJava(originalMethod);
} catch (NoSuchMethodError e) {
if (conditional) {
continue;
}
throw e;
}
}
ClassMethodActor substituteMethodActor = ClassMethodActor.fromJava(substituteMethod);
if (originalToSubstitute.put(originalMethodActor, substituteMethodActor) != null) {
ProgramError.unexpected("a substitute has already been registered for " + (isConstructor ? constructor : originalMethod));
}
if (substituteToOriginal.put(substituteMethodActor, originalMethodActor) != null) {
ProgramError.unexpected("only one original method per substitute allowed - " + substituteMethod);
}
Trace.line(2, "Substituted " + originalMethodActor.format("%h.%n(%p)"));
Trace.line(2, " with " + substituteMethodActor.format("%h.%n(%p)"));
originalMethodActor.setFlagsFromSubstitute(substituteMethodActor);
} else {
// Any other method in the substitutor class must be either inlined or static.
if (substituteMethod.getAnnotation(INLINE.class) == null &&
substituteMethod.getAnnotation(ALIAS.class) == null &&
substituteMethod.getAnnotation(INTRINSIC.class) == null &&
!Modifier.isStatic(substituteMethod.getModifiers())) {
ProgramError.unexpected("method without @" + SUBSTITUTE.class.getSimpleName() + " annotation in " + substitutor + " must be static, have @" + INTRINSIC.class.getSimpleName() +
"(UNSAFE_CAST) or @" + INLINE.class.getSimpleName() + " annotation: " + substituteMethod);
}
}
}
ProgramError.check(substitutionFound, "no method with " + SUBSTITUTE.class.getSimpleName() + " annotation found in " + substitutor);
}
@HOSTED_ONLY
public static void processAnnotationInfo(METHOD_SUBSTITUTIONS annotation, ClassActor substitutor) {
// These two checks make it impossible for method substitutions holders to have (non-alias) instance fields.
// A substitute non-static method could never access such a field given that the receiver is
// cast (via UNSAFE_CAST) to be an instance of the substitutee.
ProgramError.check(substitutor.superClassActor.typeDescriptor == JavaTypeDescriptor.OBJECT, "method substitution class must directly subclass java.lang.Object");
for (FieldActor field : substitutor.localInstanceFieldActors()) {
if (field.getAnnotation(ALIAS.class) == null) {
throw FatalError.unexpected("method substitution class cannot declare any non-aliased instance fields: " + substitutor);
}
}
FatalError.check(substitutor.isFinal(), "method substitution class must be final: " + substitutor);
Class holder;
if (annotation.value() != METHOD_SUBSTITUTIONS.class) {
assert annotation.className().isEmpty();
holder = annotation.value();
} else {
assert !annotation.className().isEmpty();
holder = Classes.forName(annotation.className(), false, substitutor.classLoader);
}
if (!annotation.innerClass().isEmpty()) {
holder = Classes.getInnerClass(holder, annotation.innerClass());
}
register(holder, substitutor.toJava());
}
@HOSTED_ONLY
public static Method findMethod(Class declaringClass, String name, SignatureDescriptor signatureDescriptor) {
for (Method javaMethod : declaringClass.getDeclaredMethods()) {
if (javaMethod.getName().equals(name)) {
if (signatureDescriptor.parametersEqual(SignatureDescriptor.fromJava(javaMethod))) {
return javaMethod;
}
}
}
return null;
}
@HOSTED_ONLY
public static Constructor findConstructor(Class declaringClass, SignatureDescriptor signatureDescriptor) {
for (Constructor constructor : declaringClass.getDeclaredConstructors()) {
if (signatureDescriptor.parametersEqual(SignatureDescriptor.fromJava(constructor))) {
return constructor;
}
}
return null;
}
/**
* Searches for a substitute implementation for a given method.
*
* @param originalMethod
* @return a substitute implementation for {@code javaMethod} or null if no substitution is found
*/
public static ClassMethodActor findSubstituteFor(ClassMethodActor originalMethod) {
return originalToSubstitute.get(originalMethod);
}
/**
* Searches for the method that is substituted by the given method.
*/
public static ClassMethodActor findOriginal(ClassMethodActor substituteMethod) {
return substituteToOriginal.get(substituteMethod);
}
}
}