We tried, over a few rounds of the condy spec, to make claims about what kind 
of exceptions can be thrown during resolution. Specifically, that anything 
thrown will be an Error.

Bootstrap methods execute arbitrary code, so we wrap any non-Error that gets 
thrown in a BootstrapMethodError. I tried finding the equivalent rule for 
ClassLoaders, and could not. With some testing, it turns out that, in fact, 
exceptions thrown by ClassLoaders are propagated without wrapping. See the test 
below.

So, it turns out, resolution (of classes) can result in *any* kind of exception 
being thrown.

This is very old behavior, and I'm pretty sure we don't want to perturb it, but 
I thought it would be good to raise awareness of it. I'll make sure JVMS, as 
modified for condy, treats it appropriately.

—Dan

Test code:

~~~

public class Test {
    public static void main(String... args) throws Exception {
        PreemptingClassLoader l = new PreemptingClassLoader("A", "B");
        Class<?> c = l.loadClass("A");
        Runnable r = (Runnable) c.getDeclaredConstructor().newInstance();
        r.run();
    }
}

~~~

public class A implements Runnable {
    public void run() {
        System.out.println(B.foo);
    }
}

~~~

public class B {
    public static String foo = "foo";
}

~~~

import java.util.*;
import java.io.*;

public class PreemptingClassLoader extends ClassLoader {

  private final Set<String> names = new HashSet<>();
  
  public PreemptingClassLoader(String... names) {
    for (String n : names) this.names.add(n);
  }
  
  protected Class<?> loadClass(String name, boolean resolve) throws 
ClassNotFoundException {
    if (!names.contains(name)) return super.loadClass(name, resolve);
    Class<?> result = findLoadedClass(name);
    if (result == null) {
      if (name.equals("B")) throw new RuntimeException("Hi, mom"); // INSERTED 
TO TRIGGER EXCEPTION
      String filename = name.replace('.', '/') + ".class";
      try (InputStream data = getResourceAsStream(filename)) {
        if (data == null) throw new ClassNotFoundException();
        try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
          int b;
          do {
            b = data.read();
            if (b >= 0) buffer.write(b);
          } while (b >= 0);
          byte[] bytes = buffer.toByteArray();
          result = defineClass(name, bytes, 0, bytes.length);
        }
      }
      catch (IOException e) {
        throw new ClassNotFoundException("Error reading class file", e);
      }
    }
    if (resolve) resolveClass(result);
    return result;
  }

}

Reply via email to