Mercurial Hosting > luan
comparison src/luan/modules/JavaLuan.java @ 775:1a68fc55a80c
simplify dir structure
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Fri, 26 Aug 2016 14:36:40 -0600 |
parents | core/src/luan/modules/JavaLuan.java@fc08c3b42010 |
children | 305ffb00ebc1 |
comparison
equal
deleted
inserted
replaced
774:3e30cf310e56 | 775:1a68fc55a80c |
---|---|
1 package luan.modules; | |
2 | |
3 import java.lang.reflect.Array; | |
4 import java.lang.reflect.AccessibleObject; | |
5 import java.lang.reflect.Member; | |
6 import java.lang.reflect.Field; | |
7 import java.lang.reflect.Method; | |
8 import java.lang.reflect.Constructor; | |
9 import java.lang.reflect.Modifier; | |
10 import java.lang.reflect.InvocationHandler; | |
11 import java.lang.reflect.Proxy; | |
12 import java.util.Map; | |
13 import java.util.HashMap; | |
14 import java.util.List; | |
15 import java.util.ArrayList; | |
16 import java.util.Iterator; | |
17 import java.util.Collections; | |
18 import java.util.Arrays; | |
19 import luan.Luan; | |
20 import luan.LuanState; | |
21 import luan.LuanTable; | |
22 import luan.LuanException; | |
23 import luan.LuanFunction; | |
24 import luan.LuanJavaFunction; | |
25 | |
26 | |
27 public final class JavaLuan { | |
28 | |
29 public static void java(LuanState luan) throws LuanException { | |
30 check(luan,LuanException.currentSource()); | |
31 luan.java.ok = true; | |
32 } | |
33 | |
34 public static final LuanFunction javaFn; | |
35 static { | |
36 try { | |
37 javaFn = new LuanJavaFunction(JavaLuan.class.getMethod("java",LuanState.class),null); | |
38 } catch(NoSuchMethodException e) { | |
39 throw new RuntimeException(e); | |
40 } | |
41 } | |
42 | |
43 private static void checkJava(LuanState luan) throws LuanException { | |
44 if( !luan.java.ok ) | |
45 throw new LuanException("Java isn't allowed"); | |
46 } | |
47 | |
48 static final Object FAIL = new Object(); | |
49 | |
50 public static Object __index(LuanState luan,Object obj,Object key,boolean canReturnFail) throws LuanException { | |
51 checkJava(luan); | |
52 Class cls; | |
53 if( obj instanceof Static ) { | |
54 Static st = (Static)obj; | |
55 cls = st.cls; | |
56 if( key instanceof String ) { | |
57 String name = (String)key; | |
58 if( "class".equals(name) ) { | |
59 return cls; | |
60 } else if( "new".equals(name) ) { | |
61 Constructor[] constructors = cls.getConstructors(); | |
62 if( constructors.length > 0 ) { | |
63 if( constructors.length==1 ) { | |
64 return new LuanJavaFunction(constructors[0],null); | |
65 } else { | |
66 List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>(); | |
67 for( Constructor constructor : constructors ) { | |
68 fns.add(new LuanJavaFunction(constructor,null)); | |
69 } | |
70 return new AmbiguousJavaFunction(fns); | |
71 } | |
72 } | |
73 /* | |
74 } else if( "assert".equals(name) ) { | |
75 return new LuanJavaFunction(assertClass,new AssertClass(cls)); | |
76 */ | |
77 } else if( "luan_proxy".equals(name) ) { | |
78 return new LuanJavaFunction(luan_proxyMethod,st); | |
79 } else { | |
80 List<Member> members = getStaticMembers(cls,name); | |
81 if( !members.isEmpty() ) { | |
82 return member(null,members); | |
83 } | |
84 } | |
85 } | |
86 } else { | |
87 cls = obj.getClass(); | |
88 if( cls.isArray() ) { | |
89 if( "length".equals(key) ) { | |
90 return Array.getLength(obj); | |
91 } | |
92 Integer i = Luan.asInteger(key); | |
93 if( i != null ) { | |
94 return Array.get(obj,i); | |
95 } | |
96 // throw new LuanException(luan,"invalid member '"+key+"' for java array: "+obj); | |
97 } else if( key instanceof String ) { | |
98 String name = (String)key; | |
99 if( "instanceof".equals(name) ) { | |
100 return new LuanJavaFunction(instanceOf,new InstanceOf(obj)); | |
101 } else { | |
102 List<Member> members = getMembers(cls,name); | |
103 if( !members.isEmpty() ) { | |
104 return member(obj,members); | |
105 } | |
106 } | |
107 } | |
108 } | |
109 //System.out.println("invalid member '"+key+"' for java object: "+obj); | |
110 if( canReturnFail ) | |
111 return FAIL; | |
112 throw new LuanException( "invalid index '"+key+"' for java "+cls ); | |
113 } | |
114 | |
115 private static Object member(Object obj,List<Member> members) throws LuanException { | |
116 try { | |
117 if( members.size()==1 ) { | |
118 Member member = members.get(0); | |
119 if( member instanceof Static ) { | |
120 return member; | |
121 } else if( member instanceof Field ) { | |
122 Field field = (Field)member; | |
123 Object rtn = field.get(obj); | |
124 return rtn instanceof Object[] ? Arrays.asList((Object[])rtn) : rtn; | |
125 } else { | |
126 Method method = (Method)member; | |
127 return new LuanJavaFunction(method,obj); | |
128 } | |
129 } else { | |
130 List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>(); | |
131 for( Member member : members ) { | |
132 Method method = (Method)member; | |
133 fns.add(new LuanJavaFunction(method,obj)); | |
134 } | |
135 return new AmbiguousJavaFunction(fns); | |
136 } | |
137 } catch(IllegalAccessException e) { | |
138 throw new RuntimeException(e); | |
139 } | |
140 } | |
141 | |
142 public static void __new_index(LuanState luan,Object obj,Object key,Object value) throws LuanException { | |
143 checkJava(luan); | |
144 Class cls; | |
145 if( obj instanceof Static ) { | |
146 Static st = (Static)obj; | |
147 cls = st.cls; | |
148 if( key instanceof String ) { | |
149 String name = (String)key; | |
150 List<Member> members = getStaticMembers(cls,name); | |
151 if( !members.isEmpty() ) { | |
152 if( members.size() != 1 ) | |
153 throw new RuntimeException("not field '"+name+"' of "+obj); | |
154 setMember(obj,members,value); | |
155 return; | |
156 } | |
157 } | |
158 // throw new LuanException(luan,"invalid member '"+key+"' for: "+obj); | |
159 } else { | |
160 cls = obj.getClass(); | |
161 if( cls.isArray() ) { | |
162 Integer i = Luan.asInteger(key); | |
163 if( i != null ) { | |
164 Array.set(obj,i,value); | |
165 return; | |
166 } | |
167 // throw new LuanException(luan,"invalid member '"+key+"' for java array: "+obj); | |
168 } else if( key instanceof String ) { | |
169 String name = (String)key; | |
170 List<Member> members = getMembers(cls,name); | |
171 if( !members.isEmpty() ) { | |
172 if( members.size() != 1 ) | |
173 throw new RuntimeException("not field '"+name+"' of "+obj); | |
174 setMember(obj,members,value); | |
175 return; | |
176 } | |
177 } | |
178 } | |
179 throw new LuanException( "invalid index for java "+cls ); | |
180 } | |
181 | |
182 private static void setMember(Object obj,List<Member> members,Object value) { | |
183 Field field = (Field)members.get(0); | |
184 try { | |
185 try { | |
186 field.set(obj,value); | |
187 } catch(IllegalArgumentException e) { | |
188 Class cls = field.getType(); | |
189 if( value instanceof Number ) { | |
190 Number n = (Number)value; | |
191 if( cls.equals(Integer.TYPE) || cls.equals(Integer.class) ) { | |
192 int r = n.intValue(); | |
193 if( r==n.doubleValue() ) { | |
194 field.setInt(obj,r); | |
195 return; | |
196 } | |
197 } | |
198 } | |
199 throw e; | |
200 } | |
201 } catch(IllegalAccessException e) { | |
202 throw new RuntimeException(e); | |
203 } | |
204 } | |
205 | |
206 public static boolean privateAccess = false; | |
207 private static Map<Class,Map<String,List<Member>>> memberMap = new HashMap<Class,Map<String,List<Member>>>(); | |
208 | |
209 private static synchronized List<Member> getMembers(Class cls,String name) { | |
210 Map<String,List<Member>> clsMap = memberMap.get(cls); | |
211 if( clsMap == null ) { | |
212 clsMap = new HashMap<String,List<Member>>(); | |
213 for( Class c : cls.getClasses() ) { | |
214 String s = c.getSimpleName(); | |
215 List<Member> list = new ArrayList<Member>(); | |
216 clsMap.put(s,list); | |
217 list.add(new Static(c)); | |
218 } | |
219 for( Field field : cls.getFields() ) { | |
220 String s = field.getName(); | |
221 try { | |
222 if( !cls.getField(s).equals(field) ) | |
223 continue; // not accessible | |
224 } catch(NoSuchFieldException e) { | |
225 throw new RuntimeException(e); | |
226 } | |
227 List<Member> list = new ArrayList<Member>(); | |
228 clsMap.put(s,list); | |
229 list.add(field); | |
230 } | |
231 for( Method method : cls.getMethods() ) { | |
232 String s = method.getName(); | |
233 List<Member> list = clsMap.get(s); | |
234 if( list == null || !(list.get(0) instanceof Method) ) { | |
235 list = new ArrayList<Member>(); | |
236 clsMap.put(s,list); | |
237 } | |
238 list.add(method); | |
239 } | |
240 if( privateAccess ) { | |
241 for( Method method : cls.getDeclaredMethods() ) { | |
242 String s = method.getName(); | |
243 List<Member> list = clsMap.get(s); | |
244 if( list == null ) { | |
245 list = new ArrayList<Member>(); | |
246 clsMap.put(s,list); | |
247 } else if( !(list.get(0) instanceof Method) ) | |
248 continue; | |
249 if( !list.contains(method) ) { | |
250 list.add(method); | |
251 } | |
252 } | |
253 for( Field field : cls.getDeclaredFields() ) { | |
254 String s = field.getName(); | |
255 List<Member> list = clsMap.get(s); | |
256 if( list == null ) { | |
257 list = new ArrayList<Member>(); | |
258 clsMap.put(s,list); | |
259 list.add(field); | |
260 } | |
261 } | |
262 } | |
263 for( List<Member> members : clsMap.values() ) { | |
264 for( Member m : members ) { | |
265 if( m instanceof AccessibleObject ) | |
266 ((AccessibleObject)m).setAccessible(true); | |
267 } | |
268 } | |
269 memberMap.put(cls,clsMap); | |
270 } | |
271 List<Member> rtn = clsMap.get(name); | |
272 if( rtn==null ) | |
273 rtn = Collections.emptyList(); | |
274 return rtn; | |
275 } | |
276 | |
277 private static synchronized List<Member> getStaticMembers(Class cls,String name) { | |
278 List<Member> staticMembers = new ArrayList<Member>(); | |
279 for( Member m : getMembers(cls,name) ) { | |
280 if( Modifier.isStatic(m.getModifiers()) ) | |
281 staticMembers.add(m); | |
282 } | |
283 return staticMembers; | |
284 } | |
285 | |
286 static final class Static implements Member { | |
287 final Class cls; | |
288 | |
289 Static(Class cls) { | |
290 this.cls = cls; | |
291 } | |
292 | |
293 @Override public String toString() { | |
294 return cls.toString(); | |
295 } | |
296 | |
297 @Override public Class getDeclaringClass() { | |
298 return cls.getDeclaringClass(); | |
299 } | |
300 | |
301 @Override public String getName() { | |
302 return cls.getName(); | |
303 } | |
304 | |
305 @Override public int getModifiers() { | |
306 return cls.getModifiers(); | |
307 } | |
308 | |
309 @Override public boolean isSynthetic() { | |
310 return cls.isSynthetic(); | |
311 } | |
312 | |
313 public Object luan_proxy(final LuanState luan,final LuanTable t) throws LuanException { | |
314 return Proxy.newProxyInstance( | |
315 cls.getClassLoader(), | |
316 new Class[]{cls}, | |
317 new InvocationHandler() { | |
318 public Object invoke(Object proxy,Method method, Object[] args) | |
319 throws Throwable | |
320 { | |
321 if( args==null ) | |
322 args = new Object[0]; | |
323 String name = method.getName(); | |
324 Object fnObj = t.get(luan,name); | |
325 if( fnObj == null ) | |
326 throw new NullPointerException("luan_proxy couldn't find method '"+name+"'"); | |
327 LuanFunction fn = Luan.checkFunction(fnObj); | |
328 return Luan.first(fn.call(luan,args)); | |
329 } | |
330 } | |
331 ); | |
332 } | |
333 } | |
334 private static final Method luan_proxyMethod; | |
335 static { | |
336 try { | |
337 luan_proxyMethod = Static.class.getMethod("luan_proxy",LuanState.class,LuanTable.class); | |
338 luan_proxyMethod.setAccessible(true); | |
339 } catch(NoSuchMethodException e) { | |
340 throw new RuntimeException(e); | |
341 } | |
342 } | |
343 | |
344 public static Static load(LuanState luan,String name) throws LuanException { | |
345 checkJava(luan); | |
346 Class cls; | |
347 try { | |
348 cls = Class.forName(name); | |
349 } catch(ClassNotFoundException e) { | |
350 try { | |
351 cls = Thread.currentThread().getContextClassLoader().loadClass(name); | |
352 } catch(ClassNotFoundException e2) { | |
353 return null; | |
354 } | |
355 } | |
356 return new Static(cls); | |
357 } | |
358 | |
359 private static class AmbiguousJavaFunction extends LuanFunction { | |
360 private final Map<Integer,List<LuanJavaFunction>> fnMap = new HashMap<Integer,List<LuanJavaFunction>>(); | |
361 | |
362 AmbiguousJavaFunction(List<LuanJavaFunction> fns) { | |
363 for( LuanJavaFunction fn : fns ) { | |
364 Integer n = fn.getParameterTypes().length; | |
365 List<LuanJavaFunction> list = fnMap.get(n); | |
366 if( list==null ) { | |
367 list = new ArrayList<LuanJavaFunction>(); | |
368 fnMap.put(n,list); | |
369 } | |
370 list.add(fn); | |
371 } | |
372 } | |
373 | |
374 @Override public Object call(LuanState luan,Object[] args) throws LuanException { | |
375 for( LuanJavaFunction fn : fnMap.get(args.length) ) { | |
376 try { | |
377 return fn.rawCall(luan,args); | |
378 } catch(IllegalArgumentException e) {} | |
379 } | |
380 throw new LuanException("no method matched args: "+Arrays.asList(args)); | |
381 } | |
382 } | |
383 | |
384 private static class InstanceOf { | |
385 private final Object obj; | |
386 | |
387 InstanceOf(Object obj) { | |
388 this.obj = obj; | |
389 } | |
390 | |
391 public boolean instanceOf(Static st) { | |
392 return st.cls.isInstance(obj); | |
393 } | |
394 } | |
395 private static final Method instanceOf; | |
396 static { | |
397 try { | |
398 instanceOf = InstanceOf.class.getMethod("instanceOf",Static.class); | |
399 instanceOf.setAccessible(true); | |
400 } catch(NoSuchMethodException e) { | |
401 throw new RuntimeException(e); | |
402 } | |
403 } | |
404 | |
405 /* | |
406 private static class AssertClass { | |
407 private final Class cls; | |
408 | |
409 AssertClass(Class cls) { | |
410 this.cls = cls; | |
411 } | |
412 | |
413 public Object assertClass(LuanState luan,Object v) throws LuanException { | |
414 if( !cls.isInstance(v) ) { | |
415 String got = v.getClass().getSimpleName(); | |
416 String expected = cls.getSimpleName(); | |
417 throw new LuanException(luan,"bad argument #1 ("+expected+" expected, got "+got+")"); | |
418 } | |
419 return v; | |
420 } | |
421 } | |
422 private static final Method assertClass; | |
423 static { | |
424 try { | |
425 assertClass = AssertClass.class.getMethod("assertClass",LuanState.class,Object.class); | |
426 assertClass.setAccessible(true); | |
427 } catch(NoSuchMethodException e) { | |
428 throw new RuntimeException(e); | |
429 } | |
430 } | |
431 | |
432 | |
433 public static Object proxy(final LuanState luan,Static st,final LuanTable t,final Object base) throws LuanException { | |
434 return Proxy.newProxyInstance( | |
435 st.cls.getClassLoader(), | |
436 new Class[]{st.cls}, | |
437 new InvocationHandler() { | |
438 public Object invoke(Object proxy,Method method, Object[] args) | |
439 throws Throwable | |
440 { | |
441 if( args==null ) | |
442 args = new Object[0]; | |
443 String name = method.getName(); | |
444 Object fnObj = t.get(name); | |
445 if( fnObj==null && base!=null ) | |
446 return method.invoke(base,args); | |
447 LuanFunction fn = luan.checkFunction(fnObj); | |
448 return Luan.first(luan.call(fn,name,args)); | |
449 } | |
450 } | |
451 ); | |
452 } | |
453 */ | |
454 | |
455 | |
456 | |
457 // security | |
458 | |
459 public interface Security { | |
460 public void check(LuanState luan,String name) throws LuanException; | |
461 } | |
462 | |
463 private static String SECURITY_KEY = "Java.Security"; | |
464 | |
465 private static void check(LuanState luan,String name) throws LuanException { | |
466 Security s = (Security)luan.registry().get(SECURITY_KEY); | |
467 if( s!=null ) | |
468 s.check(luan,name); | |
469 } | |
470 | |
471 public static void setSecurity(LuanState luan,Security s) { | |
472 luan.registry().put(SECURITY_KEY,s); | |
473 } | |
474 | |
475 } |