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