#1516 Java class lookup is too curious

vkuzkokov Fri 29 Apr 2011

Suppose I have 2 java classes. One in the classpath:

package test.core;

import test.util.B;

public class A
{
  B getSth() { return new B(); }
}

One outside the classpath (used solely to compile the former):

package test.util;

public class B
{
}

So, when I use test.core.A through FFI:

using [java]test.core::A

class C
{
  Void main()
  {
    echo(A())
  }
}

Fantom compiler fails to find class A

ERROR: cannot compile script
sys::Err: java.lang.NoClassDefFoundError: test/util/B
  java.lang.Class.getDeclaredMethods0 (Unknown)
  java.lang.Class.privateGetDeclaredMethods (Unknown)
  java.lang.Class.getDeclaredMethods (Unknown)
  compilerJava::JavaReflect.findMethods (JavaReflect.fan:107)
  compilerJava::JavaReflect.loadType (JavaReflect.fan:60)
  compilerJava::JavaBridge.loadType (JavaBridge.fan:58)
  compilerJava::JavaType.load (JavaType.fan:144)
  compilerJava::JavaType.slots (JavaType.fan:65)
  compilerJava::JavaType.method (JavaType.fan:76)
  compilerJava::JavaBridge.resolveConstruction (JavaBridge.fan:85)
  compiler::ResolveExpr.resolveConstruction (ResolveExpr.fan:540)
  compiler::ResolveExpr.resolveExpr (ResolveExpr.fan:165)
  compiler::ResolveExpr.visitExpr (ResolveExpr.fan:123)
  compiler::Expr.walk (Expr.fan:253)
  compiler::Expr.walkExprs (Expr.fan:273)
  compiler::CallExpr.walkChildren (Expr.fan:842)
  compiler::Expr.walk (Expr.fan:252)
  compiler::Stmt.walkExpr (Stmt.fan:61)
  compiler::ExprStmt.walkChildren (Stmt.fan:117)
  compiler::Stmt.walk (Stmt.fan:48)
  33 More...

while it shouldn't try to find it in the first place. Similar Java code

import test.core.A;

class D
{
  public static void main(String[] args)
  {
    System.err.println(new A());
  }
}

compiles and works fine.

In addition to that, if you add test.util.B to classpath for compile time and remove it for runtime it will work too.

brian Fri 29 Apr 2011

I'm not sure I follow this?

alex_panchenko Fri 29 Apr 2011

The issue is that JavaBridge tries to access all members of the class and uses reflection to get method return types, etc. But it fails because B is not available. But actually it's not required to load B, as the code being compiled doesn't access it.

One of the ways to improve it is using the own library to read java classes instead of loading them into memory. Xored has the implementation and it's used to perform build in the IDE.

vkuzkokov Fri 29 Apr 2011

What happens is that compiler tries to resolve the type test.util.B even though we don't need any information about this class and the resulting code will work properly without test.util.B on classpath

brian Fri 29 Apr 2011

What happens is that compiler tries to resolve the type test.util.B even though we don't need any information about this class

I am not really sure you can say that. Class B's public signature includes class A, therefore it isn't really an optional dependency. Class B must have class A present to load. In select situations you might be able to lazily delay needing to know about A though.

Now in our case Fantom uses Java reflection to figure out the Java class type and member signatures (which is why this is failing). In a proper compiler, we would have the compiler read this information from bytecode directly for performance and flexibility. But I never got around to doing that because its so much extra work (although it is what eventually needs to happen).

vkuzkokov Fri 29 Apr 2011

It is exactly an optional dependency until I actually call a method/access a field referring to that class.

Login or Signup to reply.