<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">Index: src/org/mozilla/javascript/BaseFunction.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/BaseFunction.java,v
retrieving revision 1.60
diff -u -u -r1.60 BaseFunction.java
--- src/org/mozilla/javascript/BaseFunction.java	2 May 2007 16:42:11 -0000	1.60
+++ src/org/mozilla/javascript/BaseFunction.java	10 Aug 2007 05:18:28 -0000
@@ -375,6 +375,28 @@
         return sb.toString();
     }
 
+        String compress(int indent, int flags)
+        {
+            StringBuffer sb = new StringBuffer();
+    		String FuncName = null;
+            boolean justbody = (0 != (flags &amp; Decompiler.ONLY_BODY_FLAG));
+            if (!justbody) {
+                sb.append("function");
+    			FuncName = getFunctionName();
+    			if(FuncName.length()&gt;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; }
Index: src/org/mozilla/javascript/Context.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/Context.java,v
retrieving revision 1.255
diff -u -u -r1.255 Context.java
--- src/org/mozilla/javascript/Context.java	1 May 2007 14:11:19 -0000	1.255
+++ src/org/mozilla/javascript/Context.java	10 Aug 2007 05:18:50 -0000
@@ -1215,6 +1215,33 @@
      *
      * @exception IOException if an IOException was generated by the Reader
      */
+        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;
+    		}
+    	}
+       
+    		
     public final Object evaluateReader(Scriptable scope, Reader in,
                                        String sourceName, int lineno,
                                        Object securityDomain)
