Skip to content

Commit e6caf4b

Browse files
authored
Merge pull request #1327 from NativeScript/vmutafov/sbg-refactoring-fix
Fix issues with generic methods handling in the SBG
2 parents 83d69b9 + 228a386 commit e6caf4b

27 files changed

+758
-251
lines changed

test-app/build-tools/static-binding-generator/src/main/java/org/nativescript/staticbindinggenerator/Generator.java

Lines changed: 123 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,18 @@
33
import com.google.googlejavaformat.java.Formatter;
44
import com.google.googlejavaformat.java.FormatterException;
55

6-
import java.io.BufferedReader;
7-
import java.io.File;
8-
import java.io.FileInputStream;
9-
import java.io.FileNotFoundException;
10-
import java.io.IOException;
11-
import java.io.InputStreamReader;
12-
import java.io.PrintStream;
13-
import java.nio.file.Files;
14-
import java.nio.file.Paths;
15-
import java.util.ArrayList;
16-
import java.util.Arrays;
17-
import java.util.HashMap;
18-
import java.util.HashSet;
19-
import java.util.List;
20-
import java.util.Map;
21-
import java.util.Set;
22-
236
import org.apache.bcel.classfile.JavaClass;
247
import org.apache.bcel.classfile.Method;
8+
import org.apache.commons.io.FileUtils;
259
import org.nativescript.staticbindinggenerator.files.FileSystemHelper;
10+
import org.nativescript.staticbindinggenerator.files.impl.ClassesCollection;
2611
import org.nativescript.staticbindinggenerator.files.impl.FileSystemHelperImpl;
12+
import org.nativescript.staticbindinggenerator.generating.parsing.checkers.AndroidClassChecker;
13+
import org.nativescript.staticbindinggenerator.generating.parsing.checkers.ImplementationObjectChecker;
14+
import org.nativescript.staticbindinggenerator.generating.parsing.checkers.impl.AndroidClassCheckerImpl;
15+
import org.nativescript.staticbindinggenerator.generating.parsing.checkers.impl.ImplementationObjectCheckerImpl;
2716
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.GenericHierarchyView;
17+
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.GenericParameters;
2818
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.impl.GenericSignatureReader;
2919
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.impl.GenericsAwareClassHierarchyParserImpl;
3020
import org.nativescript.staticbindinggenerator.generating.parsing.methods.InheritedMethodsCollector;
@@ -45,10 +35,24 @@
4535
import org.nativescript.staticbindinggenerator.generating.writing.impl.MethodsWriterImpl;
4636
import org.nativescript.staticbindinggenerator.generating.writing.impl.PackageNameWriterImpl;
4737
import org.nativescript.staticbindinggenerator.naming.BcelNamingUtil;
48-
import org.nativescript.staticbindinggenerator.generating.parsing.checkers.AndroidClassChecker;
49-
import org.nativescript.staticbindinggenerator.generating.parsing.checkers.ImplementationObjectChecker;
50-
import org.nativescript.staticbindinggenerator.generating.parsing.checkers.impl.AndroidClassCheckerImpl;
51-
import org.nativescript.staticbindinggenerator.generating.parsing.checkers.impl.ImplementationObjectCheckerImpl;
38+
39+
import java.io.BufferedReader;
40+
import java.io.File;
41+
import java.io.FileInputStream;
42+
import java.io.FileNotFoundException;
43+
import java.io.IOException;
44+
import java.io.InputStreamReader;
45+
import java.io.PrintStream;
46+
import java.nio.charset.Charset;
47+
import java.nio.file.Files;
48+
import java.nio.file.Paths;
49+
import java.util.ArrayList;
50+
import java.util.Arrays;
51+
import java.util.HashMap;
52+
import java.util.HashSet;
53+
import java.util.List;
54+
import java.util.Map;
55+
import java.util.Set;
5256

