将属性添加到groovy类时ArrayIndexOutOfBoundsException

时间:2014-11-23 20:57:10

标签: java groovy

我有java遗留代码,它加载一个groovy脚本并在加载的类中添加两个属性。它适用于groovy 1.7.0,但是当我尝试将groovy升级到版本1.8.0或更高版本时,它会导致稳定的错误"类生成期间的一般错误:-1 java.lang.ArrayIndexOutOfBoundsException:-1" 。 我尝试过使用groovy-all jar但结果与Spring 2.5.6或3.2.11相同。以下是maven依赖:

<dependency>
  <groupId>org.codehaus.groovy</groupId>
  <artifactId>groovy-all</artifactId>
  <version>1.8.9</version>
</dependency>

重现错误的简短测试。有趣的是,添加简单属性(attrSingle)时不会发生错误,但仅在添加列表(attrList)时才会发生错误。

EDITED(添加了属性)

import groovy.lang.*;
import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.*;
import org.junit.Test;
import java.security.CodeSource;
import java.util.*;
import static org.junit.Assert.assertTrue;

public class TestGroovyAutonomous {

    @Test
    public void testParsing() throws Exception {
      SimpleCustomizedGroovyClassLoader customizedGroovyClassLoader = new SimpleCustomizedGroovyClassLoader(new GroovyClassLoader());
      Class<?> groovyClass;
      try {
        groovyClass = customizedGroovyClassLoader.parseClass(new GroovyCodeSource(
                "{fact -> fact.a != null}",
                customizedGroovyClassLoader.generateScriptName(),
                "/scriptSandbox"));
      } catch (Exception e) {
        System.out.println("Error loading class");
        throw e;
      }
      assertTrue(groovyClass != null);
      GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();

      Boolean attrSingle = (Boolean) groovyObject.getProperty("attrSingle");
      System.out.println("Single=" + attrSingle);

      List<String> attrList = new ArrayList<String>();
      Object[] objects = (Object[]) groovyObject.getProperty("attrList");
        for (Object o : objects)
            attrList.add((String) o);
      System.out.println("List=" + attrList);        
    }

    class SimpleCustomizedGroovyClassLoader extends GroovyClassLoader {
        public SimpleCustomizedGroovyClassLoader(ClassLoader cl) {
            super(cl);
        }

