Skip to content
This repository

Fall back to interpreter when codegen hits 64k limit and fails. #76

Open
wants to merge 1 commit into from

3 participants

Charles Oliver Nutter Hannes Wallnöfer Attila Szegedi
Charles Oliver Nutter

Currently, if optimize level is set to >= 0, it is possible for code to fail to run at all when it exceeds the JVM's 64k bytecode size limit. This patch (a work in progress) modifies Codegen to fall back to Interpreter when any errors occur during code generation.

I recognize that this catch-all is not ideal, but I'm posting this now to start a discussion. It is not clear to me why having code fail to execute is better than falling back to interpretation, but the hard break at the 64k limit means most applications of Rhino set optimize level to -1. I believe this has harmed the world's impression of Rhino and its performance.

This could also be an optional setting, enabled by default but possible to disable for those who really want code >64k to blow up. I'm not sure who those people are.

Charles Oliver Nutter

Oh, and I did not run Rhino tests, but I did test with a very large script to prove this works.


system ~/projects/rhino $ wc -l giant.js 
   15649 giant.js

system ~/projects/rhino $ java -jar build/rhino1_7R5pre/js.jar -opt 9 giant.js 
js: "giant.js", line 1: Encountered code generation error while compiling script: generated bytecode for method exceeds 64K limit.


system ~/projects/rhino $ git stash pop ; a jar
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   src/org/mozilla/javascript/optimizer/Codegen.java
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   .idea/
#   Rhino.iml
#   build/
#   giant.js
#   lib/
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (a3cfc10bff2dbacfe6aa1554f3fbd29c16dfd026)
    [javac] warning: [options] bootstrap class path not set in conjunction with -source 1.5
    [javac] 1 warning
     [echo] Calling /Users/headius/projects/rhino/xmlimplsrc/build.xml
     [echo] Compiling XMLBeans E4X implementation using ./lib/xbean.jar and ./lib/jsr173_1.0_api.jar

BUILD SUCCESSFUL
Total time: 2 seconds

system ~/projects/rhino $ java -jar build/rhino1_7R5pre/js.jar -opt 9 giant.js 
2
3
4
5
6
7
8
Hannes Wallnöfer
Collaborator
hns commented August 09, 2012

We should definitely do this, given how easy it is to implement and how much it improves the user experience.

One problem with the proposed patch is that by catching all exceptions it will mask actual bugs in bytecode generation. I think the better solution is to let Codegen throw the more specific ClassFileFormatException without converting it to a generic runtime exception, and implement the interpreter fallback in Context.compileImpl().

I'm working on a new patch. I'm also considering to add a new FALLBACK_TO_INTERPRETER context feature, but the default setting would be true (as you said, why would we want to fail by default).

Attila Szegedi
Collaborator
Charles Oliver Nutter
Hannes Wallnöfer
Collaborator
hns commented August 09, 2012

Attila, my thinking was that in some cases it might be nice to have a guarantee that code actually runs with the dialed settings. Probably not for the common case of getting things done, but for example for running our own tests.

Charlie, I think I've got it pretty much right locally so no big need to work on it (unless you really want to contribute a commit, which would be nice :)

One annoyance I found is we can't use ClassFileWriter.ClassFileFormatException in Context because ClassFileWriter is not available in the smalljs.jar target (which is an interpreter-only packaging of Rhino). Adding a org.mozilla.javascript.ClassLimitException and make ClassFileFormatException.ClassFormatException extend it... yuck!

Hannes Wallnöfer
Collaborator
hns commented August 10, 2012

I pushed my revised patch to my github fork:

hns@207bb86

As mentioned in my previous comment, the introduction of the ClassLimitException is due to jar packaging considerations (making Context class work without src/org/mozilla/classfile package).

Please review and comment!

Charles Oliver Nutter

