ClassLoaders throwing exceptions

Dan Smith daniel.smith at oracle.com
Wed Feb 7 23:48:28 UTC 2018


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;
  }

}



More information about the valhalla-spec-observers mailing list