/* Copyright (c) 2009 Andr� Arnold. 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: Andr� Arnold - initial API and implementation */ package org.eclipse.xtend.backend.expr; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import org.eclipse.xtend.backend.common.BackendType; import org.eclipse.xtend.backend.common.BackendTypesystem; import org.eclipse.xtend.backend.common.ExecutionContext; import org.eclipse.xtend.backend.common.ExpressionBase; import org.eclipse.xtend.backend.common.SourcePos; import org.eclipse.xtend.backend.functions.java.internal.JavaBuiltinConverter; import org.eclipse.xtend.backend.functions.java.internal.JavaBuiltinConverterFactory; import org.eclipse.xtend.backend.functions.java.internal.ParameterConverter; import org.eclipse.xtend.backend.util.ErrorHandler; import org.eclipse.xtend.middleend.javaannotations.ExecutionContextAware; public class MethodInvocationExpression extends ExpressionBase { private final Method _mtd; private final boolean _isStatic; private final List<ParameterConverter> _parameterConverters = new ArrayList<ParameterConverter>(); private final JavaBuiltinConverter _returnValueConverter; private final List<? extends ExpressionBase> _params; private final boolean _nullIfFirstParamIsNull; private Class<?> _clazz; /** * An expression that invokes an already known method * * @param mtd * @param params * @param nullIfFirstParamIsNull * @param sourcePos */ public MethodInvocationExpression(Method mtd, List<? extends ExpressionBase> params, boolean nullIfFirstParamIsNull, SourcePos sourcePos) { super(sourcePos); _mtd = mtd; _clazz = mtd.getDeclaringClass(); _params = params; _nullIfFirstParamIsNull = nullIfFirstParamIsNull; for (int i=0; i<mtd.getParameterTypes().length; i++) { final ParameterConverter pc = JavaBuiltinConverterFactory.getParameterConverter (mtd.getParameterTypes()[i], i); if (pc != null) _parameterConverters.add(pc); } _returnValueConverter = JavaBuiltinConverterFactory.getConverter (mtd.getReturnType()); _isStatic = (mtd.getModifiers() & Modifier.STATIC) != 0; } public Method getMethod() { return _mtd; } public List<? extends ExpressionBase> getParams() { return _params; } public boolean isNullIfFirstParamIsNull() { return _nullIfFirstParamIsNull; } @Override protected Object evaluateInternal(ExecutionContext ctx) { final List<Object> params = new ArrayList<Object>(); for (ExpressionBase expr : _params) params.add(expr.evaluate(ctx)); // this is for "method-style" invocations: if the first parameter (i.e. // the one "before the dot") is null, // shortcut the evaluation and return null // // CAUTION - without this shortcut, the polymorphic resolution may be // ambiguous in unexpected ways if (_nullIfFirstParamIsNull && params.size() > 0 && params.get(0) == null) { ctx.logNullDeRef(getPos()); return null; } Object[] rawParams = params.toArray(new Object[params.size()]); try { // param Expressions must have been evaluated before method can fully qualified with it's param types for (int i = 0; i < _parameterConverters.size(); i++) { _parameterConverters.get(i).convert(rawParams); } Object o = getInstance(ctx); if (o instanceof ExecutionContextAware) { ((ExecutionContextAware) o).setExecutionContext(ctx); } final Object resultRaw = _mtd.invoke(o, rawParams); return _returnValueConverter.javaToBackend(resultRaw); } catch (Exception e) { final List<String> paramTypes = new ArrayList<String>(); for (Object p : params) { if (p == null) paramTypes.add(Void.TYPE.getName()); else paramTypes.add(p.getClass().getName()); } ErrorHandler.handle("could not invoke method " + _mtd + " with parameters " + params + " of types " + paramTypes, e); return null; // to make the compiler happy - this is never executed } } private Object getInstance(ExecutionContext ctx) { if (_isStatic) return null; try { Object result = ctx.getContributionStateContext().retrieveState( _clazz); if (result == null) { result = _clazz.newInstance(); ctx.getContributionStateContext().storeState(_clazz, result); } return result; } catch (Exception exc) { ErrorHandler.handle(exc); return null; // just for the compiler - this is never executed } } public static List<BackendType> guessParameterTypes(Method mtd, BackendTypesystem ts) { final List<BackendType> result = new ArrayList<BackendType>(); for (Class<?> cls : mtd.getParameterTypes()) result.add(ts.findType(cls)); return result; } public String getName() { return _mtd.getName(); } public boolean isStatic() { return _isStatic; } }