package st.gravel.support.jvm.runtime; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import st.gravel.core.Symbol; import st.gravel.support.compiler.ASMClassWriter; import st.gravel.support.compiler.ast.AbsoluteReference; import st.gravel.support.compiler.ast.ClassMapping; import st.gravel.support.compiler.ast.Reference; import st.gravel.support.compiler.ast.SystemMapping; import st.gravel.support.compiler.ast.SystemMappingCompilerTools; import st.gravel.support.compiler.jvm.Invoke; import st.gravel.support.compiler.jvm.Invoke.Invoke_Factory; import st.gravel.support.compiler.jvm.InvokeInterface; import st.gravel.support.compiler.jvm.InvokeStatic; import st.gravel.support.compiler.jvm.InvokeStatic.InvokeStatic_Factory; import st.gravel.support.compiler.jvm.InvokeVirtual; import st.gravel.support.compiler.jvm.JVMArrayType; import st.gravel.support.compiler.jvm.JVMBooleanType; import st.gravel.support.compiler.jvm.JVMByteType; import st.gravel.support.compiler.jvm.JVMCharType; import st.gravel.support.compiler.jvm.JVMClass; import st.gravel.support.compiler.jvm.JVMDefinedObjectType; import st.gravel.support.compiler.jvm.JVMDoubleType; import st.gravel.support.compiler.jvm.JVMFloatType; import st.gravel.support.compiler.jvm.JVMIntType; import st.gravel.support.compiler.jvm.JVMLongType; import st.gravel.support.compiler.jvm.JVMMethodType; import st.gravel.support.compiler.jvm.JVMType; import st.gravel.support.compiler.jvm.JVMVoidType; import st.gravel.support.jvm.ArrayExtensions; import st.gravel.support.jvm.Block0; import st.gravel.support.jvm.Block1; import st.gravel.support.jvm.Block2; import st.gravel.support.jvm.ObjectClass; public final class JavaSystemMappingCompilerTools extends SystemMappingCompilerTools { private int classCounter = 1; private int nlrMarkers = 1; private static class CompiledClass { private final JVMClass jvmClass; private final Class<?> javaClass; public CompiledClass(JVMClass jvmClass, Class<?> javaClass) { this.jvmClass = jvmClass; this.javaClass = javaClass; } } private final HashMap<JVMDefinedObjectType, CompiledClass> jvmClasses = new HashMap<>(); public MethodHandle bindMethodHandle_to_(MethodHandle _methodHandle, Object _object) { return _methodHandle.bindTo(_object); } @Override public Invoke createInvokeInstruction_name_numArgs_( JVMDefinedObjectType _type, String _name, int _numArgs) { Class<?> receiverClass; try { receiverClass = Class.forName(_type.dottedClassName()); } catch (ClassNotFoundException e) { return null; } Method method = MethodTools.searchForStaticMethod(receiverClass, _name, _numArgs); if (method == null) { method = MethodTools.searchForStaticMethod(receiverClass, _name, _numArgs + 1); } if (method == null) { method = MethodTools.searchForMethod(receiverClass, _name, _numArgs, false); if (method == null) { return null; } if (receiverClass.isInterface()) { return createInvoke(InvokeInterface.factory, _type, _name, method); } else { return createInvoke(InvokeVirtual.factory, _type, _name, method); } } else { return createInvoke(InvokeStatic.factory, _type, _name, method); } } private Invoke createInvoke(Invoke_Factory invokeFactory, JVMDefinedObjectType _type, String _name, Method method) { JVMType[] arguments = ArrayExtensions.collect_( method.getParameterTypes(), new Block1<JVMType, Class<?>>() { @Override public JVMType value_(Class<?> arg1) { return jvmTypeForClass_(arg1); } }); JVMMethodType _aJVMMethodType = JVMMethodType.factory .returnType_arguments_( jvmTypeForClass_(method.getReturnType()), arguments); return invokeFactory.ownerType_name_signature_(_type, _name, _aJVMMethodType); } @Override public Object createSingletonForClass_(Class _aJavaClass) { try { return _aJavaClass.getConstructor().newInstance(); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { throw new RuntimeException(e); } } @Override public Object evaluateBlock0Class_(Class _aClass) { try { Object function = _aClass.getConstructor().newInstance(); Object value = _aClass.getMethod("value").invoke(function); return value; } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { throw new RuntimeException(e); } } @Override public Class findJavaClass_(Symbol[] path) { if ((path.length == 1) && (path[0].asString().equals("byte[]"))) return byte[].class; try { return Class .forName(referenceAsClassName_(AbsoluteReference.factory .path_(path))); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } @Override public SystemMappingCompilerTools initializeClass_systemMapping_( ClassMapping _aClassMapping, SystemMapping aSystemMapping) { Class identityClass = _aClassMapping.identityClass(); if (identityClass == null) return this; try { Method method = identityClass.getDeclaredMethod("initialize"); Object instance = aSystemMapping .singletonAtReference_(_aClassMapping.reference().nonmeta()); final MethodHandle unreflect = MethodHandles.lookup().unreflect( method); System.out.println("Initializing " + identityClass); unreflect.invoke(instance); return this; } catch (NoSuchMethodException e) { return this; } catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException(e); } catch (Throwable e) { throw new RuntimeException(e); } } @Override public boolean isAssignable_from_(Class _aClass, Class _bClass) { return _aClass != null && _bClass != null && _aClass.isAssignableFrom(_bClass); } @Override public JVMClass jvmClassForType_ifAbsent_( JVMDefinedObjectType _aJVMDefinedObjectType, Block0<JVMClass> _aBlock) { return jvmClasses.get(_aJVMDefinedObjectType).jvmClass; } @Override public JVMType jvmTypeForClass_(Class _aClass) { if (_aClass.isPrimitive()) { if (_aClass == void.class) return JVMVoidType.factory.basicNew(); if (_aClass == char.class) return JVMCharType.factory.basicNew(); if (_aClass == byte.class) return JVMByteType.factory.basicNew(); if (_aClass == int.class) return JVMIntType.factory.basicNew(); if (_aClass == long.class) return JVMLongType.factory.basicNew(); if (_aClass == boolean.class) return JVMBooleanType.factory.basicNew(); if (_aClass == float.class) return JVMFloatType.factory.basicNew(); if (_aClass == double.class) return JVMDoubleType.factory.basicNew(); throw new RuntimeException("niy: " + _aClass); } if (_aClass.isArray()) { return JVMArrayType.factory.elementType_(this .jvmTypeForClass_(_aClass.getComponentType())); } return JVMDefinedObjectType.factory.dottedClassName_(_aClass .getCanonicalName()); } @Override public MethodHandle methodHandleAt_numArgs_in_identityClass_isStatic_( String methodName, int baseNumArgs, Class javaClass, Class _identityClass, boolean isStatic) { int numArgs = baseNumArgs + (isStatic ? 1 : 0); Method method = MethodTools.searchForMethod(javaClass, methodName, numArgs, isStatic); if (method == null) { throw new RuntimeException("Method not found: " + methodName + " in: " + javaClass); } try { if (isStatic) { return MethodHandles .lookup() .in(javaClass) .findStatic(javaClass, methodName, MethodTools.asMethodType(method)); } else { return MethodHandles .lookup() .in(javaClass) .findVirtual(javaClass, methodName, MethodTools.asMethodType(method)); } } catch (NoSuchMethodException | IllegalAccessException e) { return null; } } @Override public SystemMappingCompilerTools methodNamesIn_do_(Class javaClass, Block2<Object, String, Integer> block) { for (Method m : javaClass.getMethods()) { if (!Modifier.isStatic(m.getModifiers()) && m.getDeclaringClass() == javaClass) block.value_value_(m.getName(), m.getParameterTypes().length); } return this; } @Override public AlmostFinalValue newSingletonHolder_value_( AbsoluteReference _reference, Object _value) { AlmostFinalValue almostFinalValue = new AlmostFinalValue(); almostFinalValue.setValue(_value); return almostFinalValue; } @Override public AlmostFinalValue newSingletonHolder_initializer_( final AbsoluteReference _reference, final Block0<Object> block) { AlmostFinalValue almostFinalValue = new AlmostFinalValue() { @Override protected Object initialValue() { return block.value(); } }; return almostFinalValue; } @Override public String nextExtensionPostfix() { return "Extension" + classCounter++; } @Override public String nextNlrMarker() { return "__NonLocalReturn" + nlrMarkers++; } @Override public String referenceAsClassName_(Reference reference) { if (reference.isMeta()) { return reference.nonmeta().toString() + "$Factory"; } return reference.toString(); } @Override public SystemMappingCompilerTools resetCallsites() { BaseCallSite.resetAll(); return this; } @Override public Object valueOfSingletonHolder_(AlmostFinalValue _holder) { try { return _holder.createGetter().invokeExact(); } catch (Throwable e) { throw new RuntimeException(e); } } @Override public Class writeClass_(JVMClass jvmClass) { JVMDefinedObjectType key = jvmClass.type(); CompiledClass current = jvmClasses.get(key); if (current == null) { current = new CompiledClass(jvmClass, new ASMClassWriter(jvmClass).createClass()); jvmClasses.put(key, current); } return current.javaClass; } @Override public SystemMappingCompilerTools runAstInit_(JVMClass jvmClass) { JVMDefinedObjectType key = jvmClass.type(); CompiledClass current = jvmClasses.get(key); Object[] astConstants = jvmClass.astConstants(); if (astConstants.length == 0) return this; MethodType type = MethodType.genericMethodType(astConstants.length) .changeReturnType(void.class); MethodHandle findStatic; try { findStatic = MethodHandles.lookup().findStatic(current.javaClass, "_astinit", type); findStatic.invokeWithArguments(astConstants); } catch (Throwable e) { throw new RuntimeException(e); } return this; } @Override public Class classForName_(String _aString) { CompiledClass compiledClass = jvmClasses.get(JVMDefinedObjectType.factory.dottedClassName_(_aString)); if (compiledClass != null) return compiledClass.javaClass; throw new RuntimeException("niy"); } }