Skip to content

scala_legacy crash caused by any jar in classpath with empty Class-Path: attribute #22461

Closed
@philwalk

Description

@philwalk

Compiler version

# scala -version
Scala code runner version: 1.5.4
Scala version (default): 3.6.3

Minimized code

#!/usr/bin/env -S scala_legacy -classpath ./pallet_3.jar

object LegacyBug {
  def main(args: Array[String]): Unit = {
    printf("hello legacy!\n")
  }
}

Can be reproduced by a jar with empty Class-Path: attribute.

If the jar has an empty Class-Path: attribute in the MANIFEST, it crashes.
If it has no Class-Path: attribute, or a non-empty Class-Path: attribute, no problem.

Output

[warning] MainGenericRunner class is deprecated since Scala 3.5.0, and Scala CLI features will not work.
[warning] Please be sure to update to the Scala CLI launcher to use the new features.
[warning] Check the Scala 3.5.0 release notes to troubleshoot your installation.

  Exception while compiling C:\opt\ue\jsrc\legacyBug.sc

  An unhandled exception was thrown in the compiler.
  Please file a crash report here:
  https://github.com/scala/scala3/issues/new/choose
  For non-enriched exceptions, compile with -Xno-enrich-error-messages.


     while compiling: <no file>
        during phase: <some phase>
                mode: Mode()
     library version: version 2.13.15
    compiler version: version 3.6.3
            settings: -classpath C:/opt/scala3/lib/scala.jar;C:/opt/scala3/lib/with_compiler.jar;emptyClassPath.jar -d C:\tmp\scala3-scripting13458733512999800816

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "dotty.tools.io.AbstractFile.ext()" because "file" is null
	at dotty.tools.dotc.classpath.FileUtils$.isJarOrZip(FileUtils.scala:37)
	at dotty.tools.dotc.classpath.ClassPathFactory$.newClassPath(ClassPathFactory.scala:94)
	at dotty.tools.dotc.classpath.ClassPathFactory.newClassPath(ClassPathFactory.scala:20)
	at dotty.tools.dotc.classpath.ClassPathFactory.$anonfun$2$$anonfun$3(ClassPathFactory.scala:73)
	at scala.collection.Iterator$$anon$9.next(Iterator.scala:584)
	at scala.collection.immutable.List.prependedAll(List.scala:153)
	at scala.collection.immutable.List$.from(List.scala:685)
	at scala.collection.immutable.List$.from(List.scala:682)
	at scala.collection.IterableOps$WithFilter.map(Iterable.scala:900)
	at dotty.tools.dotc.classpath.ClassPathFactory.$anonfun$2(ClassPathFactory.scala:69)
	at scala.collection.immutable.List.flatMap(List.scala:294)
	at dotty.tools.dotc.classpath.ClassPathFactory.classesInPathImpl(ClassPathFactory.scala:68)
	at dotty.tools.dotc.classpath.ClassPathFactory.classesInExpandedPath(ClassPathFactory.scala:46)
	at dotty.tools.dotc.config.PathResolver$Calculated$.basis(PathResolver.scala:232)
	at dotty.tools.dotc.config.PathResolver$Calculated$.containers$lzyINIT1(PathResolver.scala:236)
	at dotty.tools.dotc.config.PathResolver$Calculated$.containers(PathResolver.scala:236)
	at dotty.tools.dotc.config.PathResolver.containers(PathResolver.scala:258)
	at dotty.tools.dotc.config.PathResolver.result$lzyINIT1(PathResolver.scala:261)
	at dotty.tools.dotc.config.PathResolver.result(PathResolver.scala:260)
	at dotty.tools.dotc.config.JavaPlatform.classPath(JavaPlatform.scala:18)
	at dotty.tools.dotc.config.JavaPlatform.rootLoader(JavaPlatform.scala:38)
	at dotty.tools.dotc.core.Contexts$ContextBase.rootLoader(Contexts.scala:909)
	at dotty.tools.dotc.core.Definitions.RootClass$$anonfun$1(Definitions.scala:203)
	at dotty.tools.dotc.core.Symbols$.$anonfun$2(Symbols.scala:655)
	at dotty.tools.dotc.core.Symbols$.newClassSymbol(Symbols.scala:589)
	at dotty.tools.dotc.core.Symbols$.newModuleSymbol(Symbols.scala:655)
	at dotty.tools.dotc.core.Symbols$.newPackageSymbol(Symbols.scala:716)
	at dotty.tools.dotc.core.Definitions.RootClass(Definitions.scala:203)
	at dotty.tools.dotc.core.Denotations$.recurSimple$1(Denotations.scala:1352)
	at dotty.tools.dotc.core.Denotations$.recur$1(Denotations.scala:1356)
	at dotty.tools.dotc.core.Denotations$.staticRef(Denotations.scala:1360)
	at dotty.tools.dotc.core.Symbols$.requiredPackage(Symbols.scala:944)
	at dotty.tools.dotc.core.Definitions.ScalaPackageVal(Definitions.scala:215)
	at dotty.tools.dotc.core.Definitions.ScalaPackageClass(Definitions.scala:218)
	at dotty.tools.dotc.core.Definitions.AnyClass(Definitions.scala:281)
	at dotty.tools.dotc.core.Definitions.syntheticScalaClasses(Definitions.scala:2184)
	at dotty.tools.dotc.core.Definitions.syntheticCoreClasses(Definitions.scala:2199)
	at dotty.tools.dotc.core.Definitions.init(Definitions.scala:2215)
	at dotty.tools.dotc.core.Contexts$ContextBase.initialize(Contexts.scala:922)
	at dotty.tools.dotc.core.Contexts$Context.initialize(Contexts.scala:544)
	at dotty.tools.dotc.Run.rootContext(Run.scala:502)
	at dotty.tools.dotc.Run.<init>(Run.scala:523)
	at dotty.tools.dotc.Compiler.newRun(Compiler.scala:178)
	at dotty.tools.dotc.Driver.doCompile(Driver.scala:35)
	at dotty.tools.scripting.ScriptingDriver.compileAndRun(ScriptingDriver.scala:22)
	at dotty.tools.scripting.Main$.process(Main.scala:39)
	at dotty.tools.MainGenericRunner$.run$1(MainGenericRunner.scala:250)
	at dotty.tools.MainGenericRunner$.process(MainGenericRunner.scala:286)
	at dotty.tools.MainGenericRunner$.main(MainGenericRunner.scala:297)
	at dotty.tools.MainGenericRunner.main(MainGenericRunner.scala)