@@ -1397,6 +1424,34 @@
     }
 
     /**
+     * Compress the script.
+     * &lt;p&gt;
+     * 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)
+    {
+        //Make sure to clear out the TokenMapper state before running.
+        //This allows for more than one script to be compressed.
+        //However, this is not a very clean way to do the reset.
+        TokenMapper.reset();
+        Decompiler.tm = new TokenMapper();
+
+        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 the script.
      * &lt;p&gt;
      * The canonical source of the script is returned.
@@ -2291,7 +2346,6 @@
             throw new IllegalArgumentException(
                 "securityDomain should be null if setSecurityController() was never called");
         }
-
         // One of sourceReader or sourceString has to be null
         if (!(sourceReader == null ^ sourceString == null)) Kit.codeBug();
         // scope should be given if and only if compiling function
@@ -2302,6 +2356,7 @@
         if (compilationErrorReporter == null) {
             compilationErrorReporter = compilerEnv.getErrorReporter();
         }
+        
 
         if (debugger != null) {
             if (sourceReader != null) {
Index: src/org/mozilla/javascript/Decompiler.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/Decompiler.java,v
retrieving revision 1.23
diff -u -u -r1.23 Decompiler.java
--- src/org/mozilla/javascript/Decompiler.java	4 Apr 2007 20:52:09 -0000	1.23
+++ src/org/mozilla/javascript/Decompiler.java	10 Aug 2007 05:18:54 -0000
@@ -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,272 @@
  * 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;
+
+	/**
+	 * Reset the static members for the TokenMapper.
+	 */
+	public static void reset() {
+		funcObjects = new ObjArray();
+		functionVarMappings = new ArrayList();
+	}
+
+	/**
+	 * Generate new compressed tokens
+	 * &lt;p&gt;
+	 * 
+	 * @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() &gt;= 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
+	 * &lt;p&gt;
+	 * 
+	 * @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 &gt; 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
+	 * &lt;p&gt;
+	 * 
+	 * @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 &lt; 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 &gt; -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.
+	 * &lt;p&gt;
+	 * 
+	 * @param parseTree
+	 *            Mapping for each function node and corresponding parameters &amp;
+	 *            variables names
+	 */
+	private void collectFunctionMappings(ScriptOrFnNode parseTree) {
+		int level = -1;
+		collectFuncNodes(parseTree, level);
+	}
+
+	/**
+	 * Recursive method to traverse all Function nodes
+	 * &lt;p&gt;
+	 * 
+	 * @param parseTree
+	 *            Mapping for each function node and corresponding parameters &amp;
+	 *            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
+	 * &lt;p&gt;
+	 * 
+	 * @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 &amp;
+	 *            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 &amp; length) != 0) {
+			length = ((0x7FFF &amp; length) &lt;&lt; 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() &gt; 0) &amp;&amp; (currentLevel &gt;= (((Integer) functionBracePositions
+					.get(functionBracePositions.size() - 1)).intValue())))
+					|| (inArgsList)) {
+				if (prevToken != Token.DOT) {
+					str = this.getMappedToken(str, hasNewMapping);
+				}
+			}
+			if ((!inArgsList) &amp;&amp; (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))
+				&amp;&amp; (replacedTokens.size() &gt; 0)) {
+			// remove our mappings now!
+			int scopedSize = replacedTokens.size();
+			replacedTokens.remove(scopedSize - 1);
+			functionBracePositions.remove(bn);
+		}
+	}
+}
+	
+	
 public class Decompiler
 {
     /**
@@ -270,6 +541,525 @@
         return new String(sourceBuffer, offset, sourceTop - offset);
     }
 
+    //Used to be private, but making it public so we
+    //can reset the token state between compression runs.
+    //Not very pretty.
+    public static TokenMapper tm = new TokenMapper();
+    
+    /**
+     * Compress the script
+     * &lt;p&gt;
+     * 
+     * @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 &amp; 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 &lt; 0) throw new IllegalArgumentException();
+         int indentGap = properties.getInt(INDENT_GAP_PROP, 4);
+         if (indentGap &lt; 0) throw new IllegalArgumentException();
+         int caseGap = properties.getInt(CASE_GAP_PROP, 2);
+         if (caseGap &lt; 0) throw new IllegalArgumentException();
+         StringBuffer result = new StringBuffer();
+         boolean justFunctionBody = (0 != (flags &amp; Decompiler.ONLY_BODY_FLAG));
+         boolean toSource = (0 != (flags &amp; 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 &lt; 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() &gt; 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 &lt; indent; j++){
+                 // result.append(' ');
+                 result.append("");
+             }
+         } else {
+             if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) {
+                 result.append('(');
+             }
+         }
+         while (i &lt; length) {
+           if(i&gt;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 &amp;&amp; braceNesting == 0){
+                     break;
+                 }
+                 // // result.append('\n');
+                 result.append('}');
+                 // // result.append(' ');
+                 switch (getNext(encodedSource, length, i)) {
+                     case Token.EOL:
+                     case FUNCTION_END:
+                        if (
+                            (getNext(encodedSource, length, i+1) != Token.SEMI) &amp;&amp;
+                            (getNext(encodedSource, length, i+1) != Token.LP) &amp;&amp;
+                            (getNext(encodedSource, length, i+1) != Token.RP) &amp;&amp;
+                            (getNext(encodedSource, length, i+1) != Token.RC) &amp;&amp;
+                            (getNext(encodedSource, length, i+1) != Token.COMMA) &amp;&amp;
+                            (getNext(encodedSource, length, i+1) != Token.COLON) &amp;&amp;
+                            (getNext(encodedSource, length, i+1) != Token.DOT) &amp;&amp;
+                            (getNext(encodedSource, length, i) == FUNCTION_END )
+                         ){
+						    result.append(';');
+                         }
+                         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 &lt; 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 &lt; 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("&amp;=");
+                 break;
+             case Token.ASSIGN_LSH:
+                 result.append("&lt;&lt;=");
+                 break;
+             case Token.ASSIGN_RSH:
+                 result.append("&gt;&gt;=");
+                 break;
+             case Token.ASSIGN_URSH:
+                 result.append("&gt;&gt;&gt;=");
+                 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("&amp;&amp;");
+                 break;
+             case Token.BITOR:
+                 result.append("|");
+                 break;
+             case Token.BITXOR:
+                 result.append("^");
+                 break;
+             case Token.BITAND:
+                 result.append("&amp;");
+                 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("&lt;=");
+                 break;
+             case Token.LT:
+                 result.append("&lt;");
+                 break;
+             case Token.GE:
+                 result.append("&gt;=");
+                 break;
+             case Token.GT:
+                 result.append("&gt;");
+                 break;
+             case Token.INSTANCEOF:
+  			// FIXME: does this really need leading space?
+                 result.append(" instanceof ");
+                 break;
+             case Token.LSH:
+                 result.append("&lt;&lt;");
+                 break;
+             case Token.RSH:
+                 result.append("&gt;&gt;");
+                 break;
+             case Token.URSH:
+                 result.append("&gt;&gt;&gt;");
+                 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
Index: src/org/mozilla/javascript/NativeFunction.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/NativeFunction.java,v
retrieving revision 1.64
diff -u -u -r1.64 NativeFunction.java
--- src/org/mozilla/javascript/NativeFunction.java	4 Apr 2007 20:52:10 -0000	1.64
+++ src/org/mozilla/javascript/NativeFunction.java	10 Aug 2007 05:18:54 -0000
@@ -74,6 +74,26 @@
         }
     }
 
+    /**
+     * Compress the script.
+     * &lt;p&gt;
+     * 
+     * @param parseTree Mapping for each function node and corresponding parameters &amp; 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);
+        }
+    }
     public int getLength()
     {
         int paramCount = getParamCount();
Index: src/org/mozilla/javascript/ScriptRuntime.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java,v
retrieving revision 1.261
diff -u -u -r1.261 ScriptRuntime.java
--- src/org/mozilla/javascript/ScriptRuntime.java	1 May 2007 14:11:20 -0000	1.261
+++ src/org/mozilla/javascript/ScriptRuntime.java	10 Aug 2007 05:19:04 -0000
@@ -647,6 +647,8 @@
                 sb.append('\\');
                 sb.append(escapeQuote);
             } else {
+				sb.append((char)c);
+				/*
                 int hexSize;
                 if (c &lt; 256) {
                     // 2-digit hex
@@ -663,6 +665,7 @@
                     int hc = (digit &lt; 10) ? '0' + digit : 'a' - 10 + digit;
                     sb.append((char)hc);
                 }
+				*/
             }
         }
         return (sb == null) ? s : sb.toString();
