@@ -84,4 +84,112 @@ public void evaluateShallSandbox() throws Exception {
8484 r .assertLogContains ("Scripts not permitted to use method groovy.lang.Script run java.io.File java.lang.String[]" , b );
8585 }
8686
87+ @ Test public void methodTooLargeExceptionFabricated () throws Exception {
88+ // Fabricate a MethodTooLargeException which "normally" happens when evaluated
89+ // groovy script becomes a Java class too large for Java to handle internally.
90+ // In Jenkins practice this can happen not only due to large singular pipelines
91+ // (one big nudge to offload code into shared libraries), but was also seen due
92+ // to heavy nesting of exception handling and other loops (simple refactoring
93+ // can help).
94+ WorkflowJob p = r .createProject (WorkflowJob .class );
95+ // sandbox == false to allow creation of the exception here:
96+ p .setDefinition (new CpsFlowDefinition (
97+ "import groovyjarjarasm.asm.MethodTooLargeException;\n \n " +
98+ "throw new MethodTooLargeException('className', 'methodName', 'methodDescriptor', 65535);"
99+ , false ));
100+ WorkflowRun b = r .buildAndAssertStatus (Result .FAILURE , p );
101+ r .assertLogContains ("groovyjarjarasm.asm.MethodTooLargeException: Method too large: className.methodName methodDescriptor" , b );
102+ r .assertLogContains ("at WorkflowScript.run(WorkflowScript:3)" , b );
103+ r .assertLogContains ("at ___cps.transform___(Native Method)" , b );
104+ }
105+
106+ @ Test public void methodTooLargeExceptionRealistic () throws Exception {
107+ // See comments above. Here we try to really induce a "method too large"
108+ // condition by abusing the nesting of exception-handling, too many stages
109+ // or methods, and whatever else we can throw at it.
110+ WorkflowJob p = r .createProject (WorkflowJob .class );
111+ StringBuffer sbMethods = new StringBuffer ();
112+ StringBuffer sbStages = new StringBuffer ();
113+ int i , max = 255 ;
114+
115+ for (i = 0 ; i < 250 ; i ++) {
116+ // Up to 255 stages allowed
117+ sbStages .append ("stage('Stage " + i + "') { steps { method" + i + "(); } }\n " );
118+ }
119+
120+ for (i = 0 ; i < max ; i ++) {
121+ sbMethods .append ("def method" + i + "() { echo 'i = " + i + "'; }\n " );
122+ }
123+
124+ sbMethods .append ("def method() {\n " );
125+ for (i = 0 ; i < max ; i ++) {
126+ sbMethods .append ("try { // " + i + "\n " );
127+ }
128+ sbMethods .append (" Integer x = 'zzz'; // incur conversion exception\n " );
129+ for (i = 0 ; i < max ; i ++) {
130+ sbMethods .append ("} catch (Throwable t) { // " + i + "\n method" + i + "(); throw t; }\n " );
131+ }
132+ sbMethods .append ("}\n " );
133+
134+ p .setDefinition (new CpsFlowDefinition (sbMethods .toString () +
135+ "pipeline {\n " +
136+ " agent none;\n " +
137+ " stages {\n " +
138+ " stage ('Test stage') {\n " +
139+ " steps {\n " +
140+ " script {\n " +
141+ " echo 'BEGINNING TEST IN PIPELINE';\n " +
142+ " method();\n " +
143+ " echo 'ENDED TEST IN PIPELINE';\n " +
144+ " }\n " +
145+ " }\n " +
146+ " }\n " +
147+ sbStages .toString () +
148+ " }\n " +
149+ "}\n " +
150+ "//echo 'BEGINNING TEST OUT OF PIPELINE';\n " +
151+ "//method();\n " +
152+ "//echo 'ENDED TEST OUT OF PIPELINE';\n "
153+ , true ));
154+
155+ WorkflowRun b = p .scheduleBuild2 (0 ).get ();
156+
157+ // DEV-TEST // System.out.println(b.getLog());
158+
159+ r .assertLogContains ("MethodTooLargeException" , b );
160+
161+ /*
162+ // Report as of release 3880.vb_ef4b_5cfd270 (Feb 2024)
163+ // and same pattern seen since at least Jun 2022 (note
164+ // that numbers after ___cps___ differ from job to job):
165+
166+ org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
167+ General error during class generation: Method too large: WorkflowScript.___cps___1 ()Lcom/cloudbees/groovy/cps/impl/CpsFunction;
168+
169+ groovyjarjarasm.asm.MethodTooLargeException: Method too large: WorkflowScript.___cps___1 ()Lcom/cloudbees/groovy/cps/impl/CpsFunction;
170+ at groovyjarjarasm.asm.MethodWriter.computeMethodInfoSize(MethodWriter.java:2087)
171+ at groovyjarjarasm.asm.ClassWriter.toByteArray(ClassWriter.java:447)
172+ at org.codehaus.groovy.control.CompilationUnit$17.call(CompilationUnit.java:850)
173+ at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1087)
174+ at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:624)
175+ at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:602)
176+ at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:579)
177+ at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:323)
178+ at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:293)
179+ at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox$Scope.parse(GroovySandbox.java:163)
180+ at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:190)
181+ at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:175)
182+ at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:637)
183+ at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:583)
184+ at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:335)
185+ at hudson.model.ResourceController.execute(ResourceController.java:101)
186+ at hudson.model.Executor.run(Executor.java:442)
187+ */
188+
189+ r .assertLogContains ("Method too large: WorkflowScript.___cps___" , b );
190+ r .assertLogContains ("()Lcom/cloudbees/groovy/cps/impl/CpsFunction;" , b );
191+
192+ // Assert separately from (and after) log parsing, to facilitate test maintenance
193+ r .assertBuildStatus (Result .FAILURE , b );
194+ }
87195}
0 commit comments