/* * Copyright (c) 2007, 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.sun.max.vm.actor; import java.util.*; import com.sun.max.annotate.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.classfile.constant.*; import com.sun.max.vm.type.*; /** * Internal representations of Java entities. These "actors" provide runtime support for classes, fields, methods, etc. * They "act" for them by carrying out underlying actions of Java instructions. * <p> * Actors uses identity for {@linkplain #equals(Object) equality testing} and use the system identity * hash codes. */ public abstract class Actor { // Common Flags in #4.1 table 4.1, #4.5 table 4.4 and #4.6 table 4.5: public static final int ACC_PUBLIC = 0x00000001; public static final int ACC_PRIVATE = 0x00000002; public static final int ACC_PROTECTED = 0x00000004; public static final int ACC_STATIC = 0x00000008; public static final int ACC_FINAL = 0x00000010; public static final int ACC_SYNTHETIC = 0x00001000; public static final int ACC_ENUM = 0x00004000; // Made up extra flag corresponding to attributes in #4.7: public static final int DEPRECATED = 0x00008000; // Common flags referring to interfaces and classes: public static final int ACC_SUPER = 0x00000020; public static final int ACC_INTERFACE = 0x00000200; public static final int ACC_ABSTRACT = 0x00000400; public static final int ACC_ANNOTATION = 0x00002000; // VM-internal flags for classes: public static final int INNER_CLASS = 0x00100000; public static final int REFLECTION_STUB = 0x00400000; public static final int FINALIZER = 0x00800000; public static final int REMOTE = 0x02000000; // Common flags referring to fields in #4.5, Table #4.4: public static final int ACC_VOLATILE = 0x00000040; public static final int ACC_TRANSIENT = 0x00000080; // VM-internal flags referring to fields: public static final int INJECTED = 0x00010000; // an additionally injected field that was not derived from a class file field public static final int CONSTANT = 0x00020000; public static final int CONSTANT_WHEN_NOT_ZERO = 0x00040000; // Common flags referring to methods in #4.6, Table 4.5: public static final int ACC_SYNCHRONIZED = 0x00000020; public static final int ACC_BRIDGE = 0x00000040; public static final int ACC_VARARGS = 0x00000080; public static final int ACC_NATIVE = 0x00000100; // see above ACC_ABSTRACT = 0x00000400; public static final int ACC_STRICT = 0x00000800; // VM-internal flags for methods: public static final int NO_SAFEPOINT_POLLS = 0x00004000; public static final int VERIFIED = 0x00010000; public static final int TEMPLATE = 0x00200000; public static final int INITIALIZER = 0x00400000; public static final int C_FUNCTION = 0x01000000; public static final int VM_ENTRY_POINT = 0x02000000; public static final int FOLD = 0x04000000; public static final int LOCAL_SUBSTITUTE = 0x10000000; public static final int UNSAFE = 0x20000000; public static final int INLINE = 0x40000000; public static final int NEVER_INLINE = 0x80000000; /** * Mask of flags that a substitutee should adopt from its {@linkplain SUBSTITUTE substitute}. * Adoption of flags is a union operation with the existing flags of the substitutee. */ public static final int SUBSTITUTION_ADOPTED_FLAGS = FOLD | INLINE | NEVER_INLINE; /** * Mask of flags used to determine if a method must be compiled with the optimizing compiler. */ public static final int UNSAFE_FLAGS = ACC_NATIVE | TEMPLATE | UNSAFE | C_FUNCTION | VM_ENTRY_POINT | LOCAL_SUBSTITUTE | NO_SAFEPOINT_POLLS; /** * Mask of the flags defined for classes in Table 4.1 of the JVM specification. */ public static final int JAVA_CLASS_FLAGS = ACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_INTERFACE | ACC_ABSTRACT | ACC_ANNOTATION | ACC_ENUM | ACC_SYNTHETIC; /** * Mask of the flags defined for fields in Table 4.4 of the JVM specification. */ public static final int JAVA_FIELD_FLAGS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_VOLATILE | ACC_TRANSIENT | ACC_ENUM | ACC_SYNTHETIC; /** * Mask of the flags defined for methods in Table 4.5 of the JVM specification. */ public static final int JAVA_METHOD_FLAGS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC; public static final Utf8Constant NO_GENERIC_SIGNATURE = null; public static final byte[] NO_RUNTIME_VISIBLE_ANNOTATION_BYTES = null; @INSPECTED private int flags; @INSPECTED public final Utf8Constant name; protected Actor(Utf8Constant name, int flags) { if ((flags & UNSAFE_FLAGS) != 0) { flags |= UNSAFE; } this.flags = flags; this.name = name; } @INLINE public final int flags() { return flags; } @INLINE public final boolean isPublic() { return isPublic(flags()); } @INLINE public final boolean isPrivate() { return isPrivate(flags()); } @INLINE public final boolean isProtected() { return isProtected(flags()); } @INLINE public final boolean isStatic() { return isStatic(flags()); } @INLINE public final boolean isFinal() { return isFinal(flags()); } @INLINE public final boolean isSynthetic() { return isSynthetic(flags()); } @INLINE public final boolean isEnum() { return isEnum(flags()); } @INLINE public final boolean isAbstract() { return isAbstract(flags()); } @INLINE public final boolean isDeprecated() { return isDeprecated(flags()); } @INLINE public static boolean isPublic(int flags) { return (flags & ACC_PUBLIC) != 0; } @INLINE public static boolean isPrivate(int flags) { return (flags & ACC_PRIVATE) != 0; } @INLINE public static boolean isProtected(int flags) { return (flags & ACC_PROTECTED) != 0; } @INLINE public static boolean isStatic(int flags) { return (flags & ACC_STATIC) != 0; } @INLINE public static boolean isFinal(int flags) { return (flags & ACC_FINAL) != 0; } @INLINE public static boolean isSynthetic(int flags) { return (flags & ACC_SYNTHETIC) != 0; } @INLINE public static boolean isEnum(int flags) { return (flags & ACC_ENUM) != 0; } @INLINE public static boolean isAbstract(int flags) { return (flags & ACC_ABSTRACT) != 0; } @INLINE public static boolean isInterface(int flags) { return (flags & ACC_INTERFACE) != 0; } @INLINE public static boolean isInnerClass(int flags) { return (flags & INNER_CLASS) != 0; } @INLINE public static boolean isDeprecated(int flags) { return (flags & DEPRECATED) != 0; } @INLINE public static boolean isSuper(int flags) { return (flags & ACC_SUPER) != 0; } @INLINE public static boolean isAnnotation(int flags) { return (flags & ACC_ANNOTATION) != 0; } @INLINE public static boolean isBridge(int flags) { return (flags & ACC_BRIDGE) != 0; } @INLINE public static boolean isVarArgs(int flags) { return (flags & ACC_VARARGS) != 0; } @INLINE public static boolean isSynchronized(int flags) { return (flags & ACC_SYNCHRONIZED) != 0; } @INLINE public static boolean isNative(int flags) { return (flags & ACC_NATIVE) != 0; } @INLINE public static boolean isVerified(int flags) { return (flags & VERIFIED) != 0; } @INLINE public static boolean isStrict(int flags) { return (flags & ACC_STRICT) != 0; } @INLINE public static boolean isClassInitializer(int flags) { return (flags & INITIALIZER) != 0 && (flags & ACC_STATIC) != 0; } @INLINE public static boolean isInstanceInitializer(int flags) { return (flags & INITIALIZER) != 0 && (flags & ACC_STATIC) == 0; } @INLINE public static boolean isInitializer(int flags) { return (flags & INITIALIZER) != 0; } @INLINE public static boolean isCFunction(int flags) { return (flags & C_FUNCTION) != 0; } @INLINE public static boolean isVmEntryPoint(int flags) { return (flags & VM_ENTRY_POINT) != 0; } @INLINE public static boolean isTemplate(int flags) { return (flags & TEMPLATE) != 0; } @INLINE public static boolean isReflectionStub(int flags) { return (flags & REFLECTION_STUB) != 0; } @INLINE public static boolean isRemote(int flags) { return (flags & REMOTE) != 0; } @INLINE public static boolean isVolatile(int flags) { return (flags & ACC_VOLATILE) != 0; } @INLINE public static boolean isTransient(int flags) { return (flags & ACC_TRANSIENT) != 0; } @INLINE public static boolean isInjected(int flags) { return (flags & INJECTED) != 0; } @INLINE public static boolean isConstant(int flags) { return (flags & CONSTANT) != 0; } @INLINE public static boolean isConstantWhenNotZero(int flags) { return (flags & CONSTANT_WHEN_NOT_ZERO) != 0; } @INLINE public static boolean isLocalSubstitute(int flags) { return (flags & LOCAL_SUBSTITUTE) != 0; } @INLINE public static boolean isUnsafe(int flags) { return (flags & UNSAFE) != 0; } @HOSTED_ONLY public void setFlagsFromSubstitute(ClassMethodActor substitute) { final int adoptedFlagsMask = SUBSTITUTION_ADOPTED_FLAGS; flags |= adoptedFlagsMask & substitute.flags(); } @INLINE public final void beUnsafe() { flags |= UNSAFE; } @INLINE public final void beVerified() { flags |= VERIFIED; } @INLINE public static boolean isInline(int flags) { return (flags & INLINE) != 0; } @INLINE public static boolean isNeverInline(int flags) { return (flags & NEVER_INLINE) != 0; } @INLINE public static boolean isDeclaredFoldable(int flags) { return (flags & FOLD) != 0; } @INLINE public static boolean noSafepointPolls(int flags) { return (flags & NO_SAFEPOINT_POLLS) != 0; } @INLINE public static boolean hasFinalizer(int flags) { return (flags & FINALIZER) != 0; } /** * Gets the name of this actor qualified by it's declaring class (if known). * @return the qualified name of this actor */ public abstract String qualifiedName(); /** * Gets the value of the Signature class file attribute associated with this actor. * * @return null if there is no Signature attribute associated with this actor */ public abstract Utf8Constant genericSignature(); public final String genericSignatureString() { final Utf8Constant constant = genericSignature(); if (constant == null) { return null; } return constant.toString(); } /** * Gets the bytes of the RuntimeVisibleAnnotations class file attribute associated with this actor. * * @return null if there is no RuntimeVisibleAnnotations attribute associated with this actor */ public abstract byte[] runtimeVisibleAnnotationsBytes(); public String simpleName() { return javaSignature(false); } public String flagsString() { return flagsString(flags); } public static String flagsString(int flags) { final StringBuilder sb = new StringBuilder(); appendFlag(sb, isPublic(flags), "public "); appendFlag(sb, isProtected(flags), "protected "); appendFlag(sb, isPrivate(flags), "private "); /* Canonical order */ appendFlag(sb, isAbstract(flags), "abstract "); appendFlag(sb, isStatic(flags), "static "); appendFlag(sb, isFinal(flags), "final "); appendFlag(sb, isTransient(flags) || isVarArgs(flags), "transient/varargs "); appendFlag(sb, isVolatile(flags) || isBridge(flags), "volatile/bridge "); appendFlag(sb, isSynchronized(flags) || isSuper(flags), "synchronized/super "); appendFlag(sb, isNative(flags), "native "); appendFlag(sb, isStrict(flags), "strictfp "); appendFlag(sb, isInterface(flags), "interface "); appendFlag(sb, isSynthetic(flags), "synthetic "); appendFlag(sb, isEnum(flags), "enum "); appendFlag(sb, isDeprecated(flags), "deprecated "); appendFlag(sb, isAnnotation(flags), "annotation "); // Implementation specific flags appendFlag(sb, isInjected(flags), "injected "); appendFlag(sb, isConstant(flags), "constant "); appendFlag(sb, isConstantWhenNotZero(flags), "constantWhenNotZero "); appendFlag(sb, isInnerClass(flags), "innerClass "); appendFlag(sb, isTemplate(flags), "template "); appendFlag(sb, isReflectionStub(flags), "reflection_stub "); appendFlag(sb, isVerified(flags), "verified "); appendFlag(sb, isInitializer(flags), "init "); appendFlag(sb, isCFunction(flags), "c_function "); appendFlag(sb, isVmEntryPoint(flags), "vm_entry "); appendFlag(sb, isDeclaredFoldable(flags), "fold "); appendFlag(sb, isUnsafe(flags), "unsafe "); appendFlag(sb, isLocalSubstitute(flags), "substitute "); if (sb.length() > 0) { /* trim trailing space */ sb.setLength(sb.length() - 1); return sb.toString(); } return ""; } private static void appendFlag(StringBuilder sb, boolean flag, String string) { if (flag) { sb.append(string); } } /** * Gets the signature of this actor in pseudo Java source syntax. For example: * * <pre> * "java.lang.Class" * "java.lang.String getName()" * "java.io.PrintStream out" * "java.util.Collections$EmptyMap" * "java.lang.Package$1PackageInfoProxy" * "java.util.AbstractMap$1$1" * </pre> * * Or if {@code qualified == false}: * * <pre> * "Class" * "String getName()" * "PrintStream out" * "Collections$EmptyMap" * "Package$1PackageInfoProxy" * "AbstractMap$1$1" * </pre> * * Note that the signature is not correct Java syntax if it contains a type that is a nested class. * * @param qualified if true, then the types in the signature are qualified with their package names * @return the signature of this actor in pseudo Java source syntax */ public abstract String javaSignature(boolean qualified); /** * Gets a string for this actor formatted according to a given format specification. A format specification is * composed of characters that are to be copied verbatim to the result and specifiers that denote an attribute of this * actor that is to be copied to the result. A specifier is a single character preceded by a '%' character. The * accepted specifiers and the actor attribute they denote are described below: * * <pre> * Specifier | Args | Description | Example(s) * ----------+--------------+------------------------------------------------------------------------------------------ * 'T' | | Qualified return/field type | "int" "java.lang.String" * 't' | | Unqualified return/field type | "int" "String" * 'R' | | Qualified return/field type | "int" "java.lang.String" * 'r' | | Unqualified return/field type | "int" "String" * 's' | bci | Source file name and line derived from bci | "String.java:33" "Native Method" "Unknown Source" * 'H' | | Qualified holder | "java.util.Map.Entry" * 'h' | | Unqualified holder | "Entry" * 'n' | | Method/field/class name | "add" * 'P' | | Qualified parameter types, separated by ', ' | "int, java.lang.String" * 'p' | | Unqualified parameter types, separated by ', ' | "int, String" * 'f' | | The flags as a {@linkplain #flagsString() string} | "public static final" "transient" * '%' | | A '%' character | "%" * </pre> * * If a specifier is given for an actor attribute that is not applicable for this actor type, then * the specifier is ignored or causes an {@link IllegalFormatException} depending on the value of {@code strict}. * For example, a "%T" in {@code format} is ignored if this object is a {@link ClassActor} instance and {@code strict == false}. * @param strict specifies what action to take if actor attributes that don't apply to this actor are encountered in {@code format} * @param format a format specification * @param args arguments referenced by the format specifiers in {@code format}. * If there are more arguments than consumed by the format specifiers, the * extra arguments are ignored. * * @return the result of formatting this method according to {@code format} * @throws IllegalFormatException if an illegal specifier is encountered in {@code format} */ public final String format(boolean strict, String format, Object... args) throws IllegalFormatException { final StringBuilder sb = new StringBuilder(); int index = 0; int argIndex = 0; while (index < format.length()) { final char ch = format.charAt(index++); if (ch == '%') { if (index >= format.length()) { throw new UnknownFormatConversionException("An unquoted '%' character cannot terminate a method format specification"); } final char specifier = format.charAt(index++); boolean qualified = false; switch (specifier) { case 'T': case 'R': qualified = true; // fall through case 't': case 'r': { if (this instanceof MethodActor) { final MethodActor methodActor = (MethodActor) this; sb.append(methodActor.descriptor().resultDescriptor().toJavaString(qualified)); } else if (this instanceof FieldActor) { final FieldActor fieldActor = (FieldActor) this; sb.append(fieldActor.descriptor().toJavaString(qualified)); } else { if (strict) { throw new IllegalFormatConversionException(specifier, getClass()); } } break; } case 's': { final Object arg; try { arg = args[argIndex++]; } catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException) { throw new MissingFormatArgumentException(String.valueOf(specifier)); } final int bci; try { bci = (Integer) arg; } catch (ClassCastException classCastException) { throw new IllegalFormatConversionException(specifier, arg == null ? Object.class : arg.getClass()); } if (this instanceof ClassMethodActor) { final ClassMethodActor classMethodActor = (ClassMethodActor) this; final String stackTraceLine = classMethodActor.toStackTraceElement(bci).toString(); sb.append(stackTraceLine.substring(stackTraceLine.indexOf('(') + 1, stackTraceLine.lastIndexOf(')'))); } else { if (strict) { throw new IllegalFormatConversionException(specifier, getClass()); } } break; } case 'H': qualified = true; // fall through case 'h': { if (this instanceof MemberActor) { final MemberActor memberActor = (MemberActor) this; ClassActor holder = memberActor.holder(); sb.append(holder == null ? "null" : holder.typeDescriptor.toJavaString(qualified)); } else { if (strict) { throw new IllegalFormatConversionException(specifier, getClass()); } } break; } case 'n': { sb.append(name); break; } case 'P': qualified = true; // fall through case 'p': { if (this instanceof MethodActor) { final MethodActor methodActor = (MethodActor) this; String separator = ""; final SignatureDescriptor signature = methodActor.descriptor(); for (int i = 0; i < signature.numberOfParameters(); i++) { sb.append(separator).append(signature.parameterDescriptorAt(i).toJavaString(qualified)); separator = ", "; } } else { if (strict) { throw new IllegalFormatConversionException(specifier, getClass()); } } break; } case 'f': { sb.append(flagsString()); break; } case '%': { sb.append('%'); break; } default: { throw new UnknownFormatConversionException(String.valueOf(specifier)); } } } else { sb.append(ch); } } return sb.toString(); } /** * Calling this method is equivalent to calling {@link #format(String, Object...) format(format, true)}. * * @param format a format specification * @param args arguments referenced by the format specifiers in {@code format}. * If there are more arguments than consumed by the format specifiers, the * extra arguments are ignored. * @return the result of formatting this method according to {@code format} * @throws IllegalFormatException if an illegal specifier is encountered in {@code format} */ public final String format(String format, Object... args) throws IllegalFormatException { return format(true, format, args); } @Override public abstract String toString(); public abstract boolean isAccessibleBy(ClassActor accessor); public final void checkAccessBy(ClassActor accessor) { if (!isAccessibleBy(accessor)) { isAccessibleBy(accessor); throw new IllegalAccessError(accessor.name + " cannot access " + this); } } }