diff --git a/src/babel/transformation/helpers/replace-supers.js b/src/babel/transformation/helpers/replace-supers.js index 0539f84820..a57da1569f 100644 --- a/src/babel/transformation/helpers/replace-supers.js +++ b/src/babel/transformation/helpers/replace-supers.js @@ -1,9 +1,6 @@ -module.exports = ReplaceSupers; - import * as messages from "../../messages"; import * as t from "../../types"; - function isIllegalBareSuper(node, parent) { if (!t.isSuper(node)) return false; if (t.isMemberExpression(parent, { computed: false })) return false; @@ -11,6 +8,10 @@ function isIllegalBareSuper(node, parent) { return true; } +function isMemberExpressionSuper(node) { + return t.isMemberExpression(node) && t.isSuper(node.object); +} + var visitor = { enter(node, parent, scope, state) { var topLevel = state.topLevel; @@ -18,7 +19,7 @@ var visitor = { if (t.isFunction(node) && !t.isArrowFunctionExpression(node)) { // we need to call traverseLevel again so we're context aware - self.traverseLevel(node, false); + self.traverseLevel(this, false); return this.skip(); } @@ -35,7 +36,10 @@ var visitor = { var callback = self.specHandle; if (self.isLoose) callback = self.looseHandle; - return callback.call(self, this, getThisReference); + var result = callback.call(self, this, getThisReference); + if (result) this.hasSuper = true; + if (result === true) return; + return result; } }; @@ -47,6 +51,7 @@ export default class ReplaceSupers { constructor(opts: Object, inClass?: boolean = false) { this.topLevelThisReference = opts.topLevelThisReference; + this.methodPath = opts.methodPath; this.methodNode = opts.methodNode; this.superRef = opts.superRef; this.isStatic = opts.isStatic; @@ -118,16 +123,16 @@ export default class ReplaceSupers { */ replace() { - this.traverseLevel(this.methodNode.value, true); + this.traverseLevel(this.methodPath.get("value"), true); } /** * Description */ - traverseLevel(node: Object, topLevel: boolean) { + traverseLevel(path: TraversalPath, topLevel: boolean) { var state = { self: this, topLevel: topLevel }; - this.scope.traverse(node, visitor, state); + path.traverse(visitor, state); } /** @@ -190,7 +195,6 @@ export default class ReplaceSupers { looseHandle(path: TraversalPath, getThisReference: Function) { var node = path.node; if (path.isSuper()) { - this.hasSuper = true; return this.getLooseSuperProperty(node, path.parent); } else if (path.isCallExpression()) { var callee = node.callee; @@ -198,9 +202,31 @@ export default class ReplaceSupers { if (!t.isSuper(callee.object)) return; // super.test(); -> objectRef.prototype.MethodName.call(this); - this.hasSuper = true; t.appendToMemberExpression(callee, t.identifier("call")); node.arguments.unshift(getThisReference()); + return true; + } + } + + /** + * Description + */ + + specHandleAssignmentExpression(ref, path, node, getThisReference) { + if (node.operator === "=") { + // super.name = "val"; -> _set(Object.getPrototypeOf(objectRef.prototype), "name", this); + return this.setSuperProperty(node.left.property, node.right, node.left.computed, getThisReference()); + } else { + // super.age += 2; -> var _ref = super.age; super.age = _ref + 2; + ref ||= path.scope.generateUidIdentifier("ref"); + return [ + t.variableDeclaration("var", [ + t.variableDeclarator(ref, node.left) + ]), + t.expressionStatement( + t.assignmentExpression("=", node.left, t.binaryExpression(node.operator[0], ref, node.right)) + ) + ]; } } @@ -237,7 +263,7 @@ export default class ReplaceSupers { var methodName = methodNode.key.name || "METHOD_NAME"; throw this.file.errorWithNode(node, messages.get("classesIllegalSuperCall", methodName)); } - } else if (t.isMemberExpression(callee) && t.isSuper(callee.object)) { + } else if (isMemberExpressionSuper(callee)) { // super.test(); -> _get(Object.getPrototypeOf(objectRef.prototype), "test", this).call(this); property = callee.property; computed = callee.computed; @@ -247,16 +273,22 @@ export default class ReplaceSupers { // super.name; -> _get(Object.getPrototypeOf(objectRef.prototype), "name", this); property = node.property; computed = node.computed; - } else if (t.isAssignmentExpression(node) && t.isSuper(node.left.object) && methodNode.kind === "set") { - // super.name = "val"; -> _set(Object.getPrototypeOf(objectRef.prototype), "name", this); - this.hasSuper = true; - return this.setSuperProperty(node.left.property, node.right, node.left.computed, getThisReference()); + } else if (t.isUpdateExpression(node) && isMemberExpressionSuper(node.argument)) { + var binary = t.binaryExpression(node.operator[0], node.argument, t.literal(1)) + if (node.prefix) { + // ++super.foo; -> super.foo += 1; + return this.specHandleAssignmentExpression(null, path, binary, getThisReference); + } else { + // super.foo++; -> var _ref = super.foo; super.foo = _ref + 1; + var ref = path.scope.generateUidIdentifier("ref"); + return this.specHandleAssignmentExpression(ref, path, binary, getThisReference).concat(t.expressionStatement(ref)); + } + } else if (t.isAssignmentExpression(node) && isMemberExpressionSuper(node.left)) { + return this.specHandleAssignmentExpression(null, path, node, getThisReference); } if (!property) return; - this.hasSuper = true; - thisReference = getThisReference(); var superProperty = this.getSuperProperty(property, computed, thisReference); if (args) { diff --git a/src/babel/transformation/templates/helper-set.js b/src/babel/transformation/templates/helper-set.js index e75271d6eb..e1bbdcc5d9 100644 --- a/src/babel/transformation/templates/helper-set.js +++ b/src/babel/transformation/templates/helper-set.js @@ -5,15 +5,17 @@ var parent = Object.getPrototypeOf(object); if (parent !== null) { - return set(parent, property, value, receiver); + set(parent, property, value, receiver); } } else if ("value" in desc && desc.writable) { - return desc.value = value; + desc.value = value; } else { var setter = desc.set; if (setter !== undefined) { - return setter.call(receiver, value); + setter.call(receiver, value); } } + + return value; }); diff --git a/src/babel/transformation/transformers/es6/classes.js b/src/babel/transformation/transformers/es6/classes.js index b6dde9a5fb..53d25bc8c3 100644 --- a/src/babel/transformation/transformers/es6/classes.js +++ b/src/babel/transformation/transformers/es6/classes.js @@ -279,6 +279,7 @@ class ClassTransformer { if (isConstructor) this.verifyConstructor(path); var replaceSupers = new ReplaceSupers({ + methodPath: path, methodNode: node, objectRef: this.classRef, superRef: this.superName, diff --git a/src/babel/transformation/transformers/es6/object-super.js b/src/babel/transformation/transformers/es6/object-super.js index 842c5ed4e5..dce80dd2f1 100644 --- a/src/babel/transformation/transformers/es6/object-super.js +++ b/src/babel/transformation/transformers/es6/object-super.js @@ -3,7 +3,7 @@ import * as t from "../../../types"; export var check = t.isSuper; -function Property(node, scope, getObjectRef, file) { +function Property(path, node, scope, getObjectRef, file) { if (!node.method) return; var value = node.value; @@ -13,6 +13,7 @@ function Property(node, scope, getObjectRef, file) { topLevelThisReference: thisExpr, getObjectRef: getObjectRef, methodNode: node, + methodPath: path, isStatic: true, scope: scope, file: file @@ -33,8 +34,9 @@ export function ObjectExpression(node, parent, scope, file) { var objectRef; var getObjectRef = () => objectRef ||= scope.generateUidIdentifier("obj"); + var propPaths = this.get("properties"); for (var i = 0; i < node.properties.length; i++) { - Property(node.properties[i], scope, getObjectRef, file); + Property(propPaths[i], node.properties[i], scope, getObjectRef, file); } if (objectRef) { diff --git a/src/babel/traversal/path/index.js b/src/babel/traversal/path/index.js index a6457c4638..f2320dd516 100644 --- a/src/babel/traversal/path/index.js +++ b/src/babel/traversal/path/index.js @@ -92,6 +92,7 @@ export default class TraversalPath { if (this.parentPath.isExpressionStatement() || this.parentPath.isLabeledStatement()) { return this.parentPath.insertBefore(nodes); } else if (this.isPreviousType("Statement")) { + this._maybePopFromStatements(nodes); if (Array.isArray(this.container)) { this._containerInsertBefore(nodes); } else if (this.isStatementOrBlock()) { @@ -130,6 +131,13 @@ export default class TraversalPath { this._containerInsert(this.key + 1, nodes); } + _maybePopFromStatements(nodes) { + var last = nodes[nodes.length - 1]; + if (t.isExpressionStatement(last) && t.isIdentifier(last.expression)) { + nodes.pop(); + } + } + isStatementOrBlock() { if (t.isLabeledStatement(this.parent) || t.isBlockStatement(this.container)) { return false; @@ -145,6 +153,7 @@ export default class TraversalPath { if (this.parentPath.isExpressionStatement() || this.parentPath.isLabeledStatement()) { return this.parentPath.insertAfter(nodes); } else if (this.isPreviousType("Statement")) { + this._maybePopFromStatements(nodes); if (Array.isArray(this.container)) { this._containerInsertAfter(nodes); } else if (this.isStatementOrBlock()) { @@ -222,6 +231,13 @@ export default class TraversalPath { } remove() { + var removeParent = false; + if (this.parentPath) { + removeParent ||= this.parentPath.isExpressionStatement(); + removeParent ||= this.parentPath.isSequenceExpression() && this.parent.expressions.length === 1 + if (removeParent) return this.parentPath.remove(); + } + this._remove(); this.removed = true; } diff --git a/test/core/fixtures/transformation/es6.object-super/statically-bound/expected.js b/test/core/fixtures/transformation/es6.object-super/statically-bound/expected.js index bd6e043d00..7f90bdfc53 100644 --- a/test/core/fixtures/transformation/es6.object-super/statically-bound/expected.js +++ b/test/core/fixtures/transformation/es6.object-super/statically-bound/expected.js @@ -6,8 +6,6 @@ var _get = function get(object, property, receiver) { var desc = Object.getOwnPr var o = _obj = { m: function m() { - var _this = this; - return _get(Object.getPrototypeOf(_obj), "x", this); } }; diff --git a/test/core/fixtures/transformation/es7.class-properties/super-expression/expected.js b/test/core/fixtures/transformation/es7.class-properties/super-expression/expected.js index e11b4ad9b8..53454b3c98 100644 --- a/test/core/fixtures/transformation/es7.class-properties/super-expression/expected.js +++ b/test/core/fixtures/transformation/es7.class-properties/super-expression/expected.js @@ -1,9 +1,9 @@ "use strict"; -var _temp; - var Foo = (function (_Bar) { function Foo() { + var _temp; + babelHelpers.classCallCheck(this, Foo); foo((_temp = babelHelpers.get(Object.getPrototypeOf(Foo.prototype), "constructor", this).call(this), this.bar = "foo", _temp)); diff --git a/test/core/traceur.js b/test/core/traceur.js index e25a77c6d9..f732768b30 100644 --- a/test/core/traceur.js +++ b/test/core/traceur.js @@ -45,8 +45,6 @@ require("./_transformation-helper")({ "Classes/NestedClassSuperAnimal", // TODO: #426 - "Classes/SuperUnary", - "Classes/SuperPostfix", // TODO: investigate "Classes/SuperSet",