com.google.gwt.dev.javac.asm.ResolveGenericsTest.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gwt.dev.javac.asm.ResolveGenericsTest.java

Source

/*
 * 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();
    }
}