Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a method of dumping the AST #769

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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