From d4c5a06e09465d494a6fab120c00244a0e1c8c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Guillemet?= Date: Fri, 17 Feb 2023 23:44:56 +0100 Subject: [PATCH 1/8] Fix output path when provided classes have same length --- src/main/java/org/bytedeco/javacpp/tools/Builder.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/bytedeco/javacpp/tools/Builder.java b/src/main/java/org/bytedeco/javacpp/tools/Builder.java index b8b9624f..39f6d230 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Builder.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Builder.java @@ -449,7 +449,7 @@ int compile(String[] sourceFilenames, String outputFilename, ClassProperties pro /** * Creates and returns the directory where output files should be placed. * Uses {@link #outputDirectory} as is when available, but falls back - * on the shortest common path to the classes as well as the platform + * on the longest common path to the classes as well as the platform * specific library path when available, or the platform name itself * and the user provided extension when not. * @@ -476,13 +476,14 @@ File getOutputPath(Class[] classes, String[] sourcePrefixes) throws IOException String resourceURL = Loader.findResource(classes[0], resourceName).toString(); String packageURI = resourceURL.substring(0, resourceURL.lastIndexOf('/') + 1); for (int i = 1; i < classes.length; i++) { - // Use shortest common package name among all classes as default output path + // Use the longest common package name among all classes as default output path String resourceName2 = '/' + classes[i].getName().replace('.', '/') + ".class"; String resourceURL2 = Loader.findResource(classes[i], resourceName2).toString(); String packageURI2 = resourceURL2.substring(0, resourceURL2.lastIndexOf('/') + 1); - String longest = packageURI2.length() > packageURI.length() ? packageURI2 : packageURI; - String shortest = packageURI2.length() < packageURI.length() ? packageURI2 : packageURI; + String longest, shortest; + if (packageURI2.length() > packageURI.length()) { longest = packageURI2; shortest = packageURI; } + else { longest = packageURI; shortest = packageURI2; } while (!longest.startsWith(shortest) && shortest.lastIndexOf('/') > 0) { shortest = shortest.substring(0, shortest.lastIndexOf('/')); } From 4a3ef932506192ed89747c06adc9f608062324cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Guillemet?= Date: Sat, 18 Feb 2023 14:44:21 +0100 Subject: [PATCH 2/8] Add copy of declarations between classes. --- .../org/bytedeco/javacpp/tools/Context.java | 1 + .../javacpp/tools/CopiedDeclarations.java | 23 +++ .../bytedeco/javacpp/tools/Declaration.java | 12 +- .../java/org/bytedeco/javacpp/tools/Info.java | 14 ++ .../org/bytedeco/javacpp/tools/InfoMap.java | 20 +++ .../org/bytedeco/javacpp/tools/Parser.java | 139 +++++++++++++++--- 6 files changed, 190 insertions(+), 19 deletions(-) create mode 100644 src/main/java/org/bytedeco/javacpp/tools/CopiedDeclarations.java diff --git a/src/main/java/org/bytedeco/javacpp/tools/Context.java b/src/main/java/org/bytedeco/javacpp/tools/Context.java index 2dd2e3cf..6f547fd4 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Context.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Context.java @@ -71,6 +71,7 @@ class Context { TemplateMap templateMap = null; List usingList = null; Map namespaceMap = null; + CopiedDeclarations copiedDeclarations; /** Return all likely combinations of namespaces and template arguments for this C++ type */ String[] qualify(String cppName) { diff --git a/src/main/java/org/bytedeco/javacpp/tools/CopiedDeclarations.java b/src/main/java/org/bytedeco/javacpp/tools/CopiedDeclarations.java new file mode 100644 index 00000000..e76aa8ae --- /dev/null +++ b/src/main/java/org/bytedeco/javacpp/tools/CopiedDeclarations.java @@ -0,0 +1,23 @@ +package org.bytedeco.javacpp.tools; + +import java.util.ArrayList; + +class CopiedDeclarations extends ArrayList { + void add(Declaration decl, String fullname, String modifiers, String namespace) { + add(new CopiedDeclaration(decl, fullname, modifiers, namespace)); + } + + static class CopiedDeclaration { + final Declaration decl; + final String fullname; + final String modifiers; + final String namespace; + + CopiedDeclaration(Declaration decl, String fullname, String modifiers, String namespace) { + this.decl = decl; + this.fullname = fullname; + this.modifiers = modifiers; + this.namespace = namespace; + } + } +} diff --git a/src/main/java/org/bytedeco/javacpp/tools/Declaration.java b/src/main/java/org/bytedeco/javacpp/tools/Declaration.java index 510ffe51..43617346 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Declaration.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Declaration.java @@ -26,7 +26,7 @@ * * @author Samuel Audet */ -class Declaration { +class Declaration implements Cloneable { Type type = null; Declarator declarator = null; boolean abstractMember = false, constMember = false, inaccessible = false, @@ -36,4 +36,14 @@ class Declaration { public String toString() { return text; } + + @Override + public Declaration clone() { + try { + // Shallow copy is enough for the current usage + return (Declaration) super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } } diff --git a/src/main/java/org/bytedeco/javacpp/tools/Info.java b/src/main/java/org/bytedeco/javacpp/tools/Info.java index 0606350a..0fe5cc8e 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Info.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Info.java @@ -27,6 +27,10 @@ import org.bytedeco.javacpp.annotation.Cast; import org.bytedeco.javacpp.annotation.Virtual; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * Holds information useful to the {@link Parser} and associated with C++ identifiers. * Info objects are meant to be added by the user to an {@link InfoMap} passed as @@ -65,6 +69,7 @@ public Info(Info i) { base = i.base; cppText = i.cppText; javaText = i.javaText; + copyFrom = i.copyFrom == null ? null : new ArrayList<>(i.copyFrom); } /** A list of C++ identifiers, expressions, or header filenames to which this info is to be bound. @@ -124,6 +129,14 @@ public Info(Info i) { String cppText = null; /** Outputs the given code, instead of the result parsed from the declaration of C++ identifiers. */ String javaText = null; + /** List of cpp names (class, struct, member function) the declarations of which we want to copy in this class or struct. */ + List copyFrom = null; + /** If not-null, declarations for these cpp names are copied. + * Set to non-null by InfoMap.normalizeCopy or for polymorphic classes. */ + CopiedDeclarations copiedDeclarations = null; + /** Set by Parser when a class has a virtual member function or inherit from a polymorphic class. */ + boolean polymorphic = false; + public Info cppNames(String... cppNames) { this.cppNames = cppNames; return this; } public Info javaNames(String... javaNames) { this.javaNames = javaNames; return this; } @@ -161,4 +174,5 @@ public Info(Info i) { public Info base(String base) { this.base = base; return this; } public Info cppText(String cppText) { this.cppText = cppText; return this; } public Info javaText(String javaText) { this.javaText = javaText; return this; } + public Info copyFrom(String... cppNames) { this.copyFrom = Arrays.asList(cppNames); return this; } } diff --git a/src/main/java/org/bytedeco/javacpp/tools/InfoMap.java b/src/main/java/org/bytedeco/javacpp/tools/InfoMap.java index 89b7b47f..a579afe4 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/InfoMap.java +++ b/src/main/java/org/bytedeco/javacpp/tools/InfoMap.java @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.HashSet; /** * A {@link Map} containing {@link Info} objects consumed by the {@link Parser}. @@ -343,4 +344,23 @@ public InfoMap put(Info info) { public InfoMap putFirst(Info info) { return put(0, info); } + + /** Initialize copiedDeclarations for cppName we need to copy declarations from. */ + void normalizeCopy() { + HashSet copyFrom = new HashSet<>(); + for (List infoList: values()) { + for (Info i: infoList) { + if (i.copyFrom != null) + copyFrom.addAll(i.copyFrom); + } + } + for (String cppName: copyFrom) { + Info i = getFirst(cppName); + if (i == null) { + i = new Info(cppName); + put(i); + } + i.copiedDeclarations = new CopiedDeclarations(); + } + } } diff --git a/src/main/java/org/bytedeco/javacpp/tools/Parser.java b/src/main/java/org/bytedeco/javacpp/tools/Parser.java index 90beb8c8..7a24919a 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Parser.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Parser.java @@ -2626,6 +2626,16 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti if (declList.add(decl, fullname)) { first = false; if (extraDecl != null) declList.add(extraDecl); + // Do not copy constructors unless specifically asked for with Info. + // Do not copy static functions. The copy would probably need changes in the type of its argument + // to be useful anyway. + // We don't copy extraDecl either that is only used for friends and that calls a static method. + if (!decl.declarator.type.staticMember && !decl.inaccessible) { + if (info != null && info.copiedDeclarations != null) + info.copiedDeclarations.add(decl, fullname, modifiers, context.namespace); + if (context.copiedDeclarations != null && !type.constructor) + context.copiedDeclarations.add(decl, fullname, modifiers,context.namespace); + } } if (type.virtual && context.virtualize) { break; @@ -3292,17 +3302,22 @@ boolean group(Context context, DeclarationList declList) throws ParserException decl.text = type.annotations; String name = type.javaName; boolean anonymous = !typedef && type.cppName.length() == 0, derivedClass = false, skipBase = false; + List copyFrom = new ArrayList<>(); + List flattenFrom = new ArrayList<>(); if (type.cppName.length() > 0 && tokens.get().match(':')) { derivedClass = true; + boolean virtualInheritance = false; + boolean accessible = !ctx.inaccessible; for (Token token = tokens.next(); !token.match(Token.EOF); token = tokens.next()) { - boolean accessible = !ctx.inaccessible; if (token.match(Token.VIRTUAL)) { + virtualInheritance = true; continue; } else if (token.match(Token.PRIVATE, Token.PROTECTED, Token.PUBLIC)) { accessible = token.match(Token.PUBLIC); - tokens.next(); + continue; } Type t = type(context); + t.virtual = virtualInheritance; Info info = infoMap.getFirst(t.cppName); if (info != null && info.skip) { skipBase = true; @@ -3313,6 +3328,8 @@ boolean group(Context context, DeclarationList declList) throws ParserException if (tokens.get().expect(',', '{').match('{')) { break; } + virtualInheritance = false; + accessible = !ctx.inaccessible; } } if (typedef && type.indirections > 0) { @@ -3409,17 +3426,34 @@ boolean group(Context context, DeclarationList declList) throws ParserException } infoMap.put(info = new Info(type.cppName).pointerTypes(type.javaName)); } - Type base = new Type("Pointer"); + + // Choose the base Java type: first base C++ type which is not flattened and that we can inherit from. + // Keep the others in baseClasses to generate asX() casts and fill copyFrom and flattenFrom. + // If no C++ base type suits, use Pointer. + boolean polymorphic = false; + Type base = null; Iterator it = baseClasses.iterator(); while (it.hasNext()) { Type next = it.next(); Info nextInfo = infoMap.getFirst(next.cppName); - if (nextInfo == null || !nextInfo.flatten) { + if (nextInfo != null) { + polymorphic |= nextInfo.polymorphic; + if (nextInfo.flatten) { + flattenFrom.add(next); + continue; + } else if (nextInfo.polymorphic && next.virtual) { + // Virtual inheritance to a polymorphic class, we cannot inherit in Java, copy declarations instead. + copyFrom.add(next.cppName); + continue; + } + } + if (base == null) { base = next; it.remove(); - break; } } + if (base == null) base = new Type("Pointer"); + String casts = ""; if (baseClasses.size() > 0) { for (Type t : baseClasses) { @@ -3501,9 +3535,16 @@ boolean group(Context context, DeclarationList declList) throws ParserException ctx.immutable = true; if (info.beanify) ctx.beanify = true; + if (info.copyFrom != null) + copyFrom.addAll(info.copyFrom); } ctx.baseType = base.cppName; + /* Always copy member declarations. The copy will be saved in an Info if this class turns out to be + * polymorphic (in case another class will virtually inherit from it) or if the used asked for + * copy using Info.copyFrom. */ + ctx.copiedDeclarations = new CopiedDeclarations(); + DeclarationList declList2 = new DeclarationList(); if (variables.size() == 0) { declarations(ctx, declList2); @@ -3524,11 +3565,55 @@ boolean group(Context context, DeclarationList declList) throws ParserException ctx.variable = var; declarations(ctx, declList2); } + + String thisNamespace = context.namespace != null && context.javaName == null ? context.namespace : null; + for (String cf: copyFrom) { + Info infoCopyFrom = infoMap.getFirst(cf); + if (infoCopyFrom != null && infoCopyFrom.copiedDeclarations != null) { + declList2.context.inaccessible = false; + // Recompose the text of copied declarations, change the class name for constructor + // Adjust @Virtual + // Probably other modifications are necessary. + for (CopiedDeclarations.CopiedDeclaration cd : infoCopyFrom.copiedDeclarations) { + Declaration d = cd.decl.clone(); + final String funcName = (d.declarator.type != null && d.declarator.type.constructor) ? + shortName : ctx.shorten(d.declarator.javaName); + Info infoCopyTo = infoMap.getFirst(ctx.namespace == null ? funcName : ctx.namespace + "::" + funcName); + String modifiers = cd.modifiers.replace("@Virtual", ""); + if (infoCopyTo != null) { + if (infoCopyTo.virtualize) modifiers = "@Virtual " + modifiers; + if (infoCopyTo.annotations != null) { + for (String ann : infoCopyTo.annotations) + modifiers += ann + " "; + } + } + if (d.declarator.type != null && d.declarator.type.constructor) { + Parameters params = d.declarator.parameters; + d.text = spacing + "public " + shortName + params.list + " { super((Pointer)null); allocate" + params.names + "; }\n" + + d.declarator.type.annotations + "private native void allocate" + params.list + ";\n"; + d.signature = shortName + params.signature; + } else { + d.text = spacing; + if (cd.namespace != null && !cd.namespace.equals(thisNamespace)) + // Use namespace of the declaration we are copying since it can contain non-qualified types. + // TODO: this annotation won't help if the declaration contains a @Cast or other annotation + // with non-qualified types. Arrange so that auto-generated @Cast in containers are + // always fully-qualified. + d.text += "@Namespace(\""+cd.namespace+"\") "; + d.text += modifiers + d.declarator.type.annotations + context.shorten(d.declarator.type.javaName) + " " + d.declarator.javaName + d.declarator.parameters.list + ";\n"; + } + + declList2.add(d, cd.fullname); + } + } + } + String modifiers = "public static ", constructors = "", inheritedConstructors = ""; boolean implicitConstructor = true, arrayConstructor = false, defaultConstructor = false, longConstructor = false, pointerConstructor = false, abstractClass = info != null && info.purify && !ctx.virtualize, allPureConst = true, haveVariables = false; for (Declaration d : declList2) { + polymorphic |= d.declarator != null && d.declarator.type != null && d.declarator.type.virtual; if (d.declarator != null && d.declarator.type != null && d.declarator.type.using && decl.text != null) { // inheriting constructors defaultConstructor |= d.text.contains("private native void allocate();"); @@ -3637,24 +3722,41 @@ boolean group(Context context, DeclarationList declList) throws ParserException decl.text = declList.rescan(decl.text + casts + "\n"); declList.spacing = null; } - for (Type base2 : baseClasses) { + for (Type base2 : flattenFrom) { Info baseInfo = infoMap.getFirst(base2.cppName); - if (baseInfo != null && baseInfo.flatten && baseInfo.javaText != null) { - String text = baseInfo.javaText; - int start = text.indexOf('{'); - for (int n = 0; n < 2; start++) { - int c = text.charAt(start); - if (c == '\n') { - n++; - } else if (!Character.isWhitespace(c)) { - n = 0; + if (baseInfo != null) { + if (baseInfo.flatten && baseInfo.javaText != null) { + String text = baseInfo.javaText; + int start = text.indexOf('{'); + for (int n = 0; n < 2; start++) { + int c = text.charAt(start); + if (c == '\n') { + n++; + } else if (!Character.isWhitespace(c)) { + n = 0; + } } + int end = text.lastIndexOf('}'); + decl.text += text.substring(start, end).replace(base2.javaName, type.javaName); + decl.custom = true; } - int end = text.lastIndexOf('}'); - decl.text += text.substring(start, end).replace(base2.javaName, type.javaName); - decl.custom = true; } } + if (polymorphic) { + if (info == null) { + info = new Info(cppName); + info.copiedDeclarations = new CopiedDeclarations(); + infoMap.put(info); + } else if (info.copiedDeclarations == null) { + info.copiedDeclarations = new CopiedDeclarations(); + } + info.polymorphic = true; + } + + if (info != null && info.copiedDeclarations != null) { + info.copiedDeclarations.addAll(ctx.copiedDeclarations); + } + for (Declaration d : declList2) { if ((!d.inaccessible || d.declarator != null && d.declarator.type.friend) && (d.declarator == null || d.declarator.type == null @@ -4295,6 +4397,7 @@ public File[] parse(File outputDirectory, String[] classPath, Class cls) throws // fail silently as if the interface wasn't implemented } infoMap.putAll(leafInfoMap); + infoMap.normalizeCopy(); String version = Parser.class.getPackage().getImplementationVersion(); if (version == null) { From efb9e02d89b33a3c3472eee7c7af8cdc4b669b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Guillemet?= Date: Mon, 20 Feb 2023 13:23:32 +0100 Subject: [PATCH 3/8] Move #654 off this branch. --- src/main/java/org/bytedeco/javacpp/tools/Builder.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/bytedeco/javacpp/tools/Builder.java b/src/main/java/org/bytedeco/javacpp/tools/Builder.java index 39f6d230..b8b9624f 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Builder.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Builder.java @@ -449,7 +449,7 @@ int compile(String[] sourceFilenames, String outputFilename, ClassProperties pro /** * Creates and returns the directory where output files should be placed. * Uses {@link #outputDirectory} as is when available, but falls back - * on the longest common path to the classes as well as the platform + * on the shortest common path to the classes as well as the platform * specific library path when available, or the platform name itself * and the user provided extension when not. * @@ -476,14 +476,13 @@ File getOutputPath(Class[] classes, String[] sourcePrefixes) throws IOException String resourceURL = Loader.findResource(classes[0], resourceName).toString(); String packageURI = resourceURL.substring(0, resourceURL.lastIndexOf('/') + 1); for (int i = 1; i < classes.length; i++) { - // Use the longest common package name among all classes as default output path + // Use shortest common package name among all classes as default output path String resourceName2 = '/' + classes[i].getName().replace('.', '/') + ".class"; String resourceURL2 = Loader.findResource(classes[i], resourceName2).toString(); String packageURI2 = resourceURL2.substring(0, resourceURL2.lastIndexOf('/') + 1); - String longest, shortest; - if (packageURI2.length() > packageURI.length()) { longest = packageURI2; shortest = packageURI; } - else { longest = packageURI; shortest = packageURI2; } + String longest = packageURI2.length() > packageURI.length() ? packageURI2 : packageURI; + String shortest = packageURI2.length() < packageURI.length() ? packageURI2 : packageURI; while (!longest.startsWith(shortest) && shortest.lastIndexOf('/') > 0) { shortest = shortest.substring(0, shortest.lastIndexOf('/')); } From 4b30b56b91c0e69016a6f3bb9863b6158463f06a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Guillemet?= Date: Mon, 20 Feb 2023 13:27:53 +0100 Subject: [PATCH 4/8] Replaced Declaration.clone by copy constructor. --- .../bytedeco/javacpp/tools/Declaration.java | 31 ++++++++++++------- .../org/bytedeco/javacpp/tools/Parser.java | 2 +- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/bytedeco/javacpp/tools/Declaration.java b/src/main/java/org/bytedeco/javacpp/tools/Declaration.java index 43617346..2d58141f 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Declaration.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Declaration.java @@ -26,24 +26,33 @@ * * @author Samuel Audet */ -class Declaration implements Cloneable { +class Declaration { Type type = null; Declarator declarator = null; boolean abstractMember = false, constMember = false, inaccessible = false, incomplete = false, function = false, variable = false, comment = false, custom = false; String signature = "", text = ""; - public String toString() { - return text; + public Declaration() { + } + + Declaration(Declaration src) { + // Shallow copy is enough for the current usage + type = src.type; + declarator = src.declarator; + abstractMember = src.abstractMember; + constMember = src.constMember; + inaccessible = src.inaccessible; + incomplete = src.incomplete; + function = src.function; + variable = src.variable; + comment = src.comment; + custom = src.custom; + signature = src.signature; + text = src.text; } - @Override - public Declaration clone() { - try { - // Shallow copy is enough for the current usage - return (Declaration) super.clone(); - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } + public String toString() { + return text; } } diff --git a/src/main/java/org/bytedeco/javacpp/tools/Parser.java b/src/main/java/org/bytedeco/javacpp/tools/Parser.java index 7a24919a..64116458 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Parser.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Parser.java @@ -3575,7 +3575,7 @@ boolean group(Context context, DeclarationList declList) throws ParserException // Adjust @Virtual // Probably other modifications are necessary. for (CopiedDeclarations.CopiedDeclaration cd : infoCopyFrom.copiedDeclarations) { - Declaration d = cd.decl.clone(); + Declaration d = new Declaration(cd.decl); final String funcName = (d.declarator.type != null && d.declarator.type.constructor) ? shortName : ctx.shorten(d.declarator.javaName); Info infoCopyTo = infoMap.getFirst(ctx.namespace == null ? funcName : ctx.namespace + "::" + funcName); From 8bb2bb0729fe2db3a09a205f334854b280a3107b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Guillemet?= Date: Mon, 20 Feb 2023 18:09:04 +0100 Subject: [PATCH 5/8] Move polymorphic and copiedDeclaration from Info to Parser. Store Java names instead of cpp names in polymorphicClasses. --- .../java/org/bytedeco/javacpp/tools/Info.java | 6 -- .../org/bytedeco/javacpp/tools/InfoMap.java | 19 ------ .../org/bytedeco/javacpp/tools/Parser.java | 66 +++++++++++-------- 3 files changed, 40 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/bytedeco/javacpp/tools/Info.java b/src/main/java/org/bytedeco/javacpp/tools/Info.java index 0fe5cc8e..32cc7d9a 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Info.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Info.java @@ -131,12 +131,6 @@ public Info(Info i) { String javaText = null; /** List of cpp names (class, struct, member function) the declarations of which we want to copy in this class or struct. */ List copyFrom = null; - /** If not-null, declarations for these cpp names are copied. - * Set to non-null by InfoMap.normalizeCopy or for polymorphic classes. */ - CopiedDeclarations copiedDeclarations = null; - /** Set by Parser when a class has a virtual member function or inherit from a polymorphic class. */ - boolean polymorphic = false; - public Info cppNames(String... cppNames) { this.cppNames = cppNames; return this; } public Info javaNames(String... javaNames) { this.javaNames = javaNames; return this; } diff --git a/src/main/java/org/bytedeco/javacpp/tools/InfoMap.java b/src/main/java/org/bytedeco/javacpp/tools/InfoMap.java index a579afe4..72aae9d9 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/InfoMap.java +++ b/src/main/java/org/bytedeco/javacpp/tools/InfoMap.java @@ -27,7 +27,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.HashSet; /** * A {@link Map} containing {@link Info} objects consumed by the {@link Parser}. @@ -345,22 +344,4 @@ public InfoMap putFirst(Info info) { return put(0, info); } - /** Initialize copiedDeclarations for cppName we need to copy declarations from. */ - void normalizeCopy() { - HashSet copyFrom = new HashSet<>(); - for (List infoList: values()) { - for (Info i: infoList) { - if (i.copyFrom != null) - copyFrom.addAll(i.copyFrom); - } - } - for (String cppName: copyFrom) { - Info i = getFirst(cppName); - if (i == null) { - i = new Info(cppName); - put(i); - } - i.copiedDeclarations = new CopiedDeclarations(); - } - } } diff --git a/src/main/java/org/bytedeco/javacpp/tools/Parser.java b/src/main/java/org/bytedeco/javacpp/tools/Parser.java index 64116458..4a1372b1 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Parser.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Parser.java @@ -95,6 +95,10 @@ public Parser(Logger logger, Properties properties, String encoding, String line InfoMap leafInfoMap = null; TokenIndexer tokens = null; String lineSeparator = null; + HashSet polymorphicClasses = new HashSet<>(); // Contains Java names + // Keys of copiedDeclarations are the cpp names of sources we must copy declarations from: + // polymorphic class, or values of copyFrom Info (template instances in case of templated class or functions). + HashMap copiedDeclarations = new HashMap<>(); String translate(String text) { Info info = infoMap.getFirst(text); @@ -2631,13 +2635,16 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti // to be useful anyway. // We don't copy extraDecl either that is only used for friends and that calls a static method. if (!decl.declarator.type.staticMember && !decl.inaccessible) { - if (info != null && info.copiedDeclarations != null) - info.copiedDeclarations.add(decl, fullname, modifiers, context.namespace); + CopiedDeclarations cd = copiedDeclarations.get(type.constructor ? + dcl.cppName + "::" + dcl.cppName.substring(namespace + 2) : + dcl.cppName); + if (cd != null) cd.add(decl, fullname, modifiers, context.namespace); if (context.copiedDeclarations != null && !type.constructor) - context.copiedDeclarations.add(decl, fullname, modifiers,context.namespace); + context.copiedDeclarations.add(decl, fullname, modifiers, context.namespace); } } if (type.virtual && context.virtualize) { + // Prevent creation of overloads, that are not supported by the generated C++ proxy class. break; } } else if (found && n / 2 > 0 && n % 2 == 0 && n / 2 > Math.max(dcl.infoNumber, dcl.parameters.infoNumber)) { @@ -3437,11 +3444,12 @@ boolean group(Context context, DeclarationList declList) throws ParserException Type next = it.next(); Info nextInfo = infoMap.getFirst(next.cppName); if (nextInfo != null) { - polymorphic |= nextInfo.polymorphic; + boolean nextPolymorphic = polymorphicClasses.contains(next.javaName); + polymorphic |= nextPolymorphic; if (nextInfo.flatten) { flattenFrom.add(next); continue; - } else if (nextInfo.polymorphic && next.virtual) { + } else if (nextPolymorphic && next.virtual) { // Virtual inheritance to a polymorphic class, we cannot inherit in Java, copy declarations instead. copyFrom.add(next.cppName); continue; @@ -3540,9 +3548,9 @@ boolean group(Context context, DeclarationList declList) throws ParserException } ctx.baseType = base.cppName; - /* Always copy member declarations. The copy will be saved in an Info if this class turns out to be - * polymorphic (in case another class will virtually inherit from it) or if the used asked for - * copy using Info.copyFrom. */ + /* Always copy member declarations. The copy will be saved in copiedDeclarations if this class turns out to be + * polymorphic (and in case another class parsed later virtually inherits from it) or if the used asked for + * copy by listing this class in an Info.copyFrom. */ ctx.copiedDeclarations = new CopiedDeclarations(); DeclarationList declList2 = new DeclarationList(); @@ -3568,13 +3576,13 @@ boolean group(Context context, DeclarationList declList) throws ParserException String thisNamespace = context.namespace != null && context.javaName == null ? context.namespace : null; for (String cf: copyFrom) { - Info infoCopyFrom = infoMap.getFirst(cf); - if (infoCopyFrom != null && infoCopyFrom.copiedDeclarations != null) { + CopiedDeclarations cp = copiedDeclarations.get(cf); + if (cp != null) { declList2.context.inaccessible = false; // Recompose the text of copied declarations, change the class name for constructor - // Adjust @Virtual + // Adjust @Virtual. // Probably other modifications are necessary. - for (CopiedDeclarations.CopiedDeclaration cd : infoCopyFrom.copiedDeclarations) { + for (CopiedDeclarations.CopiedDeclaration cd : cp) { Declaration d = new Declaration(cd.decl); final String funcName = (d.declarator.type != null && d.declarator.type.constructor) ? shortName : ctx.shorten(d.declarator.javaName); @@ -3589,11 +3597,11 @@ boolean group(Context context, DeclarationList declList) throws ParserException } if (d.declarator.type != null && d.declarator.type.constructor) { Parameters params = d.declarator.parameters; - d.text = spacing + "public " + shortName + params.list + " { super((Pointer)null); allocate" + params.names + "; }\n" + + d.text = "\npublic " + shortName + params.list + " { super((Pointer)null); allocate" + params.names + "; }\n" + d.declarator.type.annotations + "private native void allocate" + params.list + ";\n"; d.signature = shortName + params.signature; } else { - d.text = spacing; + d.text = "\n"; if (cd.namespace != null && !cd.namespace.equals(thisNamespace)) // Use namespace of the declaration we are copying since it can contain non-qualified types. // TODO: this annotation won't help if the declaration contains a @Cast or other annotation @@ -3743,19 +3751,13 @@ boolean group(Context context, DeclarationList declList) throws ParserException } } if (polymorphic) { - if (info == null) { - info = new Info(cppName); - info.copiedDeclarations = new CopiedDeclarations(); - infoMap.put(info); - } else if (info.copiedDeclarations == null) { - info.copiedDeclarations = new CopiedDeclarations(); - } - info.polymorphic = true; + polymorphicClasses.add(type.javaName); + if (!copiedDeclarations.containsKey(type.cppName)) + copiedDeclarations.put(type.cppName, new CopiedDeclarations()); } - if (info != null && info.copiedDeclarations != null) { - info.copiedDeclarations.addAll(ctx.copiedDeclarations); - } + CopiedDeclarations cp = copiedDeclarations.get(type.cppName); + if (cp != null) cp.addAll(ctx.copiedDeclarations); for (Declaration d : declList2) { if ((!d.inaccessible || d.declarator != null && d.declarator.type.friend) @@ -4281,6 +4283,18 @@ void declarations(Context context, DeclarationList declList) throws ParserExcept } } + /** Initialize copiedDeclarations for cppName we need to copy declarations from. */ + private void initializeCopiedDeclarations() { + for (List infoList: infoMap.values()) { + for (Info i: infoList) { + if (i.copyFrom != null) + for (String cf: i.copyFrom) + if (!copiedDeclarations.containsKey(cf)) + copiedDeclarations.put(cf, new CopiedDeclarations()); + } + } + } + void parse(Context context, DeclarationList declList, String[] includePath, @@ -4397,7 +4411,7 @@ public File[] parse(File outputDirectory, String[] classPath, Class cls) throws // fail silently as if the interface wasn't implemented } infoMap.putAll(leafInfoMap); - infoMap.normalizeCopy(); + initializeCopiedDeclarations(); String version = Parser.class.getPackage().getImplementationVersion(); if (version == null) { From ff8a78ab423614e931a521dabac2bed05d712b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Guillemet?= Date: Tue, 21 Feb 2023 22:53:36 +0100 Subject: [PATCH 6/8] Add template map to copied declarations --- .../org/bytedeco/javacpp/tools/CopiedDeclarations.java | 8 +++++--- src/main/java/org/bytedeco/javacpp/tools/Parser.java | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/bytedeco/javacpp/tools/CopiedDeclarations.java b/src/main/java/org/bytedeco/javacpp/tools/CopiedDeclarations.java index e76aa8ae..3ff15af7 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/CopiedDeclarations.java +++ b/src/main/java/org/bytedeco/javacpp/tools/CopiedDeclarations.java @@ -3,8 +3,8 @@ import java.util.ArrayList; class CopiedDeclarations extends ArrayList { - void add(Declaration decl, String fullname, String modifiers, String namespace) { - add(new CopiedDeclaration(decl, fullname, modifiers, namespace)); + void add(Declaration decl, String fullname, String modifiers, Context context) { + add(new CopiedDeclaration(decl, fullname, modifiers, context.namespace, context.templateMap)); } static class CopiedDeclaration { @@ -12,12 +12,14 @@ static class CopiedDeclaration { final String fullname; final String modifiers; final String namespace; + final TemplateMap templateMap; - CopiedDeclaration(Declaration decl, String fullname, String modifiers, String namespace) { + CopiedDeclaration(Declaration decl, String fullname, String modifiers, String namespace, TemplateMap templateMap) { this.decl = decl; this.fullname = fullname; this.modifiers = modifiers; this.namespace = namespace; + this.templateMap = templateMap; } } } diff --git a/src/main/java/org/bytedeco/javacpp/tools/Parser.java b/src/main/java/org/bytedeco/javacpp/tools/Parser.java index 4a1372b1..678acb43 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Parser.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Parser.java @@ -2638,9 +2638,9 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti CopiedDeclarations cd = copiedDeclarations.get(type.constructor ? dcl.cppName + "::" + dcl.cppName.substring(namespace + 2) : dcl.cppName); - if (cd != null) cd.add(decl, fullname, modifiers, context.namespace); + if (cd != null) cd.add(decl, fullname, modifiers, context); if (context.copiedDeclarations != null && !type.constructor) - context.copiedDeclarations.add(decl, fullname, modifiers, context.namespace); + context.copiedDeclarations.add(decl, fullname, modifiers, context); } } if (type.virtual && context.virtualize) { @@ -3611,6 +3611,7 @@ boolean group(Context context, DeclarationList declList) throws ParserException d.text += modifiers + d.declarator.type.annotations + context.shorten(d.declarator.type.javaName) + " " + d.declarator.javaName + d.declarator.parameters.list + ";\n"; } + declList2.templateMap = cd.templateMap; declList2.add(d, cd.fullname); } } From ad2b1702aae760792dc45fd1da68a0ff7947c3ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Guillemet?= Date: Tue, 28 Feb 2023 00:12:46 +0100 Subject: [PATCH 7/8] Fix building of cppName when copying constructors. --- .../org/bytedeco/javacpp/tools/Parser.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/bytedeco/javacpp/tools/Parser.java b/src/main/java/org/bytedeco/javacpp/tools/Parser.java index 678acb43..22ab9615 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Parser.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Parser.java @@ -2253,8 +2253,8 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti return false; } - int namespace = dcl.cppName.lastIndexOf("::"); - if (context.namespace != null && namespace < 0) { + boolean hasNamespace = dcl.cppName.contains("::"); + if (context.namespace != null && !hasNamespace) { dcl.cppName = context.namespace + "::" + dcl.cppName; } Info info = null, fullInfo = null; @@ -2446,8 +2446,8 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti } else { dcl = declarator(context, null, n / 2, (info == null || !info.skipDefaults) && n % 2 != 0, 0, false, false); type = dcl.type; - namespace = dcl.cppName.lastIndexOf("::"); - if (context.namespace != null && namespace < 0) { + hasNamespace = dcl.cppName.contains("::"); + if (context.namespace != null && !hasNamespace) { dcl.cppName = context.namespace + "::" + dcl.cppName; } } @@ -2635,9 +2635,17 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti // to be useful anyway. // We don't copy extraDecl either that is only used for friends and that calls a static method. if (!decl.declarator.type.staticMember && !decl.inaccessible) { - CopiedDeclarations cd = copiedDeclarations.get(type.constructor ? - dcl.cppName + "::" + dcl.cppName.substring(namespace + 2) : - dcl.cppName); + final String key; + if (type.constructor) { + int template = dcl.cppName.indexOf('<'); + if (template < 0) template = dcl.cppName.length(); + int namespace = dcl.cppName.lastIndexOf("::", template); + if (namespace < 0) namespace = -2; + key = dcl.cppName + "::" + dcl.cppName.substring(namespace + 2, template); + } else { + key = dcl.cppName; + } + CopiedDeclarations cd = copiedDeclarations.get(key); if (cd != null) cd.add(decl, fullname, modifiers, context); if (context.copiedDeclarations != null && !type.constructor) context.copiedDeclarations.add(decl, fullname, modifiers, context); From f2509cef7ca79a31488ac533739a3dd2455d79ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Guillemet?= Date: Tue, 28 Feb 2023 00:13:34 +0100 Subject: [PATCH 8/8] Honor Info.javaText when copying declarations --- src/main/java/org/bytedeco/javacpp/tools/Parser.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/bytedeco/javacpp/tools/Parser.java b/src/main/java/org/bytedeco/javacpp/tools/Parser.java index 22ab9615..aff39b75 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Parser.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Parser.java @@ -3603,7 +3603,9 @@ boolean group(Context context, DeclarationList declList) throws ParserException modifiers += ann + " "; } } - if (d.declarator.type != null && d.declarator.type.constructor) { + if (infoCopyTo != null && infoCopyTo.javaText != null) + d.text = "\n" + infoCopyTo.javaText; + else if (d.declarator.type != null && d.declarator.type.constructor) { Parameters params = d.declarator.parameters; d.text = "\npublic " + shortName + params.list + " { super((Pointer)null); allocate" + params.names + "; }\n" + d.declarator.type.annotations + "private native void allocate" + params.list + ";\n";