Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java 9 compatibility report #504

Open
jponge opened this issue Oct 18, 2017 · 0 comments
Open

Java 9 compatibility report #504

jponge opened this issue Oct 18, 2017 · 0 comments

Comments

@jponge
Copy link
Member

@jponge jponge commented Oct 18, 2017

Trying to build and test Golo with the now-released Java 9, here is a compatibility report with issues and pointers.

Note that we are taking about compatibility on the classpath (that is, like it used to be for Java 7 and 8), making Golo a modular code base according to the JPMS is another issue. A quick-win can be to provide a reserved name and auto-modules, but until all Golo dependencies have migrated to JPMS and given the peculiar nature of a dynamic JVM language, this is clearly another issue.

Compilation

We have a single warning:

src/main/java/org/eclipse/golo/runtime/adapters/JavaBytecodeAdapterGenerator.java:77: warning: [deprecation] isAccessible() in AccessibleObject has been deprecated
      if (!defineClass.isAccessible()) {
                      ^
1 warning

Reflective accesses are becoming more and more complicated.

Here, we are trying to inject bytecode into a classloader:

public Class<?> generateIntoDefinitionClassloader(AdapterDefinition adapterDefinition) {
    try {
      byte[] bytecode = generate(adapterDefinition);
      ClassLoader classLoader = adapterDefinition.getClassLoader();
      Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
      if (!defineClass.isAccessible()) {
        defineClass.setAccessible(true);
      }
      return (Class<?>) defineClass.invoke(classLoader, adapterDefinition.getName(), bytecode, 0, bytecode.length);
    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
      throw new RuntimeException(e);
    }
  }

The "new way" to inject classes with Java 9 is to have a lookup on an object, hoping to have sufficient permissions on that object, and calling http://download.java.net/java/jdk9/docs/api/java/lang/invoke/MethodHandles.Lookup.html#defineClass-byte:A-

Warnings during tests execution

Executing our test suite yields illegal reflective accesses, again in JavaBytecodeAdapterGenerator:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.eclipse.golo.runtime.adapters.JavaBytecodeAdapterGenerator (file:/Users/jponge/Code/golo-lang/build/classes/java/main/) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int)
WARNING: Please consider reporting this to the maintainers of org.eclipse.golo.runtime.adapters.JavaBytecodeAdapterGenerator
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

Fixing the above compilation warning should hopefully be the cure.

Failed test

Surprisingly there is only 1 test that fails: org.eclipse.golo.compiler.CompileAndRunTest.test_method_closures.

The stacktrace reveals an issue when calling a method on a proxy:

java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at org.eclipse.golo.compiler.CompileAndRunTest.test_method_closures(CompileAndRunTest.java:928)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:108)
	at org.testng.internal.Invoker.invokeMethod(Invoker.java:661)
	at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:869)
	at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1193)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:126)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
	at org.testng.TestRunner.privateRun(TestRunner.java:744)
	at org.testng.TestRunner.run(TestRunner.java:602)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:380)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:375)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:340)
	at org.testng.SuiteRunner.run(SuiteRunner.java:289)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1301)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1226)
	at org.testng.TestNG.runSuites(TestNG.java:1144)
	at org.testng.TestNG.run(TestNG.java:1115)
	at org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor.runTests(TestNGTestClassProcessor.java:129)
	at org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor.stop(TestNGTestClassProcessor.java:88)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
	at com.sun.proxy.$Proxy1.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:119)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:146)
	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:128)
	at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
	at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: java.lang.NoSuchMethodError: class com.sun.proxy.jdk.proxy2.$Proxy19::actionPerformed
	at org.eclipse.golo.runtime.MethodInvocationSupport.fallback(MethodInvocationSupport.java:270)
	at golotest.execution.Closures.as_explicit_interface(closures.golo:43)
	... 55 more

The exception is thrown in:

function as_explicit_interface = {
  let array = array["boo"]
  let handler = |event| {
    array: set(0, event: getSource() + " -> " + event: getActionCommand())
  }
  let listener = asInterfaceInstance(ActionListener.class, handler)
  listener: actionPerformed(ActionEvent("Plop", 666, "da plop"))
  return array: get(0)
}

on the listener: actionPerformed(ActionEvent("Plop", 666, "da plop")) invocation.

