It look like that there is a bug inside the AssignabilityChecker class of 
GWT 2.7.0.

If you use the method isAssignableFromRaw(JClassType from, JClassType to) 
to check weather something is assignable to the „to“ parameter or not, the 
method will always fail if the value of the parameter „to“ is 
„java.lang.object“. 

The first thing this method does is to check if the „to“-parameter is the 
java.lang.object type. Therefor the method calls the method 
„isJavaLangObject(JClassType type)“. This method compares parameter type 
with  type.getOracle().getJavaLangObject(). But 
type.getOracle().getJavaLangObject() does not return a raw type. To solve 
this problem, the value of type.getOracle().getJavaLangObject() must be 
converted to a raw type.

The method is used to times. Once it is called from 
isAssignableFromGenericArrayType(JarrayType from, JClassType to), where the 
method isJavaLangObject(to) will work correct The second call is the one 
from isAssignableFromRaw, where the compare will fail.

To solve this problem, I have created a another method:

*private static boolean isJavaLangObjectRawType(JClassType rawType) {*

*   return rawType == 
convertToRawIfGeneric(rawType.getOracle().getJvaLangObject());*

*}*

and call this method from isAssignableFromRaw. Using this patch, everything 
works correct.

To test this error, you can download the mvp4g framework (trunk) from

*https://code.google.com/p/mvp4g/source/checkout*

and run the method:

*testGeneratedClass*

from the class:

*com.mvp4g.rebind.config.loader.annotation.ServicesAnnotationsLoaderTest*

I have created a patch and assigned it. Using the patch, the test will 
behave in the same way as it does with GWT 2.6.1.

Frank

 

Enviorement: OS X 10.10.1, Java 1.7, IntelliJ Ultimate Editition 14.0.2

 

 

-- 
You received this message because you are subscribed to the Google Groups "GWT 
Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to google-web-toolkit-contributors+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/google-web-toolkit-contributors/38630bda-8dc3-4d08-bab8-e2e7e4566b73%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
/*
 * Copyright 2014 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.typemodel;

import com.google.gwt.core.ext.typeinfo.JType;

import java.util.Set;

/**
 * A helper class to check assignability of types.
 */
class AssignabilityChecker {

  public boolean isAssignable(JClassType from, JClassType to) {
    from = convertToRawIfGeneric(from);
    to = convertToRawIfGeneric(to);

    if (to == from) {
      return true;
    }

    if (to.isWildcard() != null) {
      return isAssignableToWildcardType(from, to.isWildcard());
    }

    if (from.isTypeParameter() != null) {
      return isAssignableFromAny(from.isTypeParameter().getBounds(), to);
    }

    if (from.isWildcard() != null) {
      return isAssignableFromAny(from.isWildcard().getUpperBounds(), to);
    }

    if (from.isArray() != null) {
      return isAssignableFromGenericArrayType(from.isArray(), to);
    }

    if (to.isParameterized() != null) {
      return isAssignableToParameterizedType(from, to.isParameterized());
    }

    if (to.isTypeParameter() != null) {
      // type inference is not supported (yet)
      return isAssignableFromAll(from, to.isTypeParameter().getBounds());
    }

    if (to.isArray() != null) {
      return false;
    }

    // Only remaining cases for 'to' are being real-class or raw type
    assert to instanceof JRealClassType || to instanceof JRawType;

    return isAssignableFromRaw(from, to);
  }

  private boolean isAssignableFromAny(JClassType[] fromTypes, JClassType to) {
    for (JClassType from : fromTypes) {
      if (isAssignable(from, to)) {
        return true;
      }
    }
    return false;
  }

  private boolean isAssignableToWildcardType(JClassType from, JWildcardType to) {
    // if "to" is <? extends Foo>, "from" can be:
    // Foo, SubFoo, <? extends Foo>, <? extends SubFoo>, <T extends Foo> or <T extends SubFoo>.
    // if "to" is <? super Foo>, "from" can be:
    // Foo, SuperFoo, <? super Foo> or <? super SuperFoo>.
    return isAssignable(from, supertypeBound(to)) && isAssignableBySubtypeBound(from, to);
  }

  private boolean isAssignableBySubtypeBound(JClassType from, JWildcardType to) {
    JClassType toSubtypeBound = subtypeBound(to);
    if (toSubtypeBound == null) {
      return true;
    }
    JClassType fromSubtypeBound = subtypeBound(from);
    if (fromSubtypeBound == null) {
      return false;
    }
    return isAssignable(toSubtypeBound, fromSubtypeBound);
  }

