tags:

views:

160

answers:

3

Title says it all.

+2  A: 

Most probably you could parse the .class file and read the version from an attribute attached from the scala compiler onto the class file.

To learn more about the existance of such an attribute you might start at the sources of the scala compiler ( http://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala ).

To learn how to parse a .class file you might read in the spec ( http://jcp.org/aboutJava/communityprocess/final/jsr202/index.html ).

The example code I posted here ( http://stackoverflow.com/questions/1832241/java-illegal-class-modifiers-exception-code-0x209/1834906#1834906 ) might help at the implementation, too .

Arne
+9  A: 

I suppose the information is stored in the "pickled" part of the .class file, according to the 2008 "Reflecting Scala" rapport, from Yohann Coppel, under the supervision of Prof. Martin Odersky.

During the compilation process (represented on fig. 2), the Scala compiler generates two types of data.

  • The first one is some classic Java bytecode, which can be read and executed by a standard Java virtual machine.
  • The second one is what is called “Pickled data”, and represents the basic structure of the original source file.
    This information is enclosed in a .class file.
    The Java bytecode specification allows the compiler to “define and emit class files containing new attributes in the attributes tables of class file structures”. These attributes are silently ignored by JVMs if they do not recognize them.

alt text

The Scala compiler generates pickled data for about any data structure in a Scala program, called symbols in the pickler context.
Symbols are stored linearly with the format shown on Fig. 3.

alt text

  • The tag represents the type of data stored,
  • then the length gives the length of the following data block.
  • The data block can contain multiple information, such as the name of a symbol.
ScalaSig = "ScalaSig" Version Symtab
Version = Major_Nat Minor_Nat         <====
Symtab = numberOfEntries_Nat {Entry}

The ScalaSig attribute definition.
A more complete definition can be found in the scala.tools.nsc.symtab.PickleFormat source file (now scala.reflect.generic.PickleFormat).

You can also see how to read the Pickled data in scala.tools.nsc.util.ShowPickled.


This page shows a script (not tested) which will display the pickled data:

#!/bin/sh
#
# Shows the pickled scala data in a classfile.

if [ $# == 0 ] ; then
  echo "Usage: $0 [--bare] [-cp classpath] <class*>"
  exit 1
fi

TOOLSDIR=`dirname $0`
CPOF="$TOOLSDIR/cpof"

PACK="$TOOLSDIR/../build/pack/lib"
QUICK="$TOOLSDIR/../build/quick/classes"
STARR="$TOOLSDIR/../lib"
CP=""

if [ -f "${PACK}/scala-library.jar" ] ; then
  CP=`${TOOLSDIR}/packcp`
elif [ -d "${QUICK}/library" ] ; then
  CP=`${TOOLSDIR}/quickcp`
else
  CP=`${TOOLSDIR}/starrcp`
fi

if [ "$1" == "-cp" ] ; then
  shift
  CP="${1}:${CP}"
  shift
fi

java -cp "$CP" scala.tools.nsc.util.ShowPickled $*
VonC
+2  A: 

You can see the Scala Major/Minor version in the class file if you use javap with the verbose option. For example, the following is shown for a file compiled using scala 2.8.0 final:


javap -private -verbose T

Compiled from "SomeTest.scala"
public interface T
  SourceFile: "SomeTest.scala"
  ScalaSig: length = 0x3
   05 00 00 
  RuntimeVisibleAnnotations: length = 0xB
   00 01 00 06 00 01 00 07 73 00 08 
  minor version: 0
  major version: 49
  Constant pool:
const #1 = Asciz    SourceFile;
const #2 = Asciz    SomeTest.scala;
const #3 = Asciz    s;
const #4 = Asciz    ()Ljava/lang/String;;
const #5 = Asciz    ScalaSig;

//etc etc...

while the following is the output of a file compiled using scala 2.7.7:


javap -verbose T2
Compiled from "SomeTest2.scala"
public interface T2
  SourceFile: "SomeTest2.scala"
  ScalaSig: length = 0x87
   04 01 1B 06 08 01 02 FFFFFF84 FFFFFF90 FFFFFF80 FFFFFF91 00 05 02 02 54
   32 0A 01 03 01 07 3C 65 6D 70 74 79 3E 03 00 13
   02 00 06 10 02 07 0C 0D 01 08 0A 02 09 0A 01 04
   6C 61 6E 67 0A 01 0B 01 04 6A 61 76 61 09 02 0D
   08 02 06 4F 62 6A 65 63 74 08 05 0F 00 FFFFFF86 00 10
   01 01 73 15 01 11 10 02 12 18 0E 02 13 16 0D 01
   14 0A 01 15 01 05 73 63 61 6C 61 09 02 17 14 01
   06 50 72 65 64 65 66 09 02 19 1A 02 06 53 74 72
   69 6E 67 0A 02 17 14 
  minor version: 0
  major version: 49
  Constant pool:
const #1 = Asciz    SourceFile;
const #2 = Asciz    SomeTest2.scala;

//etc etc...

The first two bytes of the ScalaSig constant entry should represent the scala Major/Minor version, I believe, which are defined in PickleFormat. The 2.7.7 version of PickleFormat can be found here, and shows that the major/minor version differs from the 2.8.0 version.

I checked the 2.7.1 version of this class as well, but here the Major/Minor version is the same as the 2.7.7 one, so you may not be able to distinguish between minor scala versions by using this method.

Arjan Blokzijl
Interesting. +1. Did you try the `ShowPickled` util class I mention in my answer?
VonC
@VonC: I quickly tried it from the command line, but it only printed this:Version -1345413376.0I must have done something wrong here, but haven't figured out what it is so far.
Arjan Blokzijl