package com.redhat.ceylon.eclipse.core.debug.hover; import static com.redhat.ceylon.eclipse.code.hover.DocumentationHover.getDocumentationHoverText; import static com.redhat.ceylon.eclipse.core.debug.presentation.CeylonContentProviderFilter.unBoxIfVariableBoxed; import static com.redhat.ceylon.eclipse.core.debug.presentation.CeylonJDIModelPresentation.fixVariableName; import static com.redhat.ceylon.eclipse.util.Nodes.getReferencedDeclaration; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IValue; import org.eclipse.debug.core.model.IVariable; import org.eclipse.debug.ui.IDebugUIConstants; 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.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.debug.core.IJavaVariable; import org.eclipse.jdt.internal.debug.core.logicalstructures.JDIPlaceholderVariable; 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.JDILocalVariable; import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame; import org.eclipse.jdt.internal.debug.core.model.JDIThisVariable; import org.eclipse.jdt.internal.debug.core.model.JDIVariable; import org.eclipse.jdt.internal.debug.ui.IJDIPreferencesConstants; import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin; import org.eclipse.jdt.internal.debug.ui.JDIModelPresentation; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; import org.eclipse.swt.widgets.Shell; import com.redhat.ceylon.compiler.java.codegen.Naming; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.compiler.typechecker.tree.Tree.MemberOp; import com.redhat.ceylon.compiler.typechecker.tree.Tree.SafeMemberOp; import com.redhat.ceylon.eclipse.code.editor.CeylonEditor; import com.redhat.ceylon.eclipse.code.hover.SourceInfoHover; import com.redhat.ceylon.eclipse.core.debug.DebugUtils; 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.presentation.CeylonJDIModelPresentation; import com.redhat.ceylon.eclipse.util.JavaSearch; import com.redhat.ceylon.eclipse.util.Nodes; import com.redhat.ceylon.model.loader.NamingBase.Suffix; import com.redhat.ceylon.model.typechecker.model.ClassOrInterface; import com.redhat.ceylon.model.typechecker.model.Constructor; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.FunctionOrValue; import com.redhat.ceylon.model.typechecker.model.Referenceable; import com.redhat.ceylon.model.typechecker.model.Scope; 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.Value; import com.sun.jdi.ClassType; import com.sun.jdi.Method; public class CeylonDebugHover extends SourceInfoHover { public CeylonDebugHover(CeylonEditor editor) { super(editor); } public boolean isEnabled() { return DebugUtils.getFrame() != null; } public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { Object object = getHoverInfo2(textViewer, hoverRegion); if (object instanceof Object[]) { Object[] inputs = (Object[]) object; if (inputs[0] instanceof IVariable) { IVariable var = (IVariable) inputs[0]; return getVariableText(var); } } return null; } /* private IVariable resolveCeylonVariable(IJavaStackFrame frame, ITextViewer textViewer, IRegion hoverRegion) { if (frame != null) { try { IDocument document= textViewer.getDocument(); if (document != null) { String variableName= document.get(hoverRegion.getOffset(), hoverRegion.getLength()); return findCeylonVariable(frame, variableName); } } catch (BadLocationException x) { } } return null; } */ private static Pattern enclosingObjectPattern = Pattern.compile("^this\\$([0-9]+)$"); public static IJavaObject getEnclosingObject(IJavaObject object) { TreeMap<Integer, IJavaObject> enclosingScopes = new TreeMap<Integer, IJavaObject>(); if (object == null) { return null; } try { for (IVariable var : object.getVariables()) { if (var instanceof IJavaFieldVariable && var.getName().startsWith("this$")) { Matcher matcher = enclosingObjectPattern.matcher(var.getName()); if (matcher.matches()) { IValue value = var.getValue(); if (value instanceof IJavaObject) { String numberString = matcher.group(1); try { Integer number = Integer.parseInt(numberString); enclosingScopes.put(number, (IJavaObject)value); } catch(NumberFormatException e) { e.printStackTrace(); } } } } } } catch (DebugException e) { e.printStackTrace(); } if (! enclosingScopes.isEmpty()) { return enclosingScopes.lastEntry().getValue(); } return null; } private static IJavaVariable findCeylonFieldVariable(IJavaVariable object, String variableName, boolean useFixedName) { try { return findCeylonField((IJavaValue)object.getValue(), variableName, useFixedName); } catch (DebugException e) { e.printStackTrace(); } return null; } private static IJavaVariable findCeylonField(IJavaValue object, String variableName) { return findCeylonField(object, variableName, true); } private static IJavaVariable findCeylonField(IJavaValue object, String variableName, boolean useFixedName) { IVariable[] thisChildren; try { thisChildren = object.getVariables(); for (IVariable element : thisChildren) { IJavaVariable var = (IJavaVariable) element; String searchedName = useFixedName ? fixVariableName(var.getName(), var instanceof JDILocalVariable, var.isSynthetic()) : var.getName(); if (variableName.equals(searchedName)) { return var; } } } catch (DebugException e) { if (e.getStatus().getCode() != IJavaThread.ERR_THREAD_NOT_SUSPENDED) { JDIDebugUIPlugin.log(e); } } return null; } /** * Returns a local variable in the given frame based on the the given name * or <code>null</code> if none. * * @return local variable or <code>null</code> */ private static IJavaVariable findCeylonVariable(IJavaStackFrame frame, String variableName, boolean useFixedName) { if (frame != null && variableName != null) { try { if (frame.isNative()) { return null; } IVariable[] variables = frame.getVariables(); List<IJavaVariable> possibleMatches = new ArrayList<IJavaVariable>(); IJavaVariable thisVariable = null; for (IVariable variable : variables) { IJavaVariable var = (IJavaVariable) variable; String searchedName = useFixedName ? fixVariableName(var.getName(), var instanceof JDILocalVariable, var.isSynthetic()) : var.getName(); if (variableName.equals(searchedName)) { possibleMatches.add(var); } if (var instanceof JDIThisVariable) { // save for later - check for instance and static variables thisVariable = var; } } for(IJavaVariable variable: possibleMatches){ // Local Variable has more preference than Field Variable if(variable instanceof JDILocalVariable){ return variable; } } if(possibleMatches.size() > 0) { return possibleMatches.get(0); } if (thisVariable != null) { return findCeylonFieldVariable(thisVariable, variableName, useFixedName); } return null; } catch (DebugException x) { if (x.getStatus().getCode() != IJavaThread.ERR_THREAD_NOT_SUSPENDED) { JDIDebugUIPlugin.log(x); } } } return null; } private static IJavaVariable findCeylonVariable(IJavaStackFrame frame, String variableName) { return findCeylonVariable(frame, variableName, true); } /** * Returns HTML text for the given variable */ private static String getVariableText(IVariable variable) { StringBuffer buffer= new StringBuffer(); CeylonJDIModelPresentation modelPresentation = getModelPresentation(); buffer.append("<p><pre>"); //$NON-NLS-1$ String variableText= modelPresentation.getVariableText((IJavaVariable) variable); buffer.append(replaceHTMLChars(variableText)); buffer.append("</pre></p>"); //$NON-NLS-1$ modelPresentation.dispose(); if (buffer.length() > 0) { return buffer.toString(); } return null; } /** * Replaces reserved HTML characters in the given string with * their escaped equivalents. This is to ensure that variable * values containing reserved characters are correctly displayed. */ private static String replaceHTMLChars(String variableText) { StringBuffer buffer= new StringBuffer(variableText.length()); char[] characters = variableText.toCharArray(); for (int i = 0; i < characters.length; i++) { char character= characters[i]; switch (character) { case '<': buffer.append("<"); //$NON-NLS-1$ break; case '>': buffer.append(">"); //$NON-NLS-1$ break; case '&': buffer.append("&"); //$NON-NLS-1$ break; case '"': buffer.append("""); //$NON-NLS-1$ break; default: buffer.append(character); } } return buffer.toString(); } /** * Returns a configured model presentation for use displaying variables. */ private static CeylonJDIModelPresentation getModelPresentation() { CeylonJDIModelPresentation presentation = new CeylonJDIModelPresentation(); String[][] booleanPrefs= { {IJDIPreferencesConstants.PREF_SHOW_QUALIFIED_NAMES, JDIModelPresentation.DISPLAY_QUALIFIED_NAMES}}; String viewId= IDebugUIConstants.ID_VARIABLE_VIEW; for (int i = 0; i < booleanPrefs.length; i++) { boolean preferenceValue = getBooleanPreferenceValue(viewId, booleanPrefs[i][0]); presentation.setAttribute(booleanPrefs[i][1], (preferenceValue ? Boolean.TRUE : Boolean.FALSE)); } return presentation; } /** * Returns the value of this filters preference (on/off) for the given * view. * * @param part * @return boolean */ public static boolean getBooleanPreferenceValue(String id, String preference) { String compositeKey = id + "." + preference; //$NON-NLS-1$ IPreferenceStore store = JDIDebugUIPlugin.getDefault().getPreferenceStore(); boolean value = false; if (store.contains(compositeKey)) { value = store.getBoolean(compositeKey); } else { value = store.getBoolean(preference); } return value; } public IInformationControlCreator getHoverControlCreator() { return new ExpressionInformationControlCreator() { @Override public ExpressionInformationControl createInformationControl(Shell shell) { ExpressionInformationControl control = new ExpressionInformationControl(shell, false) { @Override public ExpressionInformationControlCreator getInformationPresenterControlCreator() { return new ExpressionInformationControlCreator() { @Override public ExpressionInformationControl createInformationControl(Shell shell) { ExpressionInformationControl enrichedControl = new ExpressionInformationControl(shell, true); enrichedControl.addLocationListener(new CeylonLocationListener(editor, enrichedControl)); return enrichedControl; } }; } }; control.addLocationListener(new CeylonLocationListener(editor, control)); return control; } }; } // copied from the compiler project since it is not visible private static String getErasedGetterName(Declaration decl) { String property = decl.getName(); // ERASURE if (!(decl instanceof Value) || ((Value)decl).isShared()) { if ("hash".equals(property)) { return "hashCode"; } else if ("string".equals(property)) { return "toString"; } } @SuppressWarnings("deprecation") String getterName = Naming.getGetterName(property); if (decl.isMember() && !decl.isShared()) { getterName = Naming.suffixName(Suffix.$priv$, getterName); } return getterName; } public static IJavaVariable findClassFieldOrAttribute(Declaration searchedDeclaration, ClassOrInterface searchedClassDeclaration, IJavaObject jdiObject, ClassOrInterface currentClassDeclaration) { if (jdiObject == null || currentClassDeclaration == null) { return null; } if (searchedClassDeclaration.equals(currentClassDeclaration)) { do { Declaration objectDeclaration = DebugUtils.getDeclaration(jdiObject); if (objectDeclaration instanceof Value) { Value valueDeclaration = (Value) objectDeclaration; TypeDeclaration valueTypeDeclaration = valueDeclaration.getTypeDeclaration(); if (valueTypeDeclaration.getQualifiedNameString().equals(valueDeclaration.getQualifiedNameString())) { objectDeclaration = valueTypeDeclaration; } } if (objectDeclaration instanceof ClassOrInterface) { if (objectDeclaration.equals(searchedClassDeclaration)) { break; } else if (((ClassOrInterface)objectDeclaration).inherits(searchedClassDeclaration)) { if (!(searchedDeclaration instanceof TypeParameter)) { searchedDeclaration = objectDeclaration.getMember(searchedDeclaration.getName(), Collections.<Type>emptyList(), false); } break; } } IJavaObject enclosingJdiObject = getEnclosingObject(jdiObject); if (enclosingJdiObject == jdiObject) { jdiObject = null; } else { jdiObject = enclosingJdiObject; } } while (jdiObject != null); if (jdiObject == null) { return null; } if (searchedDeclaration == null) { return null; } if (searchedDeclaration instanceof TypeParameter) { IVariable[] thisChildren; String searchedName = Naming.Prefix.$reified$ + searchedDeclaration.getName(); JDIDebugTarget debugTarget = DebugUtils.getDebugTarget(); if (debugTarget != null) { try { thisChildren = jdiObject.getVariables(); for (IVariable field : thisChildren) { if (searchedName.equals(field.getName())) { return (IJavaVariable) field; } } } catch (DebugException e) { if (e.getStatus().getCode() != IJavaThread.ERR_THREAD_NOT_SUSPENDED) { JDIDebugUIPlugin.log(e); } } } } else { } final IJavaObject currentClassScope = jdiObject; if (searchedDeclaration instanceof Value) { // values with getters CeylonJDIDebugTarget debugTarget = DebugUtils.getDebugTarget(); if (debugTarget != null) { final String getterName = getErasedGetterName(searchedDeclaration); try { ClassType classJDIType = (ClassType) ((JDIClassType)currentClassScope.getJavaType()).getUnderlyingType(); for (Method m : classJDIType.allMethods()) { if (m.name().equals(getterName)) { if (m.argumentTypeNames().size() > 0) { continue; } final String signature = m.signature(); IJavaValue result = debugTarget.getEvaluationResult(new EvaluationRunner() { @Override public void run(IJavaThread innerThread, IProgressMonitor monitor, EvaluationListener listener) throws DebugException { listener.finished( currentClassScope.sendMessage( getterName, signature, new IJavaValue[0], innerThread, false)); } }, 5000); if (result != null) { return new JDIPlaceholderVariable(searchedDeclaration.getName(), result); } } } } catch (DebugException e) { e.printStackTrace(); } } } // Simple fields return findCeylonField(currentClassScope, searchedDeclaration.getName()); } else { ClassOrInterface containerClassDeclaration = getContainingClassOrInterface(currentClassDeclaration); return findClassFieldOrAttribute(searchedDeclaration, searchedClassDeclaration, jdiObject, (ClassOrInterface) containerClassDeclaration); } } public static ClassOrInterface getContainingClassOrInterface( Declaration declaration) { Declaration containerClassDeclaration = null; do { declaration = JavaSearch.getContainingDeclaration(declaration); if (declaration instanceof ClassOrInterface) { containerClassDeclaration = declaration; break; } } while(declaration != null); return (ClassOrInterface) containerClassDeclaration; } /* (non-Javadoc) * @see org.eclipse.jface.text.ITextHoverExtension2#getHoverInfo2(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion) */ public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) { Node node = null; if (editor != null) { node = getHoverNode(hoverRegion, editor.getParseController()); } IJavaVariable var = jdiVariableForNode(textViewer, hoverRegion, node); if (var!=null) { String text = null; Referenceable model = getReferencedDeclaration(node); if (model!=null) { text = getDocumentationHoverText(model, editor, node, null); } return new DebugHoverInput(var, text); } else { return null; } } private IJavaVariable jdiVariableForNode(ITextViewer textViewer, IRegion hoverRegion, Node node) { JDIStackFrame frame = DebugUtils.getFrame(); if (frame != null && frame.getDebugTarget() instanceof CeylonJDIDebugTarget) { try { final CeylonJDIDebugTarget debugTarget = (CeylonJDIDebugTarget) frame.getDebugTarget(); // first check for 'this' - code resolve does not resolve java elements for 'this' IDocument document = textViewer.getDocument(); if (document != null) { try { String variableName = document.get(hoverRegion.getOffset(), hoverRegion.getLength()); if (variableName.equals("this")) { //$NON-NLS-1$ IJavaVariable variable = frame.findVariable(variableName); if (variable != null) { return variable; } } } catch (BadLocationException e) { return null; } } return jdiVariableForNode(debugTarget, frame, node); } catch (DebugException e) { return null; } } return null; } // public static IJavaVariable jdiVariableForNode(Node node) { // JDIStackFrame frame = DebugUtils.getFrame(); // if (frame != null // && frame.getDebugTarget() instanceof CeylonJDIDebugTarget) { // try { // final CeylonJDIDebugTarget debugTarget = // (CeylonJDIDebugTarget) frame.getDebugTarget(); // return jdiVariableForNode(debugTarget, frame, node); // } // catch (DebugException e) { // return null; // } // } // return null; // } private static IJavaVariable jdiVariableForNode(CeylonJDIDebugTarget debugTarget, JDIStackFrame frame, Node node) throws DebugException { if (node instanceof Tree.QualifiedMemberExpression) { return jdiVariableForQualifierMemberExpression(debugTarget, frame, (Tree.QualifiedMemberExpression)node); } if (node != null) { return jdiVariableForSimpleIdentifierNode(debugTarget, frame, node); } return null; } private static IJavaVariable jdiVariableForQualifierMemberExpression(CeylonJDIDebugTarget debugTarget, JDIStackFrame frame, Tree.QualifiedMemberExpression node) throws DebugException { Tree.QualifiedMemberExpression qualifiedMember = (Tree.QualifiedMemberExpression) node; Tree.MemberOperator memberOperator = qualifiedMember.getMemberOperator(); if (memberOperator instanceof MemberOp || memberOperator instanceof SafeMemberOp ) { Declaration qualifiedMemberDeclaration = qualifiedMember.getDeclaration(); Declaration container = JavaSearch.getContainingDeclaration(qualifiedMemberDeclaration); if (container instanceof ClassOrInterface) { Tree.Primary primary = qualifiedMember.getPrimary(); String prefix = ""; IJavaValue primaryValue = null; if (primary instanceof Tree.Literal) { String literalText = primary.getText(); prefix = literalText; if (primary instanceof Tree.NaturalLiteral) { primaryValue = debugTarget.newValue(Long.parseLong(literalText)); } else if (primary instanceof Tree.CharLiteral) { primaryValue = debugTarget.newValue(literalText.charAt(0)); } else if (primary instanceof Tree.FloatLiteral) { primaryValue = debugTarget.newValue(Float.parseFloat(literalText)); } else if (primary instanceof Tree.StringLiteral) { primaryValue = debugTarget.newValue(literalText); } } else { IJavaVariable primaryVariable = jdiVariableForNode(debugTarget, frame, primary); if (primaryVariable == null) { return null; } primaryValue = (IJavaValue) primaryVariable.getValue(); prefix = CeylonJDIModelPresentation.fixVariableName(primaryVariable.getName(), primaryVariable.isLocal(), primaryVariable.isSynthetic()); } IJavaObject primaryJdiObject = makeConsistentWithModel(debugTarget, primaryValue, primary.getTypeModel()); if (primaryJdiObject != null) { return unBoxIfVariableBoxed( findClassFieldOrAttribute( qualifiedMemberDeclaration, (ClassOrInterface) container, primaryJdiObject, (ClassOrInterface) container), prefix + memberOperator.getText()); } } } return null; } private static IJavaObject createCeylonObject( CeylonJDIDebugTarget debugTarget, String typeName, final String constructorSignature, final IJavaValue primitiveValue) throws DebugException { IJavaType[] types = debugTarget.getJavaTypes(typeName); if (types.length > 0 && types[0] instanceof IJavaClassType) { final IJavaClassType type = (IJavaClassType) types[0]; if (type != null) { return (IJavaObject) debugTarget.getEvaluationResult(new EvaluationRunner() { @Override public void run(IJavaThread innerThread, IProgressMonitor monitor, EvaluationListener listener) throws DebugException { listener.finished(type.newInstance(constructorSignature, new IJavaValue[] {primitiveValue}, innerThread)); } }, 5000); } } return null; } private static IJavaObject makeConsistentWithModel(CeylonJDIDebugTarget debugTarget, IJavaValue primaryJdiValue, Type modelProducedType) throws DebugException { IJavaType jdiType = primaryJdiValue.getJavaType(); IJavaObject primaryJdiObject = null; String jdiTypeQualifiedName = jdiType.getName(); switch (jdiTypeQualifiedName) { case "java.lang.String": if ("ceylon.language::String".equals(modelProducedType.asQualifiedString())) { primaryJdiObject = createCeylonObject(debugTarget, "ceylon.language.String", "(Ljava/lang/String;)V", primaryJdiValue); } break; case "long": primaryJdiObject = createCeylonObject(debugTarget, "ceylon.language.Integer", "(J)V", primaryJdiValue); break; case "byte": primaryJdiObject = createCeylonObject(debugTarget, "ceylon.language.Byte", "(B)V", primaryJdiValue); break; case "char": primaryJdiObject = createCeylonObject(debugTarget, "ceylon.language.Character", "(C)V", primaryJdiValue); break; case "double": primaryJdiObject = createCeylonObject(debugTarget, "ceylon.language.Float", "(D)V", primaryJdiValue); break; case "boolean": primaryJdiObject = createCeylonObject(debugTarget, "ceylon.language.Boolean", "(Z)V", primaryJdiValue); break; } if (primaryJdiObject == null) { if (primaryJdiValue instanceof IJavaObject) { primaryJdiObject = (IJavaObject) primaryJdiValue; } else { return null; } } Declaration primaryObjectClassDeclaration = DebugUtils.getModelDeclaration(primaryJdiObject); if (! (primaryObjectClassDeclaration instanceof ClassOrInterface)) { return null; } return primaryJdiObject; } public static IJavaVariable jdiVariableForTypeParameter(JDIDebugTarget debugTarget, JDIStackFrame frame, TypeParameter typeParameter) throws DebugException { Declaration container = JavaSearch.getContainingDeclaration(typeParameter); Declaration frameMethodDeclaration = DebugUtils.getSourceDeclaration(frame); if (container != null && frameMethodDeclaration != null) { String variableName = Naming.Prefix.$reified$ + typeParameter.getName(); if (container.equals(frameMethodDeclaration)) { return findCeylonVariable(frame, variableName, false); } else if (container instanceof FunctionOrValue && ! frame.isStatic()) { return findCeylonCapturedVariable(frame, variableName, false); } else if (container instanceof ClassOrInterface){ if (!frame.isStatic()) { IJavaObject thisObject = frame.getThis(); return unBoxIfVariableBoxed(findClassFieldOrAttribute(typeParameter, (ClassOrInterface) container, thisObject, getContainingClassOrInterface(frameMethodDeclaration))); } // case of attributes or getters of the class or an outer class } } return null; } private static IJavaVariable jdiVariableForSimpleIdentifierNode(CeylonJDIDebugTarget debugTarget, JDIStackFrame frame, Node node) throws DebugException { Referenceable referenceable = Nodes.getReferencedDeclaration(node); if (referenceable instanceof Declaration) { Declaration declaration = (Declaration) referenceable; Declaration container = JavaSearch.getContainingDeclaration(declaration); if (container instanceof Constructor && ((Constructor)container).getName() == null) { container = JavaSearch.getContainingDeclaration(container); // case of the default constructor } Declaration frameMethodDeclaration = DebugUtils.getSourceDeclaration(frame); // redescendre en utilisant le field this$x (si on est anonyme) de la classe de la méthode // => Faire une fonction qui retourne dans le scope du dessous (classe, OK, ... ou méthode et là on utilise les val$... qui avac un peu de chance sont capturés explicitement par le compilateur Ceylon ???) if (container != null && frameMethodDeclaration != null) { String variableName = declaration.getName(); if (frameMethodDeclaration instanceof Value && ((Value)frameMethodDeclaration).isParameter()) { Scope frameMethodContainer = frameMethodDeclaration.getContainer(); if (frameMethodContainer instanceof Declaration) { frameMethodDeclaration = (Declaration) frameMethodContainer; } } if (container.equals(frameMethodDeclaration)) { return unBoxIfVariableBoxed(findCeylonVariable(frame, variableName)); } else if (container instanceof FunctionOrValue && ! frame.isStatic()) { return unBoxIfVariableBoxed(findCeylonCapturedVariable(frame, variableName)); } else if (container instanceof ClassOrInterface && ! (declaration instanceof TypeParameter)){ if (!frame.isStatic()) { IJavaObject thisObject = frame.getThis(); return unBoxIfVariableBoxed(findClassFieldOrAttribute(declaration, (ClassOrInterface) container, thisObject, getContainingClassOrInterface(frameMethodDeclaration))); } // case of attributes or getters of the class or an outer class } } else { if (declaration instanceof Value && ((Value)declaration).isToplevel()) { IJavaType[] types; types = frame.getJavaDebugTarget().getJavaTypes(declaration.getQualifiedNameString() .replace("::", ".") + "_"); if (types != null && types.length > 0 && (types[0] instanceof JDIClassType)) { final JDIClassType classType = (JDIClassType) types[0]; final String typeSignature = classType.getField("value").getSignature(); final String methodSignature = "()" + typeSignature; final IJavaObject[] arguments = new IJavaObject[0]; IJavaValue result = debugTarget.getEvaluationResult(new EvaluationRunner() { @Override public void run(IJavaThread innerThread, IProgressMonitor monitor, EvaluationListener listener) throws DebugException { IJavaValue result = classType.sendMessage("get_", methodSignature, arguments, innerThread); listener.finished(result); } }, 5000); if (result != null) { return new JDIPlaceholderVariable(declaration.getName(), result); } } return null; } } } return null; } private static IJavaVariable findCeylonCapturedVariable(IJavaStackFrame frame, String variableName) { return findCeylonCapturedVariable(frame, variableName, true); } private static IJavaVariable findCeylonCapturedVariable(IJavaStackFrame frame, String variableName, boolean useFixedName) { if (frame != null && variableName != null) { try { if (frame.isNative()) { return null; } if (frame.isStatic()) { return null; } IJavaObject thisObject = frame.getThis(); if (thisObject == null) { return null; } IVariable[] variables = frame.getThis().getVariables(); for (IVariable variable : variables) { if (variable instanceof JDIVariable) { JDIVariable var = (JDIVariable) variable; if (var.isSynthetic()) { String varName = var.getName(); if (varName != null && varName.startsWith("val$")) { String searchedName = useFixedName ? fixVariableName(varName.substring(4), false, true) : varName.substring(4); if (variableName.equals(searchedName)) { return var; } } } } } } catch (DebugException x) { if (x.getStatus().getCode() != IJavaThread.ERR_THREAD_NOT_SUSPENDED) { JDIDebugUIPlugin.log(x); } } } return null; } }