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