Please can you prove that it is a bug or am I doing something wrong? I make 
an update to the AssignabilityChecker (this time with less code changes). 
If it is a bug, the changes should solve it.

Am Samstag, 17. Januar 2015 14:34:36 UTC+1 schrieb Frank Hossfeld:
>
> 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 
> <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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/google-web-toolkit-contributors/3895a392-dd05-4450-813e-eceaf447adea%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) {
    if (isJavaLangObject(to)) {
      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 isJavaLangObject(JClassType type) {
    if (type.isRawType() != null) {
      return type == convertToRawIfGeneric(type.getOracle().getJavaLangObject());
    } else {
      return type == type.getOracle().getJavaLangObject();
    }
  }
  // TODO hoss: patch end

  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