/** * 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); } }
private ClassNode makeClassNode(CompileUnit cu, Type t, Class c) { ClassNode back = null; if (cu != null) back = cu.getClass(c.getName()); if (back == null) back = ClassHelper.make(c); if (!(t instanceof Class)) { ClassNode front = configureType(t); front.setRedirect(back); return front; } return back.getPlainNodeReference(); }
/** * 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; }
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(); }
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; }
/** 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 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; }
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; }