Index: src/org/mozilla/javascript/BaseFunction.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/BaseFunction.java,v
retrieving revision 1.63
diff -u -8 -p -r1.63 BaseFunction.java
--- src/org/mozilla/javascript/BaseFunction.java	16 May 2007 12:41:00 -0000	1.63
+++ src/org/mozilla/javascript/BaseFunction.java	19 Oct 2007 11:28:27 -0000
@@ -371,16 +371,38 @@ public class BaseFunction extends IdScri
         sb.append(getArity());
         sb.append("]\n");
         if (!justbody) {
             sb.append("}\n");
         }
         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; }
 
     public String getFunctionName()
     {
         return "";
     }
Index: src/org/mozilla/javascript/Context.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/Context.java,v
retrieving revision 1.255.2.1
diff -u -8 -p -r1.255.2.1 Context.java
--- src/org/mozilla/javascript/Context.java	6 Jun 2007 14:58:49 -0000	1.255.2.1
+++ src/org/mozilla/javascript/Context.java	19 Oct 2007 11:28:27 -0000
@@ -1211,16 +1211,43 @@ public class Context
      * @param securityDomain an arbitrary object that specifies security
      *        information about the origin or owner of the script. For
      *        implementations that don't care about security, this value
      *        may be null.
      * @return the result of evaluating the source
      *
      * @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)
         throws IOException
     {
         Script script = compileReader(scope, in, sourceName, lineno,
                                       securityDomain);
         if (script != null) {
@@ -1393,16 +1420,44 @@ public class Context
         catch (IOException ioe) {
             // Should never happen because we just made the reader
             // from a String
             throw new RuntimeException();
         }
     }
 
     /**
+     * Compress the script.
+     * <p>
+     * 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.
      * <p>
      * The canonical source of the script is returned.
      *
      * @param script the script to decompile
      * @param indent the number of spaces to indent the result
      * @return a string representing the script source
      */
@@ -2287,27 +2342,27 @@ public class Context
                                Interpreter compiler,
                                ErrorReporter compilationErrorReporter)
         throws IOException
     {
         if (securityDomain != null && securityController == null) {
             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
         if (!(scope == null ^ returnFunction)) Kit.codeBug();
 
         CompilerEnvirons compilerEnv = new CompilerEnvirons();
         compilerEnv.initFromContext(this);
         if (compilationErrorReporter == null) {
             compilationErrorReporter = compilerEnv.getErrorReporter();
         }
+        
 
         if (debugger != null) {
             if (sourceReader != null) {
                 sourceString = Kit.readReader(sourceReader);
                 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 -8 -p -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	19 Oct 2007 11:28:28 -0000
@@ -36,16 +36,21 @@
  * 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 MPL or the GPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 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
  * associated with function nodes and with the toplevel script.  When
  * saved in the constant pool of a class, this string will be UTF-8
  * encoded, and token values will occupy a single byte.
 
  * Source is saved (mostly) as token numbers.  The tokens saved pretty
@@ -69,16 +74,282 @@ package org.mozilla.javascript;
  * followed by a character giving the length of the string (assumed to
  * be less than 2^16), followed by the characters of the string
  * inlined into the source string.  Changing this to some reference to
  * to the string in the compiled class' constant pool would probably
  * save a lot of space... but would require some method of deriving
  * 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
+	 * <p>
+	 * 
+	 * @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
+	 * <p>
+	 * 
+	 * @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
+	 * <p>
+	 * 
+	 * @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.
+	 * <p>
+	 * 
+	 * @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
+	 * <p>
+	 * 
+	 * @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
+	 * <p>
+	 * 
+	 * @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
 {
     /**
      * Flag to indicate that the decompilation should omit the
      * function header and trailing brace.
      */
     public static final int ONLY_BODY_FLAG = 1 << 0;
 
@@ -265,16 +536,536 @@ public class Decompiler
     }
 
     private String sourceToString(int offset)
     {
         if (offset < 0 || sourceTop < offset) Kit.codeBug();
         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
+     * <p>
+     * 
+     * @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:
+                        if (
+                            (getNext(encodedSource, length, i+1) != Token.SEMI) &&
+                            (getNext(encodedSource, length, i+1) != Token.LP) &&
+                            (getNext(encodedSource, length, i+1) != Token.RP) &&
+                            (getNext(encodedSource, length, i+1) != Token.RB) &&
+                            (getNext(encodedSource, length, i+1) != Token.RC) &&
+                            (getNext(encodedSource, length, i+1) != Token.COMMA) &&
+                            (getNext(encodedSource, length, i+1) != Token.COLON) &&
+                            (getNext(encodedSource, length, i+1) != Token.DOT) &&
+                            (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 < 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
      * just means translating tokens back to their string
      * representations; there's a little bit of lookahead logic to
      * decide the proper spacing/indentation.  Most of the work in
      * mapping the original source to the prettyprinted decompiled
      * version is done by the parser.
Index: src/org/mozilla/javascript/NativeFunction.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/NativeFunction.java,v
retrieving revision 1.65
diff -u -8 -p -r1.65 NativeFunction.java
--- src/org/mozilla/javascript/NativeFunction.java	24 May 2007 14:47:01 -0000	1.65
+++ src/org/mozilla/javascript/NativeFunction.java	19 Oct 2007 11:28:28 -0000
@@ -69,16 +69,36 @@ public abstract class NativeFunction ext
             return super.decompile(indent, flags);
         } else {
             UintMap properties = new UintMap(1);
             properties.put(Decompiler.INITIAL_INDENT_PROP, indent);
             return Decompiler.decompile(encodedSource, flags, properties);
         }
     }
 
+    /**
+     * Compress the script.
+     * <p>
+     * 
+     * @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);
+        }
+    }
     public int getLength()
     {
         int paramCount = getParamCount();
         if (getLanguageVersion() != Context.VERSION_1_2) {
             return paramCount;
         }
         Context cx = Context.getContext();
         NativeCall activation = ScriptRuntime.findFunctionActivation(cx, this);
Index: src/org/mozilla/javascript/ScriptRuntime.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java,v
retrieving revision 1.263.2.3
diff -u -8 -p -r1.263.2.3 ScriptRuntime.java
--- src/org/mozilla/javascript/ScriptRuntime.java	18 Jul 2007 12:45:45 -0000	1.263.2.3
+++ src/org/mozilla/javascript/ScriptRuntime.java	19 Oct 2007 11:28:28 -0000
@@ -642,32 +642,35 @@ public class ScriptRuntime {
             if (escape >= 0) {
                 // an \escaped sort of character
                 sb.append('\\');
                 sb.append((char)escape);
             } else if (c == escapeQuote) {
                 sb.append('\\');
                 sb.append(escapeQuote);
             } else {
+				sb.append((char)c);
+				/*
                 int hexSize;
                 if (c < 256) {
                     // 2-digit hex
                     sb.append("\\x");
                     hexSize = 2;
                 } else {
                     // Unicode.
                     sb.append("\\u");
                     hexSize = 4;
                 }
                 // append hexadecimal form of c left-padded with 0
                 for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) {
                     int digit = 0xf & (c >> shift);
                     int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit;
                     sb.append((char)hc);
                 }
+				*/
             }
         }
         return (sb == null) ? s : sb.toString();
     }
 
     static boolean isValidIdentifierName(String s)
     {
         int L = s.length();
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 -8 -p -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	19 Oct 2007 11:28:28 -0000
@@ -63,19 +63,22 @@ class TokenStream
     /*
      * For chars - because we need something out-of-range
      * to check.  (And checking EOF by exception is annoying.)
      * Note distinction from EOF token type!
      */
     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) {
             if (sourceString != null) Kit.codeBug();
             this.sourceReader = sourceReader;
             this.sourceBuffer = new char[512];
             this.sourceEnd = 0;
         } else {
@@ -735,16 +738,18 @@ class TokenStream
                 if (matchChar('=')) {
                     return Token.ASSIGN_MUL;
                 } else {
                     return Token.MUL;
                 }
 
             case '/':
                 // is it a // comment?
+            	// FIXME: RAR: comment, need to set config to optionally keep
+            	// instead of skipping!
                 if (matchChar('/')) {
                     skipLine();
                     continue retry;
                 }
                 if (matchChar('*')) {
                     boolean lookForSlash = false;
                     for (;;) {
                         c = getChar();
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.27.2.2.2.1
diff -u -8 -p -r1.27.2.2.2.1 Messages.properties
--- toolsrc/org/mozilla/javascript/tools/resources/Messages.properties	9 Aug 2007 13:17:37 -0000	1.27.2.2.2.1
+++ toolsrc/org/mozilla/javascript/tools/resources/Messages.properties	19 Oct 2007 11:28:28 -0000
@@ -60,16 +60,18 @@ msg.shell.usage =\
     Valid options are:\n\
     \    -?, -help          Displays help messages.\n\
     \    -w                 Enable warnings.\n\
     \    -version 100|110|120|130|140|150|160\n\
     \                       Set a specific language version.\n\
     \    -opt [-1|0-9]      Set optimization level.\n\
     \    -f script-filename Execute script file.\n\
     \    -e script-source   Evaluate inline script.\n\
+    \    -o output-filename   File name for compressed output.\n\
+    \    -c script-filename   File name for script to compress.\n\
     \    -debug             Generate debug code.\n\
     \    -strict            Enable strict mode warnings.\n\
     \    -fatal-warnings    Treat warnings as errors.
 
 
 msg.help =\
     \n\
     Command                Description \n\
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.72
diff -u -8 -p -r1.72 Main.java
--- toolsrc/org/mozilla/javascript/tools/shell/Main.java	4 Jun 2007 13:46:58 -0000	1.72
+++ toolsrc/org/mozilla/javascript/tools/shell/Main.java	19 Oct 2007 11:28:28 -0000
@@ -64,16 +64,21 @@ public class Main
         shellContextFactory = new ShellContextFactory();
 
     static protected final Global global = new Global();
     static protected ToolErrorReporter errorReporter;
     static protected int exitCode = 0;
     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;
 
     static {
         global.initQuitAction(new IProxy(IProxy.SYSTEM_EXIT));
     }
 
     /**
@@ -263,16 +268,30 @@ public class Main
                 if (!global.initialized) {
                     global.init(shellContextFactory);
                 }
                 IProxy iproxy = new IProxy(IProxy.EVAL_INLINE_SCRIPT);
                 iproxy.scriptText = args[i];
                 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;
             }
             if (arg.equals("-f")) {
                 processStdin = false;
                 if (++i == args.length) {
                     usageError = arg;
@@ -414,20 +433,22 @@ public class Main
             securityImpl.callProcessFileSecure(cx, scope, filename);
         }
     }
 
     static void processFileSecure(Context cx, Scriptable scope,
                                   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;
             }
 
             // Support the executable script #! syntax:  If
             // the first line begins with a '#', treat the whole
             // line as a comment.
@@ -437,19 +458,73 @@ public class Main
                     if (c == '\n' || c == '\r') {
                         source = source.substring(i);
                         break;
                     }
                 }
             }
             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));
+						/*
+						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,
                                               String path, int lineno,
                                               Object securityDomain)
     {
         try {
             return cx.compileString(scriptSource, path, lineno,