        @Override
        protected CompilationUnit createCompilationUnit(CompilerConfiguration config, CodeSource source) {
            CompilationUnit cu = super.createCompilationUnit(config, source);
            cu.addPhaseOperation(new CompilationUnit.PrimaryClassNodeOperation() {
                public void call(SourceUnit sourceUnit, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {

                    classNode.addObjectInitializerStatements(new ExpressionStatement(new MethodCallExpression(
                            VariableExpression.THIS_EXPRESSION, "setProperty",
                            new ArgumentListExpression(new ConstantExpression("attrSingle"),
                                                       new ConstantExpression(true)))));

                    ArgumentListExpression argumentListExpression = new ArgumentListExpression();
                    for (String attributeName : Arrays.asList("a", "b", "c"))
                        argumentListExpression.addExpression(new ConstantExpression(attributeName));
                    classNode.addObjectInitializerStatements(new ExpressionStatement(new MethodCallExpression(
                            VariableExpression.THIS_EXPRESSION, "setProperty",
                            new ArgumentListExpression(new ConstantExpression("attrList"), argumentListExpression))));
                }
            }, Phases.CONVERSION);
            return cu;
        }
    }
}

错误文字:

index problem in script1000001.groovy
Error loading class

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
General error during class generation: -1

java.lang.ArrayIndexOutOfBoundsException: -1
    at java.util.ArrayList.elementData(ArrayList.java:400)
    at java.util.ArrayList.remove(ArrayList.java:477)
    at org.codehaus.groovy.classgen.asm.OperandStack.remove(OperandStack.java:199)
    at org.codehaus.groovy.classgen.asm.OperandStack.replace(OperandStack.java:270)
    at org.codehaus.groovy.classgen.asm.CallSiteWriter.makeCallSite(CallSiteWriter.java:334)
    at org.codehaus.groovy.classgen.asm.InvocationWriter.makeCall(InvocationWriter.java:187)
    at org.codehaus.groovy.classgen.asm.InvocationWriter.makeCall(InvocationWriter.java:89)
    at org.codehaus.groovy.classgen.asm.InvocationWriter.makeInvokeMethodCall(InvocationWriter.java:73)
    at org.codehaus.groovy.classgen.asm.InvocationWriter.writeInvokeMethod(InvocationWriter.java:292)
    at org.codehaus.groovy.classgen.AsmClassGenerator.visitMethodCallExpression(AsmClassGenerator.java:655)
    at org.codehaus.groovy.ast.expr.MethodCallExpression.visit(MethodCallExpression.java:75)
    at org.codehaus.groovy.classgen.asm.StatementWriter.writeExpressionStatement(StatementWriter.java:599)
    at org.codehaus.groovy.classgen.asm.OptimizingStatementWriter.writeExpressionStatement(OptimizingStatementWriter.java:354)
    at org.codehaus.groovy.classgen.AsmClassGenerator.visitExpressionStatement(AsmClassGenerator.java:501)
    at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:40)
    at org.codehaus.groovy.classgen.asm.StatementWriter.writeBlockStatement(StatementWriter.java:80)
    at org.codehaus.groovy.classgen.asm.OptimizingStatementWriter.writeBlockStatement(OptimizingStatementWriter.java:155)
    at org.codehaus.groovy.classgen.AsmClassGenerator.visitBlockStatement(AsmClassGenerator.java:447)
    at org.codehaus.groovy.ast.stmt.BlockStatement.visit(BlockStatement.java:69)
    at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClassCodeContainer(ClassCodeVisitorSupport.java:101)
    at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitConstructorOrMethod(ClassCodeVisitorSupport.java:112)
    at org.codehaus.groovy.classgen.AsmClassGenerator.visitStdMethod(AsmClassGenerator.java:311)
    at org.codehaus.groovy.classgen.AsmClassGenerator.visitConstructorOrMethod(AsmClassGenerator.java:268)
    at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitConstructor(ClassCodeVisitorSupport.java:119)
    at org.codehaus.groovy.classgen.AsmClassGenerator.visitConstructor(AsmClassGenerator.java:383)
    at org.codehaus.groovy.ast.ClassNode.visitContents(ClassNode.java:1054)
    at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClass(ClassCodeVisitorSupport.java:50)
    at org.codehaus.groovy.classgen.AsmClassGenerator.visitClass(AsmClassGenerator.java:172)
    at org.codehaus.groovy.control.CompilationUnit$14.call(CompilationUnit.java:770)
    at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:970)
    at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:548)
    at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:526)
    at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:503)
    at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:302)
    at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:281)
    at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:267)
    at x.y.TestGroovyAutonomous.testParsing(TestGroovyAutonomous.java:20)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

但是使用groovy 1.7.0,这个测试打印出来:

     Single=true
     List=[a, b, c]

请帮忙。

2 个答案:

答案 0 :(得分:0)

我无法真正告诉你要做什么,而不是这个......

ArgumentListExpression argumentListExpression = new ArgumentListExpression();
for (String attributeName : Arrays.asList("a", "b", "c"))
    argumentListExpression.addExpression(new ConstantExpression(attributeName));
classNode.addObjectInitializerStatements(new ExpressionStatement(new MethodCallExpression(
                        VariableExpression.THIS_EXPRESSION, "setProperty",
                        new ArgumentListExpression(new ConstantExpression("attrList"), argumentListExpression))));

这是否会产生您所追求的行为?......

ArgumentListExpression argumentListExpression = new ArgumentListExpression();
argumentListExpression.addExpression(new ConstantExpression("attrList"))
for (String attributeName : Arrays.asList("a", "b", "c"));
    argumentListExpression.addExpression(new ConstantExpression(attributeName));
classNode.addObjectInitializerStatements(new ExpressionStatement(new MethodCallExpression(
                        VariableExpression.THIS_EXPRESSION, "setProperty", argumentListExpression)));

修改

package grails.boot;

import static org.junit.Assert.assertTrue;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyCodeSource;
import groovy.lang.GroovyObject;

import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.ArrayExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.Phases;
import org.codehaus.groovy.control.SourceUnit;
import org.junit.Test;

