private void checkClassForOtherModifiers(ClassNode node) { checkClassForModifier(node, isTransient(node.getModifiers()), "transient"); checkClassForModifier(node, isVolatile(node.getModifiers()), "volatile"); checkClassForModifier(node, isNative(node.getModifiers()), "native"); if (!(node instanceof InnerClassNode)) { checkClassForModifier(node, isStatic(node.getModifiers()), "static"); } // don't check synchronized here as it overlaps with ACC_SUPER }
private void checkClassForOverwritingFinal(ClassNode cn) { ClassNode superCN = cn.getSuperClass(); if (superCN == null) return; if (!isFinal(superCN.getModifiers())) return; StringBuilder msg = new StringBuilder(); msg.append("You are not allowed to overwrite the final "); msg.append(getDescription(superCN)); msg.append("."); addError(msg.toString(), cn); }
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 void checkMethodsForOverridingFinal(ClassNode cn) { for (MethodNode method : cn.getMethods()) { Parameter[] params = method.getParameters(); for (MethodNode superMethod : cn.getSuperClass().getMethods(method.getName())) { Parameter[] superParams = superMethod.getParameters(); if (!hasEqualParameterTypes(params, superParams)) continue; if (!superMethod.isFinal()) break; addInvalidUseOfFinalError(method, params, superMethod.getDeclaringClass()); return; } } }
private void checkNoAbstractMethodsNonabstractClass(ClassNode node) { if (isAbstract(node.getModifiers())) return; List<MethodNode> abstractMethods = node.getAbstractMethods(); if (abstractMethods == null) return; for (MethodNode method : abstractMethods) { addError( "Can't have an abstract method in a non-abstract class." + " The " + getDescription(node) + " must be declared abstract or" + " the " + getDescription(method) + " must be implemented.", node); } }
private void throwExceptionForNoStackElement(int size, ClassNode targetType, boolean coerce) { if (size > 0) return; StringBuilder sb = new StringBuilder(); sb.append("Internal compiler error while compiling ") .append(controller.getSourceUnit().getName()) .append("\n"); MethodNode methodNode = controller.getMethodNode(); if (methodNode != null) { sb.append("Method: "); sb.append(methodNode); sb.append("\n"); } ConstructorNode constructorNode = controller.getConstructorNode(); if (constructorNode != null) { sb.append("Constructor: "); sb.append(methodNode); sb.append("\n"); } sb.append("Line ").append(controller.getLineNumber()).append(","); sb.append(" expecting ") .append(coerce ? "coercion" : "casting") .append(" to ") .append(targetType.toString(false)); sb.append(" but operand stack is empty"); throw new ArrayIndexOutOfBoundsException(sb.toString()); }
public void visitField(FieldNode node) { if (currentClass.getDeclaredField(node.getName()) != node) { addError("The " + getDescription(node) + " is declared multiple times.", node); } checkInterfaceFieldModifiers(node); checkGenericsUsage(node, node.getType()); super.visitField(node); }
public void visitClass(ClassNode node) { ClassNode oldClass = currentClass; currentClass = node; checkImplementsAndExtends(node); if (source != null && !source.getErrorCollector().hasErrors()) { checkClassForIncorrectModifiers(node); checkInterfaceMethodVisibility(node); checkClassForOverwritingFinal(node); checkMethodsForIncorrectModifiers(node); checkMethodsForWeakerAccess(node); checkMethodsForOverridingFinal(node); checkNoAbstractMethodsNonabstractClass(node); checkGenericsUsage(node, node.getUnresolvedInterfaces()); checkGenericsUsage(node, node.getUnresolvedSuperClass()); } super.visitClass(node); currentClass = oldClass; }
private void checkGenericsUsage(ASTNode ref, ClassNode node) { if (node.isArray()) { checkGenericsUsage(ref, node.getComponentType()); } else if (!node.isRedirectNode() && node.isUsingGenerics()) { addError( "A transform used a generics containing ClassNode " + node + " " + "for " + getRefDescriptor(ref) + "directly. You are not suppposed to do this. " + "Please create a new ClassNode refering to the old ClassNode " + "and use the new ClassNode instead of the old one. Otherwise " + "the compiler will create wrong descriptors and a potential " + "NullPointerException in TypeResolver in the OpenJDK. If this is " + "not your own doing, please report this bug to the writer of the " + "transform.", ref); } }
private void checkRepetitiveMethod(MethodNode node) { if (isConstructor(node)) return; for (MethodNode method : currentClass.getMethods(node.getName())) { if (method == node) continue; if (!method.getDeclaringClass().equals(node.getDeclaringClass())) continue; Parameter[] p1 = node.getParameters(); Parameter[] p2 = method.getParameters(); if (p1.length != p2.length) continue; addErrorIfParamsAndReturnTypeEqual(p2, p1, node, method); } }
/** load the constant on the operand stack. */ public void pushConstant(ConstantExpression expression) { MethodVisitor mv = controller.getMethodVisitor(); Object value = expression.getValue(); ClassNode origType = expression.getType().redirect(); ClassNode type = ClassHelper.getUnwrapper(origType); boolean boxing = origType != type; boolean asPrimitive = boxing || ClassHelper.isPrimitiveType(type); if (value == null) { mv.visitInsn(ACONST_NULL); } else if (boxing && value instanceof Boolean) { // special path for boxed boolean Boolean bool = (Boolean) value; String text = bool ? "TRUE" : "FALSE"; mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", text, "Ljava/lang/Boolean;"); boxing = false; type = origType; } else if (asPrimitive) { pushPrimitiveConstant(mv, value, type); } else if (value instanceof BigDecimal) { String className = BytecodeHelper.getClassInternalName(value.getClass().getName()); mv.visitTypeInsn(NEW, className); mv.visitInsn(DUP); mv.visitLdcInsn(value.toString()); mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "(Ljava/lang/String;)V", false); } else if (value instanceof BigInteger) { String className = BytecodeHelper.getClassInternalName(value.getClass().getName()); mv.visitTypeInsn(NEW, className); mv.visitInsn(DUP); mv.visitLdcInsn(value.toString()); mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "(Ljava/lang/String;)V", false); } else if (value instanceof String) { mv.visitLdcInsn(value); } else { throw new ClassGeneratorException( "Cannot generate bytecode for constant: " + value + " of type: " + type.getName()); } push(type); if (boxing) box(); }
private void checkAbstractDeclaration(MethodNode methodNode) { if (!methodNode.isAbstract()) return; if (isAbstract(currentClass.getModifiers())) return; addError( "Can't have an abstract method in a non-abstract class." + " The " + getDescription(currentClass) + " must be declared abstract or the method '" + methodNode.getTypeDescriptor() + "' must not be abstract.", methodNode); }
private void checkMethodForWeakerAccessPrivileges(MethodNode mn, ClassNode cn) { Parameter[] params = mn.getParameters(); for (MethodNode superMethod : cn.getSuperClass().getMethods(mn.getName())) { Parameter[] superParams = superMethod.getParameters(); if (!hasEqualParameterTypes(params, superParams)) continue; if ((mn.isPrivate() && !superMethod.isPrivate()) || (mn.isProtected() && superMethod.isPublic())) { addWeakerAccessError(cn, mn, params, superMethod); return; } } }
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); } } }
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); } }
private void checkDuplicateProperties(PropertyNode node) { ClassNode cn = node.getDeclaringClass(); String name = node.getName(); String getterName = "get" + MetaClassHelper.capitalize(name); if (Character.isUpperCase(name.charAt(0))) { for (PropertyNode propNode : cn.getProperties()) { String otherName = propNode.getField().getName(); String otherGetterName = "get" + MetaClassHelper.capitalize(otherName); if (node != propNode && getterName.equals(otherGetterName)) { String msg = "The field " + name + " and " + otherName + " on the class " + cn.getName() + " will result in duplicate JavaBean properties, which is not allowed"; addError(msg, node); } } } }
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 doConvertAndCast(ClassNode targetType, boolean coerce) { int size = stack.size(); throwExceptionForNoStackElement(size, targetType, coerce); ClassNode top = stack.get(size - 1); targetType = targetType.redirect(); if (targetType == top) return; if (coerce) { controller.getInvocationWriter().coerce(top, targetType); return; } boolean primTarget = ClassHelper.isPrimitiveType(targetType); boolean primTop = ClassHelper.isPrimitiveType(top); if (primTop && primTarget) { // here we box and unbox to get the goal type if (convertPrimitive(top, targetType)) { replace(targetType); return; } box(); } else if (primTop) { // top is primitive, target is not // so box and do groovy cast controller.getInvocationWriter().castToNonPrimitiveIfNecessary(top, targetType); } else if (primTarget) { // top is not primitive so unbox // leave that BH#doCast later } else { controller.getInvocationWriter().castToNonPrimitiveIfNecessary(top, targetType); } MethodVisitor mv = controller.getMethodVisitor(); if (primTarget && !ClassHelper.boolean_TYPE.equals(targetType) && !primTop && ClassHelper.getWrapper(targetType).equals(top)) { BytecodeHelper.doCastToPrimitive(mv, top, targetType); } else { top = stack.get(size - 1); if (!WideningCategories.implementsInterfaceOrSubclassOf(top, targetType)) { BytecodeHelper.doCast(mv, targetType); } } replace(targetType); }
private void checkOverloadingPrivateAndPublic(MethodNode node) { if (isConstructor(node)) return; boolean hasPrivate = false; boolean hasPublic = false; for (MethodNode method : currentClass.getMethods(node.getName())) { if (method == node) continue; if (!method.getDeclaringClass().equals(node.getDeclaringClass())) continue; if (method.isPublic() || method.isProtected()) { hasPublic = true; } else { hasPrivate = true; } } if (hasPrivate && hasPublic) { addError( "Mixing private and public/protected methods of the same name causes multimethods to be disabled and is forbidden to avoid surprising behaviour. Renaming the private methods will solve the problem.", node); } }
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 checkFinalFieldAccess(Expression expression) { if (!(expression instanceof VariableExpression) && !(expression instanceof PropertyExpression)) return; Variable v = null; if (expression instanceof VariableExpression) { VariableExpression ve = (VariableExpression) expression; v = ve.getAccessedVariable(); } else { PropertyExpression propExp = ((PropertyExpression) expression); Expression objectExpression = propExp.getObjectExpression(); if (objectExpression instanceof VariableExpression) { VariableExpression varExp = (VariableExpression) objectExpression; if (varExp.isThisExpression()) { v = currentClass.getDeclaredField(propExp.getPropertyAsString()); } } } if (v instanceof FieldNode) { FieldNode fn = (FieldNode) v; /* * if it is static final but not accessed inside a static constructor, or, * if it is an instance final but not accessed inside a instance constructor, it is an error */ boolean isFinal = fn.isFinal(); boolean isStatic = fn.isStatic(); boolean error = isFinal && ((isStatic && !inStaticConstructor) || (!isStatic && !inConstructor)); if (error) addError( "cannot modify" + (isStatic ? " static" : "") + " final field '" + fn.getName() + "' outside of " + (isStatic ? "static initialization block." : "constructor."), expression); } }
private void addWeakerAccessError( ClassNode cn, MethodNode method, Parameter[] parameters, MethodNode superMethod) { StringBuilder msg = new StringBuilder(); msg.append(method.getName()); msg.append("("); boolean needsComma = false; for (Parameter parameter : parameters) { if (needsComma) { msg.append(","); } else { needsComma = true; } msg.append(parameter.getType()); } msg.append(") in "); msg.append(cn.getName()); msg.append(" cannot override "); msg.append(superMethod.getName()); msg.append(" in "); msg.append(superMethod.getDeclaringClass().getName()); msg.append("; attempting to assign weaker access privileges; was "); msg.append(superMethod.isPublic() ? "public" : "protected"); addError(msg.toString(), method); }
private void checkMethodsForWeakerAccess(ClassNode cn) { for (MethodNode method : cn.getMethods()) { checkMethodForWeakerAccessPrivileges(method, cn); } }
private String getDescription(ClassNode node) { return (node.isInterface() ? "interface" : "class") + " '" + node.getName() + "'"; }