From 96da6c6b9b49efe57f0a4878e0c9cc79ce47f837 Mon Sep 17 00:00:00 2001 From: LadyCailin Date: Thu, 27 Sep 2018 13:51:46 +0200 Subject: [PATCH] First pass on making various methods not load the classes at startup --- .../ClassLoading/ClassDiscovery.java | 310 ++++++++++++------ .../ClassLoading/ClassMirror/ClassMirror.java | 46 ++- .../Common/Annotations/AnnotationChecks.java | 74 +++-- .../com/laytonsmith/core/functions/Debug.java | 67 +++- .../core/functions/FunctionBase.java | 3 + 5 files changed, 358 insertions(+), 142 deletions(-) diff --git a/src/main/java/com/laytonsmith/PureUtilities/ClassLoading/ClassDiscovery.java b/src/main/java/com/laytonsmith/PureUtilities/ClassLoading/ClassDiscovery.java index 115b1395b..fb448e8af 100644 --- a/src/main/java/com/laytonsmith/PureUtilities/ClassLoading/ClassDiscovery.java +++ b/src/main/java/com/laytonsmith/PureUtilities/ClassLoading/ClassDiscovery.java @@ -119,6 +119,10 @@ public ClassDiscovery() { * Cache for class subtypes. Whenever a new URL is added to the URL cache, this is cleared. */ private final Map, Set>> classSubtypeCache = new HashMap<>(); + /** + * Cache for ClassMirror subtypes. Whenever a new URL is added to the URL cache, this is cleared. + */ + private final Map, Set>> classMirrorSubtypeCache = new HashMap<>(); /** * Cache for class annotations. Whenever a new URL is added to the URL cache, this is cleared. */ @@ -242,7 +246,7 @@ private synchronized void discover(URL rootLocation) { String url; try { url = URLDecoder.decode(rootLocation.toString(), "UTF-8"); - } catch (UnsupportedEncodingException ex) { + } catch(UnsupportedEncodingException ex) { // apparently this should never happen, but we have to catch it anyway url = null; } @@ -300,13 +304,13 @@ private synchronized void discover(URL rootLocation) { ClassMirrorVisitor mirrorVisitor = new ClassMirrorVisitor(); reader.accept(mirrorVisitor, ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); mirrors.add(mirrorVisitor.getMirror(new URL(url))); - } catch (IOException ex) { + } catch(IOException ex) { Logger.getLogger(ClassDiscovery.class.getName()).log(Level.SEVERE, null, ex); } finally { if(stream != null) { try { stream.close(); - } catch (IOException ex) { + } catch(IOException ex) { Logger.getLogger(ClassDiscovery.class.getName()).log(Level.SEVERE, null, ex); } } @@ -335,20 +339,20 @@ public void handle(String filename, InputStream in) { ClassMirrorVisitor mirrorVisitor = new ClassMirrorVisitor(); reader.accept(mirrorVisitor, ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); mirrors.add(mirrorVisitor.getMirror(rootLocationFile.toURI().toURL())); - } catch (IOException ex) { + } catch(IOException ex) { Logger.getLogger(ClassDiscovery.class.getName()).log(Level.SEVERE, null, ex); } } } }, progressIterator); - } catch (IOException ex) { + } catch(IOException ex) { Logger.getLogger(ClassDiscovery.class.getName()).log(Level.SEVERE, null, ex); } } else { throw new RuntimeException("Unknown url type: " + rootLocation); } - } catch (RuntimeException e) { + } catch(RuntimeException e) { e.printStackTrace(System.err); } finally { if(debug) { @@ -413,7 +417,7 @@ public void addAllJarsInFolder(File folder) { if(f.getName().endsWith(".jar")) { try { addDiscoveryLocation(f.toURI().toURL()); - } catch (MalformedURLException ex) { + } catch(MalformedURLException ex) { // } } @@ -458,6 +462,8 @@ public void invalidateCaches() { fieldAnnotationCache.clear(); methodAnnotationCache.clear(); constructorAnnotationCache.clear(); + classSubtypeCache.clear(); + classMirrorSubtypeCache.clear(); dirtyURLs.addAll(urlCache); } @@ -518,7 +524,7 @@ public Set> getClassesThatExtend(Class superType) { doDiscovery(); Set> mirrors = new HashSet<>(); Set> knownClasses = (Set) getKnownClasses(); - outer: + for(ClassMirror m : knownClasses) { if(doesClassExtend(m, superType)) { mirrors.add(m); @@ -528,6 +534,35 @@ public Set> getClassesThatExtend(Class superType) { return (Set) mirrors; } + /** + * Returns a list of known classes that extend the given superclass, or implement the given interface. + * + * @param + * @param superType + * @return + */ + @SuppressWarnings("unchecked") + public Set> getClassesThatExtend(ClassMirror superType) { + if(superType.getClassName().equals("java.lang.Object")) { + return (Set>) (Set) getKnownClasses(); + } + if(classMirrorSubtypeCache.containsKey(superType)) { + return new HashSet<>((Set) classMirrorSubtypeCache.get(superType)); + } + doDiscovery(); + Set> mirrors = new HashSet<>(); + Set> knownClasses = (Set) getKnownClasses(); + outer: + for(ClassMirror m : knownClasses) { + if(doesClassExtend(m, superType)) { + mirrors.add(m); + } + } + classMirrorSubtypeCache.put(superType, mirrors); + return (Set) mirrors; + + } + /** * Returns true if subClass extends, implements, or is superClass. This searches the entire known class ecosystem, * including the known ClassMirrors for this information. @@ -538,100 +573,146 @@ public Set> getClassesThatExtend(Class superType) { */ @SuppressWarnings("unchecked") public boolean doesClassExtend(ClassMirror subClass, Class superClass) { - if(subClass.directlyExtendsFrom(superClass)) { - //Trivial case, so just add this now, then continue. - return true; + return doesClassExtend(subClass, new ClassMirror(superClass)); + } + + private Set doesClassExtendWithBlacklist(ClassMirror c, Set blacklist) { + if(blacklist.contains(c)) { + return blacklist; } - //Well, crap, more complicated. Ok, so, the list of supers - //can probably be walked up even further, so we need to find - //the supers of these (and make sure it's not in the ClassMirror - //cache, to avoid loading classes unneccessarily) and then load - //the actual Class object for them. Essentially, this falls back - //to loading the class when it - //can't be found in the mirrors pool. - Set> supers = new HashSet<>(); - // Interfaces don't have a superclass. If they extend something, that's different. - if(!subClass.isInterface()) { - //Get the superclass. If it's java.lang.Object, we're done. - ClassReferenceMirror su = subClass.getSuperClass(); - while(!"Ljava/lang/Object;".equals(su.getJVMName())) { - supers.add(su); - ClassMirror find = getClassMirrorFromJVMName(su.getJVMName()); - if(find == null) { - try { - //Ok, have to Class.forName this one - Class clazz = ClassUtils.forCanonicalName(su.toString(), false, defaultClassLoader); - //We can just use isAssignableFrom now - if(superClass.isAssignableFrom(clazz)) { - return true; - } else { - //We need to add change the reference to su - su = new ClassReferenceMirror<>("L" + clazz.getSuperclass().getName().replace('.', '/') + ";"); - } - } catch (ClassNotFoundException ex) { - //Hmm, ok? I guess something bad happened, so let's break - //the loop and give up on this class. - return false; - } - } else { - su = find.getSuperClass(); - } - } - for(ClassReferenceMirror r : supers) { - // Look through the supers. If any of them equal the search class, return true - if(r.getJVMName().equals(ClassUtils.getJVMName(superClass))) { - return true; - } + while(true) { + blacklist.add(c); + Object su; + su = c.getSuperClass(); + if(su == null) { + return blacklist; } - } - //Same thing now, but for interfaces - Deque> interfaces = new ArrayDeque<>(); - Set> handled = new HashSet<>(); - interfaces.addAll(subClass.getInterfaces()); - //Also have to add all the supers' interfaces too - for(ClassReferenceMirror r : supers) { - ClassMirror find = getClassMirrorFromJVMName(r.getJVMName()); - if(find == null) { - try { - Class clazz = Class.forName(r.toString()); - for(Class c : clazz.getInterfaces()) { - interfaces.add(new ClassReferenceMirror<>("L" + c.getName().replace('.', '/') + ";")); - } - } catch (ClassNotFoundException ex) { - return false; - } + blacklist.add(su); + blacklist.addAll(doesClassExtendWithBlacklist(su, blacklist)); + if(c instanceof Class) { + for(Class iface : ((Class)c).getInterfaces()) + } else if(c instanceof ClassMirror) { + } else { - interfaces.addAll(find.getInterfaces()); + throw new Error(); } } - while(!interfaces.isEmpty()) { - ClassReferenceMirror in = interfaces.pop(); - if(ClassUtils.getJVMName(superClass).equals(in.getJVMName())) { - //Early short circuit. We know it's in the the list already. - return true; - } - if(handled.contains(in)) { - continue; - } - handled.add(in); - supers.add(in); - ClassMirror find = getClassMirrorFromJVMName(in.getJVMName()); - if(find != null) { - interfaces.addAll(find.getInterfaces()); - } else { - try { - //Again, have to check Class.forName - Class clazz = ClassUtils.forCanonicalName(in.toString(), false, getDefaultClassLoader()); - if(superClass.isAssignableFrom(clazz)) { - return true; - } - } catch (ClassNotFoundException ex) { - return false; - } - } + } + + @SuppressWarnings("unchecked") + public boolean doesClassExtend(ClassMirror subClass, ClassMirror superClass) { + if(superClass.getClassName().equals("com.laytonsmith.core.natives.interfaces.Mixed") + && subClass.getClassName().contains("CommandHelper")) { + System.out.println("Checking if " + subClass + " is extension of Mixed"); } - //Nope. - return false; + +// if(subClass.directlyExtendsFrom(superClass)) { +// //Trivial case, so just add this now, then continue. +// System.out.println("1"); +// return true; +// } +// //Well, crap, more complicated. Ok, so, the list of supers +// //can probably be walked up even further, so we need to find +// //the supers of these (and make sure it's not in the ClassMirror +// //cache, to avoid loading classes unneccessarily) and then load +// //the actual Class object for them. Essentially, this falls back +// //to loading the class when it +// //can't be found in the mirrors pool. +// Set> supers = new HashSet<>(); +// // Interfaces don't have a superclass. If they extend something, that's different. +// if(!subClass.isInterface()) { +// //Get the superclass. If it's java.lang.Object, we're done. +// ClassReferenceMirror su = subClass.getSuperClass(); +// while(!"Ljava/lang/Object;".equals(su.getJVMName())) { +// supers.add(su); +// ClassMirror find = getClassMirrorFromJVMName(su.getJVMName()); +// if(find == null) { +// try { +// //Ok, have to Class.forName this one +// Class clazz = ClassUtils.forCanonicalName(su.toString(), false, defaultClassLoader); +// //We can just use isAssignableFrom now +// if(superClass.directlyExtendsFrom(clazz)) { +// System.out.println("2"); +// return true; +// } else { +// //We need to add change the reference to su +// su = new ClassReferenceMirror<>("L" + clazz.getSuperclass().getName().replace('.', '/') + ";"); +// } +// } catch(ClassNotFoundException ex) { +// //Hmm, ok? I guess something bad happened, so let's break +// //the loop and give up on this class. +// System.out.println("3"); +// return false; +// } +// } else { +// su = find.getSuperClass(); +// } +// } +// for(ClassReferenceMirror r : supers) { +// // Look through the supers. If any of them equal the search class, return true +// if(r.getJVMName().equals(superClass.getJVMClassName())) { +// System.out.println("4"); +// return true; +// } +// } +// } +// //Same thing now, but for interfaces +// Deque> interfaces = new ArrayDeque<>(); +// Set> handled = new HashSet<>(); +// interfaces.addAll(subClass.getInterfaces()); +// //Also have to add all the supers' interfaces too +// for(ClassReferenceMirror r : supers) { +// ClassMirror find = getClassMirrorFromJVMName(r.getJVMName()); +// if(find == null) { +// try { +// Class clazz = Class.forName(r.toString()); +// for(Class c : clazz.getInterfaces()) { +// interfaces.add(new ClassReferenceMirror<>("L" + c.getName().replace('.', '/') + ";")); +// } +// } catch(ClassNotFoundException ex) { +// System.out.println("5"); +// return false; +// } +// } else { +// interfaces.addAll(find.getInterfaces()); +// } +// } +// while(!interfaces.isEmpty()) { +// ClassReferenceMirror in = interfaces.pop(); +// if(superClass.getJVMClassName().equals(in.getJVMName())) { +// //Early short circuit. We know it's in the the list already. +// System.out.println("6"); +// return true; +// } +// if(handled.contains(in)) { +// continue; +// } +// handled.add(in); +// supers.add(in); +// ClassMirror find = getClassMirrorFromJVMName(in.getJVMName()); +// if(find != null) { +// interfaces.addAll(find.getInterfaces()); +// } else { +// try { +// //Again, have to check Class.forName +// Class clazz = ClassUtils.forCanonicalName(in.toString(), false, getDefaultClassLoader()); +// if(subClass.directlyExtendsFrom(clazz)) { +// if(superClass.getClassName().equals("com.laytonsmith.core.natives.interfaces.Mixed") +// && subClass.getClassName().contains("CommandHelper")) { +// System.out.println("clazz is " + clazz + "; superClass is " + superClass); +// } +// System.out.println("7"); +// return true; +// } +// } catch(ClassNotFoundException ex) { +// System.out.println("8"); +// return false; +// } +// } +// } +// //Nope. +// System.out.println("9"); +// return false; } /** @@ -681,6 +762,29 @@ private ClassMirror getClassMirrorFromJVMName(String className) { return null; } + /** + * Converts the {@link ClassReferenceMirror} to a {@link ClassMirror} object. Note that in general, a + * ClassReferenceMirror does not need to represent an actually known class, so it is possible that the class + * referenced by the class is not known by the JVM (or at least the ClassDiscovery system) but it was known at + * compile time. If that is the case, then the system will not be able to create a ClassMirror object, which + * contains information about the class based on the information in that class's bytecode, and will therefore throw + * a ClassNotFoundException. + * + * In order to correct this problem, you'll need to link to the jar via {@link #addDiscoveryLocation(java.net.URL)} + * first. + * + * @param annotation + * @return + */ + @SuppressWarnings("unchecked") + public ClassMirror getClassMirror(ClassReferenceMirror reference) throws ClassNotFoundException { + ClassMirror mirror = (ClassMirror) getClassMirrorFromJVMName(reference.getJVMName()); + if(mirror == null) { + throw new ClassNotFoundException(); + } + return mirror; + } + /** * Returns a list of classes that have been annotated with the specified annotation. This will work with annotations * that have been declared with the {@link RetentionPolicy#CLASS} property. @@ -758,7 +862,7 @@ public Set> loadClassesWithAnnotationThatExtend(Class cm : getClassesWithAnnotationThatExtend(annotation, superClass)) { try { set.add(cm.loadClass(loader, initialize)); - } catch (NoClassDefFoundError e) { + } catch(NoClassDefFoundError e) { //Ignore this for now? //throw new Error("While trying to process " + cm.toString() + ", an error occurred.", e); } @@ -793,7 +897,7 @@ public Set> loadClassesWithAnnotation(Class annot for(ClassMirror cm : getClassesWithAnnotation(annotation)) { try { set.add(cm.loadClass(loader, initialize)); - } catch (NoClassDefFoundError e) { + } catch(NoClassDefFoundError e) { //Ignore this for now? //throw new Error("While trying to process " + cm.toString() + ", an error occurred.", e); } @@ -877,7 +981,7 @@ public Set loadMethodsWithAnnotation(Class annotat set.add(mm.loadMethod(loader, initialize)); } return set; - } catch (ClassNotFoundException ex) { + } catch(ClassNotFoundException ex) { throw new NoClassDefFoundError(); } } @@ -943,7 +1047,7 @@ public Set> loadConstructorsWithAnnotation(Class c) { try { try { packageRoot = StringUtils.replaceLast(thisClass, Pattern.quote(c.getName().replaceAll("\\.", "/") + ".class"), ""); - } catch (Exception e) { + } catch(Exception e) { //Hmm, ok, try this then packageRoot = c.getProtectionDomain().getCodeSource().getLocation().toString(); } @@ -1078,9 +1182,9 @@ public static URL GetClassContainer(Class c) { packageRoot = packageRoot.replaceFirst("jar:", ""); } return new URL(packageRoot); - } catch (UnsupportedEncodingException e) { + } catch(UnsupportedEncodingException e) { throw new RuntimeException("While interrogating " + c.getName() + ", an unexpected exception was thrown.", e); - } catch (MalformedURLException e) { + } catch(MalformedURLException e) { throw new RuntimeException("While interrogating " + c.getName() + ", an unexpected exception was thrown for potential URL: \"" + packageRoot + "\"", e); } } diff --git a/src/main/java/com/laytonsmith/PureUtilities/ClassLoading/ClassMirror/ClassMirror.java b/src/main/java/com/laytonsmith/PureUtilities/ClassLoading/ClassMirror/ClassMirror.java index 0ad6bb343..a9f1df993 100644 --- a/src/main/java/com/laytonsmith/PureUtilities/ClassLoading/ClassMirror/ClassMirror.java +++ b/src/main/java/com/laytonsmith/PureUtilities/ClassLoading/ClassMirror/ClassMirror.java @@ -152,11 +152,17 @@ public boolean isAbstract() { /** * Returns a {@link ClassReferenceMirror} to the class's superclass. * + * If the underlying Class is java.lang.Object, then this method returns + * null. * @return */ public ClassReferenceMirror getSuperClass() { if(underlyingClass != null) { - return ClassReferenceMirror.fromClass(underlyingClass.getSuperclass()); + Class su = underlyingClass.getSuperclass(); + if(su == null) { + return null; + } + return ClassReferenceMirror.fromClass(su); } return new ClassReferenceMirror<>("L" + info.superClass + ";"); } @@ -519,6 +525,44 @@ public boolean directlyExtendsFrom(Class superClass) { return false; } + /** + * Returns true if this class either extends or implements the class + * specified, or is the same as that class. Note that if it transiently + * extends from this class, it can't necessarily find that information + * without actually loading the intermediate class, so this is a less useful + * method than {@link Class#isAssignableFrom(java.lang.Class)}, however, in + * combination with a system that is aware of all classes in a class + * ecosystem, this can be used to piece together that information without + * actually loading the classes. + * + * @param superClass + * @return + */ + public boolean directlyExtendsFrom(ClassMirror superClass) { + if(underlyingClass != null) { + if(ClassUtils.getJVMName(underlyingClass).equals(superClass.getJVMClassName())) { + return true; + } + if(underlyingClass.isInterface()) { + return Arrays.asList(underlyingClass.getInterfaces()).stream() + .map((Class t) -> ClassUtils.getJVMName(t)) + .anyMatch((String t) -> t.equals(superClass.getJVMClassName())); + } else { + return ClassUtils.getJVMName(underlyingClass.getSuperclass()).equals(superClass.getJVMClassName()); + } + } + String name = superClass.getJVMClassName(); + if(("L" + info.superClass + ";").equals(name)) { + return true; + } + for(String in : info.interfaces) { + if(("L" + in + ";").equals(name)) { + return true; + } + } + return false; + } + /** * Returns the Package this class is in. If this is not in a package, null * is returned. diff --git a/src/main/java/com/laytonsmith/PureUtilities/Common/Annotations/AnnotationChecks.java b/src/main/java/com/laytonsmith/PureUtilities/Common/Annotations/AnnotationChecks.java index ad412d48f..93816f0cf 100644 --- a/src/main/java/com/laytonsmith/PureUtilities/Common/Annotations/AnnotationChecks.java +++ b/src/main/java/com/laytonsmith/PureUtilities/Common/Annotations/AnnotationChecks.java @@ -1,7 +1,11 @@ package com.laytonsmith.PureUtilities.Common.Annotations; import com.laytonsmith.PureUtilities.ClassLoading.ClassDiscovery; +import com.laytonsmith.PureUtilities.ClassLoading.ClassMirror.AbstractMethodMirror; import com.laytonsmith.PureUtilities.ClassLoading.ClassMirror.ClassMirror; +import com.laytonsmith.PureUtilities.ClassLoading.ClassMirror.ClassReferenceMirror; +import com.laytonsmith.PureUtilities.ClassLoading.ClassMirror.ConstructorMirror; +import com.laytonsmith.PureUtilities.ClassLoading.ClassMirror.MethodMirror; import com.laytonsmith.PureUtilities.Common.ReflectionUtils; import com.laytonsmith.PureUtilities.Common.StringUtils; import com.laytonsmith.PureUtilities.ExhaustiveVisitor; @@ -11,7 +15,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Member; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.Arrays; @@ -53,41 +56,50 @@ public static void checkForTypeInTypeofClasses() throws Exception { @SuppressWarnings("UnnecessaryLabelOnBreakStatement") public static void checkForceImplementation() throws Exception { Set uhohs = new HashSet<>(); - Set> set = ClassDiscovery.getDefaultInstance().loadConstructorsWithAnnotation(ForceImplementation.class); - for(Constructor cons : set) { - Class superClass = cons.getDeclaringClass(); - Set s = ClassDiscovery.getDefaultInstance().loadClassesThatExtend(superClass); + Set> set = ClassDiscovery.getDefaultInstance().getConstructorsWithAnnotation(ForceImplementation.class); + for(ConstructorMirror cons : set) { + ClassReferenceMirror superClass = cons.getDeclaringClass(); + Set> s = ClassDiscovery.getDefaultInstance().getClassesThatExtend(ClassDiscovery.getDefaultInstance().getClassMirror(superClass)); checkImplements: - for(Class c : s) { + for(ClassMirror c : s) { + if(c.getModifiers().isAbstract()) { + // Abstract classes are not required to implement this + continue; + } // c is the class we want to check to make sure it implements cons - for(Constructor cCons : c.getDeclaredConstructors()) { - if(Arrays.equals(cons.getParameterTypes(), cCons.getParameterTypes())) { + for(ConstructorMirror cCons : c.getConstructors()) { + if(Arrays.asList(cons.getParams()).equals(Arrays.asList(cCons.getParams()))) { continue checkImplements; } } - if(c.isMemberClass() && (c.getModifiers() & Modifier.STATIC) == 0) { + if(c.getJVMClassName().contains("$") && c.getModifiers().isStatic()) { // Ok, so, an inner, non static class actually passes the super class's reference to the constructor as // the first parameter, at a byte code level. So this is a different type of error, or at least, a different // error message will be helpful. - uhohs.add(c.getName() + " must be static."); + uhohs.add(c.getClassName() + " must be static."); } else { - uhohs.add(c.getName() + " must implement the constructor with signature (" + getSignature(cons) + "), but doesn't."); + uhohs.add(c.getClassName() + " must implement the constructor with signature (" + getSignature(cons) + "), but doesn't."); } } } - Set set2 = ClassDiscovery.getDefaultInstance().loadMethodsWithAnnotation(ForceImplementation.class); - for(Method cons : set2) { - Class superClass = cons.getDeclaringClass(); + Set set2 = ClassDiscovery.getDefaultInstance().getMethodsWithAnnotation(ForceImplementation.class); + for(MethodMirror cons : set2) { + ClassReferenceMirror superClass = cons.getDeclaringClass(); @SuppressWarnings("unchecked") - Set> s = ClassDiscovery.getDefaultInstance().loadClassesThatExtend(superClass); + Set> s = ClassDiscovery.getDefaultInstance().getClassesThatExtend(ClassDiscovery.getDefaultInstance().getClassMirror(superClass)); + String S = StringUtils.Join(s, "; "); checkImplements: - for(Class c : s) { + for(ClassMirror c : s) { + if(c.getModifiers().isAbstract() ) { + // Abstract classes are not required to implement this + continue; + } // First, check if maybe it has a InterfaceRunner for it findRunner: - for(Class ir : ClassDiscovery.getDefaultInstance().loadClassesWithAnnotation(InterfaceRunnerFor.class)) { - InterfaceRunnerFor ira = ir.getAnnotation(InterfaceRunnerFor.class); - if(ira.value() == c) { + for(ClassMirror ir : ClassDiscovery.getDefaultInstance().getClassesWithAnnotation(InterfaceRunnerFor.class)) { + String ira = (String) ir.getAnnotation(InterfaceRunnerFor.class).getValue("value"); + if(ira.equals(c.getClassName())) { // Aha! It does. Set c to ir, then break this for loop. // The runner for this class will act in the stead of this // class. @@ -96,12 +108,13 @@ public static void checkForceImplementation() throws Exception { } } // c is the class we want to check to make sure it implements cons - for(Method cCons : c.getDeclaredMethods()) { - if(cCons.getName().equals(cons.getName()) && Arrays.equals(cons.getParameterTypes(), cCons.getParameterTypes())) { + for(MethodMirror cCons : c.getMethods()) { + if(cCons.getName().equals(cons.getName()) && Arrays.asList(cons.getParams()).equals(Arrays.asList(cCons.getParams()))) { continue checkImplements; } } - uhohs.add(c.getName() + " must implement the method with signature " + cons.getName() + "(" + getSignature(cons) + "), but doesn't."); + System.err.println("Adding uh oh"); + uhohs.add(c.getClassName() + " must implement the method with signature " + cons.getName() + "(" + getSignature(cons) + "), but doesn't."); } } @@ -112,21 +125,10 @@ public static void checkForceImplementation() throws Exception { } } - private static String getSignature(Member executable) { + private static String getSignature(AbstractMethodMirror executable) { List l = new ArrayList<>(); -// for(Class cc : executable.getParameterTypes()){ -// l.add(cc.getName()); -// } - if(executable instanceof Method) { - for(Class cc : ((Method) executable).getParameterTypes()) { - l.add(cc.getName()); - } - } else if(executable instanceof Constructor) { - for(Class cc : ((Constructor) executable).getParameterTypes()) { - l.add(cc.getName()); - } - } else { - throw new Error("Unexpected executable type"); + for(ClassReferenceMirror crm : executable.getParams()) { + l.add(crm.toString()); } return StringUtils.Join(l, ", "); } diff --git a/src/main/java/com/laytonsmith/core/functions/Debug.java b/src/main/java/com/laytonsmith/core/functions/Debug.java index 570b6f7cd..97ea15fef 100644 --- a/src/main/java/com/laytonsmith/core/functions/Debug.java +++ b/src/main/java/com/laytonsmith/core/functions/Debug.java @@ -2,6 +2,7 @@ import com.laytonsmith.PureUtilities.Common.StreamUtils; import com.laytonsmith.PureUtilities.HeapDumper; +import com.laytonsmith.PureUtilities.Version; import com.laytonsmith.annotations.api; import com.laytonsmith.annotations.noboilerplate; import com.laytonsmith.core.CHVersion; @@ -265,8 +266,7 @@ public Construct exec(Target t, Environment environment, Construct... args) thro if(args[0] instanceof IVariable) { if(Prefs.DebugMode()) { IVariable ivar = (IVariable) args[0]; - Construct val = environment.getEnv(GlobalEnv.class).GetVarList().get(ivar.getVariableName(), t); - StreamUtils.GetSystemOut().println(ivar.getVariableName() + ": " + val.val()); + DoTrace(ivar, environment, t); } return CVoid.VOID; } else { @@ -303,6 +303,69 @@ public CHVersion since() { return CHVersion.V3_3_1; } + public static void DoTrace(IVariable var, Environment environment, Target t) { + Construct val = environment.getEnv(GlobalEnv.class).GetVarList().get(var.getVariableName(), t); + StreamUtils.GetSystemOut().println(var.getVariableName() + ": " + val.val()); + } + + } + + @api + public static class dtrace extends AbstractFunction { + + @Override + public Class[] thrown() { + return new Class[]{CRECastException.class}; + } + + @Override + public boolean isRestricted() { + return true; + } + + @Override + public Boolean runAsync() { + return null; + } + + @Override + public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException { + if(args[0] instanceof IVariable) { + IVariable ivar = (IVariable) args[0]; + trace.DoTrace(ivar, environment, t); + return CVoid.VOID; + } else { + throw new CRECastException("Expecting an ivar, but recieved " + args[0].getCType() + " instead", t); + } + } + + @Override + public boolean preResolveVariables() { + return false; + } + + @Override + public String getName() { + return "dtrace"; + } + + @Override + public Integer[] numArgs() { + return new Integer[]{1}; + } + + @Override + public String docs() { + return "void {var} Works like trace(), but ignores the debug setting, and always prints out."; + } + + @Override + public Version since() { + return CHVersion.V3_3_3; + } + + + } // @api diff --git a/src/main/java/com/laytonsmith/core/functions/FunctionBase.java b/src/main/java/com/laytonsmith/core/functions/FunctionBase.java index 8617b4819..855ad3eae 100644 --- a/src/main/java/com/laytonsmith/core/functions/FunctionBase.java +++ b/src/main/java/com/laytonsmith/core/functions/FunctionBase.java @@ -1,5 +1,6 @@ package com.laytonsmith.core.functions; +//import com.laytonsmith.PureUtilities.Common.Annotations.ForceImplementation; import com.laytonsmith.annotations.core; import com.laytonsmith.core.snapins.PackagePermission; @@ -19,6 +20,7 @@ public interface FunctionBase { * * @return */ + //@ForceImplementation public String getName(); /** @@ -37,6 +39,7 @@ public interface FunctionBase { * @return A string with the documentation, or null, which will give a standard message to the user telling them * there is no documentation for this function yet. */ + //@ForceImplementation public String docs(); /**