JSDOC/ScopeParser.vala
[gnome.introspection-doc-generator] / JSDOC / ScopeParser.vala
1  
2
3
4 namespace JSDOC {
5
6         public enum ScopeParserMode {
7                 BUILDING_SYMBOL_TREE,
8                 PASS2_SYMBOL_TREE
9         }
10
11
12         public class ScopeParser : Object {
13         
14         TokenStream ts;
15         Gee.ArrayList<string> warnings;
16         
17         bool debug = false;
18         string[] idents;
19         
20         
21     Scope global ;
22     ScopeParserMode mode;
23     //braceNesting : 0,
24     Gee.HashMap<int,Scope> indexedScopes;
25     bool munge =  true;
26
27         
28         
29         public ScopeParser(TokenStream ts) {
30                 this.ts = ts; // {TokenStream}
31                 this.warnings = new Gee.ArrayList<string>();
32                 this.globalScope = new  Scope(-1, false, -1, '');
33                 this.indexedScopes = new Gee.HashMap<int,Scope>();
34                 
35                 //this.indexedg = {};
36                 //this.timer = new Date() * 1;
37                 this.idents = { 
38                 
39                         "break",         
40                     "case",              
41                     "continue", 
42                     "default",  
43                     "delete",   
44                     "do",                
45                         "else",         
46                         "export",       
47                         "false",        
48                         "for",          
49                         "function",     
50                         "if",           
51                         "import",       
52                         "in",           
53                         "new",          
54                         "null",         
55                         "return",       
56                         "switch",       
57                         "this",         
58                         "true",         
59                         "typeof",       
60                         "var",          
61                         "void",         
62                         "while",        
63                         "with",         
64
65                         "catch",        
66                         "class",        
67                         "const",        
68                         "debugger",     
69                         "enum",         
70                         "extends",      
71                         "finally",      
72                         "super",        
73                     "throw",     
74                     "try",              
75
76                     "abstract", 
77                     "boolean",  
78                     "byte",             
79                     "char",             
80                     "double",   
81                     "final",    
82                     "float",    
83                     "goto",             
84                     "implements", 
85                     "instanceof",
86                     "int",               
87                     "interface",         
88                     "long",              
89                     "native",   
90                     "package",  
91                     "private",  
92                     "protected",         
93                     "public",    
94                     "short",    
95                     "static",   
96                     "synchronized",      
97                     "throws",    
98                     "transient",         
99                         "include",       
100                         "undefined"
101                 };
102         }
103
104  
105     
106     
107     void warn(string s) 
108     {
109         //print('****************' + s);
110         this.warnings.add(s);
111         //println("WARNING:" + htmlescape(s) + "<BR>");
112     }
113     
114     
115     // defaults should not be initialized here =- otherwise they get duped on new, rather than initalized..
116     
117   
118
119
120
121
122     void buildSymbolTree()
123     {
124         //println("<PRE>");
125         
126         this.ts.rewind();
127         this.braceNesting = 0;
128         
129        // print(JSON.stringify(this.ts.tokens, null,4));
130         
131         
132         this.globalScope = new  Scope(-1, false, -1, '');
133         this.indexedScopes = new Gee.HashMap<int,Scope>();
134         this.indexedScopes.set(0, this.globalScope );
135         
136         this.mode = ScopeParserMode.BUILDING_SYMBOL_TREE;
137         
138         this.parseScope(this.globalScope);
139         
140         //print("---------------END PASS 1 ---------------- ");
141         
142     }
143     
144     void mungeSymboltree()
145     {
146
147         if (!this.munge) {
148             return;
149         }
150
151         // One problem with obfuscation resides in the use of undeclared
152         // and un-namespaced global symbols that are 3 characters or less
153         // in length. Here is an example:
154         //
155         //     var declaredGlobalVar;
156         //
157         //     function declaredGlobalFn() {
158         //         var localvar;
159         //         localvar = abc; // abc is an undeclared global symbol
160         //     }
161         //
162         // In the example above, there is a slim chance that localvar may be
163         // munged to 'abc', conflicting with the undeclared global symbol
164         // abc, creating a potential bug. The following code detects such
165         // global symbols. This must be done AFTER the entire file has been
166         // parsed, and BEFORE munging the symbol tree. Note that declaring
167         // extra symbols in the global scope won't hurt.
168         //
169         // Note: Since we go through all the tokens to do this, we also use
170         // the opportunity to count how many times each identifier is used.
171
172         this.ts.rewind();
173         this.braceNesting = 0;
174         this.mode = ScopeParserMode.PASS2_SYMBOL_TREE;
175         
176         //println("MUNGING?");
177         
178         this.parseScope(this.globalScope);
179         
180         //this.globalScope.dump();
181         
182         
183         this.globalScope.munge();
184     }
185
186
187     void log(string str)
188     {
189         print(str);
190         //print ("                    ".substring(0, this.braceNesting*2) + str);
191         
192         //println("<B>LOG:</B>" + htmlescape(str) + "<BR/>\n");
193     }
194     void logR (string str)
195     {
196             //println("<B>LOG:</B>" + str + "<BR/>");
197     }
198
199      
200     
201
202
203     void parseScope(Scope scope) // parse a token stream..
204     {
205         //this.timerPrint("parseScope EnterScope"); 
206         //this.log(">>> ENTER SCOPE" + this.scopes.length);
207        
208         var expressionBraceNesting = this.braceNesting + 0;
209         
210         var parensNesting = 0;
211         
212         var isObjectLitAr = new Gee.ArrayList<bool>();
213         isObjectLitAr.add(false);
214      
215         
216        
217         //var scopeIndent = ''; 
218         //this.scopes.forEach(function() {
219         //    scopeIndent += '   '; 
220         //});
221         //print(">> ENTER SCOPE");
222         
223         
224         
225         
226         var token = this.ts.lookTok(1);
227         while (token != null) {
228           //  this.timerPrint("parseScope AFTER lookT: " + token.toString()); 
229             //this.dumpToken(token , this.scopes, this.braceNesting);
230             //print('SCOPE:' + token.toString());
231             //this.log(token.data);
232             //if (token.type == 'NAME') {
233             //    print('*' + token.data);
234             //}
235             switch(token.type + '.' + token.name) {
236                 case "KEYW.VAR":
237                 case "KEYW.CONST": // not really relivant as it's only mozzy that does this.
238                     //print('SCOPE-VAR:' + token.toString());
239                     var vstart = this.ts.cursor +1;
240                     
241                     //this.log("parseScope GOT VAR/CONST : " + token.toString()); 
242                     while (true) {
243                         token = this.ts.nextTok();
244                         //!this.debug|| print( token.toString());
245                        // print('SCOPE-VAR-VAL:' + JSON.stringify(token, null, 4));
246                         if (token == null) { // can return false at EOF!
247                             break;
248                         }
249                         if (token.name == "VAR" || token.data == ",") { // kludge..
250                             continue;
251                         }
252                         //this.logR("parseScope GOT VAR  : <B>" + token.toString() + "</B>"); 
253                         if (token.type != "NAME") {
254                                 
255                             for(var i = Int.max(this.ts.cursor-10,0); i < this.ts.cursor+1; i++) {
256                                 print(this.ts.tokens[i].toString());
257                             }
258                             
259                             print( "var without ident");
260                             GLib.Process.exit (0);
261                         }
262                         
263
264                         if (this.mode == ScopeParserMode.BUILDING_SYMBOL_TREE) {
265                             var identifier = scope.getIdentifier(token.data,token) ;
266                             
267                             if (identifier == false) {
268                                 scope.declareIdentifier(token.data, token);
269                             } else {
270                                 token.identifier = identifier;
271                                 this.warn("(SCOPE) The variable " + token.data  + " (line:" + token.line + ")  has already been declared in the same scope...");
272                             }
273                         }
274
275                         token = this.ts.nextTok();
276                         //!this.debug|| print(token.toString());
277                         /*
278                         assert token.getType() == Token.SEMI ||
279                                 token.getType() == Token.ASSIGN ||
280                                 token.getType() == Token.COMMA ||
281                                 token.getType() == Token.IN;
282                         */
283                         if (token.name == "IN") {
284                             break;
285                         } else {
286                             //var bn = this.braceNesting;
287                             var bn = this.braceNesting;
288                             var nts = new Gee.ArrayList<Token>();
289                             while (true) {
290                                 if (!token || token.type == "VOID" || token.data == ",") {
291                                     break;
292                                 }
293                                 nts.add(token);
294                                 token = this.ts.nextTok();
295                             }
296                             if (nts.size > 0) {
297                                 var TS = this.ts;
298                                 this.ts = new TokenStream(nts);
299                                 this.parseExpression(scope);
300                                 this.ts = TS;
301                             }
302                                
303                             this.braceNesting = bn;
304                             //this.braceNesting = bn;
305                             //this.logR("parseScope DONE  : <B>ParseExpression</B> - tok is:" + this.ts.lookT(0).toString()); 
306                             
307                             token = this.ts.lookTok(1);
308                             //!this.debug|| 
309                            // print("AFTER EXP: " + token.toString());
310                             if (token.data == ";") {
311                                 break;
312                             }
313                         }
314                     }
315                     
316                     //print("VAR:")
317                     //this.ts.dump(vstart , this.ts.cursor);
318                     
319                     break;
320                     
321                     
322                 case "KEYW.FUNCTION":
323                     //if (this.mode == 'BUILDING_SYMBOL_TREE') 
324                     //    print('SCOPE-FUNC:' + JSON.stringify(token,null,4));
325                     //println("<i>"+token.data+"</i>");
326                      var bn = this.braceNesting;
327                     this.parseFunctionDeclaration(scope);
328                      this.braceNesting = bn;
329                     break;
330
331                 case "PUNC.LEFT_CURLY": // {
332                 case "PUNC.LEFT_PAREN": // (    
333                 case "PUNC.LEFT_BRACE": // [
334                     //print('SCOPE-CURLY/PAREN:' + token.toString());
335                     //println("<i>"+token.data+"</i>");
336                     var curTS = this.ts;
337                     if (token.props.size() > 0) {
338                         
339                         // { a : ... , c : .... }
340                         
341                         for (var prop in token.props) {
342                             
343                             
344                           //  print('SCOPE-PROPS:' + JSON.stringify(token.props[prop],null,4));
345                             if (token.props[prop].val[0].data == 'function') {
346                                 // parse a function..
347                                 this.ts = new TokenStream(token.props[prop].val);
348                                 this.ts.nextTok();
349                                 this.parseFunctionDeclaration(scope);
350                                 
351                                 continue;
352                             }
353                             // key value..
354                             
355                             this.ts = new TokenStream(token.props[prop].val);
356                             this.parseExpression(scope);
357                             
358                         }
359                         this.ts = curTS;
360                         
361                         // it's an object literal..
362                         // the values could be replaced..
363                         break;
364                     }
365                     
366                     // ( ... ) or { .... } not object literals..
367                     
368                     var _this = this;
369                     for (var xx =0; xx < token.items.length; xx++) {
370                                 expr = token.items[xx];
371                     //token.items.forEach(function(expr) {
372                             //print(expr.toString());
373                            _this.ts = new TokenStream(expr);
374                             //if (curTS.data == '(') {
375                                 _this.parseScope(scope)
376                             //} else {
377                               //  _this.parseExpression(scope)
378                             //}
379                           
380                     }  
381                     this.ts = curTS;
382                     //print("NOT PROPS"); Seed.quit();
383                     
384                     //isObjectLitAr.push(false);
385                     //this.braceNesting++;
386                     
387                     //print(">>>>>> OBJLIT PUSH(false)" + this.braceNesting);
388                     break;
389
390                 case "PUNC.RIGHT_CURLY": // }
391                     //print("<< EXIT SCOPE");
392                     return;
393               
394                 case "KEYW.WITH":
395                     //print('SCOPE-WITH:' + token.toString());
396                     //println("<i>"+token.data+"</i>");   
397                     if (this.mode == "BUILDING_SYMBOL_TREE") {
398                         // Inside a 'with' block, it is impossible to figure out
399                         // statically whether a symbol is a local variable or an
400                         // object member. As a consequence, the only thing we can
401                         // do is turn the obfuscation off for the highest scope
402                         // containing the 'with' block.
403                         this.protectScopeFromObfuscation(scope);
404                         this.warn("Using 'with' is not recommended." + (this.munge ? " Moreover, using 'with' reduces the level of compression!" : ""), true);
405                     }
406                     break;
407
408                 case "KEYW.CATCH":
409                     //print('SCOPE-CATCH:' + token.toString());
410                     //println("<i>"+token.data+"</i>");
411                     this.parseCatch(scope);
412                     break;
413
414                 case "STRN.DOUBLE_QUOTE": // used for object lit detection..
415                 case "STRN.SINGLE_QUOTE":
416                   //  print('SCOPE-STRING:' + token.toString());
417                     //println("<i>"+token.data+"</i>");
418
419                     if (this.ts.lookTok(-1).data == '{' && this.ts.lookTok(1).data == ':') {
420                         // then we are in an object lit.. -> we need to flag the brace as such...
421                         isObjectLitAr.pop();
422                         isObjectLitAr.push(true);
423                         //print(">>>>>> OBJLIT REPUSH(true)");
424                     }
425                     isInObjectLitAr = isObjectLitAr[isObjectLitAr.length-1];
426                     
427                     if (isInObjectLitAr &&  this.ts.lookTok(1).data == ':' &&
428                         ( this.ts.lookTok(-1).data == '{'  ||  this.ts.lookTok(-1).data == ':' )) {
429                         // see if we can replace..
430                         // remove the quotes..
431                         // should do a bit more checking!!!! (what about wierd char's in the string..
432                         var str = token.data.substring(1,token.data.length-1);
433                         if (/^[a-z_]+$/i.test(str) && ScopeParser.idents.indexOf(str) < 0) {
434                             token.outData = str;
435                         }
436                         
437                          
438                         
439                     }
440                     
441                     break;
442                 
443                 case "NAME.NAME":
444                     //print('SCOPE-NAME:' + token.toString());
445                     //print("DEAL WITH NAME:");
446                     // got identifier..
447                     // look for  { ** : <- indicates obj literal.. ** this could occur with numbers ..
448                     // skip anyting with "." before it..!!
449                      
450                     if (this.ts.lookTok(-1).data == ".") {
451                         // skip, it's an object prop.
452                         //println("<i>"+token.data+"</i>");
453                         break;
454                     }
455                     //print("SYMBOL: " + token.toString());
456                     
457                     symbol = token.data;
458                     if (symbol == 'this') {
459                         break;
460                     }
461                     if (this.mode == 'PASS2_SYMBOL_TREE') {
462                         
463                         //println("GOT IDENT: -2 : " + this.ts.lookT(-2).toString() + " <BR> ..... -1 :  " +  this.ts.lookT(-1).toString() + " <BR> "); 
464                         
465                         //print ("MUNGE?" + symbol);
466                         
467                         //println("GOT IDENT: <B>" + symbol + "</B><BR/>");
468                              
469                             //println("GOT IDENT (2): <B>" + symbol + "</B><BR/>");
470                         identifier = this.getIdentifier(symbol, scope, token);
471                         
472                         if (identifier == false) {
473 // BUG!find out where builtin is defined...
474                             if (symbol.length <= 3 &&  Scope.builtin.indexOf(symbol) < 0) {
475                                 // Here, we found an undeclared and un-namespaced symbol that is
476                                 // 3 characters or less in length. Declare it in the global scope.
477                                 // We don't need to declare longer symbols since they won't cause
478                                 // any conflict with other munged symbols.
479                                 this.globalScope.declareIdentifier(symbol, token);
480                                 this.warn("Found an undeclared symbol: " + symbol + ' (line:' + token.line + ')', true);
481                             }
482                             
483                             //println("GOT IDENT IGNORE(3): <B>" + symbol + "</B><BR/>");
484                         } else {
485                             token.identifier = identifier;
486                             identifier.refcount++;
487                         }
488                     }   
489                     
490                     break;
491                     //println("<B>SID</B>");
492                 default:
493                     if (token.type != 'KEYW') {
494                         break;
495                     }
496                     //print('SCOPE-KEYW:' + token.toString());
497                    // print("Check eval:");
498                 
499                     symbol = token.data;
500                     
501                      if (this.mode == 'BUILDING_SYMBOL_TREE') {
502
503                         if (token.name == "EVAL") {
504                             
505                             //print(JSON.stringify(token, null,4));
506                             // look back one and see if we can find a comment!!!
507                             //if (this.ts.look(-1).type == "COMM") {
508                             if (token.prefix && token.prefix.match(/eval/)) {
509                                 // look for eval:var:noreplace\n
510                                 //print("MATCH!?");
511                                 var _t = this;
512                                 token.prefix.replace(/eval:var:([a-z_]+)/ig, function(m, a) {
513                                     //print("GOT: " + a);
514                                     var hi = _t.getIdentifier(a, scope, token);
515                                    // println("PROTECT "+a+" from munge" + (hi ? "FOUND" : "MISSING"));
516                                     if (hi) {
517                                       //  print("PROTECT "+a+" from munge");
518                                         //print(JSON.stringify(hi,null,4));
519                                         hi.toMunge = false;
520                                     }
521                                     
522                                 });
523                                 
524                                 
525                             } else {
526                                 
527                             
528                                 this.protectScopeFromObfuscation(scope);
529                                 this.warn("Using 'eval' is not recommended. (use  eval:var:noreplace in comments to optimize) " + (this.munge ? " Moreover, using 'eval' reduces the level of compression!" : ""), true);
530                             }
531
532                         }
533
534                     }
535                     break;
536                 
537                 
538             } // end switch
539             
540             
541             //print("parseScope TOK : " + token.toString()); 
542             token = this.ts.nextTok();
543             //if (this.ts.nextT()) break;
544             
545         }
546         //print("<<< EXIT SCOPE");
547         //print("<<<<<<<EXIT SCOPE ERR?" +this.scopes.length);
548     },
549
550     expN : 0,
551     parseExpression : function(scope) {
552
553         // Parse the expression until we encounter a comma or a semi-colon
554         // in the same brace nesting, bracket nesting and paren nesting.
555         // Parse functions if any...
556         //println("<i>EXP</i><BR/>");
557         !this.debug || print("PARSE EXPR");
558         this.expN++;
559          
560         // for printing stuff..
561        
562         
563         
564         var symbol;
565         var token;
566         
567         var identifier;
568
569         var expressionBraceNesting = this.braceNesting + 0;
570         var bracketNesting = 0;
571         var parensNesting = 0;
572         var isInObjectLitAr;
573         var isObjectLitAr = [ false ];
574         
575         
576             
577         
578         //print(scopeIndent + ">> ENTER EXPRESSION" + this.expN);
579         while ((token = this.ts.nextTok())) {
580      
581         
582             
583            /*
584             // moved out of loop?
585            currentScope = this.scopes[this.scopes.length-1];
586             
587             var scopeIndent = ''; 
588             this.scopes.forEach(function() {
589                 scopeIndent += '   '; 
590             });
591            */ 
592            
593            //this.dumpToken(token,  this.scopes, this.braceNesting );
594             //print('EXPR' +  token.toString());
595             
596             
597             //println("<i>"+token.data+"</i>");
598             //this.log("EXP:" + token.data);
599             switch (token.type) {
600                 case 'PUNC':
601                     //print("EXPR-PUNC:" + token.toString());
602                     
603                     switch(token.data) {
604                          
605                         case ';':
606                             //print("<< EXIT EXPRESSION");
607                             break;
608
609                         case ',':
610                             
611                             break;
612
613                        
614                         case '(': //Token.LP:
615                         case '{': //Token.LC:
616                         case '[': //Token.LB:
617                             //print('SCOPE-CURLY/PAREN/BRACE:' + token.toString());
618                            // print('SCOPE-CURLY/PAREN/BRACE:' + JSON.stringify(token, null,4));
619                             //println("<i>"+token.data+"</i>");
620                             var curTS = this.ts;
621                             if (token.props) {
622                                 
623                                 for (var prop in token.props) {
624                                     if (!token.props[prop].val.length) {
625                                         print(JSON.stringify(token.props, null,4));
626                                     }
627                                     
628                                     if (token.props[prop].val[0].data == 'function') {
629                                         // parse a function..
630                                         this.ts = new TokenStream(token.props[prop].val);
631                                         this.ts.nextTok();
632                                         this.parseFunctionDeclaration(scope);
633                                         continue;
634                                     }
635                                     // key value..
636                                     
637                                     this.ts = new TokenStream(token.props[prop].val);
638                                     this.parseExpression(scope);
639                                     
640                                 }
641                                 this.ts = curTS;
642                                 
643                                 // it's an object literal..
644                                 // the values could be replaced..
645                                 break;
646                             }
647                             
648                             
649                             var _this = this;
650                             token.items.forEach(function(expr) {
651                                   _this.ts = new TokenStream(expr);
652                                   _this.parseExpression(scope)
653                             });
654                             this.ts = curTS;
655                         
656                         
657                     
658                             ///print(">>>>> EXP PUSH(false)"+this.braceNesting);
659                             break;
660
661                        
662                         
663                          
664                             
665                         case ')': //Token.RP:
666                         case ']': //Token.RB:
667                         case '}': //Token.RB:
668                             //print("<< EXIT EXPRESSION");
669                             return;
670                            
671  
672              
673                             parensNesting++;
674                             break;
675
676                         
677                             
678                     }
679                     break;
680                     
681                 case 'STRN': // used for object lit detection..
682                     //if (this.mode == 'BUILDING_SYMBOL_TREE')    
683                         //print("EXPR-STR:" + JSON.stringify(token, null, 4));
684                
685                      
686                     break;
687                 
688                       
689              
690                 case 'NAME':
691                     if (this.mode == 'BUILDING_SYMBOL_TREE') {
692                         
693                         //print("EXPR-NAME:" + JSON.stringify(token, null, 4));
694                     } else {
695                         //print("EXPR-NAME:" + token.toString());
696                     }
697                     symbol = token.data;
698                     //print("in NAME = " + token.toString());
699                     //print("in NAME 0: " + this.ts.look(0).toString());
700                     //print("in NAME 2: " + this.ts.lookTok(2).toString());
701                     
702                     //print(this.ts.lookTok(-1).data);
703                     // prefixed with '.'
704                     if (this.ts.lookTok(-1).data == ".") {
705                         //skip '.'
706                         break;
707                     }
708                     if (symbol == 'this') {
709                         break;
710                        }
711                     
712                     if (this.mode == 'PASS2_SYMBOL_TREE') {
713
714                         identifier = this.getIdentifier(symbol, scope, token);
715                         //println("<B>??</B>");
716                         if (identifier == false) {
717
718                             if (symbol.length <= 3 &&  Scope.builtin.indexOf(symbol) < 0) {
719                                 // Here, we found an undeclared and un-namespaced symbol that is
720                                 // 3 characters or less in length. Declare it in the global scope.
721                                 // We don't need to declare longer symbols since they won't cause
722                                 // any conflict with other munged symbols.
723                                 this.globalScope.declareIdentifier(symbol, token);
724                                 this.warn("Found an undeclared symbol: " + symbol + ' (line:' + token.line + ')', true);
725                                 //print("Found an undeclared symbol: " + symbol + ' (line:' + token.line + ')');
726                                 //throw "OOPS";
727                             } else {
728                                 //print("undeclared:" + token.toString())
729                             }
730                             
731                             
732                         } else {
733                             //println("<B>++</B>");
734                             token.identifier = identifier;
735                             identifier.refcount++;
736                         }
737                         
738                     }
739                     break;
740                     
741                     
742                     
743                     
744                     //println("<B>EID</B>");
745                 case 'KEYW':   
746                     //if (this.mode == 'BUILDING_SYMBOL_TREE') 
747                     //    print("EXPR-KEYW:" + JSON.stringify(token, null, 4));
748                     
749                     //print('EXPR-KEYW:' + token.toString());
750                     if (token.name == "FUNCTION") {
751                         
752                         this.parseFunctionDeclaration(scope);
753                         break;
754                     }
755                
756                      
757                     symbol = token.data;
758                     if (this.mode == 'BUILDING_SYMBOL_TREE') {
759                         
760                         if (token.name == "EVAL") {
761                             //print(JSON.stringify(token,null,4));
762                             if (token.prefix && token.prefix.match(/eval:var:/g)) {
763                                 // look for eval:var:noreplace\n
764                                // print("GOT MATCH?");
765                                 var _t = this;
766                                 token.prefix.replace(/eval:var:([a-z]+)/ig, function(m, a) {
767                                     
768                                     //print("PROTECT: " + a);
769                                     
770                                     
771                                     var hi = _t.getIdentifier(a, scope, token);
772                                    //println("PROTECT "+a+" from munge" + (hi ? "FOUND" : "MISSING"));
773                                     if (hi) {
774                                       //  println("PROTECT "+a+" from munge");
775                                         hi.toMunge = false;
776                                     }
777                                     
778                                     
779                                 });
780                                 
781                             } else {
782                                 this.protectScopeFromObfuscation(scope);
783                                 this.warn("Using 'eval' is not recommended." + (this.munge ? " Moreover, using 'eval' reduces the level of compression!" : ""), true);
784                             }
785                             
786
787                         }
788                         break;
789                     } 
790                 default:
791                     //if (this.mode == 'BUILDING_SYMBOL_TREE') 
792                     //    print("EXPR-SKIP:" + JSON.stringify(token, null, 4));
793                     break;
794             }
795             
796         }
797         //print("<< EXIT EXPRESSION");
798         this.expN--;
799     },
800
801
802     parseCatch : function(scope) {
803
804         var symbol;
805         var token;
806          
807         var identifier;
808         
809         //token = getToken(-1);
810         //assert token.getType() == Token.CATCH;
811         token = this.ts.nextTok(1);
812         token = this.ts.nextTok(1);
813         
814         
815         //print(JSON.stringify(this.ts,null,4));
816         //assert token.getType() == Token.LP; (
817         //token = this.ts.nextTok();
818         //assert token.getType() == Token.NAME;
819         
820         symbol = token.items[0][0].data;
821         
822
823         if (this.mode == 'BUILDING_SYMBOL_TREE') {
824             // We must declare the exception identifier in the containing function
825             // scope to avoid errors related to the obfuscation process. No need to
826             // display a warning if the symbol was already declared here...
827             scope.declareIdentifier(symbol, token.items[0][0]);
828         } else {
829             //?? why inc the refcount?? - that should be set when building the tree???
830             identifier = this.getIdentifier(symbol, scope, token.items[0][0]);
831             identifier.refcount++;
832         }
833         
834         //token = this.ts.nextTok();
835         //assert token.getType() == Token.RP; // )
836     },
837     
838     parseFunctionDeclaration : function(scope) 
839     {
840         //print("PARSE FUNCTION");
841         var symbol;
842         var token;
843         
844         var fnScope = false;
845         var identifier;
846         var b4braceNesting = this.braceNesting + 0;
847         
848         //this.logR("<B>PARSING FUNCTION</B>");
849         
850
851         token = this.ts.nextTok();
852         if (token.type == "NAME") {
853             if (this.mode == 'BUILDING_SYMBOL_TREE') {
854                 // Get the name of the function and declare it in the current scope.
855                 symbol = token.data;
856                 if (scope.getIdentifier(symbol,token) != false) {
857                     this.warn("The function " + symbol + " has already been declared in the same scope...", true);
858                 }
859                 scope.declareIdentifier(symbol,token);
860             }
861             token =  this.ts.nextTok();
862         }
863         
864         
865         // return function() {.... 
866         while (token.data != "(") {
867             //print(token.toString());
868             token =  this.ts.nextTok();
869              
870         }
871         
872         
873         //assert token.getType() == Token.LP;
874         if (this.mode == 'BUILDING_SYMBOL_TREE') {
875             fnScope = new Scope(1, scope, token.n, '', token);
876             
877             //println("STORING SCOPE" + this.ts.cursor);
878             
879             this.indexedScopes[token.id] = fnScope;
880             
881         } else {
882             //qln("FETCHING SCOPE" + this.ts.cursor);
883             fnScope = this.indexedScopes[token.id];
884         }
885         //if (this.mode == 'BUILDING_SYMBOL_TREE') 
886         //  print('FUNC-PARSE:' + JSON.stringify(token,null,4));
887         // Parse function arguments.
888         var args = token.items;
889         for (var argpos =0; argpos < args.length; argpos++) {
890              
891             token = args[argpos][0];
892             //print ("FUNC ARGS: " + token.toString())
893             //assert token.getType() == Token.NAME ||
894             //        token.getType() == Token.COMMA;
895             if (token.type == 'NAME' && this.mode == 'BUILDING_SYMBOL_TREE') {
896                 symbol = token.data;
897                 identifier = fnScope.declareIdentifier(symbol,token);
898                 if (symbol == "$super" && argpos == 0) {
899                     // Exception for Prototype 1.6...
900                     identifier.preventMunging();
901                 }
902                 //argpos++;
903             }
904         }
905         
906         token = this.ts.nextTok();
907         //print('FUNC-BODY:' + JSON.stringify(token.items,null,4));
908         //Seed.quit();
909         //print(token.toString());
910         // assert token.getType() == Token.LC;
911         //this.braceNesting++;
912         
913         //token = this.ts.nextTok();
914         //print(token.toString());
915         var outTS = this.ts;
916         var _this = this;
917         token.items.forEach(function(tar) {
918             _this.ts = new TokenStream(tar);
919             _this.parseScope(fnScope);
920             
921             
922         });
923         
924         //print(JSON.stringify(this.ts,null,4));
925         //this.parseScope(fnScope);
926         this.ts = outTS;
927         // now pop it off the stack!!!
928        
929         //this.braceNesting = b4braceNesting;
930         //print("ENDFN -1: " + this.ts.lookTok(-1).toString());
931         //print("ENDFN 0: " + this.ts.lookTok(0).toString());
932         //print("ENDFN 1: " + this.ts.lookTok(1).toString());
933     },
934     
935     protectScopeFromObfuscation : function(scope) {
936             //assert scope != null;
937         
938         if (scope == this.globalScope) {
939             // The global scope does not get obfuscated,
940             // so we don't need to worry about it...
941             return;
942         }
943
944         // Find the highest local scope containing the specified scope.
945         while (scope && scope.parent != this.globalScope) {
946             scope = scope.parent;
947         }
948
949         //assert scope.getParentScope() == globalScope;
950         scope.preventMunging();
951     },
952     
953     getIdentifier: function(symbol, scope, token) {
954         var identifier;
955         while (scope != false) {
956             identifier = scope.getIdentifier(symbol, token);
957             //println("ScopeParser.getIdentgetUsedSymbols("+symbol+")=" + scope.getUsedSymbols().join(','));
958             if (identifier) {
959                 return identifier;
960             }
961             scope = scope.parent;
962         }
963         return false;
964     }
965 };