public void positionStmtsAfterEnumInitStmts(List<Statement> staticFieldStatements) { MethodNode method = getOrAddStaticConstructorNode(); Statement statement = method.getCode(); if (statement instanceof BlockStatement) { BlockStatement block = (BlockStatement) statement; // add given statements for explicitly declared static fields just after enum-special fields // are found - the $VALUES binary expression marks the end of such fields. List<Statement> blockStatements = block.getStatements(); ListIterator<Statement> litr = blockStatements.listIterator(); while (litr.hasNext()) { Statement stmt = litr.next(); if (stmt instanceof ExpressionStatement && ((ExpressionStatement) stmt).getExpression() instanceof BinaryExpression) { BinaryExpression bExp = (BinaryExpression) ((ExpressionStatement) stmt).getExpression(); if (bExp.getLeftExpression() instanceof FieldExpression) { FieldExpression fExp = (FieldExpression) bExp.getLeftExpression(); if (fExp.getFieldName().equals("$VALUES")) { for (Statement tmpStmt : staticFieldStatements) { litr.add(tmpStmt); } } } } } } }
@Override public void visitConstructorCallExpression(final ConstructorCallExpression call) { super.visitConstructorCallExpression(call); MethodNode target = (MethodNode) call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET); if (target == null && call.getLineNumber() > 0) { addError("Target constructor for constructor call expression hasn't been set", call); } else { if (target == null) { // try to find a target ArgumentListExpression argumentListExpression = InvocationWriter.makeArgumentList(call.getArguments()); List<Expression> expressions = argumentListExpression.getExpressions(); ClassNode[] args = new ClassNode[expressions.size()]; for (int i = 0; i < args.length; i++) { args[i] = typeChooser.resolveType(expressions.get(i), classNode); } MethodNode constructor = findMethodOrFail( call, call.isSuperCall() ? classNode.getSuperClass() : classNode, "<init>", args); call.putNodeMetaData(DIRECT_METHOD_CALL_TARGET, constructor); target = constructor; } } if (target != null) { memorizeInitialExpressions(target); } }
public void addStaticInitializerStatements(List<Statement> staticStatements, boolean fieldInit) { MethodNode method = getOrAddStaticConstructorNode(); BlockStatement block = null; Statement statement = method.getCode(); if (statement == null) { block = new BlockStatement(); } else if (statement instanceof BlockStatement) { block = (BlockStatement) statement; } else { block = new BlockStatement(); block.addStatement(statement); } // while anything inside a static initializer block is appended // we don't want to append in the case we have a initialization // expression of a static field. In that case we want to add // before the other statements if (!fieldInit) { block.addStatements(staticStatements); } else { List<Statement> blockStatements = block.getStatements(); staticStatements.addAll(blockStatements); blockStatements.clear(); blockStatements.addAll(staticStatements); } }
private List<FieldNode> getAnnotatedFieldOfClass(ClassNode target, ClassNode annotation) { List<FieldNode> result = new ArrayList<FieldNode>(); for (FieldNode fieldNode : target.getFields()) if (!fieldNode.getAnnotations(annotation).isEmpty()) result.add(fieldNode); return result; }
private List<FieldNode> getAnnotatedFieldsOfHierarchy(ClassNode target, ClassNode annotation) { List<FieldNode> result = new ArrayList<FieldNode>(); for (ClassNode level : ASTHelper.getHierarchyOfDSLObjectAncestors(target)) { result.addAll(getAnnotatedFieldOfClass(level, annotation)); } return result; }
public void put(Object key, MethodNode value) { if (map.containsKey(key)) { get(key).add(value); } else { List<MethodNode> list = new ArrayList<MethodNode>(2); list.add(value); map.put(key, list); } }
/** * This methods creates a list of all methods with this name of the current class and of all super * classes * * @return the methods list * @see #getDeclaredMethods(String) */ public List<MethodNode> getMethods(String name) { List<MethodNode> answer = new ArrayList<MethodNode>(); ClassNode node = this; while (node != null) { answer.addAll(node.getDeclaredMethods(name)); node = node.getSuperClass(); } return answer; }
/** * Returns whether a classNode has the specified property or not * * @param classNode The ClassNode * @param propertyName The name of the property * @return True if the property exists in the ClassNode */ public static boolean hasProperty(ClassNode classNode, String propertyName) { if (classNode == null || propertyName == null || "".equals(propertyName.trim())) return false; List properties = classNode.getProperties(); for (Iterator i = properties.iterator(); i.hasNext(); ) { PropertyNode pn = (PropertyNode) i.next(); if (pn.getName().equals(propertyName)) return true; } return false; }
/** * This method is used to add "bridge" methods for private methods of an inner/outer class, so * that the outer class is capable of calling them. It does basically the same job as access$000 * like methods in Java. * * @param node an inner/outer class node for which to generate bridge methods */ @SuppressWarnings("unchecked") private void addPrivateBridgeMethods(final ClassNode node) { Set<ASTNode> accessedMethods = (Set<ASTNode>) node.getNodeMetaData(StaticTypesMarker.PV_METHODS_ACCESS); if (accessedMethods == null) return; List<MethodNode> methods = new ArrayList<MethodNode>(node.getAllDeclaredMethods()); Map<MethodNode, MethodNode> privateBridgeMethods = (Map<MethodNode, MethodNode>) node.getNodeMetaData(PRIVATE_BRIDGE_METHODS); if (privateBridgeMethods != null) { // private bridge methods already added return; } privateBridgeMethods = new HashMap<MethodNode, MethodNode>(); int i = -1; final int access = Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC; for (MethodNode method : methods) { if (accessedMethods.contains(method)) { i++; Parameter[] methodParameters = method.getParameters(); Parameter[] newParams = new Parameter[methodParameters.length + 1]; System.arraycopy(methodParameters, 0, newParams, 1, methodParameters.length); newParams[0] = new Parameter(node.getPlainNodeReference(), "$that"); Expression arguments; if (method.getParameters() == null || method.getParameters().length == 0) { arguments = ArgumentListExpression.EMPTY_ARGUMENTS; } else { List<Expression> args = new LinkedList<Expression>(); for (Parameter parameter : methodParameters) { args.add(new VariableExpression(parameter)); } arguments = new ArgumentListExpression(args); } Expression receiver = method.isStatic() ? new ClassExpression(node) : new VariableExpression(newParams[0]); MethodCallExpression mce = new MethodCallExpression(receiver, method.getName(), arguments); mce.setMethodTarget(method); ExpressionStatement returnStatement = new ExpressionStatement(mce); MethodNode bridge = node.addMethod( "access$" + i, access, method.getReturnType(), newParams, method.getExceptions(), returnStatement); privateBridgeMethods.put(method, bridge); bridge.addAnnotation(new AnnotationNode(COMPILESTATIC_CLASSNODE)); } } if (!privateBridgeMethods.isEmpty()) { node.setNodeMetaData(PRIVATE_BRIDGE_METHODS, privateBridgeMethods); } }
/** * Tests whether the ClassNode implements the specified method name * * @param classNode The ClassNode * @param methodName The method name * @param argTypes * @return True if it implements the method */ private static boolean implementsMethod( ClassNode classNode, String methodName, Class[] argTypes) { List methods = classNode.getMethods(); for (Iterator i = methods.iterator(); i.hasNext(); ) { MethodNode mn = (MethodNode) i.next(); final boolean isZeroArg = (argTypes == null || argTypes.length == 0); boolean methodMatch = mn.getName().equals(methodName) && isZeroArg; if (methodMatch) return true; // TODO Implement further parameter analysis } return false; }
private List createPropertiesForHasManyExpression(Expression e, ClassNode classNode) { List properties = new ArrayList(); if (e instanceof MapExpression) { MapExpression me = (MapExpression) e; List mapEntries = me.getMapEntryExpressions(); for (Iterator j = mapEntries.iterator(); j.hasNext(); ) { MapEntryExpression mee = (MapEntryExpression) j.next(); Expression keyExpression = mee.getKeyExpression(); String key = keyExpression.getText(); addAssociationForKey(key, properties, classNode); } } return properties; }
/** * @return the list of abstract methods associated with this ClassNode or null if there are no * such methods */ public List<MethodNode> getAbstractMethods() { List<MethodNode> result = new ArrayList<MethodNode>(3); for (MethodNode method : getDeclaredMethodsMap().values()) { if (method.isAbstract()) { result.add(method); } } if (result.isEmpty()) { return null; } else { return result; } }
private MethodNode getOrAddStaticConstructorNode() { MethodNode method = null; List declaredMethods = getDeclaredMethods("<clinit>"); if (declaredMethods.isEmpty()) { method = addMethod( "<clinit>", ACC_STATIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement()); method.setSynthetic(true); } else { method = (MethodNode) declaredMethods.get(0); } return method; }
private Collection createPropertiesForBelongsToExpression(Expression e, ClassNode classNode) { List properties = new ArrayList(); if (e instanceof MapExpression) { MapExpression me = (MapExpression) e; List mapEntries = me.getMapEntryExpressions(); for (Iterator i = mapEntries.iterator(); i.hasNext(); ) { MapEntryExpression mme = (MapEntryExpression) i.next(); String key = mme.getKeyExpression().getText(); String type = mme.getValueExpression().getText(); properties.add( new PropertyNode( key, Modifier.PUBLIC, ClassHelper.make(type), classNode, null, null, null)); } } return properties; }
private void injectAssociationProperties(ClassNode classNode, List propertiesToAdd) { for (Iterator i = propertiesToAdd.iterator(); i.hasNext(); ) { PropertyNode pn = (PropertyNode) i.next(); if (! /*GrailsASTUtils.*/hasProperty(classNode, pn.getName())) { // if(LOG.isDebugEnabled()) { // LOG.debug("[GrailsDomainInjector] Adding property [" + pn.getName() + "] to class [" + // classNode.getName() + "]"); // } classNode.addProperty(pn); } } }
private void injectAssociations(ClassNode classNode) { List properties = classNode.getProperties(); List propertiesToAdd = new ArrayList(); for (Iterator p = properties.iterator(); p.hasNext(); ) { PropertyNode pn = (PropertyNode) p.next(); final boolean isHasManyProperty = pn.getName().equals(/*GrailsDomainClassProperty.*/ RELATES_TO_MANY) || pn.getName().equals(/*GrailsDomainClassProperty.*/ HAS_MANY); if (isHasManyProperty) { Expression e = pn.getInitialExpression(); propertiesToAdd.addAll(createPropertiesForHasManyExpression(e, classNode)); } final boolean isBelongsTo = pn.getName().equals(/*GrailsDomainClassProperty.*/ BELONGS_TO); if (isBelongsTo) { Expression e = pn.getInitialExpression(); propertiesToAdd.addAll(createPropertiesForBelongsToExpression(e, classNode)); } } injectAssociationProperties(classNode, propertiesToAdd); }
protected Expression transformConstructorCallExpression(ConstructorCallExpression cce) { inSpecialConstructorCall = cce.isSpecialCall(); Expression expression = cce.getArguments(); if (expression instanceof TupleExpression) { TupleExpression tuple = (TupleExpression) expression; if (tuple.getExpressions().size() == 1) { expression = tuple.getExpression(0); if (expression instanceof NamedArgumentListExpression) { NamedArgumentListExpression namedArgs = (NamedArgumentListExpression) expression; List<MapEntryExpression> entryExpressions = namedArgs.getMapEntryExpressions(); for (int i = 0; i < entryExpressions.size(); i++) { entryExpressions.set( i, (MapEntryExpression) transformMapEntryExpression(entryExpressions.get(i), cce.getType())); } } } } Expression ret = cce.transformExpression(this); inSpecialConstructorCall = false; return ret; }
private FieldNode getOwnerField(ClassNode target) { List<FieldNode> annotatedFields = getAnnotatedFieldsOfHierarchy(target, OWNER_ANNOTATION); if (annotatedFields.isEmpty()) return null; if (annotatedFields.size() > 1) { addCompileError( String.format( "Found more than owner key fields, only one is allowed in hierarchy (%s, %s)", getQualifiedName(annotatedFields.get(0)), getQualifiedName(annotatedFields.get(1))), annotatedFields.get(0)); return null; } return annotatedFields.get(0); }
private FieldNode getKeyField(ClassNode target) { List<FieldNode> annotatedFields = getAnnotatedFieldsOfHierarchy(target, KEY_ANNOTATION); if (annotatedFields.isEmpty()) return null; if (annotatedFields.size() > 1) { addCompileError( String.format( "Found more than one key fields, only one is allowed in hierarchy (%s, %s)", getQualifiedName(annotatedFields.get(0)), getQualifiedName(annotatedFields.get(1))), annotatedFields.get(0)); return null; } FieldNode result = annotatedFields.get(0); if (!result.getType().equals(ClassHelper.STRING_TYPE)) { addCompileError( String.format( "Key field '%s' must be of type String, but is '%s' instead", result.getName(), result.getType().getName()), result); return null; } ClassNode ancestor = ASTHelper.getHighestAncestorDSLObject(target); if (target.equals(ancestor)) return result; FieldNode firstKey = getKeyField(ancestor); if (firstKey == null) { addCompileError( String.format( "Inconsistent hierarchy: Toplevel class %s has no key, but child class %s defines '%s'.", ancestor.getName(), target.getName(), result.getName()), result); return null; } return result; }
@Override public void makeGetPropertySite( Expression receiver, final String methodName, final boolean safe, final boolean implicitThis) { Object dynamic = receiver.getNodeMetaData(StaticCompilationMetadataKeys.RECEIVER_OF_DYNAMIC_PROPERTY); if (dynamic != null) { MethodNode target = safe ? INVOKERHELPER_GETPROPERTYSAFE_METHOD : INVOKERHELPER_GETPROPERTY_METHOD; MethodCallExpression mce = new MethodCallExpression( new ClassExpression(INVOKERHELPER_TYPE), target.getName(), new ArgumentListExpression(receiver, new ConstantExpression(methodName))); mce.setSafe(false); mce.setImplicitThis(false); mce.setMethodTarget(target); mce.visit(controller.getAcg()); return; } TypeChooser typeChooser = controller.getTypeChooser(); ClassNode classNode = controller.getClassNode(); ClassNode receiverType = (ClassNode) receiver.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER); if (receiverType == null) { receiverType = typeChooser.resolveType(receiver, classNode); } Object type = receiver.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); if (type == null && receiver instanceof VariableExpression) { Variable variable = ((VariableExpression) receiver).getAccessedVariable(); if (variable instanceof Expression) { type = ((Expression) variable).getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); } } if (type != null) { // in case a "flow type" is found, it is preferred to use it instead of // the declaration type receiverType = (ClassNode) type; } boolean isClassReceiver = false; if (isClassClassNodeWrappingConcreteType(receiverType)) { isClassReceiver = true; receiverType = receiverType.getGenericsTypes()[0].getType(); } MethodVisitor mv = controller.getMethodVisitor(); if (receiverType.isArray() && methodName.equals("length")) { receiver.visit(controller.getAcg()); ClassNode arrayGetReturnType = typeChooser.resolveType(receiver, classNode); controller.getOperandStack().doGroovyCast(arrayGetReturnType); mv.visitInsn(ARRAYLENGTH); controller.getOperandStack().replace(int_TYPE); return; } else if ((receiverType.implementsInterface(COLLECTION_TYPE) || COLLECTION_TYPE.equals(receiverType)) && ("size".equals(methodName) || "length".equals(methodName))) { MethodCallExpression expr = new MethodCallExpression(receiver, "size", ArgumentListExpression.EMPTY_ARGUMENTS); expr.setMethodTarget(COLLECTION_SIZE_METHOD); expr.setImplicitThis(implicitThis); expr.setSafe(safe); expr.visit(controller.getAcg()); return; } if (makeGetPropertyWithGetter(receiver, receiverType, methodName, safe, implicitThis)) return; if (makeGetField( receiver, receiverType, methodName, implicitThis, samePackages(receiverType.getPackageName(), classNode.getPackageName()))) return; if (receiverType.isEnum()) { mv.visitFieldInsn( GETSTATIC, BytecodeHelper.getClassInternalName(receiverType), methodName, BytecodeHelper.getTypeDescription(receiverType)); controller.getOperandStack().push(receiverType); return; } if (receiver instanceof ClassExpression) { if (makeGetField( receiver, receiver.getType(), methodName, implicitThis, samePackages(receiver.getType().getPackageName(), classNode.getPackageName()))) return; if (makeGetPropertyWithGetter(receiver, receiver.getType(), methodName, safe, implicitThis)) return; if (makeGetPrivateFieldWithBridgeMethod( receiver, receiver.getType(), methodName, safe, implicitThis)) return; } if (isClassReceiver) { // we are probably looking for a property of the class if (makeGetPropertyWithGetter(receiver, CLASS_Type, methodName, safe, implicitThis)) return; if (makeGetField(receiver, CLASS_Type, methodName, false, true)) return; } if (makeGetPrivateFieldWithBridgeMethod(receiver, receiverType, methodName, safe, implicitThis)) return; // GROOVY-5580, it is still possible that we're calling a superinterface property String getterName = "get" + MetaClassHelper.capitalize(methodName); if (receiverType.isInterface()) { Set<ClassNode> allInterfaces = receiverType.getAllInterfaces(); MethodNode getterMethod = null; for (ClassNode anInterface : allInterfaces) { getterMethod = anInterface.getGetterMethod(getterName); if (getterMethod != null) break; } // GROOVY-5585 if (getterMethod == null) { getterMethod = OBJECT_TYPE.getGetterMethod(getterName); } if (getterMethod != null) { MethodCallExpression call = new MethodCallExpression(receiver, getterName, ArgumentListExpression.EMPTY_ARGUMENTS); call.setMethodTarget(getterMethod); call.setImplicitThis(false); call.setSourcePosition(receiver); call.visit(controller.getAcg()); return; } } // GROOVY-5568, we would be facing a DGM call, but instead of foo.getText(), have foo.text List<MethodNode> methods = findDGMMethodsByNameAndArguments( controller.getSourceUnit().getClassLoader(), receiverType, getterName, ClassNode.EMPTY_ARRAY); if (!methods.isEmpty()) { List<MethodNode> methodNodes = chooseBestMethod(receiverType, methods, ClassNode.EMPTY_ARRAY); if (methodNodes.size() == 1) { MethodNode getter = methodNodes.get(0); MethodCallExpression call = new MethodCallExpression(receiver, getterName, ArgumentListExpression.EMPTY_ARGUMENTS); call.setMethodTarget(getter); call.setImplicitThis(false); call.setSourcePosition(receiver); call.visit(controller.getAcg()); return; } } boolean isStaticProperty = receiver instanceof ClassExpression && (receiverType.isDerivedFrom(receiver.getType()) || receiverType.implementsInterface(receiver.getType())); if (!isStaticProperty) { if (receiverType.implementsInterface(MAP_TYPE) || MAP_TYPE.equals(receiverType)) { // for maps, replace map.foo with map.get('foo') writeMapDotProperty(receiver, methodName, mv, safe); return; } if (receiverType.implementsInterface(LIST_TYPE) || LIST_TYPE.equals(receiverType)) { writeListDotProperty(receiver, methodName, mv, safe); return; } } controller .getSourceUnit() .addError( new SyntaxException( "Access to " + (receiver instanceof ClassExpression ? receiver.getType() : receiverType) .toString(false) + "#" + methodName + " is forbidden", receiver.getLineNumber(), receiver.getColumnNumber(), receiver.getLastLineNumber(), receiver.getLastColumnNumber())); controller.getMethodVisitor().visitInsn(ACONST_NULL); controller.getOperandStack().push(OBJECT_TYPE); }
private boolean trySubscript( final Expression receiver, final String message, final Expression arguments, ClassNode rType, final ClassNode aType) { if (getWrapper(rType).isDerivedFrom(Number_TYPE) && getWrapper(aType).isDerivedFrom(Number_TYPE)) { if ("plus".equals(message) || "minus".equals(message) || "multiply".equals(message) || "div".equals(message)) { writeNumberNumberCall(receiver, message, arguments); return true; } else if ("power".equals(message)) { writePowerCall(receiver, arguments, rType, aType); return true; } else if ("mod".equals(message)) { writeModCall(receiver, arguments, rType, aType); return true; } } else if (STRING_TYPE.equals(rType) && "plus".equals(message)) { writeStringPlusCall(receiver, message, arguments); return true; } else if ("getAt".equals(message)) { if (rType.isArray() && getWrapper(aType).isDerivedFrom(Number_TYPE)) { writeArrayGet(receiver, arguments, rType, aType); return true; } else { // check if a getAt method can be found on the receiver ClassNode current = rType; MethodNode getAtNode = null; while (current != null && getAtNode == null) { getAtNode = current.getMethod("getAt", new Parameter[] {new Parameter(aType, "index")}); if (getAtNode == null && isPrimitiveType(aType)) { getAtNode = current.getMethod( "getAt", new Parameter[] {new Parameter(getWrapper(aType), "index")}); } else if (getAtNode == null && aType.isDerivedFrom(Number_TYPE)) { getAtNode = current.getMethod( "getAt", new Parameter[] {new Parameter(getUnwrapper(aType), "index")}); } current = current.getSuperClass(); } if (getAtNode != null) { MethodCallExpression call = new MethodCallExpression(receiver, "getAt", arguments); call.setSourcePosition(arguments); call.setImplicitThis(false); call.setMethodTarget(getAtNode); call.visit(controller.getAcg()); return true; } // make sure Map#getAt() and List#getAt handled with the bracket syntax are properly // compiled ClassNode[] args = {aType}; boolean acceptAnyMethod = MAP_TYPE.equals(rType) || rType.implementsInterface(MAP_TYPE) || LIST_TYPE.equals(rType) || rType.implementsInterface(LIST_TYPE); List<MethodNode> nodes = StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments( controller.getSourceUnit().getClassLoader(), rType, message, args); if (nodes.isEmpty()) { // retry with raw types rType = rType.getPlainNodeReference(); nodes = StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments( controller.getSourceUnit().getClassLoader(), rType, message, args); } nodes = StaticTypeCheckingSupport.chooseBestMethod(rType, nodes, args); if (nodes.size() == 1 || nodes.size() > 1 && acceptAnyMethod) { MethodNode methodNode = nodes.get(0); MethodCallExpression call = new MethodCallExpression(receiver, message, arguments); call.setSourcePosition(arguments); call.setImplicitThis(false); call.setMethodTarget(methodNode); call.visit(controller.getAcg()); return true; } if (implementsInterfaceOrIsSubclassOf(rType, MAP_TYPE)) { // fallback to Map#get MethodCallExpression call = new MethodCallExpression(receiver, "get", arguments); call.setMethodTarget(MAP_GET_METHOD); call.setSourcePosition(arguments); call.setImplicitThis(false); call.visit(controller.getAcg()); return true; } } } return false; }
private void addAssociationForKey(String key, List properties, ClassNode classNode) { properties.add( new PropertyNode( key, Modifier.PUBLIC, new ClassNode(Set.class), classNode, null, null, null)); }