Mercurial Hosting > luan
view src/luan/modules/JavaLuan.java @ 1120:e8fc6712b468
luan Rpc uses luan.lib.rpc
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 07 Aug 2017 23:50:52 -0600 |
parents | dd36eae6aa04 |
children | ba4daf107e07 |
line wrap: on
line source
package luan.modules; import java.lang.reflect.Array; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Member; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.util.Collections; import java.util.Arrays; import java.util.Comparator; import luan.Luan; import luan.LuanState; import luan.LuanTable; import luan.LuanException; import luan.LuanFunction; import luan.LuanJavaFunction; public final class JavaLuan { public static void java(LuanState luan) throws LuanException { check(luan,LuanException.currentSource()); luan.java.ok = true; } public static final LuanFunction javaFn; static { try { javaFn = new LuanJavaFunction(JavaLuan.class.getMethod("java",LuanState.class),null); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } } private static void checkJava(LuanState luan) throws LuanException { if( !luan.java.ok ) throw new LuanException("Java isn't allowed"); } public static Object __index(LuanState luan,Object obj,Object key) throws LuanException { checkJava(luan); Class cls; if( obj instanceof Static ) { Static st = (Static)obj; cls = st.cls; if( key instanceof String ) { String name = (String)key; if( "class".equals(name) ) { return cls; } else if( "new".equals(name) ) { Constructor[] constructors = cls.getConstructors(); if( constructors.length > 0 ) { if( constructors.length==1 ) { return new LuanJavaFunction(constructors[0],null); } else { List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>(); for( Constructor constructor : constructors ) { fns.add(new LuanJavaFunction(constructor,null)); } return new AmbiguousJavaFunction(fns); } } /* } else if( "assert".equals(name) ) { return new LuanJavaFunction(assertClass,new AssertClass(cls)); */ } else if( "luan_proxy".equals(name) ) { return new LuanJavaFunction(luan_proxyMethod,st); } else { List<Member> members = getStaticMembers(cls,name); if( !members.isEmpty() ) { return member(null,members); } } } } else { cls = obj.getClass(); if( cls.isArray() ) { if( "length".equals(key) ) { return Array.getLength(obj); } Integer i = Luan.asInteger(key); if( i != null ) { return Array.get(obj,i); } // throw new LuanException(luan,"invalid member '"+key+"' for java array: "+obj); } else if( key instanceof String ) { String name = (String)key; if( "instanceof".equals(name) ) { return new LuanJavaFunction(instanceOf,new InstanceOf(obj)); } else { List<Member> members = getMembers(cls,name); if( !members.isEmpty() ) { return member(obj,members); } } } } //System.out.println("invalid member '"+key+"' for java object: "+obj); throw new LuanException( "invalid index '"+key+"' for java "+cls ); } private static Object member(Object obj,List<Member> members) throws LuanException { try { if( members.size()==1 ) { Member member = members.get(0); if( member instanceof Static ) { return member; } else if( member instanceof Field ) { Field field = (Field)member; Object rtn = field.get(obj); return rtn instanceof Object[] ? Arrays.asList((Object[])rtn) : rtn; } else { Method method = (Method)member; return new LuanJavaFunction(method,obj); } } else { List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>(); for( Member member : members ) { Method method = (Method)member; fns.add(new LuanJavaFunction(method,obj)); } return new AmbiguousJavaFunction(fns); } } catch(IllegalAccessException e) { throw new RuntimeException(e); } } public static void __new_index(LuanState luan,Object obj,Object key,Object value) throws LuanException { checkJava(luan); Class cls; if( obj instanceof Static ) { Static st = (Static)obj; cls = st.cls; if( key instanceof String ) { String name = (String)key; List<Member> members = getStaticMembers(cls,name); if( !members.isEmpty() ) { if( members.size() != 1 ) throw new RuntimeException("not field '"+name+"' of "+obj); setMember(obj,members,value); return; } } // throw new LuanException(luan,"invalid member '"+key+"' for: "+obj); } else { cls = obj.getClass(); if( cls.isArray() ) { Integer i = Luan.asInteger(key); if( i != null ) { Array.set(obj,i,value); return; } // throw new LuanException(luan,"invalid member '"+key+"' for java array: "+obj); } else if( key instanceof String ) { String name = (String)key; List<Member> members = getMembers(cls,name); if( !members.isEmpty() ) { if( members.size() != 1 ) throw new RuntimeException("not field '"+name+"' of "+obj); setMember(obj,members,value); return; } } } throw new LuanException( "invalid index for java "+cls ); } private static void setMember(Object obj,List<Member> members,Object value) { Field field = (Field)members.get(0); try { try { field.set(obj,value); } catch(IllegalArgumentException e) { Class cls = field.getType(); if( value instanceof Number ) { Number n = (Number)value; if( cls.equals(Integer.TYPE) || cls.equals(Integer.class) ) { int r = n.intValue(); if( r==n.doubleValue() ) { field.setInt(obj,r); return; } } } throw e; } } catch(IllegalAccessException e) { throw new RuntimeException(e); } } public static boolean privateAccess = false; private static Map<Class,Map<String,List<Member>>> memberMap = new HashMap<Class,Map<String,List<Member>>>(); private static List<Member> getMembers(Class cls,String name) { Map<String,List<Member>> clsMap; synchronized(memberMap) { clsMap = memberMap.get(cls); if( clsMap == null ) { clsMap = new HashMap<String,List<Member>>(); for( Class c : cls.getClasses() ) { String s = c.getSimpleName(); List<Member> list = new ArrayList<Member>(); clsMap.put(s,list); list.add(new Static(c)); } for( Field field : cls.getFields() ) { String s = field.getName(); try { if( !cls.getField(s).equals(field) ) continue; // not accessible } catch(NoSuchFieldException e) { throw new RuntimeException(e); } List<Member> list = new ArrayList<Member>(); clsMap.put(s,list); list.add(field); } for( Method method : cls.getMethods() ) { String s = method.getName(); List<Member> list = clsMap.get(s); if( list == null || !(list.get(0) instanceof Method) ) { list = new ArrayList<Member>(); clsMap.put(s,list); } list.add(method); } if( privateAccess ) { for( Method method : cls.getDeclaredMethods() ) { String s = method.getName(); List<Member> list = clsMap.get(s); if( list == null ) { list = new ArrayList<Member>(); clsMap.put(s,list); } else if( !(list.get(0) instanceof Method) ) continue; if( !list.contains(method) ) { list.add(method); } } for( Field field : cls.getDeclaredFields() ) { String s = field.getName(); List<Member> list = clsMap.get(s); if( list == null ) { list = new ArrayList<Member>(); clsMap.put(s,list); list.add(field); } } } for( List<Member> members : clsMap.values() ) { for( Member m : members ) { if( m instanceof AccessibleObject ) ((AccessibleObject)m).setAccessible(true); } } memberMap.put(cls,clsMap); } } List<Member> rtn = clsMap.get(name); if( rtn==null ) rtn = Collections.emptyList(); return rtn; } private static List<Member> getStaticMembers(Class cls,String name) { List<Member> staticMembers = new ArrayList<Member>(); for( Member m : getMembers(cls,name) ) { if( Modifier.isStatic(m.getModifiers()) ) staticMembers.add(m); } return staticMembers; } static final class Static implements Member { final Class cls; Static(Class cls) { this.cls = cls; } @Override public String toString() { return cls.toString(); } @Override public Class getDeclaringClass() { return cls.getDeclaringClass(); } @Override public String getName() { return cls.getName(); } @Override public int getModifiers() { return cls.getModifiers(); } @Override public boolean isSynthetic() { return cls.isSynthetic(); } public Object luan_proxy(final LuanState luan,final LuanTable t) throws LuanException { return Proxy.newProxyInstance( cls.getClassLoader(), new Class[]{cls}, new InvocationHandler() { public Object invoke(Object proxy,Method method, Object[] args) throws Throwable { if( args==null ) args = new Object[0]; String name = method.getName(); Object fnObj = t.get(luan,name); if( fnObj == null ) throw new NullPointerException("luan_proxy couldn't find method '"+name+"'"); LuanFunction fn = Luan.checkFunction(fnObj); return Luan.first(fn.call(luan,args)); } } ); } } private static final Method luan_proxyMethod; static { try { luan_proxyMethod = Static.class.getMethod("luan_proxy",LuanState.class,LuanTable.class); luan_proxyMethod.setAccessible(true); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } } public static Static load(LuanState luan,String name) throws LuanException { checkJava(luan); Class cls; try { cls = Class.forName(name); } catch(ClassNotFoundException e) { try { cls = Thread.currentThread().getContextClassLoader().loadClass(name); } catch(ClassNotFoundException e2) { return null; } } return new Static(cls); } private static final Comparator<LuanJavaFunction> varArgsSorter = new Comparator<LuanJavaFunction>() { public int compare(LuanJavaFunction fn1,LuanJavaFunction fn2) { return fn2.getParameterCount() - fn1.getParameterCount(); } }; private static class AmbiguousJavaFunction extends LuanFunction { private final Map<Integer,List<LuanJavaFunction>> fnMap = new HashMap<Integer,List<LuanJavaFunction>>(); private List<LuanJavaFunction> varArgs = new ArrayList<LuanJavaFunction>(); AmbiguousJavaFunction(List<LuanJavaFunction> fns) { for( LuanJavaFunction fn : fns ) { if( fn.isVarArgs() ) { varArgs.add(fn); } else { Integer n = fn.getParameterCount(); List<LuanJavaFunction> list = fnMap.get(n); if( list==null ) { list = new ArrayList<LuanJavaFunction>(); fnMap.put(n,list); } list.add(fn); } } Collections.sort(varArgs,varArgsSorter); } @Override public Object call(LuanState luan,Object[] args) throws LuanException { List<LuanJavaFunction> list = fnMap.get(args.length); if( list != null ) { for( LuanJavaFunction fn : list ) { try { return fn.rawCall(luan,args); } catch(IllegalArgumentException e) {} } } for( LuanJavaFunction fn : varArgs ) { try { return fn.rawCall(luan,args); } catch(IllegalArgumentException e) {} } throw new LuanException("no method matched args: "+Arrays.asList(args)); } } private static class InstanceOf { private final Object obj; InstanceOf(Object obj) { this.obj = obj; } public boolean instanceOf(Static st) { return st.cls.isInstance(obj); } } private static final Method instanceOf; static { try { instanceOf = InstanceOf.class.getMethod("instanceOf",Static.class); instanceOf.setAccessible(true); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } } /* private static class AssertClass { private final Class cls; AssertClass(Class cls) { this.cls = cls; } public Object assertClass(LuanState luan,Object v) throws LuanException { if( !cls.isInstance(v) ) { String got = v.getClass().getSimpleName(); String expected = cls.getSimpleName(); throw new LuanException(luan,"bad argument #1 ("+expected+" expected, got "+got+")"); } return v; } } private static final Method assertClass; static { try { assertClass = AssertClass.class.getMethod("assertClass",LuanState.class,Object.class); assertClass.setAccessible(true); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } } public static Object proxy(final LuanState luan,Static st,final LuanTable t,final Object base) throws LuanException { return Proxy.newProxyInstance( st.cls.getClassLoader(), new Class[]{st.cls}, new InvocationHandler() { public Object invoke(Object proxy,Method method, Object[] args) throws Throwable { if( args==null ) args = new Object[0]; String name = method.getName(); Object fnObj = t.get(name); if( fnObj==null && base!=null ) return method.invoke(base,args); LuanFunction fn = luan.checkFunction(fnObj); return Luan.first(luan.call(fn,name,args)); } } ); } */ // security public interface Security { public void check(LuanState luan,String name) throws LuanException; } private static String SECURITY_KEY = "Java.Security"; private static void check(LuanState luan,String name) throws LuanException { Security s = (Security)luan.registry().get(SECURITY_KEY); if( s!=null ) s.check(luan,name); } public static void setSecurity(LuanState luan,Security s) { luan.registry().put(SECURITY_KEY,s); } }