1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.dojotoolkit.shrinksafe;
28
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34
35 import org.mozilla.javascript.ScriptOrFnNode;
36 import org.mozilla.javascript.ScriptRuntime;
37 import org.mozilla.javascript.Token;
38
39 public class TokenMapper {
40 private List functionBracePositions = new ArrayList();
41
42
43
44
45 private List replacedTokens = new ArrayList();
46
47
48
49
50
51
52 private List functionVarMappings = new ArrayList();
53 private Map debugDataList = new HashMap();
54
55 private int functionNum = 0;
56
57 private int parentScope = 0;
58
59 private int lastTokenCount = 0;
60
61 public TokenMapper(ScriptOrFnNode parseTree) {
62 collectFunctionMappings(parseTree);
63 }
64
65 public void incrementFunctionNumber() {
66 functionNum++;
67 }
68
69
70
71
72
73
74
75
76
77
78
79 private String getMappedToken(String token, boolean hasNewMapping) {
80 String newToken = null;
81 Map tokens = null;
82 String blank = new String("");
83 int localScope = functionBracePositions.size() - 1;
84
85 String oldToken = getPreviousTokenMapping(token, hasNewMapping);
86
87 if (!oldToken.equalsIgnoreCase(blank)) {
88 return oldToken;
89 } else if ((hasNewMapping || isInScopeChain(token))) {
90 newToken = new String("_" + Integer.toHexString(++lastTokenCount));
91 if (token.equals("$super") || (newToken.length() >= token.length() && token.charAt(0) != '_')) {
92 newToken = token;
93 lastTokenCount--;
94 }
95 tokens = (Map) replacedTokens.get(hasNewMapping ? localScope : parentScope);
96 tokens.put(token, newToken);
97 return newToken;
98 }
99 return token;
100 }
101
102
103
104
105
106
107
108
109
110
111 private boolean isInScopeChain(String token) {
112 int scope = functionBracePositions.size();
113 Map chainedScopeVars = (Map) functionVarMappings.get(functionNum);
114 if (!chainedScopeVars.isEmpty()) {
115 for (int i = scope; i > 0; i--) {
116 if (chainedScopeVars.containsKey(new Integer(i))) {
117 parentScope = i - 1;
118 List temp = Arrays.asList((String[]) chainedScopeVars.get(new Integer(i)));
119 if (temp.indexOf(token) != -1) {
120 return true;
121 }
122 }
123 }
124 }
125 return false;
126 }
127
128
129
130
131
132
133
134
135
136
137
138 private String getPreviousTokenMapping(String token, boolean hasNewMapping) {
139 String result = new String("");
140 int scope = replacedTokens.size() - 1;
141
142 if (scope < 0) {
143 return result;
144 }
145
146 if (hasNewMapping) {
147 Map tokens = (Map) (replacedTokens.get(scope));
148 if (tokens.containsKey(token)) {
149 result = (String) tokens.get(token);
150 return result;
151 }
152 } else {
153 for (int i = scope; i > -1; i--) {
154 Map tokens = (Map) (replacedTokens.get(i));
155 if (tokens.containsKey(token)) {
156 result = (String) tokens.get(token);
157 return result;
158 }
159 }
160 }
161 return result;
162 }
163
164
165
166
167
168
169
170
171
172
173 private void collectFunctionMappings(ScriptOrFnNode parseTree) {
174 int level = -1;
175 collectFuncNodes(parseTree, level, null);
176 }
177
178
179
180
181
182
183
184
185
186
187
188 private void collectFuncNodes(ScriptOrFnNode parseTree, int level, ScriptOrFnNode parent) {
189 level++;
190
191 DebugData debugData = new DebugData();
192 debugData.start = parseTree.getBaseLineno();
193 debugData.end = parseTree.getEndLineno();
194 debugData.paramAndVarNames = parseTree.getParamAndVarNames();
195 debugDataList.put(new Integer(parseTree.getEncodedSourceStart()), debugData);
196
197 functionVarMappings.add(new HashMap());
198
199 Map bindingNames = (Map) functionVarMappings.get(functionVarMappings.size() - 1);
200 bindingNames.put(new Integer(level), parseTree.getParamAndVarNames());
201
202 if (parent != null) {
203 bindingNames.put(new Integer(level-1), parent.getParamAndVarNames());
204 }
205
206 int nestedCount = parseTree.getFunctionCount();
207 for (int i = 0; i != nestedCount; ++i) {
208 collectFuncNodes(parseTree.getFunctionNode(i), level, parseTree);
209 bindingNames = (Map) functionVarMappings.get(functionVarMappings.size() - 1);
210 bindingNames.put(new Integer(level), parseTree.getParamAndVarNames());
211 }
212 }
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237 public int sourceCompress(String encodedSource, int offset,
238 boolean asQuotedString, StringBuffer sb, int prevToken,
239 boolean inArgsList, int currentLevel, ReplacedTokens replacedTokens) {
240
241 boolean hasNewMapping = false;
242
243 int length = encodedSource.charAt(offset);
244 ++offset;
245 if ((0x8000 & length) != 0) {
246 length = ((0x7FFF & length) << 16) | encodedSource.charAt(offset);
247 ++offset;
248 }
249 String str = encodedSource.substring(offset, offset + length);
250 if ((prevToken == Token.VAR) || (inArgsList)) {
251 hasNewMapping = true;
252 }
253 if (sb != null) {
254 String sourceStr = new String(str);
255
256 if (((functionBracePositions.size() > 0) &&
257 (currentLevel >= (((Integer) functionBracePositions.get(functionBracePositions.size() - 1)).intValue()))) ||
258 (inArgsList)) {
259 if (prevToken != Token.DOT) {
260
261 str = replacedTokens.find(str);
262 }
263 }
264 if ((!inArgsList) && (asQuotedString)) {
265 if ((prevToken == Token.LC) || (prevToken == Token.COMMA)) {
266 str = sourceStr;
267 }
268 }
269 if (!asQuotedString) {
270 sb.append(str);
271 } else {
272 sb.append('"');
273 sb.append(ScriptRuntime.escapeString(str));
274 sb.append('"');
275 }
276 }
277 else if (((functionBracePositions.size() > 0) &&
278 (currentLevel >= (((Integer) functionBracePositions.get(functionBracePositions.size() - 1)).intValue()))) ||
279 (inArgsList)) {
280 if (prevToken != Token.DOT) {
281 getMappedToken(str, hasNewMapping);
282 }
283 }
284 return offset + length;
285 }
286
287 public void enterNestingLevel(int braceNesting) {
288 functionBracePositions.add(new Integer(braceNesting + 1));
289 replacedTokens.add(new HashMap());
290 }
291
292 public boolean leaveNestingLevel(int braceNesting) {
293 boolean tokensRemoved = false;
294 Integer bn = new Integer(braceNesting);
295
296 if ((functionBracePositions.contains(bn)) && (replacedTokens.size() > 0)) {
297
298 int scopedSize = replacedTokens.size();
299 replacedTokens.remove(scopedSize - 1);
300 functionBracePositions.remove(bn);
301 tokensRemoved = true;
302 }
303 return tokensRemoved;
304 }
305
306 public Map getCurrentTokens() {
307 Map m = null;
308 if (replacedTokens.size() > 0) {
309 m = (Map)replacedTokens.get(replacedTokens.size() - 1);
310 }
311 return m;
312 }
313
314 public DebugData getDebugData(Integer functionPosition) {
315 return (DebugData)debugDataList.get(functionPosition);
316 }
317
318 public void reset() {
319 functionNum = 0;
320 parentScope = 0;
321 lastTokenCount = 0;
322 functionBracePositions = new ArrayList();
323 replacedTokens = new ArrayList();
324 }
325 }