Fix #5732 - change to cmake for build
[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                                                                                         print("failed  to get %s val from token %s\n", k, token.asString());
704                                                                                         continue;                                                                               
705                                                                                 }
706
707                                                                                 //if (val.vals.size < 1) {
708                                                                                 //print(JSON.stringify(token.props, null,4));
709                                                                                 //}
710
711
712                                                                                 if (val.vals.size > 0 && val.vals.get(0).data == "function") {
713                                                                                         // parse a function..
714                                                                                         this.ts = new TokenStream(val.vals);
715                                                                                         this.ts.nextTok();
716                                                                                         this.parseFunctionDeclaration(scope);
717                                                                                         continue;
718                                                                                 }
719                                                                                 // key value..
720
721                                                                                 this.ts = new TokenStream(val.vals);
722                                                                                 this.parseExpression(scope);
723
724                                                                         }
725                                                                         this.ts = curTS;
726
727                                                                         // it's an object literal..
728                                                                         // the values could be replaced..
729                                                                         break;
730                                                                 }
731
732
733
734                                                                 foreach(var expr in token.items) {
735
736                                                                         this.ts = new TokenStream(expr);
737                                                                         this.parseExpression(scope);
738                                                                 }
739                                                                 this.ts = curTS;
740
741
742
743                                                                 ///print(">>>>> EXP PUSH(false)"+this.braceNesting);
744                                                                 break;
745
746                                        
747                                         
748                                          
749                                             
750                                         case ")": //Token.RP:
751                                         case "]": //Token.RB:
752                                         case "}": //Token.RB:
753                                             //print("<< EXIT EXPRESSION");
754                                             return;
755                                            
756          
757                              
758                                            // parensNesting++;
759                                            // break;
760
761                                         
762                                             
763                                     }
764                                     break;
765                                     
766                                 case TokenType.STRN: // used for object lit detection..
767                                     //if (this.mode == 'BUILDING_SYMBOL_TREE')    
768                                         //print("EXPR-STR:" + JSON.stringify(token, null, 4));
769                                
770                                      
771                                     break;
772                                 
773                                       
774                              
775                                 case TokenType.NAME:
776                                     if (this.mode == ScopeParserMode.BUILDING_SYMBOL_TREE) {
777                                         
778                                         //print("EXPR-NAME:" + JSON.stringify(token, null, 4));
779                                     } else {
780                                         //print("EXPR-NAME:" + token.toString());
781                                     }
782                                     var symbol = token.data;
783                                     //print("expression got NAME = %s \n" , symbol);
784                                     //print("in NAME 0: " + this.ts.look(0).toString());
785                                     //print("in NAME 2: " + this.ts.lookTok(2).toString());
786                                     
787                                     //print(this.ts.lookTok(-1).data);
788                                     // prefixed with '.'
789                                     if (this.ts.lookTok(-1).data == ".") {
790                                         //skip '.'
791                                         break;
792                                     }
793                                     if (symbol == "this") {
794                                         break;
795                                }
796                                     
797                                     if (this.mode == ScopeParserMode.PASS2_SYMBOL_TREE) {
798
799                                         var identifier = this.getIdentifier(symbol, scope, token);
800                                         //println("<B>??</B>");
801                                         if (identifier == null) {
802
803                                             if (symbol.length <= 3 &&  Scope.builtin.index_of(symbol) < 0) {
804                                                 // Here, we found an undeclared and un-namespaced symbol that is
805                                                 // 3 characters or less in length. Declare it in the global scope.
806                                                 // We don't need to declare longer symbols since they won't cause
807                                                 // any conflict with other munged symbols.
808                                                 this.globalScope.declareIdentifier(symbol, token);
809                                                 
810                                                 this.packer.logError(
811                                                                                 Packer.ResultType.warn,
812                                                                                 this.filename,
813                                                                                 token.line,
814                                                                                 "Found an undeclared symbol: " + symbol
815                                                                         );
816
817                                                 
818                                                 
819                                                  
820                                                 //print("Found an undeclared symbol: " + symbol + ' (line:' + token.line + ')');
821                                                 //throw "OOPS";
822                                             } else {
823                                                 //print("undeclared:" + token.toString())
824                                             }
825                                             
826                                             
827                                         } else {
828                                             //println("<B>++</B>");
829                                             token.identifier = identifier;
830                                             identifier.refcount++;
831                                         }
832                                         
833                                     }
834                                     break;
835                                     
836                                     
837                                     
838                                     
839                                     //println("<B>EID</B>");
840                                 case TokenType.KEYW:   
841                                     //if (this.mode == 'BUILDING_SYMBOL_TREE') 
842                                     //    print("EXPR-KEYW:" + JSON.stringify(token, null, 4));
843                                     
844                                     //print('EXPR-KEYW:' + token.toString());
845                                     if (token.name == TokenName.FUNCTION) {
846                                         
847                                         this.parseFunctionDeclaration(scope);
848                                         break;
849                                     }
850                                
851                                      
852                                     var symbol = token.data;
853                                     if (this.mode == ScopeParserMode.BUILDING_SYMBOL_TREE) {
854                                         
855                                         if (token.name == TokenName.EVAL) {
856                                         
857                                         
858                                             //print(JSON.stringify(token,null,4));
859                                             
860                                             if (token.prefix.length > 0 && Regex.match_simple ("eval:var:", token.prefix,GLib.RegexCompileFlags.CASELESS)) {
861                                                 // look for eval:var:noreplace\n
862                                                // print("GOT MATCH?");
863
864                                                 
865                                            var regex = new GLib.Regex ("eval:var:([a-z_]+)",GLib.RegexCompileFlags.CASELESS );
866          
867                                                 regex.replace_eval (token.prefix, token.prefix.length, 0, 0, (match_info, result) => {
868                                                         var a = match_info.fetch(0);
869                                                     //print("PROTECT: " + a);
870                                                     
871                                                     
872                                                     var hi = this.getIdentifier(a, scope, token);
873                                                    //println("PROTECT "+a+" from munge" + (hi ? "FOUND" : "MISSING"));
874                                                     if (hi != null) {
875                                                       //  println("PROTECT "+a+" from munge");
876                                                         hi.toMunge = false;
877                                                     }
878                                                     return false;
879                                                     
880                                                 });
881                                                 
882                                             } else {
883                                                 this.protectScopeFromObfuscation(scope);
884                                                 
885                                                 this.packer.logError(
886                                                                                 Packer.ResultType.warn,
887                                                                                 this.filename,
888                                                                                 token.line,
889                                                                                 "Using 'eval' is not recommended. use eval:var in comment before eval to enable compression " + symbol
890                                                                         );
891
892                                                 
893
894                                             }
895                                             
896
897                                         }
898                                       
899                                     } 
900                                break;
901                                 default:
902                                     //if (this.mode == 'BUILDING_SYMBOL_TREE') 
903                                     //    print("EXPR-SKIP:" + JSON.stringify(token, null, 4));
904                                     break;
905                             }
906                             
907                         }
908                         //print("<< EXIT EXPRESSION");
909                         this.expN--;
910                 }
911
912
913                 void parseCatch(Scope scope) {
914
915                         
916                         //token = getToken(-1);
917                         //assert token.getType() == Token.CATCH;
918                         var token = this.ts.nextTok();
919                         token = this.ts.nextTok();
920                         
921                         
922                         //print(JSON.stringify(this.ts,null,4));
923                         //assert token.getType() == Token.LP; (
924                         //token = this.ts.nextTok();
925                         //assert token.getType() == Token.NAME;
926                         
927                         var symbol = token.items[0][0].data;
928                         
929
930                         if (this.mode == ScopeParserMode.BUILDING_SYMBOL_TREE) {
931                             // We must declare the exception identifier in the containing function
932                             // scope to avoid errors related to the obfuscation process. No need to
933                             // display a warning if the symbol was already declared here...
934                             scope.declareIdentifier(symbol, token.items[0][0]);
935                         } else {
936                             //?? why inc the refcount?? - that should be set when building the tree???
937                             var identifier = this.getIdentifier(symbol, scope, token.items[0][0]);
938                             identifier.refcount++;
939                         }
940                         
941                         //token = this.ts.nextTok();
942                         //assert token.getType() == Token.RP; // )
943                 }
944         
945                 void parseFunctionDeclaration (Scope scope) 
946                 {
947                         //print("PARSE FUNCTION");
948                         
949                          
950                         // var b4braceNesting = this.braceNesting + 0;
951                         
952                         //this.logR("<B>PARSING FUNCTION</B>");
953                         
954
955                         var token = this.ts.nextTok();
956                         if (token.type == TokenType.NAME) {
957                             if (this.mode == ScopeParserMode.BUILDING_SYMBOL_TREE) {
958                                 // Get the name of the function and declare it in the current scope.
959                                 var symbol = token.data;
960                                 if (scope.getIdentifier(symbol,token) != null) {
961                                 
962                                  
963                         this.packer.logError(
964                                                         Packer.ResultType.warn,
965                                                         this.filename,
966                                                         token.line,
967                                                         "The function " + symbol + " has already been declared in the same scope..."
968                                                 );
969
970                                 
971
972                                 }
973                                 scope.declareIdentifier(symbol,token);
974                             }
975                             token =  this.ts.nextTok();
976                         }
977                         
978                         
979                         // return function() {.... 
980                         while (token.data != "(") {
981                             //print(token.toString());
982                             token =  this.ts.nextTok();
983                              
984                         }
985                         
986                         Scope fnScope;
987                         //assert token.getType() == Token.LP;
988                         if (this.mode == ScopeParserMode.BUILDING_SYMBOL_TREE) {
989                             fnScope = new Scope(1, scope, token.id, "", token);
990                             
991                             //println("STORING SCOPE" + this.ts.cursor);
992                             
993                             this.indexedScopes.set(token.id,fnScope);
994                             
995                         } else {
996                             //qln("FETCHING SCOPE" + this.ts.cursor);
997                             fnScope = this.indexedScopes[token.id];
998                         }
999                         //if (this.mode == 'BUILDING_SYMBOL_TREE') 
1000                         //  print('FUNC-PARSE:' + JSON.stringify(token,null,4));
1001                         // Parse function arguments.
1002                         var args = token.items;
1003                         for (var argpos =0; argpos < args.size; argpos++) {
1004                              
1005                             token = args.get(argpos).get(0);
1006                             //print ("FUNC ARGS: " + token.toString())
1007                             //assert token.getType() == Token.NAME ||
1008                             //        token.getType() == Token.COMMA;
1009                             if (token.type == TokenType.NAME && this.mode == ScopeParserMode.BUILDING_SYMBOL_TREE) {
1010                                 var symbol = token.data;
1011                                 var identifier = fnScope.declareIdentifier(symbol,token);
1012                                 if (symbol == "$super" && argpos == 0) {
1013                                     // Exception for Prototype 1.6...
1014                                     identifier.toMunge = false;
1015                                 }
1016                                 //argpos++;
1017                             }
1018                         }
1019                         
1020                         token = this.ts.nextTok();
1021                         if (token == null) {
1022                                 return;
1023                         }
1024                         //print('FUNC-BODY:' + JSON.stringify(token.items,null,4));
1025                         //Seed.quit();
1026                         //print(token.toString());
1027                         // assert token.getType() == Token.LC;
1028                         //this.braceNesting++;
1029                         
1030                         //token = this.ts.nextTok();
1031                         //print(token.toString());
1032                         var outTS = this.ts;
1033                         foreach(var tar in token.items) {
1034                                 this.ts = new TokenStream(tar);
1035                                 this.parseScope(fnScope);
1036                             
1037                         }
1038                         
1039                         //print(JSON.stringify(this.ts,null,4));
1040                         //this.parseScope(fnScope);
1041                         this.ts = outTS;
1042                         // now pop it off the stack!!!
1043                    
1044                         //this.braceNesting = b4braceNesting;
1045                         //print("ENDFN -1: " + this.ts.lookTok(-1).toString());
1046                         //print("ENDFN 0: " + this.ts.lookTok(0).toString());
1047                         //print("ENDFN 1: " + this.ts.lookTok(1).toString());
1048                 }
1049         
1050                 void protectScopeFromObfuscation (Scope scope) {
1051                             //assert scope != null;
1052                         
1053                         if (scope == this.globalScope) {
1054                             // The global scope does not get obfuscated,
1055                             // so we don't need to worry about it...
1056                             return;
1057                         }
1058
1059                         // Find the highest local scope containing the specified scope.
1060                         while (scope != null && scope.parent != this.globalScope) {
1061                             scope = scope.parent;
1062                         }
1063  
1064                         //assert scope.getParentScope() == globalScope;
1065                         scope.preventMunging();
1066                 }
1067          
1068                 Identifier? getIdentifier(string symbol, Scope in_scope, Token token) 
1069                 {
1070                         Identifier identifier;
1071                         var scope = in_scope;
1072                         while (scope != null) {
1073                             identifier = scope.getIdentifier(symbol, token);
1074                             //println("ScopeParser.getIdentgetUsedSymbols("+symbol+")=" + scope.getUsedSymbols().join(','));
1075                             if (identifier != null) {
1076                                 return identifier;
1077                             }
1078                             scope = scope.parent;
1079                         }
1080                         return null;
1081                 }
1082                 public void printWarnings()
1083                 {
1084                         foreach(var w in this.warnings) {
1085                                 print("%s\n",w);
1086                         }
1087                 }
1088                 
1089         }
1090 }
1091