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 }