/******************************************************************************* * Copyright (c) 2012 Pivotal Software, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Pivotal Software, Inc. - initial API and implementation *******************************************************************************/ package org.grails.ide.eclipse.groovy.debug.core.evaluation; import org.codehaus.groovy.ast.ClassCodeVisitorSupport; import org.codehaus.groovy.ast.expr.ArgumentListExpression; import org.codehaus.groovy.ast.expr.BinaryExpression; import org.codehaus.groovy.ast.expr.ConstantExpression; import org.codehaus.groovy.ast.expr.MethodCallExpression; import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.syntax.Token; import org.codehaus.groovy.syntax.Types; import org.eclipse.debug.core.DebugException; import org.eclipse.jdt.debug.core.IJavaClassType; import org.eclipse.jdt.debug.core.IJavaPrimitiveValue; import org.eclipse.jdt.debug.core.IJavaValue; import org.eclipse.jdt.groovy.core.util.ReflectionUtils; /** * Static methods for overriding comparisons in evaluated scripts * This class exists because Groovy's mop does not let you take control over * operations like <. >, ==, etc. * Instead, we walk the Script ast and look for these operations. Replace them with * calls to methods in this class. * The problem is...how do we get this class loaded on the classpath??? * @author Andrew Eisenberg * @since 2.5.2 */ public class JDIComparator { public static class ComparatorVisitor extends ClassCodeVisitorSupport { /** * here, we capture comparisons * replace the LHS with a call to the appropriate method in * the JDIComparator. The RHS is replaced with 'false' * and the operator is replaced with '||' so that the * binary expression always returns the result of the LHS. */ @Override public void visitBinaryExpression(BinaryExpression expression) { super.visitBinaryExpression(expression); String compareName = JDIComparator.methodNameForToken(expression.getOperation()); if (compareName != null) { MethodCallExpression call = new MethodCallExpression( new VariableExpression("__comparator"), compareName, new ArgumentListExpression(expression.getLeftExpression(), expression.getRightExpression())); expression.setLeftExpression(call); expression.setRightExpression(ConstantExpression.FALSE); // arrgh...must use reflection to set the type Token t = expression.getOperation(); t.setText("||"); t.setMeaning(Types.LOGICAL_OR); ReflectionUtils.setPrivateField(Token.class, "type", t, Types.LOGICAL_OR); } } @Override protected SourceUnit getSourceUnit() { return null; } } private final JDITargetDelegate delegate; private final IJavaClassType scriptByteCodeAdapter; public JDIComparator(JDITargetDelegate delegate) throws DebugException { this.delegate = delegate; this.scriptByteCodeAdapter = (IJavaClassType) delegate.getType("org.codehaus.groovy.runtime.ScriptBytecodeAdapter"); } public boolean isLessThan(Object o1, Object o2) throws DebugException { return invoke("compareLessThan", o1, o2); } public boolean isGreaterThan(Object o1, Object o2) throws DebugException { return invoke("compareGreaterThan", o1, o2); } public boolean isLessThanOrEqual(Object o1, Object o2) throws DebugException { return invoke("compareLessThanEqual", o1, o2); } public boolean isGreaterThanOrEqual(Object o1, Object o2) throws DebugException { return invoke("compareGreaterThanEqual", o1, o2); } public boolean isEqual(Object o1, Object o2) throws DebugException { return invoke("compareEqual", o1, o2); } public boolean isNotEqual(Object o1, Object o2) throws DebugException { return invoke("compareNotEqual", o1, o2); } public boolean isIdentical(Object o1, Object o2) throws DebugException { return invoke("compareIdentical", o1, o2); } public boolean isNotIdentical(Object o1, Object o2) throws DebugException { return invoke("compareNotIdentical", o1, o2); } public Integer compareTo(Object o1, Object o2) throws DebugException { IJavaValue v1 = delegate.toJDIObject(o1); IJavaValue v2 = delegate.toJDIObject(o2); IJavaValue result = scriptByteCodeAdapter.sendMessage("compareTo", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Integer;", new IJavaValue[] { v1, v2 }, delegate.getThread()); return delegate.convertToInteger(result); } private boolean invoke(String methodName, Object o1, Object o2) throws DebugException { IJavaValue v1 = delegate.toJDIObject(o1); IJavaValue v2 = delegate.toJDIObject(o2); IJavaPrimitiveValue result = (IJavaPrimitiveValue) scriptByteCodeAdapter.sendMessage(methodName, "(Ljava/lang/Object;Ljava/lang/Object;)Z", new IJavaValue[] { v1, v2 }, delegate.getThread()); return result.getBooleanValue(); } public static String methodNameForToken(Token operation) { switch (operation.getType()) { case Types.COMPARE_LESS_THAN: return "isLessThan"; case Types.COMPARE_GREATER_THAN: return "isGreaterThan"; case Types.COMPARE_LESS_THAN_EQUAL: return "isLessThanOrEqual"; case Types.COMPARE_GREATER_THAN_EQUAL: return "isGreaterThanOrEqual"; case Types.COMPARE_EQUAL: return "isEqual"; case Types.COMPARE_NOT_EQUAL: return "isNotEqual"; case Types.COMPARE_IDENTICAL: return "isIdentical"; case Types.COMPARE_NOT_IDENTICAL: return "isNotIdentical"; case Types.COMPARE_TO: return "compareTo"; default: // don't convert this token type. return null; } } }