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