Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8244512: jextract throws NPE for a nested struct declaration #146

Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -110,13 +110,12 @@ public void addStaticFunctionWrapper(String javaName, String nativeName, MethodT
decrAlign();
}

public void emitPrimitiveTypedef(Type.Primitive primType, String name) {
public void emitPrimitiveTypedef(Type.Primitive primType, String className) {
Type.Primitive.Kind kind = primType.kind();
if (primitiveKindSupported(kind)) {
incrAlign();
indent();
sb.append(PUB_MODS);
String className = "C" + name;
sb.append("class ");
sb.append(className);
sb.append(" extends ");
@@ -70,10 +70,19 @@
private final String pkgName;
private StructBuilder structBuilder;
private List<String> structSources = new ArrayList<>();
private Set<String> structClassNames = new HashSet<>();
private int structClassNameCount = 0;
private String uniqueStructClassName(String name) {
return structClassNames.add(name.toLowerCase())? name : (name + "$" + structClassNameCount++);
private Set<String> nestedClassNames = new HashSet<>();
private int nestedClassNameCount = 0;
/*
* We may have case-insensitive name collision! A C program may have
* defined structs/unions/typedefs with the names FooS, fooS, FoOs, fOOs.
* Because we map structs/unions/typedefs to nested classes of header classes,
* such a case-insensitive name collision is problematic. This is because in
* a case-insensitive file system javac will overwrite classes for
* Header$CFooS, Header$CfooS, Header$CFoOs and so on! We solve this by
* generating unique case-insensitive names for nested classes.
*/
private String uniqueNestedClassName(String name) {
return nestedClassNames.add(name.toLowerCase())? name : (name + "$" + nestedClassNameCount++);
}

// have we seen this Variable earlier?
@@ -233,22 +242,14 @@ public Void visitScoped(Declaration.Scoped d, Declaration parent) {
}

boolean structClass = false;
StructBuilder oldStructBuilder = this.structBuilder;
if (!d.name().isEmpty() || !isRecord(parent)) {
//only add explicit struct layout if the struct is not to be flattened inside another struct
switch (d.kind()) {
case STRUCT:
case UNION: {
structClass = true;
/*
* We may have case-insensitive name collision! A C program may have
* defined structs with the names FooS, fooS, FoOs, fOOs. Because we
* map structs and unions to nested classes of header classes, such
* a case-insensitive name collision is problematic. This is because in
* a case-insensitive file system javac will overwrite classes for
* Header$CFooS, Header$CfooS, Header$CFoOs and so on! We solve this by
* generating unique case-insensitive names for classes.
*/
String structClassName = uniqueStructClassName("C" + name);
String structClassName = uniqueNestedClassName("C" + name);
this.structBuilder = new StructBuilder(structClassName, pkgName, constantHelper);
structBuilder.incrAlign();
structBuilder.classBegin();
@@ -261,7 +262,7 @@ public Void visitScoped(Declaration.Scoped d, Declaration parent) {
if (structClass) {
this.structBuilder.classEnd();
structSources.add(structBuilder.getSource());
this.structBuilder = null;
this.structBuilder = oldStructBuilder;
}
return null;
}
@@ -328,7 +329,7 @@ public Void visitTypedef(Declaration.Typedef tree, Declaration parent) {
return visitScoped(s, tree);
}
} else if (type instanceof Type.Primitive) {
builder.emitPrimitiveTypedef((Type.Primitive)type, tree.name());
builder.emitPrimitiveTypedef((Type.Primitive)type, uniqueNestedClassName("C" + tree.name()));
}
return null;
}
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

import java.nio.file.Path;
import org.testng.annotations.Test;
import static org.testng.Assert.assertNotNull;

/*
* @test
* @modules jdk.incubator.jextract
* @library /test/lib
* @build JextractToolRunner
* @bug 8244512
* @summary jextract throws NPE for a nested struct declaration
* @run testng/othervm -Dforeign.restricted=permit Test8244512
*/
public class Test8244512 extends JextractToolRunner {
@Test
public void testNestedStructs() {
Path nestedOutput = getOutputFilePath("nestedgen");
Path nestedH = getInputFilePath("nested.h");
run("-d", nestedOutput.toString(), nestedH.toString()).checkSuccess();
try(Loader loader = classLoader(nestedOutput)) {
checkClass(loader, "Foo");
checkClass(loader, "Bar");
checkClass(loader, "U");
checkClass(loader, "Point");
checkClass(loader, "MyStruct");
checkClass(loader, "MyStruct_Z");
checkClass(loader, "MyUnion");
checkClass(loader, "MyUnion_Z");
checkClass(loader, "X");
checkClass(loader, "X2");
} finally {
deleteDir(nestedOutput);
}
}

private static void checkClass(Loader loader, String name) {
assertNotNull(loader.loadClass("nested_h$C" + name));
}
}
@@ -0,0 +1,110 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

struct Foo {
struct Bar {
int x, y;
} bar;

enum Color {
red, green, blue
} color;
};

union U {
struct Point {
short x, y;
} point;

enum RGB {
r, g, b
} rgb;

int i;
};

struct MyStruct {
char a;
struct {
int b;
union {
int c;
};
char d;
struct MyStruct_Z {
char e;
} f;
};
union {
int g;
long h;
};
enum {
X, Y, Z
};
struct {
int i;
int j;
} k;
};

union MyUnion {
char a;
struct {
int b;
union {
int c;
};
char d;
struct MyUnion_Z {
char e;
} f;
};
struct {
int g;
int h;
};
enum {
A, B, C
};
union {
int i;
long j;
} k;
};

struct X {
struct {
union {
int y;
} Z;
};
};

struct X2 {
struct {
union {
int y;
}; // no name this time
};
};
@@ -22,3 +22,4 @@
*/

typedef long mysize_t;
typedef long MYSIZE_T;
@@ -23,3 +23,4 @@

typedef char byte_t;
typedef long size_t;
typedef long SIZE_T;