use of org.codehaus.groovy.ast.MethodNode in project groovy by apache.
the class StaticTypeCheckingVisitor method addArrayMethods.
/**
* add various getAt and setAt methods for primitive arrays
* @param receiver the receiver class
* @param name the name of the method
* @param args the argument classes
*/
private static void addArrayMethods(List<MethodNode> methods, ClassNode receiver, String name, ClassNode[] args) {
if (args.length != 1)
return;
if (!receiver.isArray())
return;
if (!isIntCategory(getUnwrapper(args[0])))
return;
if ("getAt".equals(name)) {
MethodNode node = new MethodNode(name, Opcodes.ACC_PUBLIC, receiver.getComponentType(), new Parameter[] { new Parameter(args[0], "arg") }, null, null);
node.setDeclaringClass(receiver.redirect());
methods.add(node);
} else if ("setAt".equals(name)) {
MethodNode node = new MethodNode(name, Opcodes.ACC_PUBLIC, VOID_TYPE, new Parameter[] { new Parameter(args[0], "arg") }, null, null);
node.setDeclaringClass(receiver.redirect());
methods.add(node);
}
}
use of org.codehaus.groovy.ast.MethodNode in project groovy by apache.
the class StaticTypeCheckingVisitor method allowStaticAccessToMember.
/**
* This method is used to filter search results in which null means "no match",
* to filter out illegal access to instance members from a static context.
*
* Return null if the given member is not static, but we want to access in
* a static way (staticOnly=true). If we want to access in a non-static way
* we always return the member, since then access to static members and
* non-static members is allowed.
*/
@SuppressWarnings("unchecked")
private <T> T allowStaticAccessToMember(T member, boolean staticOnly) {
if (member == null)
return null;
if (!staticOnly)
return member;
boolean isStatic;
if (member instanceof Variable) {
Variable v = (Variable) member;
isStatic = Modifier.isStatic(v.getModifiers());
} else if (member instanceof List) {
List<MethodNode> list = (List<MethodNode>) member;
if (list.size() == 1) {
return (T) Collections.singletonList(allowStaticAccessToMember(list.get(0), staticOnly));
}
return (T) Collections.emptyList();
} else {
MethodNode mn = (MethodNode) member;
isStatic = mn.isStatic();
}
if (staticOnly && !isStatic)
return null;
return member;
}
use of org.codehaus.groovy.ast.MethodNode in project groovy by apache.
the class StaticTypeCheckingVisitor method visitBinaryExpression.
@Override
public void visitBinaryExpression(BinaryExpression expression) {
int op = expression.getOperation().getType();
if (op == COMPARE_IDENTICAL || op == COMPARE_NOT_IDENTICAL) {
// we'll report those as errors later
return;
}
BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
typeCheckingContext.pushEnclosingBinaryExpression(expression);
try {
final Expression leftExpression = expression.getLeftExpression();
final Expression rightExpression = expression.getRightExpression();
leftExpression.visit(this);
SetterInfo setterInfo = removeSetterInfo(leftExpression);
if (setterInfo != null) {
if (ensureValidSetter(expression, leftExpression, rightExpression, setterInfo)) {
return;
}
} else {
rightExpression.visit(this);
}
ClassNode lType = getType(leftExpression);
ClassNode rType = getType(rightExpression);
if (isNullConstant(rightExpression)) {
if (!isPrimitiveType(lType))
// primitive types should be ignored as they will result in another failure
rType = UNKNOWN_PARAMETER_TYPE;
}
BinaryExpression reversedBinaryExpression = binX(rightExpression, expression.getOperation(), leftExpression);
ClassNode resultType = op == KEYWORD_IN ? getResultType(rType, op, lType, reversedBinaryExpression) : getResultType(lType, op, rType, expression);
if (op == KEYWORD_IN) {
// in case of the "in" operator, the receiver and the arguments are reversed
// so we use the reversedExpression and get the target method from it
storeTargetMethod(expression, (MethodNode) reversedBinaryExpression.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
} else if (op == LEFT_SQUARE_BRACKET && leftExpression instanceof VariableExpression && leftExpression.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE) == null) {
storeType(leftExpression, lType);
}
if (resultType == null) {
resultType = lType;
}
// if left expression is a closure shared variable, a second pass should be done
if (leftExpression instanceof VariableExpression) {
VariableExpression leftVar = (VariableExpression) leftExpression;
if (leftVar.isClosureSharedVariable()) {
// if left expression is a closure shared variable, we should check it twice
// see GROOVY-5874
typeCheckingContext.secondPassExpressions.add(new SecondPassExpression<Void>(expression));
}
}
if (lType.isUsingGenerics() && missesGenericsTypes(resultType) && isAssignment(op)) {
// unchecked assignment
// examples:
// List<A> list = new LinkedList()
// List<A> list = []
// Iterable<A> list = new LinkedList()
// in that case, the inferred type of the binary expression is the type of the RHS
// "completed" with generics type information available in the LHS
ClassNode completedType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());
resultType = completedType;
}
if (isArrayOp(op) && enclosingBinaryExpression != null && enclosingBinaryExpression.getLeftExpression() == expression && isAssignment(enclosingBinaryExpression.getOperation().getType()) && !lType.isArray()) {
// left hand side of an assignment : map['foo'] = ...
Expression enclosingBE_rightExpr = enclosingBinaryExpression.getRightExpression();
if (!(enclosingBE_rightExpr instanceof ClosureExpression)) {
enclosingBE_rightExpr.visit(this);
}
ClassNode[] arguments = { rType, getType(enclosingBE_rightExpr) };
List<MethodNode> nodes = findMethod(lType.redirect(), "putAt", arguments);
if (nodes.size() == 1) {
typeCheckMethodsWithGenericsOrFail(lType, arguments, nodes.get(0), enclosingBE_rightExpr);
} else if (nodes.isEmpty()) {
addNoMatchingMethodError(lType, "putAt", arguments, enclosingBinaryExpression);
}
}
boolean isEmptyDeclaration = expression instanceof DeclarationExpression && rightExpression instanceof EmptyExpression;
if (!isEmptyDeclaration && isAssignment(op)) {
if (rightExpression instanceof ConstructorCallExpression) {
inferDiamondType((ConstructorCallExpression) rightExpression, lType);
}
ClassNode originType = getOriginalDeclarationType(leftExpression);
typeCheckAssignment(expression, leftExpression, originType, rightExpression, resultType);
// and we must update the result type
if (!implementsInterfaceOrIsSubclassOf(getWrapper(resultType), getWrapper(originType))) {
resultType = originType;
} else if (lType.isUsingGenerics() && !lType.isEnum() && hasRHSIncompleteGenericTypeInfo(resultType)) {
// for example, LHS is List<ConcreteClass> and RHS is List<T> where T is a placeholder
resultType = lType;
}
// make sure we keep primitive types
if (isPrimitiveType(originType) && resultType.equals(getWrapper(originType))) {
resultType = originType;
}
// if we are in an if/else branch, keep track of assignment
if (typeCheckingContext.ifElseForWhileAssignmentTracker != null && leftExpression instanceof VariableExpression && !isNullConstant(rightExpression)) {
Variable accessedVariable = ((VariableExpression) leftExpression).getAccessedVariable();
if (accessedVariable instanceof VariableExpression) {
VariableExpression var = (VariableExpression) accessedVariable;
List<ClassNode> types = typeCheckingContext.ifElseForWhileAssignmentTracker.get(var);
if (types == null) {
types = new LinkedList<ClassNode>();
ClassNode type = var.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
types.add(type);
typeCheckingContext.ifElseForWhileAssignmentTracker.put(var, types);
}
types.add(resultType);
}
}
storeType(leftExpression, resultType);
// if right expression is a ClosureExpression, store parameter type information
if (leftExpression instanceof VariableExpression) {
if (rightExpression instanceof ClosureExpression) {
Parameter[] parameters = ((ClosureExpression) rightExpression).getParameters();
leftExpression.putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS, parameters);
} else if (rightExpression instanceof VariableExpression && ((VariableExpression) rightExpression).getAccessedVariable() instanceof Expression && ((Expression) ((VariableExpression) rightExpression).getAccessedVariable()).getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS) != null) {
Variable targetVariable = findTargetVariable((VariableExpression) leftExpression);
if (targetVariable instanceof ASTNode) {
((ASTNode) targetVariable).putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS, ((Expression) ((VariableExpression) rightExpression).getAccessedVariable()).getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS));
}
}
}
} else if (op == KEYWORD_INSTANCEOF) {
pushInstanceOfTypeInfo(leftExpression, rightExpression);
}
if (!isEmptyDeclaration) {
storeType(expression, resultType);
}
} finally {
typeCheckingContext.popEnclosingBinaryExpression();
}
}
use of org.codehaus.groovy.ast.MethodNode in project groovy by apache.
the class TraitASTTransformation method createHelperClass.
private ClassNode createHelperClass(final ClassNode cNode) {
ClassNode helper = new InnerClassNode(cNode, Traits.helperClassName(cNode), ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, ClassNode.EMPTY_ARRAY, null);
cNode.setModifiers(ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT);
checkInnerClasses(cNode);
MethodNode initializer = createInitMethod(false, cNode, helper);
MethodNode staticInitializer = createInitMethod(true, cNode, helper);
// apply the verifier to have the property nodes generated
generatePropertyMethods(cNode);
// prepare fields
List<FieldNode> fields = new ArrayList<FieldNode>();
Set<String> fieldNames = new HashSet<String>();
for (FieldNode field : cNode.getFields()) {
if (!"metaClass".equals(field.getName()) && (!field.isSynthetic() || field.getName().indexOf('$') < 0)) {
fields.add(field);
fieldNames.add(field.getName());
}
}
ClassNode fieldHelper = null;
if (!fields.isEmpty()) {
fieldHelper = new InnerClassNode(cNode, Traits.fieldHelperClassName(cNode), ACC_STATIC | ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT, ClassHelper.OBJECT_TYPE);
}
// add methods
List<MethodNode> methods = new ArrayList<MethodNode>(cNode.getMethods());
List<MethodNode> nonPublicAPIMethods = new LinkedList<MethodNode>();
for (final MethodNode methodNode : methods) {
boolean declared = methodNode.getDeclaringClass() == cNode;
if (declared) {
if (!methodNode.isSynthetic() && (methodNode.isProtected() || methodNode.getModifiers() == 0)) {
unit.addError(new SyntaxException("Cannot have protected/package private method in a trait (" + cNode.getName() + "#" + methodNode.getTypeDescriptor() + ")", methodNode.getLineNumber(), methodNode.getColumnNumber()));
return null;
}
helper.addMethod(processMethod(cNode, helper, methodNode, fieldHelper, fieldNames));
if (methodNode.isPrivate() || methodNode.isStatic()) {
nonPublicAPIMethods.add(methodNode);
}
}
}
// remove methods which should not appear in the trait interface
for (MethodNode privateMethod : nonPublicAPIMethods) {
cNode.removeMethod(privateMethod);
}
// add fields
for (FieldNode field : fields) {
processField(field, initializer, staticInitializer, fieldHelper, helper, cNode, fieldNames);
}
// clear properties to avoid generation of methods
cNode.getProperties().clear();
// copy annotations
copyClassAnnotations(cNode, helper);
// reuse the full list of fields
fields = new ArrayList<FieldNode>(cNode.getFields());
for (FieldNode field : fields) {
cNode.removeField(field.getName());
}
// visit AST xforms
registerASTTranformations(helper);
unit.getAST().addClass(helper);
if (fieldHelper != null) {
unit.getAST().addClass(fieldHelper);
}
// resolve scope (for closures)
resolveScope(helper);
if (fieldHelper != null) {
resolveScope(fieldHelper);
}
return helper;
}
use of org.codehaus.groovy.ast.MethodNode in project groovy by apache.
the class StaticTypeCheckingVisitor method visitClass.
@Override
public void visitClass(final ClassNode node) {
if (shouldSkipClassNode(node))
return;
if (extension.beforeVisitClass(node)) {
extension.afterVisitClass(node);
return;
}
Object type = node.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
if (type != null) {
// transformation has already been run on this class node
// so we'll use a silent collector in order not to duplicate errors
typeCheckingContext.pushErrorCollector();
}
typeCheckingContext.pushEnclosingClassNode(node);
Set<MethodNode> oldVisitedMethod = typeCheckingContext.alreadyVisitedMethods;
typeCheckingContext.alreadyVisitedMethods = new LinkedHashSet<MethodNode>();
super.visitClass(node);
Iterator<InnerClassNode> innerClasses = node.getInnerClasses();
while (innerClasses.hasNext()) {
InnerClassNode innerClassNode = innerClasses.next();
visitClass(innerClassNode);
}
typeCheckingContext.alreadyVisitedMethods = oldVisitedMethod;
node.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, node);
// works in a two pass sequence and we don't want to skip the second pass
for (MethodNode methodNode : node.getMethods()) {
methodNode.putNodeMetaData(StaticTypeCheckingVisitor.class, Boolean.TRUE);
}
for (ConstructorNode constructorNode : node.getDeclaredConstructors()) {
constructorNode.putNodeMetaData(StaticTypeCheckingVisitor.class, Boolean.TRUE);
}
extension.afterVisitClass(node);
}
Aggregations