2012-05-19 3 views
8

In Java 6 konnte ich JNI in Scala ganz gut verwenden. Ich hätte Code wie:Fehler mit Java beim Schreiben von JNI in Scala mit Java 7

package mypackage 
object MyClass { 
    System.loadLibrary("myclass-native") 
    @native def foo(): Int = sys.error("") 
} 

Und dann würde ich laufen:

javah -classpath target/scala-2.9.1/classes -d target/jni mypackage.MyClass$ 

Und ich würde meine Header-Dateien ganz gut bekommen.

in Java 7, erhalte ich folgende Fehlermeldung:

Exception in thread "main" java.lang.IllegalArgumentException: Not a valid class name: mypackage.MyClass. 
at com.sun.tools.javac.api.JavacTool.getTask(JavacTool.java:177) 
at com.sun.tools.javac.api.JavacTool.getTask(JavacTool.java:68) 
at com.sun.tools.javah.JavahTask.run(JavahTask.java:509) 
at com.sun.tools.javah.JavahTask.run(JavahTask.java:335) 
at com.sun.tools.javah.Main.main(Main.java:46) 

Es ist wie javah ist nicht mehr akzeptiert Dollarzeichen in Klassennamen, aber ich brauche das Dollarzeichen in Scala verwenden, um das Äquivalent von einer statischen zu erhalten Methode.

als Referenz mit Java 6:

$ java -version 
java version "1.6.0_29" 
Java(TM) SE Runtime Environment (build 1.6.0_29-b11) 
Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02, mixed mode) 
$ javah -version 
javah version "1.6.0_29" 

mit Java 7:

$ java -version 
java version "1.7.0_03" 
OpenJDK Runtime Environment (IcedTea7 2.1.1pre) (7~u3-2.1.1~pre1-1ubuntu2) 
OpenJDK 64-Bit Server VM (build 22.0-b10, mixed mode) 
$ javah -version 
javah version "1.7.0_03" 

Hat jemand etwas Glück für JNI mit Scala in Java 7 mit javah hatte?

bearbeiten

als Bug veröffentlicht bei http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7185778

Antwort

5

Der beste Weg, ein gewisses Verständnis von dem, was direkt geschieht, ist durch OpenJDK Website zu den Quellen zu gehen. Wenn wir com.sun.tools.javac.api.JavacTool

aussehen
public JavacTask getTask(Writer out, 
         JavaFileManager fileManager, 
         DiagnosticListener<? super JavaFileObject> diagnosticListener, 
         Iterable<String> options, 
         Iterable<String> classes, 
         Iterable<? extends JavaFileObject> compilationUnits) 
{ 
    try { 
     Context context = new Context(); 
     ClientCodeWrapper ccw = ClientCodeWrapper.instance(context); 

    final String kindMsg = "All compilation units must be of SOURCE kind"; 
    if (options != null) 
     for (String option : options) 
      option.getClass(); // null check 
    if (classes != null) { 
     for (String cls : classes) 
      if (!SourceVersion.isName(cls)) // implicit null check 
       throw new IllegalArgumentException("Not a valid class name: " + cls); 
    } 
    if (compilationUnits != null) { 
     compilationUnits = ccw.wrapJavaFileObjects(compilationUnits); // implicit null check 
     for (JavaFileObject cu : compilationUnits) { 
      if (cu.getKind() != JavaFileObject.Kind.SOURCE) 
       throw new IllegalArgumentException(kindMsg); 
     } 
    } 

    if (diagnosticListener != null) 
     context.put(DiagnosticListener.class, ccw.wrap(diagnosticListener)); 

    if (out == null) 
     context.put(Log.outKey, new PrintWriter(System.err, true)); 
    else 
     context.put(Log.outKey, new PrintWriter(out, true)); 

    if (fileManager == null) 
     fileManager = getStandardFileManager(diagnosticListener, null, null); 
    fileManager = ccw.wrap(fileManager); 
    context.put(JavaFileManager.class, fileManager); 
    processOptions(context, fileManager, options); 
    Main compiler = new Main("javacTask", context.get(Log.outKey)); 
    return new JavacTaskImpl(compiler, options, context, classes, compilationUnits); 
} catch (ClientCodeException ex) { 
    throw new RuntimeException(ex.getCause()); 
} 

}

können Sie die Codezeile sehen:

if (!SourceVersion.isName(cls)) // implicit null check 
         throw new IllegalArgumentException("Not a valid class name: " + cls); 

jetzt Werfen wir also einen Blick auf javax.lang.model.SourceVersion haben