  private boolean isAssignableToParameterizedType(JClassType from, JParameterizedType to) {
    // If "to" is "List<? extends CharSequence>" and "from" is StringArrayList,
    // First step is to figure out StringArrayList "is-a" List<E> and <E> is String.
    JMaybeParameterizedType parentOfFrom = asParamterizationOf(from, to);
    if (parentOfFrom == null) {
      return false;
    }

    if (parentOfFrom.isRawType() != null) {
      return true;
    }

    // If it is not raw, then it should be parameterized
    JParameterizedType parameterizedParentOfFrom = parentOfFrom.isParameterized();
    assert parameterizedParentOfFrom != null;

    JClassType[] fromTypeArgs = parameterizedParentOfFrom.getTypeArgs();
    JClassType[] toTypeArgs = to.getTypeArgs();
    for (int i = 0; i < fromTypeArgs.length; i++) {
      if (!matchTypeArgument(fromTypeArgs[i], toTypeArgs[i])) {
        return false;
      }
    }
    return true;
  }

  private boolean matchTypeArgument(JClassType from, JClassType to) {
    if (from == to) {
      return true;
    }

    if (to.isWildcard() != null) {
      return isAssignableToWildcardType(from, to.isWildcard());
    }

    return false;
  }

  private boolean isAssignableFromAll(JClassType from, JClassType[] toTypes) {
    for (JClassType to : toTypes) {
      if (!isAssignable(from, to)) {
        return false;
      }
    }
    return true;
  }

  private boolean isAssignableFromGenericArrayType(JArrayType from, JClassType to) {
    if (to.isArray() != null) {

      JType fromComponentType = from.getComponentType();
      JType toComponentType = to.isArray().getComponentType();

      if (toComponentType.isPrimitive() != null || fromComponentType.isPrimitive() != null) {
        // Only scenario for this to be assignable is; this two being equal, but we wouldn't have
        // reached here in that case
        return false;
      }

      return isAssignable((JClassType) fromComponentType, (JClassType) toComponentType);
    }

    return isJavaLangObject(to);
  }

  private boolean isAssignableFromRaw(JClassType from, JClassType to) {
    // TODO hoss: patch start
    if (isJavaLangObjectRawType(to)) {
      // TODO hoss: patch end
      return true;
    }

    Set<JClassType> fromSuperTypeHierarchy = from.getFlattenedSupertypeHierarchy();

    // Shortcut: 'to' is one of the parents.
    if (fromSuperTypeHierarchy.contains(to)) {
      return true;
    }

    // Fallback to checking erased types if it is raw.
    if (to.isRawType() != null) {
      for (JClassType fromSuper : fromSuperTypeHierarchy) {
        if (fromSuper.getErasedType() == to.getErasedType()) {
          return true;
        }
      }
    }

    return false;
  }

  // TODO hoss: patch start
  private static boolean isJavaLangObjectRawType(JClassType rawType) {
     return rawType == convertToRawIfGeneric(rawType.getOracle().getJavaLangObject());
  }
  // TODO hoss: patch end

  private static boolean isJavaLangObject(JClassType type) {
    return type == type.getOracle().getJavaLangObject();
  }

  private static JClassType convertToRawIfGeneric(JClassType from) {
    return from.isGenericType() != null ? from.isGenericType().getRawType() : from;
  }

  private static JClassType supertypeBound(JWildcardType type) {
    // If type is <? super ? super Foo>, this will return Foo.
    // (Even if you cannot write such code in java, the type can resolve to that)
    JClassType upperBound = type.getUpperBound();
    return upperBound.isWildcard() != null ? supertypeBound(upperBound.isWildcard()) : upperBound;
  }

  private static JClassType subtypeBound(JWildcardType type) {
    // If type is <? extends ? extends Foo>, this will return Foo.
    // (Even if you cannot write such code in java, the type can resolve to that)
    JClassType[] lowerBounds = type.getLowerBounds();
    return lowerBounds.length == 1 ? subtypeBound(lowerBounds[0]) : null;
  }

  private static JClassType subtypeBound(JClassType type) {
    return type.isWildcard() != null ? subtypeBound(type.isWildcard()) : type;
  }

  private static JMaybeParameterizedType asParamterizationOf(JClassType from,
                                                             JParameterizedType to) {
    for (JClassType parent : from.getFlattenedSupertypeHierarchy()) {
      JMaybeParameterizedType maybeParameterized = parent.isMaybeParameterizedType();
      if (maybeParameterized != null && maybeParameterized.getBaseType() == to.getBaseType()) {
        return maybeParameterized;
      }
    }
    return null;
  }
}

Reply via email to