| /******************************************************************************* |
| * Copyright (c) 2005, 2015 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.debug.eval.ast.engine; |
| |
| import java.text.MessageFormat; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jdi.internal.TypeImpl; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.debug.core.IJavaClassObject; |
| import org.eclipse.jdt.debug.core.IJavaClassType; |
| import org.eclipse.jdt.debug.core.IJavaFieldVariable; |
| import org.eclipse.jdt.debug.core.IJavaObject; |
| import org.eclipse.jdt.debug.core.IJavaReferenceType; |
| import org.eclipse.jdt.debug.core.IJavaType; |
| import org.eclipse.jdt.debug.core.IJavaValue; |
| import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin; |
| import org.eclipse.jdt.internal.debug.eval.ast.instructions.InstructionsEvaluationMessages; |
| |
| import com.sun.jdi.InvocationException; |
| |
| /** |
| * Common runtime context code for class loading and cache of class |
| * loader/java.lang.Class. |
| * |
| * @since 3.2 |
| */ |
| |
| public abstract class AbstractRuntimeContext implements IRuntimeContext { |
| |
| /** |
| * Cache of class loader for this runtime context |
| */ |
| private IJavaObject fClassLoader; |
| |
| /** |
| * Cache of java.lang.Class type |
| */ |
| private IJavaClassType fJavaLangClass; |
| |
| /** |
| * Java project context |
| */ |
| protected IJavaProject fProject; |
| |
| public static final String CLASS = "java.lang.Class"; //$NON-NLS-1$ |
| public static final String FOR_NAME = "forName"; //$NON-NLS-1$ |
| public static final String FOR_NAME_SIGNATURE = "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"; //$NON-NLS-1$ |
| |
| public AbstractRuntimeContext(IJavaProject project) { |
| fProject = project; |
| } |
| |
| /** |
| * Returns the class loader used to load classes for this runtime context or |
| * <code>null</code> when loaded by the bootstrap loader |
| * |
| * @return the class loader used to load classes for this runtime context or |
| * <code>null</code> when loaded by the bootstrap loader |
| * @throws CoreException |
| * if unable to resolve a class loader |
| */ |
| protected IJavaObject getClassLoaderObject() throws CoreException { |
| if (fClassLoader == null) { |
| fClassLoader = getReceivingType().getClassLoaderObject(); |
| } |
| return fClassLoader; |
| } |
| |
| /** |
| * Return the java.lang.Class type. |
| * |
| * @return the java.lang.Class type |
| * @throws CoreException |
| * if unable to retrieve the type |
| */ |
| protected IJavaClassType getJavaLangClass() throws CoreException { |
| if (fJavaLangClass == null) { |
| IJavaType[] types = getVM().getJavaTypes(CLASS); |
| if (types == null || types.length != 1) { |
| throw new CoreException( |
| new Status( |
| IStatus.ERROR, |
| JDIDebugPlugin.getUniqueIdentifier(), |
| IStatus.OK, |
| MessageFormat |
| .format(InstructionsEvaluationMessages.Instruction_No_type, |
| new Object[] { CLASS }), null)); |
| } |
| fJavaLangClass = (IJavaClassType) types[0]; |
| } |
| return fJavaLangClass; |
| } |
| |
| /** |
| * Invokes Class.classForName(String, boolean, ClassLoader) on the target to |
| * force load the specified class. |
| * |
| * @param qualifiedName |
| * name of class to load |
| * @param loader |
| * the class loader to use or <code>null</code> if the bootstrap |
| * loader |
| * @return the loaded class |
| * @throws CoreException |
| * if loading fails |
| */ |
| protected IJavaClassObject classForName(String qualifiedName, |
| IJavaObject loader) throws CoreException { |
| String tname = qualifiedName; |
| if (tname.startsWith("[")) { //$NON-NLS-1$ |
| tname = TypeImpl.signatureToName(qualifiedName); |
| } |
| IJavaType[] types = getVM().getJavaTypes(tname); |
| if (types != null && types.length > 0) { |
| // find the one with the right class loader |
| for (IJavaType type2 : types) { |
| if ( type2 instanceof IJavaReferenceType){ |
| IJavaReferenceType type = (IJavaReferenceType) type2; |
| IJavaObject cloader = type.getClassLoaderObject(); |
| if (isCompatibleLoader(loader, cloader)) { |
| return type.getClassObject(); |
| } |
| } |
| } |
| } |
| IJavaValue loaderArg = loader; |
| if (loader == null) { |
| loaderArg = getVM().nullValue(); |
| } |
| // prevent the name string from being collected during the class lookup |
| // call |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=301412 |
| final IJavaValue name = getVM().newValue(qualifiedName); |
| ((IJavaObject) name).disableCollection(); |
| IJavaValue[] args = new IJavaValue[] { name, getVM().newValue(true), |
| loaderArg }; |
| try { |
| return (IJavaClassObject) getJavaLangClass().sendMessage(FOR_NAME, |
| FOR_NAME_SIGNATURE, args, getThread()); |
| } catch (CoreException e) { |
| if (e.getStatus().getException() instanceof InvocationException) { |
| // Don't throw ClassNotFoundException |
| if (((InvocationException) e.getStatus().getException()) |
| .exception().referenceType().name() |
| .equals("java.lang.ClassNotFoundException")) { //$NON-NLS-1$ |
| return null; |
| } |
| } |
| throw e; |
| } finally { |
| ((IJavaObject) name).enableCollection(); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.jdt.internal.debug.eval.ast.engine.IRuntimeContext#classForName |
| * (java.lang.String) |
| */ |
| @Override |
| public IJavaClassObject classForName(String name) throws CoreException { |
| return classForName(name, getClassLoaderObject()); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.jdt.internal.debug.eval.ast.engine.IRuntimeContext#getProject |
| * () |
| */ |
| @Override |
| public IJavaProject getProject() { |
| return fProject; |
| } |
| |
| /** |
| * Returns whether the class loaded by the <code>otherLoader</code> is |
| * compatible with the receiver's class loader. To be compatible, the |
| * other's loader must be the same or a parent of the receiver's loader. |
| * |
| * @param recLoader |
| * class loader of receiver |
| * @param otherLoader |
| * class loader of other class |
| * @return whether compatible |
| */ |
| private boolean isCompatibleLoader(IJavaObject recLoader, |
| IJavaObject otherLoader) throws CoreException { |
| if (recLoader == null || otherLoader == null) { |
| // if either class is a bootstrap loader, then they are compatible |
| // since all loaders |
| // stem from the bootstrap loader |
| return true; |
| } |
| if (recLoader.equals(otherLoader)) { |
| return true; |
| } |
| // check parent loaders |
| IJavaObject parent = getParentLoader(recLoader); |
| while (parent != null) { |
| if (parent.equals(otherLoader)) { |
| return true; |
| } |
| parent = getParentLoader(parent); |
| } |
| return false; |
| } |
| |
| /** |
| * Returns the parent class loader of the given class loader object or |
| * <code>null</code> if none. |
| * |
| * @param loader |
| * class loader object |
| * @return parent class loader or <code>null</code> |
| * @throws CoreException |
| */ |
| private IJavaObject getParentLoader(IJavaObject loader) |
| throws CoreException { |
| // to avoid message send, first check for 'parent' field |
| IJavaFieldVariable field = loader.getField("parent", false); //$NON-NLS-1$ |
| if (field != null) { |
| IJavaValue value = (IJavaValue) field.getValue(); |
| if (value.isNull()) { |
| return null; |
| } |
| return (IJavaObject) value; |
| } |
| IJavaValue result = loader |
| .sendMessage( |
| "getParent", "()Ljava/lang/ClassLoader;", null, getThread(), false); //$NON-NLS-1$ //$NON-NLS-2$ |
| if (result.isNull()) { |
| return null; |
| } |
| return (IJavaObject) result; |
| } |
| } |