/*
* 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.vm.classfile;
import java.util.*;
import com.sun.max.program.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.thread.*;
import com.sun.max.vm.verifier.*;
/**
* A utility for raising errors and associating them with one or more enclosing calling contexts. The general pattern of
* usage is shown by the following {@linkplain ClassfileReader#loadClass example} of entering a context for
* parsing a class file:
*
* <pre>
* public ClassActor loadClass(final String name) {
* try {
* enterContext("loading" + name);
* return loadClass0(name);
* } finally {
* exitContext();
* }
* </pre>
*
* If an exception is raised via one of the static methods in this class while in the call to {@code loadClass0()},
* then the {@linkplain Throwable#getMessage() detail message} of the raised exception has a line appended to it for
* each enclosing calling context. The format of each context line is the result of calling {@link Object#toString()} on
* the argument provided to {@link #enterContext(Object)} prefixed by {@code " while "}. For example:
*
* <pre>
* java.lang.ClassFormatError: Invalid method signature: Ljava/io/PrintStream;
* while loading javasoft.sqe.tests.vm.classfmt.cpl.cplmbr201.cplmbr20102m1.cplmbr20102m11n
* </pre>
*
* The argument to {@code enterContext()} is of type {@code Object} so that the description of the context is computed
* only if an exception is raised. This reduces the overhead of entering contexts. For example, the above example could
* be re-written as follows if the cost of the string concatenation is too high compared to the cost of the call to
* {@code loadClass0()}:
*
* <pre>
* public ClassActor loadClass(final String name) {
* try {
* enterContext(new Object() {
* public String toString() {
* return "loading " + name;
* }
* });
* return loadClass0(name);
* } finally {
* exitContext();
* }
* </pre>
*
* The execution cost of entering a context can be reduced even further by using the provided
* {@linkplain #perform(Object, Runnable) callback mechanism}.
*/
public final class ErrorContext {
private ErrorContext() {
}
private static final ObjectThreadLocal<List<Object>> ERROR_CONTEXTS = new ObjectThreadLocal<List<Object>>("ERROR_CONTEXTS", "Nested error contexts during class loading") {
@Override
protected List<Object> initialValue() {
return new ArrayList<Object>();
}
};
private static List<Object> makeContexts() {
return ERROR_CONTEXTS.get();
}
/**
* Gets the current error contexts for the current thread.
*/
public static List<Object> contexts() {
final List<Object> contexts = ERROR_CONTEXTS.getWithoutInitialization();
if (contexts == null) {
return Collections.emptyList();
}
return contexts;
}
public static void enterContext(Object context) {
makeContexts().add(context);
}
public static void exitContext() {
try {
List<Object> contexts = makeContexts();
contexts.remove(contexts.size() - 1);
} catch (IndexOutOfBoundsException e) {
ProgramWarning.message("Unstructured use of error contexts");
}
}
public static void perform(Object context, Runnable runnable) {
final List<Object> contextStack = makeContexts();
contextStack.add(context);
try {
runnable.run();
} finally {
contextStack.remove(contextStack.size() - 1);
}
}
public static String appendContexts(String message) {
final StringBuilder sb = new StringBuilder();
if (message != null) {
sb.append(message);
}
final String lineSeparator = System.getProperty("line.separator", "\n");
List<Object> contexts = new ArrayList<Object>(contexts());
for (int i = contexts.size() - 1; i >= 0; --i) {
Object context = contexts.get(i);
if (sb.length() != 0) {
sb.append(lineSeparator).append(" ");
}
sb.append("while ").append(context);
}
return sb.toString();
}
public static AbstractMethodError abstractMethodError(String message) {
throw new AbstractMethodError(appendContexts(message));
}
public static IncompatibleClassChangeError incompatibleClassChangeError(String message) {
throw new IncompatibleClassChangeError(appendContexts(message));
}
public static NoSuchFieldError noSuchFieldError(String message, Throwable cause) {
throw (NoSuchFieldError) new NoSuchFieldError(appendContexts(message)).initCause(cause);
}
public static NoSuchFieldError noSuchFieldError(String message) {
throw new NoSuchFieldError(appendContexts(message));
}
public static NoSuchMethodError noSuchMethodError(String message, Throwable cause) {
throw (NoSuchMethodError) new NoSuchMethodError(appendContexts(message)).initCause(cause);
}
public static NoSuchMethodError noSuchMethodError(String message) {
throw new NoSuchMethodError(appendContexts(message));
}
public static ClassFormatError classFormatError(String message, Throwable cause) {
throw (ClassFormatError) new ClassFormatError(appendContexts(message)).initCause(cause);
}
public static ClassFormatError classFormatError(String message) {
throw new ClassFormatError(appendContexts(message));
}
public static VerifyError verifyError(String message, ClassMethodActor classMethodActor, CodeAttribute codeAttribute, int address) {
throw new ExtendedVerifyError(appendContexts(message), classMethodActor, codeAttribute, address);
}
public static VerifyError verifyError(String message) {
throw new VerifyError(appendContexts(message));
}
public static NoClassDefFoundError noClassDefFoundError(String message, Throwable cause) {
throw (NoClassDefFoundError) new NoClassDefFoundError(appendContexts(message)).initCause(cause);
}
public static NoClassDefFoundError noClassDefFoundError(String message) {
throw new NoClassDefFoundError(appendContexts(message));
}
public static UnsupportedClassVersionError unsupportedClassVersionError(String message) {
throw new UnsupportedClassVersionError(appendContexts(message));
}
}