Skip to content

Commit b52da08

Browse files
authored
Merge pull request #1340 from NativeScript/vmutafov/fix-sbg-bugs-rel
Remove unnecessary class loading and improve generic interface name w…
2 parents a22f2cf + 78e7285 commit b52da08

File tree

8 files changed

+121
-115
lines changed

8 files changed

+121
-115
lines changed

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

Lines changed: 77 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import org.apache.bcel.classfile.JavaClass;
77
import org.apache.bcel.classfile.Method;
8-
import org.apache.commons.io.FileUtils;
98
import org.nativescript.staticbindinggenerator.files.FileSystemHelper;
109
import org.nativescript.staticbindinggenerator.files.impl.ClassesCollection;
1110
import org.nativescript.staticbindinggenerator.files.impl.FileSystemHelperImpl;
@@ -15,6 +14,7 @@
1514
import org.nativescript.staticbindinggenerator.generating.parsing.checkers.impl.ImplementationObjectCheckerImpl;
1615
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.GenericHierarchyView;
1716
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.GenericParameters;
17+
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.GenericsAwareClassHierarchyParser;
1818
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.impl.GenericSignatureReader;
1919
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.impl.GenericsAwareClassHierarchyParserImpl;
2020
import org.nativescript.staticbindinggenerator.generating.parsing.methods.InheritedMethodsCollector;
@@ -35,6 +35,7 @@
3535
import org.nativescript.staticbindinggenerator.generating.writing.impl.MethodsWriterImpl;
3636
import org.nativescript.staticbindinggenerator.generating.writing.impl.PackageNameWriterImpl;
3737
import org.nativescript.staticbindinggenerator.naming.BcelNamingUtil;
38+
import org.nativescript.staticbindinggenerator.naming.JavaClassNames;
3839

3940
import java.io.BufferedReader;
4041
import java.io.File;
@@ -43,7 +44,6 @@
4344
import java.io.IOException;
4445
import java.io.InputStreamReader;
4546
import java.io.PrintStream;
46-
import java.nio.charset.Charset;
4747
import java.nio.file.Files;
4848
import java.nio.file.Paths;
4949
import java.util.ArrayList;
@@ -279,61 +279,99 @@ private String getSimpleClassname(String classname) {
279279
}
280280

281281
private void writeBinding(Writer w, DataRow dataRow, JavaClass clazz, String packageName, String name) {
282-
GenericHierarchyView genView = new GenericsAwareClassHierarchyParserImpl(new GenericSignatureReader(), classes).getClassHierarchy(clazz);
282+
GenericsAwareClassHierarchyParser genericsAwareClassHierarchyParser = new GenericsAwareClassHierarchyParserImpl(new GenericSignatureReader(), classes);
283+
List<JavaClass> userImplementedInterfaces = getInterfacesFromCache(Arrays.asList(dataRow.getInterfaces()));
284+
285+
if (clazz.isInterface()) {
286+
userImplementedInterfaces.add(clazz);
287+
clazz = getClass(JavaClassNames.BASE_JAVA_CLASS_NAME);
288+
}
289+
290+
GenericHierarchyView genView = createExtendedClassGenericHierarchyView(genericsAwareClassHierarchyParser, clazz);
291+
Map<JavaClass, GenericHierarchyView> interfaceGenericHierarchyViews = createInterfaceGenericHierarchyViews(genericsAwareClassHierarchyParser, userImplementedInterfaces);
283292

284293
writePackageNameToWriter(w, packageName);
285294
writeImportsToWriter(w, clazz, packageName);
286-
writeClassBeginningToWriter(w, clazz, dataRow.getInterfaces(), name, dataRow, genView);
295+
writeClassBeginningToWriter(w, clazz, userImplementedInterfaces, name, dataRow, genView, interfaceGenericHierarchyViews);
287296
writeFieldsToWriter(w, clazz);
288297
writeConstructorsToWriter(w, clazz, dataRow, name, genView);
289-
writeMethodsToWriter(w, genView, clazz, Arrays.asList(dataRow.getMethods()), Arrays.asList(dataRow.getInterfaces()), packageName);
298+
writeMethodsToWriter(w, genView, interfaceGenericHierarchyViews, clazz, Arrays.asList(dataRow.getMethods()), userImplementedInterfaces, packageName);
290299
writeClassEndToWriter(w);
291300
}
292301