/** 
    * Returns whether or not {@code name} is a syntactically valid 
    * qualified name in the latest source version. Unlike {@link 
    * #isIdentifier isIdentifier}, this method returns {@code false} 
    * for keywords and literals. 
    * 
    * @param name the string to check 
    * @return {@code true} if this string is a 
    * syntactically valid name, {@code false} otherwise. 
    * @jls 6.2 Names and Identifiers 
    */ 
    public static boolean isName(CharSequence name) { 
     String id = name.toString(); 

     for(String s : id.split("\\.", -1)) { 
      if (!isIdentifier(s) || isKeyword(s)) 
       return false; 
     } 
     return true; 
    } 

Sie können also sehen, dass die Methode, von der wir erwarteten, dass sie wahr ist (aber stattdessen Fals zurückgibt) e):

public static boolean isIdentifier(CharSequence name) { 
     String id = name.toString(); 

     if (id.length() == 0) { 
      return false; 
     } 
     int cp = id.codePointAt(0); 
     if (!Character.isJavaIdentifierStart(cp)) { 
      return false; 
     } 
     for (int i = Character.charCount(cp); 
       i < id.length(); 
       i += Character.charCount(cp)) { 
      cp = id.codePointAt(i); 
      if (!Character.isJavaIdentifierPart(cp)) { 
       return false; 
      } 
     } 
     return true; 
    } 

Und das Problem ist !Character.isJavaIdentifierPart(cp)

Wenn wir nun auf die Version 1.6 aussehen:

public static boolean isJavaIdentifierPart(int codePoint) { 
     boolean bJavaPart = false; 

     if (codePoint >= MIN_CODE_POINT && codePoint <= FAST_PATH_MAX) { 
      bJavaPart = CharacterDataLatin1.isJavaIdentifierPart(codePoint); 
     } else { 
      int plane = getPlane(codePoint); 
      switch(plane) { 
      case(0): 
       bJavaPart = CharacterData00.isJavaIdentifierPart(codePoint); 
       break; 
      case(1): 
       bJavaPart = CharacterData01.isJavaIdentifierPart(codePoint); 
       break; 
      case(2): 
       bJavaPart = CharacterData02.isJavaIdentifierPart(codePoint); 
       break; 
      case(3): // Undefined 
      case(4): // Undefined 
      case(5): // Undefined 
      case(6): // Undefined 
      case(7): // Undefined 
      case(8): // Undefined 
      case(9): // Undefined 
      case(10): // Undefined 
      case(11): // Undefined 
      case(12): // Undefined 
      case(13): // Undefined 
       bJavaPart = CharacterDataUndefined.isJavaIdentifierPart(codePoint); 
       break; 
      case(14): 
       bJavaPart = CharacterData0E.isJavaIdentifierPart(codePoint); 
       break; 
      case(15): // Private Use 
      case(16): // Private Use 
       bJavaPart = CharacterDataPrivateUse.isJavaIdentifierPart(codePoint); 
       break; 
      default: 
       // the argument's plane is invalid, and thus is an invalid codepoint 
       // bJavaPart remains false; 
       break; 
      } 
     } 
     return bJavaPart; 
    } 

und die 1.7-Version:

public static boolean isJavaIdentifierPart(int codePoint) { 
     return CharacterData.of(codePoint).isJavaIdentifierPart(codePoint); 
    } 

Einige Refactoring haben hier aufgetreten ist, und wenn man sich auf Character von Ihnen feststellen, dass es einige Klassen gibt, die im laufenden Betrieb von Vorlagen in /openjdk/make/tools/GenerateCharacter/CharacterData**.java.template erzeugt werden, wenn Java-Distribution Aufbau:

// Character <= 0xff (basic latin) is handled by internal fast-path 
    // to avoid initializing large tables. 
    // Note: performance of this "fast-path" code may be sub-optimal 
    // in negative cases for some accessors due to complicated ranges. 
    // Should revisit after optimization of table initialization. 

static final CharacterData of(int ch) { 
    if (ch >>> 8 == 0) {  // fast-path 
     return CharacterDataLatin1.instance; 
    } else { 
     switch(ch >>> 16) { //plane 00-16 
     case(0): 
      return CharacterData00.instance; 
     case(1): 
      return CharacterData01.instance; 
     case(2): 
      return CharacterData02.instance; 
     case(14): 
      return CharacterData0E.instance; 
     case(15): // Private Use 
     case(16): // Private Use 
      return CharacterDataPrivateUse.instance; 
     default: 
      return CharacterDataUndefined.instance; 
     } 
    } 
} 

Ich denke, Sie könnten versuchen, Java im Debug-Modus laufen und sehen, was in den beiden Fällen passiert, dann senden Sie einen genauen Fehlerbericht an die OpenJDK Jungs, weil der Bug eindeutig durch dieses Refactoring eingeführt wurde.

+1

Danke, ich habe gerade einen Fehler an OpenJDK gemeldet unter https://bugs.openjdk.java.net/show_bug.cgi?id=100267 – Mike

+0

Erneut an bugreport.sun.com gesendet. Wird zurückschreiben, wenn sie antworten. – Mike