diff --git a/CHANGELOG.md b/CHANGELOG.md index 183b6ce7db4e..6032f41ce522 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ New features: * Foreign strings now have all methods of Ruby `String`. They are treated as `#frozen?` UTF-8 Ruby Strings. +* Add `Java.add_to_classpath` method to add jar paths at runtime (#2693, @bjfish). Bug fixes: diff --git a/spec/tags/truffle/interop/add_to_classpath_tags.txt b/spec/tags/truffle/interop/add_to_classpath_tags.txt new file mode 100644 index 000000000000..2fce2b8e163a --- /dev/null +++ b/spec/tags/truffle/interop/add_to_classpath_tags.txt @@ -0,0 +1 @@ +slow:Java.add_to_classpath loads a jar file diff --git a/spec/truffle/interop/add_to_classpath_spec.rb b/spec/truffle/interop/add_to_classpath_spec.rb new file mode 100644 index 000000000000..81f9aeb883b2 --- /dev/null +++ b/spec/truffle/interop/add_to_classpath_spec.rb @@ -0,0 +1,29 @@ +# Copyright (c) 2022, 2022 Oracle and/or its affiliates. All rights reserved. This +# code is released under a tri EPL/GPL/LGPL license. You can use it, +# redistribute it and/or modify it under the terms of the: +# +# Eclipse Public License version 2.0, or +# GNU General Public License version 2, or +# GNU Lesser General Public License version 2.1. + +require_relative '../../ruby/spec_helper' + +guard -> { !TruffleRuby.native? } do + describe "Java.add_to_classpath" do + before :all do + jar_dir = File.expand_path(File.dirname(__FILE__)) + '/fixtures/examplejar' + bin_dir = TruffleRuby.graalvm_home + '/bin' + Dir.chdir(jar_dir) do + system("#{bin_dir}/javac org/truffleruby/examplejar/Example.java") + system("#{bin_dir}/jar cf example.jar org/truffleruby/examplejar/Example.class") + end + @jar_file = jar_dir + '/example.jar' + end + + it "loads a jar file" do + Java.add_to_classpath(@jar_file).should == true + example_object = Java.type('org.truffleruby.examplejar.Example').new + example_object.hello("Spec").should == "Hello Spec" + end + end +end diff --git a/spec/truffle/interop/fixtures/examplejar/org/truffleruby/examplejar/Example.java b/spec/truffle/interop/fixtures/examplejar/org/truffleruby/examplejar/Example.java new file mode 100644 index 000000000000..bf230b742751 --- /dev/null +++ b/spec/truffle/interop/fixtures/examplejar/org/truffleruby/examplejar/Example.java @@ -0,0 +1,9 @@ +package org.truffleruby.examplejar; + +public class Example { + + public String hello(String name){ + return "Hello " + name; + } + +} diff --git a/src/main/java/org/truffleruby/interop/InteropNodes.java b/src/main/java/org/truffleruby/interop/InteropNodes.java index 06af4cf2b40b..bdef49df3d6b 100644 --- a/src/main/java/org/truffleruby/interop/InteropNodes.java +++ b/src/main/java/org/truffleruby/interop/InteropNodes.java @@ -54,6 +54,7 @@ import org.truffleruby.language.control.RaiseException; import org.truffleruby.language.dispatch.DispatchNode; import org.truffleruby.language.library.RubyStringLibrary; +import org.truffleruby.language.loader.FileLoader; import org.truffleruby.language.objects.LogicalClassNode; import org.truffleruby.shared.TruffleRuby; @@ -1767,6 +1768,27 @@ private Object javaType(String name) { return env.lookupHostSymbol(name); } + } + + @Primitive(name = "java_add_to_classpath") + public abstract static class JavaAddToClasspathNode extends PrimitiveArrayArgumentsNode { + + @TruffleBoundary + @Specialization(guards = "strings.isRubyString(path)") + protected boolean javaAddToClasspath(Object path, + @CachedLibrary(limit = "LIBSTRING_CACHE") RubyStringLibrary strings) { + TruffleLanguage.Env env = getContext().getEnv(); + try { + TruffleFile file = FileLoader.getSafeTruffleFile(getLanguage(), getContext(), + strings.getJavaString(path)); + env.addToHostClassPath(file); + return true; + } catch (SecurityException e) { + throw new RaiseException(getContext(), + coreExceptions().securityError("unable to add to classpath", this), e); + } + } + } // endregion diff --git a/src/main/java/org/truffleruby/language/loader/FileLoader.java b/src/main/java/org/truffleruby/language/loader/FileLoader.java index e3eda98daacd..568ab02275fc 100644 --- a/src/main/java/org/truffleruby/language/loader/FileLoader.java +++ b/src/main/java/org/truffleruby/language/loader/FileLoader.java @@ -77,7 +77,7 @@ public Pair loadFile(String path) throws IOException { return Pair.create(source, sourceRope); } - static TruffleFile getSafeTruffleFile(RubyLanguage language, RubyContext context, String path) { + public static TruffleFile getSafeTruffleFile(RubyLanguage language, RubyContext context, String path) { final Env env = context.getEnv(); final TruffleFile file; try { diff --git a/src/main/ruby/truffleruby/core/truffle/polyglot.rb b/src/main/ruby/truffleruby/core/truffle/polyglot.rb index a2dccdddd0af..3dae5be037f8 100644 --- a/src/main/ruby/truffleruby/core/truffle/polyglot.rb +++ b/src/main/ruby/truffleruby/core/truffle/polyglot.rb @@ -535,6 +535,10 @@ class ForeignException < Exception # rubocop:disable Lint/InheritException end module Java + def self.add_to_classpath(path) + Primitive.java_add_to_classpath(path) + end + def self.type(name) Truffle::Interop.java_type(name) end diff --git a/test/mri/excludes/TestGc.rb b/test/mri/excludes/TestGc.rb index 7b519d97038e..2278940e90bf 100644 --- a/test/mri/excludes/TestGc.rb +++ b/test/mri/excludes/TestGc.rb @@ -13,3 +13,4 @@ exclude :test_start_immediate_sweep, "needs investigation" exclude :test_verify_internal_consistency, "needs investigation" exclude :test_gc_disabled_start, "fails on native: GR-38054" +exclude :test_exception_in_finalizer_procs, "transient" diff --git a/test/mri/excludes/TestThread.rb b/test/mri/excludes/TestThread.rb index 1491c6ad3d92..b0efcf7f0e8b 100644 --- a/test/mri/excludes/TestThread.rb +++ b/test/mri/excludes/TestThread.rb @@ -21,3 +21,4 @@ exclude :test_handle_interrupt, "needs investigation" exclude :test_ignore_deadlock, "needs investigation" exclude :test_switch_while_busy_loop, "transient" +exclude :test_handle_interrupt_and_p, "transient"