private static boolean usesGenericsInClassSignature(ClassNode node) { if (!node.isUsingGenerics()) return false; if (hasGenerics(node)) return true; ClassNode sclass = node.getUnresolvedSuperClass(false); if (sclass.isUsingGenerics()) return true; ClassNode[] interfaces = node.getInterfaces(); if (interfaces != null) { for (int i = 0; i < interfaces.length; i++) { if (interfaces[i].isUsingGenerics()) return true; } } return false; }
/** * Interface class nodes retrieved from {@link org.codehaus.groovy.ast.ClassNode#getInterfaces()} * or {@link org.codehaus.groovy.ast.ClassNode#getAllInterfaces()} are returned with generic type * arguments. This method allows returning a parameterized interface given the parameterized class * node which implements this interface. * * @param hint the class node where generics types are parameterized * @param target the interface we want to parameterize generics types * @return a parameterized interface class node */ public static ClassNode parameterizeType(final ClassNode hint, final ClassNode target) { ClassNode interfaceFromClassNode = null; if (hint.equals(target)) interfaceFromClassNode = hint; if (ClassHelper.OBJECT_TYPE.equals(target) && target.isUsingGenerics() && target.getGenericsTypes() != null && target.getGenericsTypes()[0].isPlaceholder()) { // Object<T> return ClassHelper.getWrapper(hint); } if (interfaceFromClassNode == null) { ClassNode[] interfaces = hint.getInterfaces(); for (ClassNode node : interfaces) { if (node.equals(target)) { interfaceFromClassNode = node; break; } else if (node.implementsInterface(target)) { // ex: classNode = LinkedList<A> , node=List<E> , anInterface = Iterable<T> return parameterizeType(parameterizeType(hint, node), target); } } } if (interfaceFromClassNode == null && hint.getUnresolvedSuperClass() != null) { return parameterizeType(hint.getUnresolvedSuperClass(), target); } if (interfaceFromClassNode == null) { // return target; interfaceFromClassNode = hint; } Map<String, GenericsType> parameters = new HashMap<String, GenericsType>(); extractPlaceholders(hint, parameters); ClassNode node = target.getPlainNodeReference(); GenericsType[] interfaceGTs = interfaceFromClassNode.getGenericsTypes(); if (interfaceGTs == null) return target; GenericsType[] types = new GenericsType[interfaceGTs.length]; for (int i = 0; i < interfaceGTs.length; i++) { GenericsType interfaceGT = interfaceGTs[i]; types[i] = interfaceGT; if (interfaceGT.isPlaceholder()) { String name = interfaceGT.getName(); if (parameters.containsKey(name)) { types[i] = parameters.get(name); } } } node.setGenericsTypes(types); return node; }
/** * @param name is the full name of the class * @param modifiers the modifiers, * @param superClass the base class name - use "java.lang.Object" if no direct base class * @param interfaces the interfaces for this class * @param mixins the mixins for this class * @see org.objectweb.asm.Opcodes */ public ClassNode( String name, int modifiers, ClassNode superClass, ClassNode[] interfaces, MixinNode[] mixins) { this.name = name; this.modifiers = modifiers; this.superClass = superClass; this.interfaces = interfaces; this.mixins = mixins; isPrimaryNode = true; if (superClass != null) { usesGenerics = superClass.isUsingGenerics(); } if (!usesGenerics && interfaces != null) { for (ClassNode anInterface : interfaces) { usesGenerics = usesGenerics || anInterface.isUsingGenerics(); if (usesGenerics) break; } } this.methods = new MapOfLists(); this.methodsList = Collections.emptyList(); }
public static ClassNode nonGeneric(ClassNode type) { if (type.isUsingGenerics()) { final ClassNode nonGen = ClassHelper.makeWithoutCaching(type.getName()); nonGen.setRedirect(type); nonGen.setGenericsTypes(null); nonGen.setUsingGenerics(false); return nonGen; } if (type.isArray()) { final ClassNode nonGen = ClassHelper.makeWithoutCaching(Object.class); nonGen.setUsingGenerics(false); return nonGen.makeArray(); } return type.getPlainNodeReference(); }
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); } }
/** * For a given classnode, fills in the supplied map with the parameterized types it defines. * * @param node * @param map */ public static void extractPlaceholders(ClassNode node, Map<String, GenericsType> map) { if (node == null) return; if (node.isArray()) { extractPlaceholders(node.getComponentType(), map); return; } if (!node.isUsingGenerics() || !node.isRedirectNode()) return; GenericsType[] parameterized = node.getGenericsTypes(); if (parameterized == null || parameterized.length == 0) return; GenericsType[] redirectGenericsTypes = node.redirect().getGenericsTypes(); if (redirectGenericsTypes == null) redirectGenericsTypes = parameterized; for (int i = 0; i < redirectGenericsTypes.length; i++) { GenericsType redirectType = redirectGenericsTypes[i]; if (redirectType.isPlaceholder()) { String name = redirectType.getName(); if (!map.containsKey(name)) map.put(name, parameterized[i]); } } }
public ClassNode parameterizedType(ClassNode baseType, ClassNode... genericsTypeArguments) { ClassNode result = baseType.getPlainNodeReference(); if (result.isUsingGenerics()) { GenericsType[] gts = new GenericsType[genericsTypeArguments.length]; int expectedLength = result.getGenericsTypes().length; if (expectedLength != genericsTypeArguments.length) { throw new GroovyBugError( "Expected number of generic type arguments for " + baseType.toString(false) + " is " + expectedLength + " but you gave " + genericsTypeArguments.length); } for (int i = 0; i < gts.length; i++) { gts[i] = new GenericsType(genericsTypeArguments[i]); } result.setGenericsTypes(gts); } return result; }
protected ClassNode createClosureClass(ClosureExpression expression, int mods) { ClassNode classNode = controller.getClassNode(); ClassNode outerClass = controller.getOutermostClass(); MethodNode methodNode = controller.getMethodNode(); String name = classNode.getName() + "$" + controller .getContext() .getNextClosureInnerName( outerClass, classNode, methodNode); // add a more informative name 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; }