diff options
Diffstat (limited to 'infrastructure/yuicompressor/src/com/yahoo')
8 files changed, 0 insertions, 2159 deletions
diff --git a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/Bootstrap.java b/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/Bootstrap.java deleted file mode 100644 index 1b95aca..0000000 --- a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/Bootstrap.java +++ /dev/null @@ -1,22 +0,0 @@ -/*
- * YUI Compressor
- * Author: Julien Lecomte <jlecomte@yahoo-inc.com>
- * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
- * Code licensed under the BSD License:
- * http://developer.yahoo.net/yui/license.txt
- */
-
-package com.yahoo.platform.yui.compressor;
-
-import java.lang.reflect.Method;
-
-public class Bootstrap {
-
- public static void main(String args[]) throws Exception {
- ClassLoader loader = new JarClassLoader();
- Thread.currentThread().setContextClassLoader(loader);
- Class c = loader.loadClass(YUICompressor.class.getName());
- Method main = c.getMethod("main", new Class[]{String[].class});
- main.invoke(null, new Object[]{args});
- }
-}
\ No newline at end of file diff --git a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/CssCompressor.java b/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/CssCompressor.java deleted file mode 100644 index 68b4de9..0000000 --- a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/CssCompressor.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * YUI Compressor - * Author: Julien Lecomte <jlecomte@yahoo-inc.com> - * Copyright (c) 2007, Yahoo! Inc. All rights reserved. - * Code licensed under the BSD License: - * http://developer.yahoo.net/yui/license.txt - * - * This code is a port of Isaac Schlueter's cssmin utility. - */ - -package com.yahoo.platform.yui.compressor; - -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; -import java.util.regex.Pattern; -import java.util.regex.Matcher; - -public class CssCompressor { - - private StringBuffer srcsb = new StringBuffer(); - - public CssCompressor(Reader in) throws IOException { - // Read the stream... - int c; - while ((c = in.read()) != -1) { - srcsb.append((char) c); - } - } - - public void compress(Writer out, int linebreakpos) - throws IOException { - - Pattern p; - Matcher m; - String css; - StringBuffer sb; - int startIndex, endIndex; - - // Remove all comment blocks... - startIndex = 0; - boolean iemac = false; - boolean preserve = false; - sb = new StringBuffer(srcsb.toString()); - while ((startIndex = sb.indexOf("/*", startIndex)) >= 0) { - preserve = sb.length() > startIndex + 2 && sb.charAt(startIndex + 2) == '!'; - endIndex = sb.indexOf("*/", startIndex + 2); - if (endIndex < 0) { - if (!preserve) { - sb.delete(startIndex, sb.length()); - } - } else if (endIndex >= startIndex + 2) { - if (sb.charAt(endIndex-1) == '\\') { - // Looks like a comment to hide rules from IE Mac. - // Leave this comment, and the following one, alone... - startIndex = endIndex + 2; - iemac = true; - } else if (iemac) { - startIndex = endIndex + 2; - iemac = false; - } else if (!preserve) { - sb.delete(startIndex, endIndex + 2); - } else { - startIndex = endIndex + 2; - } - } - } - - css = sb.toString(); - - // Normalize all whitespace strings to single spaces. Easier to work with that way. - css = css.replaceAll("\\s+", " "); - - // Make a pseudo class for the Box Model Hack - css = css.replaceAll("\"\\\\\"}\\\\\"\"", "___PSEUDOCLASSBMH___"); - - // Remove the spaces before the things that should not have spaces before them. - // But, be careful not to turn "p :link {...}" into "p:link{...}" - // Swap out any pseudo-class colons with the token, and then swap back. - sb = new StringBuffer(); - p = Pattern.compile("(^|\\})(([^\\{:])+:)+([^\\{]*\\{)"); - m = p.matcher(css); - while (m.find()) { - String s = m.group(); - s = s.replaceAll(":", "___PSEUDOCLASSCOLON___"); - m.appendReplacement(sb, s); - } - m.appendTail(sb); - css = sb.toString(); - css = css.replaceAll("\\s+([!{};:>+\\(\\)\\],])", "$1"); - css = css.replaceAll("___PSEUDOCLASSCOLON___", ":"); - - // Remove the spaces after the things that should not have spaces after them. - css = css.replaceAll("([!{}:;>+\\(\\[,])\\s+", "$1"); - - // Add the semicolon where it's missing. - css = css.replaceAll("([^;\\}])}", "$1;}"); - - // Replace 0(px,em,%) with 0. - css = css.replaceAll("([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", "$1$2"); - - // Replace 0 0 0 0; with 0. - css = css.replaceAll(":0 0 0 0;", ":0;"); - css = css.replaceAll(":0 0 0;", ":0;"); - css = css.replaceAll(":0 0;", ":0;"); - // Replace background-position:0; with background-position:0 0; - css = css.replaceAll("background-position:0;", "background-position:0 0;"); - - // Replace 0.6 to .6, but only when preceded by : or a white-space - css = css.replaceAll("(:|\\s)0+\\.(\\d+)", "$1.$2"); - - // Shorten colors from rgb(51,102,153) to #336699 - // This makes it more likely that it'll get further compressed in the next step. - p = Pattern.compile("rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)"); - m = p.matcher(css); - sb = new StringBuffer(); - while (m.find()) { - String[] rgbcolors = m.group(1).split(","); - StringBuffer hexcolor = new StringBuffer("#"); - for (int i = 0; i < rgbcolors.length; i++) { - int val = Integer.parseInt(rgbcolors[i]); - if (val < 16) { - hexcolor.append("0"); - } - hexcolor.append(Integer.toHexString(val)); - } - m.appendReplacement(sb, hexcolor.toString()); - } - m.appendTail(sb); - css = sb.toString(); - - // Shorten colors from #AABBCC to #ABC. Note that we want to make sure - // the color is not preceded by either ", " or =. Indeed, the property - // filter: chroma(color="#FFFFFF"); - // would become - // filter: chroma(color="#FFF"); - // which makes the filter break in IE. - p = Pattern.compile("([^\"'=\\s])(\\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])"); - m = p.matcher(css); - sb = new StringBuffer(); - while (m.find()) { - // Test for AABBCC pattern - if (m.group(3).equalsIgnoreCase(m.group(4)) && - m.group(5).equalsIgnoreCase(m.group(6)) && - m.group(7).equalsIgnoreCase(m.group(8))) { - m.appendReplacement(sb, m.group(1) + m.group(2) + "#" + m.group(3) + m.group(5) + m.group(7)); - } else { - m.appendReplacement(sb, m.group()); - } - } - m.appendTail(sb); - css = sb.toString(); - - // Remove empty rules. - css = css.replaceAll("[^\\}]+\\{;\\}", ""); - - if (linebreakpos >= 0) { - // Some source control tools don't like it when files containing lines longer - // than, say 8000 characters, are checked in. The linebreak option is used in - // that case to split long lines after a specific column. - int i = 0; - int linestartpos = 0; - sb = new StringBuffer(css); - while (i < sb.length()) { - char c = sb.charAt(i++); - if (c == '}' && i - linestartpos > linebreakpos) { - sb.insert(i, '\n'); - linestartpos = i; - } - } - - css = sb.toString(); - } - - // Replace the pseudo class for the Box Model Hack - css = css.replaceAll("___PSEUDOCLASSBMH___", "\"\\\\\"}\\\\\"\""); - - // Replace multiple semi-colons in a row by a single one - // See SF bug #1980989 - css = css.replaceAll(";;+", ";"); - - // Trim the final string (for any leading or trailing white spaces) - css = css.trim(); - - // Write the output... - out.write(css); - } -} diff --git a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JarClassLoader.java b/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JarClassLoader.java deleted file mode 100644 index a6d3e13..0000000 --- a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JarClassLoader.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * YUI Compressor - * Author: Julien Lecomte <jlecomte@yahoo-inc.com> - * Copyright (c) 2007, Yahoo! Inc. All rights reserved. - * Code licensed under the BSD License: - * http://developer.yahoo.net/yui/license.txt - */ - -package com.yahoo.platform.yui.compressor; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -public class JarClassLoader extends ClassLoader { - - private static String jarPath; - - public Class loadClass(String name) throws ClassNotFoundException { - - // First check if the class is already loaded - Class c = findLoadedClass(name); - if (c == null) { - c = findClass(name); - } - - if (c == null) { - c = ClassLoader.getSystemClassLoader().loadClass(name); - } - - return c; - } - - private static String getJarPath() { - - if (jarPath != null) { - return jarPath; - } - - String classname = JarClassLoader.class.getName().replace('.', '/') + ".class"; - String classpath = System.getProperty("java.class.path"); - String classpaths[] = classpath.split(System.getProperty("path.separator")); - - for (int i = 0; i < classpaths.length; i++) { - - String path = classpaths[i]; - JarFile jarFile = null; - JarEntry jarEntry = null; - - try { - jarFile = new JarFile(path); - jarEntry = findJarEntry(jarFile, classname); - } catch (IOException ioe) { - /* ignore */ - } finally { - if (jarFile != null) { - try { - jarFile.close(); - } catch (IOException ioe) { - /* ignore */ - } - } - } - - if (jarEntry != null) { - jarPath = path; - break; - } - } - - return jarPath; - } - - private static JarEntry findJarEntry(JarFile jarFile, String entryName) { - - Enumeration entries = jarFile.entries(); - - while (entries.hasMoreElements()) { - JarEntry entry = (JarEntry) entries.nextElement(); - if (entry.getName().equals(entryName)) { - return entry; - } - } - - return null; - } - - protected Class findClass(String name) { - - Class c = null; - String jarPath = getJarPath(); - - if (jarPath != null) { - JarFile jarFile = null; - try { - jarFile = new JarFile(jarPath); - c = loadClassData(jarFile, name); - } catch (IOException ioe) { - /* ignore */ - } finally { - if (jarFile != null) { - try { - jarFile.close(); - } catch (IOException ioe) { - /* ignore */ - } - } - } - } - - return c; - } - - private Class loadClassData(JarFile jarFile, String className) { - - String entryName = className.replace('.', '/') + ".class"; - JarEntry jarEntry = findJarEntry(jarFile, entryName); - if (jarEntry == null) { - return null; - } - - // Create the necessary package if needed... - int index = className.lastIndexOf('.'); - if (index >= 0) { - String packageName = className.substring(0, index); - if (getPackage(packageName) == null) { - definePackage(packageName, "", "", "", "", "", "", null); - } - } - - // Read the Jar File entry and define the class... - Class c = null; - try { - InputStream is = jarFile.getInputStream(jarEntry); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - copy(is, os); - byte[] bytes = os.toByteArray(); - c = defineClass(className, bytes, 0, bytes.length); - } catch (IOException ioe) { - /* ignore */ - } - - return c; - } - - private void copy(InputStream in, OutputStream out) throws IOException { - byte[] buf = new byte[1024]; - while (true) { - int len = in.read(buf); - if (len < 0) break; - out.write(buf, 0, len); - } - } -}
\ No newline at end of file diff --git a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptCompressor.java b/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptCompressor.java deleted file mode 100644 index e69ae1a..0000000 --- a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptCompressor.java +++ /dev/null @@ -1,1307 +0,0 @@ -/* - * YUI Compressor - * Author: Julien Lecomte <jlecomte@yahoo-inc.com> - * Copyright (c) 2007, Yahoo! Inc. All rights reserved. - * Code licensed under the BSD License: - * http://developer.yahoo.net/yui/license.txt - */ - -package com.yahoo.platform.yui.compressor; - -import yuicompressor.org.mozilla.javascript.*; - -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class JavaScriptCompressor { - - static final ArrayList ones; - static final ArrayList twos; - static final ArrayList threes; - - static final Set builtin = new HashSet(); - static final Map literals = new Hashtable(); - static final Set reserved = new HashSet(); - - static { - - // This list contains all the 3 characters or less built-in global - // symbols available in a browser. Please add to this list if you - // see anything missing. - builtin.add("NaN"); - builtin.add("top"); - - ones = new ArrayList(); - for (char c = 'A'; c <= 'Z'; c++) - ones.add(Character.toString(c)); - for (char c = 'a'; c <= 'z'; c++) - ones.add(Character.toString(c)); - - twos = new ArrayList(); - for (int i = 0; i < ones.size(); i++) { - String one = (String) ones.get(i); - for (char c = 'A'; c <= 'Z'; c++) - twos.add(one + Character.toString(c)); - for (char c = 'a'; c <= 'z'; c++) - twos.add(one + Character.toString(c)); - for (char c = '0'; c <= '9'; c++) - twos.add(one + Character.toString(c)); - } - - // Remove two-letter JavaScript reserved words and built-in globals... - twos.remove("as"); - twos.remove("is"); - twos.remove("do"); - twos.remove("if"); - twos.remove("in"); - twos.removeAll(builtin); - - threes = new ArrayList(); - for (int i = 0; i < twos.size(); i++) { - String two = (String) twos.get(i); - for (char c = 'A'; c <= 'Z'; c++) - threes.add(two + Character.toString(c)); - for (char c = 'a'; c <= 'z'; c++) - threes.add(two + Character.toString(c)); - for (char c = '0'; c <= '9'; c++) - threes.add(two + Character.toString(c)); - } - - // Remove three-letter JavaScript reserved words and built-in globals... - threes.remove("for"); - threes.remove("int"); - threes.remove("new"); - threes.remove("try"); - threes.remove("use"); - threes.remove("var"); - threes.removeAll(builtin); - - // That's up to ((26+26)*(1+(26+26+10)))*(1+(26+26+10))-8 - // (206,380 symbols per scope) - - // The following list comes from org/mozilla/javascript/Decompiler.java... - literals.put(new Integer(Token.GET), "get "); - literals.put(new Integer(Token.SET), "set "); - literals.put(new Integer(Token.TRUE), "true"); - literals.put(new Integer(Token.FALSE), "false"); - literals.put(new Integer(Token.NULL), "null"); - literals.put(new Integer(Token.THIS), "this"); - literals.put(new Integer(Token.FUNCTION), "function"); - literals.put(new Integer(Token.COMMA), ","); - literals.put(new Integer(Token.LC), "{"); - literals.put(new Integer(Token.RC), "}"); - literals.put(new Integer(Token.LP), "("); - literals.put(new Integer(Token.RP), ")"); - literals.put(new Integer(Token.LB), "["); - literals.put(new Integer(Token.RB), "]"); - literals.put(new Integer(Token.DOT), "."); - literals.put(new Integer(Token.NEW), "new "); - literals.put(new Integer(Token.DELPROP), "delete "); - literals.put(new Integer(Token.IF), "if"); - literals.put(new Integer(Token.ELSE), "else"); - literals.put(new Integer(Token.FOR), "for"); - literals.put(new Integer(Token.IN), " in "); - literals.put(new Integer(Token.WITH), "with"); - literals.put(new Integer(Token.WHILE), "while"); - literals.put(new Integer(Token.DO), "do"); - literals.put(new Integer(Token.TRY), "try"); - literals.put(new Integer(Token.CATCH), "catch"); - literals.put(new Integer(Token.FINALLY), "finally"); - literals.put(new Integer(Token.THROW), "throw"); - literals.put(new Integer(Token.SWITCH), "switch"); - literals.put(new Integer(Token.BREAK), "break"); - literals.put(new Integer(Token.CONTINUE), "continue"); - literals.put(new Integer(Token.CASE), "case"); - literals.put(new Integer(Token.DEFAULT), "default"); - literals.put(new Integer(Token.RETURN), "return"); - literals.put(new Integer(Token.VAR), "var "); - literals.put(new Integer(Token.SEMI), ";"); - literals.put(new Integer(Token.ASSIGN), "="); - literals.put(new Integer(Token.ASSIGN_ADD), "+="); - literals.put(new Integer(Token.ASSIGN_SUB), "-="); - literals.put(new Integer(Token.ASSIGN_MUL), "*="); - literals.put(new Integer(Token.ASSIGN_DIV), "/="); - literals.put(new Integer(Token.ASSIGN_MOD), "%="); - literals.put(new Integer(Token.ASSIGN_BITOR), "|="); - literals.put(new Integer(Token.ASSIGN_BITXOR), "^="); - literals.put(new Integer(Token.ASSIGN_BITAND), "&="); - literals.put(new Integer(Token.ASSIGN_LSH), "<<="); - literals.put(new Integer(Token.ASSIGN_RSH), ">>="); - literals.put(new Integer(Token.ASSIGN_URSH), ">>>="); - literals.put(new Integer(Token.HOOK), "?"); - literals.put(new Integer(Token.OBJECTLIT), ":"); - literals.put(new Integer(Token.COLON), ":"); - literals.put(new Integer(Token.OR), "||"); - literals.put(new Integer(Token.AND), "&&"); - literals.put(new Integer(Token.BITOR), "|"); - literals.put(new Integer(Token.BITXOR), "^"); - literals.put(new Integer(Token.BITAND), "&"); - literals.put(new Integer(Token.SHEQ), "==="); - literals.put(new Integer(Token.SHNE), "!=="); - literals.put(new Integer(Token.EQ), "=="); - literals.put(new Integer(Token.NE), "!="); - literals.put(new Integer(Token.LE), "<="); - literals.put(new Integer(Token.LT), "<"); - literals.put(new Integer(Token.GE), ">="); - literals.put(new Integer(Token.GT), ">"); - literals.put(new Integer(Token.INSTANCEOF), " instanceof "); - literals.put(new Integer(Token.LSH), "<<"); - literals.put(new Integer(Token.RSH), ">>"); - literals.put(new Integer(Token.URSH), ">>>"); - literals.put(new Integer(Token.TYPEOF), "typeof"); - literals.put(new Integer(Token.VOID), "void "); - literals.put(new Integer(Token.CONST), "const "); - literals.put(new Integer(Token.NOT), "!"); - literals.put(new Integer(Token.BITNOT), "~"); - literals.put(new Integer(Token.POS), "+"); - literals.put(new Integer(Token.NEG), "-"); - literals.put(new Integer(Token.INC), "++"); - literals.put(new Integer(Token.DEC), "--"); - literals.put(new Integer(Token.ADD), "+"); - literals.put(new Integer(Token.SUB), "-"); - literals.put(new Integer(Token.MUL), "*"); - literals.put(new Integer(Token.DIV), "/"); - literals.put(new Integer(Token.MOD), "%"); - literals.put(new Integer(Token.COLONCOLON), "::"); - literals.put(new Integer(Token.DOTDOT), ".."); - literals.put(new Integer(Token.DOTQUERY), ".("); - literals.put(new Integer(Token.XMLATTR), "@"); - - // See http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Reserved_Words - - // JavaScript 1.5 reserved words - reserved.add("break"); - reserved.add("case"); - reserved.add("catch"); - reserved.add("continue"); - reserved.add("default"); - reserved.add("delete"); - reserved.add("do"); - reserved.add("else"); - reserved.add("finally"); - reserved.add("for"); - reserved.add("function"); - reserved.add("if"); - reserved.add("in"); - reserved.add("instanceof"); - reserved.add("new"); - reserved.add("return"); - reserved.add("switch"); - reserved.add("this"); - reserved.add("throw"); - reserved.add("try"); - reserved.add("typeof"); - reserved.add("var"); - reserved.add("void"); - reserved.add("while"); - reserved.add("with"); - // Words reserved for future use - reserved.add("abstract"); - reserved.add("boolean"); - reserved.add("byte"); - reserved.add("char"); - reserved.add("class"); - reserved.add("const"); - reserved.add("debugger"); - reserved.add("double"); - reserved.add("enum"); - reserved.add("export"); - reserved.add("extends"); - reserved.add("final"); - reserved.add("float"); - reserved.add("goto"); - reserved.add("implements"); - reserved.add("import"); - reserved.add("int"); - reserved.add("interface"); - reserved.add("long"); - reserved.add("native"); - reserved.add("package"); - reserved.add("private"); - reserved.add("protected"); - reserved.add("public"); - reserved.add("short"); - reserved.add("static"); - reserved.add("super"); - reserved.add("synchronized"); - reserved.add("throws"); - reserved.add("transient"); - reserved.add("volatile"); - // These are not reserved, but should be taken into account - // in isValidIdentifier (See jslint source code) - reserved.add("arguments"); - reserved.add("eval"); - reserved.add("true"); - reserved.add("false"); - reserved.add("Infinity"); - reserved.add("NaN"); - reserved.add("null"); - reserved.add("undefined"); - } - - private static int countChar(String haystack, char needle) { - int idx = 0; - int count = 0; - int length = haystack.length(); - while (idx < length) { - char c = haystack.charAt(idx++); - if (c == needle) { - count++; - } - } - return count; - } - - private static int printSourceString(String source, int offset, StringBuffer sb) { - int length = source.charAt(offset); - ++offset; - if ((0x8000 & length) != 0) { - length = ((0x7FFF & length) << 16) | source.charAt(offset); - ++offset; - } - if (sb != null) { - String str = source.substring(offset, offset + length); - sb.append(str); - } - return offset + length; - } - - private static int printSourceNumber(String source, - int offset, StringBuffer sb) { - double number = 0.0; - char type = source.charAt(offset); - ++offset; - if (type == 'S') { - if (sb != null) { - number = source.charAt(offset); - } - ++offset; - } else if (type == 'J' || type == 'D') { - if (sb != null) { - long lbits; - lbits = (long) source.charAt(offset) << 48; - lbits |= (long) source.charAt(offset + 1) << 32; - lbits |= (long) source.charAt(offset + 2) << 16; - lbits |= (long) source.charAt(offset + 3); - if (type == 'J') { - number = lbits; - } else { - number = Double.longBitsToDouble(lbits); - } - } - offset += 4; - } else { - // Bad source - throw new RuntimeException(); - } - if (sb != null) { - sb.append(ScriptRuntime.numberToString(number, 10)); - } - return offset; - } - - private static ArrayList parse(Reader in, ErrorReporter reporter) - throws IOException, EvaluatorException { - - CompilerEnvirons env = new CompilerEnvirons(); - Parser parser = new Parser(env, reporter); - parser.parse(in, null, 1); - String source = parser.getEncodedSource(); - - int offset = 0; - int length = source.length(); - ArrayList tokens = new ArrayList(); - StringBuffer sb = new StringBuffer(); - - while (offset < length) { - int tt = source.charAt(offset++); - switch (tt) { - - case Token.SPECIALCOMMENT: - case Token.NAME: - case Token.REGEXP: - case Token.STRING: - sb.setLength(0); - offset = printSourceString(source, offset, sb); - tokens.add(new JavaScriptToken(tt, sb.toString())); - break; - - case Token.NUMBER: - sb.setLength(0); - offset = printSourceNumber(source, offset, sb); - tokens.add(new JavaScriptToken(tt, sb.toString())); - break; - - default: - String literal = (String) literals.get(new Integer(tt)); - if (literal != null) { - tokens.add(new JavaScriptToken(tt, literal)); - } - break; - } - } - - return tokens; - } - - private static void processStringLiterals(ArrayList tokens, boolean merge) { - - String tv; - int i, length = tokens.size(); - JavaScriptToken token, prevToken, nextToken; - - if (merge) { - - // Concatenate string literals that are being appended wherever - // it is safe to do so. Note that we take care of the case: - // "a" + "b".toUpperCase() - - for (i = 0; i < length; i++) { - token = (JavaScriptToken) tokens.get(i); - switch (token.getType()) { - - case Token.ADD: - if (i > 0 && i < length) { - prevToken = (JavaScriptToken) tokens.get(i - 1); - nextToken = (JavaScriptToken) tokens.get(i + 1); - if (prevToken.getType() == Token.STRING && nextToken.getType() == Token.STRING && - (i == length - 1 || ((JavaScriptToken) tokens.get(i + 2)).getType() != Token.DOT)) { - tokens.set(i - 1, new JavaScriptToken(Token.STRING, - prevToken.getValue() + nextToken.getValue())); - tokens.remove(i + 1); - tokens.remove(i); - i = i - 1; - length = length - 2; - break; - } - } - } - } - - } - - // Second pass... - - for (i = 0; i < length; i++) { - // APPJET modifications in this loop - token = (JavaScriptToken) tokens.get(i); - if (token.getType() == Token.STRING || token.getType() == Token.REGEXP) { - tv = token.getValue(); - if (token.getType() == Token.STRING) { - - // Finally, add the quoting characters and escape the string. We use - // the quoting character that minimizes the amount of escaping to save - // a few additional bytes. - - char quotechar; - int singleQuoteCount = countChar(tv, '\''); - int doubleQuoteCount = countChar(tv, '"'); - if (doubleQuoteCount <= singleQuoteCount) { - quotechar = '"'; - } else { - quotechar = '\''; - } - - tv = quotechar + escapeString(tv, quotechar) + quotechar; - } - - // String concatenation transforms the old script scheme: - // '<scr'+'ipt ...><'+'/script>' - // into the following: - // '<script ...></script>' - // which breaks if this code is embedded inside an HTML document. - // Since this is not the right way to do this, let's fix the code by - // transforming all "</script" into "<\/script" - - // (Anti-malware software can be sensitive to open tags) - tv = tv.replace("<script", "\\x3cscript"); - tv = tv.replace("</script", "\\x3c/script"); - - tokens.set(i, new JavaScriptToken(token.getType(), tv)); - } - } - } - - // Add necessary escaping that was removed in Rhino's tokenizer. - private static String escapeString(String s, char quotechar) { - - assert quotechar == '"' || quotechar == '\''; - - if (s == null) { - return null; - } - - StringBuffer sb = new StringBuffer(); - for (int i = 0, L = s.length(); i < L; i++) { - int c = s.charAt(i); - if (c == quotechar) { - sb.append("\\"); - } - sb.append((char) c); - } - - return sb.toString(); - } - - /* - * Simple check to see whether a string is a valid identifier name. - * If a string matches this pattern, it means it IS a valid - * identifier name. If a string doesn't match it, it does not - * necessarily mean it is not a valid identifier name. - */ - private static final Pattern SIMPLE_IDENTIFIER_NAME_PATTERN = Pattern.compile("^[a-zA-Z_][a-zA-Z0-9_]*$"); - - private static boolean isValidIdentifier(String s) { - Matcher m = SIMPLE_IDENTIFIER_NAME_PATTERN.matcher(s); - return (m.matches() && !reserved.contains(s)); - } - - /* - * Transforms obj["foo"] into obj.foo whenever possible, saving 3 bytes. - */ - private static void optimizeObjectMemberAccess(ArrayList tokens) { - - String tv; - int i, length; - JavaScriptToken token; - - for (i = 0, length = tokens.size(); i < length; i++) { - - if (((JavaScriptToken) tokens.get(i)).getType() == Token.LB && - i > 0 && i < length - 2 && - ((JavaScriptToken) tokens.get(i - 1)).getType() == Token.NAME && - ((JavaScriptToken) tokens.get(i + 1)).getType() == Token.STRING && - ((JavaScriptToken) tokens.get(i + 2)).getType() == Token.RB) { - token = (JavaScriptToken) tokens.get(i + 1); - tv = token.getValue(); - tv = tv.substring(1, tv.length() - 1); - if (isValidIdentifier(tv)) { - tokens.set(i, new JavaScriptToken(Token.DOT, ".")); - tokens.set(i + 1, new JavaScriptToken(Token.NAME, tv)); - tokens.remove(i + 2); - i = i + 2; - length = length - 1; - } - } - } - } - - /* - * Transforms 'foo': ... into foo: ... whenever possible, saving 2 bytes. - */ - private static void optimizeObjLitMemberDecl(ArrayList tokens) { - - String tv; - int i, length; - JavaScriptToken token; - - for (i = 0, length = tokens.size(); i < length; i++) { - if (((JavaScriptToken) tokens.get(i)).getType() == Token.OBJECTLIT && - i > 0 && ((JavaScriptToken) tokens.get(i - 1)).getType() == Token.STRING) { - token = (JavaScriptToken) tokens.get(i - 1); - tv = token.getValue(); - tv = tv.substring(1, tv.length() - 1); - if (isValidIdentifier(tv)) { - tokens.set(i - 1, new JavaScriptToken(Token.NAME, tv)); - } - } - } - } - - private ErrorReporter logger; - - private boolean munge; - private boolean verbose; - - private static final int BUILDING_SYMBOL_TREE = 1; - private static final int CHECKING_SYMBOL_TREE = 2; - - private int mode; - private int offset; - private int braceNesting; - private ArrayList tokens; - private Stack scopes = new Stack(); - private ScriptOrFnScope globalScope = new ScriptOrFnScope(-1, null); - private Hashtable indexedScopes = new Hashtable(); - - public JavaScriptCompressor(Reader in, ErrorReporter reporter) - throws IOException, EvaluatorException { - - this.logger = reporter; - this.tokens = parse(in, reporter); - } - - public void compress(Writer out, int linebreak, boolean munge, boolean verbose, - boolean preserveAllSemiColons, boolean disableOptimizations) - throws IOException { - - this.munge = munge; - this.verbose = verbose; - - processStringLiterals(this.tokens, !disableOptimizations); - - if (!disableOptimizations) { - optimizeObjectMemberAccess(this.tokens); - optimizeObjLitMemberDecl(this.tokens); - } - - buildSymbolTree(); - // DO NOT TOUCH this.tokens BETWEEN THESE TWO PHASES (BECAUSE OF this.indexedScopes) - mungeSymboltree(); - StringBuffer sb = printSymbolTree(linebreak, preserveAllSemiColons); - - out.write(sb.toString()); - } - - private ScriptOrFnScope getCurrentScope() { - return (ScriptOrFnScope) scopes.peek(); - } - - private void enterScope(ScriptOrFnScope scope) { - scopes.push(scope); - } - - private void leaveCurrentScope() { - scopes.pop(); - } - - private JavaScriptToken consumeToken() { - return (JavaScriptToken) tokens.get(offset++); - } - - private JavaScriptToken getToken(int delta) { - return (JavaScriptToken) tokens.get(offset + delta); - } - - /* - * Returns the identifier for the specified symbol defined in - * the specified scope or in any scope above it. Returns null - * if this symbol does not have a corresponding identifier. - */ - private JavaScriptIdentifier getIdentifier(String symbol, ScriptOrFnScope scope) { - JavaScriptIdentifier identifier; - while (scope != null) { - identifier = scope.getIdentifier(symbol); - if (identifier != null) { - return identifier; - } - scope = scope.getParentScope(); - } - return null; - } - - /* - * If either 'eval' or 'with' is used in a local scope, we must make - * sure that all containing local scopes don't get munged. Otherwise, - * the obfuscation would potentially introduce bugs. - */ - private void protectScopeFromObfuscation(ScriptOrFnScope scope) { - assert scope != null; - - if (scope == globalScope) { - // The global scope does not get obfuscated, - // so we don't need to worry about it... - return; - } - - // Find the highest local scope containing the specified scope. - while (scope.getParentScope() != globalScope) { - scope = scope.getParentScope(); - } - - assert scope.getParentScope() == globalScope; - scope.preventMunging(); - } - - private String getDebugString(int max) { - assert max > 0; - StringBuffer result = new StringBuffer(); - int start = Math.max(offset - max, 0); - int end = Math.min(offset + max, tokens.size()); - for (int i = start; i < end; i++) { - JavaScriptToken token = (JavaScriptToken) tokens.get(i); - if (i == offset - 1) { - result.append(" ---> "); - } - result.append(token.getValue()); - if (i == offset - 1) { - result.append(" <--- "); - } - } - return result.toString(); - } - - private void warn(String message, boolean showDebugString) { - if (verbose) { - if (showDebugString) { - message = message + "\n" + getDebugString(10); - } - logger.warning(message, null, -1, null, -1); - } - } - - private void parseFunctionDeclaration() { - - String symbol; - JavaScriptToken token; - ScriptOrFnScope currentScope, fnScope; - JavaScriptIdentifier identifier; - - currentScope = getCurrentScope(); - - token = consumeToken(); - if (token.getType() == Token.NAME) { - if (mode == BUILDING_SYMBOL_TREE) { - // Get the name of the function and declare it in the current scope. - symbol = token.getValue(); - if (currentScope.getIdentifier(symbol) != null) { - warn("The function " + symbol + " has already been declared in the same scope...", true); - } - currentScope.declareIdentifier(symbol); - } - token = consumeToken(); - } - - assert token.getType() == Token.LP; - if (mode == BUILDING_SYMBOL_TREE) { - fnScope = new ScriptOrFnScope(braceNesting, currentScope); - indexedScopes.put(new Integer(offset), fnScope); - } else { - fnScope = (ScriptOrFnScope) indexedScopes.get(new Integer(offset)); - } - - // Parse function arguments. - int argpos = 0; - while ((token = consumeToken()).getType() != Token.RP) { - assert token.getType() == Token.NAME || - token.getType() == Token.COMMA; - if (token.getType() == Token.NAME && mode == BUILDING_SYMBOL_TREE) { - symbol = token.getValue(); - identifier = fnScope.declareIdentifier(symbol); - if (symbol.equals("$super") && argpos == 0) { - // Exception for Prototype 1.6... - identifier.preventMunging(); - } - argpos++; - } - } - - token = consumeToken(); - assert token.getType() == Token.LC; - braceNesting++; - - token = getToken(0); - if (token.getType() == Token.STRING && - getToken(1).getType() == Token.SEMI) { - // This is a hint. Hints are empty statements that look like - // "localvar1:nomunge, localvar2:nomunge"; They allow developers - // to prevent specific symbols from getting obfuscated (some heretic - // implementations, such as Prototype 1.6, require specific variable - // names, such as $super for example, in order to work appropriately. - // Note: right now, only "nomunge" is supported in the right hand side - // of a hint. However, in the future, the right hand side may contain - // other values. - consumeToken(); - String hints = token.getValue(); - // Remove the leading and trailing quotes... - hints = hints.substring(1, hints.length() - 1).trim(); - StringTokenizer st1 = new StringTokenizer(hints, ","); - while (st1.hasMoreTokens()) { - String hint = st1.nextToken(); - int idx = hint.indexOf(':'); - if (idx <= 0 || idx >= hint.length() - 1) { - if (mode == BUILDING_SYMBOL_TREE) { - // No need to report the error twice, hence the test... - warn("Invalid hint syntax: " + hint, true); - } - break; - } - String variableName = hint.substring(0, idx).trim(); - String variableType = hint.substring(idx + 1).trim(); - if (mode == BUILDING_SYMBOL_TREE) { - fnScope.addHint(variableName, variableType); - } else if (mode == CHECKING_SYMBOL_TREE) { - identifier = fnScope.getIdentifier(variableName); - if (identifier != null) { - if (variableType.equals("nomunge")) { - identifier.preventMunging(); - } else { - warn("Unsupported hint value: " + hint, true); - } - } else { - warn("Hint refers to an unknown identifier: " + hint, true); - } - } - } - } - - parseScope(fnScope); - } - - private void parseCatch() { - - String symbol; - JavaScriptToken token; - ScriptOrFnScope currentScope; - JavaScriptIdentifier identifier; - - token = getToken(-1); - assert token.getType() == Token.CATCH; - token = consumeToken(); - assert token.getType() == Token.LP; - token = consumeToken(); - assert token.getType() == Token.NAME; - - symbol = token.getValue(); - currentScope = getCurrentScope(); - - if (mode == BUILDING_SYMBOL_TREE) { - // We must declare the exception identifier in the containing function - // scope to avoid errors related to the obfuscation process. No need to - // display a warning if the symbol was already declared here... - currentScope.declareIdentifier(symbol); - } else { - identifier = getIdentifier(symbol, currentScope); - identifier.incrementRefcount(); - } - - token = consumeToken(); - assert token.getType() == Token.RP; - } - - private void parseExpression() { - - // Parse the expression until we encounter a comma or a semi-colon - // in the same brace nesting, bracket nesting and paren nesting. - // Parse functions if any... - - String symbol; - JavaScriptToken token; - ScriptOrFnScope currentScope; - JavaScriptIdentifier identifier; - - int expressionBraceNesting = braceNesting; - int bracketNesting = 0; - int parensNesting = 0; - - int length = tokens.size(); - - while (offset < length) { - - token = consumeToken(); - currentScope = getCurrentScope(); - - switch (token.getType()) { - - case Token.SEMI: - case Token.COMMA: - if (braceNesting == expressionBraceNesting && - bracketNesting == 0 && - parensNesting == 0) { - return; - } - break; - - case Token.FUNCTION: - parseFunctionDeclaration(); - break; - - case Token.LC: - braceNesting++; - break; - - case Token.RC: - braceNesting--; - assert braceNesting >= expressionBraceNesting; - break; - - case Token.LB: - bracketNesting++; - break; - - case Token.RB: - bracketNesting--; - break; - - case Token.LP: - parensNesting++; - break; - - case Token.RP: - parensNesting--; - break; - - case Token.SPECIALCOMMENT: - if (mode == BUILDING_SYMBOL_TREE) { - protectScopeFromObfuscation(currentScope); - warn("Using JScript conditional comments is not recommended." + (munge ? " Moreover, using JScript conditional comments reduces the level of compression!" : ""), true); - } - break; - - case Token.NAME: - symbol = token.getValue(); - - if (mode == BUILDING_SYMBOL_TREE) { - - if (symbol.equals("eval")) { - - protectScopeFromObfuscation(currentScope); - warn("Using 'eval' is not recommended." + (munge ? " Moreover, using 'eval' reduces the level of compression!" : ""), true); - - } - - } else if (mode == CHECKING_SYMBOL_TREE) { - - if ((offset < 2 || - (getToken(-2).getType() != Token.DOT && - getToken(-2).getType() != Token.GET && - getToken(-2).getType() != Token.SET)) && - getToken(0).getType() != Token.OBJECTLIT) { - - identifier = getIdentifier(symbol, currentScope); - - if (identifier == null) { - - if (symbol.length() <= 3 && !builtin.contains(symbol)) { - // Here, we found an undeclared and un-namespaced symbol that is - // 3 characters or less in length. Declare it in the global scope. - // We don't need to declare longer symbols since they won't cause - // any conflict with other munged symbols. - globalScope.declareIdentifier(symbol); - warn("Found an undeclared symbol: " + symbol, true); - } - - } else { - - identifier.incrementRefcount(); - } - } - } - break; - } - } - } - - private void parseScope(ScriptOrFnScope scope) { - - String symbol; - JavaScriptToken token; - JavaScriptIdentifier identifier; - - int length = tokens.size(); - - enterScope(scope); - - while (offset < length) { - - token = consumeToken(); - - switch (token.getType()) { - - case Token.VAR: - - if (mode == BUILDING_SYMBOL_TREE && scope.incrementVarCount() > 1) { - warn("Try to use a single 'var' statement per scope.", true); - } - - /* FALLSTHROUGH */ - - case Token.CONST: - - // The var keyword is followed by at least one symbol name. - // If several symbols follow, they are comma separated. - for (; ;) { - token = consumeToken(); - - assert token.getType() == Token.NAME; - - if (mode == BUILDING_SYMBOL_TREE) { - symbol = token.getValue(); - if (scope.getIdentifier(symbol) == null) { - scope.declareIdentifier(symbol); - } else { - warn("The variable " + symbol + " has already been declared in the same scope...", true); - } - } - - token = getToken(0); - - assert token.getType() == Token.SEMI || - token.getType() == Token.ASSIGN || - token.getType() == Token.COMMA || - token.getType() == Token.IN; - - if (token.getType() == Token.IN) { - break; - } else { - parseExpression(); - token = getToken(-1); - if (token.getType() == Token.SEMI) { - break; - } - } - } - break; - - case Token.FUNCTION: - parseFunctionDeclaration(); - break; - - case Token.LC: - braceNesting++; - break; - - case Token.RC: - braceNesting--; - assert braceNesting >= scope.getBraceNesting(); - if (braceNesting == scope.getBraceNesting()) { - leaveCurrentScope(); - return; - } - break; - - case Token.WITH: - if (mode == BUILDING_SYMBOL_TREE) { - // Inside a 'with' block, it is impossible to figure out - // statically whether a symbol is a local variable or an - // object member. As a consequence, the only thing we can - // do is turn the obfuscation off for the highest scope - // containing the 'with' block. - protectScopeFromObfuscation(scope); - warn("Using 'with' is not recommended." + (munge ? " Moreover, using 'with' reduces the level of compression!" : ""), true); - } - break; - - case Token.CATCH: - parseCatch(); - break; - - case Token.SPECIALCOMMENT: - if (mode == BUILDING_SYMBOL_TREE) { - protectScopeFromObfuscation(scope); - warn("Using JScript conditional comments is not recommended." + (munge ? " Moreover, using JScript conditional comments reduces the level of compression." : ""), true); - } - break; - - case Token.NAME: - symbol = token.getValue(); - - if (mode == BUILDING_SYMBOL_TREE) { - - if (symbol.equals("eval")) { - - protectScopeFromObfuscation(scope); - warn("Using 'eval' is not recommended." + (munge ? " Moreover, using 'eval' reduces the level of compression!" : ""), true); - - } - - } else if (mode == CHECKING_SYMBOL_TREE) { - - if ((offset < 2 || getToken(-2).getType() != Token.DOT) && - getToken(0).getType() != Token.OBJECTLIT) { - - identifier = getIdentifier(symbol, scope); - - if (identifier == null) { - - if (symbol.length() <= 3 && !builtin.contains(symbol)) { - // Here, we found an undeclared and un-namespaced symbol that is - // 3 characters or less in length. Declare it in the global scope. - // We don't need to declare longer symbols since they won't cause - // any conflict with other munged symbols. - globalScope.declareIdentifier(symbol); - warn("Found an undeclared symbol: " + symbol, true); - } - - } else { - - identifier.incrementRefcount(); - } - } - } - break; - } - } - } - - private void buildSymbolTree() { - offset = 0; - braceNesting = 0; - scopes.clear(); - indexedScopes.clear(); - indexedScopes.put(new Integer(0), globalScope); - mode = BUILDING_SYMBOL_TREE; - parseScope(globalScope); - } - - private void mungeSymboltree() { - - if (!munge) { - return; - } - - // One problem with obfuscation resides in the use of undeclared - // and un-namespaced global symbols that are 3 characters or less - // in length. Here is an example: - // - // var declaredGlobalVar; - // - // function declaredGlobalFn() { - // var localvar; - // localvar = abc; // abc is an undeclared global symbol - // } - // - // In the example above, there is a slim chance that localvar may be - // munged to 'abc', conflicting with the undeclared global symbol - // abc, creating a potential bug. The following code detects such - // global symbols. This must be done AFTER the entire file has been - // parsed, and BEFORE munging the symbol tree. Note that declaring - // extra symbols in the global scope won't hurt. - // - // Note: Since we go through all the tokens to do this, we also use - // the opportunity to count how many times each identifier is used. - - offset = 0; - braceNesting = 0; - scopes.clear(); - mode = CHECKING_SYMBOL_TREE; - parseScope(globalScope); - globalScope.munge(); - } - - private StringBuffer printSymbolTree(int linebreakpos, boolean preserveAllSemiColons) - throws IOException { - - offset = 0; - braceNesting = 0; - scopes.clear(); - - String symbol; - JavaScriptToken token; - ScriptOrFnScope currentScope; - JavaScriptIdentifier identifier; - - int length = tokens.size(); - StringBuffer result = new StringBuffer(); - - int linestartpos = 0; - - enterScope(globalScope); - - while (offset < length) { - - token = consumeToken(); - symbol = token.getValue(); - currentScope = getCurrentScope(); - - switch (token.getType()) { - - case Token.NAME: - - if (offset >= 2 && getToken(-2).getType() == Token.DOT || - getToken(0).getType() == Token.OBJECTLIT) { - - result.append(symbol); - - } else { - - identifier = getIdentifier(symbol, currentScope); - if (identifier != null) { - if (identifier.getMungedValue() != null) { - result.append(identifier.getMungedValue()); - } else { - result.append(symbol); - } - if (currentScope != globalScope && identifier.getRefcount() == 0) { - warn("The symbol " + symbol + " is declared but is apparently never used.\nThis code can probably be written in a more compact way.", true); - } - } else { - result.append(symbol); - } - } - break; - - case Token.REGEXP: - case Token.NUMBER: - case Token.STRING: - result.append(symbol); - break; - - case Token.ADD: - case Token.SUB: - result.append((String) literals.get(new Integer(token.getType()))); - if (offset < length) { - token = getToken(0); - if (token.getType() == Token.INC || - token.getType() == Token.DEC || - token.getType() == Token.ADD || - token.getType() == Token.DEC) { - // Handle the case x +/- ++/-- y - // We must keep a white space here. Otherwise, x +++ y would be - // interpreted as x ++ + y by the compiler, which is a bug (due - // to the implicit assignment being done on the wrong variable) - result.append(' '); - } else if (token.getType() == Token.POS && getToken(-1).getType() == Token.ADD || - token.getType() == Token.NEG && getToken(-1).getType() == Token.SUB) { - // Handle the case x + + y and x - - y - result.append(' '); - } - } - break; - - case Token.FUNCTION: - result.append("function"); - token = consumeToken(); - if (token.getType() == Token.NAME) { - result.append(' '); - symbol = token.getValue(); - identifier = getIdentifier(symbol, currentScope); - assert identifier != null; - if (identifier.getMungedValue() != null) { - result.append(identifier.getMungedValue()); - } else { - result.append(symbol); - } - if (currentScope != globalScope && identifier.getRefcount() == 0) { - warn("The symbol " + symbol + " is declared but is apparently never used.\nThis code can probably be written in a more compact way.", true); - } - token = consumeToken(); - } - assert token.getType() == Token.LP; - result.append('('); - currentScope = (ScriptOrFnScope) indexedScopes.get(new Integer(offset)); - enterScope(currentScope); - while ((token = consumeToken()).getType() != Token.RP) { - assert token.getType() == Token.NAME || token.getType() == Token.COMMA; - if (token.getType() == Token.NAME) { - symbol = token.getValue(); - identifier = getIdentifier(symbol, currentScope); - assert identifier != null; - if (identifier.getMungedValue() != null) { - result.append(identifier.getMungedValue()); - } else { - result.append(symbol); - } - } else if (token.getType() == Token.COMMA) { - result.append(','); - } - } - result.append(')'); - token = consumeToken(); - assert token.getType() == Token.LC; - result.append('{'); - braceNesting++; - token = getToken(0); - if (token.getType() == Token.STRING && - getToken(1).getType() == Token.SEMI) { - // This is a hint. Skip it! - consumeToken(); - consumeToken(); - } - break; - - case Token.RETURN: - case Token.TYPEOF: - result.append(literals.get(new Integer(token.getType()))); - // No space needed after 'return' and 'typeof' when followed - // by '(', '[', '{', a string or a regexp. - if (offset < length) { - token = getToken(0); - if (token.getType() != Token.LP && - token.getType() != Token.LB && - token.getType() != Token.LC && - token.getType() != Token.STRING && - token.getType() != Token.REGEXP && - token.getType() != Token.SEMI) { - result.append(' '); - } - } - break; - - case Token.CASE: - case Token.THROW: - result.append(literals.get(new Integer(token.getType()))); - // White-space needed after 'case' and 'throw' when not followed by a string. - if (offset < length && getToken(0).getType() != Token.STRING) { - result.append(' '); - } - break; - - case Token.BREAK: - case Token.CONTINUE: - result.append(literals.get(new Integer(token.getType()))); - if (offset < length && getToken(0).getType() != Token.SEMI) { - // If 'break' or 'continue' is not followed by a semi-colon, it must - // be followed by a label, hence the need for a white space. - result.append(' '); - } - break; - - case Token.LC: - result.append('{'); - braceNesting++; - break; - - case Token.RC: - result.append('}'); - braceNesting--; - assert braceNesting >= currentScope.getBraceNesting(); - if (braceNesting == currentScope.getBraceNesting()) { - leaveCurrentScope(); - } - break; - - case Token.SEMI: - // No need to output a semi-colon if the next character is a right-curly... - if (preserveAllSemiColons || offset < length && getToken(0).getType() != Token.RC) { - result.append(';'); - } - - if (linebreakpos >= 0 && result.length() - linestartpos > linebreakpos) { - // Some source control tools don't like it when files containing lines longer - // than, say 8000 characters, are checked in. The linebreak option is used in - // that case to split long lines after a specific column. - result.append('\n'); - linestartpos = result.length(); - } - break; - - case Token.SPECIALCOMMENT: - if (result.length() > 0 && result.charAt(result.length() - 1) != '\n') { - result.append("\n"); - } - result.append("/*"); - result.append(symbol); - result.append("*/\n"); - break; - - default: - String literal = (String) literals.get(new Integer(token.getType())); - if (literal != null) { - result.append(literal); - } else { - warn("This symbol cannot be printed: " + symbol, true); - } - break; - } - } - - // Append a semi-colon at the end, even if unnecessary semi-colons are - // supposed to be removed. This is especially useful when concatenating - // several minified files (the absence of an ending semi-colon at the - // end of one file may very likely cause a syntax error) - if (!preserveAllSemiColons && result.length() > 0) { - if (result.charAt(result.length() - 1) == '\n') { - result.setCharAt(result.length() - 1, ';'); - } else { - result.append(';'); - } - } - - return result; - } -} diff --git a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java b/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java deleted file mode 100644 index 8668f49..0000000 --- a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * YUI Compressor - * Author: Julien Lecomte <jlecomte@yahoo-inc.com> - * Copyright (c) 2007, Yahoo! Inc. All rights reserved. - * Code licensed under the BSD License: - * http://developer.yahoo.net/yui/license.txt - */ - -package com.yahoo.platform.yui.compressor; - -import yuicompressor.org.mozilla.javascript.Token; - -/** - * JavaScriptIdentifier represents a variable/function identifier. - */ -class JavaScriptIdentifier extends JavaScriptToken { - - private int refcount = 0; - private String mungedValue; - private ScriptOrFnScope declaredScope; - private boolean markedForMunging = true; - - JavaScriptIdentifier(String value, ScriptOrFnScope declaredScope) { - super(Token.NAME, value); - this.declaredScope = declaredScope; - } - - ScriptOrFnScope getDeclaredScope() { - return declaredScope; - } - - void setMungedValue(String value) { - mungedValue = value; - } - - String getMungedValue() { - return mungedValue; - } - - void preventMunging() { - markedForMunging = false; - } - - boolean isMarkedForMunging() { - return markedForMunging; - } - - void incrementRefcount() { - refcount++; - } - - int getRefcount() { - return refcount; - } -} diff --git a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptToken.java b/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptToken.java deleted file mode 100644 index fee21d9..0000000 --- a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/JavaScriptToken.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * YUI Compressor - * Author: Julien Lecomte <jlecomte@yahoo-inc.com> - * Copyright (c) 2007, Yahoo! Inc. All rights reserved. - * Code licensed under the BSD License: - * http://developer.yahoo.net/yui/license.txt - */ - -package com.yahoo.platform.yui.compressor; - -public class JavaScriptToken { - - private int type; - private String value; - - JavaScriptToken(int type, String value) { - this.type = type; - this.value = value; - } - - int getType() { - return type; - } - - String getValue() { - return value; - } -} diff --git a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java b/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java deleted file mode 100644 index c1a2e47..0000000 --- a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * YUI Compressor - * Author: Julien Lecomte <jlecomte@yahoo-inc.com> - * Copyright (c) 2007, Yahoo! Inc. All rights reserved. - * Code licensed under the BSD License: - * http://developer.yahoo.net/yui/license.txt - */ - -package com.yahoo.platform.yui.compressor; - -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Hashtable; - -class ScriptOrFnScope { - - private int braceNesting; - private ScriptOrFnScope parentScope; - private ArrayList subScopes; - private Hashtable identifiers = new Hashtable(); - private Hashtable hints = new Hashtable(); - private boolean markedForMunging = true; - private int varcount = 0; - - ScriptOrFnScope(int braceNesting, ScriptOrFnScope parentScope) { - this.braceNesting = braceNesting; - this.parentScope = parentScope; - this.subScopes = new ArrayList(); - if (parentScope != null) { - parentScope.subScopes.add(this); - } - } - - int getBraceNesting() { - return braceNesting; - } - - ScriptOrFnScope getParentScope() { - return parentScope; - } - - JavaScriptIdentifier declareIdentifier(String symbol) { - JavaScriptIdentifier identifier = (JavaScriptIdentifier) identifiers.get(symbol); - if (identifier == null) { - identifier = new JavaScriptIdentifier(symbol, this); - identifiers.put(symbol, identifier); - } - return identifier; - } - - JavaScriptIdentifier getIdentifier(String symbol) { - return (JavaScriptIdentifier) identifiers.get(symbol); - } - - void addHint(String variableName, String variableType) { - hints.put(variableName, variableType); - } - - void preventMunging() { - if (parentScope != null) { - // The symbols in the global scope don't get munged, - // but the sub-scopes it contains do get munged. - markedForMunging = false; - } - } - - private ArrayList getUsedSymbols() { - ArrayList result = new ArrayList(); - Enumeration elements = identifiers.elements(); - while (elements.hasMoreElements()) { - JavaScriptIdentifier identifier = (JavaScriptIdentifier) elements.nextElement(); - String mungedValue = identifier.getMungedValue(); - if (mungedValue == null) { - mungedValue = identifier.getValue(); - } - result.add(mungedValue); - } - return result; - } - - private ArrayList getAllUsedSymbols() { - ArrayList result = new ArrayList(); - ScriptOrFnScope scope = this; - while (scope != null) { - result.addAll(scope.getUsedSymbols()); - scope = scope.parentScope; - } - return result; - } - - int incrementVarCount() { - varcount++; - return varcount; - } - - void munge() { - - if (!markedForMunging) { - // Stop right here if this scope was flagged as unsafe for munging. - return; - } - - int pickFromSet = 1; - - // Do not munge symbols in the global scope! - if (parentScope != null) { - - ArrayList freeSymbols = new ArrayList(); - - freeSymbols.addAll(JavaScriptCompressor.ones); - freeSymbols.removeAll(getAllUsedSymbols()); - if (freeSymbols.size() == 0) { - pickFromSet = 2; - freeSymbols.addAll(JavaScriptCompressor.twos); - freeSymbols.removeAll(getAllUsedSymbols()); - } - if (freeSymbols.size() == 0) { - pickFromSet = 3; - freeSymbols.addAll(JavaScriptCompressor.threes); - freeSymbols.removeAll(getAllUsedSymbols()); - } - if (freeSymbols.size() == 0) { - throw new IllegalStateException("The YUI Compressor ran out of symbols. Aborting..."); - } - - // APPJET: sort identifiers by popularity - JavaScriptIdentifier idArray[] = ((Hashtable<String,JavaScriptIdentifier>)identifiers).values().toArray(new JavaScriptIdentifier[0]); - java.util.Arrays.sort(idArray, new java.util.Comparator<JavaScriptIdentifier>() { - public int compare(JavaScriptIdentifier i1, JavaScriptIdentifier i2) { - return i2.getRefcount() - i1.getRefcount(); // positive if i2 is more popular, indicating i2 should come first - } - }); - java.util.Iterator<JavaScriptIdentifier> elements = java.util.Arrays.asList(idArray).iterator(); - - //Enumeration elements = identifiers.elements(); - while (elements.hasNext()) { - if (freeSymbols.size() == 0) { - pickFromSet++; - if (pickFromSet == 2) { - freeSymbols.addAll(JavaScriptCompressor.twos); - } else if (pickFromSet == 3) { - freeSymbols.addAll(JavaScriptCompressor.threes); - } else { - throw new IllegalStateException("The YUI Compressor ran out of symbols. Aborting..."); - } - // It is essential to remove the symbols already used in - // the containing scopes, or some of the variables declared - // in the containing scopes will be redeclared, which can - // lead to errors. - freeSymbols.removeAll(getAllUsedSymbols()); - } - - String mungedValue; - JavaScriptIdentifier identifier = (JavaScriptIdentifier) elements.next(); - if (identifier.isMarkedForMunging()) { - mungedValue = (String) freeSymbols.remove(0); - } else { - mungedValue = identifier.getValue(); - } - identifier.setMungedValue(mungedValue); - } - } - - for (int i = 0; i < subScopes.size(); i++) { - ScriptOrFnScope scope = (ScriptOrFnScope) subScopes.get(i); - scope.munge(); - } - } -} diff --git a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/YUICompressor.java b/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/YUICompressor.java deleted file mode 100644 index dcbaff4..0000000 --- a/infrastructure/yuicompressor/src/com/yahoo/platform/yui/compressor/YUICompressor.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * YUI Compressor - * Author: Julien Lecomte <jlecomte@yahoo-inc.com> - * Copyright (c) 2007, Yahoo! Inc. All rights reserved. - * Code licensed under the BSD License: - * http://developer.yahoo.net/yui/license.txt - */ - -package com.yahoo.platform.yui.compressor; - -import jargs.gnu.CmdLineParser; -import yuicompressor.org.mozilla.javascript.ErrorReporter; -import yuicompressor.org.mozilla.javascript.EvaluatorException; - -import java.io.*; -import java.nio.charset.Charset; - -public class YUICompressor { - - public static void main(String args[]) { - - CmdLineParser parser = new CmdLineParser(); - CmdLineParser.Option typeOpt = parser.addStringOption("type"); - CmdLineParser.Option verboseOpt = parser.addBooleanOption('v', "verbose"); - CmdLineParser.Option nomungeOpt = parser.addBooleanOption("nomunge"); - CmdLineParser.Option linebreakOpt = parser.addStringOption("line-break"); - CmdLineParser.Option preserveSemiOpt = parser.addBooleanOption("preserve-semi"); - CmdLineParser.Option disableOptimizationsOpt = parser.addBooleanOption("disable-optimizations"); - CmdLineParser.Option helpOpt = parser.addBooleanOption('h', "help"); - CmdLineParser.Option charsetOpt = parser.addStringOption("charset"); - CmdLineParser.Option outputFilenameOpt = parser.addStringOption('o', "output"); - - Reader in = null; - Writer out = null; - - try { - - parser.parse(args); - - Boolean help = (Boolean) parser.getOptionValue(helpOpt); - if (help != null && help.booleanValue()) { - usage(); - System.exit(0); - } - - boolean verbose = parser.getOptionValue(verboseOpt) != null; - - String charset = (String) parser.getOptionValue(charsetOpt); - if (charset == null || !Charset.isSupported(charset)) { - charset = System.getProperty("file.encoding"); - if (charset == null) { - charset = "UTF-8"; - } - if (verbose) { - System.err.println("\n[INFO] Using charset " + charset); - } - } - - String[] fileArgs = parser.getRemainingArgs(); - String type = (String) parser.getOptionValue(typeOpt); - - if (fileArgs.length == 0) { - - if (type == null || !type.equalsIgnoreCase("js") && !type.equalsIgnoreCase("css")) { - usage(); - System.exit(1); - } - - in = new InputStreamReader(System.in, charset); - - } else { - - if (type != null && !type.equalsIgnoreCase("js") && !type.equalsIgnoreCase("css")) { - usage(); - System.exit(1); - } - - String inputFilename = fileArgs[0]; - - if (type == null) { - int idx = inputFilename.lastIndexOf('.'); - if (idx >= 0 && idx < inputFilename.length() - 1) { - type = inputFilename.substring(idx + 1); - } - } - - if (type == null || !type.equalsIgnoreCase("js") && !type.equalsIgnoreCase("css")) { - usage(); - System.exit(1); - } - - in = new InputStreamReader(new FileInputStream(inputFilename), charset); - } - - int linebreakpos = -1; - String linebreakstr = (String) parser.getOptionValue(linebreakOpt); - if (linebreakstr != null) { - try { - linebreakpos = Integer.parseInt(linebreakstr, 10); - } catch (NumberFormatException e) { - usage(); - System.exit(1); - } - } - - String outputFilename = (String) parser.getOptionValue(outputFilenameOpt); - - if (type.equalsIgnoreCase("js")) { - - try { - - JavaScriptCompressor compressor = new JavaScriptCompressor(in, new ErrorReporter() { - - public void warning(String message, String sourceName, - int line, String lineSource, int lineOffset) { - if (line < 0) { - System.err.println("\n[WARNING] " + message); - } else { - System.err.println("\n[WARNING] " + line + ':' + lineOffset + ':' + message); - } - } - - public void error(String message, String sourceName, - int line, String lineSource, int lineOffset) { - if (line < 0) { - System.err.println("\n[ERROR] " + message); - } else { - System.err.println("\n[ERROR] " + line + ':' + lineOffset + ':' + message); - } - } - - public EvaluatorException runtimeError(String message, String sourceName, - int line, String lineSource, int lineOffset) { - error(message, sourceName, line, lineSource, lineOffset); - return new EvaluatorException(message); - } - }); - - // Close the input stream first, and then open the output stream, - // in case the output file should override the input file. - in.close(); in = null; - - if (outputFilename == null) { - out = new OutputStreamWriter(System.out, charset); - } else { - out = new OutputStreamWriter(new FileOutputStream(outputFilename), charset); - } - - boolean munge = parser.getOptionValue(nomungeOpt) == null; - boolean preserveAllSemiColons = parser.getOptionValue(preserveSemiOpt) != null; - boolean disableOptimizations = parser.getOptionValue(disableOptimizationsOpt) != null; - - compressor.compress(out, linebreakpos, munge, verbose, - preserveAllSemiColons, disableOptimizations); - - } catch (EvaluatorException e) { - - e.printStackTrace(); - // Return a special error code used specifically by the web front-end. - System.exit(2); - - } - - } else if (type.equalsIgnoreCase("css")) { - - CssCompressor compressor = new CssCompressor(in); - - // Close the input stream first, and then open the output stream, - // in case the output file should override the input file. - in.close(); in = null; - - if (outputFilename == null) { - out = new OutputStreamWriter(System.out, charset); - } else { - out = new OutputStreamWriter(new FileOutputStream(outputFilename), charset); - } - - compressor.compress(out, linebreakpos); - } - - } catch (CmdLineParser.OptionException e) { - - usage(); - System.exit(1); - - } catch (IOException e) { - - e.printStackTrace(); - System.exit(1); - - } finally { - - if (in != null) { - try { - in.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - if (out != null) { - try { - out.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - - private static void usage() { - System.out.println( - "\nUsage: java -jar yuicompressor-x.y.z.jar [options] [input file]\n\n" - - + "Global Options\n" - + " -h, --help Displays this information\n" - + " --type <js|css> Specifies the type of the input file\n" - + " --charset <charset> Read the input file using <charset>\n" - + " --line-break <column> Insert a line break after the specified column number\n" - + " -v, --verbose Display informational messages and warnings\n" - + " -o <file> Place the output into <file>. Defaults to stdout.\n\n" - - + "JavaScript Options\n" - + " --nomunge Minify only, do not obfuscate\n" - + " --preserve-semi Preserve all semicolons\n" - + " --disable-optimizations Disable all micro optimizations\n\n" - - + "If no input file is specified, it defaults to stdin. In this case, the 'type'\n" - + "option is required. Otherwise, the 'type' option is required only if the input\n" - + "file extension is neither 'js' nor 'css'."); - } -} |