package com.redhat.ceylon.eclipse.core.debug;
import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.modelJ2C;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.internal.utils.Cache;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IDebugElement;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IValue;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.Signature;
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.IJavaPrimitiveValue;
import org.eclipse.jdt.debug.core.IJavaReferenceType;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.debug.core.IJavaType;
import org.eclipse.jdt.debug.core.IJavaValue;
import org.eclipse.jdt.internal.debug.core.JavaDebugUtils;
import org.eclipse.jdt.internal.debug.core.model.JDIClassObjectValue;
import org.eclipse.jdt.internal.debug.core.model.JDIClassType;
import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
import org.eclipse.jdt.internal.debug.core.model.JDINullValue;
import org.eclipse.jdt.internal.debug.core.model.JDIObjectValue;
import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame;
import com.redhat.ceylon.compiler.java.codegen.Naming;
import com.redhat.ceylon.compiler.java.language.AbstractCallable;
import com.redhat.ceylon.compiler.java.language.LazyIterable;
import com.redhat.ceylon.compiler.java.runtime.metamodel.Metamodel;
import com.redhat.ceylon.compiler.java.runtime.model.TypeDescriptor;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnits;
import com.redhat.ceylon.eclipse.core.builder.CeylonBuilder;
import com.redhat.ceylon.eclipse.core.builder.CeylonNature;
import com.redhat.ceylon.eclipse.core.debug.model.CeylonJDIDebugTarget;
import com.redhat.ceylon.eclipse.core.debug.model.CeylonJDIDebugTarget.EvaluationListener;
import com.redhat.ceylon.eclipse.core.debug.model.CeylonJDIDebugTarget.EvaluationRunner;
import com.redhat.ceylon.eclipse.core.debug.model.CeylonJDIDebugTarget.EvaluationWaiter;
import com.redhat.ceylon.eclipse.util.JavaSearch;
import com.redhat.ceylon.eclipse.util.JavaSearch.DefaultArgumentMethodSearch;
import com.redhat.ceylon.ide.common.model.BaseIdeModelLoader;
import com.redhat.ceylon.ide.common.model.BaseIdeModule;
import com.redhat.ceylon.ide.common.model.CeylonProject;
import com.redhat.ceylon.ide.common.typechecker.CrossProjectPhasedUnit;
import com.redhat.ceylon.ide.common.util.escaping_;
import com.redhat.ceylon.model.loader.ModelLoader.DeclarationType;
import com.redhat.ceylon.model.loader.NamingBase.Suffix;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.LazyType;
import com.redhat.ceylon.model.typechecker.model.Module;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypeParameter;
import com.redhat.ceylon.model.typechecker.model.Unit;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.ClassType;
import com.sun.jdi.Field;
import com.sun.jdi.LocalVariable;
import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.ReferenceType;
public class DebugUtils {
public static Cache packagePathToProjectCache = new Cache(20);
public static IJavaProject getProject(IDebugElement debugElement) {
IDebugTarget target = debugElement.getDebugTarget();
if (target instanceof CeylonJDIDebugTarget) {
IProject project = ((CeylonJDIDebugTarget) target).getProject();
if (project != null
&& project.isAccessible()
&& CeylonNature.isEnabled(project)) {
return JavaCore.create(project);
}
}
String sourceName = null;
try {
sourceName = JavaDebugUtils.getSourceName(debugElement);
String packagePath;
int lastSlash = sourceName.lastIndexOf('/');
if (lastSlash >= 0) {
packagePath = sourceName.substring(0, lastSlash);
} else {
packagePath = sourceName;
}
synchronized (packagePathToProjectCache) {
Cache.Entry entry = packagePathToProjectCache.getEntry(packagePath);
if (entry != null) {
IJavaProject javaProject = (IJavaProject) entry.getCached();
if (javaProject == null) {
return null;
} else if(javaProject.getProject().isAccessible()) {
return javaProject;
} else {
entry.discard();
}
}
String packageName = packagePath.replace('/', '.');
List<CeylonProject<IProject, IResource, IFolder, IFile>> ceylonProjects = modelJ2C().ceylonModel().getCeylonProjectsAsJavaList();
if (packageName.startsWith(Module.LANGUAGE_MODULE_NAME) ||
packageName.startsWith("com.redhat.ceylon.") &&
! ceylonProjects.isEmpty()) {
IJavaProject jp = JavaCore.create(ceylonProjects.get(0).getIdeArtifact());
packagePathToProjectCache.addEntry(packagePath, jp);
return jp;
}
for (CeylonProject<IProject,?,?,?> cp : ceylonProjects) {
IProject p = cp.getIdeArtifact();
for (Module m : CeylonBuilder.getProjectDeclaredSourceModules(p)) {
if (m.getDirectPackage(packageName) != null) {
IJavaProject jp = JavaCore.create(p);
packagePathToProjectCache.addEntry(packagePath, jp);
return jp;
}
}
}
for (CeylonProject<IProject,?,?,?> cp : ceylonProjects) {
IProject p = cp.getIdeArtifact();
for (Module m : CeylonBuilder.getProjectExternalModules(p)) {
if (m.getDirectPackage(packageName) != null) {
IJavaProject jp = JavaCore.create(p);
packagePathToProjectCache.addEntry(packagePath, jp);
return jp;
}
}
}
}
} catch (CoreException e) {
e.printStackTrace();
}
return null;
}
public static IMethod getJavaMethod(IJavaStackFrame frame,
IJavaProject project) {
try {
IType declaringType = getTypeForJDIReferenceType(frame, project);
if (declaringType != null) {
for (IMethod method : declaringType.getMethods()) {
if (method.getElementName().equals(frame.getMethodName())
|| frame.isConstructor() && method.isConstructor()) {
String[] methodParameterTypes = new String[method
.getParameterTypes().length];
int i = 0;
for (String signature : method.getParameterTypes()) {
signature = signature
.replace("$", "####dollar####");
signature = Signature.toString(signature);
signature = signature
.replace("####dollar####", "$");
methodParameterTypes[i++] = signature;
}
if (Arrays.equals(methodParameterTypes, frame
.getArgumentTypeNames().toArray())) {
return method;
}
}
}
}
} catch (CoreException e) {
e.printStackTrace();
}
return null;
}
public static IType getTypeForJDIReferenceType(IJavaStackFrame frame,
IJavaProject javaProject) {
try {
IJavaReferenceType referenceType = frame.getReferenceType();
IType declaringType = javaProject.findType(referenceType.getName());
return declaringType;
} catch (CoreException e) {
e.printStackTrace();
}
return null;
}
public static IType getJavaType(IJavaObject object) {
IJavaProject project = getProject(object);
return project == null ? null : getJavaType(object, project);
}
public static IType getJavaType(IJavaObject object,
IJavaProject javaProject) {
try {
IJavaReferenceType referenceType = (IJavaReferenceType) object.getJavaType();
IType declaringType = javaProject.findType(referenceType.getName());
return declaringType;
} catch (CoreException e) {
e.printStackTrace();
}
return null;
}
public static IType getJavaType(IJavaClassType jdiType) {
IJavaProject project = getProject(jdiType);
return project == null ? null : getJavaType(jdiType, project);
}
public static IType getJavaType(IJavaClassType jdiType,
IJavaProject javaProject) {
try {
IType declaringType = javaProject.findType(jdiType.getName());
return declaringType;
} catch (CoreException e) {
e.printStackTrace();
}
return null;
}
public static IMethod getJavaMethod(IJavaStackFrame frame) {
IJavaProject project = getProject(frame);
return project == null ? null : getJavaMethod(frame, project);
}
public static PhasedUnit getPhasedUnit(IJavaObject object,
IJavaProject project) {
try {
IJavaType javaType = object.getJavaType();
if (javaType instanceof IJavaClassType) {
IJavaClassType javaClassType = (IJavaClassType) javaType;
String[] sourcePaths = javaClassType.getSourcePaths(null);
if (sourcePaths != null) {
for (String sourcePath : sourcePaths) {
PhasedUnit pu = getPhasedUnit(sourcePath, project);
if (pu != null) {
return pu;
}
}
}
}
} catch (DebugException e) {
e.printStackTrace();
}
return null;
}
public static PhasedUnit getPhasedUnit(IJavaStackFrame frame,
IJavaProject project) {
try {
return getPhasedUnit(frame.getSourcePath(), project);
} catch (DebugException e) {
e.printStackTrace();
}
return null;
}
@SuppressWarnings("unchecked")
public static PhasedUnit getPhasedUnit(String sourcePath,
IJavaProject project) {
PhasedUnits projectPhasedUnits = CeylonBuilder
.getProjectPhasedUnits(project.getProject());
if (projectPhasedUnits != null) {
PhasedUnit phasedUnit = null;
phasedUnit = projectPhasedUnits
.getPhasedUnitFromRelativePath(sourcePath);
if (phasedUnit != null) {
return phasedUnit;
}
}
for (Module module : CeylonBuilder
.getProjectExternalModules(project.getProject())) {
if (module instanceof BaseIdeModule) {
BaseIdeModule jdtModule = (BaseIdeModule) module;
if (jdtModule.getIsCeylonArchive()) {
PhasedUnit phasedUnit = jdtModule
.getPhasedUnitFromRelativePath(sourcePath);
if (phasedUnit != null) {
if (phasedUnit instanceof CrossProjectPhasedUnit) {
phasedUnit = ((CrossProjectPhasedUnit<IProject,IResource,IFolder,IFile>) phasedUnit)
.getOriginalProjectPhasedUnit();
}
return phasedUnit;
}
}
}
}
return null;
}
public static PhasedUnit getPhasedUnit(IJavaStackFrame frame) {
IJavaProject project = getProject(frame);
return project == null ? null : getPhasedUnit(frame, project);
}
public static Declaration getSourceDeclaration(
IJavaStackFrame frame) {
IJavaProject project = getProject(frame);
if (project == null) {
// Not a Ceylon debug target
return null;
}
IMethod method = getJavaMethod(frame, project);
PhasedUnit unit = getPhasedUnit(frame, project);
if (method != null && unit != null) {
return JavaSearch.toCeylonDeclaration(method, Arrays.asList(unit));
}
return null;
}
public static Declaration getSourceDeclaration(
IJavaObject object) {
IJavaProject project = getProject(object);
if (project == null) {
// Not a Ceylon debug target
return null;
}
IType type = getJavaType(object, project);
PhasedUnit unit = getPhasedUnit(object, project);
if (type != null) {
if (unit != null) {
Declaration result = JavaSearch.toCeylonDeclaration(type, Arrays.asList(unit));
return result;
}
}
return null;
}
public static Declaration getDeclaration(
IJavaObject object) {
Declaration declaration = null;
try {
declaration = getModelDeclaration(object);
} catch (DebugException e) {
e.printStackTrace();
}
if (declaration == null) {
declaration = getSourceDeclaration(object);
}
return declaration;
}
public static interface ProducedTypeAction<ReturnType extends IJavaValue> {
ReturnType doOnProducedType(IJavaObject producedType,
IJavaThread innerThread,
IProgressMonitor monitor) throws DebugException;
}
public static interface ProducedTypeRetriever {
IJavaObject retrieveType(IJavaObject javaObject,
boolean isMethod,
IJavaClassType metamodelType,
IJavaThread innerThread,
IProgressMonitor monitor) throws DebugException;
}
public static ProducedTypeRetriever producedTypeFromTypeDescriptor = new ProducedTypeRetriever() {
public IJavaObject retrieveType(IJavaObject typeDescriptor,
boolean isMethod,
IJavaClassType metamodelType,
IJavaThread innerThread,
IProgressMonitor monitor) throws DebugException {
if (typeDescriptor instanceof IJavaObject && ! (typeDescriptor instanceof JDINullValue)) {
IJavaValue producedType = metamodelType.sendMessage("getProducedType", "(Lcom/redhat/ceylon/compiler/java/runtime/model/TypeDescriptor;)Lcom/redhat/ceylon/model/typechecker/model/Type;", new IJavaValue[] {typeDescriptor}, innerThread);
if (producedType instanceof IJavaObject) {
return (IJavaObject) producedType;
}
}
return null;
}
};
public static ProducedTypeRetriever producedTypeFromInstance = new ProducedTypeRetriever() {
public IJavaObject retrieveType(IJavaObject instance,
boolean isMethod,
IJavaClassType metamodelType,
IJavaThread innerThread,
IProgressMonitor monitor) throws DebugException {
IJavaValue typeDescriptor = null;
boolean cancelTypeDescriptorRetrieval = false;
for(IVariable v : instance.getVariables()) {
if (v.getName().startsWith(Naming.Prefix.$reified$.name())) {
IValue reifiedTypeValue = v.getValue();
if (reifiedTypeValue == null || reifiedTypeValue instanceof JDINullValue) {
// The reified type arguments are not fully set,
// we are probably at the first line of the constructor.
// => Don't try to retrieve the produced type.
cancelTypeDescriptorRetrieval = true;
break;
}
}
}
if (! cancelTypeDescriptorRetrieval) {
try {
typeDescriptor = instance.sendMessage("$getType$", "()Lcom/redhat/ceylon/compiler/java/runtime/model/TypeDescriptor;", new IJavaValue[0], innerThread, null);
} catch(DebugException de) {
// the value surely doesn't implement ReifiedType
if (! isMethod) {
// Don't call getTypeDescriptor for objects that are in fact local Ceylon methods. It would trigger an exception
typeDescriptor = metamodelType.sendMessage("getTypeDescriptor", "(Ljava/lang/Object;)Lcom/redhat/ceylon/compiler/java/runtime/model/TypeDescriptor;", new IJavaValue[] {instance}, innerThread);
}
}
}
if (typeDescriptor instanceof IJavaObject) {
return producedTypeFromTypeDescriptor.retrieveType((IJavaObject) typeDescriptor, isMethod, metamodelType, innerThread, monitor);
}
return null;
}
};
public static ProducedTypeRetriever producedTypeItself = new ProducedTypeRetriever() {
public IJavaObject retrieveType(IJavaObject producedType,
boolean isMethod,
IJavaClassType metamodelType,
IJavaThread innerThread,
IProgressMonitor monitor) throws DebugException {
return producedType;
}
};
public static JDIClassType getMetaModelClass(JDIDebugTarget debugTarget, IJavaThread innerThread) {
IJavaType[] types = null;
try {
types = debugTarget.getJavaTypes(Metamodel.class.getName());
if (types == null || types.length == 0){
types = debugTarget.getJavaTypes(ClassLoader.class.getName());
if (types != null && types.length > 0) {
JDIClassType clClass = ((JDIClassType)types[0]);
IJavaValue res = clClass.sendMessage("getSystemClassLoader", "()Ljava/lang/ClassLoader;", new IJavaValue[0], innerThread);
if (res instanceof JDIObjectValue) {
JDIObjectValue systemCL = (JDIObjectValue) res;
types = debugTarget.getJavaTypes(Class.class.getName());
if (types != null && types.length > 0) {
JDIClassType klassClass = ((JDIClassType)types[0]);
IJavaValue metaModelName = JDIObjectValue.createValue(debugTarget, debugTarget.getVM().mirrorOf(Metamodel.class.getName()));
IJavaValue trueValue = JDIObjectValue.createValue(debugTarget, debugTarget.getVM().mirrorOf(true));
res = klassClass.sendMessage("forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;", new IJavaValue[] {metaModelName, trueValue, systemCL}, innerThread);
if (res instanceof JDIClassObjectValue) {
return (JDIClassType) ((JDIClassObjectValue) res).getInstanceType();
}
}
}
}
}
} catch (DebugException e) {
e.printStackTrace();
}
if (types != null && types.length > 0) {
return (JDIClassType) types[0];
}
return null;
}
@SuppressWarnings("unchecked")
public static <ReturnType extends IJavaValue> ReturnType doOnJdiProducedType(IValue value,
final ProducedTypeRetriever typeRetriever,
final ProducedTypeAction<ReturnType> postAction) {
if (value instanceof JDINullValue) {
return null;
}
if (value instanceof JDIObjectValue) {
JDIObjectValue objectValue = (JDIObjectValue) value;
try {
final IJavaReferenceType type = (IJavaReferenceType) objectValue.getJavaType();
final String typeName = type.getName();
if (typeName.endsWith("$impl")) {
IJavaFieldVariable thisField = objectValue.getField("$this", 0);
value = null;
if (thisField != null) {
IValue fieldValue = thisField.getValue();
if (fieldValue instanceof JDIObjectValue &&
!(fieldValue instanceof JDINullValue)) {
value = fieldValue;
objectValue = (JDIObjectValue) value;
}
}
}
if (value != null) {
final JDIObjectValue javaValue = objectValue;
final JDIDebugTarget debugTarget = objectValue.getJavaDebugTarget();
if (debugTarget instanceof CeylonJDIDebugTarget) {
CeylonJDIDebugTarget ceylonDebugTarget = (CeylonJDIDebugTarget) debugTarget;
final boolean isMethod = ceylonDebugTarget.isAnnotationPresent(type, com.redhat.ceylon.compiler.java.metadata.Method.class, 5000);
IJavaValue reifiedTypeInfo = ceylonDebugTarget.getEvaluationResult(new EvaluationRunner() {
@Override
public void run(IJavaThread innerThread, IProgressMonitor monitor,
EvaluationListener listener) throws DebugException {
JDIClassType metamodelType = getMetaModelClass(javaValue.getJavaDebugTarget(), innerThread);
IJavaValue producedType = null;
if (metamodelType != null) {
producedType = typeRetriever.retrieveType(javaValue, isMethod, metamodelType, innerThread, monitor);
}
if (producedType instanceof IJavaObject) {
listener.finished(postAction.doOnProducedType((IJavaObject)producedType, innerThread, monitor));
} else {
listener.finished(null);
}
}
}, 5000);
return (ReturnType)reifiedTypeInfo;
}
}
} catch (DebugException e) {
e.printStackTrace();
}
}
return null;
}
public static String getTypeName(IValue value, ProducedTypeRetriever typeRetriever) throws DebugException {
IJavaValue reifiedTypeNameValue = doOnJdiProducedType(value, typeRetriever, new ProducedTypeAction<IJavaValue>() {
@Override
public IJavaValue doOnProducedType(IJavaObject producedType,
IJavaThread innerThread, IProgressMonitor monitor)
throws DebugException {
if (producedType instanceof IJavaObject && ! (producedType instanceof JDINullValue)) {
IJavaValue producedTypeName = ((IJavaObject) producedType).sendMessage("asString", "()Ljava/lang/String;", new IJavaValue[] {}, innerThread, "Lcom/redhat/ceylon/model/typechecker/model/Type;");
return producedTypeName;
}
return null;
}
});
if (reifiedTypeNameValue instanceof JDIObjectValue && !(reifiedTypeNameValue instanceof JDINullValue)) {
String reifiedTypeName;
reifiedTypeName = reifiedTypeNameValue.getValueString();
return reifiedTypeName;
}
return null;
}
public static IJavaObject getJdiProducedType(IValue value, ProducedTypeRetriever typeRetriever) throws DebugException {
return doOnJdiProducedType(value, typeRetriever, new ProducedTypeAction<IJavaObject>() {
@Override
public IJavaObject doOnProducedType(IJavaObject producedType,
IJavaThread innerThread, IProgressMonitor monitor)
throws DebugException {
return producedType;
}
});
}
public static Declaration getModelDeclaration(IJavaObject jdiObject) throws DebugException {
return getModelDeclaration(jdiObject, null);
}
private static Declaration getModelDeclaration(IJavaObject jdiObject, IJavaThread evaluationThread) throws DebugException {
IJavaObject jdiDeclaration = getJdiDeclaration(jdiObject);
if (! (jdiDeclaration instanceof JDIObjectValue)) {
return null;
}
final CeylonJDIDebugTarget debugTarget = (CeylonJDIDebugTarget)((JDIObjectValue) jdiDeclaration).getJavaDebugTarget();
return toModelDeclaration(evaluationThread, debugTarget, jdiDeclaration);
}
public static Type getModelProducedType(IJavaObject jdiObject) throws DebugException {
return getModelProducedType(jdiObject, null);
}
private static Type getModelProducedType(IJavaObject jdiObject, IJavaThread evaluationThread) throws DebugException {
IJavaObject jdiProducedType = getJdiProducedType(jdiObject, producedTypeFromInstance);
if (! (jdiProducedType instanceof JDIObjectValue)) {
return null;
}
return toModelProducedType(jdiProducedType, evaluationThread);
}
public static Type toModelProducedType(IJavaObject jdiProducedType) throws DebugException {
return toModelProducedType(jdiProducedType, null);
}
private static Type toModelProducedType(IJavaObject jdiProducedType, IJavaThread evaluationThread) throws DebugException {
if (! (jdiProducedType instanceof JDIObjectValue)) {
return null;
}
final CeylonJDIDebugTarget debugTarget = (CeylonJDIDebugTarget)((JDIObjectValue) jdiProducedType).getJavaDebugTarget();
final IJavaObject jdiDeclaration = toJdiDeclaration(jdiProducedType);
Declaration declaration = toModelDeclaration(evaluationThread,
debugTarget, jdiDeclaration);
if (declaration instanceof TypeDeclaration) {
Unit unit = declaration.getUnit();
final TypeDeclaration typeDeclaration = (TypeDeclaration) declaration;
final List<TypeParameter> typeParameters = typeDeclaration.getTypeParameters();
final List<Type> typeArguments = new ArrayList<>(typeParameters.size());
ProducedTypeAction<IJavaValue> produceTypeAction = new ProducedTypeAction<IJavaValue>() {
@Override
public IJavaValue doOnProducedType(IJavaObject producedType,
IJavaThread innerThread, IProgressMonitor monitor)
throws DebugException {
IJavaObject producedTypeList = (IJavaObject) producedType.sendMessage("getTypeArgumentList", "()Ljava/util/List;", new IJavaValue[] {}, innerThread, null);
if (producedTypeList instanceof IJavaObject) {
IJavaValue size = ((IJavaObject)producedTypeList).sendMessage("size", "()I", new IJavaValue[] {}, innerThread, null);
if (size instanceof IJavaPrimitiveValue) {
final int intSize = ((IJavaPrimitiveValue)size).getIntValue();
if (intSize != typeParameters.size()) {
return debugTarget.newValue(false);
}
int i;
for (i = 0; i<intSize; i++) {
IJavaValue childTypeValue = ((IJavaObject)producedTypeList).sendMessage("get", "(I)Ljava/lang/Object;", new IJavaValue[] {debugTarget.newValue(i)}, innerThread, null);
if (childTypeValue instanceof IJavaObject) {
Type childType = toModelProducedType((IJavaObject)childTypeValue, innerThread);
if (childType == null) {
break;
}
typeArguments.add(childType);
}
}
if (i == intSize) {
return debugTarget.newValue(true);
}
}
}
return debugTarget.newValue(false);
}
};
IJavaValue result = null;
if (evaluationThread == null) {
result = doOnJdiProducedType(jdiProducedType, producedTypeItself, produceTypeAction);
} else {
result = produceTypeAction.doOnProducedType(jdiProducedType, evaluationThread, null);
}
if (result instanceof IJavaPrimitiveValue &&
((IJavaPrimitiveValue) result).getBooleanValue()) {
final Map<TypeParameter, Type> typeArgumentMap = new HashMap<>();
for (int i = 0; i< typeParameters.size(); i++) {
typeArgumentMap.put(typeParameters.get(i), typeArguments.get(i));
}
return new LazyType(unit) {
@Override
public Map<TypeParameter, Type> initTypeArguments() {
return typeArgumentMap;
}
@Override
public TypeDeclaration initDeclaration() {
return typeDeclaration;
}
};
}
}
return null;
}
public static Declaration toModelDeclaration(IJavaThread evaluationThread,
final CeylonJDIDebugTarget debugTarget,
final IJavaObject jdiDeclaration) throws DebugException {
if (jdiDeclaration == null) {
return null;
}
EvaluationRunner runner = new EvaluationRunner() {
@Override
public void run(IJavaThread innerThread, IProgressMonitor monitor,
EvaluationListener listener) throws DebugException {
IJavaValue qualifiedStringValue = jdiDeclaration.sendMessage("getQualifiedNameString", "()Ljava/lang/String;", new IJavaValue[0], innerThread, "Lcom/redhat/ceylon/model/typechecker/model/Declaration;");
listener.finished(qualifiedStringValue);
}
};
IJavaValue nameFieldValue = null;
if (evaluationThread == null) {
nameFieldValue = debugTarget.getEvaluationResult(runner, 5000);
} else {
EvaluationWaiter waiter = new EvaluationWaiter();
runner.run(evaluationThread, null, waiter);
nameFieldValue = waiter.waitForResult(5000);
}
IJavaProject javaProject = DebugUtils.getProject(jdiDeclaration);
BaseIdeModelLoader modelLoader = CeylonBuilder.getProjectModelLoader(javaProject.getProject());
String qualifiedName = nameFieldValue.getValueString();
String[] qualifiedNameParts = qualifiedName.split("::");
Declaration declaration = null;
if (qualifiedNameParts.length > 1) {
String packageName = modelLoader.getPackageNameForQualifiedClassName(qualifiedNameParts[0], qualifiedName);
Module module = modelLoader.lookupModuleByPackageName(packageName);
declaration = modelLoader.convertToDeclaration(module, qualifiedName.replace("::", "."), DeclarationType.TYPE);
}
return declaration;
}
public static IJavaObject toJdiDeclaration(IJavaObject jdiProducedType) throws DebugException {
if (jdiProducedType != null) {
return doOnJdiProducedType(jdiProducedType, producedTypeItself, new ProducedTypeAction<IJavaObject>() {
@Override
public IJavaObject doOnProducedType(
IJavaObject producedType, IJavaThread innerThread,
IProgressMonitor monitor) throws DebugException {
IJavaValue value = producedType.sendMessage("getDeclaration", "()Lcom/redhat/ceylon/model/typechecker/model/TypeDeclaration;", new IJavaValue[] {}, innerThread, null);
if (value instanceof IJavaObject) {
return (IJavaObject) value;
}
return null;
}
});
}
return null;
}
public static IJavaObject getJdiDeclaration(IValue value) throws DebugException {
IJavaObject jdiProducedType = getJdiProducedType(value, producedTypeFromInstance);
return toJdiDeclaration(jdiProducedType);
}
public static boolean isCeylonFrame(IJavaStackFrame frame) {
IDebugTarget debugTarget = frame.getDebugTarget();
if(debugTarget instanceof CeylonJDIDebugTarget) {
if (((CeylonJDIDebugTarget) debugTarget).isDebugAsJavaCode()) {
return false;
}
try {
if (frame.getSourceName() != null
&& frame.getSourceName().endsWith(".ceylon")) {
return true;
}
} catch (DebugException e) {
e.printStackTrace();
}
}
return false;
}
private final static String ABSTRACT_CALLABLE = AbstractCallable.class.getName();
private final static String LAZY_ITERABLE = LazyIterable.class.getName();
private final static String CEYLON_BOOLEAN = ceylon.language.Boolean.class.getName();
private final static String CEYLON_FALSE = ceylon.language.true_.class.getName();
private final static String CEYLON_TRUE = ceylon.language.false_.class.getName();
private final static String CEYLON_FLOAT = ceylon.language.Float.class.getName();
private final static String CEYLON_INTEGER = ceylon.language.Integer.class.getName();
private final static String CEYLON_BYTE = ceylon.language.Byte.class.getName();
private final static String CEYLON_STRING = ceylon.language.String.class.getName();
private final static String CEYLON_BASE_PACKAGE = "ceylon.language.impl.Base";
private final static String TYPE_DESCRIPTOR = TypeDescriptor.class.getName();
public static boolean isInternalCeylonMethod(Method method) {
return isJavaGeneratedMethodToStepThrough(method) ||
isCeylonGeneratedMethodToStepThrough(method) ||
isCeylonGeneratedMethodToSkipCompletely(method);
}
public static class JdiDefaultArgumentMethodSearch extends DefaultArgumentMethodSearch<Method> {
@Override
protected String getMethodName(Method method) {
return method.name();
}
@Override
protected boolean isMethodPrivate(Method method) {
return method.isPrivate();
}
@Override
protected List<Method> getMethodsOfDeclaringType(Method method, String searchedName) {
if (searchedName == null) {
return method.declaringType().methods();
} else {
return method.declaringType().methodsByName(searchedName);
}
}
@Override
protected List<String> getParameterNames(Method method) {
List<String> argumentNames = Collections.emptyList();
try {
List<LocalVariable> arguments = method.arguments();
argumentNames = new ArrayList<>(arguments.size());
for (LocalVariable arg : arguments) {
argumentNames.add(arg.name());
}
} catch (AbsentInformationException e) {
e.printStackTrace();
}
return argumentNames;
}
}
public static boolean isCeylonGeneratedMethodToStepThrough(Method method) {
Location location = method.location();
ReferenceType declaringType = location.declaringType();
final String methodName = method.name();
if (declaringType.name().startsWith(CEYLON_BASE_PACKAGE) && ! method.isConstructor()) {
return true;
}
if (methodName.equals(Naming.Unfix.$evaluate$.name())) {
if (declaringType instanceof ClassType) {
ClassType classType = (ClassType) declaringType;
String superClassName = classType.superclass().name();
if (LAZY_ITERABLE.equals(superClassName)) {
return true;
}
}
}
if (methodName.equals(Naming.Unfix.$call$.name())
|| methodName.equals(Naming.Unfix.$callvariadic$.name())
|| methodName.equals(Naming.Unfix.$calltyped$.name())) {
if (declaringType instanceof ClassType) {
ClassType classType = (ClassType) declaringType;
String superClassName = classType.superclass().name();
if (ABSTRACT_CALLABLE.equals(superClassName)) {
if (method.isSynthetic()) {
// some synthetic methods are generated by Javac apparently
return true;
}
List<Method> methods = classType.methodsByName(Naming.Unfix.$calltyped$.name());
if (methods != null && ! methods.isEmpty()) {
if (methodName.equals(Naming.Unfix.$call$.name())
|| methodName.equals(Naming.Unfix.$callvariadic$.name())) {
// they only delegate to $callTyped$
return true;
}
}
}
}
}
if (methodName.startsWith(Naming.Prefix.$default$.name())) {
CeylonJDIDebugTarget debugTarget = getDebugTarget();
return debugTarget != null
&& debugTarget.isStepFiltersEnabled()
&& debugTarget.isFiltersDefaultArgumentsCode();
}
JdiDefaultArgumentMethodSearch.Result searchResult =
new JdiDefaultArgumentMethodSearch().search(method);
if (searchResult.overloadedMethod != null) {
return true;
}
if (searchResult.defaultArgumentMethod != null) {
CeylonJDIDebugTarget debugTarget = getDebugTarget();
return debugTarget != null
&& debugTarget.isStepFiltersEnabled()
&& debugTarget.isFiltersDefaultArgumentsCode();
}
return false;
}
public static boolean isJavaGeneratedMethodToStepThrough(Method method) {
if (method.isSynthetic()) {
if (method.name().startsWith("access$")) {
return true;
}
}
if (method.isBridge()) {
return true;
}
return false;
}
public static boolean isMethodToStepThrough(Method method) {
return isJavaGeneratedMethodToStepThrough(method) ||
isCeylonGeneratedMethodToStepThrough(method);
}
public static boolean isCeylonGeneratedMethodToSkipCompletely(Method method) {
Location location = method.location();
ReferenceType declaringType = location.declaringType();
String declaringTypeName = declaringType.name();
final String methodName = method.name();
if (method.isStaticInitializer()) {
try {
if (location.sourceName() != null
&& ! location.sourceName().endsWith(".ceylon")) {
return false;
}
} catch (AbsentInformationException e1) {
e1.printStackTrace();
return false;
}
if (! declaringTypeName.endsWith("_")) {
return true;
}
if (! (declaringType instanceof ClassType)) {
return true;
}
try {
List<Location> locations = method.allLineLocations();
return locations.size() <= 1;
} catch (AbsentInformationException e) {
}
return false;
}
if (declaringTypeName.equals(CEYLON_BOOLEAN) ||
declaringTypeName.equals(CEYLON_FALSE) ||
declaringTypeName.equals(CEYLON_TRUE)) {
return true;
}
if (declaringTypeName.equals(CEYLON_INTEGER)
&& (methodName.equals("instance")
|| methodName.equals("longValue") || method
.isConstructor())) {
return true;
}
if (declaringTypeName.equals(CEYLON_FLOAT)
&& (methodName.equals("instance")
|| methodName.equals("doubleValue") || method
.isConstructor())) {
return true;
}
if (declaringTypeName.equals(CEYLON_BYTE)
&& (methodName.equals("instance")
|| methodName.equals("byteValue") || method
.isConstructor())) {
return true;
}
if (declaringTypeName.equals(CEYLON_STRING)
&& (methodName.equals("instance")
|| methodName.equals("toString") || (method
.isConstructor()
&& method.argumentTypeNames().size() == 1 && "java.lang.String"
.equals(method.argumentTypeNames().get(0))))) {
return true;
}
if (declaringTypeName.equals(ABSTRACT_CALLABLE)) {
return true;
}
if (declaringType.name().startsWith(CEYLON_BASE_PACKAGE) && method.isConstructor()) {
return true;
}
if (declaringTypeName
.startsWith(TYPE_DESCRIPTOR)) {
return true;
}
if (method.isConstructor()) {
if (declaringTypeName.endsWith("$impl")) {
return true;
} else if (declaringType instanceof ClassType) {
ClassType superClassType = ((ClassType) declaringType).superclass();
if (superClassType != null) {
if (ABSTRACT_CALLABLE.equals(superClassType.name())) {
return true;
} else if (LAZY_ITERABLE.equals(superClassType.name())) {
return true;
}
}
}
}
if ((methodName.startsWith("get") ||
methodName.startsWith("set"))
&& methodName.endsWith(Suffix.$priv$.name())) {
if (declaringType instanceof ClassType) {
ClassType classType = (ClassType) declaringType;
String fieldName = methodName.substring(3, methodName.length() - Suffix.$priv$.name().length());
fieldName = escaping_.get_().toInitialLowercase(fieldName);
Field field = classType.fieldByName(fieldName);
if (field != null) {
// we are stepping in a getter that simply returns the raw field value.
// Don't step then (or don't stop on breakpoints)
return true;
}
// if no such field is found, it means that it is a Ceylon getter or a lazy specifier, then take
// the location in account in step and breakpoint requests.
}
}
if (methodName.equals("$getType$")) {
return true;
}
if (methodName.equals("$getReifiedElement$")) {
return true;
}
try {
if (method.isStatic()
&& methodName.equals("get_")
&& method.argumentTypeNames().isEmpty()
&& method.returnType().name()
.equals(method.declaringType().name())) {
return true;
}
} catch (ClassNotLoadedException e) {}
return false;
}
public static boolean isJavaSystemMethodToSkip(Method method) {
Location location = method.location();
ReferenceType declaringType = location.declaringType();
String declaringTypeName = declaringType.name();
final String methodName = method.name();
if (declaringTypeName.equals("java.lang.ClassLoader")) {
return true;
}
if (declaringTypeName.equals("java.lang.System") &&
methodName.equals("getSecurityManager")) {
return true;
}
if (declaringTypeName.equals("java.lang.Object")
&& method.isConstructor()) {
return true;
}
return false;
}
public static boolean isMethodFiltered(Method method) {
return isCeylonGeneratedMethodToSkipCompletely(method) ||
isCeylonGeneratedMethodToStepThrough(method) ||
isJavaGeneratedMethodToStepThrough(method) ||
isJavaSystemMethodToSkip(method);
}
/**
* Returns the stack frame in which to search for variables, or <code>null</code>
* if none.
*
* @return the stack frame in which to search for variables, or <code>null</code>
* if none
*/
public static JDIStackFrame getFrame() {
IAdaptable adaptable = DebugUITools.getDebugContext();
if (adaptable != null) {
IJavaStackFrame stackFrame = (IJavaStackFrame)adaptable.getAdapter(IJavaStackFrame.class);
if (stackFrame instanceof JDIStackFrame) {
return (JDIStackFrame) stackFrame;
}
}
return null;
}
public static CeylonJDIDebugTarget getDebugTarget() {
JDIStackFrame stackFrame = getFrame();
if (stackFrame != null) {
JDIDebugTarget debugTarget = stackFrame.getJavaDebugTarget();
if (debugTarget instanceof CeylonJDIDebugTarget) {
return (CeylonJDIDebugTarget) debugTarget;
}
}
return null;
}
}