--- old/src/jdk.internal.clang/share/classes/jdk/internal/clang/Cursor.java 2018-04-02 20:55:30.000000000 -0700 +++ new/src/jdk.internal.clang/share/classes/jdk/internal/clang/Cursor.java 2018-04-02 20:55:30.000000000 -0700 @@ -59,6 +59,20 @@ public native boolean isPreprocessing(); public native boolean isInvalid(); public native boolean isDefinition(); + public native boolean isAnonymousStruct(); + + public boolean isAnonymousEnum() { + // libclang::clang_Cursor_isAnonymous only applies to struct, not enum + if (type().kind() == TypeKind.Enum) { + return spelling().isEmpty() && + type().spelling().startsWith("enum (anonymous"); + } + return false; + } + + public boolean isAnonymous() { + return isAnonymousStruct() || isAnonymousEnum(); + } public native String spelling(); public native String USR(); --- old/src/jdk.internal.clang/share/native/libjclang/jdk_internal_clang.cpp 2018-04-02 20:55:33.000000000 -0700 +++ new/src/jdk.internal.clang/share/native/libjclang/jdk_internal_clang.cpp 2018-04-02 20:55:32.000000000 -0700 @@ -338,6 +338,12 @@ return clang_isCursorDefinition(*ptr); } +JNIEXPORT jboolean JNICALL Java_jdk_internal_clang_Cursor_isAnonymousStruct + (JNIEnv *env, jobject cursor) { + CXCursor *ptr = (CXCursor*) J2P(env, cursor); + return clang_Cursor_isAnonymous(*ptr); +} + JNIEXPORT jstring JNICALL Java_jdk_internal_clang_Cursor_spelling (JNIEnv *env, jobject cursor) { CXCursor *ptr = (CXCursor*) J2P(env, cursor); --- old/src/jdk.jextract/share/classes/com/sun/tools/jextract/AsmCodeFactory.java 2018-04-02 20:55:36.000000000 -0700 +++ new/src/jdk.jextract/share/classes/com/sun/tools/jextract/AsmCodeFactory.java 2018-04-02 20:55:35.000000000 -0700 @@ -111,7 +111,9 @@ throws IOException { cw.visitEnd(); byte[] bytecodes = cw.toByteArray(); - types.put(clsName, bytecodes); + if (null != types.put(clsName, bytecodes)) { + logger.warning("Class " + clsName + " definition is overwritten"); + } } /** @@ -234,6 +236,15 @@ } private void createEnum(Cursor cursor) { + // Anonymous enum without proper name, define in global_cw + if (cursor.isAnonymousEnum()) { + logger.fine("create anonymous enum"); + cursor.stream() + .filter(cx -> cx.kind() == CursorKind.EnumConstantDecl) + .forEachOrdered(cx -> addConstant(global_cw, cx)); + return; + } + String nativeName = Utils.getIdentifier(cursor); logger.fine(() -> "create enum: " + nativeName); --- old/src/jdk.jextract/share/classes/com/sun/tools/jextract/HeaderFile.java 2018-04-02 20:55:38.000000000 -0700 +++ new/src/jdk.jextract/share/classes/com/sun/tools/jextract/HeaderFile.java 2018-04-02 20:55:38.000000000 -0700 @@ -112,11 +112,23 @@ ctx.err.println(Main.format("warn.symbol.not.found", name)); } } + JType jt = dict.computeIfAbsent(t, type -> { logger.fine(() -> "PH: Compute type for " + type.spelling()); return define(type); }); assert (jt instanceof JType2); + + if (t.kind() == TypeKind.Typedef) { + // Enum is already an TypeAlias for int with proper name + // In case of typedef has a different name with enum, do we care? + // Typedef is a type annotation, has no real use case + if (t.canonicalType().kind() == TypeKind.Enum) { + logger.finest("Skip enum typedef for " + t.spelling()); + return; + } + } + // Only main file can define interface if (cf != null && this.main == main) { cf.addType(jt, c); --- old/test/jdk/com/sun/tools/jextract/JextractToolProviderTest.java 2018-04-02 20:55:41.000000000 -0700 +++ new/test/jdk/com/sun/tools/jextract/JextractToolProviderTest.java 2018-04-02 20:55:40.000000000 -0700 @@ -34,6 +34,7 @@ import java.nio.file.Paths; import java.nio.file.Files; import java.util.Arrays; +import java.util.Map; import java.util.Optional; import java.util.spi.ToolProvider; import org.testng.annotations.Test; @@ -376,26 +377,63 @@ } } + private Class findClass(Class[] clz, String name) { + for (Class cls: clz) { + if (cls.getSimpleName().equals(name)) { + return cls; + } + } + return null; + } + + private void testEnumValue(Class enumCls, Map values) { + values.entrySet().stream(). + forEach(e -> checkIntField(enumCls, e.getKey(), e.getValue())); + } + @Test public void testAnonymousEnum() { Path anonenumJar = getOutputFilePath("anonenum.jar"); deleteFile(anonenumJar); - Path anonenumH = getInputFilePath("anonenum.h"); + Path anonenumH = getInputFilePath("anonenum.h"); try { checkSuccess(null, "-o", anonenumJar.toString(), anonenumH.toString()); Class anonenumCls = loadClass("anonenum", anonenumJar); assertNotNull(anonenumCls); - // the nested type for anonymous enum has name starting with "enum__anonymous_at" - // followed by full path name of header file + line + column numbers. Any non-ident - // char replaced by "_". But we test only the start pattern here. - Optional> optEnumCls = Arrays.stream(anonenumCls.getClasses()). - filter(c -> c.getSimpleName().startsWith("enum__anonymous_at")). - findFirst(); - assertTrue(optEnumCls.isPresent()); - Class enumCls = optEnumCls.get(); - checkIntField(enumCls, "RED", 0xff0000); - checkIntField(enumCls, "GREEN", 0x00ff00); - checkIntField(enumCls, "BLUE", 0x0000ff); + checkIntField(anonenumCls, "RED", 0xff0000); + checkIntField(anonenumCls, "GREEN", 0x00ff00); + checkIntField(anonenumCls, "BLUE", 0x0000ff); + + Class enumClz[] = anonenumCls.getClasses(); + assert(enumClz.length >= 3); + + Class enumCls = findClass(enumClz, "codetype_t"); + assertNotNull(enumCls); + testEnumValue(enumCls, Map.of( + "Java", 0, + "C", 1, + "CPP", 2, + "Python", 3, + "Ruby", 4)); + + enumCls = findClass(enumClz, "SIZE"); + assertNotNull(enumCls); + testEnumValue(enumCls, Map.of( + "XS", 0, + "S", 1, + "M", 2, + "L", 3, + "XL", 4, + "XXL", 5)); + + enumCls = findClass(enumClz, "temp"); + assertNotNull(enumCls); + testEnumValue(enumCls, Map.of( + "ONE", 1, + "TWO", 2)); + + enumCls = findClass(enumClz, "temp_t"); + assertNull(enumCls); } finally { deleteFile(anonenumJar); } --- old/test/jdk/com/sun/tools/jextract/anonenum.h 2018-04-02 20:55:43.000000000 -0700 +++ new/test/jdk/com/sun/tools/jextract/anonenum.h 2018-04-02 20:55:43.000000000 -0700 @@ -26,3 +26,25 @@ GREEN = 0x00ff00, BLUE = 0x0000ff }; + +typedef enum { + Java, + C, + CPP, + Python, + Ruby +} codetype_t; + +enum SIZE { + XS, + S, + M, + L, + XL, + XXL +}; + +typedef enum temp { + ONE = 1, + TWO +} temp_t; --- old/test/jdk/com/sun/tools/jextract/jclang-ffi/src/jdk/internal/clang/Cursor.java 2018-04-02 20:55:45.000000000 -0700 +++ new/test/jdk/com/sun/tools/jextract/jclang-ffi/src/jdk/internal/clang/Cursor.java 2018-04-02 20:55:45.000000000 -0700 @@ -57,6 +57,21 @@ return LibClang.lib.clang_isCursorDefinition(cursor) != 0; } + public boolean isAnonymousStruct() { return LibClang.lib.clang_Cursor_isAnonymous(cursor) != 0; } + + public boolean isAnonymousEnum() { + // libclang::clang_Cursor_isAnonymous only applies to struct, not enum + if (type().kind() == TypeKind.Enum) { + return spelling().isEmpty() && + type().spelling().startsWith("enum (anonymous"); + } + return false; + } + + public boolean isAnonymous() { + return isAnonymousStruct() || isAnonymousEnum(); + } + public String spelling() { return LibClang.CXStrToString( LibClang.lib.clang_getCursorSpelling(cursor));