use of org.codehaus.groovy.ast.Parameter in project groovy-core by groovy.
the class StaticTypeCheckingVisitor method getType.
protected ClassNode getType(ASTNode exp) {
ClassNode cn = exp.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
if (cn != null)
return cn;
if (exp instanceof ClassExpression) {
ClassNode node = CLASS_Type.getPlainNodeReference();
node.setGenericsTypes(new GenericsType[] { new GenericsType(((ClassExpression) exp).getType()) });
return node;
} else if (exp instanceof VariableExpression) {
VariableExpression vexp = (VariableExpression) exp;
if (vexp == VariableExpression.THIS_EXPRESSION)
return makeThis();
if (vexp == VariableExpression.SUPER_EXPRESSION)
return makeSuper();
ClassNode selfTrait = isTraitSelf(vexp);
if (selfTrait != null)
return makeSelf(selfTrait);
final Variable variable = vexp.getAccessedVariable();
if (variable instanceof FieldNode) {
checkOrMarkPrivateAccess(vexp, (FieldNode) variable);
return getType((FieldNode) variable);
}
if (variable != null && variable != vexp && variable instanceof VariableExpression) {
return getType((Expression) variable);
}
if (variable instanceof Parameter) {
Parameter parameter = (Parameter) variable;
ClassNode type = typeCheckingContext.controlStructureVariables.get(parameter);
TypeCheckingContext.EnclosingClosure enclosingClosure = typeCheckingContext.getEnclosingClosure();
ClassNode[] closureParamTypes = (ClassNode[]) (enclosingClosure != null ? enclosingClosure.getClosureExpression().getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS) : null);
if (type == null && enclosingClosure != null && "it".equals(variable.getName()) && closureParamTypes != null) {
final Parameter[] parameters = enclosingClosure.getClosureExpression().getParameters();
if (parameters.length == 0 && getTemporaryTypesForExpression(vexp) == null) {
type = closureParamTypes[0];
}
}
if (type != null) {
storeType((VariableExpression) exp, type);
return type;
}
}
}
if (exp instanceof ListExpression) {
return inferListExpressionType((ListExpression) exp);
} else if (exp instanceof MapExpression) {
return inferMapExpressionType((MapExpression) exp);
}
if (exp instanceof ConstructorCallExpression) {
return ((ConstructorCallExpression) exp).getType();
}
if (exp instanceof MethodNode) {
if ((exp == GET_DELEGATE || exp == GET_OWNER || exp == GET_THISOBJECT) && typeCheckingContext.getEnclosingClosure() != null) {
return typeCheckingContext.getEnclosingClassNode();
}
ClassNode ret = getInferredReturnType(exp);
return ret != null ? ret : ((MethodNode) exp).getReturnType();
}
if (exp instanceof ClosureExpression) {
ClassNode irt = getInferredReturnType(exp);
if (irt != null) {
irt = wrapTypeIfNecessary(irt);
ClassNode result = CLOSURE_TYPE.getPlainNodeReference();
result.setGenericsTypes(new GenericsType[] { new GenericsType(irt) });
return result;
}
}
if (exp instanceof RangeExpression) {
ClassNode plain = ClassHelper.RANGE_TYPE.getPlainNodeReference();
RangeExpression re = (RangeExpression) exp;
ClassNode fromType = getType(re.getFrom());
ClassNode toType = getType(re.getTo());
if (fromType.equals(toType)) {
plain.setGenericsTypes(new GenericsType[] { new GenericsType(wrapTypeIfNecessary(fromType)) });
} else {
plain.setGenericsTypes(new GenericsType[] { new GenericsType(wrapTypeIfNecessary(lowestUpperBound(fromType, toType))) });
}
return plain;
}
if (exp instanceof UnaryPlusExpression) {
return getType(((UnaryPlusExpression) exp).getExpression());
}
if (exp instanceof UnaryMinusExpression) {
return getType(((UnaryMinusExpression) exp).getExpression());
}
if (exp instanceof BitwiseNegationExpression) {
return getType(((BitwiseNegationExpression) exp).getExpression());
}
if (exp instanceof MethodCall) {
MethodNode target = (MethodNode) exp.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
if (target != null) {
return getType(target);
}
}
if (exp instanceof Parameter) {
return ((Parameter) exp).getOriginType();
}
if (exp instanceof FieldNode) {
FieldNode fn = (FieldNode) exp;
return getGenericsResolvedTypeOfFieldOrProperty(fn, fn.getOriginType());
}
if (exp instanceof PropertyNode) {
PropertyNode pn = (PropertyNode) exp;
return getGenericsResolvedTypeOfFieldOrProperty(pn, pn.getOriginType());
}
return exp instanceof VariableExpression ? ((VariableExpression) exp).getOriginType() : ((Expression) exp).getType();
}
use of org.codehaus.groovy.ast.Parameter in project groovy-core by groovy.
the class StaticTypeCheckingVisitor method inferSAMType.
private void inferSAMType(Parameter param, ClassNode receiver, MethodNode methodWithSAMParameter, ArgumentListExpression originalMethodCallArguments, ClosureExpression openBlock) {
// In a method call with SAM coercion the inference is to be
// understood as a two phase process. We have the normal method call
// to the target method with the closure argument and we have the
// SAM method that will be called inside the normal target method.
// To infer correctly we have to "simulate" this process. We know the
// call to the closure will be done through the SAM type, so the SAM
// type generics deliver information about the Closure. At the same
// time the SAM class is used in the target method parameter,
// providing a connection from the SAM type and the target method
// declaration class.
// First we try to get as much information about the declaration
// class through the receiver
Map<String, GenericsType> targetMethodDeclarationClassConnections = new HashMap<String, GenericsType>();
extractGenericsConnections(targetMethodDeclarationClassConnections, receiver, receiver.redirect());
// then we use the method with the SAM parameter to get more information about the declaration
Parameter[] parametersOfMethodContainingSAM = methodWithSAMParameter.getParameters();
for (int i = 0; i < parametersOfMethodContainingSAM.length; i++) {
Expression callArg = originalMethodCallArguments.getExpression(i);
// we look at the closure later in detail, so skip it here
if (callArg == openBlock)
continue;
ClassNode parameterType = parametersOfMethodContainingSAM[i].getType();
extractGenericsConnections(targetMethodDeclarationClassConnections, getType(callArg), parameterType);
}
// To make a connection to the SAM class we use that new information
// to replace the generics in the SAM type parameter of the target
// method and than that to make the connections to the SAM type generics
ClassNode paramTypeWithReceiverInformation = applyGenericsContext(targetMethodDeclarationClassConnections, param.getOriginType());
Map<String, GenericsType> SAMTypeConnections = new HashMap<String, GenericsType>();
ClassNode classForSAM = paramTypeWithReceiverInformation.redirect();
extractGenericsConnections(SAMTypeConnections, paramTypeWithReceiverInformation, classForSAM);
// should the open block provide final information we apply that
// to the corresponding parameters of the SAM type method
MethodNode methodForSAM = findSAM(classForSAM);
ClassNode[] parameterTypesForSAM = extractTypesFromParameters(methodForSAM.getParameters());
ClassNode[] blockParameterTypes = (ClassNode[]) openBlock.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS);
if (blockParameterTypes == null) {
Parameter[] p = openBlock.getParameters();
if (p.length == 0 && parameterTypesForSAM.length != 0) {
// implicit it
blockParameterTypes = parameterTypesForSAM;
} else {
blockParameterTypes = extractTypesFromParameters(p);
}
}
for (int i = 0; i < blockParameterTypes.length; i++) {
//TODO: equal length guaranteed?
extractGenericsConnections(SAMTypeConnections, blockParameterTypes[i], parameterTypesForSAM[i]);
}
// store the type of parameter and block type as meta information
for (int i = 0; i < blockParameterTypes.length; i++) {
//TODO: equal length guaranteed?
ClassNode resolvedParameter = applyGenericsContext(SAMTypeConnections, parameterTypesForSAM[i]);
blockParameterTypes[i] = resolvedParameter;
}
openBlock.putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS, blockParameterTypes);
}
use of org.codehaus.groovy.ast.Parameter in project groovy-core by groovy.
the class StaticTypeCheckingVisitor method convertClosureTypeToSAMType.
/**
* This method will convert a closure type to the appropriate SAM type, which will be used
* to infer return type generics.
*
* @param closureType the inferred type of a closure (Closure<ClosureReturnType>)
* @param samType the type into which the closure is coerced into
* @return same SAM type, but completed with information from the closure node
*/
private static ClassNode convertClosureTypeToSAMType(final Expression expression, final ClassNode closureType, final ClassNode samType, final Map<String, GenericsType> placeholders) {
if (!samType.isUsingGenerics())
return samType;
// use the generics information from the Closure to further specify the type
MethodNode sam = findSAM(samType);
if (closureType.isUsingGenerics() && sam != null) {
//correct SAM type for generics
//sam = applyGenericsContext(placeholders, sam);
// the return type of the SAM method exactly corresponds to the inferred return type
ClassNode samReturnType = sam.getReturnType();
ClassNode closureReturnType = expression.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
if (closureReturnType != null && closureReturnType.isUsingGenerics()) {
ClassNode unwrapped = closureReturnType.getGenericsTypes()[0].getType();
extractGenericsConnections(placeholders, unwrapped, samReturnType);
} else if (samReturnType.isGenericsPlaceHolder()) {
placeholders.put(samReturnType.getGenericsTypes()[0].getName(), closureType.getGenericsTypes()[0]);
}
// now repeat the same for each parameter given in the ClosureExpression
if (expression instanceof ClosureExpression) {
List<ClassNode[]> genericsToConnect = new LinkedList<ClassNode[]>();
Parameter[] closureParams = ((ClosureExpression) expression).getParameters();
ClassNode[] closureParamTypes = extractTypesFromParameters(closureParams);
if (expression.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS) != null) {
closureParamTypes = expression.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS);
}
final Parameter[] parameters = sam.getParameters();
for (int i = 0; i < parameters.length; i++) {
final Parameter parameter = parameters[i];
if (parameter.getOriginType().isUsingGenerics() && closureParamTypes.length > i) {
genericsToConnect.add(new ClassNode[] { closureParamTypes[i], parameter.getOriginType() });
}
}
for (ClassNode[] classNodes : genericsToConnect) {
ClassNode found = classNodes[0];
ClassNode expected = classNodes[1];
if (!isAssignableTo(found, expected)) {
// probably facing a type mismatch
continue;
}
ClassNode generifiedType = GenericsUtils.parameterizeType(found, expected);
while (expected.isArray()) {
expected = expected.getComponentType();
generifiedType = generifiedType.getComponentType();
}
if (expected.isGenericsPlaceHolder()) {
placeholders.put(expected.getGenericsTypes()[0].getName(), new GenericsType(generifiedType));
} else {
GenericsType[] expectedGenericsTypes = expected.getGenericsTypes();
GenericsType[] foundGenericsTypes = generifiedType.getGenericsTypes();
for (int i = 0; i < expectedGenericsTypes.length; i++) {
final GenericsType type = expectedGenericsTypes[i];
if (type.isPlaceholder()) {
String name = type.getName();
placeholders.put(name, foundGenericsTypes[i]);
}
}
}
}
}
}
ClassNode result = applyGenericsContext(placeholders, samType.redirect());
return result;
}
use of org.codehaus.groovy.ast.Parameter in project groovy-core by groovy.
the class StaticTypeCheckingVisitor method doInferClosureParameterTypes.
private void doInferClosureParameterTypes(final ClassNode receiver, final Expression arguments, final ClosureExpression expression, final MethodNode selectedMethod, final Expression hintClass, final Expression options) {
List<ClassNode[]> closureSignatures = getSignaturesFromHint(expression, selectedMethod, hintClass, options);
List<ClassNode[]> candidates = new LinkedList<ClassNode[]>();
for (ClassNode[] signature : closureSignatures) {
// in order to compute the inferred types of the closure parameters, we're using the following trick:
// 1. create a dummy MethodNode for which the return type is a class node for which the generic types are the types returned by the hint
// 2. call inferReturnTypeGenerics
// 3. fetch inferred types from the result of inferReturnTypeGenerics
// In practice, it could be done differently but it has the main advantage of reusing
// existing code, hence reducing the amount of code to debug in case of failure.
ClassNode[] inferred = resolveGenericsFromTypeHint(receiver, arguments, selectedMethod, signature);
Parameter[] closureParams = expression.getParameters();
if (// same number of arguments
signature.length == closureParams.length || // implicit it
(signature.length == 1 && closureParams.length == 0) || (closureParams.length > signature.length && inferred[inferred.length - 1].isArray())) {
// vargs
candidates.add(inferred);
}
}
Parameter[] closureParams = expression.getParameters();
if (candidates.size() > 1) {
Iterator<ClassNode[]> candIt = candidates.iterator();
while (candIt.hasNext()) {
ClassNode[] inferred = candIt.next();
final int length = closureParams.length;
for (int i = 0; i < length; i++) {
Parameter closureParam = closureParams[i];
final ClassNode originType = closureParam.getOriginType();
ClassNode inferredType;
if (i < inferred.length - 1 || inferred.length == closureParams.length) {
inferredType = inferred[i];
} else {
// vargs?
ClassNode lastArgInferred = inferred[inferred.length - 1];
if (lastArgInferred.isArray()) {
inferredType = lastArgInferred.getComponentType();
} else {
candIt.remove();
continue;
}
}
if (!typeCheckMethodArgumentWithGenerics(originType, inferredType, i == length - 1)) {
candIt.remove();
}
}
}
if (candidates.size() > 1) {
addError("Ambiguous prototypes for closure. More than one target method matches. Please use explicit argument types.", expression);
}
}
if (candidates.size() == 1) {
ClassNode[] inferred = candidates.get(0);
if (closureParams.length == 0 && inferred.length == 1) {
expression.putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS, inferred);
} else {
final int length = closureParams.length;
for (int i = 0; i < length; i++) {
Parameter closureParam = closureParams[i];
final ClassNode originType = closureParam.getOriginType();
ClassNode inferredType = OBJECT_TYPE;
if (i < inferred.length - 1 || inferred.length == closureParams.length) {
inferredType = inferred[i];
} else {
// vargs?
ClassNode lastArgInferred = inferred[inferred.length - 1];
if (lastArgInferred.isArray()) {
inferredType = lastArgInferred.getComponentType();
} else {
addError("Incorrect number of parameters. Expected " + inferred.length + " but found " + closureParams.length, expression);
}
}
boolean lastArg = i == length - 1;
if (lastArg && inferredType.isArray()) {
if (inferredType.getComponentType().equals(originType)) {
inferredType = originType;
}
} else if (!typeCheckMethodArgumentWithGenerics(originType, inferredType, lastArg)) {
addError("Expected parameter of type " + inferredType.toString(false) + " but got " + originType.toString(false), closureParam.getType());
}
typeCheckingContext.controlStructureVariables.put(closureParam, inferredType);
}
}
}
}
use of org.codehaus.groovy.ast.Parameter in project groovy-core by groovy.
the class StaticTypeCheckingVisitor method visitClosureExpression.
@Override
public void visitClosureExpression(final ClosureExpression expression) {
boolean oldStaticContext = typeCheckingContext.isInStaticContext;
typeCheckingContext.isInStaticContext = false;
// collect every variable expression used in the loop body
final Map<VariableExpression, ClassNode> varOrigType = new HashMap<VariableExpression, ClassNode>();
Statement code = expression.getCode();
code.visit(new VariableExpressionTypeMemoizer(varOrigType));
Map<VariableExpression, List<ClassNode>> oldTracker = pushAssignmentTracking();
// first, collect closure shared variables and reinitialize types
SharedVariableCollector collector = new SharedVariableCollector(getSourceUnit());
collector.visitClosureExpression(expression);
Set<VariableExpression> closureSharedExpressions = collector.getClosureSharedExpressions();
Map<VariableExpression, ListHashMap> typesBeforeVisit = null;
if (!closureSharedExpressions.isEmpty()) {
typesBeforeVisit = new HashMap<VariableExpression, ListHashMap>();
saveVariableExpressionMetadata(closureSharedExpressions, typesBeforeVisit);
}
// perform visit
typeCheckingContext.pushEnclosingClosureExpression(expression);
DelegationMetadata dmd = getDelegationMetadata(expression);
if (dmd == null) {
typeCheckingContext.delegationMetadata = new DelegationMetadata(typeCheckingContext.getEnclosingClassNode(), Closure.OWNER_FIRST, typeCheckingContext.delegationMetadata);
} else {
typeCheckingContext.delegationMetadata = new DelegationMetadata(dmd.getType(), dmd.getStrategy(), typeCheckingContext.delegationMetadata);
}
super.visitClosureExpression(expression);
typeCheckingContext.delegationMetadata = typeCheckingContext.delegationMetadata.getParent();
MethodNode node = new MethodNode("dummy", 0, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, code);
returnAdder.visitMethod(node);
TypeCheckingContext.EnclosingClosure enclosingClosure = typeCheckingContext.getEnclosingClosure();
if (!enclosingClosure.getReturnTypes().isEmpty()) {
ClassNode returnType = lowestUpperBound(enclosingClosure.getReturnTypes());
storeInferredReturnType(expression, returnType);
ClassNode inferredType = wrapClosureType(returnType);
storeType(enclosingClosure.getClosureExpression(), inferredType);
}
typeCheckingContext.popEnclosingClosure();
boolean typeChanged = isSecondPassNeededForControlStructure(varOrigType, oldTracker);
if (typeChanged)
visitClosureExpression(expression);
// restore original metadata
restoreVariableExpressionMetadata(typesBeforeVisit);
typeCheckingContext.isInStaticContext = oldStaticContext;
Parameter[] parameters = expression.getParameters();
if (parameters != null) {
for (Parameter parameter : parameters) {
typeCheckingContext.controlStructureVariables.remove(parameter);
}
}
}
Aggregations