diff --git a/js/rhino/src/org/mozilla/javascript/Context.java b/js/rhino/src/org/mozilla/javascript/Context.java index e33ee6a0c82c..911706e2e6a8 100644 --- a/js/rhino/src/org/mozilla/javascript/Context.java +++ b/js/rhino/src/org/mozilla/javascript/Context.java @@ -1590,6 +1590,14 @@ public class Context { throw new RuntimeException("Bad feature index: " + featureIndex); } + /** + * Get/Set threshold of executed instructions counter that triggers call to + * observeInstructionCount(). + * When the threshold is zero, instruction counting is disabled, + * otherwise each time the run-time executes at least the threshold value + * of script instructions, observeInstructionCount() will + * be called. + */ public int getInstructionObserverThreshold() { return instructionThreshold; } @@ -1598,6 +1606,17 @@ public class Context { instructionThreshold = threshold; } + /** + * Allow application to monitor counter of executed script instructions + * in Context subclasses. + * Run-time calls this when instruction counting is enabled and the counter + * reaches limit set by setInstructionObserverThreshold(). + * The method is useful to observe long running scripts and if necessary + * to terminate them. + * @param instructionCount amount of script instruction executed since + * last call to observeInstructionCount + * @throws Error to terminate the script + */ protected void observeInstructionCount(int instructionCount) {} /********** end of API **********/ diff --git a/js/rhino/src/org/mozilla/javascript/Interpreter.java b/js/rhino/src/org/mozilla/javascript/Interpreter.java index 7e5c67806cfe..c2ae353fa820 100644 --- a/js/rhino/src/org/mozilla/javascript/Interpreter.java +++ b/js/rhino/src/org/mozilla/javascript/Interpreter.java @@ -2075,14 +2075,12 @@ public class Interpreter extends LabelTable { stack[++stackTop] = Undefined.instance; break; case TokenStream.THROW : - cx.interpreterSecurityDomain = null; result = stack[stackTop]; if (result == DBL_MRK) result = doubleWrap(sDbl[stackTop]); --stackTop; throw new JavaScriptException(result); case TokenStream.JTHROW : - cx.interpreterSecurityDomain = null; result = stack[stackTop]; // No need to check for DBL_MRK: result is Exception --stackTop; @@ -2197,6 +2195,8 @@ public class Interpreter extends LabelTable { int exceptionType; Object errObj; + if (ex instanceof WrappedException) + ex = (Exception) ((WrappedException)ex).unwrap(); if (ex instanceof EcmaError) { errObj = ((EcmaError)ex).getErrorObject(); exceptionType = ECMA; diff --git a/js/rhino/src/org/mozilla/javascript/JavaMembers.java b/js/rhino/src/org/mozilla/javascript/JavaMembers.java index 49bfdf4adffc..9217371fb571 100644 --- a/js/rhino/src/org/mozilla/javascript/JavaMembers.java +++ b/js/rhino/src/org/mozilla/javascript/JavaMembers.java @@ -102,7 +102,8 @@ class JavaMembers { throw new RuntimeException("unexpected IllegalAccessException "+ "accessing Java field"); } catch (InvocationTargetException e) { - throw new WrappedException(e.getTargetException()); + throw WrappedException.wrapException( + JavaScriptException.wrapException(scope, e)); } // Need to wrap the object before we return it. scope = ScriptableObject.getTopLevelScope(scope); @@ -183,8 +184,8 @@ class JavaMembers { } - public void put(String name, Object javaObject, Object value, - boolean isStatic) + public void put(Scriptable scope, String name, Object javaObject, + Object value, boolean isStatic) { Hashtable ht = isStatic ? staticMembers : members; Object member = ht.get(name); @@ -212,7 +213,8 @@ class JavaMembers { throw new RuntimeException("unexpected IllegalAccessException " + "accessing Java field"); } catch (InvocationTargetException e) { - throw new WrappedException(e.getTargetException()); + throw WrappedException.wrapException( + JavaScriptException.wrapException(scope, e)); } } else { diff --git a/js/rhino/src/org/mozilla/javascript/NativeJavaClass.java b/js/rhino/src/org/mozilla/javascript/NativeJavaClass.java index ac6039a3a05f..73206de1cead 100644 --- a/js/rhino/src/org/mozilla/javascript/NativeJavaClass.java +++ b/js/rhino/src/org/mozilla/javascript/NativeJavaClass.java @@ -111,7 +111,7 @@ public class NativeJavaClass extends NativeJavaObject implements Function { } public void put(String name, Scriptable start, Object value) { - members.put(name, javaObject, value, true); + members.put(this, name, javaObject, value, true); } public Object[] getIds() { diff --git a/js/rhino/src/org/mozilla/javascript/NativeJavaObject.java b/js/rhino/src/org/mozilla/javascript/NativeJavaObject.java index 1d910b20e862..7b6a517d249e 100644 --- a/js/rhino/src/org/mozilla/javascript/NativeJavaObject.java +++ b/js/rhino/src/org/mozilla/javascript/NativeJavaObject.java @@ -103,7 +103,7 @@ public class NativeJavaObject implements Scriptable, Wrapper { // prototype. Since we can't add a property to a Java object, // we modify it in the prototype rather than copy it down. if (prototype == null || members.has(name, false)) - members.put(name, javaObject, value, false); + members.put(this, name, javaObject, value, false); else prototype.put(name, prototype, value); } diff --git a/js/rhino/src/org/mozilla/javascript/Node.java b/js/rhino/src/org/mozilla/javascript/Node.java index 78424c8e00a5..b3a18f33234e 100644 --- a/js/rhino/src/org/mozilla/javascript/Node.java +++ b/js/rhino/src/org/mozilla/javascript/Node.java @@ -37,8 +37,6 @@ package org.mozilla.javascript; -import java.util.*; - /** * This class implements the root of the intermediate representation. * @@ -330,16 +328,32 @@ public class Node implements Cloneable { public Object getProp(int propType) { if (props == null) return null; - return props.get(new Integer(propType)); + return props.getObject(propType); + } + + public int getIntProp(int propType, int defaultValue) { + if (props == null) + return defaultValue; + return props.getInt(propType, defaultValue); + } + + public int getExistingIntProp(int propType) { + return props.getExistingInt(propType); } public void putProp(int propType, Object prop) { if (props == null) - props = new Hashtable(2); + props = new UintMap(2); if (prop == null) - props.remove(new Integer(propType)); + props.remove(propType); else - props.put(new Integer(propType), prop); + props.put(propType, prop); + } + + public void putIntProp(int propType, int prop) { + if (props == null) + props = new UintMap(2); + props.put(propType, prop); } public Object getDatum() { @@ -394,15 +408,13 @@ public class Node implements Cloneable { if (props == null) return sb.toString(); - Enumeration keys = props.keys(); - Enumeration elems = props.elements(); - while (keys.hasMoreElements()) { - Integer key = (Integer) keys.nextElement(); - Object elem = elems.nextElement(); + int[] keys = props.getKeys(); + for (int i = 0; i != keys.length; ++i) { + int key = keys[i]; sb.append(" ["); - sb.append(propToString(key.intValue())); + sb.append(propToString(key)); sb.append(": "); - switch (key.intValue()) { + switch (key) { case FIXUPS_PROP : // can't add this as it recurses sb.append("fixups property"); break; @@ -416,7 +428,12 @@ public class Node implements Cloneable { sb.append("last use property"); break; default : - sb.append(elem.toString()); + if (props.isObjectType(key)) { + sb.append(props.getObject(key).toString()); + } + else { + sb.append(props.getExistingInt(key)); + } break; } sb.append(']'); @@ -463,7 +480,7 @@ public class Node implements Cloneable { protected Node next; // next sibling protected Node first; // first element of a linked list of children protected Node last; // last element of a linked list of children - protected Hashtable props; + protected UintMap props; protected Object datum; // encapsulated data; depends on type } diff --git a/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java b/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java index f54de2557c75..38f3db2c71cb 100644 --- a/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java +++ b/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java @@ -676,6 +676,17 @@ public class ScriptRuntime { return jse.value; } + /** + * Check a WrappedException. Unwrap a JavaScriptException and return + * the value, otherwise rethrow. + */ + public static Object unwrapWrappedException(WrappedException we) { + Throwable t = we.getWrappedException(); + if (t instanceof JavaScriptException) + return ((JavaScriptException) t).value; + throw we; + } + public static Object getProp(Object obj, String id, Scriptable scope) { Scriptable start; if (obj instanceof Scriptable) { diff --git a/js/rhino/src/org/mozilla/javascript/UintMap.java b/js/rhino/src/org/mozilla/javascript/UintMap.java new file mode 100644 index 000000000000..1a987a933c5a --- /dev/null +++ b/js/rhino/src/org/mozilla/javascript/UintMap.java @@ -0,0 +1,468 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (the "GPL"), in which case the + * provisions of the GPL are applicable instead of those above. + * If you wish to allow use of your version of this file only + * under the terms of the GPL and not to allow others to use your + * version of this file under the NPL, indicate your decision by + * deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete + * the provisions above, a recipient may use your version of this + * file under either the NPL or the GPL. + */ + +package org.mozilla.javascript; + +/** + * Map to associate non-negative integers to objects or integers. + * The map does not synchronize any of its operation, so either use + * it from a single thread or do own synchronization or perform all mutation + * operations on one thread before passing the map to others + * + * @author Igor Bukanov + * + */ + +class UintMap { + +// Map implementation via hashtable, +// follows "The Art of Computer Programming" by Donald E. Knuth + + public UintMap() { + this(4); + } + + public UintMap(int initialCapacity) { + if (checkWorld) check(initialCapacity >= 0); + // Table grow when number of stored keys >= 3/4 of max capacity + int minimalCapacity = initialCapacity * 4 / 3; + int i; + for (i = 2; (1 << i) < minimalCapacity; ++i) { } + minimalPower = i; + if (checkSelf) check(minimalPower >= 2); + } + + public boolean isEmpty() { + return keyCount == 0; + } + + public int size() { + return keyCount; + } + + public boolean has(int key) { + if (checkWorld) check(key >= 0); + return 0 <= findIndex(key); + } + + public boolean isObjectType(int key) { + if (checkWorld) check(key >= 0); + int index = findIndex(key); + return index >= 0 && isObjectTypeValue(index); + } + + public boolean isIntType(int key) { + if (checkWorld) check(key >= 0); + int index = findIndex(key); + return index >= 0 && !isObjectTypeValue(index); + } + + /** + * Get object value assigned with key. + * @return key object value or null if key is absent or does + * not have object value + */ + public Object getObject(int key) { + if (checkWorld) check(key >= 0); + if (values != null) { + int index = findIndex(key); + if (0 <= index) { + return values[index]; + } + } + return null; + } + + /** + * Get integer value assigned with key. + * @return key integer value or defaultValue if key is absent or does + * not have int value + */ + public int getInt(int key, int defaultValue) { + if (checkWorld) check(key >= 0); + if (ivaluesShift != 0) { + int index = findIndex(key); + if (0 <= index) { + if (!isObjectTypeValue(index)) { + return keys[ivaluesShift + index]; + } + } + } + return defaultValue; + } + + /** + * Get integer value assigned with key. + * @return key integer value or defaultValue if key does not exist or does + * not have int value + * @throws RuntimeException if key does not exist or does + * not have int value + */ + public int getExistingInt(int key) { + if (checkWorld) check(key >= 0); + if (ivaluesShift != 0) { + int index = findIndex(key); + if (0 <= index) { + if (!isObjectTypeValue(index)) { + return keys[ivaluesShift + index]; + } + } + } + // Key must exist + if (checkWorld) check(false); + return 0; + } + + public void put(int key, Object value) { + if (checkWorld) check(key >= 0 && value != null); + int index = ensureIndex(key, false); + if (values == null) { + values = new Object[1 << power]; + } + values[index] = value; + } + + public void put(int key, int value) { + if (checkWorld) check(key >= 0); + int index = ensureIndex(key, true); + if (ivaluesShift == 0) { + int N = 1 << power; + int[] tmp = new int[N * 2]; + System.arraycopy(keys, 0, tmp, 0, N); + keys = tmp; + ivaluesShift = N; + } + keys[ivaluesShift + index] = value; + if (values != null) { values[index] = null; } + } + + public void remove(int key) { + if (checkWorld) check(key >= 0); + int index = findIndex(key); + if (0 <= index) { + keys[index] = DELETED; + --keyCount; + if (values != null) { values[index] = null; } + } + } + + public void clear() { + power = 0; + keys = null; + values = null; + ivaluesShift = 0; + keyCount = 0; + occupiedCount = 0; + } + + /** Return array of present keys */ + public int[] getKeys() { + int[] keys = this.keys; + int n = keyCount; + int[] result = new int[n]; + for (int i = 0; n != 0; ++i) { + int entry = keys[i]; + if (entry != EMPTY && entry != DELETED) { + result[--n] = entry; + } + } + return result; + } + + private static int tableLookupStep(int fraction, int mask, int power) { + int shift = 32 - 2 * power; + if (shift >= 0) { + return ((fraction >>> shift) & mask) | 1; + } + else { + return (fraction & (mask >>> -shift)) | 1; + } + } + + private int findIndex(int key) { + int[] keys = this.keys; + if (keys != null) { + int fraction = key * A; + int index = fraction >>> (32 - power); + int entry = keys[index]; + if (entry == key) { return index; } + if (entry != EMPTY) { + // Search in table after first failed attempt + int mask = (1 << power) - 1; + int step = tableLookupStep(fraction, mask, power); + int n = 0; + do { + if (checkSelf) check(n++ < occupiedCount); + index = (index + step) & mask; + entry = keys[index]; + if (entry == key) { return index; } + } while (entry != EMPTY); + } + } + return -1; + } + + private int getFreeIndex(int key) { + int[] keys = this.keys; + int fraction = key * A; + int index = fraction >>> (32 - power); + if (keys[index] != EMPTY) { + int mask = (1 << power) - 1; + int step = tableLookupStep(fraction, mask, power); + int firstIndex = index; + do { + if (checkSelf) check(keys[index] != DELETED); + index = (index + step) & mask; + if (checkSelf) check(firstIndex != index); + } while (keys[index] != EMPTY); + } + return index; + } + + private void rehashTable(boolean ensureIntSpace) { + if (keys == null) { power = minimalPower; } + else { + // Check if removing deleted entries would free enough space + if (keyCount * 2 >= occupiedCount) { + // Need to grow: less then half of deleted entries + ++power; + } + } + int N = 1 << power; + int[] old = keys; + int oldShift = ivaluesShift; + if (oldShift == 0 && !ensureIntSpace) { + keys = new int[N]; + } + else { + ivaluesShift = N; keys = new int[N * 2]; + } + for (int i = 0; i != N; ++i) { keys[i] = EMPTY; } + + Object[] oldValues = values; + if (oldValues != null) { values = new Object[N]; } + + if (old != null) { + for (int i = 0, remaining = keyCount; remaining != 0; ++i) { + int entry = old[i]; + if (entry != EMPTY && entry != DELETED) { + int index = getFreeIndex(entry); + keys[index] = entry; + if (oldValues != null) { + values[index] = oldValues[i]; + } + if (oldShift != 0) { + keys[ivaluesShift + index] = old[oldShift + i]; + } + --remaining; + } + } + } + occupiedCount = keyCount; + } + +// Ensure key index creating one if necessary + private int ensureIndex(int key, boolean intType) { + int index = -1; + int firstDeleted = -1; + int[] keys = this.keys; + if (keys != null) { + int fraction = key * A; + index = fraction >>> (32 - power); + int entry = keys[index]; + if (entry == key) { return index; } + if (entry != EMPTY) { + if (entry == DELETED) { firstDeleted = index; } + // Search in table after first failed attempt + int mask = (1 << power) - 1; + int step = tableLookupStep(fraction, mask, power); + int n = 0; + do { + if (checkSelf) check(n++ < occupiedCount); + index = (index + step) & mask; + entry = keys[index]; + if (entry == key) { return index; } + if (entry == DELETED && firstDeleted < 0) { + firstDeleted = index; + } + } while (entry != EMPTY); + } + } + // Inserting of new key + if (checkSelf) check(keys == null || keys[index] == EMPTY); + if (firstDeleted >= 0) { + index = firstDeleted; + } + else { + // Need to consume empty entry: check occupation level + if (keys == null || occupiedCount * 4 >= (1 << power) * 3) { + // Too litle unused entries: rehash + rehashTable(intType); + keys = this.keys; + index = getFreeIndex(key); + } + ++occupiedCount; + } + keys[index] = key; + ++keyCount; + return index; + } + + private boolean isObjectTypeValue(int index) { + if (checkSelf) check(index >= 0 && index < (1 << power)); + return values != null && values[index] != null; + } + + private static void check(boolean condition) { + if (!condition) { throw new RuntimeException(); } + } + +// Rudimentary support for Design-by-Contract + private static final boolean checkWorld = true; + private static final boolean checkSelf = checkWorld && false; + +// A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32) +// See Knuth etc. + private static final int A = 0x9e3779b9; + + private static final int EMPTY = -1; + private static final int DELETED = -2; + +// Structure of kyes and values arrays (N == 1 << power): +// keys[0 <= i < N]: key value or EMPTY or DELETED mark +// values[0 <= i < N]: value of key at keys[i] +// keys[N <= i < 2N]: int values of keys at keys[i - N] + + private int[] keys; + private Object[] values; + + private int minimalPower; + private int power; + private int keyCount; + private int occupiedCount; // == keyCount + deleted_count + + // If ivaluesShift != 0, keys[ivaluesShift + index] contains integer + // values associated with keys + private int ivaluesShift; + +/* + public static void main(String[] args) { + UintMap map; + map = new UintMap(); + testHash(map, 10 * 1000); + map = new UintMap(30 * 1000); + testHash(map, 10 * 100); + map.clear(); + testHash(map, 4); + map = new UintMap(0); + testHash(map, 10 * 100); + } + + private static void testHash(UintMap map, int N) { + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + map.put(i, i); + check(i == map.getInt(i, -1)); + } + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + map.put(i, i); + check(i == map.getInt(i, -1)); + } + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + map.put(i, new Integer(i)); + check(-1 == map.getInt(i, -1)); + Integer obj = (Integer)map.getObject(i); + check(obj != null && i == obj.intValue()); + } + + check(map.size() == N); + + System.out.print("."); System.out.flush(); + int[] keys = map.getKeys(); + check(keys.length == N); + for (int i = 0; i != N; ++i) { + int key = keys[i]; + check(map.has(key)); + check(!map.isIntType(key)); + check(map.isObjectType(key)); + Integer obj = (Integer) map.getObject(key); + check(obj != null && key == obj.intValue()); + } + + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + check(-1 == map.getInt(i, -1)); + } + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + map.put(i * i, i); + check(i == map.getInt(i * i, -1)); + } + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + check(i == map.getInt(i * i, -1)); + } + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + map.put(i * i, new Integer(i)); + check(-1 == map.getInt(i * i, -1)); + map.remove(i * i); + check(!map.has(i * i)); + map.put(i * i, i); + check(map.isIntType(i * i)); + check(null == map.getObject(i * i)); + map.remove(i * i); + check(!map.isObjectType(i * i)); + check(!map.isIntType(i * i)); + } + + int old_size = map.size(); + for (int i = 0; i != N; ++i) { + map.remove(i * i); + check(map.size() == old_size); + } + + System.out.println(); System.out.flush(); + } +//*/ +} diff --git a/js/rhino/src/org/mozilla/javascript/optimizer/Codegen.java b/js/rhino/src/org/mozilla/javascript/optimizer/Codegen.java index d7f8fbb029ab..4d26d7f10449 100644 --- a/js/rhino/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/js/rhino/src/org/mozilla/javascript/optimizer/Codegen.java @@ -2184,44 +2184,29 @@ public class Codegen extends Interpreter { int realEnd = acquireLabel(); addByteCode(ByteCode.GOTO, realEnd); + // javascript handler; unwrap exception and GOTO to javascript // catch area. if (catchTarget != null) { - int jsHandler = classFile.markHandler(acquireLabel()); - - // MS JVM gets cranky if the exception object is left on the stack - short exceptionObject = getNewWordLocal(); - astore(exceptionObject); - - // reset the variable object local - aload(savedVariableObject); - astore(variableObjectLocal); - - aload(exceptionObject); - releaseWordLocal(exceptionObject); - - // unwrap the exception... - addScriptRuntimeInvoke("unwrapJavaScriptException", - "(Lorg/mozilla/javascript/JavaScriptException;)", - "Ljava/lang/Object;"); - // get the label to goto int catchLabel = ((Integer)catchTarget.getProp(Node.LABEL_PROP)).intValue(); - addByteCode(ByteCode.GOTO, catchLabel); - - // mark the handler - classFile.addExceptionHandler - (startLabel, catchLabel, jsHandler, - "org/mozilla/javascript/JavaScriptException"); + generateCatchBlock(JAVASCRIPTEXCEPTION, savedVariableObject, + catchLabel, startLabel); + /* + * catch WrappedExceptions, see if they are wrapped + * JavaScriptExceptions. Otherwise, rethrow. + */ + generateCatchBlock(WRAPPEDEXCEPTION, savedVariableObject, + catchLabel, startLabel); /* we also need to catch EcmaErrors and feed the associated error object to the handler */ - jsHandler = classFile.markHandler(acquireLabel()); - exceptionObject = getNewWordLocal(); + int jsHandler = classFile.markHandler(acquireLabel()); + short exceptionObject = getNewWordLocal(); astore(exceptionObject); aload(savedVariableObject); astore(variableObjectLocal); @@ -2268,6 +2253,51 @@ public class Codegen extends Interpreter { markLabel(realEnd); } + private final int JAVASCRIPTEXCEPTION = 0; + private final int WRAPPEDEXCEPTION = 1; + + private void generateCatchBlock(int exceptionType, + short savedVariableObject, + int catchLabel, + int startLabel) + { + int handler = classFile.markHandler(acquireLabel()); + + // MS JVM gets cranky if the exception object is left on the stack + short exceptionObject = getNewWordLocal(); + astore(exceptionObject); + + // reset the variable object local + aload(savedVariableObject); + astore(variableObjectLocal); + + aload(exceptionObject); + releaseWordLocal(exceptionObject); + + if (exceptionType == JAVASCRIPTEXCEPTION) { + // unwrap the exception... + addScriptRuntimeInvoke("unwrapJavaScriptException", + "(Lorg/mozilla/javascript/JavaScriptException;)", + "Ljava/lang/Object;"); + } else { + // unwrap the exception... + addScriptRuntimeInvoke("unwrapWrappedException", + "(Lorg/mozilla/javascript/WrappedException;)", + "Ljava/lang/Object;"); + } + + + String exceptionName = exceptionType == JAVASCRIPTEXCEPTION + ? "org/mozilla/javascript/JavaScriptException" + : "org/mozilla/javascript/WrappedException"; + + // mark the handler + classFile.addExceptionHandler(startLabel, catchLabel, handler, + exceptionName); + + addByteCode(ByteCode.GOTO, catchLabel); + } + private void visitThrow(Node node, Node child) { visitStatement(node); while (child != null) {