Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScriptCompressor constants change from static variables to a soft-referenced container class #20

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="lib" path="build/yuicompressor-2.4.8pre.jar"/>
<classpathentry kind="lib" path="lib/jargs-1.0.jar"/>
<classpathentry kind="lib" path="lib/rhino-1.7R2.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="build/jar"/>
</classpath>
17 changes: 17 additions & 0 deletions .project
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>yuicompressor</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
289 changes: 57 additions & 232 deletions src/com/yahoo/platform/yui/compressor/JavaScriptCompressor.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,242 +8,31 @@
*/
package com.yahoo.platform.yui.compressor;

import org.mozilla.javascript.*;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.*;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.StringTokenizer;
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));
}
import org.mozilla.javascript.CompilerEnvirons;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.Parser;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Token;

// 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));
}
public class JavaScriptCompressor {

// 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), "@");
literals.put(new Integer(Token.LET), "let ");
literals.put(new Integer(Token.YIELD), "yield ");

// 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 SoftReference sharedJavaScriptWords;

private static int countChar(String haystack, char needle) {
int idx = 0;
Expand Down Expand Up @@ -341,7 +130,7 @@ private static ArrayList parse(Reader in, ErrorReporter reporter)
break;

default:
String literal = (String) literals.get(new Integer(tt));
String literal = (String) javaScriptWords().literals.get(new Integer(tt));
if (literal != null) {
tokens.add(new JavaScriptToken(tt, literal));
}
Expand Down Expand Up @@ -458,7 +247,7 @@ private static String escapeString(String s, char quotechar) {

private static boolean isValidIdentifier(String s) {
Matcher m = SIMPLE_IDENTIFIER_NAME_PATTERN.matcher(s);
return (m.matches() && !reserved.contains(s));
return (m.matches() && !javaScriptWords().reserved.contains(s));
}

/*
Expand Down Expand Up @@ -528,13 +317,46 @@ private static void optimizeObjLitMemberDecl(ArrayList tokens) {
private Stack scopes = new Stack();
private ScriptOrFnScope globalScope = new ScriptOrFnScope(-1, null);
private Hashtable indexedScopes = new Hashtable();

private JavaScriptWords words;

private ArrayList ones;
private ArrayList twos;
private ArrayList threes;

private Set builtin = new HashSet();
private Map literals = new Hashtable();
private Set reserved = new HashSet();

public JavaScriptCompressor(Reader in, ErrorReporter reporter)
throws IOException, EvaluatorException {

prepareJavaScriptWords();

this.logger = reporter;
this.tokens = parse(in, reporter);
}

private void prepareJavaScriptWords() {
/* Create a strong reference to the precompute so that it isn't gc'd while we're alive */
words = javaScriptWords();

ones = words.ones;
twos = words.twos;
threes = words.threes;
builtin = words.builtin;
literals = words.literals;
reserved = words.reserved;
}

static JavaScriptWords javaScriptWords() {
JavaScriptWords words = sharedJavaScriptWords != null ? (JavaScriptWords) sharedJavaScriptWords.get() : null;
if (words == null) {
words = new JavaScriptWords();
sharedJavaScriptWords = new SoftReference(words);
}
return words;
}

public void compress(Writer out, Writer mungemap, int linebreak, boolean munge, boolean verbose,
boolean preserveAllSemiColons, boolean disableOptimizations)
Expand Down Expand Up @@ -1083,15 +905,18 @@ private void mungeSymboltree() {
scopes.clear();
mode = CHECKING_SYMBOL_TREE;
parseScope(globalScope);
globalScope.munge();
globalScope.munge(words);
}

private StringBuffer printSymbolTree(int linebreakpos, boolean preserveAllSemiColons)
throws IOException {

offset = 0;
braceNesting = 0;
scopes.clear();

if (tokens.isEmpty())
return new StringBuffer();

String symbol;
JavaScriptToken token;
Expand Down
Loading