1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.millscript.millscript.functions;
23
24 import org.millscript.commons.alert.Alert;
25 import org.millscript.millscript.alert.Alerts;
26 import org.millscript.millscript.vm.Machine;
27
28 import java.lang.reflect.InvocationTargetException;
29 import java.lang.reflect.Method;
30 import java.lang.reflect.Modifier;
31
32 /**
33 * This class implements a function which invokes a Java method.
34 */
35 public abstract class JMethod extends Function {
36
37 /**
38 * The Java class this method belongs to.
39 */
40 private final Class clss;
41
42 /**
43 * The Java method to invoke.
44 */
45 final Method method;
46
47 /**
48 * The type of the parameters this method requires.
49 */
50 private final Class[] pTypes;
51
52 /**
53 * The number of arguments the Java method requires.
54 */
55 private final int numArgs;
56
57 /**
58 * A buffer to use for storing the arguments prior to invoking the
59 * underlying method.
60 */
61 final Object[] argBuffer;
62
63 /**
64 * The number of arguments this function requires, which depends on whether
65 * the underlying Java method is static or not.
66 */
67 private final int required;
68
69 /**
70 * Constructs a new <code>JMethod</code> function to invoke the specified
71 * Java method.
72 *
73 * @param m the Java method to invoke
74 */
75 public JMethod( final Method m ) {
76 this.clss = m.getDeclaringClass();
77 this.method = m;
78 pTypes = method.getParameterTypes();
79 numArgs = pTypes.length;
80 argBuffer = new Object[ numArgs ];
81
82 int mods = method.getModifiers();
83 required = numArgs + ( Modifier.isStatic( mods ) ? 0 : 1 );
84 }
85
86 /**
87 * @see org.millscript.millscript.functions.Function#apply(org.millscript.millscript.vm.Machine, int)
88 */
89 @Override
90 public void apply( final Machine mc, final int nargs ) {
91 checkNargs( mc, required, nargs );
92 mc.popArgsIntoArray( argBuffer );
93 try {
94 invoke( mc );
95 } catch ( IllegalAccessException ex ) {
96 throw(
97 Alerts.eval(
98 "Illegal access",
99 "Java Function raised an access exception"
100 ).culprit( "exception details", ex.getMessage() ).mishap()
101 );
102 } catch ( IllegalArgumentException ex ) {
103 for ( int i = numArgs - 1; i >= 0; i-- ) {
104 final Object arg = argBuffer[ i ];
105 if ( !pTypes[i].isInstance( arg ) ) {
106 throw(
107 Alerts.eval(
108 "Unexpected type passed to Java Function",
109 null
110 ).
111 culprit( "expected type", pTypes[i].getName() ).
112 culprit( "actual type", arg.getClass() ).
113 culprit( "actual value", arg ).
114 mishap()
115 );
116 }
117 }
118 throw(
119 Alerts.fault( "Cannot diagnose this IllegalAccessException" ).mishap()
120 );
121 } catch ( InvocationTargetException ex ) {
122 final Throwable cause = ex.getCause();
123 if ( cause instanceof NoClassDefFoundError ) {
124 throw(
125 Alerts.fault( "Invocation target" ).culprit( "Missing class", cause.getMessage() ).mishap()
126 );
127 } else if ( cause instanceof Alert ) {
128 throw (Alert) cause;
129 } else {
130 throw(
131 Alerts.eval(
132 "The invoked Java method raised an exception",
133 null
134 ).culprit( "exception", cause ).mishap()
135 );
136 }
137 }
138 }
139
140 /**
141 * Invokes the underlying Java method in the appropriate manner using the
142 * specified machine.
143 *
144 * @param mc the machine to perform the method on
145 * @throws IllegalAccessException thrown if the underlying Java method
146 * cannot be accessed
147 * @throws InvocationTargetException thrown if the underlying Java
148 * method throws an exception
149 */
150 abstract void invoke( final Machine mc ) throws IllegalAccessException, InvocationTargetException;
151
152 /**
153 * Utility method returning a <code>Function</code> to invoke the specified
154 * Java method. This method checks the parameter types and modifiers of the
155 * supplied Java method and returns the appropriate <code>JMethod</code>.
156 *
157 * @param method the method to invoke
158 * @return a <code>Function</code> to invoke the specified method
159 */
160 public static JMethod make( final Method method ) {
161
162
163 int mods = method.getModifiers();
164 Class r = method.getReturnType();
165 if ( Modifier.isStatic( mods ) ) {
166 if ( r == null ) {
167 return new StaticVoidFunction( method );
168 } else {
169 return new StaticReturnFunction( method );
170 }
171 } else {
172 if ( r == null ) {
173 return new InstanceVoidFunction( method );
174 } else {
175 return new InstanceReturnFunction( method );
176 }
177 }
178 }
179
180 /**
181 * Checks that the specified object is an instance of the underlying Java
182 * methods class.
183 *
184 * @param x the object to validate
185 * @return the specified object if it passes validation
186 */
187 Object validate( final Object x ) {
188 if ( !clss.isInstance( x ) ) {
189 throw(
190 Alerts.eval(
191 "Unexpected type",
192 "Java methods must be applied to valid class"
193 ).
194 culprit( "expected type", clss ).
195 culprit( "actual type", x.getClass() ).
196 culprit( "actual value", x ).
197 mishap()
198 );
199 }
200 return x;
201 }
202
203 }