private void checkImplementsAndExtends(ClassNode node) { ClassNode cn = node.getSuperClass(); if (cn.isInterface() && !node.isInterface()) { addError( "You are not allowed to extend the " + getDescription(cn) + ", use implements instead.", node); } for (ClassNode anInterface : node.getInterfaces()) { cn = anInterface; if (!cn.isInterface()) { addError( "You are not allowed to implement the " + getDescription(cn) + ", use extends instead.", node); } } }
private void checkClassForAbstractAndFinal(ClassNode node) { if (!isAbstract(node.getModifiers())) return; if (!isFinal(node.getModifiers())) return; if (node.isInterface()) { addError( "The " + getDescription(node) + " must not be final. It is by definition abstract.", node); } else { addError("The " + getDescription(node) + " must not be both final and abstract.", node); } }
private static void writeGenericsBoundType( StringBuffer ret, ClassNode printType, boolean writeInterfaceMarker) { if (writeInterfaceMarker && printType.isInterface()) ret.append(":"); if (printType.equals(ClassHelper.OBJECT_TYPE) && printType.getGenericsTypes() != null) { ret.append("T"); ret.append(printType.getGenericsTypes()[0].getName()); ret.append(";"); } else { ret.append(getTypeDescription(printType, false)); addSubTypes(ret, printType.getGenericsTypes(), "<", ">"); if (!ClassHelper.isPrimitiveType(printType)) ret.append(";"); } }
private void checkInterfaceFieldModifiers(FieldNode node) { if (!currentClass.isInterface()) return; if ((node.getModifiers() & (ACC_PUBLIC | ACC_STATIC | ACC_FINAL)) == 0 || (node.getModifiers() & (ACC_PRIVATE | ACC_PROTECTED)) != 0) { addError( "The " + getDescription(node) + " is not 'public static final' but is defined in " + getDescription(currentClass) + ".", node); } }
public static Set<ClassNode> getInterfacesAndSuperInterfaces(ClassNode type) { Set<ClassNode> res = new HashSet<ClassNode>(); if (type.isInterface()) { res.add(type); return res; } ClassNode next = type; while (next != null) { Collections.addAll(res, next.getInterfaces()); next = next.getSuperClass(); } return res; }
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 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 checkInterfaceMethodVisibility(ClassNode node) { if (!node.isInterface()) return; for (MethodNode method : node.getMethods()) { if (method.isPrivate()) { addError( "Method '" + method.getName() + "' is private but should be public in " + getDescription(currentClass) + ".", method); } else if (method.isProtected()) { addError( "Method '" + method.getName() + "' is protected but should be public in " + getDescription(currentClass) + ".", method); } } }
public static boolean implementsInterfaceOrIsSubclassOf( ClassNode type, ClassNode superOrInterface) { boolean result = type.equals(superOrInterface) || type.isDerivedFrom(superOrInterface) || type.implementsInterface(superOrInterface) || type == UNKNOWN_PARAMETER_TYPE; if (result) { return true; } if (superOrInterface instanceof WideningCategories.LowestUpperBoundClassNode) { WideningCategories.LowestUpperBoundClassNode cn = (WideningCategories.LowestUpperBoundClassNode) superOrInterface; result = implementsInterfaceOrIsSubclassOf(type, cn.getSuperClass()); if (result) { for (ClassNode interfaceNode : cn.getInterfaces()) { result = type.implementsInterface(interfaceNode); if (!result) break; } } if (result) return true; } else if (superOrInterface instanceof UnionTypeClassNode) { UnionTypeClassNode union = (UnionTypeClassNode) superOrInterface; for (ClassNode delegate : union.getDelegates()) { if (implementsInterfaceOrIsSubclassOf(type, delegate)) return true; } } if (type.isArray() && superOrInterface.isArray()) { return implementsInterfaceOrIsSubclassOf( type.getComponentType(), superOrInterface.getComponentType()); } if (GROOVY_OBJECT_TYPE.equals(superOrInterface) && !type.isInterface() && isBeingCompiled(type)) { return true; } return false; }
private void checkMethodsForIncorrectModifiers(ClassNode cn) { if (!cn.isInterface()) return; for (MethodNode method : cn.getMethods()) { if (method.isFinal()) { addError( "The " + getDescription(method) + " from " + getDescription(cn) + " must not be final. It is by definition abstract.", method); } if (method.isStatic() && !isConstructor(method)) { addError( "The " + getDescription(method) + " from " + getDescription(cn) + " must not be static. Only fields may be static in an interface.", method); } } }
private void printMethod(PrintWriter out, ClassNode clazz, MethodNode methodNode) { if (methodNode.getName().equals("<clinit>")) return; if (methodNode.isPrivate() || !Utilities.isJavaIdentifier(methodNode.getName())) return; if (methodNode.isSynthetic() && methodNode.getName().equals("$getStaticMetaClass")) return; printAnnotations(out, methodNode); if (!clazz.isInterface()) printModifiers(out, methodNode.getModifiers()); printGenericsBounds(out, methodNode.getGenericsTypes()); out.print(" "); printType(out, methodNode.getReturnType()); out.print(" "); out.print(methodNode.getName()); printParams(out, methodNode); ClassNode[] exceptions = methodNode.getExceptions(); for (int i = 0; i < exceptions.length; i++) { ClassNode exception = exceptions[i]; if (i == 0) { out.print("throws "); } else { out.print(", "); } printType(out, exception); } if ((methodNode.getModifiers() & Opcodes.ACC_ABSTRACT) != 0) { out.println(";"); } else { out.print(" { "); ClassNode retType = methodNode.getReturnType(); printReturn(out, retType); out.println("}"); } }
@Override public void visit(ASTNode[] astNodes, SourceUnit source) { if (!(astNodes[0] instanceof AnnotationNode) || !(astNodes[1] instanceof AnnotatedNode)) { throw new RuntimeException("Internal error: wrong types: $node.class / $parent.class"); } AnnotatedNode parent = (AnnotatedNode) astNodes[1]; AnnotationNode node = (AnnotationNode) astNodes[0]; if (!MY_TYPE.equals(node.getClassNode()) || !(parent instanceof ClassNode)) { return; } ClassNode classNode = (ClassNode) parent; if (classNode.isInterface() || Modifier.isAbstract(classNode.getModifiers())) { return; } boolean junit3Test = isJunit3Test(classNode); boolean spockTest = isSpockTest(classNode); boolean isJunit = classNode.getName().endsWith("Tests"); if (!junit3Test && !spockTest && !isJunit) return; Expression value = node.getMember("value"); ClassExpression ce; if (value instanceof ClassExpression) { ce = (ClassExpression) value; testFor(classNode, ce); } else { if (!junit3Test) { List<AnnotationNode> annotations = classNode.getAnnotations(MY_TYPE); if (annotations.size() > 0) return; // bail out, in this case it was already applied as a local transform // no explicit class specified try by convention String fileName = source.getName(); String className = GrailsResourceUtils.getClassName(new FileSystemResource(fileName)); if (className != null) { boolean isSpock = className.endsWith("Spec"); String targetClassName = null; if (isJunit) { targetClassName = className.substring(0, className.indexOf("Tests")); } else if (isSpock) { targetClassName = className.substring(0, className.indexOf("Spec")); } if (targetClassName != null) { Resource targetResource = getResourceLocator().findResourceForClassName(targetClassName); if (targetResource != null) { try { if (GrailsResourceUtils.isDomainClass(targetResource.getURL())) { testFor( classNode, new ClassExpression( new ClassNode(targetClassName, 0, ClassHelper.OBJECT_TYPE))); } else { for (String artefactType : artefactTypeToTestMap.keySet()) { if (targetClassName.endsWith(artefactType)) { testFor( classNode, new ClassExpression( new ClassNode(targetClassName, 0, ClassHelper.OBJECT_TYPE))); break; } } } } catch (IOException e) { // ignore } } } } } } }
@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 List getPrimaryClassNodes(boolean sort) { if (sort == true) { List<ModuleNode> sortedModules = this.ast.getSortedModules(); if (sortedModules != null) { return sortedModules; } } // FIXASC (groovychange) rewritten /*old{ List unsorted = new ArrayList(); Iterator modules = this.ast.getModules().iterator(); while (modules.hasNext()) { ModuleNode module = (ModuleNode) modules.next(); Iterator classNodes = module.getClasses().iterator(); while (classNodes.hasNext()) { ClassNode classNode = (ClassNode) classNodes.next(); unsorted.add(classNode); } } */ // new List<ClassNode> unsorted = new ArrayList<ClassNode>(); for (ModuleNode module : this.ast.getModules()) { unsorted.addAll(module.getClasses()); } // FIXASC (groovychange) end if (!sort) return unsorted; // GRECLIPSE: start: rewritten sort algorithm /*old{ int[] indexClass = new int[unsorted.size()]; int[] indexInterface = new int[unsorted.size()]; { int i = 0; for (Iterator<ClassNode> iter = unsorted.iterator(); iter.hasNext(); i++) { ClassNode element = iter.next(); if (element.isInterface()) { indexInterface[i] = getSuperInterfaceCount(element); indexClass[i] = -1; } else { indexClass[i] = getSuperClassCount(element); indexInterface[i] = -1; } } } List<ClassNode> sorted = getSorted(indexInterface, unsorted); sorted.addAll(getSorted(indexClass, unsorted)); */ // newcode: // Sort them by how many types are in their hierarchy, but all interfaces first. // Algorithm: // Create a list of integers. Each integer captures the index into the unsorted // list (bottom 16bits) and the count of how many types are in that types // hierarchy (top 16bits). For classes the count is augmented by 2000 so that // when sorting the classes will come out after the interfaces. // This list of integers is sorted. We then just go through it and for the // lower 16bits of each entry (0xffff) that is the index of the next value to // pull from the unsorted list and put into the sorted list. // Will break down if more than 2000 interfaces in the type hierarchy for an // individual type, or a project contains > 65535 files... but if you've got // that kind of setup, you have other problems... List<Integer> countIndexPairs = new ArrayList<Integer>(); { int i = 0; for (Iterator iter = unsorted.iterator(); iter.hasNext(); i++) { ClassNode node = (ClassNode) iter.next(); if (node.isInterface()) { countIndexPairs.add((getSuperInterfaceCount(node) << 16) + i); } else { countIndexPairs.add(((getSuperClassCount(node) + 2000) << 16) + i); } } } Collections.sort(countIndexPairs); List sorted = new ArrayList(); for (int i : countIndexPairs) { sorted.add(unsorted.get(i & 0xffff)); } this.ast.setSortedModules(sorted); // end return sorted; }
private String getDescription(ClassNode node) { return (node.isInterface() ? "interface" : "class") + " '" + node.getName() + "'"; }
private void printClassContents(PrintWriter out, ClassNode classNode) throws FileNotFoundException { if (classNode instanceof InnerClassNode && ((InnerClassNode) classNode).isAnonymous()) { // if it is an anonymous inner class, don't generate the stub code for it. return; } try { Verifier verifier = new Verifier() { public void addCovariantMethods(ClassNode cn) {} protected void addTimeStamp(ClassNode node) {} protected void addInitialization(ClassNode node) {} protected void addPropertyMethod(MethodNode method) { doAddMethod(method); } protected void addReturnIfNeeded(MethodNode node) {} protected void addMethod( ClassNode node, boolean shouldBeSynthetic, String name, int modifiers, ClassNode returnType, Parameter[] parameters, ClassNode[] exceptions, Statement code) { doAddMethod( new MethodNode(name, modifiers, returnType, parameters, exceptions, code)); } protected void addConstructor( Parameter[] newParams, ConstructorNode ctor, Statement code, ClassNode node) { if (code instanceof ExpressionStatement) { // GROOVY-4508 Statement temp = code; code = new BlockStatement(); ((BlockStatement) code).addStatement(temp); } ConstructorNode ctrNode = new ConstructorNode(ctor.getModifiers(), newParams, ctor.getExceptions(), code); ctrNode.setDeclaringClass(node); constructors.add(ctrNode); } protected void addDefaultParameters(DefaultArgsAction action, MethodNode method) { final Parameter[] parameters = method.getParameters(); final Expression[] saved = new Expression[parameters.length]; for (int i = 0; i < parameters.length; i++) { if (parameters[i].hasInitialExpression()) saved[i] = parameters[i].getInitialExpression(); } super.addDefaultParameters(action, method); for (int i = 0; i < parameters.length; i++) { if (saved[i] != null) parameters[i].setInitialExpression(saved[i]); } } private void doAddMethod(MethodNode method) { String sig = method.getTypeDescriptor(); if (propertyMethodsWithSigs.containsKey(sig)) return; propertyMethods.add(method); propertyMethodsWithSigs.put(sig, method); } @Override protected void addDefaultConstructor(ClassNode node) { // not required for stub generation } }; verifier.visitClass(classNode); currentModule = classNode.getModule(); boolean isInterface = classNode.isInterface(); boolean isEnum = (classNode.getModifiers() & Opcodes.ACC_ENUM) != 0; boolean isAnnotationDefinition = classNode.isAnnotationDefinition(); printAnnotations(out, classNode); printModifiers( out, classNode.getModifiers() & ~(isInterface ? Opcodes.ACC_ABSTRACT : 0) & ~(isEnum ? Opcodes.ACC_FINAL : 0)); if (isInterface) { if (isAnnotationDefinition) { out.print("@"); } out.print("interface "); } else if (isEnum) { out.print("enum "); } else { out.print("class "); } String className = classNode.getNameWithoutPackage(); if (classNode instanceof InnerClassNode) className = className.substring(className.lastIndexOf("$") + 1); out.println(className); printGenericsBounds(out, classNode, true); ClassNode superClass = classNode.getUnresolvedSuperClass(false); if (!isInterface && !isEnum) { out.print(" extends "); printType(out, superClass); } ClassNode[] interfaces = classNode.getInterfaces(); if (interfaces != null && interfaces.length > 0 && !isAnnotationDefinition) { if (isInterface) { out.println(" extends"); } else { out.println(" implements"); } for (int i = 0; i < interfaces.length - 1; ++i) { out.print(" "); printType(out, interfaces[i]); out.print(","); } out.print(" "); printType(out, interfaces[interfaces.length - 1]); } out.println(" {"); printFields(out, classNode); printMethods(out, classNode, isEnum); for (Iterator<InnerClassNode> inner = classNode.getInnerClasses(); inner.hasNext(); ) { // GROOVY-4004: Clear the methods from the outer class so that they don't get duplicated in // inner ones propertyMethods.clear(); propertyMethodsWithSigs.clear(); constructors.clear(); printClassContents(out, inner.next()); } out.println("}"); } finally { propertyMethods.clear(); propertyMethodsWithSigs.clear(); constructors.clear(); currentModule = null; } }