@Nullable public <T extends GroovyPsiElement> PsiType getType(@NotNull T element, @NotNull Function<T, PsiType> calculator) { PsiType type = myCalculatedTypes.get(element); if (type == null) { RecursionGuard.StackStamp stamp = ourGuard.markStack(); type = calculator.fun(element); if (type == null) { type = UNKNOWN_TYPE; } if (stamp.mayCacheNow()) { type = ConcurrencyUtil.cacheOrGet(myCalculatedTypes, element, type); } else { final PsiType alreadyInferred = myCalculatedTypes.get(element); if (alreadyInferred != null) { type = alreadyInferred; } } } if (!type.isValid()) { error(element, type); } return UNKNOWN_TYPE == type ? null : type; }
@NotNull public PsiMethod[] getConstructors() { PsiMethod[] cached = myConstructors; if (cached == null) { RecursionGuard.StackStamp stamp = ourGuard.markStack(); List<PsiMethod> result = new ArrayList<PsiMethod>(); for (final PsiMethod method : getMethods()) { if (method.isConstructor()) { addExpandingReflectedMethods(result, method); } } cached = result.toArray(new PsiMethod[result.size()]); if (stamp.mayCacheNow()) { myConstructors = cached; } } return cached; }
@NotNull public GrMethod[] getCodeConstructors() { GrMethod[] cached = myCodeConstructors; if (cached == null) { RecursionGuard.StackStamp stamp = ourGuard.markStack(); List<GrMethod> result = new ArrayList<GrMethod>(); for (final GrMethod method : getCodeMethods()) { if (method.isConstructor()) { result.add(method); } } cached = result.toArray(new GrMethod[result.size()]); if (stamp.mayCacheNow()) { myCodeConstructors = cached; } } return cached; }
@Nullable public <T extends GroovyPsiElement> PsiType getType(@NotNull T element, @NotNull Function<T, PsiType> calculator) { PsiType type = myCalculatedTypes.get(element); if (type == null) { RecursionGuard.StackStamp stamp = ourGuard.markStack(); type = calculator.fun(element); if (type == null) { type = UNKNOWN_TYPE; } if (stamp.mayCacheNow()) { type = ConcurrencyUtil.cacheOrGet(myCalculatedTypes, element, type); } else { final PsiType alreadyInferred = myCalculatedTypes.get(element); if (alreadyInferred != null) { type = alreadyInferred; } } } if (!type.isValid()) { LOG.error("Type is invalid: " + type + "; element: " + element + " of class " + element.getClass()); } return UNKNOWN_TYPE == type ? null : type; }
@Nullable static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass derivedClass, @NotNull GlobalSearchScope scope, @NotNull PsiClass superClass) { ScopedClassHierarchy hierarchy = getHierarchy(derivedClass, scope); Map<PsiClass, PsiClassType.ClassResolveResult> map = hierarchy.mySupersWithSubstitutors; if(map == null) { map = ContainerUtil.newTroveMap(CLASS_HASHING_STRATEGY); RecursionGuard.StackStamp stamp = ourGuard.markStack(); hierarchy.visitType(JavaPsiFacade.getElementFactory(derivedClass.getProject()).createType(derivedClass, PsiSubstitutor.EMPTY), map); if(stamp.mayCacheNow()) { hierarchy.mySupersWithSubstitutors = map; } } PsiClassType.ClassResolveResult resolveResult = map.get(superClass); if(resolveResult == null) { return null; } PsiClass cachedClass = assertNotNull(resolveResult.getElement()); PsiSubstitutor cachedSubstitutor = resolveResult.getSubstitutor(); return cachedClass == superClass ? cachedSubstitutor : mirrorSubstitutor(superClass, cachedClass, cachedSubstitutor); }
@NotNull List<PsiClassType.ClassResolveResult> getImmediateSupersWithCapturing() { List<PsiClassType.ClassResolveResult> list = myImmediateSupersWithCapturing; if(list == null) { RecursionGuard.StackStamp stamp = ourGuard.markStack(); list = ourGuard.doPreventingRecursion(this, true, new Computable<List<PsiClassType.ClassResolveResult>>() { @Override public List<PsiClassType.ClassResolveResult> compute() { return calcImmediateSupersWithCapturing(); } }); if(list == null) { return Collections.emptyList(); } if(stamp.mayCacheNow()) { myImmediateSupersWithCapturing = list; } } return list; }
@Nullable public <T extends PsiExpression> PsiType getType(@NotNull T expr, @NotNull Function<T, PsiType> f) { final boolean isOverloadCheck = MethodCandidateInfo.isOverloadCheck(); PsiType type = isOverloadCheck ? null : myCalculatedTypes.get(expr); if (type == null) { final RecursionGuard.StackStamp dStackStamp = PsiDiamondType.ourDiamondGuard.markStack(); type = f.fun(expr); if (!dStackStamp.mayCacheNow() || isOverloadCheck) { return type; } if (type == null) type = TypeConversionUtil.NULL_TYPE; myCalculatedTypes.put(expr, type); if (type instanceof PsiClassReferenceType) { // convert reference-based class type to the PsiImmediateClassType, since the reference may become invalid PsiClassType.ClassResolveResult result = ((PsiClassReferenceType)type).resolveGenerics(); PsiClass psiClass = result.getElement(); type = psiClass == null ? type // for type with unresolved reference, leave it in the cache // for clients still might be able to retrieve its getCanonicalText() from the reference text : new PsiImmediateClassType(psiClass, result.getSubstitutor(), ((PsiClassReferenceType)type).getLanguageLevel(), type.getAnnotations()); } } if (!type.isValid()) { if (expr.isValid()) { PsiJavaCodeReferenceElement refInside = type instanceof PsiClassReferenceType ? ((PsiClassReferenceType)type).getReference() : null; @NonNls String typeinfo = type + " (" + type.getClass() + ")" + (refInside == null ? "" : "; ref inside: "+refInside + " ("+refInside.getClass()+") valid:"+refInside.isValid()); LOG.error("Type is invalid: " + typeinfo + "; expr: '" + expr + "' (" + expr.getClass() + ") is valid"); } else { LOG.error("Expression: '"+expr+"' is invalid, must not be used for getType()"); } } return type == TypeConversionUtil.NULL_TYPE ? null : type; }
private Pair<PsiType, ConstraintType> inferTypeForMethodTypeParameterInner(@NotNull PsiTypeParameter typeParameter, @NotNull PsiParameter[] parameters, @NotNull PsiExpression[] arguments, @NotNull PsiSubstitutor partialSubstitutor, final PsiElement parent, @NotNull ParameterTypeInferencePolicy policy) { PsiType[] paramTypes = PsiType.createArray(arguments.length); PsiType[] argTypes = PsiType.createArray(arguments.length); if (parameters.length > 0) { for (int j = 0; j < argTypes.length; j++) { final PsiExpression argument = arguments[j]; if (argument == null) continue; if (argument instanceof PsiMethodCallExpression && PsiResolveHelper.ourGuard.currentStack().contains(argument)) continue; final RecursionGuard.StackStamp stackStamp = PsiDiamondType.ourDiamondGuard.markStack(); argTypes[j] = argument.getType(); if (!stackStamp.mayCacheNow()) { argTypes[j] = null; continue; } final PsiParameter parameter = parameters[Math.min(j, parameters.length - 1)]; if (j >= parameters.length && !parameter.isVarArgs()) break; paramTypes[j] = parameter.getType(); if (paramTypes[j] instanceof PsiEllipsisType) { paramTypes[j] = ((PsiEllipsisType)paramTypes[j]).getComponentType(); if (arguments.length == parameters.length && argTypes[j] instanceof PsiArrayType && !(((PsiArrayType)argTypes[j]).getComponentType() instanceof PsiPrimitiveType)) { argTypes[j] = ((PsiArrayType)argTypes[j]).getComponentType(); } } } } return inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, argTypes, partialSubstitutor, parent, policy); }
@NotNull public PsiSubstitutor getSubstitutor(boolean includeReturnConstraint) { PsiSubstitutor substitutor = myCalcedSubstitutor; if (substitutor == null || !includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8) || isOverloadCheck()) { PsiSubstitutor incompleteSubstitutor = super.getSubstitutor(); PsiMethod method = getElement(); if (myTypeArguments == null) { final RecursionGuard.StackStamp stackStamp = PsiDiamondType.ourDiamondGuard.markStack(); final PsiSubstitutor inferredSubstitutor = inferTypeArguments(DefaultParameterTypeInferencePolicy.INSTANCE, includeReturnConstraint); if (!stackStamp.mayCacheNow() || isOverloadCheck() || !includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8) || getMarkerList() != null && PsiResolveHelper.ourGraphGuard.currentStack().contains(getMarkerList().getParent())) { return inferredSubstitutor; } myCalcedSubstitutor = substitutor = inferredSubstitutor; } else { PsiTypeParameter[] typeParams = method.getTypeParameters(); for (int i = 0; i < myTypeArguments.length && i < typeParams.length; i++) { incompleteSubstitutor = incompleteSubstitutor.put(typeParams[i], myTypeArguments[i]); } myCalcedSubstitutor = substitutor = incompleteSubstitutor; } } return substitutor; }
@Override public V get(Object key) { final Map<K, V> map = getMap(); V value = map.get(getKey(key)); if (value == null) { RecursionGuard.StackStamp stamp = ourGuard.markStack(); value = create((K)key); if (stamp.mayCacheNow()) { map.put((K)getKey(key), value == null ? (V)NULL : value); } } return value == NULL ? null : value; }
@Override @Nullable public <T extends SemElement> List<T> getSemElements(final SemKey<T> key, @NotNull final PsiElement psi) { List<T> cached = _getCachedSemElements(key, true, psi); if (cached != null) { return cached; } ensureInitialized(); RecursionGuard.StackStamp stamp = RecursionManager.createGuard("semService").markStack(); LinkedHashSet<T> result = new LinkedHashSet<T>(); final Map<SemKey, List<SemElement>> map = new THashMap<SemKey, List<SemElement>>(); for (final SemKey each : key.getInheritors()) { List<SemElement> list = createSemElements(each, psi); map.put(each, list); result.addAll((List<T>)list); } if (stamp.mayCacheNow()) { final SemCacheChunk persistent = getOrCreateChunk(psi); for (SemKey semKey : map.keySet()) { persistent.putSemElements(semKey, map.get(semKey)); } } return new ArrayList<T>(result); }
@Nullable public <T extends PsiExpression> PsiType getType(@NotNull T expr, @NotNull Function<T, PsiType> f) { PsiType type = getCachedType(expr); if (type == null) { final RecursionGuard.StackStamp dStackStamp = PsiDiamondType.ourDiamondGuard.markStack(); final RecursionGuard.StackStamp gStackStamp = PsiResolveHelper.ourGraphGuard.markStack(); type = f.fun(expr); if (!dStackStamp.mayCacheNow() || !gStackStamp.mayCacheNow()) { return type; } if (type == null) type = TypeConversionUtil.NULL_TYPE; Reference<PsiType> ref = new SoftReference<PsiType>(type); myCalculatedTypes.put(expr, ref); if (type instanceof PsiClassReferenceType) { // convert reference-based class type to the PsiImmediateClassType, since the reference may become invalid PsiClassType.ClassResolveResult result = ((PsiClassReferenceType)type).resolveGenerics(); PsiClass psiClass = result.getElement(); type = psiClass == null ? type // for type with unresolved reference, leave it in the cache // for clients still might be able to retrieve its getCanonicalText() from the reference text : new PsiImmediateClassType(psiClass, result.getSubstitutor(), ((PsiClassReferenceType)type).getLanguageLevel(), type.getAnnotations()); } } if (!type.isValid()) { if (expr.isValid()) { PsiJavaCodeReferenceElement refInside = type instanceof PsiClassReferenceType ? ((PsiClassReferenceType)type).getReference() : null; @NonNls String typeinfo = type + " (" + type.getClass() + ")" + (refInside == null ? "" : "; ref inside: "+refInside + " ("+refInside.getClass()+") valid:"+refInside.isValid()); LOG.error("Type is invalid: " + typeinfo + "; expr: '" + expr + "' (" + expr.getClass() + ") is valid"); } else { LOG.error("Expression: '"+expr+"' is invalid, must not be used for getType()"); } } return type == TypeConversionUtil.NULL_TYPE ? null : type; }
private Pair<PsiType, ConstraintType> inferTypeForMethodTypeParameterInner(@NotNull PsiTypeParameter typeParameter, @NotNull PsiParameter[] parameters, @NotNull PsiExpression[] arguments, @NotNull PsiSubstitutor partialSubstitutor, final PsiElement parent, @NotNull ParameterTypeInferencePolicy policy) { PsiType[] paramTypes = new PsiType[arguments.length]; PsiType[] argTypes = new PsiType[arguments.length]; if (parameters.length > 0) { for (int j = 0; j < argTypes.length; j++) { final PsiExpression argument = arguments[j]; if (argument == null) continue; if (argument instanceof PsiMethodCallExpression && ourGuard.currentStack().contains(argument)) continue; final RecursionGuard.StackStamp stackStamp = PsiDiamondType.ourDiamondGuard.markStack(); argTypes[j] = argument.getType(); if (!stackStamp.mayCacheNow()) { argTypes[j] = null; continue; } final PsiParameter parameter = parameters[Math.min(j, parameters.length - 1)]; if (j >= parameters.length && !parameter.isVarArgs()) break; paramTypes[j] = parameter.getType(); if (paramTypes[j] instanceof PsiEllipsisType) { paramTypes[j] = ((PsiEllipsisType)paramTypes[j]).getComponentType(); if (arguments.length == parameters.length && argTypes[j] instanceof PsiArrayType && !(((PsiArrayType)argTypes[j]).getComponentType() instanceof PsiPrimitiveType)) { argTypes[j] = ((PsiArrayType)argTypes[j]).getComponentType(); } } } } return inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, argTypes, partialSubstitutor, parent, policy); }
@Override public PsiSubstitutor getSubstitutor() { if (myCalcedSubstitutor == null) { PsiSubstitutor incompleteSubstitutor = super.getSubstitutor(); PsiMethod method = getElement(); if (myTypeArguments == null) { final RecursionGuard.StackStamp stackStamp = PsiDiamondType.ourDiamondGuard.markStack(); final PsiSubstitutor inferredSubstitutor = inferTypeArguments(DefaultParameterTypeInferencePolicy.INSTANCE); if (!stackStamp.mayCacheNow()) { return inferredSubstitutor; } myCalcedSubstitutor = inferredSubstitutor; } else { PsiTypeParameter[] typeParams = method.getTypeParameters(); for (int i = 0; i < myTypeArguments.length && i < typeParams.length; i++) { incompleteSubstitutor = incompleteSubstitutor.put(typeParams[i], myTypeArguments[i]); } myCalcedSubstitutor = incompleteSubstitutor; } } return myCalcedSubstitutor; }
@Override @Nullable public <T extends SemElement> List<T> getSemElements(final SemKey<T> key, @NotNull final PsiElement psi) { List<T> cached = _getCachedSemElements(key, true, psi); if (cached != null) { return cached; } RecursionGuard.StackStamp stamp = RecursionManager.createGuard("semService").markStack(); LinkedHashSet<T> result = new LinkedHashSet<T>(); final Map<SemKey, List<SemElement>> map = new THashMap<SemKey, List<SemElement>>(); for (final SemKey each : myInheritors.get(key)) { List<SemElement> list = createSemElements(each, psi); map.put(each, list); result.addAll((List<T>)list); } if (stamp.mayCacheNow()) { final SemCacheChunk persistent = getOrCreateChunk(psi); for (SemKey semKey : map.keySet()) { persistent.putSemElements(semKey, map.get(semKey)); } } return new ArrayList<T>(result); }
private List<GrField> getSyntheticFields() { List<GrField> fields = AST_TRANSFORM_FIELD.getCachedValue(this); if (fields == null) { final RecursionGuard.StackStamp stamp = ourGuard.markStack(); fields = AstTransformContributor.runContributorsForFields(this); if (stamp.mayCacheNow()) { fields = AST_TRANSFORM_FIELD.putCachedValue(this, fields); } } return fields; }
@NotNull public GrMethod[] getCodeMethods() { GrMethod[] cached = myGroovyMethods; if (cached == null) { RecursionGuard.StackStamp stamp = ourGuard.markStack(); GrTypeDefinitionBody body = getBody(); cached = body != null ? body.getMethods() : GrMethod.EMPTY_ARRAY; if (stamp.mayCacheNow()) { myGroovyMethods = cached; } } return cached; }
@NotNull public PsiClass[] getInnerClasses() { PsiClass[] inners = myInnerClasses; if (inners == null) { RecursionGuard.StackStamp stamp = ourGuard.markStack(); final GrTypeDefinitionBody body = getBody(); inners = body != null ? body.getInnerClasses() : PsiClass.EMPTY_ARRAY; if (stamp.mayCacheNow()) { myInnerClasses = inners; } } return inners; }
@Override public V get(Object key) { ConcurrentMap<K, V> map = myMap; K k = notNull(key); V value = map.get(k); if (value == null) { RecursionGuard.StackStamp stamp = ourGuard.markStack(); value = create((K)key); if (stamp.mayCacheNow()) { V v = notNull(value); value = ConcurrencyUtil.cacheOrGet(map, k, v); } } return value == FAKE_NULL() ? null : value; }
@Override public V get(Object key) { Map<K, V> map = getMap(); K k = notNull(key); V value = map.get(k); if (value == null) { RecursionGuard.StackStamp stamp = ourGuard.markStack(); value = create((K)key); if (stamp.mayCacheNow()) { V v = notNull(value); map.put(k, v); } } return nullize(value); }
@Override @Nullable public <T extends SemElement> List<T> getSemElements(final SemKey<T> key, @Nonnull final PsiElement psi) { List<T> cached = _getCachedSemElements(key, true, psi); if (cached != null) { return cached; } ensureInitialized(); RecursionGuard.StackStamp stamp = RecursionManager.createGuard("semService").markStack(); LinkedHashSet<T> result = new LinkedHashSet<>(); final Map<SemKey, List<SemElement>> map = new THashMap<>(); for (final SemKey each : key.getInheritors()) { List<SemElement> list = createSemElements(each, psi); map.put(each, list); result.addAll((List<T>)list); } if (stamp.mayCacheNow()) { final SemCacheChunk persistent = getOrCreateChunk(psi); for (SemKey semKey : map.keySet()) { persistent.putSemElements(semKey, map.get(semKey)); } } return new ArrayList<>(result); }
@NotNull public PsiSubstitutor getSubstitutor(boolean includeReturnConstraint) { PsiSubstitutor substitutor = myCalcedSubstitutor; if(substitutor == null || !includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8) || isOverloadCheck()) { PsiSubstitutor incompleteSubstitutor = super.getSubstitutor(); PsiMethod method = getElement(); if(myTypeArguments == null) { final RecursionGuard.StackStamp stackStamp = PsiDiamondType.ourDiamondGuard.markStack(); final PsiSubstitutor inferredSubstitutor = inferTypeArguments(DefaultParameterTypeInferencePolicy.INSTANCE, includeReturnConstraint); if(!stackStamp.mayCacheNow() || isOverloadCheck() || !includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8) || getMarkerList() != null && PsiResolveHelper .ourGraphGuard.currentStack().contains(getMarkerList().getParent()) || LambdaUtil.isLambdaParameterCheck()) { return inferredSubstitutor; } myCalcedSubstitutor = substitutor = inferredSubstitutor; } else { PsiTypeParameter[] typeParams = method.getTypeParameters(); for(int i = 0; i < myTypeArguments.length && i < typeParams.length; i++) { incompleteSubstitutor = incompleteSubstitutor.put(typeParams[i], myTypeArguments[i]); } myCalcedSubstitutor = substitutor = incompleteSubstitutor; } } return substitutor; }
@Nullable public <T extends PsiExpression> PsiType getType(@NotNull T expr, @NotNull Function<T, PsiType> f) { final boolean isOverloadCheck = MethodCandidateInfo.isOverloadCheck() || LambdaUtil.isLambdaParameterCheck(); final boolean polyExpression = PsiPolyExpressionUtil.isPolyExpression(expr); PsiType type = isOverloadCheck && polyExpression ? null : myCalculatedTypes.get(expr); if(type == null) { final RecursionGuard.StackStamp dStackStamp = PsiDiamondType.ourDiamondGuard.markStack(); type = f.fun(expr); if(!dStackStamp.mayCacheNow()) { return type; } //cache standalone expression types as they do not depend on the context if(isOverloadCheck && polyExpression) { return type; } if(type == null) { type = TypeConversionUtil.NULL_TYPE; } myCalculatedTypes.put(expr, type); if(type instanceof PsiClassReferenceType) { // convert reference-based class type to the PsiImmediateClassType, since the reference may become invalid PsiClassType.ClassResolveResult result = ((PsiClassReferenceType) type).resolveGenerics(); PsiClass psiClass = result.getElement(); type = psiClass == null ? type // for type with unresolved reference, leave it in the cache // for clients still might be able to retrieve its getCanonicalText() from the reference text : new PsiImmediateClassType(psiClass, result.getSubstitutor(), ((PsiClassReferenceType) type).getLanguageLevel(), type.getAnnotationProvider()); } } if(!type.isValid()) { if(expr.isValid()) { PsiJavaCodeReferenceElement refInside = type instanceof PsiClassReferenceType ? ((PsiClassReferenceType) type).getReference() : null; @NonNls String typeinfo = type + " (" + type.getClass() + ")" + (refInside == null ? "" : "; ref inside: " + refInside + " (" + refInside.getClass() + ") valid:" + refInside.isValid ()); LOG.error("Type is invalid: " + typeinfo + "; expr: '" + expr + "' (" + expr.getClass() + ") is valid"); } else { LOG.error("Expression: '" + expr + "' is invalid, must not be used for getType()"); } } return type == TypeConversionUtil.NULL_TYPE ? null : type; }
private Pair<PsiType, ConstraintType> inferTypeForMethodTypeParameterInner(@NotNull PsiTypeParameter typeParameter, @NotNull PsiParameter[] parameters, @NotNull PsiExpression[] arguments, @NotNull PsiSubstitutor partialSubstitutor, final PsiElement parent, @NotNull ParameterTypeInferencePolicy policy) { PsiType[] paramTypes = PsiType.createArray(arguments.length); PsiType[] argTypes = PsiType.createArray(arguments.length); if(parameters.length > 0) { for(int j = 0; j < argTypes.length; j++) { final PsiExpression argument = arguments[j]; if(argument == null) { continue; } if(argument instanceof PsiMethodCallExpression && PsiResolveHelper.ourGuard.currentStack().contains(argument)) { continue; } final RecursionGuard.StackStamp stackStamp = PsiDiamondType.ourDiamondGuard.markStack(); argTypes[j] = argument.getType(); if(!stackStamp.mayCacheNow()) { argTypes[j] = null; continue; } final PsiParameter parameter = parameters[Math.min(j, parameters.length - 1)]; if(j >= parameters.length && !parameter.isVarArgs()) { break; } paramTypes[j] = parameter.getType(); if(paramTypes[j] instanceof PsiEllipsisType) { paramTypes[j] = ((PsiEllipsisType) paramTypes[j]).getComponentType(); if(arguments.length == parameters.length && argTypes[j] instanceof PsiArrayType && !(((PsiArrayType) argTypes[j]).getComponentType() instanceof PsiPrimitiveType)) { argTypes[j] = ((PsiArrayType) argTypes[j]).getComponentType(); } } } } return inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, argTypes, partialSubstitutor, parent, policy); }