use of org.codehaus.groovy.ast.ASTNode 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.ASTNode in project groovy by apache.
the class ClosureWriter method createClosureClass.
protected ClassNode createClosureClass(ClosureExpression expression, int mods) {
ClassNode classNode = controller.getClassNode();
ClassNode outerClass = controller.getOutermostClass();
MethodNode methodNode = controller.getMethodNode();
String name = classNode.getName() + "$" + // add a more informative name
controller.getContext().getNextClosureInnerName(outerClass, classNode, methodNode);
boolean staticMethodOrInStaticClass = controller.isStaticMethod() || classNode.isStaticClass();
Parameter[] parameters = expression.getParameters();
if (parameters == null) {
parameters = Parameter.EMPTY_ARRAY;
} else if (parameters.length == 0) {
// let's create a default 'it' parameter
Parameter it = new Parameter(ClassHelper.OBJECT_TYPE, "it", ConstantExpression.NULL);
parameters = new Parameter[] { it };
Variable ref = expression.getVariableScope().getDeclaredVariable("it");
if (ref != null)
it.setClosureSharedVariable(ref.isClosureSharedVariable());
}
Parameter[] localVariableParams = getClosureSharedVariables(expression);
removeInitialValues(localVariableParams);
InnerClassNode answer = new InnerClassNode(classNode, name, mods, ClassHelper.CLOSURE_TYPE.getPlainNodeReference());
answer.setEnclosingMethod(controller.getMethodNode());
answer.setSynthetic(true);
answer.setUsingGenerics(outerClass.isUsingGenerics());
answer.setSourcePosition(expression);
if (staticMethodOrInStaticClass) {
answer.setStaticClass(true);
}
if (controller.isInScriptBody()) {
answer.setScriptBody(true);
}
MethodNode method = answer.addMethod("doCall", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, expression.getCode());
method.setSourcePosition(expression);
VariableScope varScope = expression.getVariableScope();
if (varScope == null) {
throw new RuntimeException("Must have a VariableScope by now! for expression: " + expression + " class: " + name);
} else {
method.setVariableScope(varScope.copy());
}
if (parameters.length > 1 || (parameters.length == 1 && parameters[0].getType() != null && parameters[0].getType() != ClassHelper.OBJECT_TYPE && !ClassHelper.OBJECT_TYPE.equals(parameters[0].getType().getComponentType()))) {
// let's add a typesafe call method
MethodNode call = answer.addMethod("call", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, new ReturnStatement(new MethodCallExpression(VariableExpression.THIS_EXPRESSION, "doCall", new ArgumentListExpression(parameters))));
call.setSourcePosition(expression);
}
// let's make the constructor
BlockStatement block = new BlockStatement();
// this block does not get a source position, because we don't
// want this synthetic constructor to show up in corbertura reports
VariableExpression outer = new VariableExpression("_outerInstance");
outer.setSourcePosition(expression);
block.getVariableScope().putReferencedLocalVariable(outer);
VariableExpression thisObject = new VariableExpression("_thisObject");
thisObject.setSourcePosition(expression);
block.getVariableScope().putReferencedLocalVariable(thisObject);
TupleExpression conArgs = new TupleExpression(outer, thisObject);
block.addStatement(new ExpressionStatement(new ConstructorCallExpression(ClassNode.SUPER, conArgs)));
// let's assign all the parameter fields from the outer context
for (Parameter param : localVariableParams) {
String paramName = param.getName();
ClassNode type = param.getType();
if (true) {
VariableExpression initialValue = new VariableExpression(paramName);
initialValue.setAccessedVariable(param);
initialValue.setUseReferenceDirectly(true);
ClassNode realType = type;
type = ClassHelper.makeReference();
param.setType(ClassHelper.makeReference());
FieldNode paramField = answer.addField(paramName, ACC_PRIVATE | ACC_SYNTHETIC, type, initialValue);
paramField.setOriginType(ClassHelper.getWrapper(param.getOriginType()));
paramField.setHolder(true);
String methodName = Verifier.capitalize(paramName);
// let's add a getter & setter
Expression fieldExp = new FieldExpression(paramField);
answer.addMethod("get" + methodName, ACC_PUBLIC, realType.getPlainNodeReference(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new ReturnStatement(fieldExp));
}
}
Parameter[] params = new Parameter[2 + localVariableParams.length];
params[0] = new Parameter(ClassHelper.OBJECT_TYPE, "_outerInstance");
params[1] = new Parameter(ClassHelper.OBJECT_TYPE, "_thisObject");
System.arraycopy(localVariableParams, 0, params, 2, localVariableParams.length);
ASTNode sn = answer.addConstructor(ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, block);
sn.setSourcePosition(expression);
correctAccessedVariable(answer, expression);
return answer;
}
use of org.codehaus.groovy.ast.ASTNode in project groovy by apache.
the class BytecodeSequence method visit.
/**
* Delegates to the visit method used for this class.
* If the visitor is a ClassGenerator, then
* {@link ClassGenerator#visitBytecodeSequence(BytecodeSequence)}
* is called with this instance. If the visitor is no
* ClassGenerator, then this method will call visit on
* each ASTNode element sorted by this class. If one
* element is a BytecodeInstruction, then it will be skipped
* as it is no ASTNode.
*
* @param visitor the visitor
* @see ClassGenerator
*/
public void visit(GroovyCodeVisitor visitor) {
if (visitor instanceof ClassGenerator) {
ClassGenerator gen = (ClassGenerator) visitor;
gen.visitBytecodeSequence(this);
return;
}
for (Iterator iterator = instructions.iterator(); iterator.hasNext(); ) {
Object part = (Object) iterator.next();
if (part instanceof ASTNode) {
((ASTNode) part).visit(visitor);
}
}
}
use of org.codehaus.groovy.ast.ASTNode in project groovy by apache.
the class VerifierCodeVisitorTest method assertInvalidName.
protected void assertInvalidName(String name) {
try {
VerifierCodeVisitor.assertValidIdentifier(name, "variable name", new ASTNode());
fail("Should have thrown exception due to invalid name: " + name);
} catch (RuntimeParserException e) {
System.out.println("Caught invalid exception: " + e);
}
}
use of org.codehaus.groovy.ast.ASTNode in project groovy by apache.
the class ContextualClassCodeVisitor method popContext.
protected TreeContext popContext() {
final TreeContext treeContext = treeContextStack.pop();
List<TreeContextAction> actions = treeContext.getOnPopHandlers();
for (TreeContextAction contextAction : actions) {
contextAction.call(treeContext);
}
lastContext = treeContext;
ASTNode parentNode = treeContext.parent != null ? treeContext.parent.node : null;
if (treeContext.node instanceof Expression && parentNode != null) {
ClassCodeExpressionTransformer trn = new ClassCodeExpressionTransformer() {
@Override
protected SourceUnit getSourceUnit() {
return null;
}
@Override
public Expression transform(final Expression exp) {
if (exp == treeContext.node) {
Expression replacement = treeContext.getReplacement();
if (replacement != null) {
return replacement;
}
}
return super.transform(exp);
}
};
// todo: reliable way to call the transformer
//parentNode.visit(trn);
}
return treeContext;
}
Aggregations