Skip to content
This repository has been archived by the owner on Sep 28, 2022. It is now read-only.

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
Contributor

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 subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants