Skip to content

Remove unnecessary class loading and improve generic interface name w… #1340

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

Merged
merged 1 commit into from
Apr 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.commons.io.FileUtils;
import org.nativescript.staticbindinggenerator.files.FileSystemHelper;
import org.nativescript.staticbindinggenerator.files.impl.ClassesCollection;
import org.nativescript.staticbindinggenerator.files.impl.FileSystemHelperImpl;
Expand All @@ -15,6 +14,7 @@
import org.nativescript.staticbindinggenerator.generating.parsing.checkers.impl.ImplementationObjectCheckerImpl;
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.GenericHierarchyView;
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.GenericParameters;
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.GenericsAwareClassHierarchyParser;
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.impl.GenericSignatureReader;
import org.nativescript.staticbindinggenerator.generating.parsing.classes.hierarchy.generics.impl.GenericsAwareClassHierarchyParserImpl;
import org.nativescript.staticbindinggenerator.generating.parsing.methods.InheritedMethodsCollector;
Expand All @@ -35,6 +35,7 @@
import org.nativescript.staticbindinggenerator.generating.writing.impl.MethodsWriterImpl;
import org.nativescript.staticbindinggenerator.generating.writing.impl.PackageNameWriterImpl;
import org.nativescript.staticbindinggenerator.naming.BcelNamingUtil;
import org.nativescript.staticbindinggenerator.naming.JavaClassNames;

import java.io.BufferedReader;
import java.io.File;
Expand All @@ -43,7 +44,6 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
Expand Down Expand Up @@ -279,61 +279,99 @@ private String getSimpleClassname(String classname) {
}

private void writeBinding(Writer w, DataRow dataRow, JavaClass clazz, String packageName, String name) {
GenericHierarchyView genView = new GenericsAwareClassHierarchyParserImpl(new GenericSignatureReader(), classes).getClassHierarchy(clazz);
GenericsAwareClassHierarchyParser genericsAwareClassHierarchyParser = new GenericsAwareClassHierarchyParserImpl(new GenericSignatureReader(), classes);
List<JavaClass> userImplementedInterfaces = getInterfacesFromCache(Arrays.asList(dataRow.getInterfaces()));

if (clazz.isInterface()) {
userImplementedInterfaces.add(clazz);
clazz = getClass(JavaClassNames.BASE_JAVA_CLASS_NAME);
}

GenericHierarchyView genView = createExtendedClassGenericHierarchyView(genericsAwareClassHierarchyParser, clazz);
Map<JavaClass, GenericHierarchyView> interfaceGenericHierarchyViews = createInterfaceGenericHierarchyViews(genericsAwareClassHierarchyParser, userImplementedInterfaces);

writePackageNameToWriter(w, packageName);
writeImportsToWriter(w, clazz, packageName);
writeClassBeginningToWriter(w, clazz, dataRow.getInterfaces(), name, dataRow, genView);
writeClassBeginningToWriter(w, clazz, userImplementedInterfaces, name, dataRow, genView, interfaceGenericHierarchyViews);
writeFieldsToWriter(w, clazz);
writeConstructorsToWriter(w, clazz, dataRow, name, genView);
writeMethodsToWriter(w, genView, clazz, Arrays.asList(dataRow.getMethods()), Arrays.asList(dataRow.getInterfaces()), packageName);
writeMethodsToWriter(w, genView, interfaceGenericHierarchyViews, clazz, Arrays.asList(dataRow.getMethods()), userImplementedInterfaces, packageName);
writeClassEndToWriter(w);
}

private void writeClassBeginningToWriter(Writer writer, JavaClass clazz, String[] implementedInterfacesNames, String generatedClassName, DataRow dataRow, GenericHierarchyView genericHierarchyView) {
private Map<JavaClass, GenericHierarchyView> createInterfaceGenericHierarchyViews(GenericsAwareClassHierarchyParser genericsAwareClassHierarchyParser, List<JavaClass> implementedInterfaces) {
Map<JavaClass, GenericHierarchyView> interfaceGenericHierarchyViews = new HashMap<>(implementedInterfaces.size());

for (JavaClass implementedInterface : implementedInterfaces) {
GenericHierarchyView genericHierarchyView = genericsAwareClassHierarchyParser.getClassHierarchy(implementedInterface);
interfaceGenericHierarchyViews.put(implementedInterface, genericHierarchyView);
}

return interfaceGenericHierarchyViews;
}

private GenericHierarchyView createExtendedClassGenericHierarchyView(GenericsAwareClassHierarchyParser genericsAwareClassHierarchyParser, JavaClass extendedClass) {
return genericsAwareClassHierarchyParser.getClassHierarchy(extendedClass);
}

private void writeClassBeginningToWriter(Writer writer, JavaClass clazz, List<JavaClass> implementedInterfaces, String generatedClassName, DataRow dataRow, GenericHierarchyView genericHierarchyView, Map<JavaClass, GenericHierarchyView> interfaceGenericHierarchyViews) {
ClassWriter classWriter = new ClassWriterImpl(writer);
StringBuilder extendedClassNameBuilder = new StringBuilder();
extendedClassNameBuilder.append(BcelNamingUtil.resolveClassName(clazz.getClassName()));

GenericParameters initialClassGenericParameters = genericHierarchyView.getInitialClassGenericParameters();
boolean hasCustomJsName = !dataRow.getFilename().isEmpty();

List<String> implementedInterfacesNames = mapNamesWithGenericArgumentsIfNecessary(implementedInterfaces, interfaceGenericHierarchyViews);
String extendedClassName = mapNameWithGenericArgumentsIfNecessary(clazz, genericHierarchyView);

if (hasCustomJsName) {
classWriter.writeBeginningOfNamedChildClass(generatedClassName, dataRow.getJsFilename(), extendedClassName, implementedInterfacesNames);
} else {
classWriter.writeBeginningOfChildClass(generatedClassName, extendedClassName, implementedInterfacesNames);
}
}

private String mapNameWithGenericArgumentsIfNecessary(JavaClass extendedClass, GenericHierarchyView extendedClassGenericHierarchyView) {
return getClassNameWithPossibleGenericArguments(extendedClass, extendedClassGenericHierarchyView);
}

private List<String> mapNamesWithGenericArgumentsIfNecessary(List<JavaClass> implementedInterfaces, Map<JavaClass, GenericHierarchyView> interfaceGenericHierarchyViews) {
List<String> res = new ArrayList<>();

for (JavaClass implementedInterface : implementedInterfaces) {
GenericHierarchyView genericHierarchyView = interfaceGenericHierarchyViews.get(implementedInterface);
String className = getClassNameWithPossibleGenericArguments(implementedInterface, genericHierarchyView);
res.add(className);
}

return res;
}

private String getClassNameWithPossibleGenericArguments(JavaClass classToCheck, GenericHierarchyView classToCheckGenericHierarchyView) {
GenericParameters initialClassGenericParameters = classToCheckGenericHierarchyView.getInitialClassGenericParameters();
StringBuilder classNameBuilder = new StringBuilder();
classNameBuilder.append(BcelNamingUtil.resolveClassName(classToCheck.getClassName()));

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

if (initialClassGenericParametersMapCount > 0) {
extendedClassNameBuilder.append('<');
classNameBuilder.append('<');
int parameterCounter = 0;
for (Map.Entry<String, String> genericParameter : initialClassGenericParametersMap.entrySet()) {
String resolvedGeneriParameterValue = BcelNamingUtil.resolveClassName(genericParameter.getValue());
extendedClassNameBuilder.append(resolvedGeneriParameterValue);
classNameBuilder.append(resolvedGeneriParameterValue);

if (parameterCounter != initialClassGenericParametersMapCount - 1) {
extendedClassNameBuilder.append(", ");
classNameBuilder.append(", ");
parameterCounter += 1;
}
}
extendedClassNameBuilder.append('>');
classNameBuilder.append('>');
}

}

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

String extendedClassName = extendedClassNameBuilder.toString();
if (hasCustomJsName) {
if (clazz.isInterface()) { // extending an interface
classWriter.writeBeginningOfNamedClassImplementingSingleInterface(generatedClassName, dataRow.getJsFilename(), extendedClassName);
} else {
classWriter.writeBeginningOfNamedChildClass(generatedClassName, dataRow.getJsFilename(), extendedClassName, Arrays.asList(implementedInterfacesNames));
}
} else {
if (clazz.isInterface()) { // extending an interface
classWriter.writeBeginningOfClassImplementingSingleInterface(generatedClassName, extendedClassName);
} else {
classWriter.writeBeginningOfChildClass(generatedClassName, extendedClassName, Arrays.asList(implementedInterfacesNames));
}
}
return classNameBuilder.toString();
}

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

boolean isInterface = clazz.isInterface();
if (isInterface) {
methodsWriter.writeDefaultConstructor(generatedClassName);
} else {
for (Method method : clazz.getMethods()) {
if (method.getName().equals("<init>") && (method.isPublic() || method.isProtected())) {
JavaMethod javaMethod = new JavaMethodImpl(method, clazz);
ReifiedJavaMethod reifiedJavaMethod = methodSignatureReifier.transformJavaMethod(javaMethod);
methodsWriter.writeConstructor(generatedClassName, reifiedJavaMethod, hasInitMethod2);
}
for (Method method : clazz.getMethods()) {
if (method.getName().equals("<init>") && (method.isPublic() || method.isProtected())) {
JavaMethod javaMethod = new JavaMethodImpl(method, clazz);
ReifiedJavaMethod reifiedJavaMethod = methodSignatureReifier.transformJavaMethod(javaMethod);
methodsWriter.writeConstructor(generatedClassName, reifiedJavaMethod, hasInitMethod2);
}
}
}

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

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

List<JavaClass> userImplementedInterfaces = getInterfacesFromCache(userImplementedInterfacesNames);
InheritedMethodsCollector inheritedMethodsCollector = new InheritedMethodsCollectorImpl.Builder()
.forJavaClass(clazz)
.withClassesCache(classes)
.withAdditionalImplementedInterfaces(userImplementedInterfaces)
.withGenericHierarchyView(genericHierarchyView)
.withInterfacesGenericHierarchyViews(interfaceGenericHierarchyViews)
.withPackageName(packageName)
.build();

Expand Down Expand Up @@ -427,10 +459,8 @@ private void writeMethodsToWriter(Writer writer, GenericHierarchyView genericHie
methodsWriter.writeGetInstanceMethod(normalizedClassName);
}

if (!isInterface) {
methodsWriter.writeInternalRuntimeHashCodeMethod();
methodsWriter.writeInternalRuntimeEqualsMethod();
}
methodsWriter.writeInternalRuntimeHashCodeMethod();
methodsWriter.writeInternalRuntimeEqualsMethod();
}

private boolean areAllArgumentsAndReturnTypePublic(ReifiedJavaMethod method) {
Expand Down Expand Up @@ -490,4 +520,4 @@ private JavaClass getClass(String className) {

return clazz;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package org.nativescript.staticbindinggenerator;

import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.zip.ZipEntry;

public class GetInterfaceNames {
private static final String CLASS_EXT = ".class";
private static String currentDir;

/*
Expand Down Expand Up @@ -45,37 +46,41 @@ public static void generateInterfaceFile(List<DataRow> rows)
out.close();
}

private static void generateInterfaceNames(String pathToJar, List<String> interfacesList) throws IOException, ClassNotFoundException {
private static void generateInterfaceNames(String pathToJar, List<String> interfacesList) {
if (pathToJar == null) {
return;
}

JarFile jarFile = new JarFile(pathToJar);
Enumeration<JarEntry> currentJarFile = jarFile.entries();

URLClassLoader cl = getClassLoader(pathToJar);

while (currentJarFile.hasMoreElements()) {
JarEntry jarEntry = currentJarFile.nextElement();

if ((!jarEntry.isDirectory()) && (jarEntry.getName().endsWith(".class"))) {
String className = jarEntry.getName().substring(0, jarEntry.getName().length() - 6);
className = className.replace('/', '.');

@SuppressWarnings("rawtypes")
Class c = null;
JarInputStream jis = null;
try {
String name;
jis = new JarInputStream(new FileInputStream(pathToJar));
for (ZipEntry ze = jis.getNextEntry(); ze != null; ze = jis.getNextEntry()) {
try {
c = cl.loadClass(className);
} catch (IllegalAccessError e) {
} catch (NoClassDefFoundError localNoClassDefFoundError) {
name = ze.getName();
if (name.endsWith(CLASS_EXT)) {
name = name.substring(0, name.length() - CLASS_EXT.length()).replace('/', '.').replace('$', '.');
ClassParser cp = new ClassParser(jis, name);
JavaClass clazz = cp.parse();
if (clazz.isInterface()) {
String res = clazz.getClassName().replace('$', '.');
interfacesList.add(res);
}
}
} catch (IOException e) {
throw new RuntimeException("Error while parsing class file!", e);
}
if ((c != null) && (c.isInterface() == true)) {
String res = c.getName().replace('$', '.');
interfacesList.add(res);
}
} catch (IOException ioe) {
throw new RuntimeException("Error while reading JAR entry!", ioe);
} finally {
if (jis != null) {
try {
jis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
jarFile.close();
}

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

return new PrintWriter(new BufferedWriter(new FileWriter(checkFile.getAbsolutePath(), true)));
}

private static URLClassLoader getClassLoader(String pathToJar) throws MalformedURLException {
URL[] urls = {new URL("jar:file:" + pathToJar + "!/")};
return URLClassLoader.newInstance(urls);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ public class InheritedMethodsCollectorImpl implements InheritedMethodsCollector
private final JavaMethodUtils javaMethodUtils;
private final JavaClassUtils javaClassUtils;
private final GenericHierarchyView genericHierarchyView;
private final Map<JavaClass, GenericHierarchyView> interfacesGenericHierarchyViews;
private final String packageName;
private final JavaClass javaClass;
private final List<JavaClass> implementedInterfaces;

private InheritedMethodsCollectorImpl(Builder builder) {
this.classesCache = builder.classesCache;
this.genericHierarchyView = builder.genericHierarchyView;
this.interfacesGenericHierarchyViews = builder.interfacesGenericHierarchyViews;
this.packageName = builder.packageName;
this.javaClass = builder.javaClass;
this.implementedInterfaces = builder.interfaces;
Expand Down Expand Up @@ -75,7 +77,7 @@ public InheritedMethodsView collect() {
HashSet<JavaClass> visited = new HashSet<>(); // saves some interface traversing

for (JavaClass interfaze : implementedInterfaces) {
GenericHierarchyView interfaceGenView = new GenericsAwareClassHierarchyParserImpl(new GenericSignatureReader(), classesCache).getClassHierarchy(interfaze);
GenericHierarchyView interfaceGenView = interfacesGenericHierarchyViews.get(interfaze);
findUnimplementedInterfaceMethods(interfaze, visited, allMethods, packageName, toImplement, overridable, interfaceGenView);
}

Expand Down Expand Up @@ -180,6 +182,7 @@ private JavaClass tryGetClassFromCache(String name) {
public static class Builder {
private Map<String, JavaClass> classesCache;
private GenericHierarchyView genericHierarchyView;
private Map<JavaClass, GenericHierarchyView> interfacesGenericHierarchyViews;
private JavaClass javaClass;
private List<JavaClass> interfaces;
private String packageName;
Expand All @@ -194,6 +197,11 @@ public Builder withGenericHierarchyView(GenericHierarchyView genericHierarchyVie
return this;
}

public Builder withInterfacesGenericHierarchyViews(Map<JavaClass, GenericHierarchyView> interfacesGenericHierarchyViews){
this.interfacesGenericHierarchyViews = interfacesGenericHierarchyViews;
return this;
}

public Builder forJavaClass(JavaClass javaClass) {
this.javaClass = javaClass;
return this;
Expand Down
Loading