private void addProperty(ClassNode cNode, PropertyNode pNode) { final FieldNode fn = pNode.getField(); cNode.getFields().remove(fn); cNode.addProperty( pNode.getName(), pNode.getModifiers() | ACC_FINAL, pNode.getType(), pNode.getInitialExpression(), pNode.getGetterBlock(), pNode.getSetterBlock()); final FieldNode newfn = cNode.getField(fn.getName()); cNode.getFields().remove(newfn); cNode.addField(fn); }
private void assertMembersNamesAreUnique() { Map<String, FieldNode> allDslCollectionFieldNodesOfHierarchy = new HashMap<String, FieldNode>(); for (ClassNode level : ASTHelper.getHierarchyOfDSLObjectAncestors(annotatedClass)) { for (FieldNode field : level.getFields()) { if (!ASTHelper.isListOrMap(field.getType())) continue; String memberName = getElementNameForCollectionField(field); FieldNode conflictingField = allDslCollectionFieldNodesOfHierarchy.get(memberName); if (conflictingField != null) { addCompileError( String.format( "Member name %s is used more than once: %s:%s and %s:%s", memberName, field.getOwner().getName(), field.getName(), conflictingField.getOwner().getName(), conflictingField.getName()), field); return; } allDslCollectionFieldNodesOfHierarchy.put(memberName, field); } } }
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; }
public static List<FieldNode> getInstanceNonPropertyFields(ClassNode cNode) { final List<FieldNode> result = new ArrayList<FieldNode>(); for (FieldNode fNode : cNode.getFields()) { if (!fNode.isStatic() && cNode.getProperty(fNode.getName()) == null) { result.add(fNode); } } return result; }
public static List<FieldNode> getSuperNonPropertyFields(ClassNode cNode) { final List<FieldNode> result; if (cNode == ClassHelper.OBJECT_TYPE) { result = new ArrayList<FieldNode>(); } else { result = getSuperNonPropertyFields(cNode.getSuperClass()); } for (FieldNode fNode : cNode.getFields()) { if (!fNode.isStatic() && cNode.getProperty(fNode.getName()) == null) { result.add(fNode); } } return result; }
/** * If we are in a constructor, that is static compiled, but in a class, that is not, it may happen * that init code from object initializers, fields or properties is added into the constructor * code. The backend assumes a purely static contructor, so it may fail if it encounters dynamic * code here. Thus we make this kind of code fail */ private void checkForConstructorWithCSButClassWithout(MethodNode node) { if (!(node instanceof ConstructorNode)) return; Object meta = node.getNodeMetaData(STATIC_COMPILE_NODE); if (!Boolean.TRUE.equals(meta)) return; ClassNode clz = typeCheckingContext.getEnclosingClassNode(); meta = clz.getNodeMetaData(STATIC_COMPILE_NODE); if (Boolean.TRUE.equals(meta)) return; if (clz.getObjectInitializerStatements().isEmpty() && clz.getFields().isEmpty() && clz.getProperties().isEmpty()) { return; } addStaticTypeError( "Cannot statically compile constructor implicitly including non static elements from object initializers, properties or fields.", node); }
private void visitClassNode(ClassNode cNode, List<PackageScopeTarget> value) { String cName = cNode.getName(); if (cNode.isInterface() && value.size() != 1 && value.get(0) != PackageScopeTarget.CLASS) { throw new RuntimeException( "Error processing interface '" + cName + "'. " + MY_TYPE_NAME + " not allowed for interfaces except when targeting Class level."); } if (value.contains(groovy.transform.PackageScopeTarget.CLASS)) { if (cNode.isSyntheticPublic()) revertVisibility(cNode); else throw new RuntimeException( "Can't use " + MY_TYPE_NAME + " for class '" + cNode.getName() + "' which has explicit visibility."); } if (value.contains(groovy.transform.PackageScopeTarget.METHODS)) { final List<MethodNode> mList = cNode.getMethods(); for (MethodNode mNode : mList) { if (mNode.isSyntheticPublic()) revertVisibility(mNode); } } if (value.contains(PackageScopeTarget.FIELDS)) { final List<PropertyNode> pList = cNode.getProperties(); List<PropertyNode> foundProps = new ArrayList<PropertyNode>(); List<String> foundNames = new ArrayList<String>(); for (PropertyNode pNode : pList) { foundProps.add(pNode); foundNames.add(pNode.getName()); } for (PropertyNode pNode : foundProps) { pList.remove(pNode); } final List<FieldNode> fList = cNode.getFields(); for (FieldNode fNode : fList) { if (foundNames.contains(fNode.getName())) { revertVisibility(fNode); } } } }
private void validateFieldAnnotations() { for (FieldNode fieldNode : annotatedClass.getFields()) { if (shouldFieldBeIgnored(fieldNode)) continue; AnnotationNode annotation = getAnnotation(fieldNode, DSL_FIELD_ANNOTATION); if (annotation == null) continue; if (ASTHelper.isListOrMap(fieldNode.getType())) return; if (annotation.getMember("members") != null) { addCompileError( String.format( "@Field.members is only valid for List or Map fields, but field %s is of type %s", fieldNode.getName(), fieldNode.getType().getName()), annotation); } } }
private void printFields(PrintWriter out, ClassNode classNode) { boolean isInterface = classNode.isInterface(); List<FieldNode> fields = classNode.getFields(); if (fields == null) return; List<FieldNode> enumFields = new ArrayList<FieldNode>(fields.size()); List<FieldNode> normalFields = new ArrayList<FieldNode>(fields.size()); for (FieldNode field : fields) { boolean isSynthetic = (field.getModifiers() & Opcodes.ACC_SYNTHETIC) != 0; if (field.isEnum()) { enumFields.add(field); } else if (!isSynthetic) { normalFields.add(field); } } printEnumFields(out, enumFields); for (FieldNode normalField : normalFields) { printField(out, normalField, isInterface); } }
private void validateFields(BlockStatement block) { Validation.Option mode = getEnumMemberValue( getAnnotation(annotatedClass, VALIDATION_ANNOTATION), "option", Validation.Option.class, Validation.Option.IGNORE_UNMARKED); for (FieldNode fieldNode : annotatedClass.getFields()) { if (shouldFieldBeIgnoredForValidation(fieldNode)) continue; ClosureExpression validationClosure = createGroovyTruthClosureExpression(block.getVariableScope()); String message = null; AnnotationNode validateAnnotation = getAnnotation(fieldNode, VALIDATE_ANNOTATION); if (validateAnnotation != null) { message = getMemberStringValue( validateAnnotation, "message", "'" + fieldNode.getName() + "' must be set!"); Expression member = validateAnnotation.getMember("value"); if (member instanceof ClassExpression) { ClassNode memberType = member.getType(); if (memberType.equals(ClassHelper.make(Validate.Ignore.class))) continue; else if (!memberType.equals(ClassHelper.make(Validate.GroovyTruth.class))) { addError( "value of Validate must be either Validate.GroovyTruth, Validate.Ignore or a closure.", fieldNode); } } else if (member instanceof ClosureExpression) { validationClosure = (ClosureExpression) member; } } if (validateAnnotation != null || mode == Validation.Option.VALIDATE_UNMARKED) { block.addStatement( new AssertStatement( new BooleanExpression( callX(validationClosure, "call", args(varX(fieldNode.getName())))), message == null ? ConstantExpression.NULL : new ConstantExpression(message))); } } }
private void createConstructorMapCommon(ClassNode cNode, BlockStatement body) { final List<FieldNode> fList = cNode.getFields(); for (FieldNode fNode : fList) { if (fNode.isPublic()) continue; // public fields will be rejected elsewhere if (cNode.getProperty(fNode.getName()) != null) continue; // a property if (fNode.isFinal() && fNode.isStatic()) continue; if (fNode.getName().contains("$")) continue; // internal field if (fNode.isFinal() && fNode.getInitialExpression() != null) body.addStatement(checkFinalArgNotOverridden(cNode, fNode)); body.addStatement(createConstructorStatementDefault(fNode)); } final Parameter[] params = new Parameter[] {new Parameter(HASHMAP_TYPE, "args")}; doAddConstructor( cNode, new ConstructorNode( ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, new IfStatement( equalsNullExpr(new VariableExpression("args")), new EmptyStatement(), body))); }
public void visit(ASTNode[] nodes, SourceUnit source) { init(nodes, source); AnnotatedNode parent = (AnnotatedNode) nodes[1]; AnnotationNode node = (AnnotationNode) nodes[0]; // temporarily have weaker check which allows for old Deprecated Annotation // if (!MY_TYPE.equals(node.getClassNode())) return; if (!node.getClassNode().getName().endsWith(".Immutable")) return; List<PropertyNode> newProperties = new ArrayList<PropertyNode>(); if (parent instanceof ClassNode) { final List<String> knownImmutableClasses = getKnownImmutableClasses(node); final List<String> knownImmutables = getKnownImmutables(node); ClassNode cNode = (ClassNode) parent; String cName = cNode.getName(); checkNotInterface(cNode, MY_TYPE_NAME); makeClassFinal(cNode); final List<PropertyNode> pList = getInstanceProperties(cNode); for (PropertyNode pNode : pList) { adjustPropertyForImmutability(pNode, newProperties); } for (PropertyNode pNode : newProperties) { cNode.getProperties().remove(pNode); addProperty(cNode, pNode); } final List<FieldNode> fList = cNode.getFields(); for (FieldNode fNode : fList) { ensureNotPublic(cName, fNode); } createConstructors(cNode, knownImmutableClasses, knownImmutables); if (!hasAnnotation(cNode, EqualsAndHashCodeASTTransformation.MY_TYPE)) { createHashCode(cNode, true, false, false, null, null); createEquals(cNode, false, false, false, null, null); } if (!hasAnnotation(cNode, ToStringASTTransformation.MY_TYPE)) { createToString(cNode, false, false, null, null, false, true); } } }
private Variable findClassMember(ClassNode cn, String name) { if (cn == null) return null; if (cn.isScript()) { return new DynamicVariable(name, false); } for (FieldNode fn : cn.getFields()) { if (fn.getName().equals(name)) return fn; } for (MethodNode mn : cn.getMethods()) { String pName = getPropertyName(mn); // GRECLIPSE: start // must set the declaringClass /*old{ if (pName != null && pName.equals(name)) return new PropertyNode(pName, mn.getModifiers(), getPropertyType(mn), cn, null, null, null); */ // newcode if (pName != null && pName.equals(name)) { PropertyNode property = new PropertyNode(pName, mn.getModifiers(), getPropertyType(mn), cn, null, null, null); property.setDeclaringClass(cn); property.getField().setDeclaringClass(cn); return property; } // GRECLIPSE: end } for (PropertyNode pn : cn.getProperties()) { if (pn.getName().equals(name)) return pn; } Variable ret = findClassMember(cn.getSuperClass(), name); if (ret != null) return ret; return findClassMember(cn.getOuterClass(), name); }
/** Adds special accessors for private constants so that inner classes can retrieve them. */ @SuppressWarnings("unchecked") private void addPrivateFieldsAccessors(ClassNode node) { Set<ASTNode> accessedFields = (Set<ASTNode>) node.getNodeMetaData(StaticTypesMarker.PV_FIELDS_ACCESS); if (accessedFields == null) return; Map<String, MethodNode> privateConstantAccessors = (Map<String, MethodNode>) node.getNodeMetaData(PRIVATE_FIELDS_ACCESSORS); if (privateConstantAccessors != null) { // already added return; } int acc = -1; privateConstantAccessors = new HashMap<String, MethodNode>(); final int access = Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC; for (FieldNode fieldNode : node.getFields()) { if (accessedFields.contains(fieldNode)) { acc++; Parameter param = new Parameter(node.getPlainNodeReference(), "$that"); Expression receiver = fieldNode.isStatic() ? new ClassExpression(node) : new VariableExpression(param); Statement stmt = new ExpressionStatement(new PropertyExpression(receiver, fieldNode.getName())); MethodNode accessor = node.addMethod( "pfaccess$" + acc, access, fieldNode.getOriginType(), new Parameter[] {param}, ClassNode.EMPTY_ARRAY, stmt); privateConstantAccessors.put(fieldNode.getName(), accessor); } } node.setNodeMetaData(PRIVATE_FIELDS_ACCESSORS, privateConstantAccessors); }
private Set<String> getPropertyNamesToIncludeInWhiteList( final SourceUnit sourceUnit, final ClassNode classNode) { if (CLASS_NAME_TO_WHITE_LIST_PROPERTY_NAMES.containsKey(classNode)) { return CLASS_NAME_TO_WHITE_LIST_PROPERTY_NAMES.get(classNode); } final Set<String> propertyNamesToIncludeInWhiteList = new HashSet<String>(); final Set<String> unbindablePropertyNames = new HashSet<String>(); final Set<String> bindablePropertyNames = new HashSet<String>(); if (!classNode.getSuperClass().equals(new ClassNode(Object.class))) { final Set<String> parentClassPropertyNames = getPropertyNamesToIncludeInWhiteList(sourceUnit, classNode.getSuperClass()); bindablePropertyNames.addAll(parentClassPropertyNames); } final FieldNode constraintsFieldNode = classNode.getDeclaredField(CONSTRAINTS_FIELD_NAME); if (constraintsFieldNode != null && constraintsFieldNode.hasInitialExpression()) { final Expression constraintsInitialExpression = constraintsFieldNode.getInitialExpression(); if (constraintsInitialExpression instanceof ClosureExpression) { final Map<String, Map<String, Expression>> constraintsInfo = GrailsASTUtils.getConstraintMetadata((ClosureExpression) constraintsInitialExpression); for (Entry<String, Map<String, Expression>> constraintConfig : constraintsInfo.entrySet()) { final String propertyName = constraintConfig.getKey(); final Map<String, Expression> mapEntryExpressions = constraintConfig.getValue(); for (Entry<String, Expression> entry : mapEntryExpressions.entrySet()) { final String constraintName = entry.getKey(); if (BINDABLE_CONSTRAINT_NAME.equals(constraintName)) { final Expression valueExpression = entry.getValue(); Boolean bindableValue = null; if (valueExpression instanceof ConstantExpression) { final Object constantValue = ((ConstantExpression) valueExpression).getValue(); if (constantValue instanceof Boolean) { bindableValue = (Boolean) constantValue; } } if (bindableValue != null) { if (Boolean.TRUE.equals(bindableValue)) { unbindablePropertyNames.remove(propertyName); bindablePropertyNames.add(propertyName); } else { bindablePropertyNames.remove(propertyName); unbindablePropertyNames.add(propertyName); } } else { final String message = "The bindable constraint for property [" + propertyName + "] in class [" + classNode.getName() + "] has a value which is not a boolean literal and will be ignored."; GrailsASTUtils.warning(sourceUnit, valueExpression, message); } } } } } } final Set<String> fieldsInTransientsList = getPropertyNamesExpressedInTransientsList(classNode); propertyNamesToIncludeInWhiteList.addAll(bindablePropertyNames); final List<FieldNode> fields = classNode.getFields(); for (FieldNode fieldNode : fields) { final String fieldName = fieldNode.getName(); if ((!unbindablePropertyNames.contains(fieldName)) && (bindablePropertyNames.contains(fieldName) || shouldFieldBeInWhiteList(fieldNode, fieldsInTransientsList))) { propertyNamesToIncludeInWhiteList.add(fieldName); } } final Map<String, MethodNode> declaredMethodsMap = classNode.getDeclaredMethodsMap(); for (Entry<String, MethodNode> methodEntry : declaredMethodsMap.entrySet()) { final MethodNode value = methodEntry.getValue(); if (value.getDeclaringClass() == classNode) { Parameter[] parameters = value.getParameters(); if (parameters != null && parameters.length == 1) { final String methodName = value.getName(); if (methodName.startsWith("set")) { final Parameter parameter = parameters[0]; final ClassNode paramType = parameter.getType(); if (!paramType.equals(new ClassNode(Object.class))) { final String restOfMethodName = methodName.substring(3); final String propertyName = GrailsNameUtils.getPropertyName(restOfMethodName); if (!unbindablePropertyNames.contains(propertyName)) { propertyNamesToIncludeInWhiteList.add(propertyName); } } } } } } CLASS_NAME_TO_WHITE_LIST_PROPERTY_NAMES.put(classNode, propertyNamesToIncludeInWhiteList); Map<String, ClassNode> allAssociationMap = GrailsASTUtils.getAllAssociationMap(classNode); for (String associationName : allAssociationMap.keySet()) { if (!propertyNamesToIncludeInWhiteList.contains(associationName)) { propertyNamesToIncludeInWhiteList.add(associationName); } } return propertyNamesToIncludeInWhiteList; }
private void createFieldMethods() { for (FieldNode fieldNode : annotatedClass.getFields()) createMethodsForSingleField(fieldNode); }