Index: src/org/mozilla/javascript/TokenStream.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/TokenStream.java,v
retrieving revision 1.66
diff -u -u -r1.66 TokenStream.java
--- src/org/mozilla/javascript/TokenStream.java	4 Apr 2007 20:52:12 -0000	1.66
+++ src/org/mozilla/javascript/TokenStream.java	10 Aug 2007 05:19:13 -0000
@@ -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) {
@@ -740,6 +743,8 @@
 
             case '/':
                 // is it a // comment?
+            	// FIXME: RAR: comment, need to set config to optionally keep
+            	// instead of skipping!
                 if (matchChar('/')) {
                     skipLine();
                     continue retry;
Index: toolsrc/org/mozilla/javascript/tools/resources/Messages.properties
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties,v
retrieving revision 1.25
diff -u -u -r1.25 Messages.properties
--- toolsrc/org/mozilla/javascript/tools/resources/Messages.properties	18 Apr 2007 17:08:40 -0000	1.25
+++ toolsrc/org/mozilla/javascript/tools/resources/Messages.properties	10 Aug 2007 05:19:13 -0000
@@ -60,6 +60,8 @@
     \    -opt [-1|0-9]\n\
     \    -f script-filename\n\
     \    -e script-source\n\
+    \    -o output-filename\n\
+    \    -c script-filename\n\
     \    -debug
 
 
Index: toolsrc/org/mozilla/javascript/tools/shell/Main.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/toolsrc/org/mozilla/javascript/tools/shell/Main.java,v
retrieving revision 1.70
diff -u -u -r1.70 Main.java
--- toolsrc/org/mozilla/javascript/tools/shell/Main.java	1 May 2007 14:11:21 -0000	1.70
+++ toolsrc/org/mozilla/javascript/tools/shell/Main.java	10 Aug 2007 05:19:15 -0000
@@ -69,6 +69,11 @@
     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 Vector fileList = new Vector(5);
     private static SecurityProxy securityImpl;
 
@@ -267,6 +272,20 @@
                 shellContextFactory.call(iproxy);
                 continue;
             }
+            if (arg.equals("-c")) {
+            	outputCompressed = true;
+            	continue;
+            }
+            if (arg.equals("-o") &amp;&amp; ++i &lt; 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;
@@ -409,10 +428,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;
@@ -432,9 +453,63 @@
             }
             script = loadScriptFromSource(cx, source, path, 1, securityDomain);
         }
-        if (script != null) {
-            evaluateScript(script, cx, scope);
-        }
+
+        if ((script != null) &amp;&amp; (source != null)) {
+			if (outputCompressed) {
+				cout = compressScript(cx, scope, script, source, path, 1,
+						securityDomain);
+				if (isOutputFileSet) {
+					try {
+						BufferedWriter out = new BufferedWriter(new FileWriter(
+								outputFileName));
+						/*
+						FileOutputStream fos = new FileOutputStream(outputFileName);
+						OutputStreamWriter out = new OutputStreamWriter(fos, "UTF8");
+						*/
+						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,
</pre></body></html>