5357
public class Generator {
5458

@@ -71,6 +75,7 @@ public class Generator {
7175
private final FileSystemHelper fileSystemHelper;
7276
private final boolean suppressCallJSMethodExceptions;
7377
private final AndroidClassChecker androidClassChecker;
78+
private Set<String> nonPublicNestedClasses;
7479

7580
public Generator(File outputDir, List<DataRow> libs) throws IOException {
7681
this(outputDir, libs, false, false);
@@ -80,7 +85,9 @@ public Generator(File outputDir, List<DataRow> libs, boolean suppressCallJSMetho
8085
this.outputDir = outputDir;
8186
this.libs = libs;
8287
this.fileSystemHelper = new FileSystemHelperImpl(throwOnError);
83-
this.classes = readClasses(libs);
88+
ClassesCollection classesCollection = readClasses(libs);
89+
this.classes = classesCollection.getRegularClasses();
90+
this.nonPublicNestedClasses = classesCollection.getNonPublicNestedClasses();
8491
androidClassChecker = new AndroidClassCheckerImpl(classes);
8592
this.suppressCallJSMethodExceptions = suppressCallJSMethodExceptions;
8693
}
@@ -122,6 +129,9 @@ public Binding[] generateBindings(String filename) throws IOException, ClassNotF
122129

123130
public Binding generateBinding(DataRow dataRow, HashSet<String> interfaceNames) throws ClassNotFoundException {
124131
JavaClass clazz = getClass(dataRow.getBaseClassname());
132+
if (!canClassBeExtended(clazz)) {
133+
return null;
134+
}
125135

126136
boolean hasSpecifiedName = !dataRow.getFilename().isEmpty();
127137
String packageName = hasSpecifiedName ? getBaseDir(dataRow.getFilename()) : (DEFAULT_PACKAGE_NAME + "." + clazz.getPackageName());
@@ -159,7 +169,6 @@ public Binding generateBinding(DataRow dataRow, HashSet<String> interfaceNames)
159169
Writer w = new Writer();
160170

161171
writeBinding(w, dataRow, clazz, packageName, name);
162-
163172
String classname = dataRow.getFilename();
164173

165174
try {
@@ -168,8 +177,28 @@ public Binding generateBinding(DataRow dataRow, HashSet<String> interfaceNames)
168177
} catch (FormatterException e) {
169178
return new Binding(new File(baseDir, normalizedName + JAVA_EXT), w.toString(), classname);
170179
}
180+
}
181+
182+
private boolean canClassBeExtended(JavaClass javaClass) {
183+
if (javaClass.isInterface() && (javaClass.isProtected() || javaClass.isPublic())) {
184+
return true;
185+
}
171186

187+
if (javaClass.isFinal() || (!javaClass.isProtected() && !javaClass.isPublic())) {
188+
return false;
189+
}
172190

191+
nextMethod:
192+
for (Method method : javaClass.getMethods()) {
193+
if (method.getName().equals("<init>")
194+
&& !method.isStatic()
195+
&& (method.isPublic() || method.isProtected())) {
196+
197+
return true;
198+
}
199+
}
200+
201+
return false;
173202
}
174203

175204
public Binding generateBinding(DataRow dataRow) throws ClassNotFoundException {
@@ -223,17 +252,19 @@ private String getNormalizedName(String filename) {
223252
return sb.toString();
224253
}
225254

226-
private Map<String, JavaClass> readClasses(List<DataRow> libs) {
255+
private ClassesCollection readClasses(List<DataRow> libs) {
227256
Map<String, JavaClass> map = new HashMap<String, JavaClass>();
257+
Set<String> nonPublicNestedClasses = new HashSet<>();
228258
if (libs != null) {
229259
for (DataRow dr : libs) {
230260
String lib = dr.getRow();
231261
File f = new File(lib);
232-
Map<String, JavaClass> classes = f.isFile() ? fileSystemHelper.readClassesFromJar(lib) : fileSystemHelper.readClassesFromDirectory(lib);
233-
map.putAll(classes);
262+
ClassesCollection classes = f.isFile() ? fileSystemHelper.readClassesFromJar(lib) : fileSystemHelper.readClassesFromDirectory(lib);
263+
map.putAll(classes.getRegularClasses());
264+
nonPublicNestedClasses.addAll(classes.getNonPublicNestedClasses());
234265
}
235266
}
236-
return map;
267+
return new ClassesCollection(map, nonPublicNestedClasses);
237268
}
238269

239270

@@ -248,24 +279,48 @@ private String getSimpleClassname(String classname) {
248279
}
249280

250281
private void writeBinding(Writer w, DataRow dataRow, JavaClass clazz, String packageName, String name) {
282+
GenericHierarchyView genView = new GenericsAwareClassHierarchyParserImpl(new GenericSignatureReader(), classes).getClassHierarchy(clazz);
283+
251284
writePackageNameToWriter(w, packageName);
252285
writeImportsToWriter(w, clazz, packageName);
253-
writeClassBeginningToWriter(w, clazz, dataRow.getInterfaces(), name, dataRow);
286+
writeClassBeginningToWriter(w, clazz, dataRow.getInterfaces(), name, dataRow, genView);
254287
writeFieldsToWriter(w, clazz);
255-
256-
GenericHierarchyView genView = new GenericsAwareClassHierarchyParserImpl(new GenericSignatureReader(), classes).getClassHierarchy(clazz);
257288
writeConstructorsToWriter(w, clazz, dataRow, name, genView);
258289
writeMethodsToWriter(w, genView, clazz, Arrays.asList(dataRow.getMethods()), Arrays.asList(dataRow.getInterfaces()), packageName);
259290
writeClassEndToWriter(w);
260291
}
261292

262-
private void writeClassBeginningToWriter(Writer writer, JavaClass clazz, String[] implementedInterfacesNames, String generatedClassName, DataRow dataRow) {
293+
private void writeClassBeginningToWriter(Writer writer, JavaClass clazz, String[] implementedInterfacesNames, String generatedClassName, DataRow dataRow, GenericHierarchyView genericHierarchyView) {
263294
ClassWriter classWriter = new ClassWriterImpl(writer);
264-
String extendedClassName;
265-
extendedClassName = BcelNamingUtil.resolveClassName(clazz.getClassName());
295+
StringBuilder extendedClassNameBuilder = new StringBuilder();
296+
extendedClassNameBuilder.append(BcelNamingUtil.resolveClassName(clazz.getClassName()));
297+
298+
GenericParameters initialClassGenericParameters = genericHierarchyView.getInitialClassGenericParameters();
299+
300+
if (initialClassGenericParameters != null) {
301+
Map<String, String> initialClassGenericParametersMap = initialClassGenericParameters.getGenericParameters();
302+
int initialClassGenericParametersMapCount = initialClassGenericParametersMap.size();
303+
304+
if (initialClassGenericParametersMapCount > 0) {
305+
extendedClassNameBuilder.append('<');
306+
int parameterCounter = 0;
307+
for (Map.Entry<String, String> genericParameter : initialClassGenericParametersMap.entrySet()) {
308+
String resolvedGeneriParameterValue = BcelNamingUtil.resolveClassName(genericParameter.getValue());
309+
extendedClassNameBuilder.append(resolvedGeneriParameterValue);
310+
311+
if (parameterCounter != initialClassGenericParametersMapCount - 1) {
312+
extendedClassNameBuilder.append(", ");
313+
parameterCounter += 1;
314+
}
315+
}
316+
extendedClassNameBuilder.append('>');
317+
}
318+
319+
}
266320

267321
boolean hasCustomJsName = !dataRow.getFilename().isEmpty();
268322

323+
String extendedClassName = extendedClassNameBuilder.toString();
269324
if (hasCustomJsName) {
270325
if (clazz.isInterface()) { // extending an interface
271326
classWriter.writeBeginningOfNamedClassImplementingSingleInterface(generatedClassName, dataRow.getJsFilename(), extendedClassName);
@@ -358,14 +413,15 @@ private void writeMethodsToWriter(Writer writer, GenericHierarchyView genericHie
358413
for (ReifiedJavaMethod overridableMethod : inheritedMethodsView.getOverridableImplementedMethods()) {
359414
for (String userImplementedMethodName : userImplementedMethods) {
360415
if (overridableMethod.getName().equals(userImplementedMethodName)) {
361-
writer.writeln();
362-
writer.writeln();
363-
methodsWriter.writeMethod(overridableMethod);
416+
if (areAllArgumentsAndReturnTypePublic(overridableMethod)) {
417+
writer.writeln();
418+
writer.writeln();
419+
methodsWriter.writeMethod(overridableMethod);
420+
}
364421
}
365422
}
366423
}
367424

368-
369425
if (isApplicationClass) {
370426
String normalizedClassName = BcelNamingUtil.resolveClassName(clazz.getClassName());
371427
methodsWriter.writeGetInstanceMethod(normalizedClassName);
@@ -377,6 +433,36 @@ private void writeMethodsToWriter(Writer writer, GenericHierarchyView genericHie
377433
}
378434
}
379435

436+
private boolean areAllArgumentsAndReturnTypePublic(ReifiedJavaMethod method) {
437+
String returnType = BcelNamingUtil.resolveClassName(method.getReifiedReturnType());
438+
if (nonPublicNestedClasses.contains(returnType)) {
439+
return false;
440+
}
441+
442+
JavaClass returnTypeClass = classes.get(returnType);
443+
if (returnTypeClass != null && (!returnTypeClass.isPublic() && !returnTypeClass.isProtected())) {
444+
return false;
445+
}
446+
447+
List<String> argumentTypes = method.getReifiedArguments();
448+
for (String argumentType : argumentTypes) {
449+
argumentType = argumentType.trim();
450+
int indexOfSpace = argumentType.indexOf(' ');
451+
String withoutParamName = argumentType.substring(0, indexOfSpace);
452+
String resolvedName = BcelNamingUtil.resolveClassName(withoutParamName);
453+
if (nonPublicNestedClasses.contains(resolvedName)) {
454+
return false;
455+
}
456+
457+
JavaClass argumentTypeClass = classes.get(resolvedName);
458+
if (argumentTypeClass != null && (!argumentTypeClass.isPublic() && !argumentTypeClass.isProtected())) {
459+
return false;
460+
}
461+
}
462+
463+
return true;
464+
}
465+
380466
private List<JavaClass> getInterfacesFromCache(List<String> interfacesNames) {
381467
List<JavaClass> interfaces = new ArrayList<>(interfacesNames.size());
382468

test-app/build-tools/static-binding-generator/src/main/java/org/nativescript/staticbindinggenerator/Main.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ public static void main(String[] args) throws IOException, ClassNotFoundExceptio
4444

4545
// generate java bindings
4646
String inputBindingFilename = Paths.get(System.getProperty("user.dir"), SBG_BINDINGS_NAME).toString();
47-
new Generator(outputDir, rows, isSuppressCallJSMethodExceptionsEnabled(), false).writeBindings(inputBindingFilename);
47+
Generator generator = new Generator(outputDir, rows, isSuppressCallJSMethodExceptionsEnabled(), false);
48+
generator.writeBindings(inputBindingFilename);
4849
}
4950

5051
/*
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package org.nativescript.staticbindinggenerator.files;
22

33
import org.apache.bcel.classfile.JavaClass;
4+
import org.nativescript.staticbindinggenerator.files.impl.ClassesCollection;
45

56
import java.io.File;
67
import java.util.Map;
78

89
public interface FileSystemHelper {
910

10-
Map<String, JavaClass> readClassesFromJar(String jarPath);
11-
Map<String, JavaClass> readClassesFromDirectory(String directoryPath);
11+
ClassesCollection readClassesFromJar(String jarPath);
12+
ClassesCollection readClassesFromDirectory(String directoryPath);
1213
void cleanPreviouslyAutoGeneratedSbgFiles(File folder);
1314

1415
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.nativescript.staticbindinggenerator.files.impl;
2+
3+
import org.apache.bcel.classfile.JavaClass;
4+
5+
import java.util.Map;
6+
import java.util.Set;
7+
8+
public class ClassesCollection {
9+
private final Map<String, JavaClass> regularClasses;
10+
private final Set<String> nonPublicNestedClasses;
11+
12+
public ClassesCollection(Map<String, JavaClass> regularClasses, Set<String> nonPublicNestedClasses) {
13+
this.regularClasses = regularClasses;
14+
this.nonPublicNestedClasses = nonPublicNestedClasses;
15+
}
16+
17+
public Map<String, JavaClass> getRegularClasses() {
18+
return regularClasses;
19+
}
20+
21+
public Set<String> getNonPublicNestedClasses() {
22+
return nonPublicNestedClasses;
23+
}
24+
}

0 commit comments

Comments
 (0)