Uglifyjs: AST_Node.from_mozilla_ast fails on setter

Created on 11 May 2017  路  15Comments  路  Source: mishoo/UglifyJS

Bug report

ES5

Uglify version (2.x ~ 3.x)

JavaScript input a = {set foo(u) {} }

ast = ug.AST_Node.from_mozilla_ast({
    "type": "Program",
    "body": [
        {
            "type": "ExpressionStatement",
            "expression": {
                "type": "AssignmentExpression",
                "operator": "=",
                "left": {
                    "type": "Identifier",
                    "name": "a"
                },
                "right": {
                    "type": "ObjectExpression",
                    "properties": [
                        {
                            "type": "Property",
                            "key": {
                                "type": "Identifier",
                                "name": "foo"
                            },
                            "computed": false,
                            "value": {
                                "type": "FunctionExpression",
                                "id": null,
                                "params": [
                                    {
                                        "type": "Identifier",
                                        "name": "u"
                                    }
                                ],
                                "body": {
                                    "type": "BlockStatement",
                                    "body": []
                                },
                                "generator": false,
                                "expression": false
                            },
                            "kind": "set",
                            "method": false,
                            "shorthand": false
                        }
                    ]
                }
            }
        }
    ],
    "sourceType": "script"
});

console.log(ast.body[0].body.right.properties[0])

outputs:

AST_Node {
  ...
  value:
   ...,
  key: 'foo' }

expected:

AST_Node {
  ...
  value:
  ...,
  key:
   AST_Node {
     ...
     thedef: undefined,
     name: 'foo',
     scope: undefined } }
bug

Most helpful comment

crazy/lazy idea - how about we borrow the codegen from test/ufuzz.js, push it through esprima then AST_Node.from_mozilla_ast() for the round-trip test?

(movie voice) That's so crazy it just might work!

As long as it's only run within the UGLIFYJS_TEST_ALL=1 tests, sure.

I guess esprima could be a devDependency only. The version of acorn in packages.json for the old 2.x --acorn flag is prehistoric. Maybe we should get rid of these altogether in 3.x?

  "devDependencies": {
    "acorn": "~0.6.0",
    "escodegen": "~1.3.3",
    "esfuzz": "~0.3.1",
    "estraverse": "~1.5.1",

All 15 comments

foo is an instance of AST_ObjectSetter rather than a string.

Moreover, I couldn't produce the same Mozilla AST as you do in your example with a={set foo(u){}}:

$ echo 'a={set foo(u){}}' | uglify-js -o spidermonkey
{
  "type": "Program",
  "body": [
    {
      "type": "ExpressionStatement",
      "expression": {
        "type": "AssignmentExpression",
        "operator": "=",
        "left": {
          "type": "Identifier",
          "name": "a"
        },
        "right": {
          "type": "ObjectExpression",
          "properties": [
            {
              "type": "Property",
              "kind": "set",
              "key": {
                "type": "Literal",
                "value": {
                  "name": "foo"
                }
              },
              "value": {
                "type": "FunctionExpression",
                "id": null,
                "params": [
                  {
                    "type": "Identifier",
                    "name": "u"
                  }
                ],
                "body": {
                  "type": "BlockStatement",
                  "body": []
                }
              }
            }
          ]
        }
      }
    }
  ]
}

(Souce positional information removed for brevity.)

There is certainly a bug with object setters because the round-trip test wouldn't pass:

$ echo 'a={set foo(u){}}' | uglify-js -o spidermonkey | uglify-js -p spidermonkey
ERROR: Object doesn't support property or method 'print'
   at Anonymous function (Function code:4930:9)

Without object setter, it works:

$ echo 'a={foo:function(u){}}' | uglify-js -o spidermonkey | uglify-js -p spidermonkey
a={foo:function(u){}};

@alexlamsl The AST input is generated via esprima, I ran into this bug when I was intending to pipe babel's ast output into uglify-js. There's a difference between UglifyJS's spidermonkey output and Esprima: the type of the property key is an Identifier rather than a Literal.

(see here )

As well as I know, for an object literal with a setter, UglifyJS.parse yields a property node which has its key as an AST_Token, and its value.name as null.

@kyriosli thanks for the clarification.

Please consider adding the example you've got in https://github.com/mishoo/UglifyJS2/issues/1914#issue-227981641 into test/mocha/spidermonkey.js - the description of the test can be Should parse from esprima correctly (a={set foo(u){}}).

I ran into this bug when I was intending to pipe babel's ast output into uglify-js

off topic - Babel/Babylon has a different native AST format, but it appears that an estree plugin now exists: https://github.com/babel/babylon/pull/277

@kzc the one thing that struck me is how test/mozilla-ast.js isn't picking up any of this - but it might be based on some old/odd versions of estree or something.

it might be based on some old/odd versions of estree or something.

Either that or it simply has gaps in its test coverage.

@kzc crazy/lazy idea - how about we borrow the codegen from test/ufuzz.js, push it through esprima then AST_Node.from_mozilla_ast() for the round-trip test?

It almost seems like it could replace test/mozilla-ast.js altogether.

crazy/lazy idea - how about we borrow the codegen from test/ufuzz.js, push it through esprima then AST_Node.from_mozilla_ast() for the round-trip test?

(movie voice) That's so crazy it just might work!

As long as it's only run within the UGLIFYJS_TEST_ALL=1 tests, sure.

I guess esprima could be a devDependency only. The version of acorn in packages.json for the old 2.x --acorn flag is prehistoric. Maybe we should get rid of these altogether in 3.x?

  "devDependencies": {
    "acorn": "~0.6.0",
    "escodegen": "~1.3.3",
    "esfuzz": "~0.3.1",
    "estraverse": "~1.5.1",

@alexlamsl Wait - one problem - I don't think test/ufuzz.js generates setters and getters.

Wait - one problem - I don't think test/ufuzz.js generates setters and getters.

We can fix that :wink:

If the names of the fuzzed setters and getters would match the names of the already existing SAFE_KEYS for object literal properties in test/ufuzz.js then that fuzzed code would be exercised.

Edit: it would mean that pure_getters couldn't be used in fuzz tests though.

@kzc pure_getters=true isn't getting fuzzed for known issues anyway.

pure_getters=strict is being fuzzed by default.

Latest versions of esprima & acorn both don't have dependencies (great!) and have similar download statistics.

Which one shall we pick for test/mozilla-ast.js rewrite?

Which one shall we pick for test/mozilla-ast.js rewrite?

I prefer acorn because it is used by rollup and buble and I have some familiarity with it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kzc picture kzc  路  5Comments

hacdias picture hacdias  路  5Comments

chrismanley picture chrismanley  路  5Comments

lhtdesignde picture lhtdesignde  路  3Comments

Havunen picture Havunen  路  5Comments