293-
private void writeClassBeginningToWriter(Writer writer, JavaClass clazz, String[] implementedInterfacesNames, String generatedClassName, DataRow dataRow, GenericHierarchyView genericHierarchyView) {
302+
private Map<JavaClass, GenericHierarchyView> createInterfaceGenericHierarchyViews(GenericsAwareClassHierarchyParser genericsAwareClassHierarchyParser, List<JavaClass> implementedInterfaces) {
303+
Map<JavaClass, GenericHierarchyView> interfaceGenericHierarchyViews = new HashMap<>(implementedInterfaces.size());
304+
305+
for (JavaClass implementedInterface : implementedInterfaces) {
306+
GenericHierarchyView genericHierarchyView = genericsAwareClassHierarchyParser.getClassHierarchy(implementedInterface);
307+
interfaceGenericHierarchyViews.put(implementedInterface, genericHierarchyView);
308+
}
309+
310+
return interfaceGenericHierarchyViews;
311+
}
312+
313+
private GenericHierarchyView createExtendedClassGenericHierarchyView(GenericsAwareClassHierarchyParser genericsAwareClassHierarchyParser, JavaClass extendedClass) {
314+
return genericsAwareClassHierarchyParser.getClassHierarchy(extendedClass);
315+
}
316+
317+
private void writeClassBeginningToWriter(Writer writer, JavaClass clazz, List<JavaClass> implementedInterfaces, String generatedClassName, DataRow dataRow, GenericHierarchyView genericHierarchyView, Map<JavaClass, GenericHierarchyView> interfaceGenericHierarchyViews) {
294318
ClassWriter classWriter = new ClassWriterImpl(writer);
295-
StringBuilder extendedClassNameBuilder = new StringBuilder();
296-
extendedClassNameBuilder.append(BcelNamingUtil.resolveClassName(clazz.getClassName()));
297319

298-
GenericParameters initialClassGenericParameters = genericHierarchyView.getInitialClassGenericParameters();
320+
boolean hasCustomJsName = !dataRow.getFilename().isEmpty();
321+
322+
List<String> implementedInterfacesNames = mapNamesWithGenericArgumentsIfNecessary(implementedInterfaces, interfaceGenericHierarchyViews);
323+
String extendedClassName = mapNameWithGenericArgumentsIfNecessary(clazz, genericHierarchyView);
324+
325+
if (hasCustomJsName) {
326+
classWriter.writeBeginningOfNamedChildClass(generatedClassName, dataRow.getJsFilename(), extendedClassName, implementedInterfacesNames);
327+
} else {
328+
classWriter.writeBeginningOfChildClass(generatedClassName, extendedClassName, implementedInterfacesNames);
329+
}
330+
}
331+
332+
private String mapNameWithGenericArgumentsIfNecessary(JavaClass extendedClass, GenericHierarchyView extendedClassGenericHierarchyView) {
333+
return getClassNameWithPossibleGenericArguments(extendedClass, extendedClassGenericHierarchyView);
334+
}
335+
336+
private List<String> mapNamesWithGenericArgumentsIfNecessary(List<JavaClass> implementedInterfaces, Map<JavaClass, GenericHierarchyView> interfaceGenericHierarchyViews) {
337+
List<String> res = new ArrayList<>();
338+
339+
for (JavaClass implementedInterface : implementedInterfaces) {
340+
GenericHierarchyView genericHierarchyView = interfaceGenericHierarchyViews.get(implementedInterface);
341+
String className = getClassNameWithPossibleGenericArguments(implementedInterface, genericHierarchyView);
342+
res.add(className);
343+
}
344+
345+
return res;
346+
}
347+
348+
private String getClassNameWithPossibleGenericArguments(JavaClass classToCheck, GenericHierarchyView classToCheckGenericHierarchyView) {
349+
GenericParameters initialClassGenericParameters = classToCheckGenericHierarchyView.getInitialClassGenericParameters();
350+
StringBuilder classNameBuilder = new StringBuilder();
351+
classNameBuilder.append(BcelNamingUtil.resolveClassName(classToCheck.getClassName()));
299352

300353
if (initialClassGenericParameters != null) {
301354
Map<String, String> initialClassGenericParametersMap = initialClassGenericParameters.getGenericParameters();
302355
int initialClassGenericParametersMapCount = initialClassGenericParametersMap.size();
303356

304357
if (initialClassGenericParametersMapCount > 0) {
305-
extendedClassNameBuilder.append('<');
358+
classNameBuilder.append('<');
306359
int parameterCounter = 0;
307360
for (Map.Entry<String, String> genericParameter : initialClassGenericParametersMap.entrySet()) {
308361
String resolvedGeneriParameterValue = BcelNamingUtil.resolveClassName(genericParameter.getValue());
309-
extendedClassNameBuilder.append(resolvedGeneriParameterValue);
362+
classNameBuilder.append(resolvedGeneriParameterValue);
310363

311364
if (parameterCounter != initialClassGenericParametersMapCount - 1) {
312-
extendedClassNameBuilder.append(", ");
365+
classNameBuilder.append(", ");
313366
parameterCounter += 1;
314367
}
315368
}
316-
extendedClassNameBuilder.append('>');
369+
classNameBuilder.append('>');
317370
}
318371

319372
}
320373

321-
boolean hasCustomJsName = !dataRow.getFilename().isEmpty();
322-
323-
String extendedClassName = extendedClassNameBuilder.toString();
324-
if (hasCustomJsName) {
325-
if (clazz.isInterface()) { // extending an interface
326-
classWriter.writeBeginningOfNamedClassImplementingSingleInterface(generatedClassName, dataRow.getJsFilename(), extendedClassName);
327-
} else {
328-
classWriter.writeBeginningOfNamedChildClass(generatedClassName, dataRow.getJsFilename(), extendedClassName, Arrays.asList(implementedInterfacesNames));
329-
}
330-
} else {
331-
if (clazz.isInterface()) { // extending an interface
332-
classWriter.writeBeginningOfClassImplementingSingleInterface(generatedClassName, extendedClassName);
333-
} else {
334-
classWriter.writeBeginningOfChildClass(generatedClassName, extendedClassName, Arrays.asList(implementedInterfacesNames));
335-
}
336-
}
374+
return classNameBuilder.toString();
337375
}
338376

339377
private void writeImportsToWriter(Writer writer, JavaClass clazz, String packageName) {
@@ -372,32 +410,26 @@ private void writeConstructorsToWriter(Writer writer, JavaClass clazz, DataRow d
372410
boolean hasInitMethod = implementationObjectChecker.hasInitMethod(implObjectMethods);
373411
boolean hasInitMethod2 = !isApplicationClass && hasInitMethod;
374412

375-
boolean isInterface = clazz.isInterface();
376-
if (isInterface) {
377-
methodsWriter.writeDefaultConstructor(generatedClassName);
378-
} else {
379-
for (Method method : clazz.getMethods()) {
380-
if (method.getName().equals("<init>") && (method.isPublic() || method.isProtected())) {
381-
JavaMethod javaMethod = new JavaMethodImpl(method, clazz);
382-
ReifiedJavaMethod reifiedJavaMethod = methodSignatureReifier.transformJavaMethod(javaMethod);
383-
methodsWriter.writeConstructor(generatedClassName, reifiedJavaMethod, hasInitMethod2);
384-
}
413+
for (Method method : clazz.getMethods()) {
414+
if (method.getName().equals("<init>") && (method.isPublic() || method.isProtected())) {
415+
JavaMethod javaMethod = new JavaMethodImpl(method, clazz);
416+
ReifiedJavaMethod reifiedJavaMethod = methodSignatureReifier.transformJavaMethod(javaMethod);
417+
methodsWriter.writeConstructor(generatedClassName, reifiedJavaMethod, hasInitMethod2);
385418
}
386419
}
387420
}
388421

389-
private void writeMethodsToWriter(Writer writer, GenericHierarchyView genericHierarchyView, JavaClass clazz, List<String> userImplementedMethods, List<String> userImplementedInterfacesNames, String packageName) {
390-
boolean isInterface = clazz.isInterface();
422+
private void writeMethodsToWriter(Writer writer, GenericHierarchyView genericHierarchyView, Map<JavaClass, GenericHierarchyView> interfaceGenericHierarchyViews, JavaClass clazz, List<String> userImplementedMethods, List<JavaClass> userImplementedInterfaces, String packageName) {
391423
boolean isApplicationClass = androidClassChecker.isApplicationClass(clazz);
392424

393425
MethodsWriter methodsWriter = new MethodsWriterImpl(writer, suppressCallJSMethodExceptions, isApplicationClass);
394426

395-
List<JavaClass> userImplementedInterfaces = getInterfacesFromCache(userImplementedInterfacesNames);
396427
InheritedMethodsCollector inheritedMethodsCollector = new InheritedMethodsCollectorImpl.Builder()
397428
.forJavaClass(clazz)
398429
.withClassesCache(classes)
399430
.withAdditionalImplementedInterfaces(userImplementedInterfaces)
400431
.withGenericHierarchyView(genericHierarchyView)
432+
.withInterfacesGenericHierarchyViews(interfaceGenericHierarchyViews)
401433
.withPackageName(packageName)
402434
.build();
403435

@@ -427,10 +459,8 @@ private void writeMethodsToWriter(Writer writer, GenericHierarchyView genericHie
427459
methodsWriter.writeGetInstanceMethod(normalizedClassName);
428460
}
429461

430-
if (!isInterface) {
431-
methodsWriter.writeInternalRuntimeHashCodeMethod();
432-
methodsWriter.writeInternalRuntimeEqualsMethod();
433-
}
462+
methodsWriter.writeInternalRuntimeHashCodeMethod();
463+
methodsWriter.writeInternalRuntimeEqualsMethod();
434464
}
435465

436466
private boolean areAllArgumentsAndReturnTypePublic(ReifiedJavaMethod method) {
@@ -490,4 +520,4 @@ private JavaClass getClass(String className) {
490520

491521
return clazz;
492522
}
493-
}
523+
}
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
package org.nativescript.staticbindinggenerator;
22

3+
import org.apache.bcel.classfile.ClassParser;
4+
import org.apache.bcel.classfile.JavaClass;
5+
36
import java.io.BufferedWriter;
47
import java.io.File;
8+
import java.io.FileInputStream;
59
import java.io.FileWriter;
610
import java.io.IOException;
711
import java.io.PrintWriter;
8-
import java.net.MalformedURLException;
9-
import java.net.URL;
10-
import java.net.URLClassLoader;
1112
import java.util.ArrayList;
1213
import java.util.Collections;
13-
import java.util.Enumeration;
1414
import java.util.List;
15-
import java.util.jar.JarEntry;
16-
import java.util.jar.JarFile;
15+
import java.util.jar.JarInputStream;
16+
import java.util.zip.ZipEntry;
1717

1818
public class GetInterfaceNames {
19+
private static final String CLASS_EXT = ".class";
1920
private static String currentDir;
2021

2122
/*
@@ -45,37 +46,41 @@ public static void generateInterfaceFile(List<DataRow> rows)
4546
out.close();
4647
}
4748

48-
private static void generateInterfaceNames(String pathToJar, List<String> interfacesList) throws IOException, ClassNotFoundException {
49+
private static void generateInterfaceNames(String pathToJar, List<String> interfacesList) {
4950
if (pathToJar == null) {
5051
return;
5152
}
52-
53-
JarFile jarFile = new JarFile(pathToJar);
54-
Enumeration<JarEntry> currentJarFile = jarFile.entries();
55-
56-
URLClassLoader cl = getClassLoader(pathToJar);
57-
58-
while (currentJarFile.hasMoreElements()) {
59-
JarEntry jarEntry = currentJarFile.nextElement();
60-
61-
if ((!jarEntry.isDirectory()) && (jarEntry.getName().endsWith(".class"))) {
62-
String className = jarEntry.getName().substring(0, jarEntry.getName().length() - 6);
63-
className = className.replace('/', '.');
64-
65-
@SuppressWarnings("rawtypes")
66-
Class c = null;
53+
JarInputStream jis = null;
54+
try {
55+
String name;
56+
jis = new JarInputStream(new FileInputStream(pathToJar));
57+
for (ZipEntry ze = jis.getNextEntry(); ze != null; ze = jis.getNextEntry()) {
6758
try {
68-
c = cl.loadClass(className);
69-
} catch (IllegalAccessError e) {
70-
} catch (NoClassDefFoundError localNoClassDefFoundError) {
59+
name = ze.getName();
60+
if (name.endsWith(CLASS_EXT)) {
61+
name = name.substring(0, name.length() - CLASS_EXT.length()).replace('/', '.').replace('$', '.');
62+
ClassParser cp = new ClassParser(jis, name);
63+
JavaClass clazz = cp.parse();
64+
if (clazz.isInterface()) {
65+
String res = clazz.getClassName().replace('$', '.');
66+
interfacesList.add(res);
67+
}
68+
}
69+
} catch (IOException e) {
70+
throw new RuntimeException("Error while parsing class file!", e);
7171
}
72-
if ((c != null) && (c.isInterface() == true)) {
73-
String res = c.getName().replace('$', '.');
74-
interfacesList.add(res);
72+
}
73+
} catch (IOException ioe) {
74+
throw new RuntimeException("Error while reading JAR entry!", ioe);
75+
} finally {
76+
if (jis != null) {
77+
try {
78+
jis.close();
79+
} catch (IOException e) {
80+
e.printStackTrace();
7581
}
7682
}
7783
}
78-
jarFile.close();
7984
}
8085

8186
public static PrintWriter ensureOutputFile(String outputFileName) throws IOException {
@@ -89,9 +94,4 @@ public static PrintWriter ensureOutputFile(String outputFileName) throws IOExcep
8994

9095
return new PrintWriter(new BufferedWriter(new FileWriter(checkFile.getAbsolutePath(), true)));
9196
}
92-
93-
private static URLClassLoader getClassLoader(String pathToJar) throws MalformedURLException {
94-
URL[] urls = {new URL("jar:file:" + pathToJar + "!/")};
95-
return URLClassLoader.newInstance(urls);
96-
}
9797
}

test-app/build-tools/static-binding-generator/src/main/java/org/nativescript/staticbindinggenerator/generating/parsing/methods/impl/InheritedMethodsCollectorImpl.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ public class InheritedMethodsCollectorImpl implements InheritedMethodsCollector
2020
private final JavaMethodUtils javaMethodUtils;
2121
private final JavaClassUtils javaClassUtils;
2222
private final GenericHierarchyView genericHierarchyView;
23+
private final Map<JavaClass, GenericHierarchyView> interfacesGenericHierarchyViews;
2324
private final String packageName;
2425
private final JavaClass javaClass;
2526
private final List<JavaClass> implementedInterfaces;
2627

2728
private InheritedMethodsCollectorImpl(Builder builder) {
2829
this.classesCache = builder.classesCache;
2930
this.genericHierarchyView = builder.genericHierarchyView;
31+
this.interfacesGenericHierarchyViews = builder.interfacesGenericHierarchyViews;
3032
this.packageName = builder.packageName;
3133
this.javaClass = builder.javaClass;
3234
this.implementedInterfaces = builder.interfaces;
@@ -75,7 +77,7 @@ public InheritedMethodsView collect() {
7577
HashSet<JavaClass> visited = new HashSet<>(); // saves some interface traversing
7678

7779
for (JavaClass interfaze : implementedInterfaces) {
78-
GenericHierarchyView interfaceGenView = new GenericsAwareClassHierarchyParserImpl(new GenericSignatureReader(), classesCache).getClassHierarchy(interfaze);
80+
GenericHierarchyView interfaceGenView = interfacesGenericHierarchyViews.get(interfaze);
7981
findUnimplementedInterfaceMethods(interfaze, visited, allMethods, packageName, toImplement, overridable, interfaceGenView);
8082
}
8183

@@ -180,6 +182,7 @@ private JavaClass tryGetClassFromCache(String name) {
180182
public static class Builder {
181183
private Map<String, JavaClass> classesCache;
182184
private GenericHierarchyView genericHierarchyView;
185+
private Map<JavaClass, GenericHierarchyView> interfacesGenericHierarchyViews;
183186
private JavaClass javaClass;
184187
private List<JavaClass> interfaces;
185188
private String packageName;
@@ -194,6 +197,11 @@ public Builder withGenericHierarchyView(GenericHierarchyView genericHierarchyVie
194197
return this;
195198
}
196199

200+
public Builder withInterfacesGenericHierarchyViews(Map<JavaClass, GenericHierarchyView> interfacesGenericHierarchyViews){
201+
this.interfacesGenericHierarchyViews = interfacesGenericHierarchyViews;
202+
return this;
203+
}
204+
197205
public Builder forJavaClass(JavaClass javaClass) {
198206
this.javaClass = javaClass;
199207
return this;

0 commit comments

Comments
 (0)