/******************************************************************************* * Copyright (c) 2009-2013 CWI * 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: * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI * * Tijs van der Storm - Tijs.van.der.Storm@cwi.nl * * Emilie Balland - (CWI) * * Paul Klint - Paul.Klint@cwi.nl - CWI * * Mark Hills - Mark.Hills@cwi.nl (CWI) * * Arnold Lankamp - Arnold.Lankamp@cwi.nl * * Anya Helene Bagge - anya@ii.uib.no (UiB) *******************************************************************************/ package org.rascalmpl.interpreter.result; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import java.util.Map; import org.rascalmpl.ast.Expression; import org.rascalmpl.ast.FunctionDeclaration; import org.rascalmpl.ast.Tag; import org.rascalmpl.interpreter.IEvaluator; import org.rascalmpl.interpreter.StackTrace; import org.rascalmpl.interpreter.asserts.ImplementationError; import org.rascalmpl.interpreter.control_exceptions.MatchFailed; import org.rascalmpl.interpreter.control_exceptions.Throw; import org.rascalmpl.interpreter.env.Environment; import org.rascalmpl.interpreter.staticErrors.StaticError; import org.rascalmpl.interpreter.types.FunctionType; import org.rascalmpl.interpreter.utils.JavaBridge; import org.rascalmpl.interpreter.utils.Names; import org.rascalmpl.interpreter.utils.RuntimeExceptionFactory; import org.rascalmpl.uri.URIUtil; import org.rascalmpl.value.ISourceLocation; import org.rascalmpl.value.IValue; import org.rascalmpl.value.type.Type; public class JavaMethod extends NamedFunction { private final Object instance; private final Method method; private final boolean hasReflectiveAccess; private final JavaBridge javaBridge; public JavaMethod(IEvaluator<Result<IValue>> eval, FunctionDeclaration func, boolean varargs, Environment env, JavaBridge javaBridge){ this(eval, (FunctionType) func.getSignature().typeOf(env, true, eval), func,isDefault(func), hasTestMod(func.getSignature()), varargs, env, javaBridge); } @Override public Type getFormals() { // get formals is overridden because we can provide labeled parameters for JavaMethods (no pattern matching) FunctionDeclaration func = (FunctionDeclaration) getAst(); List<Expression> formals = func.getSignature().getParameters().getFormals().getFormals(); int arity = formals.size(); Type[] types = new Type[arity]; String[] labels = new String[arity]; FunctionType ft = getFunctionType(); for (int i = 0; i < arity; i++) { types[i] = ft.getFieldType(i); assert formals.get(i).isTypedVariable(); // Java methods only have normal parameters as in `int i, int j` labels[i] = Names.name(formals.get(i).getName()); } return TF.tupleType(types, labels); } /* * This one is to be called by cloneInto only, to avoid * looking into the environment again for obtaining the type. * (cloneInto is called when a moduleEnv is extended, so it * might not be finished yet, and hence not have all the * require types. */ private JavaMethod(IEvaluator<Result<IValue>> eval, FunctionType type, FunctionDeclaration func, boolean varargs, boolean isTest, boolean isDefault, Environment env, JavaBridge javaBridge){ super(func, eval, type , getFormals(func), Names.name(func.getSignature().getName()), isDefault, isTest, varargs, env); this.javaBridge = javaBridge; this.hasReflectiveAccess = hasReflectiveAccess(func); this.instance = javaBridge.getJavaClassInstance(func); this.method = javaBridge.lookupJavaMethod(eval, func, env, hasReflectiveAccess); } @Override public JavaMethod cloneInto(Environment env) { JavaMethod jm = new JavaMethod(getEval(), getFunctionType(), (FunctionDeclaration)getAst(), isDefault, isTest, hasVarArgs, env, javaBridge); jm.setPublic(isPublic()); return jm; } @Override public boolean isStatic() { return true; } private boolean hasReflectiveAccess(FunctionDeclaration func) { for (Tag tag : func.getTags().getTags()) { if (Names.name(tag.getName()).equals("reflect")) { return true; } } return false; } @Override public Result<IValue> call(Type[] actualTypes, IValue[] actuals, Map<String, IValue> keyArgValues) { Result<IValue> resultValue = getMemoizedResult(actuals, keyArgValues); if (resultValue != null) { return resultValue; } Type actualTypesTuple; Type formals = getFormals(); Object[] oActuals; if (hasVarArgs) { oActuals = computeVarArgsActuals(actuals, formals); } else { oActuals = actuals; } if (hasVarArgs) { actualTypesTuple = computeVarArgsActualTypes(actualTypes, formals); } else { actualTypesTuple = TF.tupleType(actualTypes); } if (!actualTypesTuple.isSubtypeOf(formals)) { // resolve overloading throw new MatchFailed(); } oActuals = addKeywordActuals(oActuals, formals, keyArgValues); if (hasReflectiveAccess) { oActuals = addCtxActual(oActuals); } if (callTracing) { printStartTrace(actuals); } Environment old = ctx.getCurrentEnvt(); try { ctx.pushEnv(getName()); Environment env = ctx.getCurrentEnvt(); bindTypeParameters(actualTypesTuple, formals, env); IValue result = invoke(oActuals); Type resultType = getReturnType().instantiate(env.getTypeBindings()); resultValue = ResultFactory.makeResult(resultType, result, eval); storeMemoizedResult(actuals, keyArgValues, resultValue); printEndTrace(resultValue.value); return resultValue; } catch (Throwable e) { printExcept(e); throw e; } finally { if (callTracing) { callNesting--; } ctx.unwind(old); } } private Object[] addCtxActual(Object[] oActuals) { Object[] newActuals = new Object[oActuals.length + 1]; System.arraycopy(oActuals, 0, newActuals, 0, oActuals.length); newActuals[oActuals.length] = ctx; return newActuals; } protected Object[] addKeywordActuals(Object[] oldActuals, Type formals, Map<String, IValue> keyArgValues) { if (!getFunctionType().hasKeywordParameters()) { return oldActuals; } Environment env = new Environment(declarationEnvironment, URIUtil.rootLocation("initializer"), "keyword parameter initializer"); Environment old = ctx.getCurrentEnvt(); try { // we set up an environment to hold the positional parameter values ctx.setCurrentEnvt(env); for (int i = 0; i < formals.getArity(); i++) { String fieldName = formals.getFieldName(i); Type fieldType = formals.getFieldType(i); env.declareVariable(fieldType, fieldName); env.storeLocalVariable(fieldName, ResultFactory.makeResult(fieldType, (IValue) oldActuals[i], ctx)); } // then we initialize the keyword parameters in this environment Type kwType = getFunctionType().getKeywordParameterTypes(); int amountOfKWArguments = kwType.getArity(); Object[] newActuals = new Object[oldActuals.length + amountOfKWArguments]; System.arraycopy(oldActuals, 0, newActuals, 0, oldActuals.length); bindKeywordArgs(keyArgValues); // then we add the resulting values in order to the actual parameter array for the Java method for (int i = 0; i < amountOfKWArguments; i++) { newActuals[oldActuals.length + i] = env.getFrameVariable(kwType.getFieldName(i)).getValue(); } return newActuals; } finally { ctx.setCurrentEnvt(old); } } public IValue invoke(Object[] oActuals) { try { return (IValue) method.invoke(instance, oActuals); } catch (InvocationTargetException e) { Throwable targetException = e.getTargetException(); if (targetException instanceof Throw) { Throw th = (Throw) targetException; StackTrace trace = new StackTrace(); trace.addAll(th.getTrace()); ISourceLocation loc = th.getLocation(); if (loc == null) { loc = getAst().getLocation(); } trace.add(loc, null); th.setLocation(loc); trace.addAll(eval.getStackTrace()); th.setTrace(trace.freeze()); throw th; } else if (targetException instanceof StaticError) { throw (StaticError) targetException; } else if (targetException instanceof ImplementationError) { throw (ImplementationError) targetException; } if (ctx.getConfiguration().printErrors()) { targetException.printStackTrace(); } throw RuntimeExceptionFactory.javaException(e.getTargetException(), getAst(), eval.getStackTrace()); } catch (Throwable e) { if(ctx.getConfiguration().printErrors()){ e.printStackTrace(); } throw RuntimeExceptionFactory.javaException(e, getAst(), eval.getStackTrace()); } } }