golf 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/golf.gemspec +1 -1
- data/lib/golf/version.rb +1 -1
- metadata +1 -121
- data/golf-java/README.markdown +0 -41
- data/golf-java/THANKS.markdown +0 -14
- data/golf-java/build/classes.zip +0 -0
- data/golf-java/build/com/thinkminimo/golf/GolfResource$MimeMapping$MimeMappingSingleton.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/GolfResource$MimeMapping.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/GolfResource.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/GolfServlet$1.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/GolfServlet$GolfContext.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/GolfServlet$GolfParams.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/GolfServlet$GolfSession.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/GolfServlet$PermanentRedirectException.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/GolfServlet$RedirectException.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/GolfServlet$StoredJSVM.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/GolfServlet.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/JsonpTunnel.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/Main$1.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/Main$RingList.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/Main.class +0 -0
- data/golf-java/build/com/thinkminimo/golf/ProxyServlet.class +0 -0
- data/golf-java/build/com/yahoo/platform/yui/compressor/CssCompressor.class +0 -0
- data/golf-java/build/com/yahoo/platform/yui/compressor/JarClassLoader.class +0 -0
- data/golf-java/build/com/yahoo/platform/yui/compressor/JavaScriptCompressor.class +0 -0
- data/golf-java/build/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.class +0 -0
- data/golf-java/build/com/yahoo/platform/yui/compressor/JavaScriptToken.class +0 -0
- data/golf-java/build/com/yahoo/platform/yui/compressor/ScriptOrFnScope.class +0 -0
- data/golf-java/build/depends.zip +0 -0
- data/golf-java/build/golf +0 -2
- data/golf-java/build/org/json/JSONArray.class +0 -0
- data/golf-java/build/org/json/JSONException.class +0 -0
- data/golf-java/build/org/json/JSONObject$1.class +0 -0
- data/golf-java/build/org/json/JSONObject$Null.class +0 -0
- data/golf-java/build/org/json/JSONObject.class +0 -0
- data/golf-java/build/org/json/JSONString.class +0 -0
- data/golf-java/build/org/json/JSONStringer.class +0 -0
- data/golf-java/build/org/json/JSONTokener.class +0 -0
- data/golf-java/build/org/json/JSONWriter.class +0 -0
- data/golf-java/build/resources.zip +0 -0
- data/golf-java/build.xml +0 -174
- data/golf-java/dist/golf.zip +0 -0
- data/golf-java/lib/ant-launcher.jar +0 -0
- data/golf-java/lib/ant.jar +0 -0
- data/golf-java/lib/commons-codec-1.4.jar +0 -0
- data/golf-java/lib/commons-collections-3.2.1.jar +0 -0
- data/golf-java/lib/commons-fileupload-1.2.1.jar +0 -0
- data/golf-java/lib/commons-httpclient-3.1.jar +0 -0
- data/golf-java/lib/commons-io-1.4.jar +0 -0
- data/golf-java/lib/commons-lang-2.4.jar +0 -0
- data/golf-java/lib/commons-logging-1.1.1.jar +0 -0
- data/golf-java/lib/cssparser-0.9.5.jar +0 -0
- data/golf-java/lib/getopt-0.1-dev.jar +0 -0
- data/golf-java/lib/htmlunit-2.6.jar +0 -0
- data/golf-java/lib/htmlunit-core-js-2.6.jar +0 -0
- data/golf-java/lib/java-xmlbuilder-1.jar +0 -0
- data/golf-java/lib/jets3t-0.7.0.jar +0 -0
- data/golf-java/lib/jetty-6.1.15.jar +0 -0
- data/golf-java/lib/jetty-util-6.1.15.jar +0 -0
- data/golf-java/lib/log4j-1.2.15.jar +0 -0
- data/golf-java/lib/nekohtml-1.9.13.jar +0 -0
- data/golf-java/lib/sac-1.3.jar +0 -0
- data/golf-java/lib/serializer-2.7.1.jar +0 -0
- data/golf-java/lib/servlet-api-2.5-20081211.jar +0 -0
- data/golf-java/lib/xalan-2.7.1.jar +0 -0
- data/golf-java/lib/xercesImpl-2.9.1.jar +0 -0
- data/golf-java/lib/xml-apis-1.3.04.jar +0 -0
- data/golf-java/resources/app_error.html +0 -60
- data/golf-java/resources/error.html +0 -29
- data/golf-java/resources/forcebot.txt +0 -0
- data/golf-java/resources/forceclient.txt +0 -0
- data/golf-java/resources/forceproxy.txt +0 -0
- data/golf-java/resources/head.html +0 -0
- data/golf-java/resources/jquery.address.js +0 -439
- data/golf-java/resources/jquery.golf.js +0 -945
- data/golf-java/resources/jquery.js +0 -4376
- data/golf-java/resources/jsdetect.html +0 -23
- data/golf-java/resources/loading.gif +0 -0
- data/golf-java/resources/new.html +0 -45
- data/golf-java/resources/new.static.html +0 -45
- data/golf-java/resources/noscript.forceclient.html +0 -11
- data/golf-java/resources/noscript.html +0 -18
- data/golf-java/resources/noscript.static.html +0 -15
- data/golf-java/resources/project.xml +0 -21
- data/golf-java/resources/proxy_project.xml +0 -11
- data/golf-java/resources/proxy_web.xml +0 -40
- data/golf-java/resources/static_project.xml +0 -37
- data/golf-java/resources/version +0 -1
- data/golf-java/resources/web.xml +0 -36
- data/golf-java/resources/welcome2golf.html +0 -50
- data/golf-java/src/com/thinkminimo/golf/GolfResource.java +0 -582
- data/golf-java/src/com/thinkminimo/golf/GolfServlet.java +0 -1055
- data/golf-java/src/com/thinkminimo/golf/JsonpTunnel.java +0 -86
- data/golf-java/src/com/thinkminimo/golf/Main.java +0 -1299
- data/golf-java/src/com/thinkminimo/golf/ProxyServlet.java +0 -577
- data/golf-java/src/com/yahoo/platform/yui/compressor/CssCompressor.java +0 -188
- data/golf-java/src/com/yahoo/platform/yui/compressor/JarClassLoader.java +0 -158
- data/golf-java/src/com/yahoo/platform/yui/compressor/JavaScriptCompressor.java +0 -1281
- data/golf-java/src/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java +0 -55
- data/golf-java/src/com/yahoo/platform/yui/compressor/JavaScriptToken.java +0 -28
- data/golf-java/src/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java +0 -160
- data/golf-java/src/org/json/JSONArray.java +0 -934
- data/golf-java/src/org/json/JSONException.java +0 -27
- data/golf-java/src/org/json/JSONObject.java +0 -1550
- data/golf-java/src/org/json/JSONString.java +0 -18
- data/golf-java/src/org/json/JSONStringer.java +0 -78
- data/golf-java/src/org/json/JSONTokener.java +0 -422
- data/golf-java/src/org/json/JSONWriter.java +0 -323
- data/golf-java/test/client/golftest/README.markdown +0 -3
- data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.css +0 -114
- data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.html +0 -17
- data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.js +0 -81
- data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.res/asc.gif +0 -0
- data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.res/bg.gif +0 -0
- data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.res/desc.gif +0 -0
- data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.res/test/test.html +0 -1
- data/golf-java/test/client/golftest/controller.js +0 -131
- data/golf-java/test/client/golftest/forceclient.txt +0 -0
- data/golf-java/test/client/golftest/forceproxy.txt +0 -0
- data/golf-java/test/client/golftest/head.html +0 -1
- data/golf-java/test/client/golftest/scripts/jquery.tablesort.js +0 -75
- data/golf-java/test/client/golftest/styles/style.css +0 -18
- data/golf-java/test/client/golftest/test/test.html +0 -1
@@ -1,1281 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* YUI Compressor
|
3
|
-
* Author: Julien Lecomte <jlecomte@yahoo-inc.com>
|
4
|
-
* Copyright (c) 2007, Yahoo! Inc. All rights reserved.
|
5
|
-
* Code licensed under the BSD License:
|
6
|
-
* http://developer.yahoo.net/yui/license.txt
|
7
|
-
*/
|
8
|
-
|
9
|
-
package com.yahoo.platform.yui.compressor;
|
10
|
-
|
11
|
-
import net.sourceforge.htmlunit.corejs.javascript.*;
|
12
|
-
|
13
|
-
import java.io.IOException;
|
14
|
-
import java.io.Reader;
|
15
|
-
import java.io.Writer;
|
16
|
-
import java.util.*;
|
17
|
-
import java.util.regex.Matcher;
|
18
|
-
import java.util.regex.Pattern;
|
19
|
-
|
20
|
-
public class JavaScriptCompressor {
|
21
|
-
|
22
|
-
static final ArrayList ones;
|
23
|
-
static final ArrayList twos;
|
24
|
-
static final ArrayList threes;
|
25
|
-
|
26
|
-
static final Set builtin = new HashSet();
|
27
|
-
static final Map literals = new Hashtable();
|
28
|
-
static final Set reserved = new HashSet();
|
29
|
-
|
30
|
-
static {
|
31
|
-
|
32
|
-
// This list contains all the 3 characters or less built-in global
|
33
|
-
// symbols available in a browser. Please add to this list if you
|
34
|
-
// see anything missing.
|
35
|
-
builtin.add("NaN");
|
36
|
-
builtin.add("top");
|
37
|
-
|
38
|
-
ones = new ArrayList();
|
39
|
-
for (char c = 'a'; c <= 'z'; c++)
|
40
|
-
ones.add(Character.toString(c));
|
41
|
-
for (char c = 'A'; c <= 'Z'; c++)
|
42
|
-
ones.add(Character.toString(c));
|
43
|
-
|
44
|
-
twos = new ArrayList();
|
45
|
-
for (int i = 0; i < ones.size(); i++) {
|
46
|
-
String one = (String) ones.get(i);
|
47
|
-
for (char c = 'a'; c <= 'z'; c++)
|
48
|
-
twos.add(one + Character.toString(c));
|
49
|
-
for (char c = 'A'; c <= 'Z'; c++)
|
50
|
-
twos.add(one + Character.toString(c));
|
51
|
-
for (char c = '0'; c <= '9'; c++)
|
52
|
-
twos.add(one + Character.toString(c));
|
53
|
-
}
|
54
|
-
|
55
|
-
// Remove two-letter JavaScript reserved words and built-in globals...
|
56
|
-
twos.remove("as");
|
57
|
-
twos.remove("is");
|
58
|
-
twos.remove("do");
|
59
|
-
twos.remove("if");
|
60
|
-
twos.remove("in");
|
61
|
-
twos.removeAll(builtin);
|
62
|
-
|
63
|
-
threes = new ArrayList();
|
64
|
-
for (int i = 0; i < twos.size(); i++) {
|
65
|
-
String two = (String) twos.get(i);
|
66
|
-
for (char c = 'a'; c <= 'z'; c++)
|
67
|
-
threes.add(two + Character.toString(c));
|
68
|
-
for (char c = 'A'; c <= 'Z'; c++)
|
69
|
-
threes.add(two + Character.toString(c));
|
70
|
-
for (char c = '0'; c <= '9'; c++)
|
71
|
-
threes.add(two + Character.toString(c));
|
72
|
-
}
|
73
|
-
|
74
|
-
// Remove three-letter JavaScript reserved words and built-in globals...
|
75
|
-
threes.remove("for");
|
76
|
-
threes.remove("int");
|
77
|
-
threes.remove("new");
|
78
|
-
threes.remove("try");
|
79
|
-
threes.remove("use");
|
80
|
-
threes.remove("var");
|
81
|
-
threes.removeAll(builtin);
|
82
|
-
|
83
|
-
// That's up to ((26+26)*(1+(26+26+10)))*(1+(26+26+10))-8
|
84
|
-
// (206,380 symbols per scope)
|
85
|
-
|
86
|
-
// The following list comes from org/mozilla/javascript/Decompiler.java...
|
87
|
-
literals.put(new Integer(Token.GET), "get ");
|
88
|
-
literals.put(new Integer(Token.SET), "set ");
|
89
|
-
literals.put(new Integer(Token.TRUE), "true");
|
90
|
-
literals.put(new Integer(Token.FALSE), "false");
|
91
|
-
literals.put(new Integer(Token.NULL), "null");
|
92
|
-
literals.put(new Integer(Token.THIS), "this");
|
93
|
-
literals.put(new Integer(Token.FUNCTION), "function");
|
94
|
-
literals.put(new Integer(Token.COMMA), ",");
|
95
|
-
literals.put(new Integer(Token.LC), "{");
|
96
|
-
literals.put(new Integer(Token.RC), "}");
|
97
|
-
literals.put(new Integer(Token.LP), "(");
|
98
|
-
literals.put(new Integer(Token.RP), ")");
|
99
|
-
literals.put(new Integer(Token.LB), "[");
|
100
|
-
literals.put(new Integer(Token.RB), "]");
|
101
|
-
literals.put(new Integer(Token.DOT), ".");
|
102
|
-
literals.put(new Integer(Token.NEW), "new ");
|
103
|
-
literals.put(new Integer(Token.DELPROP), "delete ");
|
104
|
-
literals.put(new Integer(Token.IF), "if");
|
105
|
-
literals.put(new Integer(Token.ELSE), "else");
|
106
|
-
literals.put(new Integer(Token.FOR), "for");
|
107
|
-
literals.put(new Integer(Token.IN), " in ");
|
108
|
-
literals.put(new Integer(Token.WITH), "with");
|
109
|
-
literals.put(new Integer(Token.WHILE), "while");
|
110
|
-
literals.put(new Integer(Token.DO), "do");
|
111
|
-
literals.put(new Integer(Token.TRY), "try");
|
112
|
-
literals.put(new Integer(Token.CATCH), "catch");
|
113
|
-
literals.put(new Integer(Token.FINALLY), "finally");
|
114
|
-
literals.put(new Integer(Token.THROW), "throw");
|
115
|
-
literals.put(new Integer(Token.SWITCH), "switch");
|
116
|
-
literals.put(new Integer(Token.BREAK), "break");
|
117
|
-
literals.put(new Integer(Token.CONTINUE), "continue");
|
118
|
-
literals.put(new Integer(Token.CASE), "case");
|
119
|
-
literals.put(new Integer(Token.DEFAULT), "default");
|
120
|
-
literals.put(new Integer(Token.RETURN), "return");
|
121
|
-
literals.put(new Integer(Token.VAR), "var ");
|
122
|
-
literals.put(new Integer(Token.SEMI), ";");
|
123
|
-
literals.put(new Integer(Token.ASSIGN), "=");
|
124
|
-
literals.put(new Integer(Token.ASSIGN_ADD), "+=");
|
125
|
-
literals.put(new Integer(Token.ASSIGN_SUB), "-=");
|
126
|
-
literals.put(new Integer(Token.ASSIGN_MUL), "*=");
|
127
|
-
literals.put(new Integer(Token.ASSIGN_DIV), "/=");
|
128
|
-
literals.put(new Integer(Token.ASSIGN_MOD), "%=");
|
129
|
-
literals.put(new Integer(Token.ASSIGN_BITOR), "|=");
|
130
|
-
literals.put(new Integer(Token.ASSIGN_BITXOR), "^=");
|
131
|
-
literals.put(new Integer(Token.ASSIGN_BITAND), "&=");
|
132
|
-
literals.put(new Integer(Token.ASSIGN_LSH), "<<=");
|
133
|
-
literals.put(new Integer(Token.ASSIGN_RSH), ">>=");
|
134
|
-
literals.put(new Integer(Token.ASSIGN_URSH), ">>>=");
|
135
|
-
literals.put(new Integer(Token.HOOK), "?");
|
136
|
-
literals.put(new Integer(Token.OBJECTLIT), ":");
|
137
|
-
literals.put(new Integer(Token.COLON), ":");
|
138
|
-
literals.put(new Integer(Token.OR), "||");
|
139
|
-
literals.put(new Integer(Token.AND), "&&");
|
140
|
-
literals.put(new Integer(Token.BITOR), "|");
|
141
|
-
literals.put(new Integer(Token.BITXOR), "^");
|
142
|
-
literals.put(new Integer(Token.BITAND), "&");
|
143
|
-
literals.put(new Integer(Token.SHEQ), "===");
|
144
|
-
literals.put(new Integer(Token.SHNE), "!==");
|
145
|
-
literals.put(new Integer(Token.EQ), "==");
|
146
|
-
literals.put(new Integer(Token.NE), "!=");
|
147
|
-
literals.put(new Integer(Token.LE), "<=");
|
148
|
-
literals.put(new Integer(Token.LT), "<");
|
149
|
-
literals.put(new Integer(Token.GE), ">=");
|
150
|
-
literals.put(new Integer(Token.GT), ">");
|
151
|
-
literals.put(new Integer(Token.INSTANCEOF), " instanceof ");
|
152
|
-
literals.put(new Integer(Token.LSH), "<<");
|
153
|
-
literals.put(new Integer(Token.RSH), ">>");
|
154
|
-
literals.put(new Integer(Token.URSH), ">>>");
|
155
|
-
literals.put(new Integer(Token.TYPEOF), "typeof");
|
156
|
-
literals.put(new Integer(Token.VOID), "void ");
|
157
|
-
literals.put(new Integer(Token.CONST), "const ");
|
158
|
-
literals.put(new Integer(Token.NOT), "!");
|
159
|
-
literals.put(new Integer(Token.BITNOT), "~");
|
160
|
-
literals.put(new Integer(Token.POS), "+");
|
161
|
-
literals.put(new Integer(Token.NEG), "-");
|
162
|
-
literals.put(new Integer(Token.INC), "++");
|
163
|
-
literals.put(new Integer(Token.DEC), "--");
|
164
|
-
literals.put(new Integer(Token.ADD), "+");
|
165
|
-
literals.put(new Integer(Token.SUB), "-");
|
166
|
-
literals.put(new Integer(Token.MUL), "*");
|
167
|
-
literals.put(new Integer(Token.DIV), "/");
|
168
|
-
literals.put(new Integer(Token.MOD), "%");
|
169
|
-
literals.put(new Integer(Token.COLONCOLON), "::");
|
170
|
-
literals.put(new Integer(Token.DOTDOT), "..");
|
171
|
-
literals.put(new Integer(Token.DOTQUERY), ".(");
|
172
|
-
literals.put(new Integer(Token.XMLATTR), "@");
|
173
|
-
|
174
|
-
// See http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Reserved_Words
|
175
|
-
|
176
|
-
// JavaScript 1.5 reserved words
|
177
|
-
reserved.add("break");
|
178
|
-
reserved.add("case");
|
179
|
-
reserved.add("catch");
|
180
|
-
reserved.add("continue");
|
181
|
-
reserved.add("default");
|
182
|
-
reserved.add("delete");
|
183
|
-
reserved.add("do");
|
184
|
-
reserved.add("else");
|
185
|
-
reserved.add("finally");
|
186
|
-
reserved.add("for");
|
187
|
-
reserved.add("function");
|
188
|
-
reserved.add("if");
|
189
|
-
reserved.add("in");
|
190
|
-
reserved.add("instanceof");
|
191
|
-
reserved.add("new");
|
192
|
-
reserved.add("return");
|
193
|
-
reserved.add("switch");
|
194
|
-
reserved.add("this");
|
195
|
-
reserved.add("throw");
|
196
|
-
reserved.add("try");
|
197
|
-
reserved.add("typeof");
|
198
|
-
reserved.add("var");
|
199
|
-
reserved.add("void");
|
200
|
-
reserved.add("while");
|
201
|
-
reserved.add("with");
|
202
|
-
// Words reserved for future use
|
203
|
-
reserved.add("abstract");
|
204
|
-
reserved.add("boolean");
|
205
|
-
reserved.add("byte");
|
206
|
-
reserved.add("char");
|
207
|
-
reserved.add("class");
|
208
|
-
reserved.add("const");
|
209
|
-
reserved.add("debugger");
|
210
|
-
reserved.add("double");
|
211
|
-
reserved.add("enum");
|
212
|
-
reserved.add("export");
|
213
|
-
reserved.add("extends");
|
214
|
-
reserved.add("final");
|
215
|
-
reserved.add("float");
|
216
|
-
reserved.add("goto");
|
217
|
-
reserved.add("implements");
|
218
|
-
reserved.add("import");
|
219
|
-
reserved.add("int");
|
220
|
-
reserved.add("interface");
|
221
|
-
reserved.add("long");
|
222
|
-
reserved.add("native");
|
223
|
-
reserved.add("package");
|
224
|
-
reserved.add("private");
|
225
|
-
reserved.add("protected");
|
226
|
-
reserved.add("public");
|
227
|
-
reserved.add("short");
|
228
|
-
reserved.add("static");
|
229
|
-
reserved.add("super");
|
230
|
-
reserved.add("synchronized");
|
231
|
-
reserved.add("throws");
|
232
|
-
reserved.add("transient");
|
233
|
-
reserved.add("volatile");
|
234
|
-
// These are not reserved, but should be taken into account
|
235
|
-
// in isValidIdentifier (See jslint source code)
|
236
|
-
reserved.add("arguments");
|
237
|
-
reserved.add("eval");
|
238
|
-
reserved.add("true");
|
239
|
-
reserved.add("false");
|
240
|
-
reserved.add("Infinity");
|
241
|
-
reserved.add("NaN");
|
242
|
-
reserved.add("null");
|
243
|
-
reserved.add("undefined");
|
244
|
-
}
|
245
|
-
|
246
|
-
private static int countChar(String haystack, char needle) {
|
247
|
-
int idx = 0;
|
248
|
-
int count = 0;
|
249
|
-
int length = haystack.length();
|
250
|
-
while (idx < length) {
|
251
|
-
char c = haystack.charAt(idx++);
|
252
|
-
if (c == needle) {
|
253
|
-
count++;
|
254
|
-
}
|
255
|
-
}
|
256
|
-
return count;
|
257
|
-
}
|
258
|
-
|
259
|
-
private static int printSourceString(String source, int offset, StringBuffer sb) {
|
260
|
-
int length = source.charAt(offset);
|
261
|
-
++offset;
|
262
|
-
if ((0x8000 & length) != 0) {
|
263
|
-
length = ((0x7FFF & length) << 16) | source.charAt(offset);
|
264
|
-
++offset;
|
265
|
-
}
|
266
|
-
if (sb != null) {
|
267
|
-
String str = source.substring(offset, offset + length);
|
268
|
-
sb.append(str);
|
269
|
-
}
|
270
|
-
return offset + length;
|
271
|
-
}
|
272
|
-
|
273
|
-
private static int printSourceNumber(String source,
|
274
|
-
int offset, StringBuffer sb) {
|
275
|
-
double number = 0.0;
|
276
|
-
char type = source.charAt(offset);
|
277
|
-
++offset;
|
278
|
-
if (type == 'S') {
|
279
|
-
if (sb != null) {
|
280
|
-
number = source.charAt(offset);
|
281
|
-
}
|
282
|
-
++offset;
|
283
|
-
} else if (type == 'J' || type == 'D') {
|
284
|
-
if (sb != null) {
|
285
|
-
long lbits;
|
286
|
-
lbits = (long) source.charAt(offset) << 48;
|
287
|
-
lbits |= (long) source.charAt(offset + 1) << 32;
|
288
|
-
lbits |= (long) source.charAt(offset + 2) << 16;
|
289
|
-
lbits |= (long) source.charAt(offset + 3);
|
290
|
-
if (type == 'J') {
|
291
|
-
number = lbits;
|
292
|
-
} else {
|
293
|
-
number = Double.longBitsToDouble(lbits);
|
294
|
-
}
|
295
|
-
}
|
296
|
-
offset += 4;
|
297
|
-
} else {
|
298
|
-
// Bad source
|
299
|
-
throw new RuntimeException();
|
300
|
-
}
|
301
|
-
if (sb != null) {
|
302
|
-
sb.append(ScriptRuntime.numberToString(number, 10));
|
303
|
-
}
|
304
|
-
return offset;
|
305
|
-
}
|
306
|
-
|
307
|
-
private static ArrayList parse(Reader in, ErrorReporter reporter)
|
308
|
-
throws IOException, EvaluatorException {
|
309
|
-
|
310
|
-
CompilerEnvirons env = new CompilerEnvirons();
|
311
|
-
Parser parser = new Parser(env, reporter);
|
312
|
-
parser.parse(in, null, 1);
|
313
|
-
String source = ""; //FIXME parser.getEncodedSource();
|
314
|
-
|
315
|
-
int offset = 0;
|
316
|
-
int length = source.length();
|
317
|
-
ArrayList tokens = new ArrayList();
|
318
|
-
StringBuffer sb = new StringBuffer();
|
319
|
-
|
320
|
-
while (offset < length) {
|
321
|
-
int tt = source.charAt(offset++);
|
322
|
-
switch (tt) {
|
323
|
-
|
324
|
-
case Token.NAME:
|
325
|
-
case Token.REGEXP:
|
326
|
-
case Token.STRING:
|
327
|
-
sb.setLength(0);
|
328
|
-
offset = printSourceString(source, offset, sb);
|
329
|
-
tokens.add(new JavaScriptToken(tt, sb.toString()));
|
330
|
-
break;
|
331
|
-
|
332
|
-
case Token.NUMBER:
|
333
|
-
sb.setLength(0);
|
334
|
-
offset = printSourceNumber(source, offset, sb);
|
335
|
-
tokens.add(new JavaScriptToken(tt, sb.toString()));
|
336
|
-
break;
|
337
|
-
|
338
|
-
default:
|
339
|
-
String literal = (String) literals.get(new Integer(tt));
|
340
|
-
if (literal != null) {
|
341
|
-
tokens.add(new JavaScriptToken(tt, literal));
|
342
|
-
}
|
343
|
-
break;
|
344
|
-
}
|
345
|
-
}
|
346
|
-
|
347
|
-
return tokens;
|
348
|
-
}
|
349
|
-
|
350
|
-
private static void processStringLiterals(ArrayList tokens, boolean merge) {
|
351
|
-
|
352
|
-
String tv;
|
353
|
-
int i, length = tokens.size();
|
354
|
-
JavaScriptToken token, prevToken, nextToken;
|
355
|
-
|
356
|
-
if (merge) {
|
357
|
-
|
358
|
-
// Concatenate string literals that are being appended wherever
|
359
|
-
// it is safe to do so. Note that we take care of the case:
|
360
|
-
// "a" + "b".toUpperCase()
|
361
|
-
|
362
|
-
for (i = 0; i < length; i++) {
|
363
|
-
token = (JavaScriptToken) tokens.get(i);
|
364
|
-
switch (token.getType()) {
|
365
|
-
|
366
|
-
case Token.ADD:
|
367
|
-
if (i > 0 && i < length) {
|
368
|
-
prevToken = (JavaScriptToken) tokens.get(i - 1);
|
369
|
-
nextToken = (JavaScriptToken) tokens.get(i + 1);
|
370
|
-
if (prevToken.getType() == Token.STRING && nextToken.getType() == Token.STRING &&
|
371
|
-
(i == length - 1 || ((JavaScriptToken) tokens.get(i + 2)).getType() != Token.DOT)) {
|
372
|
-
tokens.set(i - 1, new JavaScriptToken(Token.STRING,
|
373
|
-
prevToken.getValue() + nextToken.getValue()));
|
374
|
-
tokens.remove(i + 1);
|
375
|
-
tokens.remove(i);
|
376
|
-
i = i - 1;
|
377
|
-
length = length - 2;
|
378
|
-
break;
|
379
|
-
}
|
380
|
-
}
|
381
|
-
}
|
382
|
-
}
|
383
|
-
|
384
|
-
}
|
385
|
-
|
386
|
-
// Second pass...
|
387
|
-
|
388
|
-
for (i = 0; i < length; i++) {
|
389
|
-
token = (JavaScriptToken) tokens.get(i);
|
390
|
-
if (token.getType() == Token.STRING) {
|
391
|
-
tv = token.getValue();
|
392
|
-
|
393
|
-
// Finally, add the quoting characters and escape the string. We use
|
394
|
-
// the quoting character that minimizes the amount of escaping to save
|
395
|
-
// a few additional bytes.
|
396
|
-
|
397
|
-
char quotechar;
|
398
|
-
int singleQuoteCount = countChar(tv, '\'');
|
399
|
-
int doubleQuoteCount = countChar(tv, '"');
|
400
|
-
if (doubleQuoteCount <= singleQuoteCount) {
|
401
|
-
quotechar = '"';
|
402
|
-
} else {
|
403
|
-
quotechar = '\'';
|
404
|
-
}
|
405
|
-
|
406
|
-
tv = quotechar + escapeString(tv, quotechar) + quotechar;
|
407
|
-
|
408
|
-
// String concatenation transforms the old script scheme:
|
409
|
-
// '<scr'+'ipt ...><'+'/script>'
|
410
|
-
// into the following:
|
411
|
-
// '<script ...></script>'
|
412
|
-
// which breaks if this code is embedded inside an HTML document.
|
413
|
-
// Since this is not the right way to do this, let's fix the code by
|
414
|
-
// transforming all "</script" into "<\/script"
|
415
|
-
|
416
|
-
if (tv.indexOf("</script") >= 0) {
|
417
|
-
tv = tv.replaceAll("<\\/script", "<\\\\/script");
|
418
|
-
}
|
419
|
-
|
420
|
-
tokens.set(i, new JavaScriptToken(Token.STRING, tv));
|
421
|
-
}
|
422
|
-
}
|
423
|
-
}
|
424
|
-
|
425
|
-
// Add necessary escaping that was removed in Rhino's tokenizer.
|
426
|
-
private static String escapeString(String s, char quotechar) {
|
427
|
-
|
428
|
-
assert quotechar == '"' || quotechar == '\'';
|
429
|
-
|
430
|
-
if (s == null) {
|
431
|
-
return null;
|
432
|
-
}
|
433
|
-
|
434
|
-
StringBuffer sb = new StringBuffer();
|
435
|
-
for (int i = 0, L = s.length(); i < L; i++) {
|
436
|
-
int c = s.charAt(i);
|
437
|
-
if (c == quotechar) {
|
438
|
-
sb.append("\\");
|
439
|
-
}
|
440
|
-
sb.append((char) c);
|
441
|
-
}
|
442
|
-
|
443
|
-
return sb.toString();
|
444
|
-
}
|
445
|
-
|
446
|
-
/*
|
447
|
-
* Simple check to see whether a string is a valid identifier name.
|
448
|
-
* If a string matches this pattern, it means it IS a valid
|
449
|
-
* identifier name. If a string doesn't match it, it does not
|
450
|
-
* necessarily mean it is not a valid identifier name.
|
451
|
-
*/
|
452
|
-
private static final Pattern SIMPLE_IDENTIFIER_NAME_PATTERN = Pattern.compile("^[a-zA-Z_][a-zA-Z0-9_]*$");
|
453
|
-
|
454
|
-
private static boolean isValidIdentifier(String s) {
|
455
|
-
Matcher m = SIMPLE_IDENTIFIER_NAME_PATTERN.matcher(s);
|
456
|
-
return (m.matches() && !reserved.contains(s));
|
457
|
-
}
|
458
|
-
|
459
|
-
/*
|
460
|
-
* Transforms obj["foo"] into obj.foo whenever possible, saving 3 bytes.
|
461
|
-
*/
|
462
|
-
private static void optimizeObjectMemberAccess(ArrayList tokens) {
|
463
|
-
|
464
|
-
String tv;
|
465
|
-
int i, length;
|
466
|
-
JavaScriptToken token;
|
467
|
-
|
468
|
-
for (i = 0, length = tokens.size(); i < length; i++) {
|
469
|
-
|
470
|
-
if (((JavaScriptToken) tokens.get(i)).getType() == Token.LB &&
|
471
|
-
i > 0 && i < length - 2 &&
|
472
|
-
((JavaScriptToken) tokens.get(i - 1)).getType() == Token.NAME &&
|
473
|
-
((JavaScriptToken) tokens.get(i + 1)).getType() == Token.STRING &&
|
474
|
-
((JavaScriptToken) tokens.get(i + 2)).getType() == Token.RB) {
|
475
|
-
token = (JavaScriptToken) tokens.get(i + 1);
|
476
|
-
tv = token.getValue();
|
477
|
-
tv = tv.substring(1, tv.length() - 1);
|
478
|
-
if (isValidIdentifier(tv)) {
|
479
|
-
tokens.set(i, new JavaScriptToken(Token.DOT, "."));
|
480
|
-
tokens.set(i + 1, new JavaScriptToken(Token.NAME, tv));
|
481
|
-
tokens.remove(i + 2);
|
482
|
-
i = i + 2;
|
483
|
-
length = length - 1;
|
484
|
-
}
|
485
|
-
}
|
486
|
-
}
|
487
|
-
}
|
488
|
-
|
489
|
-
/*
|
490
|
-
* Transforms 'foo': ... into foo: ... whenever possible, saving 2 bytes.
|
491
|
-
*/
|
492
|
-
private static void optimizeObjLitMemberDecl(ArrayList tokens) {
|
493
|
-
|
494
|
-
String tv;
|
495
|
-
int i, length;
|
496
|
-
JavaScriptToken token;
|
497
|
-
|
498
|
-
for (i = 0, length = tokens.size(); i < length; i++) {
|
499
|
-
if (((JavaScriptToken) tokens.get(i)).getType() == Token.OBJECTLIT &&
|
500
|
-
i > 0 && ((JavaScriptToken) tokens.get(i - 1)).getType() == Token.STRING) {
|
501
|
-
token = (JavaScriptToken) tokens.get(i - 1);
|
502
|
-
tv = token.getValue();
|
503
|
-
tv = tv.substring(1, tv.length() - 1);
|
504
|
-
if (isValidIdentifier(tv)) {
|
505
|
-
tokens.set(i - 1, new JavaScriptToken(Token.NAME, tv));
|
506
|
-
}
|
507
|
-
}
|
508
|
-
}
|
509
|
-
}
|
510
|
-
|
511
|
-
private ErrorReporter logger;
|
512
|
-
|
513
|
-
private boolean munge;
|
514
|
-
private boolean verbose;
|
515
|
-
|
516
|
-
private static final int BUILDING_SYMBOL_TREE = 1;
|
517
|
-
private static final int CHECKING_SYMBOL_TREE = 2;
|
518
|
-
|
519
|
-
private int mode;
|
520
|
-
private int offset;
|
521
|
-
private int braceNesting;
|
522
|
-
private ArrayList tokens;
|
523
|
-
private Stack scopes = new Stack();
|
524
|
-
private ScriptOrFnScope globalScope = new ScriptOrFnScope(-1, null);
|
525
|
-
private Hashtable indexedScopes = new Hashtable();
|
526
|
-
|
527
|
-
public JavaScriptCompressor(Reader in, ErrorReporter reporter)
|
528
|
-
throws IOException, EvaluatorException {
|
529
|
-
|
530
|
-
this.logger = reporter;
|
531
|
-
this.tokens = parse(in, reporter);
|
532
|
-
}
|
533
|
-
|
534
|
-
public void compress(Writer out, int linebreak, boolean munge, boolean verbose,
|
535
|
-
boolean preserveAllSemiColons, boolean disableOptimizations)
|
536
|
-
throws IOException {
|
537
|
-
|
538
|
-
this.munge = munge;
|
539
|
-
this.verbose = verbose;
|
540
|
-
|
541
|
-
processStringLiterals(this.tokens, !disableOptimizations);
|
542
|
-
|
543
|
-
if (!disableOptimizations) {
|
544
|
-
optimizeObjectMemberAccess(this.tokens);
|
545
|
-
optimizeObjLitMemberDecl(this.tokens);
|
546
|
-
}
|
547
|
-
|
548
|
-
buildSymbolTree();
|
549
|
-
// DO NOT TOUCH this.tokens BETWEEN THESE TWO PHASES (BECAUSE OF this.indexedScopes)
|
550
|
-
mungeSymboltree();
|
551
|
-
StringBuffer sb = printSymbolTree(linebreak, preserveAllSemiColons);
|
552
|
-
|
553
|
-
out.write(sb.toString());
|
554
|
-
}
|
555
|
-
|
556
|
-
private ScriptOrFnScope getCurrentScope() {
|
557
|
-
return (ScriptOrFnScope) scopes.peek();
|
558
|
-
}
|
559
|
-
|
560
|
-
private void enterScope(ScriptOrFnScope scope) {
|
561
|
-
scopes.push(scope);
|
562
|
-
}
|
563
|
-
|
564
|
-
private void leaveCurrentScope() {
|
565
|
-
scopes.pop();
|
566
|
-
}
|
567
|
-
|
568
|
-
private JavaScriptToken consumeToken() {
|
569
|
-
return (JavaScriptToken) tokens.get(offset++);
|
570
|
-
}
|
571
|
-
|
572
|
-
private JavaScriptToken getToken(int delta) {
|
573
|
-
return (JavaScriptToken) tokens.get(offset + delta);
|
574
|
-
}
|
575
|
-
|
576
|
-
/*
|
577
|
-
* Returns the identifier for the specified symbol defined in
|
578
|
-
* the specified scope or in any scope above it. Returns null
|
579
|
-
* if this symbol does not have a corresponding identifier.
|
580
|
-
*/
|
581
|
-
private JavaScriptIdentifier getIdentifier(String symbol, ScriptOrFnScope scope) {
|
582
|
-
JavaScriptIdentifier identifier;
|
583
|
-
while (scope != null) {
|
584
|
-
identifier = scope.getIdentifier(symbol);
|
585
|
-
if (identifier != null) {
|
586
|
-
return identifier;
|
587
|
-
}
|
588
|
-
scope = scope.getParentScope();
|
589
|
-
}
|
590
|
-
return null;
|
591
|
-
}
|
592
|
-
|
593
|
-
/*
|
594
|
-
* If either 'eval' or 'with' is used in a local scope, we must make
|
595
|
-
* sure that all containing local scopes don't get munged. Otherwise,
|
596
|
-
* the obfuscation would potentially introduce bugs.
|
597
|
-
*/
|
598
|
-
private void protectScopeFromObfuscation(ScriptOrFnScope scope) {
|
599
|
-
assert scope != null;
|
600
|
-
|
601
|
-
if (scope == globalScope) {
|
602
|
-
// The global scope does not get obfuscated,
|
603
|
-
// so we don't need to worry about it...
|
604
|
-
return;
|
605
|
-
}
|
606
|
-
|
607
|
-
// Find the highest local scope containing the specified scope.
|
608
|
-
while (scope.getParentScope() != globalScope) {
|
609
|
-
scope = scope.getParentScope();
|
610
|
-
}
|
611
|
-
|
612
|
-
assert scope.getParentScope() == globalScope;
|
613
|
-
scope.preventMunging();
|
614
|
-
}
|
615
|
-
|
616
|
-
private String getDebugString(int max) {
|
617
|
-
assert max > 0;
|
618
|
-
StringBuffer result = new StringBuffer();
|
619
|
-
int start = Math.max(offset - max, 0);
|
620
|
-
int end = Math.min(offset + max, tokens.size());
|
621
|
-
for (int i = start; i < end; i++) {
|
622
|
-
JavaScriptToken token = (JavaScriptToken) tokens.get(i);
|
623
|
-
if (i == offset - 1) {
|
624
|
-
result.append(" ---> ");
|
625
|
-
}
|
626
|
-
result.append(token.getValue());
|
627
|
-
if (i == offset - 1) {
|
628
|
-
result.append(" <--- ");
|
629
|
-
}
|
630
|
-
}
|
631
|
-
return result.toString();
|
632
|
-
}
|
633
|
-
|
634
|
-
private void warn(String message, boolean showDebugString) {
|
635
|
-
if (verbose) {
|
636
|
-
if (showDebugString) {
|
637
|
-
message = message + "\n" + getDebugString(10);
|
638
|
-
}
|
639
|
-
logger.warning(message, null, -1, null, -1);
|
640
|
-
}
|
641
|
-
}
|
642
|
-
|
643
|
-
private void parseFunctionDeclaration() {
|
644
|
-
|
645
|
-
String symbol;
|
646
|
-
JavaScriptToken token;
|
647
|
-
ScriptOrFnScope currentScope, fnScope;
|
648
|
-
JavaScriptIdentifier identifier;
|
649
|
-
|
650
|
-
currentScope = getCurrentScope();
|
651
|
-
|
652
|
-
token = consumeToken();
|
653
|
-
if (token.getType() == Token.NAME) {
|
654
|
-
if (mode == BUILDING_SYMBOL_TREE) {
|
655
|
-
// Get the name of the function and declare it in the current scope.
|
656
|
-
symbol = token.getValue();
|
657
|
-
if (currentScope.getIdentifier(symbol) != null) {
|
658
|
-
warn("The function " + symbol + " has already been declared in the same scope...", true);
|
659
|
-
}
|
660
|
-
currentScope.declareIdentifier(symbol);
|
661
|
-
}
|
662
|
-
token = consumeToken();
|
663
|
-
}
|
664
|
-
|
665
|
-
assert token.getType() == Token.LP;
|
666
|
-
if (mode == BUILDING_SYMBOL_TREE) {
|
667
|
-
fnScope = new ScriptOrFnScope(braceNesting, currentScope);
|
668
|
-
indexedScopes.put(new Integer(offset), fnScope);
|
669
|
-
} else {
|
670
|
-
fnScope = (ScriptOrFnScope) indexedScopes.get(new Integer(offset));
|
671
|
-
}
|
672
|
-
|
673
|
-
// Parse function arguments.
|
674
|
-
int argpos = 0;
|
675
|
-
while ((token = consumeToken()).getType() != Token.RP) {
|
676
|
-
assert token.getType() == Token.NAME ||
|
677
|
-
token.getType() == Token.COMMA;
|
678
|
-
if (token.getType() == Token.NAME && mode == BUILDING_SYMBOL_TREE) {
|
679
|
-
symbol = token.getValue();
|
680
|
-
identifier = fnScope.declareIdentifier(symbol);
|
681
|
-
if (symbol.equals("$super") && argpos == 0) {
|
682
|
-
// Exception for Prototype 1.6...
|
683
|
-
identifier.preventMunging();
|
684
|
-
}
|
685
|
-
argpos++;
|
686
|
-
}
|
687
|
-
}
|
688
|
-
|
689
|
-
token = consumeToken();
|
690
|
-
assert token.getType() == Token.LC;
|
691
|
-
braceNesting++;
|
692
|
-
|
693
|
-
token = getToken(0);
|
694
|
-
if (token.getType() == Token.STRING &&
|
695
|
-
getToken(1).getType() == Token.SEMI) {
|
696
|
-
// This is a hint. Hints are empty statements that look like
|
697
|
-
// "localvar1:nomunge, localvar2:nomunge"; They allow developers
|
698
|
-
// to prevent specific symbols from getting obfuscated (some heretic
|
699
|
-
// implementations, such as Prototype 1.6, require specific variable
|
700
|
-
// names, such as $super for example, in order to work appropriately.
|
701
|
-
// Note: right now, only "nomunge" is supported in the right hand side
|
702
|
-
// of a hint. However, in the future, the right hand side may contain
|
703
|
-
// other values.
|
704
|
-
consumeToken();
|
705
|
-
String hints = token.getValue();
|
706
|
-
// Remove the leading and trailing quotes...
|
707
|
-
hints = hints.substring(1, hints.length() - 1).trim();
|
708
|
-
StringTokenizer st1 = new StringTokenizer(hints, ",");
|
709
|
-
while (st1.hasMoreTokens()) {
|
710
|
-
String hint = st1.nextToken();
|
711
|
-
int idx = hint.indexOf(':');
|
712
|
-
if (idx <= 0 || idx >= hint.length() - 1) {
|
713
|
-
if (mode == BUILDING_SYMBOL_TREE) {
|
714
|
-
// No need to report the error twice, hence the test...
|
715
|
-
warn("Invalid hint syntax: " + hint, true);
|
716
|
-
}
|
717
|
-
break;
|
718
|
-
}
|
719
|
-
String variableName = hint.substring(0, idx).trim();
|
720
|
-
String variableType = hint.substring(idx + 1).trim();
|
721
|
-
if (mode == BUILDING_SYMBOL_TREE) {
|
722
|
-
fnScope.addHint(variableName, variableType);
|
723
|
-
} else if (mode == CHECKING_SYMBOL_TREE) {
|
724
|
-
identifier = fnScope.getIdentifier(variableName);
|
725
|
-
if (identifier != null) {
|
726
|
-
if (variableType.equals("nomunge")) {
|
727
|
-
identifier.preventMunging();
|
728
|
-
} else {
|
729
|
-
warn("Unsupported hint value: " + hint, true);
|
730
|
-
}
|
731
|
-
} else {
|
732
|
-
warn("Hint refers to an unknown identifier: " + hint, true);
|
733
|
-
}
|
734
|
-
}
|
735
|
-
}
|
736
|
-
}
|
737
|
-
|
738
|
-
parseScope(fnScope);
|
739
|
-
}
|
740
|
-
|
741
|
-
private void parseCatch() {
|
742
|
-
|
743
|
-
String symbol;
|
744
|
-
JavaScriptToken token;
|
745
|
-
ScriptOrFnScope currentScope;
|
746
|
-
JavaScriptIdentifier identifier;
|
747
|
-
|
748
|
-
token = getToken(-1);
|
749
|
-
assert token.getType() == Token.CATCH;
|
750
|
-
token = consumeToken();
|
751
|
-
assert token.getType() == Token.LP;
|
752
|
-
token = consumeToken();
|
753
|
-
assert token.getType() == Token.NAME;
|
754
|
-
|
755
|
-
symbol = token.getValue();
|
756
|
-
currentScope = getCurrentScope();
|
757
|
-
|
758
|
-
if (mode == BUILDING_SYMBOL_TREE) {
|
759
|
-
// We must declare the exception identifier in the containing function
|
760
|
-
// scope to avoid errors related to the obfuscation process. No need to
|
761
|
-
// display a warning if the symbol was already declared here...
|
762
|
-
currentScope.declareIdentifier(symbol);
|
763
|
-
} else {
|
764
|
-
identifier = getIdentifier(symbol, currentScope);
|
765
|
-
identifier.incrementRefcount();
|
766
|
-
}
|
767
|
-
|
768
|
-
token = consumeToken();
|
769
|
-
assert token.getType() == Token.RP;
|
770
|
-
}
|
771
|
-
|
772
|
-
private void parseExpression() {
|
773
|
-
|
774
|
-
// Parse the expression until we encounter a comma or a semi-colon
|
775
|
-
// in the same brace nesting, bracket nesting and paren nesting.
|
776
|
-
// Parse functions if any...
|
777
|
-
|
778
|
-
String symbol;
|
779
|
-
JavaScriptToken token;
|
780
|
-
ScriptOrFnScope currentScope;
|
781
|
-
JavaScriptIdentifier identifier;
|
782
|
-
|
783
|
-
int expressionBraceNesting = braceNesting;
|
784
|
-
int bracketNesting = 0;
|
785
|
-
int parensNesting = 0;
|
786
|
-
|
787
|
-
int length = tokens.size();
|
788
|
-
|
789
|
-
while (offset < length) {
|
790
|
-
|
791
|
-
token = consumeToken();
|
792
|
-
currentScope = getCurrentScope();
|
793
|
-
|
794
|
-
switch (token.getType()) {
|
795
|
-
|
796
|
-
case Token.SEMI:
|
797
|
-
case Token.COMMA:
|
798
|
-
if (braceNesting == expressionBraceNesting &&
|
799
|
-
bracketNesting == 0 &&
|
800
|
-
parensNesting == 0) {
|
801
|
-
return;
|
802
|
-
}
|
803
|
-
break;
|
804
|
-
|
805
|
-
case Token.FUNCTION:
|
806
|
-
parseFunctionDeclaration();
|
807
|
-
break;
|
808
|
-
|
809
|
-
case Token.LC:
|
810
|
-
braceNesting++;
|
811
|
-
break;
|
812
|
-
|
813
|
-
case Token.RC:
|
814
|
-
braceNesting--;
|
815
|
-
assert braceNesting >= expressionBraceNesting;
|
816
|
-
break;
|
817
|
-
|
818
|
-
case Token.LB:
|
819
|
-
bracketNesting++;
|
820
|
-
break;
|
821
|
-
|
822
|
-
case Token.RB:
|
823
|
-
bracketNesting--;
|
824
|
-
break;
|
825
|
-
|
826
|
-
case Token.LP:
|
827
|
-
parensNesting++;
|
828
|
-
break;
|
829
|
-
|
830
|
-
case Token.RP:
|
831
|
-
parensNesting--;
|
832
|
-
break;
|
833
|
-
|
834
|
-
case Token.NAME:
|
835
|
-
symbol = token.getValue();
|
836
|
-
|
837
|
-
if (mode == BUILDING_SYMBOL_TREE) {
|
838
|
-
|
839
|
-
if (symbol.equals("eval")) {
|
840
|
-
|
841
|
-
protectScopeFromObfuscation(currentScope);
|
842
|
-
warn("Using 'eval' is not recommended." + (munge ? " Moreover, using 'eval' reduces the level of compression!" : ""), true);
|
843
|
-
|
844
|
-
}
|
845
|
-
|
846
|
-
} else if (mode == CHECKING_SYMBOL_TREE) {
|
847
|
-
|
848
|
-
if ((offset < 2 ||
|
849
|
-
(getToken(-2).getType() != Token.DOT &&
|
850
|
-
getToken(-2).getType() != Token.GET &&
|
851
|
-
getToken(-2).getType() != Token.SET)) &&
|
852
|
-
getToken(0).getType() != Token.OBJECTLIT) {
|
853
|
-
|
854
|
-
identifier = getIdentifier(symbol, currentScope);
|
855
|
-
|
856
|
-
if (identifier == null) {
|
857
|
-
|
858
|
-
if (symbol.length() <= 3 && !builtin.contains(symbol)) {
|
859
|
-
// Here, we found an undeclared and un-namespaced symbol that is
|
860
|
-
// 3 characters or less in length. Declare it in the global scope.
|
861
|
-
// We don't need to declare longer symbols since they won't cause
|
862
|
-
// any conflict with other munged symbols.
|
863
|
-
globalScope.declareIdentifier(symbol);
|
864
|
-
warn("Found an undeclared symbol: " + symbol, true);
|
865
|
-
}
|
866
|
-
|
867
|
-
} else {
|
868
|
-
|
869
|
-
identifier.incrementRefcount();
|
870
|
-
}
|
871
|
-
}
|
872
|
-
}
|
873
|
-
break;
|
874
|
-
}
|
875
|
-
}
|
876
|
-
}
|
877
|
-
|
878
|
-
private void parseScope(ScriptOrFnScope scope) {
|
879
|
-
|
880
|
-
String symbol;
|
881
|
-
JavaScriptToken token;
|
882
|
-
JavaScriptIdentifier identifier;
|
883
|
-
|
884
|
-
int length = tokens.size();
|
885
|
-
|
886
|
-
enterScope(scope);
|
887
|
-
|
888
|
-
while (offset < length) {
|
889
|
-
|
890
|
-
token = consumeToken();
|
891
|
-
|
892
|
-
switch (token.getType()) {
|
893
|
-
|
894
|
-
case Token.VAR:
|
895
|
-
|
896
|
-
if (mode == BUILDING_SYMBOL_TREE && scope.incrementVarCount() > 1) {
|
897
|
-
warn("Try to use a single 'var' statement per scope.", true);
|
898
|
-
}
|
899
|
-
|
900
|
-
/* FALLSTHROUGH */
|
901
|
-
|
902
|
-
case Token.CONST:
|
903
|
-
|
904
|
-
// The var keyword is followed by at least one symbol name.
|
905
|
-
// If several symbols follow, they are comma separated.
|
906
|
-
for (; ;) {
|
907
|
-
token = consumeToken();
|
908
|
-
|
909
|
-
assert token.getType() == Token.NAME;
|
910
|
-
|
911
|
-
if (mode == BUILDING_SYMBOL_TREE) {
|
912
|
-
symbol = token.getValue();
|
913
|
-
if (scope.getIdentifier(symbol) == null) {
|
914
|
-
scope.declareIdentifier(symbol);
|
915
|
-
} else {
|
916
|
-
warn("The variable " + symbol + " has already been declared in the same scope...", true);
|
917
|
-
}
|
918
|
-
}
|
919
|
-
|
920
|
-
token = getToken(0);
|
921
|
-
|
922
|
-
assert token.getType() == Token.SEMI ||
|
923
|
-
token.getType() == Token.ASSIGN ||
|
924
|
-
token.getType() == Token.COMMA ||
|
925
|
-
token.getType() == Token.IN;
|
926
|
-
|
927
|
-
if (token.getType() == Token.IN) {
|
928
|
-
break;
|
929
|
-
} else {
|
930
|
-
parseExpression();
|
931
|
-
token = getToken(-1);
|
932
|
-
if (token.getType() == Token.SEMI) {
|
933
|
-
break;
|
934
|
-
}
|
935
|
-
}
|
936
|
-
}
|
937
|
-
break;
|
938
|
-
|
939
|
-
case Token.FUNCTION:
|
940
|
-
parseFunctionDeclaration();
|
941
|
-
break;
|
942
|
-
|
943
|
-
case Token.LC:
|
944
|
-
braceNesting++;
|
945
|
-
break;
|
946
|
-
|
947
|
-
case Token.RC:
|
948
|
-
braceNesting--;
|
949
|
-
assert braceNesting >= scope.getBraceNesting();
|
950
|
-
if (braceNesting == scope.getBraceNesting()) {
|
951
|
-
leaveCurrentScope();
|
952
|
-
return;
|
953
|
-
}
|
954
|
-
break;
|
955
|
-
|
956
|
-
case Token.WITH:
|
957
|
-
if (mode == BUILDING_SYMBOL_TREE) {
|
958
|
-
// Inside a 'with' block, it is impossible to figure out
|
959
|
-
// statically whether a symbol is a local variable or an
|
960
|
-
// object member. As a consequence, the only thing we can
|
961
|
-
// do is turn the obfuscation off for the highest scope
|
962
|
-
// containing the 'with' block.
|
963
|
-
protectScopeFromObfuscation(scope);
|
964
|
-
warn("Using 'with' is not recommended." + (munge ? " Moreover, using 'with' reduces the level of compression!" : ""), true);
|
965
|
-
}
|
966
|
-
break;
|
967
|
-
|
968
|
-
case Token.CATCH:
|
969
|
-
parseCatch();
|
970
|
-
break;
|
971
|
-
|
972
|
-
case Token.NAME:
|
973
|
-
symbol = token.getValue();
|
974
|
-
|
975
|
-
if (mode == BUILDING_SYMBOL_TREE) {
|
976
|
-
|
977
|
-
if (symbol.equals("eval")) {
|
978
|
-
|
979
|
-
protectScopeFromObfuscation(scope);
|
980
|
-
warn("Using 'eval' is not recommended." + (munge ? " Moreover, using 'eval' reduces the level of compression!" : ""), true);
|
981
|
-
|
982
|
-
}
|
983
|
-
|
984
|
-
} else if (mode == CHECKING_SYMBOL_TREE) {
|
985
|
-
|
986
|
-
if ((offset < 2 || getToken(-2).getType() != Token.DOT) &&
|
987
|
-
getToken(0).getType() != Token.OBJECTLIT) {
|
988
|
-
|
989
|
-
identifier = getIdentifier(symbol, scope);
|
990
|
-
|
991
|
-
if (identifier == null) {
|
992
|
-
|
993
|
-
if (symbol.length() <= 3 && !builtin.contains(symbol)) {
|
994
|
-
// Here, we found an undeclared and un-namespaced symbol that is
|
995
|
-
// 3 characters or less in length. Declare it in the global scope.
|
996
|
-
// We don't need to declare longer symbols since they won't cause
|
997
|
-
// any conflict with other munged symbols.
|
998
|
-
globalScope.declareIdentifier(symbol);
|
999
|
-
warn("Found an undeclared symbol: " + symbol, true);
|
1000
|
-
}
|
1001
|
-
|
1002
|
-
} else {
|
1003
|
-
|
1004
|
-
identifier.incrementRefcount();
|
1005
|
-
}
|
1006
|
-
}
|
1007
|
-
}
|
1008
|
-
break;
|
1009
|
-
}
|
1010
|
-
}
|
1011
|
-
}
|
1012
|
-
|
1013
|
-
private void buildSymbolTree() {
|
1014
|
-
offset = 0;
|
1015
|
-
braceNesting = 0;
|
1016
|
-
scopes.clear();
|
1017
|
-
indexedScopes.clear();
|
1018
|
-
indexedScopes.put(new Integer(0), globalScope);
|
1019
|
-
mode = BUILDING_SYMBOL_TREE;
|
1020
|
-
parseScope(globalScope);
|
1021
|
-
}
|
1022
|
-
|
1023
|
-
private void mungeSymboltree() {
|
1024
|
-
|
1025
|
-
if (!munge) {
|
1026
|
-
return;
|
1027
|
-
}
|
1028
|
-
|
1029
|
-
// One problem with obfuscation resides in the use of undeclared
|
1030
|
-
// and un-namespaced global symbols that are 3 characters or less
|
1031
|
-
// in length. Here is an example:
|
1032
|
-
//
|
1033
|
-
// var declaredGlobalVar;
|
1034
|
-
//
|
1035
|
-
// function declaredGlobalFn() {
|
1036
|
-
// var localvar;
|
1037
|
-
// localvar = abc; // abc is an undeclared global symbol
|
1038
|
-
// }
|
1039
|
-
//
|
1040
|
-
// In the example above, there is a slim chance that localvar may be
|
1041
|
-
// munged to 'abc', conflicting with the undeclared global symbol
|
1042
|
-
// abc, creating a potential bug. The following code detects such
|
1043
|
-
// global symbols. This must be done AFTER the entire file has been
|
1044
|
-
// parsed, and BEFORE munging the symbol tree. Note that declaring
|
1045
|
-
// extra symbols in the global scope won't hurt.
|
1046
|
-
//
|
1047
|
-
// Note: Since we go through all the tokens to do this, we also use
|
1048
|
-
// the opportunity to count how many times each identifier is used.
|
1049
|
-
|
1050
|
-
offset = 0;
|
1051
|
-
braceNesting = 0;
|
1052
|
-
scopes.clear();
|
1053
|
-
mode = CHECKING_SYMBOL_TREE;
|
1054
|
-
parseScope(globalScope);
|
1055
|
-
globalScope.munge();
|
1056
|
-
}
|
1057
|
-
|
1058
|
-
private StringBuffer printSymbolTree(int linebreakpos, boolean preserveAllSemiColons)
|
1059
|
-
throws IOException {
|
1060
|
-
|
1061
|
-
offset = 0;
|
1062
|
-
braceNesting = 0;
|
1063
|
-
scopes.clear();
|
1064
|
-
|
1065
|
-
String symbol;
|
1066
|
-
JavaScriptToken token;
|
1067
|
-
ScriptOrFnScope currentScope;
|
1068
|
-
JavaScriptIdentifier identifier;
|
1069
|
-
|
1070
|
-
int length = tokens.size();
|
1071
|
-
StringBuffer result = new StringBuffer();
|
1072
|
-
|
1073
|
-
int linestartpos = 0;
|
1074
|
-
|
1075
|
-
enterScope(globalScope);
|
1076
|
-
|
1077
|
-
while (offset < length) {
|
1078
|
-
|
1079
|
-
token = consumeToken();
|
1080
|
-
symbol = token.getValue();
|
1081
|
-
currentScope = getCurrentScope();
|
1082
|
-
|
1083
|
-
switch (token.getType()) {
|
1084
|
-
|
1085
|
-
case Token.NAME:
|
1086
|
-
|
1087
|
-
if (offset >= 2 && getToken(-2).getType() == Token.DOT ||
|
1088
|
-
getToken(0).getType() == Token.OBJECTLIT) {
|
1089
|
-
|
1090
|
-
result.append(symbol);
|
1091
|
-
|
1092
|
-
} else {
|
1093
|
-
|
1094
|
-
identifier = getIdentifier(symbol, currentScope);
|
1095
|
-
if (identifier != null) {
|
1096
|
-
if (identifier.getMungedValue() != null) {
|
1097
|
-
result.append(identifier.getMungedValue());
|
1098
|
-
} else {
|
1099
|
-
result.append(symbol);
|
1100
|
-
}
|
1101
|
-
if (currentScope != globalScope && identifier.getRefcount() == 0) {
|
1102
|
-
warn("The symbol " + symbol + " is declared but is apparently never used.\nThis code can probably be written in a more compact way.", true);
|
1103
|
-
}
|
1104
|
-
} else {
|
1105
|
-
result.append(symbol);
|
1106
|
-
}
|
1107
|
-
}
|
1108
|
-
break;
|
1109
|
-
|
1110
|
-
case Token.REGEXP:
|
1111
|
-
case Token.NUMBER:
|
1112
|
-
case Token.STRING:
|
1113
|
-
result.append(symbol);
|
1114
|
-
break;
|
1115
|
-
|
1116
|
-
case Token.ADD:
|
1117
|
-
case Token.SUB:
|
1118
|
-
result.append((String) literals.get(new Integer(token.getType())));
|
1119
|
-
if (offset < length) {
|
1120
|
-
token = getToken(0);
|
1121
|
-
if (token.getType() == Token.INC ||
|
1122
|
-
token.getType() == Token.DEC ||
|
1123
|
-
token.getType() == Token.ADD ||
|
1124
|
-
token.getType() == Token.DEC) {
|
1125
|
-
// Handle the case x +/- ++/-- y
|
1126
|
-
// We must keep a white space here. Otherwise, x +++ y would be
|
1127
|
-
// interpreted as x ++ + y by the compiler, which is a bug (due
|
1128
|
-
// to the implicit assignment being done on the wrong variable)
|
1129
|
-
result.append(' ');
|
1130
|
-
} else if (token.getType() == Token.POS && getToken(-1).getType() == Token.ADD ||
|
1131
|
-
token.getType() == Token.NEG && getToken(-1).getType() == Token.SUB) {
|
1132
|
-
// Handle the case x + + y and x - - y
|
1133
|
-
result.append(' ');
|
1134
|
-
}
|
1135
|
-
}
|
1136
|
-
break;
|
1137
|
-
|
1138
|
-
case Token.FUNCTION:
|
1139
|
-
result.append("function");
|
1140
|
-
token = consumeToken();
|
1141
|
-
if (token.getType() == Token.NAME) {
|
1142
|
-
result.append(' ');
|
1143
|
-
symbol = token.getValue();
|
1144
|
-
identifier = getIdentifier(symbol, currentScope);
|
1145
|
-
assert identifier != null;
|
1146
|
-
if (identifier.getMungedValue() != null) {
|
1147
|
-
result.append(identifier.getMungedValue());
|
1148
|
-
} else {
|
1149
|
-
result.append(symbol);
|
1150
|
-
}
|
1151
|
-
if (currentScope != globalScope && identifier.getRefcount() == 0) {
|
1152
|
-
warn("The symbol " + symbol + " is declared but is apparently never used.\nThis code can probably be written in a more compact way.", true);
|
1153
|
-
}
|
1154
|
-
token = consumeToken();
|
1155
|
-
}
|
1156
|
-
assert token.getType() == Token.LP;
|
1157
|
-
result.append('(');
|
1158
|
-
currentScope = (ScriptOrFnScope) indexedScopes.get(new Integer(offset));
|
1159
|
-
enterScope(currentScope);
|
1160
|
-
while ((token = consumeToken()).getType() != Token.RP) {
|
1161
|
-
assert token.getType() == Token.NAME || token.getType() == Token.COMMA;
|
1162
|
-
if (token.getType() == Token.NAME) {
|
1163
|
-
symbol = token.getValue();
|
1164
|
-
identifier = getIdentifier(symbol, currentScope);
|
1165
|
-
assert identifier != null;
|
1166
|
-
if (identifier.getMungedValue() != null) {
|
1167
|
-
result.append(identifier.getMungedValue());
|
1168
|
-
} else {
|
1169
|
-
result.append(symbol);
|
1170
|
-
}
|
1171
|
-
} else if (token.getType() == Token.COMMA) {
|
1172
|
-
result.append(',');
|
1173
|
-
}
|
1174
|
-
}
|
1175
|
-
result.append(')');
|
1176
|
-
token = consumeToken();
|
1177
|
-
assert token.getType() == Token.LC;
|
1178
|
-
result.append('{');
|
1179
|
-
braceNesting++;
|
1180
|
-
token = getToken(0);
|
1181
|
-
if (token.getType() == Token.STRING &&
|
1182
|
-
getToken(1).getType() == Token.SEMI) {
|
1183
|
-
// This is a hint. Skip it!
|
1184
|
-
consumeToken();
|
1185
|
-
consumeToken();
|
1186
|
-
}
|
1187
|
-
break;
|
1188
|
-
|
1189
|
-
case Token.RETURN:
|
1190
|
-
case Token.TYPEOF:
|
1191
|
-
result.append(literals.get(new Integer(token.getType())));
|
1192
|
-
// No space needed after 'return' and 'typeof' when followed
|
1193
|
-
// by '(', '[', '{', a string or a regexp.
|
1194
|
-
if (offset < length) {
|
1195
|
-
token = getToken(0);
|
1196
|
-
if (token.getType() != Token.LP &&
|
1197
|
-
token.getType() != Token.LB &&
|
1198
|
-
token.getType() != Token.LC &&
|
1199
|
-
token.getType() != Token.STRING &&
|
1200
|
-
token.getType() != Token.REGEXP &&
|
1201
|
-
token.getType() != Token.SEMI) {
|
1202
|
-
result.append(' ');
|
1203
|
-
}
|
1204
|
-
}
|
1205
|
-
break;
|
1206
|
-
|
1207
|
-
case Token.CASE:
|
1208
|
-
case Token.THROW:
|
1209
|
-
result.append(literals.get(new Integer(token.getType())));
|
1210
|
-
// White-space needed after 'case' and 'throw' when not followed by a string.
|
1211
|
-
if (offset < length && getToken(0).getType() != Token.STRING) {
|
1212
|
-
result.append(' ');
|
1213
|
-
}
|
1214
|
-
break;
|
1215
|
-
|
1216
|
-
case Token.BREAK:
|
1217
|
-
case Token.CONTINUE:
|
1218
|
-
result.append(literals.get(new Integer(token.getType())));
|
1219
|
-
if (offset < length && getToken(0).getType() != Token.SEMI) {
|
1220
|
-
// If 'break' or 'continue' is not followed by a semi-colon, it must
|
1221
|
-
// be followed by a label, hence the need for a white space.
|
1222
|
-
result.append(' ');
|
1223
|
-
}
|
1224
|
-
break;
|
1225
|
-
|
1226
|
-
case Token.LC:
|
1227
|
-
result.append('{');
|
1228
|
-
braceNesting++;
|
1229
|
-
break;
|
1230
|
-
|
1231
|
-
case Token.RC:
|
1232
|
-
result.append('}');
|
1233
|
-
braceNesting--;
|
1234
|
-
assert braceNesting >= currentScope.getBraceNesting();
|
1235
|
-
if (braceNesting == currentScope.getBraceNesting()) {
|
1236
|
-
leaveCurrentScope();
|
1237
|
-
}
|
1238
|
-
break;
|
1239
|
-
|
1240
|
-
case Token.SEMI:
|
1241
|
-
// No need to output a semi-colon if the next character is a right-curly...
|
1242
|
-
if (preserveAllSemiColons || offset < length && getToken(0).getType() != Token.RC) {
|
1243
|
-
result.append(';');
|
1244
|
-
}
|
1245
|
-
|
1246
|
-
if (linebreakpos >= 0 && result.length() - linestartpos > linebreakpos) {
|
1247
|
-
// Some source control tools don't like it when files containing lines longer
|
1248
|
-
// than, say 8000 characters, are checked in. The linebreak option is used in
|
1249
|
-
// that case to split long lines after a specific column.
|
1250
|
-
result.append('\n');
|
1251
|
-
linestartpos = result.length();
|
1252
|
-
}
|
1253
|
-
break;
|
1254
|
-
|
1255
|
-
default:
|
1256
|
-
String literal = (String) literals.get(new Integer(token.getType()));
|
1257
|
-
if (literal != null) {
|
1258
|
-
result.append(literal);
|
1259
|
-
} else {
|
1260
|
-
warn("This symbol cannot be printed: " + symbol, true);
|
1261
|
-
}
|
1262
|
-
break;
|
1263
|
-
}
|
1264
|
-
}
|
1265
|
-
|
1266
|
-
// Append a semi-colon at the end, even if unnecessary semi-colons are
|
1267
|
-
// supposed to be removed. This is especially useful when concatenating
|
1268
|
-
// several minified files (the absence of an ending semi-colon at the
|
1269
|
-
// end of one file may very likely cause a syntax error)
|
1270
|
-
if (!preserveAllSemiColons &&
|
1271
|
-
result.length() > 0) {
|
1272
|
-
if (result.charAt(result.length() - 1) == '\n') {
|
1273
|
-
result.setCharAt(result.length() - 1, ';');
|
1274
|
-
} else {
|
1275
|
-
result.append(';');
|
1276
|
-
}
|
1277
|
-
}
|
1278
|
-
|
1279
|
-
return result;
|
1280
|
-
}
|
1281
|
-
}
|