blob: 6f131ac11699ecd372f74d3f822d265629cab61a [file] [log] [blame]
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.javac.asm;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.dev.asm.Opcodes;
import com.google.gwt.dev.asm.Type;
import com.google.gwt.dev.asm.signature.SignatureReader;
import com.google.gwt.dev.javac.MethodArgNamesLookup;
import com.google.gwt.dev.javac.Resolver;
import com.google.gwt.dev.javac.TypeOracleMediator;
import com.google.gwt.dev.javac.TypeOracleTestingUtils;
import com.google.gwt.dev.javac.TypeParameterLookup;
import com.google.gwt.dev.javac.asm.CollectClassData.ClassType;
import com.google.gwt.dev.javac.typemodel.JAbstractMethod;
import com.google.gwt.dev.javac.typemodel.JClassType;
import com.google.gwt.dev.javac.typemodel.JGenericType;
import com.google.gwt.dev.javac.typemodel.JMethod;
import com.google.gwt.dev.javac.typemodel.JPackage;
import com.google.gwt.dev.javac.typemodel.JRealClassType;
import com.google.gwt.dev.javac.typemodel.JTypeParameter;
import com.google.gwt.dev.javac.typemodel.TypeOracle;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.TypeVariable;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
/**
* Tests for {@link ResolveClassSignature} and {@link ResolveMethodSignature}.
*/
public class ResolveGenericsTest extends AsmTestCase {
public static class FailErrorTreeLogger extends TreeLogger {
@Override
public TreeLogger branch(com.google.gwt.core.ext.TreeLogger.Type type,
String msg, Throwable caught, HelpInfo helpInfo) {
if (type == TreeLogger.ERROR) {
fail(msg);
}
return this;
}
@Override
public boolean isLoggable(com.google.gwt.core.ext.TreeLogger.Type type) {
return true;
}
@Override
public void log(com.google.gwt.core.ext.TreeLogger.Type type, String msg,
Throwable caught, HelpInfo helpInfo) {
if (type == TreeLogger.ERROR) {
fail(msg);
}
}
}
private class MockResolver implements Resolver {
private final Resolver delegate;
public MockResolver(Resolver resolver) {
this.delegate = resolver;
}
public void addImplementedInterface(JRealClassType type, JClassType intf) {
delegate.addImplementedInterface(type, intf);
}
public void addThrows(JAbstractMethod method, JClassType exception) {
delegate.addThrows(method, exception);
}
public Map<String, JRealClassType> getBinaryMapper() {
return delegate.getBinaryMapper();
}
public TypeOracle getTypeOracle() {
return delegate.getTypeOracle();
}
public JMethod newMethod(JClassType type, String name,
Map<Class<? extends Annotation>, Annotation> declaredAnnotations,
JTypeParameter[] typeParams) {
return delegate.newMethod(type, name, declaredAnnotations, typeParams);
}
public void newParameter(JAbstractMethod method, JType argType,
String argName,
Map<Class<? extends Annotation>, Annotation> declaredAnnotations,
boolean argNamesAreReal) {
delegate.newParameter(method, argType, argName, declaredAnnotations,
argNamesAreReal);
}
public JRealClassType newRealClassType(JPackage pkg,
String enclosingTypeName, boolean isLocalType, String className,
boolean isIntf) {
return delegate.newRealClassType(pkg, enclosingTypeName, isLocalType,
className, isIntf);
}
public boolean resolveAnnotation(TreeLogger logger,
CollectAnnotationData annotVisitor,
Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
return true;
}
public boolean resolveAnnotations(TreeLogger logger,
List<CollectAnnotationData> annotations,
Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
return true;
}
public boolean resolveClass(TreeLogger logger, JRealClassType type) {
return true;
}
public void setReturnType(JAbstractMethod method, JType returnType) {
delegate.setReturnType(method, returnType);
}
public void setSuperClass(JRealClassType type, JClassType superType) {
delegate.setSuperClass(type, superType);
}
}
private static final TreeLogger failTreeLogger = new FailErrorTreeLogger();
private static final String OUTER_CLASS_SIG = "<H:Lcom/google/gwt/dev/javac/asm/TestHandler;>Ljava/lang/Object;";
private static final String OUTER_METHOD_SIG = "(TH;)V";
private static final String OUTER1_CLASS_SIG = "<V:Ljava/lang/Object;>Lcom/google/gwt/dev/javac/asm/TestOuter0<"
+ "Lcom/google/gwt/dev/javac/asm/TestHandler1<TV;>;>;";
private static final String OUTER1_METHOD_SIG = "(Lcom/google/gwt/dev/javac/asm/TestHandler1<TV;>;)V";
private static final String OUTER2_CLASS_SIG = "Lcom/google/gwt/dev/javac/asm/TestOuter1<Ljava/lang/String;>;";
private static final String OUTER2_METHOD_SIG = "(Lcom/google/gwt/dev/javac/asm/TestHandler1<Ljava/lang/String;>;)V";
private final TypeOracleMediator mediator;
private final TypeOracle oracle;
private final Map<JMethod, Method> reflectionMethods = new IdentityHashMap<JMethod, Method>();
private final MockResolver resolver;
@SuppressWarnings("unused")
private final JRealClassType testHandler;
@SuppressWarnings("unused")
private final JRealClassType testHandler1;
private final JRealClassType testOuter0;
private final JMethod testOuter0dispatch;
private final JRealClassType testOuter1;
private final JMethod testOuter1dispatch;
private final JRealClassType testOuter2;
private final JMethod testOuter2dispatch;
@SuppressWarnings("unused")
private final JRealClassType testType;
public ResolveGenericsTest() {
mediator = TypeOracleTestingUtils.buildStandardMediatorWith(failTreeLogger);
resolver = new MockResolver(mediator.getMockResolver());
oracle = mediator.getTypeOracle();
createUnresolvedClass(String.class, null);
testHandler = createUnresolvedClass(TestHandler.class, null);
testHandler1 = createUnresolvedClass(TestHandler1.class, null);
testOuter0 = createUnresolvedClass(TestOuter0.class, null);
testType = createUnresolvedClass(TestOuter0.Type.class, testOuter0);
testOuter1 = createUnresolvedClass(TestOuter1.class, null);
testOuter2 = createUnresolvedClass(TestOuter2.class, null);
testOuter0dispatch = createUnresolvedMethod(testOuter0, TestOuter0.class,
"dispatch", TestHandler.class);
testOuter1dispatch = createUnresolvedMethod(testOuter1, TestOuter1.class,
"dispatch", TestHandler.class);
testOuter2dispatch = createUnresolvedMethod(testOuter2, TestOuter2.class,
"dispatch", TestHandler.class);
for (JClassType type : oracle.getTypes()) {
if (type instanceof JRealClassType) {
mediator.getBinaryMapper().put(
type.getQualifiedBinaryName().replace('.', '/'),
(JRealClassType) type);
}
}
}
public void testOuter0Class() {
resolveClassSignature(testOuter0, OUTER_CLASS_SIG);
assertNotNull(testOuter0.getSuperclass());
// TODO(jat): additional checks?
}
public void testOuter0Method() {
resolveMethodSignature(testOuter0dispatch, OUTER_METHOD_SIG);
// TODO(jat): meaningful tests besides no errors?
}
public void testOuter1Class() {
resolveClassSignature(testOuter1, OUTER1_CLASS_SIG);
JClassType superClass = testOuter1.getSuperclass();
assertNotNull(superClass);
assertNotNull(superClass.isParameterized());
// TODO(jat): additional checks?
}
public void testOuter1Method() {
resolveMethodSignature(testOuter1dispatch, OUTER1_METHOD_SIG);
// TODO(jat): meaningful tests besides no errors?
}
public void testOuter2Class() {
resolveClassSignature(testOuter2, OUTER2_CLASS_SIG);
JClassType superClass = testOuter2.getSuperclass();
assertNotNull(superClass);
assertNotNull(superClass.isParameterized());
// TODO(jat): additional checks?
}
public void testOuter2Method() {
resolveMethodSignature(testOuter2dispatch, OUTER2_METHOD_SIG);
// TODO(jat): meaningful tests besides no errors?
}
private JTypeParameter[] createTypeParams(TypeVariable<?>[] typeParams) {
int n = typeParams.length;
JTypeParameter[] params = new JTypeParameter[n];
for (int i = 0; i < n; ++i) {
params[i] = new JTypeParameter(typeParams[i].getName(), i);
}
return params;
}
private JRealClassType createUnresolvedClass(Class<?> clazz,
JRealClassType enclosingType) {
String pkgName = clazz.getPackage().getName();
JPackage pkg = oracle.getOrCreatePackage(pkgName);
TypeVariable<?>[] typeParams = clazz.getTypeParameters();
JRealClassType type;
int n = typeParams.length;
String enclosingTypeName = null;
if (enclosingType != null) {
enclosingTypeName = enclosingType.getName();
}
if (n == 0) {
type = resolver.newRealClassType(pkg, enclosingTypeName, false,
clazz.getSimpleName(), clazz.isInterface());
} else {
JTypeParameter[] params = createTypeParams(typeParams);
type = new JGenericType(oracle, pkg, enclosingTypeName,
clazz.getSimpleName(), clazz.isInterface(), params);
}
return type;
}
private JMethod createUnresolvedMethod(JClassType type, Class<?> clazz,
String methodName, Class<?>... paramTypes) {
Method method = null;
try {
method = clazz.getMethod(methodName, paramTypes);
} catch (SecurityException e) {
fail("Exception " + e + " creating method " + methodName + " on " + clazz);
} catch (NoSuchMethodException e) {
fail("Exception " + e + " creating method " + methodName + " on " + clazz);
}
JTypeParameter[] typeParams = createTypeParams(method.getTypeParameters());
Map<Class<? extends Annotation>, Annotation> emptyMap = Collections.emptyMap();
JMethod result = resolver.newMethod(type, methodName, emptyMap, typeParams);
reflectionMethods.put(result, method);
return result;
}
private void resolveClassSignature(JRealClassType type, String signature) {
Map<String, JRealClassType> binaryMapper = resolver.getBinaryMapper();
TypeParameterLookup lookup = new TypeParameterLookup();
lookup.pushEnclosingScopes(type);
ResolveClassSignature classResolver = new ResolveClassSignature(resolver,
binaryMapper, failTreeLogger, type, lookup);
new SignatureReader(signature).accept(classResolver);
classResolver.finish();
}
private void resolveMethodSignature(JMethod method, String signature) {
TypeParameterLookup lookup = new TypeParameterLookup();
lookup.pushEnclosingScopes(method.getEnclosingType());
lookup.pushScope(method.getTypeParameters());
int access = Opcodes.ACC_PUBLIC;
Method reflectionMethod = reflectionMethods.get(method);
String desc = Type.getMethodDescriptor(reflectionMethod);
CollectMethodData methodData = new CollectMethodData(ClassType.TopLevel,
access, method.getName(), desc, signature, null);
Class<?>[] paramTypes = reflectionMethod.getParameterTypes();
int n = paramTypes.length;
Type[] argTypes = new Type[n];
String[] argNames = new String[n];
for (int i = 0; i < n; ++i) {
argNames[i] = "arg" + i;
argTypes[i] = Type.getType(paramTypes[i]);
}
ResolveMethodSignature methodResolver = new ResolveMethodSignature(
resolver, failTreeLogger, method, lookup, true, methodData, argTypes,
argNames, false, new MethodArgNamesLookup());
new SignatureReader(signature).accept(methodResolver);
methodResolver.finish();
}
}