public class GroovyTestAutonomous {

    @Test
    public void testParsing() throws Exception {
        SimpleCustomizedGroovyClassLoader customizedGroovyClassLoader = new SimpleCustomizedGroovyClassLoader(new GroovyClassLoader());
        Class<?> groovyClass;
        try {
            groovyClass = customizedGroovyClassLoader.parseClass(new GroovyCodeSource(
                    "{fact -> fact.a != null}",
                    customizedGroovyClassLoader.generateScriptName(),
                    "/scriptSandbox"));
        } catch (Exception e) {
            System.out.println("Error loading class");
            throw e;
        }
        assertTrue(groovyClass != null);
        GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();

        Boolean attrSingle = (Boolean) groovyObject.getProperty("attrSingle");
        System.out.println("Single=" + attrSingle);

        List<String> attrList = new ArrayList<String>();
        Object[] objects = (Object[]) groovyObject.getProperty("attrList");
        for (Object o : objects)
            attrList.add((String) o);
        System.out.println("List=" + attrList);
    }

    class SimpleCustomizedGroovyClassLoader extends GroovyClassLoader {
        public SimpleCustomizedGroovyClassLoader(ClassLoader cl) {
            super(cl);
        }

        @Override
        protected CompilationUnit createCompilationUnit(CompilerConfiguration config, CodeSource source) {
            CompilationUnit cu = super.createCompilationUnit(config, source);
            cu.addPhaseOperation(new CompilationUnit.PrimaryClassNodeOperation() {
                        public void call(SourceUnit sourceUnit, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {

                            classNode.addObjectInitializerStatements(new ExpressionStatement(new MethodCallExpression(
                                    VariableExpression.THIS_EXPRESSION, "setProperty",
                                    new ArgumentListExpression(new ConstantExpression("attrSingle"),
                                    new ConstantExpression(true)))));

                            List<Expression> args = new ArrayList<Expression>();
                            ArgumentListExpression argumentListExpression = new     ArgumentListExpression();
                            argumentListExpression.addExpression(new ConstantExpression("attrList"));
                            for (String attributeName : Arrays.asList("a", "b", "c"))
                                args.add(new ConstantExpression(attributeName));

                            ArrayExpression arrayExpression = new ArrayExpression(ClassHelper.make(Object.class), args);
                            argumentListExpression.addExpression(arrayExpression);
                            classNode.addObjectInitializerStatements(new ExpressionStatement(new MethodCallExpression(
                                    VariableExpression.THIS_EXPRESSION, "setProperty", argumentListExpression)));
                        }
                    }, Phases.CONVERSION);
            return cu;
        }
    }
}

答案 1 :(得分:0)

非常感谢Jeff,它适用于1.7.0及更高版本。

以下与初始版本更接近的代码也适用:

               List<Expression> args = new ArrayList<Expression>();
                for (String attributeName : Arrays.asList("a", "b", "c"))
                    args.add(new ConstantExpression(attributeName));
                ArrayExpression arrayExpression = new ArrayExpression(ClassHelper.make(Object.class), args);

                classNode.addObjectInitializerStatements(new ExpressionStatement(new MethodCallExpression(
                        VariableExpression.THIS_EXPRESSION, "setProperty",
                        new ArgumentListExpression(new ConstantExpression("attrList"), arrayExpression))));

此外,我们可以用ListExpression替换原始代码中的ArgumentListExpression:

                ListExpression args = new ListExpression();
                for (String attributeName : Arrays.asList("a", "b", "c"))
                    args.addExpression(new ConstantExpression(attributeName));

                classNode.addObjectInitializerStatements(new ExpressionStatement(new MethodCallExpression(
                        VariableExpression.THIS_EXPRESSION, "setProperty",
                        new ArgumentListExpression(new ConstantExpression("attrList"), args))));

与提取器代码中的更改配对:

    List<String> attrList = (ArrayList<String>) groovyObject.getProperty("attrList");
    System.out.println("List=" + attrList); 

据我了解,自版本1.8.0起,ArgumentListExpression不接受相同类型的参数(ArgumentListExpression),它必须是ListExpression或ArrayExpression类型,至少对于setProperty方法起作用。