Is there anything outstanding to get @hns's version of the patch into rhino proper?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Aug 01, 2012
Charles Oliver Nutter Fall back to interpreter when codegen hits 64k limit and fails. 2747a05
This page is out of date. Refresh to see the latest.
63  src/org/mozilla/javascript/optimizer/Codegen.java
@@ -72,44 +72,59 @@ public Object compile(CompilerEnvirons compilerEnv,
72 72
 
73 73
         String mainClassName = "org.mozilla.javascript.gen." + baseName + "_" + serial;
74 74
 
75  
-        byte[] mainClassBytes = compileToClassFile(compilerEnv, mainClassName,
76  
-                                                   tree, encodedSource,
77  
-                                                   returnFunction);
  75
+        try {
  76
+            byte[] mainClassBytes = compileToClassFile(compilerEnv, mainClassName,
  77
+                                                       tree, encodedSource,
  78
+                                                       returnFunction);
78 79
 
79  
-        return new Object[] { mainClassName, mainClassBytes };
  80
+            return new Object[] { mainClassName, mainClassBytes };
  81
+        } catch (Exception e) {
  82
+            // fall back to interpreter
  83
+            return fallbackInterpreter.compile(compilerEnv, tree, encodedSource, returnFunction);
  84
+        }
80 85
     }
81 86
 
82 87
     public Script createScriptObject(Object bytecode,
83 88
                                      Object staticSecurityDomain)
84 89
     {
85  
-        Class<?> cl = defineClass(bytecode, staticSecurityDomain);
86  
-
87  
-        Script script;
88 90
         try {
89  
-            script = (Script)cl.newInstance();
90  
-        } catch (Exception ex) {
91  
-            throw new RuntimeException
92  
-                ("Unable to instantiate compiled class:" + ex.toString());
  91
+            Class<?> cl = defineClass(bytecode, staticSecurityDomain);
  92
+
  93
+            Script script;
  94
+            try {
  95
+                script = (Script)cl.newInstance();
  96
+            } catch (Exception ex) {
  97
+                throw new RuntimeException
  98
+                    ("Unable to instantiate compiled class:" + ex.toString());
  99
+            }
  100
+            return script;
  101
+        } catch (Exception e) {
  102
+            // fall back to interpreter
  103
+            return fallbackInterpreter.createScriptObject(bytecode, staticSecurityDomain);
93 104
         }
94  
-        return script;
95 105
     }
96 106
 
97 107
     public Function createFunctionObject(Context cx, Scriptable scope,
98 108
                                          Object bytecode,
99 109
                                          Object staticSecurityDomain)
100 110
     {
101  
-        Class<?> cl = defineClass(bytecode, staticSecurityDomain);
102  
-
103  
-        NativeFunction f;
104 111
         try {
105  
-            Constructor<?>ctor = cl.getConstructors()[0];
106  
-            Object[] initArgs = { scope, cx, Integer.valueOf(0) };
107  
-            f = (NativeFunction)ctor.newInstance(initArgs);
108  
-        } catch (Exception ex) {
109  
-            throw new RuntimeException
110  
-                ("Unable to instantiate compiled class:"+ex.toString());
111  
-        }
112  
-        return f;
  112
+            Class<?> cl = defineClass(bytecode, staticSecurityDomain);
  113
+
  114
+            NativeFunction f;
  115
+            try {
  116
+                Constructor<?>ctor = cl.getConstructors()[0];
  117
+                Object[] initArgs = { scope, cx, Integer.valueOf(0) };
  118
+                f = (NativeFunction)ctor.newInstance(initArgs);
  119
+            } catch (Exception ex) {
  120
+                throw new RuntimeException
  121
+                    ("Unable to instantiate compiled class:"+ex.toString());
  122
+            }
  123
+            return f;
  124
+        } catch (Exception e) {
  125
+            // fall back to interpreter
  126
+            return fallbackInterpreter.createFunctionObject(cx, scope, bytecode, staticSecurityDomain);
  127
+        }
113 128
     }
114 129
 
115 130
     private Class<?> defineClass(Object bytecode,
@@ -1248,6 +1263,8 @@ public void setMainMethodClass(String className)
1248 1263
 
1249 1264
     private double[] itsConstantList;
1250 1265
     private int itsConstantListSize;
  1266
+
  1267
+    private final Interpreter fallbackInterpreter = new Interpreter();
1251 1268
 }
1252 1269
 
1253 1270
 
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.