asInterfaceInstance is defined in Predefined and is a straightforward delegate:

public static Object asInterfaceInstance(Object interfaceClass, Object target) {
    require(interfaceClass instanceof Class, "interfaceClass must be a Class");
    require(target instanceof FunctionReference, "target must be a FunctionReference");
    return MethodHandleProxies.asInterfaceInstance((Class<?>) interfaceClass, ((FunctionReference) target).handle());
  }

I am puzzled on why the returned proxy object does not respond to the method...

I quickly tried implementing another single method interface using a Golo closure (Supplier) but even here:

  1. the proxy object is created,
  2. calling the method does not work,
  3. reflection does not work either.

Running samples

Running samples in samples/ gives other pointers to issues.

I tried a few of them...

Coin sample

golo golo --files coin-change.golo 
Coins: [1, 2, 5, 10, 20]
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.eclipse.golo.runtime.RegularMethodFinder (file:/usr/local/Cellar/golo/3.2.0/libexec/lib/golo-3.2.0.jar) to method java.util.LinkedList$ListItr.hasNext()
WARNING: Please consider reporting this to the maintainers of org.eclipse.golo.runtime.RegularMethodFinder
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
0: 1
1: 1
2: 2
10: 11
12: 15
6: 5

Again, reflection access problems.

Decorators

Same issue:

golo golo --files decorators.golo 
42
42
number of params : 0
42
number of params : 1
42
number of params : 2
42
number of params : 3
42
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.eclipse.golo.runtime.RegularMethodFinder (file:/usr/local/Cellar/golo/3.2.0/libexec/lib/golo-3.2.0.jar) to method java.util.LinkedList$ListItr.hasNext()
WARNING: Please consider reporting this to the maintainers of org.eclipse.golo.runtime.RegularMethodFinder
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
42

Http server

It seems like the Sun-internal classes have changed. Perhaps we should get rid of this sample:

$ golo golo --files http-server.golo 
>>> http://localhost:8081/
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.eclipse.golo.runtime.RegularMethodFinder (file:/usr/local/Cellar/golo/3.2.0/libexec/lib/golo-3.2.0.jar) to method sun.net.httpserver.HttpExchangeImpl.getResponseHeaders()
WARNING: Please consider reporting this to the maintainers of org.eclipse.golo.runtime.RegularMethodFinder
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Exception in thread "HTTP-Dispatcher" java.lang.NoSuchMethodError: class sun.net.httpserver.HttpExchangeImpl::sendResponseHeaders
	at org.eclipse.golo.runtime.MethodInvocationSupport.fallback(MethodInvocationSupport.java:270)
	at samples.WebServer.__$$_sugar_closure_0(http-server.golo:30)
	at java.base/java.lang.invoke.MethodHandleProxies$1.invoke(MethodHandleProxies.java:189)
	at jdk.proxy1/com.sun.proxy.jdk.proxy1.$Proxy5.handle(Unknown Source)
	at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
	at jdk.httpserver/sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:82)
	at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:80)
	at jdk.httpserver/sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:685)
	at jdk.httpserver/com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
	at jdk.httpserver/sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:657)
	at jdk.httpserver/sun.net.httpserver.ServerImpl$DefaultExecutor.execute(ServerImpl.java:159)
	at jdk.httpserver/sun.net.httpserver.ServerImpl$Dispatcher.handle(ServerImpl.java:440)
	at jdk.httpserver/sun.net.httpserver.ServerImpl$Dispatcher.run(ServerImpl.java:405)
	at java.base/java.lang.Thread.run(Thread.java:844)

Dynamic evaluation

Works!

golo golo --files dynamic-evaluation.golo 
>>> asModule()
a!
b!
>>> anonymousModule()
a.
b.
>>> asFunction
60
>>> def
60
>>> run
w00t
w00t
w00t
>>> run_map
1
2

Async calls

Works, but gets stuck because an executor is still up and running:

golo golo --files async.golo 
Let's do some useless asynchronous operations...
>>> #fail -> java.util.concurrent.CancellationException
>>> (same thread) truth=42
>>> (another thread) truth=42
>>> #ok -> 666
>>> Fibs: [55, 6765, 832040, 102334155]
Bye!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.