--- rhino1_7R2/src/org/mozilla/javascript/BaseFunction.java.orig 2008-11-05 14:13:22.000000000 -0500 +++ rhino1_7R2/src/org/mozilla/javascript/BaseFunction.java 2008-12-10 16:13:48.841102316 -0500 @@ -401,6 +401,28 @@ return sb.toString(); } + String compress(int indent, int flags) + { + StringBuffer sb = new StringBuffer(); + String FuncName = null; + boolean justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); + if (!justbody) { + sb.append("function"); + FuncName = getFunctionName(); + if(FuncName.length()>0){ + sb.append(" "+FuncName); + } + sb.append("(){"); + } + sb.append("[native code, arity="); + sb.append(getArity()); + sb.append("]"); + if (!justbody) { + sb.append("}"); + } + return sb.toString(); + } + public int getArity() { return 0; } public int getLength() { return 0; } --- rhino1_7R2/src/org/mozilla/javascript/Context.java.orig 2008-11-05 14:13:22.000000000 -0500 +++ rhino1_7R2/src/org/mozilla/javascript/Context.java 2008-12-10 16:17:52.086973772 -0500 @@ -1241,6 +1241,33 @@ this, scope, args); } + public final String decompileReader(Scriptable scope, Reader in, + String sourceName, int lineno, + Object securityDomain) + throws IOException + { + Script script = compileReader(scope, in, sourceName, lineno, + securityDomain); + if (script != null) { + // System.err.println(script); + return decompileScript(script, 0); + } else { + return null; + } + } + + public final String compressReader(Scriptable scope, Script script, String source, + String sourceName, int lineno, Object securityDomain){ + + if (script != null) { + // System.err.println(script); + return compressScript(script, 0, source,lineno); + } else { + return null; + } + } + + /** * Check whether a string is ready to be compiled. *

@@ -1424,6 +1451,27 @@ } /** + * Compress the script. + *

+ * Compressed script is returned. + * + * @param script the script object + * @param indent the number of spaces to indent the result + * @return a string representing the script source + */ + public final String compressScript(Script script, int indent, String source,int lineno) + { + NativeFunction scriptImpl = (NativeFunction) script; + + CompilerEnvirons compilerEnv = new CompilerEnvirons(); + + Parser parser = new Parser(compilerEnv, compilerEnv.getErrorReporter()); + + ScriptOrFnNode tree = parser.parse(source, null, lineno); + + return scriptImpl.compress( tree,indent, 0); + } + /** * Decompile a JavaScript Function. *

* Decompiles a previously compiled JavaScript function object to @@ -2358,7 +2406,6 @@ sourceReader = null; } } - Parser p = new Parser(compilerEnv, compilationErrorReporter); if (returnFunction) { p.calledByCompileFunction = true; @@ -2369,6 +2416,7 @@ } else { tree = p.parse(sourceReader, sourceName, lineno); } + if (returnFunction) { if (!(tree.getFunctionCount() == 1 && tree.getFirstChild() != null --- rhino1_7R2/src/org/mozilla/javascript/Decompiler.java.orig 2008-11-05 14:13:22.000000000 -0500 +++ rhino1_7R2/src/org/mozilla/javascript/Decompiler.java 2008-12-10 16:13:48.845099830 -0500 @@ -41,6 +41,11 @@ package org.mozilla.javascript; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + /** * The following class save decompilation information about the source. * Source information is returned from the parser as a String @@ -74,6 +79,264 @@ * the final constant pool entry from information available at parse * time. */ + +class TokenMapper { + private ArrayList functionBracePositions = new ArrayList(); + + /** + * Map of all replaced tokens + */ + private ArrayList replacedTokens = new ArrayList(); + + /** + * Collection of Function nodes + */ + private static ObjArray funcObjects = new ObjArray(); + + /** + * Map of each Function node and all the variables in its current function + * scope, other variables found while traversing the prototype chain and + * variables found in the top-level scope. + */ + private static ArrayList functionVarMappings = new ArrayList(); + + public int functionNum = 0; + + private int parentScope = 0; + + private int lastTokenCount = 0; + + /** + * Generate new compressed tokens + *

+ * + * @param token + * value of the string token + * @param hasNewMapping + * boolean value indicating a new variable binding + * @return compressed token + */ + private String getMappedToken(String token, boolean hasNewMapping) { + String newToken = null; + HashMap tokens = null; + String blank = new String(""); + int localScope = functionBracePositions.size() - 1; + + String oldToken = getPreviousTokenMapping(token, hasNewMapping); + + if (!oldToken.equalsIgnoreCase(blank)) { + return oldToken; + } else if ((hasNewMapping || isInScopeChain(token))) { + lastTokenCount++; + newToken = new String("_" + Integer.toHexString(lastTokenCount)); + if (newToken.length() >= token.length()) { + newToken = token; + } + if (hasNewMapping) { + tokens = (HashMap) replacedTokens.get(localScope); + } else { + tokens = (HashMap) replacedTokens.get(parentScope); + } + + tokens.put(token, newToken); + return newToken; + } + return token; + } + + /** + * Checks for variable names in prototype chain + *

+ * + * @param token + * value of the string token + * @return boolean value indicating if the token is present in the chained + * scope + */ + private boolean isInScopeChain(String token) { + int scope = functionBracePositions.size(); + HashMap chainedScopeVars = (HashMap) functionVarMappings + .get(functionNum); + if (!chainedScopeVars.isEmpty()) { + for (int i = scope; i > 0; i--) { + if (chainedScopeVars.containsKey(new Integer(i))) { + parentScope = i - 1; + List temp = Arrays.asList((String[]) chainedScopeVars + .get(new Integer(i))); + if (temp.indexOf(token) != -1) { + return true; + } + } + } + } + return false; + } + + /** + * Checks previous token mapping + *

+ * + * @param token + * value of the string token + * @param hasNewMapping + * boolean value indicating a new variable binding + * @return string value of the previous token or blank string + */ + private String getPreviousTokenMapping(String token, boolean hasNewMapping) { + String result = new String(""); + int scope = replacedTokens.size() - 1; + + if (scope < 0) { + return result; + } + + if (hasNewMapping) { + HashMap tokens = (HashMap) (replacedTokens.get(scope)); + if (tokens.containsKey(token)) { + result = (String) tokens.get(token); + return result; + } + } else { + for (int i = scope; i > -1; i--) { + HashMap tokens = (HashMap) (replacedTokens.get(i)); + if (tokens.containsKey(token)) { + result = (String) tokens.get(token); + return result; + } + } + } + return result; + } + + /** + * Generate mappings for each Function node and parameters and variables + * names associated with it. + *

+ * + * @param parseTree + * Mapping for each function node and corresponding parameters & + * variables names + */ + private void collectFunctionMappings(ScriptOrFnNode parseTree) { + int level = -1; + collectFuncNodes(parseTree, level); + } + + /** + * Recursive method to traverse all Function nodes + *

+ * + * @param parseTree + * Mapping for each function node and corresponding parameters & + * variables names + * @param level + * scoping level + */ + private static void collectFuncNodes(ScriptOrFnNode parseTree, int level) { + level++; + functionVarMappings.add(new HashMap()); + + HashMap bindingNames = (HashMap) functionVarMappings + .get(functionVarMappings.size() - 1); + bindingNames.put(new Integer(level), parseTree.getParamAndVarNames()); + funcObjects.add(parseTree); + + int nestedCount = parseTree.getFunctionCount(); + for (int i = 0; i != nestedCount; ++i) { + collectFuncNodes(parseTree.getFunctionNode(i), level); + bindingNames = (HashMap) functionVarMappings + .get(functionVarMappings.size() - 1); + bindingNames.put(new Integer(level), parseTree + .getParamAndVarNames()); + } + } + + /** + * Compress the script + *

+ * + * @param encodedSource + * encoded source string + * @param offset + * position within the encoded source + * @param asQuotedString + * boolean value indicating a quoted string + * @param sb + * String buffer reference + * @param prevToken + * Previous token in encoded source + * @param inArgsList + * boolean value indicating position inside arguments list + * @param currentLevel + * embeded function level + * @param parseTree + * Mapping of each function node and corresponding parameters & + * variables names + * @return compressed script + */ + public int sourceCompress(String encodedSource, int offset, + boolean asQuotedString, StringBuffer sb, int prevToken, + boolean inArgsList, int currentLevel, ScriptOrFnNode parseTree) { + + boolean hasNewMapping = false; + + if (functionVarMappings.isEmpty()) + collectFunctionMappings(parseTree); + + int length = encodedSource.charAt(offset); + ++offset; + if ((0x8000 & length) != 0) { + length = ((0x7FFF & length) << 16) | encodedSource.charAt(offset); + ++offset; + } + if (sb != null) { + String str = encodedSource.substring(offset, offset + length); + String sourceStr = new String(str); + if ((prevToken == Token.VAR) || (inArgsList)) { + hasNewMapping = true; + } + if (((functionBracePositions.size() > 0) && (currentLevel >= (((Integer) functionBracePositions + .get(functionBracePositions.size() - 1)).intValue()))) + || (inArgsList)) { + if (prevToken != Token.DOT) { + str = this.getMappedToken(str, hasNewMapping); + } + } + if ((!inArgsList) && (asQuotedString)) { + if ((prevToken == Token.LC) || (prevToken == Token.COMMA)) { + str = sourceStr; + } + } + if (!asQuotedString) { + sb.append(str); + } else { + sb.append('"'); + sb.append(ScriptRuntime.escapeString(str)); + sb.append('"'); + } + } + return offset + length; + } + + public void enterNestingLevel(int braceNesting) { + functionBracePositions.add(new Integer(braceNesting + 1)); + replacedTokens.add(new HashMap()); + } + + public void leaveNestingLevel(int braceNesting) { + Integer bn = new Integer(braceNesting); + + if ((functionBracePositions.contains(bn)) + && (replacedTokens.size() > 0)) { + // remove our mappings now! + int scopedSize = replacedTokens.size(); + replacedTokens.remove(scopedSize - 1); + functionBracePositions.remove(bn); + } + } +} + + public class Decompiler { /** @@ -270,6 +533,512 @@ return new String(sourceBuffer, offset, sourceTop - offset); } + private static TokenMapper tm = new TokenMapper(); + + /** + * Compress the script + *

+ * + * @param encodedSource encoded source string + * @param flags Flags specifying format of decompilation output + * @param properties Decompilation properties + * @param parseTree Mapping for each function node and corresponding parameters & variables names + * @return compressed script + */ + public static String compress(String encodedSource, int flags, + UintMap properties, ScriptOrFnNode parseTree){ + + int length = encodedSource.length(); + if (length == 0) { return ""; } + int indent = properties.getInt(INITIAL_INDENT_PROP, 0); + if (indent < 0) throw new IllegalArgumentException(); + int indentGap = properties.getInt(INDENT_GAP_PROP, 4); + if (indentGap < 0) throw new IllegalArgumentException(); + int caseGap = properties.getInt(CASE_GAP_PROP, 2); + if (caseGap < 0) throw new IllegalArgumentException(); + StringBuffer result = new StringBuffer(); + boolean justFunctionBody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); + boolean toSource = (0 != (flags & Decompiler.TO_SOURCE_FLAG)); + // Spew tokens in source, for debugging. + // as TYPE number char + if (printSource) { + System.err.println("length:" + length); + for (int i = 0; i < length; ++i) { + // Note that tokenToName will fail unless Context.printTrees + // is true. + String tokenname = null; + if (Token.printNames) { + tokenname = Token.name(encodedSource.charAt(i)); + } + if (tokenname == null) { + tokenname = "---"; + } + String pad = tokenname.length() > 7 + ? "\t" + : "\t\t"; + System.err.println + (tokenname + + pad + (int)encodedSource.charAt(i) + + "\t'" + ScriptRuntime.escapeString + (encodedSource.substring(i, i+1)) + + "'"); + } + System.err.println(); + } + int braceNesting = 0; + boolean afterFirstEOL = false; + int i = 0; + int prevToken = 0; + boolean primeFunctionNesting = false; + boolean inArgsList = false; + boolean primeInArgsList = false; + int topFunctionType; + if (encodedSource.charAt(i) == Token.SCRIPT) { + ++i; + topFunctionType = -1; + } else { + topFunctionType = encodedSource.charAt(i + 1); + } + if (!toSource) { + // add an initial newline to exactly match js. + // result.append('\n'); + for (int j = 0; j < indent; j++){ + // result.append(' '); + result.append(""); + } + } else { + if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) { + result.append('('); + } + } + while (i < length) { + if(i>0){ + prevToken = encodedSource.charAt(i-1); + } + // System.out.println(Token.name(getNext(source, length, i))); + switch(encodedSource.charAt(i)) { + case Token.NAME: + case Token.REGEXP: // re-wrapped in '/'s in parser... + int jumpPos = getSourceStringEnd(encodedSource, i+1); + if(Token.OBJECTLIT == encodedSource.charAt(jumpPos)){ + i = printSourceString(encodedSource, i + 1, false, result); + }else{ + i = tm.sourceCompress( encodedSource, i + 1, false, result, prevToken, + inArgsList, braceNesting, parseTree); + } + continue; + case Token.STRING: + i = printSourceString(encodedSource, i + 1, true, result); + continue; + case Token.NUMBER: + i = printSourceNumber(encodedSource, i + 1, result); + continue; + case Token.TRUE: + result.append("true"); + break; + case Token.FALSE: + result.append("false"); + break; + case Token.NULL: + result.append("null"); + break; + case Token.THIS: + result.append("this"); + break; + case Token.FUNCTION: + ++i; // skip function type + tm.functionNum++; + primeInArgsList = true; + primeFunctionNesting = true; + result.append("function"); + if (Token.LP != getNext(encodedSource, length, i)) { + result.append(' '); + } + break; + case FUNCTION_END: + // Do nothing + break; + case Token.COMMA: + result.append(","); + break; + case Token.LC: + ++braceNesting; + if (Token.EOL == getNext(encodedSource, length, i)){ + indent += indentGap; + } + result.append('{'); + // // result.append('\n'); + break; + case Token.RC: { + tm.leaveNestingLevel(braceNesting); + --braceNesting; + /* don't print the closing RC if it closes the + * toplevel function and we're called from + * decompileFunctionBody. + */ + if(justFunctionBody && braceNesting == 0){ + break; + } + // // result.append('\n'); + result.append('}'); + // // result.append(' '); + switch (getNext(encodedSource, length, i)) { + case Token.EOL: + case FUNCTION_END: + indent -= indentGap; + break; + case Token.WHILE: + case Token.ELSE: + indent -= indentGap; + // result.append(' '); + result.append(""); + break; + } + break; + } + case Token.LP: + if(primeInArgsList){ + inArgsList = true; + primeInArgsList = false; + } + if(primeFunctionNesting){ + tm.enterNestingLevel(braceNesting); + primeFunctionNesting = false; + } + result.append('('); + break; + case Token.RP: + if(inArgsList){ + inArgsList = false; + } + result.append(')'); + /* + if (Token.LC == getNext(source, length, i)){ + result.append(' '); + } + */ + break; + case Token.LB: + result.append('['); + break; + case Token.RB: + result.append(']'); + break; + case Token.EOL: { + if (toSource) break; + boolean newLine = true; + if (!afterFirstEOL) { + afterFirstEOL = true; + if (justFunctionBody) { + /* throw away just added 'function name(...) {' + * and restore the original indent + */ + result.setLength(0); + indent -= indentGap; + newLine = false; + } + } + if (newLine) { + result.append('\n'); + } + /* + */ + /* add indent if any tokens remain, + * less setback if next token is + * a label, case or default. + */ + if (i + 1 < length) { + int less = 0; + int nextToken = encodedSource.charAt(i + 1); + if (nextToken == Token.CASE + || nextToken == Token.DEFAULT) + { + less = indentGap - caseGap; + } else if (nextToken == Token.RC) { + less = indentGap; + } + /* elaborate check against label... skip past a + * following inlined NAME and look for a COLON. + */ + else if (nextToken == Token.NAME) { + int afterName = getSourceStringEnd(encodedSource, i + 2); + if (encodedSource.charAt(afterName) == Token.COLON) + less = indentGap; + } + for (; less < indent; less++){ + // result.append(' '); + result.append(""); + } + } + break; + } + case Token.DOT: + result.append('.'); + break; + case Token.NEW: + result.append("new "); + break; + case Token.DELPROP: + result.append("delete "); + break; + case Token.IF: + result.append("if"); + break; + case Token.ELSE: + result.append("else"); + break; + case Token.FOR: + result.append("for"); + break; + case Token.IN: + result.append(" in "); + break; + case Token.WITH: + result.append("with"); + break; + case Token.WHILE: + result.append("while"); + break; + case Token.DO: + result.append("do"); + break; + case Token.TRY: + result.append("try"); + break; + case Token.CATCH: + result.append("catch"); + break; + case Token.FINALLY: + result.append("finally"); + break; + case Token.THROW: + result.append("throw "); + break; + case Token.SWITCH: + result.append("switch"); + break; + case Token.BREAK: + result.append("break"); + if(Token.NAME == getNext(encodedSource, length, i)){ + result.append(' '); + } + break; + case Token.CONTINUE: + result.append("continue"); + if(Token.NAME == getNext(encodedSource, length, i)){ + result.append(' '); + } + break; + case Token.CASE: + result.append("case "); + break; + case Token.DEFAULT: + result.append("default"); + break; + case Token.RETURN: + result.append("return"); + if(Token.SEMI != getNext(encodedSource, length, i)){ + result.append(' '); + } + break; + case Token.VAR: + result.append("var "); + break; + case Token.SEMI: + result.append(';'); + // result.append('\n'); + /* + if (Token.EOL != getNext(source, length, i)) { + // separators in FOR + result.append(' '); + } + */ + break; + case Token.ASSIGN: + result.append("="); + break; + case Token.ASSIGN_ADD: + result.append("+="); + break; + case Token.ASSIGN_SUB: + result.append("-="); + break; + case Token.ASSIGN_MUL: + result.append("*="); + break; + case Token.ASSIGN_DIV: + result.append("/="); + break; + case Token.ASSIGN_MOD: + result.append("%="); + break; + case Token.ASSIGN_BITOR: + result.append("|="); + break; + case Token.ASSIGN_BITXOR: + result.append("^="); + break; + case Token.ASSIGN_BITAND: + result.append("&="); + break; + case Token.ASSIGN_LSH: + result.append("<<="); + break; + case Token.ASSIGN_RSH: + result.append(">>="); + break; + case Token.ASSIGN_URSH: + result.append(">>>="); + break; + case Token.HOOK: + result.append("?"); + break; + case Token.OBJECTLIT: + // pun OBJECTLIT to mean colon in objlit property + // initialization. + // This needs to be distinct from COLON in the general case + // to distinguish from the colon in a ternary... which needs + // different spacing. + result.append(':'); + break; + case Token.COLON: + if (Token.EOL == getNext(encodedSource, length, i)) + // it's the end of a label + result.append(':'); + else + // it's the middle part of a ternary + result.append(":"); + break; + case Token.OR: + result.append("||"); + break; + case Token.AND: + result.append("&&"); + break; + case Token.BITOR: + result.append("|"); + break; + case Token.BITXOR: + result.append("^"); + break; + case Token.BITAND: + result.append("&"); + break; + case Token.SHEQ: + result.append("==="); + break; + case Token.SHNE: + result.append("!=="); + break; + case Token.EQ: + result.append("=="); + break; + case Token.NE: + result.append("!="); + break; + case Token.LE: + result.append("<="); + break; + case Token.LT: + result.append("<"); + break; + case Token.GE: + result.append(">="); + break; + case Token.GT: + result.append(">"); + break; + case Token.INSTANCEOF: + // FIXME: does this really need leading space? + result.append(" instanceof "); + break; + case Token.LSH: + result.append("<<"); + break; + case Token.RSH: + result.append(">>"); + break; + case Token.URSH: + result.append(">>>"); + break; + case Token.TYPEOF: + result.append("typeof "); + break; + case Token.VOID: + result.append("void "); + break; + case Token.NOT: + result.append('!'); + break; + case Token.BITNOT: + result.append('~'); + break; + case Token.POS: + result.append('+'); + break; + case Token.NEG: + result.append('-'); + break; + case Token.INC: + if(Token.ADD == prevToken){ + result.append(' '); + } + result.append("++"); + if(Token.ADD == getNext(encodedSource, length, i)){ + result.append(' '); + } + break; + case Token.DEC: + if(Token.SUB == prevToken){ + result.append(' '); + } + result.append("--"); + if(Token.SUB == getNext(encodedSource, length, i)){ + result.append(' '); + } + break; + case Token.ADD: + result.append("+"); + break; + case Token.SUB: + result.append("-"); + break; + case Token.MUL: + result.append("*"); + break; + case Token.DIV: + result.append("/"); + break; + case Token.MOD: + result.append("%"); + break; + case Token.COLONCOLON: + result.append("::"); + break; + case Token.DOTDOT: + result.append(".."); + break; + case Token.XMLATTR: + result.append('@'); + break; + default: + // If we don't know how to decompile it, raise an exception. + throw new RuntimeException(); + } + ++i; + } + if (!toSource) { + // add that trailing newline if it's an outermost function. + // if (!justFunctionBody){ + // result.append('\n'); + // } + } else { + if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) { + result.append(')'); + } + } + return result.toString(); + } /** * Decompile the source information associated with this js * function/script back into a string. For the most part, this --- rhino1_7R2/src/org/mozilla/javascript/NativeFunction.java.orig 2008-11-05 14:13:22.000000000 -0500 +++ rhino1_7R2/src/org/mozilla/javascript/NativeFunction.java 2008-12-10 16:13:48.849099824 -0500 @@ -75,6 +75,27 @@ } } + /** + * Compress the script. + *

+ * + * @param parseTree Mapping for each function node and corresponding parameters & variables names + * @param indent How much to indent the decompiled result + * @param flags Flags specifying format of decompilation output + * @return compressed script + */ + final String compress(ScriptOrFnNode parseTree, int indent, int flags) + { + String encodedSource = getEncodedSource(); + if (encodedSource == null) { + return super.compress(indent, flags); + } else { + UintMap properties = new UintMap(1); + properties.put(Decompiler.INITIAL_INDENT_PROP, indent); + return Decompiler.compress(encodedSource, flags, properties, parseTree); + } + } + @Override public int getLength() { --- rhino1_7R2/src/org/mozilla/javascript/TokenStream.java.orig 2008-11-05 14:13:22.000000000 -0500 +++ rhino1_7R2/src/org/mozilla/javascript/TokenStream.java 2008-12-10 16:13:48.846099919 -0500 @@ -68,9 +68,12 @@ private final static int EOF_CHAR = -1; + public StringBuffer lastComment; + TokenStream(Parser parser, Reader sourceReader, String sourceString, int lineno) { + this.lastComment = new StringBuffer(); this.parser = parser; this.lineno = lineno; if (sourceReader != null) { @@ -754,6 +757,8 @@ case '/': // is it a // comment? + // FIXME: RAR: comment, need to set config to optionally keep + // instead of skipping! if (matchChar('/')) { skipLine(); continue retry; --- rhino1_7R2/toolsrc/org/mozilla/javascript/tools/debugger/downloaded/AbstractCellEditor.java.orig 2008-12-10 16:13:48.846099919 -0500 +++ rhino1_7R2/toolsrc/org/mozilla/javascript/tools/debugger/downloaded/AbstractCellEditor.java 2008-12-10 16:13:48.846099919 -0500 @@ -0,0 +1,63 @@ + + package org.mozilla.javascript.tools.debugger.downloaded; + import java.awt.Component; + +import java.awt.event.*; +import java.awt.AWTEvent; +import javax.swing.*; +import javax.swing.event.*; +import java.util.EventObject; +import java.io.Serializable; + +public class AbstractCellEditor implements CellEditor { + + protected EventListenerList listenerList = new EventListenerList(); + + public Object getCellEditorValue() { return null; } + public boolean isCellEditable(EventObject e) { return true; } + public boolean shouldSelectCell(EventObject anEvent) { return false; } + public boolean stopCellEditing() { return true; } + public void cancelCellEditing() {} + + public void addCellEditorListener(CellEditorListener l) { + listenerList.add(CellEditorListener.class, l); + } + + public void removeCellEditorListener(CellEditorListener l) { + listenerList.remove(CellEditorListener.class, l); + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. + * @see EventListenerList + */ + protected void fireEditingStopped() { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==CellEditorListener.class) { + ((CellEditorListener)listeners[i+1]).editingStopped(new ChangeEvent(this)); + } + } + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. + * @see EventListenerList + */ + protected void fireEditingCanceled() { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==CellEditorListener.class) { + ((CellEditorListener)listeners[i+1]).editingCanceled(new ChangeEvent(this)); + } + } + } +} --- rhino1_7R2/toolsrc/org/mozilla/javascript/tools/debugger/downloaded/JTreeTable.java.orig 2008-12-10 16:13:48.847099847 -0500 +++ rhino1_7R2/toolsrc/org/mozilla/javascript/tools/debugger/downloaded/JTreeTable.java 2008-12-10 16:13:48.847099847 -0500 @@ -0,0 +1,356 @@ +/* + * @(#)JTreeTable.java 1.2 98/10/27 + * + * Copyright 1997, 1998 by Sun Microsystems, Inc., + * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. + * All rights reserved. + * + * This software is the confidential and proprietary information + * of Sun Microsystems, Inc. ("Confidential Information"). You + * shall not disclose such Confidential Information and shall use + * it only in accordance with the terms of the license agreement + * you entered into with Sun. + */ + + + package org.mozilla.javascript.tools.debugger.downloaded; + import javax.swing.*; + +import javax.swing.event.*; +import javax.swing.tree.*; +import javax.swing.table.*; + +import java.awt.Dimension; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Rectangle; + +import java.awt.event.MouseEvent; + +import java.util.EventObject; + +/** + * This example shows how to create a simple JTreeTable component, + * by using a JTree as a renderer (and editor) for the cells in a + * particular column in the JTable. + * + * @version 1.2 10/27/98 + * + * @author Philip Milne + * @author Scott Violet + */ +public class JTreeTable extends JTable { + /** A subclass of JTree. */ + protected TreeTableCellRenderer tree; + + public JTreeTable(TreeTableModel treeTableModel) { + super(); + + // Create the tree. It will be used as a renderer and editor. + tree = new TreeTableCellRenderer(treeTableModel); + + // Install a tableModel representing the visible rows in the tree. + super.setModel(new TreeTableModelAdapter(treeTableModel, tree)); + + // Force the JTable and JTree to share their row selection models. + ListToTreeSelectionModelWrapper selectionWrapper = new + ListToTreeSelectionModelWrapper(); + tree.setSelectionModel(selectionWrapper); + setSelectionModel(selectionWrapper.getListSelectionModel()); + + // Install the tree editor renderer and editor. + setDefaultRenderer(TreeTableModel.class, tree); + setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor()); + + // No grid. + setShowGrid(false); + + // No intercell spacing + setIntercellSpacing(new Dimension(0, 0)); + + // And update the height of the trees row to match that of + // the table. + if (tree.getRowHeight() < 1) { + // Metal looks better like this. + setRowHeight(18); + } + } + + /** + * Overridden to message super and forward the method to the tree. + * Since the tree is not actually in the component hieachy it will + * never receive this unless we forward it in this manner. + */ + public void updateUI() { + super.updateUI(); + if(tree != null) { + tree.updateUI(); + } + // Use the tree's default foreground and background colors in the + // table. + LookAndFeel.installColorsAndFont(this, "Tree.background", + "Tree.foreground", "Tree.font"); + } + + /* Workaround for BasicTableUI anomaly. Make sure the UI never tries to + * paint the editor. The UI currently uses different techniques to + * paint the renderers and editors and overriding setBounds() below + * is not the right thing to do for an editor. Returning -1 for the + * editing row in this case, ensures the editor is never painted. + */ + public int getEditingRow() { + return (getColumnClass(editingColumn) == TreeTableModel.class) ? -1 : + editingRow; + } + + /** + * Overridden to pass the new rowHeight to the tree. + */ + public void setRowHeight(int rowHeight) { + super.setRowHeight(rowHeight); + if (tree != null && tree.getRowHeight() != rowHeight) { + tree.setRowHeight(getRowHeight()); + } + } + + /** + * Returns the tree that is being shared between the model. + */ + public JTree getTree() { + return tree; + } + + /** + * A TreeCellRenderer that displays a JTree. + */ + public class TreeTableCellRenderer extends JTree implements + TableCellRenderer { + /** Last table/tree row asked to renderer. */ + protected int visibleRow; + + public TreeTableCellRenderer(TreeModel model) { + super(model); + } + + /** + * updateUI is overridden to set the colors of the Tree's renderer + * to match that of the table. + */ + public void updateUI() { + super.updateUI(); + // Make the tree's cell renderer use the table's cell selection + // colors. + TreeCellRenderer tcr = getCellRenderer(); + if (tcr instanceof DefaultTreeCellRenderer) { + DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer)tcr); + // For 1.1 uncomment this, 1.2 has a bug that will cause an + // exception to be thrown if the border selection color is + // null. + // dtcr.setBorderSelectionColor(null); + dtcr.setTextSelectionColor(UIManager.getColor + ("Table.selectionForeground")); + dtcr.setBackgroundSelectionColor(UIManager.getColor + ("Table.selectionBackground")); + } + } + + /** + * Sets the row height of the tree, and forwards the row height to + * the table. + */ + public void setRowHeight(int rowHeight) { + if (rowHeight > 0) { + super.setRowHeight(rowHeight); + if (JTreeTable.this != null && + JTreeTable.this.getRowHeight() != rowHeight) { + JTreeTable.this.setRowHeight(getRowHeight()); + } + } + } + + /** + * This is overridden to set the height to match that of the JTable. + */ + public void setBounds(int x, int y, int w, int h) { + super.setBounds(x, 0, w, JTreeTable.this.getHeight()); + } + + /** + * Sublcassed to translate the graphics such that the last visible + * row will be drawn at 0,0. + */ + public void paint(Graphics g) { + g.translate(0, -visibleRow * getRowHeight()); + super.paint(g); + } + + /** + * TreeCellRenderer method. Overridden to update the visible row. + */ + public Component getTableCellRendererComponent(JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, int column) { + if(isSelected) + setBackground(table.getSelectionBackground()); + else + setBackground(table.getBackground()); + + visibleRow = row; + return this; + } + } + + + /** + * TreeTableCellEditor implementation. Component returned is the + * JTree. + */ + public class TreeTableCellEditor extends AbstractCellEditor implements + TableCellEditor { + public Component getTableCellEditorComponent(JTable table, + Object value, + boolean isSelected, + int r, int c) { + return tree; + } + + /** + * Overridden to return false, and if the event is a mouse event + * it is forwarded to the tree.

+ * The behavior for this is debatable, and should really be offered + * as a property. By returning false, all keyboard actions are + * implemented in terms of the table. By returning true, the + * tree would get a chance to do something with the keyboard + * events. For the most part this is ok. But for certain keys, + * such as left/right, the tree will expand/collapse where as + * the table focus should really move to a different column. Page + * up/down should also be implemented in terms of the table. + * By returning false this also has the added benefit that clicking + * outside of the bounds of the tree node, but still in the tree + * column will select the row, whereas if this returned true + * that wouldn't be the case. + *

By returning false we are also enforcing the policy that + * the tree will never be editable (at least by a key sequence). + */ + public boolean isCellEditable(EventObject e) { + if (e instanceof MouseEvent) { + for (int counter = getColumnCount() - 1; counter >= 0; + counter--) { + if (getColumnClass(counter) == TreeTableModel.class) { + MouseEvent me = (MouseEvent)e; + MouseEvent newME = new MouseEvent(tree, me.getID(), + me.getWhen(), me.getModifiers(), + me.getX() - getCellRect(0, counter, true).x, + me.getY(), me.getClickCount(), + me.isPopupTrigger()); + tree.dispatchEvent(newME); + break; + } + } + } + return false; + } + } + + + /** + * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel + * to listen for changes in the ListSelectionModel it maintains. Once + * a change in the ListSelectionModel happens, the paths are updated + * in the DefaultTreeSelectionModel. + */ + public class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel { + /** Set to true when we are updating the ListSelectionModel. */ + protected boolean updatingListSelectionModel; + + public ListToTreeSelectionModelWrapper() { + super(); + getListSelectionModel().addListSelectionListener + (createListSelectionListener()); + } + + /** + * Returns the list selection model. ListToTreeSelectionModelWrapper + * listens for changes to this model and updates the selected paths + * accordingly. + */ + public ListSelectionModel getListSelectionModel() { + return listSelectionModel; + } + + /** + * This is overridden to set updatingListSelectionModel + * and message super. This is the only place DefaultTreeSelectionModel + * alters the ListSelectionModel. + */ + public void resetRowSelection() { + if(!updatingListSelectionModel) { + updatingListSelectionModel = true; + try { + super.resetRowSelection(); + } + finally { + updatingListSelectionModel = false; + } + } + // Notice how we don't message super if + // updatingListSelectionModel is true. If + // updatingListSelectionModel is true, it implies the + // ListSelectionModel has already been updated and the + // paths are the only thing that needs to be updated. + } + + /** + * Creates and returns an instance of ListSelectionHandler. + */ + protected ListSelectionListener createListSelectionListener() { + return new ListSelectionHandler(); + } + + /** + * If updatingListSelectionModel is false, this will + * reset the selected paths from the selected rows in the list + * selection model. + */ + protected void updateSelectedPathsFromSelectedRows() { + if(!updatingListSelectionModel) { + updatingListSelectionModel = true; + try { + // This is way expensive, ListSelectionModel needs an + // enumerator for iterating. + int min = listSelectionModel.getMinSelectionIndex(); + int max = listSelectionModel.getMaxSelectionIndex(); + + clearSelection(); + if(min != -1 && max != -1) { + for(int counter = min; counter <= max; counter++) { + if(listSelectionModel.isSelectedIndex(counter)) { + TreePath selPath = tree.getPathForRow + (counter); + + if(selPath != null) { + addSelectionPath(selPath); + } + } + } + } + } + finally { + updatingListSelectionModel = false; + } + } + } + + /** + * Class responsible for calling updateSelectedPathsFromSelectedRows + * when the selection of the list changse. + */ + class ListSelectionHandler implements ListSelectionListener { + public void valueChanged(ListSelectionEvent e) { + updateSelectedPathsFromSelectedRows(); + } + } + } +} --- rhino1_7R2/toolsrc/org/mozilla/javascript/tools/debugger/downloaded/TreeTableModel.java.orig 2008-12-10 16:13:48.848099816 -0500 +++ rhino1_7R2/toolsrc/org/mozilla/javascript/tools/debugger/downloaded/TreeTableModel.java 2008-12-10 16:13:48.847099847 -0500 @@ -0,0 +1,71 @@ +/* + * TreeTableModel.java + * + * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved. + * + * This software is the confidential and proprietary information of Sun + * Microsystems, Inc. ("Confidential Information"). You shall not + * disclose such Confidential Information and shall use it only in + * accordance with the terms of the license agreement you entered into + * with Sun. + * + * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE + * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES + * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + + + package org.mozilla.javascript.tools.debugger.downloaded; + import javax.swing.tree.TreeModel; + + +/** + * TreeTableModel is the model used by a JTreeTable. It extends TreeModel + * to add methods for getting inforamtion about the set of columns each + * node in the TreeTableModel may have. Each column, like a column in + * a TableModel, has a name and a type associated with it. Each node in + * the TreeTableModel can return a value for each of the columns and + * set that value if isCellEditable() returns true. + * + * @author Philip Milne + * @author Scott Violet + */ +public interface TreeTableModel extends TreeModel +{ + /** + * Returns the number ofs availible column. + */ + public int getColumnCount(); + + /** + * Returns the name for column number column. + */ + public String getColumnName(int column); + + /** + * Returns the type for column number column. + */ + public Class getColumnClass(int column); + + /** + * Returns the value to be displayed for node node, + * at column number column. + */ + public Object getValueAt(Object node, int column); + + /** + * Indicates whether the the value for node node, + * at column number column is editable. + */ + public boolean isCellEditable(Object node, int column); + + /** + * Sets the value for node node, + * at column number column. + */ + public void setValueAt(Object aValue, Object node, int column); +} --- rhino1_7R2/toolsrc/org/mozilla/javascript/tools/debugger/downloaded/TreeTableModelAdapter.java.orig 2008-12-10 16:13:48.847099847 -0500 +++ rhino1_7R2/toolsrc/org/mozilla/javascript/tools/debugger/downloaded/TreeTableModelAdapter.java 2008-12-10 16:13:48.847099847 -0500 @@ -0,0 +1,128 @@ +/* + * @(#)TreeTableModelAdapter.java 1.2 98/10/27 + * + * Copyright 1997, 1998 by Sun Microsystems, Inc., + * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. + * All rights reserved. + * + * This software is the confidential and proprietary information + * of Sun Microsystems, Inc. ("Confidential Information"). You + * shall not disclose such Confidential Information and shall use + * it only in accordance with the terms of the license agreement + * you entered into with Sun. + */ + + + package org.mozilla.javascript.tools.debugger.downloaded; + import javax.swing.JTree; + +import javax.swing.SwingUtilities; +import javax.swing.table.AbstractTableModel; +import javax.swing.tree.TreePath; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; + +/** + * This is a wrapper class takes a TreeTableModel and implements + * the table model interface. The implementation is trivial, with + * all of the event dispatching support provided by the superclass: + * the AbstractTableModel. + * + * @version 1.2 10/27/98 + * + * @author Philip Milne + * @author Scott Violet + */ +public class TreeTableModelAdapter extends AbstractTableModel +{ + JTree tree; + TreeTableModel treeTableModel; + + public TreeTableModelAdapter(TreeTableModel treeTableModel, JTree tree) { + this.tree = tree; + this.treeTableModel = treeTableModel; + + tree.addTreeExpansionListener(new TreeExpansionListener() { + // Don't use fireTableRowsInserted() here; the selection model + // would get updated twice. + public void treeExpanded(TreeExpansionEvent event) { + fireTableDataChanged(); + } + public void treeCollapsed(TreeExpansionEvent event) { + fireTableDataChanged(); + } + }); + + // Install a TreeModelListener that can update the table when + // tree changes. We use delayedFireTableDataChanged as we can + // not be guaranteed the tree will have finished processing + // the event before us. + treeTableModel.addTreeModelListener(new TreeModelListener() { + public void treeNodesChanged(TreeModelEvent e) { + delayedFireTableDataChanged(); + } + + public void treeNodesInserted(TreeModelEvent e) { + delayedFireTableDataChanged(); + } + + public void treeNodesRemoved(TreeModelEvent e) { + delayedFireTableDataChanged(); + } + + public void treeStructureChanged(TreeModelEvent e) { + delayedFireTableDataChanged(); + } + }); + } + + // Wrappers, implementing TableModel interface. + + public int getColumnCount() { + return treeTableModel.getColumnCount(); + } + + public String getColumnName(int column) { + return treeTableModel.getColumnName(column); + } + + public Class getColumnClass(int column) { + return treeTableModel.getColumnClass(column); + } + + public int getRowCount() { + return tree.getRowCount(); + } + + protected Object nodeForRow(int row) { + TreePath treePath = tree.getPathForRow(row); + return treePath.getLastPathComponent(); + } + + public Object getValueAt(int row, int column) { + return treeTableModel.getValueAt(nodeForRow(row), column); + } + + public boolean isCellEditable(int row, int column) { + return treeTableModel.isCellEditable(nodeForRow(row), column); + } + + public void setValueAt(Object value, int row, int column) { + treeTableModel.setValueAt(value, nodeForRow(row), column); + } + + /** + * Invokes fireTableDataChanged after all the pending events have been + * processed. SwingUtilities.invokeLater is used to handle this. + */ + protected void delayedFireTableDataChanged() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + fireTableDataChanged(); + } + }); + } +} + --- rhino1_7R2/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties.orig 2008-11-05 14:13:24.000000000 -0500 +++ rhino1_7R2/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties 2008-12-10 16:15:33.772975233 -0500 @@ -68,6 +68,8 @@ \ -opt [-1|0-9] Set optimization level.\n\ \ -f script-filename Execute script file, or "-" for interactive.\n\ \ -e script-source Evaluate inline script.\n\ + \ -o output-filename Set the output filename.\n\ + \ -c script-filename Set the script filename.\n\ \ -debug Generate debug code.\n\ \ -strict Enable strict mode warnings.\n\ \ -fatal-warnings Treat warnings as errors.\n\ --- rhino1_7R2/toolsrc/org/mozilla/javascript/tools/shell/Main.java.orig 2008-11-05 14:13:22.000000000 -0500 +++ rhino1_7R2/toolsrc/org/mozilla/javascript/tools/shell/Main.java 2008-12-10 16:13:48.849099824 -0500 @@ -86,6 +86,9 @@ static private final int EXITCODE_RUNTIME_ERROR = 3; static private final int EXITCODE_FILE_NOT_FOUND = 4; static boolean processStdin = true; + static boolean outputCompressed = false; + static String outputFileName = "dojo.js.compressed.js"; + static boolean isOutputFileSet = false; static List fileList = new ArrayList(); private static SecurityProxy securityImpl; @@ -293,6 +296,20 @@ shellContextFactory.call(iproxy); continue; } + if (arg.equals("-c")) { + outputCompressed = true; + continue; + } + if (arg.equals("-o") && ++i < args.length) { + if (args[i].startsWith("-") || args[i].length() == 0) + { + usageError = arg; + break goodUsage; + } + isOutputFileSet = true; + outputFileName = args[i]; + continue; + } if (arg.equals("-w")) { errorReporter.setIsReportingWarnings(true); continue; @@ -459,10 +476,12 @@ String path, Object securityDomain) { Script script; + String cout = null; + String source = null; if (path.endsWith(".class")) { script = loadCompiledScript(cx, path, securityDomain); } else { - String source = (String)readFileOrUrl(path, true); + source = (String)readFileOrUrl(path, true); if (source == null) { exitCode = EXITCODE_FILE_NOT_FOUND; return; @@ -482,9 +501,58 @@ } script = loadScriptFromSource(cx, source, path, 1, securityDomain); } - if (script != null) { - evaluateScript(script, cx, scope); - } + if ((script != null) && (source != null)) { + if (outputCompressed) { + cout = compressScript(cx, scope, script, source, path, 1, + securityDomain); + if (isOutputFileSet) { + try { + BufferedWriter out = new BufferedWriter(new FileWriter( + outputFileName)); + out.write(cout); + out.close(); + } catch (IOException ex) { + Context.reportError(ex.toString()); + } + System.out.println("Compressed file stored in '" + outputFileName + "'"); + } else { + global.getOut().println(cout); + } + + }else{ + evaluateScript(script, cx, scope); + } + } + } + + public static String compressScript(Context cx, Scriptable scope, Script script, String source, String sourceName, int lineno, Object securityDomain) + { + String compressedSource = null; + try { + if (script != null) { + compressedSource = cx.compressReader(scope, script, source, sourceName, + lineno, securityDomain); + } else { + compressedSource = source; + } + } catch (WrappedException we) { + global.getErr().println(we.getWrappedException().toString()); + we.printStackTrace(); + } catch (EvaluatorException ee) { + // Already printed message. + exitCode = EXITCODE_RUNTIME_ERROR; + } catch (RhinoException rex) { + errorReporter.reportException(rex); + exitCode = EXITCODE_RUNTIME_ERROR; + } catch (VirtualMachineError ex) { + // Treat StackOverflow and OutOfMemory as runtime errors + ex.printStackTrace(); + String msg = ToolErrorReporter.getMessage( + "msg.uncaughtJSException", ex.toString()); + exitCode = EXITCODE_RUNTIME_ERROR; + Context.reportError(msg); + } + return compressedSource; } public static Script loadScriptFromSource(Context cx, String scriptSource,