Open rodinaarssen opened 2 years ago
This is a duplicate of #109 ;-)
Or an extension of #109; because there we did not talk about Java classes but rather about Rascal source files.
For this to work for both source project and deployed extensions, we also need to solve #110
Ah, that's what you get from not submitting immediately.
How about Java classes in jar files?
I think #109 and #110 should both resolve this somehow. The classpath
variable of PathConfig
needs similar treatment. For example, if plugin://<libName>
points to the root of the class files then lib://<libName>
is enough to add to the classpath
of PathConfig to fix this.
and classpath
is used by the Evaluator in rascal-lsp to recreate ClassLoader
instances from these source locations which are then used by JavaBridge
to load the injected class files.
O yes, that also means that plugin://
must also implement IClassLoaderLocationResolver
(or something in that vain). That way you can hide where exactly in a plugin's private filesystem location the .class
files are stored, without having to adapt the plugin://<libName>
URI.
And that's why it is so good that every registered language has its own Evaluator, because all this stuff is bound at Evaluator instance-creation-time.
And that's why if you change dependencies mid-flight, it's ok after you call registerLanguage
again because that will trigger the loading of a new Evaluator (MUST CHECK)
und und und, ein Katz ist kein Hund
public interface IClassloaderLocationResolver {
/**
* @return the scheme this resolver supports
*/
String scheme();
/**
* Produce a classloader for the given location.
*
* @param loc a location with loc.getScheme().equals(this.getScheme())
* @param parent Classloader to defer to when a needed class is not provided directly by the returned classloader
*
* @return a classloader corresponding to the given loc, with parent classloader `parent` and never null.
* @throws IOException when the location can not be resolved even though the scheme matches,
* or something else goes wrong while loading the classloader itself.
*/
ClassLoader getClassLoader(ISourceLocation loc, ClassLoader parent) throws IOException;
}
Yes, I believe implementing this interface for the new plugin://
scheme of #110 should automatically fix #111, but only if we also fix #109 (because otherwise it only works for level 2 VScode instances and we want LSPs to work in level1)
I think this works after some recent changes, right @jurgenvinju and @rodinaarssen ?
no there is a list of TODO's to make this work properly.
okay, well at least it has improved since opening this issue, so that's progress.
definitely. to finalize this we need to come up with a design for #110, or we have to find another way how to discover and inject jar files from third-party rascal extensions into the classpath of a current project or a current extension. This is related to some of the functionality we got from OSGI in Eclipse when we used Bundle-Require
between Rascal projects.
Hi - I'm commenting here since I think this issue is the same as what I am running into. In short, is there any update on this?
I have a DSL which uses functionality that is provided via Java bindings. However, when adding these Java bindings, the language cannot be successfully registered anymore, as it results in an exception where it cannot link the method, because the class is not found. The stacktrace of this exception can be seen near the end of the logs:
To me, it looks familiar to an issue I had earlier (#156), but I don't see how I can apply the same solution here.
@aukeroorda you are right; this is a similar issue, but now the code is running in a different JVM instance. With #156 we were running a JVM instance per terminal, but registered languages are loaded all in the same "LSP multiplexer" JVM instance, the "Parameterized LSP".
@DavyLandman @rodinaarssen the question is why the classpath as listed in pathConfig.classloaders is not used by the JavaBridge that is used by the Evaluator that is constructed for the registered DSL.
I don't have time today, but that is where our fix should focus on. I really don't want to start a JVM instance for every language; so please don't consider that as a solution.
@rodinaarssen @DavyLandman I'm not clear on the status of this functionality. Is this still broken or did you fix it while doing other fixes?
Sorry, we have not fixed this yet, as we are not currently running into this (pure rascal all the way ;) )
But we have run into this in the past.
ok then I'll put in on my agenda
Hi - Thanks for looking into this again!
I wanted to provide you with the repository I use to test this, but first I did a validation run myself. Here I noticed multiple things:
This second part took my by surprise, as it seemed like the issue was silently fixed! However, this prompted me to look into where and how I use the java bindings. They were used in the picoSummarizer
section of the language server, but the resulting values of the java calls where never used and, I assume, therefore optimized away. I updated my test language to actually use the value in a different section picoHinter
of the language server, and now it crashes again. I moved it to the picoHinter
section since I can observe the output of this directly in the editor when opening a demo.pico
file.
Therefore I would like to provide the reproduction steps for this, as well as the somewhat minimal example repository. The repository is available at this skeleton repo on the java-bindings-in-language-server
branch. To clone this branch directly:
git clone -b java-bindings-in-language-server https://github.com/aukeroorda/rascal-lsp-skeletons.git
Steps:
rascal-lsp-skeletons/my-app
directory:
cd rascal-lsp-skeletons/my-app
mvn verify
code .
CorelLanguageServer.rsc
, and click Run in new Rascal terminal
above the main()
functionLanguage Parametric Rascal Language Server
in the dropdown to view the error in the logs:
2022-06-02 12:49:23,372 [pool-2-thread-36] INFO - Evaluator: evaluator for StateMachineLanguage Creating new progress bar: T22cdd9ff382c0a23d6 Loading evaluator for StateMachineLanguage []
2022-06-02 12:49:23,380 [pool-3-thread-1] INFO - Evaluator: evaluator for StateMachineLanguage Valid initialized progress bar: T22cdd9ff382c0a23d6
2022-06-02 12:49:26,031 [pool-2-thread-36] WARN - Evaluator: evaluator for StateMachineLanguage |project://my-app/src/CorelLanguageServer.rsc|(43,26,<4,0>,<4,26>) : Could not load RascalJavaBindings due to: Cannot link method com.mycompany.app.App because: class not found at |project://my-app/src/CorelLanguageServer.rsc|(43,26,<4,0>,<4,26>)
org.rascalmpl.exceptions.JavaMethodLink: Cannot link method com.mycompany.app.App because: class not found
at org.rascalmpl.interpreter.utils.JavaBridge.getJavaClassInstance(JavaBridge.java:432)
at org.rascalmpl.interpreter.result.JavaMethod.<init>(JavaMethod.java:95)
at org.rascalmpl.interpreter.result.JavaMethod.<init>(JavaMethod.java:52)
at org.rascalmpl.semantics.dynamic.FunctionDeclaration$Abstract.interpret(FunctionDeclaration.java:61)
at org.rascalmpl.semantics.dynamic.Declaration$Function.interpret(Declaration.java:117)
at org.rascalmpl.semantics.dynamic.Toplevel$GivenVisibility.interpret(Toplevel.java:35)
at org.rascalmpl.semantics.dynamic.Module$Default.interpret(Module.java:79)
at org.rascalmpl.semantics.dynamic.Import.loadModule(Import.java:319)
at org.rascalmpl.semantics.dynamic.Import.importModule(Import.java:240)
at org.rascalmpl.semantics.dynamic.Import$Default.interpret(Import.java:206)
at org.rascalmpl.semantics.dynamic.Import.evalImport(Import.java:477)
at org.rascalmpl.semantics.dynamic.Import.parseModuleAndFragments(Import.java:442)
at org.rascalmpl.interpreter.Evaluator.parseModuleAndFragments(Evaluator.java:1416)
at org.rascalmpl.interpreter.Evaluator.parseModuleAndFragments(Evaluator.java:1410)
at org.rascalmpl.semantics.dynamic.Import.buildModule(Import.java:378)
at org.rascalmpl.semantics.dynamic.Import.loadModule(Import.java:306)
at org.rascalmpl.semantics.dynamic.Import.importModule(Import.java:240)
at org.rascalmpl.interpreter.Evaluator.doImport(Evaluator.java:1144)
at org.rascalmpl.vscode.lsp.util.EvaluatorUtil.lambda$makeFutureEvaluator$2(EvaluatorUtil.java:138)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)
2022-06-02 12:49:26,032 [pool-2-thread-36] WARN - Evaluator: evaluator for StateMachineLanguage |project://my-app/src/CorelLanguageServer.rsc|(43,26,<4,0>,<4,26>) : Could not import module RascalJavaBindings: Cannot link method com.mycompany.app.App because: class not found
Advice: |http://tutor.rascal-mpl.org/Errors/Static/ModuleImport/ModuleImport.html|
2022-06-02 12:49:29,814 [pool-2-thread-36] INFO - Evaluator: evaluator for StateMachineLanguage Finishing progress bar: T22cdd9ff382c0a23d6 - Loading evaluator for StateMachineLanguage
2022-06-02 12:49:29,815 [pool-2-thread-36] INFO - Evaluator: evaluator for StateMachineLanguage Finished progress bar: T22cdd9ff382c0a23d6 - Loading evaluator for StateMachineLanguage
2022-06-02 12:49:29,816 [pool-2-thread-37] INFO - Evaluator: evaluator for StateMachineLanguage Creating new progress bar: T396db75c3937aa5e01 StateMachineLanguage: StateMachineLanguage: loading contributions []
2022-06-02 12:49:29,817 [pool-3-thread-1] INFO - Evaluator: evaluator for StateMachineLanguage Valid initialized progress bar: T396db75c3937aa5e01
2022-06-02 12:49:29,818 [pool-2-thread-37] INFO - Evaluator: evaluator for StateMachineLanguage Finishing progress bar: T396db75c3937aa5e01 - StateMachineLanguage: StateMachineLanguage: loading contributions
2022-06-02 12:49:29,818 [pool-2-thread-37] INFO - Evaluator: evaluator for StateMachineLanguage Finished progress bar: T396db75c3937aa5e01 - StateMachineLanguage: StateMachineLanguage: loading contributions
Ok thanks! This is very helpful @aukeroorda
I believe that the above is caused by the brittleness of the target folder. In several cases, I have observed the situation where .class
files are moved by VScode calling the mvn clean
command (I guess), and then mvn compile
again but perhaps too late for an LSP instance or a terminal to load the classes.
The "workaround" is to type mvn compile
in the project before registerLanguage
or before starting a terminal.
The same solution as in #156 (workaround) could work, because instead of the target://myLib
folder (which resolves to file:///...myLib/target/classes
, a reference to the jar
file would not be as unstable as the contents of the target
folder.
There is something to be said for a solution that knows how to run mvn compiler:compile
just before reloading the new classes if they are there. But we don't want to do this at every command on the REPL... Before a registerLanguage
is feasible..
As the Java classpath is fixed at JVM startup time, Java classes need to be loaded using other classloaders.