/// <summary> /// invokes the method. Returns null if a problem, the /// actual return if the method returns something, or /// an empty string "" if the method returns void /// </summary> public override Object Execute(Object o, IInternalContextAdapter context) { IDuck duck = o as IDuck; object[] parameters = new object[paramCount]; if (duck != null) { EvalParameters(parameters, context); return(duck.Invoke(methodName, parameters)); } /* * new strategy (strategy!) for introspection. Since we want * to be thread- as well as context-safe, we *must* do it now, * at execution time. There can be no in-node caching, * but if we are careful, we can do it in the context. */ MethodInfo method = null; PropertyInfo property = null; bool preparedAlready = false; object[] methodArguments = new object[paramCount]; try { /* * check the cache */ IntrospectionCacheData introspectionCacheData = context.ICacheGet(this); Type c = o.GetType(); /* * like ASTIdentifier, if we have cache information, and the * Class of Object o is the same as that in the cache, we are * safe. */ EvalParameters(parameters, context); if (introspectionCacheData != null && introspectionCacheData.ContextData == c) { preparedAlready = true; /* * and get the method from the cache */ if (introspectionCacheData.Thingy is MethodInfo) { method = (MethodInfo)introspectionCacheData.Thingy; methodArguments = BuildMethodArgs(method, parameters, paramArrayIndex); } if (introspectionCacheData.Thingy is PropertyInfo) { property = (PropertyInfo)introspectionCacheData.Thingy; } } else { /* * otherwise, do the introspection, and then * cache it */ Object obj = PerformIntrospection(context, c, parameters); if (obj is MethodInfo) { method = (MethodInfo)obj; } if (obj is PropertyInfo) { property = (PropertyInfo)obj; } if (obj != null) { introspectionCacheData = new IntrospectionCacheData(); introspectionCacheData.ContextData = c; introspectionCacheData.Thingy = obj; context.ICachePut(this, introspectionCacheData); } } /* * if we still haven't gotten the method, either we are calling * a method that doesn't exist (which is fine...) or I screwed * it up. */ if (method == null && property == null) { return(null); } } catch (Exception ex) { runtimeServices.Error(string.Format("ASTMethod.execute() : exception from introspection : {0}", ex)); throw new RuntimeException( String.Format( "Error during object introspection. Check inner exception for details. Node literal {0} Line {1} Column {2}", base.Literal, Line, Column), ex); } try { /* * get the returned object. It may be null, and that is * valid for something declared with a void return type. * Since the caller is expecting something to be returned, * as long as things are peachy, we can return an empty * String so ASTReference() correctly figures out that * all is well. */ Object obj; if (method == null) { obj = property.GetValue(o, null); } else { if (!preparedAlready) { methodArguments = BuildMethodArgs(method, parameters); } obj = method.Invoke(o, methodArguments); if (obj == null && method.ReturnType == typeof(void)) { obj = String.Empty; } } return(obj); } catch (TargetInvocationException targetInvocationException) { /* * In the event that the invocation of the method * itself throws an exception, we want to catch that * wrap it, and throw. We don't log here as we want to figure * out which reference threw the exception, so do that * above */ EventCartridge eventCartridge = context.EventCartridge; /* * if we have an event cartridge, see if it wants to veto * also, let non-Exception Throwables go... */ if (eventCartridge == null) { /* * no event cartridge to override. Just throw */ throw new MethodInvocationException( string.Format("Invocation of method '{0}' in {1} threw exception {2} : {3}", methodName, o.GetType(), targetInvocationException.GetBaseException().GetType(), targetInvocationException.GetBaseException().Message), targetInvocationException, methodName); } else { try { return(eventCartridge.HandleMethodException(o.GetType(), methodName, targetInvocationException)); } catch (Exception e) { throw new MethodInvocationException( string.Format("Invocation of method '{0}' in {1} threw exception {2} : {3}", methodName, o.GetType(), e.GetType(), e.Message), e, methodName); } } } catch (Exception e) { runtimeServices.Error( string.Format("ASTMethod.execute() : exception invoking method '{0}' in {1} : {2}", methodName, o.GetType(), e)); throw; } }