Expectation

Should not crash.
The following script verifies that scala3-3.3.1 has no problem with the jar file.

#!/opt/scala3-3.3.1/bin/scala -classpath ./emptyClassPath.jar
object LegacyBug {
  def main(args: Array[String]): Unit = {
    printf("hello legacy!\n")
  }
}

An empty string is a legal classpath entry, interpreted by the jvm as the current working directory, although it might have been created by accident. The following entry in build.sbt can produce a problem jar:

Compile / packageBin / packageOptions += Package.ManifestAttributes(java.util.jar.Attributes.Name.CLASS_PATH -> "")

Here's a script to create a problem jar named emptyClassPath.jar in the current directory:

#!/bin/bash

HERE=`pwd -P`
WORK_NAME=emptyClassPath
PROBLEM_JAR="$HERE"/$WORK_NAME.jar

WORK_DIR=/tmp/${WORK_NAME}$$
MANIFEST="$WORK_DIR"/META-INF/MANIFEST.MF
set -x
mkdir -p $WORK_DIR/META-INF

cd $WORK_DIR
touch main.class
cat > $MANIFEST <<- EOF
Manifest-Version: 1.0
Class-Path:  
Created-By: $WORK_NAME
EOF
cat $MANIFEST
/opt/jdk8/bin/jar -cmf $MANIFEST "$PROBLEM_JAR" .

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions