Skip to content

Add static and default interface methods support #1214

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 9 commits into from
Nov 26, 2018
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ bin/
.settings

.classpath
android-runtime.iml
5 changes: 5 additions & 0 deletions test-app/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ android {
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

sourceSets.main {
jniLibs.srcDir "$projectDir/libs/jni"
}
Expand Down
2 changes: 2 additions & 0 deletions test-app/app/src/main/assets/app/mainpage.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ shared.runRequireTests();
shared.runWeakRefTests();
shared.runRuntimeTests();
shared.runWorkerTests();
require("./tests/testInterfaceDefaultMethods");
require("./tests/testInterfaceStaticMethods");
require("./tests/testMetadata");
require("./tests/testAsserts");
require("./tests/testWeakRef");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
describe("Tests Java 8 default methods support", function () {

it("Test default method overrides in extending classes", function () {
var firstGen = new com.tns.tests.interfaces.defaultmethods.impl.FirstGeneration();
expect(firstGen.grow()).toBe("first generation grow");

var secondGeneration = new com.tns.tests.interfaces.defaultmethods.impl.SecondGeneration();
expect(secondGeneration.grow()).toBe("second generation grow");
})

it("Test default method overrides in interfaces chain", function () {
var producer = new com.tns.tests.interfaces.defaultmethods.impl.CarProducerImpl();
expect(producer.produce()).toBe("default produce in CarProducer");
})


})
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
describe("Tests Java 8 static methods support", function () {

it("Test static method in interface", function () {
var producer = com.tns.tests.interfaces.staticmethods.StaticProducer;
expect(producer.staticProduce()).toBe("static produce");
})


})
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.tns.tests.interfaces.defaultmethods.contract;

public interface CarProducer extends Producer{

@Override
default String produce(){
return "default produce in CarProducer";
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.tns.tests.interfaces.defaultmethods.contract;

public interface Generation{
default String grow(){
return "default grow";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.tns.tests.interfaces.defaultmethods.contract;

public interface Producer{

default String produce(){
return "default produce in Producer";
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.tns.tests.interfaces.defaultmethods.impl;

import com.tns.tests.interfaces.defaultmethods.contract.CarProducer;

public class CarProducerImpl implements CarProducer {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.tns.tests.interfaces.defaultmethods.impl;

import com.tns.tests.interfaces.defaultmethods.contract.Generation;

public class FirstGeneration implements Generation {

@Override
public String grow(){
return "first generation grow";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.tns.tests.interfaces.defaultmethods.impl;

public class SecondGeneration extends FirstGeneration{

@Override
public String grow(){
return "second generation grow";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.tns.tests.interfaces.staticmethods;

public interface StaticProducer {
static String staticProduce(){
return "static produce";
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package com.telerik.metadata;

import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.telerik.metadata.TreeNode.FieldInfo;
import com.telerik.metadata.TreeNode.MethodInfo;
Expand All @@ -17,6 +23,7 @@
import com.telerik.metadata.desc.MethodDescriptor;
import com.telerik.metadata.desc.TypeDescriptor;
import com.telerik.metadata.dx.DexFile;
import com.telerik.metadata.parsing.ClassParser;

public class Builder {
private static class MethodNameComparator implements Comparator<MethodDescriptor> {
Expand Down Expand Up @@ -124,7 +131,9 @@ private static void setNodeMembers(ClassDescriptor clazz, TreeNode node, TreeNod
}

MethodDescriptor[] allMethods = ClassUtil.getAllMethods(clazz);
MethodDescriptor[] methods = clazz.getMethods();
MethodDescriptor[] classImplementedMethods = clazz.getMethods();
MethodDescriptor[] interfaceImplementedMethods = clazz.isClass() ? getDefaultMethodsFromImplementedInterfaces(clazz, classImplementedMethods) : new MethodDescriptor[0];
MethodDescriptor[] methods = concatenate(classImplementedMethods, interfaceImplementedMethods);

Arrays.sort(methods, methodNameComparator);

Expand All @@ -151,8 +160,8 @@ private static void setNodeMembers(ClassDescriptor clazz, TreeNode node, TreeNod
&& (m1.isPublic() || m1.isProtected())
&& (isStatic == m1IsStatic)
&& (m1.getName().equals(mi.name) && (m1
.getArgumentTypes().length == m
.getArgumentTypes().length))) {
.getArgumentTypes().length == m
.getArgumentTypes().length))) {
if (++countUnique > 1) {
break;
}
Expand All @@ -162,7 +171,7 @@ private static void setNodeMembers(ClassDescriptor clazz, TreeNode node, TreeNod

TypeDescriptor[] params = m.getArgumentTypes();
mi.signature = getMethodSignature(root, m.getReturnType(),
params);
params);

if (mi.signature != null) {
if (isStatic) {
Expand Down Expand Up @@ -195,7 +204,7 @@ private static void setFieldInfo(ClassDescriptor clazz, TreeNode node, TreeNode
TypeDescriptor t = f.getType();
boolean isPrimitive = ClassUtil.isPrimitive(t);

fi.valueType = isPrimitive ? TreeNode.getPrimitive(t): getOrCreateNode(root, t);
fi.valueType = isPrimitive ? TreeNode.getPrimitive(t) : getOrCreateNode(root, t);
fi.isFinalType = f.isFinal();

if (f.isStatic()) {
Expand Down Expand Up @@ -242,8 +251,19 @@ private static void getFieldsFromImplementedInterfaces(ClassDescriptor clazz, Tr
}
}

private static MethodDescriptor[] getDefaultMethodsFromImplementedInterfaces(ClassDescriptor clazz, MethodDescriptor[] originalClassMethodDescriptors) {
ClassParser parser = ClassParser.forClassDescriptor(clazz);

Set<MethodDescriptor> defaultMethods = parser.getAllDefaultMethodsFromImplementedInterfaces();
Set<MethodDescriptor> classMethods = new HashSet<MethodDescriptor>(Arrays.asList(originalClassMethodDescriptors));

defaultMethods.removeAll(classMethods);

return defaultMethods.toArray(new MethodDescriptor[0]);
}

private static TreeNode getOrCreateNode(TreeNode root, TypeDescriptor type)
throws Exception {
throws Exception {
TreeNode node;

String typeName = type.getSignature();
Expand Down Expand Up @@ -272,7 +292,7 @@ private static TreeNode getOrCreateNode(TreeNode root, ClassDescriptor clazz, St

if (ClassUtil.isArray(clazz)) {
throw new UnsupportedOperationException("unexpected class="
+ clazz.getClassName());
+ clazz.getClassName());
}

TreeNode node = root;
Expand Down Expand Up @@ -307,7 +327,7 @@ private static TreeNode getOrCreateNode(TreeNode root, ClassDescriptor clazz, St
if (child == null) {
child = node.createChild(outerClassname);
child.nodeType = outer.isInterface() ? TreeNode.Interface
: TreeNode.Class;
: TreeNode.Class;
if (outer.isStatic()) {
child.nodeType |= TreeNode.Static;
}
Expand All @@ -324,7 +344,7 @@ private static TreeNode getOrCreateNode(TreeNode root, ClassDescriptor clazz, St
child.nodeType = tmp.nodeType;
} else {
child.nodeType = clazz.isInterface() ? TreeNode.Interface
: TreeNode.Class;
: TreeNode.Class;
if (clazz.isStatic()) {
child.nodeType |= TreeNode.Static;
}
Expand All @@ -337,8 +357,8 @@ private static TreeNode getOrCreateNode(TreeNode root, ClassDescriptor clazz, St
baseClass = ClassUtil.getClassByName(predefinedSuperClassname);
} else {
baseClass = clazz.isInterface()
? ClassUtil.getClassByName("java.lang.Object")
: ClassUtil.getSuperclass(clazz);
? ClassUtil.getClassByName("java.lang.Object")
: ClassUtil.getSuperclass(clazz);
}
if (baseClass != null) {
node.baseClassNode = getOrCreateNode(root, baseClass, null);
Expand All @@ -358,7 +378,7 @@ private static void copyBasePublicApi(ClassDescriptor baseClass, TreeNode node,
}

private static TreeNode createArrayNode(TreeNode root, String className)
throws Exception {
throws Exception {
TreeNode currentNode = root;
String currentClassname = className;

Expand All @@ -385,7 +405,7 @@ private static TreeNode createArrayNode(TreeNode root, String className)
} else {
ClassDescriptor clazz = ClassRepo.findClass(name);
child.nodeType = clazz.isInterface() ? TreeNode.Interface
: TreeNode.Class;
: TreeNode.Class;
if (clazz.isStatic()) {
child.nodeType |= TreeNode.Static;
}
Expand All @@ -397,22 +417,22 @@ private static TreeNode createArrayNode(TreeNode root, String className)
}

private static ArrayList<TreeNode> getMethodSignature(TreeNode root,
TypeDescriptor retType, TypeDescriptor[] params) throws Exception {
TypeDescriptor retType, TypeDescriptor[] params) throws Exception {
ArrayList<TreeNode> sig = new ArrayList<TreeNode>();
boolean isVoid = retType.equals(TypeDescriptor.VOID);

TreeNode node = null;
if (!isVoid) {
boolean isPrimitive = ClassUtil.isPrimitive(retType);
node = isPrimitive ? TreeNode.getPrimitive(retType)
: getOrCreateNode(root, retType);
: getOrCreateNode(root, retType);
}
sig.add(node);

for (TypeDescriptor param : params) {
boolean isPrimitive = ClassUtil.isPrimitive(param);
node = isPrimitive ? TreeNode.getPrimitive(param)
: getOrCreateNode(root, param);
: getOrCreateNode(root, param);
if (node == null) {
return null;
}
Expand All @@ -421,4 +441,16 @@ private static ArrayList<TreeNode> getMethodSignature(TreeNode root,

return sig;
}

private static <T> T[] concatenate(T[] a, T[] b) {
int aLen = a.length;
int bLen = b.length;

@SuppressWarnings("unchecked")
T[] c = (T[]) Array.newInstance(a.getClass().getComponentType(), aLen + bLen);
System.arraycopy(a, 0, c, 0, aLen);
System.arraycopy(b, 0, c, aLen, bLen);

return c;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public boolean isStatic() {
return m.isStatic();
}

@Override
public boolean isAbstract() {
return m.isAbstract();
}

@Override
public String getName() {
return m.getName();
Expand Down Expand Up @@ -62,4 +67,19 @@ public TypeDescriptor getReturnType() {
public MetadataInfoAnnotationDescriptor getMetadataInfoAnnotation() {
return null;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

MethodInfo that = (MethodInfo) o;

return m.equals(that.m);
}

@Override
public int hashCode() {
return m.hashCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public interface MethodDescriptor {
boolean isProtected();
boolean isSynthetic();
boolean isStatic();
boolean isAbstract();

String getName();
String getSignature();
Expand Down
Loading