public void positionStmtsAfterEnumInitStmts(List<Statement> staticFieldStatements) { MethodNode method = getOrAddStaticConstructorNode(); Statement statement = method.getCode(); if (statement instanceof BlockStatement) { BlockStatement block = (BlockStatement) statement; // add given statements for explicitly declared static fields just after enum-special fields // are found - the $VALUES binary expression marks the end of such fields. List<Statement> blockStatements = block.getStatements(); ListIterator<Statement> litr = blockStatements.listIterator(); while (litr.hasNext()) { Statement stmt = litr.next(); if (stmt instanceof ExpressionStatement && ((ExpressionStatement) stmt).getExpression() instanceof BinaryExpression) { BinaryExpression bExp = (BinaryExpression) ((ExpressionStatement) stmt).getExpression(); if (bExp.getLeftExpression() instanceof FieldExpression) { FieldExpression fExp = (FieldExpression) bExp.getLeftExpression(); if (fExp.getFieldName().equals("$VALUES")) { for (Statement tmpStmt : staticFieldStatements) { litr.add(tmpStmt); } } } } } } }
public void addStaticInitializerStatements(List<Statement> staticStatements, boolean fieldInit) { MethodNode method = getOrAddStaticConstructorNode(); BlockStatement block = null; Statement statement = method.getCode(); if (statement == null) { block = new BlockStatement(); } else if (statement instanceof BlockStatement) { block = (BlockStatement) statement; } else { block = new BlockStatement(); block.addStatement(statement); } // while anything inside a static initializer block is appended // we don't want to append in the case we have a initialization // expression of a static field. In that case we want to add // before the other statements if (!fieldInit) { block.addStatements(staticStatements); } else { List<Statement> blockStatements = block.getStatements(); staticStatements.addAll(blockStatements); blockStatements.clear(); blockStatements.addAll(staticStatements); } }
public Map<String, MethodNode> getDeclaredMethodsMap() { // Start off with the methods from the superclass. ClassNode parent = getSuperClass(); Map<String, MethodNode> result = null; if (parent != null) { result = parent.getDeclaredMethodsMap(); } else { result = new HashMap<String, MethodNode>(); } // add in unimplemented abstract methods from the interfaces for (ClassNode iface : getInterfaces()) { Map<String, MethodNode> ifaceMethodsMap = iface.getDeclaredMethodsMap(); for (String methSig : ifaceMethodsMap.keySet()) { if (!result.containsKey(methSig)) { MethodNode methNode = ifaceMethodsMap.get(methSig); result.put(methSig, methNode); } } } // And add in the methods implemented in this class. for (MethodNode method : getMethods()) { String sig = method.getTypeDescriptor(); result.put(sig, method); } return result; }
/** * Finds a method matching the given name and parameters in this class or any parent class. * * @return the method matching the given name and parameters or null */ public MethodNode getMethod(String name, Parameter[] parameters) { for (MethodNode method : getMethods(name)) { if (parametersEqual(method.getParameters(), parameters)) { return method; } } return null; }
public void addMethod(MethodNode node) { node.setDeclaringClass(this); ClassNode base = redirect(); if (base.methodsList.isEmpty()) { base.methodsList = new ArrayList<MethodNode>(); } base.methodsList.add(node); base.methods.put(node.getName(), node); }
private void memorizeInitialExpressions(final MethodNode node) { // add node metadata for default parameters because they are erased by the Verifier if (node.getParameters() != null) { for (Parameter parameter : node.getParameters()) { parameter.putNodeMetaData( StaticTypesMarker.INITIAL_EXPRESSION, parameter.getInitialExpression()); } } }
/** * 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); } }
/** Adds a synthetic method as part of the compilation process */ public MethodNode addSyntheticMethod( String name, int modifiers, ClassNode returnType, Parameter[] parameters, ClassNode[] exceptions, Statement code) { MethodNode answer = addMethod(name, modifiers | ACC_SYNTHETIC, returnType, parameters, exceptions, code); answer.setSynthetic(true); return answer; }
/** * Tests whether the ClassNode implements the specified method name * * @param classNode The ClassNode * @param methodName The method name * @param argTypes * @return True if it implements the method */ private static boolean implementsMethod( ClassNode classNode, String methodName, Class[] argTypes) { List methods = classNode.getMethods(); for (Iterator i = methods.iterator(); i.hasNext(); ) { MethodNode mn = (MethodNode) i.next(); final boolean isZeroArg = (argTypes == null || argTypes.length == 0); boolean methodMatch = mn.getName().equals(methodName) && isZeroArg; if (methodMatch) return true; // TODO Implement further parameter analysis } return false; }
public MethodNode getGetterMethod(String getterName) { for (MethodNode method : getDeclaredMethods(getterName)) { if (getterName.equals(method.getName()) && ClassHelper.VOID_TYPE != method.getReturnType() && method.getParameters().length == 0) { return method; } } ClassNode parent = getSuperClass(); if (parent != null) return parent.getGetterMethod(getterName); return null; }
public MethodNode getSetterMethod(String setterName, boolean voidOnly) { for (MethodNode method : getDeclaredMethods(setterName)) { if (setterName.equals(method.getName()) && (!voidOnly || ClassHelper.VOID_TYPE == method.getReturnType()) && method.getParameters().length == 1) { return method; } } ClassNode parent = getSuperClass(); if (parent != null) return parent.getSetterMethod(setterName, voidOnly); return null; }
/** * @return the list of abstract methods associated with this ClassNode or null if there are no * such methods */ public List<MethodNode> getAbstractMethods() { List<MethodNode> result = new ArrayList<MethodNode>(3); for (MethodNode method : getDeclaredMethodsMap().values()) { if (method.isAbstract()) { result.add(method); } } if (result.isEmpty()) { return null; } else { return result; } }
public void removeMethod(MethodNode node) { ClassNode base = redirect(); if (!base.methodsList.isEmpty()) { base.methodsList.remove(node); } base.methods.remove(node.getName(), node); }
@Override public void visitMethod(final MethodNode node) { if (isSkipMode(node)) { node.putNodeMetaData(STATIC_COMPILE_NODE, false); } super.visitMethod(node); checkForConstructorWithCSButClassWithout(node); }
private MethodNode getOrAddStaticConstructorNode() { MethodNode method = null; List declaredMethods = getDeclaredMethods("<clinit>"); if (declaredMethods.isEmpty()) { method = addMethod( "<clinit>", ACC_STATIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement()); method.setSynthetic(true); } else { method = (MethodNode) declaredMethods.get(0); } return method; }
/** * Returns true if the given method has a possibly matching instance method with the given name * and arguments. * * @param name the name of the method of interest * @param arguments the arguments to match against * @return true if a matching method was found */ public boolean hasPossibleMethod(String name, Expression arguments) { int count = 0; if (arguments instanceof TupleExpression) { TupleExpression tuple = (TupleExpression) arguments; // TODO this won't strictly be true when using list expansion in argument calls count = tuple.getExpressions().size(); } ClassNode node = this; do { for (MethodNode method : getMethods(name)) { if (method.getParameters().length == count && !method.isStatic()) { return true; } } node = node.getSuperClass(); } while (node != null); return false; }
private void replaceObfuscatedMethodNames(AsmagicDiffVisitor dv) { for (MethodNode mn : dv.newMethods) { if (mn.visibleAnnotations != null) { for (Object oan : mn.visibleAnnotations) { AnnotationNode an = (AnnotationNode) oan; if (an.desc.contains("AsmagicMethodReplace")) { List<Object> vals = an.values; String alias = null; if (vals != null || vals.size() > 1) { alias = (String) vals.get(1); dv.methodsToReplace.put(alias, 1); mn.name = alias; } } } } } }
public InlineResult doInline( @NotNull MethodVisitor adapter, @NotNull LocalVarRemapper remapper, boolean remapReturn, @NotNull LabelOwner labelOwner) { // analyze body MethodNode transformedNode = markPlacesForInlineAndRemoveInlinable(node); // substitute returns with "goto end" instruction to keep non local returns in lambdas Label end = new Label(); transformedNode = doInline(transformedNode); removeClosureAssertions(transformedNode); InsnList instructions = transformedNode.instructions; instructions.resetLabels(); MethodNode resultNode = new MethodNode( InlineCodegenUtil.API, transformedNode.access, transformedNode.name, transformedNode.desc, transformedNode.signature, ArrayUtil.toStringArray(transformedNode.exceptions)); RemapVisitor visitor = new RemapVisitor(resultNode, remapper, nodeRemapper); try { transformedNode.accept(visitor); } catch (Exception e) { throw wrapException(e, transformedNode, "couldn't inline method call"); } resultNode.visitLabel(end); if (inliningContext.isRoot()) { InternalFinallyBlockInliner.processInlineFunFinallyBlocks(resultNode, lambdasFinallyBlocks); } processReturns(resultNode, labelOwner, remapReturn, end); // flush transformed node to output resultNode.accept(new InliningInstructionAdapter(adapter)); return result; }
@SuppressWarnings("unchecked") private boolean makeGetPrivateFieldWithBridgeMethod( final Expression receiver, final ClassNode receiverType, final String fieldName, final boolean safe, final boolean implicitThis) { FieldNode field = receiverType.getField(fieldName); ClassNode classNode = controller.getClassNode(); if (field != null && Modifier.isPrivate(field.getModifiers()) && (StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(receiverType, classNode) || StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(classNode, receiverType)) && !receiverType.equals(classNode)) { Map<String, MethodNode> accessors = (Map<String, MethodNode>) receiverType .redirect() .getNodeMetaData(StaticCompilationMetadataKeys.PRIVATE_FIELDS_ACCESSORS); if (accessors != null) { MethodNode methodNode = accessors.get(fieldName); if (methodNode != null) { MethodCallExpression mce = new MethodCallExpression( receiver, methodNode.getName(), new ArgumentListExpression( field.isStatic() ? new ConstantExpression(null) : receiver)); mce.setMethodTarget(methodNode); mce.setSafe(safe); mce.setImplicitThis(implicitThis); mce.visit(controller.getAcg()); return true; } } } return false; }
private static void showDocument(Document doc) { StringBuffer content = new StringBuffer(); Node node = doc.getChildNodes().item(0); ApplicationNode appNode = new ApplicationNode(node); content.append("Application \n"); List<ClassNode> classes = appNode.getClasses(); for (int i = 0; i < classes.size(); i++) { ClassNode classNode = classes.get(i); content.append(SPACE + "Class: " + classNode.getName() + " \n"); List<MethodNode> methods = classNode.getMethods(); for (int j = 0; j < methods.size(); j++) { MethodNode methodNode = methods.get(j); content.append(SPACE + SPACE + "Method: " + methodNode.getName() + " \n"); } } System.out.println(content.toString()); }
/** * Returns true if the given method has a possibly matching static method with the given name and * arguments. * * @param name the name of the method of interest * @param arguments the arguments to match against * @return true if a matching method was found */ public boolean hasPossibleStaticMethod(String name, Expression arguments) { int count = 0; if (arguments instanceof TupleExpression) { TupleExpression tuple = (TupleExpression) arguments; // TODO this won't strictly be true when using list expansion in argument calls count = tuple.getExpressions().size(); } else if (arguments instanceof MapExpression) { count = 1; } for (MethodNode method : getMethods(name)) { if (method.isStatic()) { Parameter[] parameters = method.getParameters(); if (parameters.length == count) return true; // handle varargs case if (parameters.length > 0 && parameters[parameters.length - 1].getType().isArray()) { if (count >= parameters.length - 1) return true; } // handle parameters with default values int nonDefaultParameters = 0; for (Parameter parameter : parameters) { if (!parameter.hasInitialExpression()) { nonDefaultParameters++; } } if (count < parameters.length && nonDefaultParameters <= count) { return true; } } } return false; }
/** * 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); }
public MethodNode tryFindPossibleMethod(String name, Expression arguments) { int count = 0; if (arguments instanceof TupleExpression) { TupleExpression tuple = (TupleExpression) arguments; // TODO this won't strictly be true when using list expansion in argument calls count = tuple.getExpressions().size(); } else return null; MethodNode res = null; ClassNode node = this; TupleExpression args = (TupleExpression) arguments; do { for (MethodNode method : node.getMethods(name)) { if (method.getParameters().length == count) { boolean match = true; for (int i = 0; i != count; ++i) if (!args.getType().isDerivedFrom(method.getParameters()[i].getType())) { match = false; break; } if (match) { if (res == null) res = method; else { if (res.getParameters().length != count) return null; if (node.equals(this)) return null; match = true; for (int i = 0; i != count; ++i) if (!res.getParameters()[i].getType().equals(method.getParameters()[i].getType())) { match = false; break; } if (!match) return null; } } } } node = node.getSuperClass(); } while (node != null); return res; }
protected Expression transformPropertyExpression(PropertyExpression pe) { if (currentMethod != null && currentMethod.isStatic() && pe.getObjectExpression() instanceof VariableExpression && ((VariableExpression) pe.getObjectExpression()).isSuperExpression()) { PropertyExpression pexp = new PropertyExpression( new ClassExpression(currentClass.getSuperClass()), transform(pe.getProperty())); pexp.setSourcePosition(pe); return pexp; } boolean oldInPropertyExpression = inPropertyExpression; Expression oldFoundArgs = foundArgs; Expression oldFoundConstant = foundConstant; inPropertyExpression = true; foundArgs = null; foundConstant = null; Expression objectExpression = transform(pe.getObjectExpression()); boolean candidate = false; if (objectExpression instanceof MethodCallExpression) { candidate = ((MethodCallExpression) objectExpression).isImplicitThis(); } if (foundArgs != null && foundConstant != null && candidate) { Expression result = findStaticMethodImportFromModule(foundConstant, foundArgs); if (result != null) { objectExpression = result; objectExpression.setSourcePosition(pe); } } inPropertyExpression = oldInPropertyExpression; foundArgs = oldFoundArgs; foundConstant = oldFoundConstant; pe.setObjectExpression(objectExpression); return pe; }
@NotNull public MethodNode prepareNode(@NotNull MethodNode node) { final int capturedParamsSize = parameters.getCaptured().size(); final int realParametersSize = parameters.getReal().size(); Type[] types = Type.getArgumentTypes(node.desc); Type returnType = Type.getReturnType(node.desc); ArrayList<Type> capturedTypes = parameters.getCapturedTypes(); Type[] allTypes = ArrayUtil.mergeArrays(types, capturedTypes.toArray(new Type[capturedTypes.size()])); node.instructions.resetLabels(); MethodNode transformedNode = new MethodNode( InlineCodegenUtil.API, node.access, node.name, Type.getMethodDescriptor(returnType, allTypes), node.signature, null) { private final boolean isInliningLambda = nodeRemapper.isInsideInliningLambda(); private int getNewIndex(int var) { return var + (var < realParametersSize ? 0 : capturedParamsSize); } @Override public void visitVarInsn(int opcode, int var) { super.visitVarInsn(opcode, getNewIndex(var)); } @Override public void visitIincInsn(int var, int increment) { super.visitIincInsn(getNewIndex(var), increment); } @Override public void visitMaxs(int maxStack, int maxLocals) { super.visitMaxs(maxStack, maxLocals + capturedParamsSize); } @Override public void visitLineNumber(int line, @NotNull Label start) { if (isInliningLambda) { super.visitLineNumber(line, start); } } @Override public void visitLocalVariable( @NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index) { if (isInliningLambda) { super.visitLocalVariable(name, desc, signature, start, end, getNewIndex(index)); } } }; node.accept(transformedNode); transformCaptured(transformedNode); return transformedNode; }
private MethodNode doInline(MethodNode node) { final Deque<InvokeCall> currentInvokes = new LinkedList<InvokeCall>(invokeCalls); final MethodNode resultNode = new MethodNode(node.access, node.name, node.desc, node.signature, null); final Iterator<AnonymousObjectGeneration> iterator = anonymousObjectGenerations.iterator(); RemappingMethodAdapter remappingMethodAdapter = new RemappingMethodAdapter( resultNode.access, resultNode.desc, resultNode, new TypeRemapper(currentTypeMapping)); InlineAdapter lambdaInliner = new InlineAdapter(remappingMethodAdapter, parameters.totalSize()) { private AnonymousObjectGeneration anonymousObjectGen; private void handleAnonymousObjectGeneration() { anonymousObjectGen = iterator.next(); if (anonymousObjectGen.shouldRegenerate()) { // TODO: need poping of type but what to do with local funs??? Type newLambdaType = Type.getObjectType(inliningContext.nameGenerator.genLambdaClassName()); currentTypeMapping.put( anonymousObjectGen.getOwnerInternalName(), newLambdaType.getInternalName()); AnonymousObjectTransformer transformer = new AnonymousObjectTransformer( anonymousObjectGen.getOwnerInternalName(), inliningContext.subInlineWithClassRegeneration( inliningContext.nameGenerator, currentTypeMapping, anonymousObjectGen), isSameModule, newLambdaType); InlineResult transformResult = transformer.doTransform(anonymousObjectGen, nodeRemapper); result.addAllClassesToRemove(transformResult); if (inliningContext.isInliningLambda && !anonymousObjectGen.isStaticOrigin()) { // this class is transformed and original not used so we should remove original one // after inlining // Note: It is unsafe to remove anonymous class that is referenced by GETSTATIC // within lambda // because it can be local function from outer scope result.addClassToRemove(anonymousObjectGen.getOwnerInternalName()); } if (transformResult.getReifiedTypeParametersUsages().wereUsedReifiedParameters()) { ReifiedTypeInliner.putNeedClassReificationMarker(mv); result .getReifiedTypeParametersUsages() .mergeAll(transformResult.getReifiedTypeParametersUsages()); } } } @Override public void anew(@NotNull Type type) { if (isAnonymousConstructorCall(type.getInternalName(), "<init>")) { handleAnonymousObjectGeneration(); } // in case of regenerated anonymousObjectGen type would be remapped to new one via // remappingMethodAdapter super.anew(type); } @Override public void visitMethodInsn( int opcode, String owner, String name, String desc, boolean itf) { if ( /*INLINE_RUNTIME.equals(owner) &&*/ isInvokeOnLambda(owner, name)) { // TODO add method assert !currentInvokes.isEmpty(); InvokeCall invokeCall = currentInvokes.remove(); LambdaInfo info = invokeCall.lambdaInfo; if (info == null) { // noninlinable lambda super.visitMethodInsn(opcode, owner, name, desc, itf); return; } int valueParamShift = getNextLocalIndex(); // NB: don't inline cause it changes putStackValuesIntoLocals( info.getInvokeParamsWithoutCaptured(), valueParamShift, this, desc); addInlineMarker(this, true); Parameters lambdaParameters = info.addAllParameters(nodeRemapper); InlinedLambdaRemapper newCapturedRemapper = new InlinedLambdaRemapper( info.getLambdaClassType().getInternalName(), nodeRemapper, lambdaParameters); setLambdaInlining(true); MethodInliner inliner = new MethodInliner( info.getNode(), lambdaParameters, inliningContext.subInlineLambda(info), newCapturedRemapper, true /*cause all calls in same module as lambda*/, "Lambda inlining " + info.getLambdaClassType().getInternalName()); LocalVarRemapper remapper = new LocalVarRemapper(lambdaParameters, valueParamShift); InlineResult lambdaResult = inliner.doInline( this.mv, remapper, true, info); // TODO add skipped this and receiver result.addAllClassesToRemove(lambdaResult); // return value boxing/unboxing Method bridge = typeMapper .mapSignature( ClosureCodegen.getErasedInvokeFunction(info.getFunctionDescriptor())) .getAsmMethod(); Method delegate = typeMapper.mapSignature(info.getFunctionDescriptor()).getAsmMethod(); StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), this); setLambdaInlining(false); addInlineMarker(this, false); } else if (isAnonymousConstructorCall(owner, name)) { // TODO add method assert anonymousObjectGen != null : "<init> call not corresponds to new call" + owner + " " + name; if (anonymousObjectGen.shouldRegenerate()) { // put additional captured parameters on stack for (CapturedParamDesc capturedParamDesc : anonymousObjectGen.getAllRecapturedParameters()) { visitFieldInsn( Opcodes.GETSTATIC, capturedParamDesc.getContainingLambdaName(), "$$$" + capturedParamDesc.getFieldName(), capturedParamDesc.getType().getDescriptor()); } super.visitMethodInsn( opcode, anonymousObjectGen.getNewLambdaType().getInternalName(), name, anonymousObjectGen.getNewConstructorDescriptor(), itf); anonymousObjectGen = null; } else { super.visitMethodInsn( opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf); } } else if (ReifiedTypeInliner.isNeedClassReificationMarker( new MethodInsnNode(opcode, owner, name, desc, false))) { // we will put it if needed in anew processing } else { super.visitMethodInsn( opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf); } } @Override public void visitFieldInsn( int opcode, @NotNull String owner, @NotNull String name, @NotNull String desc) { if (opcode == Opcodes.GETSTATIC && isAnonymousSingletonLoad(owner, name)) { handleAnonymousObjectGeneration(); } super.visitFieldInsn(opcode, owner, name, desc); } @Override public void visitMaxs(int stack, int locals) { lambdasFinallyBlocks = resultNode.tryCatchBlocks.size(); super.visitMaxs(stack, locals); } }; node.accept(lambdaInliner); return resultNode; }
@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 boolean makeGetPropertyWithGetter( final Expression receiver, final ClassNode receiverType, final String methodName, final boolean safe, final boolean implicitThis) { // does a getter exists ? String getterName = "get" + MetaClassHelper.capitalize(methodName); MethodNode getterNode = receiverType.getGetterMethod(getterName); if (getterNode == null) { getterName = "is" + MetaClassHelper.capitalize(methodName); getterNode = receiverType.getGetterMethod(getterName); } if (getterNode != null && receiver instanceof ClassExpression && !CLASS_Type.equals(receiverType) && !getterNode.isStatic()) { return false; } // GROOVY-5561: if two files are compiled in the same source unit // and that one references the other, the getters for properties have not been // generated by the compiler yet (generated by the Verifier) PropertyNode propertyNode = receiverType.getProperty(methodName); if (propertyNode != null) { // it is possible to use a getter String prefix = "get"; if (boolean_TYPE.equals(propertyNode.getOriginType())) { prefix = "is"; } getterName = prefix + MetaClassHelper.capitalize(methodName); getterNode = new MethodNode( getterName, ACC_PUBLIC, propertyNode.getOriginType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE); getterNode.setDeclaringClass(receiverType); if (propertyNode.isStatic()) getterNode.setModifiers(ACC_PUBLIC + ACC_STATIC); } if (getterNode != null) { MethodCallExpression call = new MethodCallExpression(receiver, getterName, ArgumentListExpression.EMPTY_ARGUMENTS); call.setSourcePosition(receiver); call.setMethodTarget(getterNode); call.setImplicitThis(implicitThis); call.setSafe(safe); call.visit(controller.getAcg()); return true; } if (receiverType instanceof InnerClassNode && !receiverType.isStaticClass()) { if (makeGetPropertyWithGetter( receiver, receiverType.getOuterClass(), methodName, safe, implicitThis)) { return true; } } // go upper level ClassNode superClass = receiverType.getSuperClass(); if (superClass != null) { return makeGetPropertyWithGetter(receiver, superClass, methodName, safe, implicitThis); } return false; }
static { ARRAYLIST_CONSTRUCTOR = new ConstructorNode( ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE); ARRAYLIST_CONSTRUCTOR.setDeclaringClass(StaticCompilationVisitor.ARRAYLIST_CLASSNODE); }
protected Expression transformMethodCallExpression(MethodCallExpression mce) { Expression args = transform(mce.getArguments()); Expression method = transform(mce.getMethod()); Expression object = transform(mce.getObjectExpression()); boolean isExplicitThisOrSuper = false; boolean isExplicitSuper = false; if (object instanceof VariableExpression) { VariableExpression ve = (VariableExpression) object; isExplicitThisOrSuper = !mce.isImplicitThis() && (ve.isThisExpression() || ve.isSuperExpression()); isExplicitSuper = ve.isSuperExpression(); } if (mce.isImplicitThis() || isExplicitThisOrSuper) { if (mce.isImplicitThis()) { Expression ret = findStaticMethodImportFromModule(method, args); if (ret != null) { setSourcePosition(ret, mce); return ret; } if (method instanceof ConstantExpression && !inLeftExpression) { // could be a closure field String methodName = (String) ((ConstantExpression) method).getValue(); ret = findStaticFieldOrPropAccessorImportFromModule(methodName); if (ret != null) { ret = new MethodCallExpression(ret, "call", args); setSourcePosition(ret, mce); return ret; } } } else if (currentMethod != null && currentMethod.isStatic() && isExplicitSuper) { MethodCallExpression ret = new MethodCallExpression( new ClassExpression(currentClass.getSuperClass()), method, args); setSourcePosition(ret, mce); return ret; } if (method instanceof ConstantExpression) { ConstantExpression ce = (ConstantExpression) method; Object value = ce.getValue(); if (value instanceof String) { String methodName = (String) value; boolean lookForPossibleStaticMethod = !methodName.equals("call"); if (currentMethod != null && !currentMethod.isStatic()) { if (currentClass.hasPossibleMethod(methodName, args)) { lookForPossibleStaticMethod = false; } } if (!inClosure && (inSpecialConstructorCall || (lookForPossibleStaticMethod && currentClass.hasPossibleStaticMethod(methodName, args)))) { StaticMethodCallExpression smce = new StaticMethodCallExpression(currentClass, methodName, args); setSourcePosition(smce, mce); return smce; } } } } MethodCallExpression result = new MethodCallExpression(object, method, args); result.setSafe(mce.isSafe()); result.setImplicitThis(mce.isImplicitThis()); result.setSpreadSafe(mce.isSpreadSafe()); result.setMethodTarget(mce.getMethodTarget()); // GROOVY-6757 result.setGenericsTypes(mce.getGenericsTypes()); setSourcePosition(result, mce); return result; }