| /* |
| * 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.javac.CompilationUnitTypeOracleUpdater; |
| import com.google.gwt.dev.javac.MethodArgNamesLookup; |
| import com.google.gwt.dev.javac.Resolver; |
| 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 com.google.gwt.dev.util.Name.BinaryName; |
| |
| import org.objectweb.asm.Opcodes; |
| import org.objectweb.asm.Type; |
| import org.objectweb.asm.signature.SignatureReader; |
| |
| 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 { |
| |
| /** |
| * A logger that fails the tests if an error is logged. |
| */ |
| 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; |
| } |
| |
| @Override |
| public void addImplementedInterface(JRealClassType type, JClassType intf) { |
| delegate.addImplementedInterface(type, intf); |
| } |
| |
| @Override |
| public void addThrows(JAbstractMethod method, JClassType exception) { |
| delegate.addThrows(method, exception); |
| } |
| |
| @Override |
| public TypeOracle getTypeOracle() { |
| return delegate.getTypeOracle(); |
| } |
| |
| @Override |
| public JMethod newMethod(JClassType type, String name, |
| Map<Class<? extends Annotation>, Annotation> declaredAnnotations, |
| JTypeParameter[] typeParams) { |
| return delegate.newMethod(type, name, declaredAnnotations, typeParams); |
| } |
| |
| @Override |
| public void newParameter(JAbstractMethod method, JType argType, |
| String argName, |
| Map<Class<? extends Annotation>, Annotation> declaredAnnotations, |
| boolean argNamesAreReal) { |
| delegate.newParameter(method, argType, argName, declaredAnnotations, |
| argNamesAreReal); |
| } |
| |
| @Override |
| public JRealClassType newRealClassType(JPackage pkg, |
| String enclosingTypeName, boolean isLocalType, String className, |
| boolean isIntf) { |
| return delegate.newRealClassType(pkg, enclosingTypeName, isLocalType, |
| className, isIntf); |
| } |
| |
| @Override |
| public boolean resolveAnnotations(TreeLogger logger, |
| List<CollectAnnotationData> annotations, |
| Map<Class<? extends Annotation>, Annotation> declaredAnnotations) { |
| return true; |
| } |
| |
| @Override |
| public boolean resolveClass(TreeLogger logger, JRealClassType type) { |
| return true; |
| } |
| |
| @Override |
| public void setReturnType(JAbstractMethod method, JType returnType) { |
| delegate.setReturnType(method, returnType); |
| } |
| |
| @Override |
| public void setSuperClass(JRealClassType type, JClassType superType) { |
| delegate.setSuperClass(type, superType); |
| } |
| |
| @Override |
| public JRealClassType findByInternalName(String typeInternalName) { |
| return delegate.findByInternalName(typeInternalName); |
| } |
| } |
| |
| 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 CompilationUnitTypeOracleUpdater typeOracleUpdater; |
| |
| 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() { |
| typeOracleUpdater = |
| TypeOracleTestingUtils.buildStandardUpdaterWith(failTreeLogger); |
| resolver = new MockResolver(typeOracleUpdater.getMockResolver()); |
| oracle = typeOracleUpdater.getTypeOracle(); |
| 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) { |
| typeOracleUpdater.getTypesByInternalName().put( |
| BinaryName.toInternalName(type.getQualifiedBinaryName()), (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) { |
| TypeParameterLookup lookup = new TypeParameterLookup(); |
| lookup.pushEnclosingScopes(type); |
| ResolveClassSignature classResolver = |
| new ResolveClassSignature(resolver, 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(); |
| } |
| } |