Class RuntimeClassExtractor

java.lang.Object
org.ek9lang.core.RuntimeClassExtractor

public final class RuntimeClassExtractor extends Object
Extracts EK9 runtime classes (org.ek9.lang and ek9 packages) from the running JVM and packages them into a versioned JAR file with SHA-256 checksum.

This enables per-project runtime caching, keyed on the compiler version AND a content hash of the runtime classes (runtimeContentHash()): - First build: Extract EK9 runtime + dependency classes from compiler JVM → create JAR + checksum - Subsequent builds: Reuse cached JAR if checksum valid - A different compiler (another version, or a development build that kept its version but changed its runtime) gets its own JAR, and the superseded entries are removed - the cache holds one.

The version alone was the key until 2026-10-05 (Steve decision E): a development build with an unchanged version then packaged the STALE cached runtime into every program jar, which surfaced as NoClassDefFoundError: org/ek9/lang/UncaughtFailure until .ek9/ was deleted.

Extracts all EK9 runtime classes plus third-party dependencies (Jackson, JSONPath, SLF4J) including inner and anonymous classes required for runtime functionality. Uses direct JAR scanning with standard Java APIs (JarFile, ClassLoader).

  • Field Details

    • NOTICES_RESOURCE

      static final String NOTICES_RESOURCE
      THE notices of the third-party code in every compiled program (the DEPENDENCY_PACKAGES), written into the runtime jar - and so into each program jar - at NOTICES_ENTRY. Their licences (Apache-2.0, MIT) ask that the notice travel with every copy, and whoever ships an EK9 program ships these libraries. A dependency package added above needs its entry there (RuntimeClassExtractorTest checks), and in THIRD_PARTY_NOTICES.md.
      See Also:
    • NOTICES_ENTRY

      public static final String NOTICES_ENTRY
      Where the notices go in the runtime jar, and so in every program jar.
      See Also:
  • Constructor Details

    • RuntimeClassExtractor

      public RuntimeClassExtractor()
  • Method Details

    • runtimeContentHash

      public static String runtimeContentHash()
      THE content hash of the runtime this JVM would package: computed ONCE per process (lazily, on first use) over every class that extractRuntimeJar(FileHandling, String, String) could take - names plus, for a jar, the CRC-32 and size its central directory already records (no class is loaded or inflated), and for an exploded class directory the bytes themselves. It is what keys the per-project cache.
      Returns:
      16 lower-case hex characters
    • contentHashOf

      static String contentHashOf(ClassLoader classLoader, List<String> packages)
      The content hash of the classes under packages as classLoader would serve them: where a class is on several roots, the first root (the one the loader resolves) counts.
    • notices

      static byte[] notices()
      The notices text, as bytes.
    • extractRuntimeJar

      public Optional<File> extractRuntimeJar(FileHandling fileHandling, String projectDir, String version)
      Extract EK9 runtime classes into a JAR file keyed on the version and runtimeContentHash(). Creates JAR at: <projectDir>/.ek9/runtime/ek9-runtime-<version>-<contentHash>.jar Also creates corresponding .sha256 checksum file, and removes every superseded cache entry.
      Parameters:
      fileHandling - FileHandling instance for JAR creation and checksums
      projectDir - Project directory (source file directory)
      version - Compiler version (e.g., "0.0.1-0")
      Returns:
      Optional containing the runtime JAR file if successful
    • extractRuntimeJar

      Optional<File> extractRuntimeJar(FileHandling fileHandling, String projectDir, String version, String contentHash)
      As extractRuntimeJar(FileHandling, String, String) but with the content hash given, so a test can stand in for a changed runtime.