support for directives
authorMihai Bazon <mihai@bazon.net>
Tue, 18 Sep 2012 10:21:09 +0000 (13:21 +0300)
committerMihai Bazon <mihai@bazon.net>
Tue, 18 Sep 2012 10:21:09 +0000 (13:21 +0300)
lib/ast.js
lib/compress.js
lib/output.js
lib/parse.js
lib/scope.js

index 39a62b1..5c4b7e6 100644 (file)
@@ -126,7 +126,7 @@ var AST_Debugger = DEFNODE("Debugger", null, {
     $documentation: "Represents a debugger statement"
 }, AST_StatementBase);
 
-var AST_Directive = DEFNODE("Directive", "value", {
+var AST_Directive = DEFNODE("Directive", "value scope", {
     $documentation: "Represents a directive, like \"use strict\";"
 }, AST_StatementBase);
 
@@ -235,7 +235,7 @@ var AST_With = DEFNODE("With", "expression", {
 
 /* -----[ scope and functions ]----- */
 
-var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent_scope enclosed cname", {
+var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
     $documentation: "Base class for all statements introducing a lexical scope",
 }, AST_Block);
 
index aedd78f..9d5dac1 100644 (file)
@@ -742,6 +742,13 @@ function Compressor(options, false_by_default) {
 
     /* -----[ node squeezers ]----- */
 
+    SQUEEZE(AST_Directive, function(self, compressor){
+        if (self.hoisted || self.scope.has_directive(self.value) !== self.scope) {
+            return new AST_EmptyStatement(self);
+        }
+        return self;
+    });
+
     SQUEEZE(AST_Debugger, function(self, compressor){
         if (compressor.option("drop_debugger"))
             return new AST_EmptyStatement(self);
@@ -796,6 +803,10 @@ function Compressor(options, false_by_default) {
             var vars = {}, vars_found = 0, vardecl = [];
             var tw = new TreeWalker(function(node){
                 if (node !== self) {
+                    if (node instanceof AST_Directive && (hoist_funs || hoist_vars) && !node.hoisted) {
+                        hoisted.unshift(node.clone());
+                        node.hoisted = true;
+                    }
                     if (node instanceof AST_Defun && hoist_funs && !node.hoisted) {
                         hoisted.push(node.clone());
                         node.hoisted = true;
index ee11aeb..149fc05 100644 (file)
@@ -428,6 +428,7 @@ function OutputStream(options) {
 
     DEFPRINT(AST_Directive, function(self, output){
         output.print_string(self.value);
+        output.semicolon();
     });
     DEFPRINT(AST_Debugger, function(self, output){
         output.print("debugger");
index 77c7534..6a5aad2 100644 (file)
@@ -773,8 +773,8 @@ function parse($TEXT, exigent_mode) {
           case "string":
             var dir = S.in_directives, stat = simple_statement();
             // XXXv2: decide how to fix directives
-            // if (dir && stat instanceof AST_String && !is("punc", ","))
-            //     return new AST_Directive({ value: stat.value });
+            if (dir && stat.body instanceof AST_String && !is("punc", ","))
+                return new AST_Directive({ value: stat.body.value });
             return stat;
           case "num":
           case "regexp":
index 79d6e91..dfbff4b 100644 (file)
@@ -82,6 +82,11 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
             scope = save_scope;
             return true;        // don't descend again in TreeWalker
         }
+        if (node instanceof AST_Directive) {
+            node.scope = scope;
+            push_uniq(scope.directives, node.value);
+            return true;
+        }
         if (node instanceof AST_With) {
             for (var s = scope; s; s = s.parent_scope)
                 s.uses_with = true;
@@ -193,6 +198,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
 });
 
 AST_Scope.DEFMETHOD("init_scope_vars", function(){
+    this.directives = [];     // contains the directives defined in this scope, i.e. "use strict"
     this.variables = {};      // map name to AST_SymbolVar (variables defined in this scope; includes functions)
     this.functions = {};      // map name to AST_SymbolDefun (functions defined in this scope)
     this.uses_with = false;   // will be set to true if this or some nested scope uses the `with` statement
@@ -233,6 +239,11 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
         : (this.parent_scope && this.parent_scope.find_variable(name));
 });
 
+AST_Scope.DEFMETHOD("has_directive", function(value){
+    return this.parent_scope && this.parent_scope.has_directive(value)
+        || (this.directives.indexOf(value) >= 0 ? this : null);
+});
+
 AST_Scope.DEFMETHOD("def_function", function(symbol){
     this.functions[symbol.name] = this.def_variable(symbol);
 });