Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/aot-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ jobs:
target/graalvm-native-image/jelly-cli version | grep "JVM reflection: supported"

# Test RDF conversions
echo '_:b <http://t.org/> _:b .' | target/graalvm-native-image/jelly-cli \
rdf to-jelly --in-format=nt > out.jelly && \
echo '_:b <http://t.org/> _:b .' > in.nt
target/graalvm-native-image/jelly-cli \
rdf to-jelly --in-format=nt in.nt > out.jelly && \
[ -s out.jelly ]
target/graalvm-native-image/jelly-cli \
rdf from-jelly --out-format=jelly-text out.jelly > out.txt && \
Expand All @@ -56,6 +57,10 @@ jobs:
echo '<?xml version="1.0"?><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Seq rdf:about="http://example.org/favourite-fruit"></rdf:Seq></rdf:RDF>' | \
target/graalvm-native-image/jelly-cli rdf to-jelly --in-format "rdfxml" > rdfxml.jelly && \
[ -s rdfxml.jelly ]

# Test rdf validate
target/graalvm-native-image/jelly-cli \
rdf validate out.jelly --compare-to-rdf-file in.nt

- name: Upload binary
uses: actions/upload-artifact@v4
Expand Down
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ lazy val graalOptions = Seq(
"--initialize-at-build-time=org.glassfish.json.UnicodeDetectingInputStream",
"-H:+TrackPrimitiveValues", // SkipFlow optimization -- will be default in GraalVM 25
"-H:+UsePredicates", // SkipFlow optimization -- will be default in GraalVM 25
// Make sure we don't include stuff that should be unreachable in the native image
"-H:AbortOnMethodReachable=*UUID.randomUUID*",
)

lazy val TestSerial = config("test-serial") extend Test
Expand Down
35 changes: 27 additions & 8 deletions src/main/java/eu/neverblink/jelly/cli/graal/GraalSubstitutes.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

import java.net.URI;
import java.nio.charset.Charset;
import java.security.Provider;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

// Substitutions of classes and methods for GraalVM native image builds.
// These try to remove things from the static analysis (and, in effect, from the final binary)
Expand Down Expand Up @@ -53,14 +53,33 @@ final class HttpLibSubstitute { }
final class HttpEnvSubstitute { }

/**
* UUID generation is used by Jena for blank node IDs, but we can fall back to the default implementation.
* Use pseudo-random UUIDs instead of secure random ones for seeding the blank node allocator.
* The secure random number generation pulls in a lot of stuff we don't need.
* <p>
* For conversion commands, this is not used at all, because we preserve blank node IDs.
*/
@Substitute
@TargetClass(className = "sun.security.jca.ProviderList")
final class ProviderListSubstitute {
@TargetClass(org.apache.jena.riot.lang.BlankNodeAllocatorHash.class)
final class BlankNodeAllocatorHashSubstitute {
@Substitute
UUID freshSeed() {
ThreadLocalRandom r = ThreadLocalRandom.current();
return new UUID(r.nextLong(), r.nextLong());
}
}

/**
* Jena uses secure random number generation to create blank node IDs in its Model API.
* <p>
* Replaced with pseudo-random UUIDs to avoid including secure random number generation in the native image.
* This should be good enough for our purposes, because this is only used in init code for vocabularies
* and not for user data.
*/
@TargetClass(org.apache.jena.graph.BlankNodeId.class)
final class BlankNodeIdSubstitute {
@Substitute
public List<Provider> providers() {
return List.of();
public static String createFreshId() {
ThreadLocalRandom r = ThreadLocalRandom.current();
return new UUID(r.nextLong(), r.nextLong()).toString();
}
}

Expand Down