Skip to content

Commit

Permalink
Add a method of dumping the AST
Browse files Browse the repository at this point in the history
  • Loading branch information
Richard van Velzen authored and rvanvelzen committed Aug 17, 2016
1 parent 8430123 commit 51e944b
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 10 deletions.
5 changes: 4 additions & 1 deletion bin/uglifyjs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ You need to pass an argument to this option to specify the name that your module
.describe("name-cache", "File to hold mangled names mappings")
.describe("pure-funcs", "List of functions that can be safely removed if their return value is not used")
.describe("dump-spidermonkey-ast", "Dump SpiderMonkey AST to stdout.")
.describe("dump-ast", "Dump the AST instead of generating output")

.alias("p", "prefix")
.alias("o", "output")
Expand Down Expand Up @@ -130,6 +131,7 @@ You need to pass an argument to this option to specify the name that your module
.boolean("bare-returns")
.boolean("keep-fnames")
.boolean("reserve-domprops")
.boolean("dump-ast")

.wrap(80)

Expand Down Expand Up @@ -476,7 +478,8 @@ async.eachLimit(files, 1, function (file, cb) {
print(JSON.stringify(TOPLEVEL.to_mozilla_ast(), null, 2));
} else {
time_it("generate", function(){
TOPLEVEL.print(output);
if (ARGS.dump_ast) output.print(TOPLEVEL.dump());
else TOPLEVEL.print(output);
});

output = output.get();
Expand Down
35 changes: 26 additions & 9 deletions lib/ast.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,24 @@ function DEFNODE(type, props, methods, base) {
if (arguments.length < 4) base = AST_Node;
if (!props) props = [];
else props = props.split(/\s+/);
var dump_props = [];
props = props.map(function(prop){
if (/!$/.test(prop)) {
prop = prop.slice(0, -1);
} else {
dump_props.push(prop);
}
return prop;
});
var self_props = props;
if (base && base.PROPS)
props = props.concat(base.PROPS);
var code = "return function AST_" + type + "(props){ if (props) { ";
for (var i = props.length; --i >= 0;) {
if (base) {
if (base.PROPS) props = props.concat(base.PROPS);
if (base.DUMP_PROPS) dump_props = dump_props.concat(base.DUMP_PROPS);
}
var code = "return function AST_" + type + "(props){";
if (type) { code += "this._class = 'AST_" + type + "';"; }
code += " if (props) { ";
for (var i = 0; i < props.length; ++i) {
code += "this." + props[i] + " = props." + props[i] + ";";
}
var proto = base && new base;
Expand All @@ -66,6 +79,7 @@ function DEFNODE(type, props, methods, base) {
if (base) base.SUBCLASSES.push(ctor);
ctor.prototype.CTOR = ctor;
ctor.PROPS = props || null;
ctor.DUMP_PROPS = dump_props.sort();
ctor.SELF_PROPS = self_props;
ctor.SUBCLASSES = [];
if (type) {
Expand All @@ -88,7 +102,7 @@ function DEFNODE(type, props, methods, base) {
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw", {
}, null);

var AST_Node = DEFNODE("Node", "start end", {
var AST_Node = DEFNODE("Node", "start! end!", {
clone: function() {
return new this.CTOR(this);
},
Expand All @@ -102,6 +116,9 @@ var AST_Node = DEFNODE("Node", "start end", {
},
walk: function(visitor) {
return this._walk(visitor); // not sure the indirection will be any help
},
dump: function () {
return JSON.stringify(dump_internal(this), null, 2);
}
}, null);

Expand All @@ -121,7 +138,7 @@ var AST_Debugger = DEFNODE("Debugger", null, {
$documentation: "Represents a debugger statement",
}, AST_Statement);

var AST_Directive = DEFNODE("Directive", "value scope quote", {
var AST_Directive = DEFNODE("Directive", "value scope! quote", {
$documentation: "Represents a directive, like \"use strict\";",
$propdoc: {
value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
Expand Down Expand Up @@ -278,7 +295,7 @@ var AST_With = DEFNODE("With", "expression", {

/* -----[ scope and functions ]----- */

var AST_Scope = DEFNODE("Scope", "directives 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",
$propdoc: {
directives: "[string*/S] an array of directives declared in this scope",
Expand All @@ -292,7 +309,7 @@ var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_
},
}, AST_Block);

var AST_Toplevel = DEFNODE("Toplevel", "globals", {
var AST_Toplevel = DEFNODE("Toplevel", "globals!", {
$documentation: "The toplevel scope",
$propdoc: {
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
Expand Down Expand Up @@ -789,7 +806,7 @@ var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
$documentation: "An object getter property",
}, AST_ObjectProperty);

var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
var AST_Symbol = DEFNODE("Symbol", "scope! name thedef!", {
$propdoc: {
name: "[string] name of this symbol",
scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
Expand Down
21 changes: 21 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,27 @@ function set_intersection(a, b) {
});
};

function dump_internal(val) {
if (val) {
if (typeof val.map == 'function') {
return val.filter(function (x) {
return x != null;
}).map(dump_internal);
}

if (val.CTOR) {
var out = {_class: val._class};
val.CTOR.DUMP_PROPS.forEach(function(prop){
if (val[prop] != null) {
out[prop] = dump_internal(val[prop]);
}
});
return out;
}
}
return val;
};

// this function is taken from Acorn [1], written by Marijn Haverbeke
// [1] https://github.com/marijnh/acorn
function makePredicate(words) {
Expand Down

0 comments on commit 51e944b

Please sign in to comment.