# HG changeset patch # User Doug Simon # Date 1364247342 -3600 # Node ID c7a2a937233f22463a22b266d23dacec48e60a2c # Parent a7d3f7b5b462a845dc925025680033d1134c0fbe added @Alias annotation for accessing fields and methods otherwise inaccessible due to Java access rules diff -r a7d3f7b5b462 -r c7a2a937233f graal/com.oracle.graal.api.replacements/src/com/oracle/graal/api/replacements/Alias.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.api.replacements/src/com/oracle/graal/api/replacements/Alias.java Mon Mar 25 22:35:42 2013 +0100 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2013, 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.oracle.graal.api.replacements; + +import java.lang.annotation.*; + +/** + * Mechanism for accessing fields and methods otherwise inaccessible due to Java language access + * control rules. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.FIELD}) +public @interface Alias { + + /** + * The name of the aliased field or method. If the default value is specified for this element, + * then the name of the annotated field or method is implied. + */ + String name() default ""; + + /** + * Gets the field + * descriptor of the aliased field or the method + * descriptor of the aliased method. + *

+ * If the default value is specified for this element, then the descriptor of the annotated + * field or method is implied. + */ + String descriptor() default ""; + + /** + * Specifies the class in which the aliased field or method is declared. If the default value is + * specified for this element, then a non-default value must be given for the + * {@link #declaringClassName()} element. + */ + Class declaringClass() default Alias.class; + + /** + * Specifies the class in which the aliased field or method is declared. This method is provided + * for cases where the declaring class is not accessible (according to Java language access + * control rules) in the scope of the alias method. + * + * If the default value is specified for this element, then a non-default value must be given + * for the {@link #declaringClassName()} element. + */ + String declaringClassName() default ""; + + /** + * Specifies the suffix of the declaring class name if it is an inner class. + */ + String innerClass() default ""; + + /** + * Specifies if the aliased target must exist. This property is useful, for example, to handle + * differences in JDK versions for private methods. + */ + boolean optional() default false; +} diff -r a7d3f7b5b462 -r c7a2a937233f graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/AliasResolutionPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/AliasResolutionPhase.java Mon Mar 25 22:35:42 2013 +0100 @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2011, 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.oracle.graal.replacements; + +import static com.oracle.graal.api.meta.MetaUtil.*; +import static com.oracle.graal.replacements.ReplacementsInstaller.*; +import static java.lang.Thread.*; +import static java.lang.reflect.Modifier.*; + +import java.lang.reflect.*; +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.replacements.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.phases.*; + +/** + * Resolves field and method {@linkplain Alias aliases} to the aliased fields and methods. + */ +public class AliasResolutionPhase extends Phase { + + private final MetaAccessProvider runtime; + + public AliasResolutionPhase(MetaAccessProvider runtime) { + this.runtime = runtime; + } + + @Override + protected void run(StructuredGraph graph) { + for (LoadFieldNode loadField : graph.getNodes(LoadFieldNode.class)) { + ResolvedJavaField field = loadField.field(); + Field aliasedField = getAliasedField(field); + if (aliasedField != null) { + LoadFieldNode replacement = graph.add(new LoadFieldNode(loadField.object(), runtime.lookupJavaField(aliasedField))); + graph.replaceFixedWithFixed(loadField, replacement); + } + } + for (StoreFieldNode storeField : graph.getNodes().filter(StoreFieldNode.class)) { + ResolvedJavaField field = storeField.field(); + Field aliasedField = getAliasedField(field); + if (aliasedField != null) { + StoreFieldNode replacement = graph.add(new StoreFieldNode(storeField.object(), runtime.lookupJavaField(aliasedField), storeField.value())); + graph.replaceFixedWithFixed(storeField, replacement); + } + } + + for (Invoke invoke : graph.getInvokes()) { + if (invoke.callTarget() instanceof MethodCallTargetNode) { + MethodCallTargetNode methodCallTarget = invoke.methodCallTarget(); + ResolvedJavaMethod method = methodCallTarget.targetMethod(); + Method aliasedMethod = getAliasedMethod(method); + if (aliasedMethod != null) { + methodCallTarget.setTargetMethod(runtime.lookupJavaMethod(aliasedMethod)); + } + } + } + } + + private static Field getAliasedField(ResolvedJavaField field) { + Alias alias = field.getAnnotation(Alias.class); + if (alias == null) { + return null; + } + Class holder = declaringClass(alias, field); + if (holder == null) { + assert alias.optional(); + return null; + } + + String name = alias.name(); + if (name.isEmpty()) { + name = field.getName(); + } + + Class type; + if (alias.descriptor().isEmpty()) { + type = getMirrorOrFail((ResolvedJavaType) field.getType(), currentThread().getContextClassLoader()); + } else { + type = resolveType(alias.descriptor(), false); + } + + for (Field f : holder.getDeclaredFields()) { + if (f.getName().equals(name) && f.getType().equals(type) && isStatic(f.getModifiers()) == isStatic(field.getModifiers())) { + return f; + } + } + if (alias.optional()) { + return null; + } + throw new GraalInternalError("Could not resolve field alias %s", format("%T %H.%n", field)); + } + + private Method getAliasedMethod(ResolvedJavaMethod method) { + Alias alias = method.getAnnotation(Alias.class); + if (alias == null) { + return null; + } + Class holder = declaringClass(alias, method); + if (holder == null) { + assert alias.optional(); + return null; + } + + String name = alias.name(); + if (name.isEmpty()) { + name = method.getName(); + } + + Class[] parameters; + if (alias.descriptor().isEmpty()) { + parameters = NodeIntrinsificationPhase.signatureToTypes(method.getSignature(), null); + } else { + Signature signature = runtime.parseMethodDescriptor(alias.descriptor()); + parameters = NodeIntrinsificationPhase.signatureToTypes(signature, null); + } + + for (Method m : holder.getDeclaredMethods()) { + if (m.getName().equals(name) && Arrays.equals(m.getParameterTypes(), parameters) && isStatic(m.getModifiers()) == isStatic(method.getModifiers())) { + return m; + } + } + if (alias.optional()) { + return null; + } + throw new GraalInternalError("Could not resolve method alias %s", format("%R %H.%n(%P)", method)); + } + + private static Class getInnerClass(Class outerClass, String innerClassSimpleName) { + for (Class innerClass : outerClass.getDeclaredClasses()) { + if (innerClass.getSimpleName().equals(innerClassSimpleName)) { + return innerClass; + } + } + return null; + } + + private static Class declaringClass(Alias alias, Object member) { + Class holder; + if (alias.declaringClass() == Alias.class) { + assert !alias.declaringClassName().isEmpty() : "declaring class missing for alias " + member; + holder = resolveType(alias.declaringClassName(), alias.optional()); + } else { + assert alias.declaringClassName().isEmpty() : "declaring class specified more than once for alias " + member; + holder = alias.declaringClass(); + } + if (holder != null && !alias.innerClass().isEmpty()) { + holder = getInnerClass(holder, alias.innerClass()); + } + return holder; + } +} diff -r a7d3f7b5b462 -r c7a2a937233f graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsInstaller.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsInstaller.java Mon Mar 25 17:37:21 2013 +0100 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsInstaller.java Mon Mar 25 22:35:42 2013 +0100 @@ -210,6 +210,7 @@ * Does final processing of a snippet graph. */ protected void finalizeGraph(ResolvedJavaMethod method, StructuredGraph graph) { + new AliasResolutionPhase(runtime).apply(graph); new NodeIntrinsificationPhase(runtime, pool).apply(graph); assert SnippetTemplate.hasConstantParameter(method) || NodeIntrinsificationVerificationPhase.verify(graph); @@ -255,6 +256,7 @@ Debug.dump(graph, "%s: %s", method.getName(), GraphBuilderPhase.class.getSimpleName()); new WordTypeVerificationPhase(runtime, target.wordKind).apply(graph); + new AliasResolutionPhase(runtime).apply(graph); new NodeIntrinsificationPhase(runtime, pool).apply(graph); return graph; @@ -277,6 +279,7 @@ * Called after all inlining for a given graph is complete. */ protected void afterInlining(StructuredGraph graph) { + new AliasResolutionPhase(runtime).apply(graph); new NodeIntrinsificationPhase(runtime, pool).apply(graph); new WordTypeRewriterPhase(runtime, target.wordKind).apply(graph); @@ -340,7 +343,7 @@ * @param optional if true, resolution failure returns null * @return the resolved class or null if resolution fails and {@code optional} is true */ - private static Class resolveType(String className, boolean optional) { + static Class resolveType(String className, boolean optional) { try { // Need to use launcher class path to handle classes // that are not on the boot class path